diff options
author | Alexander Orzechowski <alex@ozal.ski> | 2023-03-03 21:13:56 -0500 |
---|---|---|
committer | Kirill Primak <vyivel@eclair.cafe> | 2024-01-18 18:36:54 +0300 |
commit | 869baff25221e1a1881e9559453faa43f90da33e (patch) | |
tree | 732c0c4cb4ea0efcaf407fd7e3766c767d057f45 /sway/desktop/render.c | |
parent | seat: Remove dead seatop_render function (diff) | |
download | sway-869baff25221e1a1881e9559453faa43f90da33e.tar.gz sway-869baff25221e1a1881e9559453faa43f90da33e.tar.zst sway-869baff25221e1a1881e9559453faa43f90da33e.zip |
renderer: Remove in favor of scene_graph
Diffstat (limited to 'sway/desktop/render.c')
-rw-r--r-- | sway/desktop/render.c | 1081 |
1 files changed, 0 insertions, 1081 deletions
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 | |||
31 | struct render_data { | ||
32 | struct render_context *ctx; | ||
33 | const pixman_region32_t *damage; | ||
34 | float alpha; | ||
35 | struct wlr_box *clip_box; | ||
36 | }; | ||
37 | |||
38 | static 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 | |||
46 | static 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 | */ | ||
67 | static int scale_length(int length, int offset, float scale) { | ||
68 | return roundf((offset + length) * scale) - roundf(offset * scale); | ||
69 | } | ||
70 | |||
71 | static 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 | |||
82 | static 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 | |||
123 | damage_finish: | ||
124 | pixman_region32_fini(&damage); | ||
125 | } | ||
126 | |||
127 | static 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 | |||
158 | static 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 | |||
167 | static 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 | ||
177 | static 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 | ||
189 | void 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 | |||
220 | damage_finish: | ||
221 | pixman_region32_fini(&damage); | ||
222 | } | ||
223 | |||
224 | void 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 | |||
231 | static 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 | |||
254 | static 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 | |||
264 | static 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 | */ | ||
324 | static 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 | */ | ||
397 | static 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 | */ | ||
644 | static 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 | |||
665 | struct 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 | |||
673 | static 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 | */ | ||
682 | static 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 | |||
726 | static bool container_is_focused(struct sway_container *con, void *data) { | ||
727 | return con->current.focused; | ||
728 | } | ||
729 | |||
730 | static 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 | */ | ||
737 | static 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 | */ | ||
805 | static 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 | |||
864 | static 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 | |||
888 | static 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 | |||
905 | static 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 | |||
922 | static 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 | |||
957 | static 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 | |||
976 | void 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 | |||
1074 | render_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 | } | ||