diff options
Diffstat (limited to 'swaybar/render.c')
-rw-r--r-- | swaybar/render.c | 638 |
1 files changed, 397 insertions, 241 deletions
diff --git a/swaybar/render.c b/swaybar/render.c index 6fc09078..26248d35 100644 --- a/swaybar/render.c +++ b/swaybar/render.c | |||
@@ -1,35 +1,92 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <limits.h> | ||
1 | #include <stdlib.h> | 3 | #include <stdlib.h> |
2 | #include <stdint.h> | 4 | #include <stdint.h> |
3 | #include <string.h> | 5 | #include <string.h> |
4 | 6 | #include <wlr/util/log.h> | |
5 | #include "client/cairo.h" | 7 | #include "cairo.h" |
6 | #include "client/pango.h" | 8 | #include "pango.h" |
7 | #include "client/window.h" | 9 | #include "pool-buffer.h" |
10 | #include "swaybar/bar.h" | ||
8 | #include "swaybar/config.h" | 11 | #include "swaybar/config.h" |
9 | #include "swaybar/status_line.h" | 12 | #include "swaybar/ipc.h" |
10 | #include "swaybar/render.h" | 13 | #include "swaybar/render.h" |
11 | #ifdef ENABLE_TRAY | 14 | #include "swaybar/status_line.h" |
12 | #include "swaybar/tray/tray.h" | 15 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
13 | #include "swaybar/tray/sni.h" | 16 | |
14 | #endif | 17 | static const int WS_HORIZONTAL_PADDING = 5; |
15 | #include "log.h" | 18 | static const double WS_VERTICAL_PADDING = 1.5; |
16 | 19 | static const double BORDER_WIDTH = 1; | |
17 | 20 | ||
18 | /* internal spacing */ | 21 | static uint32_t render_status_line_error(cairo_t *cairo, |
19 | static int margin = 3; | 22 | struct swaybar_output *output, struct swaybar_config *config, |
20 | static int ws_horizontal_padding = 5; | 23 | const char *error, double *x, uint32_t surface_height) { |
21 | static double ws_vertical_padding = 1.5; | 24 | if (!error) { |
22 | static int ws_spacing = 1; | 25 | return 0; |
23 | 26 | } | |
24 | /** | 27 | |
25 | * Renders a sharp line of any width and height. | 28 | uint32_t height = surface_height * output->scale; |
26 | * | 29 | |
27 | * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 | 30 | cairo_set_source_u32(cairo, 0xFF0000FF); |
28 | * if the line has a width/height of one pixel, respectively. | 31 | |
29 | */ | 32 | int margin = 3 * output->scale; |
30 | static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) { | 33 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; |
31 | cairo_set_source_u32(cairo, color); | 34 | |
35 | int text_width, text_height; | ||
36 | get_text_size(cairo, config->font, | ||
37 | &text_width, &text_height, output->scale, false, "%s", error); | ||
38 | |||
39 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | ||
40 | uint32_t ideal_surface_height = ideal_height / output->scale; | ||
41 | if (surface_height < ideal_surface_height) { | ||
42 | return ideal_surface_height; | ||
43 | } | ||
44 | *x -= text_width + margin; | ||
45 | |||
46 | double text_y = height / 2.0 - text_height / 2.0; | ||
47 | cairo_move_to(cairo, *x, (int)floor(text_y)); | ||
48 | pango_printf(cairo, config->font, output->scale, false, "%s", error); | ||
49 | *x -= margin; | ||
50 | return surface_height; | ||
51 | } | ||
52 | |||
53 | static uint32_t render_status_line_text(cairo_t *cairo, | ||
54 | struct swaybar_output *output, struct swaybar_config *config, | ||
55 | const char *text, bool focused, double *x, uint32_t surface_height) { | ||
56 | if (!text) { | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | uint32_t height = surface_height * output->scale; | ||
61 | |||
62 | cairo_set_source_u32(cairo, focused ? | ||
63 | config->colors.focused_statusline : config->colors.statusline); | ||
32 | 64 | ||
65 | int text_width, text_height; | ||
66 | get_text_size(cairo, config->font, &text_width, &text_height, | ||
67 | output->scale, config->pango_markup, "%s", text); | ||
68 | |||
69 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | ||
70 | int margin = 3 * output->scale; | ||
71 | |||
72 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | ||
73 | uint32_t ideal_surface_height = ideal_height / output->scale; | ||
74 | if (surface_height < ideal_surface_height) { | ||
75 | return ideal_surface_height; | ||
76 | } | ||
77 | |||
78 | *x -= text_width + margin; | ||
79 | double text_y = height / 2.0 - text_height / 2.0; | ||
80 | cairo_move_to(cairo, *x, (int)floor(text_y)); | ||
81 | pango_printf(cairo, config->font, output->scale, | ||
82 | config->pango_markup, "%s", text); | ||
83 | *x -= margin; | ||
84 | return surface_height; | ||
85 | } | ||
86 | |||
87 | static void render_sharp_line(cairo_t *cairo, uint32_t color, | ||
88 | double x, double y, double width, double height) { | ||
89 | cairo_set_source_u32(cairo, color); | ||
33 | if (width > 1 && height > 1) { | 90 | if (width > 1 && height > 1) { |
34 | cairo_rectangle(cairo, x, y, width, height); | 91 | cairo_rectangle(cairo, x, y, width, height); |
35 | cairo_fill(cairo); | 92 | cairo_fill(cairo); |
@@ -39,13 +96,11 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y | |||
39 | height += y; | 96 | height += y; |
40 | width = x; | 97 | width = x; |
41 | } | 98 | } |
42 | |||
43 | if (height == 1) { | 99 | if (height == 1) { |
44 | y += 0.5; | 100 | y += 0.5; |
45 | width += x; | 101 | width += x; |
46 | height = y; | 102 | height = y; |
47 | } | 103 | } |
48 | |||
49 | cairo_move_to(cairo, x, y); | 104 | cairo_move_to(cairo, x, y); |
50 | cairo_set_line_width(cairo, 1.0); | 105 | cairo_set_line_width(cairo, 1.0); |
51 | cairo_line_to(cairo, width, height); | 106 | cairo_line_to(cairo, width, height); |
@@ -53,176 +108,256 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y | |||
53 | } | 108 | } |
54 | } | 109 | } |
55 | 110 | ||
56 | static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) { | 111 | static void block_hotspot_callback(struct swaybar_output *output, |
57 | int width, height, sep_width; | 112 | int x, int y, uint32_t button, void *data) { |
58 | get_text_size(window->cairo, window->font, &width, &height, | 113 | struct i3bar_block *block = data; |
59 | window->scale, block->markup, "%s", block->full_text); | 114 | struct status_line *status = output->bar->status; |
115 | i3bar_block_send_click(status, block, x, y, button); | ||
116 | } | ||
60 | 117 | ||
61 | int textwidth = width; | 118 | static uint32_t render_status_block(cairo_t *cairo, |
62 | double block_width = width; | 119 | struct swaybar_config *config, struct swaybar_output *output, |
120 | struct i3bar_block *block, double *x, | ||
121 | uint32_t surface_height, bool focused, bool edge) { | ||
122 | if (!block->full_text || !*block->full_text) { | ||
123 | return 0; | ||
124 | } | ||
63 | 125 | ||
126 | uint32_t height = surface_height * output->scale; | ||
127 | |||
128 | int text_width, text_height; | ||
129 | get_text_size(cairo, config->font, &text_width, &text_height, | ||
130 | output->scale, block->markup, "%s", block->full_text); | ||
131 | |||
132 | int margin = 3 * output->scale; | ||
133 | int ws_vertical_padding = WS_VERTICAL_PADDING * 2; | ||
134 | |||
135 | int width = text_width; | ||
64 | if (width < block->min_width) { | 136 | if (width < block->min_width) { |
65 | width = block->min_width; | 137 | width = block->min_width; |
66 | } | 138 | } |
67 | 139 | ||
68 | *x -= width; | 140 | double block_width = width; |
141 | uint32_t ideal_height = text_height + ws_vertical_padding * 2; | ||
142 | uint32_t ideal_surface_height = ideal_height / output->scale; | ||
143 | if (surface_height < ideal_surface_height) { | ||
144 | return ideal_surface_height; | ||
145 | } | ||
69 | 146 | ||
70 | if (block->border != 0 && block->border_left > 0) { | 147 | *x -= width; |
148 | if (block->border && block->border_left > 0) { | ||
71 | *x -= (block->border_left + margin); | 149 | *x -= (block->border_left + margin); |
72 | block_width += block->border_left + margin; | 150 | block_width += block->border_left + margin; |
73 | } | 151 | } |
74 | 152 | if (block->border && block->border_right > 0) { | |
75 | if (block->border != 0 && block->border_right > 0) { | ||
76 | *x -= (block->border_right + margin); | 153 | *x -= (block->border_right + margin); |
77 | block_width += block->border_right + margin; | 154 | block_width += block->border_right + margin; |
78 | } | 155 | } |
79 | 156 | ||
80 | // Add separator | 157 | int sep_width, sep_height; |
81 | if (!edge) { | 158 | if (!edge) { |
82 | if (config->sep_symbol) { | 159 | if (config->sep_symbol) { |
83 | get_text_size(window->cairo, window->font, &sep_width, &height, | 160 | get_text_size(cairo, config->font, &sep_width, &sep_height, |
84 | window->scale, false, "%s", config->sep_symbol); | 161 | output->scale, false, "%s", config->sep_symbol); |
162 | uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; | ||
163 | uint32_t _ideal_surface_height = _ideal_height / output->scale; | ||
164 | if (surface_height < _ideal_surface_height) { | ||
165 | return _ideal_surface_height; | ||
166 | } | ||
85 | if (sep_width > block->separator_block_width) { | 167 | if (sep_width > block->separator_block_width) { |
86 | block->separator_block_width = sep_width + margin * 2; | 168 | block->separator_block_width = sep_width + margin * 2; |
87 | } | 169 | } |
88 | } | 170 | } |
89 | |||
90 | *x -= block->separator_block_width; | 171 | *x -= block->separator_block_width; |
91 | } else { | 172 | } else { |
92 | *x -= margin; | 173 | *x -= margin; |
93 | } | 174 | } |
94 | 175 | ||
95 | double pos = *x; | 176 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); |
96 | 177 | hotspot->x = *x; | |
97 | block->x = (int)pos; | 178 | hotspot->y = 0; |
98 | block->width = (int)block_width; | 179 | hotspot->width = width; |
180 | hotspot->height = height; | ||
181 | hotspot->callback = block_hotspot_callback; | ||
182 | hotspot->destroy = NULL; | ||
183 | hotspot->data = block; | ||
184 | wl_list_insert(&output->hotspots, &hotspot->link); | ||
99 | 185 | ||
100 | // render background | 186 | double pos = *x; |
101 | if (block->background != 0x0) { | 187 | if (block->background) { |
102 | cairo_set_source_u32(window->cairo, block->background); | 188 | cairo_set_source_u32(cairo, block->background); |
103 | cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, (window->height * window->scale) - 2); | 189 | cairo_rectangle(cairo, pos - 0.5 * output->scale, |
104 | cairo_fill(window->cairo); | 190 | output->scale, block_width, height); |
191 | cairo_fill(cairo); | ||
105 | } | 192 | } |
106 | 193 | ||
107 | // render top border | 194 | if (block->border && block->border_top > 0) { |
108 | if (block->border != 0 && block->border_top > 0) { | 195 | render_sharp_line(cairo, block->border, |
109 | render_sharp_line(window->cairo, block->border, | 196 | pos - 0.5 * output->scale, output->scale, |
110 | pos - 0.5, | 197 | block_width, block->border_top); |
111 | 1, | ||
112 | block_width, | ||
113 | block->border_top); | ||
114 | } | 198 | } |
115 | 199 | if (block->border && block->border_bottom > 0) { | |
116 | // render bottom border | 200 | render_sharp_line(cairo, block->border, |
117 | if (block->border != 0 && block->border_bottom > 0) { | 201 | pos - 0.5 * output->scale, |
118 | render_sharp_line(window->cairo, block->border, | 202 | height - output->scale - block->border_bottom, |
119 | pos - 0.5, | 203 | block_width, block->border_bottom); |
120 | (window->height * window->scale) - 1 - block->border_bottom, | ||
121 | block_width, | ||
122 | block->border_bottom); | ||
123 | } | 204 | } |
124 | |||
125 | // render left border | ||
126 | if (block->border != 0 && block->border_left > 0) { | 205 | if (block->border != 0 && block->border_left > 0) { |
127 | render_sharp_line(window->cairo, block->border, | 206 | render_sharp_line(cairo, block->border, |
128 | pos - 0.5, | 207 | pos - 0.5 * output->scale, output->scale, |
129 | 1, | 208 | block->border_left, height); |
130 | block->border_left, | ||
131 | (window->height * window->scale) - 2); | ||
132 | |||
133 | pos += block->border_left + margin; | 209 | pos += block->border_left + margin; |
134 | } | 210 | } |
135 | 211 | ||
136 | // render text | ||
137 | double offset = 0; | 212 | double offset = 0; |
138 | |||
139 | if (strncmp(block->align, "left", 5) == 0) { | 213 | if (strncmp(block->align, "left", 5) == 0) { |
140 | offset = pos; | 214 | offset = pos; |
141 | } else if (strncmp(block->align, "right", 5) == 0) { | 215 | } else if (strncmp(block->align, "right", 5) == 0) { |
142 | offset = pos + width - textwidth; | 216 | offset = pos + width - text_width; |
143 | } else if (strncmp(block->align, "center", 6) == 0) { | 217 | } else if (strncmp(block->align, "center", 6) == 0) { |
144 | offset = pos + (width - textwidth) / 2; | 218 | offset = pos + (width - text_width) / 2; |
145 | } | 219 | } |
146 | 220 | cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0); | |
147 | cairo_move_to(window->cairo, offset, margin); | 221 | uint32_t color = block->color ? *block->color : config->colors.statusline; |
148 | cairo_set_source_u32(window->cairo, block->color); | 222 | cairo_set_source_u32(cairo, color); |
149 | pango_printf(window->cairo, window->font, window->scale, | 223 | pango_printf(cairo, config->font, output->scale, |
150 | block->markup, "%s", block->full_text); | 224 | block->markup, "%s", block->full_text); |
151 | |||
152 | pos += width; | 225 | pos += width; |
153 | 226 | ||
154 | // render right border | 227 | if (block->border && block->border_right > 0) { |
155 | if (block->border != 0 && block->border_right > 0) { | ||
156 | pos += margin; | 228 | pos += margin; |
157 | 229 | render_sharp_line(cairo, block->border, | |
158 | render_sharp_line(window->cairo, block->border, | 230 | pos - 0.5 * output->scale, output->scale, |
159 | pos - 0.5, | 231 | block->border_right, height); |
160 | 1, | ||
161 | block->border_right, | ||
162 | (window->height * window->scale) - 2); | ||
163 | |||
164 | pos += block->border_right; | 232 | pos += block->border_right; |
165 | } | 233 | } |
166 | 234 | ||
167 | // render separator | ||
168 | if (!edge && block->separator) { | 235 | if (!edge && block->separator) { |
169 | if (is_focused) { | 236 | if (focused) { |
170 | cairo_set_source_u32(window->cairo, config->colors.focused_separator); | 237 | cairo_set_source_u32(cairo, config->colors.focused_separator); |
171 | } else { | 238 | } else { |
172 | cairo_set_source_u32(window->cairo, config->colors.separator); | 239 | cairo_set_source_u32(cairo, config->colors.separator); |
173 | } | 240 | } |
174 | if (config->sep_symbol) { | 241 | if (config->sep_symbol) { |
175 | offset = pos + (block->separator_block_width - sep_width) / 2; | 242 | offset = pos + (block->separator_block_width - sep_width) / 2; |
176 | cairo_move_to(window->cairo, offset, margin); | 243 | cairo_move_to(cairo, offset, height / 2.0 - sep_height / 2.0); |
177 | pango_printf(window->cairo, window->font, window->scale, | 244 | pango_printf(cairo, config->font, output->scale, false, |
178 | false, "%s", config->sep_symbol); | 245 | "%s", config->sep_symbol); |
179 | } else { | 246 | } else { |
180 | cairo_set_line_width(window->cairo, 1); | 247 | cairo_set_line_width(cairo, 1); |
181 | cairo_move_to(window->cairo, pos + block->separator_block_width/2, | 248 | cairo_move_to(cairo, |
182 | margin); | 249 | pos + block->separator_block_width / 2, margin); |
183 | cairo_line_to(window->cairo, pos + block->separator_block_width/2, | 250 | cairo_line_to(cairo, |
184 | (window->height * window->scale) - margin); | 251 | pos + block->separator_block_width / 2, height - margin); |
185 | cairo_stroke(window->cairo); | 252 | cairo_stroke(cairo); |
186 | } | 253 | } |
187 | } | 254 | } |
255 | return surface_height; | ||
256 | } | ||
188 | 257 | ||
258 | static uint32_t render_status_line_i3bar(cairo_t *cairo, | ||
259 | struct swaybar_config *config, struct swaybar_output *output, | ||
260 | struct status_line *status, bool focused, | ||
261 | double *x, uint32_t surface_height) { | ||
262 | uint32_t max_height = 0; | ||
263 | bool edge = true; | ||
264 | struct i3bar_block *block; | ||
265 | wl_list_for_each(block, &status->blocks, link) { | ||
266 | uint32_t h = render_status_block(cairo, config, output, | ||
267 | block, x, surface_height, focused, edge); | ||
268 | max_height = h > max_height ? h : max_height; | ||
269 | edge = false; | ||
270 | } | ||
271 | return max_height; | ||
189 | } | 272 | } |
190 | 273 | ||
191 | static const char *strip_workspace_name(bool strip_num, const char *ws_name) { | 274 | static uint32_t render_status_line(cairo_t *cairo, |
192 | bool strip = false; | 275 | struct swaybar_config *config, struct swaybar_output *output, |
193 | int i; | 276 | struct status_line *status, bool focused, |
194 | 277 | double *x, uint32_t surface_height) { | |
195 | if (strip_num) { | 278 | switch (status->protocol) { |
196 | int len = strlen(ws_name); | 279 | case PROTOCOL_ERROR: |
197 | for (i = 0; i < len; ++i) { | 280 | return render_status_line_error(cairo, output, config, |
198 | if (!('0' <= ws_name[i] && ws_name[i] <= '9')) { | 281 | status->text, x, surface_height); |
199 | if (':' == ws_name[i] && i < len-1 && i > 0) { | 282 | case PROTOCOL_TEXT: |
200 | strip = true; | 283 | return render_status_line_text(cairo, output, config, |
201 | ++i; | 284 | status->text, focused, x, surface_height); |
202 | } | 285 | case PROTOCOL_I3BAR: |
203 | break; | 286 | return render_status_line_i3bar(cairo, config, output, |
204 | } | 287 | status, focused, x, surface_height); |
205 | } | 288 | case PROTOCOL_UNDEF: |
289 | return 0; | ||
206 | } | 290 | } |
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static uint32_t render_binding_mode_indicator(cairo_t *cairo, | ||
295 | struct swaybar_output *output, struct swaybar_config *config, | ||
296 | const char *mode, double x, uint32_t surface_height) { | ||
297 | uint32_t height = surface_height * output->scale; | ||
207 | 298 | ||
208 | if (strip) { | 299 | int text_width, text_height; |
209 | return ws_name + i; | 300 | get_text_size(cairo, config->font, &text_width, &text_height, |
301 | output->scale, true, "%s", mode); | ||
302 | |||
303 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | ||
304 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; | ||
305 | int border_width = BORDER_WIDTH * output->scale; | ||
306 | |||
307 | uint32_t ideal_height = text_height + ws_vertical_padding * 2 | ||
308 | + border_width * 2; | ||
309 | uint32_t ideal_surface_height = ideal_height / output->scale; | ||
310 | if (surface_height < ideal_surface_height) { | ||
311 | return ideal_surface_height; | ||
210 | } | 312 | } |
313 | uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; | ||
314 | |||
315 | cairo_set_source_u32(cairo, config->colors.binding_mode.background); | ||
316 | cairo_rectangle(cairo, x, 0, width, height); | ||
317 | cairo_fill(cairo); | ||
318 | |||
319 | cairo_set_source_u32(cairo, config->colors.binding_mode.border); | ||
320 | cairo_rectangle(cairo, x, 0, width, border_width); | ||
321 | cairo_fill(cairo); | ||
322 | cairo_rectangle(cairo, x, 0, border_width, height); | ||
323 | cairo_fill(cairo); | ||
324 | cairo_rectangle(cairo, x + width - border_width, 0, border_width, height); | ||
325 | cairo_fill(cairo); | ||
326 | cairo_rectangle(cairo, x, height - border_width, width, border_width); | ||
327 | cairo_fill(cairo); | ||
328 | |||
329 | double text_y = height / 2.0 - text_height / 2.0; | ||
330 | cairo_set_source_u32(cairo, config->colors.binding_mode.text); | ||
331 | cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); | ||
332 | pango_printf(cairo, config->font, output->scale, true, "%s", mode); | ||
333 | return surface_height; | ||
334 | } | ||
211 | 335 | ||
336 | static const char *strip_workspace_number(const char *ws_name) { | ||
337 | size_t len = strlen(ws_name); | ||
338 | for (size_t i = 0; i < len; ++i) { | ||
339 | if (ws_name[i] < '0' || ws_name[i] > '9') { | ||
340 | if (':' == ws_name[i] && i < len - 1 && i > 0) { | ||
341 | return ws_name + i + 1; | ||
342 | } | ||
343 | return ws_name; | ||
344 | } | ||
345 | } | ||
212 | return ws_name; | 346 | return ws_name; |
213 | } | 347 | } |
214 | 348 | ||
215 | void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) { | 349 | static void workspace_hotspot_callback(struct swaybar_output *output, |
216 | const char *stripped_name = strip_workspace_name(swaybar.config->strip_workspace_numbers, workspace_name); | 350 | int x, int y, uint32_t button, void *data) { |
217 | 351 | ipc_send_workspace_command(output->bar, (const char *)data); | |
218 | get_text_size(window->cairo, window->font, width, height, | ||
219 | window->scale, true, "%s", stripped_name); | ||
220 | *width += 2 * ws_horizontal_padding; | ||
221 | *height += 2 * ws_vertical_padding; | ||
222 | } | 352 | } |
223 | 353 | ||
224 | static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) { | 354 | static uint32_t render_workspace_button(cairo_t *cairo, |
225 | const char *stripped_name = strip_workspace_name(config->strip_workspace_numbers, ws->name); | 355 | struct swaybar_output *output, struct swaybar_config *config, |
356 | struct swaybar_workspace *ws, double *x, uint32_t surface_height) { | ||
357 | const char *name = ws->name; | ||
358 | if (config->strip_workspace_numbers) { | ||
359 | name = strip_workspace_number(ws->name); | ||
360 | } | ||
226 | 361 | ||
227 | struct box_colors box_colors; | 362 | struct box_colors box_colors; |
228 | if (ws->urgent) { | 363 | if (ws->urgent) { |
@@ -235,133 +370,154 @@ static void render_workspace_button(struct window *window, struct config *config | |||
235 | box_colors = config->colors.inactive_workspace; | 370 | box_colors = config->colors.inactive_workspace; |
236 | } | 371 | } |
237 | 372 | ||
238 | int width, height; | 373 | uint32_t height = surface_height * output->scale; |
239 | workspace_button_size(window, stripped_name, &width, &height); | ||
240 | 374 | ||
241 | // background | 375 | int text_width, text_height; |
242 | cairo_set_source_u32(window->cairo, box_colors.background); | 376 | get_text_size(cairo, config->font, &text_width, &text_height, |
243 | cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); | 377 | output->scale, true, "%s", name); |
244 | cairo_fill(window->cairo); | 378 | |
245 | 379 | int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; | |
246 | // border | 380 | int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; |
247 | cairo_set_source_u32(window->cairo, box_colors.border); | 381 | int border_width = BORDER_WIDTH * output->scale; |
248 | cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); | 382 | |
249 | cairo_stroke(window->cairo); | 383 | uint32_t ideal_height = ws_vertical_padding * 2 + text_height |
250 | 384 | + border_width * 2; | |
251 | // text | 385 | uint32_t ideal_surface_height = ideal_height / output->scale; |
252 | cairo_set_source_u32(window->cairo, box_colors.text); | 386 | if (surface_height < ideal_surface_height) { |
253 | cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin); | 387 | return ideal_surface_height; |
254 | pango_printf(window->cairo, window->font, window->scale, | 388 | } |
255 | true, "%s", stripped_name); | ||
256 | |||
257 | *x += width + ws_spacing; | ||
258 | } | ||
259 | 389 | ||
260 | static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) { | 390 | uint32_t width = ws_horizontal_padding * 2 + text_width + border_width * 2; |
261 | int width, height; | 391 | |
262 | get_text_size(window->cairo, window->font, &width, &height, | 392 | cairo_set_source_u32(cairo, box_colors.background); |
263 | window->scale, false, "%s", config->mode); | 393 | cairo_rectangle(cairo, *x, 0, width, height); |
264 | 394 | cairo_fill(cairo); | |
265 | // background | 395 | |
266 | cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); | 396 | cairo_set_source_u32(cairo, box_colors.border); |
267 | cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, | 397 | cairo_rectangle(cairo, *x, 0, width, border_width); |
268 | height + ws_vertical_padding * 2); | 398 | cairo_fill(cairo); |
269 | cairo_fill(window->cairo); | 399 | cairo_rectangle(cairo, *x, 0, border_width, height); |
270 | 400 | cairo_fill(cairo); | |
271 | // border | 401 | cairo_rectangle(cairo, *x + width - border_width, 0, border_width, height); |
272 | cairo_set_source_u32(window->cairo, config->colors.binding_mode.border); | 402 | cairo_fill(cairo); |
273 | cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, | 403 | cairo_rectangle(cairo, *x, height - border_width, width, border_width); |
274 | height + ws_vertical_padding * 2); | 404 | cairo_fill(cairo); |
275 | cairo_stroke(window->cairo); | 405 | |
276 | 406 | double text_y = height / 2.0 - text_height / 2.0; | |
277 | // text | 407 | cairo_set_source_u32(cairo, box_colors.text); |
278 | cairo_set_source_u32(window->cairo, config->colors.binding_mode.text); | 408 | cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); |
279 | cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin); | 409 | pango_printf(cairo, config->font, output->scale, true, "%s", name); |
280 | pango_printf(window->cairo, window->font, window->scale, | 410 | |
281 | false, "%s", config->mode); | 411 | struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); |
412 | hotspot->x = *x; | ||
413 | hotspot->y = 0; | ||
414 | hotspot->width = width; | ||
415 | hotspot->height = height; | ||
416 | hotspot->callback = workspace_hotspot_callback; | ||
417 | hotspot->destroy = free; | ||
418 | hotspot->data = strdup(ws->name); | ||
419 | wl_list_insert(&output->hotspots, &hotspot->link); | ||
420 | |||
421 | *x += width; | ||
422 | return surface_height; | ||
282 | } | 423 | } |
283 | 424 | ||
284 | void render(struct output *output, struct config *config, struct status_line *line) { | 425 | static uint32_t render_to_cairo(cairo_t *cairo, |
285 | int i; | 426 | struct swaybar *bar, struct swaybar_output *output) { |
286 | 427 | struct swaybar_config *config = bar->config; | |
287 | struct window *window = output->window; | ||
288 | cairo_t *cairo = window->cairo; | ||
289 | bool is_focused = output->focused; | ||
290 | |||
291 | // Clear | ||
292 | cairo_save(cairo); | ||
293 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | ||
294 | cairo_paint(cairo); | ||
295 | cairo_restore(cairo); | ||
296 | |||
297 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | 428 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); |
298 | 429 | if (output->focused) { | |
299 | // Background | ||
300 | if (is_focused) { | ||
301 | cairo_set_source_u32(cairo, config->colors.focused_background); | 430 | cairo_set_source_u32(cairo, config->colors.focused_background); |
302 | } else { | 431 | } else { |
303 | cairo_set_source_u32(cairo, config->colors.background); | 432 | cairo_set_source_u32(cairo, config->colors.background); |
304 | } | 433 | } |
305 | cairo_paint(cairo); | 434 | cairo_paint(cairo); |
306 | 435 | ||
307 | #ifdef ENABLE_TRAY | 436 | uint32_t max_height = 0; |
308 | uint32_t tray_width = tray_render(output, config); | 437 | /* |
309 | #else | 438 | * Each render_* function takes the actual height of the bar, and returns |
310 | const uint32_t tray_width = window->width * window->scale; | 439 | * the ideal height. If the actual height is too short, the render function |
311 | #endif | 440 | * can do whatever it wants - the buffer won't be committed. If the actual |
312 | 441 | * height is too tall, the render function should adapt its drawing to | |
313 | // Command output | 442 | * utilize the available space. |
314 | if (is_focused) { | 443 | */ |
315 | cairo_set_source_u32(cairo, config->colors.focused_statusline); | 444 | double x = output->width * output->scale; |
316 | } else { | 445 | if (bar->status) { |
317 | cairo_set_source_u32(cairo, config->colors.statusline); | 446 | uint32_t h = render_status_line(cairo, config, output, |
318 | } | 447 | bar->status, output->focused, &x, output->height); |
319 | 448 | max_height = h > max_height ? h : max_height; | |
320 | int width, height; | 449 | } |
321 | 450 | x = 0; | |
322 | if (line->protocol == TEXT) { | 451 | if (config->workspace_buttons) { |
323 | get_text_size(window->cairo, window->font, &width, &height, | 452 | struct swaybar_workspace *ws; |
324 | window->scale, config->pango_markup, "%s", line->text_line); | 453 | wl_list_for_each_reverse(ws, &output->workspaces, link) { |
325 | cairo_move_to(cairo, tray_width - margin - width, margin); | 454 | uint32_t h = render_workspace_button(cairo, |
326 | pango_printf(window->cairo, window->font, window->scale, | 455 | output, config, ws, &x, output->height); |
327 | config->pango_markup, "%s", line->text_line); | 456 | max_height = h > max_height ? h : max_height; |
328 | } else if (line->protocol == I3BAR && line->block_line) { | ||
329 | double pos = tray_width - 0.5; | ||
330 | bool edge = true; | ||
331 | for (i = line->block_line->length - 1; i >= 0; --i) { | ||
332 | struct status_block *block = line->block_line->items[i]; | ||
333 | if (block->full_text && block->full_text[0]) { | ||
334 | render_block(window, config, block, &pos, edge, is_focused); | ||
335 | edge = false; | ||
336 | } | ||
337 | } | 457 | } |
338 | } | 458 | } |
459 | if (config->binding_mode_indicator && config->mode) { | ||
460 | uint32_t h = render_binding_mode_indicator(cairo, | ||
461 | output, config, config->mode, x, output->height); | ||
462 | max_height = h > max_height ? h : max_height; | ||
463 | } | ||
339 | 464 | ||
340 | cairo_set_line_width(cairo, 1.0); | 465 | return max_height > output->height ? max_height : output->height; |
341 | double x = 0.5; | 466 | } |
342 | 467 | ||
343 | // Workspaces | 468 | void render_frame(struct swaybar *bar, struct swaybar_output *output) { |
344 | if (config->workspace_buttons) { | 469 | struct swaybar_hotspot *hotspot, *tmp; |
345 | for (i = 0; i < output->workspaces->length; ++i) { | 470 | wl_list_for_each_safe(hotspot, tmp, &output->hotspots, link) { |
346 | struct workspace *ws = output->workspaces->items[i]; | 471 | if (hotspot->destroy) { |
347 | render_workspace_button(window, config, ws, &x); | 472 | hotspot->destroy(hotspot->data); |
348 | } | 473 | } |
474 | wl_list_remove(&hotspot->link); | ||
475 | free(hotspot); | ||
349 | } | 476 | } |
350 | 477 | ||
351 | // binding mode indicator | 478 | cairo_surface_t *recorder = cairo_recording_surface_create( |
352 | if (config->mode && config->binding_mode_indicator) { | 479 | CAIRO_CONTENT_COLOR_ALPHA, NULL); |
353 | render_binding_mode_indicator(window, config, x); | 480 | cairo_t *cairo = cairo_create(recorder); |
481 | cairo_save(cairo); | ||
482 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | ||
483 | cairo_paint(cairo); | ||
484 | cairo_restore(cairo); | ||
485 | uint32_t height = render_to_cairo(cairo, bar, output); | ||
486 | if (bar->config->height >= 0 && height < (uint32_t)bar->config->height) { | ||
487 | height = bar->config->height; | ||
354 | } | 488 | } |
355 | } | 489 | if (height != output->height) { |
356 | 490 | // Reconfigure surface | |
357 | void set_window_height(struct window *window, int height) { | 491 | zwlr_layer_surface_v1_set_size(output->layer_surface, 0, height); |
358 | int text_width, text_height; | 492 | zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, height); |
359 | get_text_size(window->cairo, window->font, | 493 | // TODO: this could infinite loop if the compositor assigns us a |
360 | &text_width, &text_height, window->scale, false, | 494 | // different height than what we asked for |
361 | "Test string for measuring purposes"); | 495 | wl_surface_commit(output->surface); |
362 | if (height > 0) { | 496 | wl_display_roundtrip(bar->display); |
363 | margin = (height - text_height) / 2; | 497 | } else { |
364 | ws_vertical_padding = margin - 1.5; | 498 | // Replay recording into shm and send it off |
365 | } | 499 | output->current_buffer = get_next_buffer(bar->shm, |
366 | window->height = (text_height + margin * 2) / window->scale; | 500 | output->buffers, |
501 | output->width * output->scale, | ||
502 | output->height * output->scale); | ||
503 | cairo_t *shm = output->current_buffer->cairo; | ||
504 | |||
505 | cairo_save(shm); | ||
506 | cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); | ||
507 | cairo_paint(shm); | ||
508 | cairo_restore(shm); | ||
509 | |||
510 | cairo_set_source_surface(shm, recorder, 0.0, 0.0); | ||
511 | cairo_paint(shm); | ||
512 | |||
513 | wl_surface_set_buffer_scale(output->surface, output->scale); | ||
514 | wl_surface_attach(output->surface, | ||
515 | output->current_buffer->buffer, 0, 0); | ||
516 | wl_surface_damage(output->surface, 0, 0, | ||
517 | output->width, output->height); | ||
518 | wl_surface_commit(output->surface); | ||
519 | wl_display_roundtrip(bar->display); | ||
520 | } | ||
521 | cairo_surface_destroy(recorder); | ||
522 | cairo_destroy(cairo); | ||
367 | } | 523 | } |