aboutsummaryrefslogtreecommitdiffstats
path: root/swaybar/render.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybar/render.c')
-rw-r--r--swaybar/render.c353
1 files changed, 209 insertions, 144 deletions
diff --git a/swaybar/render.c b/swaybar/render.c
index df066622..92b73f4c 100644
--- a/swaybar/render.c
+++ b/swaybar/render.c
@@ -5,7 +5,7 @@
5#include <stdlib.h> 5#include <stdlib.h>
6#include <stdint.h> 6#include <stdint.h>
7#include <string.h> 7#include <string.h>
8#include "cairo.h" 8#include "cairo_util.h"
9#include "pango.h" 9#include "pango.h"
10#include "pool-buffer.h" 10#include "pool-buffer.h"
11#include "swaybar/bar.h" 11#include "swaybar/bar.h"
@@ -14,6 +14,7 @@
14#include "swaybar/ipc.h" 14#include "swaybar/ipc.h"
15#include "swaybar/render.h" 15#include "swaybar/render.h"
16#include "swaybar/status_line.h" 16#include "swaybar/status_line.h"
17#include "log.h"
17#if HAVE_TRAY 18#if HAVE_TRAY
18#include "swaybar/tray/tray.h" 19#include "swaybar/tray/tray.h"
19#endif 20#endif
@@ -23,28 +24,51 @@ static const int WS_HORIZONTAL_PADDING = 5;
23static const double WS_VERTICAL_PADDING = 1.5; 24static const double WS_VERTICAL_PADDING = 1.5;
24static const double BORDER_WIDTH = 1; 25static const double BORDER_WIDTH = 1;
25 26
26static uint32_t render_status_line_error(cairo_t *cairo, 27struct render_context {
27 struct swaybar_output *output, double *x) { 28 cairo_t *cairo;
29 struct swaybar_output *output;
30 cairo_font_options_t *textaa_sharp;
31 cairo_font_options_t *textaa_safe;
32 uint32_t background_color;
33};
34
35static void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) {
36 uint32_t salpha = fontcolor & 0xFF;
37 uint32_t balpha = ctx->background_color & 0xFF;
38
39 // Subpixel antialiasing requires blend be done in cairo, not compositor
40 cairo_font_options_t *fo = salpha == balpha ?
41 ctx->textaa_sharp : ctx->textaa_safe;
42 cairo_set_font_options(ctx->cairo, fo);
43
44 // Color emojis, being semitransparent bitmaps, are leaky with 'SOURCE'
45 cairo_operator_t op = salpha == 0xFF ?
46 CAIRO_OPERATOR_OVER : CAIRO_OPERATOR_SOURCE;
47 cairo_set_operator(ctx->cairo, op);
48}
49
50static uint32_t render_status_line_error(struct render_context *ctx, double *x) {
51 struct swaybar_output *output = ctx->output;
28 const char *error = output->bar->status->text; 52 const char *error = output->bar->status->text;
29 if (!error) { 53 if (!error) {
30 return 0; 54 return 0;
31 } 55 }
32 56
33 uint32_t height = output->height * output->scale; 57 uint32_t height = output->height;
34 58
59 cairo_t *cairo = ctx->cairo;
35 cairo_set_source_u32(cairo, 0xFF0000FF); 60 cairo_set_source_u32(cairo, 0xFF0000FF);
36 61
37 int margin = 3 * output->scale; 62 int margin = 3;
38 double ws_vertical_padding = 63 double ws_vertical_padding = output->bar->config->status_padding;
39 output->bar->config->status_padding * output->scale;
40 64
41 char *font = output->bar->config->font; 65 char *font = output->bar->config->font;
42 int text_width, text_height; 66 int text_width, text_height;
43 get_text_size(cairo, font, &text_width, &text_height, NULL, 67 get_text_size(cairo, font, &text_width, &text_height, NULL,
44 output->scale, false, "%s", error); 68 1, false, "%s", error);
45 69
46 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 70 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
47 uint32_t ideal_surface_height = ideal_height / output->scale; 71 uint32_t ideal_surface_height = ideal_height;
48 if (!output->bar->config->height && 72 if (!output->bar->config->height &&
49 output->height < ideal_surface_height) { 73 output->height < ideal_surface_height) {
50 return ideal_surface_height; 74 return ideal_surface_height;
@@ -53,42 +77,45 @@ static uint32_t render_status_line_error(cairo_t *cairo,
53 77
54 double text_y = height / 2.0 - text_height / 2.0; 78 double text_y = height / 2.0 - text_height / 2.0;
55 cairo_move_to(cairo, *x, (int)floor(text_y)); 79 cairo_move_to(cairo, *x, (int)floor(text_y));
56 pango_printf(cairo, font, output->scale, false, "%s", error); 80 choose_text_aa_mode(ctx, 0xFF0000FF);
81 render_text(cairo, font, 1, false, "%s", error);
57 *x -= margin; 82 *x -= margin;
58 return output->height; 83 return output->height;
59} 84}
60 85
61static uint32_t render_status_line_text(cairo_t *cairo, 86static uint32_t render_status_line_text(struct render_context *ctx, double *x) {
62 struct swaybar_output *output, double *x) { 87 struct swaybar_output *output = ctx->output;
63 const char *text = output->bar->status->text; 88 const char *text = output->bar->status->text;
64 if (!text) { 89 if (!text) {
65 return 0; 90 return 0;
66 } 91 }
67 92
93 cairo_t *cairo = ctx->cairo;
68 struct swaybar_config *config = output->bar->config; 94 struct swaybar_config *config = output->bar->config;
69 cairo_set_source_u32(cairo, output->focused ? 95 uint32_t fontcolor = output->focused ?
70 config->colors.focused_statusline : config->colors.statusline); 96 config->colors.focused_statusline : config->colors.statusline;
97 cairo_set_source_u32(cairo, fontcolor);
71 98
72 int text_width, text_height; 99 int text_width, text_height;
73 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 100 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
74 output->scale, config->pango_markup, "%s", text); 101 1, config->pango_markup, "%s", text);
75 102
76 double ws_vertical_padding = config->status_padding * output->scale; 103 double ws_vertical_padding = config->status_padding;
77 int margin = 3 * output->scale; 104 int margin = 3;
78 105
79 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 106 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
80 uint32_t ideal_surface_height = ideal_height / output->scale; 107 uint32_t ideal_surface_height = ideal_height;
81 if (!output->bar->config->height && 108 if (!output->bar->config->height &&
82 output->height < ideal_surface_height) { 109 output->height < ideal_surface_height) {
83 return ideal_surface_height; 110 return ideal_surface_height;
84 } 111 }
85 112
86 *x -= text_width + margin; 113 *x -= text_width + margin;
87 uint32_t height = output->height * output->scale; 114 uint32_t height = output->height;
88 double text_y = height / 2.0 - text_height / 2.0; 115 double text_y = height / 2.0 - text_height / 2.0;
89 cairo_move_to(cairo, *x, (int)floor(text_y)); 116 cairo_move_to(cairo, *x, (int)floor(text_y));
90 pango_printf(cairo, config->font, output->scale, 117 choose_text_aa_mode(ctx, fontcolor);
91 config->pango_markup, "%s", text); 118 render_text(cairo, config->font, 1, config->pango_markup, "%s", text);
92 *x -= margin; 119 *x -= margin;
93 return output->height; 120 return output->height;
94} 121}
@@ -96,6 +123,7 @@ static uint32_t render_status_line_text(cairo_t *cairo,
96static void render_sharp_rectangle(cairo_t *cairo, uint32_t color, 123static void render_sharp_rectangle(cairo_t *cairo, uint32_t color,
97 double x, double y, double width, double height) { 124 double x, double y, double width, double height) {
98 cairo_save(cairo); 125 cairo_save(cairo);
126 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
99 cairo_set_source_u32(cairo, color); 127 cairo_set_source_u32(cairo, color);
100 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 128 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
101 cairo_rectangle(cairo, x, y, width, height); 129 cairo_rectangle(cairo, x, y, width, height);
@@ -109,6 +137,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
109 render_sharp_rectangle(cairo, color, x, y, width, height); 137 render_sharp_rectangle(cairo, color, x, y, width, height);
110 } else { 138 } else {
111 cairo_save(cairo); 139 cairo_save(cairo);
140 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
112 cairo_set_source_u32(cairo, color); 141 cairo_set_source_u32(cairo, color);
113 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE); 142 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_NONE);
114 if (width == 1) { 143 if (width == 1) {
@@ -135,10 +164,10 @@ static enum hotspot_event_handling block_hotspot_callback(
135 struct i3bar_block *block = data; 164 struct i3bar_block *block = data;
136 struct status_line *status = output->bar->status; 165 struct status_line *status = output->bar->status;
137 return i3bar_block_send_click(status, block, x, y, 166 return i3bar_block_send_click(status, block, x, y,
138 x - (double)hotspot->x / output->scale, 167 x - (double)hotspot->x,
139 y - (double)hotspot->y / output->scale, 168 y - (double)hotspot->y,
140 (double)hotspot->width / output->scale, 169 (double)hotspot->width,
141 (double)hotspot->height / output->scale, 170 (double)hotspot->height,
142 output->scale, button); 171 output->scale, button);
143} 172}
144 173
@@ -146,9 +175,8 @@ static void i3bar_block_unref_callback(void *data) {
146 i3bar_block_unref(data); 175 i3bar_block_unref(data);
147} 176}
148 177
149static uint32_t render_status_block(cairo_t *cairo, 178static uint32_t render_status_block(struct render_context *ctx,
150 struct swaybar_output *output, struct i3bar_block *block, double *x, 179 struct i3bar_block *block, double *x, bool edge, bool use_short_text) {
151 bool edge, bool use_short_text) {
152 if (!block->full_text || !*block->full_text) { 180 if (!block->full_text || !*block->full_text) {
153 return 0; 181 return 0;
154 } 182 }
@@ -158,20 +186,21 @@ static uint32_t render_status_block(cairo_t *cairo,
158 text = block->short_text; 186 text = block->short_text;
159 } 187 }
160 188
189 cairo_t *cairo = ctx->cairo;
190 struct swaybar_output *output = ctx->output;
161 struct swaybar_config *config = output->bar->config; 191 struct swaybar_config *config = output->bar->config;
162
163 int text_width, text_height; 192 int text_width, text_height;
164 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 193 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1,
165 output->scale, block->markup, "%s", text); 194 block->markup, "%s", text);
166 195
167 int margin = 3 * output->scale; 196 int margin = 3;
168 double ws_vertical_padding = config->status_padding * output->scale; 197 double ws_vertical_padding = config->status_padding;
169 198
170 int width = text_width; 199 int width = text_width;
171 if (block->min_width_str) { 200 if (block->min_width_str) {
172 int w; 201 int w;
173 get_text_size(cairo, config->font, &w, NULL, NULL, 202 get_text_size(cairo, config->font, &w, NULL, NULL, 1, block->markup,
174 output->scale, block->markup, "%s", block->min_width_str); 203 "%s", block->min_width_str);
175 block->min_width = w; 204 block->min_width = w;
176 } 205 }
177 if (width < block->min_width) { 206 if (width < block->min_width) {
@@ -180,20 +209,20 @@ static uint32_t render_status_block(cairo_t *cairo,
180 209
181 double block_width = width; 210 double block_width = width;
182 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 211 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
183 uint32_t ideal_surface_height = ideal_height / output->scale; 212 uint32_t ideal_surface_height = ideal_height;
184 if (!output->bar->config->height && 213 if (!output->bar->config->height &&
185 output->height < ideal_surface_height) { 214 output->height < ideal_surface_height) {
186 return ideal_surface_height; 215 return ideal_surface_height;
187 } 216 }
188 217
189 *x -= width; 218 *x -= width;
190 if ((block->border || block->urgent) && block->border_left > 0) { 219 if ((block->border_set || block->urgent) && block->border_left > 0) {
191 *x -= (block->border_left * output->scale + margin); 220 *x -= (block->border_left + margin);
192 block_width += block->border_left * output->scale + margin; 221 block_width += block->border_left + margin;
193 } 222 }
194 if ((block->border || block->urgent) && block->border_right > 0) { 223 if ((block->border_set || block->urgent) && block->border_right > 0) {
195 *x -= (block->border_right * output->scale + margin); 224 *x -= (block->border_right + margin);
196 block_width += block->border_right * output->scale + margin; 225 block_width += block->border_right + margin;
197 } 226 }
198 227
199 int sep_width, sep_height; 228 int sep_width, sep_height;
@@ -201,9 +230,9 @@ static uint32_t render_status_block(cairo_t *cairo,
201 if (!edge) { 230 if (!edge) {
202 if (config->sep_symbol) { 231 if (config->sep_symbol) {
203 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 232 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL,
204 output->scale, false, "%s", config->sep_symbol); 233 1, false, "%s", config->sep_symbol);
205 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 234 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
206 uint32_t _ideal_surface_height = _ideal_height / output->scale; 235 uint32_t _ideal_surface_height = _ideal_height;
207 if (!output->bar->config->height && 236 if (!output->bar->config->height &&
208 output->height < _ideal_surface_height) { 237 output->height < _ideal_surface_height) {
209 return _ideal_surface_height; 238 return _ideal_surface_height;
@@ -214,10 +243,10 @@ static uint32_t render_status_block(cairo_t *cairo,
214 } 243 }
215 *x -= sep_block_width; 244 *x -= sep_block_width;
216 } else if (config->status_edge_padding) { 245 } else if (config->status_edge_padding) {
217 *x -= config->status_edge_padding * output->scale; 246 *x -= config->status_edge_padding;
218 } 247 }
219 248
220 uint32_t height = output->height * output->scale; 249 uint32_t height = output->height;
221 if (output->bar->status->click_events) { 250 if (output->bar->status->click_events) {
222 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 251 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
223 hotspot->x = *x; 252 hotspot->x = *x;
@@ -240,23 +269,26 @@ static uint32_t render_status_block(cairo_t *cairo,
240 if (bg_color) { 269 if (bg_color) {
241 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, 270 render_sharp_rectangle(cairo, bg_color, x_pos, y_pos,
242 block_width, render_height); 271 block_width, render_height);
272 ctx->background_color = bg_color;
243 } 273 }
244 274
245 uint32_t border_color = block->urgent 275 uint32_t border_color = block->urgent
246 ? config->colors.urgent_workspace.border : block->border; 276 ? config->colors.urgent_workspace.border : block->border;
247 if (border_color && block->border_top > 0) { 277 if (block->border_set || block->urgent) {
248 render_sharp_line(cairo, border_color, x_pos, y_pos, 278 if (block->border_top > 0) {
249 block_width, block->border_top * output->scale); 279 render_sharp_line(cairo, border_color, x_pos, y_pos,
250 } 280 block_width, block->border_top);
251 if (border_color && block->border_bottom > 0) { 281 }
252 render_sharp_line(cairo, border_color, x_pos, 282 if (block->border_bottom > 0) {
253 y_pos + render_height - block->border_bottom * output->scale, 283 render_sharp_line(cairo, border_color, x_pos,
254 block_width, block->border_bottom * output->scale); 284 y_pos + render_height - block->border_bottom,
255 } 285 block_width, block->border_bottom);
256 if (border_color && block->border_left > 0) { 286 }
257 render_sharp_line(cairo, border_color, x_pos, y_pos, 287 if (block->border_left > 0) {
258 block->border_left * output->scale, render_height); 288 render_sharp_line(cairo, border_color, x_pos, y_pos,
259 x_pos += block->border_left * output->scale + margin; 289 block->border_left, render_height);
290 }
291 x_pos += block->border_left + margin;
260 } 292 }
261 293
262 double offset = 0; 294 double offset = 0;
@@ -274,30 +306,35 @@ static uint32_t render_status_block(cairo_t *cairo,
274 color = block->color_set ? block->color : color; 306 color = block->color_set ? block->color : color;
275 color = block->urgent ? config->colors.urgent_workspace.text : color; 307 color = block->urgent ? config->colors.urgent_workspace.text : color;
276 cairo_set_source_u32(cairo, color); 308 cairo_set_source_u32(cairo, color);
277 pango_printf(cairo, config->font, output->scale, 309 choose_text_aa_mode(ctx, color);
278 block->markup, "%s", text); 310 render_text(cairo, config->font, 1, block->markup, "%s", text);
279 x_pos += width; 311 x_pos += width;
280 312
281 if (block->border && block->border_right > 0) { 313 if (block->border_set || block->urgent) {
282 x_pos += margin; 314 x_pos += margin;
283 render_sharp_line(cairo, border_color, x_pos, y_pos, 315 if (block->border_right > 0) {
284 block->border_right * output->scale, render_height); 316 render_sharp_line(cairo, border_color, x_pos, y_pos,
285 x_pos += block->border_right * output->scale; 317 block->border_right, render_height);
318 }
319 x_pos += block->border_right;
286 } 320 }
287 321
288 if (!edge && block->separator) { 322 if (!edge && block->separator) {
289 if (output->focused) { 323 if (output->focused) {
290 cairo_set_source_u32(cairo, config->colors.focused_separator); 324 color = config->colors.focused_separator;
291 } else { 325 } else {
292 cairo_set_source_u32(cairo, config->colors.separator); 326 color = config->colors.separator;
293 } 327 }
328 cairo_set_source_u32(cairo, color);
294 if (config->sep_symbol) { 329 if (config->sep_symbol) {
295 offset = x_pos + (sep_block_width - sep_width) / 2; 330 offset = x_pos + (sep_block_width - sep_width) / 2;
296 double sep_y = height / 2.0 - sep_height / 2.0; 331 double sep_y = height / 2.0 - sep_height / 2.0;
297 cairo_move_to(cairo, offset, (int)floor(sep_y)); 332 cairo_move_to(cairo, offset, (int)floor(sep_y));
298 pango_printf(cairo, config->font, output->scale, false, 333 choose_text_aa_mode(ctx, color);
334 render_text(cairo, config->font, 1, false,
299 "%s", config->sep_symbol); 335 "%s", config->sep_symbol);
300 } else { 336 } else {
337 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
301 cairo_set_line_width(cairo, 1); 338 cairo_set_line_width(cairo, 1);
302 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin); 339 cairo_move_to(cairo, x_pos + sep_block_width / 2, margin);
303 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin); 340 cairo_line_to(cairo, x_pos + sep_block_width / 2, height - margin);
@@ -317,18 +354,18 @@ static void predict_status_block_pos(cairo_t *cairo,
317 struct swaybar_config *config = output->bar->config; 354 struct swaybar_config *config = output->bar->config;
318 355
319 int text_width, text_height; 356 int text_width, text_height;
320 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 357 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1,
321 output->scale, block->markup, "%s", block->full_text); 358 block->markup, "%s", block->full_text);
322 359
323 int margin = 3 * output->scale; 360 int margin = 3;
324 double ws_vertical_padding = config->status_padding * output->scale; 361 double ws_vertical_padding = config->status_padding;
325 362
326 int width = text_width; 363 int width = text_width;
327 364
328 if (block->min_width_str) { 365 if (block->min_width_str) {
329 int w; 366 int w;
330 get_text_size(cairo, config->font, &w, NULL, NULL, 367 get_text_size(cairo, config->font, &w, NULL, NULL,
331 output->scale, block->markup, "%s", block->min_width_str); 368 1, block->markup, "%s", block->min_width_str);
332 block->min_width = w; 369 block->min_width = w;
333 } 370 }
334 if (width < block->min_width) { 371 if (width < block->min_width) {
@@ -336,18 +373,18 @@ static void predict_status_block_pos(cairo_t *cairo,
336 } 373 }
337 374
338 uint32_t ideal_height = text_height + ws_vertical_padding * 2; 375 uint32_t ideal_height = text_height + ws_vertical_padding * 2;
339 uint32_t ideal_surface_height = ideal_height / output->scale; 376 uint32_t ideal_surface_height = ideal_height;
340 if (!output->bar->config->height && 377 if (!output->bar->config->height &&
341 output->height < ideal_surface_height) { 378 output->height < ideal_surface_height) {
342 return; 379 return;
343 } 380 }
344 381
345 *x -= width; 382 *x -= width;
346 if ((block->border || block->urgent) && block->border_left > 0) { 383 if ((block->border_set || block->urgent) && block->border_left > 0) {
347 *x -= (block->border_left * output->scale + margin); 384 *x -= (block->border_left + margin);
348 } 385 }
349 if ((block->border || block->urgent) && block->border_right > 0) { 386 if ((block->border_set || block->urgent) && block->border_right > 0) {
350 *x -= (block->border_right * output->scale + margin); 387 *x -= (block->border_right + margin);
351 } 388 }
352 389
353 int sep_width, sep_height; 390 int sep_width, sep_height;
@@ -355,9 +392,9 @@ static void predict_status_block_pos(cairo_t *cairo,
355 if (!edge) { 392 if (!edge) {
356 if (config->sep_symbol) { 393 if (config->sep_symbol) {
357 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, 394 get_text_size(cairo, config->font, &sep_width, &sep_height, NULL,
358 output->scale, false, "%s", config->sep_symbol); 395 1, false, "%s", config->sep_symbol);
359 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; 396 uint32_t _ideal_height = sep_height + ws_vertical_padding * 2;
360 uint32_t _ideal_surface_height = _ideal_height / output->scale; 397 uint32_t _ideal_surface_height = _ideal_height;
361 if (!output->bar->config->height && 398 if (!output->bar->config->height &&
362 output->height < _ideal_surface_height) { 399 output->height < _ideal_surface_height) {
363 return; 400 return;
@@ -368,13 +405,13 @@ static void predict_status_block_pos(cairo_t *cairo,
368 } 405 }
369 *x -= sep_block_width; 406 *x -= sep_block_width;
370 } else if (config->status_edge_padding) { 407 } else if (config->status_edge_padding) {
371 *x -= config->status_edge_padding * output->scale; 408 *x -= config->status_edge_padding;
372 } 409 }
373} 410}
374 411
375static double predict_status_line_pos(cairo_t *cairo, 412static double predict_status_line_pos(cairo_t *cairo,
376 struct swaybar_output *output, double x) { 413 struct swaybar_output *output, double x) {
377 bool edge = x == output->width * output->scale; 414 bool edge = x == output->width;
378 struct i3bar_block *block; 415 struct i3bar_block *block;
379 wl_list_for_each(block, &output->bar->status->blocks, link) { 416 wl_list_for_each(block, &output->bar->status->blocks, link) {
380 predict_status_block_pos(cairo, output, block, &x, edge); 417 predict_status_block_pos(cairo, output, block, &x, edge);
@@ -389,24 +426,24 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo,
389 struct swaybar_config *config = output->bar->config; 426 struct swaybar_config *config = output->bar->config;
390 427
391 int text_width, text_height; 428 int text_width, text_height;
392 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 429 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1,
393 output->scale, config->pango_markup, "%s", ws->label); 430 config->pango_markup, "%s", ws->label);
394 431
395 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 432 int ws_vertical_padding = WS_VERTICAL_PADDING;
396 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 433 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
397 int border_width = BORDER_WIDTH * output->scale; 434 int border_width = BORDER_WIDTH;
398 435
399 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 436 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
400 + border_width * 2; 437 + border_width * 2;
401 uint32_t ideal_surface_height = ideal_height / output->scale; 438 uint32_t ideal_surface_height = ideal_height;
402 if (!output->bar->config->height && 439 if (!output->bar->config->height &&
403 output->height < ideal_surface_height) { 440 output->height < ideal_surface_height) {
404 return 0; 441 return 0;
405 } 442 }
406 443
407 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 444 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
408 if (width < config->workspace_min_width * output->scale) { 445 if (width < config->workspace_min_width) {
409 width = config->workspace_min_width * output->scale; 446 width = config->workspace_min_width;
410 } 447 }
411 return width; 448 return width;
412} 449}
@@ -438,38 +475,39 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo,
438 475
439 int text_width, text_height; 476 int text_width, text_height;
440 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 477 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
441 output->scale, output->bar->mode_pango_markup, 478 1, output->bar->mode_pango_markup,
442 "%s", mode); 479 "%s", mode);
443 480
444 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 481 int ws_vertical_padding = WS_VERTICAL_PADDING;
445 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 482 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
446 int border_width = BORDER_WIDTH * output->scale; 483 int border_width = BORDER_WIDTH;
447 484
448 uint32_t ideal_height = text_height + ws_vertical_padding * 2 485 uint32_t ideal_height = text_height + ws_vertical_padding * 2
449 + border_width * 2; 486 + border_width * 2;
450 uint32_t ideal_surface_height = ideal_height / output->scale; 487 uint32_t ideal_surface_height = ideal_height;
451 if (!output->bar->config->height && 488 if (!output->bar->config->height &&
452 output->height < ideal_surface_height) { 489 output->height < ideal_surface_height) {
453 return 0; 490 return 0;
454 } 491 }
455 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 492 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
456 if (width < config->workspace_min_width * output->scale) { 493 if (width < config->workspace_min_width) {
457 width = config->workspace_min_width * output->scale; 494 width = config->workspace_min_width;
458 } 495 }
459 return width; 496 return width;
460} 497}
461 498
462static uint32_t render_status_line_i3bar(cairo_t *cairo, 499static uint32_t render_status_line_i3bar(struct render_context *ctx, double *x) {
463 struct swaybar_output *output, double *x) { 500 struct swaybar_output *output = ctx->output;
464 uint32_t max_height = 0; 501 uint32_t max_height = 0;
465 bool edge = *x == output->width * output->scale; 502 bool edge = *x == output->width;
466 struct i3bar_block *block; 503 struct i3bar_block *block;
467 bool use_short_text = false; 504 bool use_short_text = false;
468 505
506 cairo_t *cairo = ctx->cairo;
469 double reserved_width = 507 double reserved_width =
470 predict_workspace_buttons_length(cairo, output) + 508 predict_workspace_buttons_length(cairo, output) +
471 predict_binding_mode_indicator_length(cairo, output) + 509 predict_binding_mode_indicator_length(cairo, output) +
472 3 * output->scale; // require a bit of space for margin 510 3; // require a bit of space for margin
473 511
474 double predicted_full_pos = 512 double predicted_full_pos =
475 predict_status_line_pos(cairo, output, *x); 513 predict_status_line_pos(cairo, output, *x);
@@ -479,7 +517,7 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
479 } 517 }
480 518
481 wl_list_for_each(block, &output->bar->status->blocks, link) { 519 wl_list_for_each(block, &output->bar->status->blocks, link) {
482 uint32_t h = render_status_block(cairo, output, block, x, edge, 520 uint32_t h = render_status_block(ctx, block, x, edge,
483 use_short_text); 521 use_short_text);
484 max_height = h > max_height ? h : max_height; 522 max_height = h > max_height ? h : max_height;
485 edge = false; 523 edge = false;
@@ -487,53 +525,56 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo,
487 return max_height; 525 return max_height;
488} 526}
489 527
490static uint32_t render_status_line(cairo_t *cairo, 528static uint32_t render_status_line(struct render_context *ctx, double *x) {
491 struct swaybar_output *output, double *x) { 529 struct status_line *status = ctx->output->bar->status;
492 struct status_line *status = output->bar->status;
493 switch (status->protocol) { 530 switch (status->protocol) {
494 case PROTOCOL_ERROR: 531 case PROTOCOL_ERROR:
495 return render_status_line_error(cairo, output, x); 532 return render_status_line_error(ctx, x);
496 case PROTOCOL_TEXT: 533 case PROTOCOL_TEXT:
497 return render_status_line_text(cairo, output, x); 534 return render_status_line_text(ctx, x);
498 case PROTOCOL_I3BAR: 535 case PROTOCOL_I3BAR:
499 return render_status_line_i3bar(cairo, output, x); 536 return render_status_line_i3bar(ctx, x);
500 case PROTOCOL_UNDEF: 537 case PROTOCOL_UNDEF:
501 return 0; 538 return 0;
502 } 539 }
503 return 0; 540 return 0;
504} 541}
505 542
506static uint32_t render_binding_mode_indicator(cairo_t *cairo, 543static uint32_t render_binding_mode_indicator(struct render_context *ctx,
507 struct swaybar_output *output, double x) { 544 double x) {
545 struct swaybar_output *output = ctx->output;
508 const char *mode = output->bar->mode; 546 const char *mode = output->bar->mode;
509 if (!mode) { 547 if (!mode) {
510 return 0; 548 return 0;
511 } 549 }
512 550
551 cairo_t *cairo = ctx->cairo;
513 struct swaybar_config *config = output->bar->config; 552 struct swaybar_config *config = output->bar->config;
514 int text_width, text_height; 553 int text_width, text_height;
515 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 554 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
516 output->scale, output->bar->mode_pango_markup, 555 1, output->bar->mode_pango_markup,
517 "%s", mode); 556 "%s", mode);
518 557
519 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 558 int ws_vertical_padding = WS_VERTICAL_PADDING;
520 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 559 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
521 int border_width = BORDER_WIDTH * output->scale; 560 int border_width = BORDER_WIDTH;
522 561
523 uint32_t ideal_height = text_height + ws_vertical_padding * 2 562 uint32_t ideal_height = text_height + ws_vertical_padding * 2
524 + border_width * 2; 563 + border_width * 2;
525 uint32_t ideal_surface_height = ideal_height / output->scale; 564 uint32_t ideal_surface_height = ideal_height;
526 if (!output->bar->config->height && 565 if (!output->bar->config->height &&
527 output->height < ideal_surface_height) { 566 output->height < ideal_surface_height) {
528 return ideal_surface_height; 567 return ideal_surface_height;
529 } 568 }
530 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 569 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
531 if (width < config->workspace_min_width * output->scale) { 570 if (width < config->workspace_min_width) {
532 width = config->workspace_min_width * output->scale; 571 width = config->workspace_min_width;
533 } 572 }
534 573
535 uint32_t height = output->height * output->scale; 574 uint32_t height = output->height;
575 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
536 cairo_set_source_u32(cairo, config->colors.binding_mode.background); 576 cairo_set_source_u32(cairo, config->colors.binding_mode.background);
577 ctx->background_color = config->colors.binding_mode.background;
537 cairo_rectangle(cairo, x, 0, width, height); 578 cairo_rectangle(cairo, x, 0, width, height);
538 cairo_fill(cairo); 579 cairo_fill(cairo);
539 580
@@ -550,8 +591,9 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo,
550 double text_y = height / 2.0 - text_height / 2.0; 591 double text_y = height / 2.0 - text_height / 2.0;
551 cairo_set_source_u32(cairo, config->colors.binding_mode.text); 592 cairo_set_source_u32(cairo, config->colors.binding_mode.text);
552 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); 593 cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y));
553 pango_printf(cairo, config->font, output->scale, 594 choose_text_aa_mode(ctx, config->colors.binding_mode.text);
554 output->bar->mode_pango_markup, "%s", mode); 595 render_text(cairo, config->font, 1, output->bar->mode_pango_markup,
596 "%s", mode);
555 return output->height; 597 return output->height;
556} 598}
557 599
@@ -565,9 +607,9 @@ static enum hotspot_event_handling workspace_hotspot_callback(
565 return HOTSPOT_IGNORE; 607 return HOTSPOT_IGNORE;
566} 608}
567 609
568static uint32_t render_workspace_button(cairo_t *cairo, 610static uint32_t render_workspace_button(struct render_context *ctx,
569 struct swaybar_output *output,
570 struct swaybar_workspace *ws, double *x) { 611 struct swaybar_workspace *ws, double *x) {
612 struct swaybar_output *output = ctx->output;
571 struct swaybar_config *config = output->bar->config; 613 struct swaybar_config *config = output->bar->config;
572 struct box_colors box_colors; 614 struct box_colors box_colors;
573 if (ws->urgent) { 615 if (ws->urgent) {
@@ -580,30 +622,33 @@ static uint32_t render_workspace_button(cairo_t *cairo,
580 box_colors = config->colors.inactive_workspace; 622 box_colors = config->colors.inactive_workspace;
581 } 623 }
582 624
583 uint32_t height = output->height * output->scale; 625 uint32_t height = output->height;
584 626
627 cairo_t *cairo = ctx->cairo;
585 int text_width, text_height; 628 int text_width, text_height;
586 get_text_size(cairo, config->font, &text_width, &text_height, NULL, 629 get_text_size(cairo, config->font, &text_width, &text_height, NULL,
587 output->scale, config->pango_markup, "%s", ws->label); 630 1, config->pango_markup, "%s", ws->label);
588 631
589 int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; 632 int ws_vertical_padding = WS_VERTICAL_PADDING;
590 int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; 633 int ws_horizontal_padding = WS_HORIZONTAL_PADDING;
591 int border_width = BORDER_WIDTH * output->scale; 634 int border_width = BORDER_WIDTH;
592 635
593 uint32_t ideal_height = ws_vertical_padding * 2 + text_height 636 uint32_t ideal_height = ws_vertical_padding * 2 + text_height
594 + border_width * 2; 637 + border_width * 2;
595 uint32_t ideal_surface_height = ideal_height / output->scale; 638 uint32_t ideal_surface_height = ideal_height;
596 if (!output->bar->config->height && 639 if (!output->bar->config->height &&
597 output->height < ideal_surface_height) { 640 output->height < ideal_surface_height) {
598 return ideal_surface_height; 641 return ideal_surface_height;
599 } 642 }
600 643
601 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; 644 uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2;
602 if (width < config->workspace_min_width * output->scale) { 645 if (width < config->workspace_min_width) {
603 width = config->workspace_min_width * output->scale; 646 width = config->workspace_min_width;
604 } 647 }
605 648
649 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
606 cairo_set_source_u32(cairo, box_colors.background); 650 cairo_set_source_u32(cairo, box_colors.background);
651 ctx->background_color = box_colors.background;
607 cairo_rectangle(cairo, *x, 0, width, height); 652 cairo_rectangle(cairo, *x, 0, width, height);
608 cairo_fill(cairo); 653 cairo_fill(cairo);
609 654
@@ -620,7 +665,8 @@ static uint32_t render_workspace_button(cairo_t *cairo,
620 double text_y = height / 2.0 - text_height / 2.0; 665 double text_y = height / 2.0 - text_height / 2.0;
621 cairo_set_source_u32(cairo, box_colors.text); 666 cairo_set_source_u32(cairo, box_colors.text);
622 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); 667 cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y));
623 pango_printf(cairo, config->font, output->scale, config->pango_markup, 668 choose_text_aa_mode(ctx, box_colors.text);
669 render_text(cairo, config->font, 1, config->pango_markup,
624 "%s", ws->label); 670 "%s", ws->label);
625 671
626 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); 672 struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot));
@@ -637,20 +683,24 @@ static uint32_t render_workspace_button(cairo_t *cairo,
637 return output->height; 683 return output->height;
638} 684}
639 685
640static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) { 686static uint32_t render_to_cairo(struct render_context *ctx) {
687 cairo_t *cairo = ctx->cairo;
688 struct swaybar_output *output = ctx->output;
641 struct swaybar *bar = output->bar; 689 struct swaybar *bar = output->bar;
642 struct swaybar_config *config = bar->config; 690 struct swaybar_config *config = bar->config;
643 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 691 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
644 if (output->focused) { 692 if (output->focused) {
645 cairo_set_source_u32(cairo, config->colors.focused_background); 693 ctx->background_color = config->colors.focused_background;
646 } else { 694 } else {
647 cairo_set_source_u32(cairo, config->colors.background); 695 ctx->background_color = config->colors.background;
648 } 696 }
697
698 cairo_set_source_u32(cairo, ctx->background_color);
649 cairo_paint(cairo); 699 cairo_paint(cairo);
650 700
651 int th; 701 int th;
652 get_text_size(cairo, config->font, NULL, &th, NULL, output->scale, false, ""); 702 get_text_size(cairo, config->font, NULL, &th, NULL, 1, false, "");
653 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4) / output->scale; 703 uint32_t max_height = (th + WS_VERTICAL_PADDING * 4);
654 /* 704 /*
655 * Each render_* function takes the actual height of the bar, and returns 705 * Each render_* function takes the actual height of the bar, and returns
656 * the ideal height. If the actual height is too short, the render function 706 * the ideal height. If the actual height is too short, the render function
@@ -658,7 +708,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
658 * height is too tall, the render function should adapt its drawing to 708 * height is too tall, the render function should adapt its drawing to
659 * utilize the available space. 709 * utilize the available space.
660 */ 710 */
661 double x = output->width * output->scale; 711 double x = output->width;
662#if HAVE_TRAY 712#if HAVE_TRAY
663 if (bar->tray) { 713 if (bar->tray) {
664 uint32_t h = render_tray(cairo, output, &x); 714 uint32_t h = render_tray(cairo, output, &x);
@@ -666,19 +716,19 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaybar_output *output) {
666 } 716 }
667#endif 717#endif
668 if (bar->status) { 718 if (bar->status) {
669 uint32_t h = render_status_line(cairo, output, &x); 719 uint32_t h = render_status_line(ctx, &x);
670 max_height = h > max_height ? h : max_height; 720 max_height = h > max_height ? h : max_height;
671 } 721 }
672 x = 0; 722 x = 0;
673 if (config->workspace_buttons) { 723 if (config->workspace_buttons) {
674 struct swaybar_workspace *ws; 724 struct swaybar_workspace *ws;
675 wl_list_for_each(ws, &output->workspaces, link) { 725 wl_list_for_each(ws, &output->workspaces, link) {
676 uint32_t h = render_workspace_button(cairo, output, ws, &x); 726 uint32_t h = render_workspace_button(ctx, ws, &x);
677 max_height = h > max_height ? h : max_height; 727 max_height = h > max_height ? h : max_height;
678 } 728 }
679 } 729 }
680 if (config->binding_mode_indicator) { 730 if (config->binding_mode_indicator) {
681 uint32_t h = render_binding_mode_indicator(cairo, output, x); 731 uint32_t h = render_binding_mode_indicator(ctx, x);
682 max_height = h > max_height ? h : max_height; 732 max_height = h > max_height ? h : max_height;
683 } 733 }
684 734
@@ -708,26 +758,36 @@ void render_frame(struct swaybar_output *output) {
708 758
709 free_hotspots(&output->hotspots); 759 free_hotspots(&output->hotspots);
710 760
761 struct render_context ctx = { 0 };
762 ctx.output = output;
763
711 cairo_surface_t *recorder = cairo_recording_surface_create( 764 cairo_surface_t *recorder = cairo_recording_surface_create(
712 CAIRO_CONTENT_COLOR_ALPHA, NULL); 765 CAIRO_CONTENT_COLOR_ALPHA, NULL);
713 cairo_t *cairo = cairo_create(recorder); 766 cairo_t *cairo = cairo_create(recorder);
767 cairo_scale(cairo, output->scale, output->scale);
714 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 768 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
769 ctx.cairo = cairo;
770
715 cairo_font_options_t *fo = cairo_font_options_create(); 771 cairo_font_options_t *fo = cairo_font_options_create();
716 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); 772 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
773 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
774 ctx.textaa_safe = fo;
717 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { 775 if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) {
718 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); 776 ctx.textaa_sharp = ctx.textaa_safe;
719 } else { 777 } else {
778 fo = cairo_font_options_create();
779 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
720 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); 780 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
721 cairo_font_options_set_subpixel_order(fo, 781 cairo_font_options_set_subpixel_order(fo,
722 to_cairo_subpixel_order(output->subpixel)); 782 to_cairo_subpixel_order(output->subpixel));
783 ctx.textaa_sharp = fo;
723 } 784 }
724 cairo_set_font_options(cairo, fo); 785
725 cairo_font_options_destroy(fo);
726 cairo_save(cairo); 786 cairo_save(cairo);
727 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); 787 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
728 cairo_paint(cairo); 788 cairo_paint(cairo);
729 cairo_restore(cairo); 789 cairo_restore(cairo);
730 uint32_t height = render_to_cairo(cairo, output); 790 uint32_t height = render_to_cairo(&ctx);
731 int config_height = output->bar->config->height; 791 int config_height = output->bar->config->height;
732 if (config_height > 0) { 792 if (config_height > 0) {
733 height = config_height; 793 height = config_height;
@@ -779,6 +839,11 @@ void render_frame(struct swaybar_output *output) {
779 839
780 wl_surface_commit(output->surface); 840 wl_surface_commit(output->surface);
781 } 841 }
842
843 if (ctx.textaa_sharp != ctx.textaa_safe) {
844 cairo_font_options_destroy(ctx.textaa_sharp);
845 }
846 cairo_font_options_destroy(ctx.textaa_safe);
782 cairo_surface_destroy(recorder); 847 cairo_surface_destroy(recorder);
783 cairo_destroy(cairo); 848 cairo_destroy(cairo);
784} 849}