aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/tray/tray.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/tray/tray.c')
-rw-r--r--swaybar/tray/tray.c211
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
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