From e6cb55e2f8176b0ea8a6dbf15c728c56d8b74056 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 7 Dec 2018 12:33:45 +0000 Subject: swaybar: add StatusNotifierHost to tray --- swaybar/tray/host.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 swaybar/tray/host.c (limited to 'swaybar/tray/host.c') diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c new file mode 100644 index 00000000..3cc90254 --- /dev/null +++ b/swaybar/tray/host.c @@ -0,0 +1,177 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include "swaybar/tray/host.h" +#include "swaybar/tray/tray.h" +#include "list.h" +#include "log.h" + +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); +} + +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); + if (sni) { + list_add(tray->items, sni); + } + } +} + +static int handle_sni_registered(sd_bus_message *msg, void *data, + sd_bus_error *error) { + char *id; + int ret = sd_bus_message_read(msg, "s", &id); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to parse register SNI message: %s", strerror(-ret)); + } + + struct swaybar_tray *tray = data; + add_sni(tray, id); + + return ret; +} + +static int handle_sni_unregistered(sd_bus_message *msg, void *data, + sd_bus_error *error) { + char *id; + int ret = sd_bus_message_read(msg, "s", &id); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to parse unregister SNI message: %s", strerror(-ret)); + } + + struct swaybar_tray *tray = 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]); + list_del(tray->items, idx); + } + return ret; +} + +static int get_registered_snis_callback(sd_bus_message *msg, void *data, + sd_bus_error *error) { + if (sd_bus_message_is_method_error(msg, NULL)) { + sd_bus_error err = *sd_bus_message_get_error(msg); + wlr_log(WLR_ERROR, "Failed to get registered SNIs: %s", err.message); + return -sd_bus_error_get_errno(&err); + } + + int ret = sd_bus_message_enter_container(msg, 'v', NULL); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to read registered SNIs: %s", strerror(-ret)); + return ret; + } + + char **ids; + ret = sd_bus_message_read_strv(msg, &ids); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to read registered SNIs: %s", strerror(-ret)); + return ret; + } + + if (ids) { + struct swaybar_tray *tray = data; + for (char **id = ids; *id; ++id) { + add_sni(tray, *id); + } + } + + return ret; +} + +static bool register_to_watcher(struct swaybar_host *host) { + // this is called asynchronously in case the watcher is owned by this process + int ret = sd_bus_call_method_async(host->tray->bus, NULL, + host->watcher_interface, watcher_path, host->watcher_interface, + "RegisterStatusNotifierHost", NULL, NULL, "s", host->service); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to send register call: %s", strerror(-ret)); + return false; + } + + ret = sd_bus_call_method_async(host->tray->bus, NULL, + host->watcher_interface, watcher_path, + "org.freedesktop.DBus.Properties", "Get", + get_registered_snis_callback, host->tray, "ss", + host->watcher_interface, "RegisteredStatusNotifierItems"); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to get registered SNIs: %s", strerror(-ret)); + } + + return ret >= 0; +} + +bool init_host(struct swaybar_host *host, char *protocol, + struct swaybar_tray *tray) { + size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; + host->watcher_interface = malloc(len); + if (!host->watcher_interface) { + return false; + } + snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol); + + sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL; + int ret = sd_bus_match_signal(tray->bus, ®_slot, host->watcher_interface, + watcher_path, host->watcher_interface, + "StatusNotifierItemRegistered", handle_sni_registered, tray); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to subscribe to registering events: %s", + strerror(-ret)); + goto error; + } + ret = sd_bus_match_signal(tray->bus, &unreg_slot, host->watcher_interface, + watcher_path, host->watcher_interface, + "StatusNotifierItemUnregistered", handle_sni_unregistered, tray); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s", + strerror(-ret)); + goto error; + } + + pid_t pid = getpid(); + size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d", + protocol, pid) + 1; + host->service = malloc(service_len); + if (!host->service) { + goto error; + } + snprintf(host->service, service_len, "org.%s.StatusNotifierHost-%d", protocol, pid); + ret = sd_bus_request_name(tray->bus, host->service, 0); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); + goto error; + } + + host->tray = tray; + if (!register_to_watcher(host)) { + goto error; + } + + sd_bus_slot_set_floating(reg_slot, 1); + sd_bus_slot_set_floating(unreg_slot, 1); + + wlr_log(WLR_DEBUG, "Registered %s", host->service); + return true; +error: + sd_bus_slot_unref(reg_slot); + sd_bus_slot_unref(unreg_slot); + finish_host(host); + return false; +} + +void finish_host(struct swaybar_host *host) { + sd_bus_release_name(host->tray->bus, host->service); + free(host->service); + free(host->watcher_interface); +} -- cgit v1.2.3 From 74655f835aa9fe0e976473d443f62d253602696c Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 7 Dec 2018 12:33:45 +0000 Subject: swaybar: add StatusNotifierItem to tray --- include/swaybar/tray/item.h | 38 +++++++ include/swaybar/tray/tray.h | 2 +- swaybar/meson.build | 1 + swaybar/tray/host.c | 9 +- swaybar/tray/item.c | 236 ++++++++++++++++++++++++++++++++++++++++++++ swaybar/tray/tray.c | 3 +- 6 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 include/swaybar/tray/item.h create mode 100644 swaybar/tray/item.c (limited to 'swaybar/tray/host.c') diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h new file mode 100644 index 00000000..57affb78 --- /dev/null +++ b/include/swaybar/tray/item.h @@ -0,0 +1,38 @@ +#ifndef _SWAYBAR_TRAY_ITEM_H +#define _SWAYBAR_TRAY_ITEM_H + +#include +#include "swaybar/tray/tray.h" +#include "list.h" + +struct swaybar_pixmap { + int size; + unsigned char pixels[]; +}; + +struct swaybar_sni { + // icon properties + struct swaybar_tray *tray; + cairo_surface_t *icon; + int min_size; + int max_size; + + // dbus properties + char *watcher_id; + char *service; + char *path; + char *interface; + + char *status; + char *icon_name; + list_t *icon_pixmap; // struct swaybar_pixmap * + char *attention_icon_name; + list_t *attention_icon_pixmap; // struct swaybar_pixmap * + bool item_is_menu; + char *menu; +}; + +struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray); +void destroy_sni(struct swaybar_sni *sni); + +#endif diff --git a/include/swaybar/tray/tray.h b/include/swaybar/tray/tray.h index 1d976b4a..8958b69a 100644 --- a/include/swaybar/tray/tray.h +++ b/include/swaybar/tray/tray.h @@ -24,7 +24,7 @@ struct swaybar_tray { struct swaybar_host host_xdg; struct swaybar_host host_kde; - list_t *items; // char * + list_t *items; // struct swaybar_sni * struct swaybar_watcher *watcher_xdg; struct swaybar_watcher *watcher_kde; 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 #include #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 +#include +#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); -- cgit v1.2.3 From 2fd41fe9c85a866173fc2770ed7669871258bced Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 7 Dec 2018 12:40:45 +0000 Subject: swaybar: set bar dirty on SNI event --- include/swaybar/bar.h | 2 ++ swaybar/bar.c | 2 +- swaybar/tray/host.c | 2 ++ swaybar/tray/item.c | 13 +++++++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'swaybar/tray/host.c') diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index ad942242..e377b8de 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -88,6 +88,8 @@ bool bar_setup(struct swaybar *bar, const char *socket_path); void bar_run(struct swaybar *bar); void bar_teardown(struct swaybar *bar); +void set_bar_dirty(struct swaybar *bar); + /* * Determines whether the bar should be visible and changes it to be so. * If the current visibility of the bar is the different to what it should be, diff --git a/swaybar/bar.c b/swaybar/bar.c index ebb9bc12..4fd9c488 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -124,7 +124,7 @@ static void destroy_layer_surface(struct swaybar_output *output) { output->frame_scheduled = false; } -static void set_bar_dirty(struct swaybar *bar) { +void set_bar_dirty(struct swaybar *bar) { struct swaybar_output *output; wl_list_for_each(output, &bar->outputs, link) { set_output_dirty(output); diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index 8ab896d4..c5756f17 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c @@ -4,6 +4,7 @@ #include #include #include +#include "swaybar/bar.h" #include "swaybar/tray/host.h" #include "swaybar/tray/item.h" #include "swaybar/tray/tray.h" @@ -56,6 +57,7 @@ static int handle_sni_unregistered(sd_bus_message *msg, void *data, wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id); destroy_sni(tray->items->items[idx]); list_del(tray->items, idx); + set_bar_dirty(tray->bar); } return ret; } diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 450b6882..98d3dc1f 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -24,6 +24,13 @@ static bool sni_ready(struct swaybar_sni *sni) { sni->icon_name || sni->icon_pixmap); } +static void set_sni_dirty(struct swaybar_sni *sni) { + if (sni_ready(sni)) { + sni->min_size = sni->max_size = 0; // invalidate previous icon + set_bar_dirty(sni->tray->bar); + } +} + 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)"); @@ -124,6 +131,11 @@ static int get_property_callback(sd_bus_message *msg, void *data, *str = strdup(*str); } } + + if (strcmp(prop, "Status") == 0 || (sni->status && (sni->status[0] == 'N' ? + prop[0] == 'A' : strncmp(prop, "Icon", 4) == 0))) { + set_sni_dirty(sni); + } cleanup: free(data); return ret; @@ -185,6 +197,7 @@ static int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *erro free(sni->status); sni->status = strdup(status); wlr_log(WLR_DEBUG, "%s has new Status '%s'", sni->watcher_id, status); + set_sni_dirty(sni); } return ret; } -- cgit v1.2.3 From 371089f62c2894ff55478c2a4298505e3ed12f3f Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 17 Dec 2018 14:08:16 +0000 Subject: swaybar: handle new and lost StatusNotifierWatcher --- swaybar/tray/host.c | 32 +++++++++++++++++++++++++++++++- swaybar/tray/tray.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) (limited to 'swaybar/tray/host.c') diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index c5756f17..30339fec 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c @@ -115,6 +115,25 @@ static bool register_to_watcher(struct swaybar_host *host) { return ret >= 0; } +static int handle_new_watcher(sd_bus_message *msg, + void *data, sd_bus_error *error) { + char *service, *old_owner, *new_owner; + int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret)); + return ret; + } + + if (!*old_owner) { + struct swaybar_host *host = data; + if (strcmp(service, host->watcher_interface) == 0) { + register_to_watcher(host); + } + } + + return 0; +} + bool init_host(struct swaybar_host *host, char *protocol, struct swaybar_tray *tray) { size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; @@ -124,7 +143,7 @@ bool init_host(struct swaybar_host *host, char *protocol, } snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol); - sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL; + sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL; int ret = sd_bus_match_signal(tray->bus, ®_slot, host->watcher_interface, watcher_path, host->watcher_interface, "StatusNotifierItemRegistered", handle_sni_registered, tray); @@ -142,6 +161,15 @@ bool init_host(struct swaybar_host *host, char *protocol, goto error; } + ret = sd_bus_match_signal(tray->bus, &watcher_slot, "org.freedesktop.DBus", + "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", + handle_new_watcher, host); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s", + strerror(-ret)); + goto error; + } + pid_t pid = getpid(); size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d", protocol, pid) + 1; @@ -163,12 +191,14 @@ bool init_host(struct swaybar_host *host, char *protocol, sd_bus_slot_set_floating(reg_slot, 1); sd_bus_slot_set_floating(unreg_slot, 1); + sd_bus_slot_set_floating(watcher_slot, 1); wlr_log(WLR_DEBUG, "Registered %s", host->service); return true; error: sd_bus_slot_unref(reg_slot); sd_bus_slot_unref(unreg_slot); + sd_bus_slot_unref(watcher_slot); finish_host(host); return false; } diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index f186ed86..acc300af 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -12,6 +12,27 @@ #include "list.h" #include "log.h" +static int handle_lost_watcher(sd_bus_message *msg, + void *data, sd_bus_error *error) { + char *service, *old_owner, *new_owner; + int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret)); + return ret; + } + + if (!*new_owner) { + struct swaybar_tray *tray = data; + if (strcmp(service, "org.freedesktop.StatusNotifierWatcher") == 0) { + tray->watcher_xdg = create_watcher("freedesktop", tray->bus); + } else if (strcmp(service, "org.kde.StatusNotifierWatcher") == 0) { + tray->watcher_kde = create_watcher("kde", tray->bus); + } + } + + return 0; +} + struct swaybar_tray *create_tray(struct swaybar *bar) { wlr_log(WLR_DEBUG, "Initializing tray"); @@ -33,6 +54,14 @@ struct swaybar_tray *create_tray(struct swaybar *bar) { tray->watcher_xdg = create_watcher("freedesktop", tray->bus); tray->watcher_kde = create_watcher("kde", tray->bus); + ret = sd_bus_match_signal(bus, NULL, "org.freedesktop.DBus", + "/org/freedesktop/DBus", "org.freedesktop.DBus", + "NameOwnerChanged", handle_lost_watcher, tray); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s", + strerror(-ret)); + } + tray->items = create_list(); init_host(&tray->host_xdg, "freedesktop", tray); -- cgit v1.2.3