aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/render.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/render.c')
-rw-r--r--swaybar/render.c638
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 17static const int WS_HORIZONTAL_PADDING = 5;
15#include "log.h" 18static const double WS_VERTICAL_PADDING = 1.5;
16 19static const double BORDER_WIDTH = 1;
17 20
18/* internal spacing */ 21static uint32_t render_status_line_error(cairo_t *cairo,
19static int margin = 3; 22 struct swaybar_output *output, struct swaybar_config *config,
20static int ws_horizontal_padding = 5; 23 const char *error, double *x, uint32_t surface_height) {
21static double ws_vertical_padding = 1.5; 24 if (!error) {
22static 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;
30static 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
53static 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
87static 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
56static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) { 111static 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; 118static 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
258static 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
191static const char *strip_workspace_name(bool strip_num, const char *ws_name) { 274static 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
294static 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
336static 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
215void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) { 349static 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
224static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) { 354static 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
260static 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
284void render(struct output *output, struct config *config, struct status_line *line) { 425static 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 468void 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
357void 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}