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