summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/desktop/transaction.h67
-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.c320
-rw-r--r--sway/desktop/transaction.c415
-rw-r--r--sway/desktop/xdg_shell.c75
-rw-r--r--sway/desktop/xdg_shell_v6.c73
-rw-r--r--sway/desktop/xwayland.c72
-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.c178
-rw-r--r--sway/tree/workspace.c5
35 files changed, 1441 insertions, 672 deletions
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
new file mode 100644
index 00000000..7ab80eb8
--- /dev/null
+++ b/include/sway/desktop/transaction.h
@@ -0,0 +1,67 @@
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 saved texture that should be rendered for a view.
56 *
57 * The addresses pointed at by the width and height pointers will be populated
58 * with the surface's dimensions, which may be different to the texture's
59 * dimensions if output scaling is used.
60 *
61 * This function should only be called if it is known that the view has
62 * instructions.
63 */
64struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,
65 int *width, int *height);
66
67#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 91865232..b99044d3 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_tiled)(struct sway_view *view, bool tiled); 35 void (*set_tiled)(struct sway_view *view, bool tiled);
36 void (*set_fullscreen)(struct sway_view *view, bool fullscreen); 36 void (*set_fullscreen)(struct sway_view *view, bool fullscreen);
@@ -69,6 +69,8 @@ struct sway_view {
69 bool border_left; 69 bool border_left;
70 bool border_right; 70 bool border_right;
71 71
72 bool destroying;
73
72 list_t *executed_criteria; // struct criteria * 74 list_t *executed_criteria; // struct criteria *
73 list_t *marks; // char * 75 list_t *marks; // char *
74 76
@@ -104,8 +106,6 @@ struct sway_xdg_shell_v6_view {
104 struct wl_listener map; 106 struct wl_listener map;
105 struct wl_listener unmap; 107 struct wl_listener unmap;
106 struct wl_listener destroy; 108 struct wl_listener destroy;
107
108 int pending_width, pending_height;
109}; 109};
110 110
111struct sway_xdg_shell_view { 111struct sway_xdg_shell_view {
@@ -120,8 +120,6 @@ struct sway_xdg_shell_view {
120 struct wl_listener map; 120 struct wl_listener map;
121 struct wl_listener unmap; 121 struct wl_listener unmap;
122 struct wl_listener destroy; 122 struct wl_listener destroy;
123
124 int pending_width, pending_height;
125}; 123};
126 124
127struct sway_xwayland_view { 125struct sway_xwayland_view {
@@ -139,9 +137,6 @@ struct sway_xwayland_view {
139 struct wl_listener map; 137 struct wl_listener map;
140 struct wl_listener unmap; 138 struct wl_listener unmap;
141 struct wl_listener destroy; 139 struct wl_listener destroy;
142
143 int pending_lx, pending_ly;
144 int pending_width, pending_height;
145}; 140};
146 141
147struct sway_xwayland_unmanaged { 142struct sway_xwayland_unmanaged {
@@ -213,10 +208,15 @@ uint32_t view_get_window_type(struct sway_view *view);
213 208
214const char *view_get_shell(struct sway_view *view); 209const char *view_get_shell(struct sway_view *view);
215 210
216void view_configure(struct sway_view *view, double ox, double oy, int width, 211uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
217 int height); 212 int height);
218 213
219/** 214/**
215 * Center the view in its workspace and build the swayc decorations around it.
216 */
217void view_init_floating(struct sway_view *view);
218
219/**
220 * Configure the view's position and size based on the swayc's position and 220 * Configure the view's position and size based on the swayc's position and
221 * size, taking borders into consideration. 221 * size, taking borders into consideration.
222 */ 222 */
@@ -242,6 +242,8 @@ void view_for_each_surface(struct sway_view *view,
242void view_init(struct sway_view *view, enum sway_view_type type, 242void view_init(struct sway_view *view, enum sway_view_type type,
243 const struct sway_view_impl *impl); 243 const struct sway_view_impl *impl);
244 244
245void view_free(struct sway_view *view);
246
245void view_destroy(struct sway_view *view); 247void view_destroy(struct sway_view *view);
246 248
247void view_map(struct sway_view *view, struct wlr_surface *wlr_surface); 249void 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..a4fae388 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 movement_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 6244ca4a..541d765d 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,107 @@ 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 int width, height;
358 struct wlr_texture *texture =
359 transaction_get_saved_texture(view, &width, &height);
360 if (!texture) {
361 return;
362 }
363 struct wlr_box box = {
364 .x = view->swayc->current.view_x - output->swayc->current.swayc_x,
365 .y = view->swayc->current.view_y - output->swayc->current.swayc_y,
366 .width = width,
367 .height = height,
368 };
369
370 struct wlr_box output_box = {
371 .width = output->swayc->current.swayc_width,
372 .height = output->swayc->current.swayc_height,
373 };
374
375 struct wlr_box intersection;
376 bool intersects = wlr_box_intersection(&output_box, &box, &intersection);
377 if (!intersects) {
378 return;
379 }
380
381 scale_box(&box, wlr_output->scale);
382
383 float matrix[9];
384 wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
385 wlr_output->transform_matrix);
386
387 render_texture(wlr_output, damage, texture, &box, matrix, alpha);
388}
389
350/** 390/**
351 * Render a view's surface and left/bottom/right borders. 391 * Render a view's surface and left/bottom/right borders.
352 */ 392 */
353static void render_view(struct sway_output *output, pixman_region32_t *damage, 393static void render_view(struct sway_output *output, pixman_region32_t *damage,
354 struct sway_container *con, struct border_colors *colors) { 394 struct sway_container *con, struct border_colors *colors) {
355 struct sway_view *view = con->sway_view; 395 struct sway_view *view = con->sway_view;
356 render_view_surfaces(view, output, damage, view->swayc->alpha); 396 if (view->swayc->instructions->length) {
397 render_saved_view(view, output, damage, view->swayc->alpha);
398 } else {
399 render_view_surfaces(view, output, damage, view->swayc->alpha);
400 }
357 401
358 struct wlr_box box; 402 struct wlr_box box;
359 float output_scale = output->wlr_output->scale; 403 float output_scale = output->wlr_output->scale;
360 float color[4]; 404 float color[4];
405 struct sway_container_state *state = &con->current;
361 406
362 if (view->border != B_NONE) { 407 if (state->border != B_NONE) {
363 if (view->border_left) { 408 if (state->border_left) {
364 memcpy(&color, colors->child_border, sizeof(float) * 4); 409 memcpy(&color, colors->child_border, sizeof(float) * 4);
365 premultiply_alpha(color, con->alpha); 410 premultiply_alpha(color, con->alpha);
366 box.x = con->x; 411 box.x = state->swayc_x;
367 box.y = view->y; 412 box.y = state->view_y;
368 box.width = view->border_thickness; 413 box.width = state->border_thickness;
369 box.height = view->height; 414 box.height = state->view_height;
370 scale_box(&box, output_scale); 415 scale_box(&box, output_scale);
371 render_rect(output->wlr_output, damage, &box, color); 416 render_rect(output->wlr_output, damage, &box, color);
372 } 417 }
373 418
374 if (view->border_right) { 419 if (state->border_right) {
375 if (con->parent->children->length == 1 420 if (state->parent->current.children->length == 1
376 && con->parent->layout == L_HORIZ) { 421 && state->parent->current.layout == L_HORIZ) {
377 memcpy(&color, colors->indicator, sizeof(float) * 4); 422 memcpy(&color, colors->indicator, sizeof(float) * 4);
378 } else { 423 } else {
379 memcpy(&color, colors->child_border, sizeof(float) * 4); 424 memcpy(&color, colors->child_border, sizeof(float) * 4);
380 } 425 }
381 premultiply_alpha(color, con->alpha); 426 premultiply_alpha(color, con->alpha);
382 box.x = view->x + view->width; 427 box.x = state->view_x + state->view_width;
383 box.y = view->y; 428 box.y = state->view_y;
384 box.width = view->border_thickness; 429 box.width = state->border_thickness;
385 box.height = view->height; 430 box.height = state->view_height;
386 scale_box(&box, output_scale); 431 scale_box(&box, output_scale);
387 render_rect(output->wlr_output, damage, &box, color); 432 render_rect(output->wlr_output, damage, &box, color);
388 } 433 }
389 434
390 if (view->border_bottom) { 435 if (state->border_bottom) {
391 if (con->parent->children->length == 1 436 if (state->parent->current.children->length == 1
392 && con->parent->layout == L_VERT) { 437 && con->current.parent->current.layout == L_VERT) {
393 memcpy(&color, colors->indicator, sizeof(float) * 4); 438 memcpy(&color, colors->indicator, sizeof(float) * 4);
394 } else { 439 } else {
395 memcpy(&color, colors->child_border, sizeof(float) * 4); 440 memcpy(&color, colors->child_border, sizeof(float) * 4);
396 } 441 }
397 premultiply_alpha(color, con->alpha); 442 premultiply_alpha(color, con->alpha);
398 box.x = con->x; 443 box.x = state->swayc_x;
399 box.y = view->y + view->height; 444 box.y = state->view_y + state->view_height;
400 box.width = con->width; 445 box.width = state->swayc_width;
401 box.height = view->border_thickness; 446 box.height = state->border_thickness;
402 scale_box(&box, output_scale); 447 scale_box(&box, output_scale);
403 render_rect(output->wlr_output, damage, &box, color); 448 render_rect(output->wlr_output, damage, &box, color);
404 } 449 }
@@ -422,11 +467,13 @@ static void render_titlebar(struct sway_output *output,
422 struct wlr_texture *marks_texture) { 467 struct wlr_texture *marks_texture) {
423 struct wlr_box box; 468 struct wlr_box box;
424 float color[4]; 469 float color[4];
425 struct sway_view *view = con->type == C_VIEW ? con->sway_view : NULL; 470 struct sway_container_state *state = &con->current;
426 float output_scale = output->wlr_output->scale; 471 float output_scale = output->wlr_output->scale;
427 enum sway_container_layout layout = con->parent->layout; 472 enum sway_container_layout layout = state->parent->current.layout;
428 bool is_last_child = 473 list_t *children = state->parent->current.children;
429 con->parent->children->items[con->parent->children->length - 1] == con; 474 bool is_last_child = children->items[children->length - 1] == con;
475 double output_x = output->swayc->current.swayc_x;
476 double output_y = output->swayc->current.swayc_y;
430 477
431 // Single pixel bar above title 478 // Single pixel bar above title
432 memcpy(&color, colors->border, sizeof(float) * 4); 479 memcpy(&color, colors->border, sizeof(float) * 4);
@@ -443,9 +490,9 @@ static void render_titlebar(struct sway_output *output,
443 bool connects_sides = false; 490 bool connects_sides = false;
444 if (layout == L_HORIZ || layout == L_VERT || 491 if (layout == L_HORIZ || layout == L_VERT ||
445 (layout == L_STACKED && is_last_child)) { 492 (layout == L_STACKED && is_last_child)) {
446 if (view) { 493 if (con->type == C_VIEW) {
447 left_offset = view->border_left * view->border_thickness; 494 left_offset = state->border_left * state->border_thickness;
448 right_offset = view->border_right * view->border_thickness; 495 right_offset = state->border_right * state->border_thickness;
449 connects_sides = true; 496 connects_sides = true;
450 } 497 }
451 } 498 }
@@ -479,10 +526,9 @@ static void render_titlebar(struct sway_output *output,
479 struct wlr_box texture_box; 526 struct wlr_box texture_box;
480 wlr_texture_get_size(marks_texture, 527 wlr_texture_get_size(marks_texture,
481 &texture_box.width, &texture_box.height); 528 &texture_box.width, &texture_box.height);
482 texture_box.x = (x - output->swayc->x + width - TITLEBAR_H_PADDING) 529 texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING)
483 * output_scale - texture_box.width; 530 * output_scale - texture_box.width;
484 texture_box.y = (y - output->swayc->y + TITLEBAR_V_PADDING) 531 texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
485 * output_scale;
486 532
487 float matrix[9]; 533 float matrix[9];
488 wlr_matrix_project_box(matrix, &texture_box, 534 wlr_matrix_project_box(matrix, &texture_box,
@@ -503,10 +549,8 @@ static void render_titlebar(struct sway_output *output,
503 struct wlr_box texture_box; 549 struct wlr_box texture_box;
504 wlr_texture_get_size(title_texture, 550 wlr_texture_get_size(title_texture,
505 &texture_box.width, &texture_box.height); 551 &texture_box.width, &texture_box.height);
506 texture_box.x = (x - output->swayc->x + TITLEBAR_H_PADDING) 552 texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale;
507 * output_scale; 553 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 554
511 float matrix[9]; 555 float matrix[9];
512 wlr_matrix_project_box(matrix, &texture_box, 556 wlr_matrix_project_box(matrix, &texture_box,
@@ -566,15 +610,15 @@ static void render_titlebar(struct sway_output *output,
566 // Left pixel in line with bottom bar 610 // Left pixel in line with bottom bar
567 box.x = x; 611 box.x = x;
568 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 612 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
569 box.width = view->border_thickness * view->border_left; 613 box.width = state->border_thickness * state->border_left;
570 box.height = TITLEBAR_BORDER_THICKNESS; 614 box.height = TITLEBAR_BORDER_THICKNESS;
571 scale_box(&box, output_scale); 615 scale_box(&box, output_scale);
572 render_rect(output->wlr_output, output_damage, &box, color); 616 render_rect(output->wlr_output, output_damage, &box, color);
573 617
574 // Right pixel in line with bottom bar 618 // Right pixel in line with bottom bar
575 box.x = x + width - view->border_thickness * view->border_right; 619 box.x = x + width - state->border_thickness * state->border_right;
576 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; 620 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
577 box.width = view->border_thickness * view->border_right; 621 box.width = state->border_thickness * state->border_right;
578 box.height = TITLEBAR_BORDER_THICKNESS; 622 box.height = TITLEBAR_BORDER_THICKNESS;
579 scale_box(&box, output_scale); 623 scale_box(&box, output_scale);
580 render_rect(output->wlr_output, output_damage, &box, color); 624 render_rect(output->wlr_output, output_damage, &box, color);
@@ -587,8 +631,8 @@ static void render_titlebar(struct sway_output *output,
587static void render_top_border(struct sway_output *output, 631static void render_top_border(struct sway_output *output,
588 pixman_region32_t *output_damage, struct sway_container *con, 632 pixman_region32_t *output_damage, struct sway_container *con,
589 struct border_colors *colors) { 633 struct border_colors *colors) {
590 struct sway_view *view = con->sway_view; 634 struct sway_container_state *state = &con->current;
591 if (!view->border_top) { 635 if (!state->border_top) {
592 return; 636 return;
593 } 637 }
594 struct wlr_box box; 638 struct wlr_box box;
@@ -598,10 +642,10 @@ static void render_top_border(struct sway_output *output,
598 // Child border - top edge 642 // Child border - top edge
599 memcpy(&color, colors->child_border, sizeof(float) * 4); 643 memcpy(&color, colors->child_border, sizeof(float) * 4);
600 premultiply_alpha(color, con->alpha); 644 premultiply_alpha(color, con->alpha);
601 box.x = con->x; 645 box.x = state->swayc_x;
602 box.y = con->y; 646 box.y = state->swayc_y;
603 box.width = con->width; 647 box.width = state->swayc_width;
604 box.height = view->border_thickness; 648 box.height = state->border_thickness;
605 scale_box(&box, output_scale); 649 scale_box(&box, output_scale);
606 render_rect(output->wlr_output, output_damage, &box, color); 650 render_rect(output->wlr_output, output_damage, &box, color);
607} 651}
@@ -621,30 +665,34 @@ static void render_container_simple(struct sway_output *output,
621 struct sway_seat *seat = input_manager_current_seat(input_manager); 665 struct sway_seat *seat = input_manager_current_seat(input_manager);
622 struct sway_container *focus = seat_get_focus(seat); 666 struct sway_container *focus = seat_get_focus(seat);
623 667
624 for (int i = 0; i < con->children->length; ++i) { 668 for (int i = 0; i < con->current.children->length; ++i) {
625 struct sway_container *child = con->children->items[i]; 669 struct sway_container *child = con->current.children->items[i];
626 670
627 if (child->type == C_VIEW) { 671 if (child->type == C_VIEW) {
672 struct sway_view *view = child->sway_view;
628 struct border_colors *colors; 673 struct border_colors *colors;
629 struct wlr_texture *title_texture; 674 struct wlr_texture *title_texture;
630 struct wlr_texture *marks_texture; 675 struct wlr_texture *marks_texture;
676 struct sway_container_state *state = &child->current;
677
631 if (focus == child || parent_focused) { 678 if (focus == child || parent_focused) {
632 colors = &config->border_colors.focused; 679 colors = &config->border_colors.focused;
633 title_texture = child->title_focused; 680 title_texture = child->title_focused;
634 marks_texture = child->sway_view->marks_focused; 681 marks_texture = view->marks_focused;
635 } else if (seat_get_focus_inactive(seat, con) == child) { 682 } else if (seat_get_focus_inactive(seat, con) == child) {
636 colors = &config->border_colors.focused_inactive; 683 colors = &config->border_colors.focused_inactive;
637 title_texture = child->title_focused_inactive; 684 title_texture = child->title_focused_inactive;
638 marks_texture = child->sway_view->marks_focused_inactive; 685 marks_texture = view->marks_focused_inactive;
639 } else { 686 } else {
640 colors = &config->border_colors.unfocused; 687 colors = &config->border_colors.unfocused;
641 title_texture = child->title_unfocused; 688 title_texture = child->title_unfocused;
642 marks_texture = child->sway_view->marks_unfocused; 689 marks_texture = view->marks_unfocused;
643 } 690 }
644 691
645 if (child->sway_view->border == B_NORMAL) { 692 if (state->border == B_NORMAL) {
646 render_titlebar(output, damage, child, child->x, child->y, 693 render_titlebar(output, damage, child, state->swayc_x,
647 child->width, colors, title_texture, marks_texture); 694 state->swayc_y, state->swayc_width, colors,
695 title_texture, marks_texture);
648 } else { 696 } else {
649 render_top_border(output, damage, child, colors); 697 render_top_border(output, damage, child, colors);
650 } 698 }
@@ -662,22 +710,23 @@ static void render_container_simple(struct sway_output *output,
662static void render_container_tabbed(struct sway_output *output, 710static void render_container_tabbed(struct sway_output *output,
663 pixman_region32_t *damage, struct sway_container *con, 711 pixman_region32_t *damage, struct sway_container *con,
664 bool parent_focused) { 712 bool parent_focused) {
665 if (!con->children->length) { 713 if (!con->current.children->length) {
666 return; 714 return;
667 } 715 }
668 struct sway_seat *seat = input_manager_current_seat(input_manager); 716 struct sway_seat *seat = input_manager_current_seat(input_manager);
669 struct sway_container *focus = seat_get_focus(seat); 717 struct sway_container *focus = seat_get_focus(seat);
670 struct sway_container *current = seat_get_active_child(seat, con); 718 struct sway_container *current = seat_get_active_current_child(seat, con);
671 struct border_colors *current_colors = NULL; 719 struct border_colors *current_colors = &config->border_colors.unfocused;
720 struct sway_container_state *pstate = &con->current;
672 721
673 // Render tabs 722 // Render tabs
674 for (int i = 0; i < con->children->length; ++i) { 723 for (int i = 0; i < con->current.children->length; ++i) {
675 struct sway_container *child = con->children->items[i]; 724 struct sway_container *child = con->current.children->items[i];
725 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
726 struct sway_container_state *cstate = &child->current;
676 struct border_colors *colors; 727 struct border_colors *colors;
677 struct wlr_texture *title_texture; 728 struct wlr_texture *title_texture;
678 struct wlr_texture *marks_texture; 729 struct wlr_texture *marks_texture;
679 struct sway_view *view =
680 child->type == C_VIEW ? child->sway_view : NULL;
681 730
682 if (focus == child || parent_focused) { 731 if (focus == child || parent_focused) {
683 colors = &config->border_colors.focused; 732 colors = &config->border_colors.focused;
@@ -686,22 +735,22 @@ static void render_container_tabbed(struct sway_output *output,
686 } else if (child == current) { 735 } else if (child == current) {
687 colors = &config->border_colors.focused_inactive; 736 colors = &config->border_colors.focused_inactive;
688 title_texture = child->title_focused_inactive; 737 title_texture = child->title_focused_inactive;
689 marks_texture = view ? view->marks_focused : NULL; 738 marks_texture = view ? view->marks_focused_inactive : NULL;
690 } else { 739 } else {
691 colors = &config->border_colors.unfocused; 740 colors = &config->border_colors.unfocused;
692 title_texture = child->title_unfocused; 741 title_texture = child->title_unfocused;
693 marks_texture = view ? view->marks_unfocused : NULL; 742 marks_texture = view ? view->marks_unfocused : NULL;
694 } 743 }
695 744
696 int tab_width = con->width / con->children->length; 745 int tab_width = pstate->swayc_width / pstate->children->length;
697 int x = con->x + tab_width * i; 746 int x = pstate->swayc_x + tab_width * i;
698 // Make last tab use the remaining width of the parent 747 // Make last tab use the remaining width of the parent
699 if (i == con->children->length - 1) { 748 if (i == pstate->children->length - 1) {
700 tab_width = con->width - tab_width * i; 749 tab_width = pstate->swayc_width - tab_width * i;
701 } 750 }
702 751
703 render_titlebar(output, damage, child, x, child->y, tab_width, colors, 752 render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
704 title_texture, marks_texture); 753 colors, title_texture, marks_texture);
705 754
706 if (child == current) { 755 if (child == current) {
707 current_colors = colors; 756 current_colors = colors;
@@ -709,11 +758,13 @@ static void render_container_tabbed(struct sway_output *output,
709 } 758 }
710 759
711 // Render surface and left/right/bottom borders 760 // Render surface and left/right/bottom borders
712 if (current->type == C_VIEW) { 761 if (current) {
713 render_view(output, damage, current, current_colors); 762 if (current->type == C_VIEW) {
714 } else { 763 render_view(output, damage, current, current_colors);
715 render_container(output, damage, current, 764 } else {
716 parent_focused || current == focus); 765 render_container(output, damage, current,
766 parent_focused || current == focus);
767 }
717 } 768 }
718} 769}
719 770
@@ -723,22 +774,23 @@ static void render_container_tabbed(struct sway_output *output,
723static void render_container_stacked(struct sway_output *output, 774static void render_container_stacked(struct sway_output *output,
724 pixman_region32_t *damage, struct sway_container *con, 775 pixman_region32_t *damage, struct sway_container *con,
725 bool parent_focused) { 776 bool parent_focused) {
726 if (!con->children->length) { 777 if (!con->current.children->length) {
727 return; 778 return;
728 } 779 }
729 struct sway_seat *seat = input_manager_current_seat(input_manager); 780 struct sway_seat *seat = input_manager_current_seat(input_manager);
730 struct sway_container *focus = seat_get_focus(seat); 781 struct sway_container *focus = seat_get_focus(seat);
731 struct sway_container *current = seat_get_active_child(seat, con); 782 struct sway_container *current = seat_get_active_current_child(seat, con);
732 struct border_colors *current_colors = NULL; 783 struct border_colors *current_colors = &config->border_colors.unfocused;
784 struct sway_container_state *pstate = &con->current;
733 785
734 // Render titles 786 // Render titles
735 for (int i = 0; i < con->children->length; ++i) { 787 for (int i = 0; i < con->current.children->length; ++i) {
736 struct sway_container *child = con->children->items[i]; 788 struct sway_container *child = con->current.children->items[i];
789 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
790 struct sway_container_state *cstate = &child->current;
737 struct border_colors *colors; 791 struct border_colors *colors;
738 struct wlr_texture *title_texture; 792 struct wlr_texture *title_texture;
739 struct wlr_texture *marks_texture; 793 struct wlr_texture *marks_texture;
740 struct sway_view *view =
741 child->type == C_VIEW ? child->sway_view : NULL;
742 794
743 if (focus == child || parent_focused) { 795 if (focus == child || parent_focused) {
744 colors = &config->border_colors.focused; 796 colors = &config->border_colors.focused;
@@ -754,9 +806,9 @@ static void render_container_stacked(struct sway_output *output,
754 marks_texture = view ? view->marks_unfocused : NULL; 806 marks_texture = view ? view->marks_unfocused : NULL;
755 } 807 }
756 808
757 int y = con->y + container_titlebar_height() * i; 809 int y = pstate->swayc_y + container_titlebar_height() * i;
758 render_titlebar(output, damage, child, child->x, y, child->width, 810 render_titlebar(output, damage, child, cstate->swayc_x, y,
759 colors, title_texture, marks_texture); 811 cstate->swayc_width, colors, title_texture, marks_texture);
760 812
761 if (child == current) { 813 if (child == current) {
762 current_colors = colors; 814 current_colors = colors;
@@ -764,18 +816,20 @@ static void render_container_stacked(struct sway_output *output,
764 } 816 }
765 817
766 // Render surface and left/right/bottom borders 818 // Render surface and left/right/bottom borders
767 if (current->type == C_VIEW) { 819 if (current) {
768 render_view(output, damage, current, current_colors); 820 if (current->type == C_VIEW) {
769 } else { 821 render_view(output, damage, current, current_colors);
770 render_container(output, damage, current, 822 } else {
771 parent_focused || current == focus); 823 render_container(output, damage, current,
824 parent_focused || current == focus);
825 }
772 } 826 }
773} 827}
774 828
775static void render_container(struct sway_output *output, 829static void render_container(struct sway_output *output,
776 pixman_region32_t *damage, struct sway_container *con, 830 pixman_region32_t *damage, struct sway_container *con,
777 bool parent_focused) { 831 bool parent_focused) {
778 switch (con->layout) { 832 switch (con->current.layout) {
779 case L_NONE: 833 case L_NONE:
780 case L_HORIZ: 834 case L_HORIZ:
781 case L_VERT: 835 case L_VERT:
@@ -812,10 +866,11 @@ static void render_floating_container(struct sway_output *soutput,
812 marks_texture = view->marks_unfocused; 866 marks_texture = view->marks_unfocused;
813 } 867 }
814 868
815 if (con->sway_view->border == B_NORMAL) { 869 if (con->current.border == B_NORMAL) {
816 render_titlebar(soutput, damage, con, con->x, con->y, con->width, 870 render_titlebar(soutput, damage, con, con->current.swayc_x,
817 colors, title_texture, marks_texture); 871 con->current.swayc_y, con->current.swayc_width, colors,
818 } else if (con->sway_view->border != B_NONE) { 872 title_texture, marks_texture);
873 } else if (con->current.border != B_NONE) {
819 render_top_border(soutput, damage, con, colors); 874 render_top_border(soutput, damage, con, colors);
820 } 875 }
821 render_view(soutput, damage, con, colors); 876 render_view(soutput, damage, con, colors);
@@ -826,17 +881,18 @@ static void render_floating_container(struct sway_output *soutput,
826 881
827static void render_floating(struct sway_output *soutput, 882static void render_floating(struct sway_output *soutput,
828 pixman_region32_t *damage) { 883 pixman_region32_t *damage) {
829 for (int i = 0; i < root_container.children->length; ++i) { 884 for (int i = 0; i < root_container.current.children->length; ++i) {
830 struct sway_container *output = root_container.children->items[i]; 885 struct sway_container *output =
831 for (int j = 0; j < output->children->length; ++j) { 886 root_container.current.children->items[i];
832 struct sway_container *workspace = output->children->items[j]; 887 for (int j = 0; j < output->current.children->length; ++j) {
833 struct sway_workspace *ws = workspace->sway_workspace; 888 struct sway_container *ws = output->current.children->items[j];
834 if (!workspace_is_visible(workspace)) { 889 if (!workspace_is_visible(ws)) {
835 continue; 890 continue;
836 } 891 }
837 for (int k = 0; k < ws->floating->children->length; ++k) { 892 list_t *floating =
838 struct sway_container *floater = 893 ws->current.ws_floating->current.children;
839 ws->floating->children->items[k]; 894 for (int k = 0; k < floating->length; ++k) {
895 struct sway_container *floater = floating->items[k];
840 render_floating_container(soutput, damage, floater); 896 render_floating_container(soutput, damage, floater);
841 } 897 }
842 } 898 }
@@ -850,7 +906,7 @@ static struct sway_container *output_get_active_workspace(
850 seat_get_focus_inactive(seat, output->swayc); 906 seat_get_focus_inactive(seat, output->swayc);
851 if (!focus) { 907 if (!focus) {
852 // We've never been to this output before 908 // We've never been to this output before
853 focus = output->swayc->children->items[0]; 909 focus = output->swayc->current.children->items[0];
854 } 910 }
855 struct sway_container *workspace = focus; 911 struct sway_container *workspace = focus;
856 if (workspace->type != C_WORKSPACE) { 912 if (workspace->type != C_WORKSPACE) {
@@ -891,8 +947,9 @@ static void render_output(struct sway_output *output, struct timespec *when,
891 } 947 }
892 948
893 struct sway_container *workspace = output_get_active_workspace(output); 949 struct sway_container *workspace = output_get_active_workspace(output);
950 struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
894 951
895 if (workspace->sway_workspace->fullscreen) { 952 if (fullscreen_view) {
896 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; 953 float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
897 954
898 int nrects; 955 int nrects;
@@ -903,10 +960,9 @@ static void render_output(struct sway_output *output, struct timespec *when,
903 } 960 }
904 961
905 // TODO: handle views smaller than the output 962 // TODO: handle views smaller than the output
906 render_view_surfaces( 963 render_view_surfaces(fullscreen_view, output, damage, 1.0f);
907 workspace->sway_workspace->fullscreen, output, damage, 1.0f);
908 964
909 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 965 if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
910 render_unmanaged(output, damage, 966 render_unmanaged(output, damage,
911 &root_container.sway_root->xwayland_unmanaged); 967 &root_container.sway_root->xwayland_unmanaged);
912 } 968 }
@@ -1022,11 +1078,11 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
1022 }; 1078 };
1023 1079
1024 struct sway_container *workspace = output_get_active_workspace(output); 1080 struct sway_container *workspace = output_get_active_workspace(output);
1025 if (workspace->sway_workspace->fullscreen) { 1081 if (workspace->current.ws_fullscreen) {
1026 send_frame_done_container_iterator( 1082 send_frame_done_container_iterator(
1027 workspace->sway_workspace->fullscreen->swayc, &data); 1083 workspace->current.ws_fullscreen->swayc, &data);
1028 1084
1029 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 1085 if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) {
1030 send_frame_done_unmanaged(&data, 1086 send_frame_done_unmanaged(&data,
1031 &root_container.sway_root->xwayland_unmanaged); 1087 &root_container.sway_root->xwayland_unmanaged);
1032 } 1088 }
@@ -1168,6 +1224,16 @@ void output_damage_from_view(struct sway_output *output,
1168 output_damage_view(output, view, false); 1224 output_damage_view(output, view, false);
1169} 1225}
1170 1226
1227// Expecting an unscaled box in layout coordinates
1228void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
1229 struct wlr_box box;
1230 memcpy(&box, _box, sizeof(struct wlr_box));
1231 box.x -= output->swayc->current.swayc_x;
1232 box.y -= output->swayc->current.swayc_y;
1233 scale_box(&box, output->wlr_output->scale);
1234 wlr_output_damage_add_box(output->damage, &box);
1235}
1236
1171static void output_damage_whole_container_iterator(struct sway_container *con, 1237static void output_damage_whole_container_iterator(struct sway_container *con,
1172 void *data) { 1238 void *data) {
1173 struct sway_output *output = data; 1239 struct sway_output *output = data;
@@ -1182,10 +1248,10 @@ static void output_damage_whole_container_iterator(struct sway_container *con,
1182void output_damage_whole_container(struct sway_output *output, 1248void output_damage_whole_container(struct sway_output *output,
1183 struct sway_container *con) { 1249 struct sway_container *con) {
1184 struct wlr_box box = { 1250 struct wlr_box box = {
1185 .x = con->x - output->wlr_output->lx, 1251 .x = con->current.swayc_x - output->wlr_output->lx,
1186 .y = con->y - output->wlr_output->ly, 1252 .y = con->current.swayc_y - output->wlr_output->ly,
1187 .width = con->width, 1253 .width = con->current.swayc_width,
1188 .height = con->height, 1254 .height = con->current.swayc_height,
1189 }; 1255 };
1190 scale_box(&box, output->wlr_output->scale); 1256 scale_box(&box, output->wlr_output->scale);
1191 wlr_output_damage_add_box(output->damage, &box); 1257 wlr_output_damage_add_box(output->damage, &box);
@@ -1209,18 +1275,20 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
1209 wl_list_remove(&output->destroy.link); 1275 wl_list_remove(&output->destroy.link);
1210 output->wlr_output->data = NULL; 1276 output->wlr_output->data = NULL;
1211 free(output); 1277 free(output);
1278
1279 arrange_and_commit(&root_container);
1212} 1280}
1213 1281
1214static void handle_mode(struct wl_listener *listener, void *data) { 1282static void handle_mode(struct wl_listener *listener, void *data) {
1215 struct sway_output *output = wl_container_of(listener, output, mode); 1283 struct sway_output *output = wl_container_of(listener, output, mode);
1216 arrange_layers(output); 1284 arrange_layers(output);
1217 arrange_output(output->swayc); 1285 arrange_and_commit(output->swayc);
1218} 1286}
1219 1287
1220static void handle_transform(struct wl_listener *listener, void *data) { 1288static void handle_transform(struct wl_listener *listener, void *data) {
1221 struct sway_output *output = wl_container_of(listener, output, transform); 1289 struct sway_output *output = wl_container_of(listener, output, transform);
1222 arrange_layers(output); 1290 arrange_layers(output);
1223 arrange_output(output->swayc); 1291 arrange_and_commit(output->swayc);
1224} 1292}
1225 1293
1226static void handle_scale_iterator(struct sway_container *view, void *data) { 1294static void handle_scale_iterator(struct sway_container *view, void *data) {
@@ -1230,8 +1298,8 @@ static void handle_scale_iterator(struct sway_container *view, void *data) {
1230static void handle_scale(struct wl_listener *listener, void *data) { 1298static void handle_scale(struct wl_listener *listener, void *data) {
1231 struct sway_output *output = wl_container_of(listener, output, scale); 1299 struct sway_output *output = wl_container_of(listener, output, scale);
1232 arrange_layers(output); 1300 arrange_layers(output);
1233 arrange_output(output->swayc);
1234 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); 1301 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
1302 arrange_and_commit(output->swayc);
1235} 1303}
1236 1304
1237void handle_new_output(struct wl_listener *listener, void *data) { 1305void handle_new_output(struct wl_listener *listener, void *data) {
@@ -1296,5 +1364,5 @@ void output_enable(struct sway_output *output) {
1296 output->damage_destroy.notify = damage_handle_destroy; 1364 output->damage_destroy.notify = damage_handle_destroy;
1297 1365
1298 arrange_layers(output); 1366 arrange_layers(output);
1299 arrange_root(); 1367 arrange_and_commit(&root_container);
1300} 1368}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
new file mode 100644
index 00000000..d2932c87
--- /dev/null
+++ b/sway/desktop/transaction.c
@@ -0,0 +1,415 @@
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 int saved_buffer_width, saved_buffer_height;
45 uint32_t serial;
46 bool ready;
47};
48
49struct sway_transaction *transaction_create() {
50 struct sway_transaction *transaction =
51 calloc(1, sizeof(struct sway_transaction));
52 transaction->instructions = create_list();
53 if (server.debug_txn_timings) {
54 clock_gettime(CLOCK_MONOTONIC, &transaction->create_time);
55 }
56 return transaction;
57}
58
59static void remove_saved_view_buffer(
60 struct sway_transaction_instruction *instruction) {
61 if (instruction->saved_buffer) {
62 wlr_buffer_unref(instruction->saved_buffer);
63 instruction->saved_buffer = NULL;
64 }
65}
66
67static void save_view_buffer(struct sway_view *view,
68 struct sway_transaction_instruction *instruction) {
69 if (!sway_assert(instruction->saved_buffer == NULL,
70 "Didn't expect instruction to have a saved buffer already")) {
71 remove_saved_view_buffer(instruction);
72 }
73 if (view->surface && wlr_surface_has_buffer(view->surface)) {
74 instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer);
75 instruction->saved_buffer_width = view->surface->current->width;
76 instruction->saved_buffer_height = view->surface->current->height;
77 }
78}
79
80static void transaction_destroy(struct sway_transaction *transaction) {
81 // Free instructions
82 for (int i = 0; i < transaction->instructions->length; ++i) {
83 struct sway_transaction_instruction *instruction =
84 transaction->instructions->items[i];
85 struct sway_container *con = instruction->container;
86 for (int j = 0; j < con->instructions->length; ++j) {
87 if (con->instructions->items[j] == instruction) {
88 list_del(con->instructions, j);
89 break;
90 }
91 }
92 if (con->destroying && !con->instructions->length) {
93 container_free(con);
94 }
95 remove_saved_view_buffer(instruction);
96 free(instruction);
97 }
98 list_free(transaction->instructions);
99
100 if (transaction->timer) {
101 wl_event_source_remove(transaction->timer);
102 }
103 free(transaction);
104}
105
106static void copy_pending_state(struct sway_container *container,
107 struct sway_container_state *state) {
108 state->layout = container->layout;
109 state->swayc_x = container->x;
110 state->swayc_y = container->y;
111 state->swayc_width = container->width;
112 state->swayc_height = container->height;
113 state->has_gaps = container->has_gaps;
114 state->current_gaps = container->current_gaps;
115 state->gaps_inner = container->gaps_inner;
116 state->gaps_outer = container->gaps_outer;
117 state->parent = container->parent;
118
119 if (container->type == C_VIEW) {
120 struct sway_view *view = container->sway_view;
121 state->view_x = view->x;
122 state->view_y = view->y;
123 state->view_width = view->width;
124 state->view_height = view->height;
125 state->is_fullscreen = view->is_fullscreen;
126 state->border = view->border;
127 state->border_thickness = view->border_thickness;
128 state->border_top = view->border_top;
129 state->border_left = view->border_left;
130 state->border_right = view->border_right;
131 state->border_bottom = view->border_bottom;
132 } else if (container->type == C_WORKSPACE) {
133 state->ws_fullscreen = container->sway_workspace->fullscreen;
134 state->ws_floating = container->sway_workspace->floating;
135 state->children = create_list();
136 list_cat(state->children, container->children);
137 } else {
138 state->children = create_list();
139 list_cat(state->children, container->children);
140 }
141}
142
143static bool transaction_has_container(struct sway_transaction *transaction,
144 struct sway_container *container) {
145 for (int i = 0; i < transaction->instructions->length; ++i) {
146 struct sway_transaction_instruction *instruction =
147 transaction->instructions->items[i];
148 if (instruction->container == container) {
149 return true;
150 }
151 }
152 return false;
153}
154
155void transaction_add_container(struct sway_transaction *transaction,
156 struct sway_container *container) {
157 if (transaction_has_container(transaction, container)) {
158 return;
159 }
160 struct sway_transaction_instruction *instruction =
161 calloc(1, sizeof(struct sway_transaction_instruction));
162 instruction->transaction = transaction;
163 instruction->container = container;
164
165 copy_pending_state(container, &instruction->state);
166
167 if (container->type == C_VIEW) {
168 save_view_buffer(container->sway_view, instruction);
169 }
170 list_add(transaction->instructions, instruction);
171}
172
173/**
174 * Apply a transaction to the "current" state of the tree.
175 */
176static void transaction_apply(struct sway_transaction *transaction) {
177 wlr_log(L_DEBUG, "Applying transaction %p", transaction);
178 if (server.debug_txn_timings) {
179 struct timespec now;
180 clock_gettime(CLOCK_MONOTONIC, &now);
181 struct timespec *create = &transaction->create_time;
182 struct timespec *commit = &transaction->commit_time;
183 float ms_arranging = (commit->tv_sec - create->tv_sec) * 1000 +
184 (commit->tv_nsec - create->tv_nsec) / 1000000.0;
185 float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 +
186 (now.tv_nsec - commit->tv_nsec) / 1000000.0;
187 float ms_total = ms_arranging + ms_waiting;
188 wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, "
189 "%.1fms total (%.1f frames if 60Hz)", transaction,
190 ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60));
191 }
192
193 // Apply the instruction state to the container's current state
194 for (int i = 0; i < transaction->instructions->length; ++i) {
195 struct sway_transaction_instruction *instruction =
196 transaction->instructions->items[i];
197 struct sway_container *container = instruction->container;
198
199 // Damage the old and new locations
200 struct wlr_box old_box = {
201 .x = container->current.swayc_x,
202 .y = container->current.swayc_y,
203 .width = container->current.swayc_width,
204 .height = container->current.swayc_height,
205 };
206 struct wlr_box new_box = {
207 .x = instruction->state.swayc_x,
208 .y = instruction->state.swayc_y,
209 .width = instruction->state.swayc_width,
210 .height = instruction->state.swayc_height,
211 };
212 for (int j = 0; j < root_container.children->length; ++j) {
213 struct sway_container *output = root_container.children->items[j];
214 output_damage_box(output->sway_output, &old_box);
215 output_damage_box(output->sway_output, &new_box);
216 }
217
218 // There are separate children lists for each instruction state, the
219 // container's current state and the container's pending state
220 // (ie. con->children). The list itself needs to be freed here.
221 // Any child containers which are being deleted will be cleaned up in
222 // transaction_destroy().
223 list_free(container->current.children);
224
225 memcpy(&container->current, &instruction->state,
226 sizeof(struct sway_container_state));
227 }
228}
229
230/**
231 * For simplicity, we only progress the queue if it can be completely flushed.
232 */
233static void transaction_progress_queue() {
234 // We iterate this list in reverse because we're more likely to find a
235 // waiting transactions at the end of the list.
236 for (int i = server.transactions->length - 1; i >= 0; --i) {
237 struct sway_transaction *transaction = server.transactions->items[i];
238 if (transaction->num_waiting) {
239 return;
240 }
241 }
242 for (int i = 0; i < server.transactions->length; ++i) {
243 struct sway_transaction *transaction = server.transactions->items[i];
244 transaction_apply(transaction);
245 transaction_destroy(transaction);
246 }
247 server.transactions->length = 0;
248}
249
250static int handle_timeout(void *data) {
251 struct sway_transaction *transaction = data;
252 wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)",
253 transaction, transaction->num_waiting);
254 transaction->num_waiting = 0;
255 transaction_progress_queue();
256 return 0;
257}
258
259static bool should_configure(struct sway_container *con,
260 struct sway_transaction_instruction *instruction) {
261 if (con->type != C_VIEW) {
262 return false;
263 }
264 if (con->destroying) {
265 return false;
266 }
267 // The settled dimensions are what size the view will be once any pending
268 // configures have applied (excluding the one we might be configuring now).
269 // If these match the dimensions that this transaction wants then we don't
270 // need to configure it.
271 int settled_width = con->current.view_width;
272 int settled_height = con->current.view_height;
273 if (con->instructions->length) {
274 struct sway_transaction_instruction *last_instruction =
275 con->instructions->items[con->instructions->length - 1];
276 settled_width = last_instruction->state.view_width;
277 settled_height = last_instruction->state.view_height;
278 }
279 if (settled_width == instruction->state.view_width &&
280 settled_height == instruction->state.view_height) {
281 return false;
282 }
283 return true;
284}
285
286void transaction_commit(struct sway_transaction *transaction) {
287 wlr_log(L_DEBUG, "Transaction %p committing with %i instructions",
288 transaction, transaction->instructions->length);
289 transaction->num_waiting = 0;
290 for (int i = 0; i < transaction->instructions->length; ++i) {
291 struct sway_transaction_instruction *instruction =
292 transaction->instructions->items[i];
293 struct sway_container *con = instruction->container;
294 if (should_configure(con, instruction)) {
295 instruction->serial = view_configure(con->sway_view,
296 instruction->state.view_x,
297 instruction->state.view_y,
298 instruction->state.view_width,
299 instruction->state.view_height);
300 ++transaction->num_waiting;
301
302 // From here on we are rendering a saved buffer of the view, which
303 // means we can send a frame done event to make the client redraw it
304 // as soon as possible. Additionally, this is required if a view is
305 // mapping and its default geometry doesn't intersect an output.
306 struct timespec when;
307 wlr_surface_send_frame_done(con->sway_view->surface, &when);
308 }
309 list_add(con->instructions, instruction);
310 }
311 transaction->num_configures = transaction->num_waiting;
312 if (server.debug_txn_timings) {
313 clock_gettime(CLOCK_MONOTONIC, &transaction->commit_time);
314 }
315 if (server.transactions->length || transaction->num_waiting) {
316 list_add(server.transactions, transaction);
317 } else {
318 // There are no other transactions in progress, and this one has nothing
319 // to wait for, so we can skip the queue.
320 wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction);
321 transaction_apply(transaction);
322 transaction_destroy(transaction);
323 return;
324 }
325
326 if (transaction->num_waiting) {
327 // Set up a timer which the views must respond within
328 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
329 handle_timeout, transaction);
330 wl_event_source_timer_update(transaction->timer, TIMEOUT_MS);
331 }
332
333 // The debug tree shows the pending/live tree. Here is a good place to
334 // update it, because we make a transaction every time we change the pending
335 // tree.
336 update_debug_tree();
337}
338
339static void set_instruction_ready(
340 struct sway_transaction_instruction *instruction) {
341 instruction->ready = true;
342 struct sway_transaction *transaction = instruction->transaction;
343
344 if (server.debug_txn_timings) {
345 struct timespec now;
346 clock_gettime(CLOCK_MONOTONIC, &now);
347 struct timespec *start = &transaction->commit_time;
348 float ms = (now.tv_sec - start->tv_sec) * 1000 +
349 (now.tv_nsec - start->tv_nsec) / 1000000.0;
350 wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)",
351 transaction,
352 transaction->num_configures - transaction->num_waiting + 1,
353 transaction->num_configures, ms,
354 instruction->container->name);
355
356 }
357
358 // If all views are ready, apply the transaction.
359 // If the transaction has timed out then its num_waiting will be 0 already.
360 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) {
361#if !TRANSACTION_DEBUG
362 wlr_log(L_DEBUG, "Transaction %p is ready", transaction);
363 wl_event_source_timer_update(transaction->timer, 0);
364 transaction_progress_queue();
365#endif
366 }
367}
368
369/**
370 * Mark all of the view's instructions as ready up to and including the
371 * instruction at the given index. This allows the view to skip a configure.
372 */
373static void set_instructions_ready(struct sway_view *view, int index) {
374 for (int i = 0; i <= index; ++i) {
375 struct sway_transaction_instruction *instruction =
376 view->swayc->instructions->items[i];
377 set_instruction_ready(instruction);
378 }
379}
380
381void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) {
382 for (int i = 0; i < view->swayc->instructions->length; ++i) {
383 struct sway_transaction_instruction *instruction =
384 view->swayc->instructions->items[i];
385 if (instruction->serial == serial && !instruction->ready) {
386 set_instructions_ready(view, i);
387 return;
388 }
389 }
390}
391
392void transaction_notify_view_ready_by_size(struct sway_view *view,
393 int width, int height) {
394 for (int i = 0; i < view->swayc->instructions->length; ++i) {
395 struct sway_transaction_instruction *instruction =
396 view->swayc->instructions->items[i];
397 if (!instruction->ready && instruction->state.view_width == width &&
398 instruction->state.view_height == height) {
399 set_instructions_ready(view, i);
400 return;
401 }
402 }
403}
404
405struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,
406 int *width, int *height) {
407 struct sway_transaction_instruction *instruction =
408 view->swayc->instructions->items[0];
409 if (!instruction->saved_buffer) {
410 return NULL;
411 }
412 *width = instruction->saved_buffer_width;
413 *height = instruction->saved_buffer_height;
414 return instruction->saved_buffer->texture;
415}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 6ac0f9c7..da3a1cdd 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) {
@@ -166,10 +163,6 @@ static void destroy(struct sway_view *view) {
166 if (xdg_shell_view == NULL) { 163 if (xdg_shell_view == NULL) {
167 return; 164 return;
168 } 165 }
169 wl_list_remove(&xdg_shell_view->destroy.link);
170 wl_list_remove(&xdg_shell_view->map.link);
171 wl_list_remove(&xdg_shell_view->unmap.link);
172 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
173 free(xdg_shell_view); 166 free(xdg_shell_view);
174} 167}
175 168
@@ -189,18 +182,16 @@ static void handle_commit(struct wl_listener *listener, void *data) {
189 struct sway_xdg_shell_view *xdg_shell_view = 182 struct sway_xdg_shell_view *xdg_shell_view =
190 wl_container_of(listener, xdg_shell_view, commit); 183 wl_container_of(listener, xdg_shell_view, commit);
191 struct sway_view *view = &xdg_shell_view->view; 184 struct sway_view *view = &xdg_shell_view->view;
192 if (view->swayc && container_is_floating(view->swayc)) { 185 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
193 int width = view->wlr_xdg_surface->geometry.width; 186
194 int height = view->wlr_xdg_surface->geometry.height; 187 if (!view->swayc) {
195 if (!width && !height) { 188 return;
196 width = view->wlr_xdg_surface->surface->current->width; 189 }
197 height = view->wlr_xdg_surface->surface->current->height; 190
198 } 191 if (view->swayc->instructions->length) {
199 view_update_size(view, width, height); 192 transaction_notify_view_ready(view, xdg_surface->configure_serial);
200 } else {
201 view_update_size(view, xdg_shell_view->pending_width,
202 xdg_shell_view->pending_height);
203 } 193 }
194
204 view_update_title(view, false); 195 view_update_title(view, false);
205 view_damage_from(view); 196 view_damage_from(view);
206} 197}
@@ -215,8 +206,13 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
215static void handle_unmap(struct wl_listener *listener, void *data) { 206static void handle_unmap(struct wl_listener *listener, void *data) {
216 struct sway_xdg_shell_view *xdg_shell_view = 207 struct sway_xdg_shell_view *xdg_shell_view =
217 wl_container_of(listener, xdg_shell_view, unmap); 208 wl_container_of(listener, xdg_shell_view, unmap);
209 struct sway_view *view = &xdg_shell_view->view;
218 210
219 view_unmap(&xdg_shell_view->view); 211 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
212 return;
213 }
214
215 view_unmap(view);
220 216
221 wl_list_remove(&xdg_shell_view->commit.link); 217 wl_list_remove(&xdg_shell_view->commit.link);
222 wl_list_remove(&xdg_shell_view->new_popup.link); 218 wl_list_remove(&xdg_shell_view->new_popup.link);
@@ -234,8 +230,17 @@ static void handle_map(struct wl_listener *listener, void *data) {
234 view->natural_width = view->wlr_xdg_surface->surface->current->width; 230 view->natural_width = view->wlr_xdg_surface->surface->current->width;
235 view->natural_height = view->wlr_xdg_surface->surface->current->height; 231 view->natural_height = view->wlr_xdg_surface->surface->current->height;
236 } 232 }
233
237 view_map(view, view->wlr_xdg_surface->surface); 234 view_map(view, view->wlr_xdg_surface->surface);
238 235
236 if (xdg_surface->toplevel->client_pending.fullscreen) {
237 view_set_fullscreen(view, true);
238 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
239 arrange_and_commit(ws);
240 } else {
241 arrange_and_commit(view->swayc->parent);
242 }
243
239 xdg_shell_view->commit.notify = handle_commit; 244 xdg_shell_view->commit.notify = handle_commit;
240 wl_signal_add(&xdg_surface->surface->events.commit, 245 wl_signal_add(&xdg_surface->surface->events.commit,
241 &xdg_shell_view->commit); 246 &xdg_shell_view->commit);
@@ -243,16 +248,22 @@ static void handle_map(struct wl_listener *listener, void *data) {
243 xdg_shell_view->new_popup.notify = handle_new_popup; 248 xdg_shell_view->new_popup.notify = handle_new_popup;
244 wl_signal_add(&xdg_surface->events.new_popup, 249 wl_signal_add(&xdg_surface->events.new_popup,
245 &xdg_shell_view->new_popup); 250 &xdg_shell_view->new_popup);
246
247 if (xdg_surface->toplevel->client_pending.fullscreen) {
248 view_set_fullscreen(view, true);
249 }
250} 251}
251 252
252static void handle_destroy(struct wl_listener *listener, void *data) { 253static void handle_destroy(struct wl_listener *listener, void *data) {
253 struct sway_xdg_shell_view *xdg_shell_view = 254 struct sway_xdg_shell_view *xdg_shell_view =
254 wl_container_of(listener, xdg_shell_view, destroy); 255 wl_container_of(listener, xdg_shell_view, destroy);
255 view_destroy(&xdg_shell_view->view); 256 struct sway_view *view = &xdg_shell_view->view;
257 if (!sway_assert(view->swayc == NULL || view->swayc->destroying,
258 "Tried to destroy a mapped view")) {
259 return;
260 }
261 wl_list_remove(&xdg_shell_view->destroy.link);
262 wl_list_remove(&xdg_shell_view->map.link);
263 wl_list_remove(&xdg_shell_view->unmap.link);
264 wl_list_remove(&xdg_shell_view->request_fullscreen.link);
265 view->wlr_xdg_surface = NULL;
266 view_destroy(view);
256} 267}
257 268
258static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 269static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
@@ -261,6 +272,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
261 struct wlr_xdg_toplevel_set_fullscreen_event *e = data; 272 struct wlr_xdg_toplevel_set_fullscreen_event *e = data;
262 struct wlr_xdg_surface *xdg_surface = 273 struct wlr_xdg_surface *xdg_surface =
263 xdg_shell_view->view.wlr_xdg_surface; 274 xdg_shell_view->view.wlr_xdg_surface;
275 struct sway_view *view = &xdg_shell_view->view;
264 276
265 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, 277 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL,
266 "xdg_shell requested fullscreen of surface with role %i", 278 "xdg_shell requested fullscreen of surface with role %i",
@@ -271,7 +283,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
271 return; 283 return;
272 } 284 }
273 285
274 view_set_fullscreen(&xdg_shell_view->view, e->fullscreen); 286 view_set_fullscreen(view, e->fullscreen);
287
288 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
289 arrange_and_commit(ws);
275} 290}
276 291
277void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 292void 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 a37a4caf..69819280 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) {
@@ -161,10 +159,6 @@ static void destroy(struct sway_view *view) {
161 if (xdg_shell_v6_view == NULL) { 159 if (xdg_shell_v6_view == NULL) {
162 return; 160 return;
163 } 161 }
164 wl_list_remove(&xdg_shell_v6_view->destroy.link);
165 wl_list_remove(&xdg_shell_v6_view->map.link);
166 wl_list_remove(&xdg_shell_v6_view->unmap.link);
167 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
168 free(xdg_shell_v6_view); 162 free(xdg_shell_v6_view);
169} 163}
170 164
@@ -184,18 +178,15 @@ static void handle_commit(struct wl_listener *listener, void *data) {
184 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 178 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
185 wl_container_of(listener, xdg_shell_v6_view, commit); 179 wl_container_of(listener, xdg_shell_v6_view, commit);
186 struct sway_view *view = &xdg_shell_v6_view->view; 180 struct sway_view *view = &xdg_shell_v6_view->view;
187 if (view->swayc && container_is_floating(view->swayc)) { 181 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6;
188 int width = view->wlr_xdg_surface_v6->geometry.width; 182
189 int height = view->wlr_xdg_surface_v6->geometry.height; 183 if (!view->swayc) {
190 if (!width && !height) { 184 return;
191 width = view->wlr_xdg_surface_v6->surface->current->width;
192 height = view->wlr_xdg_surface_v6->surface->current->height;
193 }
194 view_update_size(view, width, height);
195 } else {
196 view_update_size(view, xdg_shell_v6_view->pending_width,
197 xdg_shell_v6_view->pending_height);
198 } 185 }
186 if (view->swayc->instructions->length) {
187 transaction_notify_view_ready(view, xdg_surface_v6->configure_serial);
188 }
189
199 view_update_title(view, false); 190 view_update_title(view, false);
200 view_damage_from(view); 191 view_damage_from(view);
201} 192}
@@ -210,8 +201,13 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
210static void handle_unmap(struct wl_listener *listener, void *data) { 201static void handle_unmap(struct wl_listener *listener, void *data) {
211 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 202 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
212 wl_container_of(listener, xdg_shell_v6_view, unmap); 203 wl_container_of(listener, xdg_shell_v6_view, unmap);
204 struct sway_view *view = &xdg_shell_v6_view->view;
205
206 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
207 return;
208 }
213 209
214 view_unmap(&xdg_shell_v6_view->view); 210 view_unmap(view);
215 211
216 wl_list_remove(&xdg_shell_v6_view->commit.link); 212 wl_list_remove(&xdg_shell_v6_view->commit.link);
217 wl_list_remove(&xdg_shell_v6_view->new_popup.link); 213 wl_list_remove(&xdg_shell_v6_view->new_popup.link);
@@ -229,8 +225,17 @@ static void handle_map(struct wl_listener *listener, void *data) {
229 view->natural_width = view->wlr_xdg_surface_v6->surface->current->width; 225 view->natural_width = view->wlr_xdg_surface_v6->surface->current->width;
230 view->natural_height = view->wlr_xdg_surface_v6->surface->current->height; 226 view->natural_height = view->wlr_xdg_surface_v6->surface->current->height;
231 } 227 }
228
232 view_map(view, view->wlr_xdg_surface_v6->surface); 229 view_map(view, view->wlr_xdg_surface_v6->surface);
233 230
231 if (xdg_surface->toplevel->client_pending.fullscreen) {
232 view_set_fullscreen(view, true);
233 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
234 arrange_and_commit(ws);
235 } else {
236 arrange_and_commit(view->swayc->parent);
237 }
238
234 xdg_shell_v6_view->commit.notify = handle_commit; 239 xdg_shell_v6_view->commit.notify = handle_commit;
235 wl_signal_add(&xdg_surface->surface->events.commit, 240 wl_signal_add(&xdg_surface->surface->events.commit,
236 &xdg_shell_v6_view->commit); 241 &xdg_shell_v6_view->commit);
@@ -238,16 +243,18 @@ static void handle_map(struct wl_listener *listener, void *data) {
238 xdg_shell_v6_view->new_popup.notify = handle_new_popup; 243 xdg_shell_v6_view->new_popup.notify = handle_new_popup;
239 wl_signal_add(&xdg_surface->events.new_popup, 244 wl_signal_add(&xdg_surface->events.new_popup,
240 &xdg_shell_v6_view->new_popup); 245 &xdg_shell_v6_view->new_popup);
241
242 if (xdg_surface->toplevel->client_pending.fullscreen) {
243 view_set_fullscreen(view, true);
244 }
245} 246}
246 247
247static void handle_destroy(struct wl_listener *listener, void *data) { 248static void handle_destroy(struct wl_listener *listener, void *data) {
248 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 249 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
249 wl_container_of(listener, xdg_shell_v6_view, destroy); 250 wl_container_of(listener, xdg_shell_v6_view, destroy);
250 view_destroy(&xdg_shell_v6_view->view); 251 struct sway_view *view = &xdg_shell_v6_view->view;
252 wl_list_remove(&xdg_shell_v6_view->destroy.link);
253 wl_list_remove(&xdg_shell_v6_view->map.link);
254 wl_list_remove(&xdg_shell_v6_view->unmap.link);
255 wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
256 view->wlr_xdg_surface_v6 = NULL;
257 view_destroy(view);
251} 258}
252 259
253static void handle_request_fullscreen(struct wl_listener *listener, void *data) { 260static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
@@ -256,6 +263,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
256 struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data; 263 struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data;
257 struct wlr_xdg_surface_v6 *xdg_surface = 264 struct wlr_xdg_surface_v6 *xdg_surface =
258 xdg_shell_v6_view->view.wlr_xdg_surface_v6; 265 xdg_shell_v6_view->view.wlr_xdg_surface_v6;
266 struct sway_view *view = &xdg_shell_v6_view->view;
259 267
260 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL, 268 if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL,
261 "xdg_shell_v6 requested fullscreen of surface with role %i", 269 "xdg_shell_v6 requested fullscreen of surface with role %i",
@@ -266,7 +274,10 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
266 return; 274 return;
267 } 275 }
268 276
269 view_set_fullscreen(&xdg_shell_v6_view->view, e->fullscreen); 277 view_set_fullscreen(view, e->fullscreen);
278
279 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
280 arrange_and_commit(ws);
270} 281}
271 282
272void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { 283void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index fcc8164f..6e63c505 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) {
@@ -257,14 +258,6 @@ static void destroy(struct sway_view *view) {
257 if (xwayland_view == NULL) { 258 if (xwayland_view == NULL) {
258 return; 259 return;
259 } 260 }
260 wl_list_remove(&xwayland_view->destroy.link);
261 wl_list_remove(&xwayland_view->request_configure.link);
262 wl_list_remove(&xwayland_view->request_fullscreen.link);
263 wl_list_remove(&xwayland_view->set_title.link);
264 wl_list_remove(&xwayland_view->set_class.link);
265 wl_list_remove(&xwayland_view->set_window_type.link);
266 wl_list_remove(&xwayland_view->map.link);
267 wl_list_remove(&xwayland_view->unmap.link);
268 free(xwayland_view); 261 free(xwayland_view);
269} 262}
270 263
@@ -285,22 +278,27 @@ static void handle_commit(struct wl_listener *listener, void *data) {
285 wl_container_of(listener, xwayland_view, commit); 278 wl_container_of(listener, xwayland_view, commit);
286 struct sway_view *view = &xwayland_view->view; 279 struct sway_view *view = &xwayland_view->view;
287 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 280 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
288 if (view->swayc && container_is_floating(view->swayc)) { 281 struct wlr_surface_state *surface_state = xsurface->surface->current;
289 view_update_size(view, xsurface->width, xsurface->height); 282
290 } else { 283 if (view->swayc->instructions->length) {
291 view_update_size(view, xwayland_view->pending_width, 284 transaction_notify_view_ready_by_size(view,
292 xwayland_view->pending_height); 285 surface_state->width, surface_state->height);
293 } 286 }
294 view_update_position(view,
295 xwayland_view->pending_lx, xwayland_view->pending_ly);
296 view_damage_from(view); 287 view_damage_from(view);
297} 288}
298 289
299static void handle_unmap(struct wl_listener *listener, void *data) { 290static void handle_unmap(struct wl_listener *listener, void *data) {
300 struct sway_xwayland_view *xwayland_view = 291 struct sway_xwayland_view *xwayland_view =
301 wl_container_of(listener, xwayland_view, unmap); 292 wl_container_of(listener, xwayland_view, unmap);
293 struct sway_view *view = &xwayland_view->view;
294
295 if (!sway_assert(view->surface, "Cannot unmap unmapped view")) {
296 return;
297 }
298
299 view_unmap(view);
300
302 wl_list_remove(&xwayland_view->commit.link); 301 wl_list_remove(&xwayland_view->commit.link);
303 view_unmap(&xwayland_view->view);
304} 302}
305 303
306static void handle_map(struct wl_listener *listener, void *data) { 304static void handle_map(struct wl_listener *listener, void *data) {
@@ -322,12 +320,31 @@ static void handle_map(struct wl_listener *listener, void *data) {
322 320
323 if (xsurface->fullscreen) { 321 if (xsurface->fullscreen) {
324 view_set_fullscreen(view, true); 322 view_set_fullscreen(view, true);
323 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
324 arrange_and_commit(ws);
325 } else {
326 arrange_and_commit(view->swayc->parent);
325 } 327 }
326} 328}
327 329
328static void handle_destroy(struct wl_listener *listener, void *data) { 330static void handle_destroy(struct wl_listener *listener, void *data) {
329 struct sway_xwayland_view *xwayland_view = 331 struct sway_xwayland_view *xwayland_view =
330 wl_container_of(listener, xwayland_view, destroy); 332 wl_container_of(listener, xwayland_view, destroy);
333 struct sway_view *view = &xwayland_view->view;
334
335 if (view->surface) {
336 view_unmap(view);
337 wl_list_remove(&xwayland_view->commit.link);
338 }
339
340 wl_list_remove(&xwayland_view->destroy.link);
341 wl_list_remove(&xwayland_view->request_configure.link);
342 wl_list_remove(&xwayland_view->request_fullscreen.link);
343 wl_list_remove(&xwayland_view->set_title.link);
344 wl_list_remove(&xwayland_view->set_class.link);
345 wl_list_remove(&xwayland_view->set_window_type.link);
346 wl_list_remove(&xwayland_view->map.link);
347 wl_list_remove(&xwayland_view->unmap.link);
331 view_destroy(&xwayland_view->view); 348 view_destroy(&xwayland_view->view);
332} 349}
333 350
@@ -343,10 +360,12 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
343 return; 360 return;
344 } 361 }
345 if (container_is_floating(view->swayc)) { 362 if (container_is_floating(view->swayc)) {
346 configure(view, view->x, view->y, ev->width, ev->height); 363 configure(view, view->swayc->current.view_x,
364 view->swayc->current.view_y, ev->width, ev->height);
347 } else { 365 } else {
348 configure(view, view->x, view->y, 366 configure(view, view->swayc->current.view_x,
349 view->width, view->height); 367 view->swayc->current.view_y, view->swayc->current.view_width,
368 view->swayc->current.view_height);
350 } 369 }
351} 370}
352 371
@@ -359,6 +378,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
359 return; 378 return;
360 } 379 }
361 view_set_fullscreen(view, xsurface->fullscreen); 380 view_set_fullscreen(view, xsurface->fullscreen);
381
382 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
383 arrange_and_commit(ws);
362} 384}
363 385
364static void handle_set_title(struct wl_listener *listener, void *data) { 386static 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 2de0c7a8..6f6137c4 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);
@@ -753,9 +768,6 @@ static void update_title_texture(struct sway_container *con,
753 "Unexpected type %s", container_type_to_str(con->type))) { 768 "Unexpected type %s", container_type_to_str(con->type))) {
754 return; 769 return;
755 } 770 }
756 if (!con->width) {
757 return;
758 }
759 struct sway_container *output = container_parent(con, C_OUTPUT); 771 struct sway_container *output = container_parent(con, C_OUTPUT);
760 if (!output) { 772 if (!output) {
761 return; 773 return;
@@ -917,13 +929,12 @@ void container_set_floating(struct sway_container *container, bool enable) {
917 929
918 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 930 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
919 struct sway_seat *seat = input_manager_current_seat(input_manager); 931 struct sway_seat *seat = input_manager_current_seat(input_manager);
920 container_damage_whole(container);
921 932
922 if (enable) { 933 if (enable) {
923 container_remove_child(container); 934 container_remove_child(container);
924 container_add_child(workspace->sway_workspace->floating, container); 935 container_add_child(workspace->sway_workspace->floating, container);
925 if (container->type == C_VIEW) { 936 if (container->type == C_VIEW) {
926 view_autoconfigure(container->sway_view); 937 view_init_floating(container->sway_view);
927 } 938 }
928 seat_set_focus(seat, seat_get_focus_inactive(seat, container)); 939 seat_set_focus(seat, seat_get_focus_inactive(seat, container));
929 container_reap_empty_recursive(workspace); 940 container_reap_empty_recursive(workspace);
@@ -939,8 +950,8 @@ void container_set_floating(struct sway_container *container, bool enable) {
939 container->is_sticky = false; 950 container->is_sticky = false;
940 container_reap_empty_recursive(workspace->sway_workspace->floating); 951 container_reap_empty_recursive(workspace->sway_workspace->floating);
941 } 952 }
942 arrange_workspace(workspace); 953
943 container_damage_whole(container); 954 ipc_event_window(container, "floating");
944} 955}
945 956
946void container_set_geometry_from_floating_view(struct sway_container *con) { 957void container_set_geometry_from_floating_view(struct sway_container *con) {
@@ -969,3 +980,10 @@ bool container_is_floating(struct sway_container *container) {
969 } 980 }
970 return container->parent == workspace->sway_workspace->floating; 981 return container->parent == workspace->sway_workspace->floating;
971} 982}
983
984void container_get_box(struct sway_container *container, struct wlr_box *box) {
985 box->x = container->x;
986 box->y = container->y;
987 box->width = container->width;
988 box->height = container->height;
989}
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 98637c33..6263bfb0 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,32 +140,33 @@ 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
166 container_set_geometry_from_floating_view(view->swayc);
167
144 // Don't maximize floating windows 168 // Don't maximize floating windows
145 view_set_tiled(view, false); 169 view_set_tiled(view, false);
146
147 view_configure(view, lx, ly, width, height);
148} 170}
149 171
150void view_autoconfigure(struct sway_view *view) { 172void view_autoconfigure(struct sway_view *view) {
@@ -156,12 +178,14 @@ void view_autoconfigure(struct sway_view *view) {
156 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 178 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
157 179
158 if (view->is_fullscreen) { 180 if (view->is_fullscreen) {
159 view_configure(view, output->x, output->y, output->width, output->height); 181 view->x = output->x;
182 view->y = output->y;
183 view->width = output->width;
184 view->height = output->height;
160 return; 185 return;
161 } 186 }
162 187
163 if (container_is_floating(view->swayc)) { 188 if (container_is_floating(view->swayc)) {
164 view_autoconfigure_floating(view);
165 return; 189 return;
166 } 190 }
167 191
@@ -181,20 +205,22 @@ void view_autoconfigure(struct sway_view *view) {
181 } 205 }
182 } 206 }
183 207
208 struct sway_container *con = view->swayc;
209
184 view->border_top = view->border_bottom = true; 210 view->border_top = view->border_bottom = true;
185 view->border_left = view->border_right = true; 211 view->border_left = view->border_right = true;
186 if (config->hide_edge_borders == E_BOTH 212 if (config->hide_edge_borders == E_BOTH
187 || config->hide_edge_borders == E_VERTICAL 213 || config->hide_edge_borders == E_VERTICAL
188 || (config->hide_edge_borders == E_SMART && !other_views)) { 214 || (config->hide_edge_borders == E_SMART && !other_views)) {
189 view->border_left = view->swayc->x != ws->x; 215 view->border_left = con->x != ws->x;
190 int right_x = view->swayc->x + view->swayc->width; 216 int right_x = con->x + con->width;
191 view->border_right = right_x != ws->x + ws->width; 217 view->border_right = right_x != ws->x + ws->width;
192 } 218 }
193 if (config->hide_edge_borders == E_BOTH 219 if (config->hide_edge_borders == E_BOTH
194 || config->hide_edge_borders == E_HORIZONTAL 220 || config->hide_edge_borders == E_HORIZONTAL
195 || (config->hide_edge_borders == E_SMART && !other_views)) { 221 || (config->hide_edge_borders == E_SMART && !other_views)) {
196 view->border_top = view->swayc->y != ws->y; 222 view->border_top = con->y != ws->y;
197 int bottom_y = view->swayc->y + view->swayc->height; 223 int bottom_y = con->y + con->height;
198 view->border_bottom = bottom_y != ws->y + ws->height; 224 view->border_bottom = bottom_y != ws->y + ws->height;
199 } 225 }
200 226
@@ -205,45 +231,44 @@ void view_autoconfigure(struct sway_view *view) {
205 // In a tabbed or stacked container, the swayc's y is the top of the title 231 // In a tabbed or stacked container, the swayc's y is the top of the title
206 // area. We have to offset the surface y by the height of the title bar, and 232 // area. We have to offset the surface y by the height of the title bar, and
207 // disable any top border because we'll always have the title bar. 233 // disable any top border because we'll always have the title bar.
208 if (view->swayc->parent->layout == L_TABBED) { 234 if (con->parent->layout == L_TABBED) {
209 y_offset = container_titlebar_height(); 235 y_offset = container_titlebar_height();
210 view->border_top = false; 236 view->border_top = false;
211 } else if (view->swayc->parent->layout == L_STACKED) { 237 } else if (con->parent->layout == L_STACKED) {
212 y_offset = container_titlebar_height() 238 y_offset = container_titlebar_height() * con->parent->children->length;
213 * view->swayc->parent->children->length;
214 view->border_top = false; 239 view->border_top = false;
215 } 240 }
216 241
217 switch (view->border) { 242 switch (view->border) {
218 case B_NONE: 243 case B_NONE:
219 x = view->swayc->x; 244 x = con->x;
220 y = view->swayc->y + y_offset; 245 y = con->y + y_offset;
221 width = view->swayc->width; 246 width = con->width;
222 height = view->swayc->height - y_offset; 247 height = con->height - y_offset;
223 break; 248 break;
224 case B_PIXEL: 249 case B_PIXEL:
225 x = view->swayc->x + view->border_thickness * view->border_left; 250 x = con->x + view->border_thickness * view->border_left;
226 y = view->swayc->y + view->border_thickness * view->border_top + y_offset; 251 y = con->y + view->border_thickness * view->border_top + y_offset;
227 width = view->swayc->width 252 width = con->width
228 - view->border_thickness * view->border_left 253 - view->border_thickness * view->border_left
229 - view->border_thickness * view->border_right; 254 - view->border_thickness * view->border_right;
230 height = view->swayc->height - y_offset 255 height = con->height - y_offset
231 - view->border_thickness * view->border_top 256 - view->border_thickness * view->border_top
232 - view->border_thickness * view->border_bottom; 257 - view->border_thickness * view->border_bottom;
233 break; 258 break;
234 case B_NORMAL: 259 case B_NORMAL:
235 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border 260 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border
236 x = view->swayc->x + view->border_thickness * view->border_left; 261 x = con->x + view->border_thickness * view->border_left;
237 width = view->swayc->width 262 width = con->width
238 - view->border_thickness * view->border_left 263 - view->border_thickness * view->border_left
239 - view->border_thickness * view->border_right; 264 - view->border_thickness * view->border_right;
240 if (y_offset) { 265 if (y_offset) {
241 y = view->swayc->y + y_offset; 266 y = con->y + y_offset;
242 height = view->swayc->height - y_offset 267 height = con->height - y_offset
243 - view->border_thickness * view->border_bottom; 268 - view->border_thickness * view->border_bottom;
244 } else { 269 } else {
245 y = view->swayc->y + container_titlebar_height(); 270 y = con->y + container_titlebar_height();
246 height = view->swayc->height - container_titlebar_height() 271 height = con->height - container_titlebar_height()
247 - view->border_thickness * view->border_bottom; 272 - view->border_thickness * view->border_bottom;
248 } 273 }
249 break; 274 break;
@@ -251,8 +276,9 @@ void view_autoconfigure(struct sway_view *view) {
251 276
252 view->x = x; 277 view->x = x;
253 view->y = y; 278 view->y = y;
279 view->width = width;
280 view->height = height;
254 view_set_tiled(view, true); 281 view_set_tiled(view, true);
255 view_configure(view, x, y, width, height);
256} 282}
257 283
258void view_set_activated(struct sway_view *view, bool activated) { 284void view_set_activated(struct sway_view *view, bool activated) {
@@ -268,8 +294,7 @@ void view_set_tiled(struct sway_view *view, bool tiled) {
268 } 294 }
269} 295}
270 296
271// Set fullscreen, but without IPC events or arranging windows. 297void view_set_fullscreen(struct sway_view *view, bool fullscreen) {
272void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) {
273 if (view->is_fullscreen == fullscreen) { 298 if (view->is_fullscreen == fullscreen) {
274 return; 299 return;
275 } 300 }
@@ -315,27 +340,17 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) {
315 } else { 340 } else {
316 workspace->sway_workspace->fullscreen = NULL; 341 workspace->sway_workspace->fullscreen = NULL;
317 if (container_is_floating(view->swayc)) { 342 if (container_is_floating(view->swayc)) {
318 view_configure(view, view->saved_x, view->saved_y, 343 view->x = view->saved_x;
319 view->saved_width, view->saved_height); 344 view->y = view->saved_y;
345 view->width = view->saved_width;
346 view->height = view->saved_height;
347 container_set_geometry_from_floating_view(view->swayc);
320 } else { 348 } else {
321 view->swayc->width = view->swayc->saved_width; 349 view->swayc->width = view->swayc->saved_width;
322 view->swayc->height = view->swayc->saved_height; 350 view->swayc->height = view->swayc->saved_height;
323 view_autoconfigure(view);
324 } 351 }
325 } 352 }
326}
327
328void view_set_fullscreen(struct sway_view *view, bool fullscreen) {
329 if (view->is_fullscreen == fullscreen) {
330 return;
331 }
332 353
333 view_set_fullscreen_raw(view, fullscreen);
334
335 struct sway_container *workspace =
336 container_parent(view->swayc, C_WORKSPACE);
337 arrange_workspace(workspace);
338 output_damage_whole(workspace->parent->sway_output);
339 ipc_event_window(view->swayc, "fullscreen_mode"); 354 ipc_event_window(view->swayc, "fullscreen_mode");
340} 355}
341 356
@@ -365,6 +380,9 @@ static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
365 380
366void view_for_each_surface(struct sway_view *view, 381void view_for_each_surface(struct sway_view *view,
367 wlr_surface_iterator_func_t iterator, void *user_data) { 382 wlr_surface_iterator_func_t iterator, void *user_data) {
383 if (!view->surface) {
384 return;
385 }
368 if (view->impl->for_each_surface) { 386 if (view->impl->for_each_surface) {
369 view->impl->for_each_surface(view, iterator, user_data); 387 view->impl->for_each_surface(view, iterator, user_data);
370 } else { 388 } else {
@@ -518,8 +536,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
518 536
519 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 537 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
520 container_set_floating(view->swayc, true); 538 container_set_floating(view->swayc, true);
521 } else {
522 arrange_children_of(cont->parent);
523 } 539 }
524 540
525 input_manager_set_focus(input_manager, cont); 541 input_manager_set_focus(input_manager, cont);
@@ -531,42 +547,26 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
531 container_notify_subtree_changed(view->swayc->parent); 547 container_notify_subtree_changed(view->swayc->parent);
532 view_execute_criteria(view); 548 view_execute_criteria(view);
533 549
534 container_damage_whole(cont);
535 view_handle_container_reparent(&view->container_reparent, NULL); 550 view_handle_container_reparent(&view->container_reparent, NULL);
536} 551}
537 552
538void view_unmap(struct sway_view *view) { 553void view_unmap(struct sway_view *view) {
539 if (!sway_assert(view->surface != NULL, "cannot unmap unmapped view")) {
540 return;
541 }
542
543 wl_signal_emit(&view->events.unmap, view); 554 wl_signal_emit(&view->events.unmap, view);
544 555
545 if (view->is_fullscreen) {
546 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
547 ws->sway_workspace->fullscreen = NULL;
548 }
549
550 container_damage_whole(view->swayc);
551
552 wl_list_remove(&view->surface_new_subsurface.link); 556 wl_list_remove(&view->surface_new_subsurface.link);
553 wl_list_remove(&view->container_reparent.link); 557 wl_list_remove(&view->container_reparent.link);
554 558
555 struct sway_container *parent = container_destroy(view->swayc); 559 if (view->is_fullscreen) {
556 560 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
557 view->swayc = NULL; 561 ws->sway_workspace->fullscreen = NULL;
558 view->surface = NULL; 562 container_destroy(view->swayc);
559
560 if (view->title_format) {
561 free(view->title_format);
562 view->title_format = NULL;
563 }
564 563
565 if (parent->type == C_OUTPUT) { 564 arrange_and_commit(ws->parent);
566 arrange_output(parent);
567 } else { 565 } else {
568 arrange_children_of(parent); 566 struct sway_container *parent = container_destroy(view->swayc);
567 arrange_and_commit(parent);
569 } 568 }
569 view->surface = NULL;
570} 570}
571 571
572void view_update_position(struct sway_view *view, double lx, double ly) { 572void view_update_position(struct sway_view *view, double lx, double ly) {
@@ -940,7 +940,7 @@ void view_update_marks_textures(struct sway_view *view) {
940} 940}
941 941
942bool view_is_visible(struct sway_view *view) { 942bool view_is_visible(struct sway_view *view) {
943 if (!view->swayc) { 943 if (!view->swayc || view->swayc->destroying) {
944 return false; 944 return false;
945 } 945 }
946 struct sway_container *workspace = 946 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);