summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/desktop/transaction.h60
-rw-r--r--include/sway/output.h2
-rw-r--r--include/sway/server.h7
-rw-r--r--include/sway/tree/arrange.h30
-rw-r--r--include/sway/tree/container.h48
-rw-r--r--include/sway/tree/view.h29
-rw-r--r--sway/commands/border.c7
-rw-r--r--sway/commands/floating.c3
-rw-r--r--sway/commands/fullscreen.c4
-rw-r--r--sway/commands/gaps.c18
-rw-r--r--sway/commands/layout.c2
-rw-r--r--sway/commands/move.c41
-rw-r--r--sway/commands/reload.c2
-rw-r--r--sway/commands/resize.c2
-rw-r--r--sway/commands/smart_gaps.c5
-rw-r--r--sway/commands/split.c2
-rw-r--r--sway/commands/swap.c12
-rw-r--r--sway/config.c4
-rw-r--r--sway/desktop/desktop.c3
-rw-r--r--sway/desktop/layer_shell.c2
-rw-r--r--sway/desktop/output.c282
-rw-r--r--sway/desktop/transaction.c292
-rw-r--r--sway/desktop/xdg_shell.c78
-rw-r--r--sway/desktop/xdg_shell_v6.c76
-rw-r--r--sway/desktop/xwayland.c76
-rw-r--r--sway/main.c1
-rw-r--r--sway/meson.build1
-rw-r--r--sway/server.c4
-rw-r--r--sway/tree/arrange.c347
-rw-r--r--sway/tree/container.c200
-rw-r--r--sway/tree/layout.c76
-rw-r--r--sway/tree/output.c3
-rw-r--r--sway/tree/view.c173
-rw-r--r--sway/tree/workspace.c5
34 files changed, 1237 insertions, 660 deletions
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
new file mode 100644
index 00000000..d6adc609
--- /dev/null
+++ b/include/sway/desktop/transaction.h
@@ -0,0 +1,60 @@
1#ifndef _SWAY_TRANSACTION_H
2#define _SWAY_TRANSACTION_H
3#include <wlr/render/wlr_texture.h>
4#include "sway/tree/container.h"
5
6/**
7 * Transactions enable us to perform atomic layout updates.
8 *
9 * When we want to make adjustments to the layout, we create a transaction.
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
12 * relationships.
13 *
14 * Calling transaction_commit() makes sway notify of all the affected clients
15 * with their new sizes. We then wait for all the views to respond with their
16 * new surface sizes. When all are ready, or when a timeout has passed, we apply
17 * the updates all at the same time.
18 */
19
20struct sway_transaction;
21
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 */
30void transaction_add_container(struct sway_transaction *transaction,
31 struct sway_container *container);
32
33/**
34 * Add a box to be damaged when the transaction is applied.
35 * The box should be in layout coordinates.
36 */
37void transaction_add_damage(struct sway_transaction *transaction,
38 struct wlr_box *box);
39
40/**
41 * Submit a transaction to the client views for configuration.
42 */
43void transaction_commit(struct sway_transaction *transaction);
44
45/**
46 * Notify the transaction system that a view is ready for the new layout.
47 *
48 * When all views in the transaction are ready, the layout will be applied.
49 */
50void transaction_notify_view_ready(struct sway_view *view, uint32_t serial);
51
52/**
53 * Get the texture that should be rendered for a view.
54 *
55 * In most cases this will return the normal live texture for a view, but if the
56 * view is in a transaction then it'll return a saved texture.
57 */
58struct wlr_texture *transaction_get_texture(struct sway_view *view);
59
60#endif
diff --git a/include/sway/output.h b/include/sway/output.h
index 70f746dc..e6ca0d02 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -42,6 +42,8 @@ void output_damage_surface(struct sway_output *output, double ox, double oy,
42void output_damage_from_view(struct sway_output *output, 42void output_damage_from_view(struct sway_output *output,
43 struct sway_view *view); 43 struct sway_view *view);
44 44
45void output_damage_box(struct sway_output *output, struct wlr_box *box);
46
45void output_damage_whole_container(struct sway_output *output, 47void output_damage_whole_container(struct sway_output *output,
46 struct sway_container *con); 48 struct sway_container *con);
47 49
diff --git a/include/sway/server.h b/include/sway/server.h
index 963d4dc1..b07e86a7 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -12,6 +12,7 @@
12#include <wlr/render/wlr_renderer.h> 12#include <wlr/render/wlr_renderer.h>
13// TODO WLR: make Xwayland optional 13// TODO WLR: make Xwayland optional
14#include <wlr/xwayland.h> 14#include <wlr/xwayland.h>
15#include "list.h"
15 16
16struct sway_server { 17struct sway_server {
17 struct wl_display *wl_display; 18 struct wl_display *wl_display;
@@ -43,6 +44,12 @@ struct sway_server {
43 44
44 struct wlr_wl_shell *wl_shell; 45 struct wlr_wl_shell *wl_shell;
45 struct wl_listener wl_shell_surface; 46 struct wl_listener wl_shell_surface;
47
48 bool terminating;
49
50 // When a view is being destroyed and is waiting for a transaction to
51 // complete it will be stored here.
52 list_t *destroying_containers;
46}; 53};
47 54
48struct sway_server server; 55struct sway_server server;
diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h
index a14bc5dc..6c8c0dba 100644
--- a/include/sway/tree/arrange.h
+++ b/include/sway/tree/arrange.h
@@ -1,5 +1,6 @@
1#ifndef _SWAY_ARRANGE_H 1#ifndef _SWAY_ARRANGE_H
2#define _SWAY_ARRANGE_H 2#define _SWAY_ARRANGE_H
3#include "sway/desktop/transaction.h"
3 4
4struct sway_container; 5struct sway_container;
5 6
@@ -9,16 +10,23 @@ void remove_gaps(struct sway_container *c);
9// Add gaps around container 10// Add gaps around container
10void add_gaps(struct sway_container *c); 11void add_gaps(struct sway_container *c);
11 12
12// Determine the root container's geometry, then iterate to everything below 13/**
13void arrange_root(void); 14 * Arrange layout for all the children of the given container, and add them to
14 15 * the given transaction.
15// Determine the output's geometry, then iterate to everything below 16 *
16void arrange_output(struct sway_container *output); 17 * Use this function if you need to arrange multiple sections of the tree in one
17 18 * transaction.
18// Determine the workspace's geometry, then iterate to everything below 19 */
19void arrange_workspace(struct sway_container *workspace); 20void arrange_windows(struct sway_container *container,
20 21 struct sway_transaction *transaction);
21// Arrange layout for all the children of the given workspace/container 22
22void arrange_children_of(struct sway_container *parent); 23/**
24 * Arrange layout for the given container and commit the transaction.
25 *
26 * This function is a wrapper around arrange_windows, and handles creating and
27 * committing the transaction for you. Use this function if you're only doing
28 * one arrange operation.
29 */
30void arrange_and_commit(struct sway_container *container);
23 31
24#endif 32#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index b3406bbe..7e78cbef 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -54,6 +54,37 @@ struct sway_output;
54struct sway_workspace; 54struct sway_workspace;
55struct sway_view; 55struct sway_view;
56 56
57struct sway_container_state {
58 // Container/swayc properties
59 enum sway_container_layout layout;
60 double swayc_x, swayc_y;
61 double swayc_width, swayc_height;
62
63 bool has_gaps;
64 double current_gaps;
65 double gaps_inner;
66 double gaps_outer;
67
68 struct sway_container *parent;
69 list_t *children;
70
71 // View properties
72 double view_x, view_y;
73 double view_width, view_height;
74 bool is_fullscreen;
75
76 enum sway_container_border border;
77 int border_thickness;
78 bool border_top;
79 bool border_bottom;
80 bool border_left;
81 bool border_right;
82
83 // Workspace properties
84 struct sway_view *ws_fullscreen;
85 struct sway_container *ws_floating;
86};
87
57struct sway_container { 88struct sway_container {
58 union { 89 union {
59 // TODO: Encapsulate state for other node types as well like C_CONTAINER 90 // TODO: Encapsulate state for other node types as well like C_CONTAINER
@@ -69,6 +100,10 @@ struct sway_container {
69 */ 100 */
70 size_t id; 101 size_t id;
71 102
103 // The pending state is the main container properties, and the current state is in the below struct.
104 // This means most places of the code can refer to the main variables (pending state) and it'll just work.
105 struct sway_container_state current;
106
72 char *name; // The view's title (unformatted) 107 char *name; // The view's title (unformatted)
73 char *formatted_title; // The title displayed in the title bar 108 char *formatted_title; // The title displayed in the title bar
74 109
@@ -97,8 +132,6 @@ struct sway_container {
97 132
98 struct sway_container *parent; 133 struct sway_container *parent;
99 134
100 list_t *marks; // list of char*
101
102 float alpha; 135 float alpha;
103 136
104 struct wlr_texture *title_focused; 137 struct wlr_texture *title_focused;
@@ -107,6 +140,10 @@ struct sway_container {
107 struct wlr_texture *title_urgent; 140 struct wlr_texture *title_urgent;
108 size_t title_height; 141 size_t title_height;
109 142
143 list_t *instructions; // struct sway_transaction_instruction *
144
145 bool destroying;
146
110 struct { 147 struct {
111 struct wl_signal destroy; 148 struct wl_signal destroy;
112 // Raised after the tree updates, but before arrange_windows 149 // Raised after the tree updates, but before arrange_windows
@@ -150,6 +187,8 @@ struct sway_container *workspace_create(struct sway_container *output,
150struct sway_container *container_view_create( 187struct sway_container *container_view_create(
151 struct sway_container *sibling, struct sway_view *sway_view); 188 struct sway_container *sibling, struct sway_view *sway_view);
152 189
190void container_free(struct sway_container *cont);
191
153struct sway_container *container_destroy(struct sway_container *container); 192struct sway_container *container_destroy(struct sway_container *container);
154 193
155struct sway_container *container_close(struct sway_container *container); 194struct sway_container *container_close(struct sway_container *container);
@@ -253,4 +292,9 @@ void container_set_geometry_from_floating_view(struct sway_container *con);
253 */ 292 */
254bool container_is_floating(struct sway_container *container); 293bool container_is_floating(struct sway_container *container);
255 294
295/**
296 * Get a container's box in layout coordinates.
297 */
298struct wlr_box *container_get_box(struct sway_container *container);
299
256#endif 300#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 3df38e2d..5a615b43 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -29,15 +29,15 @@ struct sway_view_impl {
29 const char *(*get_string_prop)(struct sway_view *view, 29 const char *(*get_string_prop)(struct sway_view *view,
30 enum sway_view_prop prop); 30 enum sway_view_prop prop);
31 uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); 31 uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop);
32 void (*configure)(struct sway_view *view, double lx, double ly, int width, 32 uint32_t (*configure)(struct sway_view *view, double lx, double ly,
33 int height); 33 int width, int height);
34 void (*set_activated)(struct sway_view *view, bool activated); 34 void (*set_activated)(struct sway_view *view, bool activated);
35 void (*set_fullscreen)(struct sway_view *view, bool fullscreen); 35 void (*set_fullscreen)(struct sway_view *view, bool fullscreen);
36 bool (*wants_floating)(struct sway_view *view); 36 bool (*wants_floating)(struct sway_view *view);
37 void (*for_each_surface)(struct sway_view *view, 37 void (*for_each_surface)(struct sway_view *view,
38 wlr_surface_iterator_func_t iterator, void *user_data); 38 wlr_surface_iterator_func_t iterator, void *user_data);
39 void (*close)(struct sway_view *view); 39 void (*close)(struct sway_view *view);
40 void (*destroy)(struct sway_view *view); 40 void (*free)(struct sway_view *view);
41}; 41};
42 42
43struct sway_view { 43struct sway_view {
@@ -68,6 +68,8 @@ struct sway_view {
68 bool border_left; 68 bool border_left;
69 bool border_right; 69 bool border_right;
70 70
71 bool destroying;
72
71 list_t *executed_criteria; // struct criteria * 73 list_t *executed_criteria; // struct criteria *
72 list_t *marks; // char * 74 list_t *marks; // char *
73 75
@@ -103,8 +105,6 @@ struct sway_xdg_shell_v6_view {
103 struct wl_listener map; 105 struct wl_listener map;
104 struct wl_listener unmap; 106 struct wl_listener unmap;
105 struct wl_listener destroy; 107 struct wl_listener destroy;
106
107 int pending_width, pending_height;
108}; 108};
109 109
110struct sway_xdg_shell_view { 110struct sway_xdg_shell_view {
@@ -119,8 +119,6 @@ struct sway_xdg_shell_view {
119 struct wl_listener map; 119 struct wl_listener map;
120 struct wl_listener unmap; 120 struct wl_listener unmap;
121 struct wl_listener destroy; 121 struct wl_listener destroy;
122
123 int pending_width, pending_height;
124}; 122};
125 123
126struct sway_xwayland_view { 124struct sway_xwayland_view {
@@ -138,9 +136,6 @@ struct sway_xwayland_view {
138 struct wl_listener map; 136 struct wl_listener map;
139 struct wl_listener unmap; 137 struct wl_listener unmap;
140 struct wl_listener destroy; 138 struct wl_listener destroy;
141
142 int pending_lx, pending_ly;
143 int pending_width, pending_height;
144}; 139};
145 140
146struct sway_xwayland_unmanaged { 141struct sway_xwayland_unmanaged {
@@ -212,10 +207,15 @@ uint32_t view_get_window_type(struct sway_view *view);
212 207
213const char *view_get_shell(struct sway_view *view); 208const char *view_get_shell(struct sway_view *view);
214 209
215void view_configure(struct sway_view *view, double ox, double oy, int width, 210uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
216 int height); 211 int height);
217 212
218/** 213/**
214 * Center the view in its workspace and build the swayc decorations around it.
215 */
216void view_init_floating(struct sway_view *view);
217
218/**
219 * Configure the view's position and size based on the swayc's position and 219 * Configure the view's position and size based on the swayc's position and
220 * size, taking borders into consideration. 220 * size, taking borders into consideration.
221 */ 221 */
@@ -239,11 +239,16 @@ void view_for_each_surface(struct sway_view *view,
239void view_init(struct sway_view *view, enum sway_view_type type, 239void view_init(struct sway_view *view, enum sway_view_type type,
240 const struct sway_view_impl *impl); 240 const struct sway_view_impl *impl);
241 241
242void view_free(struct sway_view *view);
243
242void view_destroy(struct sway_view *view); 244void view_destroy(struct sway_view *view);
243 245
244void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); 246void view_map(struct sway_view *view, struct wlr_surface *wlr_surface);
245 247
246void view_unmap(struct sway_view *view); 248/**
249 * Unmap the view and return the surviving parent (after reaping).
250 */
251struct sway_container *view_unmap(struct sway_view *view);
247 252
248void view_update_position(struct sway_view *view, double lx, double ly); 253void view_update_position(struct sway_view *view, double lx, double ly);
249 254
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 0b059562..6db85395 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -3,6 +3,7 @@
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/input/cursor.h" 4#include "sway/input/cursor.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "sway/tree/arrange.h"
6#include "sway/tree/container.h" 7#include "sway/tree/container.h"
7#include "sway/tree/view.h" 8#include "sway/tree/view.h"
8 9
@@ -38,13 +39,11 @@ struct cmd_results *cmd_border(int argc, char **argv) {
38 } 39 }
39 40
40 if (container_is_floating(view->swayc)) { 41 if (container_is_floating(view->swayc)) {
41 container_damage_whole(view->swayc);
42 container_set_geometry_from_floating_view(view->swayc); 42 container_set_geometry_from_floating_view(view->swayc);
43 container_damage_whole(view->swayc);
44 } else {
45 view_autoconfigure(view);
46 } 43 }
47 44
45 arrange_and_commit(view->swayc);
46
48 struct sway_seat *seat = input_manager_current_seat(input_manager); 47 struct sway_seat *seat = input_manager_current_seat(input_manager);
49 if (seat->cursor) { 48 if (seat->cursor) {
50 cursor_send_pointer_motion(seat->cursor, 0, false); 49 cursor_send_pointer_motion(seat->cursor, 0, false);
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index 46b761da..e6003521 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -36,5 +36,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
36 36
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);
40 arrange_and_commit(workspace);
41
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 42 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40} 43}
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index ec9ec276..1a4d8b41 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -1,6 +1,7 @@
1#include "log.h" 1#include "log.h"
2#include "sway/commands.h" 2#include "sway/commands.h"
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/tree/arrange.h"
4#include "sway/tree/container.h" 5#include "sway/tree/container.h"
5#include "sway/tree/view.h" 6#include "sway/tree/view.h"
6#include "sway/tree/layout.h" 7#include "sway/tree/layout.h"
@@ -32,5 +33,8 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
32 33
33 view_set_fullscreen(view, wants_fullscreen); 34 view_set_fullscreen(view, wants_fullscreen);
34 35
36 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
37 arrange_and_commit(workspace->parent);
38
35 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 39 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
36} 40}
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index aacb792b..801fb179 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -31,21 +31,19 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
31 31
32 if (strcmp(argv[1], "on") == 0) { 32 if (strcmp(argv[1], "on") == 0) {
33 config->edge_gaps = true; 33 config->edge_gaps = true;
34 arrange_root();
35 } else if (strcmp(argv[1], "off") == 0) { 34 } else if (strcmp(argv[1], "off") == 0) {
36 config->edge_gaps = false; 35 config->edge_gaps = false;
37 arrange_root();
38 } else if (strcmp(argv[1], "toggle") == 0) { 36 } else if (strcmp(argv[1], "toggle") == 0) {
39 if (!config->active) { 37 if (!config->active) {
40 return cmd_results_new(CMD_INVALID, "gaps", 38 return cmd_results_new(CMD_INVALID, "gaps",
41 "Cannot toggle gaps while not running."); 39 "Cannot toggle gaps while not running.");
42 } 40 }
43 config->edge_gaps = !config->edge_gaps; 41 config->edge_gaps = !config->edge_gaps;
44 arrange_root();
45 } else { 42 } else {
46 return cmd_results_new(CMD_INVALID, "gaps", 43 return cmd_results_new(CMD_INVALID, "gaps",
47 "gaps edge_gaps on|off|toggle"); 44 "gaps edge_gaps on|off|toggle");
48 } 45 }
46 arrange_and_commit(&root_container);
49 } else { 47 } else {
50 int amount_idx = 0; // the current index in argv 48 int amount_idx = 0; // the current index in argv
51 enum gaps_op op = GAPS_OP_SET; 49 enum gaps_op op = GAPS_OP_SET;
@@ -120,13 +118,13 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
120 "gaps inner|outer <amount>"); 118 "gaps inner|outer <amount>");
121 } 119 }
122 return cmd_results_new(CMD_INVALID, "gaps", 120 return cmd_results_new(CMD_INVALID, "gaps",
123 "gaps inner|outer all|workspace|current set|plus|minus <amount>"); 121 "gaps inner|outer all|workspace|current set|plus|minus <amount>");
124 } 122 }
125 123
126 if (amount_idx == 0) { // gaps <amount> 124 if (amount_idx == 0) { // gaps <amount>
127 config->gaps_inner = val; 125 config->gaps_inner = val;
128 config->gaps_outer = val; 126 config->gaps_outer = val;
129 arrange_root(); 127 arrange_and_commit(&root_container);
130 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 128 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
131 } 129 }
132 // Other variants. The middle-length variant (gaps inner|outer <amount>) 130 // Other variants. The middle-length variant (gaps inner|outer <amount>)
@@ -150,14 +148,14 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
150 break; 148 break;
151 } 149 }
152 } 150 }
153 151
154 if (scope == GAPS_SCOPE_ALL) { 152 if (scope == GAPS_SCOPE_ALL) {
155 if (inner) { 153 if (inner) {
156 config->gaps_inner = total; 154 config->gaps_inner = total;
157 } else { 155 } else {
158 config->gaps_outer = total; 156 config->gaps_outer = total;
159 } 157 }
160 arrange_root(); 158 arrange_and_commit(&root_container);
161 } else { 159 } else {
162 struct sway_container *c = 160 struct sway_container *c =
163 config->handler_context.current_container; 161 config->handler_context.current_container;
@@ -171,11 +169,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
171 c->gaps_outer = total; 169 c->gaps_outer = total;
172 } 170 }
173 171
174 if (c->parent) { 172 arrange_and_commit(c->parent ? c->parent : &root_container);
175 arrange_children_of(c->parent);
176 } else {
177 arrange_root();
178 }
179 } 173 }
180 } 174 }
181 175
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index a009e38f..9945fa5c 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_children_of(parent); 52 arrange_and_commit(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 dc9a6f6f..2c9fb77a 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -5,8 +5,10 @@
5#include <wlr/types/wlr_output_layout.h> 5#include <wlr/types/wlr_output_layout.h>
6#include <wlr/util/log.h> 6#include <wlr/util/log.h>
7#include "sway/commands.h" 7#include "sway/commands.h"
8#include "sway/desktop/transaction.h"
8#include "sway/input/seat.h" 9#include "sway/input/seat.h"
9#include "sway/output.h" 10#include "sway/output.h"
11#include "sway/tree/arrange.h"
10#include "sway/tree/container.h" 12#include "sway/tree/container.h"
11#include "sway/tree/layout.h" 13#include "sway/tree/layout.h"
12#include "sway/tree/workspace.h" 14#include "sway/tree/workspace.h"
@@ -96,6 +98,12 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
96 seat_set_focus(config->handler_context.seat, focus); 98 seat_set_focus(config->handler_context.seat, focus);
97 container_reap_empty(old_parent); 99 container_reap_empty(old_parent);
98 container_reap_empty(destination->parent); 100 container_reap_empty(destination->parent);
101
102 struct sway_transaction *txn = transaction_create();
103 arrange_windows(old_parent, txn);
104 arrange_windows(destination->parent, txn);
105 transaction_commit(txn);
106
99 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 107 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
100 } else if (strcasecmp(argv[1], "to") == 0 108 } else if (strcasecmp(argv[1], "to") == 0
101 && strcasecmp(argv[2], "output") == 0) { 109 && strcasecmp(argv[2], "output") == 0) {
@@ -125,6 +133,12 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
125 seat_set_focus(config->handler_context.seat, old_parent); 133 seat_set_focus(config->handler_context.seat, old_parent);
126 container_reap_empty(old_parent); 134 container_reap_empty(old_parent);
127 container_reap_empty(focus->parent); 135 container_reap_empty(focus->parent);
136
137 struct sway_transaction *txn = transaction_create();
138 arrange_windows(old_parent, txn);
139 arrange_windows(focus->parent, txn);
140 transaction_commit(txn);
141
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 142 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 } 143 }
130 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 144 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
@@ -152,9 +166,28 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
152 current = container_parent(current, C_WORKSPACE); 166 current = container_parent(current, C_WORKSPACE);
153 } 167 }
154 container_move_to(current, destination); 168 container_move_to(current, destination);
169
170 struct sway_transaction *txn = transaction_create();
171 arrange_windows(source, txn);
172 arrange_windows(destination, txn);
173 transaction_commit(txn);
174
155 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 175 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
156} 176}
157 177
178static void move_in_direction(struct sway_container *container,
179 enum wlr_direction direction, int move_amt) {
180 struct sway_container *old_parent = container->parent;
181 container_move(container, direction, move_amt);
182
183 struct sway_transaction *txn = transaction_create();
184 arrange_windows(old_parent, txn);
185 if (container->parent != old_parent) {
186 arrange_windows(container->parent, txn);
187 }
188 transaction_commit(txn);
189}
190
158struct cmd_results *cmd_move(int argc, char **argv) { 191struct cmd_results *cmd_move(int argc, char **argv) {
159 struct cmd_results *error = NULL; 192 struct cmd_results *error = NULL;
160 int move_amt = 10; 193 int move_amt = 10;
@@ -173,13 +206,13 @@ struct cmd_results *cmd_move(int argc, char **argv) {
173 } 206 }
174 207
175 if (strcasecmp(argv[0], "left") == 0) { 208 if (strcasecmp(argv[0], "left") == 0) {
176 container_move(current, MOVE_LEFT, move_amt); 209 move_in_direction(current, MOVE_LEFT, move_amt);
177 } else if (strcasecmp(argv[0], "right") == 0) { 210 } else if (strcasecmp(argv[0], "right") == 0) {
178 container_move(current, MOVE_RIGHT, move_amt); 211 move_in_direction(current, MOVE_RIGHT, move_amt);
179 } else if (strcasecmp(argv[0], "up") == 0) { 212 } else if (strcasecmp(argv[0], "up") == 0) {
180 container_move(current, MOVE_UP, move_amt); 213 move_in_direction(current, MOVE_UP, move_amt);
181 } else if (strcasecmp(argv[0], "down") == 0) { 214 } else if (strcasecmp(argv[0], "down") == 0) {
182 container_move(current, MOVE_DOWN, move_amt); 215 move_in_direction(current, MOVE_DOWN, move_amt);
183 } else if (strcasecmp(argv[0], "container") == 0 216 } else if (strcasecmp(argv[0], "container") == 0
184 || strcasecmp(argv[0], "window") == 0) { 217 || strcasecmp(argv[0], "window") == 0) {
185 return cmd_move_container(current, argc, argv); 218 return cmd_move_container(current, argc, argv);
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index 092dd46f..9fc213c4 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_root(); 15 arrange_and_commit(&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 29637953..6357343e 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -182,7 +182,7 @@ static void resize_tiled(int amount, enum resize_axis axis) {
182 } 182 }
183 } 183 }
184 184
185 arrange_children_of(parent->parent); 185 arrange_and_commit(parent->parent);
186} 186}
187 187
188static void resize(int amount, enum resize_axis axis, enum resize_unit unit) { 188static void resize(int amount, enum resize_axis axis, enum resize_unit unit) {
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index 38700d65..f687e78e 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -16,13 +16,14 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
16 16
17 if (strcmp(argv[0], "on") == 0) { 17 if (strcmp(argv[0], "on") == 0) {
18 config->smart_gaps = true; 18 config->smart_gaps = true;
19 arrange_root();
20 } else if (strcmp(argv[0], "off") == 0) { 19 } else if (strcmp(argv[0], "off") == 0) {
21 config->smart_gaps = false; 20 config->smart_gaps = false;
22 arrange_root();
23 } else { 21 } else {
24 return cmd_results_new(CMD_INVALID, "smart_gaps", 22 return cmd_results_new(CMD_INVALID, "smart_gaps",
25 "Expected 'smart_gaps <on|off>' "); 23 "Expected 'smart_gaps <on|off>' ");
26 } 24 }
25
26 arrange_and_commit(&root_container);
27
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28} 29}
diff --git a/sway/commands/split.c b/sway/commands/split.c
index 57e42a5a..c40f4d9f 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_children_of(parent); 19 arrange_and_commit(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 e8dfc57f..e052058f 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,6 +1,8 @@
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/layout.h" 6#include "sway/tree/layout.h"
5#include "sway/tree/view.h" 7#include "sway/tree/view.h"
6#include "stringop.h" 8#include "stringop.h"
@@ -76,5 +78,15 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
76 } 78 }
77 79
78 container_swap(current, other); 80 container_swap(current, other);
81
82 struct sway_transaction *txn = transaction_create();
83 arrange_windows(current->parent, txn);
84
85 if (other->parent != current->parent) {
86 arrange_windows(other->parent, txn);
87 }
88
89 transaction_commit(txn);
90
79 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 91 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
80} 92}
diff --git a/sway/config.c b/sway/config.c
index 949c5cd3..12a02163 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -531,7 +531,7 @@ static int detect_brace_on_following_line(FILE *file, char *line,
531 } while (peeked && strlen(peeked) == 0); 531 } while (peeked && strlen(peeked) == 0);
532 532
533 if (peeked && strlen(peeked) == 1 && peeked[0] == '{') { 533 if (peeked && strlen(peeked) == 1 && peeked[0] == '{') {
534 fseek(file, position, SEEK_SET); 534 fseek(file, position, SEEK_SET);
535 } else { 535 } else {
536 lines = 0; 536 lines = 0;
537 } 537 }
@@ -735,6 +735,6 @@ void config_update_font_height(bool recalculate) {
735 } 735 }
736 736
737 if (config->font_height != prev_max_height) { 737 if (config->font_height != prev_max_height) {
738 arrange_root(); 738 arrange_and_commit(&root_container);
739 } 739 }
740} 740}
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,
7 for (int i = 0; i < root_container.children->length; ++i) { 7 for (int i = 0; i < root_container.children->length; ++i) {
8 struct sway_container *cont = root_container.children->items[i]; 8 struct sway_container *cont = root_container.children->items[i];
9 if (cont->type == C_OUTPUT) { 9 if (cont->type == C_OUTPUT) {
10 output_damage_surface(cont->sway_output, lx - cont->x, ly - cont->y, 10 output_damage_surface(cont->sway_output,
11 lx - cont->current.swayc_x, ly - cont->current.swayc_y,
11 surface, whole); 12 surface, whole);
12 } 13 }
13 } 14 }
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) {
176 sizeof(struct wlr_box)) != 0) { 176 sizeof(struct wlr_box)) != 0) {
177 wlr_log(L_DEBUG, "Usable area changed, rearranging output"); 177 wlr_log(L_DEBUG, "Usable area changed, rearranging output");
178 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 178 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
179 arrange_output(output->swayc); 179 arrange_and_commit(output->swayc);
180 } 180 }
181 181
182 // Arrange non-exlusive surfaces from top->bottom 182 // Arrange non-exlusive surfaces from top->bottom
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index d4115be8..9db95ef5 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -6,6 +6,7 @@
6#include <wayland-server.h> 6#include <wayland-server.h>
7#include <wlr/render/wlr_renderer.h> 7#include <wlr/render/wlr_renderer.h>
8#include <wlr/types/wlr_box.h> 8#include <wlr/types/wlr_box.h>
9#include <wlr/types/wlr_buffer.h>
9#include <wlr/types/wlr_matrix.h> 10#include <wlr/types/wlr_matrix.h>
10#include <wlr/types/wlr_output_damage.h> 11#include <wlr/types/wlr_output_damage.h>
11#include <wlr/types/wlr_output_layout.h> 12#include <wlr/types/wlr_output_layout.h>
@@ -69,6 +70,7 @@ struct render_data {
69 struct root_geometry root_geo; 70 struct root_geometry root_geo;
70 struct sway_output *output; 71 struct sway_output *output;
71 pixman_region32_t *damage; 72 pixman_region32_t *damage;
73 struct sway_view *view;
72 float alpha; 74 float alpha;
73}; 75};
74 76
@@ -100,8 +102,8 @@ static bool get_surface_box(struct root_geometry *geo,
100 wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); 102 wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box);
101 103
102 struct wlr_box output_box = { 104 struct wlr_box output_box = {
103 .width = output->swayc->width, 105 .width = output->swayc->current.swayc_width,
104 .height = output->swayc->height, 106 .height = output->swayc->current.swayc_height,
105 }; 107 };
106 108
107 struct wlr_box intersection; 109 struct wlr_box intersection;
@@ -124,10 +126,10 @@ static void output_view_for_each_surface(struct sway_view *view,
124 struct root_geometry *geo, wlr_surface_iterator_func_t iterator, 126 struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
125 void *user_data) { 127 void *user_data) {
126 struct render_data *data = user_data; 128 struct render_data *data = user_data;
127 geo->x = view->x - data->output->swayc->x; 129 geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x;
128 geo->y = view->y - data->output->swayc->y; 130 geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y;
129 geo->width = view->surface->current->width; 131 geo->width = view->swayc->current.view_width;
130 geo->height = view->surface->current->height; 132 geo->height = view->swayc->current.view_height;
131 geo->rotation = 0; // TODO 133 geo->rotation = 0; // TODO
132 134
133 view_for_each_surface(view, iterator, user_data); 135 view_for_each_surface(view, iterator, user_data);
@@ -153,8 +155,8 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged,
153 wl_list_for_each(unmanaged_surface, unmanaged, link) { 155 wl_list_for_each(unmanaged_surface, unmanaged, link) {
154 struct wlr_xwayland_surface *xsurface = 156 struct wlr_xwayland_surface *xsurface =
155 unmanaged_surface->wlr_xwayland_surface; 157 unmanaged_surface->wlr_xwayland_surface;
156 double ox = unmanaged_surface->lx - output->swayc->x; 158 double ox = unmanaged_surface->lx - output->swayc->current.swayc_x;
157 double oy = unmanaged_surface->ly - output->swayc->y; 159 double oy = unmanaged_surface->ly - output->swayc->current.swayc_y;
158 160
159 surface_for_each_surface(xsurface->surface, ox, oy, geo, 161 surface_for_each_surface(xsurface->surface, ox, oy, geo,
160 iterator, user_data); 162 iterator, user_data);
@@ -241,13 +243,13 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
241 float alpha = data->alpha; 243 float alpha = data->alpha;
242 244
243 struct wlr_texture *texture = wlr_surface_get_texture(surface); 245 struct wlr_texture *texture = wlr_surface_get_texture(surface);
244 if (texture == NULL) { 246 if (!texture) {
245 return; 247 return;
246 } 248 }
247 249
248 struct wlr_box box; 250 struct wlr_box box;
249 bool intersects = get_surface_box(&data->root_geo, data->output, surface, 251 bool intersects = get_surface_box(&data->root_geo, data->output, surface,
250 sx, sy, &box); 252 sx, sy, &box);
251 if (!intersects) { 253 if (!intersects) {
252 return; 254 return;
253 } 255 }
@@ -341,64 +343,105 @@ static void render_view_surfaces(struct sway_view *view,
341 struct render_data data = { 343 struct render_data data = {
342 .output = output, 344 .output = output,
343 .damage = damage, 345 .damage = damage,
346 .view = view,
344 .alpha = alpha, 347 .alpha = alpha,
345 }; 348 };
346 output_view_for_each_surface( 349 output_view_for_each_surface(
347 view, &data.root_geo, render_surface_iterator, &data); 350 view, &data.root_geo, render_surface_iterator, &data);
348} 351}
349 352
353static void render_saved_view(struct sway_view *view,
354 struct sway_output *output, pixman_region32_t *damage, float alpha) {
355 struct wlr_output *wlr_output = output->wlr_output;
356
357 struct wlr_texture *texture = transaction_get_texture(view);
358 if (!texture) {
359 return;
360 }
361 struct wlr_box box = {
362 .x = view->swayc->current.view_x - output->swayc->current.swayc_x,
363 .y = view->swayc->current.view_y - output->swayc->current.swayc_y,
364 .width = view->swayc->current.view_width,
365 .height = view->swayc->current.view_height,
366 };
367
368 struct wlr_box output_box = {
369 .width = output->swayc->current.swayc_width,
370 .height = output->swayc->current.swayc_height,
371 };
372
373 struct wlr_box intersection;
374 bool intersects = wlr_box_intersection(&output_box, &box, &intersection);
375 if (!intersects) {
376 return;
377 }
378
379 scale_box(&box, wlr_output->scale);
380
381 float matrix[9];
382 wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
383 wlr_output->transform_matrix);
384
385 render_texture(wlr_output, damage, texture, &box, matrix, alpha);
386}
387
350/** 388/**
351 * Render a view's surface and left/bottom/right borders. 389 * Render a view's surface and left/bottom/right borders.
352 */ 390 */
353static void render_view(struct sway_output *output, pixman_region32_t *damage, 391static void render_view(struct sway_output *output, pixman_region32_t *damage,
354 struct sway_container *con, struct border_colors *colors) { 392 struct sway_container *con, struct border_colors *colors) {
355 struct sway_view *view = con->sway_view; 393 struct sway_view *view = con->sway_view;
356 render_view_surfaces(view, output, damage, view->swayc->alpha); 394 if (view->swayc->instructions->length) {
395 render_saved_view(view, output, damage, view->swayc->alpha);
396 } else {
397 render_view_surfaces(view, output, damage, view->swayc->alpha);
398 }
357 399
358 struct wlr_box box; 400 struct wlr_box box;
359 float output_scale = output->wlr_output->scale; 401 float output_scale = output->wlr_output->scale;
360 float color[4]; 402 float color[4];
403 struct sway_container_state *state = &con->current;
361 404
362 if (view->border != B_NONE) { 405 if (state->border != B_NONE) {
363 if (view->border_left) { 406 if (state->border_left) {
364 memcpy(&color, colors->child_border, sizeof(float) * 4); 407 memcpy(&color, colors->child_border, sizeof(float) * 4);
365 premultiply_alpha(color, con->alpha); 408 premultiply_alpha(color, con->alpha);
366 box.x = con->x; 409 box.x = state->swayc_x;
367 box.y = view->y; 410 box.y = state->view_y;
368 box.width = view->border_thickness; 411 box.width = state->border_thickness;
369 box.height = view->height; 412 box.height = state->view_height;
370 scale_box(&box, output_scale); 413 scale_box(&box, output_scale);
371 render_rect(output->wlr_output, damage, &box, color); 414 render_rect(output->wlr_output, damage, &box, color);
372 } 415 }
373 416
374 if (view->border_right) { 417 if (state->border_right) {
375 if (con->parent->children->length == 1 418 if (state->parent->current.children->length == 1
376 && con->parent->layout == L_HORIZ) { 419 && state->parent->current.layout == L_HORIZ) {
377 memcpy(&color, colors->indicator, sizeof(float) * 4); 420 memcpy(&color, colors->indicator, sizeof(float) * 4);
378 } else { 421 } else {
379 memcpy(&color, colors->child_border, sizeof(float) * 4); 422 memcpy(&color, colors->child_border, sizeof(float) * 4);
380 } 423 }
381 premultiply_alpha(color, con->alpha); 424 premultiply_alpha(color, con->alpha);
382 box.x = view->x + view->width; 425 box.x = state->view_x + state->view_width;
383 box.y = view->y; 426 box.y = state->view_y;
384 box.width = view->border_thickness; 427 box.width = state->border_thickness;
385 box.height = view->height; 428 box.height = state->view_height;
386 scale_box(&box, output_scale); 429 scale_box(&box, output_scale);
387 render_rect(output->wlr_output, damage, &box, color); 430 render_rect(output->wlr_output, damage, &box, color);
388 } 431 }
389 432
390 if (view->border_bottom) { 433 if (state->border_bottom) {
391 if (con->parent->children->length == 1 434 if (state->parent->current.children->length == 1
392 && con->parent->layout == L_VERT) { 435 && con->current.parent->current.layout == L_VERT) {
393 memcpy(&color, colors->indicator, sizeof(float) * 4); 436 memcpy(&color, colors->indicator, sizeof(float) * 4);
394 } else { 437 } else {
395 memcpy(&color, colors->child_border, sizeof(float) * 4); 438 memcpy(&color, colors->child_border, sizeof(float) * 4);
396 } 439 }
397 premultiply_alpha(color, con->alpha); 440 premultiply_alpha(color, con->alpha);
398 box.x = con->x; 441 box.x = state->swayc_x;
399 box.y = view->y + view->height; 442 box.y = state->view_y + state->view_height;
400 box.width = con->width; 443 box.width = state->swayc_width;
401 box.height = view->border_thickness; 444 box.height = state->border_thickness;
402 scale_box(&box, output_scale); 445 scale_box(&box, output_scale);
403 render_rect(output->wlr_output, damage, &box, color); 446 render_rect(output->wlr_output, damage, &box, color);
404 } 447 }
@@ -422,11 +465,13 @@ static void render_titlebar(struct sway_output *output,
422 struct wlr_texture *marks_texture) { 465 struct wlr_texture *marks_texture) {
423 struct wlr_box box; 466 struct wlr_box box;
424 float color[4]; 467 float color[4];
425 struct sway_view *view = con->type == C_VIEW ? con->sway_view : NULL; 468 struct sway_container_state *state = &con->current;
426 float output_scale = output->wlr_output->scale; 469 float output_scale = output->wlr_output->scale;
427 enum sway_container_layout layout = con->parent->layout; 470 enum sway_container_layout layout = state->parent->current.layout;
428 bool is_last_child = 471 list_t *children = state->parent->current.children;
429 con->parent->children->items[con->parent->children->length - 1] == con; 472 bool is_last_child = children->items[children->length - 1] == con;
473 double output_x = output->swayc->current.swayc_x;
474 double output_y = output->swayc->current.swayc_y;
430 475
431 // Single pixel bar above title 476 // Single pixel bar above title
432 memcpy(&color, colors->border, sizeof(float) * 4); 477 memcpy(&color, colors->border, sizeof(float) * 4);
@@ -443,9 +488,9 @@ static void render_titlebar(struct sway_output *output,
443 bool connects_sides = false; 488 bool connects_sides = false;
444 if (layout == L_HORIZ || layout == L_VERT || 489 if (layout == L_HORIZ || layout == L_VERT ||
445 (layout == L_STACKED && is_last_child)) { 490 (layout == L_STACKED && is_last_child)) {
446 if (view) { 491 if (con->type == C_VIEW) {
447 left_offset = view->border_left * view->border_thickness; 492 left_offset = state->border_left * state->border_thickness;
448 right_offset = view->border_right * view->border_thickness; 493 right_offset = state->border_right * state->border_thickness;
449 connects_sides = true; 494 connects_sides = true;
450 } 495 }
451 } 496 }
@@ -479,10 +524,9 @@ static void render_titlebar(struct sway_output *output,
479 struct wlr_box texture_box; 524 struct wlr_box texture_box;
480 wlr_texture_get_size(marks_texture, 525 wlr_texture_get_size(marks_texture,
481 &texture_box.width, &texture_box.height); 526 &texture_box.width, &texture_box.height);
482 texture_box.x = (x - output->swayc->x + width - TITLEBAR_H_PADDING) 527 texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING)
483 * output_scale - texture_box.width; 528 * output_scale - texture_box.width;
484 texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING) 529 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
485 * output_scale;
486 530
487 float matrix[9]; 531 float matrix[9];
488 wlr_matrix_project_box(matrix, &texture_box, 532 wlr_matrix_project_box(matrix, &texture_box,
@@ -503,10 +547,8 @@ static void render_titlebar(struct sway_output *output,
503 struct wlr_box texture_box; 547 struct wlr_box texture_box;
504 wlr_texture_get_size(title_texture, 548 wlr_texture_get_size(title_texture,
505 &texture_box.width, &texture_box.height); 549 &texture_box.width, &texture_box.height);
506 texture_box.x = (x - output->swayc->x + TITLEBAR_H_PADDING) 550 texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale;
507 * output_scale; 551 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
508 texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING)
509 * output_scale;
510 552
511 float matrix[9]; 553 float matrix[9];
512 wlr_matrix_project_box(matrix, &texture_box, 554 wlr_matrix_project_box(matrix, &texture_box,
@@ -566,15 +608,15 @@ static void render_titlebar(struct sway_output *output,
566 // Left pixel in line with bottom bar 608 // Left pixel in line with bottom bar
567 box.x = x; 609 box.x = x;
568 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 610 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
569 box.width = view->border_thickness * view->border_left; 611 box.width = state->border_thickness * state->border_left;
570 box.height = TITLEBAR_BORDER_THICKNESS; 612 box.height = TITLEBAR_BORDER_THICKNESS;
571 scale_box(&box, output_scale); 613 scale_box(&box, output_scale);
572 render_rect(output->wlr_output, output_damage, &box, color); 614 render_rect(output->wlr_output, output_damage, &box, color);
573 615
574 // Right pixel in line with bottom bar 616 // Right pixel in line with bottom bar
575 box.x = x + width - view->border_thickness * view->border_right; 617 box.x = x + width - state->border_thickness * state->border_right;
576 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 618 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
577 box.width = view->border_thickness * view->border_right; 619 box.width = state->border_thickness * state->border_right;
578 box.height = TITLEBAR_BORDER_THICKNESS; 620 box.height = TITLEBAR_BORDER_THICKNESS;
579 scale_box(&box, output_scale); 621 scale_box(&box, output_scale);
580 render_rect(output->wlr_output, output_damage, &box, color); 622 render_rect(output->wlr_output, output_damage, &box, color);
@@ -587,8 +629,8 @@ static void render_titlebar(struct sway_output *output,
587static void render_top_border(struct sway_output *output, 629static void render_top_border(struct sway_output *output,
588 pixman_region32_t *output_damage, struct sway_container *con, 630 pixman_region32_t *output_damage, struct sway_container *con,
589 struct border_colors *colors) { 631 struct border_colors *colors) {
590 struct sway_view *view = con->sway_view; 632 struct sway_container_state *state = &con->current;
591 if (!view->border_top) { 633 if (!state->border_top) {
592 return; 634 return;
593 } 635 }
594 struct wlr_box box; 636 struct wlr_box box;
@@ -598,10 +640,10 @@ static void render_top_border(struct sway_output *output,
598 // Child border - top edge 640 // Child border - top edge
599 memcpy(&color, colors->child_border, sizeof(float) * 4); 641 memcpy(&color, colors->child_border, sizeof(float) * 4);
600 premultiply_alpha(color, con->alpha); 642 premultiply_alpha(color, con->alpha);
601 box.x = con->x; 643 box.x = state->swayc_x;
602 box.y = con->y; 644 box.y = state->swayc_y;
603 box.width = con->width; 645 box.width = state->swayc_width;
604 box.height = view->border_thickness; 646 box.height = state->border_thickness;
605 scale_box(&box, output_scale); 647 scale_box(&box, output_scale);
606 render_rect(output->wlr_output, output_damage, &box, color); 648 render_rect(output->wlr_output, output_damage, &box, color);
607} 649}
@@ -621,30 +663,34 @@ static void render_container_simple(struct sway_output *output,
621 struct sway_seat *seat = input_manager_current_seat(input_manager); 663 struct sway_seat *seat = input_manager_current_seat(input_manager);
622 struct sway_container *focus = seat_get_focus(seat); 664 struct sway_container *focus = seat_get_focus(seat);
623 665
624 for (int i = 0; i < con->children->length; ++i) { 666 for (int i = 0; i < con->current.children->length; ++i) {
625 struct sway_container *child = con->children->items[i]; 667 struct sway_container *child = con->current.children->items[i];
626 668
627 if (child->type == C_VIEW) { 669 if (child->type == C_VIEW) {
670 struct sway_view *view = child->sway_view;
628 struct border_colors *colors; 671 struct border_colors *colors;
629 struct wlr_texture *title_texture; 672 struct wlr_texture *title_texture;
630 struct wlr_texture *marks_texture; 673 struct wlr_texture *marks_texture;
674 struct sway_container_state *state = &child->current;
675
631 if (focus == child || parent_focused) { 676 if (focus == child || parent_focused) {
632 colors = &config->border_colors.focused; 677 colors = &config->border_colors.focused;
633 title_texture = child->title_focused; 678 title_texture = child->title_focused;
634 marks_texture = child->sway_view->marks_focused; 679 marks_texture = view->marks_focused;
635 } else if (seat_get_focus_inactive(seat, con) == child) { 680 } else if (seat_get_focus_inactive(seat, con) == child) {
636 colors = &config->border_colors.focused_inactive; 681 colors = &config->border_colors.focused_inactive;
637 title_texture = child->title_focused_inactive; 682 title_texture = child->title_focused_inactive;
638 marks_texture = child->sway_view->marks_focused_inactive; 683 marks_texture = view->marks_focused_inactive;
639 } else { 684 } else {
640 colors = &config->border_colors.unfocused; 685 colors = &config->border_colors.unfocused;
641 title_texture = child->title_unfocused; 686 title_texture = child->title_unfocused;
642 marks_texture = child->sway_view->marks_unfocused; 687 marks_texture = view->marks_unfocused;
643 } 688 }
644 689
645 if (child->sway_view->border == B_NORMAL) { 690 if (state->border == B_NORMAL) {
646 render_titlebar(output, damage, child, child->x, child->y, 691 render_titlebar(output, damage, child, state->swayc_x,
647 child->width, colors, title_texture, marks_texture); 692 state->swayc_y, state->swayc_width, colors,
693 title_texture, marks_texture);
648 } else { 694 } else {
649 render_top_border(output, damage, child, colors); 695 render_top_border(output, damage, child, colors);
650 } 696 }
@@ -662,22 +708,23 @@ static void render_container_simple(struct sway_output *output,
662static void render_container_tabbed(struct sway_output *output, 708static void render_container_tabbed(struct sway_output *output,
663 pixman_region32_t *damage, struct sway_container *con, 709 pixman_region32_t *damage, struct sway_container *con,
664 bool parent_focused) { 710 bool parent_focused) {
665 if (!con->children->length) { 711 if (!con->current.children->length) {
666 return; 712 return;
667 } 713 }
668 struct sway_seat *seat = input_manager_current_seat(input_manager); 714 struct sway_seat *seat = input_manager_current_seat(input_manager);
669 struct sway_container *focus = seat_get_focus(seat); 715 struct sway_container *focus = seat_get_focus(seat);
670 struct sway_container *current = seat_get_active_child(seat, con); 716 struct sway_container *current = seat_get_active_child(seat, con);
671 struct border_colors *current_colors = NULL; 717 struct border_colors *current_colors = NULL;
718 struct sway_container_state *pstate = &con->current;
672 719
673 // Render tabs 720 // Render tabs
674 for (int i = 0; i < con->children->length; ++i) { 721 for (int i = 0; i < con->current.children->length; ++i) {
675 struct sway_container *child = con->children->items[i]; 722 struct sway_container *child = con->current.children->items[i];
723 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
724 struct sway_container_state *cstate = &child->current;
676 struct border_colors *colors; 725 struct border_colors *colors;
677 struct wlr_texture *title_texture; 726 struct wlr_texture *title_texture;
678 struct wlr_texture *marks_texture; 727 struct wlr_texture *marks_texture;
679 struct sway_view *view =
680 child->type == C_VIEW ? child->sway_view : NULL;
681 728
682 if (focus == child || parent_focused) { 729 if (focus == child || parent_focused) {
683 colors = &config->border_colors.focused; 730 colors = &config->border_colors.focused;
@@ -686,22 +733,22 @@ static void render_container_tabbed(struct sway_output *output,
686 } else if (child == current) { 733 } else if (child == current) {
687 colors = &config->border_colors.focused_inactive; 734 colors = &config->border_colors.focused_inactive;
688 title_texture = child->title_focused_inactive; 735 title_texture = child->title_focused_inactive;
689 marks_texture = view ? view->marks_focused : NULL; 736 marks_texture = view ? view->marks_focused_inactive : NULL;
690 } else { 737 } else {
691 colors = &config->border_colors.unfocused; 738 colors = &config->border_colors.unfocused;
692 title_texture = child->title_unfocused; 739 title_texture = child->title_unfocused;
693 marks_texture = view ? view->marks_unfocused : NULL; 740 marks_texture = view ? view->marks_unfocused : NULL;
694 } 741 }
695 742
696 int tab_width = con->width / con->children->length; 743 int tab_width = pstate->swayc_width / pstate->children->length;
697 int x = con->x + tab_width * i; 744 int x = pstate->swayc_x + tab_width * i;
698 // Make last tab use the remaining width of the parent 745 // Make last tab use the remaining width of the parent
699 if (i == con->children->length - 1) { 746 if (i == pstate->children->length - 1) {
700 tab_width = con->width - tab_width * i; 747 tab_width = pstate->swayc_width - tab_width * i;
701 } 748 }
702 749
703 render_titlebar(output, damage, child, x, child->y, tab_width, colors, 750 render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
704 title_texture, marks_texture); 751 colors, title_texture, marks_texture);
705 752
706 if (child == current) { 753 if (child == current) {
707 current_colors = colors; 754 current_colors = colors;
@@ -723,22 +770,23 @@ static void render_container_tabbed(struct sway_output *output,
723static void render_container_stacked(struct sway_output *output, 770static void render_container_stacked(struct sway_output *output,
724 pixman_region32_t *damage, struct sway_container *con, 771 pixman_region32_t *damage, struct sway_container *con,
725 bool parent_focused) { 772 bool parent_focused) {
726 if (!con->children->length) { 773 if (!con->current.children->length) {
727 return; 774 return;
728 } 775 }
729 struct sway_seat *seat = input_manager_current_seat(input_manager); 776 struct sway_seat *seat = input_manager_current_seat(input_manager);
730 struct sway_container *focus = seat_get_focus(seat); 777 struct sway_container *focus = seat_get_focus(seat);
731 struct sway_container *current = seat_get_active_child(seat, con); 778 struct sway_container *current = seat_get_active_child(seat, con);
732 struct border_colors *current_colors = NULL; 779 struct border_colors *current_colors = NULL;
780 struct sway_container_state *pstate = &con->current;
733 781
734 // Render titles 782 // Render titles
735 for (int i = 0; i < con->children->length; ++i) { 783 for (int i = 0; i < con->current.children->length; ++i) {
736 struct sway_container *child = con->children->items[i]; 784 struct sway_container *child = con->current.children->items[i];
785 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
786 struct sway_container_state *cstate = &child->current;
737 struct border_colors *colors; 787 struct border_colors *colors;
738 struct wlr_texture *title_texture; 788 struct wlr_texture *title_texture;
739 struct wlr_texture *marks_texture; 789 struct wlr_texture *marks_texture;
740 struct sway_view *view =
741 child->type == C_VIEW ? child->sway_view : NULL;
742 790
743 if (focus == child || parent_focused) { 791 if (focus == child || parent_focused) {
744 colors = &config->border_colors.focused; 792 colors = &config->border_colors.focused;
@@ -754,9 +802,9 @@ static void render_container_stacked(struct sway_output *output,
754 marks_texture = view ? view->marks_unfocused : NULL; 802 marks_texture = view ? view->marks_unfocused : NULL;
755 } 803 }
756 804
757 int y = con->y + container_titlebar_height() * i; 805 int y = pstate->swayc_y + container_titlebar_height() * i;
758 render_titlebar(output, damage, child, child->x, y, child->width, 806 render_titlebar(output, damage, child, cstate->swayc_x, y,
759 colors, title_texture, marks_texture); 807 cstate->swayc_width, colors, title_texture, marks_texture);
760 808
761 if (child == current) { 809 if (child == current) {
762 current_colors = colors; 810 current_colors = colors;
@@ -775,7 +823,7 @@ static void render_container_stacked(struct sway_output *output,
775static void render_container(struct sway_output *output, 823static void render_container(struct sway_output *output,
776 pixman_region32_t *damage, struct sway_container *con, 824 pixman_region32_t *damage, struct sway_container *con,
777 bool parent_focused) { 825 bool parent_focused) {
778 switch (con->layout) { 826 switch (con->current.layout) {
779 case L_NONE: 827 case L_NONE:
780 case L_HORIZ: 828 case L_HORIZ:
781 case L_VERT: 829 case L_VERT:
@@ -812,9 +860,10 @@ static void render_floating_container(struct sway_output *soutput,
812 marks_texture = view->marks_unfocused; 860 marks_texture = view->marks_unfocused;
813 } 861 }
814 862
815 if (con->sway_view->border == B_NORMAL) { 863 if (con->current.border == B_NORMAL) {
816 render_titlebar(soutput, damage, con, con->x, con->y, con->width, 864 render_titlebar(soutput, damage, con, con->current.swayc_x,
817 colors, title_texture, marks_texture); 865 con->current.swayc_y, con->current.swayc_width, colors,
866 title_texture, marks_texture);
818 } else { 867 } else {
819 render_top_border(soutput, damage, con, colors); 868 render_top_border(soutput, damage, con, colors);
820 } 869 }
@@ -826,17 +875,18 @@ static void render_floating_container(struct sway_output *soutput,
826 875
827static void render_floating(struct sway_output *soutput, 876static void render_floating(struct sway_output *soutput,
828 pixman_region32_t *damage) { 877 pixman_region32_t *damage) {
829 for (int i = 0; i < root_container.children->length; ++i) { 878 for (int i = 0; i < root_container.current.children->length; ++i) {
830 struct sway_container *output = root_container.children->items[i]; 879 struct sway_container *output =
831 for (int j = 0; j < output->children->length; ++j) { 880 root_container.current.children->items[i];
832 struct sway_container *workspace = output->children->items[j]; 881 for (int j = 0; j < output->current.children->length; ++j) {
833 struct sway_workspace *ws = workspace->sway_workspace; 882 struct sway_container *ws = output->current.children->items[j];
834 if (!workspace_is_visible(workspace)) { 883 if (!workspace_is_visible(ws)) {
835 continue; 884 continue;
836 } 885 }
837 for (int k = 0; k < ws->floating->children->length; ++k) { 886 list_t *floating =
838 struct sway_container *floater = 887 ws->current.ws_floating->current.children;
839 ws->floating->children->items[k]; 888 for (int k = 0; k < floating->length; ++k) {
889 struct sway_container *floater = floating->items[k];
840 render_floating_container(soutput, damage, floater); 890 render_floating_container(soutput, damage, floater);
841 } 891 }
842 } 892 }
@@ -850,7 +900,7 @@ static struct sway_container *output_get_active_workspace(
850 seat_get_focus_inactive(seat, output->swayc); 900 seat_get_focus_inactive(seat, output->swayc);
851 if (!focus) { 901 if (!focus) {
852 // We've never been to this output before 902 // We've never been to this output before
853 focus = output->swayc->children->items[0]; 903 focus = output->swayc->current.children->items[0];
854 } 904 }
855 struct sway_container *workspace = focus; 905 struct sway_container *workspace = focus;
856 if (workspace->type != C_WORKSPACE) { 906 if (workspace->type != C_WORKSPACE) {
@@ -891,8 +941,9 @@ static void render_output(struct sway_output *output, struct timespec *when,
891 } 941 }
892 942
893 struct sway_container *workspace = output_get_active_workspace(output); 943 struct sway_container *workspace = output_get_active_workspace(output);
944 struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
894 945
895 if (workspace->sway_workspace->fullscreen) { 946 if (fullscreen_view) {
896 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; 947 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
897 948
898 int nrects; 949 int nrects;
@@ -903,10 +954,9 @@ static void render_output(struct sway_output *output, struct timespec *when,
903 } 954 }
904 955
905 // TODO: handle views smaller than the output 956 // TODO: handle views smaller than the output
906 render_view_surfaces( 957 render_view_surfaces(fullscreen_view, output, damage, 1.0f);
907 workspace->sway_workspace->fullscreen, output, damage, 1.0f);
908 958
909 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 959 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
910 render_unmanaged(output, damage, 960 render_unmanaged(output, damage,
911 &root_container.sway_root->xwayland_unmanaged); 961 &root_container.sway_root->xwayland_unmanaged);
912 } 962 }
@@ -1022,11 +1072,11 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
1022 }; 1072 };
1023 1073
1024 struct sway_container *workspace = output_get_active_workspace(output); 1074 struct sway_container *workspace = output_get_active_workspace(output);
1025 if (workspace->sway_workspace->fullscreen) { 1075 if (workspace->current.ws_fullscreen) {
1026 send_frame_done_container_iterator( 1076 send_frame_done_container_iterator(
1027 workspace->sway_workspace->fullscreen->swayc, &data); 1077 workspace->current.ws_fullscreen->swayc, &data);
1028 1078
1029 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 1079 if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) {
1030 send_frame_done_unmanaged(&data, 1080 send_frame_done_unmanaged(&data,
1031 &root_container.sway_root->xwayland_unmanaged); 1081 &root_container.sway_root->xwayland_unmanaged);
1032 } 1082 }
@@ -1168,6 +1218,16 @@ void output_damage_from_view(struct sway_output *output,
1168 output_damage_view(output, view, false); 1218 output_damage_view(output, view, false);
1169} 1219}
1170 1220
1221// Expecting an unscaled box in layout coordinates
1222void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
1223 struct wlr_box box;
1224 memcpy(&box, _box, sizeof(struct wlr_box));
1225 box.x -= output->swayc->current.swayc_x;
1226 box.y -= output->swayc->current.swayc_y;
1227 scale_box(&box, output->wlr_output->scale);
1228 wlr_output_damage_add_box(output->damage, &box);
1229}
1230
1171static void output_damage_whole_container_iterator(struct sway_container *con, 1231static void output_damage_whole_container_iterator(struct sway_container *con,
1172 void *data) { 1232 void *data) {
1173 struct sway_output *output = data; 1233 struct sway_output *output = data;
@@ -1182,10 +1242,10 @@ static void output_damage_whole_container_iterator(struct sway_container *con,
1182void output_damage_whole_container(struct sway_output *output, 1242void output_damage_whole_container(struct sway_output *output,
1183 struct sway_container *con) { 1243 struct sway_container *con) {
1184 struct wlr_box box = { 1244 struct wlr_box box = {
1185 .x = con->x - output->wlr_output->lx, 1245 .x = con->current.swayc_x - output->wlr_output->lx,
1186 .y = con->y - output->wlr_output->ly, 1246 .y = con->current.swayc_y - output->wlr_output->ly,
1187 .width = con->width, 1247 .width = con->current.swayc_width,
1188 .height = con->height, 1248 .height = con->current.swayc_height,
1189 }; 1249 };
1190 scale_box(&box, output->wlr_output->scale); 1250 scale_box(&box, output->wlr_output->scale);
1191 wlr_output_damage_add_box(output->damage, &box); 1251 wlr_output_damage_add_box(output->damage, &box);
@@ -1212,13 +1272,13 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
1212static void handle_mode(struct wl_listener *listener, void *data) { 1272static void handle_mode(struct wl_listener *listener, void *data) {
1213 struct sway_output *output = wl_container_of(listener, output, mode); 1273 struct sway_output *output = wl_container_of(listener, output, mode);
1214 arrange_layers(output); 1274 arrange_layers(output);
1215 arrange_output(output->swayc); 1275 arrange_and_commit(output->swayc);
1216} 1276}
1217 1277
1218static void handle_transform(struct wl_listener *listener, void *data) { 1278static void handle_transform(struct wl_listener *listener, void *data) {
1219 struct sway_output *output = wl_container_of(listener, output, transform); 1279 struct sway_output *output = wl_container_of(listener, output, transform);
1220 arrange_layers(output); 1280 arrange_layers(output);
1221 arrange_output(output->swayc); 1281 arrange_and_commit(output->swayc);
1222} 1282}
1223 1283
1224static void handle_scale_iterator(struct sway_container *view, void *data) { 1284static void handle_scale_iterator(struct sway_container *view, void *data) {
@@ -1228,8 +1288,8 @@ static void handle_scale_iterator(struct sway_container *view, void *data) {
1228static void handle_scale(struct wl_listener *listener, void *data) { 1288static void handle_scale(struct wl_listener *listener, void *data) {
1229 struct sway_output *output = wl_container_of(listener, output, scale); 1289 struct sway_output *output = wl_container_of(listener, output, scale);
1230 arrange_layers(output); 1290 arrange_layers(output);
1231 arrange_output(output->swayc);
1232 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); 1291 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
1292 arrange_and_commit(output->swayc);
1233} 1293}
1234 1294
1235void handle_new_output(struct wl_listener *listener, void *data) { 1295void handle_new_output(struct wl_listener *listener, void *data) {
@@ -1293,5 +1353,5 @@ void output_enable(struct sway_output *output) {
1293 output->damage_destroy.notify = damage_handle_destroy; 1353 output->damage_destroy.notify = damage_handle_destroy;
1294 1354
1295 arrange_layers(output); 1355 arrange_layers(output);
1296 arrange_root(); 1356 arrange_and_commit(&root_container);
1297} 1357}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
new file mode 100644
index 00000000..6e09537a
--- /dev/null
+++ b/sway/desktop/transaction.c
@@ -0,0 +1,292 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h>
3#include <stdlib.h>
4#include <string.h>
5#include <wlr/types/wlr_buffer.h>
6#include <wlr/types/wlr_linux_dmabuf.h>
7#include "sway/debug.h"
8#include "sway/desktop/transaction.h"
9#include "sway/output.h"
10#include "sway/tree/container.h"
11#include "sway/tree/view.h"
12#include "sway/tree/workspace.h"
13#include "list.h"
14#include "log.h"
15
16/**
17 * How long we should wait for views to respond to the configure before giving
18 * up and applying the transaction anyway.
19 */
20#define TIMEOUT_MS 200
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
29struct sway_transaction {
30 struct wl_event_source *timer;
31 list_t *instructions; // struct sway_transaction_instruction *
32 list_t *damage; // struct wlr_box *
33 size_t num_waiting;
34};
35
36struct sway_transaction_instruction {
37 struct sway_transaction *transaction;
38 struct sway_container *container;
39 struct sway_container_state state;
40 struct wlr_buffer *saved_buffer;
41 uint32_t serial;
42 bool ready;
43};
44
45struct sway_transaction *transaction_create() {
46 struct sway_transaction *transaction =
47 calloc(1, sizeof(struct sway_transaction));
48 transaction->instructions = create_list();
49 transaction->damage = create_list();
50 return transaction;
51}
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
73static void transaction_destroy(struct sway_transaction *transaction) {
74 // Free instructions
75 for (int i = 0; i < transaction->instructions->length; ++i) {
76 struct sway_transaction_instruction *instruction =
77 transaction->instructions->items[i];
78 struct sway_container *con = instruction->container;
79 for (int j = 0; j < con->instructions->length; ++j) {
80 if (con->instructions->items[j] == instruction) {
81 list_del(con->instructions, j);
82 break;
83 }
84 }
85 if (con->destroying && !con->instructions->length) {
86 container_free(con);
87 }
88 remove_saved_view_buffer(instruction);
89 free(instruction);
90 }
91 list_free(transaction->instructions);
92
93 // Free damage
94 list_foreach(transaction->damage, free);
95 list_free(transaction->damage);
96
97 free(transaction);
98}
99
100static void copy_pending_state(struct sway_container *container,
101 struct sway_container_state *state) {
102 state->layout = container->layout;
103 state->swayc_x = container->x;
104 state->swayc_y = container->y;
105 state->swayc_width = container->width;
106 state->swayc_height = container->height;
107 state->has_gaps = container->has_gaps;
108 state->current_gaps = container->current_gaps;
109 state->gaps_inner = container->gaps_inner;
110 state->gaps_outer = container->gaps_outer;
111 state->parent = container->parent;
112
113 if (container->type == C_VIEW) {
114 struct sway_view *view = container->sway_view;
115 state->view_x = view->x;
116 state->view_y = view->y;
117 state->view_width = view->width;
118 state->view_height = view->height;
119 state->is_fullscreen = view->is_fullscreen;
120 state->border = view->border;
121 state->border_thickness = view->border_thickness;
122 state->border_top = view->border_top;
123 state->border_left = view->border_left;
124 state->border_right = view->border_right;
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;
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);
160
161 if (container->type == C_VIEW) {
162 save_view_buffer(container->sway_view, instruction);
163 }
164 list_add(transaction->instructions, instruction);
165}
166
167void transaction_add_damage(struct sway_transaction *transaction,
168 struct wlr_box *_box) {
169 struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
170 memcpy(box, _box, sizeof(struct wlr_box));
171 list_add(transaction->damage, box);
172}
173
174/**
175 * Apply a transaction to the "current" state of the tree.
176 */
177static void transaction_apply(struct sway_transaction *transaction) {
178 int i;
179 // Apply the instruction state to the container's current state
180 for (i = 0; i < transaction->instructions->length; ++i) {
181 struct sway_transaction_instruction *instruction =
182 transaction->instructions->items[i];
183 struct sway_container *container = instruction->container;
184
185 // There are separate children lists for each instruction state, the
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);
191
192 memcpy(&container->current, &instruction->state,
193 sizeof(struct sway_container_state));
194 }
195
196 // Apply damage
197 for (i = 0; i < transaction->damage->length; ++i) {
198 struct wlr_box *box = transaction->damage->items[i];
199 for (int j = 0; j < root_container.children->length; ++j) {
200 struct sway_container *output = root_container.children->items[j];
201 output_damage_box(output->sway_output, box);
202 }
203 }
204}
205
206static int handle_timeout(void *data) {
207 struct sway_transaction *transaction = data;
208 wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting), applying anyway",
209 transaction, transaction->num_waiting);
210 transaction_apply(transaction);
211 transaction_destroy(transaction);
212 return 0;
213}
214
215void transaction_commit(struct sway_transaction *transaction) {
216 wlr_log(L_DEBUG, "Transaction %p committing with %i instructions",
217 transaction, transaction->instructions->length);
218 transaction->num_waiting = 0;
219 for (int i = 0; i < transaction->instructions->length; ++i) {
220 struct sway_transaction_instruction *instruction =
221 transaction->instructions->items[i];
222 struct sway_container *con = instruction->container;
223 if (con->type == C_VIEW && !con->destroying &&
224 (con->current.view_width != instruction->state.view_width ||
225 con->current.view_height != instruction->state.view_height)) {
226 instruction->serial = view_configure(con->sway_view,
227 instruction->state.view_x,
228 instruction->state.view_y,
229 instruction->state.view_width,
230 instruction->state.view_height);
231 if (instruction->serial) {
232 ++transaction->num_waiting;
233 }
234 }
235 list_add(con->instructions, instruction);
236 }
237 if (!transaction->num_waiting) {
238 wlr_log(L_DEBUG, "Transaction %p has nothing to wait for, applying",
239 transaction);
240 transaction_apply(transaction);
241 transaction_destroy(transaction);
242 return;
243 }
244
245 // Set up a timer which the views must respond within
246 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
247 handle_timeout, transaction);
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();
254}
255
256void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) {
257 // Find the instruction
258 struct sway_transaction_instruction *instruction = NULL;
259 for (int i = 0; i < view->swayc->instructions->length; ++i) {
260 struct sway_transaction_instruction *tmp_instruction =
261 view->swayc->instructions->items[i];
262 if (tmp_instruction->serial == serial && !tmp_instruction->ready) {
263 instruction = tmp_instruction;
264 break;
265 }
266 }
267 if (!instruction) {
268 return;
269 }
270 instruction->ready = true;
271
272 // If all views are ready, apply the transaction
273 struct sway_transaction *transaction = instruction->transaction;
274 if (--transaction->num_waiting == 0) {
275#if !TRANSACTION_DEBUG
276 wlr_log(L_DEBUG, "Transaction %p is ready, applying", transaction);
277 wl_event_source_timer_update(transaction->timer, 0);
278 transaction_apply(transaction);
279 transaction_destroy(transaction);
280#endif
281 }
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}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index d2b8822c..ab35b98f 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -8,6 +8,7 @@
8#include "sway/input/input-manager.h" 8#include "sway/input/input-manager.h"
9#include "sway/input/seat.h" 9#include "sway/input/seat.h"
10#include "sway/server.h" 10#include "sway/server.h"
11#include "sway/tree/arrange.h"
11#include "sway/tree/container.h" 12#include "sway/tree/container.h"
12#include "sway/tree/layout.h" 13#include "sway/tree/layout.h"
13#include "sway/tree/view.h" 14#include "sway/tree/view.h"
@@ -87,18 +88,14 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p
87 } 88 }
88} 89}
89 90
90static void configure(struct sway_view *view, double lx, double ly, int width, 91static uint32_t configure(struct sway_view *view, double lx, double ly,
91 int height) { 92 int width, int height) {
92 struct sway_xdg_shell_view *xdg_shell_view = 93 struct sway_xdg_shell_view *xdg_shell_view =
93 xdg_shell_view_from_view(view); 94 xdg_shell_view_from_view(view);
94 if (xdg_shell_view == NULL) { 95 if (xdg_shell_view == NULL) {
95 return; 96 return 0;
96 } 97 }
97 98 return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height);
98 xdg_shell_view->pending_width = width;
99 xdg_shell_view->pending_height = height;
100 wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height);
101 view_update_position(view, lx, ly);
102} 99}
103 100
104static void set_activated(struct sway_view *view, bool activated) { 101static void set_activated(struct sway_view *view, bool activated) {
@@ -146,16 +143,12 @@ static void _close(struct sway_view *view) {
146 } 143 }
147} 144}
148 145
149static void destroy(struct sway_view *view) { 146static void _free(struct sway_view *view) {
150 struct sway_xdg_shell_view *xdg_shell_view = 147 struct sway_xdg_shell_view *xdg_shell_view =
151 xdg_shell_view_from_view(view); 148 xdg_shell_view_from_view(view);
152 if (xdg_shell_view == NULL) { 149 if (xdg_shell_view == NULL) {
153 return; 150 return;
154 } 151 }
155 wl_list_remove(&xdg_shell_view->destroy.link);
156 wl_list_remove(&xdg_shell_view->map.link);
157 wl_list_remove(&xdg_shell_view->unmap.link);
158 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
159 free(xdg_shell_view); 152 free(xdg_shell_view);
160} 153}
161 154
@@ -167,25 +160,23 @@ static const struct sway_view_impl view_impl = {
167 .wants_floating = wants_floating, 160 .wants_floating = wants_floating,
168 .for_each_surface = for_each_surface, 161 .for_each_surface = for_each_surface,
169 .close = _close, 162 .close = _close,
170 .destroy = destroy, 163 .free = _free,
171}; 164};
172 165
173static void handle_commit(struct wl_listener *listener, void *data) { 166static void handle_commit(struct wl_listener *listener, void *data) {
174 struct sway_xdg_shell_view *xdg_shell_view = 167 struct sway_xdg_shell_view *xdg_shell_view =
175 wl_container_of(listener, xdg_shell_view, commit); 168 wl_container_of(listener, xdg_shell_view, commit);
176 struct sway_view *view = &xdg_shell_view->view; 169 struct sway_view *view = &xdg_shell_view->view;
177 if (view->swayc && container_is_floating(view->swayc)) { 170 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
178 int width = view->wlr_xdg_surface->geometry.width; 171
179 int height = view->wlr_xdg_surface->geometry.height; 172 if (!view->swayc) {
180 if (!width && !height) { 173 return;
181 width = view->wlr_xdg_surface->surface->current->width;
182 height = view->wlr_xdg_surface->surface->current->height;
183 }
184 view_update_size(view, width, height);
185 } else {
186 view_update_size(view, xdg_shell_view->pending_width,
187 xdg_shell_view->pending_height);
188 } 174 }
175
176 if (view->swayc->instructions->length) {
177 transaction_notify_view_ready(view, xdg_surface->configure_serial);
178 }
179
189 view_update_title(view, false); 180 view_update_title(view, false);
190 view_damage_from(view); 181 view_damage_from(view);
191} 182}
@@ -200,11 +191,18 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
200static void handle_unmap(struct wl_listener *listener, void *data) { 191static void handle_unmap(struct wl_listener *listener, void *data) {
201 struct sway_xdg_shell_view *xdg_shell_view = 192 struct sway_xdg_shell_view *xdg_shell_view =
202 wl_container_of(listener, xdg_shell_view, unmap); 193 wl_container_of(listener, xdg_shell_view, unmap);
194 struct sway_view *view = &xdg_shell_view->view;
203 195
204 view_unmap(&xdg_shell_view->view); 196 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
197 return;
198 }
199
200 struct sway_container *parent = view_unmap(view);
201 arrange_and_commit(parent);
205 202
206 wl_list_remove(&xdg_shell_view->commit.link); 203 wl_list_remove(&xdg_shell_view->commit.link);
207 wl_list_remove(&xdg_shell_view->new_popup.link); 204 wl_list_remove(&xdg_shell_view->new_popup.link);
205 view->surface = NULL;
208} 206}
209 207
210static void handle_map(struct wl_listener *listener, void *data) { 208static void handle_map(struct wl_listener *listener, void *data) {
@@ -219,8 +217,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
219 view->natural_width = view->wlr_xdg_surface->surface->current->width; 217 view->natural_width = view->wlr_xdg_surface->surface->current->width;
220 view->natural_height = view->wlr_xdg_surface->surface->current->height; 218 view->natural_height = view->wlr_xdg_surface->surface->current->height;
221 } 219 }
220
222 view_map(view, view->wlr_xdg_surface->surface); 221 view_map(view, view->wlr_xdg_surface->surface);
223 222
223 if (xdg_surface->toplevel->client_pending.fullscreen) {
224 view_set_fullscreen(view, true);
225 }
226 arrange_and_commit(view->swayc->parent);
227
224 xdg_shell_view->commit.notify = handle_commit; 228 xdg_shell_view->commit.notify = handle_commit;
225 wl_signal_add(&xdg_surface->surface->events.commit, 229 wl_signal_add(&xdg_surface->surface->events.commit,
226 &xdg_shell_view->commit); 230 &xdg_shell_view->commit);
@@ -228,16 +232,22 @@ static void handle_map(struct wl_listener *listener, void *data) {
228 xdg_shell_view->new_popup.notify = handle_new_popup; 232 xdg_shell_view->new_popup.notify = handle_new_popup;
229 wl_signal_add(&xdg_surface->events.new_popup, 233 wl_signal_add(&xdg_surface->events.new_popup,
230 &xdg_shell_view->new_popup); 234 &xdg_shell_view->new_popup);
231
232 if (xdg_surface->toplevel->client_pending.fullscreen) {
233 view_set_fullscreen(view, true);
234 }
235} 235}
236 236
237static void handle_destroy(struct wl_listener *listener, void *data) { 237static void handle_destroy(struct wl_listener *listener, void *data) {
238 struct sway_xdg_shell_view *xdg_shell_view = 238 struct sway_xdg_shell_view *xdg_shell_view =
239 wl_container_of(listener, xdg_shell_view, destroy); 239 wl_container_of(listener, xdg_shell_view, destroy);
240 view_destroy(&xdg_shell_view->view); 240 struct sway_view *view = &xdg_shell_view->view;
241 if (!sway_assert(view->swayc == NULL || view->swayc->destroying,
242 "Tried to destroy a mapped view")) {
243 return;
244 }
245 wl_list_remove(&xdg_shell_view->destroy.link);
246 wl_list_remove(&xdg_shell_view->map.link);
247 wl_list_remove(&xdg_shell_view->unmap.link);
248 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
249 view->wlr_xdg_surface = NULL;
250 view_destroy(view);
241} 251}
242 252
243static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 253static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
@@ -246,6 +256,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
246 struct wlr_xdg_toplevel_set_fullscreen_event *e = data; 256 struct wlr_xdg_toplevel_set_fullscreen_event *e = data;
247 struct wlr_xdg_surface *xdg_surface = 257 struct wlr_xdg_surface *xdg_surface =
248 xdg_shell_view->view.wlr_xdg_surface; 258 xdg_shell_view->view.wlr_xdg_surface;
259 struct sway_view *view = &xdg_shell_view->view;
249 260
250 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, 261 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL,
251 "xdg_shell requested fullscreen of surface with role %i", 262 "xdg_shell requested fullscreen of surface with role %i",
@@ -256,7 +267,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
256 return; 267 return;
257 } 268 }
258 269
259 view_set_fullscreen(&xdg_shell_view->view, e->fullscreen); 270 view_set_fullscreen(view, e->fullscreen);
271
272 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
273 arrange_and_commit(ws);
260} 274}
261 275
262void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 276void 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 6ffe334a..76c1fa24 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -3,9 +3,10 @@
3#include <stdlib.h> 3#include <stdlib.h>
4#include <wayland-server.h> 4#include <wayland-server.h>
5#include <wlr/types/wlr_xdg_shell_v6.h> 5#include <wlr/types/wlr_xdg_shell_v6.h>
6#include "sway/server.h"
7#include "sway/tree/arrange.h"
6#include "sway/tree/container.h" 8#include "sway/tree/container.h"
7#include "sway/tree/layout.h" 9#include "sway/tree/layout.h"
8#include "sway/server.h"
9#include "sway/tree/view.h" 10#include "sway/tree/view.h"
10#include "sway/input/seat.h" 11#include "sway/input/seat.h"
11#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
@@ -86,18 +87,15 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p
86 } 87 }
87} 88}
88 89
89static void configure(struct sway_view *view, double lx, double ly, int width, 90static uint32_t configure(struct sway_view *view, double lx, double ly,
90 int height) { 91 int width, int height) {
91 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 92 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
92 xdg_shell_v6_view_from_view(view); 93 xdg_shell_v6_view_from_view(view);
93 if (xdg_shell_v6_view == NULL) { 94 if (xdg_shell_v6_view == NULL) {
94 return; 95 return 0;
95 } 96 }
96 97 return wlr_xdg_toplevel_v6_set_size(
97 xdg_shell_v6_view->pending_width = width; 98 view->wlr_xdg_surface_v6, width, height);
98 xdg_shell_v6_view->pending_height = height;
99 wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height);
100 view_update_position(view, lx, ly);
101} 99}
102 100
103static void set_activated(struct sway_view *view, bool activated) { 101static void set_activated(struct sway_view *view, bool activated) {
@@ -145,16 +143,12 @@ static void _close(struct sway_view *view) {
145 } 143 }
146} 144}
147 145
148static void destroy(struct sway_view *view) { 146static void _free(struct sway_view *view) {
149 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 147 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
150 xdg_shell_v6_view_from_view(view); 148 xdg_shell_v6_view_from_view(view);
151 if (xdg_shell_v6_view == NULL) { 149 if (xdg_shell_v6_view == NULL) {
152 return; 150 return;
153 } 151 }
154 wl_list_remove(&xdg_shell_v6_view->destroy.link);
155 wl_list_remove(&xdg_shell_v6_view->map.link);
156 wl_list_remove(&xdg_shell_v6_view->unmap.link);
157 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
158 free(xdg_shell_v6_view); 152 free(xdg_shell_v6_view);
159} 153}
160 154
@@ -166,25 +160,22 @@ static const struct sway_view_impl view_impl = {
166 .wants_floating = wants_floating, 160 .wants_floating = wants_floating,
167 .for_each_surface = for_each_surface, 161 .for_each_surface = for_each_surface,
168 .close = _close, 162 .close = _close,
169 .destroy = destroy, 163 .free = _free,
170}; 164};
171 165
172static void handle_commit(struct wl_listener *listener, void *data) { 166static void handle_commit(struct wl_listener *listener, void *data) {
173 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 167 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
174 wl_container_of(listener, xdg_shell_v6_view, commit); 168 wl_container_of(listener, xdg_shell_v6_view, commit);
175 struct sway_view *view = &xdg_shell_v6_view->view; 169 struct sway_view *view = &xdg_shell_v6_view->view;
176 if (view->swayc && container_is_floating(view->swayc)) { 170 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6;
177 int width = view->wlr_xdg_surface_v6->geometry.width; 171
178 int height = view->wlr_xdg_surface_v6->geometry.height; 172 if (!view->swayc) {
179 if (!width && !height) { 173 return;
180 width = view->wlr_xdg_surface_v6->surface->current->width;
181 height = view->wlr_xdg_surface_v6->surface->current->height;
182 }
183 view_update_size(view, width, height);
184 } else {
185 view_update_size(view, xdg_shell_v6_view->pending_width,
186 xdg_shell_v6_view->pending_height);
187 } 174 }
175 if (view->swayc->instructions->length) {
176 transaction_notify_view_ready(view, xdg_surface_v6->configure_serial);
177 }
178
188 view_update_title(view, false); 179 view_update_title(view, false);
189 view_damage_from(view); 180 view_damage_from(view);
190} 181}
@@ -199,11 +190,18 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
199static void handle_unmap(struct wl_listener *listener, void *data) { 190static void handle_unmap(struct wl_listener *listener, void *data) {
200 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 191 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
201 wl_container_of(listener, xdg_shell_v6_view, unmap); 192 wl_container_of(listener, xdg_shell_v6_view, unmap);
193 struct sway_view *view = &xdg_shell_v6_view->view;
194
195 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
196 return;
197 }
202 198
203 view_unmap(&xdg_shell_v6_view->view); 199 struct sway_container *parent = view_unmap(view);
200 arrange_and_commit(parent);
204 201
205 wl_list_remove(&xdg_shell_v6_view->commit.link); 202 wl_list_remove(&xdg_shell_v6_view->commit.link);
206 wl_list_remove(&xdg_shell_v6_view->new_popup.link); 203 wl_list_remove(&xdg_shell_v6_view->new_popup.link);
204 view->surface = NULL;
207} 205}
208 206
209static void handle_map(struct wl_listener *listener, void *data) { 207static void handle_map(struct wl_listener *listener, void *data) {
@@ -218,8 +216,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
218 view->natural_width = view->wlr_xdg_surface_v6->surface->current->width; 216 view->natural_width = view->wlr_xdg_surface_v6->surface->current->width;
219 view->natural_height = view->wlr_xdg_surface_v6->surface->current->height; 217 view->natural_height = view->wlr_xdg_surface_v6->surface->current->height;
220 } 218 }
219
221 view_map(view, view->wlr_xdg_surface_v6->surface); 220 view_map(view, view->wlr_xdg_surface_v6->surface);
222 221
222 if (xdg_surface->toplevel->client_pending.fullscreen) {
223 view_set_fullscreen(view, true);
224 }
225 arrange_and_commit(view->swayc->parent);
226
223 xdg_shell_v6_view->commit.notify = handle_commit; 227 xdg_shell_v6_view->commit.notify = handle_commit;
224 wl_signal_add(&xdg_surface->surface->events.commit, 228 wl_signal_add(&xdg_surface->surface->events.commit,
225 &xdg_shell_v6_view->commit); 229 &xdg_shell_v6_view->commit);
@@ -227,16 +231,18 @@ static void handle_map(struct wl_listener *listener, void *data) {
227 xdg_shell_v6_view->new_popup.notify = handle_new_popup; 231 xdg_shell_v6_view->new_popup.notify = handle_new_popup;
228 wl_signal_add(&xdg_surface->events.new_popup, 232 wl_signal_add(&xdg_surface->events.new_popup,
229 &xdg_shell_v6_view->new_popup); 233 &xdg_shell_v6_view->new_popup);
230
231 if (xdg_surface->toplevel->client_pending.fullscreen) {
232 view_set_fullscreen(view, true);
233 }
234} 234}
235 235
236static void handle_destroy(struct wl_listener *listener, void *data) { 236static void handle_destroy(struct wl_listener *listener, void *data) {
237 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 237 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
238 wl_container_of(listener, xdg_shell_v6_view, destroy); 238 wl_container_of(listener, xdg_shell_v6_view, destroy);
239 view_destroy(&xdg_shell_v6_view->view); 239 struct sway_view *view = &xdg_shell_v6_view->view;
240 wl_list_remove(&xdg_shell_v6_view->destroy.link);
241 wl_list_remove(&xdg_shell_v6_view->map.link);
242 wl_list_remove(&xdg_shell_v6_view->unmap.link);
243 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
244 view->wlr_xdg_surface_v6 = NULL;
245 view_destroy(view);
240} 246}
241 247
242static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 248static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
@@ -245,6 +251,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
245 struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data; 251 struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data;
246 struct wlr_xdg_surface_v6 *xdg_surface = 252 struct wlr_xdg_surface_v6 *xdg_surface =
247 xdg_shell_v6_view->view.wlr_xdg_surface_v6; 253 xdg_shell_v6_view->view.wlr_xdg_surface_v6;
254 struct sway_view *view = &xdg_shell_v6_view->view;
248 255
249 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL, 256 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL,
250 "xdg_shell_v6 requested fullscreen of surface with role %i", 257 "xdg_shell_v6 requested fullscreen of surface with role %i",
@@ -255,7 +262,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
255 return; 262 return;
256 } 263 }
257 264
258 view_set_fullscreen(&xdg_shell_v6_view->view, e->fullscreen); 265 view_set_fullscreen(view, e->fullscreen);
266
267 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
268 arrange_and_commit(ws);
259} 269}
260 270
261void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { 271void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 6447b711..a1837420 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -11,6 +11,7 @@
11#include "sway/input/seat.h" 11#include "sway/input/seat.h"
12#include "sway/output.h" 12#include "sway/output.h"
13#include "sway/server.h" 13#include "sway/server.h"
14#include "sway/tree/arrange.h"
14#include "sway/tree/container.h" 15#include "sway/tree/container.h"
15#include "sway/tree/layout.h" 16#include "sway/tree/layout.h"
16#include "sway/tree/view.h" 17#include "sway/tree/view.h"
@@ -167,19 +168,18 @@ static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) {
167 } 168 }
168} 169}
169 170
170static void configure(struct sway_view *view, double lx, double ly, int width, 171static uint32_t configure(struct sway_view *view, double lx, double ly, int width,
171 int height) { 172 int height) {
172 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); 173 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
173 if (xwayland_view == NULL) { 174 if (xwayland_view == NULL) {
174 return; 175 return 0;
175 } 176 }
176 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 177 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
177 178
178 xwayland_view->pending_lx = lx;
179 xwayland_view->pending_ly = ly;
180 xwayland_view->pending_width = width;
181 xwayland_view->pending_height = height;
182 wlr_xwayland_surface_configure(xsurface, lx, ly, width, height); 179 wlr_xwayland_surface_configure(xsurface, lx, ly, width, height);
180
181 // xwayland doesn't give us a serial for the configure
182 return 0;
183} 183}
184 184
185static void set_activated(struct sway_view *view, bool activated) { 185static void set_activated(struct sway_view *view, bool activated) {
@@ -218,19 +218,11 @@ static void _close(struct sway_view *view) {
218 wlr_xwayland_surface_close(view->wlr_xwayland_surface); 218 wlr_xwayland_surface_close(view->wlr_xwayland_surface);
219} 219}
220 220
221static void destroy(struct sway_view *view) { 221static void _free(struct sway_view *view) {
222 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); 222 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
223 if (xwayland_view == NULL) { 223 if (xwayland_view == NULL) {
224 return; 224 return;
225 } 225 }
226 wl_list_remove(&xwayland_view->destroy.link);
227 wl_list_remove(&xwayland_view->request_configure.link);
228 wl_list_remove(&xwayland_view->request_fullscreen.link);
229 wl_list_remove(&xwayland_view->set_title.link);
230 wl_list_remove(&xwayland_view->set_class.link);
231 wl_list_remove(&xwayland_view->set_window_type.link);
232 wl_list_remove(&xwayland_view->map.link);
233 wl_list_remove(&xwayland_view->unmap.link);
234 free(xwayland_view); 226 free(xwayland_view);
235} 227}
236 228
@@ -242,7 +234,7 @@ static const struct sway_view_impl view_impl = {
242 .set_fullscreen = set_fullscreen, 234 .set_fullscreen = set_fullscreen,
243 .wants_floating = wants_floating, 235 .wants_floating = wants_floating,
244 .close = _close, 236 .close = _close,
245 .destroy = destroy, 237 .free = _free,
246}; 238};
247 239
248static void handle_commit(struct wl_listener *listener, void *data) { 240static void handle_commit(struct wl_listener *listener, void *data) {
@@ -250,22 +242,35 @@ static void handle_commit(struct wl_listener *listener, void *data) {
250 wl_container_of(listener, xwayland_view, commit); 242 wl_container_of(listener, xwayland_view, commit);
251 struct sway_view *view = &xwayland_view->view; 243 struct sway_view *view = &xwayland_view->view;
252 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 244 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
253 if (view->swayc && container_is_floating(view->swayc)) { 245
254 view_update_size(view, xsurface->width, xsurface->height); 246 // Don't allow xwayland views to do resize or reposition themselves if
255 } else { 247 // they're involved in a transaction. Once the transaction has finished
256 view_update_size(view, xwayland_view->pending_width, 248 // they'll apply the next time a commit happens.
257 xwayland_view->pending_height); 249 if (view->swayc && view->swayc->instructions->length) {
250 if (view->swayc && container_is_floating(view->swayc)) {
251 view_update_size(view, xsurface->width, xsurface->height);
252 } else {
253 view_update_size(view, view->swayc->width, view->swayc->height);
254 }
255 view_update_position(view, view->x, view->y);
258 } 256 }
259 view_update_position(view,
260 xwayland_view->pending_lx, xwayland_view->pending_ly);
261 view_damage_from(view); 257 view_damage_from(view);
262} 258}
263 259
264static void handle_unmap(struct wl_listener *listener, void *data) { 260static void handle_unmap(struct wl_listener *listener, void *data) {
265 struct sway_xwayland_view *xwayland_view = 261 struct sway_xwayland_view *xwayland_view =
266 wl_container_of(listener, xwayland_view, unmap); 262 wl_container_of(listener, xwayland_view, unmap);
263 struct sway_view *view = &xwayland_view->view;
264
265 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
266 return;
267 }
268
269 struct sway_container *parent = view_unmap(view);
270 arrange_and_commit(parent);
271
267 wl_list_remove(&xwayland_view->commit.link); 272 wl_list_remove(&xwayland_view->commit.link);
268 view_unmap(&xwayland_view->view); 273 view->surface = NULL;
269} 274}
270 275
271static void handle_map(struct wl_listener *listener, void *data) { 276static void handle_map(struct wl_listener *listener, void *data) {
@@ -289,11 +294,30 @@ static void handle_map(struct wl_listener *listener, void *data) {
289 if (xsurface->fullscreen) { 294 if (xsurface->fullscreen) {
290 view_set_fullscreen(view, true); 295 view_set_fullscreen(view, true);
291 } 296 }
297 arrange_and_commit(view->swayc->parent);
292} 298}
293 299
294static void handle_destroy(struct wl_listener *listener, void *data) { 300static void handle_destroy(struct wl_listener *listener, void *data) {
295 struct sway_xwayland_view *xwayland_view = 301 struct sway_xwayland_view *xwayland_view =
296 wl_container_of(listener, xwayland_view, destroy); 302 wl_container_of(listener, xwayland_view, destroy);
303 struct sway_view *view = &xwayland_view->view;
304
305 if (view->surface) {
306 struct sway_container *parent = view_unmap(view);
307 arrange_and_commit(parent);
308
309 wl_list_remove(&xwayland_view->commit.link);
310 view->surface = NULL;
311 }
312
313 wl_list_remove(&xwayland_view->destroy.link);
314 wl_list_remove(&xwayland_view->request_configure.link);
315 wl_list_remove(&xwayland_view->request_fullscreen.link);
316 wl_list_remove(&xwayland_view->set_title.link);
317 wl_list_remove(&xwayland_view->set_class.link);
318 wl_list_remove(&xwayland_view->set_window_type.link);
319 wl_list_remove(&xwayland_view->map.link);
320 wl_list_remove(&xwayland_view->unmap.link);
297 view_destroy(&xwayland_view->view); 321 view_destroy(&xwayland_view->view);
298} 322}
299 323
@@ -309,7 +333,8 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
309 return; 333 return;
310 } 334 }
311 // TODO: Let floating views do whatever 335 // TODO: Let floating views do whatever
312 configure(view, view->swayc->x, view->swayc->y, view->width, view->height); 336 configure(view, view->swayc->current.view_x, view->swayc->current.view_y,
337 view->swayc->current.view_width, view->swayc->current.view_height);
313} 338}
314 339
315static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 340static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
@@ -321,6 +346,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
321 return; 346 return;
322 } 347 }
323 view_set_fullscreen(view, xsurface->fullscreen); 348 view_set_fullscreen(view, xsurface->fullscreen);
349 arrange_and_commit(view->swayc);
324} 350}
325 351
326static void handle_set_title(struct wl_listener *listener, void *data) { 352static void handle_set_title(struct wl_listener *listener, void *data) {
diff --git a/sway/main.c b/sway/main.c
index a325dc3a..61ae6a5f 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -34,6 +34,7 @@ struct sway_server server;
34void sway_terminate(int exit_code) { 34void sway_terminate(int exit_code) {
35 terminate_request = true; 35 terminate_request = true;
36 exit_value = exit_code; 36 exit_value = exit_code;
37 server.terminating = true;
37 wl_display_terminate(server.wl_display); 38 wl_display_terminate(server.wl_display);
38} 39}
39 40
diff --git a/sway/meson.build b/sway/meson.build
index 0da67ed7..c461b0ff 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -12,6 +12,7 @@ sway_sources = files(
12 'desktop/desktop.c', 12 'desktop/desktop.c',
13 'desktop/layer_shell.c', 13 'desktop/layer_shell.c',
14 'desktop/output.c', 14 'desktop/output.c',
15 'desktop/transaction.c',
15 'desktop/xdg_shell_v6.c', 16 'desktop/xdg_shell_v6.c',
16 'desktop/xdg_shell.c', 17 'desktop/xdg_shell.c',
17 'desktop/xwayland.c', 18 'desktop/xwayland.c',
diff --git a/sway/server.c b/sway/server.c
index a467283b..86d4a643 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -20,6 +20,7 @@
20#include <wlr/util/log.h> 20#include <wlr/util/log.h>
21// TODO WLR: make Xwayland optional 21// TODO WLR: make Xwayland optional
22#include <wlr/xwayland.h> 22#include <wlr/xwayland.h>
23#include "list.h"
23#include "sway/config.h" 24#include "sway/config.h"
24#include "sway/input/input-manager.h" 25#include "sway/input/input-manager.h"
25#include "sway/server.h" 26#include "sway/server.h"
@@ -112,6 +113,8 @@ bool server_init(struct sway_server *server) {
112 return false; 113 return false;
113 } 114 }
114 115
116 server->destroying_containers = create_list();
117
115 input_manager = input_manager_create(server); 118 input_manager = input_manager_create(server);
116 return true; 119 return true;
117} 120}
@@ -119,6 +122,7 @@ bool server_init(struct sway_server *server) {
119void server_fini(struct sway_server *server) { 122void server_fini(struct sway_server *server) {
120 // TODO: free sway-specific resources 123 // TODO: free sway-specific resources
121 wl_display_destroy(server->wl_display); 124 wl_display_destroy(server->wl_display);
125 list_free(server->destroying_containers);
122} 126}
123 127
124void server_run(struct sway_server *server) { 128void server_run(struct sway_server *server) {
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 53c95820..cb3f8ba2 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -5,7 +5,6 @@
5#include <string.h> 5#include <string.h>
6#include <wlr/types/wlr_output.h> 6#include <wlr/types/wlr_output.h>
7#include <wlr/types/wlr_output_layout.h> 7#include <wlr/types/wlr_output_layout.h>
8#include "sway/debug.h"
9#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
10#include "sway/tree/container.h" 9#include "sway/tree/container.h"
11#include "sway/tree/layout.h" 10#include "sway/tree/layout.h"
@@ -17,116 +16,6 @@
17 16
18struct sway_container root_container; 17struct sway_container root_container;
19 18
20void arrange_root() {
21 if (config->reloading) {
22 return;
23 }
24 struct wlr_output_layout *output_layout =
25 root_container.sway_root->output_layout;
26 const struct wlr_box *layout_box =
27 wlr_output_layout_get_box(output_layout, NULL);
28 root_container.x = layout_box->x;
29 root_container.y = layout_box->y;
30 root_container.width = layout_box->width;
31 root_container.height = layout_box->height;
32 for (int i = 0; i < root_container.children->length; ++i) {
33 struct sway_container *output = root_container.children->items[i];
34 arrange_output(output);
35 }
36}
37
38void arrange_output(struct sway_container *output) {
39 if (config->reloading) {
40 return;
41 }
42 if (!sway_assert(output->type == C_OUTPUT,
43 "called arrange_output() on non-output container")) {
44 return;
45 }
46
47 const struct wlr_box *output_box = wlr_output_layout_get_box(
48 root_container.sway_root->output_layout,
49 output->sway_output->wlr_output);
50 output->x = output_box->x;
51 output->y = output_box->y;
52 output->width = output_box->width;
53 output->height = output_box->height;
54 wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f",
55 output->name, output->x, output->y);
56
57 for (int i = 0; i < output->children->length; ++i) {
58 struct sway_container *workspace = output->children->items[i];
59 arrange_workspace(workspace);
60 }
61 container_damage_whole(output);
62}
63
64void arrange_workspace(struct sway_container *workspace) {
65 if (config->reloading) {
66 return;
67 }
68 if (!sway_assert(workspace->type == C_WORKSPACE,
69 "called arrange_workspace() on non-workspace container")) {
70 return;
71 }
72
73 struct sway_container *output = workspace->parent;
74 struct wlr_box *area = &output->sway_output->usable_area;
75 wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d",
76 area->width, area->height, area->x, area->y);
77
78 remove_gaps(workspace);
79
80 workspace->width = area->width;
81 workspace->height = area->height;
82 workspace->x = output->x + area->x;
83 workspace->y = output->y + area->y;
84
85 add_gaps(workspace);
86
87 wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f",
88 workspace->name, workspace->x, workspace->y);
89 arrange_children_of(workspace);
90}
91
92void remove_gaps(struct sway_container *c) {
93 if (c->current_gaps == 0) {
94 wlr_log(L_DEBUG, "Removing gaps: not gapped: %p", c);
95 return;
96 }
97
98 c->width += c->current_gaps * 2;
99 c->height += c->current_gaps * 2;
100 c->x -= c->current_gaps;
101 c->y -= c->current_gaps;
102
103 c->current_gaps = 0;
104
105 wlr_log(L_DEBUG, "Removing gaps %p", c);
106}
107
108void add_gaps(struct sway_container *c) {
109 if (c->current_gaps > 0 || c->type == C_CONTAINER) {
110 wlr_log(L_DEBUG, "Not adding gaps: %p", c);
111 return;
112 }
113
114 if (c->type == C_WORKSPACE &&
115 !(config->edge_gaps || (config->smart_gaps && c->children->length > 1))) {
116 return;
117 }
118
119 double gaps = c->has_gaps ? c->gaps_inner : config->gaps_inner;
120
121 c->x += gaps;
122 c->y += gaps;
123 c->width -= 2 * gaps;
124 c->height -= 2 * gaps;
125 c->current_gaps = gaps;
126
127 wlr_log(L_DEBUG, "Adding gaps: %p", c);
128}
129
130static void apply_horiz_layout(struct sway_container *parent) { 19static void apply_horiz_layout(struct sway_container *parent) {
131 size_t num_children = parent->children->length; 20 size_t num_children = parent->children->length;
132 if (!num_children) { 21 if (!num_children) {
@@ -136,8 +25,8 @@ static void apply_horiz_layout(struct sway_container *parent) {
136 if (parent->parent->layout == L_TABBED) { 25 if (parent->parent->layout == L_TABBED) {
137 parent_offset = container_titlebar_height(); 26 parent_offset = container_titlebar_height();
138 } else if (parent->parent->layout == L_STACKED) { 27 } else if (parent->parent->layout == L_STACKED) {
139 parent_offset = 28 parent_offset = container_titlebar_height() *
140 container_titlebar_height() * parent->parent->children->length; 29 parent->parent->children->length;
141 } 30 }
142 size_t parent_height = parent->height - parent_offset; 31 size_t parent_height = parent->height - parent_offset;
143 32
@@ -145,7 +34,6 @@ static void apply_horiz_layout(struct sway_container *parent) {
145 double total_width = 0; 34 double total_width = 0;
146 for (size_t i = 0; i < num_children; ++i) { 35 for (size_t i = 0; i < num_children; ++i) {
147 struct sway_container *child = parent->children->items[i]; 36 struct sway_container *child = parent->children->items[i];
148
149 if (child->width <= 0) { 37 if (child->width <= 0) {
150 if (num_children > 1) { 38 if (num_children > 1) {
151 child->width = parent->width / (num_children - 1); 39 child->width = parent->width / (num_children - 1);
@@ -161,25 +49,21 @@ static void apply_horiz_layout(struct sway_container *parent) {
161 // Resize windows 49 // Resize windows
162 wlr_log(L_DEBUG, "Arranging %p horizontally", parent); 50 wlr_log(L_DEBUG, "Arranging %p horizontally", parent);
163 double child_x = parent->x; 51 double child_x = parent->x;
164 struct sway_container *child;
165 for (size_t i = 0; i < num_children; ++i) { 52 for (size_t i = 0; i < num_children; ++i) {
166 child = parent->children->items[i]; 53 struct sway_container *child = parent->children->items[i];
167 wlr_log(L_DEBUG, 54 wlr_log(L_DEBUG,
168 "Calculating arrangement for %p:%d (will scale %f by %f)", 55 "Calculating arrangement for %p:%d (will scale %f by %f)",
169 child, child->type, child->width, scale); 56 child, child->type, child->width, scale);
170 child->x = child_x; 57 child->x = child_x;
171 child->y = parent->y + parent_offset; 58 child->y = parent->y + parent_offset;
59 child->width = floor(child->width * scale);
172 child->height = parent_height; 60 child->height = parent_height;
61 child_x += child->width;
173 62
63 // Make last child use remaining width of parent
174 if (i == num_children - 1) { 64 if (i == num_children - 1) {
175 // Make last child use remaining width of parent
176 child->width = parent->x + parent->width - child->x; 65 child->width = parent->x + parent->width - child->x;
177 } else {
178 child->width = floor(child->width * scale);
179 } 66 }
180
181 child_x += child->width;
182
183 add_gaps(child); 67 add_gaps(child);
184 } 68 }
185} 69}
@@ -202,7 +86,6 @@ static void apply_vert_layout(struct sway_container *parent) {
202 double total_height = 0; 86 double total_height = 0;
203 for (size_t i = 0; i < num_children; ++i) { 87 for (size_t i = 0; i < num_children; ++i) {
204 struct sway_container *child = parent->children->items[i]; 88 struct sway_container *child = parent->children->items[i];
205
206 if (child->height <= 0) { 89 if (child->height <= 0) {
207 if (num_children > 1) { 90 if (num_children > 1) {
208 child->height = parent_height / (num_children - 1); 91 child->height = parent_height / (num_children - 1);
@@ -218,25 +101,22 @@ static void apply_vert_layout(struct sway_container *parent) {
218 // Resize 101 // Resize
219 wlr_log(L_DEBUG, "Arranging %p vertically", parent); 102 wlr_log(L_DEBUG, "Arranging %p vertically", parent);
220 double child_y = parent->y + parent_offset; 103 double child_y = parent->y + parent_offset;
221 struct sway_container *child;
222 for (size_t i = 0; i < num_children; ++i) { 104 for (size_t i = 0; i < num_children; ++i) {
223 child = parent->children->items[i]; 105 struct sway_container *child = parent->children->items[i];
224 wlr_log(L_DEBUG, 106 wlr_log(L_DEBUG,
225 "Calculating arrangement for %p:%d (will scale %f by %f)", 107 "Calculating arrangement for %p:%d (will scale %f by %f)",
226 child, child->type, child->height, scale); 108 child, child->type, child->height, scale);
227 child->x = parent->x; 109 child->x = parent->x;
228 child->y = child_y; 110 child->y = child_y;
229 child->width = parent->width; 111 child->width = parent->width;
112 child->height = floor(child->height * scale);
113 child_y += child->height;
230 114
115 // Make last child use remaining height of parent
231 if (i == num_children - 1) { 116 if (i == num_children - 1) {
232 // Make last child use remaining height of parent 117 child->height =
233 child->height = parent->y + parent_offset + parent_height - child->y; 118 parent->y + parent_offset + parent_height - child->y;
234 } else {
235 child->height = floor(child->height * scale);
236 } 119 }
237
238 child_y += child->height;
239
240 add_gaps(child); 120 add_gaps(child);
241 } 121 }
242} 122}
@@ -264,26 +144,41 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) {
264 } 144 }
265} 145}
266 146
267void arrange_children_of(struct sway_container *parent) { 147/**
268 if (config->reloading) { 148 * If a container has been deleted from the pending tree state, we must add it
269 return; 149 * to the transaction so it can be freed afterwards. To do this, we iterate the
270 } 150 * server's destroying_containers list and add all of them. We may add more than
271 if (!sway_assert(parent->type == C_WORKSPACE || parent->type == C_CONTAINER, 151 * what we need to, but this is easy and has no negative consequences.
272 "container is a %s", container_type_to_str(parent->type))) { 152 */
273 return; 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);
274 } 157 }
158}
159
160static void arrange_children_of(struct sway_container *parent,
161 struct sway_transaction *transaction);
275 162
276 struct sway_container *workspace = parent; 163static void arrange_floating(struct sway_container *floating,
277 if (workspace->type != C_WORKSPACE) { 164 struct sway_transaction *transaction) {
278 workspace = container_parent(workspace, C_WORKSPACE); 165 for (int i = 0; i < floating->children->length; ++i) {
166 struct sway_container *floater = floating->children->items[i];
167 if (floater->type == C_VIEW) {
168 view_autoconfigure(floater->sway_view);
169 } else {
170 arrange_children_of(floater, transaction);
171 }
172 transaction_add_container(transaction, floater);
279 } 173 }
174 transaction_add_container(transaction, floating);
175}
280 176
281 if (workspace->sway_workspace->fullscreen) { 177static void arrange_children_of(struct sway_container *parent,
282 // Just arrange the fullscreen view and jump out 178 struct sway_transaction *transaction) {
283 view_autoconfigure(workspace->sway_workspace->fullscreen); 179 if (config->reloading) {
284 return; 180 return;
285 } 181 }
286
287 wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, 182 wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent,
288 parent->name, parent->width, parent->height, parent->x, parent->y); 183 parent->name, parent->width, parent->height, parent->x, parent->y);
289 184
@@ -299,13 +194,15 @@ void arrange_children_of(struct sway_container *parent) {
299 case L_STACKED: 194 case L_STACKED:
300 apply_tabbed_or_stacked_layout(parent); 195 apply_tabbed_or_stacked_layout(parent);
301 break; 196 break;
302 default: 197 case L_NONE:
303 wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout);
304 apply_horiz_layout(parent); 198 apply_horiz_layout(parent);
305 break; 199 break;
200 case L_FLOATING:
201 arrange_floating(parent, transaction);
202 break;
306 } 203 }
307 204
308 // Apply x, y, width and height to children and recurse if needed 205 // Recurse into child containers
309 for (int i = 0; i < parent->children->length; ++i) { 206 for (int i = 0; i < parent->children->length; ++i) {
310 struct sway_container *child = parent->children->items[i]; 207 struct sway_container *child = parent->children->items[i];
311 if (parent->has_gaps && !child->has_gaps) { 208 if (parent->has_gaps && !child->has_gaps) {
@@ -316,21 +213,149 @@ void arrange_children_of(struct sway_container *parent) {
316 if (child->type == C_VIEW) { 213 if (child->type == C_VIEW) {
317 view_autoconfigure(child->sway_view); 214 view_autoconfigure(child->sway_view);
318 } else { 215 } else {
319 arrange_children_of(child); 216 arrange_children_of(child, transaction);
320 } 217 }
218 transaction_add_container(transaction, child);
321 } 219 }
220}
322 221
323 // If container is a workspace, process floating containers too 222static void arrange_workspace(struct sway_container *workspace,
324 if (parent->type == C_WORKSPACE) { 223 struct sway_transaction *transaction) {
325 struct sway_workspace *ws = workspace->sway_workspace; 224 if (config->reloading) {
326 for (int i = 0; i < ws->floating->children->length; ++i) { 225 return;
327 struct sway_container *child = ws->floating->children->items[i]; 226 }
328 if (child->type != C_VIEW) { 227 struct sway_container *output = workspace->parent;
329 arrange_children_of(child); 228 struct wlr_box *area = &output->sway_output->usable_area;
330 } 229 wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d",
331 } 230 area->width, area->height, area->x, area->y);
231 remove_gaps(workspace);
232 workspace->width = area->width;
233 workspace->height = area->height;
234 workspace->x = output->x + area->x;
235 workspace->y = output->y + area->y;
236 add_gaps(workspace);
237 transaction_add_container(transaction, workspace);
238 wlr_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
239 workspace->x, workspace->y);
240 arrange_floating(workspace->sway_workspace->floating, transaction);
241 arrange_children_of(workspace, transaction);
242}
243
244static void arrange_output(struct sway_container *output,
245 struct sway_transaction *transaction) {
246 if (config->reloading) {
247 return;
248 }
249 const struct wlr_box *output_box = wlr_output_layout_get_box(
250 root_container.sway_root->output_layout,
251 output->sway_output->wlr_output);
252 output->x = output_box->x;
253 output->y = output_box->y;
254 output->width = output_box->width;
255 output->height = output_box->height;
256 transaction_add_container(transaction, output);
257 wlr_log(L_DEBUG, "Arranging output '%s' at %f,%f",
258 output->name, output->x, output->y);
259 for (int i = 0; i < output->children->length; ++i) {
260 struct sway_container *workspace = output->children->items[i];
261 arrange_workspace(workspace, transaction);
262 }
263}
264
265static void arrange_root(struct sway_transaction *transaction) {
266 if (config->reloading) {
267 return;
268 }
269 struct wlr_output_layout *output_layout =
270 root_container.sway_root->output_layout;
271 const struct wlr_box *layout_box =
272 wlr_output_layout_get_box(output_layout, NULL);
273 root_container.x = layout_box->x;
274 root_container.y = layout_box->y;
275 root_container.width = layout_box->width;
276 root_container.height = layout_box->height;
277 transaction_add_container(transaction, &root_container);
278 for (int i = 0; i < root_container.children->length; ++i) {
279 struct sway_container *output = root_container.children->items[i];
280 arrange_output(output, transaction);
281 }
282}
283
284void arrange_windows(struct sway_container *container,
285 struct sway_transaction *transaction) {
286 switch (container->type) {
287 case C_ROOT:
288 arrange_root(transaction);
289 break;
290 case C_OUTPUT:
291 arrange_output(container, transaction);
292 break;
293 case C_WORKSPACE:
294 arrange_workspace(container, transaction);
295 break;
296 case C_CONTAINER:
297 arrange_children_of(container, transaction);
298 transaction_add_container(transaction, container);
299 break;
300 case C_VIEW:
301 view_autoconfigure(container->sway_view);
302 transaction_add_container(transaction, container);
303 break;
304 case C_TYPES:
305 break;
306 }
307 // Add damage for whatever container arrange_windows() was called with,
308 // unless it was called with the special floating container, in which case
309 // we'll damage the entire output.
310 if (container->type == C_CONTAINER && container->layout == L_FLOATING) {
311 struct sway_container *output = container_parent(container, C_OUTPUT);
312 transaction_add_damage(transaction, container_get_box(output));
313 } else {
314 transaction_add_damage(transaction, container_get_box(container));
315 }
316 add_deleted_containers(transaction);
317}
318
319void arrange_and_commit(struct sway_container *container) {
320 struct sway_transaction *transaction = transaction_create();
321 arrange_windows(container, transaction);
322 transaction_commit(transaction);
323}
324
325void remove_gaps(struct sway_container *c) {
326 if (c->current_gaps == 0) {
327 wlr_log(L_DEBUG, "Removing gaps: not gapped: %p", c);
328 return;
332 } 329 }
333 330
334 container_damage_whole(parent); 331 c->width += c->current_gaps * 2;
335 update_debug_tree(); 332 c->height += c->current_gaps * 2;
333 c->x -= c->current_gaps;
334 c->y -= c->current_gaps;
335
336 c->current_gaps = 0;
337
338 wlr_log(L_DEBUG, "Removing gaps %p", c);
339}
340
341void add_gaps(struct sway_container *c) {
342 if (c->current_gaps > 0 || c->type == C_CONTAINER) {
343 wlr_log(L_DEBUG, "Not adding gaps: %p", c);
344 return;
345 }
346
347 if (c->type == C_WORKSPACE &&
348 !(config->edge_gaps || (config->smart_gaps && c->children->length > 1))) {
349 return;
350 }
351
352 double gaps = c->has_gaps ? c->gaps_inner : config->gaps_inner;
353
354 c->x += gaps;
355 c->y += gaps;
356 c->width -= 2 * gaps;
357 c->height -= 2 * gaps;
358 c->current_gaps = gaps;
359
360 wlr_log(L_DEBUG, "Adding gaps: %p", c);
336} 361}
diff --git a/sway/tree/container.c b/sway/tree/container.c
index af55a54e..484d26a5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -16,7 +16,6 @@
16#include "sway/ipc-server.h" 16#include "sway/ipc-server.h"
17#include "sway/output.h" 17#include "sway/output.h"
18#include "sway/server.h" 18#include "sway/server.h"
19#include "sway/tree/arrange.h"
20#include "sway/tree/layout.h" 19#include "sway/tree/layout.h"
21#include "sway/tree/view.h" 20#include "sway/tree/view.h"
22#include "sway/tree/workspace.h" 21#include "sway/tree/workspace.h"
@@ -113,9 +112,11 @@ struct sway_container *container_create(enum sway_container_type type) {
113 c->layout = L_NONE; 112 c->layout = L_NONE;
114 c->type = type; 113 c->type = type;
115 c->alpha = 1.0f; 114 c->alpha = 1.0f;
115 c->instructions = create_list();
116 116
117 if (type != C_VIEW) { 117 if (type != C_VIEW) {
118 c->children = create_list(); 118 c->children = create_list();
119 c->current.children = create_list();
119 } 120 }
120 121
121 wl_signal_init(&c->events.destroy); 122 wl_signal_init(&c->events.destroy);
@@ -132,42 +133,68 @@ struct sway_container *container_create(enum sway_container_type type) {
132 return c; 133 return c;
133} 134}
134 135
135static void _container_destroy(struct sway_container *cont) { 136static void container_workspace_free(struct sway_workspace *ws) {
136 if (cont == NULL) { 137 list_foreach(ws->output_priority, free);
137 return; 138 list_free(ws->output_priority);
138 } 139 ws->floating->destroying = true;
139 140 container_free(ws->floating);
140 wl_signal_emit(&cont->events.destroy, cont); 141 free(ws);
142}
141 143
142 struct sway_container *parent = cont->parent; 144void container_free(struct sway_container *cont) {
143 if (cont->children != NULL && cont->children->length) { 145 if (!sway_assert(cont->destroying,
144 // remove children until there are no more, container_destroy calls 146 "Tried to free container which wasn't marked as destroying")) {
145 // container_remove_child, which removes child from this container 147 return;
146 while (cont->children != NULL && cont->children->length > 0) {
147 struct sway_container *child = cont->children->items[0];
148 ipc_event_window(child, "close");
149 container_remove_child(child);
150 _container_destroy(child);
151 }
152 }
153 if (cont->marks) {
154 list_foreach(cont->marks, free);
155 list_free(cont->marks);
156 }
157 if (parent) {
158 parent = container_remove_child(cont);
159 } 148 }
160 if (cont->name) { 149 if (!sway_assert(cont->instructions->length == 0,
161 free(cont->name); 150 "Tried to free container with pending instructions")) {
151 return;
162 } 152 }
163 153 free(cont->name);
164 wlr_texture_destroy(cont->title_focused); 154 wlr_texture_destroy(cont->title_focused);
165 wlr_texture_destroy(cont->title_focused_inactive); 155 wlr_texture_destroy(cont->title_focused_inactive);
166 wlr_texture_destroy(cont->title_unfocused); 156 wlr_texture_destroy(cont->title_unfocused);
167 wlr_texture_destroy(cont->title_urgent); 157 wlr_texture_destroy(cont->title_urgent);
168 158
159 for (int i = 0; i < server.destroying_containers->length; ++i) {
160 if (server.destroying_containers->items[i] == cont) {
161 list_del(server.destroying_containers, i);
162 break;
163 }
164 }
165
166 list_free(cont->instructions);
169 list_free(cont->children); 167 list_free(cont->children);
170 cont->children = NULL; 168 list_free(cont->current.children);
169
170 switch (cont->type) {
171 case C_ROOT:
172 break;
173 case C_OUTPUT:
174 cont->sway_output->swayc = NULL;
175 break;
176 case C_WORKSPACE:
177 container_workspace_free(cont->sway_workspace);
178 break;
179 case C_CONTAINER:
180 break;
181 case C_VIEW:
182 {
183 struct sway_view *view = cont->sway_view;
184 view->swayc = NULL;
185 free(view->title_format);
186 view->title_format = NULL;
187
188 if (view->destroying) {
189 view_free(view);
190 }
191 }
192 break;
193 case C_TYPES:
194 sway_assert(false, "Didn't expect to see C_TYPES here");
195 break;
196 }
197
171 free(cont); 198 free(cont);
172} 199}
173 200
@@ -184,7 +211,6 @@ static struct sway_container *container_workspace_destroy(
184 } 211 }
185 212
186 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); 213 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
187 ipc_event_window(workspace, "close");
188 214
189 struct sway_container *parent = workspace->parent; 215 struct sway_container *parent = workspace->parent;
190 if (!workspace_is_empty(workspace) && output) { 216 if (!workspace_is_empty(workspace) && output) {
@@ -209,24 +235,6 @@ static struct sway_container *container_workspace_destroy(
209 } 235 }
210 } 236 }
211 237
212 struct sway_workspace *sway_workspace = workspace->sway_workspace;
213
214 // This emits the destroy event and also destroys the swayc.
215 _container_destroy(workspace);
216
217 // Clean up the floating container
218 sway_workspace->floating->parent = NULL;
219 _container_destroy(sway_workspace->floating);
220
221 list_foreach(sway_workspace->output_priority, free);
222 list_free(sway_workspace->output_priority);
223
224 free(sway_workspace);
225
226 if (output) {
227 output_damage_whole(output->sway_output);
228 }
229
230 return parent; 238 return parent;
231} 239}
232 240
@@ -263,11 +271,10 @@ static struct sway_container *container_output_destroy(
263 container_add_child(new_output, workspace); 271 container_add_child(new_output, workspace);
264 ipc_event_workspace(workspace, NULL, "move"); 272 ipc_event_workspace(workspace, NULL, "move");
265 } else { 273 } else {
266 container_workspace_destroy(workspace); 274 container_destroy(workspace);
267 } 275 }
268 276
269 container_sort_workspaces(new_output); 277 container_sort_workspaces(new_output);
270 arrange_output(new_output);
271 } 278 }
272 } 279 }
273 } 280 }
@@ -282,12 +289,8 @@ static struct sway_container *container_output_destroy(
282 output->sway_output->swayc = NULL; 289 output->sway_output->swayc = NULL;
283 290
284 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); 291 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
285 _container_destroy(output);
286 return &root_container;
287}
288 292
289static void container_root_finish(struct sway_container *con) { 293 return &root_container;
290 wlr_log(L_ERROR, "TODO: destroy the root container");
291} 294}
292 295
293bool container_reap_empty(struct sway_container *con) { 296bool container_reap_empty(struct sway_container *con) {
@@ -303,13 +306,13 @@ bool container_reap_empty(struct sway_container *con) {
303 case C_WORKSPACE: 306 case C_WORKSPACE:
304 if (!workspace_is_visible(con) && workspace_is_empty(con)) { 307 if (!workspace_is_visible(con) && workspace_is_empty(con)) {
305 wlr_log(L_DEBUG, "Destroying workspace via reaper"); 308 wlr_log(L_DEBUG, "Destroying workspace via reaper");
306 container_workspace_destroy(con); 309 container_destroy(con);
307 return true; 310 return true;
308 } 311 }
309 break; 312 break;
310 case C_CONTAINER: 313 case C_CONTAINER:
311 if (con->children->length == 0) { 314 if (con->children->length == 0) {
312 _container_destroy(con); 315 container_destroy(con);
313 return true; 316 return true;
314 } 317 }
315 case C_VIEW: 318 case C_VIEW:
@@ -346,46 +349,48 @@ struct sway_container *container_flatten(struct sway_container *container) {
346 return container; 349 return container;
347} 350}
348 351
352/**
353 * container_destroy() is the first step in destroying a container. We'll emit
354 * events, detach it from the tree and mark it as destroying. The container will
355 * remain in memory until it's no longer used by a transaction, then it will be
356 * freed via container_free().
357 */
349struct sway_container *container_destroy(struct sway_container *con) { 358struct sway_container *container_destroy(struct sway_container *con) {
350 if (con == NULL) { 359 if (con == NULL) {
351 return NULL; 360 return NULL;
352 } 361 }
362 if (con->destroying) {
363 return NULL;
364 }
353 365
354 struct sway_container *parent = con->parent; 366 // The below functions move their children to somewhere else.
367 if (con->type == C_OUTPUT) {
368 container_output_destroy(con);
369 } else if (con->type == C_WORKSPACE) {
370 // Workspaces will refuse to be destroyed if they're the last workspace
371 // on their output.
372 if (!container_workspace_destroy(con)) {
373 return NULL;
374 }
375 }
355 376
356 switch (con->type) { 377 // At this point the container being destroyed shouldn't have any children
357 case C_ROOT: 378 // unless sway is terminating.
358 container_root_finish(con); 379 if (!server.terminating) {
359 break; 380 if (!sway_assert(!con->children || con->children->length == 0,
360 case C_OUTPUT: 381 "Didn't expect to see children here")) {
361 // dont try to reap the root after this 382 return NULL;
362 container_output_destroy(con); 383 }
363 break;
364 case C_WORKSPACE:
365 // dont try to reap the output after this
366 container_workspace_destroy(con);
367 break;
368 case C_CONTAINER:
369 if (con->children->length) {
370 for (int i = 0; i < con->children->length; ++i) {
371 struct sway_container *child = con->children->items[0];
372 ipc_event_window(child, "close");
373 container_remove_child(child);
374 container_add_child(parent, child);
375 }
376 }
377 ipc_event_window(con, "close");
378 _container_destroy(con);
379 break;
380 case C_VIEW:
381 _container_destroy(con);
382 break;
383 case C_TYPES:
384 wlr_log(L_ERROR, "container_destroy called on an invalid "
385 "container");
386 break;
387 } 384 }
388 385
386 wl_signal_emit(&con->events.destroy, con);
387 ipc_event_window(con, "close");
388
389 struct sway_container *parent = container_remove_child(con);
390
391 con->destroying = true;
392 list_add(server.destroying_containers, con);
393
389 return container_reap_empty_recursive(parent); 394 return container_reap_empty_recursive(parent);
390} 395}
391 396
@@ -763,9 +768,6 @@ static void update_title_texture(struct sway_container *con,
763 "Unexpected type %s", container_type_to_str(con->type))) { 768 "Unexpected type %s", container_type_to_str(con->type))) {
764 return; 769 return;
765 } 770 }
766 if (!con->width) {
767 return;
768 }
769 struct sway_container *output = container_parent(con, C_OUTPUT); 771 struct sway_container *output = container_parent(con, C_OUTPUT);
770 if (!output) { 772 if (!output) {
771 return; 773 return;
@@ -927,13 +929,12 @@ void container_set_floating(struct sway_container *container, bool enable) {
927 929
928 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 930 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
929 struct sway_seat *seat = input_manager_current_seat(input_manager); 931 struct sway_seat *seat = input_manager_current_seat(input_manager);
930 container_damage_whole(container);
931 932
932 if (enable) { 933 if (enable) {
933 container_remove_child(container); 934 container_remove_child(container);
934 container_add_child(workspace->sway_workspace->floating, container); 935 container_add_child(workspace->sway_workspace->floating, container);
935 if (container->type == C_VIEW) { 936 if (container->type == C_VIEW) {
936 view_autoconfigure(container->sway_view); 937 view_init_floating(container->sway_view);
937 } 938 }
938 seat_set_focus(seat, seat_get_focus_inactive(seat, container)); 939 seat_set_focus(seat, seat_get_focus_inactive(seat, container));
939 container_reap_empty_recursive(workspace); 940 container_reap_empty_recursive(workspace);
@@ -946,8 +947,8 @@ void container_set_floating(struct sway_container *container, bool enable) {
946 container->is_sticky = false; 947 container->is_sticky = false;
947 container_reap_empty_recursive(workspace->sway_workspace->floating); 948 container_reap_empty_recursive(workspace->sway_workspace->floating);
948 } 949 }
949 arrange_workspace(workspace); 950
950 container_damage_whole(container); 951 ipc_event_window(container, "floating");
951} 952}
952 953
953void container_set_geometry_from_floating_view(struct sway_container *con) { 954void container_set_geometry_from_floating_view(struct sway_container *con) {
@@ -976,3 +977,12 @@ bool container_is_floating(struct sway_container *container) {
976 } 977 }
977 return container->parent == workspace->sway_workspace->floating; 978 return container->parent == workspace->sway_workspace->floating;
978} 979}
980
981struct wlr_box *container_get_box(struct sway_container *container) {
982 struct wlr_box *box = calloc(1, sizeof(struct wlr_box));
983 box->x = container->x;
984 box->y = container->y;
985 box->width = container->width;
986 box->height = container->height;
987 return box;
988}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index d1ad044d..14631ad4 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -22,7 +22,7 @@ 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_root(); 25 arrange_and_commit(&root_container);
26} 26}
27 27
28void layout_init(void) { 28void layout_init(void) {
@@ -30,7 +30,9 @@ void layout_init(void) {
30 root_container.type = C_ROOT; 30 root_container.type = C_ROOT;
31 root_container.layout = L_NONE; 31 root_container.layout = L_NONE;
32 root_container.name = strdup("root"); 32 root_container.name = strdup("root");
33 root_container.instructions = create_list();
33 root_container.children = create_list(); 34 root_container.children = create_list();
35 root_container.current.children = create_list();
34 wl_signal_init(&root_container.events.destroy); 36 wl_signal_init(&root_container.events.destroy);
35 37
36 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); 38 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
@@ -57,18 +59,17 @@ static int index_child(const struct sway_container *child) {
57 return -1; 59 return -1;
58} 60}
59 61
60static void container_handle_fullscreen_reparent(struct sway_container *viewcon, 62static void container_handle_fullscreen_reparent(struct sway_container *con,
61 struct sway_container *old_parent) { 63 struct sway_container *old_parent) {
62 if (viewcon->type != C_VIEW || !viewcon->sway_view->is_fullscreen) { 64 if (con->type != C_VIEW || !con->sway_view->is_fullscreen) {
63 return; 65 return;
64 } 66 }
65 struct sway_view *view = viewcon->sway_view; 67 struct sway_view *view = con->sway_view;
66 struct sway_container *old_workspace = old_parent; 68 struct sway_container *old_workspace = old_parent;
67 if (old_workspace && old_workspace->type != C_WORKSPACE) { 69 if (old_workspace && old_workspace->type != C_WORKSPACE) {
68 old_workspace = container_parent(old_workspace, C_WORKSPACE); 70 old_workspace = container_parent(old_workspace, C_WORKSPACE);
69 } 71 }
70 struct sway_container *new_workspace = container_parent(view->swayc, 72 struct sway_container *new_workspace = container_parent(con, C_WORKSPACE);
71 C_WORKSPACE);
72 if (old_workspace == new_workspace) { 73 if (old_workspace == new_workspace) {
73 return; 74 return;
74 } 75 }
@@ -79,15 +80,19 @@ static void container_handle_fullscreen_reparent(struct sway_container *viewcon,
79 80
80 // Mark the new workspace as fullscreen 81 // Mark the new workspace as fullscreen
81 if (new_workspace->sway_workspace->fullscreen) { 82 if (new_workspace->sway_workspace->fullscreen) {
82 view_set_fullscreen_raw( 83 view_set_fullscreen(new_workspace->sway_workspace->fullscreen, false);
83 new_workspace->sway_workspace->fullscreen, false);
84 } 84 }
85 new_workspace->sway_workspace->fullscreen = view; 85 new_workspace->sway_workspace->fullscreen = view;
86 // Resize view to new output dimensions 86 // Resize view to new output dimensions
87 struct sway_container *output = new_workspace->parent; 87 struct sway_container *output = new_workspace->parent;
88 view_configure(view, 0, 0, output->width, output->height); 88 view->x = output->x;
89 view->swayc->width = output->width; 89 view->y = output->y;
90 view->swayc->height = output->height; 90 view->width = output->width;
91 view->height = output->height;
92 con->x = output->x;
93 con->y = output->y;
94 con->width = output->width;
95 con->height = output->height;
91} 96}
92 97
93void container_insert_child(struct sway_container *parent, 98void container_insert_child(struct sway_container *parent,
@@ -189,18 +194,7 @@ void container_move_to(struct sway_container *container,
189 } 194 }
190 container_notify_subtree_changed(old_parent); 195 container_notify_subtree_changed(old_parent);
191 container_notify_subtree_changed(new_parent); 196 container_notify_subtree_changed(new_parent);
192 if (old_parent) { 197
193 if (old_parent->type == C_OUTPUT) {
194 arrange_output(old_parent);
195 } else {
196 arrange_children_of(old_parent);
197 }
198 }
199 if (new_parent->type == C_OUTPUT) {
200 arrange_output(new_parent);
201 } else {
202 arrange_children_of(new_parent);
203 }
204 // If view was moved to a fullscreen workspace, refocus the fullscreen view 198 // If view was moved to a fullscreen workspace, refocus the fullscreen view
205 struct sway_container *new_workspace = container; 199 struct sway_container *new_workspace = container;
206 if (new_workspace->type != C_WORKSPACE) { 200 if (new_workspace->type != C_WORKSPACE) {
@@ -215,7 +209,8 @@ void container_move_to(struct sway_container *container,
215 if (focus_ws->type != C_WORKSPACE) { 209 if (focus_ws->type != C_WORKSPACE) {
216 focus_ws = container_parent(focus_ws, C_WORKSPACE); 210 focus_ws = container_parent(focus_ws, C_WORKSPACE);
217 } 211 }
218 seat_set_focus(seat, new_workspace->sway_workspace->fullscreen->swayc); 212 seat_set_focus(seat,
213 new_workspace->sway_workspace->fullscreen->swayc);
219 if (focus_ws != new_workspace) { 214 if (focus_ws != new_workspace) {
220 seat_set_focus(seat, focus); 215 seat_set_focus(seat, focus);
221 } 216 }
@@ -309,7 +304,6 @@ static void workspace_rejigger(struct sway_container *ws,
309 container_reap_empty_recursive(original_parent); 304 container_reap_empty_recursive(original_parent);
310 wl_signal_emit(&child->events.reparent, original_parent); 305 wl_signal_emit(&child->events.reparent, original_parent);
311 container_create_notify(new_parent); 306 container_create_notify(new_parent);
312 arrange_workspace(ws);
313} 307}
314 308
315static void move_out_of_tabs_stacks(struct sway_container *container, 309static void move_out_of_tabs_stacks(struct sway_container *container,
@@ -320,11 +314,6 @@ static void move_out_of_tabs_stacks(struct sway_container *container,
320 wlr_log(L_DEBUG, "Changing layout of %zd", current->parent->id); 314 wlr_log(L_DEBUG, "Changing layout of %zd", current->parent->id);
321 current->parent->layout = move_dir == 315 current->parent->layout = move_dir ==
322 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 316 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
323 if (current->parent->type == C_WORKSPACE) {
324 arrange_workspace(current->parent);
325 } else {
326 arrange_children_of(current->parent);
327 }
328 return; 317 return;
329 } 318 }
330 319
@@ -340,11 +329,6 @@ static void move_out_of_tabs_stacks(struct sway_container *container,
340 container_flatten(new_parent->parent); 329 container_flatten(new_parent->parent);
341 } 330 }
342 container_create_notify(new_parent); 331 container_create_notify(new_parent);
343 if (is_workspace) {
344 arrange_workspace(new_parent->parent);
345 } else {
346 arrange_children_of(new_parent);
347 }
348 container_notify_subtree_changed(new_parent); 332 container_notify_subtree_changed(new_parent);
349} 333}
350 334
@@ -368,10 +352,7 @@ void container_move(struct sway_container *container,
368 352
369 struct sway_container *new_parent = container_flatten(parent); 353 struct sway_container *new_parent = container_flatten(parent);
370 if (new_parent != parent) { 354 if (new_parent != parent) {
371 // Special case: we were the last one in this container, so flatten it 355 // Special case: we were the last one in this container, so leave
372 // and leave
373 arrange_children_of(new_parent);
374 update_debug_tree();
375 return; 356 return;
376 } 357 }
377 358
@@ -453,12 +434,9 @@ void container_move(struct sway_container *container,
453 wlr_log(L_DEBUG, "Hit limit, " 434 wlr_log(L_DEBUG, "Hit limit, "
454 "promoting descendant to sibling"); 435 "promoting descendant to sibling");
455 // Special case 436 // Special case
456 struct sway_container *old_parent = container->parent;
457 container_insert_child(current->parent, container, 437 container_insert_child(current->parent, container,
458 index + (offs < 0 ? 0 : 1)); 438 index + (offs < 0 ? 0 : 1));
459 container->width = container->height = 0; 439 container->width = container->height = 0;
460 arrange_children_of(current->parent);
461 arrange_children_of(old_parent);
462 return; 440 return;
463 } 441 }
464 } else { 442 } else {
@@ -492,14 +470,11 @@ void container_move(struct sway_container *container,
492 wlr_log(L_DEBUG, "Swapping siblings"); 470 wlr_log(L_DEBUG, "Swapping siblings");
493 sibling->parent->children->items[index + offs] = container; 471 sibling->parent->children->items[index + offs] = container;
494 sibling->parent->children->items[index] = sibling; 472 sibling->parent->children->items[index] = sibling;
495 arrange_children_of(sibling->parent);
496 } else { 473 } else {
497 wlr_log(L_DEBUG, "Promoting to sibling of cousin"); 474 wlr_log(L_DEBUG, "Promoting to sibling of cousin");
498 container_insert_child(sibling->parent, container, 475 container_insert_child(sibling->parent, container,
499 index_child(sibling) + (offs > 0 ? 0 : 1)); 476 index_child(sibling) + (offs > 0 ? 0 : 1));
500 container->width = container->height = 0; 477 container->width = container->height = 0;
501 arrange_children_of(sibling->parent);
502 arrange_children_of(old_parent);
503 } 478 }
504 sibling = NULL; 479 sibling = NULL;
505 break; 480 break;
@@ -513,8 +488,6 @@ void container_move(struct sway_container *container,
513 "(move dir: %d)", limit, move_dir); 488 "(move dir: %d)", limit, move_dir);
514 container_insert_child(sibling, container, limit); 489 container_insert_child(sibling, container, limit);
515 container->width = container->height = 0; 490 container->width = container->height = 0;
516 arrange_children_of(sibling);
517 arrange_children_of(old_parent);
518 sibling = NULL; 491 sibling = NULL;
519 } else { 492 } else {
520 wlr_log(L_DEBUG, "Reparenting container (perpendicular)"); 493 wlr_log(L_DEBUG, "Reparenting container (perpendicular)");
@@ -538,8 +511,6 @@ void container_move(struct sway_container *container,
538 container_add_child(sibling, container); 511 container_add_child(sibling, container);
539 } 512 }
540 container->width = container->height = 0; 513 container->width = container->height = 0;
541 arrange_children_of(sibling);
542 arrange_children_of(old_parent);
543 sibling = NULL; 514 sibling = NULL;
544 } 515 }
545 break; 516 break;
@@ -864,7 +835,6 @@ struct sway_container *container_split(struct sway_container *child,
864 // Special case: this just behaves like splitt 835 // Special case: this just behaves like splitt
865 child->prev_layout = child->layout; 836 child->prev_layout = child->layout;
866 child->layout = layout; 837 child->layout = layout;
867 arrange_children_of(child);
868 return child; 838 return child;
869 } 839 }
870 840
@@ -882,7 +852,7 @@ struct sway_container *container_split(struct sway_container *child,
882 852
883 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 853 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
884 bool set_focus = (seat_get_focus(seat) == child); 854 bool set_focus = (seat_get_focus(seat) == child);
885 855
886 add_gaps(cont); 856 add_gaps(cont);
887 857
888 if (child->type == C_WORKSPACE) { 858 if (child->type == C_WORKSPACE) {
@@ -912,7 +882,6 @@ struct sway_container *container_split(struct sway_container *child,
912 } 882 }
913 883
914 container_notify_subtree_changed(cont); 884 container_notify_subtree_changed(cont);
915 arrange_children_of(cont);
916 return cont; 885 return cont;
917} 886}
918 887
@@ -1050,9 +1019,6 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {
1050 prev_workspace_name = stored_prev_name; 1019 prev_workspace_name = stored_prev_name;
1051 } 1020 }
1052 1021
1053 arrange_children_of(con1->parent);
1054 arrange_children_of(con2->parent);
1055
1056 if (fs1 && con2->type == C_VIEW) { 1022 if (fs1 && con2->type == C_VIEW) {
1057 view_set_fullscreen(con2->sway_view, true); 1023 view_set_fullscreen(con2->sway_view, true);
1058 } 1024 }
diff --git a/sway/tree/output.c b/sway/tree/output.c
index ed7e941e..e2927cdb 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -26,12 +26,9 @@ static void restore_workspaces(struct sway_container *output) {
26 j--; 26 j--;
27 } 27 }
28 } 28 }
29
30 arrange_output(other);
31 } 29 }
32 30
33 container_sort_workspaces(output); 31 container_sort_workspaces(output);
34 arrange_output(output);
35} 32}
36 33
37struct sway_container *output_create( 34struct sway_container *output_create(
diff --git a/sway/tree/view.c b/sway/tree/view.c
index c9c82405..cb36f123 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -28,20 +28,22 @@ void view_init(struct sway_view *view, enum sway_view_type type,
28 wl_signal_init(&view->events.unmap); 28 wl_signal_init(&view->events.unmap);
29} 29}
30 30
31void view_destroy(struct sway_view *view) { 31void view_free(struct sway_view *view) {
32 if (view == NULL) { 32 if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) {
33 return; 33 return;
34 } 34 }
35 35 if (!sway_assert(view->destroying,
36 if (view->surface != NULL) { 36 "Tried to free view which wasn't marked as destroying")) {
37 view_unmap(view); 37 return;
38 }
39 if (!sway_assert(view->swayc == NULL,
40 "Tried to free view which still has a swayc "
41 "(might have a pending transaction?)")) {
42 return;
38 } 43 }
39
40 list_free(view->executed_criteria); 44 list_free(view->executed_criteria);
41 45
42 for (int i = 0; i < view->marks->length; ++i) { 46 list_foreach(view->marks, free);
43 free(view->marks->items[i]);
44 }
45 list_free(view->marks); 47 list_free(view->marks);
46 48
47 wlr_texture_destroy(view->marks_focused); 49 wlr_texture_destroy(view->marks_focused);
@@ -49,15 +51,34 @@ void view_destroy(struct sway_view *view) {
49 wlr_texture_destroy(view->marks_unfocused); 51 wlr_texture_destroy(view->marks_unfocused);
50 wlr_texture_destroy(view->marks_urgent); 52 wlr_texture_destroy(view->marks_urgent);
51 53
52 container_destroy(view->swayc); 54 if (view->impl->free) {
53 55 view->impl->free(view);
54 if (view->impl->destroy) {
55 view->impl->destroy(view);
56 } else { 56 } else {
57 free(view); 57 free(view);
58 } 58 }
59} 59}
60 60
61/**
62 * The view may or may not be involved in a transaction. For example, a view may
63 * unmap then attempt to destroy itself before we've applied the new layout. If
64 * an unmapping view is still involved in a transaction then it'll still have a
65 * swayc.
66 *
67 * If there's no transaction we can simply free the view. Otherwise the
68 * destroying flag will make the view get freed when the transaction is
69 * finished.
70 */
71void view_destroy(struct sway_view *view) {
72 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
73 return;
74 }
75 view->destroying = true;
76
77 if (!view->swayc) {
78 view_free(view);
79 }
80}
81
61const char *view_get_title(struct sway_view *view) { 82const char *view_get_title(struct sway_view *view) {
62 if (view->impl->get_string_prop) { 83 if (view->impl->get_string_prop) {
63 return view->impl->get_string_prop(view, VIEW_PROP_TITLE); 84 return view->impl->get_string_prop(view, VIEW_PROP_TITLE);
@@ -119,29 +140,30 @@ const char *view_get_shell(struct sway_view *view) {
119 return "unknown"; 140 return "unknown";
120} 141}
121 142
122void view_configure(struct sway_view *view, double lx, double ly, int width, 143uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
123 int height) { 144 int height) {
124 if (view->impl->configure) { 145 if (view->impl->configure) {
125 view->impl->configure(view, lx, ly, width, height); 146 return view->impl->configure(view, lx, ly, width, height);
126 } 147 }
148 return 0;
127} 149}
128 150
129static void view_autoconfigure_floating(struct sway_view *view) { 151void view_init_floating(struct sway_view *view) {
130 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 152 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
131 int max_width = ws->width * 0.6666; 153 int max_width = ws->width * 0.6666;
132 int max_height = ws->height * 0.6666; 154 int max_height = ws->height * 0.6666;
133 int width = 155 view->width =
134 view->natural_width > max_width ? max_width : view->natural_width; 156 view->natural_width > max_width ? max_width : view->natural_width;
135 int height = 157 view->height =
136 view->natural_height > max_height ? max_height : view->natural_height; 158 view->natural_height > max_height ? max_height : view->natural_height;
137 int lx = ws->x + (ws->width - width) / 2; 159 view->x = ws->x + (ws->width - view->width) / 2;
138 int ly = ws->y + (ws->height - height) / 2; 160 view->y = ws->y + (ws->height - view->height) / 2;
139 161
140 // If the view's border is B_NONE then these properties are ignored. 162 // If the view's border is B_NONE then these properties are ignored.
141 view->border_top = view->border_bottom = true; 163 view->border_top = view->border_bottom = true;
142 view->border_left = view->border_right = true; 164 view->border_left = view->border_right = true;
143 165
144 view_configure(view, lx, ly, width, height); 166 container_set_geometry_from_floating_view(view->swayc);
145} 167}
146 168
147void view_autoconfigure(struct sway_view *view) { 169void view_autoconfigure(struct sway_view *view) {
@@ -153,12 +175,14 @@ void view_autoconfigure(struct sway_view *view) {
153 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 175 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
154 176
155 if (view->is_fullscreen) { 177 if (view->is_fullscreen) {
156 view_configure(view, output->x, output->y, output->width, output->height); 178 view->x = output->x;
179 view->y = output->y;
180 view->width = output->width;
181 view->height = output->height;
157 return; 182 return;
158 } 183 }
159 184
160 if (container_is_floating(view->swayc)) { 185 if (container_is_floating(view->swayc)) {
161 view_autoconfigure_floating(view);
162 return; 186 return;
163 } 187 }
164 188
@@ -178,20 +202,22 @@ void view_autoconfigure(struct sway_view *view) {
178 } 202 }
179 } 203 }
180 204
205 struct sway_container *con = view->swayc;
206
181 view->border_top = view->border_bottom = true; 207 view->border_top = view->border_bottom = true;
182 view->border_left = view->border_right = true; 208 view->border_left = view->border_right = true;
183 if (config->hide_edge_borders == E_BOTH 209 if (config->hide_edge_borders == E_BOTH
184 || config->hide_edge_borders == E_VERTICAL 210 || config->hide_edge_borders == E_VERTICAL
185 || (config->hide_edge_borders == E_SMART && !other_views)) { 211 || (config->hide_edge_borders == E_SMART && !other_views)) {
186 view->border_left = view->swayc->x != ws->x; 212 view->border_left = con->x != ws->x;
187 int right_x = view->swayc->x + view->swayc->width; 213 int right_x = con->x + con->width;
188 view->border_right = right_x != ws->x + ws->width; 214 view->border_right = right_x != ws->x + ws->width;
189 } 215 }
190 if (config->hide_edge_borders == E_BOTH 216 if (config->hide_edge_borders == E_BOTH
191 || config->hide_edge_borders == E_HORIZONTAL 217 || config->hide_edge_borders == E_HORIZONTAL
192 || (config->hide_edge_borders == E_SMART && !other_views)) { 218 || (config->hide_edge_borders == E_SMART && !other_views)) {
193 view->border_top = view->swayc->y != ws->y; 219 view->border_top = con->y != ws->y;
194 int bottom_y = view->swayc->y + view->swayc->height; 220 int bottom_y = con->y + con->height;
195 view->border_bottom = bottom_y != ws->y + ws->height; 221 view->border_bottom = bottom_y != ws->y + ws->height;
196 } 222 }
197 223
@@ -202,45 +228,44 @@ void view_autoconfigure(struct sway_view *view) {
202 // In a tabbed or stacked container, the swayc's y is the top of the title 228 // In a tabbed or stacked container, the swayc's y is the top of the title
203 // area. We have to offset the surface y by the height of the title bar, and 229 // area. We have to offset the surface y by the height of the title bar, and
204 // disable any top border because we'll always have the title bar. 230 // disable any top border because we'll always have the title bar.
205 if (view->swayc->parent->layout == L_TABBED) { 231 if (con->parent->layout == L_TABBED) {
206 y_offset = container_titlebar_height(); 232 y_offset = container_titlebar_height();
207 view->border_top = false; 233 view->border_top = false;
208 } else if (view->swayc->parent->layout == L_STACKED) { 234 } else if (con->parent->layout == L_STACKED) {
209 y_offset = container_titlebar_height() 235 y_offset = container_titlebar_height() * con->parent->children->length;
210 * view->swayc->parent->children->length;
211 view->border_top = false; 236 view->border_top = false;
212 } 237 }
213 238
214 switch (view->border) { 239 switch (view->border) {
215 case B_NONE: 240 case B_NONE:
216 x = view->swayc->x; 241 x = con->x;
217 y = view->swayc->y + y_offset; 242 y = con->y + y_offset;
218 width = view->swayc->width; 243 width = con->width;
219 height = view->swayc->height - y_offset; 244 height = con->height - y_offset;
220 break; 245 break;
221 case B_PIXEL: 246 case B_PIXEL:
222 x = view->swayc->x + view->border_thickness * view->border_left; 247 x = con->x + view->border_thickness * view->border_left;
223 y = view->swayc->y + view->border_thickness * view->border_top + y_offset; 248 y = con->y + view->border_thickness * view->border_top + y_offset;
224 width = view->swayc->width 249 width = con->width
225 - view->border_thickness * view->border_left 250 - view->border_thickness * view->border_left
226 - view->border_thickness * view->border_right; 251 - view->border_thickness * view->border_right;
227 height = view->swayc->height - y_offset 252 height = con->height - y_offset
228 - view->border_thickness * view->border_top 253 - view->border_thickness * view->border_top
229 - view->border_thickness * view->border_bottom; 254 - view->border_thickness * view->border_bottom;
230 break; 255 break;
231 case B_NORMAL: 256 case B_NORMAL:
232 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border 257 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border
233 x = view->swayc->x + view->border_thickness * view->border_left; 258 x = con->x + view->border_thickness * view->border_left;
234 width = view->swayc->width 259 width = con->width
235 - view->border_thickness * view->border_left 260 - view->border_thickness * view->border_left
236 - view->border_thickness * view->border_right; 261 - view->border_thickness * view->border_right;
237 if (y_offset) { 262 if (y_offset) {
238 y = view->swayc->y + y_offset; 263 y = con->y + y_offset;
239 height = view->swayc->height - y_offset 264 height = con->height - y_offset
240 - view->border_thickness * view->border_bottom; 265 - view->border_thickness * view->border_bottom;
241 } else { 266 } else {
242 y = view->swayc->y + container_titlebar_height(); 267 y = con->y + container_titlebar_height();
243 height = view->swayc->height - container_titlebar_height() 268 height = con->height - container_titlebar_height()
244 - view->border_thickness * view->border_bottom; 269 - view->border_thickness * view->border_bottom;
245 } 270 }
246 break; 271 break;
@@ -248,7 +273,8 @@ void view_autoconfigure(struct sway_view *view) {
248 273
249 view->x = x; 274 view->x = x;
250 view->y = y; 275 view->y = y;
251 view_configure(view, x, y, width, height); 276 view->width = width;
277 view->height = height;
252} 278}
253 279
254void view_set_activated(struct sway_view *view, bool activated) { 280void view_set_activated(struct sway_view *view, bool activated) {
@@ -257,8 +283,7 @@ void view_set_activated(struct sway_view *view, bool activated) {
257 } 283 }
258} 284}
259 285
260// Set fullscreen, but without IPC events or arranging windows. 286void view_set_fullscreen(struct sway_view *view, bool fullscreen) {
261void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) {
262 if (view->is_fullscreen == fullscreen) { 287 if (view->is_fullscreen == fullscreen) {
263 return; 288 return;
264 } 289 }
@@ -304,27 +329,17 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) {
304 } else { 329 } else {
305 workspace->sway_workspace->fullscreen = NULL; 330 workspace->sway_workspace->fullscreen = NULL;
306 if (container_is_floating(view->swayc)) { 331 if (container_is_floating(view->swayc)) {
307 view_configure(view, view->saved_x, view->saved_y, 332 view->x = view->saved_x;
308 view->saved_width, view->saved_height); 333 view->y = view->saved_y;
334 view->width = view->saved_width;
335 view->height = view->saved_height;
336 container_set_geometry_from_floating_view(view->swayc);
309 } else { 337 } else {
310 view->swayc->width = view->swayc->saved_width; 338 view->swayc->width = view->swayc->saved_width;
311 view->swayc->height = view->swayc->saved_height; 339 view->swayc->height = view->swayc->saved_height;
312 view_autoconfigure(view);
313 } 340 }
314 } 341 }
315}
316
317void view_set_fullscreen(struct sway_view *view, bool fullscreen) {
318 if (view->is_fullscreen == fullscreen) {
319 return;
320 }
321 342
322 view_set_fullscreen_raw(view, fullscreen);
323
324 struct sway_container *workspace =
325 container_parent(view->swayc, C_WORKSPACE);
326 arrange_workspace(workspace);
327 output_damage_whole(workspace->parent->sway_output);
328 ipc_event_window(view->swayc, "fullscreen_mode"); 343 ipc_event_window(view->swayc, "fullscreen_mode");
329} 344}
330 345
@@ -354,6 +369,9 @@ static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
354 369
355void view_for_each_surface(struct sway_view *view, 370void view_for_each_surface(struct sway_view *view,
356 wlr_surface_iterator_func_t iterator, void *user_data) { 371 wlr_surface_iterator_func_t iterator, void *user_data) {
372 if (!view->surface) {
373 return;
374 }
357 if (view->impl->for_each_surface) { 375 if (view->impl->for_each_surface) {
358 view->impl->for_each_surface(view, iterator, user_data); 376 view->impl->for_each_surface(view, iterator, user_data);
359 } else { 377 } else {
@@ -507,8 +525,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
507 525
508 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 526 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
509 container_set_floating(view->swayc, true); 527 container_set_floating(view->swayc, true);
510 } else {
511 arrange_children_of(cont->parent);
512 } 528 }
513 529
514 input_manager_set_focus(input_manager, cont); 530 input_manager_set_focus(input_manager, cont);
@@ -520,15 +536,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
520 container_notify_subtree_changed(view->swayc->parent); 536 container_notify_subtree_changed(view->swayc->parent);
521 view_execute_criteria(view); 537 view_execute_criteria(view);
522 538
523 container_damage_whole(cont);
524 view_handle_container_reparent(&view->container_reparent, NULL); 539 view_handle_container_reparent(&view->container_reparent, NULL);
525} 540}
526 541
527void view_unmap(struct sway_view *view) { 542struct sway_container *view_unmap(struct sway_view *view) {
528 if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) {
529 return;
530 }
531
532 wl_signal_emit(&view->events.unmap, view); 543 wl_signal_emit(&view->events.unmap, view);
533 544
534 if (view->is_fullscreen) { 545 if (view->is_fullscreen) {
@@ -536,26 +547,10 @@ void view_unmap(struct sway_view *view) {
536 ws->sway_workspace->fullscreen = NULL; 547 ws->sway_workspace->fullscreen = NULL;
537 } 548 }
538 549
539 container_damage_whole(view->swayc);
540
541 wl_list_remove(&view->surface_new_subsurface.link); 550 wl_list_remove(&view->surface_new_subsurface.link);
542 wl_list_remove(&view->container_reparent.link); 551 wl_list_remove(&view->container_reparent.link);
543 552
544 struct sway_container *parent = container_destroy(view->swayc); 553 return container_destroy(view->swayc);
545
546 view->swayc = NULL;
547 view->surface = NULL;
548
549 if (view->title_format) {
550 free(view->title_format);
551 view->title_format = NULL;
552 }
553
554 if (parent->type == C_OUTPUT) {
555 arrange_output(parent);
556 } else {
557 arrange_children_of(parent);
558 }
559} 554}
560 555
561void view_update_position(struct sway_view *view, double lx, double ly) { 556void view_update_position(struct sway_view *view, double lx, double ly) {
@@ -929,7 +924,7 @@ void view_update_marks_textures(struct sway_view *view) {
929} 924}
930 925
931bool view_is_visible(struct sway_view *view) { 926bool view_is_visible(struct sway_view *view) {
932 if (!view->swayc) { 927 if (!view->swayc || view->swayc->destroying) {
933 return false; 928 return false;
934 } 929 }
935 struct sway_container *workspace = 930 struct sway_container *workspace =
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 9ba210fd..5eb4be0f 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -425,11 +425,14 @@ bool workspace_switch(struct sway_container *workspace) {
425 } 425 }
426 seat_set_focus(seat, next); 426 seat_set_focus(seat, next);
427 struct sway_container *output = container_parent(workspace, C_OUTPUT); 427 struct sway_container *output = container_parent(workspace, C_OUTPUT);
428 arrange_output(output); 428 arrange_and_commit(output);
429 return true; 429 return true;
430} 430}
431 431
432bool workspace_is_visible(struct sway_container *ws) { 432bool workspace_is_visible(struct sway_container *ws) {
433 if (ws->destroying) {
434 return false;
435 }
433 struct sway_container *output = container_parent(ws, C_OUTPUT); 436 struct sway_container *output = container_parent(ws, C_OUTPUT);
434 struct sway_seat *seat = input_manager_current_seat(input_manager); 437 struct sway_seat *seat = input_manager_current_seat(input_manager);
435 struct sway_container *focus = seat_get_focus_inactive(seat, output); 438 struct sway_container *focus = seat_get_focus_inactive(seat, output);