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