diff options
Diffstat (limited to 'sway/desktop/render.c')
-rw-r--r-- | sway/desktop/render.c | 919 |
1 files changed, 919 insertions, 0 deletions
diff --git a/sway/desktop/render.c b/sway/desktop/render.c new file mode 100644 index 00000000..7da54594 --- /dev/null +++ b/sway/desktop/render.c | |||
@@ -0,0 +1,919 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | ||
2 | #include <assert.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <strings.h> | ||
5 | #include <time.h> | ||
6 | #include <wayland-server.h> | ||
7 | #include <wlr/render/wlr_renderer.h> | ||
8 | #include <wlr/types/wlr_box.h> | ||
9 | #include <wlr/types/wlr_buffer.h> | ||
10 | #include <wlr/types/wlr_matrix.h> | ||
11 | #include <wlr/types/wlr_output_damage.h> | ||
12 | #include <wlr/types/wlr_output_layout.h> | ||
13 | #include <wlr/types/wlr_output.h> | ||
14 | #include <wlr/types/wlr_surface.h> | ||
15 | #include <wlr/util/region.h> | ||
16 | #include "log.h" | ||
17 | #include "sway/config.h" | ||
18 | #include "sway/debug.h" | ||
19 | #include "sway/input/input-manager.h" | ||
20 | #include "sway/input/seat.h" | ||
21 | #include "sway/layers.h" | ||
22 | #include "sway/output.h" | ||
23 | #include "sway/server.h" | ||
24 | #include "sway/tree/arrange.h" | ||
25 | #include "sway/tree/container.h" | ||
26 | #include "sway/tree/layout.h" | ||
27 | #include "sway/tree/view.h" | ||
28 | #include "sway/tree/workspace.h" | ||
29 | |||
30 | struct render_data { | ||
31 | struct root_geometry root_geo; | ||
32 | struct sway_output *output; | ||
33 | pixman_region32_t *damage; | ||
34 | struct sway_view *view; | ||
35 | float alpha; | ||
36 | }; | ||
37 | |||
38 | static void scale_box(struct wlr_box *box, float scale) { | ||
39 | box->x *= scale; | ||
40 | box->y *= scale; | ||
41 | box->width *= scale; | ||
42 | box->height *= scale; | ||
43 | } | ||
44 | |||
45 | static void scissor_output(struct wlr_output *wlr_output, | ||
46 | pixman_box32_t *rect) { | ||
47 | struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); | ||
48 | assert(renderer); | ||
49 | |||
50 | struct wlr_box box = { | ||
51 | .x = rect->x1, | ||
52 | .y = rect->y1, | ||
53 | .width = rect->x2 - rect->x1, | ||
54 | .height = rect->y2 - rect->y1, | ||
55 | }; | ||
56 | |||
57 | int ow, oh; | ||
58 | wlr_output_transformed_resolution(wlr_output, &ow, &oh); | ||
59 | |||
60 | enum wl_output_transform transform = | ||
61 | wlr_output_transform_invert(wlr_output->transform); | ||
62 | wlr_box_transform(&box, transform, ow, oh, &box); | ||
63 | |||
64 | wlr_renderer_scissor(renderer, &box); | ||
65 | } | ||
66 | |||
67 | static void render_texture(struct wlr_output *wlr_output, | ||
68 | pixman_region32_t *output_damage, struct wlr_texture *texture, | ||
69 | const struct wlr_box *box, const float matrix[static 9], float alpha) { | ||
70 | struct wlr_renderer *renderer = | ||
71 | wlr_backend_get_renderer(wlr_output->backend); | ||
72 | |||
73 | pixman_region32_t damage; | ||
74 | pixman_region32_init(&damage); | ||
75 | pixman_region32_union_rect(&damage, &damage, box->x, box->y, | ||
76 | box->width, box->height); | ||
77 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
78 | bool damaged = pixman_region32_not_empty(&damage); | ||
79 | if (!damaged) { | ||
80 | goto damage_finish; | ||
81 | } | ||
82 | |||
83 | int nrects; | ||
84 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
85 | for (int i = 0; i < nrects; ++i) { | ||
86 | scissor_output(wlr_output, &rects[i]); | ||
87 | wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); | ||
88 | } | ||
89 | |||
90 | damage_finish: | ||
91 | pixman_region32_fini(&damage); | ||
92 | } | ||
93 | |||
94 | static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, | ||
95 | void *_data) { | ||
96 | struct render_data *data = _data; | ||
97 | struct wlr_output *wlr_output = data->output->wlr_output; | ||
98 | float rotation = data->root_geo.rotation; | ||
99 | pixman_region32_t *output_damage = data->damage; | ||
100 | float alpha = data->alpha; | ||
101 | |||
102 | struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||
103 | if (!texture) { | ||
104 | return; | ||
105 | } | ||
106 | |||
107 | struct wlr_box box; | ||
108 | bool intersects = output_get_surface_box(&data->root_geo, data->output, | ||
109 | surface, sx, sy, &box); | ||
110 | if (!intersects) { | ||
111 | return; | ||
112 | } | ||
113 | |||
114 | scale_box(&box, wlr_output->scale); | ||
115 | |||
116 | float matrix[9]; | ||
117 | enum wl_output_transform transform = | ||
118 | wlr_output_transform_invert(surface->current.transform); | ||
119 | wlr_matrix_project_box(matrix, &box, transform, rotation, | ||
120 | wlr_output->transform_matrix); | ||
121 | |||
122 | render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); | ||
123 | } | ||
124 | |||
125 | static void render_layer(struct sway_output *output, | ||
126 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | ||
127 | struct render_data data = { | ||
128 | .output = output, | ||
129 | .damage = damage, | ||
130 | .alpha = 1.0f, | ||
131 | }; | ||
132 | output_layer_for_each_surface(layer_surfaces, &data.root_geo, | ||
133 | render_surface_iterator, &data); | ||
134 | } | ||
135 | |||
136 | static void render_unmanaged(struct sway_output *output, | ||
137 | pixman_region32_t *damage, struct wl_list *unmanaged) { | ||
138 | struct render_data data = { | ||
139 | .output = output, | ||
140 | .damage = damage, | ||
141 | .alpha = 1.0f, | ||
142 | }; | ||
143 | output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, | ||
144 | render_surface_iterator, &data); | ||
145 | } | ||
146 | |||
147 | static void render_drag_icons(struct sway_output *output, | ||
148 | pixman_region32_t *damage, struct wl_list *drag_icons) { | ||
149 | struct render_data data = { | ||
150 | .output = output, | ||
151 | .damage = damage, | ||
152 | .alpha = 1.0f, | ||
153 | }; | ||
154 | output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo, | ||
155 | render_surface_iterator, &data); | ||
156 | } | ||
157 | |||
158 | static void render_rect(struct wlr_output *wlr_output, | ||
159 | pixman_region32_t *output_damage, const struct wlr_box *_box, | ||
160 | float color[static 4]) { | ||
161 | struct wlr_renderer *renderer = | ||
162 | wlr_backend_get_renderer(wlr_output->backend); | ||
163 | |||
164 | struct wlr_box box; | ||
165 | memcpy(&box, _box, sizeof(struct wlr_box)); | ||
166 | box.x -= wlr_output->lx * wlr_output->scale; | ||
167 | box.y -= wlr_output->ly * wlr_output->scale; | ||
168 | |||
169 | pixman_region32_t damage; | ||
170 | pixman_region32_init(&damage); | ||
171 | pixman_region32_union_rect(&damage, &damage, box.x, box.y, | ||
172 | box.width, box.height); | ||
173 | pixman_region32_intersect(&damage, &damage, output_damage); | ||
174 | bool damaged = pixman_region32_not_empty(&damage); | ||
175 | if (!damaged) { | ||
176 | goto damage_finish; | ||
177 | } | ||
178 | |||
179 | int nrects; | ||
180 | pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); | ||
181 | for (int i = 0; i < nrects; ++i) { | ||
182 | scissor_output(wlr_output, &rects[i]); | ||
183 | wlr_render_rect(renderer, &box, color, | ||
184 | wlr_output->transform_matrix); | ||
185 | } | ||
186 | |||
187 | damage_finish: | ||
188 | pixman_region32_fini(&damage); | ||
189 | } | ||
190 | |||
191 | static void premultiply_alpha(float color[4], float opacity) { | ||
192 | color[3] *= opacity; | ||
193 | color[0] *= color[3]; | ||
194 | color[1] *= color[3]; | ||
195 | color[2] *= color[3]; | ||
196 | } | ||
197 | |||
198 | static void render_view_surfaces(struct sway_view *view, | ||
199 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
200 | struct render_data data = { | ||
201 | .output = output, | ||
202 | .damage = damage, | ||
203 | .view = view, | ||
204 | .alpha = alpha, | ||
205 | }; | ||
206 | output_view_for_each_surface(view, output, &data.root_geo, | ||
207 | render_surface_iterator, &data); | ||
208 | } | ||
209 | |||
210 | static void render_saved_view(struct sway_view *view, | ||
211 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
212 | struct wlr_output *wlr_output = output->wlr_output; | ||
213 | |||
214 | int width, height; | ||
215 | struct wlr_texture *texture = | ||
216 | transaction_get_saved_texture(view, &width, &height); | ||
217 | if (!texture) { | ||
218 | return; | ||
219 | } | ||
220 | struct wlr_box box = { | ||
221 | .x = view->swayc->current.view_x - output->swayc->current.swayc_x, | ||
222 | .y = view->swayc->current.view_y - output->swayc->current.swayc_y, | ||
223 | .width = width, | ||
224 | .height = height, | ||
225 | }; | ||
226 | |||
227 | struct wlr_box output_box = { | ||
228 | .width = output->swayc->current.swayc_width, | ||
229 | .height = output->swayc->current.swayc_height, | ||
230 | }; | ||
231 | |||
232 | struct wlr_box intersection; | ||
233 | bool intersects = wlr_box_intersection(&output_box, &box, &intersection); | ||
234 | if (!intersects) { | ||
235 | return; | ||
236 | } | ||
237 | |||
238 | scale_box(&box, wlr_output->scale); | ||
239 | |||
240 | float matrix[9]; | ||
241 | wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, | ||
242 | wlr_output->transform_matrix); | ||
243 | |||
244 | render_texture(wlr_output, damage, texture, &box, matrix, alpha); | ||
245 | } | ||
246 | |||
247 | /** | ||
248 | * Render a view's surface and left/bottom/right borders. | ||
249 | */ | ||
250 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | ||
251 | struct sway_container *con, struct border_colors *colors) { | ||
252 | struct sway_view *view = con->sway_view; | ||
253 | if (view->swayc->instructions->length) { | ||
254 | render_saved_view(view, output, damage, view->swayc->alpha); | ||
255 | } else { | ||
256 | render_view_surfaces(view, output, damage, view->swayc->alpha); | ||
257 | } | ||
258 | |||
259 | if (view->using_csd) { | ||
260 | return; | ||
261 | } | ||
262 | |||
263 | struct wlr_box box; | ||
264 | float output_scale = output->wlr_output->scale; | ||
265 | float color[4]; | ||
266 | struct sway_container_state *state = &con->current; | ||
267 | |||
268 | if (state->border != B_NONE) { | ||
269 | if (state->border_left) { | ||
270 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
271 | premultiply_alpha(color, con->alpha); | ||
272 | box.x = state->swayc_x; | ||
273 | box.y = state->view_y; | ||
274 | box.width = state->border_thickness; | ||
275 | box.height = state->view_height; | ||
276 | scale_box(&box, output_scale); | ||
277 | render_rect(output->wlr_output, damage, &box, color); | ||
278 | } | ||
279 | |||
280 | if (state->border_right) { | ||
281 | if (state->parent->current.children->length == 1 | ||
282 | && state->parent->current.layout == L_HORIZ) { | ||
283 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
284 | } else { | ||
285 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
286 | } | ||
287 | premultiply_alpha(color, con->alpha); | ||
288 | box.x = state->view_x + state->view_width; | ||
289 | box.y = state->view_y; | ||
290 | box.width = state->border_thickness; | ||
291 | box.height = state->view_height; | ||
292 | scale_box(&box, output_scale); | ||
293 | render_rect(output->wlr_output, damage, &box, color); | ||
294 | } | ||
295 | |||
296 | if (state->border_bottom) { | ||
297 | if (state->parent->current.children->length == 1 | ||
298 | && con->current.parent->current.layout == L_VERT) { | ||
299 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
300 | } else { | ||
301 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
302 | } | ||
303 | premultiply_alpha(color, con->alpha); | ||
304 | box.x = state->swayc_x; | ||
305 | box.y = state->view_y + state->view_height; | ||
306 | box.width = state->swayc_width; | ||
307 | box.height = state->border_thickness; | ||
308 | scale_box(&box, output_scale); | ||
309 | render_rect(output->wlr_output, damage, &box, color); | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * Render a titlebar. | ||
316 | * | ||
317 | * Care must be taken not to render over the same pixel multiple times, | ||
318 | * otherwise the colors will be incorrect when using opacity. | ||
319 | * | ||
320 | * The height is: 1px border, 3px padding, font height, 3px padding, 1px border | ||
321 | * The left side for L_TABBED is: 1px border, 2px padding, title | ||
322 | * The left side for other layouts is: 3px padding, title | ||
323 | */ | ||
324 | static void render_titlebar(struct sway_output *output, | ||
325 | pixman_region32_t *output_damage, struct sway_container *con, | ||
326 | int x, int y, int width, | ||
327 | struct border_colors *colors, struct wlr_texture *title_texture, | ||
328 | struct wlr_texture *marks_texture) { | ||
329 | struct wlr_box box; | ||
330 | float color[4]; | ||
331 | struct sway_container_state *state = &con->current; | ||
332 | float output_scale = output->wlr_output->scale; | ||
333 | enum sway_container_layout layout = state->parent->current.layout; | ||
334 | list_t *children = state->parent->current.children; | ||
335 | bool is_last_child = children->items[children->length - 1] == con; | ||
336 | double output_x = output->swayc->current.swayc_x; | ||
337 | double output_y = output->swayc->current.swayc_y; | ||
338 | |||
339 | // Single pixel bar above title | ||
340 | memcpy(&color, colors->border, sizeof(float) * 4); | ||
341 | premultiply_alpha(color, con->alpha); | ||
342 | box.x = x; | ||
343 | box.y = y; | ||
344 | box.width = width; | ||
345 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
346 | scale_box(&box, output_scale); | ||
347 | render_rect(output->wlr_output, output_damage, &box, color); | ||
348 | |||
349 | // Single pixel bar below title | ||
350 | size_t left_offset = 0, right_offset = 0; | ||
351 | bool connects_sides = false; | ||
352 | if (layout == L_HORIZ || layout == L_VERT || | ||
353 | (layout == L_STACKED && is_last_child)) { | ||
354 | if (con->type == C_VIEW) { | ||
355 | left_offset = state->border_left * state->border_thickness; | ||
356 | right_offset = state->border_right * state->border_thickness; | ||
357 | connects_sides = true; | ||
358 | } | ||
359 | } | ||
360 | box.x = x + left_offset; | ||
361 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
362 | box.width = width - left_offset - right_offset; | ||
363 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
364 | scale_box(&box, output_scale); | ||
365 | render_rect(output->wlr_output, output_damage, &box, color); | ||
366 | |||
367 | if (layout == L_TABBED) { | ||
368 | // Single pixel left edge | ||
369 | box.x = x; | ||
370 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
371 | box.width = TITLEBAR_BORDER_THICKNESS; | ||
372 | box.height = | ||
373 | container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; | ||
374 | scale_box(&box, output_scale); | ||
375 | render_rect(output->wlr_output, output_damage, &box, color); | ||
376 | |||
377 | // Single pixel right edge | ||
378 | box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; | ||
379 | render_rect(output->wlr_output, output_damage, &box, color); | ||
380 | } | ||
381 | |||
382 | size_t inner_width = width - TITLEBAR_H_PADDING * 2; | ||
383 | |||
384 | // Marks | ||
385 | size_t marks_ob_width = 0; // output-buffer-local | ||
386 | if (config->show_marks && marks_texture) { | ||
387 | struct wlr_box texture_box; | ||
388 | wlr_texture_get_size(marks_texture, | ||
389 | &texture_box.width, &texture_box.height); | ||
390 | texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) | ||
391 | * output_scale - texture_box.width; | ||
392 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
393 | |||
394 | float matrix[9]; | ||
395 | wlr_matrix_project_box(matrix, &texture_box, | ||
396 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
397 | 0.0, output->wlr_output->transform_matrix); | ||
398 | |||
399 | if (inner_width * output_scale < texture_box.width) { | ||
400 | texture_box.width = inner_width * output_scale; | ||
401 | } | ||
402 | render_texture(output->wlr_output, output_damage, marks_texture, | ||
403 | &texture_box, matrix, con->alpha); | ||
404 | marks_ob_width = texture_box.width; | ||
405 | |||
406 | // Gap between the marks and bottom padding, for when the marks texture | ||
407 | // height is smaller than the config's font height | ||
408 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
409 | premultiply_alpha(color, con->alpha); | ||
410 | box.x = texture_box.x; | ||
411 | box.y = texture_box.y + texture_box.height; | ||
412 | box.width = texture_box.width; | ||
413 | box.height = config->font_height * output_scale - texture_box.height; | ||
414 | if (box.height > 0) { | ||
415 | render_rect(output->wlr_output, output_damage, &box, color); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | // Title text | ||
420 | size_t title_ob_width = 0; // output-buffer-local | ||
421 | if (title_texture) { | ||
422 | struct wlr_box texture_box; | ||
423 | wlr_texture_get_size(title_texture, | ||
424 | &texture_box.width, &texture_box.height); | ||
425 | texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; | ||
426 | texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; | ||
427 | |||
428 | float matrix[9]; | ||
429 | wlr_matrix_project_box(matrix, &texture_box, | ||
430 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
431 | 0.0, output->wlr_output->transform_matrix); | ||
432 | |||
433 | if (inner_width * output_scale - marks_ob_width < texture_box.width) { | ||
434 | texture_box.width = inner_width * output_scale - marks_ob_width; | ||
435 | } | ||
436 | render_texture(output->wlr_output, output_damage, title_texture, | ||
437 | &texture_box, matrix, con->alpha); | ||
438 | title_ob_width = texture_box.width; | ||
439 | |||
440 | // Gap between the title and bottom padding, for when the title texture | ||
441 | // height is smaller than the config's font height | ||
442 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
443 | premultiply_alpha(color, con->alpha); | ||
444 | box.x = texture_box.x; | ||
445 | box.y = texture_box.y + texture_box.height; | ||
446 | box.width = texture_box.width; | ||
447 | box.height = config->font_height * output_scale - texture_box.height; | ||
448 | if (box.height > 0) { | ||
449 | render_rect(output->wlr_output, output_damage, &box, color); | ||
450 | } | ||
451 | } | ||
452 | |||
453 | // Padding above title | ||
454 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
455 | premultiply_alpha(color, con->alpha); | ||
456 | box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
457 | box.y = y + TITLEBAR_BORDER_THICKNESS; | ||
458 | box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; | ||
459 | box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; | ||
460 | scale_box(&box, output_scale); | ||
461 | render_rect(output->wlr_output, output_damage, &box, color); | ||
462 | |||
463 | // Padding below title | ||
464 | box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; | ||
465 | render_rect(output->wlr_output, output_damage, &box, color); | ||
466 | |||
467 | // Filler between title and marks | ||
468 | box.width = inner_width * output_scale - title_ob_width - marks_ob_width; | ||
469 | if (box.width > 0) { | ||
470 | box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width; | ||
471 | box.y = (y + TITLEBAR_V_PADDING) * output_scale; | ||
472 | box.height = config->font_height * output_scale; | ||
473 | render_rect(output->wlr_output, output_damage, &box, color); | ||
474 | } | ||
475 | |||
476 | // Padding left of title | ||
477 | left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
478 | box.x = x + left_offset; | ||
479 | box.y = y + TITLEBAR_V_PADDING; | ||
480 | box.width = TITLEBAR_H_PADDING - left_offset; | ||
481 | box.height = config->font_height; | ||
482 | scale_box(&box, output_scale); | ||
483 | render_rect(output->wlr_output, output_damage, &box, color); | ||
484 | |||
485 | // Padding right of marks | ||
486 | right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; | ||
487 | box.x = x + width - TITLEBAR_H_PADDING; | ||
488 | box.y = y + TITLEBAR_V_PADDING; | ||
489 | box.width = TITLEBAR_H_PADDING - right_offset; | ||
490 | box.height = config->font_height; | ||
491 | scale_box(&box, output_scale); | ||
492 | render_rect(output->wlr_output, output_damage, &box, color); | ||
493 | |||
494 | if (connects_sides) { | ||
495 | // Left pixel in line with bottom bar | ||
496 | box.x = x; | ||
497 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
498 | box.width = state->border_thickness * state->border_left; | ||
499 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
500 | scale_box(&box, output_scale); | ||
501 | render_rect(output->wlr_output, output_damage, &box, color); | ||
502 | |||
503 | // Right pixel in line with bottom bar | ||
504 | box.x = x + width - state->border_thickness * state->border_right; | ||
505 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; | ||
506 | box.width = state->border_thickness * state->border_right; | ||
507 | box.height = TITLEBAR_BORDER_THICKNESS; | ||
508 | scale_box(&box, output_scale); | ||
509 | render_rect(output->wlr_output, output_damage, &box, color); | ||
510 | } | ||
511 | } | ||
512 | |||
513 | /** | ||
514 | * Render the top border line for a view using "border pixel". | ||
515 | */ | ||
516 | static void render_top_border(struct sway_output *output, | ||
517 | pixman_region32_t *output_damage, struct sway_container *con, | ||
518 | struct border_colors *colors) { | ||
519 | struct sway_container_state *state = &con->current; | ||
520 | if (!state->border_top) { | ||
521 | return; | ||
522 | } | ||
523 | struct wlr_box box; | ||
524 | float color[4]; | ||
525 | float output_scale = output->wlr_output->scale; | ||
526 | |||
527 | // Child border - top edge | ||
528 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
529 | premultiply_alpha(color, con->alpha); | ||
530 | box.x = state->swayc_x; | ||
531 | box.y = state->swayc_y; | ||
532 | box.width = state->swayc_width; | ||
533 | box.height = state->border_thickness; | ||
534 | scale_box(&box, output_scale); | ||
535 | render_rect(output->wlr_output, output_damage, &box, color); | ||
536 | } | ||
537 | |||
538 | static void render_container(struct sway_output *output, | ||
539 | pixman_region32_t *damage, struct sway_container *con, bool parent_focused); | ||
540 | |||
541 | /** | ||
542 | * Render a container's children using a L_HORIZ or L_VERT layout. | ||
543 | * | ||
544 | * Wrap child views in borders and leave child containers borderless because | ||
545 | * they'll apply their own borders to their children. | ||
546 | */ | ||
547 | static void render_container_simple(struct sway_output *output, | ||
548 | pixman_region32_t *damage, struct sway_container *con, | ||
549 | bool parent_focused) { | ||
550 | for (int i = 0; i < con->current.children->length; ++i) { | ||
551 | struct sway_container *child = con->current.children->items[i]; | ||
552 | |||
553 | if (child->type == C_VIEW) { | ||
554 | struct sway_view *view = child->sway_view; | ||
555 | struct border_colors *colors; | ||
556 | struct wlr_texture *title_texture; | ||
557 | struct wlr_texture *marks_texture; | ||
558 | struct sway_container_state *state = &child->current; | ||
559 | |||
560 | if (view_is_urgent(view)) { | ||
561 | colors = &config->border_colors.urgent; | ||
562 | title_texture = child->title_urgent; | ||
563 | marks_texture = view->marks_urgent; | ||
564 | } else if (state->focused || parent_focused) { | ||
565 | colors = &config->border_colors.focused; | ||
566 | title_texture = child->title_focused; | ||
567 | marks_texture = view->marks_focused; | ||
568 | } else if (con->current.focused_inactive_child == child) { | ||
569 | colors = &config->border_colors.focused_inactive; | ||
570 | title_texture = child->title_focused_inactive; | ||
571 | marks_texture = view->marks_focused_inactive; | ||
572 | } else { | ||
573 | colors = &config->border_colors.unfocused; | ||
574 | title_texture = child->title_unfocused; | ||
575 | marks_texture = view->marks_unfocused; | ||
576 | } | ||
577 | |||
578 | if (!view->using_csd) { | ||
579 | if (state->border == B_NORMAL) { | ||
580 | render_titlebar(output, damage, child, state->swayc_x, | ||
581 | state->swayc_y, state->swayc_width, colors, | ||
582 | title_texture, marks_texture); | ||
583 | } else { | ||
584 | render_top_border(output, damage, child, colors); | ||
585 | } | ||
586 | } | ||
587 | render_view(output, damage, child, colors); | ||
588 | } else { | ||
589 | render_container(output, damage, child, | ||
590 | parent_focused || child->current.focused); | ||
591 | } | ||
592 | } | ||
593 | } | ||
594 | |||
595 | /** | ||
596 | * Render a container's children using the L_TABBED layout. | ||
597 | */ | ||
598 | static void render_container_tabbed(struct sway_output *output, | ||
599 | pixman_region32_t *damage, struct sway_container *con, | ||
600 | bool parent_focused) { | ||
601 | if (!con->current.children->length) { | ||
602 | return; | ||
603 | } | ||
604 | struct sway_container_state *pstate = &con->current; | ||
605 | struct sway_container *current = pstate->focused_inactive_child; | ||
606 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
607 | |||
608 | double width_gap_adjustment = 2 * pstate->current_gaps; | ||
609 | int tab_width = | ||
610 | (pstate->swayc_width - width_gap_adjustment) / pstate->children->length; | ||
611 | |||
612 | // Render tabs | ||
613 | for (int i = 0; i < pstate->children->length; ++i) { | ||
614 | struct sway_container *child = pstate->children->items[i]; | ||
615 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
616 | struct sway_container_state *cstate = &child->current; | ||
617 | struct border_colors *colors; | ||
618 | struct wlr_texture *title_texture; | ||
619 | struct wlr_texture *marks_texture; | ||
620 | bool urgent = view ? | ||
621 | view_is_urgent(view) : container_has_urgent_child(child); | ||
622 | |||
623 | if (urgent) { | ||
624 | colors = &config->border_colors.urgent; | ||
625 | title_texture = child->title_urgent; | ||
626 | marks_texture = view ? view->marks_urgent : NULL; | ||
627 | } else if (cstate->focused || parent_focused) { | ||
628 | colors = &config->border_colors.focused; | ||
629 | title_texture = child->title_focused; | ||
630 | marks_texture = view ? view->marks_focused : NULL; | ||
631 | } else if (child == pstate->focused_inactive_child) { | ||
632 | colors = &config->border_colors.focused_inactive; | ||
633 | title_texture = child->title_focused_inactive; | ||
634 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
635 | } else { | ||
636 | colors = &config->border_colors.unfocused; | ||
637 | title_texture = child->title_unfocused; | ||
638 | marks_texture = view ? view->marks_unfocused : NULL; | ||
639 | } | ||
640 | |||
641 | int x = cstate->swayc_x + tab_width * i; | ||
642 | |||
643 | // Make last tab use the remaining width of the parent | ||
644 | if (i == pstate->children->length - 1) { | ||
645 | tab_width = | ||
646 | pstate->swayc_width - width_gap_adjustment - tab_width * i; | ||
647 | } | ||
648 | |||
649 | render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, | ||
650 | colors, title_texture, marks_texture); | ||
651 | |||
652 | if (child == current) { | ||
653 | current_colors = colors; | ||
654 | } | ||
655 | } | ||
656 | |||
657 | // Render surface and left/right/bottom borders | ||
658 | if (current->type == C_VIEW) { | ||
659 | render_view(output, damage, current, current_colors); | ||
660 | } else { | ||
661 | render_container(output, damage, current, | ||
662 | parent_focused || current->current.focused); | ||
663 | } | ||
664 | } | ||
665 | |||
666 | /** | ||
667 | * Render a container's children using the L_STACKED layout. | ||
668 | */ | ||
669 | static void render_container_stacked(struct sway_output *output, | ||
670 | pixman_region32_t *damage, struct sway_container *con, | ||
671 | bool parent_focused) { | ||
672 | if (!con->current.children->length) { | ||
673 | return; | ||
674 | } | ||
675 | struct sway_container_state *pstate = &con->current; | ||
676 | struct sway_container *current = pstate->focused_inactive_child; | ||
677 | struct border_colors *current_colors = &config->border_colors.unfocused; | ||
678 | |||
679 | size_t titlebar_height = container_titlebar_height(); | ||
680 | |||
681 | // Render titles | ||
682 | for (int i = 0; i < pstate->children->length; ++i) { | ||
683 | struct sway_container *child = pstate->children->items[i]; | ||
684 | struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; | ||
685 | struct sway_container_state *cstate = &child->current; | ||
686 | struct border_colors *colors; | ||
687 | struct wlr_texture *title_texture; | ||
688 | struct wlr_texture *marks_texture; | ||
689 | bool urgent = view ? | ||
690 | view_is_urgent(view) : container_has_urgent_child(child); | ||
691 | |||
692 | if (urgent) { | ||
693 | colors = &config->border_colors.urgent; | ||
694 | title_texture = child->title_urgent; | ||
695 | marks_texture = view ? view->marks_urgent : NULL; | ||
696 | } else if (cstate->focused || parent_focused) { | ||
697 | colors = &config->border_colors.focused; | ||
698 | title_texture = child->title_focused; | ||
699 | marks_texture = view ? view->marks_focused : NULL; | ||
700 | } else if (child == pstate->focused_inactive_child) { | ||
701 | colors = &config->border_colors.focused_inactive; | ||
702 | title_texture = child->title_focused_inactive; | ||
703 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
704 | } else { | ||
705 | colors = &config->border_colors.unfocused; | ||
706 | title_texture = child->title_unfocused; | ||
707 | marks_texture = view ? view->marks_unfocused : NULL; | ||
708 | } | ||
709 | |||
710 | int y = cstate->swayc_y + titlebar_height * i; | ||
711 | render_titlebar(output, damage, child, cstate->swayc_x, y, | ||
712 | cstate->swayc_width, colors, title_texture, marks_texture); | ||
713 | |||
714 | if (child == current) { | ||
715 | current_colors = colors; | ||
716 | } | ||
717 | } | ||
718 | |||
719 | // Render surface and left/right/bottom borders | ||
720 | if (current->type == C_VIEW) { | ||
721 | render_view(output, damage, current, current_colors); | ||
722 | } else { | ||
723 | render_container(output, damage, current, | ||
724 | parent_focused || current->current.focused); | ||
725 | } | ||
726 | } | ||
727 | |||
728 | static void render_container(struct sway_output *output, | ||
729 | pixman_region32_t *damage, struct sway_container *con, | ||
730 | bool parent_focused) { | ||
731 | switch (con->current.layout) { | ||
732 | case L_NONE: | ||
733 | case L_HORIZ: | ||
734 | case L_VERT: | ||
735 | render_container_simple(output, damage, con, parent_focused); | ||
736 | break; | ||
737 | case L_STACKED: | ||
738 | render_container_stacked(output, damage, con, parent_focused); | ||
739 | break; | ||
740 | case L_TABBED: | ||
741 | render_container_tabbed(output, damage, con, parent_focused); | ||
742 | break; | ||
743 | case L_FLOATING: | ||
744 | sway_assert(false, "Didn't expect to see floating here"); | ||
745 | } | ||
746 | } | ||
747 | |||
748 | static void render_floating_container(struct sway_output *soutput, | ||
749 | pixman_region32_t *damage, struct sway_container *con) { | ||
750 | if (con->type == C_VIEW) { | ||
751 | struct sway_view *view = con->sway_view; | ||
752 | struct border_colors *colors; | ||
753 | struct wlr_texture *title_texture; | ||
754 | struct wlr_texture *marks_texture; | ||
755 | |||
756 | if (view_is_urgent(view)) { | ||
757 | colors = &config->border_colors.urgent; | ||
758 | title_texture = con->title_urgent; | ||
759 | marks_texture = view->marks_urgent; | ||
760 | } else if (con->current.focused) { | ||
761 | colors = &config->border_colors.focused; | ||
762 | title_texture = con->title_focused; | ||
763 | marks_texture = view->marks_focused; | ||
764 | } else { | ||
765 | colors = &config->border_colors.unfocused; | ||
766 | title_texture = con->title_unfocused; | ||
767 | marks_texture = view->marks_unfocused; | ||
768 | } | ||
769 | |||
770 | if (!view->using_csd) { | ||
771 | if (con->current.border == B_NORMAL) { | ||
772 | render_titlebar(soutput, damage, con, con->current.swayc_x, | ||
773 | con->current.swayc_y, con->current.swayc_width, colors, | ||
774 | title_texture, marks_texture); | ||
775 | } else if (con->current.border != B_NONE) { | ||
776 | render_top_border(soutput, damage, con, colors); | ||
777 | } | ||
778 | } | ||
779 | render_view(soutput, damage, con, colors); | ||
780 | } else { | ||
781 | render_container(soutput, damage, con, false); | ||
782 | } | ||
783 | } | ||
784 | |||
785 | static void render_floating(struct sway_output *soutput, | ||
786 | pixman_region32_t *damage) { | ||
787 | for (int i = 0; i < root_container.current.children->length; ++i) { | ||
788 | struct sway_container *output = | ||
789 | root_container.current.children->items[i]; | ||
790 | for (int j = 0; j < output->current.children->length; ++j) { | ||
791 | struct sway_container *ws = output->current.children->items[j]; | ||
792 | if (!workspace_is_visible(ws)) { | ||
793 | continue; | ||
794 | } | ||
795 | list_t *floating = | ||
796 | ws->current.ws_floating->current.children; | ||
797 | for (int k = 0; k < floating->length; ++k) { | ||
798 | struct sway_container *floater = floating->items[k]; | ||
799 | render_floating_container(soutput, damage, floater); | ||
800 | } | ||
801 | } | ||
802 | } | ||
803 | } | ||
804 | |||
805 | const char *damage_debug = NULL; | ||
806 | |||
807 | void output_render(struct sway_output *output, struct timespec *when, | ||
808 | pixman_region32_t *damage) { | ||
809 | struct wlr_output *wlr_output = output->wlr_output; | ||
810 | |||
811 | struct wlr_renderer *renderer = | ||
812 | wlr_backend_get_renderer(wlr_output->backend); | ||
813 | if (!sway_assert(renderer != NULL, | ||
814 | "expected the output backend to have a renderer")) { | ||
815 | return; | ||
816 | } | ||
817 | |||
818 | wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); | ||
819 | |||
820 | bool damage_whole_before_swap = false; | ||
821 | if (!pixman_region32_not_empty(damage)) { | ||
822 | // Output isn't damaged but needs buffer swap | ||
823 | goto renderer_end; | ||
824 | } | ||
825 | |||
826 | if (damage_debug != NULL) { | ||
827 | if (strcmp(damage_debug, "highlight") == 0) { | ||
828 | wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); | ||
829 | damage_whole_before_swap = true; | ||
830 | } else if (strcmp(damage_debug, "rerender") == 0) { | ||
831 | int width, height; | ||
832 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
833 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
834 | } | ||
835 | } | ||
836 | |||
837 | struct sway_container *workspace = output_get_active_workspace(output); | ||
838 | struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; | ||
839 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
840 | |||
841 | if (output_has_opaque_lockscreen(output, seat) && seat->focused_layer) { | ||
842 | struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer; | ||
843 | struct sway_layer_surface *sway_layer_surface = | ||
844 | layer_from_wlr_layer_surface(seat->focused_layer); | ||
845 | struct render_data data = { | ||
846 | .output = output, | ||
847 | .damage = damage, | ||
848 | .alpha = 1.0f, | ||
849 | }; | ||
850 | output_surface_for_each_surface(wlr_layer_surface->surface, | ||
851 | sway_layer_surface->geo.x, sway_layer_surface->geo.y, | ||
852 | &data.root_geo, render_surface_iterator, &data); | ||
853 | } else if (fullscreen_view) { | ||
854 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | ||
855 | |||
856 | int nrects; | ||
857 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
858 | for (int i = 0; i < nrects; ++i) { | ||
859 | scissor_output(wlr_output, &rects[i]); | ||
860 | wlr_renderer_clear(renderer, clear_color); | ||
861 | } | ||
862 | |||
863 | // TODO: handle views smaller than the output | ||
864 | if (fullscreen_view->swayc->instructions->length) { | ||
865 | render_saved_view(fullscreen_view, output, damage, 1.0f); | ||
866 | } else { | ||
867 | render_view_surfaces(fullscreen_view, output, damage, 1.0f); | ||
868 | } | ||
869 | |||
870 | if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { | ||
871 | render_unmanaged(output, damage, | ||
872 | &root_container.sway_root->xwayland_unmanaged); | ||
873 | } | ||
874 | } else { | ||
875 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | ||
876 | |||
877 | int nrects; | ||
878 | pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); | ||
879 | for (int i = 0; i < nrects; ++i) { | ||
880 | scissor_output(wlr_output, &rects[i]); | ||
881 | wlr_renderer_clear(renderer, clear_color); | ||
882 | } | ||
883 | |||
884 | render_layer(output, damage, | ||
885 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | ||
886 | render_layer(output, damage, | ||
887 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | ||
888 | |||
889 | render_container(output, damage, workspace, workspace->current.focused); | ||
890 | render_floating(output, damage); | ||
891 | |||
892 | render_unmanaged(output, damage, | ||
893 | &root_container.sway_root->xwayland_unmanaged); | ||
894 | render_layer(output, damage, | ||
895 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | ||
896 | } | ||
897 | render_layer(output, damage, | ||
898 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | ||
899 | render_drag_icons(output, damage, &root_container.sway_root->drag_icons); | ||
900 | |||
901 | renderer_end: | ||
902 | if (root_container.sway_root->debug_tree) { | ||
903 | wlr_render_texture(renderer, root_container.sway_root->debug_tree, | ||
904 | wlr_output->transform_matrix, 0, 0, 1); | ||
905 | } | ||
906 | |||
907 | if (damage_whole_before_swap || root_container.sway_root->debug_tree) { | ||
908 | int width, height; | ||
909 | wlr_output_transformed_resolution(wlr_output, &width, &height); | ||
910 | pixman_region32_union_rect(damage, damage, 0, 0, width, height); | ||
911 | } | ||
912 | |||
913 | wlr_renderer_scissor(renderer, NULL); | ||
914 | wlr_renderer_end(renderer); | ||
915 | if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { | ||
916 | return; | ||
917 | } | ||
918 | output->last_frame = *when; | ||
919 | } | ||