mirror of
https://github.com/swaywm/sway.git
synced 2026-04-23 06:46:27 -04:00
Use wlroots surface locking for transactions
References: https://github.com/swaywm/wlroots/issues/2957
This commit is contained in:
parent
22226560e3
commit
6f9940c039
7 changed files with 92 additions and 140 deletions
|
|
@ -42,6 +42,11 @@ void transaction_commit_dirty_client(void);
|
|||
void transaction_notify_view_ready_by_serial(struct sway_view *view,
|
||||
uint32_t serial);
|
||||
|
||||
/**
|
||||
* Unlock the cached surface state held by the transaction system.
|
||||
*/
|
||||
void transaction_unlock_view_by_serial(struct sway_view *view, uint32_t serial);
|
||||
|
||||
/**
|
||||
* Notify the transaction system that a view is ready for the new layout, but
|
||||
* identifying the instruction by geometry rather than by serial.
|
||||
|
|
|
|||
|
|
@ -56,15 +56,6 @@ 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;
|
||||
enum wl_output_transform transform;
|
||||
struct wlr_fbox source_box;
|
||||
struct wl_list link; // sway_view::saved_buffers
|
||||
};
|
||||
|
||||
struct sway_view {
|
||||
enum sway_view_type type;
|
||||
const struct sway_view_impl *impl;
|
||||
|
|
@ -87,7 +78,8 @@ struct sway_view {
|
|||
bool allow_request_urgent;
|
||||
struct wl_event_source *urgent_timer;
|
||||
|
||||
struct wl_list saved_buffers; // sway_saved_buffer::link
|
||||
bool surface_locked;
|
||||
uint32_t surface_locked_seq;
|
||||
|
||||
// The geometry for whatever the client is committing, regardless of
|
||||
// transaction state. Updated on every commit.
|
||||
|
|
@ -129,6 +121,7 @@ struct sway_view {
|
|||
struct sway_xdg_shell_view {
|
||||
struct sway_view view;
|
||||
|
||||
struct wl_listener cache;
|
||||
struct wl_listener commit;
|
||||
struct wl_listener request_move;
|
||||
struct wl_listener request_resize;
|
||||
|
|
@ -357,9 +350,9 @@ void view_set_urgent(struct sway_view *view, bool enable);
|
|||
|
||||
bool view_is_urgent(struct sway_view *view);
|
||||
|
||||
void view_remove_saved_buffer(struct sway_view *view);
|
||||
void view_lock_pending(struct sway_view *view);
|
||||
|
||||
void view_save_buffer(struct sway_view *view);
|
||||
void view_unlock_pending(struct sway_view *view);
|
||||
|
||||
bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor);
|
||||
|
||||
|
|
|
|||
|
|
@ -432,10 +432,6 @@ static bool scan_out_fullscreen_view(struct sway_output *output,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!wl_list_empty(&view->saved_buffers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < workspace->current.floating->length; ++i) {
|
||||
struct sway_container *floater =
|
||||
workspace->current.floating->items[i];
|
||||
|
|
|
|||
|
|
@ -289,76 +289,13 @@ static void render_view_popups(struct sway_view *view,
|
|||
render_surface_iterator, &data);
|
||||
}
|
||||
|
||||
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 (wl_list_empty(&view->saved_buffers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool floating = container_is_current_floating(view->container);
|
||||
|
||||
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 proj_box = {
|
||||
.x = saved_buf->x - view->saved_geometry.x - output->lx,
|
||||
.y = saved_buf->y - view->saved_geometry.y - output->ly,
|
||||
.width = saved_buf->width,
|
||||
.height = saved_buf->height,
|
||||
};
|
||||
|
||||
struct wlr_box output_box = {
|
||||
.width = output->width,
|
||||
.height = output->height,
|
||||
};
|
||||
|
||||
struct wlr_box intersection;
|
||||
bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box);
|
||||
if (!intersects) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct wlr_box dst_box = proj_box;
|
||||
scale_box(&proj_box, wlr_output->scale);
|
||||
|
||||
float matrix[9];
|
||||
enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform);
|
||||
wlr_matrix_project_box(matrix, &proj_box, transform, 0,
|
||||
wlr_output->transform_matrix);
|
||||
|
||||
if (!floating) {
|
||||
dst_box.width = fmin(dst_box.width,
|
||||
view->container->current.content_width -
|
||||
(saved_buf->x - view->container->current.content_x) + view->saved_geometry.x);
|
||||
dst_box.height = fmin(dst_box.height,
|
||||
view->container->current.content_height -
|
||||
(saved_buf->y - view->container->current.content_y) + view->saved_geometry.y);
|
||||
}
|
||||
scale_box(&dst_box, wlr_output->scale);
|
||||
|
||||
render_texture(wlr_output, damage, saved_buf->buffer->texture,
|
||||
&saved_buf->source_box, &dst_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
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a view's surface and left/bottom/right borders.
|
||||
*/
|
||||
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 (!wl_list_empty(&view->saved_buffers)) {
|
||||
render_saved_view(view, output, damage, view->container->alpha);
|
||||
} else if (view->surface) {
|
||||
if (view->surface) {
|
||||
render_view_toplevels(view, output, damage, view->container->alpha);
|
||||
}
|
||||
|
||||
|
|
@ -1063,9 +1000,7 @@ void output_render(struct sway_output *output, struct timespec *when,
|
|||
}
|
||||
|
||||
if (fullscreen_con->view) {
|
||||
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) {
|
||||
if (fullscreen_con->view->surface) {
|
||||
render_view_toplevels(fullscreen_con->view,
|
||||
output, damage, 1.0f);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,20 +232,6 @@ static void apply_workspace_state(struct sway_workspace *ws,
|
|||
static void apply_container_state(struct sway_container *container,
|
||||
struct sway_container_state *state) {
|
||||
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
|
||||
// container's current state and the container's pending state
|
||||
|
|
@ -256,12 +242,6 @@ static void apply_container_state(struct sway_container *container,
|
|||
|
||||
memcpy(&container->current, state, sizeof(struct sway_container_state));
|
||||
|
||||
if (view && !wl_list_empty(&view->saved_buffers)) {
|
||||
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
|
||||
// the container. This is important for fullscreen views which
|
||||
// refuse to resize to the size of the output.
|
||||
|
|
@ -303,6 +283,7 @@ static void transaction_apply(struct sway_transaction *transaction) {
|
|||
}
|
||||
|
||||
// Apply the instruction state to the node's current state
|
||||
// Applying the last instruction may destroy the transaction
|
||||
for (int i = 0; i < transaction->instructions->length; ++i) {
|
||||
struct sway_transaction_instruction *instruction =
|
||||
transaction->instructions->items[i];
|
||||
|
|
@ -355,8 +336,29 @@ static int handle_timeout(void *data) {
|
|||
struct sway_transaction *transaction = data;
|
||||
sway_log(SWAY_DEBUG, "Transaction %p timed out (%zi waiting)",
|
||||
transaction, transaction->num_waiting);
|
||||
transaction->num_waiting = 0;
|
||||
transaction_progress();
|
||||
|
||||
struct sway_view **views =
|
||||
calloc(transaction->instructions->length, sizeof(struct sway_view *));
|
||||
size_t views_len = 0;
|
||||
|
||||
for (int i = 0; i < transaction->instructions->length; ++i) {
|
||||
struct sway_transaction_instruction *instruction =
|
||||
transaction->instructions->items[i];
|
||||
struct sway_node *node = instruction->node;
|
||||
|
||||
if (node->type == N_CONTAINER && node->sway_container->view &&
|
||||
node->sway_container->view->surface_locked) {
|
||||
views[views_len] = node->sway_container->view;
|
||||
views_len++;
|
||||
}
|
||||
}
|
||||
|
||||
// This will destroy the transaction
|
||||
for (size_t i = 0; i < views_len; ++i) {
|
||||
view_unlock_pending(views[i]);
|
||||
}
|
||||
free(views);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -415,7 +417,7 @@ static void transaction_commit(struct sway_transaction *transaction) {
|
|||
++transaction->num_waiting;
|
||||
}
|
||||
|
||||
// From here on we are rendering a saved buffer of the view, which
|
||||
// From here on we are freezing surface commits for the view, which
|
||||
// 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.
|
||||
|
|
@ -425,8 +427,8 @@ static void transaction_commit(struct sway_transaction *transaction) {
|
|||
node->sway_container->view->surface, &now);
|
||||
}
|
||||
if (!hidden && node_is_view(node) &&
|
||||
wl_list_empty(&node->sway_container->view->saved_buffers)) {
|
||||
view_save_buffer(node->sway_container->view);
|
||||
!node->sway_container->view->surface_locked) {
|
||||
view_lock_pending(node->sway_container->view);
|
||||
memcpy(&node->sway_container->view->saved_geometry,
|
||||
&node->sway_container->view->geometry,
|
||||
sizeof(struct wlr_box));
|
||||
|
|
@ -508,6 +510,15 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view,
|
|||
}
|
||||
}
|
||||
|
||||
void transaction_unlock_view_by_serial(struct sway_view *view,
|
||||
uint32_t serial) {
|
||||
struct sway_transaction_instruction *instruction =
|
||||
view->container->node.instruction;
|
||||
if (instruction != NULL && instruction->serial == serial) {
|
||||
view_unlock_pending(view);
|
||||
}
|
||||
}
|
||||
|
||||
void transaction_notify_view_ready_by_geometry(struct sway_view *view,
|
||||
double x, double y, int width, int height) {
|
||||
struct sway_transaction_instruction *instruction =
|
||||
|
|
|
|||
|
|
@ -279,6 +279,26 @@ static const struct sway_view_impl view_impl = {
|
|||
.destroy = destroy,
|
||||
};
|
||||
|
||||
static void handle_cache(struct wl_listener *listener, void *data) {
|
||||
struct sway_xdg_shell_view *xdg_shell_view =
|
||||
wl_container_of(listener, xdg_shell_view, cache);
|
||||
struct wlr_surface_state *cached = data;
|
||||
struct sway_view *view = &xdg_shell_view->view;
|
||||
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
|
||||
|
||||
if (!view->container->node.instruction) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_xdg_surface_state *xdg_cached;
|
||||
wl_list_for_each(xdg_cached, &xdg_surface->cached, cached_state_link) {
|
||||
if (cached->seq == xdg_cached->seq) {
|
||||
transaction_unlock_view_by_serial(view, xdg_cached->configure_serial);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_commit(struct wl_listener *listener, void *data) {
|
||||
struct sway_xdg_shell_view *xdg_shell_view =
|
||||
wl_container_of(listener, xdg_shell_view, commit);
|
||||
|
|
@ -418,6 +438,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
|
|||
|
||||
view_unmap(view);
|
||||
|
||||
wl_list_remove(&xdg_shell_view->cache.link);
|
||||
wl_list_remove(&xdg_shell_view->commit.link);
|
||||
wl_list_remove(&xdg_shell_view->new_popup.link);
|
||||
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
|
||||
|
|
@ -458,6 +479,10 @@ static void handle_map(struct wl_listener *listener, void *data) {
|
|||
|
||||
transaction_commit_dirty();
|
||||
|
||||
xdg_shell_view->cache.notify = handle_cache;
|
||||
wl_signal_add(&xdg_surface->surface->events.cache,
|
||||
&xdg_shell_view->cache);
|
||||
|
||||
xdg_shell_view->commit.notify = handle_commit;
|
||||
wl_signal_add(&xdg_surface->surface->events.commit,
|
||||
&xdg_shell_view->commit);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ 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);
|
||||
|
|
@ -57,11 +56,12 @@ void view_destroy(struct sway_view *view) {
|
|||
return;
|
||||
}
|
||||
wl_list_remove(&view->events.unmap.listener_list);
|
||||
if (!wl_list_empty(&view->saved_buffers)) {
|
||||
view_remove_saved_buffer(view);
|
||||
if (view->surface_locked) {
|
||||
view_unlock_pending(view);
|
||||
}
|
||||
list_free(view->executed_criteria);
|
||||
|
||||
|
||||
free(view->title_format);
|
||||
|
||||
if (view->impl->destroy) {
|
||||
|
|
@ -1376,41 +1376,28 @@ bool view_is_urgent(struct sway_view *view) {
|
|||
return view->urgent.tv_sec || view->urgent.tv_nsec;
|
||||
}
|
||||
|
||||
void view_remove_saved_buffer(struct sway_view *view) {
|
||||
if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) {
|
||||
void view_lock_pending(struct sway_view *view) {
|
||||
if (!sway_assert(!view->surface_locked,
|
||||
"Didn't expect surface to be already locked")) {
|
||||
view_unlock_pending(view);
|
||||
}
|
||||
|
||||
if (view->surface == NULL) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// TODO: maybe lock the whole surface tree?
|
||||
view->surface_locked = true;
|
||||
view->surface_locked_seq = wlr_surface_lock_pending(view->surface);
|
||||
}
|
||||
|
||||
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 = 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, &saved_buffer->link);
|
||||
void view_unlock_pending(struct sway_view *view) {
|
||||
if (!sway_assert(view->surface_locked, "Expected surface to be locked")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void view_save_buffer(struct sway_view *view) {
|
||||
if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) {
|
||||
view_remove_saved_buffer(view);
|
||||
}
|
||||
view_for_each_surface(view, view_save_buffer_iterator, view);
|
||||
view->surface_locked = false;
|
||||
wlr_surface_unlock_cached(view->surface, view->surface_locked_seq);
|
||||
}
|
||||
|
||||
bool view_is_transient_for(struct sway_view *child,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue