diff options
author | Ian Fan <ianfan0@gmail.com> | 2018-12-07 12:33:45 +0000 |
---|---|---|
committer | Ian Fan <ianfan0@gmail.com> | 2018-12-31 20:40:18 +0000 |
commit | 74655f835aa9fe0e976473d443f62d253602696c (patch) | |
tree | 238835505b049fc4b0479f0fa25e2c30deb0adb6 | |
parent | swaybar: add StatusNotifierHost to tray (diff) | |
download | sway-74655f835aa9fe0e976473d443f62d253602696c.tar.gz sway-74655f835aa9fe0e976473d443f62d253602696c.tar.zst sway-74655f835aa9fe0e976473d443f62d253602696c.zip |
swaybar: add StatusNotifierItem to tray
-rw-r--r-- | include/swaybar/tray/item.h | 38 | ||||
-rw-r--r-- | include/swaybar/tray/tray.h | 2 | ||||
-rw-r--r-- | swaybar/meson.build | 1 | ||||
-rw-r--r-- | swaybar/tray/host.c | 9 | ||||
-rw-r--r-- | swaybar/tray/item.c | 236 | ||||
-rw-r--r-- | swaybar/tray/tray.c | 3 |
6 files changed, 283 insertions, 6 deletions
diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h new file mode 100644 index 00000000..57affb78 --- /dev/null +++ b/include/swaybar/tray/item.h | |||
@@ -0,0 +1,38 @@ | |||
1 | #ifndef _SWAYBAR_TRAY_ITEM_H | ||
2 | #define _SWAYBAR_TRAY_ITEM_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include "swaybar/tray/tray.h" | ||
6 | #include "list.h" | ||
7 | |||
8 | struct swaybar_pixmap { | ||
9 | int size; | ||
10 | unsigned char pixels[]; | ||
11 | }; | ||
12 | |||
13 | struct swaybar_sni { | ||
14 | // icon properties | ||
15 | struct swaybar_tray *tray; | ||
16 | cairo_surface_t *icon; | ||
17 | int min_size; | ||
18 | int max_size; | ||
19 | |||
20 | // dbus properties | ||
21 | char *watcher_id; | ||
22 | char *service; | ||
23 | char *path; | ||
24 | char *interface; | ||
25 | |||
26 | char *status; | ||
27 | char *icon_name; | ||
28 | list_t *icon_pixmap; // struct swaybar_pixmap * | ||
29 | char *attention_icon_name; | ||
30 | list_t *attention_icon_pixmap; // struct swaybar_pixmap * | ||
31 | bool item_is_menu; | ||
32 | char *menu; | ||
33 | }; | ||
34 | |||
35 | struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray); | ||
36 | void destroy_sni(struct swaybar_sni *sni); | ||
37 | |||
38 | #endif | ||
diff --git a/include/swaybar/tray/tray.h b/include/swaybar/tray/tray.h index 1d976b4a..8958b69a 100644 --- a/include/swaybar/tray/tray.h +++ b/include/swaybar/tray/tray.h | |||
@@ -24,7 +24,7 @@ struct swaybar_tray { | |||
24 | 24 | ||
25 | struct swaybar_host host_xdg; | 25 | struct swaybar_host host_xdg; |
26 | struct swaybar_host host_kde; | 26 | struct swaybar_host host_kde; |
27 | list_t *items; // char * | 27 | list_t *items; // struct swaybar_sni * |
28 | struct swaybar_watcher *watcher_xdg; | 28 | struct swaybar_watcher *watcher_xdg; |
29 | struct swaybar_watcher *watcher_kde; | 29 | struct swaybar_watcher *watcher_kde; |
30 | 30 | ||
diff --git a/swaybar/meson.build b/swaybar/meson.build index 85783a0f..312ca97b 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build | |||
@@ -1,6 +1,7 @@ | |||
1 | tray_files = get_option('enable-tray') ? [ | 1 | tray_files = get_option('enable-tray') ? [ |
2 | 'tray/host.c', | 2 | 'tray/host.c', |
3 | 'tray/icon.c', | 3 | 'tray/icon.c', |
4 | 'tray/item.c', | ||
4 | 'tray/tray.c', | 5 | 'tray/tray.c', |
5 | 'tray/watcher.c' | 6 | 'tray/watcher.c' |
6 | ] : [] | 7 | ] : [] |
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index 3cc90254..8ab896d4 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <string.h> | 5 | #include <string.h> |
6 | #include <unistd.h> | 6 | #include <unistd.h> |
7 | #include "swaybar/tray/host.h" | 7 | #include "swaybar/tray/host.h" |
8 | #include "swaybar/tray/item.h" | ||
8 | #include "swaybar/tray/tray.h" | 9 | #include "swaybar/tray/tray.h" |
9 | #include "list.h" | 10 | #include "list.h" |
10 | #include "log.h" | 11 | #include "log.h" |
@@ -12,15 +13,15 @@ | |||
12 | static const char *watcher_path = "/StatusNotifierWatcher"; | 13 | static const char *watcher_path = "/StatusNotifierWatcher"; |
13 | 14 | ||
14 | static int cmp_sni_id(const void *item, const void *cmp_to) { | 15 | static int cmp_sni_id(const void *item, const void *cmp_to) { |
15 | const char *sni = item; | 16 | const struct swaybar_sni *sni = item; |
16 | return strcmp(sni, cmp_to); | 17 | return strcmp(sni->watcher_id, cmp_to); |
17 | } | 18 | } |
18 | 19 | ||
19 | static void add_sni(struct swaybar_tray *tray, char *id) { | 20 | static void add_sni(struct swaybar_tray *tray, char *id) { |
20 | int idx = list_seq_find(tray->items, cmp_sni_id, id); | 21 | int idx = list_seq_find(tray->items, cmp_sni_id, id); |
21 | if (idx == -1) { | 22 | if (idx == -1) { |
22 | wlr_log(WLR_DEBUG, "Registering Status Notifier Item '%s'", id); | 23 | wlr_log(WLR_DEBUG, "Registering Status Notifier Item '%s'", id); |
23 | char *sni = strdup(id); | 24 | struct swaybar_sni *sni = create_sni(id, tray); |
24 | if (sni) { | 25 | if (sni) { |
25 | list_add(tray->items, sni); | 26 | list_add(tray->items, sni); |
26 | } | 27 | } |
@@ -53,7 +54,7 @@ static int handle_sni_unregistered(sd_bus_message *msg, void *data, | |||
53 | int idx = list_seq_find(tray->items, cmp_sni_id, id); | 54 | int idx = list_seq_find(tray->items, cmp_sni_id, id); |
54 | if (idx != -1) { | 55 | if (idx != -1) { |
55 | wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id); | 56 | wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id); |
56 | free(tray->items->items[idx]); | 57 | destroy_sni(tray->items->items[idx]); |
57 | list_del(tray->items, idx); | 58 | list_del(tray->items, idx); |
58 | } | 59 | } |
59 | return ret; | 60 | return ret; |
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c new file mode 100644 index 00000000..561a3425 --- /dev/null +++ b/swaybar/tray/item.c | |||
@@ -0,0 +1,236 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include "swaybar/tray/host.h" | ||
5 | #include "swaybar/tray/item.h" | ||
6 | #include "swaybar/tray/tray.h" | ||
7 | #include "list.h" | ||
8 | #include "log.h" | ||
9 | |||
10 | // TODO menu | ||
11 | |||
12 | static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, | ||
13 | const char *prop, list_t **dest) { | ||
14 | int ret = sd_bus_message_enter_container(msg, 'a', "(iiay)"); | ||
15 | if (ret < 0) { | ||
16 | wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); | ||
17 | return ret; | ||
18 | } | ||
19 | |||
20 | if (sd_bus_message_at_end(msg, 0)) { | ||
21 | return ret; | ||
22 | } | ||
23 | |||
24 | list_t *pixmaps = create_list(); | ||
25 | if (!pixmaps) { | ||
26 | return -12; // -ENOMEM | ||
27 | } | ||
28 | |||
29 | while (!sd_bus_message_at_end(msg, 0)) { | ||
30 | ret = sd_bus_message_enter_container(msg, 'r', "iiay"); | ||
31 | if (ret < 0) { | ||
32 | wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); | ||
33 | goto error; | ||
34 | } | ||
35 | |||
36 | int size; | ||
37 | ret = sd_bus_message_read(msg, "ii", NULL, &size); | ||
38 | if (ret < 0) { | ||
39 | wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); | ||
40 | goto error; | ||
41 | } | ||
42 | |||
43 | const void *pixels; | ||
44 | size_t npixels; | ||
45 | ret = sd_bus_message_read_array(msg, 'y', &pixels, &npixels); | ||
46 | if (ret < 0) { | ||
47 | wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); | ||
48 | goto error; | ||
49 | } | ||
50 | |||
51 | struct swaybar_pixmap *pixmap = | ||
52 | malloc(sizeof(struct swaybar_pixmap) + npixels); | ||
53 | pixmap->size = size; | ||
54 | memcpy(pixmap->pixels, pixels, npixels); | ||
55 | list_add(pixmaps, pixmap); | ||
56 | |||
57 | sd_bus_message_exit_container(msg); | ||
58 | } | ||
59 | *dest = pixmaps; | ||
60 | |||
61 | return ret; | ||
62 | error: | ||
63 | list_free_items_and_destroy(pixmaps); | ||
64 | return ret; | ||
65 | } | ||
66 | |||
67 | struct get_property_data { | ||
68 | struct swaybar_sni *sni; | ||
69 | const char *prop; | ||
70 | const char *type; | ||
71 | void *dest; | ||
72 | }; | ||
73 | |||
74 | static int get_property_callback(sd_bus_message *msg, void *data, | ||
75 | sd_bus_error *error) { | ||
76 | struct get_property_data *d = data; | ||
77 | struct swaybar_sni *sni = d->sni; | ||
78 | const char *prop = d->prop; | ||
79 | const char *type = d->type; | ||
80 | void *dest = d->dest; | ||
81 | |||
82 | int ret; | ||
83 | if (sd_bus_message_is_method_error(msg, NULL)) { | ||
84 | sd_bus_error err = *sd_bus_message_get_error(msg); | ||
85 | wlr_log(WLR_DEBUG, "Failed to get property %s: %s", prop, err.message); | ||
86 | ret = -sd_bus_error_get_errno(&err); | ||
87 | goto cleanup; | ||
88 | } | ||
89 | |||
90 | ret = sd_bus_message_enter_container(msg, 'v', type); | ||
91 | if (ret < 0) { | ||
92 | wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, strerror(-ret)); | ||
93 | goto cleanup; | ||
94 | } | ||
95 | |||
96 | if (!type) { | ||
97 | ret = read_pixmap(msg, sni, prop, dest); | ||
98 | if (ret < 0) { | ||
99 | goto cleanup; | ||
100 | } | ||
101 | } else { | ||
102 | ret = sd_bus_message_read(msg, type, dest); | ||
103 | if (ret < 0) { | ||
104 | wlr_log(WLR_DEBUG, "Failed to read property %s: %s", prop, | ||
105 | strerror(-ret)); | ||
106 | goto cleanup; | ||
107 | } else if (*type == 's' || *type == 'o') { | ||
108 | char **str = dest; | ||
109 | *str = strdup(*str); | ||
110 | } | ||
111 | } | ||
112 | cleanup: | ||
113 | free(data); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | static void sni_get_property_async(struct swaybar_sni *sni, const char *prop, | ||
118 | const char *type, void *dest) { | ||
119 | struct get_property_data *data = malloc(sizeof(struct get_property_data)); | ||
120 | data->sni = sni; | ||
121 | data->prop = prop; | ||
122 | data->type = type; | ||
123 | data->dest = dest; | ||
124 | int ret = sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, | ||
125 | sni->path, "org.freedesktop.DBus.Properties", "Get", | ||
126 | get_property_callback, data, "ss", sni->interface, prop); | ||
127 | if (ret < 0) { | ||
128 | wlr_log(WLR_DEBUG, "Failed to get property %s: %s", prop, strerror(-ret)); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | static int handle_new_icon(sd_bus_message *msg, void *data, sd_bus_error *error) { | ||
133 | struct swaybar_sni *sni = data; | ||
134 | wlr_log(WLR_DEBUG, "%s has new IconName", sni->watcher_id); | ||
135 | |||
136 | free(sni->icon_name); | ||
137 | sni->icon_name = NULL; | ||
138 | sni_get_property_async(sni, "IconName", "s", &sni->icon_name); | ||
139 | |||
140 | list_free_items_and_destroy(sni->icon_pixmap); | ||
141 | sni->icon_pixmap = NULL; | ||
142 | sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap); | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static int handle_new_attention_icon(sd_bus_message *msg, void *data, | ||
148 | sd_bus_error *error) { | ||
149 | struct swaybar_sni *sni = data; | ||
150 | wlr_log(WLR_DEBUG, "%s has new AttentionIconName", sni->watcher_id); | ||
151 | |||
152 | free(sni->attention_icon_name); | ||
153 | sni->attention_icon_name = NULL; | ||
154 | sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name); | ||
155 | |||
156 | list_free_items_and_destroy(sni->attention_icon_pixmap); | ||
157 | sni->attention_icon_pixmap = NULL; | ||
158 | sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *error) { | ||
164 | char *status; | ||
165 | int ret = sd_bus_message_read(msg, "s", &status); | ||
166 | if (ret < 0) { | ||
167 | wlr_log(WLR_DEBUG, "Failed to read new status message: %s", strerror(-ret)); | ||
168 | } else { | ||
169 | struct swaybar_sni *sni = data; | ||
170 | free(sni->status); | ||
171 | sni->status = strdup(status); | ||
172 | wlr_log(WLR_DEBUG, "%s has new Status '%s'", sni->watcher_id, status); | ||
173 | } | ||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | static void sni_match_signal(struct swaybar_sni *sni, char *signal, | ||
178 | sd_bus_message_handler_t callback) { | ||
179 | int ret = sd_bus_match_signal(sni->tray->bus, NULL, sni->service, sni->path, | ||
180 | sni->interface, signal, callback, sni); | ||
181 | if (ret < 0) { | ||
182 | wlr_log(WLR_DEBUG, "Failed to subscribe to signal %s: %s", signal, | ||
183 | strerror(-ret)); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) { | ||
188 | struct swaybar_sni *sni = calloc(1, sizeof(struct swaybar_sni)); | ||
189 | if (!sni) { | ||
190 | return NULL; | ||
191 | } | ||
192 | sni->tray = tray; | ||
193 | sni->watcher_id = strdup(id); | ||
194 | char *path_ptr = strchr(id, '/'); | ||
195 | if (!path_ptr) { | ||
196 | sni->service = strdup(id); | ||
197 | sni->path = strdup("/StatusNotifierItem"); | ||
198 | sni->interface = "org.freedesktop.StatusNotifierItem"; | ||
199 | } else { | ||
200 | sni->service = strndup(id, path_ptr - id); | ||
201 | sni->path = strdup(path_ptr); | ||
202 | sni->interface = "org.kde.StatusNotifierItem"; | ||
203 | } | ||
204 | |||
205 | // Ignored: Category, Id, Title, WindowId, OverlayIconName, | ||
206 | // OverlayIconPixmap, AttentionMovieName, ToolTip | ||
207 | sni_get_property_async(sni, "Status", "s", &sni->status); | ||
208 | sni_get_property_async(sni, "IconName", "s", &sni->icon_name); | ||
209 | sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap); | ||
210 | sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name); | ||
211 | sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap); | ||
212 | sni_get_property_async(sni, "ItemIsMenu", "b", &sni->item_is_menu); | ||
213 | sni_get_property_async(sni, "Menu", "o", &sni->menu); | ||
214 | |||
215 | sni_match_signal(sni, "NewIcon", handle_new_icon); | ||
216 | sni_match_signal(sni, "NewAttentionIcon", handle_new_attention_icon); | ||
217 | sni_match_signal(sni, "NewStatus", handle_new_status); | ||
218 | |||
219 | return sni; | ||
220 | } | ||
221 | |||
222 | void destroy_sni(struct swaybar_sni *sni) { | ||
223 | if (!sni) { | ||
224 | return; | ||
225 | } | ||
226 | |||
227 | free(sni->watcher_id); | ||
228 | free(sni->service); | ||
229 | free(sni->path); | ||
230 | free(sni->status); | ||
231 | free(sni->icon_name); | ||
232 | free(sni->icon_pixmap); | ||
233 | free(sni->attention_icon_name); | ||
234 | free(sni->menu); | ||
235 | free(sni); | ||
236 | } | ||
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index e760812c..4ef28a78 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "swaybar/bar.h" | 5 | #include "swaybar/bar.h" |
6 | #include "swaybar/tray/icon.h" | 6 | #include "swaybar/tray/icon.h" |
7 | #include "swaybar/tray/host.h" | 7 | #include "swaybar/tray/host.h" |
8 | #include "swaybar/tray/item.h" | ||
8 | #include "swaybar/tray/tray.h" | 9 | #include "swaybar/tray/tray.h" |
9 | #include "swaybar/tray/watcher.h" | 10 | #include "swaybar/tray/watcher.h" |
10 | #include "list.h" | 11 | #include "list.h" |
@@ -48,7 +49,7 @@ void destroy_tray(struct swaybar_tray *tray) { | |||
48 | finish_host(&tray->host_xdg); | 49 | finish_host(&tray->host_xdg); |
49 | finish_host(&tray->host_kde); | 50 | finish_host(&tray->host_kde); |
50 | for (int i = 0; i < tray->items->length; ++i) { | 51 | for (int i = 0; i < tray->items->length; ++i) { |
51 | free(tray->items->items[0]); | 52 | destroy_sni(tray->items->items[0]); |
52 | } | 53 | } |
53 | list_free(tray->items); | 54 | list_free(tray->items); |
54 | destroy_watcher(tray->watcher_xdg); | 55 | destroy_watcher(tray->watcher_xdg); |