aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar')
-rw-r--r--swaybar/bar.c56
-rw-r--r--swaybar/config.c5
-rw-r--r--swaybar/i3bar.c39
-rw-r--r--swaybar/image.c136
-rw-r--r--swaybar/input.c64
-rw-r--r--swaybar/ipc.c39
-rw-r--r--swaybar/main.c3
-rw-r--r--swaybar/meson.build5
-rw-r--r--swaybar/render.c416
-rw-r--r--swaybar/status_line.c14
-rw-r--r--swaybar/tray/host.c11
-rw-r--r--swaybar/tray/icon.c27
-rw-r--r--swaybar/tray/item.c54
-rw-r--r--swaybar/tray/tray.c4
-rw-r--r--swaybar/tray/watcher.c12
15 files changed, 576 insertions, 309 deletions
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 231c1ad7..5b1213a8 100644
--- a/swaybar/bar.c
+++ b/swaybar/bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <errno.h> 2#include <errno.h>
4#include <fcntl.h> 3#include <fcntl.h>
@@ -51,10 +50,6 @@ static void swaybar_output_free(struct swaybar_output *output) {
51 if (output->surface != NULL) { 50 if (output->surface != NULL) {
52 wl_surface_destroy(output->surface); 51 wl_surface_destroy(output->surface);
53 } 52 }
54 if (output->input_region != NULL) {
55 wl_region_destroy(output->input_region);
56 }
57 zxdg_output_v1_destroy(output->xdg_output);
58 wl_output_destroy(output->output); 53 wl_output_destroy(output->output);
59 destroy_buffer(&output->buffers[0]); 54 destroy_buffer(&output->buffers[0]);
60 destroy_buffer(&output->buffers[1]); 55 destroy_buffer(&output->buffers[1]);
@@ -90,7 +85,7 @@ static void layer_surface_closed(void *_output,
90 swaybar_output_free(output); 85 swaybar_output_free(output);
91} 86}
92 87
93struct zwlr_layer_surface_v1_listener layer_surface_listener = { 88static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
94 .configure = layer_surface_configure, 89 .configure = layer_surface_configure,
95 .closed = layer_surface_closed, 90 .closed = layer_surface_closed,
96}; 91};
@@ -114,10 +109,9 @@ static void add_layer_surface(struct swaybar_output *output) {
114 109
115 if (overlay) { 110 if (overlay) {
116 // Empty input region 111 // Empty input region
117 output->input_region = wl_compositor_create_region(bar->compositor); 112 struct wl_region *region = wl_compositor_create_region(bar->compositor);
118 assert(output->input_region); 113 wl_surface_set_input_region(output->surface, region);
119 114 wl_region_destroy(region);
120 wl_surface_set_input_region(output->surface, output->input_region);
121 } 115 }
122 116
123 zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); 117 zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position);
@@ -172,7 +166,7 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) {
172 if (bar->status) { 166 if (bar->status) {
173 sway_log(SWAY_DEBUG, "Sending %s signal to status command", 167 sway_log(SWAY_DEBUG, "Sending %s signal to status command",
174 visible ? "cont" : "stop"); 168 visible ? "cont" : "stop");
175 kill(bar->status->pid, visible ? 169 kill(-bar->status->pid, visible ?
176 bar->status->cont_signal : bar->status->stop_signal); 170 bar->status->cont_signal : bar->status->stop_signal);
177 } 171 }
178 } 172 }
@@ -230,7 +224,7 @@ static void output_scale(void *data, struct wl_output *wl_output,
230 } 224 }
231} 225}
232 226
233struct wl_output_listener output_listener = { 227static const struct wl_output_listener output_listener = {
234 .geometry = output_geometry, 228 .geometry = output_geometry,
235 .mode = output_mode, 229 .mode = output_mode,
236 .done = output_done, 230 .done = output_done,
@@ -307,7 +301,7 @@ static void xdg_output_handle_description(void *data,
307 } 301 }
308} 302}
309 303
310struct zxdg_output_v1_listener xdg_output_listener = { 304static const struct zxdg_output_v1_listener xdg_output_listener = {
311 .logical_position = xdg_output_handle_logical_position, 305 .logical_position = xdg_output_handle_logical_position,
312 .logical_size = xdg_output_handle_logical_size, 306 .logical_size = xdg_output_handle_logical_size,
313 .done = xdg_output_handle_done, 307 .done = xdg_output_handle_done,
@@ -367,6 +361,9 @@ static void handle_global(void *data, struct wl_registry *registry,
367 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { 361 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
368 bar->xdg_output_manager = wl_registry_bind(registry, name, 362 bar->xdg_output_manager = wl_registry_bind(registry, name,
369 &zxdg_output_manager_v1_interface, 2); 363 &zxdg_output_manager_v1_interface, 2);
364 } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
365 bar->cursor_shape_manager = wl_registry_bind(registry, name,
366 &wp_cursor_shape_manager_v1_interface, 1);
370 } 367 }
371} 368}
372 369
@@ -430,15 +427,17 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
430 // Second roundtrip for xdg-output 427 // Second roundtrip for xdg-output
431 wl_display_roundtrip(bar->display); 428 wl_display_roundtrip(bar->display);
432 429
433 struct swaybar_seat *seat; 430 if (!bar->cursor_shape_manager) {
434 wl_list_for_each(seat, &bar->seats, link) { 431 struct swaybar_seat *seat;
435 struct swaybar_pointer *pointer = &seat->pointer; 432 wl_list_for_each(seat, &bar->seats, link) {
436 if (!pointer) { 433 struct swaybar_pointer *pointer = &seat->pointer;
437 continue; 434 if (!pointer) {
435 continue;
436 }
437 pointer->cursor_surface =
438 wl_compositor_create_surface(bar->compositor);
439 assert(pointer->cursor_surface);
438 } 440 }
439 pointer->cursor_surface =
440 wl_compositor_create_surface(bar->compositor);
441 assert(pointer->cursor_surface);
442 } 441 }
443 442
444 if (bar->config->status_command) { 443 if (bar->config->status_command) {
@@ -461,13 +460,28 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
461 460
462static void display_in(int fd, short mask, void *data) { 461static void display_in(int fd, short mask, void *data) {
463 struct swaybar *bar = data; 462 struct swaybar *bar = data;
463 if (mask & (POLLHUP | POLLERR)) {
464 if (mask & POLLERR) {
465 sway_log(SWAY_ERROR, "Wayland display poll error");
466 }
467 bar->running = false;
468 return;
469 }
464 if (wl_display_dispatch(bar->display) == -1) { 470 if (wl_display_dispatch(bar->display) == -1) {
471 sway_log(SWAY_ERROR, "wl_display_dispatch failed");
465 bar->running = false; 472 bar->running = false;
466 } 473 }
467} 474}
468 475
469static void ipc_in(int fd, short mask, void *data) { 476static void ipc_in(int fd, short mask, void *data) {
470 struct swaybar *bar = data; 477 struct swaybar *bar = data;
478 if (mask & (POLLHUP | POLLERR)) {
479 if (mask & POLLERR) {
480 sway_log(SWAY_ERROR, "IPC poll error");
481 }
482 bar->running = false;
483 return;
484 }
471 if (handle_ipc_readable(bar)) { 485 if (handle_ipc_readable(bar)) {
472 set_bar_dirty(bar); 486 set_bar_dirty(bar);
473 } 487 }
diff --git a/swaybar/config.c b/swaybar/config.c
index abedaec0..55bfcb72 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 1#include <stdlib.h>
3#include <string.h> 2#include <string.h>
4#include "swaybar/config.h" 3#include "swaybar/config.h"
@@ -26,7 +25,7 @@ struct swaybar_config *init_config(void) {
26 config->status_command = NULL; 25 config->status_command = NULL;
27 config->pango_markup = false; 26 config->pango_markup = false;
28 config->position = parse_position("bottom"); 27 config->position = parse_position("bottom");
29 config->font = strdup("monospace 10"); 28 config->font_description = pango_font_description_from_string("monospace 10");
30 config->mode = strdup("dock"); 29 config->mode = strdup("dock");
31 config->hidden_state = strdup("hide"); 30 config->hidden_state = strdup("hide");
32 config->sep_symbol = NULL; 31 config->sep_symbol = NULL;
@@ -105,7 +104,7 @@ void free_tray_binding(struct tray_binding *binding) {
105 104
106void free_config(struct swaybar_config *config) { 105void free_config(struct swaybar_config *config) {
107 free(config->status_command); 106 free(config->status_command);
108 free(config->font); 107 pango_font_description_free(config->font_description);
109 free(config->mode); 108 free(config->mode);
110 free(config->hidden_state); 109 free(config->hidden_state);
111 free(config->sep_symbol); 110 free(config->sep_symbol);
diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c
index 4bcd5843..62c22d43 100644
--- a/swaybar/i3bar.c
+++ b/swaybar/i3bar.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <json.h> 1#include <json.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <ctype.h> 3#include <ctype.h>
@@ -28,6 +27,19 @@ void i3bar_block_unref(struct i3bar_block *block) {
28 } 27 }
29} 28}
30 29
30static bool i3bar_parse_json_color(json_object *json, uint32_t *color) {
31 if (!json) {
32 return false;
33 }
34
35 const char *hexstring = json_object_get_string(json);
36 bool color_set = parse_color(hexstring, color);
37 if (!color_set) {
38 sway_log(SWAY_ERROR, "Ignoring invalid block hexadecimal color string: %s", hexstring);
39 }
40 return color_set;
41}
42
31static void i3bar_parse_json(struct status_line *status, 43static void i3bar_parse_json(struct status_line *status,
32 struct json_object *json_array) { 44 struct json_object *json_array) {
33 struct i3bar_block *block, *tmp; 45 struct i3bar_block *block, *tmp;
@@ -68,13 +80,7 @@ static void i3bar_parse_json(struct status_line *status,
68 strdup(json_object_get_string(full_text)) : NULL; 80 strdup(json_object_get_string(full_text)) : NULL;
69 block->short_text = short_text ? 81 block->short_text = short_text ?
70 strdup(json_object_get_string(short_text)) : NULL; 82 strdup(json_object_get_string(short_text)) : NULL;
71 if (color) { 83 block->color_set = i3bar_parse_json_color(color, &block->color);
72 const char *hexstring = json_object_get_string(color);
73 block->color_set = parse_color(hexstring, &block->color);
74 if (!block->color_set) {
75 sway_log(SWAY_ERROR, "Invalid block color: %s", hexstring);
76 }
77 }
78 if (min_width) { 84 if (min_width) {
79 json_type type = json_object_get_type(min_width); 85 json_type type = json_object_get_type(min_width);
80 if (type == json_type_int) { 86 if (type == json_type_int) {
@@ -100,14 +106,8 @@ static void i3bar_parse_json(struct status_line *status,
100 block->separator_block_width = separator_block_width ? 106 block->separator_block_width = separator_block_width ?
101 json_object_get_int(separator_block_width) : 9; 107 json_object_get_int(separator_block_width) : 9;
102 // Airblader features 108 // Airblader features
103 const char *hex = background ? json_object_get_string(background) : NULL; 109 i3bar_parse_json_color(background, &block->background);
104 if (hex && !parse_color(hex, &block->background)) { 110 block->border_set = i3bar_parse_json_color(border, &block->border);
105 sway_log(SWAY_ERROR, "Ignoring invalid block background: %s", hex);
106 }
107 hex = border ? json_object_get_string(border) : NULL;
108 if (hex && !parse_color(hex, &block->border)) {
109 sway_log(SWAY_ERROR, "Ignoring invalid block border: %s", hex);
110 }
111 block->border_top = border_top ? json_object_get_int(border_top) : 1; 111 block->border_top = border_top ? json_object_get_int(border_top) : 1;
112 block->border_bottom = border_bottom ? 112 block->border_bottom = border_bottom ?
113 json_object_get_int(border_bottom) : 1; 113 json_object_get_int(border_bottom) : 1;
@@ -268,11 +268,16 @@ bool i3bar_handle_readable(struct status_line *status) {
268 268
269enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, 269enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
270 struct i3bar_block *block, double x, double y, double rx, double ry, 270 struct i3bar_block *block, double x, double y, double rx, double ry,
271 double w, double h, int scale, uint32_t button) { 271 double w, double h, int scale, uint32_t button, bool released) {
272 sway_log(SWAY_DEBUG, "block %s clicked", block->name); 272 sway_log(SWAY_DEBUG, "block %s clicked", block->name);
273 if (!block->name || !status->click_events) { 273 if (!block->name || !status->click_events) {
274 return HOTSPOT_PROCESS; 274 return HOTSPOT_PROCESS;
275 } 275 }
276 if (released) {
277 // Since we handle the pressed event, also handle the released event
278 // to block it from falling through to a binding in the bar
279 return HOTSPOT_IGNORE;
280 }
276 281
277 struct json_object *event_json = json_object_new_object(); 282 struct json_object *event_json = json_object_new_object();
278 json_object_object_add(event_json, "name", 283 json_object_object_add(event_json, "name",
diff --git a/swaybar/image.c b/swaybar/image.c
new file mode 100644
index 00000000..ed24b9f9
--- /dev/null
+++ b/swaybar/image.c
@@ -0,0 +1,136 @@
1#include <assert.h>
2#include "config.h"
3#include "log.h"
4#include "swaybar/image.h"
5
6#if HAVE_GDK_PIXBUF
7#include <gdk-pixbuf/gdk-pixbuf.h>
8#endif
9
10#if HAVE_GDK_PIXBUF
11static cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(
12 const GdkPixbuf *gdkbuf) {
13 int chan = gdk_pixbuf_get_n_channels(gdkbuf);
14 if (chan < 3) {
15 return NULL;
16 }
17
18 const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf);
19 if (!gdkpix) {
20 return NULL;
21 }
22 gint w = gdk_pixbuf_get_width(gdkbuf);
23 gint h = gdk_pixbuf_get_height(gdkbuf);
24 int stride = gdk_pixbuf_get_rowstride(gdkbuf);
25
26 cairo_format_t fmt = (chan == 3) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;
27 cairo_surface_t * cs = cairo_image_surface_create (fmt, w, h);
28 cairo_surface_flush (cs);
29 if ( !cs || cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) {
30 return NULL;
31 }
32
33 int cstride = cairo_image_surface_get_stride(cs);
34 unsigned char * cpix = cairo_image_surface_get_data(cs);
35
36 if (chan == 3) {
37 int i;
38 for (i = h; i; --i) {
39 const guint8 *gp = gdkpix;
40 unsigned char *cp = cpix;
41 const guint8* end = gp + 3*w;
42 while (gp < end) {
43#if G_BYTE_ORDER == G_LITTLE_ENDIAN
44 cp[0] = gp[2];
45 cp[1] = gp[1];
46 cp[2] = gp[0];
47#else
48 cp[1] = gp[0];
49 cp[2] = gp[1];
50 cp[3] = gp[2];
51#endif
52 gp += 3;
53 cp += 4;
54 }
55 gdkpix += stride;
56 cpix += cstride;
57 }
58 } else {
59 /* premul-color = alpha/255 * color/255 * 255 = (alpha*color)/255
60 * (z/255) = z/256 * 256/255 = z/256 (1 + 1/255)
61 * = z/256 + (z/256)/255 = (z + z/255)/256
62 * # recurse once
63 * = (z + (z + z/255)/256)/256
64 * = (z + z/256 + z/256/255) / 256
65 * # only use 16bit uint operations, loose some precision,
66 * # result is floored.
67 * -> (z + z>>8)>>8
68 * # add 0x80/255 = 0.5 to convert floor to round
69 * => (z+0x80 + (z+0x80)>>8 ) >> 8
70 * ------
71 * tested as equal to lround(z/255.0) for uint z in [0..0xfe02]
72 */
73#define PREMUL_ALPHA(x,a,b,z) \
74 G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \
75 G_STMT_END
76 int i;
77 for (i = h; i; --i) {
78 const guint8 *gp = gdkpix;
79 unsigned char *cp = cpix;
80 const guint8* end = gp + 4*w;
81 guint z1, z2, z3;
82 while (gp < end) {
83#if G_BYTE_ORDER == G_LITTLE_ENDIAN
84 PREMUL_ALPHA(cp[0], gp[2], gp[3], z1);
85 PREMUL_ALPHA(cp[1], gp[1], gp[3], z2);
86 PREMUL_ALPHA(cp[2], gp[0], gp[3], z3);
87 cp[3] = gp[3];
88#else
89 PREMUL_ALPHA(cp[1], gp[0], gp[3], z1);
90 PREMUL_ALPHA(cp[2], gp[1], gp[3], z2);
91 PREMUL_ALPHA(cp[3], gp[2], gp[3], z3);
92 cp[0] = gp[3];
93#endif
94 gp += 4;
95 cp += 4;
96 }
97 gdkpix += stride;
98 cpix += cstride;
99 }
100#undef PREMUL_ALPHA
101 }
102 cairo_surface_mark_dirty(cs);
103 return cs;
104}
105#endif // HAVE_GDK_PIXBUF
106
107cairo_surface_t *load_image(const char *path) {
108 cairo_surface_t *image;
109#if HAVE_GDK_PIXBUF
110 GError *err = NULL;
111 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
112 if (!pixbuf) {
113 sway_log(SWAY_ERROR, "Failed to load background image (%s).",
114 err->message);
115 return NULL;
116 }
117 image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
118 g_object_unref(pixbuf);
119#else
120 image = cairo_image_surface_create_from_png(path);
121#endif // HAVE_GDK_PIXBUF
122 if (!image) {
123 sway_log(SWAY_ERROR, "Failed to read background image.");
124 return NULL;
125 }
126 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
127 sway_log(SWAY_ERROR, "Failed to read background image: %s."
128#if !HAVE_GDK_PIXBUF
129 "\nSway was compiled without gdk_pixbuf support, so only"
130 "\nPNG images can be loaded. This is the likely cause."
131#endif // !HAVE_GDK_PIXBUF
132 , cairo_status_to_string(cairo_surface_status(image)));
133 return NULL;
134 }
135 return image;
136}
diff --git a/swaybar/input.c b/swaybar/input.c
index 4fe6dd93..ada4bc86 100644
--- a/swaybar/input.c
+++ b/swaybar/input.c
@@ -81,8 +81,16 @@ void update_cursor(struct swaybar_seat *seat) {
81 int scale = pointer->current ? pointer->current->scale : 1; 81 int scale = pointer->current ? pointer->current->scale : 1;
82 pointer->cursor_theme = wl_cursor_theme_load( 82 pointer->cursor_theme = wl_cursor_theme_load(
83 cursor_theme, cursor_size * scale, seat->bar->shm); 83 cursor_theme, cursor_size * scale, seat->bar->shm);
84 if (!pointer->cursor_theme) {
85 sway_log(SWAY_ERROR, "Failed to load cursor theme");
86 return;
87 }
84 struct wl_cursor *cursor; 88 struct wl_cursor *cursor;
85 cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); 89 cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default");
90 if (!cursor) {
91 sway_log(SWAY_ERROR, "Failed to get default cursor from theme");
92 return;
93 }
86 pointer->cursor_image = cursor->images[0]; 94 pointer->cursor_image = cursor->images[0];
87 wl_surface_set_buffer_scale(pointer->cursor_surface, scale); 95 wl_surface_set_buffer_scale(pointer->cursor_surface, scale);
88 wl_surface_attach(pointer->cursor_surface, 96 wl_surface_attach(pointer->cursor_surface,
@@ -101,7 +109,9 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
101 wl_fixed_t surface_x, wl_fixed_t surface_y) { 109 wl_fixed_t surface_x, wl_fixed_t surface_y) {
102 struct swaybar_seat *seat = data; 110 struct swaybar_seat *seat = data;
103 struct swaybar_pointer *pointer = &seat->pointer; 111 struct swaybar_pointer *pointer = &seat->pointer;
104 pointer->serial = serial; 112 seat->pointer.x = wl_fixed_to_double(surface_x);
113 seat->pointer.y = wl_fixed_to_double(surface_y);
114
105 struct swaybar_output *output; 115 struct swaybar_output *output;
106 wl_list_for_each(output, &seat->bar->outputs, link) { 116 wl_list_for_each(output, &seat->bar->outputs, link) {
107 if (output->surface == surface) { 117 if (output->surface == surface) {
@@ -109,7 +119,18 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
109 break; 119 break;
110 } 120 }
111 } 121 }
112 update_cursor(seat); 122
123 if (seat->bar->cursor_shape_manager) {
124 struct wp_cursor_shape_device_v1 *device =
125 wp_cursor_shape_manager_v1_get_pointer(
126 seat->bar->cursor_shape_manager, wl_pointer);
127 wp_cursor_shape_device_v1_set_shape(device, serial,
128 WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
129 wp_cursor_shape_device_v1_destroy(device);
130 } else {
131 pointer->serial = serial;
132 update_cursor(seat);
133 }
113} 134}
114 135
115static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, 136static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
@@ -139,16 +160,15 @@ static bool check_bindings(struct swaybar *bar, uint32_t button,
139} 160}
140 161
141static bool process_hotspots(struct swaybar_output *output, 162static bool process_hotspots(struct swaybar_output *output,
142 double x, double y, uint32_t button) { 163 double x, double y, uint32_t button, uint32_t state) {
143 double px = x * output->scale; 164 bool released = state == WL_POINTER_BUTTON_STATE_RELEASED;
144 double py = y * output->scale;
145 struct swaybar_hotspot *hotspot; 165 struct swaybar_hotspot *hotspot;
146 wl_list_for_each(hotspot, &output->hotspots, link) { 166 wl_list_for_each(hotspot, &output->hotspots, link) {
147 if (px >= hotspot->x && py >= hotspot->y 167 if (x >= hotspot->x && y >= hotspot->y
148 && px < hotspot->x + hotspot->width 168 && x < hotspot->x + hotspot->width
149 && py < hotspot->y + hotspot->height) { 169 && y < hotspot->y + hotspot->height) {
150 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, 170 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y,
151 button, hotspot->data)) { 171 button, released, hotspot->data)) {
152 return true; 172 return true;
153 } 173 }
154 } 174 }
@@ -166,14 +186,11 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
166 return; 186 return;
167 } 187 }
168 188
169 if (check_bindings(seat->bar, button, state)) { 189 if (process_hotspots(output, pointer->x, pointer->y, button, state)) {
170 return; 190 return;
171 } 191 }
172 192
173 if (state != WL_POINTER_BUTTON_STATE_PRESSED) { 193 check_bindings(seat->bar, button, state);
174 return;
175 }
176 process_hotspots(output, pointer->x, pointer->y, button);
177} 194}
178 195
179static void workspace_next(struct swaybar *bar, struct swaybar_output *output, 196static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
@@ -209,7 +226,7 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
209 } 226 }
210 } 227 }
211 228
212 if (new) { 229 if (new && new != active) {
213 ipc_send_workspace_command(bar, new->name); 230 ipc_send_workspace_command(bar, new->name);
214 231
215 // Since we're asking Sway to switch to 'new', it should become visible. 232 // Since we're asking Sway to switch to 'new', it should become visible.
@@ -222,15 +239,15 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
222static void process_discrete_scroll(struct swaybar_seat *seat, 239static void process_discrete_scroll(struct swaybar_seat *seat,
223 struct swaybar_output *output, struct swaybar_pointer *pointer, 240 struct swaybar_output *output, struct swaybar_pointer *pointer,
224 uint32_t axis, wl_fixed_t value) { 241 uint32_t axis, wl_fixed_t value) {
225 // If there is a button press binding, execute it, skip default behavior,
226 // and check button release bindings
227 uint32_t button = wl_axis_to_button(axis, value); 242 uint32_t button = wl_axis_to_button(axis, value);
228 if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { 243 if (process_hotspots(output, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
229 check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED); 244 // (Currently hotspots don't do anything on release events, so no need to emit one)
230 return; 245 return;
231 } 246 }
232 247
233 if (process_hotspots(output, pointer->x, pointer->y, button)) { 248 // If there is a button press binding, execute it, and check button release bindings
249 if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
250 check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
234 return; 251 return;
235 } 252 }
236 253
@@ -339,7 +356,7 @@ static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
339 seat->axis[axis].discrete_steps += abs(discrete); 356 seat->axis[axis].discrete_steps += abs(discrete);
340} 357}
341 358
342static struct wl_pointer_listener pointer_listener = { 359static const struct wl_pointer_listener pointer_listener = {
343 .enter = wl_pointer_enter, 360 .enter = wl_pointer_enter,
344 .leave = wl_pointer_leave, 361 .leave = wl_pointer_leave,
345 .motion = wl_pointer_motion, 362 .motion = wl_pointer_motion,
@@ -403,7 +420,8 @@ static void wl_touch_up(void *data, struct wl_touch *wl_touch,
403 } 420 }
404 if (time - slot->time < 500) { 421 if (time - slot->time < 500) {
405 // Tap, treat it like a pointer click 422 // Tap, treat it like a pointer click
406 process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT); 423 process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
424 // (Currently hotspots don't do anything on release events, so no need to emit one)
407 } 425 }
408 slot->output = NULL; 426 slot->output = NULL;
409} 427}
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index 6bbe9408..03500bdf 100644
--- a/swaybar/ipc.c
+++ b/swaybar/ipc.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <limits.h> 1#include <limits.h>
3#include <poll.h> 2#include <poll.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -147,8 +146,10 @@ static bool ipc_parse_config(
147 146
148 json_object *font = json_object_object_get(bar_config, "font"); 147 json_object *font = json_object_object_get(bar_config, "font");
149 if (font) { 148 if (font) {
150 free(config->font); 149 pango_font_description_free(config->font_description);
151 config->font = parse_font(json_object_get_string(font)); 150 char *font_value = parse_font(json_object_get_string(font));
151 config->font_description = pango_font_description_from_string(font_value);
152 free(font_value);
152 } 153 }
153 154
154 json_object *gaps = json_object_object_get(bar_config, "gaps"); 155 json_object *gaps = json_object_object_get(bar_config, "gaps");
@@ -424,12 +425,9 @@ bool ipc_initialize(struct swaybar *bar) {
424 } 425 }
425 free(res); 426 free(res);
426 427
427 struct swaybar_config *config = bar->config; 428 char *subscribe =
428 char subscribe[128]; // suitably large buffer 429 "[ \"barconfig_update\", \"bar_state_update\", \"mode\", \"workspace\" ]";
429 len = snprintf(subscribe, 128, 430 len = strlen(subscribe);
430 "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]",
431 config->binding_mode_indicator ? ", \"mode\"" : "",
432 config->workspace_buttons ? ", \"workspace\"" : "");
433 free(ipc_single_command(bar->ipc_event_socketfd, 431 free(ipc_single_command(bar->ipc_event_socketfd,
434 IPC_SUBSCRIBE, subscribe, &len)); 432 IPC_SUBSCRIBE, subscribe, &len));
435 return true; 433 return true;
@@ -485,8 +483,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload,
485 destroy_layer_surface(output); 483 destroy_layer_surface(output);
486 wl_list_remove(&output->link); 484 wl_list_remove(&output->link);
487 wl_list_insert(&bar->unused_outputs, &output->link); 485 wl_list_insert(&bar->unused_outputs, &output->link);
488 } else if (!oldcfg->font || !newcfg->font || 486 } else if (!pango_font_description_equal(oldcfg->font_description, newcfg->font_description)) {
489 strcmp(oldcfg->font, newcfg->font) != 0) {
490 output->height = 0; // force update height 487 output->height = 0; // force update height
491 } 488 }
492 } 489 }
@@ -547,9 +544,23 @@ bool handle_ipc_readable(struct swaybar *bar) {
547 return false; 544 return false;
548 } 545 }
549 546
550 json_object *result = json_tokener_parse(resp->payload); 547 // The default depth of 32 is too small to represent some nested layouts, but
551 if (!result) { 548 // we can't pass INT_MAX here because json-c (as of this writing) prefaults
552 sway_log(SWAY_ERROR, "failed to parse payload as json"); 549 // all the memory for its stack.
550 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
551 if (!tok) {
552 sway_log_errno(SWAY_ERROR, "failed to create tokener");
553 free_ipc_response(resp);
554 return false;
555 }
556
557 json_object *result = json_tokener_parse_ex(tok, resp->payload, -1);
558 enum json_tokener_error err = json_tokener_get_error(tok);
559 json_tokener_free(tok);
560
561 if (err != json_tokener_success) {
562 sway_log(SWAY_ERROR, "failed to parse payload as json: %s",
563 json_tokener_error_desc(err));
553 free_ipc_response(resp); 564 free_ipc_response(resp);
554 return false; 565 return false;
555 } 566 }
diff --git a/swaybar/main.c b/swaybar/main.c
index 5c36d66b..3dc67233 100644
--- a/swaybar/main.c
+++ b/swaybar/main.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdio.h> 1#include <stdio.h>
3#include <stdlib.h> 2#include <stdlib.h>
4#include <string.h> 3#include <string.h>
@@ -18,7 +17,7 @@ int main(int argc, char **argv) {
18 char *socket_path = NULL; 17 char *socket_path = NULL;
19 bool debug = false; 18 bool debug = false;
20 19
21 static struct option long_options[] = { 20 static const struct option long_options[] = {
22 {"help", no_argument, NULL, 'h'}, 21 {"help", no_argument, NULL, 'h'},
23 {"version", no_argument, NULL, 'v'}, 22 {"version", no_argument, NULL, 'v'},
24 {"socket", required_argument, NULL, 's'}, 23 {"socket", required_argument, NULL, 's'},
diff --git a/swaybar/meson.build b/swaybar/meson.build
index 9feb3cd2..34bbdeea 100644
--- a/swaybar/meson.build
+++ b/swaybar/meson.build
@@ -8,7 +8,6 @@ tray_files = have_tray ? [
8 8
9swaybar_deps = [ 9swaybar_deps = [
10 cairo, 10 cairo,
11 client_protos,
12 gdk_pixbuf, 11 gdk_pixbuf,
13 jsonc, 12 jsonc,
14 math, 13 math,
@@ -27,12 +26,14 @@ executable(
27 'bar.c', 26 'bar.c',
28 'config.c', 27 'config.c',
29 'i3bar.c', 28 'i3bar.c',
29 'image.c',
30 'input.c', 30 'input.c',
31 'ipc.c', 31 'ipc.c',
32 'main.c', 32 'main.c',
33 'render.c', 33 'render.c',
34 'status_line.c', 34 'status_line.c',
35 tray_files 35 tray_files,
36 wl_protos_src,
36 ], 37 ],
37 include_directories: [sway_inc], 38 include_directories: [sway_inc],
38 dependencies: swaybar_deps, 39 dependencies: swaybar_deps,
diff --git a/swaybar/render.c b/swaybar/render.c
index df066622..879a4e42 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,11 +1,10 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <linux/input-event-codes.h> 2#include <linux/input-event-codes.h>
4#include <limits.h> 3#include <limits.h>
5#include <stdlib.h> 4#include <stdlib.h>
6#include <stdint.h> 5#include <stdint.h>
7#include <string.h> 6#include <string.h>
8#include "cairo.h" 7#include "cairo_util.h"
9#include "pango.h" 8#include "pango.h"
10#include "pool-buffer.h" 9#include "pool-buffer.h"
11#include "swaybar/bar.h" 10#include "swaybar/bar.h"
@@ -14,6 +13,7 @@
14#include "swaybar/ipc.h" 13#include "swaybar/ipc.h"
15#include "swaybar/render.h" 14#include "swaybar/render.h"
16#include "swaybar/status_line.h" 15#include "swaybar/status_line.h"
16#include "log.h"
17#if HAVE_TRAY 17#if HAVE_TRAY
18#include "swaybar/tray/tray.h" 18#include "swaybar/tray/tray.h"
19#endif 19#endif
@@ -23,28 +23,51 @@ static const int WS_HORIZONTAL_PADDING = 5;
23static const double WS_VERTICAL_PADDING = 1.5; 23static const double WS_VERTICAL_PADDING = 1.5;
24static const double BORDER_WIDTH = 1; 24static const double BORDER_WIDTH = 1;
25 25
26static uint32_t render_status_line_error(cairo_t *cairo, 26struct render_context {
27 struct swaybar_output *output, double *x) { 27 cairo_t *cairo;
28 struct swaybar_output *output;
29 cairo_font_options_t *textaa_sharp;
30 cairo_font_options_t *textaa_safe;
31 uint32_t background_color;
32};
33
34static void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) {
35 uint32_t salpha = fontcolor & 0xFF;
36 uint32_t balpha = ctx->background_color & 0xFF;
37
38 // Subpixel antialiasing requires blend be done in cairo, not compositor
39 cairo_font_options_t *fo = salpha == balpha ?
40 ctx->textaa_sharp : ctx->textaa_safe;
41 cairo_set_font_options(ctx->cairo, fo);
42
43 // Color emojis, being semitransparent bitmaps, are leaky with 'SOURCE'
44 cairo_operator_t op = salpha == 0xFF ?
45 CAIRO_OPERATOR_OVER : CAIRO_OPERATOR_SOURCE;
46 cairo_set_operator(ctx->cairo, op);
47}
48
49static uint32_t render_status_line_error(struct render_context *ctx, double *x) {
50 struct swaybar_output *output = ctx->output;
28 const char *error = output->bar->status->text; 51 const char *error = output->bar->status->text;
29 if (!error) { 52 if (!error) {
30 return 0; 53 return 0;
31 } 54 }
32 55
33 uint32_t height = output->height * output->scale; 56 uint32_t height = output->height;
34 57
58 cairo_t *cairo = ctx->cairo;
35 cairo_set_source_u32(cairo, 0xFF0000FF); 59 cairo_set_source_u32(cairo, 0xFF0000FF);
36 60
37 int margin = 3 * output->scale; 61 int margin = 3;
38 double ws_vertical_padding = 62 double ws_vertical_padding = output->bar->config->status_padding;
39 output->bar->config->status_padding * output->scale;
40 63
41 char *font = output->bar->config->font; 64 PangoFontDescription *font = output->bar->config->font_description;
42 int text_width, text_height; 65 int text_width, text_height;
43 get_text_size(cairo, font, &text_width, &text_height, NULL, 66 get_text_size(cairo, font, &text_width, &text_height, NULL,
44 output->scale, false, "%s", error); 67 1, false, "%s", error);
45 68
46 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 69 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
47 uint32_t ideal_surface_height = ideal_height / output->scale; 70 uint32_t ideal_surface_height = ideal_height;
48 if (!output->bar->config->height && 71 if (!output->bar->config->height &&
49 output->height < ideal_surface_height) { 72 output->height < ideal_surface_height) {
50 return ideal_surface_height; 73 return ideal_surface_height;
@@ -53,42 +76,45 @@ static uint32_t render_status_line_error(cairo_t *cairo,
53 76
54 double text_y = height / 2.0 - text_height / 2.0; 77 double text_y = height / 2.0 - text_height / 2.0;
55 cairo_move_to(cairo, *x, (int)floor(text_y)); 78 cairo_move_to(cairo, *x, (int)floor(text_y));
56 pango_printf(cairo, font, output->scale, false, "%s", error); 79 choose_text_aa_mode(ctx, 0xFF0000FF);
80 render_text(cairo, font, 1, false, "%s", error);
57 *x -= margin; 81 *x -= margin;
58 return output->height; 82 return output->height;
59} 83}
60 84
61static uint32_t render_status_line_text(cairo_t *cairo, 85static uint32_t render_status_line_text(struct render_context *ctx, double *x) {
62 struct swaybar_output *output, double *x) { 86 struct swaybar_output *output = ctx->output;
63 const char *text = output->bar->status->text; 87 const char *text = output->bar->status->text;
64 if (!text) { 88 if (!text) {
65 return 0; 89 return 0;
66 } 90 }
67 91
92 cairo_t *cairo = ctx->cairo;
68 struct swaybar_config *config = output->bar->config; 93 struct swaybar_config *config = output->bar->config;
69 cairo_set_source_u32(cairo, output->focused ? 94 uint32_t fontcolor = output->focused ?
70 config->colors.focused_statusline : config->colors.statusline); 95 config->colors.focused_statusline : config->colors.statusline;
96 cairo_set_source_u32(cairo, fontcolor);
71 97
72 int text_width, text_height; 98 int text_width, text_height;
73 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 99 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
74 output->scale, config->pango_markup, "%s", text); 100 1, config->pango_markup, "%s", text);
75 101
76 double ws_vertical_padding = config->status_padding * output->scale; 102 double ws_vertical_padding = config->status_padding;
77 int margin = 3 * output->scale; 103 int margin = 3;
78 104
79 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 105 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
80 uint32_t ideal_surface_height = ideal_height / output->scale; 106 uint32_t ideal_surface_height = ideal_height;
81 if (!output->bar->config->height && 107 if (!output->bar->config->height &&
82 output->height < ideal_surface_height) { 108 output->height < ideal_surface_height) {
83 return ideal_surface_height; 109 return ideal_surface_height;
84 } 110 }
85 111
86 *x -= text_width + margin; 112 *x -= text_width + margin;
87 uint32_t height = output->height * output->scale; 113 uint32_t height = output->height;
88 double text_y = height / 2.0 - text_height / 2.0; 114 double text_y = height / 2.0 - text_height / 2.0;
89 cairo_move_to(cairo, *x, (int)floor(text_y)); 115 cairo_move_to(cairo, *x, (int)floor(text_y));
90 pango_printf(cairo, config->font, output->scale, 116 choose_text_aa_mode(ctx, fontcolor);
91 config->pango_markup, "%s", text); 117 render_text(cairo, config->font_description, 1, config->pango_markup, "%s", text);
92 *x -= margin; 118 *x -= margin;
93 return output->height; 119 return output->height;
94} 120}
@@ -96,6 +122,7 @@ static uint32_t render_status_line_text(cairo_t *cairo,
96static void render_sharp_rectangle(cairo_t *cairo, uint32_t color, 122static void render_sharp_rectangle(cairo_t *cairo, uint32_t color,
97 double x, double y, double width, double height) { 123 double x, double y, double width, double height) {
98 cairo_save(cairo); 124 cairo_save(cairo);
125 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
99 cairo_set_source_u32(cairo, color); 126 cairo_set_source_u32(cairo, color);
100 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 127 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
101 cairo_rectangle(cairo, x, y, width, height); 128 cairo_rectangle(cairo, x, y, width, height);
@@ -109,6 +136,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
109 render_sharp_rectangle(cairo, color, x, y, width, height); 136 render_sharp_rectangle(cairo, color, x, y, width, height);
110 } else { 137 } else {
111 cairo_save(cairo); 138 cairo_save(cairo);
139 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
112 cairo_set_source_u32(cairo, color); 140 cairo_set_source_u32(cairo, color);
113 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 141 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
114 if (width == 1) { 142 if (width == 1) {
@@ -131,24 +159,23 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
131 159
132static enum hotspot_event_handling block_hotspot_callback( 160static enum hotspot_event_handling block_hotspot_callback(
133 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 161 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
134 double x, double y, uint32_t button, void *data) { 162 double x, double y, uint32_t button, bool released, void *data) {
135 struct i3bar_block *block = data; 163 struct i3bar_block *block = data;
136 struct status_line *status = output->bar->status; 164 struct status_line *status = output->bar->status;
137 return i3bar_block_send_click(status, block, x, y, 165 return i3bar_block_send_click(status, block, x, y,
138 x - (double)hotspot->x / output->scale, 166 x - (double)hotspot->x,
139 y - (double)hotspot->y / output->scale, 167 y - (double)hotspot->y,
140 (double)hotspot->width / output->scale, 168 (double)hotspot->width,
141 (double)hotspot->height / output->scale, 169 (double)hotspot->height,
142 output->scale, button); 170 output->scale, button, released);
143} 171}
144 172
145static void i3bar_block_unref_callback(void *data) { 173static void i3bar_block_unref_callback(void *data) {
146 i3bar_block_unref(data); 174 i3bar_block_unref(data);
147} 175}
148 176
149static uint32_t render_status_block(cairo_t *cairo, 177static uint32_t render_status_block(struct render_context *ctx,
150 struct swaybar_output *output, struct i3bar_block *block, double *x, 178 struct i3bar_block *block, double *x, bool edge, bool use_short_text) {
151 bool edge, bool use_short_text) {
152 if (!block->full_text || !*block->full_text) { 179 if (!block->full_text || !*block->full_text) {
153 return 0; 180 return 0;
154 } 181 }
@@ -158,20 +185,21 @@ static uint32_t render_status_block(cairo_t *cairo,
158 text = block->short_text; 185 text = block->short_text;
159 } 186 }
160 187
188 cairo_t *cairo = ctx->cairo;
189 struct swaybar_output *output = ctx->output;
161 struct swaybar_config *config = output->bar->config; 190 struct swaybar_config *config = output->bar->config;
162
163 int text_width, text_height; 191 int text_width, text_height;
164 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 192 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,
165 output->scale, block->markup, "%s", text); 193 block->markup, "%s", text);
166 194
167 int margin = 3 * output->scale; 195 int margin = 3;
168 double ws_vertical_padding = config->status_padding * output->scale; 196 double ws_vertical_padding = config->status_padding;
169 197
170 int width = text_width; 198 int width = text_width;
171 if (block->min_width_str) { 199 if (block->min_width_str) {
172 int w; 200 int w;
173 get_text_size(cairo, config->font, &w, NULL, NULL, 201 get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup,
174 output->scale, block->markup, "%s", block->min_width_str); 202 "%s", block->min_width_str);
175 block->min_width = w; 203 block->min_width = w;
176 } 204 }
177 if (width < block->min_width) { 205 if (width < block->min_width) {
@@ -180,30 +208,30 @@ static uint32_t render_status_block(cairo_t *cairo,
180 208
181 double block_width = width; 209 double block_width = width;
182 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 210 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
183 uint32_t ideal_surface_height = ideal_height / output->scale; 211 uint32_t ideal_surface_height = ideal_height;
184 if (!output->bar->config->height && 212 if (!output->bar->config->height &&
185 output->height < ideal_surface_height) { 213 output->height < ideal_surface_height) {
186 return ideal_surface_height; 214 return ideal_surface_height;
187 } 215 }
188 216
189 *x -= width; 217 *x -= width;
190 if ((block->border || block->urgent) && block->border_left > 0) { 218 if ((block->border_set || block->urgent) && block->border_left > 0) {
191 *x -= (block->border_left * output->scale + margin); 219 *x -= (block->border_left + margin);
192 block_width += block->border_left * output->scale + margin; 220 block_width += block->border_left + margin;
193 } 221 }
194 if ((block->border || block->urgent) && block->border_right > 0) { 222 if ((block->border_set || block->urgent) && block->border_right > 0) {
195 *x -= (block->border_right * output->scale + margin); 223 *x -= (block->border_right + margin);
196 block_width += block->border_right * output->scale + margin; 224 block_width += block->border_right + margin;
197 } 225 }
198 226
199 int sep_width, sep_height; 227 int sep_width, sep_height;
200 int sep_block_width = block->separator_block_width; 228 int sep_block_width = block->separator_block_width;
201 if (!edge) { 229 if (!edge) {
202 if (config->sep_symbol) { 230 if (config->sep_symbol) {
203 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 231 get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,
204 output->scale, false, "%s", config->sep_symbol); 232 1, false, "%s", config->sep_symbol);
205 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 233 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
206 uint32_t _ideal_surface_height = _ideal_height / output->scale; 234 uint32_t _ideal_surface_height = _ideal_height;
207 if (!output->bar->config->height && 235 if (!output->bar->config->height &&
208 output->height < _ideal_surface_height) { 236 output->height < _ideal_surface_height) {
209 return _ideal_surface_height; 237 return _ideal_surface_height;
@@ -214,10 +242,10 @@ static uint32_t render_status_block(cairo_t *cairo,
214 } 242 }
215 *x -= sep_block_width; 243 *x -= sep_block_width;
216 } else if (config->status_edge_padding) { 244 } else if (config->status_edge_padding) {
217 *x -= config->status_edge_padding * output->scale; 245 *x -= config->status_edge_padding;
218 } 246 }
219 247
220 uint32_t height = output->height * output->scale; 248 uint32_t height = output->height;
221 if (output->bar->status->click_events) { 249 if (output->bar->status->click_events) {
222 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 250 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
223 hotspot->x = *x; 251 hotspot->x = *x;
@@ -240,27 +268,30 @@ static uint32_t render_status_block(cairo_t *cairo,
240 if (bg_color) { 268 if (bg_color) {
241 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, 269 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos,
242 block_width, render_height); 270 block_width, render_height);
271 ctx->background_color = bg_color;
243 } 272 }
244 273
245 uint32_t border_color = block->urgent 274 uint32_t border_color = block->urgent
246 ? config->colors.urgent_workspace.border : block->border; 275 ? config->colors.urgent_workspace.border : block->border;
247 if (border_color && block->border_top > 0) { 276 if (block->border_set || block->urgent) {
248 render_sharp_line(cairo, border_color, x_pos, y_pos, 277 if (block->border_top > 0) {
249 block_width, block->border_top * output->scale); 278 render_sharp_line(cairo, border_color, x_pos, y_pos,
250 } 279 block_width, block->border_top);
251 if (border_color && block->border_bottom > 0) { 280 }
252 render_sharp_line(cairo, border_color, x_pos, 281 if (block->border_bottom > 0) {
253 y_pos + render_height - block->border_bottom * output->scale, 282 render_sharp_line(cairo, border_color, x_pos,
254 block_width, block->border_bottom * output->scale); 283 y_pos + render_height - block->border_bottom,
255 } 284 block_width, block->border_bottom);
256 if (border_color && block->border_left > 0) { 285 }
257 render_sharp_line(cairo, border_color, x_pos, y_pos, 286 if (block->border_left > 0) {
258 block->border_left * output->scale, render_height); 287 render_sharp_line(cairo, border_color, x_pos, y_pos,
259 x_pos += block->border_left * output->scale + margin; 288 block->border_left, render_height);
289 }
290 x_pos += block->border_left + margin;
260 } 291 }
261 292
262 double offset = 0; 293 double offset = 0;
263 if (strncmp(block->align, "left", 5) == 0) { 294 if (strncmp(block->align, "left", 4) == 0) {
264 offset = x_pos; 295 offset = x_pos;
265 } else if (strncmp(block->align, "right", 5) == 0) { 296 } else if (strncmp(block->align, "right", 5) == 0) {
266 offset = x_pos + width - text_width; 297 offset = x_pos + width - text_width;
@@ -274,30 +305,35 @@ static uint32_t render_status_block(cairo_t *cairo,
274 color = block->color_set ? block->color : color; 305 color = block->color_set ? block->color : color;
275 color = block->urgent ? config->colors.urgent_workspace.text : color; 306 color = block->urgent ? config->colors.urgent_workspace.text : color;
276 cairo_set_source_u32(cairo, color); 307 cairo_set_source_u32(cairo, color);
277 pango_printf(cairo, config->font, output->scale, 308 choose_text_aa_mode(ctx, color);
278 block->markup, "%s", text); 309 render_text(cairo, config->font_description, 1, block->markup, "%s", text);
279 x_pos += width; 310 x_pos += width;
280 311
281 if (block->border && block->border_right > 0) { 312 if (block->border_set || block->urgent) {
282 x_pos += margin; 313 x_pos += margin;
283 render_sharp_line(cairo, border_color, x_pos, y_pos, 314 if (block->border_right > 0) {
284 block->border_right * output->scale, render_height); 315 render_sharp_line(cairo, border_color, x_pos, y_pos,
285 x_pos += block->border_right * output->scale; 316 block->border_right, render_height);
317 }
318 x_pos += block->border_right;
286 } 319 }
287 320
288 if (!edge && block->separator) { 321 if (!edge && block->separator) {
289 if (output->focused) { 322 if (output->focused) {
290 cairo_set_source_u32(cairo, config->colors.focused_separator); 323 color = config->colors.focused_separator;
291 } else { 324 } else {
292 cairo_set_source_u32(cairo, config->colors.separator); 325 color = config->colors.separator;
293 } 326 }
327 cairo_set_source_u32(cairo, color);
294 if (config->sep_symbol) { 328 if (config->sep_symbol) {
295 offset = x_pos + (sep_block_width - sep_width) / 2; 329 offset = x_pos + (sep_block_width - sep_width) / 2;
296 double sep_y = height / 2.0 - sep_height / 2.0; 330 double sep_y = height / 2.0 - sep_height / 2.0;
297 cairo_move_to(cairo, offset, (int)floor(sep_y)); 331 cairo_move_to(cairo, offset, (int)floor(sep_y));
298 pango_printf(cairo, config->font, output->scale, false, 332 choose_text_aa_mode(ctx, color);
333 render_text(cairo, config->font_description, 1, false,
299 "%s", config->sep_symbol); 334 "%s", config->sep_symbol);
300 } else { 335 } else {
336 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
301 cairo_set_line_width(cairo, 1); 337 cairo_set_line_width(cairo, 1);
302 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin); 338 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin);
303 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin); 339 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin);
@@ -317,18 +353,18 @@ static void predict_status_block_pos(cairo_t *cairo,
317 struct swaybar_config *config = output->bar->config; 353 struct swaybar_config *config = output->bar->config;
318 354
319 int text_width, text_height; 355 int text_width, text_height;
320 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 356 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,
321 output->scale, block->markup, "%s", block->full_text); 357 block->markup, "%s", block->full_text);
322 358
323 int margin = 3 * output->scale; 359 int margin = 3;
324 double ws_vertical_padding = config->status_padding * output->scale; 360 double ws_vertical_padding = config->status_padding;
325 361
326 int width = text_width; 362 int width = text_width;
327 363
328 if (block->min_width_str) { 364 if (block->min_width_str) {
329 int w; 365 int w;
330 get_text_size(cairo, config->font, &w, NULL, NULL, 366 get_text_size(cairo, config->font_description, &w, NULL, NULL,
331 output->scale, block->markup, "%s", block->min_width_str); 367 1, block->markup, "%s", block->min_width_str);
332 block->min_width = w; 368 block->min_width = w;
333 } 369 }
334 if (width < block->min_width) { 370 if (width < block->min_width) {
@@ -336,28 +372,28 @@ static void predict_status_block_pos(cairo_t *cairo,
336 } 372 }
337 373
338 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 374 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
339 uint32_t ideal_surface_height = ideal_height / output->scale; 375 uint32_t ideal_surface_height = ideal_height;
340 if (!output->bar->config->height && 376 if (!output->bar->config->height &&
341 output->height < ideal_surface_height) { 377 output->height < ideal_surface_height) {
342 return; 378 return;
343 } 379 }
344 380
345 *x -= width; 381 *x -= width;
346 if ((block->border || block->urgent) && block->border_left > 0) { 382 if ((block->border_set || block->urgent) && block->border_left > 0) {
347 *x -= (block->border_left * output->scale + margin); 383 *x -= (block->border_left + margin);
348 } 384 }
349 if ((block->border || block->urgent) && block->border_right > 0) { 385 if ((block->border_set || block->urgent) && block->border_right > 0) {
350 *x -= (block->border_right * output->scale + margin); 386 *x -= (block->border_right + margin);
351 } 387 }
352 388
353 int sep_width, sep_height; 389 int sep_width, sep_height;
354 int sep_block_width = block->separator_block_width; 390 int sep_block_width = block->separator_block_width;
355 if (!edge) { 391 if (!edge) {
356 if (config->sep_symbol) { 392 if (config->sep_symbol) {
357 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 393 get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,
358 output->scale, false, "%s", config->sep_symbol); 394 1, false, "%s", config->sep_symbol);
359 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 395 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
360 uint32_t _ideal_surface_height = _ideal_height / output->scale; 396 uint32_t _ideal_surface_height = _ideal_height;
361 if (!output->bar->config->height && 397 if (!output->bar->config->height &&
362 output->height < _ideal_surface_height) { 398 output->height < _ideal_surface_height) {
363 return; 399 return;
@@ -368,13 +404,13 @@ static void predict_status_block_pos(cairo_t *cairo,
368 } 404 }
369 *x -= sep_block_width; 405 *x -= sep_block_width;
370 } else if (config->status_edge_padding) { 406 } else if (config->status_edge_padding) {
371 *x -= config->status_edge_padding * output->scale; 407 *x -= config->status_edge_padding;
372 } 408 }
373} 409}
374 410
375static double predict_status_line_pos(cairo_t *cairo, 411static double predict_status_line_pos(cairo_t *cairo,
376 struct swaybar_output *output, double x) { 412 struct swaybar_output *output, double x) {
377 bool edge = x == output->width * output->scale; 413 bool edge = x == output->width;
378 struct i3bar_block *block; 414 struct i3bar_block *block;
379 wl_list_for_each(block, &output->bar->status->blocks, link) { 415 wl_list_for_each(block, &output->bar->status->blocks, link) {
380 predict_status_block_pos(cairo, output, block, &x, edge); 416 predict_status_block_pos(cairo, output, block, &x, edge);
@@ -389,24 +425,24 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo,
389 struct swaybar_config *config = output->bar->config; 425 struct swaybar_config *config = output->bar->config;
390 426
391 int text_width, text_height; 427 int text_width, text_height;
392 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 428 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1,
393 output->scale, config->pango_markup, "%s", ws->label); 429 config->pango_markup, "%s", ws->label);
394 430
395 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 431 int ws_vertical_padding = WS_VERTICAL_PADDING;
396 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 432 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
397 int border_width = BORDER_WIDTH * output->scale; 433 int border_width = BORDER_WIDTH;
398 434
399 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 435 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
400 + border_width * 2; 436 + border_width * 2;
401 uint32_t ideal_surface_height = ideal_height / output->scale; 437 uint32_t ideal_surface_height = ideal_height;
402 if (!output->bar->config->height && 438 if (!output->bar->config->height &&
403 output->height < ideal_surface_height) { 439 output->height < ideal_surface_height) {
404 return 0; 440 return 0;
405 } 441 }
406 442
407 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 443 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
408 if (width < config->workspace_min_width * output->scale) { 444 if (width < config->workspace_min_width) {
409 width = config->workspace_min_width * output->scale; 445 width = config->workspace_min_width;
410 } 446 }
411 return width; 447 return width;
412} 448}
@@ -437,39 +473,40 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
437 } 473 }
438 474
439 int text_width, text_height; 475 int text_width, text_height;
440 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 476 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
441 output->scale, output->bar->mode_pango_markup, 477 1, output->bar->mode_pango_markup,
442 "%s", mode); 478 "%s", mode);
443 479
444 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 480 int ws_vertical_padding = WS_VERTICAL_PADDING;
445 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 481 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
446 int border_width = BORDER_WIDTH * output->scale; 482 int border_width = BORDER_WIDTH;
447 483
448 uint32_t ideal_height = text_height + ws_vertical_padding * 2 484 uint32_t ideal_height = text_height + ws_vertical_padding * 2
449 + border_width * 2; 485 + border_width * 2;
450 uint32_t ideal_surface_height = ideal_height / output->scale; 486 uint32_t ideal_surface_height = ideal_height;
451 if (!output->bar->config->height && 487 if (!output->bar->config->height &&
452 output->height < ideal_surface_height) { 488 output->height < ideal_surface_height) {
453 return 0; 489 return 0;
454 } 490 }
455 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 491 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
456 if (width < config->workspace_min_width * output->scale) { 492 if (width < config->workspace_min_width) {
457 width = config->workspace_min_width * output->scale; 493 width = config->workspace_min_width;
458 } 494 }
459 return width; 495 return width;
460} 496}
461 497
462static uint32_t render_status_line_i3bar(cairo_t *cairo, 498static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) {
463 struct swaybar_output *output, double *x) { 499 struct swaybar_output *output = ctx->output;
464 uint32_t max_height = 0; 500 uint32_t max_height = 0;
465 bool edge = *x == output->width * output->scale; 501 bool edge = *x == output->width;
466 struct i3bar_block *block; 502 struct i3bar_block *block;
467 bool use_short_text = false; 503 bool use_short_text = false;
468 504
505 cairo_t *cairo = ctx->cairo;
469 double reserved_width = 506 double reserved_width =
470 predict_workspace_buttons_length(cairo, output) + 507 predict_workspace_buttons_length(cairo, output) +
471 predict_binding_mode_indicator_length(cairo, output) + 508 predict_binding_mode_indicator_length(cairo, output) +
472 3 * output->scale; // require a bit of space for margin 509 3; // require a bit of space for margin
473 510
474 double predicted_full_pos = 511 double predicted_full_pos =
475 predict_status_line_pos(cairo, output, *x); 512 predict_status_line_pos(cairo, output, *x);
@@ -479,7 +516,7 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
479 } 516 }
480 517
481 wl_list_for_each(block, &output->bar->status->blocks, link) { 518 wl_list_for_each(block, &output->bar->status->blocks, link) {
482 uint32_t h = render_status_block(cairo, output, block, x, edge, 519 uint32_t h = render_status_block(ctx, block, x, edge,
483 use_short_text); 520 use_short_text);
484 max_height = h > max_height ? h : max_height; 521 max_height = h > max_height ? h : max_height;
485 edge = false; 522 edge = false;
@@ -487,53 +524,56 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
487 return max_height; 524 return max_height;
488} 525}
489 526
490static uint32_t render_status_line(cairo_t *cairo, 527static uint32_t render_status_line(struct render_context *ctx, double *x) {
491 struct swaybar_output *output, double *x) { 528 struct status_line *status = ctx->output->bar->status;
492 struct status_line *status = output->bar->status;
493 switch (status->protocol) { 529 switch (status->protocol) {
494 case PROTOCOL_ERROR: 530 case PROTOCOL_ERROR:
495 return render_status_line_error(cairo, output, x); 531 return render_status_line_error(ctx, x);
496 case PROTOCOL_TEXT: 532 case PROTOCOL_TEXT:
497 return render_status_line_text(cairo, output, x); 533 return render_status_line_text(ctx, x);
498 case PROTOCOL_I3BAR: 534 case PROTOCOL_I3BAR:
499 return render_status_line_i3bar(cairo, output, x); 535 return render_status_line_i3bar(ctx, x);
500 case PROTOCOL_UNDEF: 536 case PROTOCOL_UNDEF:
501 return 0; 537 return 0;
502 } 538 }
503 return 0; 539 return 0;
504} 540}
505 541
506static uint32_t render_binding_mode_indicator(cairo_t *cairo, 542static uint32_t render_binding_mode_indicator(struct render_context *ctx,
507 struct swaybar_output *output, double x) { 543 double x) {
544 struct swaybar_output *output = ctx->output;
508 const char *mode = output->bar->mode; 545 const char *mode = output->bar->mode;
509 if (!mode) { 546 if (!mode) {
510 return 0; 547 return 0;
511 } 548 }
512 549
550 cairo_t *cairo = ctx->cairo;
513 struct swaybar_config *config = output->bar->config; 551 struct swaybar_config *config = output->bar->config;
514 int text_width, text_height; 552 int text_width, text_height;
515 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 553 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
516 output->scale, output->bar->mode_pango_markup, 554 1, output->bar->mode_pango_markup,
517 "%s", mode); 555 "%s", mode);
518 556
519 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 557 int ws_vertical_padding = WS_VERTICAL_PADDING;
520 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 558 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
521 int border_width = BORDER_WIDTH * output->scale; 559 int border_width = BORDER_WIDTH;
522 560
523 uint32_t ideal_height = text_height + ws_vertical_padding * 2 561 uint32_t ideal_height = text_height + ws_vertical_padding * 2
524 + border_width * 2; 562 + border_width * 2;
525 uint32_t ideal_surface_height = ideal_height / output->scale; 563 uint32_t ideal_surface_height = ideal_height;
526 if (!output->bar->config->height && 564 if (!output->bar->config->height &&
527 output->height < ideal_surface_height) { 565 output->height < ideal_surface_height) {
528 return ideal_surface_height; 566 return ideal_surface_height;
529 } 567 }
530 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 568 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
531 if (width < config->workspace_min_width * output->scale) { 569 if (width < config->workspace_min_width) {
532 width = config->workspace_min_width * output->scale; 570 width = config->workspace_min_width;
533 } 571 }
534 572
535 uint32_t height = output->height * output->scale; 573 uint32_t height = output->height;
574 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
536 cairo_set_source_u32(cairo, config->colors.binding_mode.background); 575 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
576 ctx->background_color = config->colors.binding_mode.background;
537 cairo_rectangle(cairo, x, 0, width, height); 577 cairo_rectangle(cairo, x, 0, width, height);
538 cairo_fill(cairo); 578 cairo_fill(cairo);
539 579
@@ -550,24 +590,30 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
550 double text_y = height / 2.0 - text_height / 2.0; 590 double text_y = height / 2.0 - text_height / 2.0;
551 cairo_set_source_u32(cairo, config->colors.binding_mode.text); 591 cairo_set_source_u32(cairo, config->colors.binding_mode.text);
552 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); 592 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
553 pango_printf(cairo, config->font, output->scale, 593 choose_text_aa_mode(ctx, config->colors.binding_mode.text);
554 output->bar->mode_pango_markup, "%s", mode); 594 render_text(cairo, config->font_description, 1, output->bar->mode_pango_markup,
595 "%s", mode);
555 return output->height; 596 return output->height;
556} 597}
557 598
558static enum hotspot_event_handling workspace_hotspot_callback( 599static enum hotspot_event_handling workspace_hotspot_callback(
559 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 600 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
560 double x, double y, uint32_t button, void *data) { 601 double x, double y, uint32_t button, bool released, void *data) {
561 if (button != BTN_LEFT) { 602 if (button != BTN_LEFT) {
562 return HOTSPOT_PROCESS; 603 return HOTSPOT_PROCESS;
563 } 604 }
605 if (released) {
606 // Since we handle the pressed event, also handle the released event
607 // to block it from falling through to a binding in the bar
608 return HOTSPOT_IGNORE;
609 }
564 ipc_send_workspace_command(output->bar, (const char *)data); 610 ipc_send_workspace_command(output->bar, (const char *)data);
565 return HOTSPOT_IGNORE; 611 return HOTSPOT_IGNORE;
566} 612}
567 613
568static uint32_t render_workspace_button(cairo_t *cairo, 614static uint32_t render_workspace_button(struct render_context *ctx,
569 struct swaybar_output *output,
570 struct swaybar_workspace *ws, double *x) { 615 struct swaybar_workspace *ws, double *x) {
616 struct swaybar_output *output = ctx->output;
571 struct swaybar_config *config = output->bar->config; 617 struct swaybar_config *config = output->bar->config;
572 struct box_colors box_colors; 618 struct box_colors box_colors;
573 if (ws->urgent) { 619 if (ws->urgent) {
@@ -580,30 +626,33 @@ static uint32_t render_workspace_button(cairo_t *cairo,
580 box_colors = config->colors.inactive_workspace; 626 box_colors = config->colors.inactive_workspace;
581 } 627 }
582 628
583 uint32_t height = output->height * output->scale; 629 uint32_t height = output->height;
584 630
631 cairo_t *cairo = ctx->cairo;
585 int text_width, text_height; 632 int text_width, text_height;
586 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 633 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
587 output->scale, config->pango_markup, "%s", ws->label); 634 1, config->pango_markup, "%s", ws->label);
588 635
589 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 636 int ws_vertical_padding = WS_VERTICAL_PADDING;
590 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 637 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
591 int border_width = BORDER_WIDTH * output->scale; 638 int border_width = BORDER_WIDTH;
592 639
593 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 640 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
594 + border_width * 2; 641 + border_width * 2;
595 uint32_t ideal_surface_height = ideal_height / output->scale; 642 uint32_t ideal_surface_height = ideal_height;
596 if (!output->bar->config->height && 643 if (!output->bar->config->height &&
597 output->height < ideal_surface_height) { 644 output->height < ideal_surface_height) {
598 return ideal_surface_height; 645 return ideal_surface_height;
599 } 646 }
600 647
601 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 648 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
602 if (width < config->workspace_min_width * output->scale) { 649 if (width < config->workspace_min_width) {
603 width = config->workspace_min_width * output->scale; 650 width = config->workspace_min_width;
604 } 651 }
605 652
653 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
606 cairo_set_source_u32(cairo, box_colors.background); 654 cairo_set_source_u32(cairo, box_colors.background);
655 ctx->background_color = box_colors.background;
607 cairo_rectangle(cairo, *x, 0, width, height); 656 cairo_rectangle(cairo, *x, 0, width, height);
608 cairo_fill(cairo); 657 cairo_fill(cairo);
609 658
@@ -620,7 +669,8 @@ static uint32_t render_workspace_button(cairo_t *cairo,
620 double text_y = height / 2.0 - text_height / 2.0; 669 double text_y = height / 2.0 - text_height / 2.0;
621 cairo_set_source_u32(cairo, box_colors.text); 670 cairo_set_source_u32(cairo, box_colors.text);
622 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); 671 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
623 pango_printf(cairo, config->font, output->scale, config->pango_markup, 672 choose_text_aa_mode(ctx, box_colors.text);
673 render_text(cairo, config->font_description, 1, config->pango_markup,
624 "%s", ws->label); 674 "%s", ws->label);
625 675
626 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 676 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
@@ -637,20 +687,15 @@ static uint32_t render_workspace_button(cairo_t *cairo,
637 return output->height; 687 return output->height;
638} 688}
639 689
640static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { 690static uint32_t render_to_cairo(struct render_context *ctx) {
691 cairo_t *cairo = ctx->cairo;
692 struct swaybar_output *output = ctx->output;
641 struct swaybar *bar = output->bar; 693 struct swaybar *bar = output->bar;
642 struct swaybar_config *config = bar->config; 694 struct swaybar_config *config = bar->config;
643 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
644 if (output->focused) {
645 cairo_set_source_u32(cairo, config->colors.focused_background);
646 } else {
647 cairo_set_source_u32(cairo, config->colors.background);
648 }
649 cairo_paint(cairo);
650 695
651 int th; 696 int th;
652 get_text_size(cairo, config->font, NULL, &th, NULL, output->scale, false, ""); 697 get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, "");
653 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4) / output->scale; 698 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4);
654 /* 699 /*
655 * Each render_* function takes the actual height of the bar, and returns 700 * Each render_* function takes the actual height of the bar, and returns
656 * the ideal height. If the actual height is too short, the render function 701 * the ideal height. If the actual height is too short, the render function
@@ -658,7 +703,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
658 * height is too tall, the render function should adapt its drawing to 703 * height is too tall, the render function should adapt its drawing to
659 * utilize the available space. 704 * utilize the available space.
660 */ 705 */
661 double x = output->width * output->scale; 706 double x = output->width;
662#if HAVE_TRAY 707#if HAVE_TRAY
663 if (bar->tray) { 708 if (bar->tray) {
664 uint32_t h = render_tray(cairo, output, &x); 709 uint32_t h = render_tray(cairo, output, &x);
@@ -666,19 +711,19 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
666 } 711 }
667#endif 712#endif
668 if (bar->status) { 713 if (bar->status) {
669 uint32_t h = render_status_line(cairo, output, &x); 714 uint32_t h = render_status_line(ctx, &x);
670 max_height = h > max_height ? h : max_height; 715 max_height = h > max_height ? h : max_height;
671 } 716 }
672 x = 0; 717 x = 0;
673 if (config->workspace_buttons) { 718 if (config->workspace_buttons) {
674 struct swaybar_workspace *ws; 719 struct swaybar_workspace *ws;
675 wl_list_for_each(ws, &output->workspaces, link) { 720 wl_list_for_each(ws, &output->workspaces, link) {
676 uint32_t h = render_workspace_button(cairo, output, ws, &x); 721 uint32_t h = render_workspace_button(ctx, ws, &x);
677 max_height = h > max_height ? h : max_height; 722 max_height = h > max_height ? h : max_height;
678 } 723 }
679 } 724 }
680 if (config->binding_mode_indicator) { 725 if (config->binding_mode_indicator) {
681 uint32_t h = render_binding_mode_indicator(cairo, output, x); 726 uint32_t h = render_binding_mode_indicator(ctx, x);
682 max_height = h > max_height ? h : max_height; 727 max_height = h > max_height ? h : max_height;
683 } 728 }
684 729
@@ -708,26 +753,44 @@ void render_frame(struct swaybar_output *output) {
708 753
709 free_hotspots(&output->hotspots); 754 free_hotspots(&output->hotspots);
710 755
756 uint32_t background_color;
757 if (output->focused) {
758 background_color = output->bar->config->colors.focused_background;
759 } else {
760 background_color = output->bar->config->colors.background;
761 }
762
763 struct render_context ctx = { 0 };
764 ctx.output = output;
765 // initial background color used for deciding the best way to antialias text
766 ctx.background_color = background_color;
767
711 cairo_surface_t *recorder = cairo_recording_surface_create( 768 cairo_surface_t *recorder = cairo_recording_surface_create(
712 CAIRO_CONTENT_COLOR_ALPHA, NULL); 769 CAIRO_CONTENT_COLOR_ALPHA, NULL);
713 cairo_t *cairo = cairo_create(recorder); 770 cairo_t *cairo = cairo_create(recorder);
771 cairo_scale(cairo, output->scale, output->scale);
714 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 772 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
773 ctx.cairo = cairo;
774
715 cairo_font_options_t *fo = cairo_font_options_create(); 775 cairo_font_options_t *fo = cairo_font_options_create();
716 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); 776 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
777 ctx.textaa_safe = fo;
717 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { 778 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
718 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); 779 ctx.textaa_sharp = ctx.textaa_safe;
719 } else { 780 } else {
781 fo = cairo_font_options_create();
720 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); 782 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
721 cairo_font_options_set_subpixel_order(fo, 783 cairo_font_options_set_subpixel_order(fo,
722 to_cairo_subpixel_order(output->subpixel)); 784 to_cairo_subpixel_order(output->subpixel));
785 ctx.textaa_sharp = fo;
723 } 786 }
724 cairo_set_font_options(cairo, fo); 787
725 cairo_font_options_destroy(fo); 788
726 cairo_save(cairo); 789 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
727 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 790 cairo_set_source_u32(cairo, background_color);
728 cairo_paint(cairo); 791 cairo_paint(cairo);
729 cairo_restore(cairo); 792
730 uint32_t height = render_to_cairo(cairo, output); 793 uint32_t height = render_to_cairo(&ctx);
731 int config_height = output->bar->config->height; 794 int config_height = output->bar->config->height;
732 if (config_height > 0) { 795 if (config_height > 0) {
733 height = config_height; 796 height = config_height;
@@ -753,9 +816,7 @@ void render_frame(struct swaybar_output *output) {
753 output->width * output->scale, 816 output->width * output->scale,
754 output->height * output->scale); 817 output->height * output->scale);
755 if (!output->current_buffer) { 818 if (!output->current_buffer) {
756 cairo_surface_destroy(recorder); 819 goto cleanup;
757 cairo_destroy(cairo);
758 return;
759 } 820 }
760 cairo_t *shm = output->current_buffer->cairo; 821 cairo_t *shm = output->current_buffer->cairo;
761 822
@@ -773,12 +834,29 @@ void render_frame(struct swaybar_output *output) {
773 wl_surface_damage(output->surface, 0, 0, 834 wl_surface_damage(output->surface, 0, 0,
774 output->width, output->height); 835 output->width, output->height);
775 836
837 uint32_t bg_alpha = background_color & 0xFF;
838 if (bg_alpha == 0xFF) {
839 struct wl_region *region =
840 wl_compositor_create_region(output->bar->compositor);
841 wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
842 wl_surface_set_opaque_region(output->surface, region);
843 wl_region_destroy(region);
844 } else {
845 wl_surface_set_opaque_region(output->surface, NULL);
846 }
847
776 struct wl_callback *frame_callback = wl_surface_frame(output->surface); 848 struct wl_callback *frame_callback = wl_surface_frame(output->surface);
777 wl_callback_add_listener(frame_callback, &output_frame_listener, output); 849 wl_callback_add_listener(frame_callback, &output_frame_listener, output);
778 output->frame_scheduled = true; 850 output->frame_scheduled = true;
779 851
780 wl_surface_commit(output->surface); 852 wl_surface_commit(output->surface);
781 } 853 }
854
855cleanup:
856 if (ctx.textaa_sharp != ctx.textaa_safe) {
857 cairo_font_options_destroy(ctx.textaa_sharp);
858 }
859 cairo_font_options_destroy(ctx.textaa_safe);
782 cairo_surface_destroy(recorder); 860 cairo_surface_destroy(recorder);
783 cairo_destroy(cairo); 861 cairo_destroy(cairo);
784} 862}
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index ecd91032..e542e606 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h> 1#include <assert.h>
3#include <fcntl.h> 2#include <fcntl.h>
4#include <sys/ioctl.h> 3#include <sys/ioctl.h>
@@ -117,11 +116,11 @@ bool status_handle_readable(struct status_line *status) {
117 status->text = status->buffer; 116 status->text = status->buffer;
118 // intentional fall-through 117 // intentional fall-through
119 case PROTOCOL_TEXT: 118 case PROTOCOL_TEXT:
120 errno = 0;
121 while (true) { 119 while (true) {
122 if (status->buffer[read_bytes - 1] == '\n') { 120 if (status->buffer[read_bytes - 1] == '\n') {
123 status->buffer[read_bytes - 1] = '\0'; 121 status->buffer[read_bytes - 1] = '\0';
124 } 122 }
123 errno = 0;
125 read_bytes = getline(&status->buffer, 124 read_bytes = getline(&status->buffer,
126 &status->buffer_size, status->read); 125 &status->buffer_size, status->read);
127 if (errno == EAGAIN) { 126 if (errno == EAGAIN) {
@@ -157,7 +156,12 @@ struct status_line *status_line_init(char *cmd) {
157 assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before " 156 assert(!getenv("WAYLAND_SOCKET") && "display must be initialized before "
158 " starting `status-command`; WAYLAND_SOCKET should not be set"); 157 " starting `status-command`; WAYLAND_SOCKET should not be set");
159 status->pid = fork(); 158 status->pid = fork();
160 if (status->pid == 0) { 159 if (status->pid < 0) {
160 sway_log_errno(SWAY_ERROR, "fork failed");
161 exit(1);
162 } else if (status->pid == 0) {
163 setpgid(0, 0);
164
161 dup2(pipe_read_fd[1], STDOUT_FILENO); 165 dup2(pipe_read_fd[1], STDOUT_FILENO);
162 close(pipe_read_fd[0]); 166 close(pipe_read_fd[0]);
163 close(pipe_read_fd[1]); 167 close(pipe_read_fd[1]);
@@ -185,8 +189,8 @@ struct status_line *status_line_init(char *cmd) {
185 189
186void status_line_free(struct status_line *status) { 190void status_line_free(struct status_line *status) {
187 status_line_close_fds(status); 191 status_line_close_fds(status);
188 kill(status->pid, status->cont_signal); 192 kill(-status->pid, status->cont_signal);
189 kill(status->pid, SIGTERM); 193 kill(-status->pid, SIGTERM);
190 waitpid(status->pid, NULL, 0); 194 waitpid(status->pid, NULL, 0);
191 if (status->protocol == PROTOCOL_I3BAR) { 195 if (status->protocol == PROTOCOL_I3BAR) {
192 struct i3bar_block *block, *tmp; 196 struct i3bar_block *block, *tmp;
diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c
index ddf2416d..79b54606 100644
--- a/swaybar/tray/host.c
+++ b/swaybar/tray/host.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stdio.h> 2#include <stdio.h>
4#include <stdlib.h> 3#include <stdlib.h>
@@ -10,6 +9,7 @@
10#include "swaybar/tray/tray.h" 9#include "swaybar/tray/tray.h"
11#include "list.h" 10#include "list.h"
12#include "log.h" 11#include "log.h"
12#include "stringop.h"
13 13
14static const char *watcher_path = "/StatusNotifierWatcher"; 14static const char *watcher_path = "/StatusNotifierWatcher";
15 15
@@ -138,12 +138,10 @@ static int handle_new_watcher(sd_bus_message *msg,
138 138
139bool init_host(struct swaybar_host *host, char *protocol, 139bool init_host(struct swaybar_host *host, char *protocol,
140 struct swaybar_tray *tray) { 140 struct swaybar_tray *tray) {
141 size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; 141 host->watcher_interface = format_str("org.%s.StatusNotifierWatcher", protocol);
142 host->watcher_interface = malloc(len);
143 if (!host->watcher_interface) { 142 if (!host->watcher_interface) {
144 return false; 143 return false;
145 } 144 }
146 snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol);
147 145
148 sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL; 146 sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL;
149 int ret = sd_bus_match_signal(tray->bus, &reg_slot, host->watcher_interface, 147 int ret = sd_bus_match_signal(tray->bus, &reg_slot, host->watcher_interface,
@@ -173,13 +171,10 @@ bool init_host(struct swaybar_host *host, char *protocol,
173 } 171 }
174 172
175 pid_t pid = getpid(); 173 pid_t pid = getpid();
176 size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d", 174 host->service = format_str("org.%s.StatusNotifierHost-%d", protocol, pid);
177 protocol, pid) + 1;
178 host->service = malloc(service_len);
179 if (!host->service) { 175 if (!host->service) {
180 goto error; 176 goto error;
181 } 177 }
182 snprintf(host->service, service_len, "org.%s.StatusNotifierHost-%d", protocol, pid);
183 ret = sd_bus_request_name(tray->bus, host->service, 0); 178 ret = sd_bus_request_name(tray->bus, host->service, 0);
184 if (ret < 0) { 179 if (ret < 0) {
185 sway_log(SWAY_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); 180 sway_log(SWAY_DEBUG, "Failed to acquire service name: %s", strerror(-ret));
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c
index c426c3d4..659edd86 100644
--- a/swaybar/tray/icon.c
+++ b/swaybar/tray/icon.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <ctype.h> 1#include <ctype.h>
3#include <dirent.h> 2#include <dirent.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -40,9 +39,7 @@ static list_t *get_basedirs(void) {
40 data_dirs = strdup(data_dirs); 39 data_dirs = strdup(data_dirs);
41 char *dir = strtok(data_dirs, ":"); 40 char *dir = strtok(data_dirs, ":");
42 do { 41 do {
43 size_t path_len = snprintf(NULL, 0, "%s/icons", dir) + 1; 42 char *path = format_str("%s/icons", dir);
44 char *path = malloc(path_len);
45 snprintf(path, path_len, "%s/icons", dir);
46 list_add(basedirs, path); 43 list_add(basedirs, path);
47 } while ((dir = strtok(NULL, ":"))); 44 } while ((dir = strtok(NULL, ":")));
48 free(data_dirs); 45 free(data_dirs);
@@ -206,13 +203,7 @@ static const char *entry_handler(char *group, char *key, char *value,
206 */ 203 */
207static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { 204static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
208 // look for index.theme file 205 // look for index.theme file
209 size_t path_len = snprintf(NULL, 0, "%s/%s/index.theme", basedir, 206 char *path = format_str("%s/%s/index.theme", basedir, theme_name);
210 theme_name) + 1;
211 char *path = malloc(path_len);
212 if (!path) {
213 return NULL;
214 }
215 snprintf(path, path_len, "%s/%s/index.theme", basedir, theme_name);
216 FILE *theme_file = fopen(path, "r"); 207 FILE *theme_file = fopen(path, "r");
217 free(path); 208 free(path);
218 if (!theme_file) { 209 if (!theme_file) {
@@ -416,26 +407,20 @@ static char *find_icon_in_subdir(char *name, char *basedir, char *theme,
416#endif 407#endif
417 }; 408 };
418 409
419 size_t path_len = snprintf(NULL, 0, "%s/%s/%s/%s.EXT", basedir, theme,
420 subdir, name) + 1;
421 char *path = malloc(path_len);
422
423 for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) { 410 for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) {
424 snprintf(path, path_len, "%s/%s/%s/%s.%s", basedir, theme, subdir, 411 char *path = format_str("%s/%s/%s/%s.%s",
425 name, extensions[i]); 412 basedir, theme, subdir, name, extensions[i]);
426 if (access(path, R_OK) == 0) { 413 if (access(path, R_OK) == 0) {
427 return path; 414 return path;
428 } 415 }
416 free(path);
429 } 417 }
430 418
431 free(path);
432 return NULL; 419 return NULL;
433} 420}
434 421
435static bool theme_exists_in_basedir(char *theme, char *basedir) { 422static bool theme_exists_in_basedir(char *theme, char *basedir) {
436 size_t path_len = snprintf(NULL, 0, "%s/%s", basedir, theme) + 1; 423 char *path = format_str("%s/%s", basedir, theme);
437 char *path = malloc(path_len);
438 snprintf(path, path_len, "%s/%s", basedir, theme);
439 bool ret = dir_exists(path); 424 bool ret = dir_exists(path);
440 free(path); 425 free(path);
441 return ret; 426 return ret;
diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c
index a5660f62..ca6c03ad 100644
--- a/swaybar/tray/item.c
+++ b/swaybar/tray/item.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <arpa/inet.h> 1#include <arpa/inet.h>
3#include <cairo.h> 2#include <cairo.h>
4#include <limits.h> 3#include <limits.h>
@@ -7,13 +6,13 @@
7#include <string.h> 6#include <string.h>
8#include "swaybar/bar.h" 7#include "swaybar/bar.h"
9#include "swaybar/config.h" 8#include "swaybar/config.h"
9#include "swaybar/image.h"
10#include "swaybar/input.h" 10#include "swaybar/input.h"
11#include "swaybar/tray/host.h" 11#include "swaybar/tray/host.h"
12#include "swaybar/tray/icon.h" 12#include "swaybar/tray/icon.h"
13#include "swaybar/tray/item.h" 13#include "swaybar/tray/item.h"
14#include "swaybar/tray/tray.h" 14#include "swaybar/tray/tray.h"
15#include "background-image.h" 15#include "cairo_util.h"
16#include "cairo.h"
17#include "list.h" 16#include "list.h"
18#include "log.h" 17#include "log.h"
19#include "wlr-layer-shell-unstable-v1-client-protocol.h" 18#include "wlr-layer-shell-unstable-v1-client-protocol.h"
@@ -118,8 +117,13 @@ static int get_property_callback(sd_bus_message *msg, void *data,
118 117
119 int ret; 118 int ret;
120 if (sd_bus_message_is_method_error(msg, NULL)) { 119 if (sd_bus_message_is_method_error(msg, NULL)) {
121 sway_log(SWAY_ERROR, "%s %s: %s", sni->watcher_id, prop, 120 const sd_bus_error *err = sd_bus_message_get_error(msg);
122 sd_bus_message_get_error(msg)->message); 121 sway_log_importance_t log_lv = SWAY_ERROR;
122 if ((!strcmp(prop, "IconThemePath")) &&
123 (!strcmp(err->name, SD_BUS_ERROR_UNKNOWN_PROPERTY))) {
124 log_lv = SWAY_DEBUG;
125 }
126 sway_log(log_lv, "%s %s: %s", sni->watcher_id, prop, err->message);
123 ret = sd_bus_message_get_errno(msg); 127 ret = sd_bus_message_get_errno(msg);
124 goto cleanup; 128 goto cleanup;
125 } 129 }
@@ -380,13 +384,18 @@ static int cmp_sni_id(const void *item, const void *cmp_to) {
380 384
381static enum hotspot_event_handling icon_hotspot_callback( 385static enum hotspot_event_handling icon_hotspot_callback(
382 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 386 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
383 double x, double y, uint32_t button, void *data) { 387 double x, double y, uint32_t button, bool released, void *data) {
384 sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); 388 sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data);
385 389
386 struct swaybar_tray *tray = output->bar->tray; 390 struct swaybar_tray *tray = output->bar->tray;
387 int idx = list_seq_find(tray->items, cmp_sni_id, data); 391 int idx = list_seq_find(tray->items, cmp_sni_id, data);
388 392
389 if (idx != -1) { 393 if (idx != -1) {
394 if (released) {
395 // Since we handle the pressed event, also handle the released event
396 // to block it from falling through to a binding in the bar
397 return HOTSPOT_IGNORE;
398 }
390 struct swaybar_sni *sni = tray->items->items[idx]; 399 struct swaybar_sni *sni = tray->items->items[idx];
391 // guess global position since wayland doesn't expose it 400 // guess global position since wayland doesn't expose it
392 struct swaybar_config *config = tray->bar->config; 401 struct swaybar_config *config = tray->bar->config;
@@ -421,7 +430,7 @@ static void reload_sni(struct swaybar_sni *sni, char *icon_theme,
421 list_free(icon_search_paths); 430 list_free(icon_search_paths);
422 if (icon_path) { 431 if (icon_path) {
423 cairo_surface_destroy(sni->icon); 432 cairo_surface_destroy(sni->icon);
424 sni->icon = load_background_image(icon_path); 433 sni->icon = load_image(icon_path);
425 free(icon_path); 434 free(icon_path);
426 return; 435 return;
427 } 436 }
@@ -461,6 +470,11 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
461 sni->target_size = target_size; 470 sni->target_size = target_size;
462 } 471 }
463 472
473 // Passive
474 if (sni->status && sni->status[0] == 'P') {
475 return 0;
476 }
477
464 int icon_size; 478 int icon_size;
465 cairo_surface_t *icon; 479 cairo_surface_t *icon;
466 if (sni->icon) { 480 if (sni->icon) {
@@ -488,24 +502,36 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
488 cairo_destroy(cairo_icon); 502 cairo_destroy(cairo_icon);
489 } 503 }
490 504
491 int padded_size = icon_size + 2*padding; 505 double descaled_padding = (double)padding / output->scale;
492 *x -= padded_size; 506 double descaled_icon_size = (double)icon_size / output->scale;
493 int y = floor((height - padded_size) / 2.0); 507
508 int size = descaled_icon_size + 2 * descaled_padding;
509 *x -= size;
510 int icon_y = floor((output->height - size) / 2.0);
494 511
495 cairo_operator_t op = cairo_get_operator(cairo); 512 cairo_operator_t op = cairo_get_operator(cairo);
496 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); 513 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
497 cairo_set_source_surface(cairo, icon, *x + padding, y + padding); 514
498 cairo_rectangle(cairo, *x, y, padded_size, padded_size); 515 cairo_matrix_t scale_matrix;
516 cairo_pattern_t *icon_pattern = cairo_pattern_create_for_surface(icon);
517 // TODO: check cairo_pattern_status for "ENOMEM"
518 cairo_matrix_init_scale(&scale_matrix, output->scale, output->scale);
519 cairo_matrix_translate(&scale_matrix, -(*x + descaled_padding), -(icon_y + descaled_padding));
520 cairo_pattern_set_matrix(icon_pattern, &scale_matrix);
521 cairo_set_source(cairo, icon_pattern);
522 cairo_rectangle(cairo, *x, icon_y, size, size);
499 cairo_fill(cairo); 523 cairo_fill(cairo);
524
500 cairo_set_operator(cairo, op); 525 cairo_set_operator(cairo, op);
501 526
527 cairo_pattern_destroy(icon_pattern);
502 cairo_surface_destroy(icon); 528 cairo_surface_destroy(icon);
503 529
504 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 530 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
505 hotspot->x = *x; 531 hotspot->x = *x;
506 hotspot->y = 0; 532 hotspot->y = 0;
507 hotspot->width = height; 533 hotspot->width = size;
508 hotspot->height = height; 534 hotspot->height = output->height;
509 hotspot->callback = icon_hotspot_callback; 535 hotspot->callback = icon_hotspot_callback;
510 hotspot->destroy = free; 536 hotspot->destroy = free;
511 hotspot->data = strdup(sni->watcher_id); 537 hotspot->data = strdup(sni->watcher_id);
diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c
index 5fe6f9c3..b0545f4a 100644
--- a/swaybar/tray/tray.c
+++ b/swaybar/tray/tray.c
@@ -116,8 +116,8 @@ uint32_t render_tray(cairo_t *cairo, struct swaybar_output *output, double *x) {
116 } 116 }
117 } // else display on all 117 } // else display on all
118 118
119 if ((int) output->height*output->scale <= 2*config->tray_padding) { 119 if ((int)(output->height * output->scale) <= 2 * config->tray_padding) {
120 return 2*config->tray_padding + 1; 120 return (2 * config->tray_padding + 1) / output->scale;
121 } 121 }
122 122
123 uint32_t max_height = 0; 123 uint32_t max_height = 0;
diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c
index 16afc27c..3cfea8d8 100644
--- a/swaybar/tray/watcher.c
+++ b/swaybar/tray/watcher.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h> 1#include <stdbool.h>
3#include <stddef.h> 2#include <stddef.h>
4#include <stdio.h> 3#include <stdio.h>
@@ -6,6 +5,7 @@
6#include <string.h> 5#include <string.h>
7#include "list.h" 6#include "list.h"
8#include "log.h" 7#include "log.h"
8#include "stringop.h"
9#include "swaybar/tray/watcher.h" 9#include "swaybar/tray/watcher.h"
10 10
11static const char *obj_path = "/StatusNotifierWatcher"; 11static const char *obj_path = "/StatusNotifierWatcher";
@@ -76,9 +76,7 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) {
76 service = service_or_path; 76 service = service_or_path;
77 path = "/StatusNotifierItem"; 77 path = "/StatusNotifierItem";
78 } 78 }
79 size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1; 79 id = format_str("%s%s", service, path);
80 id = malloc(id_len);
81 snprintf(id, id_len, "%s%s", service, path);
82 } 80 }
83 81
84 if (list_seq_find(watcher->items, cmp_id, id) == -1) { 82 if (list_seq_find(watcher->items, cmp_id, id) == -1) {
@@ -107,7 +105,7 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) {
107 sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); 105 sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service);
108 list_add(watcher->hosts, strdup(service)); 106 list_add(watcher->hosts, strdup(service));
109 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, 107 sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
110 "StatusNotifierHostRegistered", "s", service); 108 "StatusNotifierHostRegistered", "");
111 } else { 109 } else {
112 sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service); 110 sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service);
113 } 111 }
@@ -159,9 +157,7 @@ struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) {
159 return NULL; 157 return NULL;
160 } 158 }
161 159
162 size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; 160 watcher->interface = format_str("org.%s.StatusNotifierWatcher", protocol);
163 watcher->interface = malloc(len);
164 snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol);
165 161
166 sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; 162 sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL;
167 int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, 163 int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path,