aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/render.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/render.c')
-rw-r--r--swaybar/render.c388
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 */
19static int margin = 3;
20static int ws_horizontal_padding = 5;
21static double ws_vertical_padding = 1.5;
22static 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 */
30static 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
56static 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 13static 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
191static 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
215void 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
224static 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
260static 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
284void 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 34void 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
357void 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}