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