diff options
Diffstat (limited to 'swaybar/render.c')
-rw-r--r-- | swaybar/render.c | 388 |
1 files changed, 42 insertions, 346 deletions
diff --git a/swaybar/render.c b/swaybar/render.c index 6fc09078..2eaa0195 100644 --- a/swaybar/render.c +++ b/swaybar/render.c | |||
@@ -1,367 +1,63 @@ | |||
1 | #include <stdlib.h> | 1 | #include <stdlib.h> |
2 | #include <stdint.h> | 2 | #include <stdint.h> |
3 | #include <string.h> | 3 | #include <string.h> |
4 | 4 | #include <wlr/util/log.h> | |
5 | #include "client/cairo.h" | 5 | #include "cairo.h" |
6 | #include "client/pango.h" | 6 | #include "pango.h" |
7 | #include "client/window.h" | 7 | #include "pool-buffer.h" |
8 | #include "swaybar/bar.h" | ||
8 | #include "swaybar/config.h" | 9 | #include "swaybar/config.h" |
9 | #include "swaybar/status_line.h" | ||
10 | #include "swaybar/render.h" | 10 | #include "swaybar/render.h" |
11 | #ifdef ENABLE_TRAY | 11 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
12 | #include "swaybar/tray/tray.h" | ||
13 | #include "swaybar/tray/sni.h" | ||
14 | #endif | ||
15 | #include "log.h" | ||
16 | |||
17 | |||
18 | /* internal spacing */ | ||
19 | static int margin = 3; | ||
20 | static int ws_horizontal_padding = 5; | ||
21 | static double ws_vertical_padding = 1.5; | ||
22 | static int ws_spacing = 1; | ||
23 | |||
24 | /** | ||
25 | * Renders a sharp line of any width and height. | ||
26 | * | ||
27 | * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 | ||
28 | * if the line has a width/height of one pixel, respectively. | ||
29 | */ | ||
30 | static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) { | ||
31 | cairo_set_source_u32(cairo, color); | ||
32 | |||
33 | if (width > 1 && height > 1) { | ||
34 | cairo_rectangle(cairo, x, y, width, height); | ||
35 | cairo_fill(cairo); | ||
36 | } else { | ||
37 | if (width == 1) { | ||
38 | x += 0.5; | ||
39 | height += y; | ||
40 | width = x; | ||
41 | } | ||
42 | |||
43 | if (height == 1) { | ||
44 | y += 0.5; | ||
45 | width += x; | ||
46 | height = y; | ||
47 | } | ||
48 | |||
49 | cairo_move_to(cairo, x, y); | ||
50 | cairo_set_line_width(cairo, 1.0); | ||
51 | cairo_line_to(cairo, width, height); | ||
52 | cairo_stroke(cairo); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge, bool is_focused) { | ||
57 | int width, height, sep_width; | ||
58 | get_text_size(window->cairo, window->font, &width, &height, | ||
59 | window->scale, block->markup, "%s", block->full_text); | ||
60 | |||
61 | int textwidth = width; | ||
62 | double block_width = width; | ||
63 | |||
64 | if (width < block->min_width) { | ||
65 | width = block->min_width; | ||
66 | } | ||
67 | |||
68 | *x -= width; | ||
69 | |||
70 | if (block->border != 0 && block->border_left > 0) { | ||
71 | *x -= (block->border_left + margin); | ||
72 | block_width += block->border_left + margin; | ||
73 | } | ||
74 | |||
75 | if (block->border != 0 && block->border_right > 0) { | ||
76 | *x -= (block->border_right + margin); | ||
77 | block_width += block->border_right + margin; | ||
78 | } | ||
79 | |||
80 | // Add separator | ||
81 | if (!edge) { | ||
82 | if (config->sep_symbol) { | ||
83 | get_text_size(window->cairo, window->font, &sep_width, &height, | ||
84 | window->scale, false, "%s", config->sep_symbol); | ||
85 | if (sep_width > block->separator_block_width) { | ||
86 | block->separator_block_width = sep_width + margin * 2; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | *x -= block->separator_block_width; | ||
91 | } else { | ||
92 | *x -= margin; | ||
93 | } | ||
94 | |||
95 | double pos = *x; | ||
96 | |||
97 | block->x = (int)pos; | ||
98 | block->width = (int)block_width; | ||
99 | |||
100 | // render background | ||
101 | if (block->background != 0x0) { | ||
102 | cairo_set_source_u32(window->cairo, block->background); | ||
103 | cairo_rectangle(window->cairo, pos - 0.5, 1, block_width, (window->height * window->scale) - 2); | ||
104 | cairo_fill(window->cairo); | ||
105 | } | ||
106 | |||
107 | // render top border | ||
108 | if (block->border != 0 && block->border_top > 0) { | ||
109 | render_sharp_line(window->cairo, block->border, | ||
110 | pos - 0.5, | ||
111 | 1, | ||
112 | block_width, | ||
113 | block->border_top); | ||
114 | } | ||
115 | |||
116 | // render bottom border | ||
117 | if (block->border != 0 && block->border_bottom > 0) { | ||
118 | render_sharp_line(window->cairo, block->border, | ||
119 | pos - 0.5, | ||
120 | (window->height * window->scale) - 1 - block->border_bottom, | ||
121 | block_width, | ||
122 | block->border_bottom); | ||
123 | } | ||
124 | |||
125 | // render left border | ||
126 | if (block->border != 0 && block->border_left > 0) { | ||
127 | render_sharp_line(window->cairo, block->border, | ||
128 | pos - 0.5, | ||
129 | 1, | ||
130 | block->border_left, | ||
131 | (window->height * window->scale) - 2); | ||
132 | |||
133 | pos += block->border_left + margin; | ||
134 | } | ||
135 | |||
136 | // render text | ||
137 | double offset = 0; | ||
138 | |||
139 | if (strncmp(block->align, "left", 5) == 0) { | ||
140 | offset = pos; | ||
141 | } else if (strncmp(block->align, "right", 5) == 0) { | ||
142 | offset = pos + width - textwidth; | ||
143 | } else if (strncmp(block->align, "center", 6) == 0) { | ||
144 | offset = pos + (width - textwidth) / 2; | ||
145 | } | ||
146 | |||
147 | cairo_move_to(window->cairo, offset, margin); | ||
148 | cairo_set_source_u32(window->cairo, block->color); | ||
149 | pango_printf(window->cairo, window->font, window->scale, | ||
150 | block->markup, "%s", block->full_text); | ||
151 | |||
152 | pos += width; | ||
153 | 12 | ||
154 | // render right border | 13 | static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar *bar, |
155 | if (block->border != 0 && block->border_right > 0) { | 14 | struct swaybar_output *output) { |
156 | pos += margin; | 15 | struct swaybar_config *config = bar->config; |
157 | |||
158 | render_sharp_line(window->cairo, block->border, | ||
159 | pos - 0.5, | ||
160 | 1, | ||
161 | block->border_right, | ||
162 | (window->height * window->scale) - 2); | ||
163 | |||
164 | pos += block->border_right; | ||
165 | } | ||
166 | |||
167 | // render separator | ||
168 | if (!edge && block->separator) { | ||
169 | if (is_focused) { | ||
170 | cairo_set_source_u32(window->cairo, config->colors.focused_separator); | ||
171 | } else { | ||
172 | cairo_set_source_u32(window->cairo, config->colors.separator); | ||
173 | } | ||
174 | if (config->sep_symbol) { | ||
175 | offset = pos + (block->separator_block_width - sep_width) / 2; | ||
176 | cairo_move_to(window->cairo, offset, margin); | ||
177 | pango_printf(window->cairo, window->font, window->scale, | ||
178 | false, "%s", config->sep_symbol); | ||
179 | } else { | ||
180 | cairo_set_line_width(window->cairo, 1); | ||
181 | cairo_move_to(window->cairo, pos + block->separator_block_width/2, | ||
182 | margin); | ||
183 | cairo_line_to(window->cairo, pos + block->separator_block_width/2, | ||
184 | (window->height * window->scale) - margin); | ||
185 | cairo_stroke(window->cairo); | ||
186 | } | ||
187 | } | ||
188 | 16 | ||
189 | } | ||
190 | |||
191 | static const char *strip_workspace_name(bool strip_num, const char *ws_name) { | ||
192 | bool strip = false; | ||
193 | int i; | ||
194 | |||
195 | if (strip_num) { | ||
196 | int len = strlen(ws_name); | ||
197 | for (i = 0; i < len; ++i) { | ||
198 | if (!('0' <= ws_name[i] && ws_name[i] <= '9')) { | ||
199 | if (':' == ws_name[i] && i < len-1 && i > 0) { | ||
200 | strip = true; | ||
201 | ++i; | ||
202 | } | ||
203 | break; | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | if (strip) { | ||
209 | return ws_name + i; | ||
210 | } | ||
211 | |||
212 | return ws_name; | ||
213 | } | ||
214 | |||
215 | void workspace_button_size(struct window *window, const char *workspace_name, int *width, int *height) { | ||
216 | const char *stripped_name = strip_workspace_name(swaybar.config->strip_workspace_numbers, workspace_name); | ||
217 | |||
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 | } | ||
223 | |||
224 | static void render_workspace_button(struct window *window, struct config *config, struct workspace *ws, double *x) { | ||
225 | const char *stripped_name = strip_workspace_name(config->strip_workspace_numbers, ws->name); | ||
226 | |||
227 | struct box_colors box_colors; | ||
228 | if (ws->urgent) { | ||
229 | box_colors = config->colors.urgent_workspace; | ||
230 | } else if (ws->focused) { | ||
231 | box_colors = config->colors.focused_workspace; | ||
232 | } else if (ws->visible) { | ||
233 | box_colors = config->colors.active_workspace; | ||
234 | } else { | ||
235 | box_colors = config->colors.inactive_workspace; | ||
236 | } | ||
237 | |||
238 | int width, height; | ||
239 | workspace_button_size(window, stripped_name, &width, &height); | ||
240 | |||
241 | // background | ||
242 | cairo_set_source_u32(window->cairo, box_colors.background); | ||
243 | cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); | ||
244 | cairo_fill(window->cairo); | ||
245 | |||
246 | // border | ||
247 | cairo_set_source_u32(window->cairo, box_colors.border); | ||
248 | cairo_rectangle(window->cairo, *x, 1.5, width - 1, height); | ||
249 | cairo_stroke(window->cairo); | ||
250 | |||
251 | // text | ||
252 | cairo_set_source_u32(window->cairo, box_colors.text); | ||
253 | cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin); | ||
254 | pango_printf(window->cairo, window->font, window->scale, | ||
255 | true, "%s", stripped_name); | ||
256 | |||
257 | *x += width + ws_spacing; | ||
258 | } | ||
259 | |||
260 | static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) { | ||
261 | int width, height; | ||
262 | get_text_size(window->cairo, window->font, &width, &height, | ||
263 | window->scale, false, "%s", config->mode); | ||
264 | |||
265 | // background | ||
266 | cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); | ||
267 | cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, | ||
268 | height + ws_vertical_padding * 2); | ||
269 | cairo_fill(window->cairo); | ||
270 | |||
271 | // border | ||
272 | cairo_set_source_u32(window->cairo, config->colors.binding_mode.border); | ||
273 | cairo_rectangle(window->cairo, pos, 1.5, width + ws_horizontal_padding * 2 - 1, | ||
274 | height + ws_vertical_padding * 2); | ||
275 | cairo_stroke(window->cairo); | ||
276 | |||
277 | // text | ||
278 | cairo_set_source_u32(window->cairo, config->colors.binding_mode.text); | ||
279 | cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin); | ||
280 | pango_printf(window->cairo, window->font, window->scale, | ||
281 | false, "%s", config->mode); | ||
282 | } | ||
283 | |||
284 | void render(struct output *output, struct config *config, struct status_line *line) { | ||
285 | int i; | ||
286 | |||
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); | 17 | cairo_save(cairo); |
293 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | 18 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); |
294 | cairo_paint(cairo); | 19 | cairo_paint(cairo); |
295 | cairo_restore(cairo); | 20 | cairo_restore(cairo); |
296 | 21 | ||
297 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | 22 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); |
298 | 23 | if (output->focused) { | |
299 | // Background | ||
300 | if (is_focused) { | ||
301 | cairo_set_source_u32(cairo, config->colors.focused_background); | 24 | cairo_set_source_u32(cairo, config->colors.focused_background); |
302 | } else { | 25 | } else { |
303 | cairo_set_source_u32(cairo, config->colors.background); | 26 | cairo_set_source_u32(cairo, config->colors.background); |
304 | } | 27 | } |
305 | cairo_paint(cairo); | 28 | cairo_paint(cairo); |
306 | 29 | ||
307 | #ifdef ENABLE_TRAY | 30 | // TODO: use actual height |
308 | uint32_t tray_width = tray_render(output, config); | 31 | return 20; |
309 | #else | 32 | } |
310 | const uint32_t tray_width = window->width * window->scale; | 33 | |
311 | #endif | 34 | void render_frame(struct swaybar *bar, |
312 | 35 | struct swaybar_output *output) { | |
313 | // Command output | 36 | cairo_surface_t *recorder = cairo_recording_surface_create( |
314 | if (is_focused) { | 37 | CAIRO_CONTENT_COLOR_ALPHA, NULL); |
315 | cairo_set_source_u32(cairo, config->colors.focused_statusline); | 38 | cairo_t *cairo = cairo_create(recorder); |
39 | uint32_t height = render_to_cairo(cairo, bar, output); | ||
40 | if (height != output->height) { | ||
41 | // Reconfigure surface | ||
42 | zwlr_layer_surface_v1_set_size( | ||
43 | output->layer_surface, 0, height); | ||
44 | // TODO: this could infinite loop if the compositor assigns us a | ||
45 | // different height than what we asked for | ||
46 | wl_surface_commit(output->surface); | ||
47 | wl_display_roundtrip(bar->display); | ||
316 | } else { | 48 | } else { |
317 | cairo_set_source_u32(cairo, config->colors.statusline); | 49 | // Replay recording into shm and send it off |
318 | } | 50 | output->current_buffer = get_next_buffer(bar->shm, |
319 | 51 | output->buffers, output->width, output->height); | |
320 | int width, height; | 52 | cairo_t *shm = output->current_buffer->cairo; |
321 | 53 | cairo_set_source_surface(shm, recorder, 0.0, 0.0); | |
322 | if (line->protocol == TEXT) { | 54 | cairo_paint(shm); |
323 | get_text_size(window->cairo, window->font, &width, &height, | 55 | wl_surface_attach(output->surface, |
324 | window->scale, config->pango_markup, "%s", line->text_line); | 56 | output->current_buffer->buffer, 0, 0); |
325 | cairo_move_to(cairo, tray_width - margin - width, margin); | 57 | wl_surface_damage(output->surface, 0, 0, output->width, output->height); |
326 | pango_printf(window->cairo, window->font, window->scale, | 58 | wl_surface_commit(output->surface); |
327 | config->pango_markup, "%s", line->text_line); | 59 | wl_display_roundtrip(bar->display); |
328 | } else if (line->protocol == I3BAR && line->block_line) { | 60 | } |
329 | double pos = tray_width - 0.5; | 61 | cairo_surface_destroy(recorder); |
330 | bool edge = true; | 62 | cairo_destroy(cairo); |
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 | } | ||
338 | } | ||
339 | |||
340 | cairo_set_line_width(cairo, 1.0); | ||
341 | double x = 0.5; | ||
342 | |||
343 | // Workspaces | ||
344 | if (config->workspace_buttons) { | ||
345 | for (i = 0; i < output->workspaces->length; ++i) { | ||
346 | struct workspace *ws = output->workspaces->items[i]; | ||
347 | render_workspace_button(window, config, ws, &x); | ||
348 | } | ||
349 | } | ||
350 | |||
351 | // binding mode indicator | ||
352 | if (config->mode && config->binding_mode_indicator) { | ||
353 | render_binding_mode_indicator(window, config, x); | ||
354 | } | ||
355 | } | ||
356 | |||
357 | void set_window_height(struct window *window, int height) { | ||
358 | int text_width, text_height; | ||
359 | get_text_size(window->cairo, window->font, | ||
360 | &text_width, &text_height, window->scale, false, | ||
361 | "Test string for measuring purposes"); | ||
362 | if (height > 0) { | ||
363 | margin = (height - text_height) / 2; | ||
364 | ws_vertical_padding = margin - 1.5; | ||
365 | } | ||
366 | window->height = (text_height + margin * 2) / window->scale; | ||
367 | } | 63 | } |