diff options
Diffstat (limited to 'swaybar')
-rw-r--r-- | swaybar/bar.c | 7 | ||||
-rw-r--r-- | swaybar/tray/dbus.c | 102 | ||||
-rw-r--r-- | swaybar/tray/icon.c | 68 | ||||
-rw-r--r-- | swaybar/tray/sni.c | 254 | ||||
-rw-r--r-- | swaybar/tray/sni_watcher.c | 182 | ||||
-rw-r--r-- | swaybar/tray/tray.c | 211 |
6 files changed, 536 insertions, 288 deletions
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 */ | 139 | struct async_prop_data { |
140 | char const *sig; | ||
141 | void(*callback)(DBusMessageIter *, void *, enum property_status); | ||
142 | void *usr_data; | ||
143 | }; | ||
144 | |||
145 | static 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 | |||
186 | bail: | ||
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 | |||
195 | bool 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 | |||
202 | bool 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 | ||
140 | void dispatch_dbus() { | 240 | void 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 | ||
83 | static 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 */ | ||
327 | static 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 */ |
294 | static char *find_icon_file(const char *name, int size) { | 345 | static 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 | ||
374 | cairo_surface_t *find_icon(const char *name, int size) { | 425 | cairo_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 | ||
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 | } |
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 | ||
12 | static list_t *items = NULL; | 12 | static list_t *items = NULL; |
13 | static list_t *hosts = NULL; | 13 | static list_t *hosts = NULL; |
14 | static 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 | */ |
22 | static const char *interface_xml = | 27 | static 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 | ||
80 | struct ObjPathItem { | ||
81 | char *obj_path; | ||
82 | char *unique_name; | ||
83 | }; | ||
84 | |||
85 | static 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 | } | ||
93 | static 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 | */ | ||
110 | static 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 | } | ||
119 | static 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 | |||
69 | static void host_registered_signal(DBusConnection *connection) { | 125 | static 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 | ||
187 | static 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 | |||
131 | static void respond_to_introspect(DBusConnection *connection, DBusMessage *request) { | 200 | static 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 | ||
142 | static void register_item(DBusConnection *connection, DBusMessage *message) { | 211 | static 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 | 257 | send_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 | ||
292 | static void get_all(DBusConnection *connection, DBusMessage *message) { | 416 | static 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 | ||
41 | static void get_items_reply(DBusPendingCall *pending, void *_data) { | 41 | static 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 | } | ||
66 | static 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 | |||
100 | bail: | ||
101 | dbus_message_unref(reply); | ||
102 | dbus_pending_call_unref(pending); | ||
103 | return; | ||
104 | } | 101 | } |
105 | static 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 = | 103 | static 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 | ||
132 | static DBusHandlerResult signal_handler(DBusConnection *connection, | 118 | static 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 | ||
290 | void tray_mouse_event(struct output *output, int x, int y, | 326 | void 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 | ||