aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop/transaction.c
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-06-23 16:24:11 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-06-23 16:24:11 +1000
commit38398e2d77d57dc06b67ec88a54091c897915602 (patch)
treec80935807865fd96ab7d037070287d4dfaba1863 /sway/desktop/transaction.c
parentPreserve buffers during transactions (diff)
downloadsway-38398e2d77d57dc06b67ec88a54091c897915602.tar.gz
sway-38398e2d77d57dc06b67ec88a54091c897915602.tar.zst
sway-38398e2d77d57dc06b67ec88a54091c897915602.zip
Implement atomic layout updates for tree operations
This implements atomic layout updates for when views map, reparent or unmap.
Diffstat (limited to 'sway/desktop/transaction.c')
-rw-r--r--sway/desktop/transaction.c176
1 files changed, 114 insertions, 62 deletions
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 @@
9#include "sway/output.h" 9#include "sway/output.h"
10#include "sway/tree/container.h" 10#include "sway/tree/container.h"
11#include "sway/tree/view.h" 11#include "sway/tree/view.h"
12#include "sway/tree/workspace.h"
12#include "list.h" 13#include "list.h"
13#include "log.h" 14#include "log.h"
14 15
@@ -18,6 +19,13 @@
18 */ 19 */
19#define TIMEOUT_MS 200 20#define TIMEOUT_MS 200
20 21
22/**
23 * If enabled, sway will always wait for the transaction timeout before
24 * applying it, rather than applying it when the views are ready. This allows us
25 * to observe the rendered state while a transaction is in progress.
26 */
27#define TRANSACTION_DEBUG false
28
21struct sway_transaction { 29struct sway_transaction {
22 struct wl_event_source *timer; 30 struct wl_event_source *timer;
23 list_t *instructions; // struct sway_transaction_instruction * 31 list_t *instructions; // struct sway_transaction_instruction *
@@ -29,7 +37,9 @@ struct sway_transaction_instruction {
29 struct sway_transaction *transaction; 37 struct sway_transaction *transaction;
30 struct sway_container *container; 38 struct sway_container *container;
31 struct sway_container_state state; 39 struct sway_container_state state;
40 struct wlr_buffer *saved_buffer;
32 uint32_t serial; 41 uint32_t serial;
42 bool ready;
33}; 43};
34 44
35struct sway_transaction *transaction_create() { 45struct sway_transaction *transaction_create() {
@@ -40,44 +50,55 @@ struct sway_transaction *transaction_create() {
40 return transaction; 50 return transaction;
41} 51}
42 52
53static void remove_saved_view_buffer(
54 struct sway_transaction_instruction *instruction) {
55 if (instruction->saved_buffer) {
56 wlr_buffer_unref(instruction->saved_buffer);
57 instruction->saved_buffer = NULL;
58 }
59}
60
61static void save_view_buffer(struct sway_view *view,
62 struct sway_transaction_instruction *instruction) {
63 if (!sway_assert(instruction->saved_buffer == NULL,
64 "Didn't expect instruction to have a saved buffer already")) {
65 remove_saved_view_buffer(instruction);
66 }
67 if (view->surface && wlr_surface_has_buffer(view->surface)) {
68 wlr_buffer_ref(view->surface->buffer);
69 instruction->saved_buffer = view->surface->buffer;
70 }
71}
72
43static void transaction_destroy(struct sway_transaction *transaction) { 73static void transaction_destroy(struct sway_transaction *transaction) {
44 int i;
45 // Free instructions 74 // Free instructions
46 for (i = 0; i < transaction->instructions->length; ++i) { 75 for (int i = 0; i < transaction->instructions->length; ++i) {
47 struct sway_transaction_instruction *instruction = 76 struct sway_transaction_instruction *instruction =
48 transaction->instructions->items[i]; 77 transaction->instructions->items[i];
49 if (instruction->container->type == C_VIEW) { 78 struct sway_container *con = instruction->container;
50 struct sway_view *view = instruction->container->sway_view; 79 for (int j = 0; j < con->instructions->length; ++j) {
51 for (int j = 0; j < view->instructions->length; ++j) { 80 if (con->instructions->items[j] == instruction) {
52 if (view->instructions->items[j] == instruction) { 81 list_del(con->instructions, j);
53 list_del(view->instructions, j); 82 break;
54 break;
55 }
56 } 83 }
57 } 84 }
85 if (con->destroying && !con->instructions->length) {
86 container_free(con);
87 }
88 remove_saved_view_buffer(instruction);
58 free(instruction); 89 free(instruction);
59 } 90 }
60 list_free(transaction->instructions); 91 list_free(transaction->instructions);
61 92
62 // Free damage 93 // Free damage
63 for (i = 0; i < transaction->damage->length; ++i) { 94 list_foreach(transaction->damage, free);
64 struct wlr_box *box = transaction->damage->items[i];
65 free(box);
66 }
67 list_free(transaction->damage); 95 list_free(transaction->damage);
68 96
69 free(transaction); 97 free(transaction);
70} 98}
71 99
72void transaction_add_container(struct sway_transaction *transaction, 100static void copy_pending_state(struct sway_container *container,
73 struct sway_container *container) { 101 struct sway_container_state *state) {
74 struct sway_transaction_instruction *instruction =
75 calloc(1, sizeof(struct sway_transaction_instruction));
76 instruction->transaction = transaction;
77 instruction->container = container;
78
79 // Copy the container's main (pending) properties into the instruction state
80 struct sway_container_state *state = &instruction->state;
81 state->layout = container->layout; 102 state->layout = container->layout;
82 state->swayc_x = container->x; 103 state->swayc_x = container->x;
83 state->swayc_y = container->y; 104 state->swayc_y = container->y;
@@ -87,6 +108,7 @@ void transaction_add_container(struct sway_transaction *transaction,
87 state->current_gaps = container->current_gaps; 108 state->current_gaps = container->current_gaps;
88 state->gaps_inner = container->gaps_inner; 109 state->gaps_inner = container->gaps_inner;
89 state->gaps_outer = container->gaps_outer; 110 state->gaps_outer = container->gaps_outer;
111 state->parent = container->parent;
90 112
91 if (container->type == C_VIEW) { 113 if (container->type == C_VIEW) {
92 struct sway_view *view = container->sway_view; 114 struct sway_view *view = container->sway_view;
@@ -101,8 +123,44 @@ void transaction_add_container(struct sway_transaction *transaction,
101 state->border_left = view->border_left; 123 state->border_left = view->border_left;
102 state->border_right = view->border_right; 124 state->border_right = view->border_right;
103 state->border_bottom = view->border_bottom; 125 state->border_bottom = view->border_bottom;
126 } else if (container->type == C_WORKSPACE) {
127 state->ws_fullscreen = container->sway_workspace->fullscreen;
128 state->ws_floating = container->sway_workspace->floating;
129 state->children = create_list();
130 list_cat(state->children, container->children);
131 } else {
132 state->children = create_list();
133 list_cat(state->children, container->children);
134 }
135}
136
137static bool transaction_has_container(struct sway_transaction *transaction,
138 struct sway_container *container) {
139 for (int i = 0; i < transaction->instructions->length; ++i) {
140 struct sway_transaction_instruction *instruction =
141 transaction->instructions->items[i];
142 if (instruction->container == container) {
143 return true;
144 }
145 }
146 return false;
147}
148
149void transaction_add_container(struct sway_transaction *transaction,
150 struct sway_container *container) {
151 if (transaction_has_container(transaction, container)) {
152 return;
104 } 153 }
154 struct sway_transaction_instruction *instruction =
155 calloc(1, sizeof(struct sway_transaction_instruction));
156 instruction->transaction = transaction;
157 instruction->container = container;
158
159 copy_pending_state(container, &instruction->state);
105 160
161 if (container->type == C_VIEW) {
162 save_view_buffer(container->sway_view, instruction);
163 }
106 list_add(transaction->instructions, instruction); 164 list_add(transaction->instructions, instruction);
107} 165}
108 166
@@ -113,47 +171,29 @@ void transaction_add_damage(struct sway_transaction *transaction,
113 list_add(transaction->damage, box); 171 list_add(transaction->damage, box);
114} 172}
115 173
116static void save_view_buffer(struct sway_view *view) {
117 if (view->saved_buffer) {
118 wlr_buffer_unref(view->saved_buffer);
119 }
120 wlr_buffer_ref(view->surface->buffer);
121 view->saved_buffer = view->surface->buffer;
122 view->saved_surface_width = view->surface->current->width;
123 view->saved_surface_height = view->surface->current->height;
124}
125
126static void remove_saved_view_buffer(struct sway_view *view) {
127 if (view->saved_buffer) {
128 wlr_buffer_unref(view->saved_buffer);
129 view->saved_buffer = NULL;
130 view->saved_surface_width = 0;
131 view->saved_surface_height = 0;
132 }
133}
134
135/** 174/**
136 * Apply a transaction to the "current" state of the tree. 175 * Apply a transaction to the "current" state of the tree.
137 *
138 * This is mostly copying stuff from the pending state into the main swayc
139 * properties, but also includes reparenting and deleting containers.
140 */ 176 */
141static void transaction_apply(struct sway_transaction *transaction) { 177static void transaction_apply(struct sway_transaction *transaction) {
142 int i; 178 int i;
179 // Apply the instruction state to the container's current state
143 for (i = 0; i < transaction->instructions->length; ++i) { 180 for (i = 0; i < transaction->instructions->length; ++i) {
144 struct sway_transaction_instruction *instruction = 181 struct sway_transaction_instruction *instruction =
145 transaction->instructions->items[i]; 182 transaction->instructions->items[i];
146 struct sway_container *container = instruction->container; 183 struct sway_container *container = instruction->container;
147 184
148 memcpy(&instruction->container->current, &instruction->state, 185 // There are separate children lists for each instruction state, the
149 sizeof(struct sway_container_state)); 186 // container's current state and the container's pending state
187 // (ie. con->children). The list itself needs to be freed here.
188 // Any child containers which are being deleted will be cleaned up in
189 // transaction_destroy().
190 list_free(container->current.children);
150 191
151 if (container->type == C_VIEW) { 192 memcpy(&container->current, &instruction->state,
152 remove_saved_view_buffer(container->sway_view); 193 sizeof(struct sway_container_state));
153 }
154 } 194 }
155 195
156 // Damage 196 // Apply damage
157 for (i = 0; i < transaction->damage->length; ++i) { 197 for (i = 0; i < transaction->damage->length; ++i) {
158 struct wlr_box *box = transaction->damage->items[i]; 198 struct wlr_box *box = transaction->damage->items[i];
159 for (int j = 0; j < root_container.children->length; ++j) { 199 for (int j = 0; j < root_container.children->length; ++j) {
@@ -161,8 +201,6 @@ static void transaction_apply(struct sway_transaction *transaction) {
161 output_damage_box(output->sway_output, box); 201 output_damage_box(output->sway_output, box);
162 } 202 }
163 } 203 }
164
165 update_debug_tree();
166} 204}
167 205
168static int handle_timeout(void *data) { 206static int handle_timeout(void *data) {
@@ -182,7 +220,7 @@ void transaction_commit(struct sway_transaction *transaction) {
182 struct sway_transaction_instruction *instruction = 220 struct sway_transaction_instruction *instruction =
183 transaction->instructions->items[i]; 221 transaction->instructions->items[i];
184 struct sway_container *con = instruction->container; 222 struct sway_container *con = instruction->container;
185 if (con->type == C_VIEW && 223 if (con->type == C_VIEW && !con->destroying &&
186 (con->current.view_width != instruction->state.view_width || 224 (con->current.view_width != instruction->state.view_width ||
187 con->current.view_height != instruction->state.view_height)) { 225 con->current.view_height != instruction->state.view_height)) {
188 instruction->serial = view_configure(con->sway_view, 226 instruction->serial = view_configure(con->sway_view,
@@ -191,14 +229,12 @@ void transaction_commit(struct sway_transaction *transaction) {
191 instruction->state.view_width, 229 instruction->state.view_width,
192 instruction->state.view_height); 230 instruction->state.view_height);
193 if (instruction->serial) { 231 if (instruction->serial) {
194 save_view_buffer(con->sway_view);
195 list_add(con->sway_view->instructions, instruction);
196 ++transaction->num_waiting; 232 ++transaction->num_waiting;
197 } 233 }
198 } 234 }
235 list_add(con->instructions, instruction);
199 } 236 }
200 if (!transaction->num_waiting) { 237 if (!transaction->num_waiting) {
201 // This can happen if the transaction only contains xwayland views
202 wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying", 238 wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying",
203 transaction); 239 transaction);
204 transaction_apply(transaction); 240 transaction_apply(transaction);
@@ -210,31 +246,47 @@ void transaction_commit(struct sway_transaction *transaction) {
210 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, 246 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
211 handle_timeout, transaction); 247 handle_timeout, transaction);
212 wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); 248 wl_event_source_timer_update(transaction->timer, TIMEOUT_MS);
249
250 // The debug tree shows the pending/live tree. Here is a good place to
251 // update it, because we make a transaction every time we change the pending
252 // tree.
253 update_debug_tree();
213} 254}
214 255
215void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { 256void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) {
216 // Find the instruction 257 // Find the instruction
217 struct sway_transaction_instruction *instruction = NULL; 258 struct sway_transaction_instruction *instruction = NULL;
218 for (int i = 0; i < view->instructions->length; ++i) { 259 for (int i = 0; i < view->swayc->instructions->length; ++i) {
219 struct sway_transaction_instruction *tmp_instruction = 260 struct sway_transaction_instruction *tmp_instruction =
220 view->instructions->items[i]; 261 view->swayc->instructions->items[i];
221 if (tmp_instruction->serial == serial) { 262 if (tmp_instruction->serial == serial && !tmp_instruction->ready) {
222 instruction = tmp_instruction; 263 instruction = tmp_instruction;
223 list_del(view->instructions, i);
224 break; 264 break;
225 } 265 }
226 } 266 }
227 if (!instruction) { 267 if (!instruction) {
228 // This can happen if the view acknowledges the configure after the
229 // transaction has timed out and applied.
230 return; 268 return;
231 } 269 }
270 instruction->ready = true;
271
232 // If all views are ready, apply the transaction 272 // If all views are ready, apply the transaction
233 struct sway_transaction *transaction = instruction->transaction; 273 struct sway_transaction *transaction = instruction->transaction;
234 if (--transaction->num_waiting == 0) { 274 if (--transaction->num_waiting == 0) {
275#if !TRANSACTION_DEBUG
235 wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction); 276 wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction);
236 wl_event_source_timer_update(transaction->timer, 0); 277 wl_event_source_timer_update(transaction->timer, 0);
237 transaction_apply(transaction); 278 transaction_apply(transaction);
238 transaction_destroy(transaction); 279 transaction_destroy(transaction);
280#endif
239 } 281 }
240} 282}
283
284struct wlr_texture *transaction_get_texture(struct sway_view *view) {
285 if (!view->swayc || !view->swayc->instructions->length) {
286 return view->surface->buffer->texture;
287 }
288 struct sway_transaction_instruction *instruction =
289 view->swayc->instructions->items[0];
290 return instruction->saved_buffer ?
291 instruction->saved_buffer->texture : NULL;
292}