From 59c94887018bdfa578c4371c4275061ca6e71b3e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 3 Jun 2018 16:35:06 +1000 Subject: WIP: Atomic layout updates ground work --- sway/desktop/output.c | 70 +++++++++++++-- sway/desktop/transaction.c | 214 ++++++++++++++++++++++++++++++++++++++++++++ sway/desktop/xdg_shell.c | 29 +++--- sway/desktop/xdg_shell_v6.c | 30 +++---- sway/desktop/xwayland.c | 33 ++++--- 5 files changed, 317 insertions(+), 59 deletions(-) create mode 100644 sway/desktop/transaction.c (limited to 'sway/desktop') diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 3142bdb4..c5d445a6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -69,6 +69,7 @@ struct render_data { struct root_geometry root_geo; struct sway_output *output; pixman_region32_t *damage; + struct sway_view *view; float alpha; }; @@ -108,6 +109,38 @@ static bool get_surface_box(struct root_geometry *geo, return wlr_box_intersection(&output_box, &rotated_box, &intersection); } +static bool get_view_box(struct root_geometry *geo, + struct sway_output *output, struct sway_view *view, int sx, int sy, + struct wlr_box *surface_box) { + int sw = view->width; + int sh = view->height; + + double _sx = sx, _sy = sy; + rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, + geo->rotation); + + struct wlr_box box = { + .x = geo->x + _sx, + .y = geo->y + _sy, + .width = sw, + .height = sh, + }; + if (surface_box != NULL) { + memcpy(surface_box, &box, sizeof(struct wlr_box)); + } + + struct wlr_box rotated_box; + wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); + + struct wlr_box output_box = { + .width = output->swayc->width, + .height = output->swayc->height, + }; + + struct wlr_box intersection; + return wlr_box_intersection(&output_box, &rotated_box, &intersection); +} + static void surface_for_each_surface(struct wlr_surface *surface, double ox, double oy, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { @@ -225,13 +258,26 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, pixman_region32_t *output_damage = data->damage; float alpha = data->alpha; - if (!wlr_surface_has_buffer(surface)) { - return; + struct wlr_texture *texture = NULL; + struct wlr_box box; + bool intersects; + + // If this is the main surface of a view, render the saved_texture instead + // if it exists. It exists when we are mid-transaction. + if (data->view && data->view->saved_texture && + data->view->surface == surface) { + texture = data->view->saved_texture; + intersects = get_view_box(&data->root_geo, data->output, data->view, + sx, sy, &box); + } else { + if (!wlr_surface_has_buffer(surface)) { + return; + } + texture = surface->texture; + intersects = get_surface_box(&data->root_geo, data->output, surface, + sx, sy, &box); } - struct wlr_box box; - bool intersects = get_surface_box(&data->root_geo, data->output, surface, - sx, sy, &box); if (!intersects) { return; } @@ -244,8 +290,7 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, wlr_matrix_project_box(matrix, &box, transform, rotation, wlr_output->transform_matrix); - render_texture(wlr_output, output_damage, surface->texture, &box, matrix, - alpha); + render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); } static void render_layer(struct sway_output *output, @@ -315,6 +360,7 @@ static void render_view_surfaces(struct sway_view *view, struct render_data data = { .output = output, .damage = damage, + .view = view, .alpha = alpha, }; output_view_for_each_surface( @@ -1134,6 +1180,16 @@ void output_damage_from_view(struct sway_output *output, output_damage_view(output, view, false); } +// Expecting an unscaled box in layout coordinates +void output_damage_box(struct sway_output *output, struct wlr_box *_box) { + struct wlr_box box; + memcpy(&box, _box, sizeof(struct wlr_box)); + box.x -= output->swayc->x; + box.y -= output->swayc->y; + scale_box(&box, output->wlr_output->scale); + wlr_output_damage_add_box(output->damage, &box); +} + static void output_damage_whole_container_iterator(struct sway_container *con, void *data) { struct sway_output *output = data; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c new file mode 100644 index 00000000..69f97e3d --- /dev/null +++ b/sway/desktop/transaction.c @@ -0,0 +1,214 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include "sway/debug.h" +#include "sway/desktop/transaction.h" +#include "sway/output.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "list.h" +#include "log.h" + +/** + * How long we should wait for views to respond to the configure before giving + * up and applying the transaction anyway. + */ +#define TIMEOUT_MS 200 + +struct sway_transaction_instruction { + struct sway_transaction *transaction; + struct sway_container *container; + struct sway_container_state state; + uint32_t serial; +}; + +struct sway_transaction *transaction_create() { + struct sway_transaction *transaction = + calloc(1, sizeof(struct sway_transaction)); + transaction->instructions = create_list(); + transaction->damage = create_list(); + return transaction; +} + +static void transaction_destroy(struct sway_transaction *transaction) { + int i; + // Free instructions + for (i = 0; i < transaction->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + transaction->instructions->items[i]; + if (instruction->container->type == C_VIEW) { + struct sway_view *view = instruction->container->sway_view; + for (int j = 0; j < view->instructions->length; ++j) { + if (view->instructions->items[j] == instruction) { + list_del(view->instructions, j); + break; + } + } + } + free(instruction); + } + list_free(transaction->instructions); + + // Free damage + for (i = 0; i < transaction->damage->length; ++i) { + struct wlr_box *box = transaction->damage->items[i]; + free(box); + } + list_free(transaction->damage); + + free(transaction); +} + +void transaction_add_container(struct sway_transaction *transaction, + struct sway_container *container) { + struct sway_transaction_instruction *instruction = + calloc(1, sizeof(struct sway_transaction_instruction)); + instruction->transaction = transaction; + instruction->container = container; + memcpy(&instruction->state, &container->pending, + sizeof(struct sway_container_state)); + list_add(transaction->instructions, instruction); +} + +void transaction_add_damage(struct sway_transaction *transaction, + struct wlr_box *_box) { + struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); + memcpy(box, _box, sizeof(struct wlr_box)); + list_add(transaction->damage, box); +} + +static void save_view_texture(struct sway_view *view) { + wlr_texture_destroy(view->saved_texture); + view->saved_texture = NULL; + + // TODO: Copy the texture and store it in view->saved_texture. +} + +static void remove_saved_view_texture(struct sway_view *view) { + wlr_texture_destroy(view->saved_texture); + view->saved_texture = NULL; +} + +/** + * Apply a transaction to the "current" state of the tree. + * + * This is mostly copying stuff from the pending state into the main swayc + * properties, but also includes reparenting and deleting containers. + */ +static void transaction_apply(struct sway_transaction *transaction) { + int i; + for (i = 0; i < transaction->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + transaction->instructions->items[i]; + struct sway_container_state *state = &instruction->state; + struct sway_container *container = instruction->container; + + container->layout = state->layout; + container->x = state->swayc_x; + container->y = state->swayc_y; + container->width = state->swayc_width; + container->height = state->swayc_height; + + if (container->type == C_VIEW) { + struct sway_view *view = container->sway_view; + view->x = state->view_x; + view->y = state->view_y; + view->width = state->view_width; + view->height = state->view_height; + view->is_fullscreen = state->is_fullscreen; + view->border = state->border; + view->border_thickness = state->border_thickness; + view->border_top = state->border_top; + view->border_left = state->border_left; + view->border_right = state->border_right; + view->border_bottom = state->border_bottom; + + remove_saved_view_texture(view); + } + } + + // Damage + for (i = 0; i < transaction->damage->length; ++i) { + struct wlr_box *box = transaction->damage->items[i]; + for (int j = 0; j < root_container.children->length; ++j) { + struct sway_container *output = root_container.children->items[j]; + output_damage_box(output->sway_output, box); + } + } + + update_debug_tree(); +} + +static int handle_timeout(void *data) { + struct sway_transaction *transaction = data; + wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting), applying anyway", + transaction, transaction->num_waiting); + transaction_apply(transaction); + transaction_destroy(transaction); + return 0; +} + +void transaction_commit(struct sway_transaction *transaction) { + wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", + transaction, transaction->instructions->length); + transaction->num_waiting = 0; + for (int i = 0; i < transaction->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + transaction->instructions->items[i]; + if (instruction->container->type == C_VIEW) { + struct sway_view *view = instruction->container->sway_view; + instruction->serial = view_configure(view, + instruction->state.view_x, + instruction->state.view_y, + instruction->state.view_width, + instruction->state.view_height); + if (instruction->serial) { + save_view_texture(view); + list_add(view->instructions, instruction); + ++transaction->num_waiting; + } + } + } + if (!transaction->num_waiting) { + // This can happen if the transaction only contains xwayland views + wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying", + transaction); + transaction_apply(transaction); + transaction_destroy(transaction); + return; + } + + // Set up a timer which the views must respond within + transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_timeout, transaction); + wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); +} + +void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { + // Find the instruction + struct sway_transaction_instruction *instruction = NULL; + for (int i = 0; i < view->instructions->length; ++i) { + struct sway_transaction_instruction *tmp_instruction = + view->instructions->items[i]; + if (tmp_instruction->serial == serial) { + instruction = tmp_instruction; + list_del(view->instructions, i); + break; + } + } + if (!instruction) { + // This can happen if the view acknowledges the configure after the + // transaction has timed out and applied. + return; + } + // If all views are ready, apply the transaction + struct sway_transaction *transaction = instruction->transaction; + if (--transaction->num_waiting == 0) { + wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction); + wl_event_source_timer_update(transaction->timer, 0); + transaction_apply(transaction); + transaction_destroy(transaction); + } +} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index d2b8822c..f43a0a1b 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -5,6 +5,7 @@ #include #include #include "log.h" +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/server.h" @@ -87,18 +88,14 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p } } -static void configure(struct sway_view *view, double lx, double ly, int width, - int height) { +static uint32_t configure(struct sway_view *view, double lx, double ly, + int width, int height) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); if (xdg_shell_view == NULL) { - return; + return 0; } - - xdg_shell_view->pending_width = width; - xdg_shell_view->pending_height = height; - wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); - view_update_position(view, lx, ly); + return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); } static void set_activated(struct sway_view *view, bool activated) { @@ -174,18 +171,12 @@ 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); struct sway_view *view = &xdg_shell_view->view; - if (view->swayc && container_is_floating(view->swayc)) { - int width = view->wlr_xdg_surface->geometry.width; - int height = view->wlr_xdg_surface->geometry.height; - if (!width && !height) { - width = view->wlr_xdg_surface->surface->current->width; - height = view->wlr_xdg_surface->surface->current->height; - } - view_update_size(view, width, height); - } else { - view_update_size(view, xdg_shell_view->pending_width, - xdg_shell_view->pending_height); + struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; + + if (view->instructions->length) { + transaction_notify_view_ready(view, xdg_surface->configure_serial); } + view_update_title(view, false); view_damage_from(view); } diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 6ffe334a..bce59174 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -3,6 +3,7 @@ #include #include #include +#include "sway/desktop/transaction.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" #include "sway/server.h" @@ -86,18 +87,15 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p } } -static void configure(struct sway_view *view, double lx, double ly, int width, - int height) { +static uint32_t configure(struct sway_view *view, double lx, double ly, + int width, int height) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = xdg_shell_v6_view_from_view(view); if (xdg_shell_v6_view == NULL) { - return; + return 0; } - - xdg_shell_v6_view->pending_width = width; - xdg_shell_v6_view->pending_height = height; - wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height); - view_update_position(view, lx, ly); + return wlr_xdg_toplevel_v6_set_size( + view->wlr_xdg_surface_v6, width, height); } static void set_activated(struct sway_view *view, bool activated) { @@ -173,18 +171,12 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, commit); struct sway_view *view = &xdg_shell_v6_view->view; - if (view->swayc && container_is_floating(view->swayc)) { - int width = view->wlr_xdg_surface_v6->geometry.width; - int height = view->wlr_xdg_surface_v6->geometry.height; - if (!width && !height) { - width = view->wlr_xdg_surface_v6->surface->current->width; - height = view->wlr_xdg_surface_v6->surface->current->height; - } - view_update_size(view, width, height); - } else { - view_update_size(view, xdg_shell_v6_view->pending_width, - xdg_shell_v6_view->pending_height); + struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; + + if (view->instructions->length) { + transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); } + view_update_title(view, false); view_damage_from(view); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 6447b711..6a3c1b66 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -167,19 +167,18 @@ static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) { } } -static void configure(struct sway_view *view, double lx, double ly, int width, +static uint32_t configure(struct sway_view *view, double lx, double ly, int width, int height) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); if (xwayland_view == NULL) { - return; + return 0; } struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - xwayland_view->pending_lx = lx; - xwayland_view->pending_ly = ly; - xwayland_view->pending_width = width; - xwayland_view->pending_height = height; wlr_xwayland_surface_configure(xsurface, lx, ly, width, height); + + // xwayland doesn't give us a serial for the configure + return 0; } static void set_activated(struct sway_view *view, bool activated) { @@ -250,15 +249,21 @@ static void handle_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, commit); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (view->swayc && container_is_floating(view->swayc)) { - view_update_size(view, xsurface->width, xsurface->height); - } else { - view_update_size(view, xwayland_view->pending_width, - xwayland_view->pending_height); + + // Don't allow xwayland views to do resize or reposition themselves if + // they're involved in a transaction. Once the transaction has finished + // they'll apply the next time a commit happens. + if (view->instructions->length) { + if (view->swayc && container_is_floating(view->swayc)) { + view_update_size(view, xsurface->width, xsurface->height); + } else { + view_update_size(view, view->swayc->pending.swayc_width, + view->swayc->pending.swayc_height); + } + view_update_position(view, + view->swayc->pending.view_x, view->swayc->pending.view_y); + view_damage_from(view); } - view_update_position(view, - xwayland_view->pending_lx, xwayland_view->pending_ly); - view_damage_from(view); } static void handle_unmap(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From f9e6d703d298dbdee0770fd9e0c64ab2d7ac7deb Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 6 Jun 2018 19:19:30 +1000 Subject: Make main properties be the pending state --- sway/desktop/desktop.c | 3 ++- sway/desktop/output.c | 28 ++++++++++++++------------ sway/desktop/transaction.c | 49 +++++++++++++++++++++++++--------------------- sway/desktop/xwayland.c | 9 ++++----- 4 files changed, 48 insertions(+), 41 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index 66f33151..e495790c 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -7,7 +7,8 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *cont = root_container.children->items[i]; if (cont->type == C_OUTPUT) { - output_damage_surface(cont->sway_output, lx - cont->x, ly - cont->y, + output_damage_surface(cont->sway_output, + lx - cont->current.swayc_x, ly - cont->current.swayc_y, surface, whole); } } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c5d445a6..8af05bc3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -774,9 +774,10 @@ static void render_container_stacked(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int y = con->y + container_titlebar_height() * i; - render_titlebar(output, damage, child, child->x, y, child->width, - colors, title_texture, marks_texture); + int y = con->current.swayc_y + container_titlebar_height() * i; + render_titlebar(output, damage, child, child->current.swayc_x, y, + child->current.swayc_width, colors, + title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -795,7 +796,7 @@ static void render_container_stacked(struct sway_output *output, static void render_container(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused) { - switch (con->layout) { + switch (con->current.layout) { case L_NONE: case L_HORIZ: case L_VERT: @@ -832,9 +833,10 @@ static void render_floating_container(struct sway_output *soutput, marks_texture = view->marks_unfocused; } - if (con->sway_view->border == B_NORMAL) { - render_titlebar(soutput, damage, con, con->x, con->y, con->width, - colors, title_texture, marks_texture); + if (con->current.border == B_NORMAL) { + render_titlebar(soutput, damage, con, con->current.swayc_x, + con->current.swayc_y, con->current.swayc_width, colors, + title_texture, marks_texture); } else { render_top_border(soutput, damage, con, colors); } @@ -1184,8 +1186,8 @@ void output_damage_from_view(struct sway_output *output, void output_damage_box(struct sway_output *output, struct wlr_box *_box) { struct wlr_box box; memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->swayc->x; - box.y -= output->swayc->y; + box.x -= output->swayc->current.swayc_x; + box.y -= output->swayc->current.swayc_y; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); } @@ -1204,10 +1206,10 @@ static void output_damage_whole_container_iterator(struct sway_container *con, void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { struct wlr_box box = { - .x = con->x - output->wlr_output->lx, - .y = con->y - output->wlr_output->ly, - .width = con->width, - .height = con->height, + .x = con->current.swayc_x - output->wlr_output->lx, + .y = con->current.swayc_y - output->wlr_output->ly, + .width = con->current.swayc_width, + .height = con->current.swayc_height, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 69f97e3d..313e707b 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -67,8 +67,30 @@ void transaction_add_container(struct sway_transaction *transaction, calloc(1, sizeof(struct sway_transaction_instruction)); instruction->transaction = transaction; instruction->container = container; - memcpy(&instruction->state, &container->pending, - sizeof(struct sway_container_state)); + + // Copy the container's main (pending) properties into the instruction state + struct sway_container_state *state = &instruction->state; + state->layout = container->layout; + state->swayc_x = container->x; + state->swayc_y = container->y; + state->swayc_width = container->width; + state->swayc_height = container->height; + + if (container->type == C_VIEW) { + struct sway_view *view = container->sway_view; + state->view_x = view->x; + state->view_y = view->y; + state->view_width = view->width; + state->view_height = view->height; + state->is_fullscreen = view->is_fullscreen; + state->border = view->border; + state->border_thickness = view->border_thickness; + state->border_top = view->border_top; + state->border_left = view->border_left; + state->border_right = view->border_right; + state->border_bottom = view->border_bottom; + } + list_add(transaction->instructions, instruction); } @@ -102,30 +124,13 @@ static void transaction_apply(struct sway_transaction *transaction) { for (i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - struct sway_container_state *state = &instruction->state; struct sway_container *container = instruction->container; - container->layout = state->layout; - container->x = state->swayc_x; - container->y = state->swayc_y; - container->width = state->swayc_width; - container->height = state->swayc_height; + memcpy(&instruction->container->current, &instruction->state, + sizeof(struct sway_container_state)); if (container->type == C_VIEW) { - struct sway_view *view = container->sway_view; - view->x = state->view_x; - view->y = state->view_y; - view->width = state->view_width; - view->height = state->view_height; - view->is_fullscreen = state->is_fullscreen; - view->border = state->border; - view->border_thickness = state->border_thickness; - view->border_top = state->border_top; - view->border_left = state->border_left; - view->border_right = state->border_right; - view->border_bottom = state->border_bottom; - - remove_saved_view_texture(view); + remove_saved_view_texture(container->sway_view); } } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 6a3c1b66..d8442530 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -257,11 +257,9 @@ static void handle_commit(struct wl_listener *listener, void *data) { if (view->swayc && container_is_floating(view->swayc)) { view_update_size(view, xsurface->width, xsurface->height); } else { - view_update_size(view, view->swayc->pending.swayc_width, - view->swayc->pending.swayc_height); + view_update_size(view, view->swayc->width, view->swayc->height); } - view_update_position(view, - view->swayc->pending.view_x, view->swayc->pending.view_y); + view_update_position(view, view->x, view->y); view_damage_from(view); } } @@ -314,7 +312,8 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { return; } // TODO: Let floating views do whatever - configure(view, view->swayc->x, view->swayc->y, view->width, view->height); + configure(view, view->swayc->current.view_x, view->swayc->current.view_y, + view->swayc->current.view_width, view->swayc->current.view_height); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From bb66e6d578fdc68fb33d0fde921390d74f20bb31 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 6 Jun 2018 22:57:34 +1000 Subject: Refactor everything that needs to arrange windows * The arrange_foo functions are now replaced with arrange_and_commit, or with manually created transactions and arrange_windows x2. * The arrange functions are now only called from the highest level functions rather than from both high level and low level functions. * Due to the previous point, view_set_fullscreen_raw and view_set_fullscreen are both merged into one function again. * Floating and fullscreen are now working with transactions. --- sway/desktop/layer_shell.c | 2 +- sway/desktop/output.c | 8 ++++---- sway/desktop/transaction.c | 19 ++++++++++++++----- sway/desktop/xdg_shell.c | 18 ++++++++++++------ sway/desktop/xdg_shell_v6.c | 20 +++++++++++++------- sway/desktop/xwayland.c | 3 +++ 6 files changed, 47 insertions(+), 23 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 3accdefb..fe5fc316 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -176,7 +176,7 @@ void arrange_layers(struct sway_output *output) { sizeof(struct wlr_box)) != 0) { wlr_log(L_DEBUG, "Usable area changed, rearranging output"); memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); - arrange_output(output->swayc); + arrange_and_commit(output->swayc); } // Arrange non-exlusive surfaces from top->bottom diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8af05bc3..29efdd50 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1238,13 +1238,13 @@ static void handle_destroy(struct wl_listener *listener, void *data) { static void handle_mode(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, mode); arrange_layers(output); - arrange_output(output->swayc); + arrange_and_commit(output->swayc); } static void handle_transform(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, transform); arrange_layers(output); - arrange_output(output->swayc); + arrange_and_commit(output->swayc); } static void handle_scale_iterator(struct sway_container *view, void *data) { @@ -1254,8 +1254,8 @@ static void handle_scale_iterator(struct sway_container *view, void *data) { static void handle_scale(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, scale); arrange_layers(output); - arrange_output(output->swayc); container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); + arrange_and_commit(output->swayc); } void handle_new_output(struct wl_listener *listener, void *data) { @@ -1314,5 +1314,5 @@ void output_enable(struct sway_output *output) { output->damage_destroy.notify = damage_handle_destroy; arrange_layers(output); - arrange_root(); + arrange_and_commit(&root_container); } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 313e707b..ee9883e2 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -17,6 +17,13 @@ */ #define TIMEOUT_MS 200 +struct sway_transaction { + struct wl_event_source *timer; + list_t *instructions; // struct sway_transaction_instruction * + list_t *damage; // struct wlr_box * + size_t num_waiting; +}; + struct sway_transaction_instruction { struct sway_transaction *transaction; struct sway_container *container; @@ -162,16 +169,18 @@ void transaction_commit(struct sway_transaction *transaction) { for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - if (instruction->container->type == C_VIEW) { - struct sway_view *view = instruction->container->sway_view; - instruction->serial = view_configure(view, + struct sway_container *con = instruction->container; + if (con->type == C_VIEW && + (con->current.view_width != instruction->state.view_width || + con->current.view_height != instruction->state.view_height)) { + instruction->serial = view_configure(con->sway_view, instruction->state.view_x, instruction->state.view_y, instruction->state.view_width, instruction->state.view_height); if (instruction->serial) { - save_view_texture(view); - list_add(view->instructions, instruction); + save_view_texture(con->sway_view); + list_add(con->sway_view->instructions, instruction); ++transaction->num_waiting; } } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index f43a0a1b..d22c967c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -5,10 +5,10 @@ #include #include #include "log.h" -#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" @@ -210,8 +210,14 @@ static void handle_map(struct wl_listener *listener, void *data) { view->natural_width = view->wlr_xdg_surface->surface->current->width; view->natural_height = view->wlr_xdg_surface->surface->current->height; } + view_map(view, view->wlr_xdg_surface->surface); + if (xdg_surface->toplevel->client_pending.fullscreen) { + view_set_fullscreen(view, true); + } + arrange_and_commit(view->swayc->parent); + xdg_shell_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, &xdg_shell_view->commit); @@ -219,10 +225,6 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_view->new_popup.notify = handle_new_popup; wl_signal_add(&xdg_surface->events.new_popup, &xdg_shell_view->new_popup); - - if (xdg_surface->toplevel->client_pending.fullscreen) { - view_set_fullscreen(view, true); - } } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -237,6 +239,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) struct wlr_xdg_toplevel_set_fullscreen_event *e = data; struct wlr_xdg_surface *xdg_surface = xdg_shell_view->view.wlr_xdg_surface; + struct sway_view *view = &xdg_shell_view->view; if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, "xdg_shell requested fullscreen of surface with role %i", @@ -247,7 +250,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } - view_set_fullscreen(&xdg_shell_view->view, e->fullscreen); + view_set_fullscreen(view, e->fullscreen); + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); } void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index bce59174..7ec9e6cb 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -3,10 +3,10 @@ #include #include #include -#include "sway/desktop/transaction.h" +#include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" -#include "sway/server.h" #include "sway/tree/view.h" #include "sway/input/seat.h" #include "sway/input/input-manager.h" @@ -210,8 +210,14 @@ static void handle_map(struct wl_listener *listener, void *data) { view->natural_width = view->wlr_xdg_surface_v6->surface->current->width; view->natural_height = view->wlr_xdg_surface_v6->surface->current->height; } + view_map(view, view->wlr_xdg_surface_v6->surface); + if (xdg_surface->toplevel->client_pending.fullscreen) { + view_set_fullscreen(view, true); + } + arrange_and_commit(view->swayc->parent); + xdg_shell_v6_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, &xdg_shell_v6_view->commit); @@ -219,10 +225,6 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_v6_view->new_popup.notify = handle_new_popup; wl_signal_add(&xdg_surface->events.new_popup, &xdg_shell_v6_view->new_popup); - - if (xdg_surface->toplevel->client_pending.fullscreen) { - view_set_fullscreen(view, true); - } } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -237,6 +239,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data; struct wlr_xdg_surface_v6 *xdg_surface = xdg_shell_v6_view->view.wlr_xdg_surface_v6; + struct sway_view *view = &xdg_shell_v6_view->view; if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL, "xdg_shell_v6 requested fullscreen of surface with role %i", @@ -247,7 +250,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } - view_set_fullscreen(&xdg_shell_v6_view->view, e->fullscreen); + view_set_fullscreen(view, e->fullscreen); + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); } void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index d8442530..70929d48 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -11,6 +11,7 @@ #include "sway/input/seat.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" #include "sway/tree/view.h" @@ -292,6 +293,7 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xsurface->fullscreen) { view_set_fullscreen(view, true); } + arrange_and_commit(view->swayc); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -325,6 +327,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } view_set_fullscreen(view, xsurface->fullscreen); + arrange_and_commit(view->swayc); } static void handle_set_title(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From 1c89f32533534f6e78c81c95578f40df45bb9016 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 18 Jun 2018 20:42:12 +1000 Subject: Preserve buffers during transactions * Also fix parts of the rendering where it was rendering the pending state instead of current. --- sway/desktop/output.c | 101 +++++++++++++++++++++++---------------------- sway/desktop/transaction.c | 28 ++++++++----- sway/desktop/xwayland.c | 2 +- 3 files changed, 71 insertions(+), 60 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 37528cac..a485cb10 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -112,8 +113,8 @@ static bool get_surface_box(struct root_geometry *geo, static bool get_view_box(struct root_geometry *geo, struct sway_output *output, struct sway_view *view, int sx, int sy, struct wlr_box *surface_box) { - int sw = view->width; - int sh = view->height; + int sw = view->saved_surface_width; + int sh = view->saved_surface_height; double _sx = sx, _sy = sy; rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, @@ -157,10 +158,10 @@ static void output_view_for_each_surface(struct sway_view *view, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct render_data *data = user_data; - geo->x = view->x - data->output->swayc->x; - geo->y = view->y - data->output->swayc->y; - geo->width = view->surface->current->width; - geo->height = view->surface->current->height; + geo->x = view->swayc->current.view_x - data->output->swayc->x; + geo->y = view->swayc->current.view_y - data->output->swayc->y; + geo->width = view->swayc->current.view_width; + geo->height = view->swayc->current.view_height; geo->rotation = 0; // TODO view_for_each_surface(view, iterator, user_data); @@ -277,11 +278,11 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, struct wlr_box box; bool intersects; - // If this is the main surface of a view, render the saved_texture instead + // If this is the main surface of a view, render the saved_buffer instead // if it exists. It exists when we are mid-transaction. - if (data->view && data->view->saved_texture && + if (data->view && data->view->saved_buffer && data->view->surface == surface) { - texture = data->view->saved_texture; + texture = data->view->saved_buffer->texture; intersects = get_view_box(&data->root_geo, data->output, data->view, sx, sy, &box); } else { @@ -405,46 +406,46 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, float output_scale = output->wlr_output->scale; float color[4]; - if (view->border != B_NONE) { - if (view->border_left) { + if (con->current.border != B_NONE) { + if (con->current.border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = view->y; - box.width = view->border_thickness; - box.height = view->height; + box.x = con->current.swayc_x; + box.y = con->current.view_y; + box.width = con->current.border_thickness; + box.height = con->current.view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (view->border_right) { + if (con->current.border_right) { if (con->parent->children->length == 1 - && con->parent->layout == L_HORIZ) { + && con->parent->current.layout == L_HORIZ) { memcpy(&color, colors->indicator, sizeof(float) * 4); } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } premultiply_alpha(color, con->alpha); - box.x = view->x + view->width; - box.y = view->y; - box.width = view->border_thickness; - box.height = view->height; + box.x = con->current.view_x + con->current.view_width; + box.y = con->current.view_y; + box.width = con->current.border_thickness; + box.height = con->current.view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (view->border_bottom) { + if (con->current.border_bottom) { if (con->parent->children->length == 1 - && con->parent->layout == L_VERT) { + && con->parent->current.layout == L_VERT) { memcpy(&color, colors->indicator, sizeof(float) * 4); } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = view->y + view->height; - box.width = con->width; - box.height = view->border_thickness; + box.x = con->current.swayc_x; + box.y = con->current.view_y + con->current.view_height; + box.width = con->current.swayc_width; + box.height = con->current.border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } @@ -468,9 +469,8 @@ static void render_titlebar(struct sway_output *output, struct wlr_texture *marks_texture) { struct wlr_box box; float color[4]; - struct sway_view *view = con->type == C_VIEW ? con->sway_view : NULL; float output_scale = output->wlr_output->scale; - enum sway_container_layout layout = con->parent->layout; + enum sway_container_layout layout = con->parent->current.layout; bool is_last_child = con->parent->children->items[con->parent->children->length - 1] == con; @@ -489,9 +489,11 @@ static void render_titlebar(struct sway_output *output, bool connects_sides = false; if (layout == L_HORIZ || layout == L_VERT || (layout == L_STACKED && is_last_child)) { - if (view) { - left_offset = view->border_left * view->border_thickness; - right_offset = view->border_right * view->border_thickness; + if (con->type == C_VIEW) { + left_offset = + con->current.border_left * con->current.border_thickness; + right_offset = + con->current.border_right * con->current.border_thickness; connects_sides = true; } } @@ -612,15 +614,16 @@ static void render_titlebar(struct sway_output *output, // Left pixel in line with bottom bar box.x = x; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = view->border_thickness * view->border_left; + box.width = con->current.border_thickness * con->current.border_left; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Right pixel in line with bottom bar - box.x = x + width - view->border_thickness * view->border_right; + box.x = x + width - + con->current.border_thickness * con->current.border_right; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = view->border_thickness * view->border_right; + box.width = con->current.border_thickness * con->current.border_right; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); @@ -633,8 +636,7 @@ static void render_titlebar(struct sway_output *output, static void render_top_border(struct sway_output *output, pixman_region32_t *output_damage, struct sway_container *con, struct border_colors *colors) { - struct sway_view *view = con->sway_view; - if (!view->border_top) { + if (!con->current.border_top) { return; } struct wlr_box box; @@ -644,10 +646,10 @@ static void render_top_border(struct sway_output *output, // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y; - box.width = con->width; - box.height = view->border_thickness; + box.x = con->current.swayc_x; + box.y = con->current.swayc_y; + box.width = con->current.swayc_width; + box.height = con->current.border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); } @@ -688,9 +690,10 @@ static void render_container_simple(struct sway_output *output, marks_texture = child->sway_view->marks_unfocused; } - if (child->sway_view->border == B_NORMAL) { - render_titlebar(output, damage, child, child->x, child->y, - child->width, colors, title_texture, marks_texture); + if (child->current.border == B_NORMAL) { + render_titlebar(output, damage, child, child->current.swayc_x, + child->current.swayc_y, child->current.swayc_width, + colors, title_texture, marks_texture); } else { render_top_border(output, damage, child, colors); } @@ -739,15 +742,15 @@ static void render_container_tabbed(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int tab_width = con->width / con->children->length; - int x = con->x + tab_width * i; + int tab_width = con->current.swayc_width / con->children->length; + int x = con->current.swayc_x + tab_width * i; // Make last tab use the remaining width of the parent if (i == con->children->length - 1) { - tab_width = con->width - tab_width * i; + tab_width = con->current.swayc_width - tab_width * i; } - render_titlebar(output, damage, child, x, child->y, tab_width, colors, - title_texture, marks_texture); + render_titlebar(output, damage, child, x, child->current.swayc_y, + tab_width, colors, title_texture, marks_texture); if (child == current) { current_colors = colors; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 07bfbf7a..77377a18 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "sway/debug.h" #include "sway/desktop/transaction.h" @@ -112,16 +113,23 @@ void transaction_add_damage(struct sway_transaction *transaction, list_add(transaction->damage, box); } -static void save_view_texture(struct sway_view *view) { - wlr_texture_destroy(view->saved_texture); - view->saved_texture = NULL; - - // TODO: Copy the texture and store it in view->saved_texture. +static void save_view_buffer(struct sway_view *view) { + if (view->saved_buffer) { + wlr_buffer_unref(view->saved_buffer); + } + wlr_buffer_ref(view->surface->buffer); + view->saved_buffer = view->surface->buffer; + view->saved_surface_width = view->surface->current->width; + view->saved_surface_height = view->surface->current->height; } -static void remove_saved_view_texture(struct sway_view *view) { - wlr_texture_destroy(view->saved_texture); - view->saved_texture = NULL; +static void remove_saved_view_buffer(struct sway_view *view) { + if (view->saved_buffer) { + wlr_buffer_unref(view->saved_buffer); + view->saved_buffer = NULL; + view->saved_surface_width = 0; + view->saved_surface_height = 0; + } } /** @@ -141,7 +149,7 @@ static void transaction_apply(struct sway_transaction *transaction) { sizeof(struct sway_container_state)); if (container->type == C_VIEW) { - remove_saved_view_texture(container->sway_view); + remove_saved_view_buffer(container->sway_view); } } @@ -183,7 +191,7 @@ void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_width, instruction->state.view_height); if (instruction->serial) { - save_view_texture(con->sway_view); + save_view_buffer(con->sway_view); list_add(con->sway_view->instructions, instruction); ++transaction->num_waiting; } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 70929d48..55917bf6 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -261,8 +261,8 @@ static void handle_commit(struct wl_listener *listener, void *data) { view_update_size(view, view->swayc->width, view->swayc->height); } view_update_position(view, view->x, view->y); - view_damage_from(view); } + view_damage_from(view); } static void handle_unmap(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From 38398e2d77d57dc06b67ec88a54091c897915602 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 23 Jun 2018 16:24:11 +1000 Subject: Implement atomic layout updates for tree operations This implements atomic layout updates for when views map, reparent or unmap. --- sway/desktop/output.c | 293 ++++++++++++++++++++++---------------------- sway/desktop/transaction.c | 176 ++++++++++++++++---------- sway/desktop/xdg_shell.c | 35 ++++-- sway/desktop/xdg_shell_v6.c | 30 +++-- sway/desktop/xwayland.c | 45 +++++-- 5 files changed, 339 insertions(+), 240 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a485cb10..9db95ef5 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -102,40 +102,8 @@ static bool get_surface_box(struct root_geometry *geo, wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); struct wlr_box output_box = { - .width = output->swayc->width, - .height = output->swayc->height, - }; - - struct wlr_box intersection; - return wlr_box_intersection(&output_box, &rotated_box, &intersection); -} - -static bool get_view_box(struct root_geometry *geo, - struct sway_output *output, struct sway_view *view, int sx, int sy, - struct wlr_box *surface_box) { - int sw = view->saved_surface_width; - int sh = view->saved_surface_height; - - double _sx = sx, _sy = sy; - rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, - geo->rotation); - - struct wlr_box box = { - .x = geo->x + _sx, - .y = geo->y + _sy, - .width = sw, - .height = sh, - }; - if (surface_box != NULL) { - memcpy(surface_box, &box, sizeof(struct wlr_box)); - } - - struct wlr_box rotated_box; - wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); - - struct wlr_box output_box = { - .width = output->swayc->width, - .height = output->swayc->height, + .width = output->swayc->current.swayc_width, + .height = output->swayc->current.swayc_height, }; struct wlr_box intersection; @@ -158,8 +126,8 @@ static void output_view_for_each_surface(struct sway_view *view, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { struct render_data *data = user_data; - geo->x = view->swayc->current.view_x - data->output->swayc->x; - geo->y = view->swayc->current.view_y - data->output->swayc->y; + geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x; + geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y; geo->width = view->swayc->current.view_width; geo->height = view->swayc->current.view_height; geo->rotation = 0; // TODO @@ -187,8 +155,8 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged, wl_list_for_each(unmanaged_surface, unmanaged, link) { struct wlr_xwayland_surface *xsurface = unmanaged_surface->wlr_xwayland_surface; - double ox = unmanaged_surface->lx - output->swayc->x; - double oy = unmanaged_surface->ly - output->swayc->y; + double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; + double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; surface_for_each_surface(xsurface->surface, ox, oy, geo, iterator, user_data); @@ -274,26 +242,14 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, pixman_region32_t *output_damage = data->damage; float alpha = data->alpha; - struct wlr_texture *texture = NULL; - struct wlr_box box; - bool intersects; - - // If this is the main surface of a view, render the saved_buffer instead - // if it exists. It exists when we are mid-transaction. - if (data->view && data->view->saved_buffer && - data->view->surface == surface) { - texture = data->view->saved_buffer->texture; - intersects = get_view_box(&data->root_geo, data->output, data->view, - sx, sy, &box); - } else { - texture = wlr_surface_get_texture(surface); - if (texture == NULL) { - return; - } - intersects = get_surface_box(&data->root_geo, data->output, surface, - sx, sy, &box); + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (!texture) { + return; } + struct wlr_box box; + bool intersects = get_surface_box(&data->root_geo, data->output, surface, + sx, sy, &box); if (!intersects) { return; } @@ -394,58 +350,98 @@ static void render_view_surfaces(struct sway_view *view, view, &data.root_geo, 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; + + struct wlr_texture *texture = transaction_get_texture(view); + if (!texture) { + return; + } + struct wlr_box box = { + .x = view->swayc->current.view_x - output->swayc->current.swayc_x, + .y = view->swayc->current.view_y - output->swayc->current.swayc_y, + .width = view->swayc->current.view_width, + .height = view->swayc->current.view_height, + }; + + struct wlr_box output_box = { + .width = output->swayc->current.swayc_width, + .height = output->swayc->current.swayc_height, + }; + + struct wlr_box intersection; + bool intersects = wlr_box_intersection(&output_box, &box, &intersection); + if (!intersects) { + return; + } + + 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, texture, &box, matrix, alpha); +} + /** * 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->sway_view; - render_view_surfaces(view, output, damage, view->swayc->alpha); + if (view->swayc->instructions->length) { + render_saved_view(view, output, damage, view->swayc->alpha); + } else { + render_view_surfaces(view, output, damage, view->swayc->alpha); + } struct wlr_box box; float output_scale = output->wlr_output->scale; float color[4]; + struct sway_container_state *state = &con->current; - if (con->current.border != B_NONE) { - if (con->current.border_left) { + if (state->border != B_NONE) { + if (state->border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->current.swayc_x; - box.y = con->current.view_y; - box.width = con->current.border_thickness; - box.height = con->current.view_height; + box.x = state->swayc_x; + box.y = state->view_y; + box.width = state->border_thickness; + box.height = state->view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (con->current.border_right) { - if (con->parent->children->length == 1 - && con->parent->current.layout == L_HORIZ) { + if (state->border_right) { + if (state->parent->current.children->length == 1 + && state->parent->current.layout == L_HORIZ) { memcpy(&color, colors->indicator, sizeof(float) * 4); } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } premultiply_alpha(color, con->alpha); - box.x = con->current.view_x + con->current.view_width; - box.y = con->current.view_y; - box.width = con->current.border_thickness; - box.height = con->current.view_height; + box.x = state->view_x + state->view_width; + box.y = state->view_y; + box.width = state->border_thickness; + box.height = state->view_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } - if (con->current.border_bottom) { - if (con->parent->children->length == 1 - && con->parent->current.layout == L_VERT) { + if (state->border_bottom) { + if (state->parent->current.children->length == 1 + && con->current.parent->current.layout == L_VERT) { memcpy(&color, colors->indicator, sizeof(float) * 4); } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } premultiply_alpha(color, con->alpha); - box.x = con->current.swayc_x; - box.y = con->current.view_y + con->current.view_height; - box.width = con->current.swayc_width; - box.height = con->current.border_thickness; + box.x = state->swayc_x; + box.y = state->view_y + state->view_height; + box.width = state->swayc_width; + box.height = state->border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); } @@ -469,10 +465,13 @@ static void render_titlebar(struct sway_output *output, struct wlr_texture *marks_texture) { struct wlr_box box; float color[4]; + struct sway_container_state *state = &con->current; float output_scale = output->wlr_output->scale; - enum sway_container_layout layout = con->parent->current.layout; - bool is_last_child = - con->parent->children->items[con->parent->children->length - 1] == con; + enum sway_container_layout layout = state->parent->current.layout; + list_t *children = state->parent->current.children; + bool is_last_child = children->items[children->length - 1] == con; + double output_x = output->swayc->current.swayc_x; + double output_y = output->swayc->current.swayc_y; // Single pixel bar above title memcpy(&color, colors->border, sizeof(float) * 4); @@ -490,10 +489,8 @@ static void render_titlebar(struct sway_output *output, if (layout == L_HORIZ || layout == L_VERT || (layout == L_STACKED && is_last_child)) { if (con->type == C_VIEW) { - left_offset = - con->current.border_left * con->current.border_thickness; - right_offset = - con->current.border_right * con->current.border_thickness; + left_offset = state->border_left * state->border_thickness; + right_offset = state->border_right * state->border_thickness; connects_sides = true; } } @@ -527,10 +524,9 @@ static void render_titlebar(struct sway_output *output, struct wlr_box texture_box; wlr_texture_get_size(marks_texture, &texture_box.width, &texture_box.height); - texture_box.x = (x - output->swayc->x + width - TITLEBAR_H_PADDING) + texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING) * output_scale - texture_box.width; - texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING) - * output_scale; + texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; float matrix[9]; wlr_matrix_project_box(matrix, &texture_box, @@ -551,10 +547,8 @@ static void render_titlebar(struct sway_output *output, struct wlr_box texture_box; wlr_texture_get_size(title_texture, &texture_box.width, &texture_box.height); - texture_box.x = (x - output->swayc->x + TITLEBAR_H_PADDING) - * output_scale; - texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING) - * output_scale; + texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale; + texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale; float matrix[9]; wlr_matrix_project_box(matrix, &texture_box, @@ -614,16 +608,15 @@ static void render_titlebar(struct sway_output *output, // Left pixel in line with bottom bar box.x = x; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = con->current.border_thickness * con->current.border_left; + box.width = state->border_thickness * state->border_left; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Right pixel in line with bottom bar - box.x = x + width - - con->current.border_thickness * con->current.border_right; + box.x = x + width - state->border_thickness * state->border_right; box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; - box.width = con->current.border_thickness * con->current.border_right; + box.width = state->border_thickness * state->border_right; box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); @@ -636,7 +629,8 @@ static void render_titlebar(struct sway_output *output, static void render_top_border(struct sway_output *output, pixman_region32_t *output_damage, struct sway_container *con, struct border_colors *colors) { - if (!con->current.border_top) { + struct sway_container_state *state = &con->current; + if (!state->border_top) { return; } struct wlr_box box; @@ -646,10 +640,10 @@ static void render_top_border(struct sway_output *output, // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->current.swayc_x; - box.y = con->current.swayc_y; - box.width = con->current.swayc_width; - box.height = con->current.border_thickness; + box.x = state->swayc_x; + box.y = state->swayc_y; + box.width = state->swayc_width; + box.height = state->border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); } @@ -669,31 +663,34 @@ static void render_container_simple(struct sway_output *output, struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); - for (int i = 0; i < con->children->length; ++i) { - struct sway_container *child = con->children->items[i]; + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; if (child->type == C_VIEW) { + struct sway_view *view = child->sway_view; struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; + struct sway_container_state *state = &child->current; + if (focus == child || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; - marks_texture = child->sway_view->marks_focused; + marks_texture = view->marks_focused; } else if (seat_get_focus_inactive(seat, con) == child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; - marks_texture = child->sway_view->marks_focused_inactive; + marks_texture = view->marks_focused_inactive; } else { colors = &config->border_colors.unfocused; title_texture = child->title_unfocused; - marks_texture = child->sway_view->marks_unfocused; + marks_texture = view->marks_unfocused; } - if (child->current.border == B_NORMAL) { - render_titlebar(output, damage, child, child->current.swayc_x, - child->current.swayc_y, child->current.swayc_width, - colors, title_texture, marks_texture); + if (state->border == B_NORMAL) { + render_titlebar(output, damage, child, state->swayc_x, + state->swayc_y, state->swayc_width, colors, + title_texture, marks_texture); } else { render_top_border(output, damage, child, colors); } @@ -711,22 +708,23 @@ static void render_container_simple(struct sway_output *output, static void render_container_tabbed(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused) { - if (!con->children->length) { + if (!con->current.children->length) { return; } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); struct sway_container *current = seat_get_active_child(seat, con); struct border_colors *current_colors = NULL; + struct sway_container_state *pstate = &con->current; // Render tabs - for (int i = 0; i < con->children->length; ++i) { - struct sway_container *child = con->children->items[i]; + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; + struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + struct sway_container_state *cstate = &child->current; struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - struct sway_view *view = - child->type == C_VIEW ? child->sway_view : NULL; if (focus == child || parent_focused) { colors = &config->border_colors.focused; @@ -735,22 +733,22 @@ static void render_container_tabbed(struct sway_output *output, } else if (child == current) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; - marks_texture = view ? view->marks_focused : NULL; + marks_texture = view ? view->marks_focused_inactive : NULL; } else { colors = &config->border_colors.unfocused; title_texture = child->title_unfocused; marks_texture = view ? view->marks_unfocused : NULL; } - int tab_width = con->current.swayc_width / con->children->length; - int x = con->current.swayc_x + tab_width * i; + int tab_width = pstate->swayc_width / pstate->children->length; + int x = pstate->swayc_x + tab_width * i; // Make last tab use the remaining width of the parent - if (i == con->children->length - 1) { - tab_width = con->current.swayc_width - tab_width * i; + if (i == pstate->children->length - 1) { + tab_width = pstate->swayc_width - tab_width * i; } - render_titlebar(output, damage, child, x, child->current.swayc_y, - tab_width, colors, title_texture, marks_texture); + render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width, + colors, title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -772,22 +770,23 @@ static void render_container_tabbed(struct sway_output *output, static void render_container_stacked(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused) { - if (!con->children->length) { + if (!con->current.children->length) { return; } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); struct sway_container *current = seat_get_active_child(seat, con); struct border_colors *current_colors = NULL; + struct sway_container_state *pstate = &con->current; // Render titles - for (int i = 0; i < con->children->length; ++i) { - struct sway_container *child = con->children->items[i]; + for (int i = 0; i < con->current.children->length; ++i) { + struct sway_container *child = con->current.children->items[i]; + struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + struct sway_container_state *cstate = &child->current; struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - struct sway_view *view = - child->type == C_VIEW ? child->sway_view : NULL; if (focus == child || parent_focused) { colors = &config->border_colors.focused; @@ -803,10 +802,9 @@ static void render_container_stacked(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int y = con->current.swayc_y + container_titlebar_height() * i; - render_titlebar(output, damage, child, child->current.swayc_x, y, - child->current.swayc_width, colors, - title_texture, marks_texture); + int y = pstate->swayc_y + container_titlebar_height() * i; + render_titlebar(output, damage, child, cstate->swayc_x, y, + cstate->swayc_width, colors, title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -877,17 +875,18 @@ static void render_floating_container(struct sway_output *soutput, static void render_floating(struct sway_output *soutput, pixman_region32_t *damage) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; - for (int j = 0; j < output->children->length; ++j) { - struct sway_container *workspace = output->children->items[j]; - struct sway_workspace *ws = workspace->sway_workspace; - if (!workspace_is_visible(workspace)) { + for (int i = 0; i < root_container.current.children->length; ++i) { + struct sway_container *output = + root_container.current.children->items[i]; + for (int j = 0; j < output->current.children->length; ++j) { + struct sway_container *ws = output->current.children->items[j]; + if (!workspace_is_visible(ws)) { continue; } - for (int k = 0; k < ws->floating->children->length; ++k) { - struct sway_container *floater = - ws->floating->children->items[k]; + list_t *floating = + ws->current.ws_floating->current.children; + for (int k = 0; k < floating->length; ++k) { + struct sway_container *floater = floating->items[k]; render_floating_container(soutput, damage, floater); } } @@ -901,7 +900,7 @@ static struct sway_container *output_get_active_workspace( seat_get_focus_inactive(seat, output->swayc); if (!focus) { // We've never been to this output before - focus = output->swayc->children->items[0]; + focus = output->swayc->current.children->items[0]; } struct sway_container *workspace = focus; if (workspace->type != C_WORKSPACE) { @@ -942,8 +941,9 @@ static void render_output(struct sway_output *output, struct timespec *when, } struct sway_container *workspace = output_get_active_workspace(output); + struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; - if (workspace->sway_workspace->fullscreen) { + if (fullscreen_view) { float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; int nrects; @@ -954,10 +954,9 @@ static void render_output(struct sway_output *output, struct timespec *when, } // TODO: handle views smaller than the output - render_view_surfaces( - workspace->sway_workspace->fullscreen, output, damage, 1.0f); + render_view_surfaces(fullscreen_view, output, damage, 1.0f); - if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { + if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { render_unmanaged(output, damage, &root_container.sway_root->xwayland_unmanaged); } @@ -1073,11 +1072,11 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { }; struct sway_container *workspace = output_get_active_workspace(output); - if (workspace->sway_workspace->fullscreen) { + if (workspace->current.ws_fullscreen) { send_frame_done_container_iterator( - workspace->sway_workspace->fullscreen->swayc, &data); + workspace->current.ws_fullscreen->swayc, &data); - if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { + if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) { send_frame_done_unmanaged(&data, &root_container.sway_root->xwayland_unmanaged); } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 77377a18..6e09537a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -9,6 +9,7 @@ #include "sway/output.h" #include "sway/tree/container.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" #include "list.h" #include "log.h" @@ -18,6 +19,13 @@ */ #define TIMEOUT_MS 200 +/** + * If enabled, sway will always wait for the transaction timeout before + * applying it, rather than applying it when the views are ready. This allows us + * to observe the rendered state while a transaction is in progress. + */ +#define TRANSACTION_DEBUG false + struct sway_transaction { struct wl_event_source *timer; list_t *instructions; // struct sway_transaction_instruction * @@ -29,7 +37,9 @@ struct sway_transaction_instruction { struct sway_transaction *transaction; struct sway_container *container; struct sway_container_state state; + struct wlr_buffer *saved_buffer; uint32_t serial; + bool ready; }; struct sway_transaction *transaction_create() { @@ -40,44 +50,55 @@ struct sway_transaction *transaction_create() { return transaction; } +static void remove_saved_view_buffer( + struct sway_transaction_instruction *instruction) { + if (instruction->saved_buffer) { + wlr_buffer_unref(instruction->saved_buffer); + instruction->saved_buffer = NULL; + } +} + +static void save_view_buffer(struct sway_view *view, + struct sway_transaction_instruction *instruction) { + if (!sway_assert(instruction->saved_buffer == NULL, + "Didn't expect instruction to have a saved buffer already")) { + remove_saved_view_buffer(instruction); + } + if (view->surface && wlr_surface_has_buffer(view->surface)) { + wlr_buffer_ref(view->surface->buffer); + instruction->saved_buffer = view->surface->buffer; + } +} + static void transaction_destroy(struct sway_transaction *transaction) { - int i; // Free instructions - for (i = 0; i < transaction->instructions->length; ++i) { + for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - if (instruction->container->type == C_VIEW) { - struct sway_view *view = instruction->container->sway_view; - for (int j = 0; j < view->instructions->length; ++j) { - if (view->instructions->items[j] == instruction) { - list_del(view->instructions, j); - break; - } + struct sway_container *con = instruction->container; + for (int j = 0; j < con->instructions->length; ++j) { + if (con->instructions->items[j] == instruction) { + list_del(con->instructions, j); + break; } } + if (con->destroying && !con->instructions->length) { + container_free(con); + } + remove_saved_view_buffer(instruction); free(instruction); } list_free(transaction->instructions); // Free damage - for (i = 0; i < transaction->damage->length; ++i) { - struct wlr_box *box = transaction->damage->items[i]; - free(box); - } + list_foreach(transaction->damage, free); list_free(transaction->damage); free(transaction); } -void transaction_add_container(struct sway_transaction *transaction, - struct sway_container *container) { - struct sway_transaction_instruction *instruction = - calloc(1, sizeof(struct sway_transaction_instruction)); - instruction->transaction = transaction; - instruction->container = container; - - // Copy the container's main (pending) properties into the instruction state - struct sway_container_state *state = &instruction->state; +static void copy_pending_state(struct sway_container *container, + struct sway_container_state *state) { state->layout = container->layout; state->swayc_x = container->x; state->swayc_y = container->y; @@ -87,6 +108,7 @@ void transaction_add_container(struct sway_transaction *transaction, state->current_gaps = container->current_gaps; state->gaps_inner = container->gaps_inner; state->gaps_outer = container->gaps_outer; + state->parent = container->parent; if (container->type == C_VIEW) { struct sway_view *view = container->sway_view; @@ -101,8 +123,44 @@ void transaction_add_container(struct sway_transaction *transaction, state->border_left = view->border_left; state->border_right = view->border_right; state->border_bottom = view->border_bottom; + } else if (container->type == C_WORKSPACE) { + state->ws_fullscreen = container->sway_workspace->fullscreen; + state->ws_floating = container->sway_workspace->floating; + state->children = create_list(); + list_cat(state->children, container->children); + } else { + state->children = create_list(); + list_cat(state->children, container->children); + } +} + +static bool transaction_has_container(struct sway_transaction *transaction, + struct sway_container *container) { + for (int i = 0; i < transaction->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + transaction->instructions->items[i]; + if (instruction->container == container) { + return true; + } + } + return false; +} + +void transaction_add_container(struct sway_transaction *transaction, + struct sway_container *container) { + if (transaction_has_container(transaction, container)) { + return; } + struct sway_transaction_instruction *instruction = + calloc(1, sizeof(struct sway_transaction_instruction)); + instruction->transaction = transaction; + instruction->container = container; + + copy_pending_state(container, &instruction->state); + if (container->type == C_VIEW) { + save_view_buffer(container->sway_view, instruction); + } list_add(transaction->instructions, instruction); } @@ -113,47 +171,29 @@ void transaction_add_damage(struct sway_transaction *transaction, list_add(transaction->damage, box); } -static void save_view_buffer(struct sway_view *view) { - if (view->saved_buffer) { - wlr_buffer_unref(view->saved_buffer); - } - wlr_buffer_ref(view->surface->buffer); - view->saved_buffer = view->surface->buffer; - view->saved_surface_width = view->surface->current->width; - view->saved_surface_height = view->surface->current->height; -} - -static void remove_saved_view_buffer(struct sway_view *view) { - if (view->saved_buffer) { - wlr_buffer_unref(view->saved_buffer); - view->saved_buffer = NULL; - view->saved_surface_width = 0; - view->saved_surface_height = 0; - } -} - /** * Apply a transaction to the "current" state of the tree. - * - * This is mostly copying stuff from the pending state into the main swayc - * properties, but also includes reparenting and deleting containers. */ static void transaction_apply(struct sway_transaction *transaction) { int i; + // Apply the instruction state to the container's current state for (i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *container = instruction->container; - memcpy(&instruction->container->current, &instruction->state, - sizeof(struct sway_container_state)); + // There are separate children lists for each instruction state, the + // container's current state and the container's pending state + // (ie. con->children). The list itself needs to be freed here. + // Any child containers which are being deleted will be cleaned up in + // transaction_destroy(). + list_free(container->current.children); - if (container->type == C_VIEW) { - remove_saved_view_buffer(container->sway_view); - } + memcpy(&container->current, &instruction->state, + sizeof(struct sway_container_state)); } - // Damage + // Apply damage for (i = 0; i < transaction->damage->length; ++i) { struct wlr_box *box = transaction->damage->items[i]; for (int j = 0; j < root_container.children->length; ++j) { @@ -161,8 +201,6 @@ static void transaction_apply(struct sway_transaction *transaction) { output_damage_box(output->sway_output, box); } } - - update_debug_tree(); } static int handle_timeout(void *data) { @@ -182,7 +220,7 @@ void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *con = instruction->container; - if (con->type == C_VIEW && + if (con->type == C_VIEW && !con->destroying && (con->current.view_width != instruction->state.view_width || con->current.view_height != instruction->state.view_height)) { instruction->serial = view_configure(con->sway_view, @@ -191,14 +229,12 @@ void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_width, instruction->state.view_height); if (instruction->serial) { - save_view_buffer(con->sway_view); - list_add(con->sway_view->instructions, instruction); ++transaction->num_waiting; } } + list_add(con->instructions, instruction); } if (!transaction->num_waiting) { - // This can happen if the transaction only contains xwayland views wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying", transaction); transaction_apply(transaction); @@ -210,31 +246,47 @@ void transaction_commit(struct sway_transaction *transaction) { transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, handle_timeout, transaction); wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + + // The debug tree shows the pending/live tree. Here is a good place to + // update it, because we make a transaction every time we change the pending + // tree. + update_debug_tree(); } void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { // Find the instruction struct sway_transaction_instruction *instruction = NULL; - for (int i = 0; i < view->instructions->length; ++i) { + for (int i = 0; i < view->swayc->instructions->length; ++i) { struct sway_transaction_instruction *tmp_instruction = - view->instructions->items[i]; - if (tmp_instruction->serial == serial) { + view->swayc->instructions->items[i]; + if (tmp_instruction->serial == serial && !tmp_instruction->ready) { instruction = tmp_instruction; - list_del(view->instructions, i); break; } } if (!instruction) { - // This can happen if the view acknowledges the configure after the - // transaction has timed out and applied. return; } + instruction->ready = true; + // If all views are ready, apply the transaction struct sway_transaction *transaction = instruction->transaction; if (--transaction->num_waiting == 0) { +#if !TRANSACTION_DEBUG wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction); wl_event_source_timer_update(transaction->timer, 0); transaction_apply(transaction); transaction_destroy(transaction); +#endif } } + +struct wlr_texture *transaction_get_texture(struct sway_view *view) { + if (!view->swayc || !view->swayc->instructions->length) { + return view->surface->buffer->texture; + } + struct sway_transaction_instruction *instruction = + view->swayc->instructions->items[0]; + return instruction->saved_buffer ? + instruction->saved_buffer->texture : NULL; +} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index d22c967c..ab35b98f 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -143,16 +143,12 @@ static void _close(struct sway_view *view) { } } -static void destroy(struct sway_view *view) { +static void _free(struct sway_view *view) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); if (xdg_shell_view == NULL) { return; } - wl_list_remove(&xdg_shell_view->destroy.link); - wl_list_remove(&xdg_shell_view->map.link); - wl_list_remove(&xdg_shell_view->unmap.link); - wl_list_remove(&xdg_shell_view->request_fullscreen.link); free(xdg_shell_view); } @@ -164,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .destroy = destroy, + .free = _free, }; static void handle_commit(struct wl_listener *listener, void *data) { @@ -173,7 +169,11 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; - if (view->instructions->length) { + if (!view->swayc) { + return; + } + + if (view->swayc->instructions->length) { transaction_notify_view_ready(view, xdg_surface->configure_serial); } @@ -191,11 +191,18 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap); + struct sway_view *view = &xdg_shell_view->view; + + if (!sway_assert(view->surface, "Cannot unmap unmapped view")) { + return; + } - view_unmap(&xdg_shell_view->view); + struct sway_container *parent = view_unmap(view); + arrange_and_commit(parent); wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); + view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -230,7 +237,17 @@ static void handle_map(struct wl_listener *listener, void *data) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy); - view_destroy(&xdg_shell_view->view); + struct sway_view *view = &xdg_shell_view->view; + if (!sway_assert(view->swayc == NULL || view->swayc->destroying, + "Tried to destroy a mapped view")) { + return; + } + wl_list_remove(&xdg_shell_view->destroy.link); + wl_list_remove(&xdg_shell_view->map.link); + wl_list_remove(&xdg_shell_view->unmap.link); + wl_list_remove(&xdg_shell_view->request_fullscreen.link); + view->wlr_xdg_surface = NULL; + view_destroy(view); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 7ec9e6cb..76c1fa24 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -143,16 +143,12 @@ static void _close(struct sway_view *view) { } } -static void destroy(struct sway_view *view) { +static void _free(struct sway_view *view) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = xdg_shell_v6_view_from_view(view); if (xdg_shell_v6_view == NULL) { return; } - wl_list_remove(&xdg_shell_v6_view->destroy.link); - wl_list_remove(&xdg_shell_v6_view->map.link); - wl_list_remove(&xdg_shell_v6_view->unmap.link); - wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); free(xdg_shell_v6_view); } @@ -164,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .destroy = destroy, + .free = _free, }; static void handle_commit(struct wl_listener *listener, void *data) { @@ -173,7 +169,10 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_v6_view->view; struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; - if (view->instructions->length) { + if (!view->swayc) { + return; + } + if (view->swayc->instructions->length) { transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); } @@ -191,11 +190,18 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, unmap); + struct sway_view *view = &xdg_shell_v6_view->view; - view_unmap(&xdg_shell_v6_view->view); + if (!sway_assert(view->surface, "Cannot unmap unmapped view")) { + return; + } + + struct sway_container *parent = view_unmap(view); + arrange_and_commit(parent); wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); + view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -230,7 +236,13 @@ static void handle_map(struct wl_listener *listener, void *data) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, destroy); - view_destroy(&xdg_shell_v6_view->view); + struct sway_view *view = &xdg_shell_v6_view->view; + wl_list_remove(&xdg_shell_v6_view->destroy.link); + wl_list_remove(&xdg_shell_v6_view->map.link); + wl_list_remove(&xdg_shell_v6_view->unmap.link); + wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); + view->wlr_xdg_surface_v6 = NULL; + view_destroy(view); } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 55917bf6..a1837420 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -218,19 +218,11 @@ static void _close(struct sway_view *view) { wlr_xwayland_surface_close(view->wlr_xwayland_surface); } -static void destroy(struct sway_view *view) { +static void _free(struct sway_view *view) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); if (xwayland_view == NULL) { return; } - wl_list_remove(&xwayland_view->destroy.link); - wl_list_remove(&xwayland_view->request_configure.link); - wl_list_remove(&xwayland_view->request_fullscreen.link); - wl_list_remove(&xwayland_view->set_title.link); - wl_list_remove(&xwayland_view->set_class.link); - wl_list_remove(&xwayland_view->set_window_type.link); - wl_list_remove(&xwayland_view->map.link); - wl_list_remove(&xwayland_view->unmap.link); free(xwayland_view); } @@ -242,7 +234,7 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .close = _close, - .destroy = destroy, + .free = _free, }; static void handle_commit(struct wl_listener *listener, void *data) { @@ -254,7 +246,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { // Don't allow xwayland views to do resize or reposition themselves if // they're involved in a transaction. Once the transaction has finished // they'll apply the next time a commit happens. - if (view->instructions->length) { + if (view->swayc && view->swayc->instructions->length) { if (view->swayc && container_is_floating(view->swayc)) { view_update_size(view, xsurface->width, xsurface->height); } else { @@ -268,8 +260,17 @@ static void handle_commit(struct wl_listener *listener, void *data) { static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, unmap); + struct sway_view *view = &xwayland_view->view; + + if (!sway_assert(view->surface, "Cannot unmap unmapped view")) { + return; + } + + struct sway_container *parent = view_unmap(view); + arrange_and_commit(parent); + wl_list_remove(&xwayland_view->commit.link); - view_unmap(&xwayland_view->view); + view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -293,12 +294,30 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xsurface->fullscreen) { view_set_fullscreen(view, true); } - arrange_and_commit(view->swayc); + arrange_and_commit(view->swayc->parent); } static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, destroy); + struct sway_view *view = &xwayland_view->view; + + if (view->surface) { + struct sway_container *parent = view_unmap(view); + arrange_and_commit(parent); + + wl_list_remove(&xwayland_view->commit.link); + view->surface = NULL; + } + + wl_list_remove(&xwayland_view->destroy.link); + wl_list_remove(&xwayland_view->request_configure.link); + wl_list_remove(&xwayland_view->request_fullscreen.link); + wl_list_remove(&xwayland_view->set_title.link); + wl_list_remove(&xwayland_view->set_class.link); + wl_list_remove(&xwayland_view->set_window_type.link); + wl_list_remove(&xwayland_view->map.link); + wl_list_remove(&xwayland_view->unmap.link); view_destroy(&xwayland_view->view); } -- cgit v1.2.3 From 32b865e610dd937af17ce36b8c986e41f55a4627 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 23 Jun 2018 17:47:28 +1000 Subject: Fix crash when deleting last child in a tabbed or stacked container There was no `current` child because the container was destroyed. This makes it fall back to looking in the parent's current children list. --- sway/desktop/output.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9db95ef5..1ca48975 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -713,7 +713,7 @@ static void render_container_tabbed(struct sway_output *output, } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_child(seat, con); + struct sway_container *current = seat_get_active_current_child(seat, con); struct border_colors *current_colors = NULL; struct sway_container_state *pstate = &con->current; @@ -756,11 +756,13 @@ static void render_container_tabbed(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); + if (current) { + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } } } @@ -775,7 +777,7 @@ static void render_container_stacked(struct sway_output *output, } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_active_child(seat, con); + struct sway_container *current = seat_get_active_current_child(seat, con); struct border_colors *current_colors = NULL; struct sway_container_state *pstate = &con->current; @@ -812,11 +814,13 @@ static void render_container_stacked(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current->type == C_VIEW) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent_focused || current == focus); + if (current) { + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } } } -- cgit v1.2.3 From f08a30d6d04b5f986ea1e66a017e81bcd7c77e39 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 12:33:23 +1000 Subject: Force transactions to complete in order This forces transactions to complete in order by using a singly linked list stored in the sway server. --- sway/desktop/transaction.c | 90 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 18 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 6e09537a..04142bcc 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -31,6 +31,7 @@ struct sway_transaction { list_t *instructions; // struct sway_transaction_instruction * list_t *damage; // struct wlr_box * size_t num_waiting; + struct sway_transaction *next; }; struct sway_transaction_instruction { @@ -175,6 +176,7 @@ void transaction_add_damage(struct sway_transaction *transaction, * Apply a transaction to the "current" state of the tree. */ static void transaction_apply(struct sway_transaction *transaction) { + wlr_log(L_DEBUG, "Applying transaction %p", transaction); int i; // Apply the instruction state to the container's current state for (i = 0; i < transaction->instructions->length; ++i) { @@ -203,15 +205,54 @@ static void transaction_apply(struct sway_transaction *transaction) { } } +static void progress_queue() { + struct sway_transaction *transaction = server.head_transaction; + struct sway_transaction *next = NULL; + while (transaction && !transaction->num_waiting) { + next = transaction->next; + transaction_apply(transaction); + transaction_destroy(transaction); + transaction = next; + } + server.head_transaction = transaction; +} + static int handle_timeout(void *data) { struct sway_transaction *transaction = data; - wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting), applying anyway", + wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", transaction, transaction->num_waiting); - transaction_apply(transaction); - transaction_destroy(transaction); + transaction->num_waiting = 0; + progress_queue(); return 0; } +static bool should_configure(struct sway_container *con, + struct sway_transaction_instruction *instruction) { + if (con->type != C_VIEW) { + return false; + } + if (con->destroying) { + return false; + } + // The settled dimensions are what size the view will be once any pending + // configures have applied (excluding the one we might be configuring now). + // If these match the dimensions that this transaction wants then we don't + // need to configure it. + int settled_width = con->current.view_width; + int settled_height = con->current.view_height; + if (con->instructions->length) { + struct sway_transaction_instruction *last_instruction = + con->instructions->items[con->instructions->length - 1]; + settled_width = last_instruction->state.view_width; + settled_height = last_instruction->state.view_height; + } + if (settled_width == instruction->state.view_width && + settled_height == instruction->state.view_height) { + return false; + } + return true; +} + void transaction_commit(struct sway_transaction *transaction) { wlr_log(L_DEBUG, "Transaction %p committing with %i instructions", transaction, transaction->instructions->length); @@ -220,9 +261,7 @@ void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *con = instruction->container; - if (con->type == C_VIEW && !con->destroying && - (con->current.view_width != instruction->state.view_width || - con->current.view_height != instruction->state.view_height)) { + if (should_configure(con, instruction)) { instruction->serial = view_configure(con->sway_view, instruction->state.view_x, instruction->state.view_y, @@ -234,18 +273,33 @@ void transaction_commit(struct sway_transaction *transaction) { } list_add(con->instructions, instruction); } - if (!transaction->num_waiting) { - wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying", - transaction); + if (server.head_transaction) { + // There is another transaction in progress - we must add this one to + // the queue so we complete after it. + struct sway_transaction *tail = server.head_transaction; + while (tail->next) { + tail = tail->next; + } + tail->next = transaction; + } else if (transaction->num_waiting) { + // There are no other transactions, but we're not applying immediately + // so we must jump in the queue so others will queue behind us. + server.head_transaction = transaction; + } else { + // There are no other transactions in progress, and this one has nothing + // to wait for, so we can skip the queue. + wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction); transaction_apply(transaction); transaction_destroy(transaction); return; } - // Set up a timer which the views must respond within - transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, - handle_timeout, transaction); - wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + if (transaction->num_waiting) { + // Set up a timer which the views must respond within + transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_timeout, transaction); + wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); + } // The debug tree shows the pending/live tree. Here is a good place to // update it, because we make a transaction every time we change the pending @@ -269,14 +323,14 @@ void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { } instruction->ready = true; - // If all views are ready, apply the transaction + // If all views are ready, apply the transaction. + // If the transaction has timed out then its num_waiting will be 0 already. struct sway_transaction *transaction = instruction->transaction; - if (--transaction->num_waiting == 0) { + if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { #if !TRANSACTION_DEBUG - wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction); + wlr_log(L_DEBUG, "Transaction %p is ready", transaction); wl_event_source_timer_update(transaction->timer, 0); - transaction_apply(transaction); - transaction_destroy(transaction); + progress_queue(); #endif } } -- cgit v1.2.3 From 33e03cb27795b267bcd3c7508363ec8f95127e95 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 13:08:47 +1000 Subject: Fix crash related to stacks and tabs --- sway/desktop/output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1ca48975..790751c3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -714,7 +714,7 @@ static void render_container_tabbed(struct sway_output *output, struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = NULL; + struct border_colors *current_colors = &config->border_colors.unfocused; struct sway_container_state *pstate = &con->current; // Render tabs @@ -778,7 +778,7 @@ static void render_container_stacked(struct sway_output *output, struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); struct sway_container *current = seat_get_active_current_child(seat, con); - struct border_colors *current_colors = NULL; + struct border_colors *current_colors = &config->border_colors.unfocused; struct sway_container_state *pstate = &con->current; // Render titles -- cgit v1.2.3 From 1549fb719ae75a498bf319db45281464e72c759e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 24 Jun 2018 23:01:09 +1000 Subject: Implement atomic layout updates for xwayland views --- sway/desktop/transaction.c | 44 +++++++++++++++++++++++++++----------------- sway/desktop/xwayland.c | 14 ++++---------- 2 files changed, 31 insertions(+), 27 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 04142bcc..08678b5b 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -267,9 +267,7 @@ void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_y, instruction->state.view_width, instruction->state.view_height); - if (instruction->serial) { - ++transaction->num_waiting; - } + ++transaction->num_waiting; } list_add(con->instructions, instruction); } @@ -307,20 +305,8 @@ void transaction_commit(struct sway_transaction *transaction) { update_debug_tree(); } -void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { - // Find the instruction - struct sway_transaction_instruction *instruction = NULL; - for (int i = 0; i < view->swayc->instructions->length; ++i) { - struct sway_transaction_instruction *tmp_instruction = - view->swayc->instructions->items[i]; - if (tmp_instruction->serial == serial && !tmp_instruction->ready) { - instruction = tmp_instruction; - break; - } - } - if (!instruction) { - return; - } +static void set_instruction_ready( + struct sway_transaction_instruction *instruction) { instruction->ready = true; // If all views are ready, apply the transaction. @@ -335,6 +321,30 @@ void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { } } +void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { + for (int i = 0; i < view->swayc->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + view->swayc->instructions->items[i]; + if (instruction->serial == serial && !instruction->ready) { + set_instruction_ready(instruction); + return; + } + } +} + +void transaction_notify_view_ready_by_size(struct sway_view *view, + int width, int height) { + for (int i = 0; i < view->swayc->instructions->length; ++i) { + struct sway_transaction_instruction *instruction = + view->swayc->instructions->items[i]; + if (!instruction->ready && instruction->state.view_width == width && + instruction->state.view_height == height) { + set_instruction_ready(instruction); + return; + } + } +} + struct wlr_texture *transaction_get_texture(struct sway_view *view) { if (!view->swayc || !view->swayc->instructions->length) { return view->surface->buffer->texture; diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index a1837420..7e78ef32 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -7,6 +7,7 @@ #include #include "log.h" #include "sway/desktop.h" +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/output.h" @@ -243,16 +244,9 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - // Don't allow xwayland views to do resize or reposition themselves if - // they're involved in a transaction. Once the transaction has finished - // they'll apply the next time a commit happens. - if (view->swayc && view->swayc->instructions->length) { - if (view->swayc && container_is_floating(view->swayc)) { - view_update_size(view, xsurface->width, xsurface->height); - } else { - view_update_size(view, view->swayc->width, view->swayc->height); - } - view_update_position(view, view->x, view->y); + if (view->swayc->instructions->length) { + transaction_notify_view_ready_by_size(view, + xsurface->width, xsurface->height); } view_damage_from(view); } -- cgit v1.2.3 From 289d696adc22a4247345cb544454bac0dfd5d828 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 25 Jun 2018 09:09:43 +1000 Subject: Implement transaction timings debug Launch sway with SWAY_DEBUG=txn_timings to enable it. --- sway/desktop/transaction.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 08678b5b..cb23ab69 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "sway/debug.h" @@ -32,6 +33,8 @@ struct sway_transaction { list_t *damage; // struct wlr_box * size_t num_waiting; struct sway_transaction *next; + struct timespec create_time; + struct timespec commit_time; }; struct sway_transaction_instruction { @@ -48,6 +51,9 @@ struct sway_transaction *transaction_create() { calloc(1, sizeof(struct sway_transaction)); transaction->instructions = create_list(); transaction->damage = create_list(); + if (server.debug_txn_timings) { + clock_gettime(CLOCK_MONOTONIC, &transaction->create_time); + } return transaction; } @@ -177,6 +183,20 @@ void transaction_add_damage(struct sway_transaction *transaction, */ static void transaction_apply(struct sway_transaction *transaction) { wlr_log(L_DEBUG, "Applying transaction %p", transaction); + if (server.debug_txn_timings) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct timespec *create = &transaction->create_time; + struct timespec *commit = &transaction->commit_time; + float ms_arranging = (commit->tv_sec - create->tv_sec) * 1000 + + (commit->tv_nsec - create->tv_nsec) / 1000000.0; + float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 + + (now.tv_nsec - commit->tv_nsec) / 1000000.0; + float ms_total = ms_arranging + ms_waiting; + wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " + "%.1fms total (%.1f frames if 60hz)", transaction, + ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); + } int i; // Apply the instruction state to the container's current state for (i = 0; i < transaction->instructions->length; ++i) { @@ -271,6 +291,9 @@ void transaction_commit(struct sway_transaction *transaction) { } list_add(con->instructions, instruction); } + if (server.debug_txn_timings) { + clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); + } if (server.head_transaction) { // There is another transaction in progress - we must add this one to // the queue so we complete after it. -- cgit v1.2.3 From c371ff3de8abbaf3428eadb905d7f940281196c1 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 25 Jun 2018 09:25:51 +1000 Subject: Implement per-configure debug timings --- sway/desktop/transaction.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index cb23ab69..31a9bf57 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -32,6 +32,7 @@ struct sway_transaction { list_t *instructions; // struct sway_transaction_instruction * list_t *damage; // struct wlr_box * size_t num_waiting; + size_t num_configures; struct sway_transaction *next; struct timespec create_time; struct timespec commit_time; @@ -291,6 +292,7 @@ void transaction_commit(struct sway_transaction *transaction) { } list_add(con->instructions, instruction); } + transaction->num_configures = transaction->num_waiting; if (server.debug_txn_timings) { clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); } @@ -331,10 +333,24 @@ void transaction_commit(struct sway_transaction *transaction) { static void set_instruction_ready( struct sway_transaction_instruction *instruction) { instruction->ready = true; + struct sway_transaction *transaction = instruction->transaction; + + if (server.debug_txn_timings) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct timespec *start = &transaction->commit_time; + float ms = (now.tv_sec - start->tv_sec) * 1000 + + (now.tv_nsec - start->tv_nsec) / 1000000.0; + wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)", + transaction, + transaction->num_configures - transaction->num_waiting + 1, + transaction->num_configures, ms, + instruction->container->name); + + } // If all views are ready, apply the transaction. // If the transaction has timed out then its num_waiting will be 0 already. - struct sway_transaction *transaction = instruction->transaction; if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { #if !TRANSACTION_DEBUG wlr_log(L_DEBUG, "Transaction %p is ready", transaction); -- cgit v1.2.3 From beacd4d9f9c6da7459bcde0e95031dac41387a7c Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 25 Jun 2018 16:50:01 +1000 Subject: Rename progress_queue to transaction_progress_queue --- sway/desktop/transaction.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 31a9bf57..7727ec6e 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -226,7 +226,7 @@ static void transaction_apply(struct sway_transaction *transaction) { } } -static void progress_queue() { +static void transaction_progress_queue() { struct sway_transaction *transaction = server.head_transaction; struct sway_transaction *next = NULL; while (transaction && !transaction->num_waiting) { @@ -243,7 +243,7 @@ static int handle_timeout(void *data) { wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)", transaction, transaction->num_waiting); transaction->num_waiting = 0; - progress_queue(); + transaction_progress_queue(); return 0; } @@ -355,7 +355,7 @@ static void set_instruction_ready( #if !TRANSACTION_DEBUG wlr_log(L_DEBUG, "Transaction %p is ready", transaction); wl_event_source_timer_update(transaction->timer, 0); - progress_queue(); + transaction_progress_queue(); #endif } } -- cgit v1.2.3 From 7a922c65aab27c5f4282cf15de52d240e5ac8052 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 13:15:45 +1000 Subject: Damage output when a fullscreen view unmaps Also moved the arranging into view_unmap to avoid excessive code duplication. --- sway/desktop/xdg_shell.c | 3 +-- sway/desktop/xdg_shell_v6.c | 3 +-- sway/desktop/xwayland.c | 7 ++----- 3 files changed, 4 insertions(+), 9 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index ab35b98f..a06c3bd2 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -197,8 +197,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); + view_unmap(view); wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 76c1fa24..424bca7b 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -196,8 +196,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); + view_unmap(view); wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7e78ef32..53fa42cc 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -260,8 +260,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) { return; } - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); + view_unmap(view); wl_list_remove(&xwayland_view->commit.link); view->surface = NULL; @@ -297,9 +296,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_view *view = &xwayland_view->view; if (view->surface) { - struct sway_container *parent = view_unmap(view); - arrange_and_commit(parent); - + view_unmap(view); wl_list_remove(&xwayland_view->commit.link); view->surface = NULL; } -- cgit v1.2.3 From 50190bc7609d981c45d26cd0b7d6d0fbf66feb05 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 13:18:33 +1000 Subject: Rename view's free callback to destroy --- sway/desktop/xdg_shell.c | 4 ++-- sway/desktop/xdg_shell_v6.c | 4 ++-- sway/desktop/xwayland.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index a06c3bd2..105e77ae 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -143,7 +143,7 @@ static void _close(struct sway_view *view) { } } -static void _free(struct sway_view *view) { +static void destroy(struct sway_view *view) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); if (xdg_shell_view == NULL) { @@ -160,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .free = _free, + .destroy = destroy, }; static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 424bca7b..19b30604 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -143,7 +143,7 @@ static void _close(struct sway_view *view) { } } -static void _free(struct sway_view *view) { +static void destroy(struct sway_view *view) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = xdg_shell_v6_view_from_view(view); if (xdg_shell_v6_view == NULL) { @@ -160,7 +160,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .close = _close, - .free = _free, + .destroy = destroy, }; static void handle_commit(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 53fa42cc..eea8420d 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -219,7 +219,7 @@ static void _close(struct sway_view *view) { wlr_xwayland_surface_close(view->wlr_xwayland_surface); } -static void _free(struct sway_view *view) { +static void destroy(struct sway_view *view) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); if (xwayland_view == NULL) { return; @@ -235,7 +235,7 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .close = _close, - .free = _free, + .destroy = destroy, }; static void handle_commit(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From e8001e6fbe827f6ae6842cf9f221edb322bb570e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 14:24:15 +1000 Subject: Damage output when views toggle fullscreen Also add workspace to the transaction when a view maps in fullscreen mode. --- sway/desktop/xdg_shell.c | 10 ++++++++-- sway/desktop/xdg_shell_v6.c | 10 ++++++++-- sway/desktop/xwayland.c | 12 ++++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 105e77ae..484afd0c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -221,8 +221,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xdg_surface->toplevel->client_pending.fullscreen) { view_set_fullscreen(view, true); + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); + } else { + arrange_and_commit(view->swayc->parent); } - arrange_and_commit(view->swayc->parent); xdg_shell_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, @@ -269,7 +272,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + struct sway_transaction *transaction = transaction_create(); + arrange_windows(ws, transaction); + transaction_add_damage(transaction, container_get_box(ws->parent)); + transaction_commit(transaction); } void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 19b30604..da2eda7a 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -220,8 +220,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xdg_surface->toplevel->client_pending.fullscreen) { view_set_fullscreen(view, true); + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); + } else { + arrange_and_commit(view->swayc->parent); } - arrange_and_commit(view->swayc->parent); xdg_shell_v6_view->commit.notify = handle_commit; wl_signal_add(&xdg_surface->surface->events.commit, @@ -264,7 +267,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_and_commit(ws); + struct sway_transaction *transaction = transaction_create(); + arrange_windows(ws, transaction); + transaction_add_damage(transaction, container_get_box(ws->parent)); + transaction_commit(transaction); } void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index eea8420d..720ea2fd 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -286,8 +286,11 @@ static void handle_map(struct wl_listener *listener, void *data) { if (xsurface->fullscreen) { view_set_fullscreen(view, true); + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + arrange_and_commit(ws); + } else { + arrange_and_commit(view->swayc->parent); } - arrange_and_commit(view->swayc->parent); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -337,7 +340,12 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } view_set_fullscreen(view, xsurface->fullscreen); - arrange_and_commit(view->swayc); + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + struct sway_transaction *transaction = transaction_create(); + arrange_windows(ws, transaction); + transaction_add_damage(transaction, container_get_box(ws->parent)); + transaction_commit(transaction); } static void handle_set_title(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From 0085f64ac073666aa661938f15e8c1190d69a69b Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 18:51:37 +1000 Subject: Remove timer when transaction destroys --- sway/desktop/transaction.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7727ec6e..7a99bfe2 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -102,6 +102,9 @@ static void transaction_destroy(struct sway_transaction *transaction) { list_foreach(transaction->damage, free); list_free(transaction->damage); + if (transaction->timer) { + wl_event_source_remove(transaction->timer); + } free(transaction); } -- cgit v1.2.3 From 834805f5e260bcc77d714323d4a7f4bfd1dbfb17 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 26 Jun 2018 19:40:42 +1000 Subject: Fix crash when disconnecting output We were freeing the sway_output immediately upon disconnect which left a dangling pointer in the output's container. It then tried to use the pointer when the container is freed. We don't need to store the sway_output in an output's container which is destroying, so the fix is to set the pointer to NULL and remove the use in container_free. Also added an arrange when the output is disconnected for good measure. --- sway/desktop/output.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sway/desktop') diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 790751c3..34fefaa9 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1271,6 +1271,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->destroy.link); output->wlr_output->data = NULL; free(output); + + arrange_and_commit(&root_container); } static void handle_mode(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From 61c118768564eec07ac16494d90f567e75ea60cf Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 17:23:44 +1000 Subject: Fix nitpicks --- sway/desktop/transaction.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7a99bfe2..98cde889 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -73,8 +73,7 @@ static void save_view_buffer(struct sway_view *view, remove_saved_view_buffer(instruction); } if (view->surface && wlr_surface_has_buffer(view->surface)) { - wlr_buffer_ref(view->surface->buffer); - instruction->saved_buffer = view->surface->buffer; + instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); } } @@ -198,12 +197,11 @@ static void transaction_apply(struct sway_transaction *transaction) { (now.tv_nsec - commit->tv_nsec) / 1000000.0; float ms_total = ms_arranging + ms_waiting; wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, " - "%.1fms total (%.1f frames if 60hz)", transaction, + "%.1fms total (%.1f frames if 60Hz)", transaction, ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); } - int i; // Apply the instruction state to the container's current state - for (i = 0; i < transaction->instructions->length; ++i) { + for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *container = instruction->container; @@ -220,7 +218,7 @@ static void transaction_apply(struct sway_transaction *transaction) { } // Apply damage - for (i = 0; i < transaction->damage->length; ++i) { + for (int i = 0; i < transaction->damage->length; ++i) { struct wlr_box *box = transaction->damage->items[i]; for (int j = 0; j < root_container.children->length; ++j) { struct sway_container *output = root_container.children->items[j]; -- cgit v1.2.3 From be86d3aba602fef7b51fafa8a6e7a39d1e49817f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 17:46:03 +1000 Subject: Remove transaction_add_damage Instead, damage each container when applying the transaction. --- sway/desktop/transaction.c | 42 ++++++++++++++++++++---------------------- sway/desktop/xdg_shell.c | 5 +---- sway/desktop/xdg_shell_v6.c | 5 +---- sway/desktop/xwayland.c | 5 +---- 4 files changed, 23 insertions(+), 34 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 98cde889..c29b6661 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -30,7 +30,6 @@ struct sway_transaction { struct wl_event_source *timer; list_t *instructions; // struct sway_transaction_instruction * - list_t *damage; // struct wlr_box * size_t num_waiting; size_t num_configures; struct sway_transaction *next; @@ -51,7 +50,6 @@ struct sway_transaction *transaction_create() { struct sway_transaction *transaction = calloc(1, sizeof(struct sway_transaction)); transaction->instructions = create_list(); - transaction->damage = create_list(); if (server.debug_txn_timings) { clock_gettime(CLOCK_MONOTONIC, &transaction->create_time); } @@ -97,10 +95,6 @@ static void transaction_destroy(struct sway_transaction *transaction) { } list_free(transaction->instructions); - // Free damage - list_foreach(transaction->damage, free); - list_free(transaction->damage); - if (transaction->timer) { wl_event_source_remove(transaction->timer); } @@ -174,13 +168,6 @@ void transaction_add_container(struct sway_transaction *transaction, list_add(transaction->instructions, instruction); } -void transaction_add_damage(struct sway_transaction *transaction, - struct wlr_box *_box) { - struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); - memcpy(box, _box, sizeof(struct wlr_box)); - list_add(transaction->damage, box); -} - /** * Apply a transaction to the "current" state of the tree. */ @@ -200,12 +187,32 @@ static void transaction_apply(struct sway_transaction *transaction) { "%.1fms total (%.1f frames if 60Hz)", transaction, ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60)); } + // Apply the instruction state to the container's current state for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_container *container = instruction->container; + // Damage the old and new locations + struct wlr_box old_box = { + .x = container->current.swayc_x, + .y = container->current.swayc_y, + .width = container->current.swayc_width, + .height = container->current.swayc_height, + }; + struct wlr_box new_box = { + .x = instruction->state.swayc_x, + .y = instruction->state.swayc_y, + .width = instruction->state.swayc_width, + .height = instruction->state.swayc_height, + }; + for (int j = 0; j < root_container.children->length; ++j) { + struct sway_container *output = root_container.children->items[j]; + output_damage_box(output->sway_output, &old_box); + output_damage_box(output->sway_output, &new_box); + } + // There are separate children lists for each instruction state, the // container's current state and the container's pending state // (ie. con->children). The list itself needs to be freed here. @@ -216,15 +223,6 @@ static void transaction_apply(struct sway_transaction *transaction) { memcpy(&container->current, &instruction->state, sizeof(struct sway_container_state)); } - - // Apply damage - for (int i = 0; i < transaction->damage->length; ++i) { - struct wlr_box *box = transaction->damage->items[i]; - for (int j = 0; j < root_container.children->length; ++j) { - struct sway_container *output = root_container.children->items[j]; - output_damage_box(output->sway_output, box); - } - } } static void transaction_progress_queue() { diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 484afd0c..b6fa9525 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -272,10 +272,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - struct sway_transaction *transaction = transaction_create(); - arrange_windows(ws, transaction); - transaction_add_damage(transaction, container_get_box(ws->parent)); - transaction_commit(transaction); + arrange_and_commit(ws); } void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index da2eda7a..6042a806 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -267,10 +267,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, e->fullscreen); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - struct sway_transaction *transaction = transaction_create(); - arrange_windows(ws, transaction); - transaction_add_damage(transaction, container_get_box(ws->parent)); - transaction_commit(transaction); + arrange_and_commit(ws); } void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 720ea2fd..1d5dab70 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -342,10 +342,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, xsurface->fullscreen); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - struct sway_transaction *transaction = transaction_create(); - arrange_windows(ws, transaction); - transaction_add_damage(transaction, container_get_box(ws->parent)); - transaction_commit(transaction); + arrange_and_commit(ws); } static void handle_set_title(struct wl_listener *listener, void *data) { -- cgit v1.2.3 From e6829c5991cac1bd164f800c14fccd522d702783 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 17:54:57 +1000 Subject: Move unsetting of view->surface into view_unmap --- sway/desktop/xdg_shell.c | 1 - sway/desktop/xdg_shell_v6.c | 1 - sway/desktop/xwayland.c | 2 -- 3 files changed, 4 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index b6fa9525..b076d772 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -201,7 +201,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); - view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 6042a806..7320e629 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -200,7 +200,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); - view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 1d5dab70..854da006 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -263,7 +263,6 @@ static void handle_unmap(struct wl_listener *listener, void *data) { view_unmap(view); wl_list_remove(&xwayland_view->commit.link); - view->surface = NULL; } static void handle_map(struct wl_listener *listener, void *data) { @@ -301,7 +300,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { if (view->surface) { view_unmap(view); wl_list_remove(&xwayland_view->commit.link); - view->surface = NULL; } wl_list_remove(&xwayland_view->destroy.link); -- cgit v1.2.3 From 9652529cc161e943241d946dac93ab16d5e30ee5 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 27 Jun 2018 19:07:48 +1000 Subject: Allow views to skip configures To do this properly, the transaction queue will only be processed if it can be completely processed. --- sway/desktop/transaction.c | 51 +++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 21 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index c29b6661..7a2e78e5 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -32,7 +32,6 @@ struct sway_transaction { list_t *instructions; // struct sway_transaction_instruction * size_t num_waiting; size_t num_configures; - struct sway_transaction *next; struct timespec create_time; struct timespec commit_time; }; @@ -225,16 +224,24 @@ static void transaction_apply(struct sway_transaction *transaction) { } } +/** + * For simplicity, we only progress the queue if it can be completely flushed. + */ static void transaction_progress_queue() { - struct sway_transaction *transaction = server.head_transaction; - struct sway_transaction *next = NULL; - while (transaction && !transaction->num_waiting) { - next = transaction->next; + // We iterate this list in reverse because we're more likely to find a + // waiting transactions at the end of the list. + for (int i = server.transactions->length - 1; i >= 0; --i) { + struct sway_transaction *transaction = server.transactions->items[i]; + if (transaction->num_waiting) { + return; + } + } + for (int i = 0; i < server.transactions->length; ++i) { + struct sway_transaction *transaction = server.transactions->items[i]; transaction_apply(transaction); transaction_destroy(transaction); - transaction = next; } - server.head_transaction = transaction; + list_empty(server.transactions); } static int handle_timeout(void *data) { @@ -295,18 +302,8 @@ void transaction_commit(struct sway_transaction *transaction) { if (server.debug_txn_timings) { clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time); } - if (server.head_transaction) { - // There is another transaction in progress - we must add this one to - // the queue so we complete after it. - struct sway_transaction *tail = server.head_transaction; - while (tail->next) { - tail = tail->next; - } - tail->next = transaction; - } else if (transaction->num_waiting) { - // There are no other transactions, but we're not applying immediately - // so we must jump in the queue so others will queue behind us. - server.head_transaction = transaction; + if (server.transactions->length || transaction->num_waiting) { + list_add(server.transactions, transaction); } else { // There are no other transactions in progress, and this one has nothing // to wait for, so we can skip the queue. @@ -359,12 +356,24 @@ static void set_instruction_ready( } } +/** + * Mark all of the view's instructions as ready up to and including the + * instruction at the given index. This allows the view to skip a configure. + */ +static void set_instructions_ready(struct sway_view *view, int index) { + for (int i = 0; i <= index; ++i) { + struct sway_transaction_instruction *instruction = + view->swayc->instructions->items[i]; + set_instruction_ready(instruction); + } +} + void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { for (int i = 0; i < view->swayc->instructions->length; ++i) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[i]; if (instruction->serial == serial && !instruction->ready) { - set_instruction_ready(instruction); + set_instructions_ready(view, i); return; } } @@ -377,7 +386,7 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, view->swayc->instructions->items[i]; if (!instruction->ready && instruction->state.view_width == width && instruction->state.view_height == height) { - set_instruction_ready(instruction); + set_instructions_ready(view, i); return; } } -- cgit v1.2.3 From d7169ee7ffd45318125dbe3013aadbd1482a3e5f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Jun 2018 19:44:54 +1000 Subject: Replace list_empty with a simple alternative --- sway/desktop/transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7a2e78e5..fc23ef35 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -241,7 +241,7 @@ static void transaction_progress_queue() { transaction_apply(transaction); transaction_destroy(transaction); } - list_empty(server.transactions); + server.transactions->length = 0; } static int handle_timeout(void *data) { -- cgit v1.2.3 From 3a6ed5110c76ef5bed8cc4c26a97759f6201eaac Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Jun 2018 21:13:22 +1000 Subject: Render saved buffers with the surface's dimensions --- sway/desktop/output.c | 8 +++++--- sway/desktop/transaction.c | 17 +++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) (limited to 'sway/desktop') diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 69d0bdd4..b55a3962 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -354,15 +354,17 @@ 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; - struct wlr_texture *texture = transaction_get_texture(view); + int width, height; + struct wlr_texture *texture = + transaction_get_saved_texture(view, &width, &height); if (!texture) { return; } struct wlr_box box = { .x = view->swayc->current.view_x - output->swayc->current.swayc_x, .y = view->swayc->current.view_y - output->swayc->current.swayc_y, - .width = view->swayc->current.view_width, - .height = view->swayc->current.view_height, + .width = width, + .height = height, }; struct wlr_box output_box = { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index fc23ef35..7c5a9b8f 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -41,6 +41,7 @@ struct sway_transaction_instruction { struct sway_container *container; struct sway_container_state state; struct wlr_buffer *saved_buffer; + int saved_buffer_width, saved_buffer_height; uint32_t serial; bool ready; }; @@ -71,6 +72,8 @@ static void save_view_buffer(struct sway_view *view, } if (view->surface && wlr_surface_has_buffer(view->surface)) { instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); + instruction->saved_buffer_width = view->surface->current->width; + instruction->saved_buffer_height = view->surface->current->height; } } @@ -392,12 +395,14 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, } } -struct wlr_texture *transaction_get_texture(struct sway_view *view) { - if (!view->swayc || !view->swayc->instructions->length) { - return view->surface->buffer->texture; - } +struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, + int *width, int *height) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[0]; - return instruction->saved_buffer ? - instruction->saved_buffer->texture : NULL; + if (!instruction->saved_buffer) { + return NULL; + } + *width = instruction->saved_buffer_width; + *height = instruction->saved_buffer_height; + return instruction->saved_buffer->texture; } -- cgit v1.2.3 From 96c8c024830f13a27790d4ea36b640df383a7f49 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 30 Jun 2018 14:30:14 +1000 Subject: Fix flash of background when xwayland views are mapped A flash of background was happening for two reasons: 1) We were using the xsurface's dimensions to check if the surface is ready, but these are pending dimensions. 2) In my particular setup, the default geometry of the xsurface does not intersect any output, which prevented it from receiving a frame done event. This made the transaction time out and the client would only redraw once it's been rendered. --- sway/desktop/transaction.c | 7 +++++++ sway/desktop/xwayland.c | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'sway/desktop') diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7c5a9b8f..d2932c87 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -298,6 +298,13 @@ void transaction_commit(struct sway_transaction *transaction) { instruction->state.view_width, instruction->state.view_height); ++transaction->num_waiting; + + // From here on we are rendering a saved buffer of 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. + struct timespec when; + wlr_surface_send_frame_done(con->sway_view->surface, &when); } list_add(con->instructions, instruction); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 023fb2a7..ad893248 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -269,10 +269,11 @@ static void handle_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, commit); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + struct wlr_surface_state *surface_state = xsurface->surface->current; if (view->swayc->instructions->length) { transaction_notify_view_ready_by_size(view, - xsurface->width, xsurface->height); + surface_state->width, surface_state->height); } view_damage_from(view); } -- cgit v1.2.3