feat(WIP): tray

feat(wip): tray

feat(wip): tray

feat(WIP): gdbus

feat(WIP): tray
This commit is contained in:
Alexis
2018-08-29 20:36:39 +02:00
parent 55e1905284
commit fc6e42d748
18 changed files with 888 additions and 15 deletions

View File

@ -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]);

148
src/modules/sni/snh.cpp Normal file
View File

@ -0,0 +1,148 @@
#include "modules/sni/snh.hpp"
#include <iostream>
waybar::modules::SNI::Host::Host(Glib::Dispatcher& dp)
: dp_(dp)
{
GBusNameOwnerFlags flags = static_cast<GBusNameOwnerFlags>(
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<SNI::Host *>(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<SNI::Host *>(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<SNI::Host *>(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<SNI::Host *>(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<SNI::Host *>(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<SNI::Host *>(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_);
}

165
src/modules/sni/sni.cpp Normal file
View File

@ -0,0 +1,165 @@
#include "modules/sni/sni.hpp"
#include <iostream>
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<SNI::Item *>(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<SNI::Item *>(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<Gdk::Pixbuf> waybar::modules::SNI::Item::getIconByName(
std::string name, int request_size)
{
int icon_size = 0;
Glib::RefPtr<Gtk::IconTheme> 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);
}

169
src/modules/sni/snw.cpp Normal file
View File

@ -0,0 +1,169 @@
#include "modules/sni/snw.hpp"
#include <iostream>
waybar::modules::SNI::Watcher::Watcher()
{
GBusNameOwnerFlags flags = static_cast<GBusNameOwnerFlags>(
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<SNI::Watcher*>(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<GfWatch*>(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<GfWatch*>(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);
}

26
src/modules/sni/tray.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "modules/sni/tray.hpp"
#include <iostream>
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_;
}