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