From 75cf1d70fd3ae35834e7d641fe96892d603d2b74 Mon Sep 17 00:00:00 2001 From: Alexis Date: Wed, 29 Aug 2018 20:36:39 +0200 Subject: [PATCH 1/5] feat(WIP): tray feat(wip): tray feat(wip): tray feat(WIP): gdbus feat(WIP): tray --- include/client.hpp | 18 +-- include/factory.hpp | 1 + include/modules/sni/snh.hpp | 38 +++++ include/modules/sni/sni.hpp | 41 ++++++ include/modules/sni/snw.hpp | 47 ++++++ include/modules/sni/tray.hpp | 25 ++++ meson.build | 6 +- protocol/dbus-menu.xml | 69 +++++++++ protocol/dbus-status-notifier-item.xml | 77 ++++++++++ protocol/dbus-status-notifier-watcher.xml | 42 ++++++ protocol/meson.build | 20 ++- resources/config | 2 +- resources/style.css | 6 +- src/factory.cpp | 3 + src/modules/sni/snh.cpp | 148 +++++++++++++++++++ src/modules/sni/sni.cpp | 165 +++++++++++++++++++++ src/modules/sni/snw.cpp | 169 ++++++++++++++++++++++ src/modules/sni/tray.cpp | 26 ++++ 18 files changed, 888 insertions(+), 15 deletions(-) create mode 100644 include/modules/sni/snh.hpp create mode 100644 include/modules/sni/sni.hpp create mode 100644 include/modules/sni/snw.hpp create mode 100644 include/modules/sni/tray.hpp create mode 100644 protocol/dbus-menu.xml create mode 100644 protocol/dbus-status-notifier-item.xml create mode 100644 protocol/dbus-status-notifier-watcher.xml create mode 100644 src/modules/sni/snh.cpp create mode 100644 src/modules/sni/sni.cpp create mode 100644 src/modules/sni/snw.cpp create mode 100644 src/modules/sni/tray.cpp diff --git a/include/client.hpp b/include/client.hpp index d4374dcd..01551050 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -2,14 +2,10 @@ #include #include - #include - #include #include - #include - #include "bar.hpp" namespace waybar { @@ -30,14 +26,14 @@ class Client { struct wl_seat *seat = nullptr; std::vector> bars; -private: - void bindInterfaces(); - auto setupCss(); + private: + void bindInterfaces(); + auto setupCss(); - static void handleGlobal(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version); - static void handleGlobalRemove(void *data, - struct wl_registry *registry, uint32_t name); + static void handleGlobal(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version); + static void handleGlobalRemove(void *data, + struct wl_registry *registry, uint32_t name); }; } diff --git a/include/factory.hpp b/include/factory.hpp index 718a3e74..d2e5553b 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -9,6 +9,7 @@ #include "modules/battery.hpp" #include "modules/memory.hpp" #include "modules/cpu.hpp" +#include "modules/sni/tray.hpp" #ifdef HAVE_LIBNL #include "modules/network.hpp" #endif diff --git a/include/modules/sni/snh.hpp b/include/modules/sni/snh.hpp new file mode 100644 index 00000000..585f2888 --- /dev/null +++ b/include/modules/sni/snh.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include "modules/sni/sni.hpp" + +namespace waybar::modules::SNI { + +class Host { + public: + Host(Glib::Dispatcher&); + ~Host(); + 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(SnOrgKdeStatusNotifierWatcher*, const gchar*, + gpointer); + static void itemUnregistered(SnOrgKdeStatusNotifierWatcher*, const gchar*, + gpointer); + + void getBusNameAndObjectPath(const gchar*, gchar**, 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; + SnOrgKdeStatusNotifierWatcher* watcher_ = nullptr; +}; + +} diff --git a/include/modules/sni/sni.hpp b/include/modules/sni/sni.hpp new file mode 100644 index 00000000..eb57902c --- /dev/null +++ b/include/modules/sni/sni.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace waybar::modules::SNI { + +class Item { + public: + Item(std::string, std::string, Glib::Dispatcher&); + ~Item(); + int icon_size; + int effective_icon_size; + Gtk::Image* image; + std::string category; + std::string id; + std::string status; + + std::string title; + int32_t window_id; + std::string icon_name; + std::string overlay_icon_name; + std::string attention_icon_name; + std::string attention_movie_name; + std::string icon_theme_path; + std::string menu; + bool item_is_menu; + private: + static void proxyReady(GObject* obj, GAsyncResult* res, gpointer data); + static void getAll(GObject* obj, GAsyncResult* res, gpointer data); + + void updateImage(); + Glib::RefPtr getIconByName(std::string name, int size); + std::string bus_name_; + std::string object_path_; + Glib::Dispatcher& dp_; + GCancellable* cancellable_ = nullptr; + SnOrgKdeStatusNotifierItem* proxy_ = nullptr; +}; + +} diff --git a/include/modules/sni/snw.hpp b/include/modules/sni/snw.hpp new file mode 100644 index 00000000..f8965e38 --- /dev/null +++ b/include/modules/sni/snw.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +namespace waybar::modules::SNI { + +typedef enum { + GF_WATCH_TYPE_HOST, + GF_WATCH_TYPE_ITEM +} GfWatchType; + +typedef struct { + GfWatchType type; + gchar* service; + gchar* bus_name; + gchar* object_path; + guint watch_id; +} GfWatch; + +class Watcher { + public: + Watcher(); + ~Watcher(); + private: + static void busAcquired(GDBusConnection*, const gchar*, gpointer); + static gboolean handleRegisterHost(Watcher*, + GDBusMethodInvocation*, const gchar*); + static gboolean handleRegisterItem(Watcher*, + GDBusMethodInvocation*, const gchar*); + static GfWatch* gfWatchFind(GSList* list, const gchar* bus_name, + const gchar* object_path); + static GfWatch* gfWatchNew(GfWatchType type, + const gchar* service, const gchar* bus_name, const gchar* object_path); + static void nameVanished(GDBusConnection* connection, const char* name, + gpointer data); + + void updateRegisteredItems(SnOrgKdeStatusNotifierWatcher* obj); + + uint32_t bus_name_id_; + uint32_t watcher_id_; + GSList* hosts_ = nullptr; + GSList* items_ = nullptr; + SnOrgKdeStatusNotifierWatcher *watcher_ = nullptr; +}; + +} diff --git a/include/modules/sni/tray.hpp b/include/modules/sni/tray.hpp new file mode 100644 index 00000000..5be478f4 --- /dev/null +++ b/include/modules/sni/tray.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "util/json.hpp" +#include "IModule.hpp" +#include "modules/sni/snw.hpp" +#include "modules/sni/snh.hpp" + +namespace waybar::modules::SNI { + +class Tray : public IModule { + public: + Tray(const Json::Value&); + auto update() -> void; + operator Gtk::Widget &(); + private: + std::thread thread_; + const Json::Value& config_; + Gtk::Box box_; + SNI::Watcher watcher_ ; + SNI::Host host_; +}; + +} diff --git a/meson.build b/meson.build index 1833440c..733a0040 100644 --- a/meson.build +++ b/meson.build @@ -46,6 +46,10 @@ src_files = files( 'src/modules/clock.cpp', 'src/modules/custom.cpp', 'src/modules/cpu.cpp', + 'src/modules/sni/tray.cpp', + 'src/modules/sni/snw.cpp', + 'src/modules/sni/snh.cpp', + 'src/modules/sni/sni.cpp', 'src/main.cpp', 'src/bar.cpp', 'src/client.cpp' @@ -88,7 +92,7 @@ executable( gtkmm, libnl, libnlgen, - libpulse, + libpulse ], include_directories: [include_directories('include')], install: true, diff --git a/protocol/dbus-menu.xml b/protocol/dbus-menu.xml new file mode 100644 index 00000000..ae5d7906 --- /dev/null +++ b/protocol/dbus-menu.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/protocol/dbus-status-notifier-item.xml b/protocol/dbus-status-notifier-item.xml new file mode 100644 index 00000000..bc70ee03 --- /dev/null +++ b/protocol/dbus-status-notifier-item.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/protocol/dbus-status-notifier-watcher.xml b/protocol/dbus-status-notifier-watcher.xml new file mode 100644 index 00000000..b054f7e6 --- /dev/null +++ b/protocol/dbus-status-notifier-watcher.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/protocol/meson.build b/protocol/meson.build index 793aa13e..01cb771b 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -36,10 +36,28 @@ foreach p : client_protocols client_protos_headers += wayland_scanner_client.process(xml) endforeach +gdbus_code = generator( + find_program('gdbus-codegen'), + output: '@BASENAME@.c', + arguments: ['--c-namespace', 'Sn', '--body', '--output', '@OUTPUT@', '@INPUT@'] +) + +gdbus_header = generator( + find_program('gdbus-codegen'), + output: '@BASENAME@.h', + arguments: ['--c-namespace', 'Sn', '--header', '--output', '@OUTPUT@', '@INPUT@'] +) + +client_protos_src += gdbus_code.process('./dbus-status-notifier-watcher.xml') +client_protos_headers += gdbus_header.process('./dbus-status-notifier-watcher.xml') + +client_protos_src += gdbus_code.process('./dbus-status-notifier-item.xml') +client_protos_headers += gdbus_header.process('./dbus-status-notifier-item.xml') + lib_client_protos = static_library( 'client_protos', client_protos_src + client_protos_headers, - dependencies: [wayland_client] + dependencies: [wayland_client, gtkmm] ) # for the include directory client_protos = declare_dependency( diff --git a/resources/config b/resources/config index da3012bb..de1a99a8 100644 --- a/resources/config +++ b/resources/config @@ -6,7 +6,7 @@ // Choose the order of the modules "modules-left": ["sway/workspaces", "custom/spotify"], "modules-center": ["sway/window"], - "modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock"], + "modules-right": ["pulseaudio", "network", "cpu", "memory", "battery", "clock", "tray"], // Modules configuration // "sway/workspaces": { // "disable-scroll": true, diff --git a/resources/style.css b/resources/style.css index cacdb67e..a3538d48 100644 --- a/resources/style.css +++ b/resources/style.css @@ -23,7 +23,7 @@ window { border-bottom: 3px solid white; } -#clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify { +#clock, #battery, #cpu, #memory, #network, #pulseaudio, #custom-spotify, #tray { padding: 0 10px; margin: 0 5px; } @@ -90,3 +90,7 @@ window { background: #66cc99; color: #2a5c45; } + +#tray { + background-color: #2980b9; +} \ No newline at end of file diff --git a/src/factory.cpp b/src/factory.cpp index fc69b0b1..d3c53f39 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -27,6 +27,9 @@ waybar::IModule* waybar::Factory::makeModule(const std::string &name) const if (name == "clock") { return new waybar::modules::Clock(config_[name]); } + if (name == "tray") { + return new waybar::modules::SNI::Tray(config_[name]); + } #ifdef HAVE_LIBNL if (name == "network") { return new waybar::modules::Network(config_[name]); diff --git a/src/modules/sni/snh.cpp b/src/modules/sni/snh.cpp new file mode 100644 index 00000000..49a6fb89 --- /dev/null +++ b/src/modules/sni/snh.cpp @@ -0,0 +1,148 @@ +#include "modules/sni/snh.hpp" + +#include + +waybar::modules::SNI::Host::Host(Glib::Dispatcher& dp) +: dp_(dp) +{ + 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); +} + +waybar::modules::SNI::Host::~Host() +{ +} + +void waybar::modules::SNI::Host::busAcquired(GDBusConnection* connection, + const gchar* name, gpointer data) +{ + 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); +} + +void waybar::modules::SNI::Host::nameAppeared(GDBusConnection* connection, + const gchar* name, const gchar* name_owner, gpointer data) +{ + auto host = static_cast(data); + if (host->cancellable_ != nullptr) { + std::cout << "WTF" << std::endl; + } + host->cancellable_ = g_cancellable_new(); + sn_org_kde_status_notifier_watcher_proxy_new( + connection, + G_DBUS_PROXY_FLAGS_NONE, + "org.kde.StatusNotifierWatcher", + "/StatusNotifierWatcher", + host->cancellable_, &Host::proxyReady, data); +} + +void waybar::modules::SNI::Host::nameVanished(GDBusConnection* connection, + const gchar* name, gpointer data) +{ + auto host = static_cast(data); + g_cancellable_cancel(host->cancellable_); + g_clear_object(&host->cancellable_); + g_clear_object(&host->watcher_); + host->items.clear(); +} + +void waybar::modules::SNI::Host::proxyReady(GObject* src, GAsyncResult* res, + gpointer data) +{ + GError* error = nullptr; + SnOrgKdeStatusNotifierWatcher* watcher = + sn_org_kde_status_notifier_watcher_proxy_new_finish(res, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + std::cerr << error->message << std::endl; + g_error_free(error); + return; + } + auto host = static_cast(data); + host->watcher_ = watcher; + if (error != nullptr) { + std::cerr << error->message << std::endl; + g_error_free(error); + return; + } + sn_org_kde_status_notifier_watcher_call_register_status_notifier_host( + host->watcher_, host->object_path_.c_str(), host->cancellable_, + &Host::registerHost, data); +} + +void waybar::modules::SNI::Host::registerHost(GObject* src, GAsyncResult* res, + gpointer data) +{ + GError* error = nullptr; + sn_org_kde_status_notifier_watcher_call_register_status_notifier_host_finish( + SN_ORG_KDE_STATUS_NOTIFIER_WATCHER(src), res, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + std::cerr << error->message << std::endl; + g_error_free(error); + return; + } + auto host = static_cast(data); + if (error != nullptr) { + std::cerr << error->message << std::endl; + g_error_free(error); + return; + } + g_signal_connect(host->watcher_, "status-notifier-item-registered", + G_CALLBACK(&Host::itemRegistered), data); + g_signal_connect(host->watcher_, "status-notifier-item-unregistered", + G_CALLBACK(&Host::itemUnregistered), data); + auto items = + sn_org_kde_status_notifier_watcher_dup_registered_status_notifier_items(host->watcher_); + if (items) { + for (uint32_t i = 0; items[i] != nullptr; i += 1) { + host->addRegisteredItem(items[i]); + } + } + g_strfreev(items); +} + +void waybar::modules::SNI::Host::itemRegistered( + SnOrgKdeStatusNotifierWatcher* watcher, const gchar* service, gpointer data) +{ + std::cout << "Item registered" << std::endl; + auto host = static_cast(data); + host->addRegisteredItem(service); +} + +void waybar::modules::SNI::Host::itemUnregistered( + SnOrgKdeStatusNotifierWatcher* watcher, const gchar* service, gpointer data) +{ + std::cout << "Item Unregistered" << std::endl; +} + +void waybar::modules::SNI::Host::getBusNameAndObjectPath(const gchar* service, + gchar** bus_name, gchar** object_path) +{ + gchar* tmp = g_strstr_len (service, -1, "/"); + if (tmp != nullptr) { + gchar** str = g_strsplit(service, "/", 2); + *bus_name = g_strdup(str[0]); + *object_path = g_strdup(tmp); + g_strfreev(str); + } else { + *bus_name = g_strdup(service); + *object_path = g_strdup("/StatusNotifierItem"); + } +} + +void waybar::modules::SNI::Host::addRegisteredItem(const gchar* service) +{ + gchar* bus_name = nullptr; + gchar* object_path = nullptr; + + 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 new file mode 100644 index 00000000..0cd06a71 --- /dev/null +++ b/src/modules/sni/sni.cpp @@ -0,0 +1,165 @@ +#include "modules/sni/sni.hpp" + +#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) +{ + 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); +} + +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); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_error_free(error); + return; + } + auto item = static_cast(data); + item->proxy_ = proxy; + if (error) { + std::cerr << error->message << std::endl; + g_error_free(error); + return; + } + 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); +} + +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); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_error_free(error); + return; + } + auto item = static_cast(data); + if (error) { + std::cerr << error->message << std::endl; + g_error_free(error); + return; + } + GVariantIter* it = nullptr; + g_variant_get(properties, "(a{sv})", &it); + 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); + } else if (g_strcmp0(key, "Id") == 0) { + item->id = g_variant_dup_string(value, nullptr); + } else if (g_strcmp0(key, "Title") == 0) { + item->title = g_variant_dup_string(value, nullptr); + } else if (g_strcmp0(key, "Status") == 0) { + item->status = g_variant_dup_string(value, nullptr); + } else if (g_strcmp0(key, "WindowId") == 0) { + item->window_id = g_variant_get_int32 (value); + } else if (g_strcmp0(key, "IconName") == 0) { + item->icon_name = 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); + } 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); + } 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); + } 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); + } else if (g_strcmp0(key, "Menu") == 0) { + item->menu = g_variant_dup_string(value, nullptr); + } else if (g_strcmp0(key, "ItemIsMenu") == 0) { + item->item_is_menu = g_variant_get_boolean(value); + } + g_variant_unref(value); + g_free(key); + } + 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; + return; + } + if (!item->icon_theme_path.empty()) { + GtkIconTheme* icon_theme = gtk_icon_theme_get_default(); + gtk_icon_theme_append_search_path(icon_theme, + item->icon_theme_path.c_str()); + } + item->updateImage(); + item->dp_.emit(); + // TODO: handle change +} + +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); + } + } + if (pixbuf->gobj() == nullptr) { + pixbuf = getIconByName("image-missing", icon_size); + } + image->set(pixbuf); + } else { + image->set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); + image->set_pixel_size(icon_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(); + icon_theme->rescan_if_needed(); + auto sizes = icon_theme->get_icon_sizes(name.c_str()); + for (auto size : sizes) { + // -1 == scalable + if (size == request_size || size == -1) { + icon_size = request_size; + break; + } else if (size < request_size || size > icon_size) { + icon_size = size; + } + } + if (icon_size == 0) { + 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 diff --git a/src/modules/sni/snw.cpp b/src/modules/sni/snw.cpp new file mode 100644 index 00000000..fc314f54 --- /dev/null +++ b/src/modules/sni/snw.cpp @@ -0,0 +1,169 @@ +#include "modules/sni/snw.hpp" + +#include + +waybar::modules::SNI::Watcher::Watcher() +{ + GBusNameOwnerFlags flags = static_cast( + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT + | G_BUS_NAME_OWNER_FLAGS_REPLACE); + bus_name_id_ = g_bus_own_name(G_BUS_TYPE_SESSION, + "org.kde.StatusNotifierWatcher", flags, + &Watcher::busAcquired, nullptr, nullptr, this, nullptr); + watcher_ = sn_org_kde_status_notifier_watcher_skeleton_new(); +} + +waybar::modules::SNI::Watcher::~Watcher() +{ +} + +void waybar::modules::SNI::Watcher::busAcquired(GDBusConnection* connection, + const gchar* name, gpointer data) +{ + GError* error = nullptr; + auto host = static_cast(data); + g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(host->watcher_), + connection, "/StatusNotifierWatcher", &error); + if (error != nullptr) { + std::cerr << error->message << std::endl; + g_error_free(error); + return; + } + g_signal_connect_swapped(host->watcher_, + "handle-register-status-notifier-item", + G_CALLBACK(&Watcher::handleRegisterItem), data); + g_signal_connect_swapped(host->watcher_, + "handle-register-status-notifier-host", + G_CALLBACK(&Watcher::handleRegisterHost), data); + sn_org_kde_status_notifier_watcher_set_protocol_version(host->watcher_, 0); + sn_org_kde_status_notifier_watcher_set_is_status_notifier_host_registered( + host->watcher_, TRUE); + std::cout << "Bus aquired" << std::endl; +} + +gboolean waybar::modules::SNI::Watcher::handleRegisterHost( + Watcher* obj, GDBusMethodInvocation* invocation, + const gchar* service) +{ + const gchar* bus_name = service; + const gchar* object_path = "/StatusNotifierHost"; + + if (*service == '/') { + bus_name = g_dbus_method_invocation_get_sender(invocation); + object_path = service; + } + if (g_dbus_is_name(bus_name) == FALSE) { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, "D-Bus bus name '%s' is not valid", bus_name); + return TRUE; + } + auto watch = gfWatchFind(obj->hosts_, bus_name, object_path); + if (watch != nullptr) { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, "Status Notifier Host with bus name '%s' and object path '%s' is already registered", + bus_name, object_path); + return TRUE; + } + watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path); + obj->hosts_ = g_slist_prepend(obj->hosts_, watch); + sn_org_kde_status_notifier_watcher_set_is_status_notifier_host_registered( + obj->watcher_, TRUE); + if (g_slist_length(obj->hosts_)) { + sn_org_kde_status_notifier_watcher_emit_status_notifier_host_registered( + obj->watcher_); + } + sn_org_kde_status_notifier_watcher_complete_register_status_notifier_host( + obj->watcher_, invocation); + std::cout << "Host registered: " << bus_name << std::endl; + return TRUE; +} + +gboolean waybar::modules::SNI::Watcher::handleRegisterItem( + Watcher* obj, GDBusMethodInvocation* invocation, + const gchar* service) +{ + const gchar* bus_name = service; + const gchar* object_path = "/StatusNotifierItem"; + + if (*service == '/') { + bus_name = g_dbus_method_invocation_get_sender(invocation); + object_path = service; + } + if (g_dbus_is_name(bus_name) == FALSE) { + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, "D-Bus bus name '%s' is not valid", bus_name); + return TRUE; + } + auto watch = gfWatchFind(obj->items_, bus_name, object_path); + if (watch != nullptr) { + g_warning("Status Notifier Item with bus name '%s' and object path '%s' is already registered", + bus_name, object_path); + sn_org_kde_status_notifier_watcher_complete_register_status_notifier_item( + obj->watcher_, invocation); + return TRUE; + } + watch = gfWatchNew(GF_WATCH_TYPE_ITEM, service, bus_name, object_path); + obj->items_ = g_slist_prepend(obj->items_, watch); + obj->updateRegisteredItems(obj->watcher_); + gchar* tmp = g_strdup_printf("%s%s", bus_name, object_path); + sn_org_kde_status_notifier_watcher_emit_status_notifier_item_registered( + obj->watcher_, tmp); + g_free(tmp); + sn_org_kde_status_notifier_watcher_complete_register_status_notifier_item( + obj->watcher_, invocation); + return TRUE; +} + +waybar::modules::SNI::GfWatch* waybar::modules::SNI::Watcher::gfWatchFind( + GSList* list, const gchar* bus_name, const gchar* object_path) +{ + for (GSList* l = list; l != nullptr; l = g_slist_next (l)) { + GfWatch* watch = static_cast(l->data); + if (g_strcmp0 (watch->bus_name, bus_name) == 0 + && g_strcmp0 (watch->object_path, object_path) == 0) { + return watch; + } + } + return nullptr; +} + +waybar::modules::SNI::GfWatch* waybar::modules::SNI::Watcher::gfWatchNew( + GfWatchType type, const gchar* service, const gchar* bus_name, + const gchar* object_path) +{ + GfWatch* watch = g_new0(GfWatch, 1); + watch->type = type; + watch->service = g_strdup(service); + watch->bus_name = g_strdup(bus_name); + watch->object_path = g_strdup(object_path); + watch->watch_id = g_bus_watch_name(G_BUS_TYPE_SESSION, bus_name, + G_BUS_NAME_WATCHER_FLAGS_NONE, nullptr, &Watcher::nameVanished, watch, + nullptr); + return watch; +} + +void waybar::modules::SNI::Watcher::nameVanished(GDBusConnection* connection, + const char* name, gpointer data) +{ + //TODO + std::cout << "name vanished" << std::endl; +} + +void waybar::modules::SNI::Watcher::updateRegisteredItems( + SnOrgKdeStatusNotifierWatcher* obj) +{ + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("as")); + for (GSList* l = items_; l != nullptr; l = g_slist_next(l)) { + GfWatch* watch = static_cast(l->data); + gchar* item = g_strdup_printf ("%s%s", watch->bus_name, watch->object_path); + g_variant_builder_add (&builder, "s", item); + g_free (item); + } + GVariant* variant = g_variant_builder_end(&builder); + const gchar** items = g_variant_get_strv (variant, nullptr); + sn_org_kde_status_notifier_watcher_set_registered_status_notifier_items( + 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 new file mode 100644 index 00000000..6ddf7447 --- /dev/null +++ b/src/modules/sni/tray.cpp @@ -0,0 +1,26 @@ +#include "modules/sni/tray.hpp" + +#include + +waybar::modules::SNI::Tray::Tray(const Json::Value& config) + : config_(config), watcher_(), host_(dp) +{ +} + +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 (box_.get_children().size() > 0) { + box_.set_name("tray"); + box_.show_all(); + } else { + box_.set_name(""); + } +} + +waybar::modules::SNI::Tray::operator Gtk::Widget &() { + return box_; +} From 75c9477aa8182ed429cf795a21067443adb45b12 Mon Sep 17 00:00:00 2001 From: Alexis Date: Tue, 4 Sep 2018 23:50:08 +0200 Subject: [PATCH 2/5] feat(Tray): handle item unregister --- include/modules/sni/snh.hpp | 8 ++-- include/modules/sni/sni.hpp | 12 +++--- include/modules/sni/snw.hpp | 31 +++++++-------- src/bar.cpp | 6 +-- src/modules/battery.cpp | 6 +-- src/modules/pulseaudio.cpp | 2 +- src/modules/sni/snh.cpp | 57 +++++++++++++++------------ src/modules/sni/sni.cpp | 32 +++++++--------- src/modules/sni/snw.cpp | 68 ++++++++++++++++++++------------- src/modules/sni/tray.cpp | 8 ++-- src/modules/sway/window.cpp | 2 +- src/modules/sway/workspaces.cpp | 2 +- 12 files changed, 127 insertions(+), 107 deletions(-) diff --git a/include/modules/sni/snh.hpp b/include/modules/sni/snh.hpp index 585f2888..f0a3bc2f 100644 --- a/include/modules/sni/snh.hpp +++ b/include/modules/sni/snh.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "modules/sni/sni.hpp" @@ -8,8 +9,7 @@ namespace waybar::modules::SNI { class Host { public: - Host(Glib::Dispatcher&); - ~Host(); + Host(Glib::Dispatcher*); std::vector items; private: static void busAcquired(GDBusConnection*, const gchar*, gpointer); @@ -23,14 +23,14 @@ class Host { static void itemUnregistered(SnOrgKdeStatusNotifierWatcher*, const gchar*, gpointer); - void getBusNameAndObjectPath(const gchar*, gchar**, gchar**); + 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_; + Glib::Dispatcher* dp_; GCancellable* cancellable_ = nullptr; SnOrgKdeStatusNotifierWatcher* watcher_ = nullptr; }; diff --git a/include/modules/sni/sni.hpp b/include/modules/sni/sni.hpp index eb57902c..9b04404b 100644 --- a/include/modules/sni/sni.hpp +++ b/include/modules/sni/sni.hpp @@ -7,8 +7,12 @@ namespace waybar::modules::SNI { class Item { public: - Item(std::string, std::string, Glib::Dispatcher&); - ~Item(); + Item(std::string, std::string, Glib::Dispatcher*); + + std::string bus_name; + std::string object_path; + Gtk::EventBox event_box; + int icon_size; int effective_icon_size; Gtk::Image* image; @@ -31,9 +35,7 @@ class Item { void updateImage(); Glib::RefPtr getIconByName(std::string name, int size); - std::string bus_name_; - std::string object_path_; - Glib::Dispatcher& dp_; + Glib::Dispatcher* dp_; GCancellable* cancellable_ = nullptr; SnOrgKdeStatusNotifierItem* proxy_ = nullptr; }; diff --git a/include/modules/sni/snw.hpp b/include/modules/sni/snw.hpp index f8965e38..807b0f75 100644 --- a/include/modules/sni/snw.hpp +++ b/include/modules/sni/snw.hpp @@ -5,24 +5,25 @@ namespace waybar::modules::SNI { -typedef enum { - GF_WATCH_TYPE_HOST, - GF_WATCH_TYPE_ITEM -} GfWatchType; - -typedef struct { - GfWatchType type; - gchar* service; - gchar* bus_name; - gchar* object_path; - guint watch_id; -} GfWatch; - class Watcher { public: Watcher(); ~Watcher(); private: + typedef enum { + GF_WATCH_TYPE_HOST, + GF_WATCH_TYPE_ITEM + } GfWatchType; + + typedef struct { + GfWatchType type; + Watcher* watcher; + gchar* service; + gchar* bus_name; + gchar* object_path; + guint watch_id; + } GfWatch; + static void busAcquired(GDBusConnection*, const gchar*, gpointer); static gboolean handleRegisterHost(Watcher*, GDBusMethodInvocation*, const gchar*); @@ -30,8 +31,8 @@ class Watcher { GDBusMethodInvocation*, const gchar*); static GfWatch* gfWatchFind(GSList* list, const gchar* bus_name, const gchar* object_path); - static GfWatch* gfWatchNew(GfWatchType type, - const gchar* service, const gchar* bus_name, const gchar* object_path); + static GfWatch* gfWatchNew(GfWatchType, const gchar*, const gchar*, + const gchar*, Watcher*); static void nameVanished(GDBusConnection* connection, const char* name, gpointer data); diff --git a/src/bar.cpp b/src/bar.cpp index 750816ca..7b210186 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -198,14 +198,14 @@ auto waybar::Bar::setupWidgets() -> void getModules(factory, "modules-left"); getModules(factory, "modules-center"); getModules(factory, "modules-right"); - for (auto& module : modules_left_) { + for (auto const& module : modules_left_) { left.pack_start(*module, false, true, 0); } - for (auto& module : modules_center_) { + for (auto const& module : modules_center_) { center.pack_start(*module, true, true, 0); } std::reverse(modules_right_.begin(), modules_right_.end()); - for (auto& module : modules_right_) { + for (auto const& module : modules_right_) { right.pack_end(*module, false, false, 0); } } diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 78470e77..002b8ed5 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -4,7 +4,7 @@ waybar::modules::Battery::Battery(const Json::Value& config) : ALabel(config, "{capacity}%") { try { - for (auto &node : fs::directory_iterator(data_dir_)) { + for (auto const& node : fs::directory_iterator(data_dir_)) { if (fs::is_directory(node) && fs::exists(node / "capacity") && fs::exists(node / "status") && fs::exists(node / "uevent")) { batteries_.push_back(node); @@ -20,7 +20,7 @@ waybar::modules::Battery::Battery(const Json::Value& config) if (fd_ == -1) { throw std::runtime_error("Unable to listen batteries."); } - for (auto &bat : batteries_) { + for (auto const& bat : batteries_) { inotify_add_watch(fd_, (bat / "uevent").c_str(), IN_ACCESS); } label_.set_name("battery"); @@ -51,7 +51,7 @@ auto waybar::modules::Battery::update() -> void try { uint16_t total = 0; std::string status; - for (auto &bat : batteries_) { + for (auto const& bat : batteries_) { uint16_t capacity; std::string _status; std::ifstream(bat / "capacity") >> capacity; diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 4d41fd56..3e59a675 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -118,7 +118,7 @@ const std::string waybar::modules::Pulseaudio::getPortIcon() const "hifi", "phone", }; - for (auto port : ports) { + for (auto const& port : ports) { if (port_name_.find(port) != std::string::npos) { return port; } diff --git a/src/modules/sni/snh.cpp b/src/modules/sni/snh.cpp index 49a6fb89..4c5ae7e3 100644 --- a/src/modules/sni/snh.cpp +++ b/src/modules/sni/snh.cpp @@ -2,7 +2,9 @@ #include -waybar::modules::SNI::Host::Host(Glib::Dispatcher& dp) +using namespace waybar::modules::SNI; + +Host::Host(Glib::Dispatcher* dp) : dp_(dp) { GBusNameOwnerFlags flags = static_cast( @@ -14,11 +16,7 @@ waybar::modules::SNI::Host::Host(Glib::Dispatcher& dp) &Host::busAcquired, nullptr, nullptr, this, nullptr); } -waybar::modules::SNI::Host::~Host() -{ -} - -void waybar::modules::SNI::Host::busAcquired(GDBusConnection* connection, +void Host::busAcquired(GDBusConnection* connection, const gchar* name, gpointer data) { auto host = static_cast(data); @@ -29,7 +27,7 @@ void waybar::modules::SNI::Host::busAcquired(GDBusConnection* connection, &Host::nameAppeared, &Host::nameVanished, data, nullptr); } -void waybar::modules::SNI::Host::nameAppeared(GDBusConnection* connection, +void Host::nameAppeared(GDBusConnection* connection, const gchar* name, const gchar* name_owner, gpointer data) { auto host = static_cast(data); @@ -45,7 +43,7 @@ void waybar::modules::SNI::Host::nameAppeared(GDBusConnection* connection, host->cancellable_, &Host::proxyReady, data); } -void waybar::modules::SNI::Host::nameVanished(GDBusConnection* connection, +void Host::nameVanished(GDBusConnection* connection, const gchar* name, gpointer data) { auto host = static_cast(data); @@ -55,7 +53,7 @@ void waybar::modules::SNI::Host::nameVanished(GDBusConnection* connection, host->items.clear(); } -void waybar::modules::SNI::Host::proxyReady(GObject* src, GAsyncResult* res, +void Host::proxyReady(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; @@ -78,7 +76,7 @@ void waybar::modules::SNI::Host::proxyReady(GObject* src, GAsyncResult* res, &Host::registerHost, data); } -void waybar::modules::SNI::Host::registerHost(GObject* src, GAsyncResult* res, +void Host::registerHost(GObject* src, GAsyncResult* res, gpointer data) { GError* error = nullptr; @@ -109,7 +107,7 @@ void waybar::modules::SNI::Host::registerHost(GObject* src, GAsyncResult* res, g_strfreev(items); } -void waybar::modules::SNI::Host::itemRegistered( +void Host::itemRegistered( SnOrgKdeStatusNotifierWatcher* watcher, const gchar* service, gpointer data) { std::cout << "Item registered" << std::endl; @@ -117,32 +115,41 @@ void waybar::modules::SNI::Host::itemRegistered( host->addRegisteredItem(service); } -void waybar::modules::SNI::Host::itemUnregistered( +void Host::itemUnregistered( SnOrgKdeStatusNotifierWatcher* watcher, const gchar* service, gpointer data) { - std::cout << "Item Unregistered" << std::endl; + 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); + std::cout << "Item Unregistered" << std::endl; + break; + } + } + host->dp_->emit(); } -void waybar::modules::SNI::Host::getBusNameAndObjectPath(const gchar* service, - gchar** bus_name, gchar** object_path) +std::tuple Host::getBusNameAndObjectPath( + const gchar* service) { - gchar* tmp = g_strstr_len (service, -1, "/"); + 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 = g_strdup(str[0]); - *object_path = g_strdup(tmp); + bus_name = str[0]; + object_path = tmp; g_strfreev(str); } else { - *bus_name = g_strdup(service); - *object_path = g_strdup("/StatusNotifierItem"); + bus_name = service; + object_path = "/StatusNotifierItem"; } + return { bus_name, object_path }; } -void waybar::modules::SNI::Host::addRegisteredItem(const gchar* service) +void Host::addRegisteredItem(const gchar* service) { - gchar* bus_name = nullptr; - gchar* object_path = nullptr; - - getBusNameAndObjectPath(service, &bus_name, &object_path); + auto [bus_name, object_path] = getBusNameAndObjectPath(service); 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..ae5ca4ff 100644 --- a/src/modules/sni/sni.cpp +++ b/src/modules/sni/sni.cpp @@ -2,22 +2,19 @@ #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) +waybar::modules::SNI::Item::Item(std::string bn, std::string op, + Glib::Dispatcher* dp) + : bus_name(bn), object_path(op), event_box(), + icon_size(16), effective_icon_size(0), + image(Gtk::manage(new Gtk::Image())), dp_(dp) { + event_box.add(*image); 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(), + G_DBUS_PROXY_FLAGS_NONE, bus_name.c_str(), object_path.c_str(), cancellable_, &Item::proxyReady, this); } -waybar::modules::SNI::Item::~Item() -{ -} - void waybar::modules::SNI::Item::proxyReady(GObject* obj, GAsyncResult* res, gpointer data) { @@ -36,8 +33,8 @@ void waybar::modules::SNI::Item::proxyReady(GObject* obj, GAsyncResult* res, return; } 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_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); @@ -103,8 +100,8 @@ 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()) { @@ -113,7 +110,7 @@ void waybar::modules::SNI::Item::getAll(GObject* obj, GAsyncResult* res, item->icon_theme_path.c_str()); } item->updateImage(); - item->dp_.emit(); + item->dp_->emit(); // TODO: handle change } @@ -144,11 +141,10 @@ 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) { + for (auto const& size : sizes) { // -1 == scalable if (size == request_size || size == -1) { icon_size = request_size; diff --git a/src/modules/sni/snw.cpp b/src/modules/sni/snw.cpp index fc314f54..208cc1e1 100644 --- a/src/modules/sni/snw.cpp +++ b/src/modules/sni/snw.cpp @@ -2,7 +2,9 @@ #include -waybar::modules::SNI::Watcher::Watcher() +using namespace waybar::modules::SNI; + +Watcher::Watcher() { GBusNameOwnerFlags flags = static_cast( G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT @@ -13,12 +15,12 @@ waybar::modules::SNI::Watcher::Watcher() watcher_ = sn_org_kde_status_notifier_watcher_skeleton_new(); } -waybar::modules::SNI::Watcher::~Watcher() +Watcher::~Watcher() { } -void waybar::modules::SNI::Watcher::busAcquired(GDBusConnection* connection, - const gchar* name, gpointer data) +void Watcher::busAcquired(GDBusConnection* connection, const gchar* name, + gpointer data) { GError* error = nullptr; auto host = static_cast(data); @@ -41,9 +43,8 @@ void waybar::modules::SNI::Watcher::busAcquired(GDBusConnection* connection, std::cout << "Bus aquired" << std::endl; } -gboolean waybar::modules::SNI::Watcher::handleRegisterHost( - Watcher* obj, GDBusMethodInvocation* invocation, - const gchar* service) +gboolean Watcher::handleRegisterHost(Watcher* obj, + GDBusMethodInvocation* invocation, const gchar* service) { const gchar* bus_name = service; const gchar* object_path = "/StatusNotifierHost"; @@ -64,7 +65,7 @@ gboolean waybar::modules::SNI::Watcher::handleRegisterHost( bus_name, object_path); return TRUE; } - watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path); + watch = gfWatchNew(GF_WATCH_TYPE_HOST, service, bus_name, object_path, obj); obj->hosts_ = g_slist_prepend(obj->hosts_, watch); sn_org_kde_status_notifier_watcher_set_is_status_notifier_host_registered( obj->watcher_, TRUE); @@ -78,9 +79,8 @@ gboolean waybar::modules::SNI::Watcher::handleRegisterHost( return TRUE; } -gboolean waybar::modules::SNI::Watcher::handleRegisterItem( - Watcher* obj, GDBusMethodInvocation* invocation, - const gchar* service) +gboolean Watcher::handleRegisterItem(Watcher* obj, + GDBusMethodInvocation* invocation, const gchar* service) { const gchar* bus_name = service; const gchar* object_path = "/StatusNotifierItem"; @@ -102,7 +102,7 @@ gboolean waybar::modules::SNI::Watcher::handleRegisterItem( obj->watcher_, invocation); return TRUE; } - watch = gfWatchNew(GF_WATCH_TYPE_ITEM, service, bus_name, object_path); + watch = gfWatchNew(GF_WATCH_TYPE_ITEM, service, bus_name, object_path, obj); obj->items_ = g_slist_prepend(obj->items_, watch); obj->updateRegisteredItems(obj->watcher_); gchar* tmp = g_strdup_printf("%s%s", bus_name, object_path); @@ -114,8 +114,8 @@ gboolean waybar::modules::SNI::Watcher::handleRegisterItem( return TRUE; } -waybar::modules::SNI::GfWatch* waybar::modules::SNI::Watcher::gfWatchFind( - GSList* list, const gchar* bus_name, const gchar* object_path) +Watcher::GfWatch* Watcher::gfWatchFind(GSList* list, const gchar* bus_name, + const gchar* object_path) { for (GSList* l = list; l != nullptr; l = g_slist_next (l)) { GfWatch* watch = static_cast(l->data); @@ -127,12 +127,12 @@ waybar::modules::SNI::GfWatch* waybar::modules::SNI::Watcher::gfWatchFind( return nullptr; } -waybar::modules::SNI::GfWatch* waybar::modules::SNI::Watcher::gfWatchNew( - GfWatchType type, const gchar* service, const gchar* bus_name, - const gchar* object_path) +Watcher::GfWatch* Watcher::gfWatchNew(GfWatchType type, const gchar* service, + const gchar* bus_name, const gchar* object_path, Watcher* watcher) { GfWatch* watch = g_new0(GfWatch, 1); watch->type = type; + watch->watcher = watcher; watch->service = g_strdup(service); watch->bus_name = g_strdup(bus_name); watch->object_path = g_strdup(object_path); @@ -142,26 +142,40 @@ waybar::modules::SNI::GfWatch* waybar::modules::SNI::Watcher::gfWatchNew( return watch; } -void waybar::modules::SNI::Watcher::nameVanished(GDBusConnection* connection, - const char* name, gpointer data) +void Watcher::nameVanished(GDBusConnection* connection, const char* name, + gpointer data) { - //TODO - std::cout << "name vanished" << std::endl; + auto watch = static_cast(data); + if (watch->type == GF_WATCH_TYPE_HOST) { + watch->watcher->hosts_ = g_slist_remove(watch->watcher->hosts_, watch); + if (watch->watcher->hosts_ == nullptr) { + sn_org_kde_status_notifier_watcher_set_is_status_notifier_host_registered( + watch->watcher->watcher_, FALSE); + sn_org_kde_status_notifier_watcher_emit_status_notifier_host_registered( + watch->watcher->watcher_); + } + } else if (watch->type == GF_WATCH_TYPE_ITEM) { + watch->watcher->items_ = g_slist_remove(watch->watcher->items_, watch); + watch->watcher->updateRegisteredItems(watch->watcher->watcher_); + gchar* tmp = g_strdup_printf("%s%s", watch->bus_name, watch->object_path); + sn_org_kde_status_notifier_watcher_emit_status_notifier_item_unregistered( + watch->watcher->watcher_, tmp); + g_free(tmp); + } } -void waybar::modules::SNI::Watcher::updateRegisteredItems( - SnOrgKdeStatusNotifierWatcher* obj) +void Watcher::updateRegisteredItems(SnOrgKdeStatusNotifierWatcher* obj) { GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE("as")); for (GSList* l = items_; l != nullptr; l = g_slist_next(l)) { GfWatch* watch = static_cast(l->data); - gchar* item = g_strdup_printf ("%s%s", watch->bus_name, watch->object_path); - g_variant_builder_add (&builder, "s", item); - g_free (item); + gchar* item = g_strdup_printf("%s%s", watch->bus_name, watch->object_path); + g_variant_builder_add(&builder, "s", item); + g_free(item); } GVariant* variant = g_variant_builder_end(&builder); - const gchar** items = g_variant_get_strv (variant, nullptr); + const gchar** items = g_variant_get_strv(variant, nullptr); sn_org_kde_status_notifier_watcher_set_registered_status_notifier_items( obj, items); g_variant_unref(variant); diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 6ddf7447..4a6b932e 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -3,15 +3,15 @@ #include waybar::modules::SNI::Tray::Tray(const Json::Value& config) - : config_(config), watcher_(), host_(dp) + : config_(config), watcher_(), host_(&dp) { } auto waybar::modules::SNI::Tray::update() -> void { - for (auto item : host_.items) { - item.image->set_tooltip_text(item.title); - box_.pack_start(*item.image); + for (auto& item : host_.items) { + item.event_box.set_tooltip_text(item.title); + box_.pack_start(item.event_box); } if (box_.get_children().size() > 0) { box_.set_name("tray"); diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index a2f4469b..8faf7491 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -44,7 +44,7 @@ auto waybar::modules::sway::Window::update() -> void std::tuple waybar::modules::sway::Window::getFocusedNode( Json::Value nodes) { - for (auto &node : nodes) { + for (auto const& node : nodes) { if (node["focused"].asBool() && node["type"] == "con") { return { node["id"].asInt(), node["name"].asString() }; } diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 3e74801b..878f783c 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -49,7 +49,7 @@ auto waybar::modules::sway::Workspaces::update() -> void ++it; } } - for (auto node : workspaces_) { + for (auto const& node : workspaces_) { if (!config_["all-outputs"].asBool() && bar_.output_name != node["output"].asString()) { continue; From dc799adf458e331ab4fe362a4c4bc1f8234615be Mon Sep 17 00:00:00 2001 From: Alexis Date: Sun, 9 Sep 2018 20:46:26 +0200 Subject: [PATCH 3/5] feat(Tray): icon pixmap --- include/modules/sni/sni.hpp | 2 ++ src/modules/sni/sni.cpp | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/include/modules/sni/sni.hpp b/include/modules/sni/sni.hpp index 9b04404b..2aa0a017 100644 --- a/include/modules/sni/sni.hpp +++ b/include/modules/sni/sni.hpp @@ -23,6 +23,7 @@ class Item { std::string title; int32_t window_id; std::string icon_name; + Glib::RefPtr icon_pixmap; std::string overlay_icon_name; std::string attention_icon_name; std::string attention_movie_name; @@ -34,6 +35,7 @@ class Item { static void getAll(GObject* obj, GAsyncResult* res, gpointer data); void updateImage(); + Glib::RefPtr extractPixBuf(GVariant* variant); Glib::RefPtr getIconByName(std::string name, int size); Glib::Dispatcher* dp_; GCancellable* cancellable_ = nullptr; diff --git a/src/modules/sni/sni.cpp b/src/modules/sni/sni.cpp index ae5ca4ff..d7d5000c 100644 --- a/src/modules/sni/sni.cpp +++ b/src/modules/sni/sni.cpp @@ -74,6 +74,7 @@ void waybar::modules::SNI::Item::getAll(GObject* obj, GAsyncResult* res, } else if (g_strcmp0(key, "IconName") == 0) { item->icon_name = g_variant_dup_string(value, nullptr); } else if (g_strcmp0(key, "IconPixmap") == 0) { + item->icon_pixmap = item->extractPixBuf(value); // TODO: icon pixmap } else if (g_strcmp0(key, "OverlayIconName") == 0) { item->overlay_icon_name = g_variant_dup_string(value, nullptr); @@ -114,6 +115,55 @@ void waybar::modules::SNI::Item::getAll(GObject* obj, GAsyncResult* res, // TODO: handle change } +Glib::RefPtr waybar::modules::SNI::Item::extractPixBuf( + GVariant* variant) +{ + GVariantIter* it; + g_variant_get(variant, "a(iiay)", &it); + if (it == nullptr) { + return Glib::RefPtr{}; + } + GVariant* val; + gint lwidth = 0; + gint lheight = 0; + gint width; + gint height; + guchar* array = nullptr; + while (g_variant_iter_loop(it, "(ii@ay)", &width, &height, &val)) { + if (width > 0 && height > 0 && val != nullptr + && width * height > lwidth * lheight) { + auto size = g_variant_get_size(val); + /* Sanity check */ + if (size == 4U * width * height) { + /* Find the largest image */ + gconstpointer data = g_variant_get_data(val); + if (data != nullptr) { + if (array != nullptr) { + g_free(array); + } + array = static_cast(g_memdup(data, size)); + lwidth = width; + lheight = height; + } + } + } + } + g_variant_iter_free(it); + if (array != nullptr) { + /* argb to rgba */ + for (uint32_t i = 0; i < 4U * lwidth * lheight; i += 4) { + guchar alpha = array[i]; + array[i] = array[i + 1]; + array[i + 1] = array[i + 2]; + array[i + 2] = array[i + 3]; + array[i + 3] = alpha; + } + return Gdk::Pixbuf::create_from_data(array, Gdk::Colorspace::COLORSPACE_RGB, + true, 8, lwidth, lheight, 4 * lwidth); + } + return Glib::RefPtr{}; +} + void waybar::modules::SNI::Item::updateImage() { if (!icon_name.empty()) { @@ -131,6 +181,8 @@ void waybar::modules::SNI::Item::updateImage() pixbuf = getIconByName("image-missing", icon_size); } image->set(pixbuf); + } else if (icon_pixmap) { + image->set(icon_pixmap); } else { image->set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); image->set_pixel_size(icon_size); From 56e55fa4aaa4b4bb36909b02989c9fbde1646f7e Mon Sep 17 00:00:00 2001 From: Alexis Date: Sun, 9 Sep 2018 20:47:28 +0200 Subject: [PATCH 4/5] fix: remove TODO --- src/modules/sni/sni.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/sni/sni.cpp b/src/modules/sni/sni.cpp index d7d5000c..b5c54be9 100644 --- a/src/modules/sni/sni.cpp +++ b/src/modules/sni/sni.cpp @@ -75,7 +75,6 @@ void waybar::modules::SNI::Item::getAll(GObject* obj, GAsyncResult* res, item->icon_name = g_variant_dup_string(value, nullptr); } else if (g_strcmp0(key, "IconPixmap") == 0) { item->icon_pixmap = item->extractPixBuf(value); - // TODO: icon pixmap } else if (g_strcmp0(key, "OverlayIconName") == 0) { item->overlay_icon_name = g_variant_dup_string(value, nullptr); } else if (g_strcmp0(key, "OverlayIconPixmap") == 0) { From 091b460d030d7cf70d62edb9ca71151cdade1473 Mon Sep 17 00:00:00 2001 From: Alexis Date: Mon, 17 Sep 2018 23:32:05 +0200 Subject: [PATCH 5/5] feat(Tray): handle click --- include/modules/sni/sni.hpp | 4 ++++ src/modules/sni/sni.cpp | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/include/modules/sni/sni.hpp b/include/modules/sni/sni.hpp index 2aa0a017..bcca010d 100644 --- a/include/modules/sni/sni.hpp +++ b/include/modules/sni/sni.hpp @@ -33,10 +33,14 @@ class Item { 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(); Glib::RefPtr extractPixBuf(GVariant* variant); Glib::RefPtr getIconByName(std::string name, int size); + bool handleClick(GdkEventButton* const& /*ev*/); + Glib::Dispatcher* dp_; GCancellable* cancellable_ = nullptr; SnOrgKdeStatusNotifierItem* proxy_ = nullptr; diff --git a/src/modules/sni/sni.cpp b/src/modules/sni/sni.cpp index b5c54be9..627f3492 100644 --- a/src/modules/sni/sni.cpp +++ b/src/modules/sni/sni.cpp @@ -9,6 +9,9 @@ waybar::modules::SNI::Item::Item(std::string bn, std::string op, image(Gtk::manage(new Gtk::Image())), dp_(dp) { 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_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(), @@ -70,7 +73,7 @@ void waybar::modules::SNI::Item::getAll(GObject* obj, GAsyncResult* res, } else if (g_strcmp0(key, "Status") == 0) { item->status = 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); } else if (g_strcmp0(key, "IconPixmap") == 0) { @@ -209,4 +212,34 @@ Glib::RefPtr waybar::modules::SNI::Item::getIconByName( } return icon_theme->load_icon(name.c_str(), icon_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_org_kde_status_notifier_item_call_activate_finish(item->proxy_, res, + nullptr); +} + +void waybar::modules::SNI::Item::handleSecondaryActivate(GObject* src, + GAsyncResult* res, gpointer data) +{ + auto item = static_cast(data); + sn_org_kde_status_notifier_item_call_secondary_activate_finish(item->proxy_, + res, nullptr); +} + +bool waybar::modules::SNI::Item::handleClick(GdkEventButton* const& ev) +{ + if (ev->type == GDK_BUTTON_PRESS) { + sn_org_kde_status_notifier_item_call_activate(proxy_, ev->x, ev->y, nullptr, + &Item::handleActivate, this); + } else if (ev->type == GDK_2BUTTON_PRESS) { + sn_org_kde_status_notifier_item_call_secondary_activate(proxy_, ev->x, + ev->y, nullptr, &Item::handleSecondaryActivate, this); + } else { + return false; + } + return true; } \ No newline at end of file