aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/tray
diff options
context:
space:
mode:
authorLibravatar Ian Fan <ianfan0@gmail.com>2018-12-05 17:28:14 +0000
committerLibravatar Ian Fan <ianfan0@gmail.com>2018-12-31 20:40:18 +0000
commit02df3f67aad203e87602c0124489a41382994cbc (patch)
tree25cf05a42469bbf936cdcc90e28c5974a888bf07 /swaybar/tray
parentswaybar: add skeleton dbus code to tray (diff)
downloadsway-02df3f67aad203e87602c0124489a41382994cbc.tar.gz
sway-02df3f67aad203e87602c0124489a41382994cbc.tar.zst
sway-02df3f67aad203e87602c0124489a41382994cbc.zip
swaybar: add StatusNotifierWatcher to tray
Diffstat (limited to 'swaybar/tray')
-rw-r--r--swaybar/tray/tray.c7
-rw-r--r--swaybar/tray/watcher.c212
2 files changed, 219 insertions, 0 deletions
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
index 0a4f2955..36ee3c30 100644
--- a/swaybar/tray/tray.c
+++ b/swaybar/tray/tray.c
@@ -4,6 +4,7 @@
4#include <string.h> 4#include <string.h>
5#include "swaybar/bar.h" 5#include "swaybar/bar.h"
6#include "swaybar/tray/tray.h" 6#include "swaybar/tray/tray.h"
7#include "swaybar/tray/watcher.h"
7#include "log.h" 8#include "log.h"
8 9
9struct swaybar_tray *create_tray(struct swaybar *bar) { 10struct swaybar_tray *create_tray(struct swaybar *bar) {
@@ -23,6 +24,10 @@ struct swaybar_tray *create_tray(struct swaybar *bar) {
23 tray->bar = bar; 24 tray->bar = bar;
24 tray->bus = bus; 25 tray->bus = bus;
25 tray->fd = sd_bus_get_fd(tray->bus); 26 tray->fd = sd_bus_get_fd(tray->bus);
27
28 tray->watcher_xdg = create_watcher("freedesktop", tray->bus);
29 tray->watcher_kde = create_watcher("kde", tray->bus);
30
26 return tray; 31 return tray;
27} 32}
28 33
@@ -30,6 +35,8 @@ void destroy_tray(struct swaybar_tray *tray) {
30 if (!tray) { 35 if (!tray) {
31 return; 36 return;
32 } 37 }
38 destroy_watcher(tray->watcher_xdg);
39 destroy_watcher(tray->watcher_kde);
33 sd_bus_flush_close_unref(tray->bus); 40 sd_bus_flush_close_unref(tray->bus);
34 free(tray); 41 free(tray);
35} 42}
diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c
new file mode 100644
index 00000000..198c6c85
--- /dev/null
+++ b/swaybar/tray/watcher.c
@@ -0,0 +1,212 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h>
3#include <stddef.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include "list.h"
8#include "log.h"
9#include "swaybar/tray/watcher.h"
10
11static const char *obj_path = "/StatusNotifierWatcher";
12
13static bool using_standard_protocol(struct swaybar_watcher *watcher) {
14 return watcher->interface[strlen("org.")] == 'f'; // freedesktop
15}
16
17static int cmp_id(const void *item, const void *cmp_to) {
18 return strcmp(item, cmp_to);
19}
20
21static int cmp_service(const void *item, const void *cmp_to) {
22 return strncmp(item, cmp_to, strlen(cmp_to));
23}
24
25static int handle_lost_service(sd_bus_message *msg,
26 void *data, sd_bus_error *error) {
27 char *service, *old_owner, *new_owner;
28 int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner);
29 if (ret < 0) {
30 wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret));
31 return ret;
32 }
33
34 if (!*new_owner) {
35 struct swaybar_watcher *watcher = data;
36 int idx = list_seq_find(watcher->items,
37 using_standard_protocol(watcher) ? cmp_id : cmp_service, service);
38 if (idx != -1) {
39 char *id = watcher->items->items[idx];
40 wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id);
41 list_del(watcher->items, idx);
42 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
43 "StatusNotifierItemUnregistered", "s", id);
44 free(id);
45 }
46
47 idx = list_seq_find(watcher->hosts, cmp_id, service);
48 if (idx != -1) {
49 wlr_log(WLR_DEBUG, "Unregistering Status Notifier Host '%s'", service);
50 free(watcher->hosts->items[idx]);
51 list_del(watcher->hosts, idx);
52 }
53 }
54
55 return 0;
56}
57
58static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) {
59 char *service_or_path, *id;
60 int ret = sd_bus_message_read(msg, "s", &service_or_path);
61 if (ret < 0) {
62 wlr_log(WLR_ERROR, "Failed to parse register SNI message: %s", strerror(-ret));
63 return ret;
64 }
65
66 struct swaybar_watcher *watcher = data;
67 if (using_standard_protocol(watcher)) {
68 id = strdup(service_or_path);
69 } else {
70 const char *service, *path;
71 if (service_or_path[0] == '/') {
72 service = sd_bus_message_get_sender(msg);
73 path = service_or_path;
74 } else {
75 service = service_or_path;
76 path = "/StatusNotifierItem";
77 }
78 size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1;
79 id = malloc(id_len);
80 snprintf(id, id_len, "%s%s", service, path);
81 }
82
83 if (list_seq_find(watcher->items, cmp_id, id) == -1) {
84 wlr_log(WLR_DEBUG, "Registering Status Notifier Item '%s'", id);
85 list_add(watcher->items, id);
86 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
87 "StatusNotifierItemRegistered", "s", id);
88 } else {
89 wlr_log(WLR_DEBUG, "Status Notifier Item '%s' already registered", id);
90 free(id);
91 }
92
93 return sd_bus_reply_method_return(msg, "");
94}
95
96static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) {
97 char *service;
98 int ret = sd_bus_message_read(msg, "s", &service);
99 if (ret < 0) {
100 wlr_log(WLR_ERROR, "Failed to parse register host message: %s", strerror(-ret));
101 return ret;
102 }
103
104 struct swaybar_watcher *watcher = data;
105 if (list_seq_find(watcher->hosts, cmp_id, service) == -1) {
106 wlr_log(WLR_DEBUG, "Registering Status Notifier Host '%s'", service);
107 list_add(watcher->hosts, strdup(service));
108 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
109 "StatusNotifierHostRegistered", "s", service);
110 } else {
111 wlr_log(WLR_DEBUG, "Status Notifier Host '%s' already registered", service);
112 }
113
114 return sd_bus_reply_method_return(msg, "");
115}
116
117static int get_registered_snis(sd_bus *bus, const char *obj_path,
118 const char *interface, const char *property, sd_bus_message *reply,
119 void *data, sd_bus_error *error) {
120 struct swaybar_watcher *watcher = data;
121 list_add(watcher->items, NULL); // strv expects NULL-terminated string array
122 int ret = sd_bus_message_append_strv(reply, (char **)watcher->items->items);
123 list_del(watcher->items, watcher->items->length - 1);
124 return ret;
125}
126
127static int is_host_registered(sd_bus *bus, const char *obj_path,
128 const char *interface, const char *property, sd_bus_message *reply,
129 void *data, sd_bus_error *error) {
130 struct swaybar_watcher *watcher = data;
131 int val = watcher->hosts->length > 0; // dbus expects int rather than bool
132 return sd_bus_message_append_basic(reply, 'b', &val);
133}
134
135static const sd_bus_vtable watcher_vtable[] = {
136 SD_BUS_VTABLE_START(0),
137 SD_BUS_METHOD("RegisterStatusNotifierItem", "s", "", register_sni,
138 SD_BUS_VTABLE_UNPRIVILEGED),
139 SD_BUS_METHOD("RegisterStatusNotifierHost", "s", "", register_host,
140 SD_BUS_VTABLE_UNPRIVILEGED),
141 SD_BUS_PROPERTY("RegisteredStatusNotifierItems", "as", get_registered_snis,
142 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
143 SD_BUS_PROPERTY("IsStatusNotifierHostRegistered", "b", is_host_registered,
144 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
145 SD_BUS_PROPERTY("ProtocolVersion", "i", NULL,
146 offsetof(struct swaybar_watcher, version),
147 SD_BUS_VTABLE_PROPERTY_CONST),
148 SD_BUS_SIGNAL("StatusNotifierItemRegistered", "s", 0),
149 SD_BUS_SIGNAL("StatusNotifierItemUnregistered", "s", 0),
150 SD_BUS_SIGNAL("StatusNotifierHostRegistered", NULL, 0),
151 SD_BUS_VTABLE_END
152};
153
154struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) {
155 struct swaybar_watcher *watcher =
156 calloc(1, sizeof(struct swaybar_watcher));
157 if (!watcher) {
158 return NULL;
159 }
160
161 size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1;
162 watcher->interface = malloc(len);
163 snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol);
164
165 sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL;
166 int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path,
167 watcher->interface, watcher_vtable, watcher);
168 if (ret < 0) {
169 wlr_log(WLR_ERROR, "Failed to add object vtable: %s", strerror(-ret));
170 goto error;
171 }
172
173 ret = sd_bus_match_signal(bus, &signal_slot, "org.freedesktop.DBus",
174 "/org/freedesktop/DBus", "org.freedesktop.DBus",
175 "NameOwnerChanged", handle_lost_service, watcher);
176 if (ret < 0) {
177 wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s",
178 strerror(-ret));
179 goto error;
180 }
181
182 ret = sd_bus_request_name(bus, watcher->interface, 0);
183 if (ret < 0) {
184 wlr_log(WLR_ERROR, "Failed to acquire service name: %s", strerror(-ret));
185 goto error;
186 }
187
188 sd_bus_slot_set_floating(signal_slot, 0);
189 sd_bus_slot_set_floating(vtable_slot, 0);
190
191 watcher->bus = bus;
192 watcher->hosts = create_list();
193 watcher->items = create_list();
194 watcher->version = 0;
195 wlr_log(WLR_DEBUG, "Registered %s", watcher->interface);
196 return watcher;
197error:
198 sd_bus_slot_unref(signal_slot);
199 sd_bus_slot_unref(vtable_slot);
200 destroy_watcher(watcher);
201 return NULL;
202}
203
204void destroy_watcher(struct swaybar_watcher *watcher) {
205 if (!watcher) {
206 return;
207 }
208 list_free_items_and_destroy(watcher->hosts);
209 list_free_items_and_destroy(watcher->items);
210 free(watcher->interface);
211 free(watcher);
212}