aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/output.h41
-rw-r--r--sway/desktop/output.c917
-rw-r--r--sway/desktop/render.c893
-rw-r--r--sway/meson.build1
4 files changed, 957 insertions, 895 deletions
diff --git a/include/sway/output.h b/include/sway/output.h
index e6fe55c6..b6cda83c 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -38,6 +38,16 @@ struct sway_output {
38 } events; 38 } events;
39}; 39};
40 40
41/**
42 * Contains a surface's root geometry information. For instance, when rendering
43 * a popup, this will contain the parent view's position and size.
44 */
45struct root_geometry {
46 double x, y;
47 int width, height;
48 float rotation;
49};
50
41void output_damage_whole(struct sway_output *output); 51void output_damage_whole(struct sway_output *output);
42 52
43void output_damage_surface(struct sway_output *output, double ox, double oy, 53void output_damage_surface(struct sway_output *output, double ox, double oy,
@@ -56,6 +66,35 @@ struct sway_container *output_by_name(const char *name);
56void output_enable(struct sway_output *output); 66void output_enable(struct sway_output *output);
57 67
58bool output_has_opaque_lockscreen(struct sway_output *output, 68bool output_has_opaque_lockscreen(struct sway_output *output,
59 struct sway_seat *seat); 69 struct sway_seat *seat);
70
71struct sway_container *output_get_active_workspace(struct sway_output *output);
72
73void output_render(struct sway_output *output, struct timespec *when,
74 pixman_region32_t *damage);
75
76bool output_get_surface_box(struct root_geometry *geo,
77 struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
78 struct wlr_box *surface_box);
79
80void output_surface_for_each_surface(struct wlr_surface *surface,
81 double ox, double oy, struct root_geometry *geo,
82 wlr_surface_iterator_func_t iterator, void *user_data);
83
84void output_view_for_each_surface(struct sway_view *view,
85 struct sway_output *output, struct root_geometry *geo,
86 wlr_surface_iterator_func_t iterator, void *user_data);
87
88void output_layer_for_each_surface(struct wl_list *layer_surfaces,
89 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
90 void *user_data);
91
92void output_unmanaged_for_each_surface(struct wl_list *unmanaged,
93 struct sway_output *output, struct root_geometry *geo,
94 wlr_surface_iterator_func_t iterator, void *user_data);
95
96void output_drag_icons_for_each_surface(struct wl_list *drag_icons,
97 struct sway_output *output, struct root_geometry *geo,
98 wlr_surface_iterator_func_t iterator, void *user_data);
60 99
61#endif 100#endif
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 336163ea..8b50bc44 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -56,25 +56,7 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh,
56 *sy = ry + ph/2 - sh/2; 56 *sy = ry + ph/2 - sh/2;
57} 57}
58 58
59/** 59bool output_get_surface_box(struct root_geometry *geo,
60 * Contains a surface's root geometry information. For instance, when rendering
61 * a popup, this will contain the parent view's position and size.
62 */
63struct root_geometry {
64 double x, y;
65 int width, height;
66 float rotation;
67};
68
69struct render_data {
70 struct root_geometry root_geo;
71 struct sway_output *output;
72 pixman_region32_t *damage;
73 struct sway_view *view;
74 float alpha;
75};
76
77static bool get_surface_box(struct root_geometry *geo,
78 struct sway_output *output, struct wlr_surface *surface, int sx, int sy, 60 struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
79 struct wlr_box *surface_box) { 61 struct wlr_box *surface_box) {
80 if (!wlr_surface_has_buffer(surface)) { 62 if (!wlr_surface_has_buffer(surface)) {
@@ -110,7 +92,7 @@ static bool get_surface_box(struct root_geometry *geo,
110 return wlr_box_intersection(&output_box, &rotated_box, &intersection); 92 return wlr_box_intersection(&output_box, &rotated_box, &intersection);
111} 93}
112 94
113static void surface_for_each_surface(struct wlr_surface *surface, 95void output_surface_for_each_surface(struct wlr_surface *surface,
114 double ox, double oy, struct root_geometry *geo, 96 double ox, double oy, struct root_geometry *geo,
115 wlr_surface_iterator_func_t iterator, void *user_data) { 97 wlr_surface_iterator_func_t iterator, void *user_data) {
116 geo->x = ox; 98 geo->x = ox;
@@ -122,12 +104,11 @@ static void surface_for_each_surface(struct wlr_surface *surface,
122 wlr_surface_for_each_surface(surface, iterator, user_data); 104 wlr_surface_for_each_surface(surface, iterator, user_data);
123} 105}
124 106
125static void output_view_for_each_surface(struct sway_view *view, 107void output_view_for_each_surface(struct sway_view *view,
126 struct root_geometry *geo, wlr_surface_iterator_func_t iterator, 108 struct sway_output *output, struct root_geometry *geo,
127 void *user_data) { 109 wlr_surface_iterator_func_t iterator, void *user_data) {
128 struct render_data *data = user_data; 110 geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x;
129 geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x; 111 geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y;
130 geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y;
131 geo->width = view->swayc->current.view_width; 112 geo->width = view->swayc->current.view_width;
132 geo->height = view->swayc->current.view_height; 113 geo->height = view->swayc->current.view_height;
133 geo->rotation = 0; // TODO 114 geo->rotation = 0; // TODO
@@ -135,20 +116,20 @@ static void output_view_for_each_surface(struct sway_view *view,
135 view_for_each_surface(view, iterator, user_data); 116 view_for_each_surface(view, iterator, user_data);
136} 117}
137 118
138static void layer_for_each_surface(struct wl_list *layer_surfaces, 119void output_layer_for_each_surface(struct wl_list *layer_surfaces,
139 struct root_geometry *geo, wlr_surface_iterator_func_t iterator, 120 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
140 void *user_data) { 121 void *user_data) {
141 struct sway_layer_surface *layer_surface; 122 struct sway_layer_surface *layer_surface;
142 wl_list_for_each(layer_surface, layer_surfaces, link) { 123 wl_list_for_each(layer_surface, layer_surfaces, link) {
143 struct wlr_layer_surface *wlr_layer_surface = 124 struct wlr_layer_surface *wlr_layer_surface =
144 layer_surface->layer_surface; 125 layer_surface->layer_surface;
145 surface_for_each_surface(wlr_layer_surface->surface, 126 output_surface_for_each_surface(wlr_layer_surface->surface,
146 layer_surface->geo.x, layer_surface->geo.y, geo, iterator, 127 layer_surface->geo.x, layer_surface->geo.y, geo, iterator,
147 user_data); 128 user_data);
148 } 129 }
149} 130}
150 131
151static void unmanaged_for_each_surface(struct wl_list *unmanaged, 132void output_unmanaged_for_each_surface(struct wl_list *unmanaged,
152 struct sway_output *output, struct root_geometry *geo, 133 struct sway_output *output, struct root_geometry *geo,
153 wlr_surface_iterator_func_t iterator, void *user_data) { 134 wlr_surface_iterator_func_t iterator, void *user_data) {
154 struct sway_xwayland_unmanaged *unmanaged_surface; 135 struct sway_xwayland_unmanaged *unmanaged_surface;
@@ -158,12 +139,12 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged,
158 double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; 139 double ox = unmanaged_surface->lx - output->swayc->current.swayc_x;
159 double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; 140 double oy = unmanaged_surface->ly - output->swayc->current.swayc_y;
160 141
161 surface_for_each_surface(xsurface->surface, ox, oy, geo, 142 output_surface_for_each_surface(xsurface->surface, ox, oy, geo,
162 iterator, user_data); 143 iterator, user_data);
163 } 144 }
164} 145}
165 146
166static void drag_icons_for_each_surface(struct wl_list *drag_icons, 147void output_drag_icons_for_each_surface(struct wl_list *drag_icons,
167 struct sway_output *output, struct root_geometry *geo, 148 struct sway_output *output, struct root_geometry *geo,
168 wlr_surface_iterator_func_t iterator, void *user_data) { 149 wlr_surface_iterator_func_t iterator, void *user_data) {
169 struct sway_drag_icon *drag_icon; 150 struct sway_drag_icon *drag_icon;
@@ -172,7 +153,7 @@ static void drag_icons_for_each_surface(struct wl_list *drag_icons,
172 double oy = drag_icon->y - output->swayc->y; 153 double oy = drag_icon->y - output->swayc->y;
173 154
174 if (drag_icon->wlr_drag_icon->mapped) { 155 if (drag_icon->wlr_drag_icon->mapped) {
175 surface_for_each_surface(drag_icon->wlr_drag_icon->surface, 156 output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
176 ox, oy, geo, iterator, user_data); 157 ox, oy, geo, iterator, user_data);
177 } 158 }
178 } 159 }
@@ -185,746 +166,7 @@ static void scale_box(struct wlr_box *box, float scale) {
185 box->height *= scale; 166 box->height *= scale;
186} 167}
187 168
188static void scissor_output(struct wlr_output *wlr_output, 169struct sway_container *output_get_active_workspace(struct sway_output *output) {
189 pixman_box32_t *rect) {
190 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
191 assert(renderer);
192
193 struct wlr_box box = {
194 .x = rect->x1,
195 .y = rect->y1,
196 .width = rect->x2 - rect->x1,
197 .height = rect->y2 - rect->y1,
198 };
199
200 int ow, oh;
201 wlr_output_transformed_resolution(wlr_output, &ow, &oh);
202
203 enum wl_output_transform transform =
204 wlr_output_transform_invert(wlr_output->transform);
205 wlr_box_transform(&box, transform, ow, oh, &box);
206
207 wlr_renderer_scissor(renderer, &box);
208}
209
210static void render_texture(struct wlr_output *wlr_output,
211 pixman_region32_t *output_damage, struct wlr_texture *texture,
212 const struct wlr_box *box, const float matrix[static 9], float alpha) {
213 struct wlr_renderer *renderer =
214 wlr_backend_get_renderer(wlr_output->backend);
215
216 pixman_region32_t damage;
217 pixman_region32_init(&damage);
218 pixman_region32_union_rect(&damage, &damage, box->x, box->y,
219 box->width, box->height);
220 pixman_region32_intersect(&damage, &damage, output_damage);
221 bool damaged = pixman_region32_not_empty(&damage);
222 if (!damaged) {
223 goto damage_finish;
224 }
225
226 int nrects;
227 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
228 for (int i = 0; i < nrects; ++i) {
229 scissor_output(wlr_output, &rects[i]);
230 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
231 }
232
233damage_finish:
234 pixman_region32_fini(&damage);
235}
236
237static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
238 void *_data) {
239 struct render_data *data = _data;
240 struct wlr_output *wlr_output = data->output->wlr_output;
241 float rotation = data->root_geo.rotation;
242 pixman_region32_t *output_damage = data->damage;
243 float alpha = data->alpha;
244
245 struct wlr_texture *texture = wlr_surface_get_texture(surface);
246 if (!texture) {
247 return;
248 }
249
250 struct wlr_box box;
251 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
252 sx, sy, &box);
253 if (!intersects) {
254 return;
255 }
256
257 scale_box(&box, wlr_output->scale);
258
259 float matrix[9];
260 enum wl_output_transform transform =
261 wlr_output_transform_invert(surface->current.transform);
262 wlr_matrix_project_box(matrix, &box, transform, rotation,
263 wlr_output->transform_matrix);
264
265 render_texture(wlr_output, output_damage, texture, &box, matrix, alpha);
266}
267
268static void render_layer(struct sway_output *output,
269 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
270 struct render_data data = {
271 .output = output,
272 .damage = damage,
273 .alpha = 1.0f,
274 };
275 layer_for_each_surface(layer_surfaces, &data.root_geo,
276 render_surface_iterator, &data);
277}
278
279static void render_unmanaged(struct sway_output *output,
280 pixman_region32_t *damage, struct wl_list *unmanaged) {
281 struct render_data data = {
282 .output = output,
283 .damage = damage,
284 .alpha = 1.0f,
285 };
286 unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
287 render_surface_iterator, &data);
288}
289
290static void render_drag_icons(struct sway_output *output,
291 pixman_region32_t *damage, struct wl_list *drag_icons) {
292 struct render_data data = {
293 .output = output,
294 .damage = damage,
295 .alpha = 1.0f,
296 };
297 drag_icons_for_each_surface(drag_icons, output, &data.root_geo,
298 render_surface_iterator, &data);
299}
300
301static void render_rect(struct wlr_output *wlr_output,
302 pixman_region32_t *output_damage, const struct wlr_box *_box,
303 float color[static 4]) {
304 struct wlr_renderer *renderer =
305 wlr_backend_get_renderer(wlr_output->backend);
306
307 struct wlr_box box;
308 memcpy(&box, _box, sizeof(struct wlr_box));
309 box.x -= wlr_output->lx * wlr_output->scale;
310 box.y -= wlr_output->ly * wlr_output->scale;
311
312 pixman_region32_t damage;
313 pixman_region32_init(&damage);
314 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
315 box.width, box.height);
316 pixman_region32_intersect(&damage, &damage, output_damage);
317 bool damaged = pixman_region32_not_empty(&damage);
318 if (!damaged) {
319 goto damage_finish;
320 }
321
322 int nrects;
323 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
324 for (int i = 0; i < nrects; ++i) {
325 scissor_output(wlr_output, &rects[i]);
326 wlr_render_rect(renderer, &box, color,
327 wlr_output->transform_matrix);
328 }
329
330damage_finish:
331 pixman_region32_fini(&damage);
332}
333
334static void premultiply_alpha(float color[4], float opacity) {
335 color[3] *= opacity;
336 color[0] *= color[3];
337 color[1] *= color[3];
338 color[2] *= color[3];
339}
340
341static void render_view_surfaces(struct sway_view *view,
342 struct sway_output *output, pixman_region32_t *damage, float alpha) {
343 struct render_data data = {
344 .output = output,
345 .damage = damage,
346 .view = view,
347 .alpha = alpha,
348 };
349 output_view_for_each_surface(
350 view, &data.root_geo, render_surface_iterator, &data);
351}
352
353static void render_saved_view(struct sway_view *view,
354 struct sway_output *output, pixman_region32_t *damage, float alpha) {
355 struct wlr_output *wlr_output = output->wlr_output;
356
357 int width, height;
358 struct wlr_texture *texture =
359 transaction_get_saved_texture(view, &width, &height);
360 if (!texture) {
361 return;
362 }
363 struct wlr_box box = {
364 .x = view->swayc->current.view_x - output->swayc->current.swayc_x,
365 .y = view->swayc->current.view_y - output->swayc->current.swayc_y,
366 .width = width,
367 .height = height,
368 };
369
370 struct wlr_box output_box = {
371 .width = output->swayc->current.swayc_width,
372 .height = output->swayc->current.swayc_height,
373 };
374
375 struct wlr_box intersection;
376 bool intersects = wlr_box_intersection(&output_box, &box, &intersection);
377 if (!intersects) {
378 return;
379 }
380
381 scale_box(&box, wlr_output->scale);
382
383 float matrix[9];
384 wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
385 wlr_output->transform_matrix);
386
387 render_texture(wlr_output, damage, texture, &box, matrix, alpha);
388}
389
390/**
391 * Render a view's surface and left/bottom/right borders.
392 */
393static void render_view(struct sway_output *output, pixman_region32_t *damage,
394 struct sway_container *con, struct border_colors *colors) {
395 struct sway_view *view = con->sway_view;
396 if (view->swayc->instructions->length) {
397 render_saved_view(view, output, damage, view->swayc->alpha);
398 } else {
399 render_view_surfaces(view, output, damage, view->swayc->alpha);
400 }
401
402 struct wlr_box box;
403 float output_scale = output->wlr_output->scale;
404 float color[4];
405 struct sway_container_state *state = &con->current;
406
407 if (state->border != B_NONE) {
408 if (state->border_left) {
409 memcpy(&color, colors->child_border, sizeof(float) * 4);
410 premultiply_alpha(color, con->alpha);
411 box.x = state->swayc_x;
412 box.y = state->view_y;
413 box.width = state->border_thickness;
414 box.height = state->view_height;
415 scale_box(&box, output_scale);
416 render_rect(output->wlr_output, damage, &box, color);
417 }
418
419 if (state->border_right) {
420 if (state->parent->current.children->length == 1
421 && state->parent->current.layout == L_HORIZ) {
422 memcpy(&color, colors->indicator, sizeof(float) * 4);
423 } else {
424 memcpy(&color, colors->child_border, sizeof(float) * 4);
425 }
426 premultiply_alpha(color, con->alpha);
427 box.x = state->view_x + state->view_width;
428 box.y = state->view_y;
429 box.width = state->border_thickness;
430 box.height = state->view_height;
431 scale_box(&box, output_scale);
432 render_rect(output->wlr_output, damage, &box, color);
433 }
434
435 if (state->border_bottom) {
436 if (state->parent->current.children->length == 1
437 && con->current.parent->current.layout == L_VERT) {
438 memcpy(&color, colors->indicator, sizeof(float) * 4);
439 } else {
440 memcpy(&color, colors->child_border, sizeof(float) * 4);
441 }
442 premultiply_alpha(color, con->alpha);
443 box.x = state->swayc_x;
444 box.y = state->view_y + state->view_height;
445 box.width = state->swayc_width;
446 box.height = state->border_thickness;
447 scale_box(&box, output_scale);
448 render_rect(output->wlr_output, damage, &box, color);
449 }
450 }
451}
452
453/**
454 * Render a titlebar.
455 *
456 * Care must be taken not to render over the same pixel multiple times,
457 * otherwise the colors will be incorrect when using opacity.
458 *
459 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
460 * The left side for L_TABBED is: 1px border, 2px padding, title
461 * The left side for other layouts is: 3px padding, title
462 */
463static void render_titlebar(struct sway_output *output,
464 pixman_region32_t *output_damage, struct sway_container *con,
465 int x, int y, int width,
466 struct border_colors *colors, struct wlr_texture *title_texture,
467 struct wlr_texture *marks_texture) {
468 struct wlr_box box;
469 float color[4];
470 struct sway_container_state *state = &con->current;
471 float output_scale = output->wlr_output->scale;
472 enum sway_container_layout layout = state->parent->current.layout;
473 list_t *children = state->parent->current.children;
474 bool is_last_child = children->items[children->length - 1] == con;
475 double output_x = output->swayc->current.swayc_x;
476 double output_y = output->swayc->current.swayc_y;
477
478 // Single pixel bar above title
479 memcpy(&color, colors->border, sizeof(float) * 4);
480 premultiply_alpha(color, con->alpha);
481 box.x = x;
482 box.y = y;
483 box.width = width;
484 box.height = TITLEBAR_BORDER_THICKNESS;
485 scale_box(&box, output_scale);
486 render_rect(output->wlr_output, output_damage, &box, color);
487
488 // Single pixel bar below title
489 size_t left_offset = 0, right_offset = 0;
490 bool connects_sides = false;
491 if (layout == L_HORIZ || layout == L_VERT ||
492 (layout == L_STACKED && is_last_child)) {
493 if (con->type == C_VIEW) {
494 left_offset = state->border_left * state->border_thickness;
495 right_offset = state->border_right * state->border_thickness;
496 connects_sides = true;
497 }
498 }
499 box.x = x + left_offset;
500 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
501 box.width = width - left_offset - right_offset;
502 box.height = TITLEBAR_BORDER_THICKNESS;
503 scale_box(&box, output_scale);
504 render_rect(output->wlr_output, output_damage, &box, color);
505
506 if (layout == L_TABBED) {
507 // Single pixel left edge
508 box.x = x;
509 box.y = y + TITLEBAR_BORDER_THICKNESS;
510 box.width = TITLEBAR_BORDER_THICKNESS;
511 box.height =
512 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2;
513 scale_box(&box, output_scale);
514 render_rect(output->wlr_output, output_damage, &box, color);
515
516 // Single pixel right edge
517 box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale;
518 render_rect(output->wlr_output, output_damage, &box, color);
519 }
520
521 size_t inner_width = width - TITLEBAR_H_PADDING * 2;
522
523 // Marks
524 size_t marks_ob_width = 0; // output-buffer-local
525 if (config->show_marks && marks_texture) {
526 struct wlr_box texture_box;
527 wlr_texture_get_size(marks_texture,
528 &texture_box.width, &texture_box.height);
529 texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING)
530 * output_scale - texture_box.width;
531 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
532
533 float matrix[9];
534 wlr_matrix_project_box(matrix, &texture_box,
535 WL_OUTPUT_TRANSFORM_NORMAL,
536 0.0, output->wlr_output->transform_matrix);
537
538 if (inner_width * output_scale < texture_box.width) {
539 texture_box.width = inner_width * output_scale;
540 }
541 render_texture(output->wlr_output, output_damage, marks_texture,
542 &texture_box, matrix, con->alpha);
543 marks_ob_width = texture_box.width;
544
545 // Gap between the marks and bottom padding, for when the marks texture
546 // height is smaller than the config's font height
547 memcpy(&color, colors->background, sizeof(float) * 4);
548 premultiply_alpha(color, con->alpha);
549 box.x = texture_box.x;
550 box.y = texture_box.y + texture_box.height;
551 box.width = texture_box.width;
552 box.height = config->font_height * output_scale - texture_box.height;
553 if (box.height > 0) {
554 render_rect(output->wlr_output, output_damage, &box, color);
555 }
556 }
557
558 // Title text
559 size_t title_ob_width = 0; // output-buffer-local
560 if (title_texture) {
561 struct wlr_box texture_box;
562 wlr_texture_get_size(title_texture,
563 &texture_box.width, &texture_box.height);
564 texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale;
565 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
566
567 float matrix[9];
568 wlr_matrix_project_box(matrix, &texture_box,
569 WL_OUTPUT_TRANSFORM_NORMAL,
570 0.0, output->wlr_output->transform_matrix);
571
572 if (inner_width * output_scale - marks_ob_width < texture_box.width) {
573 texture_box.width = inner_width * output_scale - marks_ob_width;
574 }
575 render_texture(output->wlr_output, output_damage, title_texture,
576 &texture_box, matrix, con->alpha);
577 title_ob_width = texture_box.width;
578
579 // Gap between the title and bottom padding, for when the title texture
580 // height is smaller than the config's font height
581 memcpy(&color, colors->background, sizeof(float) * 4);
582 premultiply_alpha(color, con->alpha);
583 box.x = texture_box.x;
584 box.y = texture_box.y + texture_box.height;
585 box.width = texture_box.width;
586 box.height = config->font_height * output_scale - texture_box.height;
587 if (box.height > 0) {
588 render_rect(output->wlr_output, output_damage, &box, color);
589 }
590 }
591
592 // Padding above title
593 memcpy(&color, colors->background, sizeof(float) * 4);
594 premultiply_alpha(color, con->alpha);
595 box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
596 box.y = y + TITLEBAR_BORDER_THICKNESS;
597 box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2;
598 box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS;
599 scale_box(&box, output_scale);
600 render_rect(output->wlr_output, output_damage, &box, color);
601
602 // Padding below title
603 box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale;
604 render_rect(output->wlr_output, output_damage, &box, color);
605
606 // Filler between title and marks
607 box.width = inner_width * output_scale - title_ob_width - marks_ob_width;
608 if (box.width > 0) {
609 box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width;
610 box.y = (y + TITLEBAR_V_PADDING) * output_scale;
611 box.height = config->font_height * output_scale;
612 render_rect(output->wlr_output, output_damage, &box, color);
613 }
614
615 // Padding left of title
616 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
617 box.x = x + left_offset;
618 box.y = y + TITLEBAR_V_PADDING;
619 box.width = TITLEBAR_H_PADDING - left_offset;
620 box.height = config->font_height;
621 scale_box(&box, output_scale);
622 render_rect(output->wlr_output, output_damage, &box, color);
623
624 // Padding right of marks
625 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
626 box.x = x + width - TITLEBAR_H_PADDING;
627 box.y = y + TITLEBAR_V_PADDING;
628 box.width = TITLEBAR_H_PADDING - right_offset;
629 box.height = config->font_height;
630 scale_box(&box, output_scale);
631 render_rect(output->wlr_output, output_damage, &box, color);
632
633 if (connects_sides) {
634 // Left pixel in line with bottom bar
635 box.x = x;
636 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
637 box.width = state->border_thickness * state->border_left;
638 box.height = TITLEBAR_BORDER_THICKNESS;
639 scale_box(&box, output_scale);
640 render_rect(output->wlr_output, output_damage, &box, color);
641
642 // Right pixel in line with bottom bar
643 box.x = x + width - state->border_thickness * state->border_right;
644 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
645 box.width = state->border_thickness * state->border_right;
646 box.height = TITLEBAR_BORDER_THICKNESS;
647 scale_box(&box, output_scale);
648 render_rect(output->wlr_output, output_damage, &box, color);
649 }
650}
651
652/**
653 * Render the top border line for a view using "border pixel".
654 */
655static void render_top_border(struct sway_output *output,
656 pixman_region32_t *output_damage, struct sway_container *con,
657 struct border_colors *colors) {
658 struct sway_container_state *state = &con->current;
659 if (!state->border_top) {
660 return;
661 }
662 struct wlr_box box;
663 float color[4];
664 float output_scale = output->wlr_output->scale;
665
666 // Child border - top edge
667 memcpy(&color, colors->child_border, sizeof(float) * 4);
668 premultiply_alpha(color, con->alpha);
669 box.x = state->swayc_x;
670 box.y = state->swayc_y;
671 box.width = state->swayc_width;
672 box.height = state->border_thickness;
673 scale_box(&box, output_scale);
674 render_rect(output->wlr_output, output_damage, &box, color);
675}
676
677static void render_container(struct sway_output *output,
678 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
679
680/**
681 * Render a container's children using a L_HORIZ or L_VERT layout.
682 *
683 * Wrap child views in borders and leave child containers borderless because
684 * they'll apply their own borders to their children.
685 */
686static void render_container_simple(struct sway_output *output,
687 pixman_region32_t *damage, struct sway_container *con,
688 bool parent_focused) {
689 struct sway_seat *seat = input_manager_current_seat(input_manager);
690 struct sway_container *focus = seat_get_focus(seat);
691
692 for (int i = 0; i < con->current.children->length; ++i) {
693 struct sway_container *child = con->current.children->items[i];
694
695 if (child->type == C_VIEW) {
696 struct sway_view *view = child->sway_view;
697 struct border_colors *colors;
698 struct wlr_texture *title_texture;
699 struct wlr_texture *marks_texture;
700 struct sway_container_state *state = &child->current;
701
702 if (focus == child || parent_focused) {
703 colors = &config->border_colors.focused;
704 title_texture = child->title_focused;
705 marks_texture = view->marks_focused;
706 } else if (seat_get_focus_inactive(seat, con) == child) {
707 colors = &config->border_colors.focused_inactive;
708 title_texture = child->title_focused_inactive;
709 marks_texture = view->marks_focused_inactive;
710 } else {
711 colors = &config->border_colors.unfocused;
712 title_texture = child->title_unfocused;
713 marks_texture = view->marks_unfocused;
714 }
715
716 if (state->border == B_NORMAL) {
717 render_titlebar(output, damage, child, state->swayc_x,
718 state->swayc_y, state->swayc_width, colors,
719 title_texture, marks_texture);
720 } else {
721 render_top_border(output, damage, child, colors);
722 }
723 render_view(output, damage, child, colors);
724 } else {
725 render_container(output, damage, child,
726 parent_focused || focus == child);
727 }
728 }
729}
730
731/**
732 * Render a container's children using the L_TABBED layout.
733 */
734static void render_container_tabbed(struct sway_output *output,
735 pixman_region32_t *damage, struct sway_container *con,
736 bool parent_focused) {
737 if (!con->current.children->length) {
738 return;
739 }
740 struct sway_seat *seat = input_manager_current_seat(input_manager);
741 struct sway_container *focus = seat_get_focus(seat);
742 struct sway_container *current = seat_get_active_current_child(seat, con);
743 struct border_colors *current_colors = &config->border_colors.unfocused;
744 struct sway_container_state *pstate = &con->current;
745
746 // Render tabs
747 for (int i = 0; i < con->current.children->length; ++i) {
748 struct sway_container *child = con->current.children->items[i];
749 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
750 struct sway_container_state *cstate = &child->current;
751 struct border_colors *colors;
752 struct wlr_texture *title_texture;
753 struct wlr_texture *marks_texture;
754
755 if (focus == child || parent_focused) {
756 colors = &config->border_colors.focused;
757 title_texture = child->title_focused;
758 marks_texture = view ? view->marks_focused : NULL;
759 } else if (child == current) {
760 colors = &config->border_colors.focused_inactive;
761 title_texture = child->title_focused_inactive;
762 marks_texture = view ? view->marks_focused_inactive : NULL;
763 } else {
764 colors = &config->border_colors.unfocused;
765 title_texture = child->title_unfocused;
766 marks_texture = view ? view->marks_unfocused : NULL;
767 }
768
769 int tab_width = pstate->swayc_width / pstate->children->length;
770 int x = pstate->swayc_x + tab_width * i;
771 // Make last tab use the remaining width of the parent
772 if (i == pstate->children->length - 1) {
773 tab_width = pstate->swayc_width - tab_width * i;
774 }
775
776 render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
777 colors, title_texture, marks_texture);
778
779 if (child == current) {
780 current_colors = colors;
781 }
782 }
783
784 // Render surface and left/right/bottom borders
785 if (current) {
786 if (current->type == C_VIEW) {
787 render_view(output, damage, current, current_colors);
788 } else {
789 render_container(output, damage, current,
790 parent_focused || current == focus);
791 }
792 }
793}
794
795/**
796 * Render a container's children using the L_STACKED layout.
797 */
798static void render_container_stacked(struct sway_output *output,
799 pixman_region32_t *damage, struct sway_container *con,
800 bool parent_focused) {
801 if (!con->current.children->length) {
802 return;
803 }
804 struct sway_seat *seat = input_manager_current_seat(input_manager);
805 struct sway_container *focus = seat_get_focus(seat);
806 struct sway_container *current = seat_get_active_current_child(seat, con);
807 struct border_colors *current_colors = &config->border_colors.unfocused;
808 struct sway_container_state *pstate = &con->current;
809
810 // Render titles
811 for (int i = 0; i < con->current.children->length; ++i) {
812 struct sway_container *child = con->current.children->items[i];
813 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
814 struct sway_container_state *cstate = &child->current;
815 struct border_colors *colors;
816 struct wlr_texture *title_texture;
817 struct wlr_texture *marks_texture;
818
819 if (focus == child || parent_focused) {
820 colors = &config->border_colors.focused;
821 title_texture = child->title_focused;
822 marks_texture = view ? view->marks_focused : NULL;
823 } else if (child == current) {
824 colors = &config->border_colors.focused_inactive;
825 title_texture = child->title_focused_inactive;
826 marks_texture = view ? view->marks_focused_inactive : NULL;
827 } else {
828 colors = &config->border_colors.unfocused;
829 title_texture = child->title_unfocused;
830 marks_texture = view ? view->marks_unfocused : NULL;
831 }
832
833 int y = pstate->swayc_y + container_titlebar_height() * i;
834 render_titlebar(output, damage, child, cstate->swayc_x, y,
835 cstate->swayc_width, colors, title_texture, marks_texture);
836
837 if (child == current) {
838 current_colors = colors;
839 }
840 }
841
842 // Render surface and left/right/bottom borders
843 if (current) {
844 if (current->type == C_VIEW) {
845 render_view(output, damage, current, current_colors);
846 } else {
847 render_container(output, damage, current,
848 parent_focused || current == focus);
849 }
850 }
851}
852
853static void render_container(struct sway_output *output,
854 pixman_region32_t *damage, struct sway_container *con,
855 bool parent_focused) {
856 switch (con->current.layout) {
857 case L_NONE:
858 case L_HORIZ:
859 case L_VERT:
860 render_container_simple(output, damage, con, parent_focused);
861 break;
862 case L_STACKED:
863 render_container_stacked(output, damage, con, parent_focused);
864 break;
865 case L_TABBED:
866 render_container_tabbed(output, damage, con, parent_focused);
867 break;
868 case L_FLOATING:
869 sway_assert(false, "Didn't expect to see floating here");
870 }
871}
872
873static void render_floating_container(struct sway_output *soutput,
874 pixman_region32_t *damage, struct sway_container *con) {
875 if (con->type == C_VIEW) {
876 struct sway_view *view = con->sway_view;
877 struct sway_seat *seat = input_manager_current_seat(input_manager);
878 struct sway_container *focus = seat_get_focus(seat);
879 struct border_colors *colors;
880 struct wlr_texture *title_texture;
881 struct wlr_texture *marks_texture;
882
883 if (focus == con) {
884 colors = &config->border_colors.focused;
885 title_texture = con->title_focused;
886 marks_texture = view->marks_focused;
887 } else {
888 colors = &config->border_colors.unfocused;
889 title_texture = con->title_unfocused;
890 marks_texture = view->marks_unfocused;
891 }
892
893 if (con->current.border == B_NORMAL) {
894 render_titlebar(soutput, damage, con, con->current.swayc_x,
895 con->current.swayc_y, con->current.swayc_width, colors,
896 title_texture, marks_texture);
897 } else if (con->current.border != B_NONE) {
898 render_top_border(soutput, damage, con, colors);
899 }
900 render_view(soutput, damage, con, colors);
901 } else {
902 render_container(soutput, damage, con, false);
903 }
904}
905
906static void render_floating(struct sway_output *soutput,
907 pixman_region32_t *damage) {
908 for (int i = 0; i < root_container.current.children->length; ++i) {
909 struct sway_container *output =
910 root_container.current.children->items[i];
911 for (int j = 0; j < output->current.children->length; ++j) {
912 struct sway_container *ws = output->current.children->items[j];
913 if (!workspace_is_visible(ws)) {
914 continue;
915 }
916 list_t *floating =
917 ws->current.ws_floating->current.children;
918 for (int k = 0; k < floating->length; ++k) {
919 struct sway_container *floater = floating->items[k];
920 render_floating_container(soutput, damage, floater);
921 }
922 }
923 }
924}
925
926static struct sway_container *output_get_active_workspace(
927 struct sway_output *output) {
928 struct sway_seat *seat = input_manager_current_seat(input_manager); 170 struct sway_seat *seat = input_manager_current_seat(input_manager);
929 struct sway_container *focus = 171 struct sway_container *focus =
930 seat_get_focus_inactive(seat, output->swayc); 172 seat_get_focus_inactive(seat, output->swayc);
@@ -975,119 +217,6 @@ bool output_has_opaque_lockscreen(struct sway_output *output,
975 return false; 217 return false;
976} 218}
977 219
978static void render_output(struct sway_output *output, struct timespec *when,
979 pixman_region32_t *damage) {
980 struct wlr_output *wlr_output = output->wlr_output;
981
982 struct wlr_renderer *renderer =
983 wlr_backend_get_renderer(wlr_output->backend);
984 if (!sway_assert(renderer != NULL,
985 "expected the output backend to have a renderer")) {
986 return;
987 }
988
989 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
990
991 bool damage_whole_before_swap = false;
992 if (!pixman_region32_not_empty(damage)) {
993 // Output isn't damaged but needs buffer swap
994 goto renderer_end;
995 }
996
997 const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG");
998 if (damage_debug != NULL) {
999 if (strcmp(damage_debug, "highlight") == 0) {
1000 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
1001 damage_whole_before_swap = true;
1002 } else if (strcmp(damage_debug, "rerender") == 0) {
1003 int width, height;
1004 wlr_output_transformed_resolution(wlr_output, &width, &height);
1005 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1006 }
1007 }
1008
1009 struct sway_container *workspace = output_get_active_workspace(output);
1010 struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
1011 struct sway_seat *seat = input_manager_current_seat(input_manager);
1012
1013 if (output_has_opaque_lockscreen(output, seat)) {
1014 struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer;
1015 struct sway_layer_surface *sway_layer_surface =
1016 layer_from_wlr_layer_surface(seat->focused_layer);
1017 struct render_data data = {
1018 .output = output,
1019 .damage = damage,
1020 .alpha = 1.0f,
1021 };
1022 surface_for_each_surface(wlr_layer_surface->surface,
1023 sway_layer_surface->geo.x, sway_layer_surface->geo.y,
1024 &data.root_geo, render_surface_iterator, &data);
1025 } else if (fullscreen_view) {
1026 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
1027
1028 int nrects;
1029 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1030 for (int i = 0; i < nrects; ++i) {
1031 scissor_output(wlr_output, &rects[i]);
1032 wlr_renderer_clear(renderer, clear_color);
1033 }
1034
1035 // TODO: handle views smaller than the output
1036 render_view_surfaces(fullscreen_view, output, damage, 1.0f);
1037
1038 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
1039 render_unmanaged(output, damage,
1040 &root_container.sway_root->xwayland_unmanaged);
1041 }
1042 } else {
1043 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
1044
1045 int nrects;
1046 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
1047 for (int i = 0; i < nrects; ++i) {
1048 scissor_output(wlr_output, &rects[i]);
1049 wlr_renderer_clear(renderer, clear_color);
1050 }
1051
1052 render_layer(output, damage,
1053 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
1054 render_layer(output, damage,
1055 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
1056
1057 struct sway_seat *seat = input_manager_current_seat(input_manager);
1058 struct sway_container *focus = seat_get_focus(seat);
1059 render_container(output, damage, workspace, focus == workspace);
1060 render_floating(output, damage);
1061
1062 render_unmanaged(output, damage,
1063 &root_container.sway_root->xwayland_unmanaged);
1064 render_layer(output, damage,
1065 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
1066 }
1067 render_layer(output, damage,
1068 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
1069 render_drag_icons(output, damage, &root_container.sway_root->drag_icons);
1070
1071renderer_end:
1072 if (root_container.sway_root->debug_tree) {
1073 wlr_render_texture(renderer, root_container.sway_root->debug_tree,
1074 wlr_output->transform_matrix, 0, 0, 1);
1075 }
1076
1077 if (damage_whole_before_swap || root_container.sway_root->debug_tree) {
1078 int width, height;
1079 wlr_output_transformed_resolution(wlr_output, &width, &height);
1080 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
1081 }
1082
1083 wlr_renderer_scissor(renderer, NULL);
1084 wlr_renderer_end(renderer);
1085 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
1086 return;
1087 }
1088 output->last_frame = *when;
1089}
1090
1091struct send_frame_done_data { 220struct send_frame_done_data {
1092 struct root_geometry root_geo; 221 struct root_geometry root_geo;
1093 struct sway_output *output; 222 struct sway_output *output;
@@ -1103,7 +232,7 @@ static void send_frame_done_iterator(struct wlr_surface *surface,
1103 return; 232 return;
1104 } 233 }
1105 234
1106 bool intersects = get_surface_box(&data->root_geo, data->output, surface, 235 bool intersects = output_get_surface_box(&data->root_geo, data->output, surface,
1107 sx, sy, NULL); 236 sx, sy, NULL);
1108 if (intersects) { 237 if (intersects) {
1109 wlr_surface_send_frame_done(surface, data->when); 238 wlr_surface_send_frame_done(surface, data->when);
@@ -1112,19 +241,19 @@ static void send_frame_done_iterator(struct wlr_surface *surface,
1112 241
1113static void send_frame_done_layer(struct send_frame_done_data *data, 242static void send_frame_done_layer(struct send_frame_done_data *data,
1114 struct wl_list *layer_surfaces) { 243 struct wl_list *layer_surfaces) {
1115 layer_for_each_surface(layer_surfaces, &data->root_geo, 244 output_layer_for_each_surface(layer_surfaces, &data->root_geo,
1116 send_frame_done_iterator, data); 245 send_frame_done_iterator, data);
1117} 246}
1118 247
1119static void send_frame_done_unmanaged(struct send_frame_done_data *data, 248static void send_frame_done_unmanaged(struct send_frame_done_data *data,
1120 struct wl_list *unmanaged) { 249 struct wl_list *unmanaged) {
1121 unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, 250 output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
1122 send_frame_done_iterator, data); 251 send_frame_done_iterator, data);
1123} 252}
1124 253
1125static void send_frame_done_drag_icons(struct send_frame_done_data *data, 254static void send_frame_done_drag_icons(struct send_frame_done_data *data,
1126 struct wl_list *drag_icons) { 255 struct wl_list *drag_icons) {
1127 drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, 256 output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo,
1128 send_frame_done_iterator, data); 257 send_frame_done_iterator, data);
1129} 258}
1130 259
@@ -1139,7 +268,7 @@ static void send_frame_done_container_iterator(struct sway_container *con,
1139 return; 268 return;
1140 } 269 }
1141 270
1142 output_view_for_each_surface(con->sway_view, &data->root_geo, 271 output_view_for_each_surface(con->sway_view, data->output, &data->root_geo,
1143 send_frame_done_iterator, data); 272 send_frame_done_iterator, data);
1144} 273}
1145 274
@@ -1206,7 +335,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
1206 } 335 }
1207 336
1208 if (needs_swap) { 337 if (needs_swap) {
1209 render_output(output, &now, &damage); 338 output_render(output, &now, &damage);
1210 } 339 }
1211 340
1212 pixman_region32_fini(&damage); 341 pixman_region32_fini(&damage);
@@ -1233,7 +362,7 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
1233 bool whole = data->whole; 362 bool whole = data->whole;
1234 363
1235 struct wlr_box box; 364 struct wlr_box box;
1236 bool intersects = get_surface_box(&data->root_geo, data->output, surface, 365 bool intersects = output_get_surface_box(&data->root_geo, data->output, surface,
1237 sx, sy, &box); 366 sx, sy, &box);
1238 if (!intersects) { 367 if (!intersects) {
1239 return; 368 return;
@@ -1283,7 +412,7 @@ void output_damage_surface(struct sway_output *output, double ox, double oy,
1283 .whole = whole, 412 .whole = whole,
1284 }; 413 };
1285 414
1286 surface_for_each_surface(surface, ox, oy, &data.root_geo, 415 output_surface_for_each_surface(surface, ox, oy, &data.root_geo,
1287 damage_surface_iterator, &data); 416 damage_surface_iterator, &data);
1288} 417}
1289 418
@@ -1302,7 +431,7 @@ static void output_damage_view(struct sway_output *output,
1302 .whole = whole, 431 .whole = whole,
1303 }; 432 };
1304 433
1305 output_view_for_each_surface(view, &data.root_geo, 434 output_view_for_each_surface(view, output, &data.root_geo,
1306 damage_surface_iterator, &data); 435 damage_surface_iterator, &data);
1307} 436}
1308 437
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
new file mode 100644
index 00000000..43948f29
--- /dev/null
+++ b/sway/desktop/render.c
@@ -0,0 +1,893 @@
1#define _POSIX_C_SOURCE 200809L
2#include <assert.h>
3#include <stdlib.h>
4#include <strings.h>
5#include <time.h>
6#include <wayland-server.h>
7#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_buffer.h>
10#include <wlr/types/wlr_matrix.h>
11#include <wlr/types/wlr_output_damage.h>
12#include <wlr/types/wlr_output_layout.h>
13#include <wlr/types/wlr_output.h>
14#include <wlr/types/wlr_surface.h>
15#include <wlr/util/region.h>
16#include "log.h"
17#include "sway/config.h"
18#include "sway/input/input-manager.h"
19#include "sway/input/seat.h"
20#include "sway/layers.h"
21#include "sway/output.h"
22#include "sway/server.h"
23#include "sway/tree/arrange.h"
24#include "sway/tree/container.h"
25#include "sway/tree/layout.h"
26#include "sway/tree/view.h"
27#include "sway/tree/workspace.h"
28
29struct render_data {
30 struct root_geometry root_geo;
31 struct sway_output *output;
32 pixman_region32_t *damage;
33 struct sway_view *view;
34 float alpha;
35};
36
37static void scale_box(struct wlr_box *box, float scale) {
38 box->x *= scale;
39 box->y *= scale;
40 box->width *= scale;
41 box->height *= scale;
42}
43
44static void scissor_output(struct wlr_output *wlr_output,
45 pixman_box32_t *rect) {
46 struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
47 assert(renderer);
48
49 struct wlr_box box = {
50 .x = rect->x1,
51 .y = rect->y1,
52 .width = rect->x2 - rect->x1,
53 .height = rect->y2 - rect->y1,
54 };
55
56 int ow, oh;
57 wlr_output_transformed_resolution(wlr_output, &ow, &oh);
58
59 enum wl_output_transform transform =
60 wlr_output_transform_invert(wlr_output->transform);
61 wlr_box_transform(&box, transform, ow, oh, &box);
62
63 wlr_renderer_scissor(renderer, &box);
64}
65
66static void render_texture(struct wlr_output *wlr_output,
67 pixman_region32_t *output_damage, struct wlr_texture *texture,
68 const struct wlr_box *box, const float matrix[static 9], float alpha) {
69 struct wlr_renderer *renderer =
70 wlr_backend_get_renderer(wlr_output->backend);
71
72 pixman_region32_t damage;
73 pixman_region32_init(&damage);
74 pixman_region32_union_rect(&damage, &damage, box->x, box->y,
75 box->width, box->height);
76 pixman_region32_intersect(&damage, &damage, output_damage);
77 bool damaged = pixman_region32_not_empty(&damage);
78 if (!damaged) {
79 goto damage_finish;
80 }
81
82 int nrects;
83 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
84 for (int i = 0; i < nrects; ++i) {
85 scissor_output(wlr_output, &rects[i]);
86 wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
87 }
88
89damage_finish:
90 pixman_region32_fini(&damage);
91}
92
93static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
94 void *_data) {
95 struct render_data *data = _data;
96 struct wlr_output *wlr_output = data->output->wlr_output;
97 float rotation = data->root_geo.rotation;
98 pixman_region32_t *output_damage = data->damage;
99 float alpha = data->alpha;
100
101 struct wlr_texture *texture = wlr_surface_get_texture(surface);
102 if (!texture) {
103 return;
104 }
105
106 struct wlr_box box;
107 bool intersects = output_get_surface_box(&data->root_geo, data->output,
108 surface, sx, sy, &box);
109 if (!intersects) {
110 return;
111 }
112
113 scale_box(&box, wlr_output->scale);
114
115 float matrix[9];
116 enum wl_output_transform transform =
117 wlr_output_transform_invert(surface->current.transform);
118 wlr_matrix_project_box(matrix, &box, transform, rotation,
119 wlr_output->transform_matrix);
120
121 render_texture(wlr_output, output_damage, texture, &box, matrix, alpha);
122}
123
124static void render_layer(struct sway_output *output,
125 pixman_region32_t *damage, struct wl_list *layer_surfaces) {
126 struct render_data data = {
127 .output = output,
128 .damage = damage,
129 .alpha = 1.0f,
130 };
131 output_layer_for_each_surface(layer_surfaces, &data.root_geo,
132 render_surface_iterator, &data);
133}
134
135static void render_unmanaged(struct sway_output *output,
136 pixman_region32_t *damage, struct wl_list *unmanaged) {
137 struct render_data data = {
138 .output = output,
139 .damage = damage,
140 .alpha = 1.0f,
141 };
142 output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
143 render_surface_iterator, &data);
144}
145
146static void render_drag_icons(struct sway_output *output,
147 pixman_region32_t *damage, struct wl_list *drag_icons) {
148 struct render_data data = {
149 .output = output,
150 .damage = damage,
151 .alpha = 1.0f,
152 };
153 output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo,
154 render_surface_iterator, &data);
155}
156
157static void render_rect(struct wlr_output *wlr_output,
158 pixman_region32_t *output_damage, const struct wlr_box *_box,
159 float color[static 4]) {
160 struct wlr_renderer *renderer =
161 wlr_backend_get_renderer(wlr_output->backend);
162
163 struct wlr_box box;
164 memcpy(&box, _box, sizeof(struct wlr_box));
165 box.x -= wlr_output->lx * wlr_output->scale;
166 box.y -= wlr_output->ly * wlr_output->scale;
167
168 pixman_region32_t damage;
169 pixman_region32_init(&damage);
170 pixman_region32_union_rect(&damage, &damage, box.x, box.y,
171 box.width, box.height);
172 pixman_region32_intersect(&damage, &damage, output_damage);
173 bool damaged = pixman_region32_not_empty(&damage);
174 if (!damaged) {
175 goto damage_finish;
176 }
177
178 int nrects;
179 pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
180 for (int i = 0; i < nrects; ++i) {
181 scissor_output(wlr_output, &rects[i]);
182 wlr_render_rect(renderer, &box, color,
183 wlr_output->transform_matrix);
184 }
185
186damage_finish:
187 pixman_region32_fini(&damage);
188}
189
190static void premultiply_alpha(float color[4], float opacity) {
191 color[3] *= opacity;
192 color[0] *= color[3];
193 color[1] *= color[3];
194 color[2] *= color[3];
195}
196
197static void render_view_surfaces(struct sway_view *view,
198 struct sway_output *output, pixman_region32_t *damage, float alpha) {
199 struct render_data data = {
200 .output = output,
201 .damage = damage,
202 .view = view,
203 .alpha = alpha,
204 };
205 output_view_for_each_surface(view, output, &data.root_geo,
206 render_surface_iterator, &data);
207}
208
209static void render_saved_view(struct sway_view *view,
210 struct sway_output *output, pixman_region32_t *damage, float alpha) {
211 struct wlr_output *wlr_output = output->wlr_output;
212
213 int width, height;
214 struct wlr_texture *texture =
215 transaction_get_saved_texture(view, &width, &height);
216 if (!texture) {
217 return;
218 }
219 struct wlr_box box = {
220 .x = view->swayc->current.view_x - output->swayc->current.swayc_x,
221 .y = view->swayc->current.view_y - output->swayc->current.swayc_y,
222 .width = width,
223 .height = height,
224 };
225
226 struct wlr_box output_box = {
227 .width = output->swayc->current.swayc_width,
228 .height = output->swayc->current.swayc_height,
229 };
230
231 struct wlr_box intersection;
232 bool intersects = wlr_box_intersection(&output_box, &box, &intersection);
233 if (!intersects) {
234 return;
235 }
236
237 scale_box(&box, wlr_output->scale);
238
239 float matrix[9];
240 wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
241 wlr_output->transform_matrix);
242
243 render_texture(wlr_output, damage, texture, &box, matrix, alpha);
244}
245
246/**
247 * Render a view's surface and left/bottom/right borders.
248 */
249static void render_view(struct sway_output *output, pixman_region32_t *damage,
250 struct sway_container *con, struct border_colors *colors) {
251 struct sway_view *view = con->sway_view;
252 if (view->swayc->instructions->length) {
253 render_saved_view(view, output, damage, view->swayc->alpha);
254 } else {
255 render_view_surfaces(view, output, damage, view->swayc->alpha);
256 }
257
258 struct wlr_box box;
259 float output_scale = output->wlr_output->scale;
260 float color[4];
261 struct sway_container_state *state = &con->current;
262
263 if (state->border != B_NONE) {
264 if (state->border_left) {
265 memcpy(&color, colors->child_border, sizeof(float) * 4);
266 premultiply_alpha(color, con->alpha);
267 box.x = state->swayc_x;
268 box.y = state->view_y;
269 box.width = state->border_thickness;
270 box.height = state->view_height;
271 scale_box(&box, output_scale);
272 render_rect(output->wlr_output, damage, &box, color);
273 }
274
275 if (state->border_right) {
276 if (state->parent->current.children->length == 1
277 && state->parent->current.layout == L_HORIZ) {
278 memcpy(&color, colors->indicator, sizeof(float) * 4);
279 } else {
280 memcpy(&color, colors->child_border, sizeof(float) * 4);
281 }
282 premultiply_alpha(color, con->alpha);
283 box.x = state->view_x + state->view_width;
284 box.y = state->view_y;
285 box.width = state->border_thickness;
286 box.height = state->view_height;
287 scale_box(&box, output_scale);
288 render_rect(output->wlr_output, damage, &box, color);
289 }
290
291 if (state->border_bottom) {
292 if (state->parent->current.children->length == 1
293 && con->current.parent->current.layout == L_VERT) {
294 memcpy(&color, colors->indicator, sizeof(float) * 4);
295 } else {
296 memcpy(&color, colors->child_border, sizeof(float) * 4);
297 }
298 premultiply_alpha(color, con->alpha);
299 box.x = state->swayc_x;
300 box.y = state->view_y + state->view_height;
301 box.width = state->swayc_width;
302 box.height = state->border_thickness;
303 scale_box(&box, output_scale);
304 render_rect(output->wlr_output, damage, &box, color);
305 }
306 }
307}
308
309/**
310 * Render a titlebar.
311 *
312 * Care must be taken not to render over the same pixel multiple times,
313 * otherwise the colors will be incorrect when using opacity.
314 *
315 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
316 * The left side for L_TABBED is: 1px border, 2px padding, title
317 * The left side for other layouts is: 3px padding, title
318 */
319static void render_titlebar(struct sway_output *output,
320 pixman_region32_t *output_damage, struct sway_container *con,
321 int x, int y, int width,
322 struct border_colors *colors, struct wlr_texture *title_texture,
323 struct wlr_texture *marks_texture) {
324 struct wlr_box box;
325 float color[4];
326 struct sway_container_state *state = &con->current;
327 float output_scale = output->wlr_output->scale;
328 enum sway_container_layout layout = state->parent->current.layout;
329 list_t *children = state->parent->current.children;
330 bool is_last_child = children->items[children->length - 1] == con;
331 double output_x = output->swayc->current.swayc_x;
332 double output_y = output->swayc->current.swayc_y;
333
334 // Single pixel bar above title
335 memcpy(&color, colors->border, sizeof(float) * 4);
336 premultiply_alpha(color, con->alpha);
337 box.x = x;
338 box.y = y;
339 box.width = width;
340 box.height = TITLEBAR_BORDER_THICKNESS;
341 scale_box(&box, output_scale);
342 render_rect(output->wlr_output, output_damage, &box, color);
343
344 // Single pixel bar below title
345 size_t left_offset = 0, right_offset = 0;
346 bool connects_sides = false;
347 if (layout == L_HORIZ || layout == L_VERT ||
348 (layout == L_STACKED && is_last_child)) {
349 if (con->type == C_VIEW) {
350 left_offset = state->border_left * state->border_thickness;
351 right_offset = state->border_right * state->border_thickness;
352 connects_sides = true;
353 }
354 }
355 box.x = x + left_offset;
356 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
357 box.width = width - left_offset - right_offset;
358 box.height = TITLEBAR_BORDER_THICKNESS;
359 scale_box(&box, output_scale);
360 render_rect(output->wlr_output, output_damage, &box, color);
361
362 if (layout == L_TABBED) {
363 // Single pixel left edge
364 box.x = x;
365 box.y = y + TITLEBAR_BORDER_THICKNESS;
366 box.width = TITLEBAR_BORDER_THICKNESS;
367 box.height =
368 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2;
369 scale_box(&box, output_scale);
370 render_rect(output->wlr_output, output_damage, &box, color);
371
372 // Single pixel right edge
373 box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale;
374 render_rect(output->wlr_output, output_damage, &box, color);
375 }
376
377 size_t inner_width = width - TITLEBAR_H_PADDING * 2;
378
379 // Marks
380 size_t marks_ob_width = 0; // output-buffer-local
381 if (config->show_marks && marks_texture) {
382 struct wlr_box texture_box;
383 wlr_texture_get_size(marks_texture,
384 &texture_box.width, &texture_box.height);
385 texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING)
386 * output_scale - texture_box.width;
387 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
388
389 float matrix[9];
390 wlr_matrix_project_box(matrix, &texture_box,
391 WL_OUTPUT_TRANSFORM_NORMAL,
392 0.0, output->wlr_output->transform_matrix);
393
394 if (inner_width * output_scale < texture_box.width) {
395 texture_box.width = inner_width * output_scale;
396 }
397 render_texture(output->wlr_output, output_damage, marks_texture,
398 &texture_box, matrix, con->alpha);
399 marks_ob_width = texture_box.width;
400
401 // Gap between the marks and bottom padding, for when the marks texture
402 // height is smaller than the config's font height
403 memcpy(&color, colors->background, sizeof(float) * 4);
404 premultiply_alpha(color, con->alpha);
405 box.x = texture_box.x;
406 box.y = texture_box.y + texture_box.height;
407 box.width = texture_box.width;
408 box.height = config->font_height * output_scale - texture_box.height;
409 if (box.height > 0) {
410 render_rect(output->wlr_output, output_damage, &box, color);
411 }
412 }
413
414 // Title text
415 size_t title_ob_width = 0; // output-buffer-local
416 if (title_texture) {
417 struct wlr_box texture_box;
418 wlr_texture_get_size(title_texture,
419 &texture_box.width, &texture_box.height);
420 texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale;
421 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
422
423 float matrix[9];
424 wlr_matrix_project_box(matrix, &texture_box,
425 WL_OUTPUT_TRANSFORM_NORMAL,
426 0.0, output->wlr_output->transform_matrix);
427
428 if (inner_width * output_scale - marks_ob_width < texture_box.width) {
429 texture_box.width = inner_width * output_scale - marks_ob_width;
430 }
431 render_texture(output->wlr_output, output_damage, title_texture,
432 &texture_box, matrix, con->alpha);
433 title_ob_width = texture_box.width;
434
435 // Gap between the title and bottom padding, for when the title texture
436 // height is smaller than the config's font height
437 memcpy(&color, colors->background, sizeof(float) * 4);
438 premultiply_alpha(color, con->alpha);
439 box.x = texture_box.x;
440 box.y = texture_box.y + texture_box.height;
441 box.width = texture_box.width;
442 box.height = config->font_height * output_scale - texture_box.height;
443 if (box.height > 0) {
444 render_rect(output->wlr_output, output_damage, &box, color);
445 }
446 }
447
448 // Padding above title
449 memcpy(&color, colors->background, sizeof(float) * 4);
450 premultiply_alpha(color, con->alpha);
451 box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
452 box.y = y + TITLEBAR_BORDER_THICKNESS;
453 box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2;
454 box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS;
455 scale_box(&box, output_scale);
456 render_rect(output->wlr_output, output_damage, &box, color);
457
458 // Padding below title
459 box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale;
460 render_rect(output->wlr_output, output_damage, &box, color);
461
462 // Filler between title and marks
463 box.width = inner_width * output_scale - title_ob_width - marks_ob_width;
464 if (box.width > 0) {
465 box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width;
466 box.y = (y + TITLEBAR_V_PADDING) * output_scale;
467 box.height = config->font_height * output_scale;
468 render_rect(output->wlr_output, output_damage, &box, color);
469 }
470
471 // Padding left of title
472 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
473 box.x = x + left_offset;
474 box.y = y + TITLEBAR_V_PADDING;
475 box.width = TITLEBAR_H_PADDING - left_offset;
476 box.height = config->font_height;
477 scale_box(&box, output_scale);
478 render_rect(output->wlr_output, output_damage, &box, color);
479
480 // Padding right of marks
481 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
482 box.x = x + width - TITLEBAR_H_PADDING;
483 box.y = y + TITLEBAR_V_PADDING;
484 box.width = TITLEBAR_H_PADDING - right_offset;
485 box.height = config->font_height;
486 scale_box(&box, output_scale);
487 render_rect(output->wlr_output, output_damage, &box, color);
488
489 if (connects_sides) {
490 // Left pixel in line with bottom bar
491 box.x = x;
492 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
493 box.width = state->border_thickness * state->border_left;
494 box.height = TITLEBAR_BORDER_THICKNESS;
495 scale_box(&box, output_scale);
496 render_rect(output->wlr_output, output_damage, &box, color);
497
498 // Right pixel in line with bottom bar
499 box.x = x + width - state->border_thickness * state->border_right;
500 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
501 box.width = state->border_thickness * state->border_right;
502 box.height = TITLEBAR_BORDER_THICKNESS;
503 scale_box(&box, output_scale);
504 render_rect(output->wlr_output, output_damage, &box, color);
505 }
506}
507
508/**
509 * Render the top border line for a view using "border pixel".
510 */
511static void render_top_border(struct sway_output *output,
512 pixman_region32_t *output_damage, struct sway_container *con,
513 struct border_colors *colors) {
514 struct sway_container_state *state = &con->current;
515 if (!state->border_top) {
516 return;
517 }
518 struct wlr_box box;
519 float color[4];
520 float output_scale = output->wlr_output->scale;
521
522 // Child border - top edge
523 memcpy(&color, colors->child_border, sizeof(float) * 4);
524 premultiply_alpha(color, con->alpha);
525 box.x = state->swayc_x;
526 box.y = state->swayc_y;
527 box.width = state->swayc_width;
528 box.height = state->border_thickness;
529 scale_box(&box, output_scale);
530 render_rect(output->wlr_output, output_damage, &box, color);
531}
532
533static void render_container(struct sway_output *output,
534 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
535
536/**
537 * Render a container's children using a L_HORIZ or L_VERT layout.
538 *
539 * Wrap child views in borders and leave child containers borderless because
540 * they'll apply their own borders to their children.
541 */
542static void render_container_simple(struct sway_output *output,
543 pixman_region32_t *damage, struct sway_container *con,
544 bool parent_focused) {
545 struct sway_seat *seat = input_manager_current_seat(input_manager);
546 struct sway_container *focus = seat_get_focus(seat);
547
548 for (int i = 0; i < con->current.children->length; ++i) {
549 struct sway_container *child = con->current.children->items[i];
550
551 if (child->type == C_VIEW) {
552 struct sway_view *view = child->sway_view;
553 struct border_colors *colors;
554 struct wlr_texture *title_texture;
555 struct wlr_texture *marks_texture;
556 struct sway_container_state *state = &child->current;
557
558 if (focus == child || parent_focused) {
559 colors = &config->border_colors.focused;
560 title_texture = child->title_focused;
561 marks_texture = view->marks_focused;
562 } else if (seat_get_focus_inactive(seat, con) == child) {
563 colors = &config->border_colors.focused_inactive;
564 title_texture = child->title_focused_inactive;
565 marks_texture = view->marks_focused_inactive;
566 } else {
567 colors = &config->border_colors.unfocused;
568 title_texture = child->title_unfocused;
569 marks_texture = view->marks_unfocused;
570 }
571
572 if (state->border == B_NORMAL) {
573 render_titlebar(output, damage, child, state->swayc_x,
574 state->swayc_y, state->swayc_width, colors,
575 title_texture, marks_texture);
576 } else {
577 render_top_border(output, damage, child, colors);
578 }
579 render_view(output, damage, child, colors);
580 } else {
581 render_container(output, damage, child,
582 parent_focused || focus == child);
583 }
584 }
585}
586
587/**
588 * Render a container's children using the L_TABBED layout.
589 */
590static void render_container_tabbed(struct sway_output *output,
591 pixman_region32_t *damage, struct sway_container *con,
592 bool parent_focused) {
593 if (!con->current.children->length) {
594 return;
595 }
596 struct sway_seat *seat = input_manager_current_seat(input_manager);
597 struct sway_container *focus = seat_get_focus(seat);
598 struct sway_container *current = seat_get_active_current_child(seat, con);
599 struct border_colors *current_colors = &config->border_colors.unfocused;
600 struct sway_container_state *pstate = &con->current;
601
602 // Render tabs
603 for (int i = 0; i < con->current.children->length; ++i) {
604 struct sway_container *child = con->current.children->items[i];
605 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
606 struct sway_container_state *cstate = &child->current;
607 struct border_colors *colors;
608 struct wlr_texture *title_texture;
609 struct wlr_texture *marks_texture;
610
611 if (focus == child || parent_focused) {
612 colors = &config->border_colors.focused;
613 title_texture = child->title_focused;
614 marks_texture = view ? view->marks_focused : NULL;
615 } else if (child == current) {
616 colors = &config->border_colors.focused_inactive;
617 title_texture = child->title_focused_inactive;
618 marks_texture = view ? view->marks_focused_inactive : NULL;
619 } else {
620 colors = &config->border_colors.unfocused;
621 title_texture = child->title_unfocused;
622 marks_texture = view ? view->marks_unfocused : NULL;
623 }
624
625 int tab_width = pstate->swayc_width / pstate->children->length;
626 int x = pstate->swayc_x + tab_width * i;
627 // Make last tab use the remaining width of the parent
628 if (i == pstate->children->length - 1) {
629 tab_width = pstate->swayc_width - tab_width * i;
630 }
631
632 render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
633 colors, title_texture, marks_texture);
634
635 if (child == current) {
636 current_colors = colors;
637 }
638 }
639
640 // Render surface and left/right/bottom borders
641 if (current) {
642 if (current->type == C_VIEW) {
643 render_view(output, damage, current, current_colors);
644 } else {
645 render_container(output, damage, current,
646 parent_focused || current == focus);
647 }
648 }
649}
650
651/**
652 * Render a container's children using the L_STACKED layout.
653 */
654static void render_container_stacked(struct sway_output *output,
655 pixman_region32_t *damage, struct sway_container *con,
656 bool parent_focused) {
657 if (!con->current.children->length) {
658 return;
659 }
660 struct sway_seat *seat = input_manager_current_seat(input_manager);
661 struct sway_container *focus = seat_get_focus(seat);
662 struct sway_container *current = seat_get_active_current_child(seat, con);
663 struct border_colors *current_colors = &config->border_colors.unfocused;
664 struct sway_container_state *pstate = &con->current;
665
666 // Render titles
667 for (int i = 0; i < con->current.children->length; ++i) {
668 struct sway_container *child = con->current.children->items[i];
669 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
670 struct sway_container_state *cstate = &child->current;
671 struct border_colors *colors;
672 struct wlr_texture *title_texture;
673 struct wlr_texture *marks_texture;
674
675 if (focus == child || parent_focused) {
676 colors = &config->border_colors.focused;
677 title_texture = child->title_focused;
678 marks_texture = view ? view->marks_focused : NULL;
679 } else if (child == current) {
680 colors = &config->border_colors.focused_inactive;
681 title_texture = child->title_focused_inactive;
682 marks_texture = view ? view->marks_focused_inactive : NULL;
683 } else {
684 colors = &config->border_colors.unfocused;
685 title_texture = child->title_unfocused;
686 marks_texture = view ? view->marks_unfocused : NULL;
687 }
688
689 int y = pstate->swayc_y + container_titlebar_height() * i;
690 render_titlebar(output, damage, child, cstate->swayc_x, y,
691 cstate->swayc_width, colors, title_texture, marks_texture);
692
693 if (child == current) {
694 current_colors = colors;
695 }
696 }
697
698 // Render surface and left/right/bottom borders
699 if (current) {
700 if (current->type == C_VIEW) {
701 render_view(output, damage, current, current_colors);
702 } else {
703 render_container(output, damage, current,
704 parent_focused || current == focus);
705 }
706 }
707}
708
709static void render_container(struct sway_output *output,
710 pixman_region32_t *damage, struct sway_container *con,
711 bool parent_focused) {
712 switch (con->current.layout) {
713 case L_NONE:
714 case L_HORIZ:
715 case L_VERT:
716 render_container_simple(output, damage, con, parent_focused);
717 break;
718 case L_STACKED:
719 render_container_stacked(output, damage, con, parent_focused);
720 break;
721 case L_TABBED:
722 render_container_tabbed(output, damage, con, parent_focused);
723 break;
724 case L_FLOATING:
725 sway_assert(false, "Didn't expect to see floating here");
726 }
727}
728
729static void render_floating_container(struct sway_output *soutput,
730 pixman_region32_t *damage, struct sway_container *con) {
731 if (con->type == C_VIEW) {
732 struct sway_view *view = con->sway_view;
733 struct sway_seat *seat = input_manager_current_seat(input_manager);
734 struct sway_container *focus = seat_get_focus(seat);
735 struct border_colors *colors;
736 struct wlr_texture *title_texture;
737 struct wlr_texture *marks_texture;
738
739 if (focus == con) {
740 colors = &config->border_colors.focused;
741 title_texture = con->title_focused;
742 marks_texture = view->marks_focused;
743 } else {
744 colors = &config->border_colors.unfocused;
745 title_texture = con->title_unfocused;
746 marks_texture = view->marks_unfocused;
747 }
748
749 if (con->current.border == B_NORMAL) {
750 render_titlebar(soutput, damage, con, con->current.swayc_x,
751 con->current.swayc_y, con->current.swayc_width, colors,
752 title_texture, marks_texture);
753 } else if (con->current.border != B_NONE) {
754 render_top_border(soutput, damage, con, colors);
755 }
756 render_view(soutput, damage, con, colors);
757 } else {
758 render_container(soutput, damage, con, false);
759 }
760}
761
762static void render_floating(struct sway_output *soutput,
763 pixman_region32_t *damage) {
764 for (int i = 0; i < root_container.current.children->length; ++i) {
765 struct sway_container *output =
766 root_container.current.children->items[i];
767 for (int j = 0; j < output->current.children->length; ++j) {
768 struct sway_container *ws = output->current.children->items[j];
769 if (!workspace_is_visible(ws)) {
770 continue;
771 }
772 list_t *floating =
773 ws->current.ws_floating->current.children;
774 for (int k = 0; k < floating->length; ++k) {
775 struct sway_container *floater = floating->items[k];
776 render_floating_container(soutput, damage, floater);
777 }
778 }
779 }
780}
781
782void output_render(struct sway_output *output, struct timespec *when,
783 pixman_region32_t *damage) {
784 struct wlr_output *wlr_output = output->wlr_output;
785
786 struct wlr_renderer *renderer =
787 wlr_backend_get_renderer(wlr_output->backend);
788 if (!sway_assert(renderer != NULL,
789 "expected the output backend to have a renderer")) {
790 return;
791 }
792
793 wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
794
795 bool damage_whole_before_swap = false;
796 if (!pixman_region32_not_empty(damage)) {
797 // Output isn't damaged but needs buffer swap
798 goto renderer_end;
799 }
800
801 const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG");
802 if (damage_debug != NULL) {
803 if (strcmp(damage_debug, "highlight") == 0) {
804 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
805 damage_whole_before_swap = true;
806 } else if (strcmp(damage_debug, "rerender") == 0) {
807 int width, height;
808 wlr_output_transformed_resolution(wlr_output, &width, &height);
809 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
810 }
811 }
812
813 struct sway_container *workspace = output_get_active_workspace(output);
814 struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
815 struct sway_seat *seat = input_manager_current_seat(input_manager);
816
817 if (output_has_opaque_lockscreen(output, seat)) {
818 struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer;
819 struct sway_layer_surface *sway_layer_surface =
820 layer_from_wlr_layer_surface(seat->focused_layer);
821 struct render_data data = {
822 .output = output,
823 .damage = damage,
824 .alpha = 1.0f,
825 };
826 output_surface_for_each_surface(wlr_layer_surface->surface,
827 sway_layer_surface->geo.x, sway_layer_surface->geo.y,
828 &data.root_geo, render_surface_iterator, &data);
829 } else if (fullscreen_view) {
830 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
831
832 int nrects;
833 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
834 for (int i = 0; i < nrects; ++i) {
835 scissor_output(wlr_output, &rects[i]);
836 wlr_renderer_clear(renderer, clear_color);
837 }
838
839 // TODO: handle views smaller than the output
840 render_view_surfaces(fullscreen_view, output, damage, 1.0f);
841
842 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
843 render_unmanaged(output, damage,
844 &root_container.sway_root->xwayland_unmanaged);
845 }
846 } else {
847 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
848
849 int nrects;
850 pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
851 for (int i = 0; i < nrects; ++i) {
852 scissor_output(wlr_output, &rects[i]);
853 wlr_renderer_clear(renderer, clear_color);
854 }
855
856 render_layer(output, damage,
857 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
858 render_layer(output, damage,
859 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
860
861 struct sway_seat *seat = input_manager_current_seat(input_manager);
862 struct sway_container *focus = seat_get_focus(seat);
863 render_container(output, damage, workspace, focus == workspace);
864 render_floating(output, damage);
865
866 render_unmanaged(output, damage,
867 &root_container.sway_root->xwayland_unmanaged);
868 render_layer(output, damage,
869 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
870 }
871 render_layer(output, damage,
872 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
873 render_drag_icons(output, damage, &root_container.sway_root->drag_icons);
874
875renderer_end:
876 if (root_container.sway_root->debug_tree) {
877 wlr_render_texture(renderer, root_container.sway_root->debug_tree,
878 wlr_output->transform_matrix, 0, 0, 1);
879 }
880
881 if (damage_whole_before_swap || root_container.sway_root->debug_tree) {
882 int width, height;
883 wlr_output_transformed_resolution(wlr_output, &width, &height);
884 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
885 }
886
887 wlr_renderer_scissor(renderer, NULL);
888 wlr_renderer_end(renderer);
889 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
890 return;
891 }
892 output->last_frame = *when;
893}
diff --git a/sway/meson.build b/sway/meson.build
index a81a3406..e492aeee 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -13,6 +13,7 @@ sway_sources = files(
13 'desktop/idle_inhibit_v1.c', 13 'desktop/idle_inhibit_v1.c',
14 'desktop/layer_shell.c', 14 'desktop/layer_shell.c',
15 'desktop/output.c', 15 'desktop/output.c',
16 'desktop/render.c',
16 'desktop/transaction.c', 17 'desktop/transaction.c',
17 'desktop/xdg_shell_v6.c', 18 'desktop/xdg_shell_v6.c',
18 'desktop/xdg_shell.c', 19 'desktop/xdg_shell.c',