diff options
Diffstat (limited to 'swaybar/tray/tray.c')
-rw-r--r-- | swaybar/tray/tray.c | 211 |
1 files changed, 136 insertions, 75 deletions
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 | ||