diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index ab2dc8e4..53c5d34e 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -55,6 +55,13 @@ struct sway_view_impl { void (*destroy)(struct sway_view *view); }; +struct sway_saved_buffer { + struct wlr_client_buffer *buffer; + int x, y; + int width, height; + struct wl_list link; // sway_view::saved_buffers +}; + struct sway_view { enum sway_view_type type; const struct sway_view_impl *impl; @@ -65,9 +72,6 @@ struct sway_view { pid_t pid; - double saved_x, saved_y; - int saved_width, saved_height; - // The size the view would want to be if it weren't tiled. // Used when changing a view from tiled to floating. int natural_width, natural_height; @@ -80,8 +84,7 @@ struct sway_view { bool allow_request_urgent; struct wl_event_source *urgent_timer; - struct wlr_client_buffer *saved_buffer; - int saved_buffer_width, saved_buffer_height; + struct wl_list saved_buffers; // sway_saved_buffer::link // The geometry for whatever the client is committing, regardless of // transaction state. Updated on every commit. diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a86622e1..18250ae6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -511,7 +511,7 @@ static bool scan_out_fullscreen_view(struct sway_output *output, return false; } - if (view->saved_buffer) { + if (!wl_list_empty(&view->saved_buffers)) { return false; } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 14753df2..491a9bc0 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -280,38 +280,45 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output, pixman_region32_t *damage, float alpha) { struct wlr_output *wlr_output = output->wlr_output; - if (!view->saved_buffer || !view->saved_buffer->texture) { + if (wl_list_empty(&view->saved_buffers)) { return; } - struct wlr_box box = { - .x = view->container->surface_x - output->lx - - view->saved_geometry.x, - .y = view->container->surface_y - output->ly - - view->saved_geometry.y, - .width = view->saved_buffer_width, - .height = view->saved_buffer_height, - }; + struct sway_saved_buffer *saved_buf; + wl_list_for_each(saved_buf, &view->saved_buffers, link) { + if (!saved_buf->buffer->texture) { + continue; + } - struct wlr_box output_box = { - .width = output->width, - .height = output->height, - }; + struct wlr_box box = { + .x = view->container->surface_x - output->lx - + view->saved_geometry.x + saved_buf->x, + .y = view->container->surface_y - output->ly - + view->saved_geometry.y + saved_buf->y, + .width = saved_buf->width, + .height = saved_buf->height, + }; - struct wlr_box intersection; - bool intersects = wlr_box_intersection(&intersection, &output_box, &box); - if (!intersects) { - return; + struct wlr_box output_box = { + .width = output->width, + .height = output->height, + }; + + struct wlr_box intersection; + bool intersects = wlr_box_intersection(&intersection, &output_box, &box); + if (!intersects) { + continue; + } + + scale_box(&box, wlr_output->scale); + + float matrix[9]; + wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + wlr_output->transform_matrix); + + render_texture(wlr_output, damage, saved_buf->buffer->texture, + &box, matrix, alpha); } - scale_box(&box, wlr_output->scale); - - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); - - render_texture(wlr_output, damage, view->saved_buffer->texture, - &box, matrix, alpha); - // FIXME: we should set the surface that this saved buffer originates from // as sampled here. // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 @@ -323,7 +330,7 @@ static void render_saved_view(struct sway_view *view, static void render_view(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, struct border_colors *colors) { struct sway_view *view = con->view; - if (view->saved_buffer) { + if (!wl_list_empty(&view->saved_buffers)) { render_saved_view(view, output, damage, view->container->alpha); } else if (view->surface) { render_view_toplevels(view, output, damage, view->container->alpha); @@ -1020,7 +1027,7 @@ void output_render(struct sway_output *output, struct timespec *when, } if (fullscreen_con->view) { - if (fullscreen_con->view->saved_buffer) { + if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { render_saved_view(fullscreen_con->view, output, damage, 1.0f); } else if (fullscreen_con->view->surface) { render_view_toplevels(fullscreen_con->view, diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index ccf60514..ef656102 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -210,14 +210,17 @@ static void apply_container_state(struct sway_container *container, struct sway_view *view = container->view; // Damage the old location desktop_damage_whole_container(container); - if (view && view->saved_buffer) { - struct wlr_box box = { - .x = container->current.content_x - view->saved_geometry.x, - .y = container->current.content_y - view->saved_geometry.y, - .width = view->saved_buffer_width, - .height = view->saved_buffer_height, - }; - desktop_damage_box(&box); + if (view && !wl_list_empty(&view->saved_buffers)) { + struct sway_saved_buffer *saved_buf; + wl_list_for_each(saved_buf, &view->saved_buffers, link) { + struct wlr_box box = { + .x = container->current.content_x - view->saved_geometry.x + saved_buf->x, + .y = container->current.content_y - view->saved_geometry.y + saved_buf->y, + .width = saved_buf->width, + .height = saved_buf->height, + }; + desktop_damage_box(&box); + } } // There are separate children lists for each instruction state, the @@ -229,7 +232,7 @@ static void apply_container_state(struct sway_container *container, memcpy(&container->current, state, sizeof(struct sway_container_state)); - if (view && view->saved_buffer) { + if (view && !wl_list_empty(&view->saved_buffers)) { if (!container->node.destroying || container->node.ntxnrefs == 1) { view_remove_saved_buffer(view); } @@ -432,7 +435,7 @@ static void transaction_commit(struct sway_transaction *transaction) { wlr_surface_send_frame_done( node->sway_container->view->surface, &now); } - if (node_is_view(node) && !node->sway_container->view->saved_buffer) { + if (node_is_view(node) && wl_list_empty(&node->sway_container->view->saved_buffers)) { view_save_buffer(node->sway_container->view); memcpy(&node->sway_container->view->saved_geometry, &node->sway_container->view->geometry, diff --git a/sway/tree/view.c b/sway/tree/view.c index 8e12a229..25951deb 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -36,6 +36,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, view->type = type; view->impl = impl; view->executed_criteria = create_list(); + wl_list_init(&view->saved_buffers); view->allow_request_urgent = true; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; wl_signal_init(&view->events.unmap); @@ -54,6 +55,9 @@ void view_destroy(struct sway_view *view) { "(might have a pending transaction?)")) { return; } + if (!wl_list_empty(&view->saved_buffers)) { + view_remove_saved_buffer(view); + } list_free(view->executed_criteria); free(view->title_format); @@ -1176,23 +1180,38 @@ bool view_is_urgent(struct sway_view *view) { } void view_remove_saved_buffer(struct sway_view *view) { - if (!sway_assert(view->saved_buffer, "Expected a saved buffer")) { + if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { return; } - wlr_buffer_unlock(&view->saved_buffer->base); - view->saved_buffer = NULL; + struct sway_saved_buffer *saved_buf, *tmp; + wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { + wlr_buffer_unlock(&saved_buf->buffer->base); + wl_list_remove(&saved_buf->link); + free(saved_buf); + } +} + +static void view_save_buffer_iterator(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct sway_view *view = data; + + if (surface && wlr_surface_has_buffer(surface)) { + wlr_buffer_lock(&surface->buffer->base); + struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); + saved_buffer->buffer = surface->buffer; + saved_buffer->width = surface->current.width; + saved_buffer->height = surface->current.height; + saved_buffer->x = sx; + saved_buffer->y = sy; + wl_list_insert(&view->saved_buffers, &saved_buffer->link); + } } void view_save_buffer(struct sway_view *view) { - if (!sway_assert(!view->saved_buffer, "Didn't expect saved buffer")) { + if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { view_remove_saved_buffer(view); } - if (view->surface && wlr_surface_has_buffer(view->surface)) { - wlr_buffer_lock(&view->surface->buffer->base); - view->saved_buffer = view->surface->buffer; - view->saved_buffer_width = view->surface->current.width; - view->saved_buffer_height = view->surface->current.height; - } + view_for_each_surface(view, view_save_buffer_iterator, view); } bool view_is_transient_for(struct sway_view *child,