summaryrefslogtreecommitdiffstats
path: root/swaybar
diff options
context:
space:
mode:
authorLibravatar Ian Fan <ianfan0@gmail.com>2018-12-07 12:33:45 +0000
committerLibravatar Ian Fan <ianfan0@gmail.com>2018-12-31 20:40:18 +0000
commit74655f835aa9fe0e976473d443f62d253602696c (patch)
tree238835505b049fc4b0479f0fa25e2c30deb0adb6 /swaybar
parentswaybar: add StatusNotifierHost to tray (diff)
downloadsway-74655f835aa9fe0e976473d443f62d253602696c.tar.gz
sway-74655f835aa9fe0e976473d443f62d253602696c.tar.zst
sway-74655f835aa9fe0e976473d443f62d253602696c.zip
swaybar: add StatusNotifierItem to tray
Diffstat (limited to 'swaybar')
-rw-r--r--swaybar/meson.build1
-rw-r--r--swaybar/tray/host.c9
-rw-r--r--swaybar/tray/item.c236
-rw-r--r--swaybar/tray/tray.c3
4 files changed, 244 insertions, 5 deletions
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 @@
1tray_files = get_option('enable-tray') ? [ 1tray_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 @@
12static const char *watcher_path = "/StatusNotifierWatcher"; 13static const char *watcher_path = "/StatusNotifierWatcher";
13 14
14static int cmp_sni_id(const void *item, const void *cmp_to) { 15static 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
19static void add_sni(struct swaybar_tray *tray, char *id) { 20static 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
12static 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;
62error:
63 list_free_items_and_destroy(pixmaps);
64 return ret;
65}
66
67struct get_property_data {
68 struct swaybar_sni *sni;
69 const char *prop;
70 const char *type;
71 void *dest;
72};
73
74static 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 }
112cleanup:
113 free(data);
114 return ret;
115}
116
117static 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
132static 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
147static 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
163static 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
177static 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
187struct 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
222void 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);