aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/desktop/transaction.h62
-rw-r--r--include/sway/input/seat.h11
-rw-r--r--include/sway/output.h2
-rw-r--r--include/sway/server.h9
-rw-r--r--include/sway/tree/arrange.h34
-rw-r--r--include/sway/tree/container.h48
-rw-r--r--include/sway/tree/view.h22
-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.c61
-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.c316
-rw-r--r--sway/desktop/transaction.c403
-rw-r--r--sway/desktop/xdg_shell.c75
-rw-r--r--sway/desktop/xdg_shell_v6.c73
-rw-r--r--sway/desktop/xwayland.c71
-rw-r--r--sway/input/seat.c12
-rw-r--r--sway/meson.build1
-rw-r--r--sway/server.c11
-rw-r--r--sway/tree/arrange.c338
-rw-r--r--sway/tree/container.c214
-rw-r--r--sway/tree/layout.c76
-rw-r--r--sway/tree/output.c3
-rw-r--r--sway/tree/view.c176
-rw-r--r--sway/tree/workspace.c5
35 files changed, 1419 insertions, 670 deletions
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
new file mode 100644
index 00000000..fcfed297
--- /dev/null
+++ b/include/sway/desktop/transaction.h
@@ -0,0 +1,62 @@
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 * Submit a transaction to the client views for configuration.
35 */
36void transaction_commit(struct sway_transaction *transaction);
37
38/**
39 * Notify the transaction system that a view is ready for the new layout.
40 *
41 * When all views in the transaction are ready, the layout will be applied.
42 */
43void transaction_notify_view_ready(struct sway_view *view, uint32_t serial);
44
45/**
46 * Notify the transaction system that a view is ready for the new layout, but
47 * identifying the instruction by width and height rather than by serial.
48 *
49 * This is used by xwayland views, as they don't have serials.
50 */
51void transaction_notify_view_ready_by_size(struct sway_view *view,
52 int width, int height);
53
54/**
55 * Get the texture that should be rendered for a view.
56 *
57 * In most cases this will return the normal live texture for a view, but if the
58 * view is in a transaction then it'll return a saved texture.
59 */
60struct wlr_texture *transaction_get_texture(struct sway_view *view);
61
62#endif
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 1f7792ba..0e440701 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -119,6 +119,17 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,
119 struct sway_container *container); 119 struct sway_container *container);
120 120
121/** 121/**
122 * Return the immediate child of container which was most recently focused, with
123 * fallback to selecting the child in the parent's `current` (rendered) children
124 * list.
125 *
126 * This is useful for when a tabbed container and its children are destroyed but
127 * still being rendered, and we have to render an appropriate child.
128 */
129struct sway_container *seat_get_active_current_child(struct sway_seat *seat,
130 struct sway_container *container);
131
132/**
122 * Iterate over the focus-inactive children of the container calling the 133 * Iterate over the focus-inactive children of the container calling the
123 * function on each. 134 * function on each.
124 */ 135 */
diff --git a/include/sway/output.h b/include/sway/output.h
index 8180ce3d..19fc5e99 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -46,6 +46,8 @@ void output_damage_surface(struct sway_output *output, double ox, double oy,
46void output_damage_from_view(struct sway_output *output, 46void output_damage_from_view(struct sway_output *output,
47 struct sway_view *view); 47 struct sway_view *view);
48 48
49void output_damage_box(struct sway_output *output, struct wlr_box *box);
50
49void output_damage_whole_container(struct sway_output *output, 51void output_damage_whole_container(struct sway_output *output,
50 struct sway_container *con); 52 struct sway_container *con);
51 53
diff --git a/include/sway/server.h b/include/sway/server.h
index b016aba8..1e1aa3cc 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -11,6 +11,7 @@
11#include <wlr/types/wlr_xdg_shell.h> 11#include <wlr/types/wlr_xdg_shell.h>
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 "list.h"
14#include "sway/xwayland.h" 15#include "sway/xwayland.h"
15 16
16struct sway_server { 17struct sway_server {
@@ -40,6 +41,14 @@ struct sway_server {
40 struct sway_xwayland xwayland; 41 struct sway_xwayland xwayland;
41 struct wl_listener xwayland_surface; 42 struct wl_listener xwayland_surface;
42 struct wl_listener xwayland_ready; 43 struct wl_listener xwayland_ready;
44
45 bool debug_txn_timings;
46
47 list_t *transactions;
48
49 // When a view is being destroyed and is waiting for a transaction to
50 // complete it will be stored here.
51 list_t *destroying_containers;
43}; 52};
44 53
45struct sway_server server; 54struct sway_server server;
diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h
index a14bc5dc..58235642 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,27 @@ 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); 20 * You must set the desired state of the container before calling
20 21 * arrange_windows, then don't change any state-tracked properties in the
21// Arrange layout for all the children of the given workspace/container 22 * container until you've called transaction_commit.
22void arrange_children_of(struct sway_container *parent); 23 */
24void arrange_windows(struct sway_container *container,
25 struct sway_transaction *transaction);
26
27/**
28 * Arrange layout for the given container and commit the transaction.
29 *
30 * This function is a wrapper around arrange_windows, and handles creating and
31 * committing the transaction for you. Use this function if you're only doing
32 * one arrange operation.
33 */
34void arrange_and_commit(struct sway_container *container);
23 35
24#endif 36#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index b3406bbe..728daa84 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 */
298void container_get_box(struct sway_container *container, struct wlr_box *box);
299
256#endif 300#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 3df38e2d..1bcb0582 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -29,8 +29,8 @@ 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);
@@ -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,6 +239,8 @@ 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);
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..4061df3a 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"
@@ -88,6 +90,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
88 } 90 }
89 free(ws_name); 91 free(ws_name);
90 struct sway_container *old_parent = current->parent; 92 struct sway_container *old_parent = current->parent;
93 struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
91 struct sway_container *destination = seat_get_focus_inactive( 94 struct sway_container *destination = seat_get_focus_inactive(
92 config->handler_context.seat, ws); 95 config->handler_context.seat, ws);
93 container_move_to(current, destination); 96 container_move_to(current, destination);
@@ -96,6 +99,15 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
96 seat_set_focus(config->handler_context.seat, focus); 99 seat_set_focus(config->handler_context.seat, focus);
97 container_reap_empty(old_parent); 100 container_reap_empty(old_parent);
98 container_reap_empty(destination->parent); 101 container_reap_empty(destination->parent);
102
103 // TODO: Ideally we would arrange the surviving parent after reaping,
104 // but container_reap_empty does not return it, so we arrange the
105 // workspace instead.
106 struct sway_transaction *txn = transaction_create();
107 arrange_windows(old_ws, txn);
108 arrange_windows(destination->parent, txn);
109 transaction_commit(txn);
110
99 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 111 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
100 } else if (strcasecmp(argv[1], "to") == 0 112 } else if (strcasecmp(argv[1], "to") == 0
101 && strcasecmp(argv[2], "output") == 0) { 113 && strcasecmp(argv[2], "output") == 0) {
@@ -121,10 +133,20 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
121 focus = destination->children->items[0]; 133 focus = destination->children->items[0];
122 } 134 }
123 struct sway_container *old_parent = current->parent; 135 struct sway_container *old_parent = current->parent;
136 struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
124 container_move_to(current, focus); 137 container_move_to(current, focus);
125 seat_set_focus(config->handler_context.seat, old_parent); 138 seat_set_focus(config->handler_context.seat, old_parent);
126 container_reap_empty(old_parent); 139 container_reap_empty(old_parent);
127 container_reap_empty(focus->parent); 140 container_reap_empty(focus->parent);
141
142 // TODO: Ideally we would arrange the surviving parent after reaping,
143 // but container_reap_empty does not return it, so we arrange the
144 // workspace instead.
145 struct sway_transaction *txn = transaction_create();
146 arrange_windows(old_ws, txn);
147 arrange_windows(focus->parent, txn);
148 transaction_commit(txn);
149
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 150 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 } 151 }
130 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 152 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
@@ -152,6 +174,37 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
152 current = container_parent(current, C_WORKSPACE); 174 current = container_parent(current, C_WORKSPACE);
153 } 175 }
154 container_move_to(current, destination); 176 container_move_to(current, destination);
177
178 struct sway_transaction *txn = transaction_create();
179 arrange_windows(source, txn);
180 arrange_windows(destination, txn);
181 transaction_commit(txn);
182
183 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
184}
185
186static struct cmd_results *move_in_direction(struct sway_container *container,
187 enum wlr_direction direction, int move_amt) {
188 if (container->type == C_WORKSPACE) {
189 return cmd_results_new(CMD_FAILURE, "move",
190 "Cannot move workspaces in a direction");
191 }
192 // For simplicity, we'll arrange the entire workspace. The reason for this
193 // is moving the container might reap the old parent, and container_move
194 // does not return a surviving parent.
195 // TODO: Make container_move return the surviving parent so we can arrange
196 // just that.
197 struct sway_container *old_ws = container_parent(container, C_WORKSPACE);
198 container_move(container, direction, move_amt);
199 struct sway_container *new_ws = container_parent(container, C_WORKSPACE);
200
201 struct sway_transaction *txn = transaction_create();
202 arrange_windows(old_ws, txn);
203 if (new_ws != old_ws) {
204 arrange_windows(new_ws, txn);
205 }
206 transaction_commit(txn);
207
155 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 208 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
156} 209}
157 210
@@ -173,13 +226,13 @@ struct cmd_results *cmd_move(int argc, char **argv) {
173 } 226 }
174 227
175 if (strcasecmp(argv[0], "left") == 0) { 228 if (strcasecmp(argv[0], "left") == 0) {
176 container_move(current, MOVE_LEFT, move_amt); 229 return move_in_direction(current, MOVE_LEFT, move_amt);
177 } else if (strcasecmp(argv[0], "right") == 0) { 230 } else if (strcasecmp(argv[0], "right") == 0) {
178 container_move(current, MOVE_RIGHT, move_amt); 231 return move_in_direction(current, MOVE_RIGHT, move_amt);
179 } else if (strcasecmp(argv[0], "up") == 0) { 232 } else if (strcasecmp(argv[0], "up") == 0) {
180 container_move(current, MOVE_UP, move_amt); 233 return move_in_direction(current, MOVE_UP, move_amt);
181 } else if (strcasecmp(argv[0], "down") == 0) { 234 } else if (strcasecmp(argv[0], "down") == 0) {
182 container_move(current, MOVE_DOWN, move_amt); 235 return move_in_direction(current, MOVE_DOWN, move_amt);
183 } else if (strcasecmp(argv[0], "container") == 0 236 } else if (strcasecmp(argv[0], "container") == 0
184 || strcasecmp(argv[0], "window") == 0) { 237 || strcasecmp(argv[0], "window") == 0) {
185 return cmd_move_container(current, argc, argv); 238 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 b57d1ee6..ff37bbf1 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 f0f1603a..69d0bdd4 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_current_child(seat, con);
671 struct border_colors *current_colors = NULL; 717 struct border_colors *current_colors = &config->border_colors.unfocused;
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;
@@ -709,11 +756,13 @@ static void render_container_tabbed(struct sway_output *output,
709 } 756 }
710 757
711 // Render surface and left/right/bottom borders 758 // Render surface and left/right/bottom borders
712 if (current->type == C_VIEW) { 759 if (current) {
713 render_view(output, damage, current, current_colors); 760 if (current->type == C_VIEW) {
714 } else { 761 render_view(output, damage, current, current_colors);
715 render_container(output, damage, current, 762 } else {
716 parent_focused || current == focus); 763 render_container(output, damage, current,
764 parent_focused || current == focus);
765 }
717 } 766 }
718} 767}
719 768
@@ -723,22 +772,23 @@ static void render_container_tabbed(struct sway_output *output,
723static void render_container_stacked(struct sway_output *output, 772static void render_container_stacked(struct sway_output *output,
724 pixman_region32_t *damage, struct sway_container *con, 773 pixman_region32_t *damage, struct sway_container *con,
725 bool parent_focused) { 774 bool parent_focused) {
726 if (!con->children->length) { 775 if (!con->current.children->length) {
727 return; 776 return;
728 } 777 }
729 struct sway_seat *seat = input_manager_current_seat(input_manager); 778 struct sway_seat *seat = input_manager_current_seat(input_manager);
730 struct sway_container *focus = seat_get_focus(seat); 779 struct sway_container *focus = seat_get_focus(seat);
731 struct sway_container *current = seat_get_active_child(seat, con); 780 struct sway_container *current = seat_get_active_current_child(seat, con);
732 struct border_colors *current_colors = NULL; 781 struct border_colors *current_colors = &config->border_colors.unfocused;
782 struct sway_container_state *pstate = &con->current;
733 783
734 // Render titles 784 // Render titles
735 for (int i = 0; i < con->children->length; ++i) { 785 for (int i = 0; i < con->current.children->length; ++i) {
736 struct sway_container *child = con->children->items[i]; 786 struct sway_container *child = con->current.children->items[i];
787 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
788 struct sway_container_state *cstate = &child->current;
737 struct border_colors *colors; 789 struct border_colors *colors;
738 struct wlr_texture *title_texture; 790 struct wlr_texture *title_texture;
739 struct wlr_texture *marks_texture; 791 struct wlr_texture *marks_texture;
740 struct sway_view *view =
741 child->type == C_VIEW ? child->sway_view : NULL;
742 792
743 if (focus == child || parent_focused) { 793 if (focus == child || parent_focused) {
744 colors = &config->border_colors.focused; 794 colors = &config->border_colors.focused;
@@ -754,9 +804,9 @@ static void render_container_stacked(struct sway_output *output,
754 marks_texture = view ? view->marks_unfocused : NULL; 804 marks_texture = view ? view->marks_unfocused : NULL;
755 } 805 }
756 806
757 int y = con->y + container_titlebar_height() * i; 807 int y = pstate->swayc_y + container_titlebar_height() * i;
758 render_titlebar(output, damage, child, child->x, y, child->width, 808 render_titlebar(output, damage, child, cstate->swayc_x, y,
759 colors, title_texture, marks_texture); 809 cstate->swayc_width, colors, title_texture, marks_texture);
760 810
761 if (child == current) { 811 if (child == current) {
762 current_colors = colors; 812 current_colors = colors;
@@ -764,18 +814,20 @@ static void render_container_stacked(struct sway_output *output,
764 } 814 }
765 815
766 // Render surface and left/right/bottom borders 816 // Render surface and left/right/bottom borders
767 if (current->type == C_VIEW) { 817 if (current) {
768 render_view(output, damage, current, current_colors); 818 if (current->type == C_VIEW) {
769 } else { 819 render_view(output, damage, current, current_colors);
770 render_container(output, damage, current, 820 } else {
771 parent_focused || current == focus); 821 render_container(output, damage, current,
822 parent_focused || current == focus);
823 }
772 } 824 }
773} 825}
774 826
775static void render_container(struct sway_output *output, 827static void render_container(struct sway_output *output,
776 pixman_region32_t *damage, struct sway_container *con, 828 pixman_region32_t *damage, struct sway_container *con,
777 bool parent_focused) { 829 bool parent_focused) {
778 switch (con->layout) { 830 switch (con->current.layout) {
779 case L_NONE: 831 case L_NONE:
780 case L_HORIZ: 832 case L_HORIZ:
781 case L_VERT: 833 case L_VERT:
@@ -812,9 +864,10 @@ static void render_floating_container(struct sway_output *soutput,
812 marks_texture = view->marks_unfocused; 864 marks_texture = view->marks_unfocused;
813 } 865 }
814 866
815 if (con->sway_view->border == B_NORMAL) { 867 if (con->current.border == B_NORMAL) {
816 render_titlebar(soutput, damage, con, con->x, con->y, con->width, 868 render_titlebar(soutput, damage, con, con->current.swayc_x,
817 colors, title_texture, marks_texture); 869 con->current.swayc_y, con->current.swayc_width, colors,
870 title_texture, marks_texture);
818 } else { 871 } else {
819 render_top_border(soutput, damage, con, colors); 872 render_top_border(soutput, damage, con, colors);
820 } 873 }
@@ -826,17 +879,18 @@ static void render_floating_container(struct sway_output *soutput,
826 879
827static void render_floating(struct sway_output *soutput, 880static void render_floating(struct sway_output *soutput,
828 pixman_region32_t *damage) { 881 pixman_region32_t *damage) {
829 for (int i = 0; i < root_container.children->length; ++i) { 882 for (int i = 0; i < root_container.current.children->length; ++i) {
830 struct sway_container *output = root_container.children->items[i]; 883 struct sway_container *output =
831 for (int j = 0; j < output->children->length; ++j) { 884 root_container.current.children->items[i];
832 struct sway_container *workspace = output->children->items[j]; 885 for (int j = 0; j < output->current.children->length; ++j) {
833 struct sway_workspace *ws = workspace->sway_workspace; 886 struct sway_container *ws = output->current.children->items[j];
834 if (!workspace_is_visible(workspace)) { 887 if (!workspace_is_visible(ws)) {
835 continue; 888 continue;
836 } 889 }
837 for (int k = 0; k < ws->floating->children->length; ++k) { 890 list_t *floating =
838 struct sway_container *floater = 891 ws->current.ws_floating->current.children;
839 ws->floating->children->items[k]; 892 for (int k = 0; k < floating->length; ++k) {
893 struct sway_container *floater = floating->items[k];
840 render_floating_container(soutput, damage, floater); 894 render_floating_container(soutput, damage, floater);
841 } 895 }
842 } 896 }
@@ -850,7 +904,7 @@ static struct sway_container *output_get_active_workspace(
850 seat_get_focus_inactive(seat, output->swayc); 904 seat_get_focus_inactive(seat, output->swayc);
851 if (!focus) { 905 if (!focus) {
852 // We've never been to this output before 906 // We've never been to this output before
853 focus = output->swayc->children->items[0]; 907 focus = output->swayc->current.children->items[0];
854 } 908 }
855 struct sway_container *workspace = focus; 909 struct sway_container *workspace = focus;
856 if (workspace->type != C_WORKSPACE) { 910 if (workspace->type != C_WORKSPACE) {
@@ -891,8 +945,9 @@ static void render_output(struct sway_output *output, struct timespec *when,
891 } 945 }
892 946
893 struct sway_container *workspace = output_get_active_workspace(output); 947 struct sway_container *workspace = output_get_active_workspace(output);
948 struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
894 949
895 if (workspace->sway_workspace->fullscreen) { 950 if (fullscreen_view) {
896 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; 951 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
897 952
898 int nrects; 953 int nrects;
@@ -903,10 +958,9 @@ static void render_output(struct sway_output *output, struct timespec *when,
903 } 958 }
904 959
905 // TODO: handle views smaller than the output 960 // TODO: handle views smaller than the output
906 render_view_surfaces( 961 render_view_surfaces(fullscreen_view, output, damage, 1.0f);
907 workspace->sway_workspace->fullscreen, output, damage, 1.0f);
908 962
909 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 963 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
910 render_unmanaged(output, damage, 964 render_unmanaged(output, damage,
911 &root_container.sway_root->xwayland_unmanaged); 965 &root_container.sway_root->xwayland_unmanaged);
912 } 966 }
@@ -1022,11 +1076,11 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
1022 }; 1076 };
1023 1077
1024 struct sway_container *workspace = output_get_active_workspace(output); 1078 struct sway_container *workspace = output_get_active_workspace(output);
1025 if (workspace->sway_workspace->fullscreen) { 1079 if (workspace->current.ws_fullscreen) {
1026 send_frame_done_container_iterator( 1080 send_frame_done_container_iterator(
1027 workspace->sway_workspace->fullscreen->swayc, &data); 1081 workspace->current.ws_fullscreen->swayc, &data);
1028 1082
1029 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 1083 if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) {
1030 send_frame_done_unmanaged(&data, 1084 send_frame_done_unmanaged(&data,
1031 &root_container.sway_root->xwayland_unmanaged); 1085 &root_container.sway_root->xwayland_unmanaged);
1032 } 1086 }
@@ -1168,6 +1222,16 @@ void output_damage_from_view(struct sway_output *output,
1168 output_damage_view(output, view, false); 1222 output_damage_view(output, view, false);
1169} 1223}
1170 1224
1225// Expecting an unscaled box in layout coordinates
1226void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
1227 struct wlr_box box;
1228 memcpy(&box, _box, sizeof(struct wlr_box));
1229 box.x -= output->swayc->current.swayc_x;
1230 box.y -= output->swayc->current.swayc_y;
1231 scale_box(&box, output->wlr_output->scale);
1232 wlr_output_damage_add_box(output->damage, &box);
1233}
1234
1171static void output_damage_whole_container_iterator(struct sway_container *con, 1235static void output_damage_whole_container_iterator(struct sway_container *con,
1172 void *data) { 1236 void *data) {
1173 struct sway_output *output = data; 1237 struct sway_output *output = data;
@@ -1182,10 +1246,10 @@ static void output_damage_whole_container_iterator(struct sway_container *con,
1182void output_damage_whole_container(struct sway_output *output, 1246void output_damage_whole_container(struct sway_output *output,
1183 struct sway_container *con) { 1247 struct sway_container *con) {
1184 struct wlr_box box = { 1248 struct wlr_box box = {
1185 .x = con->x - output->wlr_output->lx, 1249 .x = con->current.swayc_x - output->wlr_output->lx,
1186 .y = con->y - output->wlr_output->ly, 1250 .y = con->current.swayc_y - output->wlr_output->ly,
1187 .width = con->width, 1251 .width = con->current.swayc_width,
1188 .height = con->height, 1252 .height = con->current.swayc_height,
1189 }; 1253 };
1190 scale_box(&box, output->wlr_output->scale); 1254 scale_box(&box, output->wlr_output->scale);
1191 wlr_output_damage_add_box(output->damage, &box); 1255 wlr_output_damage_add_box(output->damage, &box);
@@ -1209,18 +1273,20 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
1209 wl_list_remove(&output->destroy.link); 1273 wl_list_remove(&output->destroy.link);
1210 output->wlr_output->data = NULL; 1274 output->wlr_output->data = NULL;
1211 free(output); 1275 free(output);
1276
1277 arrange_and_commit(&root_container);
1212} 1278}
1213 1279
1214static void handle_mode(struct wl_listener *listener, void *data) { 1280static void handle_mode(struct wl_listener *listener, void *data) {
1215 struct sway_output *output = wl_container_of(listener, output, mode); 1281 struct sway_output *output = wl_container_of(listener, output, mode);
1216 arrange_layers(output); 1282 arrange_layers(output);
1217 arrange_output(output->swayc); 1283 arrange_and_commit(output->swayc);
1218} 1284}
1219 1285
1220static void handle_transform(struct wl_listener *listener, void *data) { 1286static void handle_transform(struct wl_listener *listener, void *data) {
1221 struct sway_output *output = wl_container_of(listener, output, transform); 1287 struct sway_output *output = wl_container_of(listener, output, transform);
1222 arrange_layers(output); 1288 arrange_layers(output);
1223 arrange_output(output->swayc); 1289 arrange_and_commit(output->swayc);
1224} 1290}
1225 1291
1226static void handle_scale_iterator(struct sway_container *view, void *data) { 1292static void handle_scale_iterator(struct sway_container *view, void *data) {
@@ -1230,8 +1296,8 @@ static void handle_scale_iterator(struct sway_container *view, void *data) {
1230static void handle_scale(struct wl_listener *listener, void *data) { 1296static void handle_scale(struct wl_listener *listener, void *data) {
1231 struct sway_output *output = wl_container_of(listener, output, scale); 1297 struct sway_output *output = wl_container_of(listener, output, scale);
1232 arrange_layers(output); 1298 arrange_layers(output);
1233 arrange_output(output->swayc);
1234 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); 1299 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
1300 arrange_and_commit(output->swayc);
1235} 1301}
1236 1302
1237void handle_new_output(struct wl_listener *listener, void *data) { 1303void handle_new_output(struct wl_listener *listener, void *data) {
@@ -1296,5 +1362,5 @@ void output_enable(struct sway_output *output) {
1296 output->damage_destroy.notify = damage_handle_destroy; 1362 output->damage_destroy.notify = damage_handle_destroy;
1297 1363
1298 arrange_layers(output); 1364 arrange_layers(output);
1299 arrange_root(); 1365 arrange_and_commit(&root_container);
1300} 1366}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
new file mode 100644
index 00000000..fc23ef35
--- /dev/null
+++ b/sway/desktop/transaction.c
@@ -0,0 +1,403 @@
1#define _POSIX_C_SOURCE 200809L
2#include <stdbool.h>
3#include <stdlib.h>
4#include <string.h>
5#include <time.h>
6#include <wlr/types/wlr_buffer.h>
7#include <wlr/types/wlr_linux_dmabuf.h>
8#include "sway/debug.h"
9#include "sway/desktop/transaction.h"
10#include "sway/output.h"
11#include "sway/tree/container.h"
12#include "sway/tree/view.h"
13#include "sway/tree/workspace.h"
14#include "list.h"
15#include "log.h"
16
17/**
18 * How long we should wait for views to respond to the configure before giving
19 * up and applying the transaction anyway.
20 */
21#define TIMEOUT_MS 200
22
23/**
24 * If enabled, sway will always wait for the transaction timeout before
25 * applying it, rather than applying it when the views are ready. This allows us
26 * to observe the rendered state while a transaction is in progress.
27 */
28#define TRANSACTION_DEBUG false
29
30struct sway_transaction {
31 struct wl_event_source *timer;
32 list_t *instructions; // struct sway_transaction_instruction *
33 size_t num_waiting;
34 size_t num_configures;
35 struct timespec create_time;
36 struct timespec commit_time;
37};
38
39struct sway_transaction_instruction {
40 struct sway_transaction *transaction;
41 struct sway_container *container;
42 struct sway_container_state state;
43 struct wlr_buffer *saved_buffer;
44 uint32_t serial;
45 bool ready;
46};
47
48struct sway_transaction *transaction_create() {
49 struct sway_transaction *transaction =
50 calloc(1, sizeof(struct sway_transaction));
51 transaction->instructions = create_list();
52 if (server.debug_txn_timings) {
53 clock_gettime(CLOCK_MONOTONIC, &transaction->create_time);
54 }
55 return transaction;
56}
57
58static void remove_saved_view_buffer(
59 struct sway_transaction_instruction *instruction) {
60 if (instruction->saved_buffer) {
61 wlr_buffer_unref(instruction->saved_buffer);
62 instruction->saved_buffer = NULL;
63 }
64}
65
66static void save_view_buffer(struct sway_view *view,
67 struct sway_transaction_instruction *instruction) {
68 if (!sway_assert(instruction->saved_buffer == NULL,
69 "Didn't expect instruction to have a saved buffer already")) {
70 remove_saved_view_buffer(instruction);
71 }
72 if (view->surface && wlr_surface_has_buffer(view->surface)) {
73 instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer);
74 }
75}
76
77static void transaction_destroy(struct sway_transaction *transaction) {
78 // Free instructions
79 for (int i = 0; i < transaction->instructions->length; ++i) {
80 struct sway_transaction_instruction *instruction =
81 transaction->instructions->items[i];
82 struct sway_container *con = instruction->container;
83 for (int j = 0; j < con->instructions->length; ++j) {
84 if (con->instructions->items[j] == instruction) {
85 list_del(con->instructions, j);
86 break;
87 }
88 }
89 if (con->destroying && !con->instructions->length) {
90 container_free(con);
91 }
92 remove_saved_view_buffer(instruction);
93 free(instruction);
94 }
95 list_free(transaction->instructions);
96
97 if (transaction->timer) {
98 wl_event_source_remove(transaction->timer);
99 }
100 free(transaction);
101}
102
103static void copy_pending_state(struct sway_container *container,
104 struct sway_container_state *state) {
105 state->layout = container->layout;
106 state->swayc_x = container->x;
107 state->swayc_y = container->y;
108 state->swayc_width = container->width;
109 state->swayc_height = container->height;
110 state->has_gaps = container->has_gaps;
111 state->current_gaps = container->current_gaps;
112 state->gaps_inner = container->gaps_inner;
113 state->gaps_outer = container->gaps_outer;
114 state->parent = container->parent;
115
116 if (container->type == C_VIEW) {
117 struct sway_view *view = container->sway_view;
118 state->view_x = view->x;
119 state->view_y = view->y;
120 state->view_width = view->width;
121 state->view_height = view->height;
122 state->is_fullscreen = view->is_fullscreen;
123 state->border = view->border;
124 state->border_thickness = view->border_thickness;
125 state->border_top = view->border_top;
126 state->border_left = view->border_left;
127 state->border_right = view->border_right;
128 state->border_bottom = view->border_bottom;
129 } else if (container->type == C_WORKSPACE) {
130 state->ws_fullscreen = container->sway_workspace->fullscreen;
131 state->ws_floating = container->sway_workspace->floating;
132 state->children = create_list();
133 list_cat(state->children, container->children);
134 } else {
135 state->children = create_list();
136 list_cat(state->children, container->children);
137 }
138}
139
140static bool transaction_has_container(struct sway_transaction *transaction,
141 struct sway_container *container) {
142 for (int i = 0; i < transaction->instructions->length; ++i) {
143 struct sway_transaction_instruction *instruction =
144 transaction->instructions->items[i];
145 if (instruction->container == container) {
146 return true;
147 }
148 }
149 return false;
150}
151
152void transaction_add_container(struct sway_transaction *transaction,
153 struct sway_container *container) {
154 if (transaction_has_container(transaction, container)) {
155 return;
156 }
157 struct sway_transaction_instruction *instruction =
158 calloc(1, sizeof(struct sway_transaction_instruction));
159 instruction->transaction = transaction;
160 instruction->container = container;
161
162 copy_pending_state(container, &instruction->state);
163
164 if (container->type == C_VIEW) {
165 save_view_buffer(container->sway_view, instruction);
166 }
167 list_add(transaction->instructions, instruction);
168}
169
170/**
171 * Apply a transaction to the "current" state of the tree.
172 */
173static void transaction_apply(struct sway_transaction *transaction) {
174 wlr_log(L_DEBUG, "Applying transaction %p", transaction);
175 if (server.debug_txn_timings) {
176 struct timespec now;
177 clock_gettime(CLOCK_MONOTONIC, &now);
178 struct timespec *create = &transaction->create_time;
179 struct timespec *commit = &transaction->commit_time;
180 float ms_arranging = (commit->tv_sec - create->tv_sec) * 1000 +
181 (commit->tv_nsec - create->tv_nsec) / 1000000.0;
182 float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 +
183 (now.tv_nsec - commit->tv_nsec) / 1000000.0;
184 float ms_total = ms_arranging + ms_waiting;
185 wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, "
186 "%.1fms total (%.1f frames if 60Hz)", transaction,
187 ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60));
188 }
189
190 // Apply the instruction state to the container's current state
191 for (int i = 0; i < transaction->instructions->length; ++i) {
192 struct sway_transaction_instruction *instruction =
193 transaction->instructions->items[i];
194 struct sway_container *container = instruction->container;
195
196 // Damage the old and new locations
197 struct wlr_box old_box = {
198 .x = container->current.swayc_x,
199 .y = container->current.swayc_y,
200 .width = container->current.swayc_width,
201 .height = container->current.swayc_height,
202 };
203 struct wlr_box new_box = {
204 .x = instruction->state.swayc_x,
205 .y = instruction->state.swayc_y,
206 .width = instruction->state.swayc_width,
207 .height = instruction->state.swayc_height,
208 };
209 for (int j = 0; j < root_container.children->length; ++j) {
210 struct sway_container *output = root_container.children->items[j];
211 output_damage_box(output->sway_output, &old_box);
212 output_damage_box(output->sway_output, &new_box);
213 }
214
215 // There are separate children lists for each instruction state, the
216 // container's current state and the container's pending state
217 // (ie. con->children). The list itself needs to be freed here.
218 // Any child containers which are being deleted will be cleaned up in
219 // transaction_destroy().
220 list_free(container->current.children);
221
222 memcpy(&container->current, &instruction->state,
223 sizeof(struct sway_container_state));
224 }
225}
226
227/**
228 * For simplicity, we only progress the queue if it can be completely flushed.
229 */
230static void transaction_progress_queue() {
231 // We iterate this list in reverse because we're more likely to find a
232 // waiting transactions at the end of the list.
233 for (int i = server.transactions->length - 1; i >= 0; --i) {
234 struct sway_transaction *transaction = server.transactions->items[i];
235 if (transaction->num_waiting) {
236 return;
237 }
238 }
239 for (int i = 0; i < server.transactions->length; ++i) {
240 struct sway_transaction *transaction = server.transactions->items[i];
241 transaction_apply(transaction);
242 transaction_destroy(transaction);
243 }
244 server.transactions->length = 0;
245}
246
247static int handle_timeout(void *data) {
248 struct sway_transaction *transaction = data;
249 wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)",
250 transaction, transaction->num_waiting);
251 transaction->num_waiting = 0;
252 transaction_progress_queue();
253 return 0;
254}
255
256static bool should_configure(struct sway_container *con,
257 struct sway_transaction_instruction *instruction) {
258 if (con->type != C_VIEW) {
259 return false;
260 }
261 if (con->destroying) {
262 return false;
263 }
264 // The settled dimensions are what size the view will be once any pending
265 // configures have applied (excluding the one we might be configuring now).
266 // If these match the dimensions that this transaction wants then we don't
267 // need to configure it.
268 int settled_width = con->current.view_width;
269 int settled_height = con->current.view_height;
270 if (con->instructions->length) {
271 struct sway_transaction_instruction *last_instruction =
272 con->instructions->items[con->instructions->length - 1];
273 settled_width = last_instruction->state.view_width;
274 settled_height = last_instruction->state.view_height;
275 }
276 if (settled_width == instruction->state.view_width &&
277 settled_height == instruction->state.view_height) {
278 return false;
279 }
280 return true;
281}
282
283void transaction_commit(struct sway_transaction *transaction) {
284 wlr_log(L_DEBUG, "Transaction %p committing with %i instructions",
285 transaction, transaction->instructions->length);
286 transaction->num_waiting = 0;
287 for (int i = 0; i < transaction->instructions->length; ++i) {
288 struct sway_transaction_instruction *instruction =
289 transaction->instructions->items[i];
290 struct sway_container *con = instruction->container;
291 if (should_configure(con, instruction)) {
292 instruction->serial = view_configure(con->sway_view,
293 instruction->state.view_x,
294 instruction->state.view_y,
295 instruction->state.view_width,
296 instruction->state.view_height);
297 ++transaction->num_waiting;
298 }
299 list_add(con->instructions, instruction);
300 }
301 transaction->num_configures = transaction->num_waiting;
302 if (server.debug_txn_timings) {
303 clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time);
304 }
305 if (server.transactions->length || transaction->num_waiting) {
306 list_add(server.transactions, transaction);
307 } else {
308 // There are no other transactions in progress, and this one has nothing
309 // to wait for, so we can skip the queue.
310 wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction);
311 transaction_apply(transaction);
312 transaction_destroy(transaction);
313 return;
314 }
315
316 if (transaction->num_waiting) {
317 // Set up a timer which the views must respond within
318 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
319 handle_timeout, transaction);
320 wl_event_source_timer_update(transaction->timer, TIMEOUT_MS);
321 }
322
323 // The debug tree shows the pending/live tree. Here is a good place to
324 // update it, because we make a transaction every time we change the pending
325 // tree.
326 update_debug_tree();
327}
328
329static void set_instruction_ready(
330 struct sway_transaction_instruction *instruction) {
331 instruction->ready = true;
332 struct sway_transaction *transaction = instruction->transaction;
333
334 if (server.debug_txn_timings) {
335 struct timespec now;
336 clock_gettime(CLOCK_MONOTONIC, &now);
337 struct timespec *start = &transaction->commit_time;
338 float ms = (now.tv_sec - start->tv_sec) * 1000 +
339 (now.tv_nsec - start->tv_nsec) / 1000000.0;
340 wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)",
341 transaction,
342 transaction->num_configures - transaction->num_waiting + 1,
343 transaction->num_configures, ms,
344 instruction->container->name);
345
346 }
347
348 // If all views are ready, apply the transaction.
349 // If the transaction has timed out then its num_waiting will be 0 already.
350 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) {
351#if !TRANSACTION_DEBUG
352 wlr_log(L_DEBUG, "Transaction %p is ready", transaction);
353 wl_event_source_timer_update(transaction->timer, 0);
354 transaction_progress_queue();
355#endif
356 }
357}
358
359/**
360 * Mark all of the view's instructions as ready up to and including the
361 * instruction at the given index. This allows the view to skip a configure.
362 */
363static void set_instructions_ready(struct sway_view *view, int index) {
364 for (int i = 0; i <= index; ++i) {
365 struct sway_transaction_instruction *instruction =
366 view->swayc->instructions->items[i];
367 set_instruction_ready(instruction);
368 }
369}
370
371void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) {
372 for (int i = 0; i < view->swayc->instructions->length; ++i) {
373 struct sway_transaction_instruction *instruction =
374 view->swayc->instructions->items[i];
375 if (instruction->serial == serial && !instruction->ready) {
376 set_instructions_ready(view, i);
377 return;
378 }
379 }
380}
381
382void transaction_notify_view_ready_by_size(struct sway_view *view,
383 int width, int height) {
384 for (int i = 0; i < view->swayc->instructions->length; ++i) {
385 struct sway_transaction_instruction *instruction =
386 view->swayc->instructions->items[i];
387 if (!instruction->ready && instruction->state.view_width == width &&
388 instruction->state.view_height == height) {
389 set_instructions_ready(view, i);
390 return;
391 }
392 }
393}
394
395struct wlr_texture *transaction_get_texture(struct sway_view *view) {
396 if (!view->swayc || !view->swayc->instructions->length) {
397 return view->surface->buffer->texture;
398 }
399 struct sway_transaction_instruction *instruction =
400 view->swayc->instructions->items[0];
401 return instruction->saved_buffer ?
402 instruction->saved_buffer->texture : NULL;
403}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 8457c06b..0f45399d 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) {
@@ -153,10 +150,6 @@ static void destroy(struct sway_view *view) {
153 if (xdg_shell_view == NULL) { 150 if (xdg_shell_view == NULL) {
154 return; 151 return;
155 } 152 }
156 wl_list_remove(&xdg_shell_view->destroy.link);
157 wl_list_remove(&xdg_shell_view->map.link);
158 wl_list_remove(&xdg_shell_view->unmap.link);
159 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
160 free(xdg_shell_view); 153 free(xdg_shell_view);
161} 154}
162 155
@@ -175,18 +168,16 @@ static void handle_commit(struct wl_listener *listener, void *data) {
175 struct sway_xdg_shell_view *xdg_shell_view = 168 struct sway_xdg_shell_view *xdg_shell_view =
176 wl_container_of(listener, xdg_shell_view, commit); 169 wl_container_of(listener, xdg_shell_view, commit);
177 struct sway_view *view = &xdg_shell_view->view; 170 struct sway_view *view = &xdg_shell_view->view;
178 if (view->swayc && container_is_floating(view->swayc)) { 171 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
179 int width = view->wlr_xdg_surface->geometry.width; 172
180 int height = view->wlr_xdg_surface->geometry.height; 173 if (!view->swayc) {
181 if (!width && !height) { 174 return;
182 width = view->wlr_xdg_surface->surface->current->width; 175 }
183 height = view->wlr_xdg_surface->surface->current->height; 176
184 } 177 if (view->swayc->instructions->length) {
185 view_update_size(view, width, height); 178 transaction_notify_view_ready(view, xdg_surface->configure_serial);
186 } else {
187 view_update_size(view, xdg_shell_view->pending_width,
188 xdg_shell_view->pending_height);
189 } 179 }
180
190 view_update_title(view, false); 181 view_update_title(view, false);
191 view_damage_from(view); 182 view_damage_from(view);
192} 183}
@@ -201,8 +192,13 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
201static void handle_unmap(struct wl_listener *listener, void *data) { 192static void handle_unmap(struct wl_listener *listener, void *data) {
202 struct sway_xdg_shell_view *xdg_shell_view = 193 struct sway_xdg_shell_view *xdg_shell_view =
203 wl_container_of(listener, xdg_shell_view, unmap); 194 wl_container_of(listener, xdg_shell_view, unmap);
195 struct sway_view *view = &xdg_shell_view->view;
204 196
205 view_unmap(&xdg_shell_view->view); 197 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
198 return;
199 }
200
201 view_unmap(view);
206 202
207 wl_list_remove(&xdg_shell_view->commit.link); 203 wl_list_remove(&xdg_shell_view->commit.link);
208 wl_list_remove(&xdg_shell_view->new_popup.link); 204 wl_list_remove(&xdg_shell_view->new_popup.link);
@@ -220,8 +216,17 @@ static void handle_map(struct wl_listener *listener, void *data) {
220 view->natural_width = view->wlr_xdg_surface->surface->current->width; 216 view->natural_width = view->wlr_xdg_surface->surface->current->width;
221 view->natural_height = view->wlr_xdg_surface->surface->current->height; 217 view->natural_height = view->wlr_xdg_surface->surface->current->height;
222 } 218 }
219
223 view_map(view, view->wlr_xdg_surface->surface); 220 view_map(view, view->wlr_xdg_surface->surface);
224 221
222 if (xdg_surface->toplevel->client_pending.fullscreen) {
223 view_set_fullscreen(view, true);
224 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
225 arrange_and_commit(ws);
226 } else {
227 arrange_and_commit(view->swayc->parent);
228 }
229
225 xdg_shell_view->commit.notify = handle_commit; 230 xdg_shell_view->commit.notify = handle_commit;
226 wl_signal_add(&xdg_surface->surface->events.commit, 231 wl_signal_add(&xdg_surface->surface->events.commit,
227 &xdg_shell_view->commit); 232 &xdg_shell_view->commit);
@@ -229,16 +234,22 @@ static void handle_map(struct wl_listener *listener, void *data) {
229 xdg_shell_view->new_popup.notify = handle_new_popup; 234 xdg_shell_view->new_popup.notify = handle_new_popup;
230 wl_signal_add(&xdg_surface->events.new_popup, 235 wl_signal_add(&xdg_surface->events.new_popup,
231 &xdg_shell_view->new_popup); 236 &xdg_shell_view->new_popup);
232
233 if (xdg_surface->toplevel->client_pending.fullscreen) {
234 view_set_fullscreen(view, true);
235 }
236} 237}
237 238
238static void handle_destroy(struct wl_listener *listener, void *data) { 239static void handle_destroy(struct wl_listener *listener, void *data) {
239 struct sway_xdg_shell_view *xdg_shell_view = 240 struct sway_xdg_shell_view *xdg_shell_view =
240 wl_container_of(listener, xdg_shell_view, destroy); 241 wl_container_of(listener, xdg_shell_view, destroy);
241 view_destroy(&xdg_shell_view->view); 242 struct sway_view *view = &xdg_shell_view->view;
243 if (!sway_assert(view->swayc == NULL || view->swayc->destroying,
244 "Tried to destroy a mapped view")) {
245 return;
246 }
247 wl_list_remove(&xdg_shell_view->destroy.link);
248 wl_list_remove(&xdg_shell_view->map.link);
249 wl_list_remove(&xdg_shell_view->unmap.link);
250 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
251 view->wlr_xdg_surface = NULL;
252 view_destroy(view);
242} 253}
243 254
244static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 255static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
@@ -247,6 +258,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
247 struct wlr_xdg_toplevel_set_fullscreen_event *e = data; 258 struct wlr_xdg_toplevel_set_fullscreen_event *e = data;
248 struct wlr_xdg_surface *xdg_surface = 259 struct wlr_xdg_surface *xdg_surface =
249 xdg_shell_view->view.wlr_xdg_surface; 260 xdg_shell_view->view.wlr_xdg_surface;
261 struct sway_view *view = &xdg_shell_view->view;
250 262
251 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, 263 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL,
252 "xdg_shell requested fullscreen of surface with role %i", 264 "xdg_shell requested fullscreen of surface with role %i",
@@ -257,7 +269,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
257 return; 269 return;
258 } 270 }
259 271
260 view_set_fullscreen(&xdg_shell_view->view, e->fullscreen); 272 view_set_fullscreen(view, e->fullscreen);
273
274 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
275 arrange_and_commit(ws);
261} 276}
262 277
263void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 278void 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 eb1cef26..b296f1a8 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) {
@@ -153,10 +151,6 @@ static void destroy(struct sway_view *view) {
153 if (xdg_shell_v6_view == NULL) { 151 if (xdg_shell_v6_view == NULL) {
154 return; 152 return;
155 } 153 }
156 wl_list_remove(&xdg_shell_v6_view->destroy.link);
157 wl_list_remove(&xdg_shell_v6_view->map.link);
158 wl_list_remove(&xdg_shell_v6_view->unmap.link);
159 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
160 free(xdg_shell_v6_view); 154 free(xdg_shell_v6_view);
161} 155}
162 156
@@ -175,18 +169,15 @@ static void handle_commit(struct wl_listener *listener, void *data) {
175 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 169 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
176 wl_container_of(listener, xdg_shell_v6_view, commit); 170 wl_container_of(listener, xdg_shell_v6_view, commit);
177 struct sway_view *view = &xdg_shell_v6_view->view; 171 struct sway_view *view = &xdg_shell_v6_view->view;
178 if (view->swayc && container_is_floating(view->swayc)) { 172 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6;
179 int width = view->wlr_xdg_surface_v6->geometry.width; 173
180 int height = view->wlr_xdg_surface_v6->geometry.height; 174 if (!view->swayc) {
181 if (!width && !height) { 175 return;
182 width = view->wlr_xdg_surface_v6->surface->current->width;
183 height = view->wlr_xdg_surface_v6->surface->current->height;
184 }
185 view_update_size(view, width, height);
186 } else {
187 view_update_size(view, xdg_shell_v6_view->pending_width,
188 xdg_shell_v6_view->pending_height);
189 } 176 }
177 if (view->swayc->instructions->length) {
178 transaction_notify_view_ready(view, xdg_surface_v6->configure_serial);
179 }
180
190 view_update_title(view, false); 181 view_update_title(view, false);
191 view_damage_from(view); 182 view_damage_from(view);
192} 183}
@@ -201,8 +192,13 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
201static void handle_unmap(struct wl_listener *listener, void *data) { 192static void handle_unmap(struct wl_listener *listener, void *data) {
202 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 193 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
203 wl_container_of(listener, xdg_shell_v6_view, unmap); 194 wl_container_of(listener, xdg_shell_v6_view, unmap);
195 struct sway_view *view = &xdg_shell_v6_view->view;
196
197 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
198 return;
199 }
204 200
205 view_unmap(&xdg_shell_v6_view->view); 201 view_unmap(view);
206 202
207 wl_list_remove(&xdg_shell_v6_view->commit.link); 203 wl_list_remove(&xdg_shell_v6_view->commit.link);
208 wl_list_remove(&xdg_shell_v6_view->new_popup.link); 204 wl_list_remove(&xdg_shell_v6_view->new_popup.link);
@@ -220,8 +216,17 @@ static void handle_map(struct wl_listener *listener, void *data) {
220 view->natural_width = view->wlr_xdg_surface_v6->surface->current->width; 216 view->natural_width = view->wlr_xdg_surface_v6->surface->current->width;
221 view->natural_height = view->wlr_xdg_surface_v6->surface->current->height; 217 view->natural_height = view->wlr_xdg_surface_v6->surface->current->height;
222 } 218 }
219
223 view_map(view, view->wlr_xdg_surface_v6->surface); 220 view_map(view, view->wlr_xdg_surface_v6->surface);
224 221
222 if (xdg_surface->toplevel->client_pending.fullscreen) {
223 view_set_fullscreen(view, true);
224 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
225 arrange_and_commit(ws);
226 } else {
227 arrange_and_commit(view->swayc->parent);
228 }
229
225 xdg_shell_v6_view->commit.notify = handle_commit; 230 xdg_shell_v6_view->commit.notify = handle_commit;
226 wl_signal_add(&xdg_surface->surface->events.commit, 231 wl_signal_add(&xdg_surface->surface->events.commit,
227 &xdg_shell_v6_view->commit); 232 &xdg_shell_v6_view->commit);
@@ -229,16 +234,18 @@ static void handle_map(struct wl_listener *listener, void *data) {
229 xdg_shell_v6_view->new_popup.notify = handle_new_popup; 234 xdg_shell_v6_view->new_popup.notify = handle_new_popup;
230 wl_signal_add(&xdg_surface->events.new_popup, 235 wl_signal_add(&xdg_surface->events.new_popup,
231 &xdg_shell_v6_view->new_popup); 236 &xdg_shell_v6_view->new_popup);
232
233 if (xdg_surface->toplevel->client_pending.fullscreen) {
234 view_set_fullscreen(view, true);
235 }
236} 237}
237 238
238static void handle_destroy(struct wl_listener *listener, void *data) { 239static void handle_destroy(struct wl_listener *listener, void *data) {
239 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 240 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
240 wl_container_of(listener, xdg_shell_v6_view, destroy); 241 wl_container_of(listener, xdg_shell_v6_view, destroy);
241 view_destroy(&xdg_shell_v6_view->view); 242 struct sway_view *view = &xdg_shell_v6_view->view;
243 wl_list_remove(&xdg_shell_v6_view->destroy.link);
244 wl_list_remove(&xdg_shell_v6_view->map.link);
245 wl_list_remove(&xdg_shell_v6_view->unmap.link);
246 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
247 view->wlr_xdg_surface_v6 = NULL;
248 view_destroy(view);
242} 249}
243 250
244static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 251static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
@@ -247,6 +254,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
247 struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data; 254 struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data;
248 struct wlr_xdg_surface_v6 *xdg_surface = 255 struct wlr_xdg_surface_v6 *xdg_surface =
249 xdg_shell_v6_view->view.wlr_xdg_surface_v6; 256 xdg_shell_v6_view->view.wlr_xdg_surface_v6;
257 struct sway_view *view = &xdg_shell_v6_view->view;
250 258
251 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL, 259 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL,
252 "xdg_shell_v6 requested fullscreen of surface with role %i", 260 "xdg_shell_v6 requested fullscreen of surface with role %i",
@@ -257,7 +265,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
257 return; 265 return;
258 } 266 }
259 267
260 view_set_fullscreen(&xdg_shell_v6_view->view, e->fullscreen); 268 view_set_fullscreen(view, e->fullscreen);
269
270 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
271 arrange_and_commit(ws);
261} 272}
262 273
263void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { 274void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index df5f6698..023fb2a7 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -7,10 +7,12 @@
7#include <wlr/xwayland.h> 7#include <wlr/xwayland.h>
8#include "log.h" 8#include "log.h"
9#include "sway/desktop.h" 9#include "sway/desktop.h"
10#include "sway/desktop/transaction.h"
10#include "sway/input/input-manager.h" 11#include "sway/input/input-manager.h"
11#include "sway/input/seat.h" 12#include "sway/input/seat.h"
12#include "sway/output.h" 13#include "sway/output.h"
13#include "sway/server.h" 14#include "sway/server.h"
15#include "sway/tree/arrange.h"
14#include "sway/tree/container.h" 16#include "sway/tree/container.h"
15#include "sway/tree/layout.h" 17#include "sway/tree/layout.h"
16#include "sway/tree/view.h" 18#include "sway/tree/view.h"
@@ -176,19 +178,18 @@ static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) {
176 } 178 }
177} 179}
178 180
179static void configure(struct sway_view *view, double lx, double ly, int width, 181static uint32_t configure(struct sway_view *view, double lx, double ly, int width,
180 int height) { 182 int height) {
181 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); 183 struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view);
182 if (xwayland_view == NULL) { 184 if (xwayland_view == NULL) {
183 return; 185 return 0;
184 } 186 }
185 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 187 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
186 188
187 xwayland_view->pending_lx = lx;
188 xwayland_view->pending_ly = ly;
189 xwayland_view->pending_width = width;
190 xwayland_view->pending_height = height;
191 wlr_xwayland_surface_configure(xsurface, lx, ly, width, height); 189 wlr_xwayland_surface_configure(xsurface, lx, ly, width, height);
190
191 // xwayland doesn't give us a serial for the configure
192 return 0;
192} 193}
193 194
194static void set_activated(struct sway_view *view, bool activated) { 195static void set_activated(struct sway_view *view, bool activated) {
@@ -249,14 +250,6 @@ static void destroy(struct sway_view *view) {
249 if (xwayland_view == NULL) { 250 if (xwayland_view == NULL) {
250 return; 251 return;
251 } 252 }
252 wl_list_remove(&xwayland_view->destroy.link);
253 wl_list_remove(&xwayland_view->request_configure.link);
254 wl_list_remove(&xwayland_view->request_fullscreen.link);
255 wl_list_remove(&xwayland_view->set_title.link);
256 wl_list_remove(&xwayland_view->set_class.link);
257 wl_list_remove(&xwayland_view->set_window_type.link);
258 wl_list_remove(&xwayland_view->map.link);
259 wl_list_remove(&xwayland_view->unmap.link);
260 free(xwayland_view); 253 free(xwayland_view);
261} 254}
262 255
@@ -276,22 +269,26 @@ static void handle_commit(struct wl_listener *listener, void *data) {
276 wl_container_of(listener, xwayland_view, commit); 269 wl_container_of(listener, xwayland_view, commit);
277 struct sway_view *view = &xwayland_view->view; 270 struct sway_view *view = &xwayland_view->view;
278 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 271 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
279 if (view->swayc && container_is_floating(view->swayc)) { 272
280 view_update_size(view, xsurface->width, xsurface->height); 273 if (view->swayc->instructions->length) {
281 } else { 274 transaction_notify_view_ready_by_size(view,
282 view_update_size(view, xwayland_view->pending_width, 275 xsurface->width, xsurface->height);
283 xwayland_view->pending_height);
284 } 276 }
285 view_update_position(view,
286 xwayland_view->pending_lx, xwayland_view->pending_ly);
287 view_damage_from(view); 277 view_damage_from(view);
288} 278}
289 279
290static void handle_unmap(struct wl_listener *listener, void *data) { 280static void handle_unmap(struct wl_listener *listener, void *data) {
291 struct sway_xwayland_view *xwayland_view = 281 struct sway_xwayland_view *xwayland_view =
292 wl_container_of(listener, xwayland_view, unmap); 282 wl_container_of(listener, xwayland_view, unmap);
283 struct sway_view *view = &xwayland_view->view;
284
285 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
286 return;
287 }
288
289 view_unmap(view);
290
293 wl_list_remove(&xwayland_view->commit.link); 291 wl_list_remove(&xwayland_view->commit.link);
294 view_unmap(&xwayland_view->view);
295} 292}
296 293
297static void handle_map(struct wl_listener *listener, void *data) { 294static void handle_map(struct wl_listener *listener, void *data) {
@@ -314,12 +311,31 @@ static void handle_map(struct wl_listener *listener, void *data) {
314 311
315 if (xsurface->fullscreen) { 312 if (xsurface->fullscreen) {
316 view_set_fullscreen(view, true); 313 view_set_fullscreen(view, true);
314 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
315 arrange_and_commit(ws);
316 } else {
317 arrange_and_commit(view->swayc->parent);
317 } 318 }
318} 319}
319 320
320static void handle_destroy(struct wl_listener *listener, void *data) { 321static void handle_destroy(struct wl_listener *listener, void *data) {
321 struct sway_xwayland_view *xwayland_view = 322 struct sway_xwayland_view *xwayland_view =
322 wl_container_of(listener, xwayland_view, destroy); 323 wl_container_of(listener, xwayland_view, destroy);
324 struct sway_view *view = &xwayland_view->view;
325
326 if (view->surface) {
327 view_unmap(view);
328 wl_list_remove(&xwayland_view->commit.link);
329 }
330
331 wl_list_remove(&xwayland_view->destroy.link);
332 wl_list_remove(&xwayland_view->request_configure.link);
333 wl_list_remove(&xwayland_view->request_fullscreen.link);
334 wl_list_remove(&xwayland_view->set_title.link);
335 wl_list_remove(&xwayland_view->set_class.link);
336 wl_list_remove(&xwayland_view->set_window_type.link);
337 wl_list_remove(&xwayland_view->map.link);
338 wl_list_remove(&xwayland_view->unmap.link);
323 view_destroy(&xwayland_view->view); 339 view_destroy(&xwayland_view->view);
324} 340}
325 341
@@ -335,10 +351,12 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
335 return; 351 return;
336 } 352 }
337 if (container_is_floating(view->swayc)) { 353 if (container_is_floating(view->swayc)) {
338 configure(view, view->x, view->y, ev->width, ev->height); 354 configure(view, view->swayc->current.view_x,
355 view->swayc->current.view_y, ev->width, ev->height);
339 } else { 356 } else {
340 configure(view, view->x, view->y, 357 configure(view, view->swayc->current.view_x,
341 view->width, view->height); 358 view->swayc->current.view_y, view->swayc->current.view_width,
359 view->swayc->current.view_height);
342 } 360 }
343} 361}
344 362
@@ -351,6 +369,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
351 return; 369 return;
352 } 370 }
353 view_set_fullscreen(view, xsurface->fullscreen); 371 view_set_fullscreen(view, xsurface->fullscreen);
372
373 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
374 arrange_and_commit(ws);
354} 375}
355 376
356static void handle_set_title(struct wl_listener *listener, void *data) { 377static void handle_set_title(struct wl_listener *listener, void *data) {
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 1c2434b0..2c2087da 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -830,6 +830,18 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,
830 return NULL; 830 return NULL;
831} 831}
832 832
833struct sway_container *seat_get_active_current_child(struct sway_seat *seat,
834 struct sway_container *container) {
835 struct sway_container *child = seat_get_active_child(seat, container);
836 if (child) {
837 return child;
838 }
839 if (container->current.children->length == 1) {
840 return container->current.children->items[0];
841 }
842 return NULL;
843}
844
833struct sway_container *seat_get_focus(struct sway_seat *seat) { 845struct sway_container *seat_get_focus(struct sway_seat *seat) {
834 if (!seat->has_focus) { 846 if (!seat->has_focus) {
835 return NULL; 847 return NULL;
diff --git a/sway/meson.build b/sway/meson.build
index 2cf90b11..9ff3f05f 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 5b052494..f5700c09 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -19,6 +19,7 @@
19#include <wlr/types/wlr_xdg_output.h> 19#include <wlr/types/wlr_xdg_output.h>
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 "list.h"
22#include "sway/config.h" 23#include "sway/config.h"
23#include "sway/input/input-manager.h" 24#include "sway/input/input-manager.h"
24#include "sway/server.h" 25#include "sway/server.h"
@@ -114,6 +115,14 @@ bool server_init(struct sway_server *server) {
114 return false; 115 return false;
115 } 116 }
116 117
118 const char *debug = getenv("SWAY_DEBUG");
119 if (debug != NULL && strcmp(debug, "txn_timings") == 0) {
120 server->debug_txn_timings = true;
121 }
122 server->destroying_containers = create_list();
123
124 server->transactions = create_list();
125
117 input_manager = input_manager_create(server); 126 input_manager = input_manager_create(server);
118 return true; 127 return true;
119} 128}
@@ -121,6 +130,8 @@ bool server_init(struct sway_server *server) {
121void server_fini(struct sway_server *server) { 130void server_fini(struct sway_server *server) {
122 // TODO: free sway-specific resources 131 // TODO: free sway-specific resources
123 wl_display_destroy(server->wl_display); 132 wl_display_destroy(server->wl_display);
133 list_free(server->destroying_containers);
134 list_free(server->transactions);
124} 135}
125 136
126void server_run(struct sway_server *server) { 137void server_run(struct sway_server *server) {
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 53c95820..582b2891 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}
275 159
276 struct sway_container *workspace = parent; 160static void arrange_children_of(struct sway_container *parent,
277 if (workspace->type != C_WORKSPACE) { 161 struct sway_transaction *transaction);
278 workspace = container_parent(workspace, C_WORKSPACE); 162
163static void arrange_floating(struct sway_container *floating,
164 struct sway_transaction *transaction) {
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,140 @@ 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_deleted_containers(transaction);
308}
309
310void arrange_and_commit(struct sway_container *container) {
311 struct sway_transaction *transaction = transaction_create();
312 arrange_windows(container, transaction);
313 transaction_commit(transaction);
314}
315
316void remove_gaps(struct sway_container *c) {
317 if (c->current_gaps == 0) {
318 wlr_log(L_DEBUG, "Removing gaps: not gapped: %p", c);
319 return;
332 } 320 }
333 321
334 container_damage_whole(parent); 322 c->width += c->current_gaps * 2;
335 update_debug_tree(); 323 c->height += c->current_gaps * 2;
324 c->x -= c->current_gaps;
325 c->y -= c->current_gaps;
326
327 c->current_gaps = 0;
328
329 wlr_log(L_DEBUG, "Removing gaps %p", c);
330}
331
332void add_gaps(struct sway_container *c) {
333 if (c->current_gaps > 0 || c->type == C_CONTAINER) {
334 wlr_log(L_DEBUG, "Not adding gaps: %p", c);
335 return;
336 }
337
338 if (c->type == C_WORKSPACE &&
339 !(config->edge_gaps || (config->smart_gaps && c->children->length > 1))) {
340 return;
341 }
342
343 double gaps = c->has_gaps ? c->gaps_inner : config->gaps_inner;
344
345 c->x += gaps;
346 c->y += gaps;
347 c->width -= 2 * gaps;
348 c->height -= 2 * gaps;
349 c->current_gaps = gaps;
350
351 wlr_log(L_DEBUG, "Adding gaps: %p", c);
336} 352}
diff --git a/sway/tree/container.c b/sway/tree/container.c
index af55a54e..ab3d9dbd 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,67 @@ 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 break;
175 case C_WORKSPACE:
176 container_workspace_free(cont->sway_workspace);
177 break;
178 case C_CONTAINER:
179 break;
180 case C_VIEW:
181 {
182 struct sway_view *view = cont->sway_view;
183 view->swayc = NULL;
184 free(view->title_format);
185 view->title_format = NULL;
186
187 if (view->destroying) {
188 view_free(view);
189 }
190 }
191 break;
192 case C_TYPES:
193 sway_assert(false, "Didn't expect to see C_TYPES here");
194 break;
195 }
196
171 free(cont); 197 free(cont);
172} 198}
173 199
@@ -184,7 +210,6 @@ static struct sway_container *container_workspace_destroy(
184 } 210 }
185 211
186 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); 212 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name);
187 ipc_event_window(workspace, "close");
188 213
189 struct sway_container *parent = workspace->parent; 214 struct sway_container *parent = workspace->parent;
190 if (!workspace_is_empty(workspace) && output) { 215 if (!workspace_is_empty(workspace) && output) {
@@ -209,24 +234,6 @@ static struct sway_container *container_workspace_destroy(
209 } 234 }
210 } 235 }
211 236
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; 237 return parent;
231} 238}
232 239
@@ -263,11 +270,10 @@ static struct sway_container *container_output_destroy(
263 container_add_child(new_output, workspace); 270 container_add_child(new_output, workspace);
264 ipc_event_workspace(workspace, NULL, "move"); 271 ipc_event_workspace(workspace, NULL, "move");
265 } else { 272 } else {
266 container_workspace_destroy(workspace); 273 container_destroy(workspace);
267 } 274 }
268 275
269 container_sort_workspaces(new_output); 276 container_sort_workspaces(new_output);
270 arrange_output(new_output);
271 } 277 }
272 } 278 }
273 } 279 }
@@ -280,14 +286,48 @@ static struct sway_container *container_output_destroy(
280 wl_list_remove(&output->sway_output->damage_frame.link); 286 wl_list_remove(&output->sway_output->damage_frame.link);
281 287
282 output->sway_output->swayc = NULL; 288 output->sway_output->swayc = NULL;
289 output->sway_output = 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); 292
286 return &root_container; 293 return &root_container;
287} 294}
288 295
289static void container_root_finish(struct sway_container *con) { 296/**
290 wlr_log(L_ERROR, "TODO: destroy the root container"); 297 * Implement the actual destroy logic, without reaping.
298 */
299static struct sway_container *container_destroy_noreaping(
300 struct sway_container *con) {
301 if (con == NULL) {
302 return NULL;
303 }
304 if (con->destroying) {
305 return NULL;
306 }
307
308 wl_signal_emit(&con->events.destroy, con);
309 ipc_event_window(con, "close");
310
311 // The below functions move their children to somewhere else.
312 if (con->type == C_OUTPUT) {
313 container_output_destroy(con);
314 } else if (con->type == C_WORKSPACE) {
315 // Workspaces will refuse to be destroyed if they're the last workspace
316 // on their output.
317 if (!container_workspace_destroy(con)) {
318 wlr_log(L_ERROR, "workspace doesn't want to destroy");
319 return NULL;
320 }
321 }
322
323 con->destroying = true;
324 list_add(server.destroying_containers, con);
325
326 if (!con->parent) {
327 return NULL;
328 }
329
330 return container_remove_child(con);
291} 331}
292 332
293bool container_reap_empty(struct sway_container *con) { 333bool container_reap_empty(struct sway_container *con) {
@@ -303,13 +343,13 @@ bool container_reap_empty(struct sway_container *con) {
303 case C_WORKSPACE: 343 case C_WORKSPACE:
304 if (!workspace_is_visible(con) && workspace_is_empty(con)) { 344 if (!workspace_is_visible(con) && workspace_is_empty(con)) {
305 wlr_log(L_DEBUG, "Destroying workspace via reaper"); 345 wlr_log(L_DEBUG, "Destroying workspace via reaper");
306 container_workspace_destroy(con); 346 container_destroy_noreaping(con);
307 return true; 347 return true;
308 } 348 }
309 break; 349 break;
310 case C_CONTAINER: 350 case C_CONTAINER:
311 if (con->children->length == 0) { 351 if (con->children->length == 0) {
312 _container_destroy(con); 352 container_destroy_noreaping(con);
313 return true; 353 return true;
314 } 354 }
315 case C_VIEW: 355 case C_VIEW:
@@ -340,50 +380,25 @@ struct sway_container *container_flatten(struct sway_container *container) {
340 struct sway_container *child = container->children->items[0]; 380 struct sway_container *child = container->children->items[0];
341 struct sway_container *parent = container->parent; 381 struct sway_container *parent = container->parent;
342 container_replace_child(container, child); 382 container_replace_child(container, child);
343 container_destroy(container); 383 container_destroy_noreaping(container);
344 container = parent; 384 container = parent;
345 } 385 }
346 return container; 386 return container;
347} 387}
348 388
389/**
390 * container_destroy() is the first step in destroying a container. We'll emit
391 * events, detach it from the tree and mark it as destroying. The container will
392 * remain in memory until it's no longer used by a transaction, then it will be
393 * freed via container_free().
394 *
395 * This function just wraps container_destroy_noreaping(), then does reaping.
396 */
349struct sway_container *container_destroy(struct sway_container *con) { 397struct sway_container *container_destroy(struct sway_container *con) {
350 if (con == NULL) { 398 struct sway_container *parent = container_destroy_noreaping(con);
351 return NULL;
352 }
353
354 struct sway_container *parent = con->parent;
355 399
356 switch (con->type) { 400 if (!parent) {
357 case C_ROOT: 401 return NULL;
358 container_root_finish(con);
359 break;
360 case C_OUTPUT:
361 // dont try to reap the root after this
362 container_output_destroy(con);
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 } 402 }
388 403
389 return container_reap_empty_recursive(parent); 404 return container_reap_empty_recursive(parent);
@@ -763,9 +778,6 @@ static void update_title_texture(struct sway_container *con,
763 "Unexpected type %s", container_type_to_str(con->type))) { 778 "Unexpected type %s", container_type_to_str(con->type))) {
764 return; 779 return;
765 } 780 }
766 if (!con->width) {
767 return;
768 }
769 struct sway_container *output = container_parent(con, C_OUTPUT); 781 struct sway_container *output = container_parent(con, C_OUTPUT);
770 if (!output) { 782 if (!output) {
771 return; 783 return;
@@ -927,13 +939,12 @@ void container_set_floating(struct sway_container *container, bool enable) {
927 939
928 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 940 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
929 struct sway_seat *seat = input_manager_current_seat(input_manager); 941 struct sway_seat *seat = input_manager_current_seat(input_manager);
930 container_damage_whole(container);
931 942
932 if (enable) { 943 if (enable) {
933 container_remove_child(container); 944 container_remove_child(container);
934 container_add_child(workspace->sway_workspace->floating, container); 945 container_add_child(workspace->sway_workspace->floating, container);
935 if (container->type == C_VIEW) { 946 if (container->type == C_VIEW) {
936 view_autoconfigure(container->sway_view); 947 view_init_floating(container->sway_view);
937 } 948 }
938 seat_set_focus(seat, seat_get_focus_inactive(seat, container)); 949 seat_set_focus(seat, seat_get_focus_inactive(seat, container));
939 container_reap_empty_recursive(workspace); 950 container_reap_empty_recursive(workspace);
@@ -946,8 +957,8 @@ void container_set_floating(struct sway_container *container, bool enable) {
946 container->is_sticky = false; 957 container->is_sticky = false;
947 container_reap_empty_recursive(workspace->sway_workspace->floating); 958 container_reap_empty_recursive(workspace->sway_workspace->floating);
948 } 959 }
949 arrange_workspace(workspace); 960
950 container_damage_whole(container); 961 ipc_event_window(container, "floating");
951} 962}
952 963
953void container_set_geometry_from_floating_view(struct sway_container *con) { 964void container_set_geometry_from_floating_view(struct sway_container *con) {
@@ -976,3 +987,10 @@ bool container_is_floating(struct sway_container *container) {
976 } 987 }
977 return container->parent == workspace->sway_workspace->floating; 988 return container->parent == workspace->sway_workspace->floating;
978} 989}
990
991void container_get_box(struct sway_container *container, struct wlr_box *box) {
992 box->x = container->x;
993 box->y = container->y;
994 box->width = container->width;
995 box->height = container->height;
996}
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..9f85bac0 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,8 +51,6 @@ 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);
53
54 if (view->impl->destroy) { 54 if (view->impl->destroy) {
55 view->impl->destroy(view); 55 view->impl->destroy(view);
56 } else { 56 } else {
@@ -58,6 +58,27 @@ void view_destroy(struct sway_view *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
322 view_set_fullscreen_raw(view, fullscreen);
323 342
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,42 +536,26 @@ 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) { 542void 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) {
535 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
536 ws->sway_workspace->fullscreen = NULL;
537 }
538
539 container_damage_whole(view->swayc);
540
541 wl_list_remove(&view->surface_new_subsurface.link); 545 wl_list_remove(&view->surface_new_subsurface.link);
542 wl_list_remove(&view->container_reparent.link); 546 wl_list_remove(&view->container_reparent.link);
543 547
544 struct sway_container *parent = container_destroy(view->swayc); 548 if (view->is_fullscreen) {
545 549 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
546 view->swayc = NULL; 550 ws->sway_workspace->fullscreen = NULL;
547 view->surface = NULL; 551 container_destroy(view->swayc);
548
549 if (view->title_format) {
550 free(view->title_format);
551 view->title_format = NULL;
552 }
553 552
554 if (parent->type == C_OUTPUT) { 553 arrange_and_commit(ws->parent);
555 arrange_output(parent);
556 } else { 554 } else {
557 arrange_children_of(parent); 555 struct sway_container *parent = container_destroy(view->swayc);
556 arrange_and_commit(parent);
558 } 557 }
558 view->surface = NULL;
559} 559}
560 560
561void view_update_position(struct sway_view *view, double lx, double ly) { 561void view_update_position(struct sway_view *view, double lx, double ly) {
@@ -929,7 +929,7 @@ void view_update_marks_textures(struct sway_view *view) {
929} 929}
930 930
931bool view_is_visible(struct sway_view *view) { 931bool view_is_visible(struct sway_view *view) {
932 if (!view->swayc) { 932 if (!view->swayc || view->swayc->destroying) {
933 return false; 933 return false;
934 } 934 }
935 struct sway_container *workspace = 935 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);