summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-07-15 05:01:25 -0700
committerLibravatar GitHub <noreply@github.com>2018-07-15 05:01:25 -0700
commit53e3f35ba348d6285478ceb4a93b020e138c95c7 (patch)
tree091c6133f3bd33b64b1853c7984eaa3921b9facf
parentMerge pull request #2266 from emersion/remove-orbital-screenshooter (diff)
parentSet signature to void (diff)
downloadsway-53e3f35ba348d6285478ceb4a93b020e138c95c7.tar.gz
sway-53e3f35ba348d6285478ceb4a93b020e138c95c7.tar.zst
sway-53e3f35ba348d6285478ceb4a93b020e138c95c7.zip
Merge pull request #2272 from RyanDwyer/simplify-transactions
Simplify transactions by using a dirty flag on containers
-rw-r--r--include/sway/desktop/transaction.h33
-rw-r--r--include/sway/server.h5
-rw-r--r--include/sway/tree/arrange.h22
-rw-r--r--include/sway/tree/container.h10
-rw-r--r--sway/commands.c2
-rw-r--r--sway/commands/border.c2
-rw-r--r--sway/commands/floating.c2
-rw-r--r--sway/commands/fullscreen.c2
-rw-r--r--sway/commands/gaps.c8
-rw-r--r--sway/commands/layout.c2
-rw-r--r--sway/commands/move.c25
-rw-r--r--sway/commands/reload.c2
-rw-r--r--sway/commands/resize.c6
-rw-r--r--sway/commands/smart_gaps.c2
-rw-r--r--sway/commands/split.c2
-rw-r--r--sway/commands/swap.c9
-rw-r--r--sway/config.c2
-rw-r--r--sway/desktop/layer_shell.c3
-rw-r--r--sway/desktop/output.c14
-rw-r--r--sway/desktop/transaction.c35
-rw-r--r--sway/desktop/xdg_shell.c8
-rw-r--r--sway/desktop/xdg_shell_v6.c8
-rw-r--r--sway/desktop/xwayland.c8
-rw-r--r--sway/server.c5
-rw-r--r--sway/tree/arrange.c78
-rw-r--r--sway/tree/container.c24
-rw-r--r--sway/tree/layout.c3
-rw-r--r--sway/tree/view.c5
-rw-r--r--sway/tree/workspace.c2
29 files changed, 139 insertions, 190 deletions
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
index 7ab80eb8..cee4afed 100644
--- a/include/sway/desktop/transaction.h
+++ b/include/sway/desktop/transaction.h
@@ -6,34 +6,25 @@
6/** 6/**
7 * Transactions enable us to perform atomic layout updates. 7 * Transactions enable us to perform atomic layout updates.
8 * 8 *
9 * When we want to make adjustments to the layout, we create a transaction. 9 * A transaction contains a list of containers and their new state.
10 * A transaction contains a list of affected containers and their new state.
11 * A state might contain a new size, or new border settings, or new parent/child 10 * A state might contain a new size, or new border settings, or new parent/child
12 * relationships. 11 * relationships.
13 * 12 *
14 * Calling transaction_commit() makes sway notify of all the affected clients 13 * Committing a transaction makes sway notify of all the affected clients with
15 * with their new sizes. We then wait for all the views to respond with their 14 * their new sizes. We then wait for all the views to respond with their new
16 * new surface sizes. When all are ready, or when a timeout has passed, we apply 15 * surface sizes. When all are ready, or when a timeout has passed, we apply the
17 * the updates all at the same time. 16 * updates all at the same time.
18 */ 17 *
19 18 * When we want to make adjustments to the layout, we change the pending state
20struct sway_transaction; 19 * in containers, mark them as dirty and call transaction_commit_dirty(). This
21 20 * create and commits a transaction from the dirty containers.
22/**
23 * Create a new transaction.
24 */
25struct sway_transaction *transaction_create(void);
26
27/**
28 * Add a container's pending state to the transaction.
29 */ 21 */
30void transaction_add_container(struct sway_transaction *transaction,
31 struct sway_container *container);
32 22
33/** 23/**
34 * Submit a transaction to the client views for configuration. 24 * Find all dirty containers, create and commit a transaction containing them,
25 * and unmark them as dirty.
35 */ 26 */
36void transaction_commit(struct sway_transaction *transaction); 27void transaction_commit_dirty(void);
37 28
38/** 29/**
39 * Notify the transaction system that a view is ready for the new layout. 30 * Notify the transaction system that a view is ready for the new layout.
diff --git a/include/sway/server.h b/include/sway/server.h
index a3e32898..a017d1c4 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -47,10 +47,7 @@ struct sway_server {
47 bool debug_txn_timings; 47 bool debug_txn_timings;
48 48
49 list_t *transactions; 49 list_t *transactions;
50 50 list_t *dirty_containers;
51 // When a view is being destroyed and is waiting for a transaction to
52 // complete it will be stored here.
53 list_t *destroying_containers;
54}; 51};
55 52
56struct sway_server server; 53struct sway_server server;
diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h
index 58235642..d6abcc81 100644
--- a/include/sway/tree/arrange.h
+++ b/include/sway/tree/arrange.h
@@ -11,26 +11,8 @@ void remove_gaps(struct sway_container *c);
11void add_gaps(struct sway_container *c); 11void add_gaps(struct sway_container *c);
12 12
13/** 13/**
14 * Arrange layout for all the children of the given container, and add them to 14 * Arrange layout for all the children of the given container.
15 * the given transaction.
16 *
17 * Use this function if you need to arrange multiple sections of the tree in one
18 * transaction.
19 *
20 * You must set the desired state of the container before calling
21 * arrange_windows, then don't change any state-tracked properties in the
22 * container until you've called transaction_commit.
23 */ 15 */
24void arrange_windows(struct sway_container *container, 16void arrange_windows(struct sway_container *container);
25 struct sway_transaction *transaction);
26
27/**
28 * Arrange layout for the given container and commit the transaction.
29 *
30 * This function is a wrapper around arrange_windows, and handles creating and
31 * committing the transaction for you. Use this function if you're only doing
32 * one arrange operation.
33 */
34void arrange_and_commit(struct sway_container *container);
35 17
36#endif 18#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index a69da9db..11780916 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -144,6 +144,10 @@ struct sway_container {
144 144
145 bool destroying; 145 bool destroying;
146 146
147 // If true, indicates that the container has pending state that differs from
148 // the current.
149 bool dirty;
150
147 struct { 151 struct {
148 struct wl_signal destroy; 152 struct wl_signal destroy;
149 // Raised after the tree updates, but before arrange_windows 153 // Raised after the tree updates, but before arrange_windows
@@ -303,4 +307,10 @@ void container_get_box(struct sway_container *container, struct wlr_box *box);
303void container_floating_move_to(struct sway_container *con, 307void container_floating_move_to(struct sway_container *con,
304 double lx, double ly); 308 double lx, double ly);
305 309
310/**
311 * Mark a container as dirty if it isn't already. Dirty containers will be
312 * included in the next transaction then unmarked as dirty.
313 */
314void container_set_dirty(struct sway_container *container);
315
306#endif 316#endif
diff --git a/sway/commands.c b/sway/commands.c
index addd64a6..50d949d4 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -9,6 +9,7 @@
9#include "sway/commands.h" 9#include "sway/commands.h"
10#include "sway/config.h" 10#include "sway/config.h"
11#include "sway/criteria.h" 11#include "sway/criteria.h"
12#include "sway/desktop/transaction.h"
12#include "sway/security.h" 13#include "sway/security.h"
13#include "sway/input/input-manager.h" 14#include "sway/input/input-manager.h"
14#include "sway/input/seat.h" 15#include "sway/input/seat.h"
@@ -322,6 +323,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
322cleanup: 323cleanup:
323 free(exec); 324 free(exec);
324 free(views); 325 free(views);
326 transaction_commit_dirty();
325 if (!results) { 327 if (!results) {
326 results = cmd_results_new(CMD_SUCCESS, NULL, NULL); 328 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
327 } 329 }
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 6db85395..9c19e20a 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -42,7 +42,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
42 container_set_geometry_from_floating_view(view->swayc); 42 container_set_geometry_from_floating_view(view->swayc);
43 } 43 }
44 44
45 arrange_and_commit(view->swayc); 45 arrange_windows(view->swayc);
46 46
47 struct sway_seat *seat = input_manager_current_seat(input_manager); 47 struct sway_seat *seat = input_manager_current_seat(input_manager);
48 if (seat->cursor) { 48 if (seat->cursor) {
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index e6003521..6ab56c3b 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -37,7 +37,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
37 container_set_floating(container, wants_floating); 37 container_set_floating(container, wants_floating);
38 38
39 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 39 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
40 arrange_and_commit(workspace); 40 arrange_windows(workspace);
41 41
42 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 42 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
43} 43}
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index 1a4d8b41..0b5beaa2 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -34,7 +34,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
34 view_set_fullscreen(view, wants_fullscreen); 34 view_set_fullscreen(view, wants_fullscreen);
35 35
36 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 36 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
37 arrange_and_commit(workspace->parent); 37 arrange_windows(workspace->parent);
38 38
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 39 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40} 40}
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 801fb179..3906eb70 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -43,7 +43,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
43 return cmd_results_new(CMD_INVALID, "gaps", 43 return cmd_results_new(CMD_INVALID, "gaps",
44 "gaps edge_gaps on|off|toggle"); 44 "gaps edge_gaps on|off|toggle");
45 } 45 }
46 arrange_and_commit(&root_container); 46 arrange_windows(&root_container);
47 } else { 47 } else {
48 int amount_idx = 0; // the current index in argv 48 int amount_idx = 0; // the current index in argv
49 enum gaps_op op = GAPS_OP_SET; 49 enum gaps_op op = GAPS_OP_SET;
@@ -124,7 +124,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
124 if (amount_idx == 0) { // gaps <amount> 124 if (amount_idx == 0) { // gaps <amount>
125 config->gaps_inner = val; 125 config->gaps_inner = val;
126 config->gaps_outer = val; 126 config->gaps_outer = val;
127 arrange_and_commit(&root_container); 127 arrange_windows(&root_container);
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 128 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 } 129 }
130 // Other variants. The middle-length variant (gaps inner|outer <amount>) 130 // Other variants. The middle-length variant (gaps inner|outer <amount>)
@@ -155,7 +155,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
155 } else { 155 } else {
156 config->gaps_outer = total; 156 config->gaps_outer = total;
157 } 157 }
158 arrange_and_commit(&root_container); 158 arrange_windows(&root_container);
159 } else { 159 } else {
160 struct sway_container *c = 160 struct sway_container *c =
161 config->handler_context.current_container; 161 config->handler_context.current_container;
@@ -169,7 +169,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
169 c->gaps_outer = total; 169 c->gaps_outer = total;
170 } 170 }
171 171
172 arrange_and_commit(c->parent ? c->parent : &root_container); 172 arrange_windows(c->parent ? c->parent : &root_container);
173 } 173 }
174 } 174 }
175 175
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 9945fa5c..c446f1f9 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -49,7 +49,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
49 } 49 }
50 50
51 container_notify_subtree_changed(parent); 51 container_notify_subtree_changed(parent);
52 arrange_and_commit(parent); 52 arrange_windows(parent);
53 53
54 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 54 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
55} 55}
diff --git a/sway/commands/move.c b/sway/commands/move.c
index a1c1e018..6ec050a8 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -6,7 +6,6 @@
6#include <wlr/types/wlr_output_layout.h> 6#include <wlr/types/wlr_output_layout.h>
7#include <wlr/util/log.h> 7#include <wlr/util/log.h>
8#include "sway/commands.h" 8#include "sway/commands.h"
9#include "sway/desktop/transaction.h"
10#include "sway/input/cursor.h" 9#include "sway/input/cursor.h"
11#include "sway/input/seat.h" 10#include "sway/input/seat.h"
12#include "sway/output.h" 11#include "sway/output.h"
@@ -105,10 +104,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
105 // TODO: Ideally we would arrange the surviving parent after reaping, 104 // TODO: Ideally we would arrange the surviving parent after reaping,
106 // but container_reap_empty does not return it, so we arrange the 105 // but container_reap_empty does not return it, so we arrange the
107 // workspace instead. 106 // workspace instead.
108 struct sway_transaction *txn = transaction_create(); 107 arrange_windows(old_ws);
109 arrange_windows(old_ws, txn); 108 arrange_windows(destination->parent);
110 arrange_windows(destination->parent, txn);
111 transaction_commit(txn);
112 109
113 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 110 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
114 } else if (strcasecmp(argv[1], "to") == 0 111 } else if (strcasecmp(argv[1], "to") == 0
@@ -144,10 +141,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
144 // TODO: Ideally we would arrange the surviving parent after reaping, 141 // TODO: Ideally we would arrange the surviving parent after reaping,
145 // but container_reap_empty does not return it, so we arrange the 142 // but container_reap_empty does not return it, so we arrange the
146 // workspace instead. 143 // workspace instead.
147 struct sway_transaction *txn = transaction_create(); 144 arrange_windows(old_ws);
148 arrange_windows(old_ws, txn); 145 arrange_windows(focus->parent);
149 arrange_windows(focus->parent, txn);
150 transaction_commit(txn);
151 146
152 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 147 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
153 } 148 }
@@ -177,10 +172,8 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
177 } 172 }
178 container_move_to(current, destination); 173 container_move_to(current, destination);
179 174
180 struct sway_transaction *txn = transaction_create(); 175 arrange_windows(source);
181 arrange_windows(source, txn); 176 arrange_windows(destination);
182 arrange_windows(destination, txn);
183 transaction_commit(txn);
184 177
185 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 178 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
186} 179}
@@ -238,12 +231,10 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
238 container_move(container, direction, move_amt); 231 container_move(container, direction, move_amt);
239 struct sway_container *new_ws = container_parent(container, C_WORKSPACE); 232 struct sway_container *new_ws = container_parent(container, C_WORKSPACE);
240 233
241 struct sway_transaction *txn = transaction_create(); 234 arrange_windows(old_ws);
242 arrange_windows(old_ws, txn);
243 if (new_ws != old_ws) { 235 if (new_ws != old_ws) {
244 arrange_windows(new_ws, txn); 236 arrange_windows(new_ws);
245 } 237 }
246 transaction_commit(txn);
247 238
248 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 239 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
249} 240}
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index c6715f9c..cea6a94b 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -12,6 +12,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
12 } 12 }
13 13
14 load_swaybars(); 14 load_swaybars();
15 arrange_and_commit(&root_container); 15 arrange_windows(&root_container);
16 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 16 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
17} 17}
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 2cf811d8..e657864c 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -268,7 +268,7 @@ static void resize_tiled(int amount, enum resize_axis axis) {
268 } 268 }
269 } 269 }
270 270
271 arrange_and_commit(parent->parent); 271 arrange_windows(parent->parent);
272} 272}
273 273
274/** 274/**
@@ -338,7 +338,7 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
338 view->height += grow_height; 338 view->height += grow_height;
339 } 339 }
340 340
341 arrange_and_commit(con); 341 arrange_windows(con);
342 342
343 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 343 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
344} 344}
@@ -410,7 +410,7 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
410 view->height += grow_height; 410 view->height += grow_height;
411 } 411 }
412 412
413 arrange_and_commit(con); 413 arrange_windows(con);
414 414
415 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 415 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
416} 416}
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index f687e78e..7d27e571 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
23 "Expected 'smart_gaps <on|off>' "); 23 "Expected 'smart_gaps <on|off>' ");
24 } 24 }
25 25
26 arrange_and_commit(&root_container); 26 arrange_windows(&root_container);
27 27
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 29}
diff --git a/sway/commands/split.c b/sway/commands/split.c
index c40f4d9f..313799da 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) {
16 } 16 }
17 struct sway_container *parent = container_split(con, layout); 17 struct sway_container *parent = container_split(con, layout);
18 container_create_notify(parent); 18 container_create_notify(parent);
19 arrange_and_commit(parent->parent); 19 arrange_windows(parent->parent);
20 20
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22} 22}
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index e052058f..2fc88308 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,7 +1,6 @@
1#include <strings.h> 1#include <strings.h>
2#include <wlr/util/log.h> 2#include <wlr/util/log.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/desktop/transaction.h"
5#include "sway/tree/arrange.h" 4#include "sway/tree/arrange.h"
6#include "sway/tree/layout.h" 5#include "sway/tree/layout.h"
7#include "sway/tree/view.h" 6#include "sway/tree/view.h"
@@ -79,14 +78,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
79 78
80 container_swap(current, other); 79 container_swap(current, other);
81 80
82 struct sway_transaction *txn = transaction_create(); 81 arrange_windows(current->parent);
83 arrange_windows(current->parent, txn);
84
85 if (other->parent != current->parent) { 82 if (other->parent != current->parent) {
86 arrange_windows(other->parent, txn); 83 arrange_windows(other->parent);
87 } 84 }
88 85
89 transaction_commit(txn);
90
91 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 86 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
92} 87}
diff --git a/sway/config.c b/sway/config.c
index d2386f46..636f5f57 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -773,6 +773,6 @@ void config_update_font_height(bool recalculate) {
773 } 773 }
774 774
775 if (config->font_height != prev_max_height) { 775 if (config->font_height != prev_max_height) {
776 arrange_and_commit(&root_container); 776 arrange_windows(&root_container);
777 } 777 }
778} 778}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index 16910c7e..91baa6f8 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -12,7 +12,6 @@
12#include "sway/layers.h" 12#include "sway/layers.h"
13#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/server.h" 14#include "sway/server.h"
15#include "sway/tree/arrange.h"
16#include "sway/tree/layout.h" 15#include "sway/tree/layout.h"
17#include "log.h" 16#include "log.h"
18 17
@@ -176,7 +175,7 @@ void arrange_layers(struct sway_output *output) {
176 sizeof(struct wlr_box)) != 0) { 175 sizeof(struct wlr_box)) != 0) {
177 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); 176 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
178 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 177 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
179 arrange_and_commit(output->swayc); 178 container_set_dirty(output->swayc);
180 } 179 }
181 180
182 // Arrange non-exlusive surfaces from top->bottom 181 // Arrange non-exlusive surfaces from top->bottom
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index a2720885..a9808406 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -492,19 +492,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
492 output->wlr_output->data = NULL; 492 output->wlr_output->data = NULL;
493 free(output); 493 free(output);
494 494
495 arrange_and_commit(&root_container); 495 arrange_windows(&root_container);
496} 496}
497 497
498static void handle_mode(struct wl_listener *listener, void *data) { 498static void handle_mode(struct wl_listener *listener, void *data) {
499 struct sway_output *output = wl_container_of(listener, output, mode); 499 struct sway_output *output = wl_container_of(listener, output, mode);
500 arrange_layers(output); 500 arrange_layers(output);
501 arrange_and_commit(output->swayc); 501 arrange_windows(output->swayc);
502 transaction_commit_dirty();
502} 503}
503 504
504static void handle_transform(struct wl_listener *listener, void *data) { 505static void handle_transform(struct wl_listener *listener, void *data) {
505 struct sway_output *output = wl_container_of(listener, output, transform); 506 struct sway_output *output = wl_container_of(listener, output, transform);
506 arrange_layers(output); 507 arrange_layers(output);
507 arrange_and_commit(output->swayc); 508 arrange_windows(output->swayc);
509 transaction_commit_dirty();
508} 510}
509 511
510static void handle_scale_iterator(struct sway_container *view, void *data) { 512static void handle_scale_iterator(struct sway_container *view, void *data) {
@@ -515,7 +517,8 @@ static void handle_scale(struct wl_listener *listener, void *data) {
515 struct sway_output *output = wl_container_of(listener, output, scale); 517 struct sway_output *output = wl_container_of(listener, output, scale);
516 arrange_layers(output); 518 arrange_layers(output);
517 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); 519 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
518 arrange_and_commit(output->swayc); 520 arrange_windows(output->swayc);
521 transaction_commit_dirty();
519} 522}
520 523
521struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { 524struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
@@ -584,5 +587,6 @@ void output_enable(struct sway_output *output) {
584 output->damage_destroy.notify = damage_handle_destroy; 587 output->damage_destroy.notify = damage_handle_destroy;
585 588
586 arrange_layers(output); 589 arrange_layers(output);
587 arrange_and_commit(&root_container); 590 arrange_windows(&root_container);
591 transaction_commit_dirty();
588} 592}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 5e42fde5..0ae042db 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -47,7 +47,7 @@ struct sway_transaction_instruction {
47 bool ready; 47 bool ready;
48}; 48};
49 49
50struct sway_transaction *transaction_create() { 50static struct sway_transaction *transaction_create() {
51 struct sway_transaction *transaction = 51 struct sway_transaction *transaction =
52 calloc(1, sizeof(struct sway_transaction)); 52 calloc(1, sizeof(struct sway_transaction));
53 transaction->instructions = create_list(); 53 transaction->instructions = create_list();
@@ -141,23 +141,8 @@ static void copy_pending_state(struct sway_container *container,
141 } 141 }
142} 142}
143 143
144static bool transaction_has_container(struct sway_transaction *transaction, 144static void transaction_add_container(struct sway_transaction *transaction,
145 struct sway_container *container) { 145 struct sway_container *container) {
146 for (int i = 0; i < transaction->instructions->length; ++i) {
147 struct sway_transaction_instruction *instruction =
148 transaction->instructions->items[i];
149 if (instruction->container == container) {
150 return true;
151 }
152 }
153 return false;
154}
155
156void transaction_add_container(struct sway_transaction *transaction,
157 struct sway_container *container) {
158 if (transaction_has_container(transaction, container)) {
159 return;
160 }
161 struct sway_transaction_instruction *instruction = 146 struct sway_transaction_instruction *instruction =
162 calloc(1, sizeof(struct sway_transaction_instruction)); 147 calloc(1, sizeof(struct sway_transaction_instruction));
163 instruction->transaction = transaction; 148 instruction->transaction = transaction;
@@ -285,7 +270,7 @@ static bool should_configure(struct sway_container *con,
285 return true; 270 return true;
286} 271}
287 272
288void transaction_commit(struct sway_transaction *transaction) { 273static void transaction_commit(struct sway_transaction *transaction) {
289 wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions", 274 wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions",
290 transaction, transaction->instructions->length); 275 transaction, transaction->instructions->length);
291 transaction->num_waiting = 0; 276 transaction->num_waiting = 0;
@@ -418,3 +403,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,
418 *height = instruction->saved_buffer_height; 403 *height = instruction->saved_buffer_height;
419 return instruction->saved_buffer->texture; 404 return instruction->saved_buffer->texture;
420} 405}
406
407void transaction_commit_dirty(void) {
408 if (!server.dirty_containers->length) {
409 return;
410 }
411 struct sway_transaction *transaction = transaction_create();
412 for (int i = 0; i < server.dirty_containers->length; ++i) {
413 struct sway_container *container = server.dirty_containers->items[i];
414 transaction_add_container(transaction, container);
415 container->dirty = false;
416 }
417 server.dirty_containers->length = 0;
418 transaction_commit(transaction);
419}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index fbeeb2e3..98c16faf 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -244,7 +244,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
244 view_set_fullscreen(view, e->fullscreen); 244 view_set_fullscreen(view, e->fullscreen);
245 245
246 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 246 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
247 arrange_and_commit(output); 247 arrange_windows(output);
248 transaction_commit_dirty();
248} 249}
249 250
250static void handle_unmap(struct wl_listener *listener, void *data) { 251static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -281,10 +282,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
281 if (xdg_surface->toplevel->client_pending.fullscreen) { 282 if (xdg_surface->toplevel->client_pending.fullscreen) {
282 view_set_fullscreen(view, true); 283 view_set_fullscreen(view, true);
283 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 284 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
284 arrange_and_commit(ws); 285 arrange_windows(ws);
285 } else { 286 } else {
286 arrange_and_commit(view->swayc->parent); 287 arrange_windows(view->swayc->parent);
287 } 288 }
289 transaction_commit_dirty();
288 290
289 xdg_shell_view->commit.notify = handle_commit; 291 xdg_shell_view->commit.notify = handle_commit;
290 wl_signal_add(&xdg_surface->surface->events.commit, 292 wl_signal_add(&xdg_surface->surface->events.commit,
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 88d9bb94..4d76f0a7 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -239,7 +239,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
239 view_set_fullscreen(view, e->fullscreen); 239 view_set_fullscreen(view, e->fullscreen);
240 240
241 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 241 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
242 arrange_and_commit(output); 242 arrange_windows(output);
243 transaction_commit_dirty();
243} 244}
244 245
245static void handle_unmap(struct wl_listener *listener, void *data) { 246static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -276,10 +277,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
276 if (xdg_surface->toplevel->client_pending.fullscreen) { 277 if (xdg_surface->toplevel->client_pending.fullscreen) {
277 view_set_fullscreen(view, true); 278 view_set_fullscreen(view, true);
278 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 279 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
279 arrange_and_commit(ws); 280 arrange_windows(ws);
280 } else { 281 } else {
281 arrange_and_commit(view->swayc->parent); 282 arrange_windows(view->swayc->parent);
282 } 283 }
284 transaction_commit_dirty();
283 285
284 xdg_shell_v6_view->commit.notify = handle_commit; 286 xdg_shell_v6_view->commit.notify = handle_commit;
285 wl_signal_add(&xdg_surface->surface->events.commit, 287 wl_signal_add(&xdg_surface->surface->events.commit,
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 460d1cc8..11516673 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -333,10 +333,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
333 if (xsurface->fullscreen) { 333 if (xsurface->fullscreen) {
334 view_set_fullscreen(view, true); 334 view_set_fullscreen(view, true);
335 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 335 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
336 arrange_and_commit(ws); 336 arrange_windows(ws);
337 } else { 337 } else {
338 arrange_and_commit(view->swayc->parent); 338 arrange_windows(view->swayc->parent);
339 } 339 }
340 transaction_commit_dirty();
340} 341}
341 342
342static void handle_destroy(struct wl_listener *listener, void *data) { 343static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -392,7 +393,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
392 view_set_fullscreen(view, xsurface->fullscreen); 393 view_set_fullscreen(view, xsurface->fullscreen);
393 394
394 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 395 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
395 arrange_and_commit(output); 396 arrange_windows(output);
397 transaction_commit_dirty();
396} 398}
397 399
398static void handle_set_title(struct wl_listener *listener, void *data) { 400static void handle_set_title(struct wl_listener *listener, void *data) {
diff --git a/sway/server.c b/sway/server.c
index 42aa2e31..f904b177 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -123,8 +123,7 @@ bool server_init(struct sway_server *server) {
123 if (debug != NULL && strcmp(debug, "txn_timings") == 0) { 123 if (debug != NULL && strcmp(debug, "txn_timings") == 0) {
124 server->debug_txn_timings = true; 124 server->debug_txn_timings = true;
125 } 125 }
126 server->destroying_containers = create_list(); 126 server->dirty_containers = create_list();
127
128 server->transactions = create_list(); 127 server->transactions = create_list();
129 128
130 input_manager = input_manager_create(server); 129 input_manager = input_manager_create(server);
@@ -134,7 +133,7 @@ bool server_init(struct sway_server *server) {
134void server_fini(struct sway_server *server) { 133void server_fini(struct sway_server *server) {
135 // TODO: free sway-specific resources 134 // TODO: free sway-specific resources
136 wl_display_destroy(server->wl_display); 135 wl_display_destroy(server->wl_display);
137 list_free(server->destroying_containers); 136 list_free(server->dirty_containers);
138 list_free(server->transactions); 137 list_free(server->transactions);
139} 138}
140 139
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index bcc3ee9a..533cf71c 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -144,38 +144,22 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) {
144 } 144 }
145} 145}
146 146
147/** 147static void arrange_children_of(struct sway_container *parent);
148 * If a container has been deleted from the pending tree state, we must add it
149 * to the transaction so it can be freed afterwards. To do this, we iterate the
150 * server's destroying_containers list and add all of them. We may add more than
151 * what we need to, but this is easy and has no negative consequences.
152 */
153static void add_deleted_containers(struct sway_transaction *transaction) {
154 for (int i = 0; i < server.destroying_containers->length; ++i) {
155 struct sway_container *child = server.destroying_containers->items[i];
156 transaction_add_container(transaction, child);
157 }
158}
159
160static void arrange_children_of(struct sway_container *parent,
161 struct sway_transaction *transaction);
162 148
163static void arrange_floating(struct sway_container *floating, 149static void arrange_floating(struct sway_container *floating) {
164 struct sway_transaction *transaction) {
165 for (int i = 0; i < floating->children->length; ++i) { 150 for (int i = 0; i < floating->children->length; ++i) {
166 struct sway_container *floater = floating->children->items[i]; 151 struct sway_container *floater = floating->children->items[i];
167 if (floater->type == C_VIEW) { 152 if (floater->type == C_VIEW) {
168 view_autoconfigure(floater->sway_view); 153 view_autoconfigure(floater->sway_view);
169 } else { 154 } else {
170 arrange_children_of(floater, transaction); 155 arrange_children_of(floater);
171 } 156 }
172 transaction_add_container(transaction, floater); 157 container_set_dirty(floater);
173 } 158 }
174 transaction_add_container(transaction, floating); 159 container_set_dirty(floating);
175} 160}
176 161
177static void arrange_children_of(struct sway_container *parent, 162static void arrange_children_of(struct sway_container *parent) {
178 struct sway_transaction *transaction) {
179 if (config->reloading) { 163 if (config->reloading) {
180 return; 164 return;
181 } 165 }
@@ -198,7 +182,7 @@ static void arrange_children_of(struct sway_container *parent,
198 apply_horiz_layout(parent); 182 apply_horiz_layout(parent);
199 break; 183 break;
200 case L_FLOATING: 184 case L_FLOATING:
201 arrange_floating(parent, transaction); 185 arrange_floating(parent);
202 break; 186 break;
203 } 187 }
204 188
@@ -213,14 +197,13 @@ static void arrange_children_of(struct sway_container *parent,
213 if (child->type == C_VIEW) { 197 if (child->type == C_VIEW) {
214 view_autoconfigure(child->sway_view); 198 view_autoconfigure(child->sway_view);
215 } else { 199 } else {
216 arrange_children_of(child, transaction); 200 arrange_children_of(child);
217 } 201 }
218 transaction_add_container(transaction, child); 202 container_set_dirty(child);
219 } 203 }
220} 204}
221 205
222static void arrange_workspace(struct sway_container *workspace, 206static void arrange_workspace(struct sway_container *workspace) {
223 struct sway_transaction *transaction) {
224 if (config->reloading) { 207 if (config->reloading) {
225 return; 208 return;
226 } 209 }
@@ -234,15 +217,14 @@ static void arrange_workspace(struct sway_container *workspace,
234 workspace->x = output->x + area->x; 217 workspace->x = output->x + area->x;
235 workspace->y = output->y + area->y; 218 workspace->y = output->y + area->y;
236 add_gaps(workspace); 219 add_gaps(workspace);
237 transaction_add_container(transaction, workspace); 220 container_set_dirty(workspace);
238 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, 221 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
239 workspace->x, workspace->y); 222 workspace->x, workspace->y);
240 arrange_floating(workspace->sway_workspace->floating, transaction); 223 arrange_floating(workspace->sway_workspace->floating);
241 arrange_children_of(workspace, transaction); 224 arrange_children_of(workspace);
242} 225}
243 226
244static void arrange_output(struct sway_container *output, 227static void arrange_output(struct sway_container *output) {
245 struct sway_transaction *transaction) {
246 if (config->reloading) { 228 if (config->reloading) {
247 return; 229 return;
248 } 230 }
@@ -253,16 +235,16 @@ static void arrange_output(struct sway_container *output,
253 output->y = output_box->y; 235 output->y = output_box->y;
254 output->width = output_box->width; 236 output->width = output_box->width;
255 output->height = output_box->height; 237 output->height = output_box->height;
256 transaction_add_container(transaction, output); 238 container_set_dirty(output);
257 wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f", 239 wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f",
258 output->name, output->x, output->y); 240 output->name, output->x, output->y);
259 for (int i = 0; i < output->children->length; ++i) { 241 for (int i = 0; i < output->children->length; ++i) {
260 struct sway_container *workspace = output->children->items[i]; 242 struct sway_container *workspace = output->children->items[i];
261 arrange_workspace(workspace, transaction); 243 arrange_workspace(workspace);
262 } 244 }
263} 245}
264 246
265static void arrange_root(struct sway_transaction *transaction) { 247static void arrange_root() {
266 if (config->reloading) { 248 if (config->reloading) {
267 return; 249 return;
268 } 250 }
@@ -274,43 +256,35 @@ static void arrange_root(struct sway_transaction *transaction) {
274 root_container.y = layout_box->y; 256 root_container.y = layout_box->y;
275 root_container.width = layout_box->width; 257 root_container.width = layout_box->width;
276 root_container.height = layout_box->height; 258 root_container.height = layout_box->height;
277 transaction_add_container(transaction, &root_container); 259 container_set_dirty(&root_container);
278 for (int i = 0; i < root_container.children->length; ++i) { 260 for (int i = 0; i < root_container.children->length; ++i) {
279 struct sway_container *output = root_container.children->items[i]; 261 struct sway_container *output = root_container.children->items[i];
280 arrange_output(output, transaction); 262 arrange_output(output);
281 } 263 }
282} 264}
283 265
284void arrange_windows(struct sway_container *container, 266void arrange_windows(struct sway_container *container) {
285 struct sway_transaction *transaction) {
286 switch (container->type) { 267 switch (container->type) {
287 case C_ROOT: 268 case C_ROOT:
288 arrange_root(transaction); 269 arrange_root();
289 break; 270 break;
290 case C_OUTPUT: 271 case C_OUTPUT:
291 arrange_output(container, transaction); 272 arrange_output(container);
292 break; 273 break;
293 case C_WORKSPACE: 274 case C_WORKSPACE:
294 arrange_workspace(container, transaction); 275 arrange_workspace(container);
295 break; 276 break;
296 case C_CONTAINER: 277 case C_CONTAINER:
297 arrange_children_of(container, transaction); 278 arrange_children_of(container);
298 transaction_add_container(transaction, container); 279 container_set_dirty(container);
299 break; 280 break;
300 case C_VIEW: 281 case C_VIEW:
301 view_autoconfigure(container->sway_view); 282 view_autoconfigure(container->sway_view);
302 transaction_add_container(transaction, container); 283 container_set_dirty(container);
303 break; 284 break;
304 case C_TYPES: 285 case C_TYPES:
305 break; 286 break;
306 } 287 }
307 add_deleted_containers(transaction);
308}
309
310void arrange_and_commit(struct sway_container *container) {
311 struct sway_transaction *transaction = transaction_create();
312 arrange_windows(container, transaction);
313 transaction_commit(transaction);
314} 288}
315 289
316void remove_gaps(struct sway_container *c) { 290void remove_gaps(struct sway_container *c) {
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 58852717..35f67cce 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -159,14 +159,6 @@ void container_free(struct sway_container *cont) {
159 wlr_texture_destroy(cont->title_focused_inactive); 159 wlr_texture_destroy(cont->title_focused_inactive);
160 wlr_texture_destroy(cont->title_unfocused); 160 wlr_texture_destroy(cont->title_unfocused);
161 wlr_texture_destroy(cont->title_urgent); 161 wlr_texture_destroy(cont->title_urgent);
162
163 for (int i = 0; i < server.destroying_containers->length; ++i) {
164 if (server.destroying_containers->items[i] == cont) {
165 list_del(server.destroying_containers, i);
166 break;
167 }
168 }
169
170 list_free(cont->instructions); 162 list_free(cont->instructions);
171 list_free(cont->children); 163 list_free(cont->children);
172 list_free(cont->current.children); 164 list_free(cont->current.children);
@@ -325,7 +317,7 @@ static struct sway_container *container_destroy_noreaping(
325 } 317 }
326 318
327 con->destroying = true; 319 con->destroying = true;
328 list_add(server.destroying_containers, con); 320 container_set_dirty(con);
329 321
330 if (!con->parent) { 322 if (!con->parent) {
331 return NULL; 323 return NULL;
@@ -1069,9 +1061,15 @@ void container_floating_move_to(struct sway_container *con,
1069 if (old_workspace != new_workspace) { 1061 if (old_workspace != new_workspace) {
1070 container_remove_child(con); 1062 container_remove_child(con);
1071 container_add_child(new_workspace->sway_workspace->floating, con); 1063 container_add_child(new_workspace->sway_workspace->floating, con);
1072 struct sway_transaction *transaction = transaction_create(); 1064 arrange_windows(old_workspace);
1073 arrange_windows(old_workspace, transaction); 1065 arrange_windows(new_workspace);
1074 arrange_windows(new_workspace, transaction); 1066 }
1075 transaction_commit(transaction); 1067}
1068
1069void container_set_dirty(struct sway_container *container) {
1070 if (container->dirty) {
1071 return;
1076 } 1072 }
1073 container->dirty = true;
1074 list_add(server.dirty_containers, container);
1077} 1075}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index ba234e89..54ddb3f9 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -22,7 +22,8 @@ struct sway_container root_container;
22 22
23static void output_layout_handle_change(struct wl_listener *listener, 23static void output_layout_handle_change(struct wl_listener *listener,
24 void *data) { 24 void *data) {
25 arrange_and_commit(&root_container); 25 arrange_windows(&root_container);
26 transaction_commit_dirty();
26} 27}
27 28
28void layout_init(void) { 29void layout_init(void) {
diff --git a/sway/tree/view.c b/sway/tree/view.c
index b356183c..bf380d98 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -594,11 +594,12 @@ void view_unmap(struct sway_view *view) {
594 ws->sway_workspace->fullscreen = NULL; 594 ws->sway_workspace->fullscreen = NULL;
595 container_destroy(view->swayc); 595 container_destroy(view->swayc);
596 596
597 arrange_and_commit(ws->parent); 597 arrange_windows(ws->parent);
598 } else { 598 } else {
599 struct sway_container *parent = container_destroy(view->swayc); 599 struct sway_container *parent = container_destroy(view->swayc);
600 arrange_and_commit(parent); 600 arrange_windows(parent);
601 } 601 }
602 transaction_commit_dirty();
602 view->surface = NULL; 603 view->surface = NULL;
603} 604}
604 605
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 50f9400a..2a2d834a 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -427,7 +427,7 @@ bool workspace_switch(struct sway_container *workspace) {
427 } 427 }
428 seat_set_focus(seat, next); 428 seat_set_focus(seat, next);
429 struct sway_container *output = container_parent(workspace, C_OUTPUT); 429 struct sway_container *output = container_parent(workspace, C_OUTPUT);
430 arrange_and_commit(output); 430 arrange_windows(output);
431 return true; 431 return true;
432} 432}
433 433