diff options
-rw-r--r-- | include/sway/output.h | 41 | ||||
-rw-r--r-- | sway/desktop/output.c | 917 | ||||
-rw-r--r-- | sway/desktop/render.c | 893 | ||||
-rw-r--r-- | sway/meson.build | 1 |
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 | */ | ||
45 | struct root_geometry { | ||
46 | double x, y; | ||
47 | int width, height; | ||
48 | float rotation; | ||
49 | }; | ||
50 | |||
41 | void output_damage_whole(struct sway_output *output); | 51 | void output_damage_whole(struct sway_output *output); |
42 | 52 | ||
43 | void output_damage_surface(struct sway_output *output, double ox, double oy, | 53 | void output_damage_surface(struct sway_output *output, double ox, double oy, |
@@ -56,6 +66,35 @@ struct sway_container *output_by_name(const char *name); | |||
56 | void output_enable(struct sway_output *output); | 66 | void output_enable(struct sway_output *output); |
57 | 67 | ||
58 | bool output_has_opaque_lockscreen(struct sway_output *output, | 68 | bool output_has_opaque_lockscreen(struct sway_output *output, |
59 | struct sway_seat *seat); | 69 | struct sway_seat *seat); |
70 | |||
71 | struct sway_container *output_get_active_workspace(struct sway_output *output); | ||
72 | |||
73 | void output_render(struct sway_output *output, struct timespec *when, | ||
74 | pixman_region32_t *damage); | ||
75 | |||
76 | bool 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 | |||
80 | void 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 | |||
84 | void 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 | |||
88 | void 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 | |||
92 | void 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 | |||
96 | void 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 | /** | 59 | bool 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 | */ | ||
63 | struct root_geometry { | ||
64 | double x, y; | ||
65 | int width, height; | ||
66 | float rotation; | ||
67 | }; | ||
68 | |||
69 | struct 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 | |||
77 | static 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 | ||
113 | static void surface_for_each_surface(struct wlr_surface *surface, | 95 | void 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 | ||
125 | static void output_view_for_each_surface(struct sway_view *view, | 107 | void 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 | ||
138 | static void layer_for_each_surface(struct wl_list *layer_surfaces, | 119 | void 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 | ||
151 | static void unmanaged_for_each_surface(struct wl_list *unmanaged, | 132 | void 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 | ||
166 | static void drag_icons_for_each_surface(struct wl_list *drag_icons, | 147 | void 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 | ||
188 | static void scissor_output(struct wlr_output *wlr_output, | 169 | struct 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 | |||
210 | static 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 | |||
233 | damage_finish: | ||
234 | pixman_region32_fini(&damage); | ||
235 | } | ||
236 | |||
237 | static 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 | |||
268 | static 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 | |||
279 | static 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 | |||
290 | static 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 | |||
301 | static 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 | |||
330 | damage_finish: | ||
331 | pixman_region32_fini(&damage); | ||
332 | } | ||
333 | |||
334 | static 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 | |||
341 | static 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 | |||
353 | static 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 | */ | ||
393 | static 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 | */ | ||
463 | static 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 | */ | ||
655 | static 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 | |||
677 | static 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 | */ | ||
686 | static 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 | */ | ||
734 | static 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 | */ | ||
798 | static 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 | |||
853 | static 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 | |||
873 | static 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 | |||
906 | static 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 | |||
926 | static 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 | ||
978 | static 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 | |||
1071 | renderer_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 | |||
1091 | struct send_frame_done_data { | 220 | struct 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 | ||
1113 | static void send_frame_done_layer(struct send_frame_done_data *data, | 242 | static 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 | ||
1119 | static void send_frame_done_unmanaged(struct send_frame_done_data *data, | 248 | static 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 | ||
1125 | static void send_frame_done_drag_icons(struct send_frame_done_data *data, | 254 | static 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 | |||
29 | struct 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 | |||
37 | static 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 | |||
44 | static 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 | |||
66 | static 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 | |||
89 | damage_finish: | ||
90 | pixman_region32_fini(&damage); | ||
91 | } | ||
92 | |||
93 | static 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 | |||
124 | static 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 | |||
135 | static 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 | |||
146 | static 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 | |||
157 | static 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 | |||
186 | damage_finish: | ||
187 | pixman_region32_fini(&damage); | ||
188 | } | ||
189 | |||
190 | static 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 | |||
197 | static 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 | |||
209 | static 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 | */ | ||
249 | static 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 | */ | ||
319 | static 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 | */ | ||
511 | static 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 | |||
533 | static 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 | */ | ||
542 | static 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 | */ | ||
590 | static 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 | */ | ||
654 | static 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 | |||
709 | static 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 | |||
729 | static 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 | |||
762 | static 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 | |||
782 | void 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 | |||
875 | renderer_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', |