diff options
Diffstat (limited to 'swaybar/tray/sni.c')
-rw-r--r-- | swaybar/tray/sni.c | 254 |
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 | ||
17 | static const char *KDE_IFACE = "org.kde.StatusNotifierItem"; | ||
18 | static 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. |
18 | static const cairo_user_data_key_t cairo_user_data_key; | 21 | static 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 */ |
41 | static void reply_icon(DBusPendingCall *pending, void *_data) { | 44 | static 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 | ||
170 | bail: | ||
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 | } |
178 | static 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 */ |
212 | static void reply_icon_name(DBusPendingCall *pending, void *_data) { | 139 | static 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 | ||
275 | bail: | ||
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); |
284 | static 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 | ||
317 | void get_icon(struct StatusNotifierItem *item) { | 173 | void 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 | ||
321 | void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) { | 179 | void 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 | } |
313 | struct 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` */ |
453 | int sni_str_cmp(const void *_item, const void *_str) { | 329 | int 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 | } |
345 | int 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 | |||
469 | void sni_free(struct StatusNotifierItem *item) { | 356 | void 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 | } |