host.c (5895B) - View raw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "swaybar/bar.h" #include "swaybar/tray/host.h" #include "swaybar/tray/item.h" #include "swaybar/tray/tray.h" #include "list.h" #include "log.h" #include "stringop.h" static const char *watcher_path = "/StatusNotifierWatcher"; static int cmp_sni_id(const void *item, const void *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) { sway_log(SWAY_INFO, "Registering Status Notifier Item '%s'", id); struct swaybar_sni *sni = create_sni(id, tray); 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) { sway_log(SWAY_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) { sway_log(SWAY_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) { sway_log(SWAY_INFO, "Unregistering Status Notifier Item '%s'", id); destroy_sni(tray->items->items[idx]); list_del(tray->items, idx); set_bar_dirty(tray->bar); } 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)) { const sd_bus_error *err = sd_bus_message_get_error(msg); sway_log(SWAY_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) { sway_log(SWAY_ERROR, "Failed to read registered SNIs: %s", strerror(-ret)); return ret; } char **ids; ret = sd_bus_message_read_strv(msg, &ids); if (ret < 0) { sway_log(SWAY_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); free(*id); } } free(ids); 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) { sway_log(SWAY_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) { sway_log(SWAY_ERROR, "Failed to get registered SNIs: %s", strerror(-ret)); } 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) { sway_log(SWAY_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) { host->watcher_interface = format_str("org.%s.StatusNotifierWatcher", protocol); if (!host->watcher_interface) { return false; } 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); if (ret < 0) { sway_log(SWAY_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) { sway_log(SWAY_ERROR, "Failed to subscribe to unregistering events: %s", strerror(-ret)); 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) { sway_log(SWAY_ERROR, "Failed to subscribe to unregistering events: %s", strerror(-ret)); goto error; } pid_t pid = getpid(); host->service = format_str("org.%s.StatusNotifierHost-%d", protocol, pid); if (!host->service) { goto error; } ret = sd_bus_request_name(tray->bus, host->service, 0); if (ret < 0) { sway_log(SWAY_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, 0); sd_bus_slot_set_floating(unreg_slot, 0); sd_bus_slot_set_floating(watcher_slot, 0); sway_log(SWAY_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; } void finish_host(struct swaybar_host *host) { sd_bus_release_name(host->tray->bus, host->service); free(host->service); free(host->watcher_interface); }