aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/tray/tray.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/tray/tray.c')
-rw-r--r--swaybar/tray/tray.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
new file mode 100644
index 00000000..9a709fe4
--- /dev/null
+++ b/swaybar/tray/tray.c
@@ -0,0 +1,279 @@
1#define _XOPEN_SOURCE 500
2#include <unistd.h>
3#include <stdlib.h>
4#include <string.h>
5#include <dbus/dbus.h>
6#include "swaybar/bar.h"
7#include "swaybar/tray/tray.h"
8#include "swaybar/tray/dbus.h"
9#include "swaybar/tray/sni.h"
10#include "swaybar/bar.h"
11#include "list.h"
12#include "log.h"
13
14struct tray *tray;
15
16static void register_host(char *name) {
17 DBusMessage *message;
18
19 message = dbus_message_new_method_call(
20 "org.freedesktop.StatusNotifierWatcher",
21 "/StatusNotifierWatcher",
22 "org.freedesktop.StatusNotifierWatcher",
23 "RegisterStatusNotifierHost");
24 if (!message) {
25 sway_log(L_ERROR, "Cannot allocate dbus method call");
26 return;
27 }
28
29 dbus_message_append_args(message,
30 DBUS_TYPE_STRING, &name,
31 DBUS_TYPE_INVALID);
32
33 dbus_connection_send(conn, message, NULL);
34
35 dbus_message_unref(message);
36}
37
38static void get_items_reply(DBusPendingCall *pending, void *_data) {
39 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
40
41 if (!reply) {
42 sway_log(L_ERROR, "Got no items reply from sni watcher");
43 goto bail;
44 }
45
46 int message_type = dbus_message_get_type(reply);
47
48 if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
49 char *msg;
50
51 dbus_message_get_args(reply, NULL,
52 DBUS_TYPE_STRING, &msg,
53 DBUS_TYPE_INVALID);
54
55 sway_log(L_ERROR, "Message is error: %s", msg);
56 goto bail;
57 }
58
59 DBusMessageIter iter;
60 DBusMessageIter variant;
61 DBusMessageIter array;
62
63 dbus_message_iter_init(reply, &iter);
64 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
65 sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
66 goto bail;
67 }
68 dbus_message_iter_recurse(&iter, &variant);
69 if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY ||
70 dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) {
71 sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
72 goto bail;
73 }
74
75 // Clear list
76 list_foreach(tray->items, (void (*)(void *))sni_free);
77 list_free(tray->items);
78 tray->items = create_list();
79
80 // O(n) function, could be faster dynamically reading values
81 int len = dbus_message_iter_get_element_count(&variant);
82
83 dbus_message_iter_recurse(&variant, &array);
84 for (int i = 0; i < len; i++) {
85 const char *name;
86 dbus_message_iter_get_basic(&array, &name);
87
88 struct StatusNotifierItem *item = sni_create(name);
89
90 sway_log(L_DEBUG, "Item registered with host: %s", name);
91 list_add(tray->items, item);
92 dirty = true;
93 }
94
95bail:
96 dbus_message_unref(reply);
97 return;
98}
99static void get_items() {
100 DBusPendingCall *pending;
101 DBusMessage *message = dbus_message_new_method_call(
102 "org.freedesktop.StatusNotifierWatcher",
103 "/StatusNotifierWatcher",
104 "org.freedesktop.DBus.Properties",
105 "Get");
106
107 const char *iface = "org.freedesktop.StatusNotifierWatcher";
108 const char *prop = "RegisteredStatusNotifierItems";
109 dbus_message_append_args(message,
110 DBUS_TYPE_STRING, &iface,
111 DBUS_TYPE_STRING, &prop,
112 DBUS_TYPE_INVALID);
113
114 bool status =
115 dbus_connection_send_with_reply(conn, message, &pending, -1);
116 dbus_message_unref(message);
117
118 if (!(pending || status)) {
119 sway_log(L_ERROR, "Could not get items");
120 return;
121 }
122
123 dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL);
124}
125
126static DBusHandlerResult signal_handler(DBusConnection *connection,
127 DBusMessage *message, void *_data) {
128 if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
129 "StatusNotifierItemRegistered")) {
130 const char *name;
131 if (!dbus_message_get_args(message, NULL,
132 DBUS_TYPE_STRING, &name,
133 DBUS_TYPE_INVALID)) {
134 sway_log(L_ERROR, "Error getting StatusNotifierItemRegistered args");
135 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
136 }
137
138 if (list_seq_find(tray->items, sni_str_cmp, name) == -1) {
139 struct StatusNotifierItem *item = sni_create(name);
140
141 list_add(tray->items, item);
142 dirty = true;
143 }
144
145 return DBUS_HANDLER_RESULT_HANDLED;
146 } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierWatcher",
147 "StatusNotifierItemUnregistered")) {
148 const char *name;
149 if (!dbus_message_get_args(message, NULL,
150 DBUS_TYPE_STRING, &name,
151 DBUS_TYPE_INVALID)) {
152 sway_log(L_ERROR, "Error getting StatusNotifierItemUnregistered args");
153 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
154 }
155
156 int index;
157 if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
158 sni_free(tray->items->items[index]);
159 list_del(tray->items, index);
160 dirty = true;
161 } else {
162 // If it's not in our list, then our list is incorrect.
163 // Fetch all items again
164 sway_log(L_INFO, "Host item list incorrect, refreshing");
165 get_items();
166 }
167
168 return DBUS_HANDLER_RESULT_HANDLED;
169 } else if (dbus_message_is_signal(message, "org.freedesktop.StatusNotifierItem",
170 "NewIcon") || dbus_message_is_signal(message,
171 "org.kde.StatusNotifierItem", "NewIcon")) {
172 const char *name;
173 int index;
174 struct StatusNotifierItem *item;
175
176 name = dbus_message_get_sender(message);
177 if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) {
178 item = tray->items->items[index];
179 get_icon(item);
180 }
181
182 return DBUS_HANDLER_RESULT_HANDLED;
183 }
184 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
185}
186
187int init_tray() {
188 tray = (struct tray *)malloc(sizeof(tray));
189
190 tray->items = create_list();
191
192 DBusError error;
193 dbus_error_init(&error);
194 char *name = NULL;
195 if (!conn) {
196 sway_log(L_ERROR, "Connection is null, cannot init SNI host");
197 goto err;
198 }
199 name = calloc(sizeof(char), 256);
200
201 if (!name) {
202 sway_log(L_ERROR, "Cannot allocate name");
203 goto err;
204 }
205
206 pid_t pid = getpid();
207 if (snprintf(name, 256, "org.freedesktop.StatusNotifierHost-%d", pid)
208 >= 256) {
209 sway_log(L_ERROR, "Cannot get host name because string is too short."
210 "This should not happen");
211 goto err;
212 }
213
214 // We want to be the sole owner of this name
215 if (dbus_bus_request_name(conn, name, DBUS_NAME_FLAG_DO_NOT_QUEUE,
216 &error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
217 sway_log(L_ERROR, "Cannot get host name and start the tray");
218 goto err;
219 }
220 if (dbus_error_is_set(&error)) {
221 sway_log(L_ERROR, "Dbus err getting host name: %s\n", error.message);
222 goto err;
223 }
224 sway_log(L_DEBUG, "Got host name");
225
226 register_host(name);
227
228 get_items();
229
230 // Perhaps use addmatch helper functions like wlc does?
231 dbus_bus_add_match(conn,
232 "type='signal',\
233 sender='org.freedesktop.StatusNotifierWatcher',\
234 member='StatusNotifierItemRegistered'",
235 &error);
236 if (dbus_error_is_set(&error)) {
237 sway_log(L_ERROR, "dbus_err: %s", error.message);
238 goto err;
239 }
240 dbus_bus_add_match(conn,
241 "type='signal',\
242 sender='org.freedesktop.StatusNotifierWatcher',\
243 member='StatusNotifierItemUnregistered'",
244 &error);
245 if (dbus_error_is_set(&error)) {
246 sway_log(L_ERROR, "dbus_err: %s", error.message);
247 return -1;
248 }
249
250 // SNI matches
251 dbus_bus_add_match(conn,
252 "type='signal',\
253 interface='org.freedesktop.StatusNotifierItem',\
254 member='NewIcon'",
255 &error);
256 if (dbus_error_is_set(&error)) {
257 sway_log(L_ERROR, "dbus_err %s", error.message);
258 goto err;
259 }
260 dbus_bus_add_match(conn,
261 "type='signal',\
262 interface='org.kde.StatusNotifierItem',\
263 member='NewIcon'",
264 &error);
265 if (dbus_error_is_set(&error)) {
266 sway_log(L_ERROR, "dbus_err %s", error.message);
267 goto err;
268 }
269
270 dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
271
272 free(name);
273 return 0;
274
275err:
276 // TODO better handle errors
277 free(name);
278 return -1;
279}