summaryrefslogtreecommitdiffstats
path: root/swaybar/tray/sni.c
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-03-28 23:04:20 -0400
committerLibravatar Drew DeVault <sir@cmpwn.com>2018-03-29 22:11:08 -0400
commitcab1352801b62d1b8a12ca1c995cb24445ce4bc9 (patch)
treebc67373916c06d48700c4f69b8c2470a2f86887f /swaybar/tray/sni.c
parentAllow sway IPC clients to fall back to i3 socket (diff)
downloadsway-cab1352801b62d1b8a12ca1c995cb24445ce4bc9.tar.gz
sway-cab1352801b62d1b8a12ca1c995cb24445ce4bc9.tar.zst
sway-cab1352801b62d1b8a12ca1c995cb24445ce4bc9.zip
Start port of swaybar to layer shell
This starts up the event loop and wayland display and shims out the basic top level rendering concepts. Also includes some changes to incorporate pango into the 1.x codebase properly.
Diffstat (limited to 'swaybar/tray/sni.c')
-rw-r--r--swaybar/tray/sni.c481
1 files changed, 0 insertions, 481 deletions
diff --git a/swaybar/tray/sni.c b/swaybar/tray/sni.c
deleted file mode 100644
index c9d00657..00000000
--- a/swaybar/tray/sni.c
+++ /dev/null
@@ -1,481 +0,0 @@
1#define _XOPEN_SOURCE 500
2#include <stdlib.h>
3#include <stdio.h>
4#include <string.h>
5#include <stdint.h>
6#include <stdbool.h>
7#include <dbus/dbus.h>
8#include <arpa/inet.h>
9#include <netinet/in.h>
10#include "swaybar/tray/dbus.h"
11#include "swaybar/tray/sni.h"
12#include "swaybar/tray/icon.h"
13#include "swaybar/bar.h"
14#include "client/cairo.h"
15#include "log.h"
16
17// Not sure what this is but cairo needs it.
18static const cairo_user_data_key_t cairo_user_data_key;
19
20struct sni_icon_ref *sni_icon_ref_create(struct StatusNotifierItem *item,
21 int height) {
22 struct sni_icon_ref *sni_ref = malloc(sizeof(struct sni_icon_ref));
23 if (!sni_ref) {
24 return NULL;
25 }
26 sni_ref->icon = cairo_image_surface_scale(item->image, height, height);
27 sni_ref->ref = item;
28
29 return sni_ref;
30}
31
32void sni_icon_ref_free(struct sni_icon_ref *sni_ref) {
33 if (!sni_ref) {
34 return;
35 }
36 cairo_surface_destroy(sni_ref->icon);
37 free(sni_ref);
38}
39
40/* Gets the pixmap of an icon */
41static void reply_icon(DBusPendingCall *pending, void *_data) {
42 struct StatusNotifierItem *item = _data;
43
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 }
63
64 DBusMessageIter iter;
65 DBusMessageIter variant; /* v[a(iiay)] */
66 DBusMessageIter array; /* a(iiay) */
67 DBusMessageIter d_struct; /* (iiay) */
68 DBusMessageIter icon; /* ay */
69
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
88 if (dbus_message_iter_get_element_count(&variant) == 0) {
89 // Can't recurse if there are no items
90 sway_log(L_INFO, "Item has no icon");
91 goto bail;
92 }
93 dbus_message_iter_recurse(&variant, &array);
94
95 dbus_message_iter_recurse(&array, &d_struct);
96
97 int width;
98 dbus_message_iter_get_basic(&d_struct, &width);
99 dbus_message_iter_next(&d_struct);
100
101 int height;
102 dbus_message_iter_get_basic(&d_struct, &height);
103 dbus_message_iter_next(&d_struct);
104
105 int len = dbus_message_iter_get_element_count(&d_struct);
106
107 if (!len) {
108 sway_log(L_ERROR, "No icon data");
109 goto bail;
110 }
111
112 // Also implies len % 4 == 0, useful below
113 if (len != width * height * 4) {
114 sway_log(L_ERROR, "Incorrect array size passed");
115 goto bail;
116 }
117
118 dbus_message_iter_recurse(&d_struct, &icon);
119
120 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
121 // FIXME support a variable stride
122 // (works on my machine though for all tested widths)
123 if (!sway_assert(stride == width * 4, "Stride must be equal to byte length")) {
124 goto bail;
125 }
126
127 // Data is by reference, no need to free
128 uint8_t *message_data;
129 dbus_message_iter_get_fixed_array(&icon, &message_data, &len);
130
131 uint8_t *image_data = malloc(stride * height);
132 if (!image_data) {
133 sway_log(L_ERROR, "Could not allocate memory for icon");
134 goto bail;
135 }
136
137 // Transform from network byte order to host byte order
138 // Assumptions are safe because the equality above
139 uint32_t *network = (uint32_t *) message_data;
140 uint32_t *host = (uint32_t *)image_data;
141 for (int i = 0; i < width * height; ++i) {
142 host[i] = ntohl(network[i]);
143 }
144
145 cairo_surface_t *image = cairo_image_surface_create_for_data(
146 image_data, CAIRO_FORMAT_ARGB32,
147 width, height, stride);
148
149 if (image) {
150 if (item->image) {
151 cairo_surface_destroy(item->image);
152 }
153 item->image = image;
154 // Free the image data on surface destruction
155 cairo_surface_set_user_data(image,
156 &cairo_user_data_key,
157 image_data,
158 free);
159 item->dirty = true;
160 dirty = true;
161
162 dbus_message_unref(reply);
163 dbus_pending_call_unref(pending);
164 return;
165 } else {
166 sway_log(L_ERROR, "Could not create image surface");
167 free(image_data);
168 }
169
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");
176 return;
177}
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
211/* Get an icon by its name */
212static void reply_icon_name(DBusPendingCall *pending, void *_data) {
213 struct StatusNotifierItem *item = _data;
214
215 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
216
217 if (!reply) {
218 sway_log(L_INFO, "Got no icon name reply from item");
219 goto bail;
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 }
254
255 char *icon_name;
256 dbus_message_iter_get_basic(&variant, &icon_name);
257
258 cairo_surface_t *image = find_icon(icon_name, 256);
259
260 if (image) {
261 sway_log(L_DEBUG, "Icon for %s found with size %d", icon_name,
262 cairo_image_surface_get_width(image));
263 if (item->image) {
264 cairo_surface_destroy(item->image);
265 }
266 item->image = image;
267 item->dirty = true;
268 dirty = true;
269
270 dbus_message_unref(reply);
271 dbus_pending_call_unref(pending);
272 return;
273 }
274
275bail:
276 if (reply) {
277 dbus_message_unref(reply);
278 }
279 dbus_pending_call_unref(pending);
280 // Now try the pixmap
281 send_icon_msg(item);
282 return;
283}
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}
316
317void get_icon(struct StatusNotifierItem *item) {
318 send_icon_name_msg(item);
319}
320
321void sni_activate(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
322 const char *iface =
323 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
324 : "org.freedesktop.StatusNotifierItem");
325 DBusMessage *message = dbus_message_new_method_call(
326 item->name,
327 "/StatusNotifierItem",
328 iface,
329 "Activate");
330
331 dbus_message_append_args(message,
332 DBUS_TYPE_INT32, &x,
333 DBUS_TYPE_INT32, &y,
334 DBUS_TYPE_INVALID);
335
336 dbus_connection_send(conn, message, NULL);
337
338 dbus_message_unref(message);
339}
340
341void sni_context_menu(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
342 const char *iface =
343 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
344 : "org.freedesktop.StatusNotifierItem");
345 DBusMessage *message = dbus_message_new_method_call(
346 item->name,
347 "/StatusNotifierItem",
348 iface,
349 "ContextMenu");
350
351 dbus_message_append_args(message,
352 DBUS_TYPE_INT32, &x,
353 DBUS_TYPE_INT32, &y,
354 DBUS_TYPE_INVALID);
355
356 dbus_connection_send(conn, message, NULL);
357
358 dbus_message_unref(message);
359}
360void sni_secondary(struct StatusNotifierItem *item, uint32_t x, uint32_t y) {
361 const char *iface =
362 (item->kde_special_snowflake ? "org.kde.StatusNotifierItem"
363 : "org.freedesktop.StatusNotifierItem");
364 DBusMessage *message = dbus_message_new_method_call(
365 item->name,
366 "/StatusNotifierItem",
367 iface,
368 "SecondaryActivate");
369
370 dbus_message_append_args(message,
371 DBUS_TYPE_INT32, &x,
372 DBUS_TYPE_INT32, &y,
373 DBUS_TYPE_INVALID);
374
375 dbus_connection_send(conn, message, NULL);
376
377 dbus_message_unref(message);
378}
379
380static void get_unique_name(struct StatusNotifierItem *item) {
381 // I think that we're fine being sync here becaues the message is
382 // directly to the message bus. Could be async though.
383 DBusMessage *message = dbus_message_new_method_call(
384 "org.freedesktop.DBus",
385 "/org/freedesktop/DBus",
386 "org.freedesktop.DBus",
387 "GetNameOwner");
388
389 dbus_message_append_args(message,
390 DBUS_TYPE_STRING, &item->name,
391 DBUS_TYPE_INVALID);
392
393 DBusMessage *reply = dbus_connection_send_with_reply_and_block(
394 conn, message, -1, NULL);
395
396 dbus_message_unref(message);
397
398 if (!reply) {
399 sway_log(L_ERROR, "Could not get unique name for item: %s",
400 item->name);
401 return;
402 }
403
404 char *unique_name;
405 if (!dbus_message_get_args(reply, NULL,
406 DBUS_TYPE_STRING, &unique_name,
407 DBUS_TYPE_INVALID)) {
408 sway_log(L_ERROR, "Error parsing method args");
409 } else {
410 if (item->unique_name) {
411 free(item->unique_name);
412 }
413 item->unique_name = strdup(unique_name);
414 }
415
416 dbus_message_unref(reply);
417}
418
419struct StatusNotifierItem *sni_create(const char *name) {
420 // Make sure `name` is well formed
421 if (!dbus_validate_bus_name(name, NULL)) {
422 sway_log(L_INFO, "Name (%s) is not a bus name. We cannot create an item.", name);
423 return NULL;
424 }
425
426 struct StatusNotifierItem *item = malloc(sizeof(struct StatusNotifierItem));
427 item->name = strdup(name);
428 item->unique_name = NULL;
429 item->image = NULL;
430 item->dirty = false;
431
432 // If it doesn't use this name then assume that it uses the KDE spec
433 // This is because xembed-sni-proxy uses neither "org.freedesktop" nor
434 // "org.kde" and just gives us the items "unique name"
435 //
436 // We could use this to our advantage and fill out the "unique name"
437 // field with the given name if it is neither freedesktop or kde, but
438 // that's makes us rely on KDE hackyness which is bad practice
439 const char freedesktop_name[] = "org.freedesktop";
440 if (strncmp(name, freedesktop_name, sizeof(freedesktop_name) - 1) != 0) {
441 item->kde_special_snowflake = true;
442 } else {
443 item->kde_special_snowflake = false;
444 }
445
446 get_icon(item);
447
448 get_unique_name(item);
449
450 return item;
451}
452/* Return 0 if `item` has a name of `str` */
453int sni_str_cmp(const void *_item, const void *_str) {
454 const struct StatusNotifierItem *item = _item;
455 const char *str = _str;
456
457 return strcmp(item->name, str);
458}
459/* Returns 0 if `item` has a unique name of `str` */
460int sni_uniq_cmp(const void *_item, const void *_str) {
461 const struct StatusNotifierItem *item = _item;
462 const char *str = _str;
463
464 if (!item->unique_name) {
465 return false;
466 }
467 return strcmp(item->unique_name, str);
468}
469void sni_free(struct StatusNotifierItem *item) {
470 if (!item) {
471 return;
472 }
473 free(item->name);
474 if (item->unique_name) {
475 free(item->unique_name);
476 }
477 if (item->image) {
478 cairo_surface_destroy(item->image);
479 }
480 free(item);
481}