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