|
| 1 | +#define _POSIX_C_SOURCE 200809L |
| 2 | +#include <stdio.h> |
| 3 | +#include <stdlib.h> |
| 4 | +#include <string.h> |
| 5 | +#include <unistd.h> |
| 6 | +#include "swaybar/bar.h" |
| 7 | +#include "swaybar/tray/host.h" |
| 8 | +#include "swaybar/tray/item.h" |
| 9 | +#include "swaybar/tray/tray.h" |
| 10 | +#include "log.h" |
| 11 | + |
| 12 | +static void add_sni(struct swaybar_tray *tray, char *id) { |
| 13 | + wlr_log(WLR_DEBUG, "Registering Status Notifier Item '%s'", id); |
| 14 | + list_add(tray->items, create_sni(id, tray)); |
| 15 | + set_bar_dirty(tray->bar); |
| 16 | +} |
| 17 | + |
| 18 | +static int handle_sni_registered(sd_bus_message *msg, void *data, |
| 19 | + sd_bus_error *error) { |
| 20 | + char *id; |
| 21 | + int ret = sd_bus_message_read(msg, "s", &id); |
| 22 | + if (ret < 0) { |
| 23 | + wlr_log(WLR_ERROR, "Failed to parse register SNI message: %s", strerror(-ret)); |
| 24 | + } |
| 25 | + |
| 26 | + struct swaybar_tray *tray = data; |
| 27 | + add_sni(tray, id); |
| 28 | + |
| 29 | + return ret; |
| 30 | +} |
| 31 | + |
| 32 | +static int cmp_sni_id(const void *item, const void *cmp_to) { |
| 33 | + const struct swaybar_sni *sni = item; |
| 34 | + return strcmp(sni->watcher_id, cmp_to); |
| 35 | +} |
| 36 | + |
| 37 | +static int handle_sni_unregistered(sd_bus_message *msg, void *data, |
| 38 | + sd_bus_error *error) { |
| 39 | + char *id; |
| 40 | + int ret = sd_bus_message_read(msg, "s", &id); |
| 41 | + if (ret < 0) { |
| 42 | + wlr_log(WLR_ERROR, "Failed to parse unregister SNI message: %s", strerror(-ret)); |
| 43 | + } |
| 44 | + |
| 45 | + struct swaybar_tray *tray = data; |
| 46 | + int idx = list_seq_find(tray->items, cmp_sni_id, id); |
| 47 | + if (idx != -1) { |
| 48 | + wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id); |
| 49 | + list_del(tray->items, idx); |
| 50 | + set_bar_dirty(tray->bar); |
| 51 | + } |
| 52 | + return ret; |
| 53 | +} |
| 54 | + |
| 55 | +char *start_host(char *protocol, struct swaybar_tray *tray) { |
| 56 | + size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; |
| 57 | + char *watcher_interface = malloc(len); |
| 58 | + snprintf(watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol); |
| 59 | + static const char *watcher_path = "/StatusNotifierWatcher"; |
| 60 | + |
| 61 | + sd_bus_slot *register_slot = NULL; |
| 62 | + int ret = sd_bus_match_signal(tray->bus, ®ister_slot, watcher_interface, |
| 63 | + watcher_path, watcher_interface, "StatusNotifierItemRegistered", |
| 64 | + handle_sni_registered, tray); |
| 65 | + if (ret < 0) { |
| 66 | + wlr_log(WLR_ERROR, "Failed to subscribe to registering events: %s", |
| 67 | + strerror(-ret)); |
| 68 | + goto error; |
| 69 | + } |
| 70 | + ret = sd_bus_match_signal(tray->bus, NULL, watcher_interface, |
| 71 | + watcher_path, watcher_interface, "StatusNotifierItemUnregistered", |
| 72 | + handle_sni_unregistered, tray); |
| 73 | + if (ret < 0) { |
| 74 | + wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s", |
| 75 | + strerror(-ret)); |
| 76 | + goto error; |
| 77 | + } |
| 78 | + sd_bus_slot_set_floating(register_slot, 1); |
| 79 | + |
| 80 | + pid_t pid = getpid(); |
| 81 | + size_t name_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d", |
| 82 | + protocol, pid) + 1; |
| 83 | + char *host = malloc(name_len); |
| 84 | + snprintf(host, name_len, "org.%s.StatusNotifierHost-%d", protocol, pid); |
| 85 | + ret = sd_bus_request_name(tray->bus, host, 0); |
| 86 | + if (ret < 0) { |
| 87 | + wlr_log(WLR_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); |
| 88 | + goto error; |
| 89 | + } |
| 90 | + |
| 91 | + // this is called asynchronously in case the watcher is owned by this process |
| 92 | + ret = sd_bus_call_method_async(tray->bus, NULL, watcher_interface, |
| 93 | + watcher_path, watcher_interface, "RegisterStatusNotifierHost", |
| 94 | + NULL, NULL, "s", host); |
| 95 | + if (ret < 0) { |
| 96 | + wlr_log(WLR_ERROR, "Failed to send register call: %s", strerror(-ret)); |
| 97 | + goto error; |
| 98 | + } |
| 99 | + |
| 100 | + struct swaybar_watcher *watcher = protocol[0] == 'f' ? |
| 101 | + tray->watcher_xdg : tray->watcher_kde; |
| 102 | + if (!watcher) { |
| 103 | + // this tray did not initialize watcher, so need to get existing items |
| 104 | + sd_bus_error error = SD_BUS_ERROR_NULL; |
| 105 | + char **ids; |
| 106 | + ret = sd_bus_get_property_strv(tray->bus, watcher_interface, watcher_path, |
| 107 | + watcher_interface, "RegisteredStatusNotifierItems", &error, &ids); |
| 108 | + if (ret < 0) { |
| 109 | + wlr_log(WLR_ERROR, "Failed to get registered items from watcher: %s", |
| 110 | + error.message); |
| 111 | + sd_bus_error_free(&error); |
| 112 | + } else if (ids) { |
| 113 | + for (char **id = ids; *id; ++id) { |
| 114 | + add_sni(tray, *id); |
| 115 | + } |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + wlr_log(WLR_DEBUG, "Registered %s", host); |
| 120 | + return host; |
| 121 | +error: |
| 122 | + sd_bus_slot_unref(register_slot); |
| 123 | + free(host); |
| 124 | + return NULL; |
| 125 | +} |
| 126 | + |
| 127 | +void stop_host(char *name, struct swaybar_tray *tray) { |
| 128 | + sd_bus_release_name(tray->bus, name); |
| 129 | + free(name); |
| 130 | +} |
0 commit comments