summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2017-12-29 14:24:23 -0500
committerLibravatar GitHub <noreply@github.com>2017-12-29 14:24:23 -0500
commit1e87c90923eef75b7d032322dfef38def944c2bb (patch)
treeed17f848e7ac95c919d23424671a8a8ad5651a86
parentMerge pull request #1544 from CedricCabessa/fix1056-swaylock-allow-popup-to-c... (diff)
parentKeep tray separate (diff)
downloadsway-1e87c90923eef75b7d032322dfef38def944c2bb.tar.gz
sway-1e87c90923eef75b7d032322dfef38def944c2bb.tar.zst
sway-1e87c90923eef75b7d032322dfef38def944c2bb.zip
Merge pull request #1431 from 4e554c4c/sni_sucks
Support libappindicator
-rw-r--r--include/swaybar/bar.h4
-rw-r--r--include/swaybar/tray/dbus.h31
-rw-r--r--include/swaybar/tray/sni.h19
-rw-r--r--swaybar/bar.c7
-rw-r--r--swaybar/tray/dbus.c102
-rw-r--r--swaybar/tray/icon.c68
-rw-r--r--swaybar/tray/sni.c254
-rw-r--r--swaybar/tray/sni_watcher.c182
-rw-r--r--swaybar/tray/tray.c211
9 files changed, 590 insertions, 288 deletions
diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h
index 50d36e76..7ec09e3e 100644
--- a/include/swaybar/bar.h
+++ b/include/swaybar/bar.h
@@ -21,6 +21,7 @@ struct bar {
21struct output { 21struct output {
22 struct window *window; 22 struct window *window;
23 struct registry *registry; 23 struct registry *registry;
24 struct output_state *state;
24 list_t *workspaces; 25 list_t *workspaces;
25#ifdef ENABLE_TRAY 26#ifdef ENABLE_TRAY
26 list_t *items; 27 list_t *items;
@@ -28,6 +29,9 @@ struct output {
28 char *name; 29 char *name;
29 int idx; 30 int idx;
30 bool focused; 31 bool focused;
32#ifdef ENABLE_TRAY
33 bool active;
34#endif
31}; 35};
32 36
33struct workspace { 37struct workspace {
diff --git a/include/swaybar/tray/dbus.h b/include/swaybar/tray/dbus.h
index eb9cfea7..c693e6f7 100644
--- a/include/swaybar/tray/dbus.h
+++ b/include/swaybar/tray/dbus.h
@@ -5,6 +5,37 @@
5#include <dbus/dbus.h> 5#include <dbus/dbus.h>
6extern DBusConnection *conn; 6extern DBusConnection *conn;
7 7
8enum property_status {
9 PROP_EXISTS, /* Will give iter */
10 PROP_ERROR, /* Will not give iter */
11 PROP_BAD_DATA, /* Will not give iter */
12 PROP_WRONG_SIG, /* Will give iter, please be careful */
13};
14
15/**
16 * Checks the signature of the given iter against `sig`. Prefer to
17 * `dbus_message_iter_get_signature` as this one frees the intermediate string.
18 */
19bool dbus_message_iter_check_signature(DBusMessageIter *iter, const char *sig);
20
21/**
22 * Fetches the property and calls `callback` with a message iter pointing it.
23 * Performs error handling and signature checking.
24 *
25 * Returns: true if message is successfully sent and false otherwise. If there
26 * is an error getting a property, `callback` will still be run, but with
27 * `status` set to the error.
28 *
29 * NOTE: `expected_signature` must remain valid until the message reply is
30 * received, please only use 'static signatures.
31 */
32bool dbus_get_prop_async(const char *destination,
33 const char *path,
34 const char *iface,
35 const char *prop,
36 const char *expected_signature,
37 void(*callback)(DBusMessageIter *iter, void *data, enum property_status status),
38 void *data);
8/** 39/**
9 * Should be called in main loop to dispatch events 40 * Should be called in main loop to dispatch events
10 */ 41 */
diff --git a/include/swaybar/tray/sni.h b/include/swaybar/tray/sni.h
index c2544e2a..95c10b9f 100644
--- a/include/swaybar/tray/sni.h
+++ b/include/swaybar/tray/sni.h
@@ -9,6 +9,8 @@ struct StatusNotifierItem {
9 char *name; 9 char *name;
10 /* Unique bus name, needed for determining signal origins */ 10 /* Unique bus name, needed for determining signal origins */
11 char *unique_name; 11 char *unique_name;
12 /* Object path, useful for items not registerd by well known name */
13 char *object_path;
12 bool kde_special_snowflake; 14 bool kde_special_snowflake;
13 15
14 cairo_surface_t *image; 16 cairo_surface_t *image;
@@ -31,6 +33,12 @@ void sni_icon_ref_free(struct sni_icon_ref *sni_ref);
31 * May return `NULL` if `name` is not valid. 33 * May return `NULL` if `name` is not valid.
32 */ 34 */
33struct StatusNotifierItem *sni_create(const char *name); 35struct StatusNotifierItem *sni_create(const char *name);
36/**
37 * Same as sni_create, but takes an object path and unique name instead of
38 * well-known name.
39 */
40struct StatusNotifierItem *sni_create_from_obj_path(const char *unique_name,
41 const char *object_path);
34 42
35/** 43/**
36 * `item` must be a struct StatusNotifierItem * 44 * `item` must be a struct StatusNotifierItem *
@@ -46,6 +54,17 @@ int sni_str_cmp(const void *item, const void *str);
46 */ 54 */
47int sni_uniq_cmp(const void *item, const void *str); 55int sni_uniq_cmp(const void *item, const void *str);
48 56
57
58struct ObjName {
59 const void *obj_path;
60 const void *name;
61};
62/**
63 * Returns 0 if `item` has a name of `obj_name->name` and object path of
64 * `obj_name->obj_path`.
65 */
66int sni_obj_name_cmp(const void *item, const void *obj_name);
67
49/** 68/**
50 * Gets an icon for the given item if found. 69 * Gets an icon for the given item if found.
51 * 70 *
diff --git a/swaybar/bar.c b/swaybar/bar.c
index f12923a8..f1b42d2c 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -247,6 +247,8 @@ void bar_setup(struct bar *bar, const char *socket_path, const char *bar_id) {
247 247
248 /* set window height */ 248 /* set window height */
249 set_window_height(bar_output->window, bar->config->height); 249 set_window_height(bar_output->window, bar->config->height);
250
251 bar_output->state = output;
250 } 252 }
251 /* spawn status command */ 253 /* spawn status command */
252 spawn_status_cmd_proc(bar); 254 spawn_status_cmd_proc(bar);
@@ -296,6 +298,11 @@ void bar_run(struct bar *bar) {
296 render(output, bar->config, bar->status); 298 render(output, bar->config, bar->status);
297 window_render(output->window); 299 window_render(output->window);
298 wl_display_flush(output->registry->display); 300 wl_display_flush(output->registry->display);
301#ifdef ENABLE_TRAY
302 output->active = true;
303 } else {
304 output->active = false;
305#endif
299 } 306 }
300 } 307 }
301 } 308 }
diff --git a/swaybar/tray/dbus.c b/swaybar/tray/dbus.c
index 8e719fd9..08abf65c 100644
--- a/swaybar/tray/dbus.c
+++ b/swaybar/tray/dbus.c
@@ -1,5 +1,6 @@
1#define _XOPEN_SOURCE 700 1#define _XOPEN_SOURCE 700
2#include <stdio.h> 2#include <stdio.h>
3#include <string.h>
3#include <stdlib.h> 4#include <stdlib.h>
4#include <stdint.h> 5#include <stdint.h>
5#include <stdbool.h> 6#include <stdbool.h>
@@ -135,7 +136,106 @@ static void dispatch_status(DBusConnection *connection, DBusDispatchStatus new_s
135 } 136 }
136} 137}
137 138
138/* Public functions below */ 139struct async_prop_data {
140 char const *sig;
141 void(*callback)(DBusMessageIter *, void *, enum property_status);
142 void *usr_data;
143};
144
145static void get_prop_callback(DBusPendingCall *pending, void *_data) {
146 struct async_prop_data *data = _data;
147
148 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
149
150 if (!reply) {
151 sway_log(L_INFO, "Got no icon name reply from item");
152 goto bail;
153 }
154
155 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
156 char *msg;
157
158 dbus_message_get_args(reply, NULL,
159 DBUS_TYPE_STRING, &msg,
160 DBUS_TYPE_INVALID);
161
162 sway_log(L_INFO, "Failure to get property: %s", msg);
163 data->callback(NULL, data->usr_data, PROP_ERROR);
164 goto bail;
165 }
166
167 DBusMessageIter iter;
168 DBusMessageIter variant;
169
170 dbus_message_iter_init(reply, &iter);
171 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
172 sway_log(L_ERROR, "Property relpy type incorrect");
173 data->callback(NULL, data->usr_data, PROP_BAD_DATA);
174 goto bail;
175 }
176 dbus_message_iter_recurse(&iter, &variant);
177
178 if (!dbus_message_iter_check_signature(&variant, data->sig)) {
179 sway_log(L_INFO, "Property returned has incorrect signatue.");
180 data->callback(&variant, data->usr_data, PROP_WRONG_SIG);
181 goto bail;
182 }
183
184 data->callback(&variant, data->usr_data, PROP_EXISTS);
185
186bail:
187 if (reply) {
188 dbus_message_unref(reply);
189 }
190 dbus_pending_call_unref(pending);
191}
192
193/* Public functions below -- see header for docs*/
194
195bool dbus_message_iter_check_signature(DBusMessageIter *iter, const char *sig) {
196 char *msg_sig = dbus_message_iter_get_signature(iter);
197 int result = strcmp(msg_sig, sig);
198 dbus_free(msg_sig);
199 return (result == 0);
200}
201
202bool dbus_get_prop_async(const char *destination,
203 const char *path, const char *iface,
204 const char *prop, const char *expected_signature,
205 void(*callback)(DBusMessageIter *, void *, enum property_status),
206 void *usr_data) {
207 struct async_prop_data *data = malloc(sizeof(struct async_prop_data));
208 if (!data) {
209 return false;
210 }
211 DBusPendingCall *pending;
212 DBusMessage *message = dbus_message_new_method_call(
213 destination, path,
214 "org.freedesktop.DBus.Properties",
215 "Get");
216
217 dbus_message_append_args(message,
218 DBUS_TYPE_STRING, &iface,
219 DBUS_TYPE_STRING, &prop,
220 DBUS_TYPE_INVALID);
221
222 bool status =
223 dbus_connection_send_with_reply(conn, message, &pending, -1);
224
225 dbus_message_unref(message);
226
227 if (!(pending || status)) {
228 sway_log(L_ERROR, "Could not get property");
229 return false;
230 }
231
232 data->sig = expected_signature;
233 data->callback = callback;
234 data->usr_data = usr_data;
235 dbus_pending_call_set_notify(pending, get_prop_callback, data, free);
236
237 return true;
238}
139 239
140void dispatch_dbus() { 240void dispatch_dbus() {
141 if (!should_dispatch || !conn) { 241 if (!should_dispatch || !conn) {
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c
index c146bf32..fc9b176d 100644
--- a/swaybar/tray/icon.c
+++ b/swaybar/tray/icon.c
@@ -80,6 +80,17 @@ static bool isdir(const char *path) {
80 80
81} 81}
82 82
83static bool isfile(const char *path) {
84 struct stat statbuf;
85 if (stat(path, &statbuf) != -1) {
86 if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
87 return true;
88 }
89 }
90 return false;
91
92}
93
83/** 94/**
84 * Returns the directory of a given theme if it exists. 95 * Returns the directory of a given theme if it exists.
85 * The returned pointer must be freed. 96 * The returned pointer must be freed.
@@ -111,15 +122,28 @@ static char *find_theme_dir(const char *theme) {
111 } 122 }
112 123
113 if ((basedir = getenv("XDG_DATA_DIRS"))) { 124 if ((basedir = getenv("XDG_DATA_DIRS"))) {
114 if (snprintf(icon_dir, 1024, "%s/icons/%s", basedir, theme) >= 1024) { 125 if (!(basedir = strdup(basedir))) {
115 sway_log(L_ERROR, "Path too long to render"); 126 sway_log_errno(L_ERROR, "Path too long to render");
116 // ditto
117 goto fail; 127 goto fail;
118 } 128 }
129 char *token = strtok(basedir, ":");
130 while (token) {
131 // By peeking at the spec, there should be a slash at
132 // the end of the data dir.
133 if (snprintf(icon_dir, 1024, "%sicons/%s", token, theme) >= 1024) {
134 sway_log(L_ERROR, "Path too long to render");
135 // ditto
136 free(basedir);
137 goto fail;
138 }
119 139
120 if (isdir(icon_dir)) { 140 if (isdir(icon_dir)) {
121 return icon_dir; 141 free(basedir);
142 return icon_dir;
143 }
144 token = strtok(NULL, ":");
122 } 145 }
146 free(basedir);
123 } 147 }
124 148
125 // Spec says use "/usr/share/pixmaps/", but I see everything in 149 // Spec says use "/usr/share/pixmaps/", but I see everything in
@@ -162,6 +186,15 @@ static list_t *find_all_theme_dirs(const char *theme) {
162 list_cat(dirs, inherits); 186 list_cat(dirs, inherits);
163 list_free(inherits); 187 list_free(inherits);
164 } 188 }
189 // 'default' usually inherits the default theme. I don't believe it has
190 // any icons, but look for them anyway
191 dir = find_theme_dir("default");
192 if (dir) {
193 list_add(dirs, dir);
194 list_t *inherits = find_inherits(dir);
195 list_cat(dirs, inherits);
196 list_free(inherits);
197 }
165 dir = find_theme_dir("hicolor"); 198 dir = find_theme_dir("hicolor");
166 if (dir) { 199 if (dir) {
167 list_add(dirs, dir); 200 list_add(dirs, dir);
@@ -290,6 +323,24 @@ fail:
290 return dirs; 323 return dirs;
291} 324}
292 325
326/* Returns true if full path and file exists */
327static bool is_valid_path(const char *file) {
328 if (strstr(file, "/") == NULL || !isfile(file)) {
329 return false;
330 }
331#ifdef WITH_GDK_PIXBUF
332 if (strstr(file, ".png") == NULL &&
333 strstr(file, ".xpm") == NULL &&
334 strstr(file, ".svg") == NULL) {
335#else
336 if (strstr(file, ".png") == NULL) {
337#endif
338 return false;
339 }
340
341 return true;
342}
343
293/* Returns the file of an icon given its name and size */ 344/* Returns the file of an icon given its name and size */
294static char *find_icon_file(const char *name, int size) { 345static char *find_icon_file(const char *name, int size) {
295 int namelen = strlen(name); 346 int namelen = strlen(name);
@@ -372,7 +423,12 @@ static char *find_icon_file(const char *name, int size) {
372} 423}
373 424
374cairo_surface_t *find_icon(const char *name, int size) { 425cairo_surface_t *find_icon(const char *name, int size) {
375 char *image_path = find_icon_file(name, size); 426 char *image_path;
427 if (is_valid_path(name)) {
428 image_path = strdup(name);
429 } else {
430 image_path = find_icon_file(name, size);
431 }
376 if (image_path == NULL) { 432 if (image_path == NULL) {
377 return NULL; 433 return NULL;
378 } 434 }
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 }
diff --git a/swaybar/tray/sni_watcher.c b/swaybar/tray/sni_watcher.c
index 86453e70..41a95c47 100644
--- a/swaybar/tray/sni_watcher.c
+++ b/swaybar/tray/sni_watcher.c
@@ -11,6 +11,7 @@
11 11
12static list_t *items = NULL; 12static list_t *items = NULL;
13static list_t *hosts = NULL; 13static list_t *hosts = NULL;
14static list_t *object_path_items = NULL;
14 15
15/** 16/**
16 * Describes the function of the StatusNotifierWatcher 17 * Describes the function of the StatusNotifierWatcher
@@ -18,6 +19,10 @@ static list_t *hosts = NULL;
18 * 19 *
19 * We also implement KDE's special snowflake protocol, it's like this but with 20 * We also implement KDE's special snowflake protocol, it's like this but with
20 * all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect. 21 * all occurrences 'freedesktop' replaced with 'kde'. There is no KDE introspect.
22 *
23 * We _also_ support registering items by object path (even though this is a
24 * huge pain in the ass). Hosts that would like to subscribe to these items have
25 * to go through the `org.swaywm.LessSuckyStatusNotifierWatcher` interface.
21 */ 26 */
22static const char *interface_xml = 27static const char *interface_xml =
23 "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'" 28 "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'"
@@ -64,8 +69,59 @@ static const char *interface_xml =
64 " <arg type='' name='service' direction='out'/>" 69 " <arg type='' name='service' direction='out'/>"
65 " </signal>" 70 " </signal>"
66 " </interface>" 71 " </interface>"
72 " <interface name='org.swaywm.LessSuckyStatusNotifierWatcher'>"
73 " <property name='RegisteredObjectPathItems' type='a(os)' access='read'/>"
74 " <signal name='ObjPathItemRegistered'>"
75 " <arg type='os' name='service' direction='out'/>"
76 " </signal>"
77 " </interface>"
67 "</node>"; 78 "</node>";
68 79
80struct ObjPathItem {
81 char *obj_path;
82 char *unique_name;
83};
84
85static void free_obj_path_item(struct ObjPathItem *item) {
86 if (!item) {
87 return;
88 }
89 free(item->unique_name);
90 free(item->obj_path);
91 free(item);
92}
93static struct ObjPathItem *create_obj_path_item(const char *unique_name, const char *obj_path) {
94 struct ObjPathItem *item = malloc(sizeof(struct ObjPathItem));
95 if (!item) {
96 return NULL;
97 }
98 item->unique_name = strdup(unique_name);
99 item->obj_path = strdup(obj_path);
100 if (!item->unique_name || !item->obj_path) {
101 free_obj_path_item(item);
102 return NULL;
103 }
104 return item;
105}
106/**
107 * NOTE: This compare function does have ordering, this is because it has to
108 * comapre two strings.
109 */
110static int obj_path_item_cmp(const void *_item1, const void *_item2) {
111 const struct ObjPathItem *item1 = _item1;
112 const struct ObjPathItem *item2 = _item2;
113 if (strcmp(item1->unique_name,item2->unique_name) == 0 &&
114 strcmp(item1->obj_path,item2->obj_path) == 0) {
115 return 0;
116 }
117 return -1;
118}
119static int obj_path_unique_name_cmp(const void *_item, const void *_unique_name) {
120 const struct ObjPathItem *item = _item;
121 const char *unique_name = _unique_name;
122 return strcmp(item->unique_name, unique_name);
123}
124
69static void host_registered_signal(DBusConnection *connection) { 125static void host_registered_signal(DBusConnection *connection) {
70 // Send one signal for each protocol 126 // Send one signal for each protocol
71 DBusMessage *signal = dbus_message_new_signal( 127 DBusMessage *signal = dbus_message_new_signal(
@@ -128,6 +184,19 @@ static void item_unregistered_signal(DBusConnection *connection, const char *nam
128 dbus_message_unref(signal); 184 dbus_message_unref(signal);
129} 185}
130 186
187static void obj_path_item_registered_signal(DBusConnection *connection, const struct ObjPathItem *item) {
188 DBusMessage *signal = dbus_message_new_signal(
189 "/StatusNotifierWatcher",
190 "org.swaywm.LessSuckyStatusNotifierWatcher",
191 "ObjPathItemRegistered");
192 dbus_message_append_args(signal,
193 DBUS_TYPE_OBJECT_PATH, &item->obj_path,
194 DBUS_TYPE_STRING, &item->unique_name,
195 DBUS_TYPE_INVALID);
196 dbus_connection_send(connection, signal, NULL);
197 dbus_message_unref(signal);
198}
199
131static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) { 200static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) {
132 DBusMessage *reply; 201 DBusMessage *reply;
133 202
@@ -141,35 +210,53 @@ static void respond_to_introspect(DBusConnection *connection, DBusMessage *reque
141 210
142static void register_item(DBusConnection *connection, DBusMessage *message) { 211static void register_item(DBusConnection *connection, DBusMessage *message) {
143 DBusError error; 212 DBusError error;
213 DBusMessage *reply;
144 char *name; 214 char *name;
145 215
146 dbus_error_init(&error); 216 dbus_error_init(&error);
147 if (!dbus_message_get_args(message, &error, 217 if (!dbus_message_get_args(message, &error,
148 DBUS_TYPE_STRING, &name, 218 DBUS_TYPE_STRING, &name,
149 DBUS_TYPE_INVALID)) { 219 DBUS_TYPE_INVALID)) {
150 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); 220 sway_log(L_ERROR, "Error parsing method args: %s", error.message);
151 } 221 }
152 222
153 sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"\n", name); 223 sway_log(L_INFO, "RegisterStatusNotifierItem called with \"%s\"", name);
154 224
155 // Don't add duplicate or not real item 225 // Don't add duplicate or not real item
156 if (!dbus_validate_bus_name(name, NULL)) { 226 if (!dbus_validate_bus_name(name, NULL)) {
157 sway_log(L_INFO, "This item is not valid, we cannot keep track of it."); 227 if (dbus_validate_path(name, NULL)) {
158 return; 228 // Item is registered by object path
159 } 229 struct ObjPathItem *item =
230 create_obj_path_item(dbus_message_get_sender(message), name);
231
232 // Add ObjPathItem
233 if (list_seq_find(object_path_items, obj_path_item_cmp, item) != -1) {
234 free_obj_path_item(item);
235 return;
236 }
237 list_add(object_path_items, item);
238 obj_path_item_registered_signal(connection, item);
239 goto send_reply;
240 } else {
241 sway_log(L_INFO, "This item is not valid, we cannot keep track of it.");
242 return;
243 }
244 } else {
160 245
161 if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) { 246 if (list_seq_find(items, (int (*)(const void *, const void *))strcmp, name) != -1) {
162 return; 247 return;
163 } 248 }
164 if (!dbus_bus_name_has_owner(connection, name, &error)) { 249 if (!dbus_bus_name_has_owner(connection, name, &error)) {
165 return; 250 return;
166 } 251 }
167 252
168 list_add(items, strdup(name)); 253 list_add(items, strdup(name));
169 item_registered_signal(connection, name); 254 item_registered_signal(connection, name);
255 }
170 256
171 // It's silly, but xembedsniproxy wants a reply for this function 257send_reply:
172 DBusMessage *reply = dbus_message_new_method_return(message); 258 // It's silly, but clients want a reply for this function
259 reply = dbus_message_new_method_return(message);
173 dbus_connection_send(connection, reply, NULL); 260 dbus_connection_send(connection, reply, NULL);
174 dbus_message_unref(reply); 261 dbus_message_unref(reply);
175} 262}
@@ -182,10 +269,10 @@ static void register_host(DBusConnection *connection, DBusMessage *message) {
182 if (!dbus_message_get_args(message, &error, 269 if (!dbus_message_get_args(message, &error,
183 DBUS_TYPE_STRING, &name, 270 DBUS_TYPE_STRING, &name,
184 DBUS_TYPE_INVALID)) { 271 DBUS_TYPE_INVALID)) {
185 sway_log(L_ERROR, "Error parsing method args: %s\n", error.message); 272 sway_log(L_ERROR, "Error parsing method args: %s", error.message);
186 } 273 }
187 274
188 sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"\n", name); 275 sway_log(L_INFO, "RegisterStatusNotifierHost called with \"%s\"", name);
189 276
190 // Don't add duplicate or not real host 277 // Don't add duplicate or not real host
191 if (!dbus_validate_bus_name(name, NULL)) { 278 if (!dbus_validate_bus_name(name, NULL)) {
@@ -215,12 +302,12 @@ static void get_property(DBusConnection *connection, DBusMessage *message) {
215 DBUS_TYPE_STRING, &interface, 302 DBUS_TYPE_STRING, &interface,
216 DBUS_TYPE_STRING, &property, 303 DBUS_TYPE_STRING, &property,
217 DBUS_TYPE_INVALID)) { 304 DBUS_TYPE_INVALID)) {
218 sway_log(L_ERROR, "Error parsing prop args: %s\n", error.message); 305 sway_log(L_ERROR, "Error parsing prop args: %s", error.message);
219 return; 306 return;
220 } 307 }
221 308
222 if (strcmp(property, "RegisteredStatusNotifierItems") == 0) { 309 if (strcmp(property, "RegisteredStatusNotifierItems") == 0) {
223 sway_log(L_INFO, "Replying with items\n"); 310 sway_log(L_INFO, "Replying with items");
224 DBusMessage *reply; 311 DBusMessage *reply;
225 reply = dbus_message_new_method_return(message); 312 reply = dbus_message_new_method_return(message);
226 DBusMessageIter iter; 313 DBusMessageIter iter;
@@ -281,6 +368,41 @@ static void get_property(DBusConnection *connection, DBusMessage *message) {
281 dbus_message_iter_close_container(&iter, &sub); 368 dbus_message_iter_close_container(&iter, &sub);
282 dbus_connection_send(connection, reply, NULL); 369 dbus_connection_send(connection, reply, NULL);
283 dbus_message_unref(reply); 370 dbus_message_unref(reply);
371 } else if (strcmp(property, "RegisteredObjectPathItems") == 0) {
372 sway_log(L_INFO, "Replying with ObjPathItems");
373 DBusMessage *reply;
374 reply = dbus_message_new_method_return(message);
375 DBusMessageIter iter;
376 DBusMessageIter variant;
377 DBusMessageIter array;
378 DBusMessageIter dstruct;
379
380 dbus_message_iter_init_append(reply, &iter);
381
382 dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
383 "a(os)", &variant);
384 dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
385 "(os)", &array);
386
387 for (int i = 0; i < object_path_items->length; ++i) {
388 struct ObjPathItem *item = object_path_items->items[i];
389
390 dbus_message_iter_open_container(&array,
391 DBUS_TYPE_STRUCT, NULL, &dstruct);
392
393 dbus_message_iter_append_basic(&dstruct,
394 DBUS_TYPE_OBJECT_PATH, &item->obj_path);
395 dbus_message_iter_append_basic(&dstruct,
396 DBUS_TYPE_STRING, &item->unique_name);
397
398 dbus_message_iter_close_container(&array, &dstruct);
399 }
400
401 dbus_message_iter_close_container(&variant, &array);
402 dbus_message_iter_close_container(&iter, &variant);
403
404 dbus_connection_send(connection, reply, NULL);
405 dbus_message_unref(reply);
284 } 406 }
285} 407}
286 408
@@ -289,6 +411,8 @@ static void set_property(DBusConnection *connection, DBusMessage *message) {
289 return; 411 return;
290} 412}
291 413
414// TODO clean me up please or get rid of me
415// also add LessSuckyStatusNotifierWatcher props
292static void get_all(DBusConnection *connection, DBusMessage *message) { 416static void get_all(DBusConnection *connection, DBusMessage *message) {
293 DBusMessage *reply; 417 DBusMessage *reply;
294 reply = dbus_message_new_method_return(message); 418 reply = dbus_message_new_method_return(message);
@@ -400,6 +524,8 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
400 const char *old_owner; 524 const char *old_owner;
401 const char *new_owner; 525 const char *new_owner;
402 int index; 526 int index;
527 bool found_obj_path_item = false;
528
403 if (!dbus_message_get_args(message, NULL, 529 if (!dbus_message_get_args(message, NULL,
404 DBUS_TYPE_STRING, &name, 530 DBUS_TYPE_STRING, &name,
405 DBUS_TYPE_STRING, &old_owner, 531 DBUS_TYPE_STRING, &old_owner,
@@ -427,6 +553,17 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
427 553
428 return DBUS_HANDLER_RESULT_HANDLED; 554 return DBUS_HANDLER_RESULT_HANDLED;
429 } 555 }
556 while ((index = list_seq_find(object_path_items, obj_path_unique_name_cmp, name)) != -1) {
557 found_obj_path_item = true;
558 struct ObjPathItem *item = object_path_items->items[index];
559 sway_log(L_INFO, "ObjPathItem lost %s", item->obj_path);
560 list_del(object_path_items, index);
561 free_obj_path_item(item);
562 }
563 if (found_obj_path_item) {
564 item_unregistered_signal(connection, name);
565 return DBUS_HANDLER_RESULT_HANDLED;
566 }
430 } 567 }
431 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 568 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
432} 569}
@@ -446,6 +583,7 @@ int init_sni_watcher() {
446 583
447 items = create_list(); 584 items = create_list();
448 hosts = create_list(); 585 hosts = create_list();
586 object_path_items = create_list();
449 587
450 int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher", 588 int status = dbus_bus_request_name(conn, "org.freedesktop.StatusNotifierWatcher",
451 DBUS_NAME_FLAG_REPLACE_EXISTING, 589 DBUS_NAME_FLAG_REPLACE_EXISTING,
@@ -456,7 +594,7 @@ int init_sni_watcher() {
456 sway_log(L_INFO, "Could not get watcher name, it may start later"); 594 sway_log(L_INFO, "Could not get watcher name, it may start later");
457 } 595 }
458 if (dbus_error_is_set(&error)) { 596 if (dbus_error_is_set(&error)) {
459 sway_log(L_ERROR, "dbus err getting watcher name: %s\n", error.message); 597 sway_log(L_ERROR, "dbus err getting watcher name: %s", error.message);
460 return -1; 598 return -1;
461 } 599 }
462 600
@@ -469,7 +607,7 @@ int init_sni_watcher() {
469 sway_log(L_INFO, "Could not get kde watcher name, it may start later"); 607 sway_log(L_INFO, "Could not get kde watcher name, it may start later");
470 } 608 }
471 if (dbus_error_is_set(&error)) { 609 if (dbus_error_is_set(&error)) {
472 sway_log(L_ERROR, "dbus err getting kde watcher name: %s\n", error.message); 610 sway_log(L_ERROR, "dbus err getting kde watcher name: %s", error.message);
473 return -1; 611 return -1;
474 } 612 }
475 613
@@ -477,7 +615,7 @@ int init_sni_watcher() {
477 "/StatusNotifierWatcher", 615 "/StatusNotifierWatcher",
478 &vtable, NULL, &error); 616 &vtable, NULL, &error);
479 if (dbus_error_is_set(&error)) { 617 if (dbus_error_is_set(&error)) {
480 sway_log(L_ERROR, "dbus_err: %s\n", error.message); 618 sway_log(L_ERROR, "dbus_err: %s", error.message);
481 return -1; 619 return -1;
482 } 620 }
483 621
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
index 91c3af06..a5248f6c 100644
--- a/swaybar/tray/tray.c
+++ b/swaybar/tray/tray.c
@@ -38,95 +38,81 @@ static void register_host(char *name) {
38 dbus_message_unref(message); 38 dbus_message_unref(message);
39} 39}
40 40
41static void get_items_reply(DBusPendingCall *pending, void *_data) { 41static void get_items_reply(DBusMessageIter *iter, void *_data, enum property_status status) {
42 DBusMessage *reply = dbus_pending_call_steal_reply(pending); 42 if (status != PROP_EXISTS) {
43 43 return;
44 if (!reply) {
45 sway_log(L_ERROR, "Got no items reply from sni watcher");
46 goto bail;
47 } 44 }
45 DBusMessageIter array;
48 46
49 int message_type = dbus_message_get_type(reply); 47 // O(n) function, could be faster dynamically reading values
48 int len = dbus_message_iter_get_element_count(iter);
50 49
51 if (message_type == DBUS_MESSAGE_TYPE_ERROR) { 50 dbus_message_iter_recurse(iter, &array);
52 char *msg; 51 for (int i = 0; i < len; i++) {
52 const char *name;
53 dbus_message_iter_get_basic(&array, &name);
53 54
54 dbus_message_get_args(reply, NULL, 55 if (list_seq_find(tray->items, sni_str_cmp, name) == -1) {
55 DBUS_TYPE_STRING, &msg, 56 struct StatusNotifierItem *item = sni_create(name);
56 DBUS_TYPE_INVALID);
57 57
58 sway_log(L_ERROR, "Message is error: %s", msg); 58 if (item) {
59 goto bail; 59 sway_log(L_DEBUG, "Item registered with host: %s", name);
60 list_add(tray->items, item);
61 dirty = true;
62 }
63 }
64 }
65}
66static void get_obj_items_reply(DBusMessageIter *iter, void *_data, enum property_status status) {
67 if (status != PROP_EXISTS) {
68 return;
60 } 69 }
61
62 DBusMessageIter iter;
63 DBusMessageIter variant;
64 DBusMessageIter array; 70 DBusMessageIter array;
71 DBusMessageIter dstruct;
65 72
66 dbus_message_iter_init(reply, &iter); 73 int len = dbus_message_iter_get_element_count(iter);
67 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
68 sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
69 goto bail;
70 }
71 dbus_message_iter_recurse(&iter, &variant);
72 if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY ||
73 dbus_message_iter_get_element_type(&variant) != DBUS_TYPE_STRING) {
74 sway_log(L_ERROR, "Replyed with wrong type, not v(as)");
75 goto bail;
76 }
77 74
78 // Clear list 75 dbus_message_iter_recurse(iter, &array);
79 list_foreach(tray->items, (void (*)(void *))sni_free); 76 for (int i = 0; i < len; i++) {
80 list_free(tray->items); 77 const char *object_path;
81 tray->items = create_list(); 78 const char *unique_name;
82 79
83 // O(n) function, could be faster dynamically reading values 80 dbus_message_iter_recurse(&array, &dstruct);
84 int len = dbus_message_iter_get_element_count(&variant);
85 81
86 dbus_message_iter_recurse(&variant, &array); 82 dbus_message_iter_get_basic(&dstruct, &object_path);
87 for (int i = 0; i < len; i++) { 83 dbus_message_iter_next(&dstruct);
88 const char *name; 84 dbus_message_iter_get_basic(&dstruct, &unique_name);
89 dbus_message_iter_get_basic(&array, &name);
90 85
91 struct StatusNotifierItem *item = sni_create(name); 86 struct ObjName obj_name = {
87 object_path,
88 unique_name,
89 };
90 if (list_seq_find(tray->items, sni_obj_name_cmp, &obj_name) == -1) {
91 struct StatusNotifierItem *item =
92 sni_create_from_obj_path(unique_name, object_path);
92 93
93 if (item) { 94 if (item) {
94 sway_log(L_DEBUG, "Item registered with host: %s", name); 95 sway_log(L_DEBUG, "Item registered with host: %s", unique_name);
95 list_add(tray->items, item); 96 list_add(tray->items, item);
96 dirty = true; 97 dirty = true;
98 }
97 } 99 }
98 } 100 }
99
100bail:
101 dbus_message_unref(reply);
102 dbus_pending_call_unref(pending);
103 return;
104} 101}
105static void get_items() {
106 DBusPendingCall *pending;
107 DBusMessage *message = dbus_message_new_method_call(
108 "org.freedesktop.StatusNotifierWatcher",
109 "/StatusNotifierWatcher",
110 "org.freedesktop.DBus.Properties",
111 "Get");
112
113 const char *iface = "org.freedesktop.StatusNotifierWatcher";
114 const char *prop = "RegisteredStatusNotifierItems";
115 dbus_message_append_args(message,
116 DBUS_TYPE_STRING, &iface,
117 DBUS_TYPE_STRING, &prop,
118 DBUS_TYPE_INVALID);
119 102
120 bool status = 103static void get_items() {
121 dbus_connection_send_with_reply(conn, message, &pending, -1); 104 // Clear list
122 dbus_message_unref(message); 105 list_foreach(tray->items, (void (*)(void *))sni_free);
106 list_free(tray->items);
107 tray->items = create_list();
123 108
124 if (!(pending || status)) { 109 dbus_get_prop_async("org.freedesktop.StatusNotifierWatcher",
125 sway_log(L_ERROR, "Could not get items"); 110 "/StatusNotifierWatcher","org.freedesktop.StatusNotifierWatcher",
126 return; 111 "RegisteredStatusNotifierItems", "as", get_items_reply, NULL);
127 }
128 112
129 dbus_pending_call_set_notify(pending, get_items_reply, NULL, NULL); 113 dbus_get_prop_async("org.freedesktop.StatusNotifierWatcher",
114 "/StatusNotifierWatcher","org.swaywm.LessSuckyStatusNotifierWatcher",
115 "RegisteredObjectPathItems", "a(os)", get_obj_items_reply, NULL);
130} 116}
131 117
132static DBusHandlerResult signal_handler(DBusConnection *connection, 118static DBusHandlerResult signal_handler(DBusConnection *connection,
@@ -162,11 +148,14 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
162 } 148 }
163 149
164 int index; 150 int index;
165 if ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) { 151 bool found_item = false;
152 while ((index = list_seq_find(tray->items, sni_str_cmp, name)) != -1) {
153 found_item = true;
166 sni_free(tray->items->items[index]); 154 sni_free(tray->items->items[index]);
167 list_del(tray->items, index); 155 list_del(tray->items, index);
168 dirty = true; 156 dirty = true;
169 } else { 157 }
158 if (found_item == false) {
170 // If it's not in our list, then our list is incorrect. 159 // If it's not in our list, then our list is incorrect.
171 // Fetch all items again 160 // Fetch all items again
172 sway_log(L_INFO, "Host item list incorrect, refreshing"); 161 sway_log(L_INFO, "Host item list incorrect, refreshing");
@@ -178,17 +167,52 @@ static DBusHandlerResult signal_handler(DBusConnection *connection,
178 "NewIcon") || dbus_message_is_signal(message, 167 "NewIcon") || dbus_message_is_signal(message,
179 "org.kde.StatusNotifierItem", "NewIcon")) { 168 "org.kde.StatusNotifierItem", "NewIcon")) {
180 const char *name; 169 const char *name;
170 const char *obj_path;
181 int index; 171 int index;
182 struct StatusNotifierItem *item; 172 struct StatusNotifierItem *item;
183 173
184 name = dbus_message_get_sender(message); 174 name = dbus_message_get_sender(message);
185 if ((index = list_seq_find(tray->items, sni_uniq_cmp, name)) != -1) { 175 obj_path = dbus_message_get_path(message);
176 struct ObjName obj_name = {
177 obj_path,
178 name,
179 };
180 if ((index = list_seq_find(tray->items, sni_obj_name_cmp, &obj_name)) != -1) {
186 item = tray->items->items[index]; 181 item = tray->items->items[index];
187 sway_log(L_INFO, "NewIcon signal from item %s", item->name); 182 sway_log(L_INFO, "NewIcon signal from item %s", item->name);
188 get_icon(item); 183 get_icon(item);
189 } 184 }
190 185
191 return DBUS_HANDLER_RESULT_HANDLED; 186 return DBUS_HANDLER_RESULT_HANDLED;
187 } else if (dbus_message_is_signal(message,
188 "org.swaywm.LessSuckyStatusNotifierWatcher",
189 "ObjPathItemRegistered")) {
190 const char *object_path;
191 const char *unique_name;
192 if (!dbus_message_get_args(message, NULL,
193 DBUS_TYPE_OBJECT_PATH, &object_path,
194 DBUS_TYPE_STRING, &unique_name,
195 DBUS_TYPE_INVALID)) {
196 sway_log(L_ERROR, "Error getting ObjPathItemRegistered args");
197 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
198 }
199
200 struct ObjName obj_name = {
201 object_path,
202 unique_name,
203 };
204 if (list_seq_find(tray->items, sni_obj_name_cmp, &obj_name) == -1) {
205 struct StatusNotifierItem *item =
206 sni_create_from_obj_path(unique_name,
207 object_path);
208
209 if (item) {
210 list_add(tray->items, item);
211 dirty = true;
212 }
213 }
214
215 return DBUS_HANDLER_RESULT_HANDLED;
192 } 216 }
193 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 217 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
194} 218}
@@ -234,6 +258,9 @@ static int init_host() {
234 258
235 register_host(name); 259 register_host(name);
236 260
261 // Chances are if an item is already running, we'll get it two times.
262 // Once from this and another time from queued signals. Still we want
263 // to do this to be a complient sni host just in case.
237 get_items(); 264 get_items();
238 265
239 // Perhaps use addmatch helper functions like wlc does? 266 // Perhaps use addmatch helper functions like wlc does?
@@ -255,6 +282,15 @@ static int init_host() {
255 sway_log(L_ERROR, "dbus_err: %s", error.message); 282 sway_log(L_ERROR, "dbus_err: %s", error.message);
256 return -1; 283 return -1;
257 } 284 }
285 dbus_bus_add_match(conn,
286 "type='signal',\
287 sender='org.freedesktop.StatusNotifierWatcher',\
288 member='ObjPathItemRegistered'",
289 &error);
290 if (dbus_error_is_set(&error)) {
291 sway_log(L_ERROR, "dbus_err: %s", error.message);
292 return -1;
293 }
258 294
259 // SNI matches 295 // SNI matches
260 dbus_bus_add_match(conn, 296 dbus_bus_add_match(conn,
@@ -287,9 +323,13 @@ err:
287 return -1; 323 return -1;
288} 324}
289 325
290void tray_mouse_event(struct output *output, int x, int y, 326void tray_mouse_event(struct output *output, int rel_x, int rel_y,
291 uint32_t button, uint32_t state) { 327 uint32_t button, uint32_t state) {
292 328
329 int x = rel_x;
330 int y = rel_y + (swaybar.config->position == DESKTOP_SHELL_PANEL_POSITION_TOP
331 ? 0 : (output->state->height - output->window->height));
332
293 struct window *window = output->window; 333 struct window *window = output->window;
294 uint32_t tray_padding = swaybar.config->tray_padding; 334 uint32_t tray_padding = swaybar.config->tray_padding;
295 int tray_width = window->width * window->scale; 335 int tray_width = window->width * window->scale;
@@ -332,6 +372,24 @@ uint32_t tray_render(struct output *output, struct config *config) {
332 return tray_width; 372 return tray_width;
333 } 373 }
334 374
375 bool clean_item = false;
376 // Clean item if only one output has tray or this is the last output
377 if (swaybar.outputs->length == 1 || config->tray_output || output->idx == swaybar.outputs->length-1) {
378 clean_item = true;
379 // More trickery is needed in case you plug off secondary outputs on live
380 } else {
381 int active_outputs = 0;
382 for (int i = 0; i < swaybar.outputs->length; i++) {
383 struct output *output = swaybar.outputs->items[i];
384 if (output->active) {
385 active_outputs++;
386 }
387 }
388 if (active_outputs == 1) {
389 clean_item = true;
390 }
391 }
392
335 for (int i = 0; i < tray->items->length; ++i) { 393 for (int i = 0; i < tray->items->length; ++i) {
336 struct StatusNotifierItem *item = 394 struct StatusNotifierItem *item =
337 tray->items->items[i]; 395 tray->items->items[i];
@@ -358,6 +416,7 @@ uint32_t tray_render(struct output *output, struct config *config) {
358 list_add(output->items, render_item); 416 list_add(output->items, render_item);
359 } else if (item->dirty) { 417 } else if (item->dirty) {
360 // item needs re-render 418 // item needs re-render
419 sway_log(L_DEBUG, "Redrawing item %d for output %d", i, output->idx);
361 sni_icon_ref_free(render_item); 420 sni_icon_ref_free(render_item);
362 output->items->items[j] = render_item = 421 output->items->items[j] = render_item =
363 sni_icon_ref_create(item, item_size); 422 sni_icon_ref_create(item, item_size);
@@ -373,7 +432,9 @@ uint32_t tray_render(struct output *output, struct config *config) {
373 cairo_fill(cairo); 432 cairo_fill(cairo);
374 cairo_set_operator(cairo, op); 433 cairo_set_operator(cairo, op);
375 434
376 item->dirty = false; 435 if (clean_item) {
436 item->dirty = false;
437 }
377 } 438 }
378 439
379 440