diff options
Diffstat (limited to 'swaybar')
-rw-r--r-- | swaybar/bar.c | 83 | ||||
-rw-r--r-- | swaybar/config.c | 23 | ||||
-rw-r--r-- | swaybar/i3bar.c | 36 | ||||
-rw-r--r-- | swaybar/input.c | 84 | ||||
-rw-r--r-- | swaybar/ipc.c | 115 | ||||
-rw-r--r-- | swaybar/main.c | 4 | ||||
-rw-r--r-- | swaybar/meson.build | 44 | ||||
-rw-r--r-- | swaybar/render.c | 190 | ||||
-rw-r--r-- | swaybar/status_line.c | 2 | ||||
-rw-r--r-- | swaybar/tray/host.c | 210 | ||||
-rw-r--r-- | swaybar/tray/icon.c | 462 | ||||
-rw-r--r-- | swaybar/tray/item.c | 472 | ||||
-rw-r--r-- | swaybar/tray/tray.c | 131 | ||||
-rw-r--r-- | swaybar/tray/watcher.c | 212 |
14 files changed, 1885 insertions, 183 deletions
diff --git a/swaybar/bar.c b/swaybar/bar.c index 08c386a7..d36367fc 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c | |||
@@ -1,4 +1,4 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <assert.h> | 2 | #include <assert.h> |
3 | #include <errno.h> | 3 | #include <errno.h> |
4 | #include <fcntl.h> | 4 | #include <fcntl.h> |
@@ -11,6 +11,7 @@ | |||
11 | #include <wayland-client.h> | 11 | #include <wayland-client.h> |
12 | #include <wayland-cursor.h> | 12 | #include <wayland-cursor.h> |
13 | #include <wlr/util/log.h> | 13 | #include <wlr/util/log.h> |
14 | #include "config.h" | ||
14 | #include "swaybar/bar.h" | 15 | #include "swaybar/bar.h" |
15 | #include "swaybar/config.h" | 16 | #include "swaybar/config.h" |
16 | #include "swaybar/i3bar.h" | 17 | #include "swaybar/i3bar.h" |
@@ -18,6 +19,9 @@ | |||
18 | #include "swaybar/ipc.h" | 19 | #include "swaybar/ipc.h" |
19 | #include "swaybar/status_line.h" | 20 | #include "swaybar/status_line.h" |
20 | #include "swaybar/render.h" | 21 | #include "swaybar/render.h" |
22 | #if HAVE_TRAY | ||
23 | #include "swaybar/tray/tray.h" | ||
24 | #endif | ||
21 | #include "ipc-client.h" | 25 | #include "ipc-client.h" |
22 | #include "list.h" | 26 | #include "list.h" |
23 | #include "log.h" | 27 | #include "log.h" |
@@ -31,6 +35,7 @@ void free_workspaces(struct wl_list *list) { | |||
31 | wl_list_for_each_safe(ws, tmp, list, link) { | 35 | wl_list_for_each_safe(ws, tmp, list, link) { |
32 | wl_list_remove(&ws->link); | 36 | wl_list_remove(&ws->link); |
33 | free(ws->name); | 37 | free(ws->name); |
38 | free(ws->label); | ||
34 | free(ws); | 39 | free(ws); |
35 | } | 40 | } |
36 | } | 41 | } |
@@ -54,6 +59,7 @@ static void swaybar_output_free(struct swaybar_output *output) { | |||
54 | free_workspaces(&output->workspaces); | 59 | free_workspaces(&output->workspaces); |
55 | wl_list_remove(&output->link); | 60 | wl_list_remove(&output->link); |
56 | free(output->name); | 61 | free(output->name); |
62 | free(output->identifier); | ||
57 | free(output); | 63 | free(output); |
58 | } | 64 | } |
59 | 65 | ||
@@ -119,7 +125,7 @@ static void destroy_layer_surface(struct swaybar_output *output) { | |||
119 | output->frame_scheduled = false; | 125 | output->frame_scheduled = false; |
120 | } | 126 | } |
121 | 127 | ||
122 | static void set_bar_dirty(struct swaybar *bar) { | 128 | void set_bar_dirty(struct swaybar *bar) { |
123 | struct swaybar_output *output; | 129 | struct swaybar_output *output; |
124 | wl_list_for_each(output, &bar->outputs, link) { | 130 | wl_list_for_each(output, &bar->outputs, link) { |
125 | set_output_dirty(output); | 131 | set_output_dirty(output); |
@@ -161,13 +167,15 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) { | |||
161 | return visible; | 167 | return visible; |
162 | } | 168 | } |
163 | 169 | ||
164 | static bool bar_uses_output(struct swaybar *bar, const char *name) { | 170 | static bool bar_uses_output(struct swaybar_output *output) { |
165 | if (bar->config->all_outputs) { | 171 | if (output->bar->config->all_outputs) { |
166 | return true; | 172 | return true; |
167 | } | 173 | } |
174 | char *identifier = output->identifier; | ||
168 | struct config_output *coutput; | 175 | struct config_output *coutput; |
169 | wl_list_for_each(coutput, &bar->config->outputs, link) { | 176 | wl_list_for_each(coutput, &output->bar->config->outputs, link) { |
170 | if (strcmp(coutput->name, name) == 0) { | 177 | if (strcmp(coutput->name, output->name) == 0 || |
178 | (identifier && strcmp(coutput->name, identifier) == 0)) { | ||
171 | return true; | 179 | return true; |
172 | } | 180 | } |
173 | } | 181 | } |
@@ -195,6 +203,10 @@ static void output_scale(void *data, struct wl_output *wl_output, | |||
195 | int32_t factor) { | 203 | int32_t factor) { |
196 | struct swaybar_output *output = data; | 204 | struct swaybar_output *output = data; |
197 | output->scale = factor; | 205 | output->scale = factor; |
206 | if (output == output->bar->pointer.current) { | ||
207 | update_cursor(output->bar); | ||
208 | render_frame(output); | ||
209 | } | ||
198 | } | 210 | } |
199 | 211 | ||
200 | struct wl_output_listener output_listener = { | 212 | struct wl_output_listener output_listener = { |
@@ -206,12 +218,16 @@ struct wl_output_listener output_listener = { | |||
206 | 218 | ||
207 | static void xdg_output_handle_logical_position(void *data, | 219 | static void xdg_output_handle_logical_position(void *data, |
208 | struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { | 220 | struct zxdg_output_v1 *xdg_output, int32_t x, int32_t y) { |
209 | // Who cares | 221 | struct swaybar_output *output = data; |
222 | output->output_x = x; | ||
223 | output->output_y = y; | ||
210 | } | 224 | } |
211 | 225 | ||
212 | static void xdg_output_handle_logical_size(void *data, | 226 | static void xdg_output_handle_logical_size(void *data, |
213 | struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { | 227 | struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) { |
214 | // Who cares | 228 | struct swaybar_output *output = data; |
229 | output->output_height = height; | ||
230 | output->output_width = width; | ||
215 | } | 231 | } |
216 | 232 | ||
217 | static void xdg_output_handle_done(void *data, | 233 | static void xdg_output_handle_done(void *data, |
@@ -220,7 +236,7 @@ static void xdg_output_handle_done(void *data, | |||
220 | struct swaybar *bar = output->bar; | 236 | struct swaybar *bar = output->bar; |
221 | 237 | ||
222 | assert(output->name != NULL); | 238 | assert(output->name != NULL); |
223 | if (!bar_uses_output(bar, output->name)) { | 239 | if (!bar_uses_output(output)) { |
224 | swaybar_output_free(output); | 240 | swaybar_output_free(output); |
225 | return; | 241 | return; |
226 | } | 242 | } |
@@ -245,7 +261,22 @@ static void xdg_output_handle_name(void *data, | |||
245 | 261 | ||
246 | static void xdg_output_handle_description(void *data, | 262 | static void xdg_output_handle_description(void *data, |
247 | struct zxdg_output_v1 *xdg_output, const char *description) { | 263 | struct zxdg_output_v1 *xdg_output, const char *description) { |
248 | // Who cares | 264 | // wlroots currently sets the description to `make model serial (name)` |
265 | // If this changes in the future, this will need to be modified. | ||
266 | struct swaybar_output *output = data; | ||
267 | free(output->identifier); | ||
268 | output->identifier = NULL; | ||
269 | char *paren = strrchr(description, '('); | ||
270 | if (paren) { | ||
271 | size_t length = paren - description; | ||
272 | output->identifier = malloc(length); | ||
273 | if (!output->identifier) { | ||
274 | wlr_log(WLR_ERROR, "Failed to allocate output identifier"); | ||
275 | return; | ||
276 | } | ||
277 | strncpy(output->identifier, description, length); | ||
278 | output->identifier[length - 1] = '\0'; | ||
279 | } | ||
249 | } | 280 | } |
250 | 281 | ||
251 | struct zxdg_output_v1_listener xdg_output_listener = { | 282 | struct zxdg_output_v1_listener xdg_output_listener = { |
@@ -272,7 +303,7 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
272 | struct swaybar *bar = data; | 303 | struct swaybar *bar = data; |
273 | if (strcmp(interface, wl_compositor_interface.name) == 0) { | 304 | if (strcmp(interface, wl_compositor_interface.name) == 0) { |
274 | bar->compositor = wl_registry_bind(registry, name, | 305 | bar->compositor = wl_registry_bind(registry, name, |
275 | &wl_compositor_interface, 3); | 306 | &wl_compositor_interface, 4); |
276 | } else if (strcmp(interface, wl_seat_interface.name) == 0) { | 307 | } else if (strcmp(interface, wl_seat_interface.name) == 0) { |
277 | bar->seat = wl_registry_bind(registry, name, | 308 | bar->seat = wl_registry_bind(registry, name, |
278 | &wl_seat_interface, 3); | 309 | &wl_seat_interface, 3); |
@@ -354,25 +385,15 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { | |||
354 | wl_display_roundtrip(bar->display); | 385 | wl_display_roundtrip(bar->display); |
355 | 386 | ||
356 | struct swaybar_pointer *pointer = &bar->pointer; | 387 | struct swaybar_pointer *pointer = &bar->pointer; |
357 | |||
358 | int max_scale = 1; | ||
359 | struct swaybar_output *output; | ||
360 | wl_list_for_each(output, &bar->outputs, link) { | ||
361 | if (output->scale > max_scale) { | ||
362 | max_scale = output->scale; | ||
363 | } | ||
364 | } | ||
365 | |||
366 | pointer->cursor_theme = | ||
367 | wl_cursor_theme_load(NULL, 24 * max_scale, bar->shm); | ||
368 | assert(pointer->cursor_theme); | ||
369 | struct wl_cursor *cursor; | ||
370 | cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); | ||
371 | assert(cursor); | ||
372 | pointer->cursor_image = cursor->images[0]; | ||
373 | pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); | 388 | pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); |
374 | assert(pointer->cursor_surface); | 389 | assert(pointer->cursor_surface); |
375 | 390 | ||
391 | #if HAVE_TRAY | ||
392 | if (!bar->config->tray_hidden) { | ||
393 | bar->tray = create_tray(bar); | ||
394 | } | ||
395 | #endif | ||
396 | |||
376 | if (bar->config->workspace_buttons) { | 397 | if (bar->config->workspace_buttons) { |
377 | ipc_get_workspaces(bar); | 398 | ipc_get_workspaces(bar); |
378 | } | 399 | } |
@@ -414,6 +435,11 @@ void bar_run(struct swaybar *bar) { | |||
414 | loop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN, | 435 | loop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN, |
415 | status_in, bar); | 436 | status_in, bar); |
416 | } | 437 | } |
438 | #if HAVE_TRAY | ||
439 | if (bar->tray) { | ||
440 | loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar->tray->bus); | ||
441 | } | ||
442 | #endif | ||
417 | while (1) { | 443 | while (1) { |
418 | errno = 0; | 444 | errno = 0; |
419 | if (wl_display_flush(bar->display) == -1 && errno != EAGAIN) { | 445 | if (wl_display_flush(bar->display) == -1 && errno != EAGAIN) { |
@@ -431,6 +457,9 @@ static void free_outputs(struct wl_list *list) { | |||
431 | } | 457 | } |
432 | 458 | ||
433 | void bar_teardown(struct swaybar *bar) { | 459 | void bar_teardown(struct swaybar *bar) { |
460 | #if HAVE_TRAY | ||
461 | destroy_tray(bar->tray); | ||
462 | #endif | ||
434 | free_outputs(&bar->outputs); | 463 | free_outputs(&bar->outputs); |
435 | if (bar->config) { | 464 | if (bar->config) { |
436 | free_config(bar->config); | 465 | free_config(bar->config); |
diff --git a/swaybar/config.c b/swaybar/config.c index 0fd1f02e..d4cc9b1a 100644 --- a/swaybar/config.c +++ b/swaybar/config.c | |||
@@ -1,9 +1,10 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <string.h> | 3 | #include <string.h> |
4 | #include <wlr/util/log.h> | 4 | #include <wlr/util/log.h> |
5 | #include "swaybar/config.h" | 5 | #include "swaybar/config.h" |
6 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 6 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
7 | #include "config.h" | ||
7 | #include "stringop.h" | 8 | #include "stringop.h" |
8 | #include "list.h" | 9 | #include "list.h" |
9 | 10 | ||
@@ -30,15 +31,24 @@ struct swaybar_config *init_config(void) { | |||
30 | config->hidden_state = strdup("hide"); | 31 | config->hidden_state = strdup("hide"); |
31 | config->sep_symbol = NULL; | 32 | config->sep_symbol = NULL; |
32 | config->strip_workspace_numbers = false; | 33 | config->strip_workspace_numbers = false; |
34 | config->strip_workspace_name = false; | ||
33 | config->binding_mode_indicator = true; | 35 | config->binding_mode_indicator = true; |
34 | config->wrap_scroll = false; | 36 | config->wrap_scroll = false; |
35 | config->workspace_buttons = true; | 37 | config->workspace_buttons = true; |
36 | config->bindings = create_list(); | 38 | config->bindings = create_list(); |
37 | wl_list_init(&config->outputs); | 39 | wl_list_init(&config->outputs); |
40 | config->status_padding = 1; | ||
41 | config->status_edge_padding = 3; | ||
38 | 42 | ||
39 | /* height */ | 43 | /* height */ |
40 | config->height = 0; | 44 | config->height = 0; |
41 | 45 | ||
46 | /* gaps */ | ||
47 | config->gaps.top = 0; | ||
48 | config->gaps.right = 0; | ||
49 | config->gaps.bottom = 0; | ||
50 | config->gaps.left = 0; | ||
51 | |||
42 | /* colors */ | 52 | /* colors */ |
43 | config->colors.background = 0x000000FF; | 53 | config->colors.background = 0x000000FF; |
44 | config->colors.focused_background = 0x000000FF; | 54 | config->colors.focused_background = 0x000000FF; |
@@ -66,6 +76,10 @@ struct swaybar_config *init_config(void) { | |||
66 | config->colors.binding_mode.background = 0x900000FF; | 76 | config->colors.binding_mode.background = 0x900000FF; |
67 | config->colors.binding_mode.text = 0xFFFFFFFF; | 77 | config->colors.binding_mode.text = 0xFFFFFFFF; |
68 | 78 | ||
79 | #if HAVE_TRAY | ||
80 | config->tray_padding = 2; | ||
81 | #endif | ||
82 | |||
69 | return config; | 83 | return config; |
70 | } | 84 | } |
71 | 85 | ||
@@ -95,5 +109,12 @@ void free_config(struct swaybar_config *config) { | |||
95 | free(coutput->name); | 109 | free(coutput->name); |
96 | free(coutput); | 110 | free(coutput); |
97 | } | 111 | } |
112 | #if HAVE_TRAY | ||
113 | list_free_items_and_destroy(config->tray_outputs); | ||
114 | for (int i = 0; i < 10; ++i) { | ||
115 | free(config->tray_bindings[i]); | ||
116 | } | ||
117 | free(config->icon_theme); | ||
118 | #endif | ||
98 | free(config); | 119 | free(config); |
99 | } | 120 | } |
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 3ea74e13..116c8f6e 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c | |||
@@ -259,8 +259,34 @@ bool i3bar_handle_readable(struct status_line *status) { | |||
259 | } | 259 | } |
260 | } | 260 | } |
261 | 261 | ||
262 | static uint32_t event_to_x11_button(uint32_t event) { | ||
263 | switch (event) { | ||
264 | case BTN_LEFT: | ||
265 | return 1; | ||
266 | case BTN_MIDDLE: | ||
267 | return 2; | ||
268 | case BTN_RIGHT: | ||
269 | return 3; | ||
270 | case SWAY_SCROLL_UP: | ||
271 | return 4; | ||
272 | case SWAY_SCROLL_DOWN: | ||
273 | return 5; | ||
274 | case SWAY_SCROLL_LEFT: | ||
275 | return 6; | ||
276 | case SWAY_SCROLL_RIGHT: | ||
277 | return 7; | ||
278 | case BTN_SIDE: | ||
279 | return 8; | ||
280 | case BTN_EXTRA: | ||
281 | return 9; | ||
282 | default: | ||
283 | return 0; | ||
284 | } | ||
285 | } | ||
286 | |||
262 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | 287 | enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, |
263 | struct i3bar_block *block, int x, int y, enum x11_button button) { | 288 | struct i3bar_block *block, int x, int y, int rx, int ry, int w, int h, |
289 | uint32_t button) { | ||
264 | wlr_log(WLR_DEBUG, "block %s clicked", block->name); | 290 | wlr_log(WLR_DEBUG, "block %s clicked", block->name); |
265 | if (!block->name || !status->click_events) { | 291 | if (!block->name || !status->click_events) { |
266 | return HOTSPOT_PROCESS; | 292 | return HOTSPOT_PROCESS; |
@@ -274,9 +300,15 @@ enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, | |||
274 | json_object_new_string(block->instance)); | 300 | json_object_new_string(block->instance)); |
275 | } | 301 | } |
276 | 302 | ||
277 | json_object_object_add(event_json, "button", json_object_new_int(button)); | 303 | json_object_object_add(event_json, "button", |
304 | json_object_new_int(event_to_x11_button(button))); | ||
305 | json_object_object_add(event_json, "event", json_object_new_int(button)); | ||
278 | json_object_object_add(event_json, "x", json_object_new_int(x)); | 306 | json_object_object_add(event_json, "x", json_object_new_int(x)); |
279 | json_object_object_add(event_json, "y", json_object_new_int(y)); | 307 | json_object_object_add(event_json, "y", json_object_new_int(y)); |
308 | json_object_object_add(event_json, "relative_x", json_object_new_int(rx)); | ||
309 | json_object_object_add(event_json, "relative_y", json_object_new_int(ry)); | ||
310 | json_object_object_add(event_json, "width", json_object_new_int(w)); | ||
311 | json_object_object_add(event_json, "height", json_object_new_int(h)); | ||
280 | if (dprintf(status->write_fd, "%s%s\n", status->clicked ? "," : "", | 312 | if (dprintf(status->write_fd, "%s%s\n", status->clicked ? "," : "", |
281 | json_object_to_json_string(event_json)) < 0) { | 313 | json_object_to_json_string(event_json)) < 0) { |
282 | status_error(status, "[failed to write click event]"); | 314 | status_error(status, "[failed to write click event]"); |
diff --git a/swaybar/input.c b/swaybar/input.c index 263d0253..bdd55e58 100644 --- a/swaybar/input.c +++ b/swaybar/input.c | |||
@@ -1,9 +1,5 @@ | |||
1 | #include <assert.h> | 1 | #include <assert.h> |
2 | #ifdef __FreeBSD__ | ||
3 | #include <dev/evdev/input-event-codes.h> | ||
4 | #else | ||
5 | #include <linux/input-event-codes.h> | 2 | #include <linux/input-event-codes.h> |
6 | #endif | ||
7 | #include <stdlib.h> | 3 | #include <stdlib.h> |
8 | #include <wayland-client.h> | 4 | #include <wayland-client.h> |
9 | #include <wayland-cursor.h> | 5 | #include <wayland-cursor.h> |
@@ -26,33 +22,39 @@ void free_hotspots(struct wl_list *list) { | |||
26 | } | 22 | } |
27 | } | 23 | } |
28 | 24 | ||
29 | static enum x11_button wl_button_to_x11_button(uint32_t button) { | 25 | static uint32_t wl_axis_to_button(uint32_t axis, wl_fixed_t value) { |
30 | switch (button) { | 26 | bool negative = wl_fixed_to_double(value) < 0; |
31 | case BTN_LEFT: | ||
32 | return LEFT; | ||
33 | case BTN_MIDDLE: | ||
34 | return MIDDLE; | ||
35 | case BTN_RIGHT: | ||
36 | return RIGHT; | ||
37 | case BTN_SIDE: | ||
38 | return BACK; | ||
39 | case BTN_EXTRA: | ||
40 | return FORWARD; | ||
41 | default: | ||
42 | return NONE; | ||
43 | } | ||
44 | } | ||
45 | |||
46 | static enum x11_button wl_axis_to_x11_button(uint32_t axis, wl_fixed_t value) { | ||
47 | switch (axis) { | 27 | switch (axis) { |
48 | case WL_POINTER_AXIS_VERTICAL_SCROLL: | 28 | case WL_POINTER_AXIS_VERTICAL_SCROLL: |
49 | return wl_fixed_to_double(value) < 0 ? SCROLL_UP : SCROLL_DOWN; | 29 | return negative ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; |
50 | case WL_POINTER_AXIS_HORIZONTAL_SCROLL: | 30 | case WL_POINTER_AXIS_HORIZONTAL_SCROLL: |
51 | return wl_fixed_to_double(value) < 0 ? SCROLL_LEFT : SCROLL_RIGHT; | 31 | return negative ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; |
52 | default: | 32 | default: |
53 | wlr_log(WLR_DEBUG, "Unexpected axis value on mouse scroll"); | 33 | wlr_log(WLR_DEBUG, "Unexpected axis value on mouse scroll"); |
54 | return NONE; | 34 | return 0; |
35 | } | ||
36 | } | ||
37 | |||
38 | void update_cursor(struct swaybar *bar) { | ||
39 | struct swaybar_pointer *pointer = &bar->pointer; | ||
40 | if (pointer->cursor_theme) { | ||
41 | wl_cursor_theme_destroy(pointer->cursor_theme); | ||
55 | } | 42 | } |
43 | int scale = pointer->current ? pointer->current->scale : 1; | ||
44 | pointer->cursor_theme = wl_cursor_theme_load(NULL, 24 * scale, bar->shm); | ||
45 | struct wl_cursor *cursor; | ||
46 | cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); | ||
47 | pointer->cursor_image = cursor->images[0]; | ||
48 | wl_surface_set_buffer_scale(pointer->cursor_surface, scale); | ||
49 | wl_surface_attach(pointer->cursor_surface, | ||
50 | wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); | ||
51 | wl_pointer_set_cursor(pointer->pointer, pointer->serial, | ||
52 | pointer->cursor_surface, | ||
53 | pointer->cursor_image->hotspot_x / scale, | ||
54 | pointer->cursor_image->hotspot_y / scale); | ||
55 | wl_surface_damage_buffer(pointer->cursor_surface, 0, 0, | ||
56 | INT32_MAX, INT32_MAX); | ||
57 | wl_surface_commit(pointer->cursor_surface); | ||
56 | } | 58 | } |
57 | 59 | ||
58 | static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | 60 | static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, |
@@ -60,6 +62,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | |||
60 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | 62 | wl_fixed_t surface_x, wl_fixed_t surface_y) { |
61 | struct swaybar *bar = data; | 63 | struct swaybar *bar = data; |
62 | struct swaybar_pointer *pointer = &bar->pointer; | 64 | struct swaybar_pointer *pointer = &bar->pointer; |
65 | pointer->serial = serial; | ||
63 | struct swaybar_output *output; | 66 | struct swaybar_output *output; |
64 | wl_list_for_each(output, &bar->outputs, link) { | 67 | wl_list_for_each(output, &bar->outputs, link) { |
65 | if (output->surface == surface) { | 68 | if (output->surface == surface) { |
@@ -67,20 +70,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | |||
67 | break; | 70 | break; |
68 | } | 71 | } |
69 | } | 72 | } |
70 | int max_scale = 1; | 73 | update_cursor(bar); |
71 | struct swaybar_output *_output; | ||
72 | wl_list_for_each(_output, &bar->outputs, link) { | ||
73 | if (_output->scale > max_scale) { | ||
74 | max_scale = _output->scale; | ||
75 | } | ||
76 | } | ||
77 | wl_surface_set_buffer_scale(pointer->cursor_surface, max_scale); | ||
78 | wl_surface_attach(pointer->cursor_surface, | ||
79 | wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); | ||
80 | wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface, | ||
81 | pointer->cursor_image->hotspot_x / max_scale, | ||
82 | pointer->cursor_image->hotspot_y / max_scale); | ||
83 | wl_surface_commit(pointer->cursor_surface); | ||
84 | } | 74 | } |
85 | 75 | ||
86 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, | 76 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, |
@@ -96,12 +86,12 @@ static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, | |||
96 | bar->pointer.y = wl_fixed_to_int(surface_y); | 86 | bar->pointer.y = wl_fixed_to_int(surface_y); |
97 | } | 87 | } |
98 | 88 | ||
99 | static bool check_bindings(struct swaybar *bar, uint32_t x11_button, | 89 | static bool check_bindings(struct swaybar *bar, uint32_t button, |
100 | uint32_t state) { | 90 | uint32_t state) { |
101 | bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; | 91 | bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; |
102 | for (int i = 0; i < bar->config->bindings->length; i++) { | 92 | for (int i = 0; i < bar->config->bindings->length; i++) { |
103 | struct swaybar_binding *binding = bar->config->bindings->items[i]; | 93 | struct swaybar_binding *binding = bar->config->bindings->items[i]; |
104 | if (binding->button == x11_button && binding->release == released) { | 94 | if (binding->button == button && binding->release == released) { |
105 | ipc_execute_binding(bar, binding); | 95 | ipc_execute_binding(bar, binding); |
106 | return true; | 96 | return true; |
107 | } | 97 | } |
@@ -118,7 +108,7 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | |||
118 | return; | 108 | return; |
119 | } | 109 | } |
120 | 110 | ||
121 | if (check_bindings(bar, wl_button_to_x11_button(button), state)) { | 111 | if (check_bindings(bar, button, state)) { |
122 | return; | 112 | return; |
123 | } | 113 | } |
124 | 114 | ||
@@ -133,8 +123,8 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | |||
133 | && y >= hotspot->y | 123 | && y >= hotspot->y |
134 | && x < hotspot->x + hotspot->width | 124 | && x < hotspot->x + hotspot->width |
135 | && y < hotspot->y + hotspot->height) { | 125 | && y < hotspot->y + hotspot->height) { |
136 | if (HOTSPOT_IGNORE == hotspot->callback(output, pointer->x, pointer->y, | 126 | if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, |
137 | wl_button_to_x11_button(button), hotspot->data)) { | 127 | pointer->x, pointer->y, button, hotspot->data)) { |
138 | return; | 128 | return; |
139 | } | 129 | } |
140 | } | 130 | } |
@@ -152,7 +142,7 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, | |||
152 | 142 | ||
153 | // If there is a button press binding, execute it, skip default behavior, | 143 | // If there is a button press binding, execute it, skip default behavior, |
154 | // and check button release bindings | 144 | // and check button release bindings |
155 | enum x11_button button = wl_axis_to_x11_button(axis, value); | 145 | uint32_t button = wl_axis_to_button(axis, value); |
156 | if (check_bindings(bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { | 146 | if (check_bindings(bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { |
157 | check_bindings(bar, button, WL_POINTER_BUTTON_STATE_RELEASED); | 147 | check_bindings(bar, button, WL_POINTER_BUTTON_STATE_RELEASED); |
158 | return; | 148 | return; |
@@ -166,8 +156,8 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, | |||
166 | && y >= hotspot->y | 156 | && y >= hotspot->y |
167 | && x < hotspot->x + hotspot->width | 157 | && x < hotspot->x + hotspot->width |
168 | && y < hotspot->y + hotspot->height) { | 158 | && y < hotspot->y + hotspot->height) { |
169 | if (HOTSPOT_IGNORE == hotspot->callback( | 159 | if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, |
170 | output, pointer->x, pointer->y, button, hotspot->data)) { | 160 | pointer->x, pointer->y, button, hotspot->data)) { |
171 | return; | 161 | return; |
172 | } | 162 | } |
173 | } | 163 | } |
diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 706f968d..097f9161 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <wlr/util/log.h> | 6 | #include <wlr/util/log.h> |
7 | #include "swaybar/config.h" | 7 | #include "swaybar/config.h" |
8 | #include "swaybar/ipc.h" | 8 | #include "swaybar/ipc.h" |
9 | #include "config.h" | ||
9 | #include "ipc-client.h" | 10 | #include "ipc-client.h" |
10 | #include "list.h" | 11 | #include "list.h" |
11 | 12 | ||
@@ -153,18 +154,21 @@ static bool ipc_parse_config( | |||
153 | return false; | 154 | return false; |
154 | } | 155 | } |
155 | json_object *markup, *mode, *hidden_state, *position, *status_command; | 156 | json_object *markup, *mode, *hidden_state, *position, *status_command; |
156 | json_object *font, *bar_height, *wrap_scroll, *workspace_buttons, *strip_workspace_numbers; | 157 | json_object *font, *gaps, *bar_height, *wrap_scroll, *workspace_buttons; |
157 | json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol, *outputs; | 158 | json_object *strip_workspace_numbers, *strip_workspace_name; |
158 | json_object *bindings; | 159 | json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol; |
160 | json_object *outputs, *bindings, *status_padding, *status_edge_padding; | ||
159 | json_object_object_get_ex(bar_config, "mode", &mode); | 161 | json_object_object_get_ex(bar_config, "mode", &mode); |
160 | json_object_object_get_ex(bar_config, "hidden_state", &hidden_state); | 162 | json_object_object_get_ex(bar_config, "hidden_state", &hidden_state); |
161 | json_object_object_get_ex(bar_config, "position", &position); | 163 | json_object_object_get_ex(bar_config, "position", &position); |
162 | json_object_object_get_ex(bar_config, "status_command", &status_command); | 164 | json_object_object_get_ex(bar_config, "status_command", &status_command); |
163 | json_object_object_get_ex(bar_config, "font", &font); | 165 | json_object_object_get_ex(bar_config, "font", &font); |
166 | json_object_object_get_ex(bar_config, "gaps", &gaps); | ||
164 | json_object_object_get_ex(bar_config, "bar_height", &bar_height); | 167 | json_object_object_get_ex(bar_config, "bar_height", &bar_height); |
165 | json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll); | 168 | json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll); |
166 | json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons); | 169 | json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons); |
167 | json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers); | 170 | json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers); |
171 | json_object_object_get_ex(bar_config, "strip_workspace_name", &strip_workspace_name); | ||
168 | json_object_object_get_ex(bar_config, "binding_mode_indicator", &binding_mode_indicator); | 172 | json_object_object_get_ex(bar_config, "binding_mode_indicator", &binding_mode_indicator); |
169 | json_object_object_get_ex(bar_config, "verbose", &verbose); | 173 | json_object_object_get_ex(bar_config, "verbose", &verbose); |
170 | json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol); | 174 | json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol); |
@@ -172,6 +176,9 @@ static bool ipc_parse_config( | |||
172 | json_object_object_get_ex(bar_config, "outputs", &outputs); | 176 | json_object_object_get_ex(bar_config, "outputs", &outputs); |
173 | json_object_object_get_ex(bar_config, "pango_markup", &markup); | 177 | json_object_object_get_ex(bar_config, "pango_markup", &markup); |
174 | json_object_object_get_ex(bar_config, "bindings", &bindings); | 178 | json_object_object_get_ex(bar_config, "bindings", &bindings); |
179 | json_object_object_get_ex(bar_config, "status_padding", &status_padding); | ||
180 | json_object_object_get_ex(bar_config, "status_edge_padding", | ||
181 | &status_edge_padding); | ||
175 | if (status_command) { | 182 | if (status_command) { |
176 | free(config->status_command); | 183 | free(config->status_command); |
177 | config->status_command = strdup(json_object_get_string(status_command)); | 184 | config->status_command = strdup(json_object_get_string(status_command)); |
@@ -190,6 +197,9 @@ static bool ipc_parse_config( | |||
190 | if (strip_workspace_numbers) { | 197 | if (strip_workspace_numbers) { |
191 | config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers); | 198 | config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers); |
192 | } | 199 | } |
200 | if (strip_workspace_name) { | ||
201 | config->strip_workspace_name = json_object_get_boolean(strip_workspace_name); | ||
202 | } | ||
193 | if (binding_mode_indicator) { | 203 | if (binding_mode_indicator) { |
194 | config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator); | 204 | config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator); |
195 | } | 205 | } |
@@ -202,6 +212,30 @@ static bool ipc_parse_config( | |||
202 | if (bar_height) { | 212 | if (bar_height) { |
203 | config->height = json_object_get_int(bar_height); | 213 | config->height = json_object_get_int(bar_height); |
204 | } | 214 | } |
215 | if (status_padding) { | ||
216 | config->status_padding = json_object_get_int(status_padding); | ||
217 | } | ||
218 | if (status_edge_padding) { | ||
219 | config->status_edge_padding = json_object_get_int(status_edge_padding); | ||
220 | } | ||
221 | if (gaps) { | ||
222 | json_object *top = json_object_object_get(gaps, "top"); | ||
223 | if (top) { | ||
224 | config->gaps.top = json_object_get_int(top); | ||
225 | } | ||
226 | json_object *right = json_object_object_get(gaps, "right"); | ||
227 | if (right) { | ||
228 | config->gaps.right = json_object_get_int(right); | ||
229 | } | ||
230 | json_object *bottom = json_object_object_get(gaps, "bottom"); | ||
231 | if (bottom) { | ||
232 | config->gaps.bottom = json_object_get_int(bottom); | ||
233 | } | ||
234 | json_object *left = json_object_object_get(gaps, "left"); | ||
235 | if (left) { | ||
236 | config->gaps.left = json_object_get_int(left); | ||
237 | } | ||
238 | } | ||
205 | if (markup) { | 239 | if (markup) { |
206 | config->pango_markup = json_object_get_boolean(markup); | 240 | config->pango_markup = json_object_get_boolean(markup); |
207 | } | 241 | } |
@@ -212,7 +246,7 @@ static bool ipc_parse_config( | |||
212 | struct swaybar_binding *binding = | 246 | struct swaybar_binding *binding = |
213 | calloc(1, sizeof(struct swaybar_binding)); | 247 | calloc(1, sizeof(struct swaybar_binding)); |
214 | binding->button = json_object_get_int( | 248 | binding->button = json_object_get_int( |
215 | json_object_object_get(bindobj, "input_code")); | 249 | json_object_object_get(bindobj, "event_code")); |
216 | binding->command = strdup(json_object_get_string( | 250 | binding->command = strdup(json_object_get_string( |
217 | json_object_object_get(bindobj, "command"))); | 251 | json_object_object_get(bindobj, "command"))); |
218 | binding->release = json_object_get_boolean( | 252 | binding->release = json_object_get_boolean( |
@@ -258,6 +292,40 @@ static bool ipc_parse_config( | |||
258 | ipc_parse_colors(config, colors); | 292 | ipc_parse_colors(config, colors); |
259 | } | 293 | } |
260 | 294 | ||
295 | #if HAVE_TRAY | ||
296 | json_object *tray_outputs, *tray_padding, *tray_bindings, *icon_theme; | ||
297 | |||
298 | if ((json_object_object_get_ex(bar_config, "tray_outputs", &tray_outputs))) { | ||
299 | config->tray_outputs = create_list(); | ||
300 | int length = json_object_array_length(tray_outputs); | ||
301 | for (int i = 0; i < length; ++i) { | ||
302 | json_object *o = json_object_array_get_idx(tray_outputs, i); | ||
303 | list_add(config->tray_outputs, strdup(json_object_get_string(o))); | ||
304 | } | ||
305 | config->tray_hidden = strcmp(config->tray_outputs->items[0], "none") == 0; | ||
306 | } | ||
307 | |||
308 | if ((json_object_object_get_ex(bar_config, "tray_padding", &tray_padding))) { | ||
309 | config->tray_padding = json_object_get_int(tray_padding); | ||
310 | } | ||
311 | |||
312 | if ((json_object_object_get_ex(bar_config, "tray_bindings", &tray_bindings))) { | ||
313 | int length = json_object_array_length(tray_bindings); | ||
314 | for (int i = 0; i < length; ++i) { | ||
315 | json_object *bind = json_object_array_get_idx(tray_bindings, i); | ||
316 | json_object *button, *command; | ||
317 | json_object_object_get_ex(bind, "input_code", &button); | ||
318 | json_object_object_get_ex(bind, "command", &command); | ||
319 | config->tray_bindings[json_object_get_int(button)] = | ||
320 | strdup(json_object_get_string(command)); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | if ((json_object_object_get_ex(bar_config, "icon_theme", &icon_theme))) { | ||
325 | config->icon_theme = strdup(json_object_get_string(icon_theme)); | ||
326 | } | ||
327 | #endif | ||
328 | |||
261 | json_object_put(bar_config); | 329 | json_object_put(bar_config); |
262 | return true; | 330 | return true; |
263 | } | 331 | } |
@@ -298,6 +366,24 @@ bool ipc_get_workspaces(struct swaybar *bar) { | |||
298 | calloc(1, sizeof(struct swaybar_workspace)); | 366 | calloc(1, sizeof(struct swaybar_workspace)); |
299 | ws->num = json_object_get_int(num); | 367 | ws->num = json_object_get_int(num); |
300 | ws->name = strdup(json_object_get_string(name)); | 368 | ws->name = strdup(json_object_get_string(name)); |
369 | ws->label = strdup(ws->name); | ||
370 | // ws->num will be -1 if workspace name doesn't begin with int. | ||
371 | if (ws->num != -1) { | ||
372 | size_t len_offset = numlen(ws->num); | ||
373 | if (bar->config->strip_workspace_name) { | ||
374 | free(ws->label); | ||
375 | ws->label = malloc(len_offset + 1 * sizeof(char)); | ||
376 | ws->label[len_offset] = '\0'; | ||
377 | strncpy(ws->label, ws->name, len_offset); | ||
378 | } else if (bar->config->strip_workspace_numbers) { | ||
379 | len_offset += ws->label[len_offset] == ':'; | ||
380 | if (strlen(ws->name) > len_offset) { | ||
381 | free(ws->label); | ||
382 | // Strip number prefix [1-?:] using len_offset. | ||
383 | ws->label = strdup(ws->name + len_offset); | ||
384 | } | ||
385 | } | ||
386 | } | ||
301 | ws->visible = json_object_get_boolean(visible); | 387 | ws->visible = json_object_get_boolean(visible); |
302 | ws->focused = json_object_get_boolean(focused); | 388 | ws->focused = json_object_get_boolean(focused); |
303 | if (ws->focused) { | 389 | if (ws->focused) { |
@@ -423,6 +509,27 @@ static bool handle_barconfig_update(struct swaybar *bar, | |||
423 | config->mode = strdup(json_object_get_string(json_mode)); | 509 | config->mode = strdup(json_object_get_string(json_mode)); |
424 | wlr_log(WLR_DEBUG, "Changing bar mode to %s", config->mode); | 510 | wlr_log(WLR_DEBUG, "Changing bar mode to %s", config->mode); |
425 | 511 | ||
512 | json_object *gaps; | ||
513 | json_object_object_get_ex(json_config, "gaps", &gaps); | ||
514 | if (gaps) { | ||
515 | json_object *top = json_object_object_get(gaps, "top"); | ||
516 | if (top) { | ||
517 | config->gaps.top = json_object_get_int(top); | ||
518 | } | ||
519 | json_object *right = json_object_object_get(gaps, "right"); | ||
520 | if (right) { | ||
521 | config->gaps.right = json_object_get_int(right); | ||
522 | } | ||
523 | json_object *bottom = json_object_object_get(gaps, "bottom"); | ||
524 | if (bottom) { | ||
525 | config->gaps.bottom = json_object_get_int(bottom); | ||
526 | } | ||
527 | json_object *left = json_object_object_get(gaps, "left"); | ||
528 | if (left) { | ||
529 | config->gaps.left = json_object_get_int(left); | ||
530 | } | ||
531 | } | ||
532 | |||
426 | return determine_bar_visibility(bar, true); | 533 | return determine_bar_visibility(bar, true); |
427 | } | 534 | } |
428 | 535 | ||
diff --git a/swaybar/main.c b/swaybar/main.c index 2672abef..fa99b1ba 100644 --- a/swaybar/main.c +++ b/swaybar/main.c | |||
@@ -1,4 +1,4 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <stdio.h> | 2 | #include <stdio.h> |
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <string.h> | 4 | #include <string.h> |
@@ -76,7 +76,7 @@ int main(int argc, char **argv) { | |||
76 | if (debug) { | 76 | if (debug) { |
77 | wlr_log_init(WLR_DEBUG, NULL); | 77 | wlr_log_init(WLR_DEBUG, NULL); |
78 | } else { | 78 | } else { |
79 | wlr_log_init(WLR_ERROR, NULL); | 79 | wlr_log_init(WLR_INFO, NULL); |
80 | } | 80 | } |
81 | 81 | ||
82 | if (!swaybar.id) { | 82 | if (!swaybar.id) { |
diff --git a/swaybar/meson.build b/swaybar/meson.build index c27cf2c2..312ca97b 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build | |||
@@ -1,3 +1,32 @@ | |||
1 | tray_files = get_option('enable-tray') ? [ | ||
2 | 'tray/host.c', | ||
3 | 'tray/icon.c', | ||
4 | 'tray/item.c', | ||
5 | 'tray/tray.c', | ||
6 | 'tray/watcher.c' | ||
7 | ] : [] | ||
8 | |||
9 | swaybar_deps = [ | ||
10 | cairo, | ||
11 | client_protos, | ||
12 | gdk_pixbuf, | ||
13 | jsonc, | ||
14 | math, | ||
15 | pango, | ||
16 | pangocairo, | ||
17 | rt, | ||
18 | wayland_client, | ||
19 | wayland_cursor, | ||
20 | wlroots, | ||
21 | ] | ||
22 | if get_option('enable-tray') | ||
23 | if systemd.found() | ||
24 | swaybar_deps += systemd | ||
25 | elif elogind.found() | ||
26 | swaybar_deps += elogind | ||
27 | endif | ||
28 | endif | ||
29 | |||
1 | executable( | 30 | executable( |
2 | 'swaybar', [ | 31 | 'swaybar', [ |
3 | 'bar.c', | 32 | 'bar.c', |
@@ -8,21 +37,10 @@ executable( | |||
8 | 'main.c', | 37 | 'main.c', |
9 | 'render.c', | 38 | 'render.c', |
10 | 'status_line.c', | 39 | 'status_line.c', |
40 | tray_files | ||
11 | ], | 41 | ], |
12 | include_directories: [sway_inc], | 42 | include_directories: [sway_inc], |
13 | dependencies: [ | 43 | dependencies: swaybar_deps, |
14 | cairo, | ||
15 | client_protos, | ||
16 | gdk_pixbuf, | ||
17 | jsonc, | ||
18 | math, | ||
19 | pango, | ||
20 | pangocairo, | ||
21 | rt, | ||
22 | wayland_client, | ||
23 | wayland_cursor, | ||
24 | wlroots, | ||
25 | ], | ||
26 | link_with: [lib_sway_common, lib_sway_client], | 44 | link_with: [lib_sway_common, lib_sway_client], |
27 | install_rpath : rpathdir, | 45 | install_rpath : rpathdir, |
28 | install: true | 46 | install: true |
diff --git a/swaybar/render.c b/swaybar/render.c index 4ebf922e..55f680ed 100644 --- a/swaybar/render.c +++ b/swaybar/render.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <assert.h> | 2 | #include <assert.h> |
3 | #include <linux/input-event-codes.h> | ||
3 | #include <limits.h> | 4 | #include <limits.h> |
4 | #include <stdlib.h> | 5 | #include <stdlib.h> |
5 | #include <stdint.h> | 6 | #include <stdint.h> |
@@ -14,6 +15,9 @@ | |||
14 | #include "swaybar/ipc.h" | 15 | #include "swaybar/ipc.h" |
15 | #include "swaybar/render.h" | 16 | #include "swaybar/render.h" |
16 | #include "swaybar/status_line.h" | 17 | #include "swaybar/status_line.h" |
18 | #if HAVE_TRAY | ||
19 | #include "swaybar/tray/tray.h" | ||
20 | #endif | ||
17 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 21 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
18 | 22 | ||
19 | static const int WS_HORIZONTAL_PADDING = 5; | 23 | static const int WS_HORIZONTAL_PADDING = 5; |
@@ -32,7 +36,8 @@ static uint32_t render_status_line_error(cairo_t *cairo, | |||
32 | cairo_set_source_u32(cairo, 0xFF0000FF); | 36 | cairo_set_source_u32(cairo, 0xFF0000FF); |
33 | 37 | ||
34 | int margin = 3 * output->scale; | 38 | int margin = 3 * output->scale; |
35 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 39 | double ws_vertical_padding = |
40 | output->bar->config->status_padding * output->scale; | ||
36 | 41 | ||
37 | char *font = output->bar->config->font; | 42 | char *font = output->bar->config->font; |
38 | int text_width, text_height; | 43 | int text_width, text_height; |
@@ -41,7 +46,8 @@ static uint32_t render_status_line_error(cairo_t *cairo, | |||
41 | 46 | ||
42 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 47 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
43 | uint32_t ideal_surface_height = ideal_height / output->scale; | 48 | uint32_t ideal_surface_height = ideal_height / output->scale; |
44 | if (output->height < ideal_surface_height) { | 49 | if (!output->bar->config->height && |
50 | output->height < ideal_surface_height) { | ||
45 | return ideal_surface_height; | 51 | return ideal_surface_height; |
46 | } | 52 | } |
47 | *x -= text_width + margin; | 53 | *x -= text_width + margin; |
@@ -68,12 +74,13 @@ static uint32_t render_status_line_text(cairo_t *cairo, | |||
68 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 74 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, |
69 | output->scale, config->pango_markup, "%s", text); | 75 | output->scale, config->pango_markup, "%s", text); |
70 | 76 | ||
71 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 77 | double ws_vertical_padding = config->status_padding * output->scale; |
72 | int margin = 3 * output->scale; | 78 | int margin = 3 * output->scale; |
73 | 79 | ||
74 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 80 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
75 | uint32_t ideal_surface_height = ideal_height / output->scale; | 81 | uint32_t ideal_surface_height = ideal_height / output->scale; |
76 | if (output->height < ideal_surface_height) { | 82 | if (!output->bar->config->height && |
83 | output->height < ideal_surface_height) { | ||
77 | return ideal_surface_height; | 84 | return ideal_surface_height; |
78 | } | 85 | } |
79 | 86 | ||
@@ -87,13 +94,24 @@ static uint32_t render_status_line_text(cairo_t *cairo, | |||
87 | return output->height; | 94 | return output->height; |
88 | } | 95 | } |
89 | 96 | ||
90 | static void render_sharp_line(cairo_t *cairo, uint32_t color, | 97 | static void render_sharp_rectangle(cairo_t *cairo, uint32_t color, |
91 | double x, double y, double width, double height) { | 98 | double x, double y, double width, double height) { |
99 | cairo_save(cairo); | ||
92 | cairo_set_source_u32(cairo, color); | 100 | cairo_set_source_u32(cairo, color); |
101 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); | ||
102 | cairo_rectangle(cairo, x, y, width, height); | ||
103 | cairo_fill(cairo); | ||
104 | cairo_restore(cairo); | ||
105 | } | ||
106 | |||
107 | static void render_sharp_line(cairo_t *cairo, uint32_t color, | ||
108 | double x, double y, double width, double height) { | ||
93 | if (width > 1 && height > 1) { | 109 | if (width > 1 && height > 1) { |
94 | cairo_rectangle(cairo, x, y, width, height); | 110 | render_sharp_rectangle(cairo, color, x, y, width, height); |
95 | cairo_fill(cairo); | ||
96 | } else { | 111 | } else { |
112 | cairo_save(cairo); | ||
113 | cairo_set_source_u32(cairo, color); | ||
114 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); | ||
97 | if (width == 1) { | 115 | if (width == 1) { |
98 | x += 0.5; | 116 | x += 0.5; |
99 | height += y; | 117 | height += y; |
@@ -108,14 +126,17 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, | |||
108 | cairo_set_line_width(cairo, 1.0); | 126 | cairo_set_line_width(cairo, 1.0); |
109 | cairo_line_to(cairo, width, height); | 127 | cairo_line_to(cairo, width, height); |
110 | cairo_stroke(cairo); | 128 | cairo_stroke(cairo); |
129 | cairo_restore(cairo); | ||
111 | } | 130 | } |
112 | } | 131 | } |
113 | 132 | ||
114 | static enum hotspot_event_handling block_hotspot_callback(struct swaybar_output *output, | 133 | static enum hotspot_event_handling block_hotspot_callback( |
115 | int x, int y, enum x11_button button, void *data) { | 134 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
135 | int x, int y, uint32_t button, void *data) { | ||
116 | struct i3bar_block *block = data; | 136 | struct i3bar_block *block = data; |
117 | struct status_line *status = output->bar->status; | 137 | struct status_line *status = output->bar->status; |
118 | return i3bar_block_send_click(status, block, x, y, button); | 138 | return i3bar_block_send_click(status, block, x, y, x - hotspot->x, |
139 | y - hotspot->y, hotspot->width, hotspot->height, button); | ||
119 | } | 140 | } |
120 | 141 | ||
121 | static void i3bar_block_unref_callback(void *data) { | 142 | static void i3bar_block_unref_callback(void *data) { |
@@ -136,7 +157,7 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
136 | output->scale, block->markup, "%s", block->full_text); | 157 | output->scale, block->markup, "%s", block->full_text); |
137 | 158 | ||
138 | int margin = 3 * output->scale; | 159 | int margin = 3 * output->scale; |
139 | int ws_vertical_padding = WS_VERTICAL_PADDING * 2; | 160 | double ws_vertical_padding = config->status_padding * output->scale; |
140 | 161 | ||
141 | int width = text_width; | 162 | int width = text_width; |
142 | if (width < block->min_width) { | 163 | if (width < block->min_width) { |
@@ -146,37 +167,40 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
146 | double block_width = width; | 167 | double block_width = width; |
147 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | 168 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; |
148 | uint32_t ideal_surface_height = ideal_height / output->scale; | 169 | uint32_t ideal_surface_height = ideal_height / output->scale; |
149 | if (output->height < ideal_surface_height) { | 170 | if (!output->bar->config->height && |
171 | output->height < ideal_surface_height) { | ||
150 | return ideal_surface_height; | 172 | return ideal_surface_height; |
151 | } | 173 | } |
152 | 174 | ||
153 | *x -= width; | 175 | *x -= width; |
154 | if (block->border && block->border_left > 0) { | 176 | if ((block->border || block->urgent) && block->border_left > 0) { |
155 | *x -= (block->border_left + margin); | 177 | *x -= (block->border_left * output->scale + margin); |
156 | block_width += block->border_left + margin; | 178 | block_width += block->border_left * output->scale + margin; |
157 | } | 179 | } |
158 | if (block->border && block->border_right > 0) { | 180 | if ((block->border || block->urgent) && block->border_right > 0) { |
159 | *x -= (block->border_right + margin); | 181 | *x -= (block->border_right * output->scale + margin); |
160 | block_width += block->border_right + margin; | 182 | block_width += block->border_right * output->scale + margin; |
161 | } | 183 | } |
162 | 184 | ||
163 | int sep_width, sep_height; | 185 | int sep_width, sep_height; |
186 | int sep_block_width = block->separator_block_width; | ||
164 | if (!edge) { | 187 | if (!edge) { |
165 | if (config->sep_symbol) { | 188 | if (config->sep_symbol) { |
166 | get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, | 189 | get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, |
167 | output->scale, false, "%s", config->sep_symbol); | 190 | output->scale, false, "%s", config->sep_symbol); |
168 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; | 191 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; |
169 | uint32_t _ideal_surface_height = _ideal_height / output->scale; | 192 | uint32_t _ideal_surface_height = _ideal_height / output->scale; |
170 | if (output->height < _ideal_surface_height) { | 193 | if (!output->bar->config->height && |
194 | output->height < _ideal_surface_height) { | ||
171 | return _ideal_surface_height; | 195 | return _ideal_surface_height; |
172 | } | 196 | } |
173 | if (sep_width > block->separator_block_width) { | 197 | if (sep_width > sep_block_width) { |
174 | block->separator_block_width = sep_width + margin * 2; | 198 | sep_block_width = sep_width + margin * 2; |
175 | } | 199 | } |
176 | } | 200 | } |
177 | *x -= block->separator_block_width; | 201 | *x -= sep_block_width; |
178 | } else { | 202 | } else if (config->status_edge_padding) { |
179 | *x -= margin; | 203 | *x -= config->status_edge_padding * output->scale; |
180 | } | 204 | } |
181 | 205 | ||
182 | uint32_t height = output->height * output->scale; | 206 | uint32_t height = output->height * output->scale; |
@@ -193,53 +217,55 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
193 | wl_list_insert(&output->hotspots, &hotspot->link); | 217 | wl_list_insert(&output->hotspots, &hotspot->link); |
194 | } | 218 | } |
195 | 219 | ||
196 | double pos = *x; | 220 | double x_pos = *x; |
197 | if (block->background) { | 221 | double y_pos = ws_vertical_padding; |
198 | cairo_set_source_u32(cairo, block->background); | 222 | double render_height = height - ws_vertical_padding * 2; |
199 | cairo_rectangle(cairo, pos - 0.5 * output->scale, | 223 | |
200 | output->scale, width, height); | 224 | uint32_t bg_color = block->urgent |
201 | cairo_fill(cairo); | 225 | ? config->colors.urgent_workspace.background : block->background; |
226 | if (bg_color) { | ||
227 | render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, | ||
228 | block_width, render_height); | ||
202 | } | 229 | } |
203 | 230 | ||
204 | if (block->border && block->border_top > 0) { | 231 | uint32_t border_color = block->urgent |
205 | render_sharp_line(cairo, block->border, | 232 | ? config->colors.urgent_workspace.border : block->border; |
206 | pos - 0.5 * output->scale, output->scale, | 233 | if (border_color && block->border_top > 0) { |
207 | block_width, block->border_top); | 234 | render_sharp_line(cairo, border_color, x_pos, y_pos, |
235 | block_width, block->border_top * output->scale); | ||
208 | } | 236 | } |
209 | if (block->border && block->border_bottom > 0) { | 237 | if (border_color && block->border_bottom > 0) { |
210 | render_sharp_line(cairo, block->border, | 238 | render_sharp_line(cairo, border_color, x_pos, |
211 | pos - 0.5 * output->scale, | 239 | y_pos + render_height - block->border_bottom * output->scale, |
212 | height - output->scale - block->border_bottom, | 240 | block_width, block->border_bottom * output->scale); |
213 | block_width, block->border_bottom); | ||
214 | } | 241 | } |
215 | if (block->border != 0 && block->border_left > 0) { | 242 | if (border_color && block->border_left > 0) { |
216 | render_sharp_line(cairo, block->border, | 243 | render_sharp_line(cairo, border_color, x_pos, y_pos, |
217 | pos - 0.5 * output->scale, output->scale, | 244 | block->border_left * output->scale, render_height); |
218 | block->border_left, height); | 245 | x_pos += block->border_left * output->scale + margin; |
219 | pos += block->border_left + margin; | ||
220 | } | 246 | } |
221 | 247 | ||
222 | double offset = 0; | 248 | double offset = 0; |
223 | if (strncmp(block->align, "left", 5) == 0) { | 249 | if (strncmp(block->align, "left", 5) == 0) { |
224 | offset = pos; | 250 | offset = x_pos; |
225 | } else if (strncmp(block->align, "right", 5) == 0) { | 251 | } else if (strncmp(block->align, "right", 5) == 0) { |
226 | offset = pos + width - text_width; | 252 | offset = x_pos + width - text_width; |
227 | } else if (strncmp(block->align, "center", 6) == 0) { | 253 | } else if (strncmp(block->align, "center", 6) == 0) { |
228 | offset = pos + (width - text_width) / 2; | 254 | offset = x_pos + (width - text_width) / 2; |
229 | } | 255 | } |
230 | cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0); | 256 | cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0); |
231 | uint32_t color = block->color ? *block->color : config->colors.statusline; | 257 | uint32_t color = block->color ? *block->color : config->colors.statusline; |
258 | color = block->urgent ? config->colors.urgent_workspace.text : color; | ||
232 | cairo_set_source_u32(cairo, color); | 259 | cairo_set_source_u32(cairo, color); |
233 | pango_printf(cairo, config->font, output->scale, | 260 | pango_printf(cairo, config->font, output->scale, |
234 | block->markup, "%s", block->full_text); | 261 | block->markup, "%s", block->full_text); |
235 | pos += width; | 262 | x_pos += width; |
236 | 263 | ||
237 | if (block->border && block->border_right > 0) { | 264 | if (block->border && block->border_right > 0) { |
238 | pos += margin; | 265 | x_pos += margin; |
239 | render_sharp_line(cairo, block->border, | 266 | render_sharp_line(cairo, border_color, x_pos, y_pos, |
240 | pos - 0.5 * output->scale, output->scale, | 267 | block->border_right * output->scale, render_height); |
241 | block->border_right, height); | 268 | x_pos += block->border_right * output->scale; |
242 | pos += block->border_right; | ||
243 | } | 269 | } |
244 | 270 | ||
245 | if (!edge && block->separator) { | 271 | if (!edge && block->separator) { |
@@ -249,16 +275,14 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
249 | cairo_set_source_u32(cairo, config->colors.separator); | 275 | cairo_set_source_u32(cairo, config->colors.separator); |
250 | } | 276 | } |
251 | if (config->sep_symbol) { | 277 | if (config->sep_symbol) { |
252 | offset = pos + (block->separator_block_width - sep_width) / 2; | 278 | offset = x_pos + (sep_block_width - sep_width) / 2; |
253 | cairo_move_to(cairo, offset, height / 2.0 - sep_height / 2.0); | 279 | cairo_move_to(cairo, offset, height / 2.0 - sep_height / 2.0); |
254 | pango_printf(cairo, config->font, output->scale, false, | 280 | pango_printf(cairo, config->font, output->scale, false, |
255 | "%s", config->sep_symbol); | 281 | "%s", config->sep_symbol); |
256 | } else { | 282 | } else { |
257 | cairo_set_line_width(cairo, 1); | 283 | cairo_set_line_width(cairo, 1); |
258 | cairo_move_to(cairo, | 284 | cairo_move_to(cairo, x_pos + sep_block_width / 2, margin); |
259 | pos + block->separator_block_width / 2, margin); | 285 | cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin); |
260 | cairo_line_to(cairo, | ||
261 | pos + block->separator_block_width / 2, height - margin); | ||
262 | cairo_stroke(cairo); | 286 | cairo_stroke(cairo); |
263 | } | 287 | } |
264 | } | 288 | } |
@@ -268,7 +292,7 @@ static uint32_t render_status_block(cairo_t *cairo, | |||
268 | static uint32_t render_status_line_i3bar(cairo_t *cairo, | 292 | static uint32_t render_status_line_i3bar(cairo_t *cairo, |
269 | struct swaybar_output *output, double *x) { | 293 | struct swaybar_output *output, double *x) { |
270 | uint32_t max_height = 0; | 294 | uint32_t max_height = 0; |
271 | bool edge = true; | 295 | bool edge = *x == output->width * output->scale; |
272 | struct i3bar_block *block; | 296 | struct i3bar_block *block; |
273 | wl_list_for_each(block, &output->bar->status->blocks, link) { | 297 | wl_list_for_each(block, &output->bar->status->blocks, link) { |
274 | uint32_t h = render_status_block(cairo, output, block, x, edge); | 298 | uint32_t h = render_status_block(cairo, output, block, x, edge); |
@@ -314,7 +338,8 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, | |||
314 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 | 338 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 |
315 | + border_width * 2; | 339 | + border_width * 2; |
316 | uint32_t ideal_surface_height = ideal_height / output->scale; | 340 | uint32_t ideal_surface_height = ideal_height / output->scale; |
317 | if (output->height < ideal_surface_height) { | 341 | if (!output->bar->config->height && |
342 | output->height < ideal_surface_height) { | ||
318 | return ideal_surface_height; | 343 | return ideal_surface_height; |
319 | } | 344 | } |
320 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | 345 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; |
@@ -342,22 +367,10 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, | |||
342 | return output->height; | 367 | return output->height; |
343 | } | 368 | } |
344 | 369 | ||
345 | static const char *strip_workspace_number(const char *ws_name) { | 370 | static enum hotspot_event_handling workspace_hotspot_callback( |
346 | size_t len = strlen(ws_name); | 371 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, |
347 | for (size_t i = 0; i < len; ++i) { | 372 | int x, int y, uint32_t button, void *data) { |
348 | if (ws_name[i] < '0' || ws_name[i] > '9') { | 373 | if (button != BTN_LEFT) { |
349 | if (':' == ws_name[i] && i < len - 1 && i > 0) { | ||
350 | return ws_name + i + 1; | ||
351 | } | ||
352 | return ws_name; | ||
353 | } | ||
354 | } | ||
355 | return ws_name; | ||
356 | } | ||
357 | |||
358 | static enum hotspot_event_handling workspace_hotspot_callback(struct swaybar_output *output, | ||
359 | int x, int y, enum x11_button button, void *data) { | ||
360 | if (button != LEFT) { | ||
361 | return HOTSPOT_PROCESS; | 374 | return HOTSPOT_PROCESS; |
362 | } | 375 | } |
363 | ipc_send_workspace_command(output->bar, (const char *)data); | 376 | ipc_send_workspace_command(output->bar, (const char *)data); |
@@ -368,11 +381,6 @@ static uint32_t render_workspace_button(cairo_t *cairo, | |||
368 | struct swaybar_output *output, | 381 | struct swaybar_output *output, |
369 | struct swaybar_workspace *ws, double *x) { | 382 | struct swaybar_workspace *ws, double *x) { |
370 | struct swaybar_config *config = output->bar->config; | 383 | struct swaybar_config *config = output->bar->config; |
371 | const char *name = ws->name; | ||
372 | if (config->strip_workspace_numbers) { | ||
373 | name = strip_workspace_number(ws->name); | ||
374 | } | ||
375 | |||
376 | struct box_colors box_colors; | 384 | struct box_colors box_colors; |
377 | if (ws->urgent) { | 385 | if (ws->urgent) { |
378 | box_colors = config->colors.urgent_workspace; | 386 | box_colors = config->colors.urgent_workspace; |
@@ -388,7 +396,7 @@ static uint32_t render_workspace_button(cairo_t *cairo, | |||
388 | 396 | ||
389 | int text_width, text_height; | 397 | int text_width, text_height; |
390 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, | 398 | get_text_size(cairo, config->font, &text_width, &text_height, NULL, |
391 | output->scale, config->pango_markup, "%s", name); | 399 | output->scale, config->pango_markup, "%s", ws->label); |
392 | 400 | ||
393 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | 401 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; |
394 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | 402 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; |
@@ -397,7 +405,8 @@ static uint32_t render_workspace_button(cairo_t *cairo, | |||
397 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height | 405 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height |
398 | + border_width * 2; | 406 | + border_width * 2; |
399 | uint32_t ideal_surface_height = ideal_height / output->scale; | 407 | uint32_t ideal_surface_height = ideal_height / output->scale; |
400 | if (output->height < ideal_surface_height) { | 408 | if (!output->bar->config->height && |
409 | output->height < ideal_surface_height) { | ||
401 | return ideal_surface_height; | 410 | return ideal_surface_height; |
402 | } | 411 | } |
403 | 412 | ||
@@ -421,7 +430,7 @@ static uint32_t render_workspace_button(cairo_t *cairo, | |||
421 | cairo_set_source_u32(cairo, box_colors.text); | 430 | cairo_set_source_u32(cairo, box_colors.text); |
422 | cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); | 431 | cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); |
423 | pango_printf(cairo, config->font, output->scale, config->pango_markup, | 432 | pango_printf(cairo, config->font, output->scale, config->pango_markup, |
424 | "%s", name); | 433 | "%s", ws->label); |
425 | 434 | ||
426 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); | 435 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); |
427 | hotspot->x = *x; | 436 | hotspot->x = *x; |
@@ -459,6 +468,12 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { | |||
459 | * utilize the available space. | 468 | * utilize the available space. |
460 | */ | 469 | */ |
461 | double x = output->width * output->scale; | 470 | double x = output->width * output->scale; |
471 | #if HAVE_TRAY | ||
472 | if (bar->tray) { | ||
473 | uint32_t h = render_tray(cairo, output, &x); | ||
474 | max_height = h > max_height ? h : max_height; | ||
475 | } | ||
476 | #endif | ||
462 | if (bar->status) { | 477 | if (bar->status) { |
463 | uint32_t h = render_status_line(cairo, output, &x); | 478 | uint32_t h = render_status_line(cairo, output, &x); |
464 | max_height = h > max_height ? h : max_height; | 479 | max_height = h > max_height ? h : max_height; |
@@ -518,12 +533,17 @@ void render_frame(struct swaybar_output *output) { | |||
518 | cairo_restore(cairo); | 533 | cairo_restore(cairo); |
519 | uint32_t height = render_to_cairo(cairo, output); | 534 | uint32_t height = render_to_cairo(cairo, output); |
520 | int config_height = output->bar->config->height; | 535 | int config_height = output->bar->config->height; |
521 | if (config_height >= 0 && height < (uint32_t)config_height) { | 536 | if (config_height > 0) { |
522 | height = config_height; | 537 | height = config_height; |
523 | } | 538 | } |
524 | if (height != output->height || output->width == 0) { | 539 | if (height != output->height || output->width == 0) { |
525 | // Reconfigure surface | 540 | // Reconfigure surface |
526 | zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height); | 541 | zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height); |
542 | zwlr_layer_surface_v1_set_margin(output->layer_surface, | ||
543 | output->bar->config->gaps.top, | ||
544 | output->bar->config->gaps.right, | ||
545 | output->bar->config->gaps.bottom, | ||
546 | output->bar->config->gaps.left); | ||
527 | if (strcmp(output->bar->config->mode, "dock") == 0) { | 547 | if (strcmp(output->bar->config->mode, "dock") == 0) { |
528 | zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height); | 548 | zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height); |
529 | } | 549 | } |
diff --git a/swaybar/status_line.c b/swaybar/status_line.c index 744d2785..f0e2c300 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c | |||
@@ -12,7 +12,6 @@ | |||
12 | #include "swaybar/config.h" | 12 | #include "swaybar/config.h" |
13 | #include "swaybar/i3bar.h" | 13 | #include "swaybar/i3bar.h" |
14 | #include "swaybar/status_line.h" | 14 | #include "swaybar/status_line.h" |
15 | #include "readline.h" | ||
16 | 15 | ||
17 | static void status_line_close_fds(struct status_line *status) { | 16 | static void status_line_close_fds(struct status_line *status) { |
18 | if (status->read_fd != -1) { | 17 | if (status->read_fd != -1) { |
@@ -185,7 +184,6 @@ void status_line_free(struct status_line *status) { | |||
185 | } | 184 | } |
186 | free(status->read); | 185 | free(status->read); |
187 | free(status->write); | 186 | free(status->write); |
188 | free((char*) status->text); | ||
189 | free(status->buffer); | 187 | free(status->buffer); |
190 | free(status); | 188 | free(status); |
191 | } | 189 | } |
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c new file mode 100644 index 00000000..cc8dd188 --- /dev/null +++ b/swaybar/tray/host.c | |||
@@ -0,0 +1,210 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | #include <unistd.h> | ||
7 | #include "swaybar/bar.h" | ||
8 | #include "swaybar/tray/host.h" | ||
9 | #include "swaybar/tray/item.h" | ||
10 | #include "swaybar/tray/tray.h" | ||
11 | #include "list.h" | ||
12 | #include "log.h" | ||
13 | |||
14 | static const char *watcher_path = "/StatusNotifierWatcher"; | ||
15 | |||
16 | static int cmp_sni_id(const void *item, const void *cmp_to) { | ||
17 | const struct swaybar_sni *sni = item; | ||
18 | return strcmp(sni->watcher_id, cmp_to); | ||
19 | } | ||
20 | |||
21 | static void add_sni(struct swaybar_tray *tray, char *id) { | ||
22 | int idx = list_seq_find(tray->items, cmp_sni_id, id); | ||
23 | if (idx == -1) { | ||
24 | wlr_log(WLR_INFO, "Registering Status Notifier Item '%s'", id); | ||
25 | struct swaybar_sni *sni = create_sni(id, tray); | ||
26 | if (sni) { | ||
27 | list_add(tray->items, sni); | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | |||
32 | static int handle_sni_registered(sd_bus_message *msg, void *data, | ||
33 | sd_bus_error *error) { | ||
34 | char *id; | ||
35 | int ret = sd_bus_message_read(msg, "s", &id); | ||
36 | if (ret < 0) { | ||
37 | wlr_log(WLR_ERROR, "Failed to parse register SNI message: %s", strerror(-ret)); | ||
38 | } | ||
39 | |||
40 | struct swaybar_tray *tray = data; | ||
41 | add_sni(tray, id); | ||
42 | |||
43 | return ret; | ||
44 | } | ||
45 | |||
46 | static int handle_sni_unregistered(sd_bus_message *msg, void *data, | ||
47 | sd_bus_error *error) { | ||
48 | char *id; | ||
49 | int ret = sd_bus_message_read(msg, "s", &id); | ||
50 | if (ret < 0) { | ||
51 | wlr_log(WLR_ERROR, "Failed to parse unregister SNI message: %s", strerror(-ret)); | ||
52 | } | ||
53 | |||
54 | struct swaybar_tray *tray = data; | ||
55 | int idx = list_seq_find(tray->items, cmp_sni_id, id); | ||
56 | if (idx != -1) { | ||
57 | wlr_log(WLR_INFO, "Unregistering Status Notifier Item '%s'", id); | ||
58 | destroy_sni(tray->items->items[idx]); | ||
59 | list_del(tray->items, idx); | ||
60 | set_bar_dirty(tray->bar); | ||
61 | } | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | static int get_registered_snis_callback(sd_bus_message *msg, void *data, | ||
66 | sd_bus_error *error) { | ||
67 | if (sd_bus_message_is_method_error(msg, NULL)) { | ||
68 | sd_bus_error err = *sd_bus_message_get_error(msg); | ||
69 | wlr_log(WLR_ERROR, "Failed to get registered SNIs: %s", err.message); | ||
70 | return -sd_bus_error_get_errno(&err); | ||
71 | } | ||
72 | |||
73 | int ret = sd_bus_message_enter_container(msg, 'v', NULL); | ||
74 | if (ret < 0) { | ||
75 | wlr_log(WLR_ERROR, "Failed to read registered SNIs: %s", strerror(-ret)); | ||
76 | return ret; | ||
77 | } | ||
78 | |||
79 | char **ids; | ||
80 | ret = sd_bus_message_read_strv(msg, &ids); | ||
81 | if (ret < 0) { | ||
82 | wlr_log(WLR_ERROR, "Failed to read registered SNIs: %s", strerror(-ret)); | ||
83 | return ret; | ||
84 | } | ||
85 | |||
86 | if (ids) { | ||
87 | struct swaybar_tray *tray = data; | ||
88 | for (char **id = ids; *id; ++id) { | ||
89 | add_sni(tray, *id); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | static bool register_to_watcher(struct swaybar_host *host) { | ||
97 | // this is called asynchronously in case the watcher is owned by this process | ||
98 | int ret = sd_bus_call_method_async(host->tray->bus, NULL, | ||
99 | host->watcher_interface, watcher_path, host->watcher_interface, | ||
100 | "RegisterStatusNotifierHost", NULL, NULL, "s", host->service); | ||
101 | if (ret < 0) { | ||
102 | wlr_log(WLR_ERROR, "Failed to send register call: %s", strerror(-ret)); | ||
103 | return false; | ||
104 | } | ||
105 | |||
106 | ret = sd_bus_call_method_async(host->tray->bus, NULL, | ||
107 | host->watcher_interface, watcher_path, | ||
108 | "org.freedesktop.DBus.Properties", "Get", | ||
109 | get_registered_snis_callback, host->tray, "ss", | ||
110 | host->watcher_interface, "RegisteredStatusNotifierItems"); | ||
111 | if (ret < 0) { | ||
112 | wlr_log(WLR_ERROR, "Failed to get registered SNIs: %s", strerror(-ret)); | ||
113 | } | ||
114 | |||
115 | return ret >= 0; | ||
116 | } | ||
117 | |||
118 | static int handle_new_watcher(sd_bus_message *msg, | ||
119 | void *data, sd_bus_error *error) { | ||
120 | char *service, *old_owner, *new_owner; | ||
121 | int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner); | ||
122 | if (ret < 0) { | ||
123 | wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret)); | ||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | if (!*old_owner) { | ||
128 | struct swaybar_host *host = data; | ||
129 | if (strcmp(service, host->watcher_interface) == 0) { | ||
130 | register_to_watcher(host); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | bool init_host(struct swaybar_host *host, char *protocol, | ||
138 | struct swaybar_tray *tray) { | ||
139 | size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; | ||
140 | host->watcher_interface = malloc(len); | ||
141 | if (!host->watcher_interface) { | ||
142 | return false; | ||
143 | } | ||
144 | snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol); | ||
145 | |||
146 | sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL; | ||
147 | int ret = sd_bus_match_signal(tray->bus, ®_slot, host->watcher_interface, | ||
148 | watcher_path, host->watcher_interface, | ||
149 | "StatusNotifierItemRegistered", handle_sni_registered, tray); | ||
150 | if (ret < 0) { | ||
151 | wlr_log(WLR_ERROR, "Failed to subscribe to registering events: %s", | ||
152 | strerror(-ret)); | ||
153 | goto error; | ||
154 | } | ||
155 | ret = sd_bus_match_signal(tray->bus, &unreg_slot, host->watcher_interface, | ||
156 | watcher_path, host->watcher_interface, | ||
157 | "StatusNotifierItemUnregistered", handle_sni_unregistered, tray); | ||
158 | if (ret < 0) { | ||
159 | wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s", | ||
160 | strerror(-ret)); | ||
161 | goto error; | ||
162 | } | ||
163 | |||
164 | ret = sd_bus_match_signal(tray->bus, &watcher_slot, "org.freedesktop.DBus", | ||
165 | "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameOwnerChanged", | ||
166 | handle_new_watcher, host); | ||
167 | if (ret < 0) { | ||
168 | wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s", | ||
169 | strerror(-ret)); | ||
170 | goto error; | ||
171 | } | ||
172 | |||
173 | pid_t pid = getpid(); | ||
174 | size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d", | ||
175 | protocol, pid) + 1; | ||
176 | host->service = malloc(service_len); | ||
177 | if (!host->service) { | ||
178 | goto error; | ||
179 | } | ||
180 | snprintf(host->service, service_len, "org.%s.StatusNotifierHost-%d", protocol, pid); | ||
181 | ret = sd_bus_request_name(tray->bus, host->service, 0); | ||
182 | if (ret < 0) { | ||
183 | wlr_log(WLR_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); | ||
184 | goto error; | ||
185 | } | ||
186 | |||
187 | host->tray = tray; | ||
188 | if (!register_to_watcher(host)) { | ||
189 | goto error; | ||
190 | } | ||
191 | |||
192 | sd_bus_slot_set_floating(reg_slot, 1); | ||
193 | sd_bus_slot_set_floating(unreg_slot, 1); | ||
194 | sd_bus_slot_set_floating(watcher_slot, 1); | ||
195 | |||
196 | wlr_log(WLR_DEBUG, "Registered %s", host->service); | ||
197 | return true; | ||
198 | error: | ||
199 | sd_bus_slot_unref(reg_slot); | ||
200 | sd_bus_slot_unref(unreg_slot); | ||
201 | sd_bus_slot_unref(watcher_slot); | ||
202 | finish_host(host); | ||
203 | return false; | ||
204 | } | ||
205 | |||
206 | void finish_host(struct swaybar_host *host) { | ||
207 | sd_bus_release_name(host->tray->bus, host->service); | ||
208 | free(host->service); | ||
209 | free(host->watcher_interface); | ||
210 | } | ||
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c new file mode 100644 index 00000000..67805858 --- /dev/null +++ b/swaybar/tray/icon.c | |||
@@ -0,0 +1,462 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <ctype.h> | ||
3 | #include <dirent.h> | ||
4 | #include <stdbool.h> | ||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <string.h> | ||
8 | #include <sys/stat.h> | ||
9 | #include <unistd.h> | ||
10 | #include <wordexp.h> | ||
11 | #include "swaybar/tray/icon.h" | ||
12 | #include "config.h" | ||
13 | #include "list.h" | ||
14 | #include "log.h" | ||
15 | #include "stringop.h" | ||
16 | |||
17 | static bool dir_exists(char *path) { | ||
18 | struct stat sb; | ||
19 | return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode); | ||
20 | } | ||
21 | |||
22 | static list_t *get_basedirs(void) { | ||
23 | list_t *basedirs = create_list(); | ||
24 | list_add(basedirs, strdup("$HOME/.icons")); // deprecated | ||
25 | |||
26 | char *data_home = getenv("XDG_DATA_HOME"); | ||
27 | list_add(basedirs, strdup(data_home && *data_home ? | ||
28 | "$XDG_DATA_HOME/icons" : "$HOME/.local/share/icons")); | ||
29 | |||
30 | list_add(basedirs, strdup("/usr/share/pixmaps")); | ||
31 | |||
32 | char *data_dirs = getenv("XDG_DATA_DIRS"); | ||
33 | if (!(data_dirs && *data_dirs)) { | ||
34 | data_dirs = "/usr/local/share:/usr/share"; | ||
35 | } | ||
36 | data_dirs = strdup(data_dirs); | ||
37 | char *dir = strtok(data_dirs, ":"); | ||
38 | do { | ||
39 | size_t path_len = snprintf(NULL, 0, "%s/icons", dir) + 1; | ||
40 | char *path = malloc(path_len); | ||
41 | snprintf(path, path_len, "%s/icons", dir); | ||
42 | list_add(basedirs, path); | ||
43 | } while ((dir = strtok(NULL, ":"))); | ||
44 | free(data_dirs); | ||
45 | |||
46 | list_t *basedirs_expanded = create_list(); | ||
47 | for (int i = 0; i < basedirs->length; ++i) { | ||
48 | wordexp_t p; | ||
49 | if (wordexp(basedirs->items[i], &p, WRDE_UNDEF) == 0) { | ||
50 | if (dir_exists(p.we_wordv[0])) { | ||
51 | list_add(basedirs_expanded, strdup(p.we_wordv[0])); | ||
52 | } | ||
53 | wordfree(&p); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | list_free_items_and_destroy(basedirs); | ||
58 | |||
59 | return basedirs_expanded; | ||
60 | } | ||
61 | |||
62 | static void destroy_theme(struct icon_theme *theme) { | ||
63 | if (!theme) { | ||
64 | return; | ||
65 | } | ||
66 | free(theme->name); | ||
67 | free(theme->comment); | ||
68 | free(theme->inherits); | ||
69 | list_free_items_and_destroy(theme->directories); | ||
70 | free(theme->dir); | ||
71 | |||
72 | for (int i = 0; i < theme->subdirs->length; ++i) { | ||
73 | struct icon_theme_subdir *subdir = theme->subdirs->items[i]; | ||
74 | free(subdir->name); | ||
75 | free(subdir); | ||
76 | } | ||
77 | list_free(theme->subdirs); | ||
78 | free(theme); | ||
79 | } | ||
80 | |||
81 | static int cmp_group(const void *item, const void *cmp_to) { | ||
82 | return strcmp(item, cmp_to); | ||
83 | } | ||
84 | |||
85 | static bool group_handler(char *old_group, char *new_group, | ||
86 | struct icon_theme *theme) { | ||
87 | if (!old_group) { // first group must be "Icon Theme" | ||
88 | return strcmp(new_group, "Icon Theme"); | ||
89 | } | ||
90 | |||
91 | if (strcmp(old_group, "Icon Theme") == 0) { | ||
92 | if (!(theme->name && theme->comment && theme->directories)) { | ||
93 | return true; | ||
94 | } | ||
95 | } else { | ||
96 | if (theme->subdirs->length == 0) { // skip | ||
97 | return false; | ||
98 | } | ||
99 | |||
100 | struct icon_theme_subdir *subdir = | ||
101 | theme->subdirs->items[theme->subdirs->length - 1]; | ||
102 | if (!subdir->size) return true; | ||
103 | |||
104 | switch (subdir->type) { | ||
105 | case FIXED: subdir->max_size = subdir->min_size = subdir->size; | ||
106 | break; | ||
107 | case SCALABLE: { | ||
108 | if (!subdir->max_size) subdir->max_size = subdir->size; | ||
109 | if (!subdir->min_size) subdir->min_size = subdir->size; | ||
110 | break; | ||
111 | } | ||
112 | case THRESHOLD: | ||
113 | subdir->max_size = subdir->size + subdir->threshold; | ||
114 | subdir->min_size = subdir->size - subdir->threshold; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | if (new_group && list_seq_find(theme->directories, cmp_group, new_group) != -1) { | ||
119 | struct icon_theme_subdir *subdir = calloc(1, sizeof(struct icon_theme_subdir)); | ||
120 | if (!subdir) { | ||
121 | return true; | ||
122 | } | ||
123 | subdir->name = strdup(new_group); | ||
124 | subdir->threshold = 2; | ||
125 | list_add(theme->subdirs, subdir); | ||
126 | } | ||
127 | |||
128 | return false; | ||
129 | } | ||
130 | |||
131 | static int entry_handler(char *group, char *key, char *value, | ||
132 | struct icon_theme *theme) { | ||
133 | if (strcmp(group, "Icon Theme") == 0) { | ||
134 | if (strcmp(key, "Name") == 0) { | ||
135 | theme->name = strdup(value); | ||
136 | } else if (strcmp(key, "Comment") == 0) { | ||
137 | theme->comment = strdup(value); | ||
138 | } else if (strcmp(key, "Inherists") == 0) { | ||
139 | theme->inherits = strdup(value); | ||
140 | } else if (strcmp(key, "Directories") == 0) { | ||
141 | theme->directories = split_string(value, ","); | ||
142 | } // Ignored: ScaledDirectories, Hidden, Example | ||
143 | } else { | ||
144 | if (theme->subdirs->length == 0) { // skip | ||
145 | return false; | ||
146 | } | ||
147 | |||
148 | struct icon_theme_subdir *subdir = | ||
149 | theme->subdirs->items[theme->subdirs->length - 1]; | ||
150 | if (strcmp(subdir->name, group) != 0) { // skip | ||
151 | return false; | ||
152 | } | ||
153 | |||
154 | char *end; | ||
155 | int n = strtol(value, &end, 10); | ||
156 | if (strcmp(key, "Size") == 0) { | ||
157 | subdir->size = n; | ||
158 | return *end != '\0'; | ||
159 | } else if (strcmp(key, "Type") == 0) { | ||
160 | if (strcmp(value, "Fixed") == 0) { | ||
161 | subdir->type = FIXED; | ||
162 | } else if (strcmp(value, "Scalable") == 0) { | ||
163 | subdir->type = SCALABLE; | ||
164 | } else if (strcmp(value, "Threshold") == 0) { | ||
165 | subdir->type = THRESHOLD; | ||
166 | } else { | ||
167 | return true; | ||
168 | } | ||
169 | } else if (strcmp(key, "MaxSize") == 0) { | ||
170 | subdir->max_size = n; | ||
171 | return *end != '\0'; | ||
172 | } else if (strcmp(key, "MinSize") == 0) { | ||
173 | subdir->min_size = n; | ||
174 | return *end != '\0'; | ||
175 | } else if (strcmp(key, "Threshold") == 0) { | ||
176 | subdir->threshold = n; | ||
177 | return *end != '\0'; | ||
178 | } // Ignored: Scale, Applications | ||
179 | } | ||
180 | return false; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * This is a Freedesktop Desktop Entry parser (essentially INI) | ||
185 | * It calls entry_handler for every entry | ||
186 | * and group_handler between every group (as well as at both ends) | ||
187 | * Handlers return whether an error occured, which stops parsing | ||
188 | */ | ||
189 | static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { | ||
190 | // look for index.theme file | ||
191 | size_t path_len = snprintf(NULL, 0, "%s/%s/index.theme", basedir, | ||
192 | theme_name) + 1; | ||
193 | char *path = malloc(path_len); | ||
194 | snprintf(path, path_len, "%s/%s/index.theme", basedir, theme_name); | ||
195 | FILE *theme_file = fopen(path, "r"); | ||
196 | if (!theme_file) { | ||
197 | return NULL; | ||
198 | } | ||
199 | |||
200 | struct icon_theme *theme = calloc(1, sizeof(struct icon_theme)); | ||
201 | if (!theme) { | ||
202 | return NULL; | ||
203 | } | ||
204 | theme->subdirs = create_list(); | ||
205 | |||
206 | bool error = false; | ||
207 | char *group = NULL; | ||
208 | char *full_line = NULL; | ||
209 | size_t full_len = 0; | ||
210 | ssize_t nread; | ||
211 | while ((nread = getline(&full_line, &full_len, theme_file)) != -1) { | ||
212 | char *line = full_line - 1; | ||
213 | while (isspace(*++line)) {} // remove leading whitespace | ||
214 | if (!*line || line[0] == '#') continue; // ignore blank lines & comments | ||
215 | |||
216 | int len = nread - (line - full_line); | ||
217 | while (isspace(line[--len])) {} | ||
218 | line[++len] = '\0'; // remove trailing whitespace | ||
219 | |||
220 | if (line[0] == '[') { // group header | ||
221 | // check well-formed | ||
222 | if (line[--len] != ']') { | ||
223 | error = true; | ||
224 | break; | ||
225 | } | ||
226 | int i = 1; | ||
227 | for (; !iscntrl(line[i]) && line[i] != '[' && line[i] != ']'; ++i) {} | ||
228 | if (i < len) { | ||
229 | error = true; | ||
230 | break; | ||
231 | } | ||
232 | |||
233 | // call handler | ||
234 | line[len] = '\0'; | ||
235 | error = group_handler(group, &line[1], theme); | ||
236 | if (error) { | ||
237 | break; | ||
238 | } | ||
239 | free(group); | ||
240 | group = strdup(&line[1]); | ||
241 | } else { // key-value pair | ||
242 | // check well-formed | ||
243 | int eok = 0; | ||
244 | for (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {} // TODO locale? | ||
245 | int i = eok - 1; | ||
246 | while (isspace(line[++i])) {} | ||
247 | if (line[i] != '=') { | ||
248 | error = true; | ||
249 | break; | ||
250 | } | ||
251 | |||
252 | line[eok] = '\0'; // split into key-value pair | ||
253 | char *value = &line[i]; | ||
254 | while (isspace(*++value)) {} | ||
255 | // TODO unescape value | ||
256 | error = entry_handler(group, line, value, theme); | ||
257 | if (error) { | ||
258 | break; | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | |||
263 | if (!error && group) { | ||
264 | error = group_handler(group, NULL, theme); | ||
265 | } | ||
266 | |||
267 | free(group); | ||
268 | free(full_line); | ||
269 | fclose(theme_file); | ||
270 | |||
271 | if (!error) { | ||
272 | theme->dir = strdup(theme_name); | ||
273 | return theme; | ||
274 | } else { | ||
275 | destroy_theme(theme); | ||
276 | return NULL; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | static list_t *load_themes_in_dir(char *basedir) { | ||
281 | DIR *dir; | ||
282 | if (!(dir = opendir(basedir))) { | ||
283 | return NULL; | ||
284 | } | ||
285 | |||
286 | list_t *themes = create_list(); | ||
287 | struct dirent *entry; | ||
288 | while ((entry = readdir(dir))) { | ||
289 | if (entry->d_name[0] == '.') continue; | ||
290 | |||
291 | struct icon_theme *theme = read_theme_file(basedir, entry->d_name); | ||
292 | if (theme) { | ||
293 | list_add(themes, theme); | ||
294 | } | ||
295 | } | ||
296 | return themes; | ||
297 | } | ||
298 | |||
299 | void init_themes(list_t **themes, list_t **basedirs) { | ||
300 | *basedirs = get_basedirs(); | ||
301 | |||
302 | *themes = create_list(); | ||
303 | for (int i = 0; i < (*basedirs)->length; ++i) { | ||
304 | list_t *dir_themes = load_themes_in_dir((*basedirs)->items[i]); | ||
305 | list_cat(*themes, dir_themes); | ||
306 | list_free(dir_themes); | ||
307 | } | ||
308 | |||
309 | list_t *theme_names = create_list(); | ||
310 | for (int i = 0; i < (*themes)->length; ++i) { | ||
311 | struct icon_theme *theme = (*themes)->items[i]; | ||
312 | list_add(theme_names, theme->name); | ||
313 | } | ||
314 | wlr_log(WLR_DEBUG, "Loaded themes: %s", join_list(theme_names, ", ")); | ||
315 | list_free(theme_names); | ||
316 | } | ||
317 | |||
318 | void finish_themes(list_t *themes, list_t *basedirs) { | ||
319 | for (int i = 0; i < themes->length; ++i) { | ||
320 | destroy_theme(themes->items[i]); | ||
321 | } | ||
322 | list_free(themes); | ||
323 | list_free_items_and_destroy(basedirs); | ||
324 | } | ||
325 | |||
326 | static char *find_icon_in_subdir(char *name, char *basedir, char *theme, | ||
327 | char *subdir) { | ||
328 | static const char *extensions[] = { | ||
329 | #if HAVE_GDK_PIXBUF | ||
330 | "svg", | ||
331 | #endif | ||
332 | "png", | ||
333 | #if HAVE_GDK_PIXBUF | ||
334 | "xpm" | ||
335 | #endif | ||
336 | }; | ||
337 | |||
338 | size_t path_len = snprintf(NULL, 0, "%s/%s/%s/%s.EXT", basedir, theme, | ||
339 | subdir, name) + 1; | ||
340 | char *path = malloc(path_len); | ||
341 | |||
342 | for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) { | ||
343 | snprintf(path, path_len, "%s/%s/%s/%s.%s", basedir, theme, subdir, | ||
344 | name, extensions[i]); | ||
345 | if (access(path, R_OK) == 0) { | ||
346 | return path; | ||
347 | } | ||
348 | } | ||
349 | |||
350 | free(path); | ||
351 | return NULL; | ||
352 | } | ||
353 | |||
354 | static bool theme_exists_in_basedir(char *theme, char *basedir) { | ||
355 | size_t path_len = snprintf(NULL, 0, "%s/%s", basedir, theme) + 1; | ||
356 | char *path = malloc(path_len); | ||
357 | snprintf(path, path_len, "%s/%s", basedir, theme); | ||
358 | bool ret = dir_exists(path); | ||
359 | free(path); | ||
360 | return ret; | ||
361 | } | ||
362 | |||
363 | static char *find_icon_with_theme(list_t *basedirs, list_t *themes, char *name, | ||
364 | int size, char *theme_name, int *min_size, int *max_size) { | ||
365 | struct icon_theme *theme = NULL; | ||
366 | for (int i = 0; i < themes->length; ++i) { | ||
367 | theme = themes->items[i]; | ||
368 | if (strcmp(theme->name, theme_name) == 0) { | ||
369 | break; | ||
370 | } | ||
371 | theme = NULL; | ||
372 | } | ||
373 | if (!theme) return NULL; | ||
374 | |||
375 | char *icon = NULL; | ||
376 | for (int i = 0; i < basedirs->length; ++i) { | ||
377 | if (!theme_exists_in_basedir(theme->dir, basedirs->items[i])) { | ||
378 | continue; | ||
379 | } | ||
380 | // search backwards to hopefully hit scalable/larger icons first | ||
381 | for (int j = theme->subdirs->length - 1; j >= 0; --j) { | ||
382 | struct icon_theme_subdir *subdir = theme->subdirs->items[j]; | ||
383 | if (size >= subdir->min_size && size <= subdir->max_size) { | ||
384 | if ((icon = find_icon_in_subdir(name, basedirs->items[i], | ||
385 | theme->dir, subdir->name))) { | ||
386 | *min_size = subdir->min_size; | ||
387 | *max_size = subdir->max_size; | ||
388 | return icon; | ||
389 | } | ||
390 | } | ||
391 | } | ||
392 | } | ||
393 | |||
394 | // inexact match | ||
395 | unsigned smallest_error = -1; // UINT_MAX | ||
396 | for (int i = 0; i < basedirs->length; ++i) { | ||
397 | if (!theme_exists_in_basedir(theme->dir, basedirs->items[i])) { | ||
398 | continue; | ||
399 | } | ||
400 | for (int j = theme->subdirs->length - 1; j >= 0; --j) { | ||
401 | struct icon_theme_subdir *subdir = theme->subdirs->items[j]; | ||
402 | unsigned error = (size > subdir->max_size ? size - subdir->max_size : 0) | ||
403 | + (size < subdir->min_size ? subdir->min_size - size : 0); | ||
404 | if (error < smallest_error) { | ||
405 | char *test_icon = find_icon_in_subdir(name, basedirs->items[i], | ||
406 | theme->dir, subdir->name); | ||
407 | if (test_icon) { | ||
408 | icon = test_icon; | ||
409 | smallest_error = error; | ||
410 | *min_size = subdir->min_size; | ||
411 | *max_size = subdir->max_size; | ||
412 | } | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | |||
417 | if (!icon && theme->inherits) { | ||
418 | icon = find_icon_with_theme(basedirs, themes, name, size, | ||
419 | theme->inherits, min_size, max_size); | ||
420 | } | ||
421 | |||
422 | return icon; | ||
423 | } | ||
424 | |||
425 | char *find_icon_in_dir(char *name, char *dir, int *min_size, int *max_size) { | ||
426 | char *icon = find_icon_in_subdir(name, dir, "", ""); | ||
427 | if (icon) { | ||
428 | *min_size = 1; | ||
429 | *max_size = 512; | ||
430 | } | ||
431 | return icon; | ||
432 | |||
433 | } | ||
434 | |||
435 | static char *find_fallback_icon(list_t *basedirs, char *name, int *min_size, | ||
436 | int *max_size) { | ||
437 | for (int i = 0; i < basedirs->length; ++i) { | ||
438 | char *icon = find_icon_in_dir(name, basedirs->items[i], min_size, max_size); | ||
439 | if (icon) { | ||
440 | return icon; | ||
441 | } | ||
442 | } | ||
443 | return NULL; | ||
444 | } | ||
445 | |||
446 | char *find_icon(list_t *themes, list_t *basedirs, char *name, int size, | ||
447 | char *theme, int *min_size, int *max_size) { | ||
448 | // TODO https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#implementation_notes | ||
449 | char *icon = NULL; | ||
450 | if (theme) { | ||
451 | icon = find_icon_with_theme(basedirs, themes, name, size, theme, | ||
452 | min_size, max_size); | ||
453 | } | ||
454 | if (!icon) { | ||
455 | icon = find_icon_with_theme(basedirs, themes, name, size, "Hicolor", | ||
456 | min_size, max_size); | ||
457 | } | ||
458 | if (!icon) { | ||
459 | icon = find_fallback_icon(basedirs, name, min_size, max_size); | ||
460 | } | ||
461 | return icon; | ||
462 | } | ||
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c new file mode 100644 index 00000000..0833dcb9 --- /dev/null +++ b/swaybar/tray/item.c | |||
@@ -0,0 +1,472 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <cairo.h> | ||
3 | #include <stdbool.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <string.h> | ||
6 | #include "swaybar/bar.h" | ||
7 | #include "swaybar/config.h" | ||
8 | #include "swaybar/input.h" | ||
9 | #include "swaybar/tray/host.h" | ||
10 | #include "swaybar/tray/icon.h" | ||
11 | #include "swaybar/tray/item.h" | ||
12 | #include "swaybar/tray/tray.h" | ||
13 | #include "background-image.h" | ||
14 | #include "cairo.h" | ||
15 | #include "list.h" | ||
16 | #include "log.h" | ||
17 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | ||
18 | |||
19 | // TODO menu | ||
20 | |||
21 | static bool sni_ready(struct swaybar_sni *sni) { | ||
22 | return sni->status && (sni->status[0] == 'N' ? // NeedsAttention | ||
23 | sni->attention_icon_name || sni->attention_icon_pixmap : | ||
24 | sni->icon_name || sni->icon_pixmap); | ||
25 | } | ||
26 | |||
27 | static void set_sni_dirty(struct swaybar_sni *sni) { | ||
28 | if (sni_ready(sni)) { | ||
29 | sni->min_size = sni->max_size = 0; // invalidate previous icon | ||
30 | set_bar_dirty(sni->tray->bar); | ||
31 | } | ||
32 | } | ||
33 | |||
34 | static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, | ||
35 | const char *prop, list_t **dest) { | ||
36 | int ret = sd_bus_message_enter_container(msg, 'a', "(iiay)"); | ||
37 | if (ret < 0) { | ||
38 | wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret)); | ||
39 | return ret; | ||
40 | } | ||
41 | |||
42 | if (sd_bus_message_at_end(msg, 0)) { | ||
43 | wlr_log(WLR_DEBUG, "%s %s no. of icons = 0", sni->watcher_id, prop); | ||
44 | return ret; | ||
45 | } | ||
46 | |||
47 | list_t *pixmaps = create_list(); | ||
48 | if (!pixmaps) { | ||
49 | return -12; // -ENOMEM | ||
50 | } | ||
51 | |||
52 | while (!sd_bus_message_at_end(msg, 0)) { | ||
53 | ret = sd_bus_message_enter_container(msg, 'r', "iiay"); | ||
54 | if (ret < 0) { | ||
55 | wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret)); | ||
56 | goto error; | ||
57 | } | ||
58 | |||
59 | int size; | ||
60 | ret = sd_bus_message_read(msg, "ii", NULL, &size); | ||
61 | if (ret < 0) { | ||
62 | wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret)); | ||
63 | goto error; | ||
64 | } | ||
65 | |||
66 | const void *pixels; | ||
67 | size_t npixels; | ||
68 | ret = sd_bus_message_read_array(msg, 'y', &pixels, &npixels); | ||
69 | if (ret < 0) { | ||
70 | wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret)); | ||
71 | goto error; | ||
72 | } | ||
73 | |||
74 | struct swaybar_pixmap *pixmap = | ||
75 | malloc(sizeof(struct swaybar_pixmap) + npixels); | ||
76 | pixmap->size = size; | ||
77 | memcpy(pixmap->pixels, pixels, npixels); | ||
78 | list_add(pixmaps, pixmap); | ||
79 | |||
80 | sd_bus_message_exit_container(msg); | ||
81 | } | ||
82 | list_free_items_and_destroy(*dest); | ||
83 | *dest = pixmaps; | ||
84 | wlr_log(WLR_DEBUG, "%s %s no. of icons = %d", sni->watcher_id, prop, | ||
85 | pixmaps->length); | ||
86 | |||
87 | return ret; | ||
88 | error: | ||
89 | list_free_items_and_destroy(pixmaps); | ||
90 | return ret; | ||
91 | } | ||
92 | |||
93 | struct get_property_data { | ||
94 | struct swaybar_sni *sni; | ||
95 | const char *prop; | ||
96 | const char *type; | ||
97 | void *dest; | ||
98 | }; | ||
99 | |||
100 | static int get_property_callback(sd_bus_message *msg, void *data, | ||
101 | sd_bus_error *error) { | ||
102 | struct get_property_data *d = data; | ||
103 | struct swaybar_sni *sni = d->sni; | ||
104 | const char *prop = d->prop; | ||
105 | const char *type = d->type; | ||
106 | void *dest = d->dest; | ||
107 | |||
108 | int ret; | ||
109 | if (sd_bus_message_is_method_error(msg, NULL)) { | ||
110 | wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, | ||
111 | sd_bus_message_get_error(msg)->message); | ||
112 | ret = sd_bus_message_get_errno(msg); | ||
113 | goto cleanup; | ||
114 | } | ||
115 | |||
116 | ret = sd_bus_message_enter_container(msg, 'v', type); | ||
117 | if (ret < 0) { | ||
118 | wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret)); | ||
119 | goto cleanup; | ||
120 | } | ||
121 | |||
122 | if (!type) { | ||
123 | ret = read_pixmap(msg, sni, prop, dest); | ||
124 | if (ret < 0) { | ||
125 | goto cleanup; | ||
126 | } | ||
127 | } else { | ||
128 | if (*type == 's' || *type == 'o') { | ||
129 | free(*(char **)dest); | ||
130 | } | ||
131 | |||
132 | ret = sd_bus_message_read(msg, type, dest); | ||
133 | if (ret < 0) { | ||
134 | wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret)); | ||
135 | goto cleanup; | ||
136 | } | ||
137 | |||
138 | if (*type == 's' || *type == 'o') { | ||
139 | char **str = dest; | ||
140 | *str = strdup(*str); | ||
141 | wlr_log(WLR_DEBUG, "%s %s = '%s'", sni->watcher_id, prop, *str); | ||
142 | } else if (*type == 'b') { | ||
143 | wlr_log(WLR_DEBUG, "%s %s = %s", sni->watcher_id, prop, | ||
144 | *(bool *)dest ? "true" : "false"); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | if (strcmp(prop, "Status") == 0 || (sni->status && (sni->status[0] == 'N' ? | ||
149 | prop[0] == 'A' : strncmp(prop, "Icon", 4) == 0))) { | ||
150 | set_sni_dirty(sni); | ||
151 | } | ||
152 | cleanup: | ||
153 | free(data); | ||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | static void sni_get_property_async(struct swaybar_sni *sni, const char *prop, | ||
158 | const char *type, void *dest) { | ||
159 | struct get_property_data *data = malloc(sizeof(struct get_property_data)); | ||
160 | data->sni = sni; | ||
161 | data->prop = prop; | ||
162 | data->type = type; | ||
163 | data->dest = dest; | ||
164 | int ret = sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, | ||
165 | sni->path, "org.freedesktop.DBus.Properties", "Get", | ||
166 | get_property_callback, data, "ss", sni->interface, prop); | ||
167 | if (ret < 0) { | ||
168 | wlr_log(WLR_ERROR, "%s %s: %s", sni->watcher_id, prop, strerror(-ret)); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | /* | ||
173 | * There is a quirk in sd-bus that in some systems, it is unable to get the | ||
174 | * well-known names on the bus, so it cannot identify if an incoming signal, | ||
175 | * which uses the sender's unique name, actually matches the callback's matching | ||
176 | * sender if the callback uses a well-known name, in which case it just calls | ||
177 | * the callback and hopes for the best, resulting in false positives. In the | ||
178 | * case of NewIcon & NewAttentionIcon, this doesn't affect anything, but it | ||
179 | * means that for NewStatus, if the SNI does not definitely match the sender, | ||
180 | * then the safe thing to do is to query the status independently. | ||
181 | * This function returns 1 if the SNI definitely matches the signal sender, | ||
182 | * which is returned by the calling function to indicate that signal matching | ||
183 | * can stop since it has already found the required callback, otherwise, it | ||
184 | * returns 0, which allows matching to continue. | ||
185 | */ | ||
186 | static int sni_check_msg_sender(struct swaybar_sni *sni, sd_bus_message *msg, | ||
187 | const char *signal) { | ||
188 | bool has_well_known_names = | ||
189 | sd_bus_creds_get_mask(sd_bus_message_get_creds(msg)) & SD_BUS_CREDS_WELL_KNOWN_NAMES; | ||
190 | if (sni->service[0] == ':' || has_well_known_names) { | ||
191 | wlr_log(WLR_DEBUG, "%s has new %s", sni->watcher_id, signal); | ||
192 | return 1; | ||
193 | } else { | ||
194 | wlr_log(WLR_DEBUG, "%s may have new %s", sni->watcher_id, signal); | ||
195 | return 0; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static int handle_new_icon(sd_bus_message *msg, void *data, sd_bus_error *error) { | ||
200 | struct swaybar_sni *sni = data; | ||
201 | sni_get_property_async(sni, "IconName", "s", &sni->icon_name); | ||
202 | sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap); | ||
203 | return sni_check_msg_sender(sni, msg, "icon"); | ||
204 | } | ||
205 | |||
206 | static int handle_new_attention_icon(sd_bus_message *msg, void *data, | ||
207 | sd_bus_error *error) { | ||
208 | struct swaybar_sni *sni = data; | ||
209 | sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name); | ||
210 | sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap); | ||
211 | return sni_check_msg_sender(sni, msg, "attention icon"); | ||
212 | } | ||
213 | |||
214 | static int handle_new_status(sd_bus_message *msg, void *data, sd_bus_error *error) { | ||
215 | struct swaybar_sni *sni = data; | ||
216 | int ret = sni_check_msg_sender(sni, msg, "status"); | ||
217 | if (ret == 1) { | ||
218 | char *status; | ||
219 | int r = sd_bus_message_read(msg, "s", &status); | ||
220 | if (r < 0) { | ||
221 | wlr_log(WLR_ERROR, "%s new status error: %s", sni->watcher_id, strerror(-ret)); | ||
222 | ret = r; | ||
223 | } else { | ||
224 | free(sni->status); | ||
225 | sni->status = strdup(status); | ||
226 | wlr_log(WLR_DEBUG, "%s has new status = '%s'", sni->watcher_id, status); | ||
227 | set_sni_dirty(sni); | ||
228 | } | ||
229 | } else { | ||
230 | sni_get_property_async(sni, "Status", "s", &sni->status); | ||
231 | } | ||
232 | |||
233 | return ret; | ||
234 | } | ||
235 | |||
236 | static void sni_match_signal(struct swaybar_sni *sni, sd_bus_slot **slot, | ||
237 | char *signal, sd_bus_message_handler_t callback) { | ||
238 | int ret = sd_bus_match_signal(sni->tray->bus, slot, sni->service, sni->path, | ||
239 | sni->interface, signal, callback, sni); | ||
240 | if (ret < 0) { | ||
241 | wlr_log(WLR_ERROR, "Failed to subscribe to signal %s: %s", signal, | ||
242 | strerror(-ret)); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | struct swaybar_sni *create_sni(char *id, struct swaybar_tray *tray) { | ||
247 | struct swaybar_sni *sni = calloc(1, sizeof(struct swaybar_sni)); | ||
248 | if (!sni) { | ||
249 | return NULL; | ||
250 | } | ||
251 | sni->tray = tray; | ||
252 | sni->watcher_id = strdup(id); | ||
253 | char *path_ptr = strchr(id, '/'); | ||
254 | if (!path_ptr) { | ||
255 | sni->service = strdup(id); | ||
256 | sni->path = strdup("/StatusNotifierItem"); | ||
257 | sni->interface = "org.freedesktop.StatusNotifierItem"; | ||
258 | } else { | ||
259 | sni->service = strndup(id, path_ptr - id); | ||
260 | sni->path = strdup(path_ptr); | ||
261 | sni->interface = "org.kde.StatusNotifierItem"; | ||
262 | sni_get_property_async(sni, "IconThemePath", "s", &sni->icon_theme_path); | ||
263 | } | ||
264 | |||
265 | // Ignored: Category, Id, Title, WindowId, OverlayIconName, | ||
266 | // OverlayIconPixmap, AttentionMovieName, ToolTip | ||
267 | sni_get_property_async(sni, "Status", "s", &sni->status); | ||
268 | sni_get_property_async(sni, "IconName", "s", &sni->icon_name); | ||
269 | sni_get_property_async(sni, "IconPixmap", NULL, &sni->icon_pixmap); | ||
270 | sni_get_property_async(sni, "AttentionIconName", "s", &sni->attention_icon_name); | ||
271 | sni_get_property_async(sni, "AttentionIconPixmap", NULL, &sni->attention_icon_pixmap); | ||
272 | sni_get_property_async(sni, "ItemIsMenu", "b", &sni->item_is_menu); | ||
273 | sni_get_property_async(sni, "Menu", "o", &sni->menu); | ||
274 | |||
275 | sni_match_signal(sni, &sni->new_icon_slot, "NewIcon", handle_new_icon); | ||
276 | sni_match_signal(sni, &sni->new_attention_icon_slot, "NewAttentionIcon", | ||
277 | handle_new_attention_icon); | ||
278 | sni_match_signal(sni, &sni->new_status_slot, "NewStatus", handle_new_status); | ||
279 | |||
280 | return sni; | ||
281 | } | ||
282 | |||
283 | void destroy_sni(struct swaybar_sni *sni) { | ||
284 | if (!sni) { | ||
285 | return; | ||
286 | } | ||
287 | |||
288 | sd_bus_slot_unref(sni->new_icon_slot); | ||
289 | sd_bus_slot_unref(sni->new_attention_icon_slot); | ||
290 | sd_bus_slot_unref(sni->new_status_slot); | ||
291 | |||
292 | free(sni->watcher_id); | ||
293 | free(sni->service); | ||
294 | free(sni->path); | ||
295 | free(sni->status); | ||
296 | free(sni->icon_name); | ||
297 | free(sni->icon_pixmap); | ||
298 | free(sni->attention_icon_name); | ||
299 | free(sni->menu); | ||
300 | free(sni); | ||
301 | } | ||
302 | |||
303 | static void handle_click(struct swaybar_sni *sni, int x, int y, | ||
304 | enum x11_button button, int delta) { | ||
305 | const char *method = sni->tray->bar->config->tray_bindings[button]; | ||
306 | if (!method) { | ||
307 | static const char *default_bindings[10] = { | ||
308 | "nop", | ||
309 | "Activate", | ||
310 | "SecondaryActivate", | ||
311 | "ContextMenu", | ||
312 | "ScrollUp", | ||
313 | "ScrollDown", | ||
314 | "ScrollLeft", | ||
315 | "ScrollRight", | ||
316 | "nop", | ||
317 | "nop" | ||
318 | }; | ||
319 | method = default_bindings[button]; | ||
320 | } | ||
321 | if (strcmp(method, "nop") == 0) { | ||
322 | return; | ||
323 | } | ||
324 | if (sni->item_is_menu && strcmp(method, "Activate") == 0) { | ||
325 | method = "ContextMenu"; | ||
326 | } | ||
327 | |||
328 | if (strncmp(method, "Scroll", strlen("Scroll")) == 0) { | ||
329 | char dir = method[strlen("Scroll")]; | ||
330 | char *orientation = (dir = 'U' || dir == 'D') ? "vertical" : "horizontal"; | ||
331 | int sign = (dir == 'U' || dir == 'L') ? -1 : 1; | ||
332 | |||
333 | sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->path, | ||
334 | sni->interface, "Scroll", NULL, NULL, "is", delta*sign, orientation); | ||
335 | } else { | ||
336 | sd_bus_call_method_async(sni->tray->bus, NULL, sni->service, sni->path, | ||
337 | sni->interface, method, NULL, NULL, "ii", x, y); | ||
338 | } | ||
339 | } | ||
340 | |||
341 | static int cmp_sni_id(const void *item, const void *cmp_to) { | ||
342 | const struct swaybar_sni *sni = item; | ||
343 | return strcmp(sni->watcher_id, cmp_to); | ||
344 | } | ||
345 | |||
346 | static enum hotspot_event_handling icon_hotspot_callback( | ||
347 | struct swaybar_output *output, struct swaybar_hotspot *hotspot, | ||
348 | int x, int y, enum x11_button button, void *data) { | ||
349 | wlr_log(WLR_DEBUG, "Clicked on %s", (char *)data); | ||
350 | |||
351 | struct swaybar_tray *tray = output->bar->tray; | ||
352 | int idx = list_seq_find(tray->items, cmp_sni_id, data); | ||
353 | |||
354 | if (idx != -1) { | ||
355 | struct swaybar_sni *sni = tray->items->items[idx]; | ||
356 | // guess global position since wayland doesn't expose it | ||
357 | struct swaybar_config *config = tray->bar->config; | ||
358 | int global_x = output->output_x + config->gaps.left + x; | ||
359 | bool top_bar = config->position & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; | ||
360 | int global_y = output->output_y + (top_bar ? config->gaps.top + y: | ||
361 | (int) output->output_height - config->gaps.bottom - y); | ||
362 | |||
363 | wlr_log(WLR_DEBUG, "Guessing click position at (%d, %d)", global_x, global_y); | ||
364 | handle_click(sni, global_x, global_y, button, 1); // TODO get delta from event | ||
365 | return HOTSPOT_IGNORE; | ||
366 | } else { | ||
367 | wlr_log(WLR_DEBUG, "but it doesn't exist"); | ||
368 | } | ||
369 | |||
370 | return HOTSPOT_PROCESS; | ||
371 | } | ||
372 | |||
373 | uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, | ||
374 | struct swaybar_sni *sni) { | ||
375 | uint32_t height = output->height * output->scale; | ||
376 | int padding = output->bar->config->tray_padding; | ||
377 | int ideal_size = height - 2*padding; | ||
378 | if ((ideal_size < sni->min_size || ideal_size > sni->max_size) && sni_ready(sni)) { | ||
379 | bool icon_found = false; | ||
380 | char *icon_name = sni->status[0] == 'N' ? | ||
381 | sni->attention_icon_name : sni->icon_name; | ||
382 | if (icon_name) { | ||
383 | char *icon_path = find_icon(sni->tray->themes, sni->tray->basedirs, | ||
384 | icon_name, ideal_size, output->bar->config->icon_theme, | ||
385 | &sni->min_size, &sni->max_size); | ||
386 | if (!icon_path && sni->icon_theme_path) { | ||
387 | icon_path = find_icon_in_dir(icon_name, sni->icon_theme_path, | ||
388 | &sni->min_size, &sni->max_size); | ||
389 | } | ||
390 | if (icon_path) { | ||
391 | cairo_surface_destroy(sni->icon); | ||
392 | sni->icon = load_background_image(icon_path); | ||
393 | free(icon_path); | ||
394 | icon_found = true; | ||
395 | } | ||
396 | } | ||
397 | if (!icon_found) { | ||
398 | list_t *pixmaps = sni->status[0] == 'N' ? | ||
399 | sni->attention_icon_pixmap : sni->icon_pixmap; | ||
400 | if (pixmaps) { | ||
401 | int idx = -1; | ||
402 | unsigned smallest_error = -1; // UINT_MAX | ||
403 | for (int i = 0; i < pixmaps->length; ++i) { | ||
404 | struct swaybar_pixmap *pixmap = pixmaps->items[i]; | ||
405 | unsigned error = (ideal_size - pixmap->size) * | ||
406 | (ideal_size < pixmap->size ? -1 : 1); | ||
407 | if (error < smallest_error) { | ||
408 | smallest_error = error; | ||
409 | idx = i; | ||
410 | } | ||
411 | } | ||
412 | struct swaybar_pixmap *pixmap = pixmaps->items[idx]; | ||
413 | cairo_surface_destroy(sni->icon); | ||
414 | sni->icon = cairo_image_surface_create_for_data(pixmap->pixels, | ||
415 | CAIRO_FORMAT_ARGB32, pixmap->size, pixmap->size, | ||
416 | cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixmap->size)); | ||
417 | } | ||
418 | } | ||
419 | } | ||
420 | |||
421 | int icon_size; | ||
422 | cairo_surface_t *icon; | ||
423 | if (sni->icon) { | ||
424 | int actual_size = cairo_image_surface_get_height(sni->icon); | ||
425 | icon_size = actual_size < ideal_size ? | ||
426 | actual_size*(ideal_size/actual_size) : ideal_size; | ||
427 | icon = cairo_image_surface_scale(sni->icon, icon_size, icon_size); | ||
428 | } else { // draw a :( | ||
429 | icon_size = ideal_size*0.8; | ||
430 | icon = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, icon_size, icon_size); | ||
431 | cairo_t *cairo_icon = cairo_create(icon); | ||
432 | cairo_set_source_u32(cairo_icon, 0xFF0000FF); | ||
433 | cairo_translate(cairo_icon, icon_size/2, icon_size/2); | ||
434 | cairo_scale(cairo_icon, icon_size/2, icon_size/2); | ||
435 | cairo_arc(cairo_icon, 0, 0, 1, 0, 7); | ||
436 | cairo_fill(cairo_icon); | ||
437 | cairo_set_operator(cairo_icon, CAIRO_OPERATOR_CLEAR); | ||
438 | cairo_arc(cairo_icon, 0.35, -0.3, 0.1, 0, 7); | ||
439 | cairo_fill(cairo_icon); | ||
440 | cairo_arc(cairo_icon, -0.35, -0.3, 0.1, 0, 7); | ||
441 | cairo_fill(cairo_icon); | ||
442 | cairo_arc(cairo_icon, 0, 0.75, 0.5, 3.71238898038469, 5.71238898038469); | ||
443 | cairo_set_line_width(cairo_icon, 0.1); | ||
444 | cairo_stroke(cairo_icon); | ||
445 | cairo_destroy(cairo_icon); | ||
446 | } | ||
447 | |||
448 | int padded_size = icon_size + 2*padding; | ||
449 | *x -= padded_size; | ||
450 | int y = floor((height - padded_size) / 2.0); | ||
451 | |||
452 | cairo_operator_t op = cairo_get_operator(cairo); | ||
453 | cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); | ||
454 | cairo_set_source_surface(cairo, icon, *x + padding, y + padding); | ||
455 | cairo_rectangle(cairo, *x, y, padded_size, padded_size); | ||
456 | cairo_fill(cairo); | ||
457 | cairo_set_operator(cairo, op); | ||
458 | |||
459 | cairo_surface_destroy(icon); | ||
460 | |||
461 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); | ||
462 | hotspot->x = *x; | ||
463 | hotspot->y = 0; | ||
464 | hotspot->width = height; | ||
465 | hotspot->height = height; | ||
466 | hotspot->callback = icon_hotspot_callback; | ||
467 | hotspot->destroy = free; | ||
468 | hotspot->data = strdup(sni->watcher_id); | ||
469 | wl_list_insert(&output->hotspots, &hotspot->link); | ||
470 | |||
471 | return output->height; | ||
472 | } | ||
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c new file mode 100644 index 00000000..d5d0c84e --- /dev/null +++ b/swaybar/tray/tray.c | |||
@@ -0,0 +1,131 @@ | |||
1 | #include <cairo.h> | ||
2 | #include <stdint.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <string.h> | ||
5 | #include "swaybar/config.h" | ||
6 | #include "swaybar/bar.h" | ||
7 | #include "swaybar/tray/icon.h" | ||
8 | #include "swaybar/tray/host.h" | ||
9 | #include "swaybar/tray/item.h" | ||
10 | #include "swaybar/tray/tray.h" | ||
11 | #include "swaybar/tray/watcher.h" | ||
12 | #include "list.h" | ||
13 | #include "log.h" | ||
14 | |||
15 | static int handle_lost_watcher(sd_bus_message *msg, | ||
16 | void *data, sd_bus_error *error) { | ||
17 | char *service, *old_owner, *new_owner; | ||
18 | int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner); | ||
19 | if (ret < 0) { | ||
20 | wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret)); | ||
21 | return ret; | ||
22 | } | ||
23 | |||
24 | if (!*new_owner) { | ||
25 | struct swaybar_tray *tray = data; | ||
26 | if (strcmp(service, "org.freedesktop.StatusNotifierWatcher") == 0) { | ||
27 | tray->watcher_xdg = create_watcher("freedesktop", tray->bus); | ||
28 | } else if (strcmp(service, "org.kde.StatusNotifierWatcher") == 0) { | ||
29 | tray->watcher_kde = create_watcher("kde", tray->bus); | ||
30 | } | ||
31 | } | ||
32 | |||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | struct swaybar_tray *create_tray(struct swaybar *bar) { | ||
37 | wlr_log(WLR_DEBUG, "Initializing tray"); | ||
38 | |||
39 | sd_bus *bus; | ||
40 | int ret = sd_bus_open_user(&bus); | ||
41 | if (ret < 0) { | ||
42 | wlr_log(WLR_ERROR, "Failed to connect to user bus: %s", strerror(-ret)); | ||
43 | return NULL; | ||
44 | } | ||
45 | |||
46 | struct swaybar_tray *tray = calloc(1, sizeof(struct swaybar_tray)); | ||
47 | if (!tray) { | ||
48 | return NULL; | ||
49 | } | ||
50 | tray->bar = bar; | ||
51 | tray->bus = bus; | ||
52 | tray->fd = sd_bus_get_fd(tray->bus); | ||
53 | |||
54 | tray->watcher_xdg = create_watcher("freedesktop", tray->bus); | ||
55 | tray->watcher_kde = create_watcher("kde", tray->bus); | ||
56 | |||
57 | ret = sd_bus_match_signal(bus, NULL, "org.freedesktop.DBus", | ||
58 | "/org/freedesktop/DBus", "org.freedesktop.DBus", | ||
59 | "NameOwnerChanged", handle_lost_watcher, tray); | ||
60 | if (ret < 0) { | ||
61 | wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s", | ||
62 | strerror(-ret)); | ||
63 | } | ||
64 | |||
65 | tray->items = create_list(); | ||
66 | |||
67 | init_host(&tray->host_xdg, "freedesktop", tray); | ||
68 | init_host(&tray->host_kde, "kde", tray); | ||
69 | |||
70 | init_themes(&tray->themes, &tray->basedirs); | ||
71 | |||
72 | return tray; | ||
73 | } | ||
74 | |||
75 | void destroy_tray(struct swaybar_tray *tray) { | ||
76 | if (!tray) { | ||
77 | return; | ||
78 | } | ||
79 | finish_host(&tray->host_xdg); | ||
80 | finish_host(&tray->host_kde); | ||
81 | for (int i = 0; i < tray->items->length; ++i) { | ||
82 | destroy_sni(tray->items->items[i]); | ||
83 | } | ||
84 | list_free(tray->items); | ||
85 | destroy_watcher(tray->watcher_xdg); | ||
86 | destroy_watcher(tray->watcher_kde); | ||
87 | sd_bus_flush_close_unref(tray->bus); | ||
88 | finish_themes(tray->themes, tray->basedirs); | ||
89 | free(tray); | ||
90 | } | ||
91 | |||
92 | void tray_in(int fd, short mask, void *data) { | ||
93 | sd_bus *bus = data; | ||
94 | int ret; | ||
95 | while ((ret = sd_bus_process(bus, NULL)) > 0) { | ||
96 | // This space intentionally left blank | ||
97 | } | ||
98 | if (ret < 0) { | ||
99 | wlr_log(WLR_ERROR, "Failed to process bus: %s", strerror(-ret)); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | static int cmp_output(const void *item, const void *cmp_to) { | ||
104 | const struct swaybar_output *output = cmp_to; | ||
105 | if (output->identifier && strcmp(item, output->identifier) == 0) { | ||
106 | return 0; | ||
107 | } | ||
108 | return strcmp(item, output->name); | ||
109 | } | ||
110 | |||
111 | uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) { | ||
112 | struct swaybar_config *config = output->bar->config; | ||
113 | if (config->tray_outputs) { | ||
114 | if (list_seq_find(config->tray_outputs, cmp_output, output) == -1) { | ||
115 | return 0; | ||
116 | } | ||
117 | } // else display on all | ||
118 | |||
119 | if ((int) output->height*output->scale <= 2*config->tray_padding) { | ||
120 | return 2*config->tray_padding + 1; | ||
121 | } | ||
122 | |||
123 | uint32_t max_height = 0; | ||
124 | struct swaybar_tray *tray = output->bar->tray; | ||
125 | for (int i = 0; i < tray->items->length; ++i) { | ||
126 | uint32_t h = render_sni(cairo, output, x, tray->items->items[i]); | ||
127 | max_height = h > max_height ? h : max_height; | ||
128 | } | ||
129 | |||
130 | return max_height; | ||
131 | } | ||
diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c new file mode 100644 index 00000000..198c6c85 --- /dev/null +++ b/swaybar/tray/watcher.c | |||
@@ -0,0 +1,212 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <stdbool.h> | ||
3 | #include <stddef.h> | ||
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <string.h> | ||
7 | #include "list.h" | ||
8 | #include "log.h" | ||
9 | #include "swaybar/tray/watcher.h" | ||
10 | |||
11 | static const char *obj_path = "/StatusNotifierWatcher"; | ||
12 | |||
13 | static bool using_standard_protocol(struct swaybar_watcher *watcher) { | ||
14 | return watcher->interface[strlen("org.")] == 'f'; // freedesktop | ||
15 | } | ||
16 | |||
17 | static int cmp_id(const void *item, const void *cmp_to) { | ||
18 | return strcmp(item, cmp_to); | ||
19 | } | ||
20 | |||
21 | static int cmp_service(const void *item, const void *cmp_to) { | ||
22 | return strncmp(item, cmp_to, strlen(cmp_to)); | ||
23 | } | ||
24 | |||
25 | static int handle_lost_service(sd_bus_message *msg, | ||
26 | void *data, sd_bus_error *error) { | ||
27 | char *service, *old_owner, *new_owner; | ||
28 | int ret = sd_bus_message_read(msg, "sss", &service, &old_owner, &new_owner); | ||
29 | if (ret < 0) { | ||
30 | wlr_log(WLR_ERROR, "Failed to parse owner change message: %s", strerror(-ret)); | ||
31 | return ret; | ||
32 | } | ||
33 | |||
34 | if (!*new_owner) { | ||
35 | struct swaybar_watcher *watcher = data; | ||
36 | int idx = list_seq_find(watcher->items, | ||
37 | using_standard_protocol(watcher) ? cmp_id : cmp_service, service); | ||
38 | if (idx != -1) { | ||
39 | char *id = watcher->items->items[idx]; | ||
40 | wlr_log(WLR_DEBUG, "Unregistering Status Notifier Item '%s'", id); | ||
41 | list_del(watcher->items, idx); | ||
42 | sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, | ||
43 | "StatusNotifierItemUnregistered", "s", id); | ||
44 | free(id); | ||
45 | } | ||
46 | |||
47 | idx = list_seq_find(watcher->hosts, cmp_id, service); | ||
48 | if (idx != -1) { | ||
49 | wlr_log(WLR_DEBUG, "Unregistering Status Notifier Host '%s'", service); | ||
50 | free(watcher->hosts->items[idx]); | ||
51 | list_del(watcher->hosts, idx); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) { | ||
59 | char *service_or_path, *id; | ||
60 | int ret = sd_bus_message_read(msg, "s", &service_or_path); | ||
61 | if (ret < 0) { | ||
62 | wlr_log(WLR_ERROR, "Failed to parse register SNI message: %s", strerror(-ret)); | ||
63 | return ret; | ||
64 | } | ||
65 | |||
66 | struct swaybar_watcher *watcher = data; | ||
67 | if (using_standard_protocol(watcher)) { | ||
68 | id = strdup(service_or_path); | ||
69 | } else { | ||
70 | const char *service, *path; | ||
71 | if (service_or_path[0] == '/') { | ||
72 | service = sd_bus_message_get_sender(msg); | ||
73 | path = service_or_path; | ||
74 | } else { | ||
75 | service = service_or_path; | ||
76 | path = "/StatusNotifierItem"; | ||
77 | } | ||
78 | size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1; | ||
79 | id = malloc(id_len); | ||
80 | snprintf(id, id_len, "%s%s", service, path); | ||
81 | } | ||
82 | |||
83 | if (list_seq_find(watcher->items, cmp_id, id) == -1) { | ||
84 | wlr_log(WLR_DEBUG, "Registering Status Notifier Item '%s'", id); | ||
85 | list_add(watcher->items, id); | ||
86 | sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, | ||
87 | "StatusNotifierItemRegistered", "s", id); | ||
88 | } else { | ||
89 | wlr_log(WLR_DEBUG, "Status Notifier Item '%s' already registered", id); | ||
90 | free(id); | ||
91 | } | ||
92 | |||
93 | return sd_bus_reply_method_return(msg, ""); | ||
94 | } | ||
95 | |||
96 | static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) { | ||
97 | char *service; | ||
98 | int ret = sd_bus_message_read(msg, "s", &service); | ||
99 | if (ret < 0) { | ||
100 | wlr_log(WLR_ERROR, "Failed to parse register host message: %s", strerror(-ret)); | ||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | struct swaybar_watcher *watcher = data; | ||
105 | if (list_seq_find(watcher->hosts, cmp_id, service) == -1) { | ||
106 | wlr_log(WLR_DEBUG, "Registering Status Notifier Host '%s'", service); | ||
107 | list_add(watcher->hosts, strdup(service)); | ||
108 | sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, | ||
109 | "StatusNotifierHostRegistered", "s", service); | ||
110 | } else { | ||
111 | wlr_log(WLR_DEBUG, "Status Notifier Host '%s' already registered", service); | ||
112 | } | ||
113 | |||
114 | return sd_bus_reply_method_return(msg, ""); | ||
115 | } | ||
116 | |||
117 | static int get_registered_snis(sd_bus *bus, const char *obj_path, | ||
118 | const char *interface, const char *property, sd_bus_message *reply, | ||
119 | void *data, sd_bus_error *error) { | ||
120 | struct swaybar_watcher *watcher = data; | ||
121 | list_add(watcher->items, NULL); // strv expects NULL-terminated string array | ||
122 | int ret = sd_bus_message_append_strv(reply, (char **)watcher->items->items); | ||
123 | list_del(watcher->items, watcher->items->length - 1); | ||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | static int is_host_registered(sd_bus *bus, const char *obj_path, | ||
128 | const char *interface, const char *property, sd_bus_message *reply, | ||
129 | void *data, sd_bus_error *error) { | ||
130 | struct swaybar_watcher *watcher = data; | ||
131 | int val = watcher->hosts->length > 0; // dbus expects int rather than bool | ||
132 | return sd_bus_message_append_basic(reply, 'b', &val); | ||
133 | } | ||
134 | |||
135 | static const sd_bus_vtable watcher_vtable[] = { | ||
136 | SD_BUS_VTABLE_START(0), | ||
137 | SD_BUS_METHOD("RegisterStatusNotifierItem", "s", "", register_sni, | ||
138 | SD_BUS_VTABLE_UNPRIVILEGED), | ||
139 | SD_BUS_METHOD("RegisterStatusNotifierHost", "s", "", register_host, | ||
140 | SD_BUS_VTABLE_UNPRIVILEGED), | ||
141 | SD_BUS_PROPERTY("RegisteredStatusNotifierItems", "as", get_registered_snis, | ||
142 | 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | ||
143 | SD_BUS_PROPERTY("IsStatusNotifierHostRegistered", "b", is_host_registered, | ||
144 | 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), | ||
145 | SD_BUS_PROPERTY("ProtocolVersion", "i", NULL, | ||
146 | offsetof(struct swaybar_watcher, version), | ||
147 | SD_BUS_VTABLE_PROPERTY_CONST), | ||
148 | SD_BUS_SIGNAL("StatusNotifierItemRegistered", "s", 0), | ||
149 | SD_BUS_SIGNAL("StatusNotifierItemUnregistered", "s", 0), | ||
150 | SD_BUS_SIGNAL("StatusNotifierHostRegistered", NULL, 0), | ||
151 | SD_BUS_VTABLE_END | ||
152 | }; | ||
153 | |||
154 | struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) { | ||
155 | struct swaybar_watcher *watcher = | ||
156 | calloc(1, sizeof(struct swaybar_watcher)); | ||
157 | if (!watcher) { | ||
158 | return NULL; | ||
159 | } | ||
160 | |||
161 | size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; | ||
162 | watcher->interface = malloc(len); | ||
163 | snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol); | ||
164 | |||
165 | sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; | ||
166 | int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, | ||
167 | watcher->interface, watcher_vtable, watcher); | ||
168 | if (ret < 0) { | ||
169 | wlr_log(WLR_ERROR, "Failed to add object vtable: %s", strerror(-ret)); | ||
170 | goto error; | ||
171 | } | ||
172 | |||
173 | ret = sd_bus_match_signal(bus, &signal_slot, "org.freedesktop.DBus", | ||
174 | "/org/freedesktop/DBus", "org.freedesktop.DBus", | ||
175 | "NameOwnerChanged", handle_lost_service, watcher); | ||
176 | if (ret < 0) { | ||
177 | wlr_log(WLR_ERROR, "Failed to subscribe to unregistering events: %s", | ||
178 | strerror(-ret)); | ||
179 | goto error; | ||
180 | } | ||
181 | |||
182 | ret = sd_bus_request_name(bus, watcher->interface, 0); | ||
183 | if (ret < 0) { | ||
184 | wlr_log(WLR_ERROR, "Failed to acquire service name: %s", strerror(-ret)); | ||
185 | goto error; | ||
186 | } | ||
187 | |||
188 | sd_bus_slot_set_floating(signal_slot, 0); | ||
189 | sd_bus_slot_set_floating(vtable_slot, 0); | ||
190 | |||
191 | watcher->bus = bus; | ||
192 | watcher->hosts = create_list(); | ||
193 | watcher->items = create_list(); | ||
194 | watcher->version = 0; | ||
195 | wlr_log(WLR_DEBUG, "Registered %s", watcher->interface); | ||
196 | return watcher; | ||
197 | error: | ||
198 | sd_bus_slot_unref(signal_slot); | ||
199 | sd_bus_slot_unref(vtable_slot); | ||
200 | destroy_watcher(watcher); | ||
201 | return NULL; | ||
202 | } | ||
203 | |||
204 | void destroy_watcher(struct swaybar_watcher *watcher) { | ||
205 | if (!watcher) { | ||
206 | return; | ||
207 | } | ||
208 | list_free_items_and_destroy(watcher->hosts); | ||
209 | list_free_items_and_destroy(watcher->items); | ||
210 | free(watcher->interface); | ||
211 | free(watcher); | ||
212 | } | ||