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