aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/tray/sni.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/tray/sni.c')
-rw-r--r--swaybar/tray/sni.c254
1 files changed, 70 insertions, 184 deletions
diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c
index c9d00657..be7d9fd7 100644
--- a/swaybar/tray/sni.c
+++ b/swaybar/tray/sni.c
@@ -14,6 +14,9 @@
14#include "client/cairo.h" 14#include "client/cairo.h"
15#include "log.h" 15#include "log.h"
16 16
17static const char *KDE_IFACE = "org.kde.StatusNotifierItem";
18static const char *FD_IFACE = "org.freedesktop.StatusNotifierItem";
19
17// Not sure what this is but cairo needs it. 20// Not sure what this is but cairo needs it.
18static const cairo_user_data_key_t cairo_user_data_key; 21static const cairo_user_data_key_t cairo_user_data_key;
19 22
@@ -38,90 +41,53 @@ void sni_icon_ref_free(struct sni_icon_ref *sni_ref) {
38} 41}
39 42
40/* Gets the pixmap of an icon */ 43/* Gets the pixmap of an icon */
41static void reply_icon(DBusPendingCall *pending, void *_data) { 44static void reply_icon(DBusMessageIter *iter /* a(iiay) */, void *_data, enum property_status status) {
42 struct StatusNotifierItem *item = _data; 45 if (status != PROP_EXISTS) {
43 46 return;
44 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
45
46 if (!reply) {
47 sway_log(L_ERROR, "Did not get reply");
48 goto bail;
49 }
50
51 int message_type = dbus_message_get_type(reply);
52
53 if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
54 char *msg;
55
56 dbus_message_get_args(reply, NULL,
57 DBUS_TYPE_STRING, &msg,
58 DBUS_TYPE_INVALID);
59
60 sway_log(L_ERROR, "Message is error: %s", msg);
61 goto bail;
62 } 47 }
48 struct StatusNotifierItem *item = _data;
63 49
64 DBusMessageIter iter;
65 DBusMessageIter variant; /* v[a(iiay)] */
66 DBusMessageIter array; /* a(iiay) */
67 DBusMessageIter d_struct; /* (iiay) */ 50 DBusMessageIter d_struct; /* (iiay) */
68 DBusMessageIter icon; /* ay */ 51 DBusMessageIter struct_items;
69 52 DBusMessageIter icon;
70 dbus_message_iter_init(reply, &iter);
71
72 // Each if here checks the types above before recursing
73 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
74 sway_log(L_ERROR, "Relpy type incorrect");
75 sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
76 dbus_message_iter_get_signature(&iter));
77 goto bail;
78 }
79 dbus_message_iter_recurse(&iter, &variant);
80
81 if (strcmp("a(iiay)", dbus_message_iter_get_signature(&variant)) != 0) {
82 sway_log(L_ERROR, "Relpy type incorrect");
83 sway_log(L_ERROR, "Should be \"a(iiay)\", is \"%s\"",
84 dbus_message_iter_get_signature(&variant));
85 goto bail;
86 }
87 53
88 if (dbus_message_iter_get_element_count(&variant) == 0) { 54 if (dbus_message_iter_get_element_count(iter) == 0) {
89 // Can't recurse if there are no items 55 // Can't recurse if there are no items
90 sway_log(L_INFO, "Item has no icon"); 56 sway_log(L_INFO, "Item has no icon");
91 goto bail; 57 return;
92 } 58 }
93 dbus_message_iter_recurse(&variant, &array);
94 59
95 dbus_message_iter_recurse(&array, &d_struct); 60 dbus_message_iter_recurse(iter, &d_struct);
61 dbus_message_iter_recurse(&d_struct, &struct_items);
96 62
97 int width; 63 int width;
98 dbus_message_iter_get_basic(&d_struct, &width); 64 dbus_message_iter_get_basic(&struct_items, &width);
99 dbus_message_iter_next(&d_struct); 65 dbus_message_iter_next(&struct_items);
100 66
101 int height; 67 int height;
102 dbus_message_iter_get_basic(&d_struct, &height); 68 dbus_message_iter_get_basic(&struct_items, &height);
103 dbus_message_iter_next(&d_struct); 69 dbus_message_iter_next(&struct_items);
104 70
105 int len = dbus_message_iter_get_element_count(&d_struct); 71 int len = dbus_message_iter_get_element_count(&struct_items);
106 72
107 if (!len) { 73 if (!len) {
108 sway_log(L_ERROR, "No icon data"); 74 sway_log(L_ERROR, "No icon data");
109 goto bail; 75 return;
110 } 76 }
111 77
112 // Also implies len % 4 == 0, useful below 78 // Also implies len % 4 == 0, useful below
113 if (len != width * height * 4) { 79 if (len != width * height * 4) {
114 sway_log(L_ERROR, "Incorrect array size passed"); 80 sway_log(L_ERROR, "Incorrect array size passed");
115 goto bail; 81 return;
116 } 82 }
117 83
118 dbus_message_iter_recurse(&d_struct, &icon); 84 dbus_message_iter_recurse(&struct_items, &icon);
119 85
120 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); 86 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
121 // FIXME support a variable stride 87 // FIXME support a variable stride
122 // (works on my machine though for all tested widths) 88 // (works on my machine though for all tested widths)
123 if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) { 89 if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
124 goto bail; 90 return;
125 } 91 }
126 92
127 // Data is by reference, no need to free 93 // Data is by reference, no need to free
@@ -131,7 +97,7 @@ static void reply_icon(DBusPendingCall *pending, void *_data) {
131 uint8_t *image_data = malloc(stride * height); 97 uint8_t *image_data = malloc(stride * height);
132 if (!image_data) { 98 if (!image_data) {
133 sway_log(L_ERROR, "Could not allocate memory for icon"); 99 sway_log(L_ERROR, "Could not allocate memory for icon");
134 goto bail; 100 return;
135 } 101 }
136 102
137 // Transform from network byte order to host byte order 103 // Transform from network byte order to host byte order
@@ -159,101 +125,29 @@ static void reply_icon(DBusPendingCall *pending, void *_data) {
159 item->dirty = true; 125 item->dirty = true;
160 dirty = true; 126 dirty = true;
161 127
162 dbus_message_unref(reply);
163 dbus_pending_call_unref(pending);
164 return; 128 return;
165 } else { 129 } else {
166 sway_log(L_ERROR, "Could not create image surface"); 130 sway_log(L_ERROR, "Could not create image surface");
167 free(image_data); 131 free(image_data);
168 } 132 }
169 133
170bail:
171 if (reply) {
172 dbus_message_unref(reply);
173 }
174 dbus_pending_call_unref(pending);
175 sway_log(L_ERROR, "Could not get icon from item"); 134 sway_log(L_ERROR, "Could not get icon from item");
176 return; 135 return;
177} 136}
178static void send_icon_msg(struct StatusNotifierItem *item) {
179 DBusPendingCall *pending;
180 DBusMessage *message = dbus_message_new_method_call(
181 item->name,
182 "/StatusNotifierItem",
183 "org.freedesktop.DBus.Properties",
184 "Get");
185 const char *iface;
186 if (item->kde_special_snowflake) {
187 iface = "org.kde.StatusNotifierItem";
188 } else {
189 iface = "org.freedesktop.StatusNotifierItem";
190 }
191 const char *prop = "IconPixmap";
192
193 dbus_message_append_args(message,
194 DBUS_TYPE_STRING, &iface,
195 DBUS_TYPE_STRING, &prop,
196 DBUS_TYPE_INVALID);
197
198 bool status =
199 dbus_connection_send_with_reply(conn, message, &pending, -1);
200
201 dbus_message_unref(message);
202
203 if (!(pending || status)) {
204 sway_log(L_ERROR, "Could not get item icon");
205 return;
206 }
207
208 dbus_pending_call_set_notify(pending, reply_icon, item, NULL);
209}
210 137
211/* Get an icon by its name */ 138/* Get an icon by its name */
212static void reply_icon_name(DBusPendingCall *pending, void *_data) { 139static void reply_icon_name(DBusMessageIter *iter, void *_data, enum property_status status) {
213 struct StatusNotifierItem *item = _data; 140 struct StatusNotifierItem *item = _data;
214 141
215 DBusMessage *reply = dbus_pending_call_steal_reply(pending); 142 if (status != PROP_EXISTS) {
216 143 dbus_get_prop_async(item->name, item->object_path,
217 if (!reply) { 144 (item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
218 sway_log(L_INFO, "Got no icon name reply from item"); 145 "IconPixmap", "a(iiay)", reply_icon, item);
219 goto bail; 146 return;
220 }
221
222 int message_type = dbus_message_get_type(reply);
223
224 if (message_type == DBUS_MESSAGE_TYPE_ERROR) {
225 char *msg;
226
227 dbus_message_get_args(reply, NULL,
228 DBUS_TYPE_STRING, &msg,
229 DBUS_TYPE_INVALID);
230
231 sway_log(L_INFO, "Could not get icon name: %s", msg);
232 goto bail;
233 }
234
235 DBusMessageIter iter; /* v[s] */
236 DBusMessageIter variant; /* s */
237
238 dbus_message_iter_init(reply, &iter);
239 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
240 sway_log(L_ERROR, "Relpy type incorrect");
241 sway_log(L_ERROR, "Should be \"v\", is \"%s\"",
242 dbus_message_iter_get_signature(&iter));
243 goto bail;
244 }
245 dbus_message_iter_recurse(&iter, &variant);
246
247
248 if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_STRING) {
249 sway_log(L_ERROR, "Relpy type incorrect");
250 sway_log(L_ERROR, "Should be \"s\", is \"%s\"",
251 dbus_message_iter_get_signature(&iter));
252 goto bail;
253 } 147 }
254 148
255 char *icon_name; 149 char *icon_name;
256 dbus_message_iter_get_basic(&variant, &icon_name); 150 dbus_message_iter_get_basic(iter, &icon_name);
257 151
258 cairo_surface_t *image = find_icon(icon_name, 256); 152 cairo_surface_t *image = find_icon(icon_name, 256);
259 153
@@ -267,55 +161,19 @@ static void reply_icon_name(DBusPendingCall *pending, void *_data) {
267 item->dirty = true; 161 item->dirty = true;
268 dirty = true; 162 dirty = true;
269 163
270 dbus_message_unref(reply);
271 dbus_pending_call_unref(pending);
272 return; 164 return;
273 } 165 }
274 166
275bail:
276 if (reply) {
277 dbus_message_unref(reply);
278 }
279 dbus_pending_call_unref(pending);
280 // Now try the pixmap 167 // Now try the pixmap
281 send_icon_msg(item); 168 dbus_get_prop_async(item->name, item->object_path,
282 return; 169 (item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
283} 170 "IconPixmap", "a(iiay)", reply_icon, item);
284static void send_icon_name_msg(struct StatusNotifierItem *item) {
285 DBusPendingCall *pending;
286 DBusMessage *message = dbus_message_new_method_call(
287 item->name,
288 "/StatusNotifierItem",
289 "org.freedesktop.DBus.Properties",
290 "Get");
291 const char *iface;
292 if (item->kde_special_snowflake) {
293 iface = "org.kde.StatusNotifierItem";
294 } else {
295 iface = "org.freedesktop.StatusNotifierItem";
296 }
297 const char *prop = "IconName";
298
299 dbus_message_append_args(message,
300 DBUS_TYPE_STRING, &iface,
301 DBUS_TYPE_STRING, &prop,
302 DBUS_TYPE_INVALID);
303
304 bool status =
305 dbus_connection_send_with_reply(conn, message, &pending, -1);
306
307 dbus_message_unref(message);
308
309 if (!(pending || status)) {
310 sway_log(L_ERROR, "Could not get item icon name");
311 return;
312 }
313
314 dbus_pending_call_set_notify(pending, reply_icon_name, item, NULL);
315} 171}
316 172
317void get_icon(struct StatusNotifierItem *item) { 173void get_icon(struct StatusNotifierItem *item) {
318 send_icon_name_msg(item); 174 dbus_get_prop_async(item->name, item->object_path,
175 (item->kde_special_snowflake ? KDE_IFACE : FD_IFACE),
176 "IconName", "s", reply_icon_name, item);
319} 177}
320 178
321void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { 179void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
@@ -324,7 +182,7 @@ void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
324 : "org.freedesktop.StatusNotifierItem"); 182 : "org.freedesktop.StatusNotifierItem");
325 DBusMessage *message = dbus_message_new_method_call( 183 DBusMessage *message = dbus_message_new_method_call(
326 item->name, 184 item->name,
327 "/StatusNotifierItem", 185 item->object_path,
328 iface, 186 iface,
329 "Activate"); 187 "Activate");
330 188
@@ -342,9 +200,10 @@ void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
342 const char *iface = 200 const char *iface =
343 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem" 201 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
344 : "org.freedesktop.StatusNotifierItem"); 202 : "org.freedesktop.StatusNotifierItem");
203 sway_log(L_INFO, "Activating context menu for item: (%s,%s)", item->name, item->object_path);
345 DBusMessage *message = dbus_message_new_method_call( 204 DBusMessage *message = dbus_message_new_method_call(
346 item->name, 205 item->name,
347 "/StatusNotifierItem", 206 item->object_path,
348 iface, 207 iface,
349 "ContextMenu"); 208 "ContextMenu");
350 209
@@ -363,7 +222,7 @@ void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
363 : "org.freedesktop.StatusNotifierItem"); 222 : "org.freedesktop.StatusNotifierItem");
364 DBusMessage *message = dbus_message_new_method_call( 223 DBusMessage *message = dbus_message_new_method_call(
365 item->name, 224 item->name,
366 "/StatusNotifierItem", 225 item->object_path,
367 iface, 226 iface,
368 "SecondaryActivate"); 227 "SecondaryActivate");
369 228
@@ -426,6 +285,8 @@ struct StatusNotifierItem *sni_create(const char *name) {
426 struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem)); 285 struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
427 item->name = strdup(name); 286 item->name = strdup(name);
428 item->unique_name = NULL; 287 item->unique_name = NULL;
288 // TODO use static str if the default path instead of all these god-damn strdups
289 item->object_path = strdup("/StatusNotifierItem");
429 item->image = NULL; 290 item->image = NULL;
430 item->dirty = false; 291 item->dirty = false;
431 292
@@ -449,6 +310,21 @@ struct StatusNotifierItem *sni_create(const char *name) {
449 310
450 return item; 311 return item;
451} 312}
313struct StatusNotifierItem *sni_create_from_obj_path(const char *unique_name,
314 const char *object_path) {
315 struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
316 // XXX strdup-ing twice to avoid a double-free; see above todo
317 item->name = strdup(unique_name);
318 item->unique_name = strdup(unique_name);
319 item->object_path = strdup(object_path);
320 item->image = NULL;
321 item->dirty = false;
322 // If they're registering by obj-path they're a special snowflake
323 item->kde_special_snowflake = true;
324
325 get_icon(item);
326 return item;
327}
452/* Return 0 if `item` has a name of `str` */ 328/* Return 0 if `item` has a name of `str` */
453int sni_str_cmp(const void *_item, const void *_str) { 329int sni_str_cmp(const void *_item, const void *_str) {
454 const struct StatusNotifierItem *item = _item; 330 const struct StatusNotifierItem *item = _item;
@@ -466,14 +342,24 @@ int sni_uniq_cmp(const void *_item, const void *_str) {
466 } 342 }
467 return strcmp(item->unique_name, str); 343 return strcmp(item->unique_name, str);
468} 344}
345int sni_obj_name_cmp(const void *_item, const void *_obj_name) {
346 const struct StatusNotifierItem *item = _item;
347 const struct ObjName *obj_name = _obj_name;
348
349 if (strcmp(item->unique_name, obj_name->name) == 0 &&
350 strcmp(item->object_path, obj_name->obj_path) == 0) {
351 return 0;
352 }
353 return 1;
354}
355
469void sni_free(struct StatusNotifierItem *item) { 356void sni_free(struct StatusNotifierItem *item) {
470 if (!item) { 357 if (!item) {
471 return; 358 return;
472 } 359 }
473 free(item->name); 360 free(item->name);
474 if (item->unique_name) { 361 free(item->unique_name);
475 free(item->unique_name); 362 free(item->object_path);
476 }
477 if (item->image) { 363 if (item->image) {
478 cairo_surface_destroy(item->image); 364 cairo_surface_destroy(item->image);
479 } 365 }