aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/tray/host.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/tray/host.c')
-rw-r--r--swaybar/tray/host.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c
new file mode 100644
index 00000000..30339fec
--- /dev/null
+++ b/swaybar/tray/host.c
@@ -0,0 +1,210 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <unistd.h>
7#include "swaybar/bar.h"
8#include "swaybar/tray/host.h"
9#include "swaybar/tray/item.h"
10#include "swaybar/tray/tray.h"
11#include "list.h"
12#include "log.h"
13
14static const char *watcher_path = "/StatusNotifierWatcher";
15
16static int cmp_sni_id(const void *item, const void *cmp_to) {
17 const struct swaybar_sni *sni = item;
18 return strcmp(sni->watcher_id, cmp_to);
19}
20
21static void add_sni(struct swaybar_tray *tray, char *id) {
22 int idx = list_seq_find(tray->items, cmp_sni_id, id);
23 if (idx == -1) {
24 wlr_log(WLR_DEBUG, "Registering Status Notifier Item '%s'", id);
25 struct swaybar_sni *sni = create_sni(id, tray);
26 if (sni) {
27 list_add(tray->items, sni);
28 }
29 }
30}
31
32static int handle_sni_registered(sd_bus_message *msg, void *data,
33 sd_bus_error *error) {
34 char *id;
35 int ret = sd_bus_message_read(msg, "s", &id);
36 if (ret < 0) {
37 wlr_log(WLR_ERROR, "Failed to parse register SNI message: %s", strerror(-ret));
38 }
39
40 struct swaybar_tray *tray = data;
41 add_sni(tray, id);
42
43 return ret;
44}
45
46static int handle_sni_unregistered(sd_bus_message *msg, void *data,
47 sd_bus_error *error) {
48 char *id;
49 int ret = sd_bus_message_read(msg, "s", &id);
50 if (ret < 0) {
51 wlr_log(WLR_ERROR, "Failed to parse unregister SNI message: %s", strerror(-ret));
52 }
53
54 struct swaybar_tray *tray = data;
55 int idx = list_seq_find(tray->items, cmp_sni_id, id);
56 if (idx != -1) {
57 wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id);
58 destroy_sni(tray->items->items[idx]);
59 list_del(tray->items, idx);
60 set_bar_dirty(tray->bar);
61 }
62 return ret;
63}
64
65static int get_registered_snis_callback(sd_bus_message *msg, void *data,
66 sd_bus_error *error) {
67 if (sd_bus_message_is_method_error(msg, NULL)) {
68 sd_bus_error err = *sd_bus_message_get_error(msg);
69 wlr_log(WLR_ERROR, "Failed to get registered SNIs: %s", err.message);
70 return -sd_bus_error_get_errno(&err);
71 }
72
73 int ret = sd_bus_message_enter_container(msg, 'v', NULL);
74 if (ret < 0) {
75 wlr_log(WLR_ERROR, "Failed to read registered SNIs: %s", strerror(-ret));
76 return ret;
77 }
78
79 char **ids;
80 ret = sd_bus_message_read_strv(msg, &ids);
81 if (ret < 0) {
82 wlr_log(WLR_ERROR, "Failed to read registered SNIs: %s", strerror(-ret));
83 return ret;
84 }
85
86 if (ids) {
87 struct swaybar_tray *tray = data;
88 for (char **id = ids; *id; ++id) {
89 add_sni(tray, *id);
90 }
91 }
92
93 return ret;
94}
95
96static bool register_to_watcher(struct swaybar_host *host) {
97 // this is called asynchronously in case the watcher is owned by this process
98 int ret = sd_bus_call_method_async(host->tray->bus, NULL,
99 host->watcher_interface, watcher_path, host->watcher_interface,
100 "RegisterStatusNotifierHost", NULL, NULL, "s", host->service);
101 if (ret < 0) {
102 wlr_log(WLR_ERROR, "Failed to send register call: %s", strerror(-ret));
103 return false;
104 }
105
106 ret = sd_bus_call_method_async(host->tray->bus, NULL,
107 host->watcher_interface, watcher_path,
108 "org.freedesktop.DBus.Properties", "Get",
109 get_registered_snis_callback, host->tray, "ss",
110 host->watcher_interface, "RegisteredStatusNotifierItems");
111 if (ret < 0) {
112 wlr_log(WLR_ERROR, "Failed to get registered SNIs: %s", strerror(-ret));
113 }
114
115 return ret >= 0;
116}
117
118static int handle_new_watcher(sd_bus_message *msg,
119 void *data, sd_bus_error *error) {
120 char *service, *old_owner, *new_owner;
121 int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner);
122 if (ret < 0) {
123 wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret));
124 return ret;
125 }
126
127 if (!*old_owner) {
128 struct swaybar_host *host = data;
129 if (strcmp(service, host->watcher_interface) == 0) {
130 register_to_watcher(host);
131 }
132 }
133
134 return 0;
135}
136
137bool init_host(struct swaybar_host *host, char *protocol,
138 struct swaybar_tray *tray) {
139 size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1;
140 host->watcher_interface = malloc(len);
141 if (!host->watcher_interface) {
142 return false;
143 }
144 snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol);
145
146 sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL;
147 int ret = sd_bus_match_signal(tray->bus, &reg_slot, host->watcher_interface,
148 watcher_path, host->watcher_interface,
149 "StatusNotifierItemRegistered", handle_sni_registered, tray);
150 if (ret < 0) {
151 wlr_log(WLR_ERROR, "Failed to subscribe to registering events: %s",
152 strerror(-ret));
153 goto error;
154 }
155 ret = sd_bus_match_signal(tray->bus, &unreg_slot, host->watcher_interface,
156 watcher_path, host->watcher_interface,
157 "StatusNotifierItemUnregistered", handle_sni_unregistered, tray);
158 if (ret < 0) {
159 wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s",
160 strerror(-ret));
161 goto error;
162 }
163
164 ret = sd_bus_match_signal(tray->bus, &watcher_slot, "org.freedesktop.DBus",
165 "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged",
166 handle_new_watcher, host);
167 if (ret < 0) {
168 wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s",
169 strerror(-ret));
170 goto error;
171 }
172
173 pid_t pid = getpid();
174 size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d",
175 protocol, pid) + 1;
176 host->service = malloc(service_len);
177 if (!host->service) {
178 goto error;
179 }
180 snprintf(host->service, service_len, "org.%s.StatusNotifierHost-%d", protocol, pid);
181 ret = sd_bus_request_name(tray->bus, host->service, 0);
182 if (ret < 0) {
183 wlr_log(WLR_DEBUG, "Failed to acquire service name: %s", strerror(-ret));
184 goto error;
185 }
186
187 host->tray = tray;
188 if (!register_to_watcher(host)) {
189 goto error;
190 }
191
192 sd_bus_slot_set_floating(reg_slot, 1);
193 sd_bus_slot_set_floating(unreg_slot, 1);
194 sd_bus_slot_set_floating(watcher_slot, 1);
195
196 wlr_log(WLR_DEBUG, "Registered %s", host->service);
197 return true;
198error:
199 sd_bus_slot_unref(reg_slot);
200 sd_bus_slot_unref(unreg_slot);
201 sd_bus_slot_unref(watcher_slot);
202 finish_host(host);
203 return false;
204}
205
206void finish_host(struct swaybar_host *host) {
207 sd_bus_release_name(host->tray->bus, host->service);
208 free(host->service);
209 free(host->watcher_interface);
210}