aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar')
-rw-r--r--swaybar/bar.c35
-rw-r--r--swaybar/config.c5
-rw-r--r--swaybar/i3bar.c39
-rw-r--r--swaybar/image.c136
-rw-r--r--swaybar/input.c60
-rw-r--r--swaybar/ipc.c21
-rw-r--r--swaybar/main.c1
-rw-r--r--swaybar/meson.build5
-rw-r--r--swaybar/render.c292
-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.c43
-rw-r--r--swaybar/tray/tray.c4
-rw-r--r--swaybar/tray/watcher.c12
15 files changed, 437 insertions, 268 deletions
diff --git a/swaybar/bar.c b/swaybar/bar.c
index 15eab782..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]);
@@ -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 }
@@ -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) {
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 6e13f177..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,
@@ -103,7 +111,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
103 struct swaybar_pointer *pointer = &seat->pointer; 111 struct swaybar_pointer *pointer = &seat->pointer;
104 seat->pointer.x = wl_fixed_to_double(surface_x); 112 seat->pointer.x = wl_fixed_to_double(surface_x);
105 seat->pointer.y = wl_fixed_to_double(surface_y); 113 seat->pointer.y = wl_fixed_to_double(surface_y);
106 pointer->serial = serial; 114
107 struct swaybar_output *output; 115 struct swaybar_output *output;
108 wl_list_for_each(output, &seat->bar->outputs, link) { 116 wl_list_for_each(output, &seat->bar->outputs, link) {
109 if (output->surface == surface) { 117 if (output->surface == surface) {
@@ -111,7 +119,18 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
111 break; 119 break;
112 } 120 }
113 } 121 }
114 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 }
115} 134}
116 135
117static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, 136static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
@@ -141,16 +160,15 @@ static bool check_bindings(struct swaybar *bar, uint32_t button,
141} 160}
142 161
143static bool process_hotspots(struct swaybar_output *output, 162static bool process_hotspots(struct swaybar_output *output,
144 double x, double y, uint32_t button) { 163 double x, double y, uint32_t button, uint32_t state) {
145 double px = x * output->scale; 164 bool released = state == WL_POINTER_BUTTON_STATE_RELEASED;
146 double py = y * output->scale;
147 struct swaybar_hotspot *hotspot; 165 struct swaybar_hotspot *hotspot;
148 wl_list_for_each(hotspot, &output->hotspots, link) { 166 wl_list_for_each(hotspot, &output->hotspots, link) {
149 if (px >= hotspot->x && py >= hotspot->y 167 if (x >= hotspot->x && y >= hotspot->y
150 && px < hotspot->x + hotspot->width 168 && x < hotspot->x + hotspot->width
151 && py < hotspot->y + hotspot->height) { 169 && y < hotspot->y + hotspot->height) {
152 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, 170 if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y,
153 button, hotspot->data)) { 171 button, released, hotspot->data)) {
154 return true; 172 return true;
155 } 173 }
156 } 174 }
@@ -168,14 +186,11 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
168 return; 186 return;
169 } 187 }
170 188
171 if (check_bindings(seat->bar, button, state)) { 189 if (process_hotspots(output, pointer->x, pointer->y, button, state)) {
172 return; 190 return;
173 } 191 }
174 192
175 if (state != WL_POINTER_BUTTON_STATE_PRESSED) { 193 check_bindings(seat->bar, button, state);
176 return;
177 }
178 process_hotspots(output, pointer->x, pointer->y, button);
179} 194}
180 195
181static void workspace_next(struct swaybar *bar, struct swaybar_output *output, 196static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
@@ -211,7 +226,7 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
211 } 226 }
212 } 227 }
213 228
214 if (new) { 229 if (new && new != active) {
215 ipc_send_workspace_command(bar, new->name); 230 ipc_send_workspace_command(bar, new->name);
216 231
217 // 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.
@@ -224,15 +239,15 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
224static void process_discrete_scroll(struct swaybar_seat *seat, 239static void process_discrete_scroll(struct swaybar_seat *seat,
225 struct swaybar_output *output, struct swaybar_pointer *pointer, 240 struct swaybar_output *output, struct swaybar_pointer *pointer,
226 uint32_t axis, wl_fixed_t value) { 241 uint32_t axis, wl_fixed_t value) {
227 // If there is a button press binding, execute it, skip default behavior,
228 // and check button release bindings
229 uint32_t button = wl_axis_to_button(axis, value); 242 uint32_t button = wl_axis_to_button(axis, value);
230 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)) {
231 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)
232 return; 245 return;
233 } 246 }
234 247
235 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);
236 return; 251 return;
237 } 252 }
238 253
@@ -405,7 +420,8 @@ static void wl_touch_up(void *data, struct wl_touch *wl_touch,
405 } 420 }
406 if (time - slot->time < 500) { 421 if (time - slot->time < 500) {
407 // Tap, treat it like a pointer click 422 // Tap, treat it like a pointer click
408 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)
409 } 425 }
410 slot->output = NULL; 426 slot->output = NULL;
411} 427}
diff --git a/swaybar/ipc.c b/swaybar/ipc.c
index a64aa1ab..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 }
@@ -550,7 +547,7 @@ bool handle_ipc_readable(struct swaybar *bar) {
550 // The default depth of 32 is too small to represent some nested layouts, but 547 // The default depth of 32 is too small to represent some nested layouts, but
551 // we can't pass INT_MAX here because json-c (as of this writing) prefaults 548 // we can't pass INT_MAX here because json-c (as of this writing) prefaults
552 // all the memory for its stack. 549 // all the memory for its stack.
553 json_tokener *tok = json_tokener_new_ex(256); 550 json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);
554 if (!tok) { 551 if (!tok) {
555 sway_log_errno(SWAY_ERROR, "failed to create tokener"); 552 sway_log_errno(SWAY_ERROR, "failed to create tokener");
556 free_ipc_response(resp); 553 free_ipc_response(resp);
diff --git a/swaybar/main.c b/swaybar/main.c
index a44c1e63..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>
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 fcc8be1d..879a4e42 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -1,4 +1,3 @@
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>
@@ -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
@@ -53,22 +53,21 @@ static uint32_t render_status_line_error(struct render_context *ctx, double *x)
53 return 0; 53 return 0;
54 } 54 }
55 55
56 uint32_t height = output->height * output->scale; 56 uint32_t height = output->height;
57 57
58 cairo_t *cairo = ctx->cairo; 58 cairo_t *cairo = ctx->cairo;
59 cairo_set_source_u32(cairo, 0xFF0000FF); 59 cairo_set_source_u32(cairo, 0xFF0000FF);
60 60
61 int margin = 3 * output->scale; 61 int margin = 3;
62 double ws_vertical_padding = 62 double ws_vertical_padding = output->bar->config->status_padding;
63 output->bar->config->status_padding * output->scale;
64 63
65 char *font = output->bar->config->font; 64 PangoFontDescription *font = output->bar->config->font_description;
66 int text_width, text_height; 65 int text_width, text_height;
67 get_text_size(cairo, font, &text_width, &text_height, NULL, 66 get_text_size(cairo, font, &text_width, &text_height, NULL,
68 output->scale, false, "%s", error); 67 1, false, "%s", error);
69 68
70 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 69 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
71 uint32_t ideal_surface_height = ideal_height / output->scale; 70 uint32_t ideal_surface_height = ideal_height;
72 if (!output->bar->config->height && 71 if (!output->bar->config->height &&
73 output->height < ideal_surface_height) { 72 output->height < ideal_surface_height) {
74 return ideal_surface_height; 73 return ideal_surface_height;
@@ -78,7 +77,7 @@ static uint32_t render_status_line_error(struct render_context *ctx, double *x)
78 double text_y = height / 2.0 - text_height / 2.0; 77 double text_y = height / 2.0 - text_height / 2.0;
79 cairo_move_to(cairo, *x, (int)floor(text_y)); 78 cairo_move_to(cairo, *x, (int)floor(text_y));
80 choose_text_aa_mode(ctx, 0xFF0000FF); 79 choose_text_aa_mode(ctx, 0xFF0000FF);
81 pango_printf(cairo, font, output->scale, false, "%s", error); 80 render_text(cairo, font, 1, false, "%s", error);
82 *x -= margin; 81 *x -= margin;
83 return output->height; 82 return output->height;
84} 83}
@@ -97,26 +96,25 @@ static uint32_t render_status_line_text(struct render_context *ctx, double *x) {
97 cairo_set_source_u32(cairo, fontcolor); 96 cairo_set_source_u32(cairo, fontcolor);
98 97
99 int text_width, text_height; 98 int text_width, text_height;
100 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 99 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
101 output->scale, config->pango_markup, "%s", text); 100 1, config->pango_markup, "%s", text);
102 101
103 double ws_vertical_padding = config->status_padding * output->scale; 102 double ws_vertical_padding = config->status_padding;
104 int margin = 3 * output->scale; 103 int margin = 3;
105 104
106 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 105 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
107 uint32_t ideal_surface_height = ideal_height / output->scale; 106 uint32_t ideal_surface_height = ideal_height;
108 if (!output->bar->config->height && 107 if (!output->bar->config->height &&
109 output->height < ideal_surface_height) { 108 output->height < ideal_surface_height) {
110 return ideal_surface_height; 109 return ideal_surface_height;
111 } 110 }
112 111
113 *x -= text_width + margin; 112 *x -= text_width + margin;
114 uint32_t height = output->height * output->scale; 113 uint32_t height = output->height;
115 double text_y = height / 2.0 - text_height / 2.0; 114 double text_y = height / 2.0 - text_height / 2.0;
116 cairo_move_to(cairo, *x, (int)floor(text_y)); 115 cairo_move_to(cairo, *x, (int)floor(text_y));
117 choose_text_aa_mode(ctx, fontcolor); 116 choose_text_aa_mode(ctx, fontcolor);
118 pango_printf(cairo, config->font, output->scale, 117 render_text(cairo, config->font_description, 1, config->pango_markup, "%s", text);
119 config->pango_markup, "%s", text);
120 *x -= margin; 118 *x -= margin;
121 return output->height; 119 return output->height;
122} 120}
@@ -161,15 +159,15 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
161 159
162static enum hotspot_event_handling block_hotspot_callback( 160static enum hotspot_event_handling block_hotspot_callback(
163 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 161 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
164 double x, double y, uint32_t button, void *data) { 162 double x, double y, uint32_t button, bool released, void *data) {
165 struct i3bar_block *block = data; 163 struct i3bar_block *block = data;
166 struct status_line *status = output->bar->status; 164 struct status_line *status = output->bar->status;
167 return i3bar_block_send_click(status, block, x, y, 165 return i3bar_block_send_click(status, block, x, y,
168 x - (double)hotspot->x / output->scale, 166 x - (double)hotspot->x,
169 y - (double)hotspot->y / output->scale, 167 y - (double)hotspot->y,
170 (double)hotspot->width / output->scale, 168 (double)hotspot->width,
171 (double)hotspot->height / output->scale, 169 (double)hotspot->height,
172 output->scale, button); 170 output->scale, button, released);
173} 171}
174 172
175static void i3bar_block_unref_callback(void *data) { 173static void i3bar_block_unref_callback(void *data) {
@@ -191,17 +189,17 @@ static uint32_t render_status_block(struct render_context *ctx,
191 struct swaybar_output *output = ctx->output; 189 struct swaybar_output *output = ctx->output;
192 struct swaybar_config *config = output->bar->config; 190 struct swaybar_config *config = output->bar->config;
193 int text_width, text_height; 191 int text_width, text_height;
194 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,
195 output->scale, block->markup, "%s", text); 193 block->markup, "%s", text);
196 194
197 int margin = 3 * output->scale; 195 int margin = 3;
198 double ws_vertical_padding = config->status_padding * output->scale; 196 double ws_vertical_padding = config->status_padding;
199 197
200 int width = text_width; 198 int width = text_width;
201 if (block->min_width_str) { 199 if (block->min_width_str) {
202 int w; 200 int w;
203 get_text_size(cairo, config->font, &w, NULL, NULL, 201 get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup,
204 output->scale, block->markup, "%s", block->min_width_str); 202 "%s", block->min_width_str);
205 block->min_width = w; 203 block->min_width = w;
206 } 204 }
207 if (width < block->min_width) { 205 if (width < block->min_width) {
@@ -210,30 +208,30 @@ static uint32_t render_status_block(struct render_context *ctx,
210 208
211 double block_width = width; 209 double block_width = width;
212 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 210 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
213 uint32_t ideal_surface_height = ideal_height / output->scale; 211 uint32_t ideal_surface_height = ideal_height;
214 if (!output->bar->config->height && 212 if (!output->bar->config->height &&
215 output->height < ideal_surface_height) { 213 output->height < ideal_surface_height) {
216 return ideal_surface_height; 214 return ideal_surface_height;
217 } 215 }
218 216
219 *x -= width; 217 *x -= width;
220 if ((block->border || block->urgent) && block->border_left > 0) { 218 if ((block->border_set || block->urgent) && block->border_left > 0) {
221 *x -= (block->border_left * output->scale + margin); 219 *x -= (block->border_left + margin);
222 block_width += block->border_left * output->scale + margin; 220 block_width += block->border_left + margin;
223 } 221 }
224 if ((block->border || block->urgent) && block->border_right > 0) { 222 if ((block->border_set || block->urgent) && block->border_right > 0) {
225 *x -= (block->border_right * output->scale + margin); 223 *x -= (block->border_right + margin);
226 block_width += block->border_right * output->scale + margin; 224 block_width += block->border_right + margin;
227 } 225 }
228 226
229 int sep_width, sep_height; 227 int sep_width, sep_height;
230 int sep_block_width = block->separator_block_width; 228 int sep_block_width = block->separator_block_width;
231 if (!edge) { 229 if (!edge) {
232 if (config->sep_symbol) { 230 if (config->sep_symbol) {
233 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 231 get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,
234 output->scale, false, "%s", config->sep_symbol); 232 1, false, "%s", config->sep_symbol);
235 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 233 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
236 uint32_t _ideal_surface_height = _ideal_height / output->scale; 234 uint32_t _ideal_surface_height = _ideal_height;
237 if (!output->bar->config->height && 235 if (!output->bar->config->height &&
238 output->height < _ideal_surface_height) { 236 output->height < _ideal_surface_height) {
239 return _ideal_surface_height; 237 return _ideal_surface_height;
@@ -244,10 +242,10 @@ static uint32_t render_status_block(struct render_context *ctx,
244 } 242 }
245 *x -= sep_block_width; 243 *x -= sep_block_width;
246 } else if (config->status_edge_padding) { 244 } else if (config->status_edge_padding) {
247 *x -= config->status_edge_padding * output->scale; 245 *x -= config->status_edge_padding;
248 } 246 }
249 247
250 uint32_t height = output->height * output->scale; 248 uint32_t height = output->height;
251 if (output->bar->status->click_events) { 249 if (output->bar->status->click_events) {
252 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 250 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
253 hotspot->x = *x; 251 hotspot->x = *x;
@@ -275,23 +273,25 @@ static uint32_t render_status_block(struct render_context *ctx,
275 273
276 uint32_t border_color = block->urgent 274 uint32_t border_color = block->urgent
277 ? config->colors.urgent_workspace.border : block->border; 275 ? config->colors.urgent_workspace.border : block->border;
278 if (border_color && block->border_top > 0) { 276 if (block->border_set || block->urgent) {
279 render_sharp_line(cairo, border_color, x_pos, y_pos, 277 if (block->border_top > 0) {
280 block_width, block->border_top * output->scale); 278 render_sharp_line(cairo, border_color, x_pos, y_pos,
281 } 279 block_width, block->border_top);
282 if (border_color && block->border_bottom > 0) { 280 }
283 render_sharp_line(cairo, border_color, x_pos, 281 if (block->border_bottom > 0) {
284 y_pos + render_height - block->border_bottom * output->scale, 282 render_sharp_line(cairo, border_color, x_pos,
285 block_width, block->border_bottom * output->scale); 283 y_pos + render_height - block->border_bottom,
286 } 284 block_width, block->border_bottom);
287 if (border_color && block->border_left > 0) { 285 }
288 render_sharp_line(cairo, border_color, x_pos, y_pos, 286 if (block->border_left > 0) {
289 block->border_left * output->scale, render_height); 287 render_sharp_line(cairo, border_color, x_pos, y_pos,
290 x_pos += block->border_left * output->scale + margin; 288 block->border_left, render_height);
289 }
290 x_pos += block->border_left + margin;
291 } 291 }
292 292
293 double offset = 0; 293 double offset = 0;
294 if (strncmp(block->align, "left", 5) == 0) { 294 if (strncmp(block->align, "left", 4) == 0) {
295 offset = x_pos; 295 offset = x_pos;
296 } else if (strncmp(block->align, "right", 5) == 0) { 296 } else if (strncmp(block->align, "right", 5) == 0) {
297 offset = x_pos + width - text_width; 297 offset = x_pos + width - text_width;
@@ -306,15 +306,16 @@ static uint32_t render_status_block(struct render_context *ctx,
306 color = block->urgent ? config->colors.urgent_workspace.text : color; 306 color = block->urgent ? config->colors.urgent_workspace.text : color;
307 cairo_set_source_u32(cairo, color); 307 cairo_set_source_u32(cairo, color);
308 choose_text_aa_mode(ctx, color); 308 choose_text_aa_mode(ctx, color);
309 pango_printf(cairo, config->font, output->scale, 309 render_text(cairo, config->font_description, 1, block->markup, "%s", text);
310 block->markup, "%s", text);
311 x_pos += width; 310 x_pos += width;
312 311
313 if (block->border && block->border_right > 0) { 312 if (block->border_set || block->urgent) {
314 x_pos += margin; 313 x_pos += margin;
315 render_sharp_line(cairo, border_color, x_pos, y_pos, 314 if (block->border_right > 0) {
316 block->border_right * output->scale, render_height); 315 render_sharp_line(cairo, border_color, x_pos, y_pos,
317 x_pos += block->border_right * output->scale; 316 block->border_right, render_height);
317 }
318 x_pos += block->border_right;
318 } 319 }
319 320
320 if (!edge && block->separator) { 321 if (!edge && block->separator) {
@@ -329,7 +330,7 @@ static uint32_t render_status_block(struct render_context *ctx,
329 double sep_y = height / 2.0 - sep_height / 2.0; 330 double sep_y = height / 2.0 - sep_height / 2.0;
330 cairo_move_to(cairo, offset, (int)floor(sep_y)); 331 cairo_move_to(cairo, offset, (int)floor(sep_y));
331 choose_text_aa_mode(ctx, color); 332 choose_text_aa_mode(ctx, color);
332 pango_printf(cairo, config->font, output->scale, false, 333 render_text(cairo, config->font_description, 1, false,
333 "%s", config->sep_symbol); 334 "%s", config->sep_symbol);
334 } else { 335 } else {
335 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 336 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
@@ -352,18 +353,18 @@ static void predict_status_block_pos(cairo_t *cairo,
352 struct swaybar_config *config = output->bar->config; 353 struct swaybar_config *config = output->bar->config;
353 354
354 int text_width, text_height; 355 int text_width, text_height;
355 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,
356 output->scale, block->markup, "%s", block->full_text); 357 block->markup, "%s", block->full_text);
357 358
358 int margin = 3 * output->scale; 359 int margin = 3;
359 double ws_vertical_padding = config->status_padding * output->scale; 360 double ws_vertical_padding = config->status_padding;
360 361
361 int width = text_width; 362 int width = text_width;
362 363
363 if (block->min_width_str) { 364 if (block->min_width_str) {
364 int w; 365 int w;
365 get_text_size(cairo, config->font, &w, NULL, NULL, 366 get_text_size(cairo, config->font_description, &w, NULL, NULL,
366 output->scale, block->markup, "%s", block->min_width_str); 367 1, block->markup, "%s", block->min_width_str);
367 block->min_width = w; 368 block->min_width = w;
368 } 369 }
369 if (width < block->min_width) { 370 if (width < block->min_width) {
@@ -371,28 +372,28 @@ static void predict_status_block_pos(cairo_t *cairo,
371 } 372 }
372 373
373 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 374 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
374 uint32_t ideal_surface_height = ideal_height / output->scale; 375 uint32_t ideal_surface_height = ideal_height;
375 if (!output->bar->config->height && 376 if (!output->bar->config->height &&
376 output->height < ideal_surface_height) { 377 output->height < ideal_surface_height) {
377 return; 378 return;
378 } 379 }
379 380
380 *x -= width; 381 *x -= width;
381 if ((block->border || block->urgent) && block->border_left > 0) { 382 if ((block->border_set || block->urgent) && block->border_left > 0) {
382 *x -= (block->border_left * output->scale + margin); 383 *x -= (block->border_left + margin);
383 } 384 }
384 if ((block->border || block->urgent) && block->border_right > 0) { 385 if ((block->border_set || block->urgent) && block->border_right > 0) {
385 *x -= (block->border_right * output->scale + margin); 386 *x -= (block->border_right + margin);
386 } 387 }
387 388
388 int sep_width, sep_height; 389 int sep_width, sep_height;
389 int sep_block_width = block->separator_block_width; 390 int sep_block_width = block->separator_block_width;
390 if (!edge) { 391 if (!edge) {
391 if (config->sep_symbol) { 392 if (config->sep_symbol) {
392 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 393 get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL,
393 output->scale, false, "%s", config->sep_symbol); 394 1, false, "%s", config->sep_symbol);
394 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 395 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
395 uint32_t _ideal_surface_height = _ideal_height / output->scale; 396 uint32_t _ideal_surface_height = _ideal_height;
396 if (!output->bar->config->height && 397 if (!output->bar->config->height &&
397 output->height < _ideal_surface_height) { 398 output->height < _ideal_surface_height) {
398 return; 399 return;
@@ -403,13 +404,13 @@ static void predict_status_block_pos(cairo_t *cairo,
403 } 404 }
404 *x -= sep_block_width; 405 *x -= sep_block_width;
405 } else if (config->status_edge_padding) { 406 } else if (config->status_edge_padding) {
406 *x -= config->status_edge_padding * output->scale; 407 *x -= config->status_edge_padding;
407 } 408 }
408} 409}
409 410
410static double predict_status_line_pos(cairo_t *cairo, 411static double predict_status_line_pos(cairo_t *cairo,
411 struct swaybar_output *output, double x) { 412 struct swaybar_output *output, double x) {
412 bool edge = x == output->width * output->scale; 413 bool edge = x == output->width;
413 struct i3bar_block *block; 414 struct i3bar_block *block;
414 wl_list_for_each(block, &output->bar->status->blocks, link) { 415 wl_list_for_each(block, &output->bar->status->blocks, link) {
415 predict_status_block_pos(cairo, output, block, &x, edge); 416 predict_status_block_pos(cairo, output, block, &x, edge);
@@ -424,24 +425,24 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo,
424 struct swaybar_config *config = output->bar->config; 425 struct swaybar_config *config = output->bar->config;
425 426
426 int text_width, text_height; 427 int text_width, text_height;
427 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,
428 output->scale, config->pango_markup, "%s", ws->label); 429 config->pango_markup, "%s", ws->label);
429 430
430 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 431 int ws_vertical_padding = WS_VERTICAL_PADDING;
431 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 432 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
432 int border_width = BORDER_WIDTH * output->scale; 433 int border_width = BORDER_WIDTH;
433 434
434 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 435 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
435 + border_width * 2; 436 + border_width * 2;
436 uint32_t ideal_surface_height = ideal_height / output->scale; 437 uint32_t ideal_surface_height = ideal_height;
437 if (!output->bar->config->height && 438 if (!output->bar->config->height &&
438 output->height < ideal_surface_height) { 439 output->height < ideal_surface_height) {
439 return 0; 440 return 0;
440 } 441 }
441 442
442 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;
443 if (width < config->workspace_min_width * output->scale) { 444 if (width < config->workspace_min_width) {
444 width = config->workspace_min_width * output->scale; 445 width = config->workspace_min_width;
445 } 446 }
446 return width; 447 return width;
447} 448}
@@ -472,24 +473,24 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
472 } 473 }
473 474
474 int text_width, text_height; 475 int text_width, text_height;
475 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 476 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
476 output->scale, output->bar->mode_pango_markup, 477 1, output->bar->mode_pango_markup,
477 "%s", mode); 478 "%s", mode);
478 479
479 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 480 int ws_vertical_padding = WS_VERTICAL_PADDING;
480 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 481 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
481 int border_width = BORDER_WIDTH * output->scale; 482 int border_width = BORDER_WIDTH;
482 483
483 uint32_t ideal_height = text_height + ws_vertical_padding * 2 484 uint32_t ideal_height = text_height + ws_vertical_padding * 2
484 + border_width * 2; 485 + border_width * 2;
485 uint32_t ideal_surface_height = ideal_height / output->scale; 486 uint32_t ideal_surface_height = ideal_height;
486 if (!output->bar->config->height && 487 if (!output->bar->config->height &&
487 output->height < ideal_surface_height) { 488 output->height < ideal_surface_height) {
488 return 0; 489 return 0;
489 } 490 }
490 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;
491 if (width < config->workspace_min_width * output->scale) { 492 if (width < config->workspace_min_width) {
492 width = config->workspace_min_width * output->scale; 493 width = config->workspace_min_width;
493 } 494 }
494 return width; 495 return width;
495} 496}
@@ -497,7 +498,7 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
497static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) { 498static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) {
498 struct swaybar_output *output = ctx->output; 499 struct swaybar_output *output = ctx->output;
499 uint32_t max_height = 0; 500 uint32_t max_height = 0;
500 bool edge = *x == output->width * output->scale; 501 bool edge = *x == output->width;
501 struct i3bar_block *block; 502 struct i3bar_block *block;
502 bool use_short_text = false; 503 bool use_short_text = false;
503 504
@@ -505,7 +506,7 @@ static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x)
505 double reserved_width = 506 double reserved_width =
506 predict_workspace_buttons_length(cairo, output) + 507 predict_workspace_buttons_length(cairo, output) +
507 predict_binding_mode_indicator_length(cairo, output) + 508 predict_binding_mode_indicator_length(cairo, output) +
508 3 * output->scale; // require a bit of space for margin 509 3; // require a bit of space for margin
509 510
510 double predicted_full_pos = 511 double predicted_full_pos =
511 predict_status_line_pos(cairo, output, *x); 512 predict_status_line_pos(cairo, output, *x);
@@ -549,27 +550,27 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx,
549 cairo_t *cairo = ctx->cairo; 550 cairo_t *cairo = ctx->cairo;
550 struct swaybar_config *config = output->bar->config; 551 struct swaybar_config *config = output->bar->config;
551 int text_width, text_height; 552 int text_width, text_height;
552 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 553 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
553 output->scale, output->bar->mode_pango_markup, 554 1, output->bar->mode_pango_markup,
554 "%s", mode); 555 "%s", mode);
555 556
556 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 557 int ws_vertical_padding = WS_VERTICAL_PADDING;
557 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 558 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
558 int border_width = BORDER_WIDTH * output->scale; 559 int border_width = BORDER_WIDTH;
559 560
560 uint32_t ideal_height = text_height + ws_vertical_padding * 2 561 uint32_t ideal_height = text_height + ws_vertical_padding * 2
561 + border_width * 2; 562 + border_width * 2;
562 uint32_t ideal_surface_height = ideal_height / output->scale; 563 uint32_t ideal_surface_height = ideal_height;
563 if (!output->bar->config->height && 564 if (!output->bar->config->height &&
564 output->height < ideal_surface_height) { 565 output->height < ideal_surface_height) {
565 return ideal_surface_height; 566 return ideal_surface_height;
566 } 567 }
567 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;
568 if (width < config->workspace_min_width * output->scale) { 569 if (width < config->workspace_min_width) {
569 width = config->workspace_min_width * output->scale; 570 width = config->workspace_min_width;
570 } 571 }
571 572
572 uint32_t height = output->height * output->scale; 573 uint32_t height = output->height;
573 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 574 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
574 cairo_set_source_u32(cairo, config->colors.binding_mode.background); 575 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
575 ctx->background_color = config->colors.binding_mode.background; 576 ctx->background_color = config->colors.binding_mode.background;
@@ -590,17 +591,22 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx,
590 cairo_set_source_u32(cairo, config->colors.binding_mode.text); 591 cairo_set_source_u32(cairo, config->colors.binding_mode.text);
591 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));
592 choose_text_aa_mode(ctx, config->colors.binding_mode.text); 593 choose_text_aa_mode(ctx, config->colors.binding_mode.text);
593 pango_printf(cairo, config->font, output->scale, 594 render_text(cairo, config->font_description, 1, output->bar->mode_pango_markup,
594 output->bar->mode_pango_markup, "%s", mode); 595 "%s", mode);
595 return output->height; 596 return output->height;
596} 597}
597 598
598static enum hotspot_event_handling workspace_hotspot_callback( 599static enum hotspot_event_handling workspace_hotspot_callback(
599 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 600 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
600 double x, double y, uint32_t button, void *data) { 601 double x, double y, uint32_t button, bool released, void *data) {
601 if (button != BTN_LEFT) { 602 if (button != BTN_LEFT) {
602 return HOTSPOT_PROCESS; 603 return HOTSPOT_PROCESS;
603 } 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 }
604 ipc_send_workspace_command(output->bar, (const char *)data); 610 ipc_send_workspace_command(output->bar, (const char *)data);
605 return HOTSPOT_IGNORE; 611 return HOTSPOT_IGNORE;
606} 612}
@@ -620,28 +626,28 @@ static uint32_t render_workspace_button(struct render_context *ctx,
620 box_colors = config->colors.inactive_workspace; 626 box_colors = config->colors.inactive_workspace;
621 } 627 }
622 628
623 uint32_t height = output->height * output->scale; 629 uint32_t height = output->height;
624 630
625 cairo_t *cairo = ctx->cairo; 631 cairo_t *cairo = ctx->cairo;
626 int text_width, text_height; 632 int text_width, text_height;
627 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 633 get_text_size(cairo, config->font_description, &text_width, &text_height, NULL,
628 output->scale, config->pango_markup, "%s", ws->label); 634 1, config->pango_markup, "%s", ws->label);
629 635
630 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 636 int ws_vertical_padding = WS_VERTICAL_PADDING;
631 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 637 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
632 int border_width = BORDER_WIDTH * output->scale; 638 int border_width = BORDER_WIDTH;
633 639
634 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 640 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
635 + border_width * 2; 641 + border_width * 2;
636 uint32_t ideal_surface_height = ideal_height / output->scale; 642 uint32_t ideal_surface_height = ideal_height;
637 if (!output->bar->config->height && 643 if (!output->bar->config->height &&
638 output->height < ideal_surface_height) { 644 output->height < ideal_surface_height) {
639 return ideal_surface_height; 645 return ideal_surface_height;
640 } 646 }
641 647
642 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;
643 if (width < config->workspace_min_width * output->scale) { 649 if (width < config->workspace_min_width) {
644 width = config->workspace_min_width * output->scale; 650 width = config->workspace_min_width;
645 } 651 }
646 652
647 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 653 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
@@ -664,7 +670,7 @@ static uint32_t render_workspace_button(struct render_context *ctx,
664 cairo_set_source_u32(cairo, box_colors.text); 670 cairo_set_source_u32(cairo, box_colors.text);
665 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));
666 choose_text_aa_mode(ctx, box_colors.text); 672 choose_text_aa_mode(ctx, box_colors.text);
667 pango_printf(cairo, config->font, output->scale, config->pango_markup, 673 render_text(cairo, config->font_description, 1, config->pango_markup,
668 "%s", ws->label); 674 "%s", ws->label);
669 675
670 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 676 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
@@ -686,19 +692,10 @@ static uint32_t render_to_cairo(struct render_context *ctx) {
686 struct swaybar_output *output = ctx->output; 692 struct swaybar_output *output = ctx->output;
687 struct swaybar *bar = output->bar; 693 struct swaybar *bar = output->bar;
688 struct swaybar_config *config = bar->config; 694 struct swaybar_config *config = bar->config;
689 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
690 if (output->focused) {
691 ctx->background_color = config->colors.focused_background;
692 } else {
693 ctx->background_color = config->colors.background;
694 }
695
696 cairo_set_source_u32(cairo, ctx->background_color);
697 cairo_paint(cairo);
698 695
699 int th; 696 int th;
700 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, "");
701 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4) / output->scale; 698 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4);
702 /* 699 /*
703 * 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
704 * 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
@@ -706,7 +703,7 @@ static uint32_t render_to_cairo(struct render_context *ctx) {
706 * 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
707 * utilize the available space. 704 * utilize the available space.
708 */ 705 */
709 double x = output->width * output->scale; 706 double x = output->width;
710#if HAVE_TRAY 707#if HAVE_TRAY
711 if (bar->tray) { 708 if (bar->tray) {
712 uint32_t h = render_tray(cairo, output, &x); 709 uint32_t h = render_tray(cairo, output, &x);
@@ -756,34 +753,43 @@ void render_frame(struct swaybar_output *output) {
756 753
757 free_hotspots(&output->hotspots); 754 free_hotspots(&output->hotspots);
758 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
759 struct render_context ctx = { 0 }; 763 struct render_context ctx = { 0 };
760 ctx.output = output; 764 ctx.output = output;
765 // initial background color used for deciding the best way to antialias text
766 ctx.background_color = background_color;
761 767
762 cairo_surface_t *recorder = cairo_recording_surface_create( 768 cairo_surface_t *recorder = cairo_recording_surface_create(
763 CAIRO_CONTENT_COLOR_ALPHA, NULL); 769 CAIRO_CONTENT_COLOR_ALPHA, NULL);
764 cairo_t *cairo = cairo_create(recorder); 770 cairo_t *cairo = cairo_create(recorder);
771 cairo_scale(cairo, output->scale, output->scale);
765 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 772 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
766 ctx.cairo = cairo; 773 ctx.cairo = cairo;
767 774
768 cairo_font_options_t *fo = cairo_font_options_create(); 775 cairo_font_options_t *fo = cairo_font_options_create();
769 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
770 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); 776 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
771 ctx.textaa_safe = fo; 777 ctx.textaa_safe = fo;
772 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { 778 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
773 ctx.textaa_sharp = ctx.textaa_safe; 779 ctx.textaa_sharp = ctx.textaa_safe;
774 } else { 780 } else {
775 fo = cairo_font_options_create(); 781 fo = cairo_font_options_create();
776 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
777 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); 782 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
778 cairo_font_options_set_subpixel_order(fo, 783 cairo_font_options_set_subpixel_order(fo,
779 to_cairo_subpixel_order(output->subpixel)); 784 to_cairo_subpixel_order(output->subpixel));
780 ctx.textaa_sharp = fo; 785 ctx.textaa_sharp = fo;
781 } 786 }
782 787
783 cairo_save(cairo); 788
784 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 789 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
790 cairo_set_source_u32(cairo, background_color);
785 cairo_paint(cairo); 791 cairo_paint(cairo);
786 cairo_restore(cairo); 792
787 uint32_t height = render_to_cairo(&ctx); 793 uint32_t height = render_to_cairo(&ctx);
788 int config_height = output->bar->config->height; 794 int config_height = output->bar->config->height;
789 if (config_height > 0) { 795 if (config_height > 0) {
@@ -810,9 +816,7 @@ void render_frame(struct swaybar_output *output) {
810 output->width * output->scale, 816 output->width * output->scale,
811 output->height * output->scale); 817 output->height * output->scale);
812 if (!output->current_buffer) { 818 if (!output->current_buffer) {
813 cairo_surface_destroy(recorder); 819 goto cleanup;
814 cairo_destroy(cairo);
815 return;
816 } 820 }
817 cairo_t *shm = output->current_buffer->cairo; 821 cairo_t *shm = output->current_buffer->cairo;
818 822
@@ -830,6 +834,17 @@ void render_frame(struct swaybar_output *output) {
830 wl_surface_damage(output->surface, 0, 0, 834 wl_surface_damage(output->surface, 0, 0,
831 output->width, output->height); 835 output->width, output->height);
832 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
833 struct wl_callback *frame_callback = wl_surface_frame(output->surface); 848 struct wl_callback *frame_callback = wl_surface_frame(output->surface);
834 wl_callback_add_listener(frame_callback, &output_frame_listener, output); 849 wl_callback_add_listener(frame_callback, &output_frame_listener, output);
835 output->frame_scheduled = true; 850 output->frame_scheduled = true;
@@ -837,6 +852,7 @@ void render_frame(struct swaybar_output *output) {
837 wl_surface_commit(output->surface); 852 wl_surface_commit(output->surface);
838 } 853 }
839 854
855cleanup:
840 if (ctx.textaa_sharp != ctx.textaa_safe) { 856 if (ctx.textaa_sharp != ctx.textaa_safe) {
841 cairo_font_options_destroy(ctx.textaa_sharp); 857 cairo_font_options_destroy(ctx.textaa_sharp);
842 } 858 }
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 19f4beac..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,12 +6,12 @@
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"
16#include "cairo_util.h" 15#include "cairo_util.h"
17#include "list.h" 16#include "list.h"
18#include "log.h" 17#include "log.h"
@@ -385,13 +384,18 @@ static int cmp_sni_id(const void *item, const void *cmp_to) {
385 384
386static enum hotspot_event_handling icon_hotspot_callback( 385static enum hotspot_event_handling icon_hotspot_callback(
387 struct swaybar_output *output, struct swaybar_hotspot *hotspot, 386 struct swaybar_output *output, struct swaybar_hotspot *hotspot,
388 double x, double y, uint32_t button, void *data) { 387 double x, double y, uint32_t button, bool released, void *data) {
389 sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); 388 sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data);
390 389
391 struct swaybar_tray *tray = output->bar->tray; 390 struct swaybar_tray *tray = output->bar->tray;
392 int idx = list_seq_find(tray->items, cmp_sni_id, data); 391 int idx = list_seq_find(tray->items, cmp_sni_id, data);
393 392
394 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 }
395 struct swaybar_sni *sni = tray->items->items[idx]; 399 struct swaybar_sni *sni = tray->items->items[idx];
396 // guess global position since wayland doesn't expose it 400 // guess global position since wayland doesn't expose it
397 struct swaybar_config *config = tray->bar->config; 401 struct swaybar_config *config = tray->bar->config;
@@ -426,7 +430,7 @@ static void reload_sni(struct swaybar_sni *sni, char *icon_theme,
426 list_free(icon_search_paths); 430 list_free(icon_search_paths);
427 if (icon_path) { 431 if (icon_path) {
428 cairo_surface_destroy(sni->icon); 432 cairo_surface_destroy(sni->icon);
429 sni->icon = load_background_image(icon_path); 433 sni->icon = load_image(icon_path);
430 free(icon_path); 434 free(icon_path);
431 return; 435 return;
432 } 436 }
@@ -466,6 +470,11 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
466 sni->target_size = target_size; 470 sni->target_size = target_size;
467 } 471 }
468 472
473 // Passive
474 if (sni->status && sni->status[0] == 'P') {
475 return 0;
476 }
477
469 int icon_size; 478 int icon_size;
470 cairo_surface_t *icon; 479 cairo_surface_t *icon;
471 if (sni->icon) { 480 if (sni->icon) {
@@ -493,24 +502,36 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x,
493 cairo_destroy(cairo_icon); 502 cairo_destroy(cairo_icon);
494 } 503 }
495 504
496 int padded_size = icon_size + 2*padding; 505 double descaled_padding = (double)padding / output->scale;
497 *x -= padded_size; 506 double descaled_icon_size = (double)icon_size / output->scale;
498 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);
499 511
500 cairo_operator_t op = cairo_get_operator(cairo); 512 cairo_operator_t op = cairo_get_operator(cairo);
501 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); 513 cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
502 cairo_set_source_surface(cairo, icon, *x + padding, y + padding); 514
503 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);
504 cairo_fill(cairo); 523 cairo_fill(cairo);
524
505 cairo_set_operator(cairo, op); 525 cairo_set_operator(cairo, op);
506 526
527 cairo_pattern_destroy(icon_pattern);
507 cairo_surface_destroy(icon); 528 cairo_surface_destroy(icon);
508 529
509 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 530 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
510 hotspot->x = *x; 531 hotspot->x = *x;
511 hotspot->y = 0; 532 hotspot->y = 0;
512 hotspot->width = height; 533 hotspot->width = size;
513 hotspot->height = height; 534 hotspot->height = output->height;
514 hotspot->callback = icon_hotspot_callback; 535 hotspot->callback = icon_hotspot_callback;
515 hotspot->destroy = free; 536 hotspot->destroy = free;
516 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,