aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop/render.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/desktop/render.c')
-rw-r--r--sway/desktop/render.c1125
1 files changed, 0 insertions, 1125 deletions
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
deleted file mode 100644
index bd85282c..00000000
--- a/sway/desktop/render.c
+++ /dev/null
@@ -1,1125 +0,0 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <GLES2/gl2.h>
4#include <stdlib.h>
5#include <strings.h>
6#include <time.h>
7#include <wayland-server-core.h>
8#include <wlr/render/gles2.h>
9#include <wlr/render/wlr_renderer.h>
10#include <wlr/types/wlr_box.h>
11#include <wlr/types/wlr_buffer.h>
12#include <wlr/types/wlr_matrix.h>
13#include <wlr/types/wlr_output_damage.h>
14#include <wlr/types/wlr_output_layout.h>
15#include <wlr/types/wlr_output.h>
16#include <wlr/types/wlr_surface.h>
17#include <wlr/util/region.h>
18#include "log.h"
19#include "config.h"
20#include "sway/config.h"
21#include "sway/input/input-manager.h"
22#include "sway/input/seat.h"
23#include "sway/layers.h"
24#include "sway/output.h"
25#include "sway/server.h"
26#include "sway/tree/arrange.h"
27#include "sway/tree/container.h"
28#include "sway/tree/root.h"
29#include "sway/tree/view.h"
30#include "sway/tree/workspace.h"
31
32struct render_data {
33 pixman_region32_t *damage;
34 float alpha;
35};
36
37/**
38 * Apply scale to a width or height.
39 *
40 * One does not simply multiply the width by the scale. We allow fractional
41 * scaling, which means the resulting scaled width might be a decimal.
42 * So we round it.
43 *
44 * But even this can produce undesirable results depending on the X or Y offset
45 * of the box. For example, with a scale of 1.5, a box with width=1 should not
46 * scale to 2px if its X coordinate is 1, because the X coordinate would have
47 * scaled to 2px.
48 */
49static int scale_length(int length, int offset, float scale) {
50 return round((offset + length) * scale) - round(offset * scale);
51}
52
53static void scissor_output(struct wlr_output *wlr_output,
54 pixman_box32_t *rect) {
55 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
56 assert(renderer);
57
58 struct wlr_box box = {
59 .x = rect->x1,
60 .y = rect->y1,
61 .width = rect->x2 - rect->x1,
62 .height = rect->y2 - rect->y1,
63 };
64
65 int ow, oh;
66 wlr_output_transformed_resolution(wlr_output, &ow, &oh);
67
68 enum wl_output_transform transform =
69 wlr_output_transform_invert(wlr_output->transform);
70 wlr_box_transform(&box, &box, transform, ow, oh);
71
72 wlr_renderer_scissor(renderer, &box);
73}
74
75static void set_scale_filter(struct wlr_output *wlr_output,
76 struct wlr_texture *texture, enum scale_filter_mode scale_filter) {
77 if (!wlr_texture_is_gles2(texture)) {
78 return;
79 }
80
81 struct wlr_gles2_texture_attribs attribs;
82 wlr_gles2_texture_get_attribs(texture, &attribs);
83
84 glBindTexture(attribs.target, attribs.tex);
85
86 switch (scale_filter) {
87 case SCALE_FILTER_LINEAR:
88 glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
89 break;
90 case SCALE_FILTER_NEAREST:
91 glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
92 break;
93 case SCALE_FILTER_DEFAULT:
94 case SCALE_FILTER_SMART:
95 assert(false); // unreachable
96 }
97}
98
99static void render_texture(struct wlr_output *wlr_output,
100 pixman_region32_t *output_damage, struct wlr_texture *texture,
101 const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
102 const float matrix[static 9], float alpha) {
103 struct wlr_renderer *renderer =
104 wlr_backend_get_renderer(wlr_output->backend);
105 struct sway_output *output = wlr_output->data;
106
107 struct wlr_gles2_texture_attribs attribs;
108 wlr_gles2_texture_get_attribs(texture, &attribs);
109
110 pixman_region32_t damage;
111 pixman_region32_init(&damage);
112 pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y,
113 dst_box->width, dst_box->height);
114 pixman_region32_intersect(&damage, &damage, output_damage);
115 bool damaged = pixman_region32_not_empty(&damage);
116 if (!damaged) {
117 goto damage_finish;
118 }
119
120 int nrects;
121 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
122 for (int i = 0; i < nrects; ++i) {
123 scissor_output(wlr_output, &rects[i]);
124 set_scale_filter(wlr_output, texture, output->scale_filter);
125 if (src_box != NULL) {
126 wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha);
127 } else {
128 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
129 }
130 }
131
132damage_finish:
133 pixman_region32_fini(&damage);
134}
135
136static void render_surface_iterator(struct sway_output *output, struct sway_view *view,
137 struct wlr_surface *surface, struct wlr_box *_box, float rotation,
138 void *_data) {
139 struct render_data *data = _data;
140 struct wlr_output *wlr_output = output->wlr_output;
141 pixman_region32_t *output_damage = data->damage;
142 float alpha = data->alpha;
143
144 struct wlr_texture *texture = wlr_surface_get_texture(surface);
145 if (!texture) {
146 return;
147 }
148
149 struct wlr_fbox src_box;
150 wlr_surface_get_buffer_source_box(surface, &src_box);
151
152 struct wlr_box dst_box = *_box;
153 scale_box(&dst_box, wlr_output->scale);
154
155 float matrix[9];
156 enum wl_output_transform transform =
157 wlr_output_transform_invert(surface->current.transform);
158 wlr_matrix_project_box(matrix, &dst_box, transform, rotation,
159 wlr_output->transform_matrix);
160
161 render_texture(wlr_output, output_damage, texture,
162 &src_box, &dst_box, matrix, alpha);
163
164 wlr_presentation_surface_sampled_on_output(server.presentation, surface,
165 wlr_output);
166}
167
168static void render_layer_toplevel(struct sway_output *output,
169 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
170 struct render_data data = {
171 .damage = damage,
172 .alpha = 1.0f,
173 };
174 output_layer_for_each_toplevel_surface(output, layer_surfaces,
175 render_surface_iterator, &data);
176}
177
178static void render_layer_popups(struct sway_output *output,
179 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
180 struct render_data data = {
181 .damage = damage,
182 .alpha = 1.0f,
183 };
184 output_layer_for_each_popup_surface(output, layer_surfaces,
185 render_surface_iterator, &data);
186}
187
188#if HAVE_XWAYLAND
189static void render_unmanaged(struct sway_output *output,
190 pixman_region32_t *damage, struct wl_list *unmanaged) {
191 struct render_data data = {
192 .damage = damage,
193 .alpha = 1.0f,
194 };
195 output_unmanaged_for_each_surface(output, unmanaged,
196 render_surface_iterator, &data);
197}
198#endif
199
200static void render_drag_icons(struct sway_output *output,
201 pixman_region32_t *damage, struct wl_list *drag_icons) {
202 struct render_data data = {
203 .damage = damage,
204 .alpha = 1.0f,
205 };
206 output_drag_icons_for_each_surface(output, drag_icons,
207 render_surface_iterator, &data);
208}
209
210// _box.x and .y are expected to be layout-local
211// _box.width and .height are expected to be output-buffer-local
212void render_rect(struct sway_output *output,
213 pixman_region32_t *output_damage, const struct wlr_box *_box,
214 float color[static 4]) {
215 struct wlr_output *wlr_output = output->wlr_output;
216 struct wlr_renderer *renderer =
217 wlr_backend_get_renderer(wlr_output->backend);
218
219 struct wlr_box box;
220 memcpy(&box, _box, sizeof(struct wlr_box));
221 box.x -= output->lx * wlr_output->scale;
222 box.y -= output->ly * wlr_output->scale;
223
224 pixman_region32_t damage;
225 pixman_region32_init(&damage);
226 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
227 box.width, box.height);
228 pixman_region32_intersect(&damage, &damage, output_damage);
229 bool damaged = pixman_region32_not_empty(&damage);
230 if (!damaged) {
231 goto damage_finish;
232 }
233
234 int nrects;
235 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
236 for (int i = 0; i < nrects; ++i) {
237 scissor_output(wlr_output, &rects[i]);
238 wlr_render_rect(renderer, &box, color,
239 wlr_output->transform_matrix);
240 }
241
242damage_finish:
243 pixman_region32_fini(&damage);
244}
245
246void premultiply_alpha(float color[4], float opacity) {
247 color[3] *= opacity;
248 color[0] *= color[3];
249 color[1] *= color[3];
250 color[2] *= color[3];
251}
252
253static void render_view_toplevels(struct sway_view *view,
254 struct sway_output *output, pixman_region32_t *damage, float alpha) {
255 struct render_data data = {
256 .damage = damage,
257 .alpha = alpha,
258 };
259 // Render all toplevels without descending into popups
260 double ox = view->container->surface_x -
261 output->lx - view->geometry.x;
262 double oy = view->container->surface_y -
263 output->ly - view->geometry.y;
264 output_surface_for_each_surface(output, view->surface, ox, oy,
265 render_surface_iterator, &data);
266}
267
268static void render_view_popups(struct sway_view *view,
269 struct sway_output *output, pixman_region32_t *damage, float alpha) {
270 struct render_data data = {
271 .damage = damage,
272 .alpha = alpha,
273 };
274 output_view_for_each_popup_surface(output, view,
275 render_surface_iterator, &data);
276}
277
278static void render_saved_view(struct sway_view *view,
279 struct sway_output *output, pixman_region32_t *damage, float alpha) {
280 struct wlr_output *wlr_output = output->wlr_output;
281
282 if (wl_list_empty(&view->saved_buffers)) {
283 return;
284 }
285 struct sway_saved_buffer *saved_buf;
286 wl_list_for_each(saved_buf, &view->saved_buffers, link) {
287 if (!saved_buf->buffer->texture) {
288 continue;
289 }
290
291 struct wlr_box box = {
292 .x = view->container->surface_x - output->lx -
293 view->saved_geometry.x + saved_buf->x,
294 .y = view->container->surface_y - output->ly -
295 view->saved_geometry.y + saved_buf->y,
296 .width = saved_buf->width,
297 .height = saved_buf->height,
298 };
299
300 struct wlr_box output_box = {
301 .width = output->width,
302 .height = output->height,
303 };
304
305 struct wlr_box intersection;
306 bool intersects = wlr_box_intersection(&intersection, &output_box, &box);
307 if (!intersects) {
308 continue;
309 }
310
311 scale_box(&box, wlr_output->scale);
312
313 float matrix[9];
314 enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
315 wlr_matrix_project_box(matrix, &box, transform, 0,
316 wlr_output->transform_matrix);
317
318 render_texture(wlr_output, damage, saved_buf->buffer->texture,
319 &saved_buf->source_box, &box, matrix, alpha);
320 }
321
322 // FIXME: we should set the surface that this saved buffer originates from
323 // as sampled here.
324 // https://github.com/swaywm/sway/pull/4465#discussion_r321082059
325}
326
327/**
328 * Render a view's surface and left/bottom/right borders.
329 */
330static void render_view(struct sway_output *output, pixman_region32_t *damage,
331 struct sway_container *con, struct border_colors *colors) {
332 struct sway_view *view = con->view;
333 if (!wl_list_empty(&view->saved_buffers)) {
334 render_saved_view(view, output, damage, view->container->alpha);
335 } else if (view->surface) {
336 render_view_toplevels(view, output, damage, view->container->alpha);
337 }
338
339 if (con->current.border == B_NONE || con->current.border == B_CSD) {
340 return;
341 }
342
343 struct wlr_box box;
344 float output_scale = output->wlr_output->scale;
345 float color[4];
346 struct sway_container_state *state = &con->current;
347
348 if (state->border_left) {
349 memcpy(&color, colors->child_border, sizeof(float) * 4);
350 premultiply_alpha(color, con->alpha);
351 box.x = state->x;
352 box.y = state->content_y;
353 box.width = state->border_thickness;
354 box.height = state->content_height;
355 scale_box(&box, output_scale);
356 render_rect(output, damage, &box, color);
357 }
358
359 list_t *siblings = container_get_current_siblings(con);
360 enum sway_container_layout layout =
361 container_current_parent_layout(con);
362
363 if (state->border_right) {
364 if (!container_is_floating(con) && siblings->length == 1 && layout == L_HORIZ) {
365 memcpy(&color, colors->indicator, sizeof(float) * 4);
366 } else {
367 memcpy(&color, colors->child_border, sizeof(float) * 4);
368 }
369 premultiply_alpha(color, con->alpha);
370 box.x = state->content_x + state->content_width;
371 box.y = state->content_y;
372 box.width = state->border_thickness;
373 box.height = state->content_height;
374 scale_box(&box, output_scale);
375 render_rect(output, damage, &box, color);
376 }
377
378 if (state->border_bottom) {
379 if (!container_is_floating(con) && siblings->length == 1 && layout == L_VERT) {
380 memcpy(&color, colors->indicator, sizeof(float) * 4);
381 } else {
382 memcpy(&color, colors->child_border, sizeof(float) * 4);
383 }
384 premultiply_alpha(color, con->alpha);
385 box.x = state->x;
386 box.y = state->content_y + state->content_height;
387 box.width = state->width;
388 box.height = state->border_thickness;
389 scale_box(&box, output_scale);
390 render_rect(output, damage, &box, color);
391 }
392}
393
394/**
395 * Render a titlebar.
396 *
397 * Care must be taken not to render over the same pixel multiple times,
398 * otherwise the colors will be incorrect when using opacity.
399 *
400 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
401 * The left side is: 1px border, 2px padding, title
402 */
403static void render_titlebar(struct sway_output *output,
404 pixman_region32_t *output_damage, struct sway_container *con,
405 int x, int y, int width,
406 struct border_colors *colors, struct wlr_texture *title_texture,
407 struct wlr_texture *marks_texture) {
408 struct wlr_box box;
409 float color[4];
410 float output_scale = output->wlr_output->scale;
411 double output_x = output->lx;
412 double output_y = output->ly;
413 int titlebar_border_thickness = config->titlebar_border_thickness;
414 int titlebar_h_padding = config->titlebar_h_padding;
415 int titlebar_v_padding = config->titlebar_v_padding;
416 enum alignment title_align = config->title_align;
417
418 // Single pixel bar above title
419 memcpy(&color, colors->border, sizeof(float) * 4);
420 premultiply_alpha(color, con->alpha);
421 box.x = x;
422 box.y = y;
423 box.width = width;
424 box.height = titlebar_border_thickness;
425 scale_box(&box, output_scale);
426 render_rect(output, output_damage, &box, color);
427
428 // Single pixel bar below title
429 box.x = x;
430 box.y = y + container_titlebar_height() - titlebar_border_thickness;
431 box.width = width;
432 box.height = titlebar_border_thickness;
433 scale_box(&box, output_scale);
434 render_rect(output, output_damage, &box, color);
435
436 // Single pixel left edge
437 box.x = x;
438 box.y = y + titlebar_border_thickness;
439 box.width = titlebar_border_thickness;
440 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
441 scale_box(&box, output_scale);
442 render_rect(output, output_damage, &box, color);
443
444 // Single pixel right edge
445 box.x = x + width - titlebar_border_thickness;
446 box.y = y + titlebar_border_thickness;
447 box.width = titlebar_border_thickness;
448 box.height = container_titlebar_height() - titlebar_border_thickness * 2;
449 scale_box(&box, output_scale);
450 render_rect(output, output_damage, &box, color);
451
452 int inner_x = x - output_x + titlebar_h_padding;
453 int bg_y = y + titlebar_border_thickness;
454 size_t inner_width = width - titlebar_h_padding * 2;
455
456 // output-buffer local
457 int ob_inner_x = round(inner_x * output_scale);
458 int ob_inner_width = scale_length(inner_width, inner_x, output_scale);
459 int ob_bg_height = scale_length(
460 (titlebar_v_padding - titlebar_border_thickness) * 2 +
461 config->font_height, bg_y, output_scale);
462
463 // Marks
464 int ob_marks_x = 0; // output-buffer-local
465 int ob_marks_width = 0; // output-buffer-local
466 if (config->show_marks && marks_texture) {
467 struct wlr_box texture_box;
468 wlr_texture_get_size(marks_texture,
469 &texture_box.width, &texture_box.height);
470 ob_marks_width = texture_box.width;
471
472 // The marks texture might be shorter than the config->font_height, in
473 // which case we need to pad it as evenly as possible above and below.
474 int ob_padding_total = ob_bg_height - texture_box.height;
475 int ob_padding_above = floor(ob_padding_total / 2.0);
476 int ob_padding_below = ceil(ob_padding_total / 2.0);
477
478 // Render texture. If the title is on the right, the marks will be on
479 // the left. Otherwise, they will be on the right.
480 if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) {
481 texture_box.x = ob_inner_x;
482 } else {
483 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
484 }
485 ob_marks_x = texture_box.x;
486
487 texture_box.y = round((bg_y - output_y) * output_scale) +
488 ob_padding_above;
489
490 float matrix[9];
491 wlr_matrix_project_box(matrix, &texture_box,
492 WL_OUTPUT_TRANSFORM_NORMAL,
493 0.0, output->wlr_output->transform_matrix);
494
495 if (ob_inner_width < texture_box.width) {
496 texture_box.width = ob_inner_width;
497 }
498 render_texture(output->wlr_output, output_damage, marks_texture,
499 NULL, &texture_box, matrix, con->alpha);
500
501 // Padding above
502 memcpy(&color, colors->background, sizeof(float) * 4);
503 premultiply_alpha(color, con->alpha);
504 box.x = texture_box.x + round(output_x * output_scale);
505 box.y = round((y + titlebar_border_thickness) * output_scale);
506 box.width = texture_box.width;
507 box.height = ob_padding_above;
508 render_rect(output, output_damage, &box, color);
509
510 // Padding below
511 box.y += ob_padding_above + texture_box.height;
512 box.height = ob_padding_below;
513 render_rect(output, output_damage, &box, color);
514 }
515
516 // Title text
517 int ob_title_x = 0; // output-buffer-local
518 int ob_title_width = 0; // output-buffer-local
519 if (title_texture) {
520 struct wlr_box texture_box;
521 wlr_texture_get_size(title_texture,
522 &texture_box.width, &texture_box.height);
523 ob_title_width = texture_box.width;
524
525 // The title texture might be shorter than the config->font_height,
526 // in which case we need to pad it above and below.
527 int ob_padding_above = round((config->font_baseline -
528 con->title_baseline + titlebar_v_padding -
529 titlebar_border_thickness) * output_scale);
530 int ob_padding_below = ob_bg_height - ob_padding_above -
531 texture_box.height;
532
533 // Render texture
534 if (texture_box.width > ob_inner_width - ob_marks_width) {
535 texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width)
536 ? ob_marks_x + ob_marks_width : ob_inner_x;
537 } else if (title_align == ALIGN_LEFT) {
538 texture_box.x = ob_inner_x;
539 } else if (title_align == ALIGN_CENTER) {
540 // If there are marks visible, center between the edge and marks.
541 // Otherwise, center in the inner area.
542 if (ob_marks_width) {
543 texture_box.x = (ob_inner_x + ob_marks_x) / 2
544 - texture_box.width / 2;
545 } else {
546 texture_box.x = ob_inner_x + ob_inner_width / 2
547 - texture_box.width / 2;
548 }
549 } else {
550 texture_box.x = ob_inner_x + ob_inner_width - texture_box.width;
551 }
552 ob_title_x = texture_box.x;
553
554 texture_box.y =
555 round((bg_y - output_y) * output_scale) + ob_padding_above;
556
557 float matrix[9];
558 wlr_matrix_project_box(matrix, &texture_box,
559 WL_OUTPUT_TRANSFORM_NORMAL,
560 0.0, output->wlr_output->transform_matrix);
561
562 if (ob_inner_width - ob_marks_width < texture_box.width) {
563 texture_box.width = ob_inner_width - ob_marks_width;
564 }
565
566 render_texture(output->wlr_output, output_damage, title_texture,
567 NULL, &texture_box, matrix, con->alpha);
568
569 // Padding above
570 memcpy(&color, colors->background, sizeof(float) * 4);
571 premultiply_alpha(color, con->alpha);
572 box.x = texture_box.x + round(output_x * output_scale);
573 box.y = round((y + titlebar_border_thickness) * output_scale);
574 box.width = texture_box.width;
575 box.height = ob_padding_above;
576 render_rect(output, output_damage, &box, color);
577
578 // Padding below
579 box.y += ob_padding_above + texture_box.height;
580 box.height = ob_padding_below;
581 render_rect(output, output_damage, &box, color);
582 }
583
584 // Determine the left + right extends of the textures (output-buffer local)
585 int ob_left_x, ob_left_width, ob_right_x, ob_right_width;
586 if (ob_title_width == 0 && ob_marks_width == 0) {
587 ob_left_x = ob_inner_x;
588 ob_left_width = 0;
589 ob_right_x = ob_inner_x;
590 ob_right_width = 0;
591 } else if (ob_title_x < ob_marks_x) {
592 ob_left_x = ob_title_x;
593 ob_left_width = ob_title_width;
594 ob_right_x = ob_marks_x;
595 ob_right_width = ob_marks_width;
596 } else {
597 ob_left_x = ob_marks_x;
598 ob_left_width = ob_marks_width;
599 ob_right_x = ob_title_x;
600 ob_right_width = ob_title_width;
601 }
602 if (ob_left_x < ob_inner_x) {
603 ob_left_x = ob_inner_x;
604 } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) {
605 ob_right_x = ob_left_x;
606 ob_right_width = ob_left_width;
607 }
608
609 // Filler between title and marks
610 box.width = ob_right_x - ob_left_x - ob_left_width;
611 if (box.width > 0) {
612 box.x = ob_left_x + ob_left_width + round(output_x * output_scale);
613 box.y = round(bg_y * output_scale);
614 box.height = ob_bg_height;
615 render_rect(output, output_damage, &box, color);
616 }
617
618 // Padding on left side
619 box.x = x + titlebar_border_thickness;
620 box.y = y + titlebar_border_thickness;
621 box.width = titlebar_h_padding - titlebar_border_thickness;
622 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
623 config->font_height;
624 scale_box(&box, output_scale);
625 int left_x = ob_left_x + round(output_x * output_scale);
626 if (box.x + box.width < left_x) {
627 box.width += left_x - box.x - box.width;
628 }
629 render_rect(output, output_damage, &box, color);
630
631 // Padding on right side
632 box.x = x + width - titlebar_h_padding;
633 box.y = y + titlebar_border_thickness;
634 box.width = titlebar_h_padding - titlebar_border_thickness;
635 box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 +
636 config->font_height;
637 scale_box(&box, output_scale);
638 int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale);
639 if (right_rx < box.x) {
640 box.width += box.x - right_rx;
641 box.x = right_rx;
642 }
643 render_rect(output, output_damage, &box, color);
644}
645
646/**
647 * Render the top border line for a view using "border pixel".
648 */
649static void render_top_border(struct sway_output *output,
650 pixman_region32_t *output_damage, struct sway_container *con,
651 struct border_colors *colors) {
652 struct sway_container_state *state = &con->current;
653 if (!state->border_top) {
654 return;
655 }
656 struct wlr_box box;
657 float color[4];
658 float output_scale = output->wlr_output->scale;
659
660 // Child border - top edge
661 memcpy(&color, colors->child_border, sizeof(float) * 4);
662 premultiply_alpha(color, con->alpha);
663 box.x = state->x;
664 box.y = state->y;
665 box.width = state->width;
666 box.height = state->border_thickness;
667 scale_box(&box, output_scale);
668 render_rect(output, output_damage, &box, color);
669}
670
671struct parent_data {
672 enum sway_container_layout layout;
673 struct wlr_box box;
674 list_t *children;
675 bool focused;
676 struct sway_container *active_child;
677};
678
679static void render_container(struct sway_output *output,
680 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
681
682/**
683 * Render a container's children using a L_HORIZ or L_VERT layout.
684 *
685 * Wrap child views in borders and leave child containers borderless because
686 * they'll apply their own borders to their children.
687 */
688static void render_containers_linear(struct sway_output *output,
689 pixman_region32_t *damage, struct parent_data *parent) {
690 for (int i = 0; i < parent->children->length; ++i) {
691 struct sway_container *child = parent->children->items[i];
692
693 if (child->view) {
694 struct sway_view *view = child->view;
695 struct border_colors *colors;
696 struct wlr_texture *title_texture;
697 struct wlr_texture *marks_texture;
698 struct sway_container_state *state = &child->current;
699
700 if (view_is_urgent(view)) {
701 colors = &config->border_colors.urgent;
702 title_texture = child->title_urgent;
703 marks_texture = child->marks_urgent;
704 } else if (state->focused || parent->focused) {
705 colors = &config->border_colors.focused;
706 title_texture = child->title_focused;
707 marks_texture = child->marks_focused;
708 } else if (child == parent->active_child) {
709 colors = &config->border_colors.focused_inactive;
710 title_texture = child->title_focused_inactive;
711 marks_texture = child->marks_focused_inactive;
712 } else {
713 colors = &config->border_colors.unfocused;
714 title_texture = child->title_unfocused;
715 marks_texture = child->marks_unfocused;
716 }
717
718 if (state->border == B_NORMAL) {
719 render_titlebar(output, damage, child, state->x,
720 state->y, state->width, colors,
721 title_texture, marks_texture);
722 } else if (state->border == B_PIXEL) {
723 render_top_border(output, damage, child, colors);
724 }
725 render_view(output, damage, child, colors);
726 } else {
727 render_container(output, damage, child,
728 parent->focused || child->current.focused);
729 }
730 }
731}
732
733/**
734 * Render a container's children using the L_TABBED layout.
735 */
736static void render_containers_tabbed(struct sway_output *output,
737 pixman_region32_t *damage, struct parent_data *parent) {
738 if (!parent->children->length) {
739 return;
740 }
741 struct sway_container *current = parent->active_child;
742 struct border_colors *current_colors = &config->border_colors.unfocused;
743 int tab_width = parent->box.width / parent->children->length;
744
745 // Render tabs
746 for (int i = 0; i < parent->children->length; ++i) {
747 struct sway_container *child = parent->children->items[i];
748 struct sway_view *view = child->view;
749 struct sway_container_state *cstate = &child->current;
750 struct border_colors *colors;
751 struct wlr_texture *title_texture;
752 struct wlr_texture *marks_texture;
753 bool urgent = view ?
754 view_is_urgent(view) : container_has_urgent_child(child);
755
756 if (urgent) {
757 colors = &config->border_colors.urgent;
758 title_texture = child->title_urgent;
759 marks_texture = child->marks_urgent;
760 } else if (cstate->focused || parent->focused) {
761 colors = &config->border_colors.focused;
762 title_texture = child->title_focused;
763 marks_texture = child->marks_focused;
764 } else if (child == parent->active_child) {
765 colors = &config->border_colors.focused_inactive;
766 title_texture = child->title_focused_inactive;
767 marks_texture = child->marks_focused_inactive;
768 } else {
769 colors = &config->border_colors.unfocused;
770 title_texture = child->title_unfocused;
771 marks_texture = child->marks_unfocused;
772 }
773
774 int x = cstate->x + tab_width * i;
775
776 // Make last tab use the remaining width of the parent
777 if (i == parent->children->length - 1) {
778 tab_width = parent->box.width - tab_width * i;
779 }
780
781 render_titlebar(output, damage, child, x, parent->box.y, tab_width,
782 colors, title_texture, marks_texture);
783
784 if (child == current) {
785 current_colors = colors;
786 }
787 }
788
789 // Render surface and left/right/bottom borders
790 if (current->view) {
791 render_view(output, damage, current, current_colors);
792 } else {
793 render_container(output, damage, current,
794 parent->focused || current->current.focused);
795 }
796}
797
798/**
799 * Render a container's children using the L_STACKED layout.
800 */
801static void render_containers_stacked(struct sway_output *output,
802 pixman_region32_t *damage, struct parent_data *parent) {
803 if (!parent->children->length) {
804 return;
805 }
806 struct sway_container *current = parent->active_child;
807 struct border_colors *current_colors = &config->border_colors.unfocused;
808 size_t titlebar_height = container_titlebar_height();
809
810 // Render titles
811 for (int i = 0; i < parent->children->length; ++i) {
812 struct sway_container *child = parent->children->items[i];
813 struct sway_view *view = child->view;
814 struct sway_container_state *cstate = &child->current;
815 struct border_colors *colors;
816 struct wlr_texture *title_texture;
817 struct wlr_texture *marks_texture;
818 bool urgent = view ?
819 view_is_urgent(view) : container_has_urgent_child(child);
820
821 if (urgent) {
822 colors = &config->border_colors.urgent;
823 title_texture = child->title_urgent;
824 marks_texture = child->marks_urgent;
825 } else if (cstate->focused || parent->focused) {
826 colors = &config->border_colors.focused;
827 title_texture = child->title_focused;
828 marks_texture = child->marks_focused;
829 } else if (child == parent->active_child) {
830 colors = &config->border_colors.focused_inactive;
831 title_texture = child->title_focused_inactive;
832 marks_texture = child->marks_focused_inactive;
833 } else {
834 colors = &config->border_colors.unfocused;
835 title_texture = child->title_unfocused;
836 marks_texture = child->marks_unfocused;
837 }
838
839 int y = parent->box.y + titlebar_height * i;
840 render_titlebar(output, damage, child, parent->box.x, y,
841 parent->box.width, colors, title_texture, marks_texture);
842
843 if (child == current) {
844 current_colors = colors;
845 }
846 }
847
848 // Render surface and left/right/bottom borders
849 if (current->view) {
850 render_view(output, damage, current, current_colors);
851 } else {
852 render_container(output, damage, current,
853 parent->focused || current->current.focused);
854 }
855}
856
857static void render_containers(struct sway_output *output,
858 pixman_region32_t *damage, struct parent_data *parent) {
859 if (config->hide_lone_tab && parent->children->length == 1) {
860 struct sway_container *child = parent->children->items[0];
861 if (child->view) {
862 render_containers_linear(output,damage, parent);
863 return;
864 }
865 }
866
867 switch (parent->layout) {
868 case L_NONE:
869 case L_HORIZ:
870 case L_VERT:
871 render_containers_linear(output, damage, parent);
872 break;
873 case L_STACKED:
874 render_containers_stacked(output, damage, parent);
875 break;
876 case L_TABBED:
877 render_containers_tabbed(output, damage, parent);
878 break;
879 }
880}
881
882static void render_container(struct sway_output *output,
883 pixman_region32_t *damage, struct sway_container *con, bool focused) {
884 struct parent_data data = {
885 .layout = con->current.layout,
886 .box = {
887 .x = con->current.x,
888 .y = con->current.y,
889 .width = con->current.width,
890 .height = con->current.height,
891 },
892 .children = con->current.children,
893 .focused = focused,
894 .active_child = con->current.focused_inactive_child,
895 };
896 render_containers(output, damage, &data);
897}
898
899static void render_workspace(struct sway_output *output,
900 pixman_region32_t *damage, struct sway_workspace *ws, bool focused) {
901 struct parent_data data = {
902 .layout = ws->current.layout,
903 .box = {
904 .x = ws->current.x,
905 .y = ws->current.y,
906 .width = ws->current.width,
907 .height = ws->current.height,
908 },
909 .children = ws->current.tiling,
910 .focused = focused,
911 .active_child = ws->current.focused_inactive_child,
912 };
913 render_containers(output, damage, &data);
914}
915
916static void render_floating_container(struct sway_output *soutput,
917 pixman_region32_t *damage, struct sway_container *con) {
918 if (con->view) {
919 struct sway_view *view = con->view;
920 struct border_colors *colors;
921 struct wlr_texture *title_texture;
922 struct wlr_texture *marks_texture;
923
924 if (view_is_urgent(view)) {
925 colors = &config->border_colors.urgent;
926 title_texture = con->title_urgent;
927 marks_texture = con->marks_urgent;
928 } else if (con->current.focused) {
929 colors = &config->border_colors.focused;
930 title_texture = con->title_focused;
931 marks_texture = con->marks_focused;
932 } else {
933 colors = &config->border_colors.unfocused;
934 title_texture = con->title_unfocused;
935 marks_texture = con->marks_unfocused;
936 }
937
938 if (con->current.border == B_NORMAL) {
939 render_titlebar(soutput, damage, con, con->current.x,
940 con->current.y, con->current.width, colors,
941 title_texture, marks_texture);
942 } else if (con->current.border == B_PIXEL) {
943 render_top_border(soutput, damage, con, colors);
944 }
945 render_view(soutput, damage, con, colors);
946 } else {
947 render_container(soutput, damage, con, con->current.focused);
948 }
949}
950
951static void render_floating(struct sway_output *soutput,
952 pixman_region32_t *damage) {
953 for (int i = 0; i < root->outputs->length; ++i) {
954 struct sway_output *output = root->outputs->items[i];
955 for (int j = 0; j < output->current.workspaces->length; ++j) {
956 struct sway_workspace *ws = output->current.workspaces->items[j];
957 if (!workspace_is_visible(ws)) {
958 continue;
959 }
960 for (int k = 0; k < ws->current.floating->length; ++k) {
961 struct sway_container *floater = ws->current.floating->items[k];
962 if (floater->fullscreen_mode != FULLSCREEN_NONE) {
963 continue;
964 }
965 render_floating_container(soutput, damage, floater);
966 }
967 }
968 }
969}
970
971static void render_seatops(struct sway_output *output,
972 pixman_region32_t *damage) {
973 struct sway_seat *seat;
974 wl_list_for_each(seat, &server.input->seats, link) {
975 seatop_render(seat, output, damage);
976 }
977}
978
979void output_render(struct sway_output *output, struct timespec *when,
980 pixman_region32_t *damage) {
981 struct wlr_output *wlr_output = output->wlr_output;
982
983 struct wlr_renderer *renderer =
984 wlr_backend_get_renderer(wlr_output->backend);
985 if (!sway_assert(renderer != NULL,
986 "expected the output backend to have a renderer")) {
987 return;
988 }
989
990 struct sway_workspace *workspace = output->current.active_workspace;
991 if (workspace == NULL) {
992 return;
993 }
994
995 struct sway_container *fullscreen_con = root->fullscreen_global;
996 if (!fullscreen_con) {
997 fullscreen_con = workspace->current.fullscreen;
998 }
999
1000 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
1001
1002 if (!pixman_region32_not_empty(damage)) {
1003 // Output isn't damaged but needs buffer swap
1004 goto renderer_end;
1005 }
1006
1007 if (debug.damage == DAMAGE_HIGHLIGHT) {
1008 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1009 } else if (debug.damage == DAMAGE_RERENDER) {
1010 int width, height;
1011 wlr_output_transformed_resolution(wlr_output, &width, &height);
1012 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1013 }
1014
1015 if (output_has_opaque_overlay_layer_surface(output)) {
1016 goto render_overlay;
1017 }
1018
1019 if (fullscreen_con) {
1020 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1021
1022 int nrects;
1023 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1024 for (int i = 0; i < nrects; ++i) {
1025 scissor_output(wlr_output, &rects[i]);
1026 wlr_renderer_clear(renderer, clear_color);
1027 }
1028
1029 if (fullscreen_con->view) {
1030 if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) {
1031 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
1032 } else if (fullscreen_con->view->surface) {
1033 render_view_toplevels(fullscreen_con->view,
1034 output, damage, 1.0f);
1035 }
1036 } else {
1037 render_container(output, damage, fullscreen_con,
1038 fullscreen_con->current.focused);
1039 }
1040
1041 for (int i = 0; i < workspace->current.floating->length; ++i) {
1042 struct sway_container *floater =
1043 workspace->current.floating->items[i];
1044 if (container_is_transient_for(floater, fullscreen_con)) {
1045 render_floating_container(output, damage, floater);
1046 }
1047 }
1048#if HAVE_XWAYLAND
1049 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1050#endif
1051 } else {
1052 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
1053
1054 int nrects;
1055 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1056 for (int i = 0; i < nrects; ++i) {
1057 scissor_output(wlr_output, &rects[i]);
1058 wlr_renderer_clear(renderer, clear_color);
1059 }
1060
1061 render_layer_toplevel(output, damage,
1062 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1063 render_layer_toplevel(output, damage,
1064 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1065
1066 render_workspace(output, damage, workspace, workspace->current.focused);
1067 render_floating(output, damage);
1068#if HAVE_XWAYLAND
1069 render_unmanaged(output, damage, &root->xwayland_unmanaged);
1070#endif
1071 render_layer_toplevel(output, damage,
1072 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1073
1074 render_layer_popups(output, damage,
1075 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1076 render_layer_popups(output, damage,
1077 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1078 render_layer_popups(output, damage,
1079 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1080 }
1081
1082 render_seatops(output, damage);
1083
1084 struct sway_seat *seat = input_manager_current_seat();
1085 struct sway_container *focus = seat_get_focused_container(seat);
1086 if (focus && focus->view) {
1087 render_view_popups(focus->view, output, damage, focus->alpha);
1088 }
1089
1090render_overlay:
1091 render_layer_toplevel(output, damage,
1092 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1093 render_layer_popups(output, damage,
1094 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1095 render_drag_icons(output, damage, &root->drag_icons);
1096
1097renderer_end:
1098 wlr_renderer_scissor(renderer, NULL);
1099 wlr_output_render_software_cursors(wlr_output, damage);
1100 wlr_renderer_end(renderer);
1101
1102 int width, height;
1103 wlr_output_transformed_resolution(wlr_output, &width, &height);
1104
1105 pixman_region32_t frame_damage;
1106 pixman_region32_init(&frame_damage);
1107
1108 enum wl_output_transform transform =
1109 wlr_output_transform_invert(wlr_output->transform);
1110 wlr_region_transform(&frame_damage, &output->damage->current,
1111 transform, width, height);
1112
1113 if (debug.damage == DAMAGE_HIGHLIGHT) {
1114 pixman_region32_union_rect(&frame_damage, &frame_damage,
1115 0, 0, wlr_output->width, wlr_output->height);
1116 }
1117
1118 wlr_output_set_damage(wlr_output, &frame_damage);
1119 pixman_region32_fini(&frame_damage);
1120
1121 if (!wlr_output_commit(wlr_output)) {
1122 return;
1123 }
1124 output->last_frame = *when;
1125}