diff options
author | Alexander Orzechowski <alex@ozal.ski> | 2023-04-27 10:25:40 +0200 |
---|---|---|
committer | Kirill Primak <vyivel@eclair.cafe> | 2024-01-18 18:36:54 +0300 |
commit | 06ad734e70227ad0527fe11b88ad37e93005ce0c (patch) | |
tree | 5a5e2606242e3d229f4979d2b7c5352a139730b0 | |
parent | xwayland: Cleanup geometry handling on commit (diff) | |
download | sway-06ad734e70227ad0527fe11b88ad37e93005ce0c.tar.gz sway-06ad734e70227ad0527fe11b88ad37e93005ce0c.tar.zst sway-06ad734e70227ad0527fe11b88ad37e93005ce0c.zip |
scene_graph: Port view saved buffers
-rw-r--r-- | include/sway/tree/view.h | 18 | ||||
-rw-r--r-- | sway/desktop/transaction.c | 49 | ||||
-rw-r--r-- | sway/desktop/xdg_shell.c | 10 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 10 | ||||
-rw-r--r-- | sway/tree/view.c | 88 |
5 files changed, 91 insertions, 84 deletions
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 8493958e..66d6db1c 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -57,21 +57,13 @@ struct sway_view_impl { | |||
57 | void (*destroy)(struct sway_view *view); | 57 | void (*destroy)(struct sway_view *view); |
58 | }; | 58 | }; |
59 | 59 | ||
60 | struct sway_saved_buffer { | ||
61 | struct wlr_client_buffer *buffer; | ||
62 | int x, y; | ||
63 | int width, height; | ||
64 | enum wl_output_transform transform; | ||
65 | struct wlr_fbox source_box; | ||
66 | struct wl_list link; // sway_view::saved_buffers | ||
67 | }; | ||
68 | |||
69 | struct sway_view { | 60 | struct sway_view { |
70 | enum sway_view_type type; | 61 | enum sway_view_type type; |
71 | const struct sway_view_impl *impl; | 62 | const struct sway_view_impl *impl; |
72 | 63 | ||
73 | struct wlr_scene_tree *scene_tree; | 64 | struct wlr_scene_tree *scene_tree; |
74 | struct wlr_scene_tree *content_tree; | 65 | struct wlr_scene_tree *content_tree; |
66 | struct wlr_scene_tree *saved_surface_tree; | ||
75 | 67 | ||
76 | struct sway_container *container; // NULL if unmapped and transactions finished | 68 | struct sway_container *container; // NULL if unmapped and transactions finished |
77 | struct wlr_surface *surface; // NULL for unmapped views | 69 | struct wlr_surface *surface; // NULL for unmapped views |
@@ -92,16 +84,10 @@ struct sway_view { | |||
92 | bool allow_request_urgent; | 84 | bool allow_request_urgent; |
93 | struct wl_event_source *urgent_timer; | 85 | struct wl_event_source *urgent_timer; |
94 | 86 | ||
95 | struct wl_list saved_buffers; // sway_saved_buffer::link | ||
96 | |||
97 | // The geometry for whatever the client is committing, regardless of | 87 | // The geometry for whatever the client is committing, regardless of |
98 | // transaction state. Updated on every commit. | 88 | // transaction state. Updated on every commit. |
99 | struct wlr_box geometry; | 89 | struct wlr_box geometry; |
100 | 90 | ||
101 | // The "old" geometry during a transaction. Used to damage the old location | ||
102 | // when a transaction is applied. | ||
103 | struct wlr_box saved_geometry; | ||
104 | |||
105 | struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; | 91 | struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; |
106 | struct wl_listener foreign_activate_request; | 92 | struct wl_listener foreign_activate_request; |
107 | struct wl_listener foreign_fullscreen_request; | 93 | struct wl_listener foreign_fullscreen_request; |
@@ -347,4 +333,6 @@ bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); | |||
347 | 333 | ||
348 | void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); | 334 | void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); |
349 | 335 | ||
336 | void view_send_frame_done(struct sway_view *view); | ||
337 | |||
350 | #endif | 338 | #endif |
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index bb725795..1ae6642b 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -232,21 +232,6 @@ static void apply_workspace_state(struct sway_workspace *ws, | |||
232 | static void apply_container_state(struct sway_container *container, | 232 | static void apply_container_state(struct sway_container *container, |
233 | struct sway_container_state *state) { | 233 | struct sway_container_state *state) { |
234 | struct sway_view *view = container->view; | 234 | struct sway_view *view = container->view; |
235 | // Damage the old location | ||
236 | desktop_damage_whole_container(container); | ||
237 | if (view && !wl_list_empty(&view->saved_buffers)) { | ||
238 | struct sway_saved_buffer *saved_buf; | ||
239 | wl_list_for_each(saved_buf, &view->saved_buffers, link) { | ||
240 | struct wlr_box box = { | ||
241 | .x = saved_buf->x - view->saved_geometry.x, | ||
242 | .y = saved_buf->y - view->saved_geometry.y, | ||
243 | .width = saved_buf->width, | ||
244 | .height = saved_buf->height, | ||
245 | }; | ||
246 | desktop_damage_box(&box); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | // There are separate children lists for each instruction state, the | 235 | // There are separate children lists for each instruction state, the |
251 | // container's current state and the container's pending state | 236 | // container's current state and the container's pending state |
252 | // (ie. con->children). The list itself needs to be freed here. | 237 | // (ie. con->children). The list itself needs to be freed here. |
@@ -256,17 +241,19 @@ static void apply_container_state(struct sway_container *container, | |||
256 | 241 | ||
257 | memcpy(&container->current, state, sizeof(struct sway_container_state)); | 242 | memcpy(&container->current, state, sizeof(struct sway_container_state)); |
258 | 243 | ||
259 | if (view && !wl_list_empty(&view->saved_buffers)) { | 244 | if (view) { |
260 | if (!container->node.destroying || container->node.ntxnrefs == 1) { | 245 | if (view->saved_surface_tree) { |
261 | view_remove_saved_buffer(view); | 246 | if (!container->node.destroying || container->node.ntxnrefs == 1) { |
247 | view_remove_saved_buffer(view); | ||
248 | } | ||
262 | } | 249 | } |
263 | } | ||
264 | 250 | ||
265 | // If the view hasn't responded to the configure, center it within | 251 | // If the view hasn't responded to the configure, center it within |
266 | // the container. This is important for fullscreen views which | 252 | // the container. This is important for fullscreen views which |
267 | // refuse to resize to the size of the output. | 253 | // refuse to resize to the size of the output. |
268 | if (view && view->surface) { | 254 | if (view->surface) { |
269 | view_center_surface(view); | 255 | view_center_surface(view); |
256 | } | ||
270 | } | 257 | } |
271 | 258 | ||
272 | // Damage the new location | 259 | // Damage the new location |
@@ -415,21 +402,11 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
415 | ++transaction->num_waiting; | 402 | ++transaction->num_waiting; |
416 | } | 403 | } |
417 | 404 | ||
418 | // From here on we are rendering a saved buffer of the view, which | 405 | view_send_frame_done(node->sway_container->view); |
419 | // means we can send a frame done event to make the client redraw it | ||
420 | // as soon as possible. Additionally, this is required if a view is | ||
421 | // mapping and its default geometry doesn't intersect an output. | ||
422 | struct timespec now; | ||
423 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
424 | wlr_surface_send_frame_done( | ||
425 | node->sway_container->view->surface, &now); | ||
426 | } | 406 | } |
427 | if (!hidden && node_is_view(node) && | 407 | if (!hidden && node_is_view(node) && |
428 | wl_list_empty(&node->sway_container->view->saved_buffers)) { | 408 | !node->sway_container->view->saved_surface_tree) { |
429 | view_save_buffer(node->sway_container->view); | 409 | view_save_buffer(node->sway_container->view); |
430 | memcpy(&node->sway_container->view->saved_geometry, | ||
431 | &node->sway_container->view->geometry, | ||
432 | sizeof(struct wlr_box)); | ||
433 | } | 410 | } |
434 | node->instruction = instruction; | 411 | node->instruction = instruction; |
435 | } | 412 | } |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index fed820cf..95b5cb9d 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -332,8 +332,16 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
332 | } | 332 | } |
333 | 333 | ||
334 | if (view->container->node.instruction) { | 334 | if (view->container->node.instruction) { |
335 | transaction_notify_view_ready_by_serial(view, | 335 | bool successful = transaction_notify_view_ready_by_serial(view, |
336 | xdg_surface->current.configure_serial); | 336 | xdg_surface->current.configure_serial); |
337 | |||
338 | // If we saved the view and this commit isn't what we're looking for | ||
339 | // that means the user will never actually see the buffers submitted to | ||
340 | // us here. Just send frame done events to these surfaces so they can | ||
341 | // commit another time for us. | ||
342 | if (view->saved_surface_tree && !successful) { | ||
343 | view_send_frame_done(view); | ||
344 | } | ||
337 | } | 345 | } |
338 | } | 346 | } |
339 | 347 | ||
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 0967c7fc..e0c80c07 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -442,8 +442,16 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
442 | } | 442 | } |
443 | 443 | ||
444 | if (view->container->node.instruction) { | 444 | if (view->container->node.instruction) { |
445 | transaction_notify_view_ready_by_geometry(view, | 445 | bool successful = transaction_notify_view_ready_by_geometry(view, |
446 | xsurface->x, xsurface->y, state->width, state->height); | 446 | xsurface->x, xsurface->y, state->width, state->height); |
447 | |||
448 | // If we saved the view and this commit isn't what we're looking for | ||
449 | // that means the user will never actually see the buffers submitted to | ||
450 | // us here. Just send frame done events to these surfaces so they can | ||
451 | // commit another time for us. | ||
452 | if (view->saved_surface_tree && !successful) { | ||
453 | view_send_frame_done(view); | ||
454 | } | ||
447 | } | 455 | } |
448 | } | 456 | } |
449 | 457 | ||
diff --git a/sway/tree/view.c b/sway/tree/view.c index ee25faf1..402fa179 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -56,7 +56,6 @@ bool view_init(struct sway_view *view, enum sway_view_type type, | |||
56 | view->type = type; | 56 | view->type = type; |
57 | view->impl = impl; | 57 | view->impl = impl; |
58 | view->executed_criteria = create_list(); | 58 | view->executed_criteria = create_list(); |
59 | wl_list_init(&view->saved_buffers); | ||
60 | view->allow_request_urgent = true; | 59 | view->allow_request_urgent = true; |
61 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; | 60 | view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; |
62 | wl_signal_init(&view->events.unmap); | 61 | wl_signal_init(&view->events.unmap); |
@@ -77,9 +76,6 @@ void view_destroy(struct sway_view *view) { | |||
77 | return; | 76 | return; |
78 | } | 77 | } |
79 | wl_list_remove(&view->events.unmap.listener_list); | 78 | wl_list_remove(&view->events.unmap.listener_list); |
80 | if (!wl_list_empty(&view->saved_buffers)) { | ||
81 | view_remove_saved_buffer(view); | ||
82 | } | ||
83 | list_free(view->executed_criteria); | 79 | list_free(view->executed_criteria); |
84 | 80 | ||
85 | view_assign_ctx(view, NULL); | 81 | view_assign_ctx(view, NULL); |
@@ -931,10 +927,10 @@ void view_center_surface(struct sway_view *view) { | |||
931 | struct sway_container *con = view->container; | 927 | struct sway_container *con = view->container; |
932 | // We always center the current coordinates rather than the next, as the | 928 | // We always center the current coordinates rather than the next, as the |
933 | // geometry immediately affects the currently active rendering. | 929 | // geometry immediately affects the currently active rendering. |
934 | con->surface_x = fmax(con->current.content_x, con->current.content_x + | 930 | int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); |
935 | (con->current.content_width - view->geometry.width) / 2); | 931 | int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); |
936 | con->surface_y = fmax(con->current.content_y, con->current.content_y + | 932 | |
937 | (con->current.content_height - view->geometry.height) / 2); | 933 | wlr_scene_node_set_position(&view->content_tree->node, x, y); |
938 | } | 934 | } |
939 | 935 | ||
940 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | 936 | struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { |
@@ -1161,40 +1157,54 @@ bool view_is_urgent(struct sway_view *view) { | |||
1161 | } | 1157 | } |
1162 | 1158 | ||
1163 | void view_remove_saved_buffer(struct sway_view *view) { | 1159 | void view_remove_saved_buffer(struct sway_view *view) { |
1164 | if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { | 1160 | if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { |
1165 | return; | 1161 | return; |
1166 | } | 1162 | } |
1167 | struct sway_saved_buffer *saved_buf, *tmp; | 1163 | |
1168 | wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { | 1164 | wlr_scene_node_destroy(&view->saved_surface_tree->node); |
1169 | wlr_buffer_unlock(&saved_buf->buffer->base); | 1165 | view->saved_surface_tree = NULL; |
1170 | wl_list_remove(&saved_buf->link); | 1166 | wlr_scene_node_set_enabled(&view->content_tree->node, true); |
1171 | free(saved_buf); | ||
1172 | } | ||
1173 | } | 1167 | } |
1174 | 1168 | ||
1175 | static void view_save_buffer_iterator(struct wlr_surface *surface, | 1169 | static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, |
1176 | int sx, int sy, void *data) { | 1170 | int sx, int sy, void *data) { |
1177 | struct sway_view *view = data; | 1171 | struct wlr_scene_tree *tree = data; |
1178 | 1172 | ||
1179 | if (surface && surface->buffer) { | 1173 | struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); |
1180 | wlr_buffer_lock(&surface->buffer->base); | 1174 | if (!sbuf) { |
1181 | struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); | 1175 | sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); |
1182 | saved_buffer->buffer = surface->buffer; | 1176 | return; |
1183 | saved_buffer->width = surface->current.width; | ||
1184 | saved_buffer->height = surface->current.height; | ||
1185 | saved_buffer->x = view->container->surface_x + sx; | ||
1186 | saved_buffer->y = view->container->surface_y + sy; | ||
1187 | saved_buffer->transform = surface->current.transform; | ||
1188 | wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); | ||
1189 | wl_list_insert(view->saved_buffers.prev, &saved_buffer->link); | ||
1190 | } | 1177 | } |
1178 | |||
1179 | wlr_scene_buffer_set_dest_size(sbuf, | ||
1180 | buffer->dst_width, buffer->dst_height); | ||
1181 | wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); | ||
1182 | wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); | ||
1183 | wlr_scene_node_set_position(&sbuf->node, sx, sy); | ||
1184 | wlr_scene_buffer_set_transform(sbuf, buffer->transform); | ||
1185 | wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); | ||
1191 | } | 1186 | } |
1192 | 1187 | ||
1193 | void view_save_buffer(struct sway_view *view) { | 1188 | void view_save_buffer(struct sway_view *view) { |
1194 | if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { | 1189 | if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { |
1195 | view_remove_saved_buffer(view); | 1190 | view_remove_saved_buffer(view); |
1196 | } | 1191 | } |
1197 | view_for_each_surface(view, view_save_buffer_iterator, view); | 1192 | |
1193 | view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); | ||
1194 | if (!view->saved_surface_tree) { | ||
1195 | sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); | ||
1196 | return; | ||
1197 | } | ||
1198 | |||
1199 | // Enable and disable the saved surface tree like so to atomitaclly update | ||
1200 | // the tree. This will prevent over damaging or other weirdness. | ||
1201 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); | ||
1202 | |||
1203 | wlr_scene_node_for_each_buffer(&view->content_tree->node, | ||
1204 | view_save_buffer_iterator, view->saved_surface_tree); | ||
1205 | |||
1206 | wlr_scene_node_set_enabled(&view->content_tree->node, false); | ||
1207 | wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true); | ||
1198 | } | 1208 | } |
1199 | 1209 | ||
1200 | bool view_is_transient_for(struct sway_view *child, | 1210 | bool view_is_transient_for(struct sway_view *child, |
@@ -1202,3 +1212,19 @@ bool view_is_transient_for(struct sway_view *child, | |||
1202 | return child->impl->is_transient_for && | 1212 | return child->impl->is_transient_for && |
1203 | child->impl->is_transient_for(child, ancestor); | 1213 | child->impl->is_transient_for(child, ancestor); |
1204 | } | 1214 | } |
1215 | |||
1216 | static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, | ||
1217 | int x, int y, void *data) { | ||
1218 | struct timespec *when = data; | ||
1219 | wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); | ||
1220 | } | ||
1221 | |||
1222 | void view_send_frame_done(struct sway_view *view) { | ||
1223 | struct timespec when; | ||
1224 | clock_gettime(CLOCK_MONOTONIC, &when); | ||
1225 | |||
1226 | struct wlr_scene_node *node; | ||
1227 | wl_list_for_each(node, &view->content_tree->children, link) { | ||
1228 | wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); | ||
1229 | } | ||
1230 | } | ||