aboutsummaryrefslogtreecommitdiff
path: root/swaybar
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar')
-rw-r--r--swaybar/meson.build1
-rw-r--r--swaybar/tray/host.c9
-rw-r--r--swaybar/tray/item.c236
-rw-r--r--swaybar/tray/tray.c3
4 files changed, 244 insertions, 5 deletions
diff --git a/swaybar/meson.build b/swaybar/meson.build
index 85783a0f..312ca97b 100644
--- a/swaybar/meson.build
+++ b/swaybar/meson.build
@@ -1,6 +1,7 @@
tray_files = get_option('enable-tray') ? [
'tray/host.c',
'tray/icon.c',
+ 'tray/item.c',
'tray/tray.c',
'tray/watcher.c'
] : []
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c
index 3cc90254..8ab896d4 100644
--- a/swaybar/tray/host.c
+++ b/swaybar/tray/host.c
@@ -5,6 +5,7 @@
#include <string.h>
#include <unistd.h>
#include "swaybar/tray/host.h"
+#include "swaybar/tray/item.h"
#include "swaybar/tray/tray.h"
#include "list.h"
#include "log.h"
@@ -12,15 +13,15 @@
static const char *watcher_path = "/StatusNotifierWatcher";
static int cmp_sni_id(const void *item, const void *cmp_to) {
- const char *sni = item;
- return strcmp(sni, cmp_to);
+ const struct swaybar_sni *sni = item;
+ return strcmp(sni->watcher_id, cmp_to);
}
static void add_sni(struct swaybar_tray *tray, char *id) {
int idx = list_seq_find(tray->items, cmp_sni_id, id);
if (idx == -1) {
wlr_log(WLR_DEBUG, "Registering Status Notifier Item '%s'", id);
- char *sni = strdup(id);
+ struct swaybar_sni *sni = create_sni(id, tray);
if (sni) {
list_add(tray->items, sni);
}
@@ -53,7 +54,7 @@ static int handle_sni_unregistered(sd_bus_message *msg, void *data,
int idx = list_seq_find(tray->items, cmp_sni_id, id);
if (idx != -1) {
wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id);
- free(tray->items->items[idx]);
+ destroy_sni(tray->items->items[idx]);
list_del(tray->items, idx);
}
return ret;
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c
new file mode 100644
index 00000000..561a3425
--- /dev/null
+++ b/swaybar/tray/item.c
@@ -0,0 +1,236 @@
+#define _POSIX_C_SOURCE 200809L
+#include <stdlib.h>
+#include <string.h>
+#include "swaybar/tray/host.h"
+#include "swaybar/tray/item.h"
+#include "swaybar/tray/tray.h"
+#include "list.h"
+#include "log.h"
+
+// TODO menu
+
+static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni,
+ const char *prop, list_t **dest) {
+ int ret = sd_bus_message_enter_container(msg, 'a', "(iiay)");
+ if (ret < 0) {
+ wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret));
+ return ret;
+ }
+
+ if (sd_bus_message_at_end(msg, 0)) {
+ return ret;
+ }
+
+ list_t *pixmaps = create_list();
+ if (!pixmaps) {
+ return -12; // -ENOMEM
+ }
+
+ while (!sd_bus_message_at_end(msg, 0)) {
+ ret = sd_bus_message_enter_container(msg, 'r', "iiay");
+ if (ret < 0) {
+ wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret));
+ goto error;
+ }
+
+ int size;
+ ret = sd_bus_message_read(msg, "ii", NULL, &size);
+ if (ret < 0) {
+ wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret));
+ goto error;
+ }
+
+ const void *pixels;
+ size_t npixels;
+ ret = sd_bus_message_read_array(msg, 'y', &pixels, &npixels);
+ if (ret < 0) {
+ wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret));
+ goto error;
+ }
+
+ struct swaybar_pixmap *pixmap =
+ malloc(sizeof(struct swaybar_pixmap) + npixels);
+ pixmap->size = size;
+ memcpy(pixmap->pixels, pixels, npixels);
+ list_add(pixmaps, pixmap);
+
+ sd_bus_message_exit_container(msg);
+ }
+ *dest = pixmaps;
+
+ return ret;
+error:
+ list_free_items_and_destroy(pixmaps);
+ return ret;
+}
+
+struct get_property_data {
+ struct swaybar_sni *sni;
+ const char *prop;
+ const char *type;
+ void *dest;
+};
+
+static int get_property_callback(sd_bus_message *msg, void *data,
+ sd_bus_error *error) {
+ struct get_property_data *d = data;
+ struct swaybar_sni *sni = d->sni;
+ const char *prop = d->prop;
+ const char *type = d->type;
+ void *dest = d->dest;
+
+ int ret;
+ if (sd_bus_message_is_method_error(msg, NULL)) {
+ sd_bus_error err = *sd_bus_message_get_error(msg);
+ wlr_log(WLR_DEBUG, "Failed to get property %s: %s", prop, err.message);
+ ret = -sd_bus_error_get_errno(&err);
+ goto cleanup;
+ }
+
+ ret = sd_bus_message_enter_container(msg, 'v', type);
+ if (ret < 0) {
+ wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret));
+ goto cleanup;
+ }
+
+ if (!type) {
+ ret = read_pixmap(msg, sni, prop, dest);
+ if (ret < 0) {
+ goto cleanup;
+ }
+ } else {
+ ret = sd_bus_message_read(msg, type, dest);
+ if (ret < 0) {
+ wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop,
+ strerror(-ret));
+ goto cleanup;
+ } else if (*type == 's' || *type == 'o') {
+ char **str = dest;
+ *str = strdup(*str);
+ }
+ }
+cleanup:
+ free(data);
+ return ret;
+}
+
+static void sni_get_property_async(struct swaybar_sni *sni, const char *prop,
+ const char *type, void *dest) {
+ struct get_property_data *data = malloc(sizeof(struct get_property_data));
+ data->sni = sni;
+ data->prop = prop;
+ data->type = type;
+ data->dest = dest;
+ int ret = sd_bus_call_method_async(sni->tray->bus, NULL, sni->service,
+ sni->path, "org.freedesktop.DBus.Properties", "Get",
+ get_property_callback, data, "ss", sni->interface, prop);
+ if (ret < 0) {
+ wlr_log(WLR_DEBUG, "Failed to get property %s: %s", prop, strerror(-ret));
+ }
+}
+
+static int handle_new_icon(sd_bus_message *msg, void *data, sd_bus_error *error) {
+ struct swaybar_sni *sni = data;
+ wlr_log(WLR_DEBUG, "%s has new IconName", sni->watcher_id);
+
+ free(sni->icon_name);
+ sni->icon_name = NULL;
+ sni_get_property_async(sni, "IconName", "s", &sni->icon_name);
+
+ list_free_items_and_destroy(sni->icon_pixmap);
+ sni->icon_pixmap = NULL;
+ sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap);
+
+ return 0;
+}
+
+static int handle_new_attention_icon(sd_bus_message *msg, void *data,
+ sd_bus_error *error) {
+ struct swaybar_sni *sni = data;
+ wlr_log(WLR_DEBUG, "%s has new AttentionIconName", sni->watcher_id);
+
+ free(sni->attention_icon_name);
+ sni->attention_icon_name = NULL;
+ sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name);
+
+ list_free_items_and_destroy(sni->attention_icon_pixmap);
+ sni->attention_icon_pixmap = NULL;
+ sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap);
+
+ return 0;
+}
+
+static int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *error) {
+ char *status;
+ int ret = sd_bus_message_read(msg, "s", &status);
+ if (ret < 0) {
+ wlr_log(WLR_DEBUG, "Failed to read new status message: %s", strerror(-ret));
+ } else {
+ struct swaybar_sni *sni = data;
+ free(sni->status);
+ sni->status = strdup(status);
+ wlr_log(WLR_DEBUG, "%s has new Status '%s'", sni->watcher_id, status);
+ }
+ return ret;
+}
+
+static void sni_match_signal(struct swaybar_sni *sni, char *signal,
+ sd_bus_message_handler_t callback) {
+ int ret = sd_bus_match_signal(sni->tray->bus, NULL, sni->service, sni->path,
+ sni->interface, signal, callback, sni);
+ if (ret < 0) {
+ wlr_log(WLR_DEBUG, "Failed to subscribe to signal %s: %s", signal,
+ strerror(-ret));
+ }
+}
+
+struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) {
+ struct swaybar_sni *sni = calloc(1, sizeof(struct swaybar_sni));
+ if (!sni) {
+ return NULL;
+ }
+ sni->tray = tray;
+ sni->watcher_id = strdup(id);
+ char *path_ptr = strchr(id, '/');
+ if (!path_ptr) {
+ sni->service = strdup(id);
+ sni->path = strdup("/StatusNotifierItem");
+ sni->interface = "org.freedesktop.StatusNotifierItem";
+ } else {
+ sni->service = strndup(id, path_ptr - id);
+ sni->path = strdup(path_ptr);
+ sni->interface = "org.kde.StatusNotifierItem";
+ }
+
+ // Ignored: Category, Id, Title, WindowId, OverlayIconName,
+ // OverlayIconPixmap, AttentionMovieName, ToolTip
+ sni_get_property_async(sni, "Status", "s", &sni->status);
+ sni_get_property_async(sni, "IconName", "s", &sni->icon_name);
+ sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap);
+ sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name);
+ sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap);
+ sni_get_property_async(sni, "ItemIsMenu", "b", &sni->item_is_menu);
+ sni_get_property_async(sni, "Menu", "o", &sni->menu);
+
+ sni_match_signal(sni, "NewIcon", handle_new_icon);
+ sni_match_signal(sni, "NewAttentionIcon", handle_new_attention_icon);
+ sni_match_signal(sni, "NewStatus", handle_new_status);
+
+ return sni;
+}
+
+void destroy_sni(struct swaybar_sni *sni) {
+ if (!sni) {
+ return;
+ }
+
+ free(sni->watcher_id);
+ free(sni->service);
+ free(sni->path);
+ free(sni->status);
+ free(sni->icon_name);
+ free(sni->icon_pixmap);
+ free(sni->attention_icon_name);
+ free(sni->menu);
+ free(sni);
+}
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
index e760812c..4ef28a78 100644
--- a/swaybar/tray/tray.c
+++ b/swaybar/tray/tray.c
@@ -5,6 +5,7 @@
#include "swaybar/bar.h"
#include "swaybar/tray/icon.h"
#include "swaybar/tray/host.h"
+#include "swaybar/tray/item.h"
#include "swaybar/tray/tray.h"
#include "swaybar/tray/watcher.h"
#include "list.h"
@@ -48,7 +49,7 @@ void destroy_tray(struct swaybar_tray *tray) {
finish_host(&tray->host_xdg);
finish_host(&tray->host_kde);
for (int i = 0; i < tray->items->length; ++i) {
- free(tray->items->items[0]);
+ destroy_sni(tray->items->items[0]);
}
list_free(tray->items);
destroy_watcher(tray->watcher_xdg);