summaryrefslogtreecommitdiffstats
path: root/swaynag/render.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaynag/render.c')
-rw-r--r--swaynag/render.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/swaynag/render.c b/swaynag/render.c
new file mode 100644
index 00000000..766409e4
--- /dev/null
+++ b/swaynag/render.c
@@ -0,0 +1,308 @@
1#include <stdint.h>
2#include "cairo.h"
3#include "log.h"
4#include "pango.h"
5#include "pool-buffer.h"
6#include "swaynag/swaynag.h"
7#include "swaynag/types.h"
8#include "wlr-layer-shell-unstable-v1-client-protocol.h"
9
10static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) {
11 uint32_t height = swaynag->height * swaynag->scale;
12 height -= swaynag->type->bar_border_thickness * swaynag->scale;
13
14 int text_width, text_height;
15 get_text_size(cairo, swaynag->type->font, &text_width, &text_height,
16 swaynag->scale, true, "%s", swaynag->message);
17
18 int padding = swaynag->type->message_padding * swaynag->scale;
19
20 uint32_t ideal_height = text_height + padding * 2;
21 uint32_t ideal_surface_height = ideal_height / swaynag->scale;
22 if (swaynag->height < ideal_surface_height) {
23 return ideal_surface_height;
24 }
25
26 cairo_set_source_u32(cairo, swaynag->type->text);
27 cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2);
28 pango_printf(cairo, swaynag->type->font, swaynag->scale, false,
29 "%s", swaynag->message);
30
31 return ideal_surface_height;
32}
33
34static void render_details_scroll_button(cairo_t *cairo,
35 struct swaynag *swaynag, struct swaynag_button *button) {
36 int text_width, text_height;
37 get_text_size(cairo, swaynag->type->font, &text_width, &text_height,
38 swaynag->scale, true, "%s", button->text);
39
40 int border = swaynag->type->button_border_thickness * swaynag->scale;
41 int padding = swaynag->type->button_padding * swaynag->scale;
42
43 cairo_set_source_u32(cairo, swaynag->type->border);
44 cairo_rectangle(cairo, button->x, button->y,
45 button->width, button->height);
46 cairo_fill(cairo);
47
48 cairo_set_source_u32(cairo, swaynag->type->button_background);
49 cairo_rectangle(cairo, button->x + border, button->y + border,
50 button->width - (border * 2), button->height - (border * 2));
51 cairo_fill(cairo);
52
53 cairo_set_source_u32(cairo, swaynag->type->text);
54 cairo_move_to(cairo, button->x + border + padding,
55 button->y + border + (button->height - text_height) / 2);
56 pango_printf(cairo, swaynag->type->font, swaynag->scale, true,
57 "%s", button->text);
58}
59
60static int get_detailed_scroll_button_width(cairo_t *cairo,
61 struct swaynag *swaynag) {
62 int up_width, down_width, temp_height;
63 get_text_size(cairo, swaynag->type->font, &up_width, &temp_height,
64 swaynag->scale, true,
65 "%s", swaynag->details.button_up.text);
66 get_text_size(cairo, swaynag->type->font, &down_width, &temp_height,
67 swaynag->scale, true,
68 "%s", swaynag->details.button_down.text);
69
70 int text_width = up_width > down_width ? up_width : down_width;
71 int border = swaynag->type->button_border_thickness * swaynag->scale;
72 int padding = swaynag->type->button_padding * swaynag->scale;
73
74 return text_width + border * 2 + padding * 2;
75}
76
77static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag,
78 uint32_t y) {
79 uint32_t width = swaynag->width * swaynag->scale;
80 uint32_t height = swaynag->height * swaynag->scale;
81 height -= swaynag->type->bar_border_thickness * swaynag->scale;
82
83 int border = swaynag->type->details_border_thickness * swaynag->scale;
84 int padding = swaynag->type->message_padding * swaynag->scale;
85 int decor = padding + border;
86
87 swaynag->details.x = decor;
88 swaynag->details.y = y * swaynag->scale + decor;
89 swaynag->details.width = width - decor * 2;
90
91 PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font,
92 swaynag->details.message, swaynag->scale, false);
93 pango_layout_set_width(layout,
94 (swaynag->details.width - padding * 2) * PANGO_SCALE);
95 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
96 pango_layout_set_single_paragraph_mode(layout, false);
97 pango_cairo_update_layout(cairo, layout);
98 swaynag->details.total_lines = pango_layout_get_line_count(layout);
99
100 PangoLayoutLine *line;
101 line = pango_layout_get_line_readonly(layout, swaynag->details.offset);
102 gint offset = line->start_index;
103 const char *text = pango_layout_get_text(layout);
104 pango_layout_set_text(layout, text + offset, strlen(text) - offset);
105
106 int text_width, text_height;
107 pango_cairo_update_layout(cairo, layout);
108 pango_layout_get_pixel_size(layout, &text_width, &text_height);
109
110 bool show_buttons = swaynag->details.offset > 0;
111 int button_width = get_detailed_scroll_button_width(cairo, swaynag);
112 if (show_buttons) {
113 swaynag->details.width -= button_width;
114 pango_layout_set_width(layout,
115 (swaynag->details.width - padding * 2) * PANGO_SCALE);
116 }
117
118 uint32_t ideal_height;
119 do {
120 ideal_height = swaynag->details.y + text_height + decor + padding * 2;
121 if (ideal_height > SWAYNAG_MAX_HEIGHT) {
122 ideal_height = SWAYNAG_MAX_HEIGHT;
123
124 if (!show_buttons) {
125 show_buttons = true;
126 swaynag->details.width -= button_width;
127 pango_layout_set_width(layout,
128 (swaynag->details.width - padding * 2) * PANGO_SCALE);
129 }
130 }
131
132 swaynag->details.height = ideal_height - swaynag->details.y - decor;
133 pango_layout_set_height(layout,
134 (swaynag->details.height - padding * 2) * PANGO_SCALE);
135 pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
136 pango_cairo_update_layout(cairo, layout);
137 pango_layout_get_pixel_size(layout, &text_width, &text_height);
138 } while (text_height != (swaynag->details.height - padding * 2));
139
140 swaynag->details.visible_lines = pango_layout_get_line_count(layout);
141
142 if (show_buttons) {
143 swaynag->details.button_up.x =
144 swaynag->details.x + swaynag->details.width;
145 swaynag->details.button_up.y = swaynag->details.y;
146 swaynag->details.button_up.width = button_width;
147 swaynag->details.button_up.height = swaynag->details.height / 2;
148 render_details_scroll_button(cairo, swaynag,
149 &swaynag->details.button_up);
150
151 swaynag->details.button_down.x =
152 swaynag->details.x + swaynag->details.width;
153 swaynag->details.button_down.y =
154 swaynag->details.button_up.y + swaynag->details.button_up.height;
155 swaynag->details.button_down.width = button_width;
156 swaynag->details.button_down.height = swaynag->details.height / 2;
157 render_details_scroll_button(cairo, swaynag,
158 &swaynag->details.button_down);
159 }
160
161 cairo_set_source_u32(cairo, swaynag->type->border);
162 cairo_rectangle(cairo, swaynag->details.x, swaynag->details.y,
163 swaynag->details.width, swaynag->details.height);
164 cairo_fill(cairo);
165
166 cairo_move_to(cairo, swaynag->details.x + padding,
167 swaynag->details.y + padding);
168 cairo_set_source_u32(cairo, swaynag->type->text);
169 pango_cairo_show_layout(cairo, layout);
170 g_object_unref(layout);
171
172 return ideal_height / swaynag->scale;
173}
174
175static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag,
176 int button_index, int *x) {
177 uint32_t height = swaynag->height * swaynag->scale;
178 height -= swaynag->type->bar_border_thickness * swaynag->scale;
179 struct swaynag_button *button = swaynag->buttons->items[button_index];
180
181 int text_width, text_height;
182 get_text_size(cairo, swaynag->type->font, &text_width, &text_height,
183 swaynag->scale, true, "%s", button->text);
184
185 int border = swaynag->type->button_border_thickness * swaynag->scale;
186 int padding = swaynag->type->button_padding * swaynag->scale;
187
188 uint32_t ideal_height = text_height + padding * 2 + border * 2;
189 uint32_t ideal_surface_height = ideal_height / swaynag->scale;
190 if (swaynag->height < ideal_surface_height) {
191 return ideal_surface_height;
192 }
193
194 button->x = *x - border - text_width - padding * 2;
195 button->y = (int)(ideal_height - text_height) / 2 - padding;
196 button->width = text_width + padding * 2;
197 button->height = text_height + padding * 2;
198
199 cairo_set_source_u32(cairo, swaynag->type->border);
200 cairo_rectangle(cairo, button->x - border, button->y - border,
201 button->width + border * 2, button->height + border * 2);
202 cairo_fill(cairo);
203
204 cairo_set_source_u32(cairo, swaynag->type->button_background);
205 cairo_rectangle(cairo, button->x, button->y,
206 button->width, button->height);
207 cairo_fill(cairo);
208
209 cairo_set_source_u32(cairo, swaynag->type->text);
210 cairo_move_to(cairo, button->x + padding, button->y + padding);
211 pango_printf(cairo, swaynag->type->font, swaynag->scale, true,
212 "%s", button->text);
213
214 *x = button->x - border;
215
216 return ideal_surface_height;
217}
218
219static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) {
220 uint32_t max_height = 0;
221
222 cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
223 cairo_set_source_u32(cairo, swaynag->type->background);
224 cairo_paint(cairo);
225
226 uint32_t h = render_message(cairo, swaynag);
227 max_height = h > max_height ? h : max_height;
228
229 int x = swaynag->width - swaynag->type->button_margin_right;
230 x *= swaynag->scale;
231 for (int i = 0; i < swaynag->buttons->length; i++) {
232 h = render_button(cairo, swaynag, i, &x);
233 max_height = h > max_height ? h : max_height;
234 x -= swaynag->type->button_gap * swaynag->scale;
235 if (i == 0) {
236 x -= swaynag->type->button_gap_close * swaynag->scale;
237 }
238 }
239
240 if (swaynag->details.visible) {
241 h = render_detailed(cairo, swaynag, max_height);
242 max_height = h > max_height ? h : max_height;
243 }
244
245 int border = swaynag->type->bar_border_thickness * swaynag->scale;
246 if (max_height > swaynag->height) {
247 max_height += border;
248 }
249 cairo_set_source_u32(cairo, swaynag->type->border_bottom);
250 cairo_rectangle(cairo, 0,
251 swaynag->height * swaynag->scale - border,
252 swaynag->width * swaynag->scale,
253 border);
254 cairo_fill(cairo);
255
256 return max_height;
257}
258
259void render_frame(struct swaynag *swaynag) {
260 if (!swaynag->run_display) {
261 return;
262 }
263
264 cairo_surface_t *recorder = cairo_recording_surface_create(
265 CAIRO_CONTENT_COLOR_ALPHA, NULL);
266 cairo_t *cairo = cairo_create(recorder);
267 cairo_save(cairo);
268 cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
269 cairo_paint(cairo);
270 cairo_restore(cairo);
271 uint32_t height = render_to_cairo(cairo, swaynag);
272 if (height != swaynag->height) {
273 zwlr_layer_surface_v1_set_size(swaynag->layer_surface, 0, height);
274 zwlr_layer_surface_v1_set_exclusive_zone(swaynag->layer_surface,
275 height);
276 wl_surface_commit(swaynag->surface);
277 wl_display_roundtrip(swaynag->display);
278 } else {
279 swaynag->current_buffer = get_next_buffer(swaynag->shm,
280 swaynag->buffers,
281 swaynag->width * swaynag->scale,
282 swaynag->height * swaynag->scale);
283 if (!swaynag->current_buffer) {
284 wlr_log(WLR_DEBUG, "Failed to get buffer. Skipping frame.");
285 goto cleanup;
286 }
287
288 cairo_t *shm = swaynag->current_buffer->cairo;
289 cairo_save(shm);
290 cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR);
291 cairo_paint(shm);
292 cairo_restore(shm);
293 cairo_set_source_surface(shm, recorder, 0.0, 0.0);
294 cairo_paint(shm);
295
296 wl_surface_set_buffer_scale(swaynag->surface, swaynag->scale);
297 wl_surface_attach(swaynag->surface,
298 swaynag->current_buffer->buffer, 0, 0);
299 wl_surface_damage(swaynag->surface, 0, 0,
300 swaynag->width, swaynag->height);
301 wl_surface_commit(swaynag->surface);
302 wl_display_roundtrip(swaynag->display);
303 }
304
305cleanup:
306 cairo_surface_destroy(recorder);
307 cairo_destroy(cairo);
308}