summaryrefslogtreecommitdiffstats
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.c178
1 files changed, 157 insertions, 21 deletions
diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c
index 86453e70..4c53fb99 100644
--- a/swaybar/tray/sni_watcher.c
+++ b/swaybar/tray/sni_watcher.c
@@ -11,6 +11,7 @@
11 11
12static list_t *items = NULL; 12static list_t *items = NULL;
13static list_t *hosts = NULL; 13static list_t *hosts = NULL;
14static list_t *object_path_items = NULL;
14 15
15/** 16/**
16 * Describes the function of the StatusNotifierWatcher 17 * Describes the function of the StatusNotifierWatcher
@@ -18,6 +19,10 @@ static list_t *hosts = NULL;
18 * 19 *
19 * We also implement KDE's special snowflake protocol, it's like this but with 20 * 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 * all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect.
22 *
23 * We _also_ support registering items by object path (even though this is a
24 * huge pain in the ass). Hosts that would like to subscribe to these items have
25 * to go through the `org.swaywm.LessSuckyStatusNotifierWatcher` interface.
21 */ 26 */
22static const char *interface_xml = 27static const char *interface_xml =
23 "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'" 28 "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
@@ -64,8 +69,59 @@ static const char *interface_xml =
64 " <arg type='' name='service' direction='out'/>" 69 " <arg type='' name='service' direction='out'/>"
65 " </signal>" 70 " </signal>"
66 " </interface>" 71 " </interface>"
72 " <interface name='org.swaywm.LessSuckyStatusNotifierWatcher'>"
73 " <property name='RegisteredObjectPathItems' type='a(os)' access='read'/>"
74 " <signal name='ObjPathItemRegistered'>"
75 " <arg type='os' name='service' direction='out'/>"
76 " </signal>"
77 " </interface>"
67 "</node>"; 78 "</node>";
68 79
80struct ObjPathItem {
81 char *obj_path;
82 char *unique_name;
83};
84
85static void free_obj_path_item(struct ObjPathItem *item) {
86 if (!item) {
87 return;
88 }
89 free(item->unique_name);
90 free(item->obj_path);
91 free(item);
92}
93static struct ObjPathItem *create_obj_path_item(const char *unique_name, const char *obj_path) {
94 struct ObjPathItem *item = malloc(sizeof(struct ObjPathItem));
95 if (!item) {
96 return NULL;
97 }
98 item->unique_name = strdup(unique_name);
99 item->obj_path = strdup(obj_path);
100 if (!item->unique_name || !item->obj_path) {
101 free_obj_path_item(item);
102 return NULL;
103 }
104 return item;
105}
106/**
107 * NOTE: This compare function does have ordering, this is because it has to
108 * comapre two strings.
109 */
110static int obj_path_item_cmp(const void *_item1, const void *_item2) {
111 const struct ObjPathItem *item1 = _item1;
112 const struct ObjPathItem *item2 = _item2;
113 if (strcmp(item1->unique_name,item2->unique_name) == 0 &&
114 strcmp(item1->obj_path,item2->obj_path) == 0) {
115 return 0;
116 }
117 return -1;
118}
119static int obj_path_unique_name_cmp(const void *_item, const void *_unique_name) {
120 const struct ObjPathItem *item = _item;
121 const char *unique_name = _unique_name;
122 return strcmp(item->unique_name, unique_name);
123}
124
69static void host_registered_signal(DBusConnection *connection) { 125static void host_registered_signal(DBusConnection *connection) {
70 // Send one signal for each protocol 126 // Send one signal for each protocol
71 DBusMessage *signal = dbus_message_new_signal( 127 DBusMessage *signal = dbus_message_new_signal(
@@ -128,6 +184,19 @@ static void item_unregistered_signal(DBusConnection *connection, const char *nam
128 dbus_message_unref(signal); 184 dbus_message_unref(signal);
129} 185}
130 186
187static void obj_path_item_registered_signal(DBusConnection *connection, const struct ObjPathItem *item) {
188 DBusMessage *signal = dbus_message_new_signal(
189 "/StatusNotifierWatcher",
190 "org.swaywm.LessSuckyStatusNotifierWatcher",
191 "ObjPathItemRegistered");
192 dbus_message_append_args(signal,
193 DBUS_TYPE_OBJECT_PATH, &item->obj_path,
194 DBUS_TYPE_STRING, &item->unique_name,
195 DBUS_TYPE_INVALID);
196 dbus_connection_send(connection, signal, NULL);
197 dbus_message_unref(signal);
198}
199
131static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) { 200static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
132 DBusMessage *reply; 201 DBusMessage *reply;
133 202
@@ -147,28 +216,44 @@ static void register_item(DBusConnection *connection, DBusMessage *message) {
147 if (!dbus_message_get_args(message, &error, 216 if (!dbus_message_get_args(message, &error,
148 DBUS_TYPE_STRING, &name, 217 DBUS_TYPE_STRING, &name,
149 DBUS_TYPE_INVALID)) { 218 DBUS_TYPE_INVALID)) {
150 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); 219 sway_log(L_ERROR, "Error parsing method args: %s", error.message);
151 } 220 }
152 221
153 sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name); 222 sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"", name);
154 223
155 // Don't add duplicate or not real item 224 // Don't add duplicate or not real item
156 if (!dbus_validate_bus_name(name, NULL)) { 225 if (!dbus_validate_bus_name(name, NULL)) {
157 sway_log(L_INFO, "This item is not valid, we cannot keep track of it."); 226 if (dbus_validate_path(name, NULL)) {
158 return; 227 // Item is registered by object path
159 } 228 struct ObjPathItem *item =
229 create_obj_path_item(dbus_message_get_sender(message), name);
230
231 // Add ObjPathItem
232 if (list_seq_find(object_path_items, obj_path_item_cmp, item) != -1) {
233 free_obj_path_item(item);
234 return;
235 }
236 list_add(object_path_items, item);
237 obj_path_item_registered_signal(connection, item);
238 return;
239 } else {
240 sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
241 return;
242 }
243 } else {
160 244
161 if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) { 245 if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
162 return; 246 return;
163 } 247 }
164 if (!dbus_bus_name_has_owner(connection, name, &error)) { 248 if (!dbus_bus_name_has_owner(connection, name, &error)) {
165 return; 249 return;
166 } 250 }
167 251
168 list_add(items, strdup(name)); 252 list_add(items, strdup(name));
169 item_registered_signal(connection, name); 253 item_registered_signal(connection, name);
254 }
170 255
171 // It's silly, but xembedsniproxy wants a reply for this function 256 // It's silly, but clients want a reply for this function
172 DBusMessage *reply = dbus_message_new_method_return(message); 257 DBusMessage *reply = dbus_message_new_method_return(message);
173 dbus_connection_send(connection, reply, NULL); 258 dbus_connection_send(connection, reply, NULL);
174 dbus_message_unref(reply); 259 dbus_message_unref(reply);
@@ -182,10 +267,10 @@ static void register_host(DBusConnection *connection, DBusMessage *message) {
182 if (!dbus_message_get_args(message, &error, 267 if (!dbus_message_get_args(message, &error,
183 DBUS_TYPE_STRING, &name, 268 DBUS_TYPE_STRING, &name,
184 DBUS_TYPE_INVALID)) { 269 DBUS_TYPE_INVALID)) {
185 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); 270 sway_log(L_ERROR, "Error parsing method args: %s", error.message);
186 } 271 }
187 272
188 sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name); 273 sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"", name);
189 274
190 // Don't add duplicate or not real host 275 // Don't add duplicate or not real host
191 if (!dbus_validate_bus_name(name, NULL)) { 276 if (!dbus_validate_bus_name(name, NULL)) {
@@ -215,12 +300,12 @@ static void get_property(DBusConnection *connection, DBusMessage *message) {
215 DBUS_TYPE_STRING, &interface, 300 DBUS_TYPE_STRING, &interface,
216 DBUS_TYPE_STRING, &property, 301 DBUS_TYPE_STRING, &property,
217 DBUS_TYPE_INVALID)) { 302 DBUS_TYPE_INVALID)) {
218 sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message); 303 sway_log(L_ERROR, "Error parsing prop args: %s", error.message);
219 return; 304 return;
220 } 305 }
221 306
222 if (strcmp(property, "RegisteredStatusNotifierItems") == 0) { 307 if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
223 sway_log(L_INFO, "Replying with items\n"); 308 sway_log(L_INFO, "Replying with items");
224 DBusMessage *reply; 309 DBusMessage *reply;
225 reply = dbus_message_new_method_return(message); 310 reply = dbus_message_new_method_return(message);
226 DBusMessageIter iter; 311 DBusMessageIter iter;
@@ -281,6 +366,41 @@ static void get_property(DBusConnection *connection, DBusMessage *message) {
281 dbus_message_iter_close_container(&iter, &sub); 366 dbus_message_iter_close_container(&iter, &sub);
282 dbus_connection_send(connection, reply, NULL); 367 dbus_connection_send(connection, reply, NULL);
283 dbus_message_unref(reply); 368 dbus_message_unref(reply);
369 } else if (strcmp(property, "RegisteredObjectPathItems") == 0) {
370 sway_log(L_INFO, "Replying with ObjPathItems");
371 DBusMessage *reply;
372 reply = dbus_message_new_method_return(message);
373 DBusMessageIter iter;
374 DBusMessageIter variant;
375 DBusMessageIter array;
376 DBusMessageIter dstruct;
377
378 dbus_message_iter_init_append(reply, &iter);
379
380 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
381 "a(os)", &variant);
382 dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
383 "(os)", &array);
384
385 for (int i = 0; i < object_path_items->length; ++i) {
386 struct ObjPathItem *item = object_path_items->items[i];
387
388 dbus_message_iter_open_container(&array,
389 DBUS_TYPE_STRUCT, NULL, &dstruct);
390
391 dbus_message_iter_append_basic(&dstruct,
392 DBUS_TYPE_OBJECT_PATH, item->obj_path);
393 dbus_message_iter_append_basic(&dstruct,
394 DBUS_TYPE_STRING, item->unique_name);
395
396 dbus_message_iter_close_container(&array, &dstruct);
397 }
398
399 dbus_message_iter_close_container(&variant, &array);
400 dbus_message_iter_close_container(&iter, &variant);
401
402 dbus_connection_send(connection, reply, NULL);
403 dbus_message_unref(reply);
284 } 404 }
285} 405}
286 406
@@ -289,6 +409,8 @@ static void set_property(DBusConnection *connection, DBusMessage *message) {
289 return; 409 return;
290} 410}
291 411
412// TODO clean me up please or get rid of me
413// also add LessSuckyStatusNotifierWatcher props
292static void get_all(DBusConnection *connection, DBusMessage *message) { 414static void get_all(DBusConnection *connection, DBusMessage *message) {
293 DBusMessage *reply; 415 DBusMessage *reply;
294 reply = dbus_message_new_method_return(message); 416 reply = dbus_message_new_method_return(message);
@@ -400,6 +522,8 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
400 const char *old_owner; 522 const char *old_owner;
401 const char *new_owner; 523 const char *new_owner;
402 int index; 524 int index;
525 bool found_obj_path_item = false;
526
403 if (!dbus_message_get_args(message, NULL, 527 if (!dbus_message_get_args(message, NULL,
404 DBUS_TYPE_STRING, &name, 528 DBUS_TYPE_STRING, &name,
405 DBUS_TYPE_STRING, &old_owner, 529 DBUS_TYPE_STRING, &old_owner,
@@ -427,6 +551,17 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
427 551
428 return DBUS_HANDLER_RESULT_HANDLED; 552 return DBUS_HANDLER_RESULT_HANDLED;
429 } 553 }
554 while ((index = list_seq_find(object_path_items, obj_path_unique_name_cmp, name)) != -1) {
555 found_obj_path_item = true;
556 struct ObjPathItem *item = object_path_items->items[index];
557 sway_log(L_INFO, "ObjPathItem lost %s", item->obj_path);
558 list_del(object_path_items, index);
559 free_obj_path_item(item);
560 }
561 if (found_obj_path_item) {
562 item_unregistered_signal(connection, name);
563 return DBUS_HANDLER_RESULT_HANDLED;
564 }
430 } 565 }
431 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 566 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
432} 567}
@@ -446,6 +581,7 @@ int init_sni_watcher() {
446 581
447 items = create_list(); 582 items = create_list();
448 hosts = create_list(); 583 hosts = create_list();
584 object_path_items = create_list();
449 585
450 int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher", 586 int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
451 DBUS_NAME_FLAG_REPLACE_EXISTING, 587 DBUS_NAME_FLAG_REPLACE_EXISTING,
@@ -456,7 +592,7 @@ int init_sni_watcher() {
456 sway_log(L_INFO, "Could not get watcher name, it may start later"); 592 sway_log(L_INFO, "Could not get watcher name, it may start later");
457 } 593 }
458 if (dbus_error_is_set(&error)) { 594 if (dbus_error_is_set(&error)) {
459 sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message); 595 sway_log(L_ERROR, "dbus err getting watcher name: %s", error.message);
460 return -1; 596 return -1;
461 } 597 }
462 598
@@ -469,7 +605,7 @@ int init_sni_watcher() {
469 sway_log(L_INFO, "Could not get kde watcher name, it may start later"); 605 sway_log(L_INFO, "Could not get kde watcher name, it may start later");
470 } 606 }
471 if (dbus_error_is_set(&error)) { 607 if (dbus_error_is_set(&error)) {
472 sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message); 608 sway_log(L_ERROR, "dbus err getting kde watcher name: %s", error.message);
473 return -1; 609 return -1;
474 } 610 }
475 611
@@ -477,7 +613,7 @@ int init_sni_watcher() {
477 "/StatusNotifierWatcher", 613 "/StatusNotifierWatcher",
478 &vtable, NULL, &error); 614 &vtable, NULL, &error);
479 if (dbus_error_is_set(&error)) { 615 if (dbus_error_is_set(&error)) {
480 sway_log(L_ERROR, "dbus_err: %s\n", error.message); 616 sway_log(L_ERROR, "dbus_err: %s", error.message);
481 return -1; 617 return -1;
482 } 618 }
483 619