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