mirror of
https://github.com/NickHu/sway
synced 2024-12-26 21:58:30 +01:00
scene_graph: Port view saved buffers
This commit is contained in:
parent
ed2724bd6c
commit
06ad734e70
5 changed files with 90 additions and 83 deletions
|
@ -57,21 +57,13 @@ struct sway_view_impl {
|
||||||
void (*destroy)(struct sway_view *view);
|
void (*destroy)(struct sway_view *view);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sway_saved_buffer {
|
|
||||||
struct wlr_client_buffer *buffer;
|
|
||||||
int x, y;
|
|
||||||
int width, height;
|
|
||||||
enum wl_output_transform transform;
|
|
||||||
struct wlr_fbox source_box;
|
|
||||||
struct wl_list link; // sway_view::saved_buffers
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sway_view {
|
struct sway_view {
|
||||||
enum sway_view_type type;
|
enum sway_view_type type;
|
||||||
const struct sway_view_impl *impl;
|
const struct sway_view_impl *impl;
|
||||||
|
|
||||||
struct wlr_scene_tree *scene_tree;
|
struct wlr_scene_tree *scene_tree;
|
||||||
struct wlr_scene_tree *content_tree;
|
struct wlr_scene_tree *content_tree;
|
||||||
|
struct wlr_scene_tree *saved_surface_tree;
|
||||||
|
|
||||||
struct sway_container *container; // NULL if unmapped and transactions finished
|
struct sway_container *container; // NULL if unmapped and transactions finished
|
||||||
struct wlr_surface *surface; // NULL for unmapped views
|
struct wlr_surface *surface; // NULL for unmapped views
|
||||||
|
@ -92,16 +84,10 @@ struct sway_view {
|
||||||
bool allow_request_urgent;
|
bool allow_request_urgent;
|
||||||
struct wl_event_source *urgent_timer;
|
struct wl_event_source *urgent_timer;
|
||||||
|
|
||||||
struct wl_list saved_buffers; // sway_saved_buffer::link
|
|
||||||
|
|
||||||
// The geometry for whatever the client is committing, regardless of
|
// The geometry for whatever the client is committing, regardless of
|
||||||
// transaction state. Updated on every commit.
|
// transaction state. Updated on every commit.
|
||||||
struct wlr_box geometry;
|
struct wlr_box geometry;
|
||||||
|
|
||||||
// The "old" geometry during a transaction. Used to damage the old location
|
|
||||||
// when a transaction is applied.
|
|
||||||
struct wlr_box saved_geometry;
|
|
||||||
|
|
||||||
struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel;
|
struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel;
|
||||||
struct wl_listener foreign_activate_request;
|
struct wl_listener foreign_activate_request;
|
||||||
struct wl_listener foreign_fullscreen_request;
|
struct wl_listener foreign_fullscreen_request;
|
||||||
|
@ -347,4 +333,6 @@ bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor);
|
||||||
|
|
||||||
void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
|
void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
|
||||||
|
|
||||||
|
void view_send_frame_done(struct sway_view *view);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -232,21 +232,6 @@ static void apply_workspace_state(struct sway_workspace *ws,
|
||||||
static void apply_container_state(struct sway_container *container,
|
static void apply_container_state(struct sway_container *container,
|
||||||
struct sway_container_state *state) {
|
struct sway_container_state *state) {
|
||||||
struct sway_view *view = container->view;
|
struct sway_view *view = container->view;
|
||||||
// Damage the old location
|
|
||||||
desktop_damage_whole_container(container);
|
|
||||||
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 = saved_buf->x - view->saved_geometry.x,
|
|
||||||
.y = saved_buf->y - view->saved_geometry.y,
|
|
||||||
.width = saved_buf->width,
|
|
||||||
.height = saved_buf->height,
|
|
||||||
};
|
|
||||||
desktop_damage_box(&box);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There are separate children lists for each instruction state, the
|
// There are separate children lists for each instruction state, the
|
||||||
// container's current state and the container's pending state
|
// container's current state and the container's pending state
|
||||||
// (ie. con->children). The list itself needs to be freed here.
|
// (ie. con->children). The list itself needs to be freed here.
|
||||||
|
@ -256,17 +241,19 @@ static void apply_container_state(struct sway_container *container,
|
||||||
|
|
||||||
memcpy(&container->current, state, sizeof(struct sway_container_state));
|
memcpy(&container->current, state, sizeof(struct sway_container_state));
|
||||||
|
|
||||||
if (view && !wl_list_empty(&view->saved_buffers)) {
|
if (view) {
|
||||||
if (!container->node.destroying || container->node.ntxnrefs == 1) {
|
if (view->saved_surface_tree) {
|
||||||
view_remove_saved_buffer(view);
|
if (!container->node.destroying || container->node.ntxnrefs == 1) {
|
||||||
|
view_remove_saved_buffer(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If the view hasn't responded to the configure, center it within
|
// If the view hasn't responded to the configure, center it within
|
||||||
// the container. This is important for fullscreen views which
|
// the container. This is important for fullscreen views which
|
||||||
// refuse to resize to the size of the output.
|
// refuse to resize to the size of the output.
|
||||||
if (view && view->surface) {
|
if (view->surface) {
|
||||||
view_center_surface(view);
|
view_center_surface(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Damage the new location
|
// Damage the new location
|
||||||
|
@ -415,21 +402,11 @@ static void transaction_commit(struct sway_transaction *transaction) {
|
||||||
++transaction->num_waiting;
|
++transaction->num_waiting;
|
||||||
}
|
}
|
||||||
|
|
||||||
// From here on we are rendering a saved buffer of the view, which
|
view_send_frame_done(node->sway_container->view);
|
||||||
// means we can send a frame done event to make the client redraw it
|
|
||||||
// as soon as possible. Additionally, this is required if a view is
|
|
||||||
// mapping and its default geometry doesn't intersect an output.
|
|
||||||
struct timespec now;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
wlr_surface_send_frame_done(
|
|
||||||
node->sway_container->view->surface, &now);
|
|
||||||
}
|
}
|
||||||
if (!hidden && node_is_view(node) &&
|
if (!hidden && node_is_view(node) &&
|
||||||
wl_list_empty(&node->sway_container->view->saved_buffers)) {
|
!node->sway_container->view->saved_surface_tree) {
|
||||||
view_save_buffer(node->sway_container->view);
|
view_save_buffer(node->sway_container->view);
|
||||||
memcpy(&node->sway_container->view->saved_geometry,
|
|
||||||
&node->sway_container->view->geometry,
|
|
||||||
sizeof(struct wlr_box));
|
|
||||||
}
|
}
|
||||||
node->instruction = instruction;
|
node->instruction = instruction;
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,8 +332,16 @@ static void handle_commit(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (view->container->node.instruction) {
|
if (view->container->node.instruction) {
|
||||||
transaction_notify_view_ready_by_serial(view,
|
bool successful = transaction_notify_view_ready_by_serial(view,
|
||||||
xdg_surface->current.configure_serial);
|
xdg_surface->current.configure_serial);
|
||||||
|
|
||||||
|
// If we saved the view and this commit isn't what we're looking for
|
||||||
|
// that means the user will never actually see the buffers submitted to
|
||||||
|
// us here. Just send frame done events to these surfaces so they can
|
||||||
|
// commit another time for us.
|
||||||
|
if (view->saved_surface_tree && !successful) {
|
||||||
|
view_send_frame_done(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -442,8 +442,16 @@ static void handle_commit(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (view->container->node.instruction) {
|
if (view->container->node.instruction) {
|
||||||
transaction_notify_view_ready_by_geometry(view,
|
bool successful = transaction_notify_view_ready_by_geometry(view,
|
||||||
xsurface->x, xsurface->y, state->width, state->height);
|
xsurface->x, xsurface->y, state->width, state->height);
|
||||||
|
|
||||||
|
// If we saved the view and this commit isn't what we're looking for
|
||||||
|
// that means the user will never actually see the buffers submitted to
|
||||||
|
// us here. Just send frame done events to these surfaces so they can
|
||||||
|
// commit another time for us.
|
||||||
|
if (view->saved_surface_tree && !successful) {
|
||||||
|
view_send_frame_done(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,6 @@ bool view_init(struct sway_view *view, enum sway_view_type type,
|
||||||
view->type = type;
|
view->type = type;
|
||||||
view->impl = impl;
|
view->impl = impl;
|
||||||
view->executed_criteria = create_list();
|
view->executed_criteria = create_list();
|
||||||
wl_list_init(&view->saved_buffers);
|
|
||||||
view->allow_request_urgent = true;
|
view->allow_request_urgent = true;
|
||||||
view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
|
view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
|
||||||
wl_signal_init(&view->events.unmap);
|
wl_signal_init(&view->events.unmap);
|
||||||
|
@ -77,9 +76,6 @@ void view_destroy(struct sway_view *view) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wl_list_remove(&view->events.unmap.listener_list);
|
wl_list_remove(&view->events.unmap.listener_list);
|
||||||
if (!wl_list_empty(&view->saved_buffers)) {
|
|
||||||
view_remove_saved_buffer(view);
|
|
||||||
}
|
|
||||||
list_free(view->executed_criteria);
|
list_free(view->executed_criteria);
|
||||||
|
|
||||||
view_assign_ctx(view, NULL);
|
view_assign_ctx(view, NULL);
|
||||||
|
@ -931,10 +927,10 @@ void view_center_surface(struct sway_view *view) {
|
||||||
struct sway_container *con = view->container;
|
struct sway_container *con = view->container;
|
||||||
// We always center the current coordinates rather than the next, as the
|
// We always center the current coordinates rather than the next, as the
|
||||||
// geometry immediately affects the currently active rendering.
|
// geometry immediately affects the currently active rendering.
|
||||||
con->surface_x = fmax(con->current.content_x, con->current.content_x +
|
int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2);
|
||||||
(con->current.content_width - view->geometry.width) / 2);
|
int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2);
|
||||||
con->surface_y = fmax(con->current.content_y, con->current.content_y +
|
|
||||||
(con->current.content_height - view->geometry.height) / 2);
|
wlr_scene_node_set_position(&view->content_tree->node, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
|
struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
|
||||||
|
@ -1161,40 +1157,54 @@ bool view_is_urgent(struct sway_view *view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void view_remove_saved_buffer(struct sway_view *view) {
|
void view_remove_saved_buffer(struct sway_view *view) {
|
||||||
if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) {
|
if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct sway_saved_buffer *saved_buf, *tmp;
|
|
||||||
wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) {
|
wlr_scene_node_destroy(&view->saved_surface_tree->node);
|
||||||
wlr_buffer_unlock(&saved_buf->buffer->base);
|
view->saved_surface_tree = NULL;
|
||||||
wl_list_remove(&saved_buf->link);
|
wlr_scene_node_set_enabled(&view->content_tree->node, true);
|
||||||
free(saved_buf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void view_save_buffer_iterator(struct wlr_surface *surface,
|
static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer,
|
||||||
int sx, int sy, void *data) {
|
int sx, int sy, void *data) {
|
||||||
struct sway_view *view = data;
|
struct wlr_scene_tree *tree = data;
|
||||||
|
|
||||||
if (surface && surface->buffer) {
|
struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL);
|
||||||
wlr_buffer_lock(&surface->buffer->base);
|
if (!sbuf) {
|
||||||
struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer));
|
sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface");
|
||||||
saved_buffer->buffer = surface->buffer;
|
return;
|
||||||
saved_buffer->width = surface->current.width;
|
|
||||||
saved_buffer->height = surface->current.height;
|
|
||||||
saved_buffer->x = view->container->surface_x + sx;
|
|
||||||
saved_buffer->y = view->container->surface_y + sy;
|
|
||||||
saved_buffer->transform = surface->current.transform;
|
|
||||||
wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box);
|
|
||||||
wl_list_insert(view->saved_buffers.prev, &saved_buffer->link);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_scene_buffer_set_dest_size(sbuf,
|
||||||
|
buffer->dst_width, buffer->dst_height);
|
||||||
|
wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region);
|
||||||
|
wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box);
|
||||||
|
wlr_scene_node_set_position(&sbuf->node, sx, sy);
|
||||||
|
wlr_scene_buffer_set_transform(sbuf, buffer->transform);
|
||||||
|
wlr_scene_buffer_set_buffer(sbuf, buffer->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void view_save_buffer(struct sway_view *view) {
|
void view_save_buffer(struct sway_view *view) {
|
||||||
if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) {
|
if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) {
|
||||||
view_remove_saved_buffer(view);
|
view_remove_saved_buffer(view);
|
||||||
}
|
}
|
||||||
view_for_each_surface(view, view_save_buffer_iterator, view);
|
|
||||||
|
view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree);
|
||||||
|
if (!view->saved_surface_tree) {
|
||||||
|
sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable and disable the saved surface tree like so to atomitaclly update
|
||||||
|
// the tree. This will prevent over damaging or other weirdness.
|
||||||
|
wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false);
|
||||||
|
|
||||||
|
wlr_scene_node_for_each_buffer(&view->content_tree->node,
|
||||||
|
view_save_buffer_iterator, view->saved_surface_tree);
|
||||||
|
|
||||||
|
wlr_scene_node_set_enabled(&view->content_tree->node, false);
|
||||||
|
wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool view_is_transient_for(struct sway_view *child,
|
bool view_is_transient_for(struct sway_view *child,
|
||||||
|
@ -1202,3 +1212,19 @@ bool view_is_transient_for(struct sway_view *child,
|
||||||
return child->impl->is_transient_for &&
|
return child->impl->is_transient_for &&
|
||||||
child->impl->is_transient_for(child, ancestor);
|
child->impl->is_transient_for(child, ancestor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer,
|
||||||
|
int x, int y, void *data) {
|
||||||
|
struct timespec *when = data;
|
||||||
|
wl_signal_emit_mutable(&scene_buffer->events.frame_done, when);
|
||||||
|
}
|
||||||
|
|
||||||
|
void view_send_frame_done(struct sway_view *view) {
|
||||||
|
struct timespec when;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &when);
|
||||||
|
|
||||||
|
struct wlr_scene_node *node;
|
||||||
|
wl_list_for_each(node, &view->content_tree->children, link) {
|
||||||
|
wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue