aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/tray/sni_watcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/tray/sni_watcher.c')
-rw-r--r--swaybar/tray/sni_watcher.c497
1 files changed, 0 insertions, 497 deletions
diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c
deleted file mode 100644
index 86453e70..00000000
--- a/swaybar/tray/sni_watcher.c
+++ /dev/null
@@ -1,497 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <unistd.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <stdbool.h>
7#include <dbus/dbus.h>
8#include "swaybar/tray/dbus.h"
9#include "list.h"
10#include "log.h"
11
12static list_t *items = NULL;
13static list_t *hosts = NULL;
14
15/**
16 * Describes the function of the StatusNotifierWatcher
17 * See https://freedesktop.org/wiki/Specifications/StatusNotifierItem/StatusNotifierWatcher/
18 *
19 * We also implement KDE's special snowflake protocol, it's like this but with
20 * all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect.
21 */
22static const char *interface_xml =
23 "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
24 "'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>"
25 "<node>"
26 " <interface name='org.freedesktop.DBus.Introspectable'>"
27 " <method name='Introspect'>"
28 " <arg name='xml_data' direction='out' type='s'/>"
29 " </method>"
30 " </interface>"
31 " <interface name='org.freedesktop.DBus.Properties'>"
32 " <method name='Get'>"
33 " <arg name='interface' direction='in' type='s'/>"
34 " <arg name='propname' direction='in' type='s'/>"
35 " <arg name='value' direction='out' type='v'/>"
36 " </method>"
37 " <method name='Set'>"
38 " <arg name='interface' direction='in' type='s'/>"
39 " <arg name='propname' direction='in' type='s'/>"
40 " <arg name='value' direction='in' type='v'/>"
41 " </method>"
42 " <method name='GetAll'>"
43 " <arg name='interface' direction='in' type='s'/>"
44 " <arg name='props' direction='out' type='a{sv}'/>"
45 " </method>"
46 " </interface>"
47 " <interface name='org.freedesktop.StatusNotifierWatcher'>"
48 " <method name='RegisterStatusNotifierItem'>"
49 " <arg type='s' name='service' direction='in'/>"
50 " </method>"
51 " <method name='RegisterStatusNotifierHost'>"
52 " <arg type='s' name='service' direction='in'/>"
53 " </method>"
54 " <property name='RegisteredStatusNotifierItems' type='as' access='read'/>"
55 " <property name='IsStatusNotifierHostRegistered' type='b' access='read'/>"
56 " <property name='ProtocolVersion' type='i' access='read'/>"
57 " <signal name='StatusNotifierItemRegistered'>"
58 " <arg type='s' name='service' direction='out'/>"
59 " </signal>"
60 " <signal name='StatusNotifierItemUnregistered'>"
61 " <arg type='s' name='service' direction='out'/>"
62 " </signal>"
63 " <signal name='StatusNotifierHostRegistered'>"
64 " <arg type='' name='service' direction='out'/>"
65 " </signal>"
66 " </interface>"
67 "</node>";
68
69static void host_registered_signal(DBusConnection *connection) {
70 // Send one signal for each protocol
71 DBusMessage *signal = dbus_message_new_signal(
72 "/StatusNotifierWatcher",
73 "org.freedesktop.StatusNotifierWatcher",
74 "StatusNotifierHostRegistered");
75
76 dbus_connection_send(connection, signal, NULL);
77 dbus_message_unref(signal);
78
79
80 signal = dbus_message_new_signal(
81 "/StatusNotifierWatcher",
82 "org.kde.StatusNotifierWatcher",
83 "StatusNotifierHostRegistered");
84
85 dbus_connection_send(connection, signal, NULL);
86 dbus_message_unref(signal);
87}
88static void item_registered_signal(DBusConnection *connection, const char *name) {
89 DBusMessage *signal = dbus_message_new_signal(
90 "/StatusNotifierWatcher",
91 "org.freedesktop.StatusNotifierWatcher",
92 "StatusNotifierItemRegistered");
93 dbus_message_append_args(signal,
94 DBUS_TYPE_STRING, &name,
95 DBUS_TYPE_INVALID);
96 dbus_connection_send(connection, signal, NULL);
97 dbus_message_unref(signal);
98
99 signal = dbus_message_new_signal(
100 "/StatusNotifierWatcher",
101 "org.kde.StatusNotifierWatcher",
102 "StatusNotifierItemRegistered");
103 dbus_message_append_args(signal,
104 DBUS_TYPE_STRING, &name,
105 DBUS_TYPE_INVALID);
106 dbus_connection_send(connection, signal, NULL);
107 dbus_message_unref(signal);
108}
109static void item_unregistered_signal(DBusConnection *connection, const char *name) {
110 DBusMessage *signal = dbus_message_new_signal(
111 "/StatusNotifierWatcher",
112 "org.freedesktop.StatusNotifierWatcher",
113 "StatusNotifierItemUnregistered");
114 dbus_message_append_args(signal,
115 DBUS_TYPE_STRING, &name,
116 DBUS_TYPE_INVALID);
117 dbus_connection_send(connection, signal, NULL);
118 dbus_message_unref(signal);
119
120 signal = dbus_message_new_signal(
121 "/StatusNotifierWatcher",
122 "org.kde.StatusNotifierWatcher",
123 "StatusNotifierItemUnregistered");
124 dbus_message_append_args(signal,
125 DBUS_TYPE_STRING, &name,
126 DBUS_TYPE_INVALID);
127 dbus_connection_send(connection, signal, NULL);
128 dbus_message_unref(signal);
129}
130
131static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
132 DBusMessage *reply;
133
134 reply = dbus_message_new_method_return(request);
135 dbus_message_append_args(reply,
136 DBUS_TYPE_STRING, &interface_xml,
137 DBUS_TYPE_INVALID);
138 dbus_connection_send(connection, reply, NULL);
139 dbus_message_unref(reply);
140}
141
142static void register_item(DBusConnection *connection, DBusMessage *message) {
143 DBusError error;
144 char *name;
145
146 dbus_error_init(&error);
147 if (!dbus_message_get_args(message, &error,
148 DBUS_TYPE_STRING, &name,
149 DBUS_TYPE_INVALID)) {
150 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
151 }
152
153 sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name);
154
155 // Don't add duplicate or not real item
156 if (!dbus_validate_bus_name(name, NULL)) {
157 sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
158 return;
159 }
160
161 if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
162 return;
163 }
164 if (!dbus_bus_name_has_owner(connection, name, &error)) {
165 return;
166 }
167
168 list_add(items, strdup(name));
169 item_registered_signal(connection, name);
170
171 // It's silly, but xembedsniproxy wants a reply for this function
172 DBusMessage *reply = dbus_message_new_method_return(message);
173 dbus_connection_send(connection, reply, NULL);
174 dbus_message_unref(reply);
175}
176
177static void register_host(DBusConnection *connection, DBusMessage *message) {
178 DBusError error;
179 char *name;
180
181 dbus_error_init(&error);
182 if (!dbus_message_get_args(message, &error,
183 DBUS_TYPE_STRING, &name,
184 DBUS_TYPE_INVALID)) {
185 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message);
186 }
187
188 sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name);
189
190 // Don't add duplicate or not real host
191 if (!dbus_validate_bus_name(name, NULL)) {
192 sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
193 return;
194 }
195
196
197 if (list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name) != -1) {
198 return;
199 }
200 if (!dbus_bus_name_has_owner(connection, name, &error)) {
201 return;
202 }
203
204 list_add(hosts, strdup(name));
205 host_registered_signal(connection);
206}
207
208static void get_property(DBusConnection *connection, DBusMessage *message) {
209 DBusError error;
210 char *interface;
211 char *property;
212
213 dbus_error_init(&error);
214 if (!dbus_message_get_args(message, &error,
215 DBUS_TYPE_STRING, &interface,
216 DBUS_TYPE_STRING, &property,
217 DBUS_TYPE_INVALID)) {
218 sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message);
219 return;
220 }
221
222 if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
223 sway_log(L_INFO, "Replying with items\n");
224 DBusMessage *reply;
225 reply = dbus_message_new_method_return(message);
226 DBusMessageIter iter;
227 DBusMessageIter sub;
228 DBusMessageIter subsub;
229
230 dbus_message_iter_init_append(reply, &iter);
231
232 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
233 "as", &sub);
234 dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
235 "s", &subsub);
236
237 for (int i = 0; i < items->length; ++i) {
238 dbus_message_iter_append_basic(&subsub,
239 DBUS_TYPE_STRING, &items->items[i]);
240 }
241
242 dbus_message_iter_close_container(&sub, &subsub);
243 dbus_message_iter_close_container(&iter, &sub);
244
245 dbus_connection_send(connection, reply, NULL);
246 dbus_message_unref(reply);
247 } else if (strcmp(property, "IsStatusNotifierHostRegistered") == 0) {
248 DBusMessage *reply;
249 DBusMessageIter iter;
250 DBusMessageIter sub;
251 int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
252
253 reply = dbus_message_new_method_return(message);
254
255 dbus_message_iter_init_append(reply, &iter);
256
257 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
258 "b", &sub);
259 dbus_message_iter_append_basic(&sub,
260 DBUS_TYPE_BOOLEAN, &registered);
261
262 dbus_message_iter_close_container(&iter, &sub);
263
264 dbus_connection_send(connection, reply, NULL);
265 dbus_message_unref(reply);
266 } else if (strcmp(property, "ProtocolVersion") == 0) {
267 DBusMessage *reply;
268 DBusMessageIter iter;
269 DBusMessageIter sub;
270 const int version = 0;
271
272 reply = dbus_message_new_method_return(message);
273
274 dbus_message_iter_init_append(reply, &iter);
275
276 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
277 "i", &sub);
278 dbus_message_iter_append_basic(&sub,
279 DBUS_TYPE_INT32, &version);
280
281 dbus_message_iter_close_container(&iter, &sub);
282 dbus_connection_send(connection, reply, NULL);
283 dbus_message_unref(reply);
284 }
285}
286
287static void set_property(DBusConnection *connection, DBusMessage *message) {
288 // All properties are read only and we don't allow new properties
289 return;
290}
291
292static void get_all(DBusConnection *connection, DBusMessage *message) {
293 DBusMessage *reply;
294 reply = dbus_message_new_method_return(message);
295 DBusMessageIter iter; /* a{v} */
296 DBusMessageIter arr;
297 DBusMessageIter dict;
298 DBusMessageIter sub;
299 DBusMessageIter subsub;
300 int registered = (hosts == NULL || hosts->length == 0) ? 0 : 1;
301 const int version = 0;
302 const char *prop;
303
304 // Could clean this up with a function for each prop
305 dbus_message_iter_init_append(reply, &iter);
306 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
307 "{sv}", &arr);
308
309 prop = "RegisteredStatusNotifierItems";
310 dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
311 NULL, &dict);
312 dbus_message_iter_append_basic(&dict,
313 DBUS_TYPE_STRING, &prop);
314 dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
315 "as", &sub);
316 dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY,
317 "s", &subsub);
318 for (int i = 0; i < items->length; ++i) {
319 dbus_message_iter_append_basic(&subsub,
320 DBUS_TYPE_STRING, &items->items[i]);
321 }
322 dbus_message_iter_close_container(&sub, &subsub);
323 dbus_message_iter_close_container(&dict, &sub);
324 dbus_message_iter_close_container(&arr, &dict);
325
326 prop = "IsStatusNotifierHostRegistered";
327 dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
328 NULL, &dict);
329 dbus_message_iter_append_basic(&dict,
330 DBUS_TYPE_STRING, &prop);
331 dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
332 "b", &sub);
333 dbus_message_iter_append_basic(&sub,
334 DBUS_TYPE_BOOLEAN, &registered);
335 dbus_message_iter_close_container(&dict, &sub);
336 dbus_message_iter_close_container(&arr, &dict);
337
338 prop = "ProtocolVersion";
339 dbus_message_iter_open_container(&arr, DBUS_TYPE_DICT_ENTRY,
340 NULL, &dict);
341 dbus_message_iter_append_basic(&dict,
342 DBUS_TYPE_STRING, &prop);
343 dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT,
344 "i", &sub);
345 dbus_message_iter_append_basic(&sub,
346 DBUS_TYPE_INT32, &version);
347 dbus_message_iter_close_container(&dict, &sub);
348 dbus_message_iter_close_container(&arr, &dict);
349
350 dbus_message_iter_close_container(&iter, &arr);
351
352 dbus_connection_send(connection, reply, NULL);
353 dbus_message_unref(reply);
354}
355
356static DBusHandlerResult message_handler(DBusConnection *connection,
357 DBusMessage *message, void *data) {
358 const char *interface_name = dbus_message_get_interface(message);
359 const char *member_name = dbus_message_get_member(message);
360
361 // In order of the xml above
362 if (strcmp(interface_name, "org.freedesktop.DBus.Introspectable") == 0 &&
363 strcmp(member_name, "Introspect") == 0) {
364 // We don't have an introspect for KDE
365 respond_to_introspect(connection, message);
366 return DBUS_HANDLER_RESULT_HANDLED;
367 } else if (strcmp(interface_name, "org.freedesktop.DBus.Properties") == 0) {
368 if (strcmp(member_name, "Get") == 0) {
369 get_property(connection, message);
370 return DBUS_HANDLER_RESULT_HANDLED;
371 } else if (strcmp(member_name, "Set") == 0) {
372 set_property(connection, message);
373 return DBUS_HANDLER_RESULT_HANDLED;
374 } else if (strcmp(member_name, "GetAll") == 0) {
375 get_all(connection, message);
376 return DBUS_HANDLER_RESULT_HANDLED;
377 } else {
378 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
379 }
380 } else if (strcmp(interface_name, "org.freedesktop.StatusNotifierWatcher") == 0 ||
381 strcmp(interface_name, "org.kde.StatusNotifierWatcher") == 0) {
382 if (strcmp(member_name, "RegisterStatusNotifierItem") == 0) {
383 register_item(connection, message);
384 return DBUS_HANDLER_RESULT_HANDLED;
385 } else if (strcmp(member_name, "RegisterStatusNotifierHost") == 0) {
386 register_host(connection, message);
387 return DBUS_HANDLER_RESULT_HANDLED;
388 } else {
389 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
390 }
391 }
392 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
393}
394
395static DBusHandlerResult signal_handler(DBusConnection *connection,
396 DBusMessage *message, void *_data) {
397 if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged")) {
398 // Only eat the message if it is name that we are watching
399 const char *name;
400 const char *old_owner;
401 const char *new_owner;
402 int index;
403 if (!dbus_message_get_args(message, NULL,
404 DBUS_TYPE_STRING, &name,
405 DBUS_TYPE_STRING, &old_owner,
406 DBUS_TYPE_STRING, &new_owner,
407 DBUS_TYPE_INVALID)) {
408 sway_log(L_ERROR, "Error getting LostName args");
409 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
410 }
411 if (strcmp(new_owner, "") != 0) {
412 // Name is not lost
413 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
414 }
415 if ((index = list_seq_find(items, (int (*)(const void *, const void *))strcmp, name)) != -1) {
416 sway_log(L_INFO, "Status Notifier Item lost %s", name);
417 free(items->items[index]);
418 list_del(items, index);
419 item_unregistered_signal(connection, name);
420
421 return DBUS_HANDLER_RESULT_HANDLED;
422 }
423 if ((index = list_seq_find(hosts, (int (*)(const void *, const void *))strcmp, name)) != -1) {
424 sway_log(L_INFO, "Status Notifier Host lost %s", name);
425 free(hosts->items[index]);
426 list_del(hosts, index);
427
428 return DBUS_HANDLER_RESULT_HANDLED;
429 }
430 }
431 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
432}
433
434static const DBusObjectPathVTable vtable = {
435 .message_function = message_handler,
436 .unregister_function = NULL,
437};
438
439int init_sni_watcher() {
440 DBusError error;
441 dbus_error_init(&error);
442 if (!conn) {
443 sway_log(L_ERROR, "Connection is null, cannot initiate StatusNotifierWatcher");
444 return -1;
445 }
446
447 items = create_list();
448 hosts = create_list();
449
450 int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
451 DBUS_NAME_FLAG_REPLACE_EXISTING,
452 &error);
453 if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
454 sway_log(L_DEBUG, "Got watcher name");
455 } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
456 sway_log(L_INFO, "Could not get watcher name, it may start later");
457 }
458 if (dbus_error_is_set(&error)) {
459 sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message);
460 return -1;
461 }
462
463 status = dbus_bus_request_name(conn, "org.kde.StatusNotifierWatcher",
464 DBUS_NAME_FLAG_REPLACE_EXISTING,
465 &error);
466 if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
467 sway_log(L_DEBUG, "Got kde watcher name");
468 } else if (status == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
469 sway_log(L_INFO, "Could not get kde watcher name, it may start later");
470 }
471 if (dbus_error_is_set(&error)) {
472 sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message);
473 return -1;
474 }
475
476 dbus_connection_try_register_object_path(conn,
477 "/StatusNotifierWatcher",
478 &vtable, NULL, &error);
479 if (dbus_error_is_set(&error)) {
480 sway_log(L_ERROR, "dbus_err: %s\n", error.message);
481 return -1;
482 }
483
484 dbus_bus_add_match(conn,
485 "type='signal',\
486 sender='org.freedesktop.DBus',\
487 interface='org.freedesktop.DBus',\
488 member='NameOwnerChanged'",
489 &error);
490
491 if (dbus_error_is_set(&error)) {
492 sway_log(L_ERROR, "DBus error getting match args: %s", error.message);
493 }
494
495 dbus_connection_add_filter(conn, signal_handler, NULL, NULL);
496 return 0;
497}