diff options
Diffstat (limited to 'swaybar/tray/sni_watcher.c')
-rw-r--r-- | swaybar/tray/sni_watcher.c | 178 |
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 | ||
12 | static list_t *items = NULL; | 12 | static list_t *items = NULL; |
13 | static list_t *hosts = NULL; | 13 | static list_t *hosts = NULL; |
14 | static 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 | */ |
22 | static const char *interface_xml = | 27 | static 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 | ||
80 | struct ObjPathItem { | ||
81 | char *obj_path; | ||
82 | char *unique_name; | ||
83 | }; | ||
84 | |||
85 | static 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 | } | ||
93 | static 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 | */ | ||
110 | static 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 | } | ||
119 | static 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 | |||
69 | static void host_registered_signal(DBusConnection *connection) { | 125 | static 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 | ||
187 | static 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 | |||
131 | static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) { | 200 | static 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 | ||
292 | static void get_all(DBusConnection *connection, DBusMessage *message) { | 414 | static 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 | ||