aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
authorLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-08-30 21:00:10 +1000
committerLibravatar Ryan Dwyer <ryandwyer1@gmail.com>2018-09-05 18:01:43 +1000
commit7586f150c058997d9dde387ea7c091ffa7a3c3c7 (patch)
tree63d19027974c1db62ce3a74ca1d2314eb6d5049b /sway
parentMerge pull request #2569 from RyanDwyer/deny-reload-repeat (diff)
downloadsway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.tar.gz
sway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.tar.zst
sway-7586f150c058997d9dde387ea7c091ffa7a3c3c7.zip
Implement type safe arguments and demote sway_container
This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once.
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c27
-rw-r--r--sway/commands/border.c13
-rw-r--r--sway/commands/floating.c18
-rw-r--r--sway/commands/focus.c264
-rw-r--r--sway/commands/fullscreen.c18
-rw-r--r--sway/commands/gaps.c35
-rw-r--r--sway/commands/hide_edge_borders.c4
-rw-r--r--sway/commands/kill.c20
-rw-r--r--sway/commands/layout.c186
-rw-r--r--sway/commands/mark.c7
-rw-r--r--sway/commands/move.c873
-rw-r--r--sway/commands/opacity.c3
-rw-r--r--sway/commands/reload.c2
-rw-r--r--sway/commands/rename.c11
-rw-r--r--sway/commands/resize.c86
-rw-r--r--sway/commands/scratchpad.c42
-rw-r--r--sway/commands/seat/cursor.c4
-rw-r--r--sway/commands/show_marks.c10
-rw-r--r--sway/commands/smart_gaps.c2
-rw-r--r--sway/commands/split.c23
-rw-r--r--sway/commands/sticky.c27
-rw-r--r--sway/commands/swap.c76
-rw-r--r--sway/commands/title_format.c7
-rw-r--r--sway/commands/unmark.c13
-rw-r--r--sway/commands/urgent.c7
-rw-r--r--sway/commands/workspace.c2
-rw-r--r--sway/config.c2
-rw-r--r--sway/config/bar.c14
-rw-r--r--sway/config/output.c47
-rw-r--r--sway/criteria.c41
-rw-r--r--sway/debug-tree.c111
-rw-r--r--sway/desktop/desktop.c31
-rw-r--r--sway/desktop/layer_shell.c23
-rw-r--r--sway/desktop/output.c182
-rw-r--r--sway/desktop/render.c259
-rw-r--r--sway/desktop/transaction.c374
-rw-r--r--sway/desktop/xdg_shell.c47
-rw-r--r--sway/desktop/xdg_shell_v6.c44
-rw-r--r--sway/desktop/xwayland.c50
-rw-r--r--sway/input/cursor.c158
-rw-r--r--sway/input/input-manager.c14
-rw-r--r--sway/input/seat.c514
-rw-r--r--sway/ipc-json.c189
-rw-r--r--sway/ipc-server.c55
-rw-r--r--sway/main.c6
-rw-r--r--sway/meson.build1
-rw-r--r--sway/server.c7
-rw-r--r--sway/tree/arrange.c110
-rw-r--r--sway/tree/container.c871
-rw-r--r--sway/tree/node.c151
-rw-r--r--sway/tree/output.c348
-rw-r--r--sway/tree/root.c186
-rw-r--r--sway/tree/view.c277
-rw-r--r--sway/tree/workspace.c493
54 files changed, 3153 insertions, 3232 deletions
diff --git a/sway/commands.c b/sway/commands.c
index e72b8916..b32628cd 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -212,6 +212,24 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
212 return res; 212 return res;
213} 213}
214 214
215static void set_config_node(struct sway_node *node) {
216 config->handler_context.node = node;
217 switch (node->type) {
218 case N_CONTAINER:
219 config->handler_context.container = node->sway_container;
220 config->handler_context.workspace = node->sway_container->workspace;
221 break;
222 case N_WORKSPACE:
223 config->handler_context.container = NULL;
224 config->handler_context.workspace = node->sway_workspace;
225 break;
226 default:
227 config->handler_context.container = NULL;
228 config->handler_context.workspace = NULL;
229 break;
230 }
231}
232
215struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { 233struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
216 // Even though this function will process multiple commands we will only 234 // Even though this function will process multiple commands we will only
217 // return the last error, if any (for now). (Since we have access to an 235 // return the last error, if any (for now). (Since we have access to an
@@ -295,12 +313,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
295 if (!config->handler_context.using_criteria) { 313 if (!config->handler_context.using_criteria) {
296 // without criteria, the command acts upon the focused 314 // without criteria, the command acts upon the focused
297 // container 315 // container
298 config->handler_context.current_container = 316 set_config_node(seat_get_focus_inactive(seat, &root->node));
299 seat_get_focus_inactive(seat, &root_container);
300 if (!sway_assert(config->handler_context.current_container,
301 "could not get focus-inactive for root container")) {
302 return NULL;
303 }
304 struct cmd_results *res = handler->handle(argc-1, argv+1); 317 struct cmd_results *res = handler->handle(argc-1, argv+1);
305 if (res->status != CMD_SUCCESS) { 318 if (res->status != CMD_SUCCESS) {
306 free_argv(argc, argv); 319 free_argv(argc, argv);
@@ -314,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
314 } else { 327 } else {
315 for (int i = 0; i < views->length; ++i) { 328 for (int i = 0; i < views->length; ++i) {
316 struct sway_view *view = views->items[i]; 329 struct sway_view *view = views->items[i];
317 config->handler_context.current_container = view->swayc; 330 set_config_node(&view->container->node);
318 struct cmd_results *res = handler->handle(argc-1, argv+1); 331 struct cmd_results *res = handler->handle(argc-1, argv+1);
319 if (res->status != CMD_SUCCESS) { 332 if (res->status != CMD_SUCCESS) {
320 free_argv(argc, argv); 333 free_argv(argc, argv);
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 9502c877..95498b2f 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -13,13 +13,12 @@ struct cmd_results *cmd_border(int argc, char **argv) {
13 return error; 13 return error;
14 } 14 }
15 15
16 struct sway_container *container = 16 struct sway_container *container = config->handler_context.container;
17 config->handler_context.current_container; 17 if (!container->view) {
18 if (container->type != C_VIEW) {
19 return cmd_results_new(CMD_INVALID, "border", 18 return cmd_results_new(CMD_INVALID, "border",
20 "Only views can have borders"); 19 "Only views can have borders");
21 } 20 }
22 struct sway_view *view = container->sway_view; 21 struct sway_view *view = container->view;
23 22
24 if (strcmp(argv[0], "none") == 0) { 23 if (strcmp(argv[0], "none") == 0) {
25 view->border = B_NONE; 24 view->border = B_NONE;
@@ -38,11 +37,11 @@ struct cmd_results *cmd_border(int argc, char **argv) {
38 view->border_thickness = atoi(argv[1]); 37 view->border_thickness = atoi(argv[1]);
39 } 38 }
40 39
41 if (container_is_floating(view->swayc)) { 40 if (container_is_floating(view->container)) {
42 container_set_geometry_from_floating_view(view->swayc); 41 container_set_geometry_from_floating_view(view->container);
43 } 42 }
44 43
45 arrange_windows(view->swayc); 44 arrange_container(view->container);
46 45
47 struct sway_seat *seat = input_manager_current_seat(input_manager); 46 struct sway_seat *seat = input_manager_current_seat(input_manager);
48 if (seat->cursor) { 47 if (seat->cursor) {
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index 436376e3..d8729094 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -15,24 +15,23 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
15 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) { 15 if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
16 return error; 16 return error;
17 } 17 }
18 struct sway_container *container = 18 struct sway_container *container = config->handler_context.container;
19 config->handler_context.current_container; 19 struct sway_workspace *workspace = config->handler_context.workspace;
20 if (container->type == C_WORKSPACE && container->children->length == 0) { 20 if (!container && workspace->tiling->length == 0) {
21 return cmd_results_new(CMD_INVALID, "floating", 21 return cmd_results_new(CMD_INVALID, "floating",
22 "Can't float an empty workspace"); 22 "Can't float an empty workspace");
23 } 23 }
24 if (container->type == C_WORKSPACE) { 24 if (!container) {
25 // Wrap the workspace's children in a container so we can float it 25 // Wrap the workspace's children in a container so we can float it
26 struct sway_container *workspace = container; 26 container = workspace_wrap_children(workspace);
27 container = workspace_wrap_children(container);
28 workspace->layout = L_HORIZ; 27 workspace->layout = L_HORIZ;
29 seat_set_focus(config->handler_context.seat, container); 28 seat_set_focus(config->handler_context.seat, &container->node);
30 } 29 }
31 30
32 // If the container is in a floating split container, 31 // If the container is in a floating split container,
33 // operate on the split container instead of the child. 32 // operate on the split container instead of the child.
34 if (container_is_floating_or_child(container)) { 33 if (container_is_floating_or_child(container)) {
35 while (container->parent->type != C_WORKSPACE) { 34 while (container->parent) {
36 container = container->parent; 35 container = container->parent;
37 } 36 }
38 } 37 }
@@ -51,8 +50,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
51 50
52 container_set_floating(container, wants_floating); 51 container_set_floating(container, wants_floating);
53 52
54 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 53 arrange_workspace(container->workspace);
55 arrange_windows(workspace);
56 54
57 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 55 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
58} 56}
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index f342e524..e31898af 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -34,211 +34,139 @@ static bool parse_movement_direction(const char *name,
34} 34}
35 35
36/** 36/**
37 * Get swayc in the direction of newly entered output. 37 * Get node in the direction of newly entered output.
38 */ 38 */
39static struct sway_container *get_swayc_in_output_direction( 39static struct sway_node *get_node_in_output_direction(
40 struct sway_container *output, enum movement_direction dir, 40 struct sway_output *output, enum movement_direction dir) {
41 struct sway_seat *seat) { 41 struct sway_seat *seat = config->handler_context.seat;
42 if (!output) { 42 struct sway_workspace *ws = output_get_active_workspace(output);
43 return NULL; 43 if (ws->fullscreen) {
44 } 44 return seat_get_focus_inactive(seat, &ws->fullscreen->node);
45
46 struct sway_container *ws = seat_get_focus_inactive(seat, output);
47 if (ws->type != C_WORKSPACE) {
48 ws = container_parent(ws, C_WORKSPACE);
49 }
50
51 if (ws == NULL) {
52 wlr_log(WLR_ERROR, "got an output without a workspace");
53 return NULL;
54 } 45 }
46 struct sway_container *container = NULL;
55 47
56 if (ws->children->length > 0) { 48 if (ws->tiling->length > 0) {
57 switch (dir) { 49 switch (dir) {
58 case MOVE_LEFT: 50 case MOVE_LEFT:
59 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { 51 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
60 // get most right child of new output 52 // get most right child of new output
61 return ws->children->items[ws->children->length-1]; 53 container = ws->tiling->items[ws->tiling->length-1];
62 } else { 54 } else {
63 return seat_get_focus_inactive(seat, ws); 55 container = seat_get_focus_inactive_tiling(seat, ws);
64 } 56 }
57 return &container->node;
65 case MOVE_RIGHT: 58 case MOVE_RIGHT:
66 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { 59 if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
67 // get most left child of new output 60 // get most left child of new output
68 return ws->children->items[0]; 61 container = ws->tiling->items[0];
69 } else { 62 } else {
70 return seat_get_focus_inactive(seat, ws); 63 container = seat_get_focus_inactive_tiling(seat, ws);
71 } 64 }
65 return &container->node;
72 case MOVE_UP: 66 case MOVE_UP:
67 if (ws->layout == L_VERT || ws->layout == L_STACKED) {
68 // get most bottom child of new output
69 container = ws->tiling->items[ws->tiling->length-1];
70 } else {
71 container = seat_get_focus_inactive_tiling(seat, ws);
72 }
73 return &container->node;
73 case MOVE_DOWN: { 74 case MOVE_DOWN: {
74 struct sway_container *focused = 75 if (ws->layout == L_VERT || ws->layout == L_STACKED) {
75 seat_get_focus_inactive(seat, ws); 76 // get most top child of new output
76 if (focused && focused->parent) { 77 container = ws->tiling->items[0];
77 struct sway_container *parent = focused->parent; 78 } else {
78 if (parent->layout == L_VERT) { 79 container = seat_get_focus_inactive_tiling(seat, ws);
79 if (dir == MOVE_UP) {
80 // get child furthest down on new output
81 int idx = parent->children->length - 1;
82 return parent->children->items[idx];
83 } else if (dir == MOVE_DOWN) {
84 // get child furthest up on new output
85 return parent->children->items[0];
86 }
87 }
88 return focused;
89 } 80 }
90 break; 81 return &container->node;
91 } 82 }
92 default: 83 default:
93 break; 84 break;
94 } 85 }
95 } 86 }
96 87
97 return ws; 88 return &ws->node;
98} 89}
99 90
100static struct sway_container *container_get_in_direction( 91static struct sway_node *node_get_in_direction(struct sway_container *container,
101 struct sway_container *container, struct sway_seat *seat, 92 struct sway_seat *seat, enum movement_direction dir) {
102 enum movement_direction dir) {
103 struct sway_container *parent = container->parent;
104
105 if (dir == MOVE_CHILD) { 93 if (dir == MOVE_CHILD) {
106 return seat_get_focus_inactive(seat, container); 94 return seat_get_active_child(seat, &container->node);
107 } 95 }
108 if (container->is_fullscreen) { 96 if (container->is_fullscreen) {
109 if (dir == MOVE_PARENT) { 97 if (dir == MOVE_PARENT) {
110 return NULL; 98 return NULL;
111 } 99 }
112 container = container_parent(container, C_OUTPUT); 100 // Fullscreen container with a direction - go straight to outputs
113 parent = container->parent; 101 struct sway_output *output = container->workspace->output;
114 } else { 102 struct sway_output *new_output = output_get_in_direction(output, dir);
115 if (dir == MOVE_PARENT) { 103 return get_node_in_output_direction(new_output, dir);
116 if (parent->type == C_OUTPUT || container_is_floating(container)) { 104 }
117 return NULL; 105 if (dir == MOVE_PARENT) {
118 } else { 106 return node_get_parent(&container->node);
119 return parent;
120 }
121 }
122 } 107 }
123 108
124 struct sway_container *wrap_candidate = NULL; 109 struct sway_container *wrap_candidate = NULL;
125 while (true) { 110 struct sway_container *current = container;
111 while (current) {
126 bool can_move = false; 112 bool can_move = false;
127 int desired; 113 int desired;
128 int idx = list_find(container->parent->children, container); 114 int idx = container_sibling_index(current);
129 if (idx == -1) { 115 enum sway_container_layout parent_layout =
130 return NULL; 116 container_parent_layout(current);
131 } 117 list_t *siblings = container_get_siblings(current);
132 if (parent->type == C_ROOT) {
133 enum wlr_direction wlr_dir = 0;
134 if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
135 "got invalid direction: %d", dir)) {
136 return NULL;
137 }
138 int lx = container->x + container->width / 2;
139 int ly = container->y + container->height / 2;
140 struct wlr_output_layout *layout =
141 root_container.sway_root->output_layout;
142 struct wlr_output *wlr_adjacent =
143 wlr_output_layout_adjacent_output(layout, wlr_dir,
144 container->sway_output->wlr_output, lx, ly);
145 struct sway_container *adjacent =
146 output_from_wlr_output(wlr_adjacent);
147 118
148 if (!adjacent || adjacent == container) { 119 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
149 if (!wrap_candidate) { 120 if (parent_layout == L_HORIZ || parent_layout == L_TABBED) {
150 return NULL; 121 can_move = true;
151 } 122 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
152 return seat_get_focus_inactive_view(seat, wrap_candidate);
153 }
154 struct sway_container *next =
155 get_swayc_in_output_direction(adjacent, dir, seat);
156 if (next == NULL) {
157 return NULL;
158 }
159 struct sway_container *next_workspace = next;
160 if (next_workspace->type != C_WORKSPACE) {
161 next_workspace = container_parent(next_workspace, C_WORKSPACE);
162 }
163 sway_assert(next_workspace, "Next container has no workspace");
164 if (next_workspace->sway_workspace->fullscreen) {
165 return seat_get_focus_inactive(seat,
166 next_workspace->sway_workspace->fullscreen);
167 }
168 if (next->children && next->children->length) {
169 // TODO consider floating children as well
170 return seat_get_focus_inactive_view(seat, next);
171 } else {
172 return next;
173 } 123 }
174 } else { 124 } else {
175 if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { 125 if (parent_layout == L_VERT || parent_layout == L_STACKED) {
176 if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { 126 can_move = true;
177 can_move = true; 127 desired = idx + (dir == MOVE_UP ? -1 : 1);
178 desired = idx + (dir == MOVE_LEFT ? -1 : 1);
179 }
180 } else {
181 if (parent->layout == L_VERT || parent->layout == L_STACKED) {
182 can_move = true;
183 desired = idx + (dir == MOVE_UP ? -1 : 1);
184 }
185 } 128 }
186 } 129 }
187 130
188 if (can_move) { 131 if (can_move) {
189 // TODO handle floating 132 if (desired < 0 || desired >= siblings->length) {
190 if (desired < 0 || desired >= parent->children->length) {
191 can_move = false; 133 can_move = false;
192 int len = parent->children->length; 134 int len = siblings->length;
193 if (config->focus_wrapping != WRAP_NO && !wrap_candidate 135 if (config->focus_wrapping != WRAP_NO && !wrap_candidate
194 && len > 1) { 136 && len > 1) {
195 if (desired < 0) { 137 if (desired < 0) {
196 wrap_candidate = parent->children->items[len-1]; 138 wrap_candidate = siblings->items[len-1];
197 } else { 139 } else {
198 wrap_candidate = parent->children->items[0]; 140 wrap_candidate = siblings->items[0];
199 } 141 }
200 if (config->focus_wrapping == WRAP_FORCE) { 142 if (config->focus_wrapping == WRAP_FORCE) {
201 return seat_get_focus_inactive_view(seat, 143 struct sway_container *c = seat_get_focus_inactive_view(
202 wrap_candidate); 144 seat, &wrap_candidate->node);
145 return &c->node;
203 } 146 }
204 } 147 }
205 } else { 148 } else {
206 struct sway_container *desired_con = 149 struct sway_container *desired_con = siblings->items[desired];
207 parent->children->items[desired]; 150 struct sway_container *c = seat_get_focus_inactive_view(
208 wlr_log(WLR_DEBUG, 151 seat, &desired_con->node);
209 "cont %d-%p dir %i sibling %d: %p", idx, 152 return &c->node;
210 container, dir, desired, desired_con);
211 return seat_get_focus_inactive_view(seat, desired_con);
212 } 153 }
213 } 154 }
214 155
215 if (!can_move) { 156 current = current->parent;
216 container = parent;
217 parent = parent->parent;
218 if (!parent) {
219 // wrapping is the last chance
220 if (!wrap_candidate) {
221 return NULL;
222 }
223 return seat_get_focus_inactive_view(seat, wrap_candidate);
224 }
225 }
226 } 157 }
227}
228 158
229static struct cmd_results *focus_mode(struct sway_container *con, 159 // Check a different output
230 struct sway_seat *seat, bool floating) { 160 struct sway_output *output = container->workspace->output;
231 struct sway_container *ws = con->type == C_WORKSPACE ? 161 struct sway_output *new_output = output_get_in_direction(output, dir);
232 con : container_parent(con, C_WORKSPACE); 162 if (new_output) {
233 163 return get_node_in_output_direction(new_output, dir);
234 // If the container is in a floating split container,
235 // operate on the split container instead of the child.
236 if (container_is_floating_or_child(con)) {
237 while (con->parent->type != C_WORKSPACE) {
238 con = con->parent;
239 }
240 } 164 }
165 return NULL;
166}
241 167
168static struct cmd_results *focus_mode(struct sway_workspace *ws,
169 struct sway_seat *seat, bool floating) {
242 struct sway_container *new_focus = NULL; 170 struct sway_container *new_focus = NULL;
243 if (floating) { 171 if (floating) {
244 new_focus = seat_get_focus_inactive_floating(seat, ws); 172 new_focus = seat_get_focus_inactive_floating(seat, ws);
@@ -246,7 +174,7 @@ static struct cmd_results *focus_mode(struct sway_container *con,
246 new_focus = seat_get_focus_inactive_tiling(seat, ws); 174 new_focus = seat_get_focus_inactive_tiling(seat, ws);
247 } 175 }
248 if (new_focus) { 176 if (new_focus) {
249 seat_set_focus(seat, new_focus); 177 seat_set_focus(seat, &new_focus->node);
250 } else { 178 } else {
251 return cmd_results_new(CMD_FAILURE, "focus", 179 return cmd_results_new(CMD_FAILURE, "focus",
252 "Failed to find a %s container in workspace", 180 "Failed to find a %s container in workspace",
@@ -255,14 +183,14 @@ static struct cmd_results *focus_mode(struct sway_container *con,
255 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 183 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
256} 184}
257 185
258static struct cmd_results *focus_output(struct sway_container *con, 186static struct cmd_results *focus_output(struct sway_seat *seat,
259 struct sway_seat *seat, int argc, char **argv) { 187 int argc, char **argv) {
260 if (!argc) { 188 if (!argc) {
261 return cmd_results_new(CMD_INVALID, "focus", 189 return cmd_results_new(CMD_INVALID, "focus",
262 "Expected 'focus output <direction|name>'"); 190 "Expected 'focus output <direction|name>'");
263 } 191 }
264 char *identifier = join_args(argv, argc); 192 char *identifier = join_args(argv, argc);
265 struct sway_container *output = output_by_name(identifier); 193 struct sway_output *output = output_by_name(identifier);
266 194
267 if (!output) { 195 if (!output) {
268 enum movement_direction direction; 196 enum movement_direction direction;
@@ -272,14 +200,13 @@ static struct cmd_results *focus_output(struct sway_container *con,
272 return cmd_results_new(CMD_INVALID, "focus", 200 return cmd_results_new(CMD_INVALID, "focus",
273 "There is no output with that name"); 201 "There is no output with that name");
274 } 202 }
275 struct sway_container *focus = seat_get_focus(seat); 203 struct sway_workspace *ws = seat_get_focused_workspace(seat);
276 focus = container_parent(focus, C_OUTPUT); 204 output = output_get_in_direction(ws->output, direction);
277 output = container_get_in_direction(focus, seat, direction);
278 } 205 }
279 206
280 free(identifier); 207 free(identifier);
281 if (output) { 208 if (output) {
282 seat_set_focus(seat, seat_get_focus_inactive(seat, output)); 209 seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node));
283 } 210 }
284 211
285 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 212 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -289,29 +216,32 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
289 if (config->reading || !config->active) { 216 if (config->reading || !config->active) {
290 return cmd_results_new(CMD_DEFER, NULL, NULL); 217 return cmd_results_new(CMD_DEFER, NULL, NULL);
291 } 218 }
292 struct sway_container *con = config->handler_context.current_container; 219 struct sway_node *node = config->handler_context.node;
220 struct sway_container *container = config->handler_context.container;
221 struct sway_workspace *workspace = config->handler_context.workspace;
293 struct sway_seat *seat = config->handler_context.seat; 222 struct sway_seat *seat = config->handler_context.seat;
294 if (con->type < C_WORKSPACE) { 223 if (node->type < N_WORKSPACE) {
295 return cmd_results_new(CMD_FAILURE, "focus", 224 return cmd_results_new(CMD_FAILURE, "focus",
296 "Command 'focus' cannot be used above the workspace level"); 225 "Command 'focus' cannot be used above the workspace level");
297 } 226 }
298 227
299 if (argc == 0) { 228 if (argc == 0) {
300 seat_set_focus(seat, con); 229 seat_set_focus(seat, node);
301 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 230 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
302 } 231 }
303 232
304 if (strcmp(argv[0], "floating") == 0) { 233 if (strcmp(argv[0], "floating") == 0) {
305 return focus_mode(con, seat, true); 234 return focus_mode(workspace, seat, true);
306 } else if (strcmp(argv[0], "tiling") == 0) { 235 } else if (strcmp(argv[0], "tiling") == 0) {
307 return focus_mode(con, seat, false); 236 return focus_mode(workspace, seat, false);
308 } else if (strcmp(argv[0], "mode_toggle") == 0) { 237 } else if (strcmp(argv[0], "mode_toggle") == 0) {
309 return focus_mode(con, seat, !container_is_floating_or_child(con)); 238 bool floating = container && container_is_floating_or_child(container);
239 return focus_mode(workspace, seat, !floating);
310 } 240 }
311 241
312 if (strcmp(argv[0], "output") == 0) { 242 if (strcmp(argv[0], "output") == 0) {
313 argc--; argv++; 243 argc--; argv++;
314 return focus_output(con, seat, argc, argv); 244 return focus_output(seat, argc, argv);
315 } 245 }
316 246
317 enum movement_direction direction = 0; 247 enum movement_direction direction = 0;
@@ -321,8 +251,18 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
321 "or 'focus output <direction|name>'"); 251 "or 'focus output <direction|name>'");
322 } 252 }
323 253
324 struct sway_container *next_focus = container_get_in_direction( 254 if (node->type == N_WORKSPACE) {
325 con, seat, direction); 255 // A workspace is focused, so just jump to the next output
256 struct sway_output *new_output =
257 output_get_in_direction(workspace->output, direction);
258 struct sway_node *node =
259 get_node_in_output_direction(new_output, direction);
260 seat_set_focus(seat, node);
261 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
262 }
263
264 struct sway_node *next_focus =
265 node_get_in_direction(container, seat, direction);
326 if (next_focus) { 266 if (next_focus) {
327 seat_set_focus(seat, next_focus); 267 seat_set_focus(seat, next_focus);
328 } 268 }
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index ac65dffb..3bbe00c5 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -12,18 +12,18 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
12 if ((error = checkarg(argc, "fullscreen", EXPECTED_LESS_THAN, 2))) { 12 if ((error = checkarg(argc, "fullscreen", EXPECTED_LESS_THAN, 2))) {
13 return error; 13 return error;
14 } 14 }
15 struct sway_container *container = 15 struct sway_node *node = config->handler_context.node;
16 config->handler_context.current_container; 16 struct sway_container *container = config->handler_context.container;
17 if (container->type == C_WORKSPACE && container->children->length == 0) { 17 struct sway_workspace *workspace = config->handler_context.workspace;
18 if (node->type == N_WORKSPACE && workspace->tiling->length == 0) {
18 return cmd_results_new(CMD_INVALID, "fullscreen", 19 return cmd_results_new(CMD_INVALID, "fullscreen",
19 "Can't fullscreen an empty workspace"); 20 "Can't fullscreen an empty workspace");
20 } 21 }
21 if (container->type == C_WORKSPACE) { 22 if (node->type == N_WORKSPACE) {
22 // Wrap the workspace's children in a container so we can fullscreen it 23 // Wrap the workspace's children in a container so we can fullscreen it
23 struct sway_container *workspace = container; 24 container = workspace_wrap_children(workspace);
24 container = workspace_wrap_children(container);
25 workspace->layout = L_HORIZ; 25 workspace->layout = L_HORIZ;
26 seat_set_focus(config->handler_context.seat, container); 26 seat_set_focus(config->handler_context.seat, &container->node);
27 } 27 }
28 bool enable = !container->is_fullscreen; 28 bool enable = !container->is_fullscreen;
29 29
@@ -32,9 +32,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
32 } 32 }
33 33
34 container_set_fullscreen(container, enable); 34 container_set_fullscreen(container, enable);
35 35 arrange_workspace(workspace);
36 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
37 arrange_windows(workspace->parent);
38 36
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 37 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40} 38}
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 3906eb70..d676e475 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -2,6 +2,7 @@
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/arrange.h"
5#include "sway/tree/workspace.h"
5#include "log.h" 6#include "log.h"
6#include "stringop.h" 7#include "stringop.h"
7#include <math.h> 8#include <math.h>
@@ -43,7 +44,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
43 return cmd_results_new(CMD_INVALID, "gaps", 44 return cmd_results_new(CMD_INVALID, "gaps",
44 "gaps edge_gaps on|off|toggle"); 45 "gaps edge_gaps on|off|toggle");
45 } 46 }
46 arrange_windows(&root_container); 47 arrange_root();
47 } else { 48 } else {
48 int amount_idx = 0; // the current index in argv 49 int amount_idx = 0; // the current index in argv
49 enum gaps_op op = GAPS_OP_SET; 50 enum gaps_op op = GAPS_OP_SET;
@@ -124,7 +125,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
124 if (amount_idx == 0) { // gaps <amount> 125 if (amount_idx == 0) { // gaps <amount>
125 config->gaps_inner = val; 126 config->gaps_inner = val;
126 config->gaps_outer = val; 127 config->gaps_outer = val;
127 arrange_windows(&root_container); 128 arrange_root();
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 129 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 } 130 }
130 // Other variants. The middle-length variant (gaps inner|outer <amount>) 131 // Other variants. The middle-length variant (gaps inner|outer <amount>)
@@ -155,21 +156,27 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
155 } else { 156 } else {
156 config->gaps_outer = total; 157 config->gaps_outer = total;
157 } 158 }
158 arrange_windows(&root_container); 159 arrange_root();
159 } else { 160 } else {
160 struct sway_container *c = 161 if (scope == GAPS_SCOPE_WORKSPACE) {
161 config->handler_context.current_container; 162 struct sway_workspace *ws = config->handler_context.workspace;
162 if (scope == GAPS_SCOPE_WORKSPACE && c->type != C_WORKSPACE) { 163 ws->has_gaps = true;
163 c = container_parent(c, C_WORKSPACE); 164 if (inner) {
164 } 165 ws->gaps_inner = total;
165 c->has_gaps = true; 166 } else {
166 if (inner) { 167 ws->gaps_outer = total;
167 c->gaps_inner = total; 168 }
169 arrange_workspace(ws);
168 } else { 170 } else {
169 c->gaps_outer = total; 171 struct sway_container *c = config->handler_context.container;
172 c->has_gaps = true;
173 if (inner) {
174 c->gaps_inner = total;
175 } else {
176 c->gaps_outer = total;
177 }
178 arrange_workspace(c->workspace);
170 } 179 }
171
172 arrange_windows(c->parent ? c->parent : &root_container);
173 } 180 }
174 } 181 }
175 182
diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c
index e494f6aa..0a5c7f28 100644
--- a/sway/commands/hide_edge_borders.c
+++ b/sway/commands/hide_edge_borders.c
@@ -5,8 +5,8 @@
5#include "sway/tree/view.h" 5#include "sway/tree/view.h"
6 6
7static void _configure_view(struct sway_container *con, void *data) { 7static void _configure_view(struct sway_container *con, void *data) {
8 if (con->type == C_VIEW) { 8 if (con->view) {
9 view_autoconfigure(con->sway_view); 9 view_autoconfigure(con->view);
10 } 10 }
11} 11}
12 12
diff --git a/sway/commands/kill.c b/sway/commands/kill.c
index f3fa52f1..85ca0f33 100644
--- a/sway/commands/kill.c
+++ b/sway/commands/kill.c
@@ -2,15 +2,27 @@
2#include "log.h" 2#include "log.h"
3#include "sway/input/input-manager.h" 3#include "sway/input/input-manager.h"
4#include "sway/input/seat.h" 4#include "sway/input/seat.h"
5#include "sway/tree/view.h"
6#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "sway/tree/view.h"
7#include "sway/tree/workspace.h"
7#include "sway/commands.h" 8#include "sway/commands.h"
8 9
10static void close_container_iterator(struct sway_container *con, void *data) {
11 if (con->view) {
12 view_close(con->view);
13 }
14}
15
9struct cmd_results *cmd_kill(int argc, char **argv) { 16struct cmd_results *cmd_kill(int argc, char **argv) {
10 struct sway_container *con = 17 struct sway_container *con = config->handler_context.container;
11 config->handler_context.current_container; 18 struct sway_workspace *ws = config->handler_context.workspace;
12 19
13 container_close(con); 20 if (con) {
21 close_container_iterator(con, NULL);
22 container_for_each_child(con, close_container_iterator, NULL);
23 } else {
24 workspace_for_each_container(ws, close_container_iterator, NULL);
25 }
14 26
15 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 27 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
16} 28}
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index a06832de..8fa1ce98 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -4,21 +4,20 @@
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/tree/arrange.h" 5#include "sway/tree/arrange.h"
6#include "sway/tree/container.h" 6#include "sway/tree/container.h"
7#include "sway/tree/workspace.h"
7#include "log.h" 8#include "log.h"
8 9
9static bool parse_layout_string(char *s, enum sway_container_layout *ptr) { 10static enum sway_container_layout parse_layout_string(char *s) {
10 if (strcasecmp(s, "splith") == 0) { 11 if (strcasecmp(s, "splith") == 0) {
11 *ptr = L_HORIZ; 12 return L_HORIZ;
12 } else if (strcasecmp(s, "splitv") == 0) { 13 } else if (strcasecmp(s, "splitv") == 0) {
13 *ptr = L_VERT; 14 return L_VERT;
14 } else if (strcasecmp(s, "tabbed") == 0) { 15 } else if (strcasecmp(s, "tabbed") == 0) {
15 *ptr = L_TABBED; 16 return L_TABBED;
16 } else if (strcasecmp(s, "stacking") == 0) { 17 } else if (strcasecmp(s, "stacking") == 0) {
17 *ptr = L_STACKED; 18 return L_STACKED;
18 } else {
19 return false;
20 } 19 }
21 return true; 20 return L_NONE;
22} 21}
23 22
24static const char* expected_syntax = 23static const char* expected_syntax =
@@ -26,84 +25,129 @@ static const char* expected_syntax =
26 "'layout toggle [split|all]' or " 25 "'layout toggle [split|all]' or "
27 "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; 26 "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'";
28 27
28static enum sway_container_layout get_layout_toggle(int argc, char **argv,
29 enum sway_container_layout layout,
30 enum sway_container_layout prev_split_layout) {
31 // "layout toggle"
32 if (argc == 0) {
33 return layout == L_HORIZ ? L_VERT : L_HORIZ;
34 }
35
36 if (argc == 2) {
37 // "layout toggle split" (same as "layout toggle")
38 if (strcasecmp(argv[1], "split") == 0) {
39 return layout == L_HORIZ ? L_VERT : L_HORIZ;
40 }
41 // "layout toggle all"
42 if (strcasecmp(argv[1], "all") == 0) {
43 return layout == L_HORIZ ? L_VERT :
44 layout == L_VERT ? L_STACKED :
45 layout == L_STACKED ? L_TABBED : L_HORIZ;
46 }
47 return L_NONE;
48 }
49
50 enum sway_container_layout parsed;
51 int curr = 1;
52 for (; curr < argc; curr++) {
53 parsed = parse_layout_string(argv[curr]);
54 if (parsed == layout || (strcmp(argv[curr], "split") == 0 &&
55 (layout == L_VERT || layout == L_HORIZ))) {
56 break;
57 }
58 }
59 for (int i = curr + 1; i != curr; ++i) {
60 // cycle round to find next valid layout
61 if (i >= argc) {
62 i = 1;
63 }
64 parsed = parse_layout_string(argv[i]);
65 if (parsed != L_NONE) {
66 return parsed;
67 }
68 if (strcmp(argv[i], "split") == 0) {
69 return layout == L_HORIZ ? L_VERT :
70 layout == L_VERT ? L_HORIZ : prev_split_layout;
71 }
72 // invalid layout strings are silently ignored
73 }
74 return L_NONE;
75}
76
77static enum sway_container_layout get_layout(int argc, char **argv,
78 enum sway_container_layout layout,
79 enum sway_container_layout prev_split_layout) {
80 // Check if assigned directly
81 enum sway_container_layout parsed = parse_layout_string(argv[0]);
82 if (parsed != L_NONE) {
83 return parsed;
84 }
85
86 if (strcasecmp(argv[0], "default") == 0) {
87 return prev_split_layout;
88 }
89
90 if (strcasecmp(argv[0], "toggle") == 0) {
91 argc--; argv++;
92 return get_layout_toggle(argc, argv, layout, prev_split_layout);
93 }
94
95 return L_NONE;
96}
97
29struct cmd_results *cmd_layout(int argc, char **argv) { 98struct cmd_results *cmd_layout(int argc, char **argv) {
30 struct cmd_results *error = NULL; 99 struct cmd_results *error = NULL;
31 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { 100 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
32 return error; 101 return error;
33 } 102 }
34 struct sway_container *parent = config->handler_context.current_container; 103 struct sway_container *container = config->handler_context.container;
104 struct sway_workspace *workspace = config->handler_context.workspace;
35 105
36 if (container_is_floating(parent)) { 106 if (container && container_is_floating(container)) {
37 return cmd_results_new(CMD_FAILURE, "layout", 107 return cmd_results_new(CMD_FAILURE, "layout",
38 "Unable to change layout of floating windows"); 108 "Unable to change layout of floating windows");
39 } 109 }
40 110
41 while (parent->type == C_VIEW) { 111 // Typically we change the layout of the current container, but if the
42 parent = parent->parent; 112 // current container is a view (it usually is) then we'll change the layout
113 // of the parent instead, as it doesn't make sense for views to have layout.
114 if (container && container->view) {
115 container = container->parent;
43 } 116 }
44 117
45 enum sway_container_layout prev = parent->layout; 118 // We could be working with a container OR a workspace. These are different
46 bool assigned_directly = parse_layout_string(argv[0], &parent->layout); 119 // structures, so we set up pointers to they layouts so we can refer them in
47 if (!assigned_directly) { 120 // an abstract way.
48 if (strcasecmp(argv[0], "default") == 0) { 121 enum sway_container_layout new_layout = L_NONE;
49 parent->layout = parent->prev_split_layout; 122 enum sway_container_layout old_layout = L_NONE;
50 } else if (strcasecmp(argv[0], "toggle") == 0) { 123 if (container) {
51 if (argc == 1) { 124 old_layout = container->layout;
52 parent->layout = 125 new_layout = get_layout(argc, argv,
53 parent->layout == L_STACKED ? L_TABBED : 126 container->layout, container->prev_split_layout);
54 parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED; 127 } else {
55 } else if (argc == 2) { 128 old_layout = workspace->layout;
56 if (strcasecmp(argv[1], "all") == 0) { 129 new_layout = get_layout(argc, argv,
57 parent->layout = 130 workspace->layout, workspace->prev_split_layout);
58 parent->layout == L_HORIZ ? L_VERT :
59 parent->layout == L_VERT ? L_STACKED :
60 parent->layout == L_STACKED ? L_TABBED : L_HORIZ;
61 } else if (strcasecmp(argv[1], "split") == 0) {
62 parent->layout =
63 parent->layout == L_HORIZ ? L_VERT :
64 parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
65 } else {
66 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
67 }
68 } else {
69 enum sway_container_layout parsed_layout;
70 int curr = 1;
71 for (; curr < argc; curr++) {
72 bool valid = parse_layout_string(argv[curr], &parsed_layout);
73 if ((valid && parsed_layout == parent->layout) ||
74 (strcmp(argv[curr], "split") == 0 &&
75 (parent->layout == L_VERT || parent->layout == L_HORIZ))) {
76 break;
77 }
78 }
79 for (int i = curr + 1; i != curr; ++i) {
80 // cycle round to find next valid layout
81 if (i >= argc) {
82 i = 1;
83 }
84 if (parse_layout_string(argv[i], &parent->layout)) {
85 break;
86 } else if (strcmp(argv[i], "split") == 0) {
87 parent->layout =
88 parent->layout == L_HORIZ ? L_VERT :
89 parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
90 break;
91 } // invalid layout strings are silently ignored
92 }
93 }
94 } else {
95 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
96 }
97 } 131 }
98 if (parent->layout == L_NONE) { 132 if (new_layout == L_NONE) {
99 parent->layout = container_get_default_layout(parent); 133 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
100 } 134 }
101 if (prev != parent->layout) { 135 if (new_layout != old_layout) {
102 if (prev != L_TABBED && prev != L_STACKED) { 136 if (container) {
103 parent->prev_split_layout = prev; 137 if (old_layout != L_TABBED && old_layout != L_STACKED) {
138 container->prev_split_layout = old_layout;
139 }
140 container->layout = new_layout;
141 container_update_representation(container);
142 arrange_container(container);
143 } else {
144 if (old_layout != L_TABBED && old_layout != L_STACKED) {
145 workspace->prev_split_layout = old_layout;
146 }
147 workspace->layout = new_layout;
148 workspace_update_representation(workspace);
149 arrange_workspace(workspace);
104 } 150 }
105 container_notify_subtree_changed(parent);
106 arrange_windows(parent->parent);
107 } 151 }
108 152
109 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 153 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index 9ea8c301..fb95a7d0 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -18,13 +18,12 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
18 if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) { 18 if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
19 return error; 19 return error;
20 } 20 }
21 struct sway_container *container = 21 struct sway_container *container = config->handler_context.container;
22 config->handler_context.current_container; 22 if (!container->view) {
23 if (container->type != C_VIEW) {
24 return cmd_results_new(CMD_INVALID, "mark", 23 return cmd_results_new(CMD_INVALID, "mark",
25 "Only views can have marks"); 24 "Only views can have marks");
26 } 25 }
27 struct sway_view *view = container->sway_view; 26 struct sway_view *view = container->view;
28 27
29 bool add = false, toggle = false; 28 bool add = false, toggle = false;
30 while (argc > 0 && strncmp(*argv, "--", 2) == 0) { 29 while (argc > 0 && strncmp(*argv, "--", 2) == 0) {
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 4426f24e..1b2e830c 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -40,7 +40,7 @@ enum wlr_direction opposite_direction(enum wlr_direction d) {
40 } 40 }
41} 41}
42 42
43static struct sway_container *output_in_direction(const char *direction_string, 43static struct sway_output *output_in_direction(const char *direction_string,
44 struct wlr_output *reference, int ref_lx, int ref_ly) { 44 struct wlr_output *reference, int ref_lx, int ref_ly) {
45 struct { 45 struct {
46 char *name; 46 char *name;
@@ -63,447 +63,367 @@ static struct sway_container *output_in_direction(const char *direction_string,
63 63
64 if (direction) { 64 if (direction) {
65 struct wlr_output *target = wlr_output_layout_adjacent_output( 65 struct wlr_output *target = wlr_output_layout_adjacent_output(
66 root_container.sway_root->output_layout, 66 root->output_layout, direction, reference, ref_lx, ref_ly);
67 direction, reference, ref_lx, ref_ly);
68 67
69 if (!target) { 68 if (!target) {
70 target = wlr_output_layout_farthest_output( 69 target = wlr_output_layout_farthest_output(
71 root_container.sway_root->output_layout, 70 root->output_layout, opposite_direction(direction),
72 opposite_direction(direction), reference, ref_lx, ref_ly); 71 reference, ref_lx, ref_ly);
73 } 72 }
74 73
75 if (target) { 74 if (target) {
76 struct sway_output *sway_output = target->data; 75 return target->data;
77 return sway_output->swayc;
78 } 76 }
79 } 77 }
80 78
81 return output_by_name(direction_string); 79 return output_by_name(direction_string);
82} 80}
83 81
84static void container_move_to(struct sway_container *container, 82static bool is_parallel(enum sway_container_layout layout,
85 struct sway_container *destination) { 83 enum movement_direction dir) {
86 if (!sway_assert(container->type == C_CONTAINER || 84 switch (layout) {
87 container->type == C_VIEW, "Expected a container or view")) { 85 case L_TABBED:
88 return; 86 case L_HORIZ:
87 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
88 case L_STACKED:
89 case L_VERT:
90 return dir == MOVE_UP || dir == MOVE_DOWN;
91 default:
92 return false;
89 } 93 }
90 if (container == destination 94}
91 || container_has_ancestor(container, destination)) { 95
96/**
97 * Ensures all seats focus the fullscreen container if needed.
98 */
99static void workspace_focus_fullscreen(struct sway_workspace *workspace) {
100 if (!workspace->fullscreen) {
92 return; 101 return;
93 } 102 }
94 struct sway_container *old_parent = NULL; 103 struct sway_seat *seat;
95 struct sway_container *new_parent = NULL; 104 struct sway_workspace *focus_ws;
96 if (container_is_floating(container)) { 105 wl_list_for_each(seat, &input_manager->seats, link) {
97 // Resolve destination into a workspace 106 focus_ws = seat_get_focused_workspace(seat);
98 struct sway_container *new_ws = NULL; 107 if (focus_ws == workspace) {
99 if (destination->type == C_OUTPUT) { 108 struct sway_node *new_focus =
100 new_ws = output_get_active_workspace(destination->sway_output); 109 seat_get_focus_inactive(seat, &workspace->fullscreen->node);
101 } else if (destination->type == C_WORKSPACE) { 110 seat_set_focus(seat, new_focus);
102 new_ws = destination;
103 } else {
104 new_ws = container_parent(destination, C_WORKSPACE);
105 } 111 }
106 if (!new_ws) { 112 }
107 // This can happen if the user has run "move container to mark foo", 113}
108 // where mark foo is on a hidden scratchpad container. 114
109 return; 115static void container_move_to_container_from_direction(
116 struct sway_container *container, struct sway_container *destination,
117 enum movement_direction move_dir) {
118 if (destination->view) {
119 if (destination->parent == container->parent) {
120 wlr_log(WLR_DEBUG, "Swapping siblings");
121 list_t *siblings = container_get_siblings(container);
122 int container_index = list_find(siblings, container);
123 int destination_index = list_find(siblings, destination);
124 list_swap(siblings, container_index, destination_index);
125 } else {
126 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
127 int offset = move_dir == MOVE_LEFT || move_dir == MOVE_UP;
128 int index = container_sibling_index(destination) + offset;
129 if (destination->parent) {
130 container_insert_child(destination->parent, container, index);
131 } else {
132 workspace_insert_tiling(destination->workspace,
133 container, index);
134 }
135 container->width = container->height = 0;
110 } 136 }
111 struct sway_container *old_output = 137 return;
112 container_parent(container, C_OUTPUT); 138 }
113 old_parent = container_remove_child(container); 139
114 workspace_add_floating(new_ws, container); 140 if (is_parallel(destination->layout, move_dir)) {
115 container_handle_fullscreen_reparent(container, old_parent); 141 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
142 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ?
143 0 : destination->children->length;
144 container_insert_child(destination, container, index);
145 container->width = container->height = 0;
146 return;
147 }
148
149 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
150 struct sway_node *focus_inactive = seat_get_active_child(
151 config->handler_context.seat, &destination->node);
152 if (!focus_inactive || focus_inactive == &destination->node) {
153 // The container has no children
154 container_add_child(destination, container);
155 return;
156 }
157
158 // Try again but with the child
159 container_move_to_container_from_direction(container,
160 focus_inactive->sway_container, move_dir);
161}
162
163static void container_move_to_workspace_from_direction(
164 struct sway_container *container, struct sway_workspace *workspace,
165 enum movement_direction move_dir) {
166 if (is_parallel(workspace->layout, move_dir)) {
167 wlr_log(WLR_DEBUG, "Reparenting container (parallel)");
168 int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ?
169 0 : workspace->tiling->length;
170 workspace_insert_tiling(workspace, container, index);
171 return;
172 }
173
174 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
175 struct sway_container *focus_inactive = seat_get_focus_inactive_tiling(
176 config->handler_context.seat, workspace);
177 if (!focus_inactive) {
178 // The workspace has no tiling children
179 workspace_add_tiling(workspace, container);
180 return;
181 }
182 while (focus_inactive->parent) {
183 focus_inactive = focus_inactive->parent;
184 }
185 container_move_to_container_from_direction(container, focus_inactive,
186 move_dir);
187}
188
189static void container_move_to_workspace(struct sway_container *container,
190 struct sway_workspace *workspace) {
191 if (container->workspace == workspace) {
192 return;
193 }
194 struct sway_workspace *old_workspace = container->workspace;
195 if (container_is_floating(container)) {
196 struct sway_output *old_output = container->workspace->output;
197 container_detach(container);
198 workspace_add_floating(workspace, container);
199 container_handle_fullscreen_reparent(container);
116 // If changing output, center it within the workspace 200 // If changing output, center it within the workspace
117 if (old_output != new_ws->parent && !container->is_fullscreen) { 201 if (old_output != workspace->output && !container->is_fullscreen) {
118 container_floating_move_to_center(container); 202 container_floating_move_to_center(container);
119 } 203 }
120 } else { 204 } else {
121 old_parent = container_remove_child(container); 205 container_detach(container);
122 container->width = container->height = 0; 206 container->width = container->height = 0;
123 container->saved_width = container->saved_height = 0; 207 container->saved_width = container->saved_height = 0;
124 208 workspace_add_tiling(workspace, container);
125 if (destination->type == C_VIEW) { 209 container_update_representation(container);
126 new_parent = container_add_sibling(destination, container);
127 } else {
128 new_parent = destination;
129 container_add_child(destination, container);
130 }
131 } 210 }
132 211 if (container->view) {
133 if (container->type == C_VIEW) {
134 ipc_event_window(container, "move"); 212 ipc_event_window(container, "move");
135 } 213 }
136 container_notify_subtree_changed(old_parent); 214 workspace_detect_urgent(old_workspace);
137 container_notify_subtree_changed(new_parent); 215 workspace_detect_urgent(workspace);
138 216 workspace_focus_fullscreen(workspace);
139 // If view was moved to a fullscreen workspace, refocus the fullscreen view 217}
140 struct sway_container *new_workspace = container; 218
141 if (new_workspace->type != C_WORKSPACE) { 219static void container_move_to_container(struct sway_container *container,
142 new_workspace = container_parent(new_workspace, C_WORKSPACE); 220 struct sway_container *destination) {
143 } 221 if (container == destination
144 if (new_workspace->sway_workspace->fullscreen) { 222 || container_has_ancestor(container, destination)
145 struct sway_seat *seat; 223 || container_has_ancestor(destination, container)) {
146 struct sway_container *focus, *focus_ws; 224 return;
147 wl_list_for_each(seat, &input_manager->seats, link) {
148 focus = seat_get_focus(seat);
149 focus_ws = focus;
150 if (focus_ws->type != C_WORKSPACE) {
151 focus_ws = container_parent(focus_ws, C_WORKSPACE);
152 }
153 if (focus_ws == new_workspace) {
154 struct sway_container *new_focus = seat_get_focus_inactive(seat,
155 new_workspace->sway_workspace->fullscreen);
156 seat_set_focus(seat, new_focus);
157 }
158 }
159 } 225 }
160 // Update workspace urgent state 226 if (container_is_floating(container)) {
161 struct sway_container *old_workspace = old_parent; 227 return;
162 if (old_workspace->type != C_WORKSPACE) {
163 old_workspace = container_parent(old_workspace, C_WORKSPACE);
164 }
165 if (new_workspace != old_workspace) {
166 workspace_detect_urgent(new_workspace);
167 if (old_workspace) {
168 workspace_detect_urgent(old_workspace);
169 }
170 } 228 }
171} 229 struct sway_workspace *old_workspace = container->workspace;
172 230
173static bool is_parallel(enum sway_container_layout layout, 231 container_detach(container);
174 enum movement_direction dir) { 232 container->width = container->height = 0;
175 switch (layout) { 233 container->saved_width = container->saved_height = 0;
176 case L_TABBED: 234
177 case L_HORIZ: 235 if (destination->view) {
178 return dir == MOVE_LEFT || dir == MOVE_RIGHT; 236 container_add_sibling(destination, container, 1);
179 case L_STACKED: 237 } else {
180 case L_VERT: 238 container_add_child(destination, container);
181 return dir == MOVE_UP || dir == MOVE_DOWN;
182 default:
183 return false;
184 } 239 }
185}
186 240
187static enum movement_direction invert_movement(enum movement_direction dir) { 241 if (container->view) {
188 switch (dir) { 242 ipc_event_window(container, "move");
189 case MOVE_LEFT:
190 return MOVE_RIGHT;
191 case MOVE_RIGHT:
192 return MOVE_LEFT;
193 case MOVE_UP:
194 return MOVE_DOWN;
195 case MOVE_DOWN:
196 return MOVE_UP;
197 default:
198 sway_assert(0, "This function expects left|right|up|down");
199 return MOVE_LEFT;
200 } 243 }
201}
202 244
203static int move_offs(enum movement_direction move_dir) { 245 workspace_focus_fullscreen(destination->workspace);
204 return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
205}
206 246
207/* Gets the index of the most extreme member based on the movement offset */ 247 // Update workspace urgent state
208static int container_limit(struct sway_container *container, 248 workspace_detect_urgent(destination->workspace);
209 enum movement_direction move_dir) { 249 if (old_workspace != destination->workspace) {
210 return move_offs(move_dir) < 0 ? 0 : container->children->length; 250 workspace_detect_urgent(old_workspace);
251 }
211} 252}
212 253
213/* Takes one child, sets it aside, wraps the rest of the children in a new 254/* Takes one child, sets it aside, wraps the rest of the children in a new
214 * container, switches the layout of the workspace, and drops the child back in. 255 * container, switches the layout of the workspace, and drops the child back in.
215 * In other words, rejigger it. */ 256 * In other words, rejigger it. */
216static void workspace_rejigger(struct sway_container *ws, 257static void workspace_rejigger(struct sway_workspace *ws,
217 struct sway_container *child, enum movement_direction move_dir) { 258 struct sway_container *child, enum movement_direction move_dir) {
218 struct sway_container *original_parent = child->parent; 259 if (!sway_assert(child->parent == NULL, "Expected a root child")) {
219 struct sway_container *new_parent = 260 return;
220 container_split(ws, ws->layout);
221
222 container_remove_child(child);
223 for (int i = 0; i < ws->children->length; ++i) {
224 struct sway_container *_child = ws->children->items[i];
225 container_move_to(new_parent, _child);
226 } 261 }
262 container_detach(child);
263 workspace_wrap_children(ws);
227 264
228 int index = move_offs(move_dir); 265 int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1;
229 container_insert_child(ws, child, index < 0 ? 0 : 1); 266 workspace_insert_tiling(ws, child, index);
230 ws->layout = 267 ws->layout =
231 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 268 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
232 269 workspace_update_representation(ws);
233 container_flatten(ws);
234 container_reap_empty(original_parent);
235 container_create_notify(new_parent);
236} 270}
237 271
238static void move_out_of_tabs_stacks(struct sway_container *container, 272static void move_out_of_tabs_stacks(struct sway_container *container,
239 struct sway_container *current, enum movement_direction move_dir, 273 struct sway_container *current, enum movement_direction move_dir,
240 int offs) { 274 int offs) {
241 if (container->parent == current->parent 275 enum sway_container_layout layout = move_dir ==
242 && current->parent->children->length == 1) { 276 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT;
243 wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); 277 list_t *siblings = container_get_siblings(container);
244 current->parent->layout = move_dir == 278 if (container == current && siblings->length == 1) {
245 MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; 279 wlr_log(WLR_DEBUG, "Changing layout of parent");
280 if (container->parent) {
281 container->parent->layout = layout;
282 container_update_representation(container);
283 } else {
284 container->workspace->layout = layout;
285 workspace_update_representation(container->workspace);
286 }
246 return; 287 return;
247 } 288 }
248 289
249 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); 290 wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split");
250 bool is_workspace = current->parent->type == C_WORKSPACE; 291 if (container->parent) {
251 struct sway_container *new_parent = container_split(current->parent, 292 struct sway_container *new_parent =
252 move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); 293 container_split(current->parent, layout);
253 if (is_workspace) {
254 container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1);
255 } else {
256 container_insert_child(new_parent, container, offs < 0 ? 0 : 1); 294 container_insert_child(new_parent, container, offs < 0 ? 0 : 1);
257 container_reap_empty(new_parent->parent); 295 container_reap_empty(new_parent);
258 container_flatten(new_parent->parent); 296 container_flatten(new_parent);
297 } else {
298 // Changing a workspace
299 struct sway_workspace *workspace = container->workspace;
300 workspace_split(workspace, layout);
301 workspace_insert_tiling(workspace, container, offs < 0 ? 0 : 1);
259 } 302 }
260 container_create_notify(new_parent);
261 container_notify_subtree_changed(new_parent);
262} 303}
263 304
264static void container_move(struct sway_container *container, 305// Returns true if moved
265 enum movement_direction move_dir, int move_amt) { 306static bool container_move_in_direction(struct sway_container *container,
266 if (!sway_assert( 307 enum movement_direction move_dir) {
267 container->type != C_CONTAINER || container->type != C_VIEW,
268 "Can only move containers and views")) {
269 return;
270 }
271 int offs = move_offs(move_dir);
272
273 struct sway_container *sibling = NULL;
274 struct sway_container *current = container;
275 struct sway_container *parent = current->parent;
276 struct sway_container *top = &root_container;
277
278 // If moving a fullscreen view, only consider outputs 308 // If moving a fullscreen view, only consider outputs
279 if (container->is_fullscreen) { 309 if (container->is_fullscreen) {
280 current = container_parent(container, C_OUTPUT); 310 struct sway_output *new_output =
281 } else if (container_is_fullscreen_or_child(container) || 311 output_get_in_direction(container->workspace->output, move_dir);
282 container_is_floating_or_child(container)) { 312 if (!new_output) {
283 // If we've fullscreened a split container, only allow the child to move 313 return false;
284 // around within the fullscreen parent. 314 }
285 // Same with floating a split container. 315 struct sway_workspace *ws = output_get_active_workspace(new_output);
286 struct sway_container *ws = container_parent(container, C_WORKSPACE); 316 container_move_to_workspace(container, ws);
287 top = ws->sway_workspace->fullscreen; 317 return true;
288 }
289
290 struct sway_container *new_parent = container_flatten(parent);
291 if (new_parent != parent) {
292 // Special case: we were the last one in this container, so leave
293 return;
294 } 318 }
295 319
296 while (!sibling) { 320 // If container is in a split container by itself, move out of the split
297 if (current == top) { 321 if (container->parent) {
298 return; 322 struct sway_container *new_parent =
323 container_flatten(container->parent);
324 if (new_parent != container->parent) {
325 return true;
299 } 326 }
327 }
300 328
301 parent = current->parent; 329 // Look for a suitable *container* sibling or parent.
302 wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, 330 // The below loop stops once we hit the workspace because current->parent
303 container_type_to_str(current->type), current->name); 331 // is NULL for the topmost containers in a workspace.
304 332 struct sway_container *current = container;
305 int index = container_sibling_index(current); 333 int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1;
306 334
307 switch (current->type) { 335 while (current) {
308 case C_OUTPUT: { 336 struct sway_container *parent = current->parent;
309 enum wlr_direction wlr_dir = 0; 337 list_t *siblings = container_get_siblings(current);
310 if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir), 338 enum sway_container_layout layout = container_parent_layout(current);
311 "got invalid direction: %d", move_dir)) { 339 int index = list_find(siblings, current);
312 return; 340 int desired = index + offs;
313 } 341
314 double ref_lx = current->x + current->width / 2; 342 if (is_parallel(layout, move_dir)) {
315 double ref_ly = current->y + current->height / 2; 343 if (desired == -1 || desired == siblings->length) {
316 struct wlr_output *next = wlr_output_layout_adjacent_output( 344 if (current->parent == container->parent) {
317 root_container.sway_root->output_layout, wlr_dir, 345 if (!(parent && parent->is_fullscreen) &&
318 current->sway_output->wlr_output, ref_lx, ref_ly); 346 (layout == L_TABBED || layout == L_STACKED)) {
319 if (!next) { 347 move_out_of_tabs_stacks(container, current,
320 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); 348 move_dir, offs);
321 return; 349 return true;
322 }
323 struct sway_output *next_output = next->data;
324 current = next_output->swayc;
325 wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name);
326 // Select workspace and get outta here
327 current = seat_get_focus_inactive(
328 config->handler_context.seat, current);
329 if (current->type != C_WORKSPACE) {
330 current = container_parent(current, C_WORKSPACE);
331 }
332 sibling = current;
333 break;
334 }
335 case C_WORKSPACE:
336 if (!is_parallel(current->layout, move_dir)) {
337 if (current->children->length >= 2) {
338 wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)",
339 current->children->length);
340 workspace_rejigger(current, container, move_dir);
341 return;
342 } else {
343 wlr_log(WLR_DEBUG, "Selecting output");
344 current = current->parent;
345 }
346 } else if (current->layout == L_TABBED
347 || current->layout == L_STACKED) {
348 wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks");
349 workspace_rejigger(current, container, move_dir);
350 } else {
351 wlr_log(WLR_DEBUG, "Selecting output");
352 current = current->parent;
353 }
354 break;
355 case C_CONTAINER:
356 case C_VIEW:
357 if (is_parallel(parent->layout, move_dir)) {
358 if ((index == parent->children->length - 1 && offs > 0)
359 || (index == 0 && offs < 0)) {
360 if (current->parent == container->parent) {
361 if (!parent->is_fullscreen &&
362 (parent->layout == L_TABBED ||
363 parent->layout == L_STACKED)) {
364 move_out_of_tabs_stacks(container, current,
365 move_dir, offs);
366 return;
367 } else {
368 wlr_log(WLR_DEBUG, "Hit limit, selecting parent");
369 current = current->parent;
370 }
371 } else { 350 } else {
372 wlr_log(WLR_DEBUG, "Hit limit, " 351 current = current->parent;
373 "promoting descendant to sibling"); 352 continue;
374 // Special case 353 }
354 } else {
355 // Special case
356 if (current->parent) {
375 container_insert_child(current->parent, container, 357 container_insert_child(current->parent, container,
376 index + (offs < 0 ? 0 : 1)); 358 index + (offs < 0 ? 0 : 1));
377 container->width = container->height = 0; 359 } else {
378 return; 360 workspace_insert_tiling(current->workspace, container,
361 index + (offs < 0 ? 0 : 1));
379 } 362 }
380 } else { 363 return true;
381 sibling = parent->children->items[index + offs];
382 wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id);
383 } 364 }
384 } else if (!parent->is_fullscreen && (parent->layout == L_TABBED ||
385 parent->layout == L_STACKED)) {
386 move_out_of_tabs_stacks(container, current, move_dir, offs);
387 return;
388 } else { 365 } else {
389 wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); 366 // Container can move within its siblings
390 current = current->parent; 367 container_move_to_container_from_direction(container,
368 siblings->items[desired], move_dir);
369 return true;
391 } 370 }
392 break; 371 } else if (!(parent && parent->is_fullscreen) &&
393 default: 372 (layout == L_TABBED || layout == L_STACKED)) {
394 sway_assert(0, "Not expecting to see container of type %s here", 373 move_out_of_tabs_stacks(container, current, move_dir, offs);
395 container_type_to_str(current->type)); 374 return true;
396 return;
397 } 375 }
398 }
399 376
400 // Part two: move stuff around 377 current = current->parent;
401 int index = container_sibling_index(container);
402 struct sway_container *old_parent = container->parent;
403 378
404 while (sibling) { 379 // Don't allow containers to move out of their
405 switch (sibling->type) { 380 // fullscreen or floating parent
406 case C_VIEW: 381 if (current &&
407 if (sibling->parent == container->parent) { 382 (current->is_fullscreen || container_is_floating(current))) {
408 wlr_log(WLR_DEBUG, "Swapping siblings"); 383 return false;
409 sibling->parent->children->items[index + offs] = container;
410 sibling->parent->children->items[index] = sibling;
411 } else {
412 wlr_log(WLR_DEBUG, "Promoting to sibling of cousin");
413 container_insert_child(sibling->parent, container,
414 container_sibling_index(sibling) + (offs > 0 ? 0 : 1));
415 container->width = container->height = 0;
416 }
417 sibling = NULL;
418 break;
419 case C_WORKSPACE: // Note: only in the case of moving between outputs
420 case C_CONTAINER:
421 if (is_parallel(sibling->layout, move_dir)) {
422 int limit = container_limit(sibling, invert_movement(move_dir));
423 wlr_log(WLR_DEBUG, "limit: %d", limit);
424 wlr_log(WLR_DEBUG,
425 "Reparenting container (parallel) to index %d "
426 "(move dir: %d)", limit, move_dir);
427 container_insert_child(sibling, container, limit);
428 container->width = container->height = 0;
429 sibling = NULL;
430 } else {
431 wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)");
432 struct sway_container *focus_inactive = seat_get_focus_inactive(
433 config->handler_context.seat, sibling);
434 if (focus_inactive && focus_inactive != sibling) {
435 while (focus_inactive->parent != sibling) {
436 focus_inactive = focus_inactive->parent;
437 }
438 wlr_log(WLR_DEBUG, "Focus inactive: id:%zd",
439 focus_inactive->id);
440 sibling = focus_inactive;
441 continue;
442 } else if (sibling->children->length) {
443 wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily");
444 container_remove_child(container);
445 container_add_sibling(sibling->children->items[0], container);
446 } else {
447 wlr_log(WLR_DEBUG, "No kiddos, adding container alone");
448 container_remove_child(container);
449 container_add_child(sibling, container);
450 }
451 container->width = container->height = 0;
452 sibling = NULL;
453 }
454 break;
455 default:
456 sway_assert(0, "Not expecting to see container of type %s here",
457 container_type_to_str(sibling->type));
458 return;
459 } 384 }
460 } 385 }
461 386
462 container_notify_subtree_changed(old_parent); 387 // Maybe rejigger the workspace
463 container_notify_subtree_changed(container->parent); 388 struct sway_workspace *ws = container->workspace;
464 389 if (!is_parallel(ws->layout, move_dir)) {
465 if (container->type == C_VIEW) { 390 if (ws->tiling->length >= 2) {
466 ipc_event_window(container, "move"); 391 workspace_rejigger(ws, container, move_dir);
467 } 392 return true;
468 393 }
469 if (old_parent) { 394 } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
470 seat_set_focus(config->handler_context.seat, old_parent); 395 workspace_rejigger(ws, container, move_dir);
471 seat_set_focus(config->handler_context.seat, container); 396 return true;
472 } 397 }
473 398
474 struct sway_container *last_ws = old_parent; 399 // Try adjacent output
475 struct sway_container *next_ws = container->parent; 400 struct sway_output *output =
476 if (last_ws && last_ws->type != C_WORKSPACE) { 401 output_get_in_direction(container->workspace->output, move_dir);
477 last_ws = container_parent(last_ws, C_WORKSPACE); 402 if (output) {
478 } 403 struct sway_workspace *ws = output_get_active_workspace(output);
479 if (next_ws && next_ws->type != C_WORKSPACE) { 404 container_move_to_workspace_from_direction(container, ws, move_dir);
480 next_ws = container_parent(next_ws, C_WORKSPACE); 405 return true;
481 } 406 }
482 if (last_ws && next_ws && last_ws != next_ws) { 407 wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go");
483 ipc_event_workspace(last_ws, next_ws, "focus"); 408 return false;
484 workspace_detect_urgent(last_ws);
485 workspace_detect_urgent(next_ws);
486 }
487 container_end_mouse_operation(container);
488} 409}
489 410
490static struct cmd_results *cmd_move_container(struct sway_container *current, 411static struct cmd_results *cmd_move_container(int argc, char **argv) {
491 int argc, char **argv) {
492 struct cmd_results *error = NULL; 412 struct cmd_results *error = NULL;
493 if ((error = checkarg(argc, "move container/window", 413 if ((error = checkarg(argc, "move container/window",
494 EXPECTED_AT_LEAST, 3))) { 414 EXPECTED_AT_LEAST, 3))) {
495 return error; 415 return error;
496 } 416 }
497 417
498 if (current->type == C_WORKSPACE) { 418 struct sway_node *node = config->handler_context.node;
499 if (current->children->length == 0) { 419 struct sway_workspace *workspace = config->handler_context.workspace;
420 struct sway_container *container = config->handler_context.container;
421 if (node->type == N_WORKSPACE) {
422 if (workspace->tiling->length == 0) {
500 return cmd_results_new(CMD_FAILURE, "move", 423 return cmd_results_new(CMD_FAILURE, "move",
501 "Can't move an empty workspace"); 424 "Can't move an empty workspace");
502 } 425 }
503 current = workspace_wrap_children(current); 426 container = workspace_wrap_children(workspace);
504 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
505 return cmd_results_new(CMD_FAILURE, "move",
506 "Can only move containers and views.");
507 } 427 }
508 428
509 bool no_auto_back_and_forth = false; 429 bool no_auto_back_and_forth = false;
@@ -530,15 +450,15 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
530 } 450 }
531 451
532 struct sway_seat *seat = config->handler_context.seat; 452 struct sway_seat *seat = config->handler_context.seat;
533 struct sway_container *old_parent = current->parent; 453 struct sway_container *old_parent = container->parent;
534 struct sway_container *old_ws = container_parent(current, C_WORKSPACE); 454 struct sway_workspace *old_ws = container->workspace;
535 struct sway_container *old_output = container_parent(current, C_OUTPUT); 455 struct sway_output *old_output = old_ws->output;
536 struct sway_container *destination = NULL; 456 struct sway_node *destination = NULL;
537 457
538 // determine destination 458 // determine destination
539 if (strcasecmp(argv[1], "workspace") == 0) { 459 if (strcasecmp(argv[1], "workspace") == 0) {
540 // move container to workspace x 460 // move container to workspace x
541 struct sway_container *ws = NULL; 461 struct sway_workspace *ws = NULL;
542 char *ws_name = NULL; 462 char *ws_name = NULL;
543 if (strcasecmp(argv[2], "next") == 0 || 463 if (strcasecmp(argv[2], "next") == 0 ||
544 strcasecmp(argv[2], "prev") == 0 || 464 strcasecmp(argv[2], "prev") == 0 ||
@@ -588,8 +508,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
588 // We have to create the workspace, but if the container is 508 // We have to create the workspace, but if the container is
589 // sticky and the workspace is going to be created on the same 509 // sticky and the workspace is going to be created on the same
590 // output, we'll bail out first. 510 // output, we'll bail out first.
591 if (current->is_sticky) { 511 if (container->is_sticky) {
592 struct sway_container *new_output = 512 struct sway_output *new_output =
593 workspace_get_initial_output(ws_name); 513 workspace_get_initial_output(ws_name);
594 if (old_output == new_output) { 514 if (old_output == new_output) {
595 free(ws_name); 515 free(ws_name);
@@ -601,105 +521,113 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
601 ws = workspace_create(NULL, ws_name); 521 ws = workspace_create(NULL, ws_name);
602 } 522 }
603 free(ws_name); 523 free(ws_name);
604 destination = seat_get_focus_inactive(seat, ws); 524 destination = seat_get_focus_inactive(seat, &ws->node);
605 } else if (strcasecmp(argv[1], "output") == 0) { 525 } else if (strcasecmp(argv[1], "output") == 0) {
606 struct sway_container *dest_output = output_in_direction(argv[2], 526 struct sway_output *new_output = output_in_direction(argv[2],
607 old_output->sway_output->wlr_output, current->x, current->y); 527 old_output->wlr_output, container->x, container->y);
608 if (!dest_output) { 528 if (!new_output) {
609 return cmd_results_new(CMD_FAILURE, "move workspace", 529 return cmd_results_new(CMD_FAILURE, "move workspace",
610 "Can't find output with name/direction '%s'", argv[2]); 530 "Can't find output with name/direction '%s'", argv[2]);
611 } 531 }
612 destination = seat_get_focus_inactive(seat, dest_output); 532 destination = seat_get_focus_inactive(seat, &new_output->node);
613 if (!destination) {
614 // We've never been to this output before
615 destination = dest_output->children->items[0];
616 }
617 } else if (strcasecmp(argv[1], "mark") == 0) { 533 } else if (strcasecmp(argv[1], "mark") == 0) {
618 struct sway_view *dest_view = view_find_mark(argv[2]); 534 struct sway_view *dest_view = view_find_mark(argv[2]);
619 if (dest_view == NULL) { 535 if (dest_view == NULL) {
620 return cmd_results_new(CMD_FAILURE, "move", 536 return cmd_results_new(CMD_FAILURE, "move",
621 "Mark '%s' not found", argv[2]); 537 "Mark '%s' not found", argv[2]);
622 } 538 }
623 destination = dest_view->swayc; 539 destination = &dest_view->container->node;
624 } else { 540 } else {
625 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 541 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
626 } 542 }
627 543
628 struct sway_container *new_output = destination->type == C_OUTPUT ? 544 if (container->is_sticky &&
629 destination : container_parent(destination, C_OUTPUT); 545 node_has_ancestor(destination, &old_output->node)) {
630 if (current->is_sticky && old_output == new_output) {
631 return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky " 546 return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky "
632 "container to another workspace on the same output"); 547 "container to another workspace on the same output");
633 } 548 }
634 549
635 struct sway_container *new_output_last_ws = old_output == new_output ? 550 struct sway_output *new_output = node_get_output(destination);
636 NULL : seat_get_active_child(seat, new_output); 551 struct sway_workspace *new_output_last_ws = old_output == new_output ?
637 struct sway_container *new_workspace = destination->type == C_WORKSPACE ? 552 NULL : output_get_active_workspace(new_output);
638 destination : container_parent(destination, C_WORKSPACE);
639 553
640 // move container, arrange windows and return focus 554 // move container, arrange windows and return focus
641 container_move_to(current, destination); 555 switch (destination->type) {
556 case N_WORKSPACE:
557 container_move_to_workspace(container, destination->sway_workspace);
558 break;
559 case N_OUTPUT: {
560 struct sway_output *output = destination->sway_output;
561 struct sway_workspace *ws = output_get_active_workspace(output);
562 container_move_to_workspace(container, ws);
563 }
564 break;
565 case N_CONTAINER:
566 container_move_to_container(container, destination->sway_container);
567 break;
568 case N_ROOT:
569 break;
570 }
571 struct sway_workspace *new_workspace =
572 output_get_active_workspace(new_output);
642 if (new_output_last_ws && new_output_last_ws != new_workspace) { 573 if (new_output_last_ws && new_output_last_ws != new_workspace) {
643 // change focus on destination output back to its last active workspace 574 // change focus on destination output back to its last active workspace
644 struct sway_container *new_output_last_focus = 575 struct sway_node *new_output_last_focus =
645 seat_get_focus_inactive(seat, new_output_last_ws); 576 seat_get_focus_inactive(seat, &new_output_last_ws->node);
646 seat_set_focus_warp(seat, new_output_last_focus, false, false); 577 seat_set_focus_warp(seat, new_output_last_focus, false, false);
647 } 578 }
648 struct sway_container *focus = seat_get_focus_inactive(seat, old_parent); 579
580 struct sway_node *focus = NULL;
581 if (old_parent) {
582 focus = seat_get_focus_inactive(seat, &old_parent->node);
583 } else {
584 focus = seat_get_focus_inactive(seat, &old_ws->node);
585 }
649 seat_set_focus_warp(seat, focus, true, false); 586 seat_set_focus_warp(seat, focus, true, false);
650 container_reap_empty(old_parent);
651 container_reap_empty(destination->parent);
652 587
653 // TODO: Ideally we would arrange the surviving parent after reaping, 588 if (old_parent) {
654 // but container_reap_empty does not return it, so we arrange the 589 container_reap_empty(old_parent);
655 // workspace instead. 590 } else {
656 arrange_windows(old_ws); 591 workspace_consider_destroy(old_ws);
657 arrange_windows(destination->parent); 592 }
593
594 arrange_workspace(old_ws);
595 arrange_node(node_get_parent(destination));
658 596
659 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 597 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
660} 598}
661 599
662static void workspace_move_to_output(struct sway_container *workspace, 600static void workspace_move_to_output(struct sway_workspace *workspace,
663 struct sway_container *output) { 601 struct sway_output *output) {
664 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 602 if (workspace->output == output) {
665 return; 603 return;
666 } 604 }
667 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 605 struct sway_output *old_output = workspace->output;
668 return; 606 workspace_detach(workspace);
669 } 607 struct sway_workspace *new_output_old_ws =
670 if (workspace->parent == output) { 608 output_get_active_workspace(output);
671 return;
672 }
673 struct sway_container *old_output = container_remove_child(workspace);
674 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
675 struct sway_container *new_output_focus =
676 seat_get_focus_inactive(seat, output);
677 609
678 container_add_child(output, workspace); 610 output_add_workspace(output, workspace);
679 611
680 // If moving the last workspace from the old output, create a new workspace 612 // If moving the last workspace from the old output, create a new workspace
681 // on the old output 613 // on the old output
682 if (old_output->children->length == 0) { 614 struct sway_seat *seat = config->handler_context.seat;
683 char *ws_name = workspace_next_name(old_output->name); 615 if (old_output->workspaces->length == 0) {
684 struct sway_container *ws = workspace_create(old_output, ws_name); 616 char *ws_name = workspace_next_name(old_output->wlr_output->name);
617 struct sway_workspace *ws = workspace_create(old_output, ws_name);
685 free(ws_name); 618 free(ws_name);
686 seat_set_focus(seat, ws); 619 seat_set_focus(seat, &ws->node);
687 } 620 }
688 621
689 // Try to remove an empty workspace from the destination output. 622 workspace_consider_destroy(new_output_old_ws);
690 container_reap_empty(new_output_focus);
691 623
692 output_sort_workspaces(output); 624 output_sort_workspaces(output);
693 seat_set_focus(seat, output); 625 seat_set_focus(seat, &output->node);
694 workspace_output_raise_priority(workspace, old_output, output); 626 workspace_output_raise_priority(workspace, old_output, output);
695 ipc_event_workspace(NULL, workspace, "move"); 627 ipc_event_workspace(NULL, workspace, "move");
696
697 container_notify_subtree_changed(old_output);
698 container_notify_subtree_changed(output);
699} 628}
700 629
701static struct cmd_results *cmd_move_workspace(struct sway_container *current, 630static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
702 int argc, char **argv) {
703 struct cmd_results *error = NULL; 631 struct cmd_results *error = NULL;
704 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { 632 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) {
705 return error; 633 return error;
@@ -716,27 +644,25 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
716 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 644 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
717 } 645 }
718 646
719 struct sway_container *source = container_parent(current, C_OUTPUT); 647 struct sway_workspace *workspace = config->handler_context.workspace;
720 int center_x = current->width / 2 + current->x, 648 struct sway_output *old_output = workspace->output;
721 center_y = current->height / 2 + current->y; 649 int center_x = workspace->width / 2 + workspace->x,
722 struct sway_container *destination = output_in_direction(argv[2], 650 center_y = workspace->height / 2 + workspace->y;
723 source->sway_output->wlr_output, center_x, center_y); 651 struct sway_output *new_output = output_in_direction(argv[2],
724 if (!destination) { 652 old_output->wlr_output, center_x, center_y);
653 if (!new_output) {
725 return cmd_results_new(CMD_FAILURE, "move workspace", 654 return cmd_results_new(CMD_FAILURE, "move workspace",
726 "Can't find output with name/direction '%s'", argv[2]); 655 "Can't find output with name/direction '%s'", argv[2]);
727 } 656 }
728 if (current->type != C_WORKSPACE) { 657 workspace_move_to_output(workspace, new_output);
729 current = container_parent(current, C_WORKSPACE);
730 }
731 workspace_move_to_output(current, destination);
732 658
733 arrange_windows(source); 659 arrange_output(old_output);
734 arrange_windows(destination); 660 arrange_output(new_output);
735 661
736 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 662 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
737} 663}
738 664
739static struct cmd_results *move_in_direction(struct sway_container *container, 665static struct cmd_results *cmd_move_in_direction(
740 enum movement_direction direction, int argc, char **argv) { 666 enum movement_direction direction, int argc, char **argv) {
741 int move_amt = 10; 667 int move_amt = 10;
742 if (argc > 1) { 668 if (argc > 1) {
@@ -748,7 +674,8 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
748 } 674 }
749 } 675 }
750 676
751 if (container->type == C_WORKSPACE) { 677 struct sway_container *container = config->handler_context.container;
678 if (!container) {
752 return cmd_results_new(CMD_FAILURE, "move", 679 return cmd_results_new(CMD_FAILURE, "move",
753 "Cannot move workspaces in a direction"); 680 "Cannot move workspaces in a direction");
754 } 681 }
@@ -780,20 +707,34 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
780 container_floating_move_to(container, lx, ly); 707 container_floating_move_to(container, lx, ly);
781 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 708 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
782 } 709 }
783 // For simplicity, we'll arrange the entire workspace. The reason for this 710 struct sway_workspace *old_ws = container->workspace;
784 // is moving the container might reap the old parent, and container_move
785 // does not return a surviving parent.
786 // TODO: Make container_move return the surviving parent so we can arrange
787 // just that.
788 struct sway_container *old_ws = container_parent(container, C_WORKSPACE);
789 container_move(container, direction, move_amt);
790 struct sway_container *new_ws = container_parent(container, C_WORKSPACE);
791 711
792 arrange_windows(old_ws); 712 if (!container_move_in_direction(container, direction)) {
713 // Container didn't move
714 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
715 }
716
717 struct sway_workspace *new_ws = container->workspace;
718
719 arrange_workspace(old_ws);
793 if (new_ws != old_ws) { 720 if (new_ws != old_ws) {
794 arrange_windows(new_ws); 721 arrange_workspace(new_ws);
722 }
723
724 if (container->view) {
725 ipc_event_window(container, "move");
795 } 726 }
796 727
728 seat_set_focus(config->handler_context.seat, &new_ws->node);
729 seat_set_focus(config->handler_context.seat, &container->node);
730
731 if (old_ws != new_ws) {
732 ipc_event_workspace(old_ws, new_ws, "focus");
733 workspace_detect_urgent(old_ws);
734 workspace_detect_urgent(new_ws);
735 }
736 container_end_mouse_operation(container);
737
797 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 738 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
798} 739}
799 740
@@ -802,9 +743,9 @@ static const char *expected_position_syntax =
802 "'move [absolute] position center' or " 743 "'move [absolute] position center' or "
803 "'move position cursor|mouse|pointer'"; 744 "'move position cursor|mouse|pointer'";
804 745
805static struct cmd_results *move_to_position(struct sway_container *container, 746static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
806 int argc, char **argv) { 747 struct sway_container *container = config->handler_context.container;
807 if (!container_is_floating(container)) { 748 if (!container || !container_is_floating(container)) {
808 return cmd_results_new(CMD_FAILURE, "move", 749 return cmd_results_new(CMD_FAILURE, "move",
809 "Only floating containers " 750 "Only floating containers "
810 "can be moved to an absolute position"); 751 "can be moved to an absolute position");
@@ -842,10 +783,10 @@ static struct cmd_results *move_to_position(struct sway_container *container,
842 } else if (strcmp(argv[0], "center") == 0) { 783 } else if (strcmp(argv[0], "center") == 0) {
843 double lx, ly; 784 double lx, ly;
844 if (absolute) { 785 if (absolute) {
845 lx = root_container.x + (root_container.width - container->width) / 2; 786 lx = root->x + (root->width - container->width) / 2;
846 ly = root_container.y + (root_container.height - container->height) / 2; 787 ly = root->y + (root->height - container->height) / 2;
847 } else { 788 } else {
848 struct sway_container *ws = container_parent(container, C_WORKSPACE); 789 struct sway_workspace *ws = container->workspace;
849 lx = ws->x + (ws->width - container->width) / 2; 790 lx = ws->x + (ws->width - container->width) / 2;
850 ly = ws->y + (ws->height - container->height) / 2; 791 ly = ws->y + (ws->height - container->height) / 2;
851 } 792 }
@@ -881,30 +822,31 @@ static struct cmd_results *move_to_position(struct sway_container *container,
881 } 822 }
882 823
883 if (!absolute) { 824 if (!absolute) {
884 struct sway_container *ws = container_parent(container, C_WORKSPACE); 825 lx += container->workspace->x;
885 lx += ws->x; 826 ly += container->workspace->y;
886 ly += ws->y;
887 } 827 }
888 container_floating_move_to(container, lx, ly); 828 container_floating_move_to(container, lx, ly);
889 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 829 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
890} 830}
891 831
892static struct cmd_results *move_to_scratchpad(struct sway_container *con) { 832static struct cmd_results *cmd_move_to_scratchpad(void) {
893 if (con->type == C_WORKSPACE && con->children->length == 0) { 833 struct sway_node *node = config->handler_context.node;
834 struct sway_container *con = config->handler_context.container;
835 struct sway_workspace *ws = config->handler_context.workspace;
836 if (node->type == N_WORKSPACE && ws->tiling->length == 0) {
894 return cmd_results_new(CMD_INVALID, "move", 837 return cmd_results_new(CMD_INVALID, "move",
895 "Can't move an empty workspace to the scratchpad"); 838 "Can't move an empty workspace to the scratchpad");
896 } 839 }
897 if (con->type == C_WORKSPACE) { 840 if (node->type == N_WORKSPACE) {
898 // Wrap the workspace's children in a container 841 // Wrap the workspace's children in a container
899 struct sway_container *workspace = con; 842 con = workspace_wrap_children(ws);
900 con = workspace_wrap_children(con); 843 ws->layout = L_HORIZ;
901 workspace->layout = L_HORIZ;
902 } 844 }
903 845
904 // If the container is in a floating split container, 846 // If the container is in a floating split container,
905 // operate on the split container instead of the child. 847 // operate on the split container instead of the child.
906 if (container_is_floating_or_child(con)) { 848 if (container_is_floating_or_child(con)) {
907 while (con->parent->type != C_WORKSPACE) { 849 while (con->parent) {
908 con = con->parent; 850 con = con->parent;
909 } 851 }
910 } 852 }
@@ -922,32 +864,31 @@ struct cmd_results *cmd_move(int argc, char **argv) {
922 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { 864 if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
923 return error; 865 return error;
924 } 866 }
925 struct sway_container *current = config->handler_context.current_container;
926 867
927 if (strcasecmp(argv[0], "left") == 0) { 868 if (strcasecmp(argv[0], "left") == 0) {
928 return move_in_direction(current, MOVE_LEFT, argc, argv); 869 return cmd_move_in_direction(MOVE_LEFT, argc, argv);
929 } else if (strcasecmp(argv[0], "right") == 0) { 870 } else if (strcasecmp(argv[0], "right") == 0) {
930 return move_in_direction(current, MOVE_RIGHT, argc, argv); 871 return cmd_move_in_direction(MOVE_RIGHT, argc, argv);
931 } else if (strcasecmp(argv[0], "up") == 0) { 872 } else if (strcasecmp(argv[0], "up") == 0) {
932 return move_in_direction(current, MOVE_UP, argc, argv); 873 return cmd_move_in_direction(MOVE_UP, argc, argv);
933 } else if (strcasecmp(argv[0], "down") == 0) { 874 } else if (strcasecmp(argv[0], "down") == 0) {
934 return move_in_direction(current, MOVE_DOWN, argc, argv); 875 return cmd_move_in_direction(MOVE_DOWN, argc, argv);
935 } else if ((strcasecmp(argv[0], "container") == 0 876 } else if ((strcasecmp(argv[0], "container") == 0
936 || strcasecmp(argv[0], "window") == 0) || 877 || strcasecmp(argv[0], "window") == 0) ||
937 (strcasecmp(argv[0], "--no-auto-back-and-forth") && 878 (strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2
938 (strcasecmp(argv[0], "container") == 0 879 && (strcasecmp(argv[1], "container") == 0
939 || strcasecmp(argv[0], "window") == 0))) { 880 || strcasecmp(argv[1], "window") == 0))) {
940 return cmd_move_container(current, argc, argv); 881 return cmd_move_container(argc, argv);
941 } else if (strcasecmp(argv[0], "workspace") == 0) { 882 } else if (strcasecmp(argv[0], "workspace") == 0) {
942 return cmd_move_workspace(current, argc, argv); 883 return cmd_move_workspace(argc, argv);
943 } else if (strcasecmp(argv[0], "scratchpad") == 0 884 } else if (strcasecmp(argv[0], "scratchpad") == 0
944 || (strcasecmp(argv[0], "to") == 0 && argc == 2 885 || (strcasecmp(argv[0], "to") == 0 && argc == 2
945 && strcasecmp(argv[1], "scratchpad") == 0)) { 886 && strcasecmp(argv[1], "scratchpad") == 0)) {
946 return move_to_scratchpad(current); 887 return cmd_move_to_scratchpad();
947 } else if (strcasecmp(argv[0], "position") == 0) { 888 } else if (strcasecmp(argv[0], "position") == 0) {
948 return move_to_position(current, argc, argv); 889 return cmd_move_to_position(argc, argv);
949 } else if (strcasecmp(argv[0], "absolute") == 0) { 890 } else if (strcasecmp(argv[0], "absolute") == 0) {
950 return move_to_position(current, argc, argv); 891 return cmd_move_to_position(argc, argv);
951 } else { 892 } else {
952 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 893 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
953 } 894 }
diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c
index 68fd9f42..9cdaad7f 100644
--- a/sway/commands/opacity.c
+++ b/sway/commands/opacity.c
@@ -19,8 +19,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) {
19 return error; 19 return error;
20 } 20 }
21 21
22 struct sway_container *con = 22 struct sway_container *con = config->handler_context.container;
23 config->handler_context.current_container;
24 23
25 float opacity = 0.0f; 24 float opacity = 0.0f;
26 25
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index f8ca374d..36fb9092 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -42,7 +42,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
42 } 42 }
43 list_free(bar_ids); 43 list_free(bar_ids);
44 44
45 arrange_windows(&root_container); 45 arrange_root();
46 46
47 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 47 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
48} 48}
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index 21d2aa64..d982f941 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -25,14 +25,11 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
25 } 25 }
26 26
27 int argn = 1; 27 int argn = 1;
28 struct sway_container *workspace; 28 struct sway_workspace *workspace = NULL;
29 29
30 if (strcasecmp(argv[1], "to") == 0) { 30 if (strcasecmp(argv[1], "to") == 0) {
31 // 'rename workspace to new_name' 31 // 'rename workspace to new_name'
32 workspace = config->handler_context.current_container; 32 workspace = config->handler_context.workspace;
33 if (workspace->type != C_WORKSPACE) {
34 workspace = container_parent(workspace, C_WORKSPACE);
35 }
36 } else if (strcasecmp(argv[1], "number") == 0) { 33 } else if (strcasecmp(argv[1], "number") == 0) {
37 // 'rename workspace number x to new_name' 34 // 'rename workspace number x to new_name'
38 if (!isdigit(argv[2][0])) { 35 if (!isdigit(argv[2][0])) {
@@ -78,7 +75,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
78 return cmd_results_new(CMD_INVALID, "rename", 75 return cmd_results_new(CMD_INVALID, "rename",
79 "Cannot use special workspace name '%s'", argv[argn]); 76 "Cannot use special workspace name '%s'", argv[argn]);
80 } 77 }
81 struct sway_container *tmp_workspace = workspace_by_name(new_name); 78 struct sway_workspace *tmp_workspace = workspace_by_name(new_name);
82 if (tmp_workspace) { 79 if (tmp_workspace) {
83 free(new_name); 80 free(new_name);
84 return cmd_results_new(CMD_INVALID, "rename", 81 return cmd_results_new(CMD_INVALID, "rename",
@@ -89,7 +86,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
89 free(workspace->name); 86 free(workspace->name);
90 workspace->name = new_name; 87 workspace->name = new_name;
91 88
92 output_sort_workspaces(workspace->parent); 89 output_sort_workspaces(workspace->output);
93 ipc_event_workspace(NULL, workspace, "rename"); 90 ipc_event_workspace(NULL, workspace, "rename");
94 91
95 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 92 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index ad659ef5..99e9dbda 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -10,6 +10,7 @@
10#include "sway/commands.h" 10#include "sway/commands.h"
11#include "sway/tree/arrange.h" 11#include "sway/tree/arrange.h"
12#include "sway/tree/view.h" 12#include "sway/tree/view.h"
13#include "sway/tree/workspace.h"
13#include "log.h" 14#include "log.h"
14 15
15static const int MIN_SANE_W = 100, MIN_SANE_H = 60; 16static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
@@ -75,7 +76,7 @@ static int parse_resize_amount(int argc, char **argv,
75 76
76static void calculate_constraints(int *min_width, int *max_width, 77static void calculate_constraints(int *min_width, int *max_width,
77 int *min_height, int *max_height) { 78 int *min_height, int *max_height) {
78 struct sway_container *con = config->handler_context.current_container; 79 struct sway_container *con = config->handler_context.container;
79 80
80 if (config->floating_minimum_width == -1) { // no minimum 81 if (config->floating_minimum_width == -1) { // no minimum
81 *min_width = 0; 82 *min_width = 0;
@@ -96,8 +97,7 @@ static void calculate_constraints(int *min_width, int *max_width,
96 if (config->floating_maximum_width == -1) { // no maximum 97 if (config->floating_maximum_width == -1) { // no maximum
97 *max_width = INT_MAX; 98 *max_width = INT_MAX;
98 } else if (config->floating_maximum_width == 0) { // automatic 99 } else if (config->floating_maximum_width == 0) { // automatic
99 struct sway_container *ws = container_parent(con, C_WORKSPACE); 100 *max_width = con->workspace->width;
100 *max_width = ws->width;
101 } else { 101 } else {
102 *max_width = config->floating_maximum_width; 102 *max_width = config->floating_maximum_width;
103 } 103 }
@@ -105,8 +105,7 @@ static void calculate_constraints(int *min_width, int *max_width,
105 if (config->floating_maximum_height == -1) { // no maximum 105 if (config->floating_maximum_height == -1) { // no maximum
106 *max_height = INT_MAX; 106 *max_height = INT_MAX;
107 } else if (config->floating_maximum_height == 0) { // automatic 107 } else if (config->floating_maximum_height == 0) { // automatic
108 struct sway_container *ws = container_parent(con, C_WORKSPACE); 108 *max_height = con->workspace->height;
109 *max_height = ws->height;
110 } else { 109 } else {
111 *max_height = config->floating_maximum_height; 110 *max_height = config->floating_maximum_height;
112 } 111 }
@@ -191,11 +190,11 @@ static void resize_tiled(struct sway_container *parent, int amount,
191 normalize_axis(axis) == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT; 190 normalize_axis(axis) == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT;
192 int minor_weight = 0; 191 int minor_weight = 0;
193 int major_weight = 0; 192 int major_weight = 0;
194 while (parent->parent) { 193 while (parent) {
195 struct sway_container *next = parent->parent; 194 list_t *siblings = container_get_siblings(parent);
196 if (next->layout == parallel_layout) { 195 if (container_parent_layout(parent) == parallel_layout) {
197 for (int i = 0; i < next->children->length; i++) { 196 for (int i = 0; i < siblings->length; i++) {
198 struct sway_container *sibling = next->children->items[i]; 197 struct sway_container *sibling = siblings->items[i];
199 198
200 int sibling_pos = parallel_coord(sibling, axis); 199 int sibling_pos = parallel_coord(sibling, axis);
201 int focused_pos = parallel_coord(focused, axis); 200 int focused_pos = parallel_coord(focused, axis);
@@ -213,17 +212,13 @@ static void resize_tiled(struct sway_container *parent, int amount,
213 break; 212 break;
214 } 213 }
215 } 214 }
216 parent = next; 215 parent = parent->parent;
217 } 216 }
218 217 if (!parent) {
219 if (parent->type == C_ROOT) { 218 // Can't resize in this direction
220 return; 219 return;
221 } 220 }
222 221
223 wlr_log(WLR_DEBUG,
224 "Found the proper parent: %p. It has %d l conts, and %d r conts",
225 parent->parent, minor_weight, major_weight);
226
227 // Implement up/down/left/right direction by zeroing one of the weights, 222 // Implement up/down/left/right direction by zeroing one of the weights,
228 // then setting the axis to be horizontal or vertical 223 // then setting the axis to be horizontal or vertical
229 if (axis == RESIZE_AXIS_UP || axis == RESIZE_AXIS_LEFT) { 224 if (axis == RESIZE_AXIS_UP || axis == RESIZE_AXIS_LEFT) {
@@ -237,9 +232,10 @@ static void resize_tiled(struct sway_container *parent, int amount,
237 232
238 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks 233 //TODO: Ensure rounding is done in such a way that there are NO pixel leaks
239 // ^ ????? 234 // ^ ?????
235 list_t *siblings = container_get_siblings(parent);
240 236
241 for (int i = 0; i < parent->parent->children->length; i++) { 237 for (int i = 0; i < siblings->length; i++) {
242 struct sway_container *sibling = parent->parent->children->items[i]; 238 struct sway_container *sibling = siblings->items[i];
243 239
244 int sibling_pos = parallel_coord(sibling, axis); 240 int sibling_pos = parallel_coord(sibling, axis);
245 int focused_pos = parallel_coord(focused, axis); 241 int focused_pos = parallel_coord(focused, axis);
@@ -277,8 +273,8 @@ static void resize_tiled(struct sway_container *parent, int amount,
277 enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ? 273 enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ?
278 WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM; 274 WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM;
279 275
280 for (int i = 0; i < parent->parent->children->length; i++) { 276 for (int i = 0; i < siblings->length; i++) {
281 struct sway_container *sibling = parent->parent->children->items[i]; 277 struct sway_container *sibling = siblings->items[i];
282 278
283 int sibling_pos = parallel_coord(sibling, axis); 279 int sibling_pos = parallel_coord(sibling, axis);
284 int focused_pos = parallel_coord(focused, axis); 280 int focused_pos = parallel_coord(focused, axis);
@@ -316,7 +312,11 @@ static void resize_tiled(struct sway_container *parent, int amount,
316 } 312 }
317 } 313 }
318 314
319 arrange_windows(parent->parent); 315 if (parent->parent) {
316 arrange_container(parent->parent);
317 } else {
318 arrange_workspace(parent->workspace);
319 }
320} 320}
321 321
322void container_resize_tiled(struct sway_container *parent, 322void container_resize_tiled(struct sway_container *parent,
@@ -346,7 +346,7 @@ void container_resize_tiled(struct sway_container *parent,
346 */ 346 */
347static struct cmd_results *resize_adjust_floating(enum resize_axis axis, 347static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
348 struct resize_amount *amount) { 348 struct resize_amount *amount) {
349 struct sway_container *con = config->handler_context.current_container; 349 struct sway_container *con = config->handler_context.container;
350 int grow_width = 0, grow_height = 0; 350 int grow_width = 0, grow_height = 0;
351 switch (axis) { 351 switch (axis) {
352 case RESIZE_AXIS_HORIZONTAL: 352 case RESIZE_AXIS_HORIZONTAL:
@@ -400,15 +400,15 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
400 con->width += grow_width; 400 con->width += grow_width;
401 con->height += grow_height; 401 con->height += grow_height;
402 402
403 if (con->type == C_VIEW) { 403 if (con->view) {
404 struct sway_view *view = con->sway_view; 404 struct sway_view *view = con->view;
405 view->x += grow_x; 405 view->x += grow_x;
406 view->y += grow_y; 406 view->y += grow_y;
407 view->width += grow_width; 407 view->width += grow_width;
408 view->height += grow_height; 408 view->height += grow_height;
409 } 409 }
410 410
411 arrange_windows(con); 411 arrange_container(con);
412 412
413 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 413 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
414} 414}
@@ -418,7 +418,7 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
418 */ 418 */
419static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, 419static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
420 struct resize_amount *amount) { 420 struct resize_amount *amount) {
421 struct sway_container *current = config->handler_context.current_container; 421 struct sway_container *current = config->handler_context.container;
422 422
423 if (amount->unit == RESIZE_UNIT_DEFAULT) { 423 if (amount->unit == RESIZE_UNIT_DEFAULT) {
424 amount->unit = RESIZE_UNIT_PPT; 424 amount->unit = RESIZE_UNIT_PPT;
@@ -456,13 +456,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
456 width->unit == RESIZE_UNIT_DEFAULT) { 456 width->unit == RESIZE_UNIT_DEFAULT) {
457 // Convert to px 457 // Convert to px
458 struct sway_container *parent = con->parent; 458 struct sway_container *parent = con->parent;
459 while (parent->type >= C_WORKSPACE && parent->layout != L_HORIZ) { 459 while (parent && parent->layout != L_HORIZ) {
460 parent = parent->parent; 460 parent = parent->parent;
461 } 461 }
462 if (parent->type >= C_WORKSPACE) { 462 if (parent) {
463 width->amount = parent->width * width->amount / 100; 463 width->amount = parent->width * width->amount / 100;
464 width->unit = RESIZE_UNIT_PX; 464 } else {
465 width->amount = con->workspace->width * width->amount / 100;
465 } 466 }
467 width->unit = RESIZE_UNIT_PX;
466 } 468 }
467 if (width->unit == RESIZE_UNIT_PX) { 469 if (width->unit == RESIZE_UNIT_PX) {
468 resize_tiled(con, width->amount - con->width, 470 resize_tiled(con, width->amount - con->width,
@@ -475,13 +477,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
475 height->unit == RESIZE_UNIT_DEFAULT) { 477 height->unit == RESIZE_UNIT_DEFAULT) {
476 // Convert to px 478 // Convert to px
477 struct sway_container *parent = con->parent; 479 struct sway_container *parent = con->parent;
478 while (parent->type >= C_WORKSPACE && parent->layout != L_VERT) { 480 while (parent && parent->layout != L_VERT) {
479 parent = parent->parent; 481 parent = parent->parent;
480 } 482 }
481 if (parent->type >= C_WORKSPACE) { 483 if (parent) {
482 height->amount = parent->height * height->amount / 100; 484 height->amount = parent->height * height->amount / 100;
483 height->unit = RESIZE_UNIT_PX; 485 } else {
486 height->amount = con->workspace->height * height->amount / 100;
484 } 487 }
488 height->unit = RESIZE_UNIT_PX;
485 } 489 }
486 if (height->unit == RESIZE_UNIT_PX) { 490 if (height->unit == RESIZE_UNIT_PX) {
487 resize_tiled(con, height->amount - con->height, 491 resize_tiled(con, height->amount - con->height,
@@ -508,15 +512,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
508 con->width = width->amount; 512 con->width = width->amount;
509 con->height = height->amount; 513 con->height = height->amount;
510 514
511 if (con->type == C_VIEW) { 515 if (con->view) {
512 struct sway_view *view = con->sway_view; 516 struct sway_view *view = con->view;
513 view->x -= grow_width / 2; 517 view->x -= grow_width / 2;
514 view->y -= grow_height / 2; 518 view->y -= grow_height / 2;
515 view->width += grow_width; 519 view->width += grow_width;
516 view->height += grow_height; 520 view->height += grow_height;
517 } 521 }
518 522
519 arrange_windows(con); 523 arrange_container(con);
520 524
521 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 525 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
522} 526}
@@ -555,7 +559,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
555 } 559 }
556 560
557 // If 0, don't resize that dimension 561 // If 0, don't resize that dimension
558 struct sway_container *con = config->handler_context.current_container; 562 struct sway_container *con = config->handler_context.container;
559 if (width.amount <= 0) { 563 if (width.amount <= 0) {
560 width.amount = con->width; 564 width.amount = con->width;
561 } 565 }
@@ -624,7 +628,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
624 first_amount.amount *= multiplier; 628 first_amount.amount *= multiplier;
625 second_amount.amount *= multiplier; 629 second_amount.amount *= multiplier;
626 630
627 struct sway_container *con = config->handler_context.current_container; 631 struct sway_container *con = config->handler_context.container;
628 if (container_is_floating(con)) { 632 if (container_is_floating(con)) {
629 // Floating containers can only resize in px. Choose an amount which 633 // Floating containers can only resize in px. Choose an amount which
630 // uses px, with fallback to an amount that specified no unit. 634 // uses px, with fallback to an amount that specified no unit.
@@ -657,14 +661,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
657} 661}
658 662
659struct cmd_results *cmd_resize(int argc, char **argv) { 663struct cmd_results *cmd_resize(int argc, char **argv) {
660 struct sway_container *current = config->handler_context.current_container; 664 struct sway_container *current = config->handler_context.container;
661 if (!current) { 665 if (!current) {
662 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); 666 return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
663 } 667 }
664 if (current->type != C_VIEW && current->type != C_CONTAINER) {
665 return cmd_results_new(CMD_INVALID, "resize",
666 "Can only resize views/containers");
667 }
668 668
669 struct cmd_results *error; 669 struct cmd_results *error;
670 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { 670 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c
index 7da20015..d8bae615 100644
--- a/sway/commands/scratchpad.c
+++ b/sway/commands/scratchpad.c
@@ -9,36 +9,34 @@
9 9
10static void scratchpad_toggle_auto(void) { 10static void scratchpad_toggle_auto(void) {
11 struct sway_seat *seat = input_manager_current_seat(input_manager); 11 struct sway_seat *seat = input_manager_current_seat(input_manager);
12 struct sway_container *focus = seat_get_focus(seat); 12 struct sway_container *focus = seat_get_focused_container(seat);
13 struct sway_container *ws = focus->type == C_WORKSPACE ? 13 struct sway_workspace *ws = seat_get_focused_workspace(seat);
14 focus : container_parent(focus, C_WORKSPACE);
15 14
16 // If the focus is in a floating split container, 15 // If the focus is in a floating split container,
17 // operate on the split container instead of the child. 16 // operate on the split container instead of the child.
18 if (container_is_floating_or_child(focus)) { 17 if (focus && container_is_floating_or_child(focus)) {
19 while (focus->parent->type != C_WORKSPACE) { 18 while (focus->parent) {
20 focus = focus->parent; 19 focus = focus->parent;
21 } 20 }
22 } 21 }
23 22
24
25 // Check if the currently focused window is a scratchpad window and should 23 // Check if the currently focused window is a scratchpad window and should
26 // be hidden again. 24 // be hidden again.
27 if (focus->scratchpad) { 25 if (focus && focus->scratchpad) {
28 wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", 26 wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s",
29 focus->name); 27 focus->title);
30 root_scratchpad_hide(focus); 28 root_scratchpad_hide(focus);
31 return; 29 return;
32 } 30 }
33 31
34 // Check if there is an unfocused scratchpad window on the current workspace 32 // Check if there is an unfocused scratchpad window on the current workspace
35 // and focus it. 33 // and focus it.
36 for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { 34 for (int i = 0; i < ws->floating->length; ++i) {
37 struct sway_container *floater = ws->sway_workspace->floating->items[i]; 35 struct sway_container *floater = ws->floating->items[i];
38 if (floater->scratchpad && focus != floater) { 36 if (floater->scratchpad && focus != floater) {
39 wlr_log(WLR_DEBUG, 37 wlr_log(WLR_DEBUG,
40 "Focusing other scratchpad window (%s) in this workspace", 38 "Focusing other scratchpad window (%s) in this workspace",
41 floater->name); 39 floater->title);
42 root_scratchpad_show(floater); 40 root_scratchpad_show(floater);
43 return; 41 return;
44 } 42 }
@@ -46,25 +44,23 @@ static void scratchpad_toggle_auto(void) {
46 44
47 // Check if there is a visible scratchpad window on another workspace. 45 // Check if there is a visible scratchpad window on another workspace.
48 // In this case we move it to the current workspace. 46 // In this case we move it to the current workspace.
49 for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { 47 for (int i = 0; i < root->scratchpad->length; ++i) {
50 struct sway_container *con = 48 struct sway_container *con = root->scratchpad->items[i];
51 root_container.sway_root->scratchpad->items[i];
52 if (con->parent) { 49 if (con->parent) {
53 wlr_log(WLR_DEBUG, 50 wlr_log(WLR_DEBUG,
54 "Moving a visible scratchpad window (%s) to this workspace", 51 "Moving a visible scratchpad window (%s) to this workspace",
55 con->name); 52 con->title);
56 root_scratchpad_show(con); 53 root_scratchpad_show(con);
57 return; 54 return;
58 } 55 }
59 } 56 }
60 57
61 // Take the container at the bottom of the scratchpad list 58 // Take the container at the bottom of the scratchpad list
62 if (!sway_assert(root_container.sway_root->scratchpad->length, 59 if (!sway_assert(root->scratchpad->length, "Scratchpad is empty")) {
63 "Scratchpad is empty")) {
64 return; 60 return;
65 } 61 }
66 struct sway_container *con = root_container.sway_root->scratchpad->items[0]; 62 struct sway_container *con = root->scratchpad->items[0];
67 wlr_log(WLR_DEBUG, "Showing %s from list", con->name); 63 wlr_log(WLR_DEBUG, "Showing %s from list", con->title);
68 root_scratchpad_show(con); 64 root_scratchpad_show(con);
69} 65}
70 66
@@ -74,7 +70,7 @@ static void scratchpad_toggle_container(struct sway_container *con) {
74 } 70 }
75 71
76 // Check if it matches a currently visible scratchpad window and hide it. 72 // Check if it matches a currently visible scratchpad window and hide it.
77 if (con->parent) { 73 if (con->workspace) {
78 root_scratchpad_hide(con); 74 root_scratchpad_hide(con);
79 return; 75 return;
80 } 76 }
@@ -91,18 +87,18 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) {
91 return cmd_results_new(CMD_INVALID, "scratchpad", 87 return cmd_results_new(CMD_INVALID, "scratchpad",
92 "Expected 'scratchpad show'"); 88 "Expected 'scratchpad show'");
93 } 89 }
94 if (!root_container.sway_root->scratchpad->length) { 90 if (!root->scratchpad->length) {
95 return cmd_results_new(CMD_INVALID, "scratchpad", 91 return cmd_results_new(CMD_INVALID, "scratchpad",
96 "Scratchpad is empty"); 92 "Scratchpad is empty");
97 } 93 }
98 94
99 if (config->handler_context.using_criteria) { 95 if (config->handler_context.using_criteria) {
100 struct sway_container *con = config->handler_context.current_container; 96 struct sway_container *con = config->handler_context.container;
101 97
102 // If the container is in a floating split container, 98 // If the container is in a floating split container,
103 // operate on the split container instead of the child. 99 // operate on the split container instead of the child.
104 if (container_is_floating_or_child(con)) { 100 if (container_is_floating_or_child(con)) {
105 while (con->parent->type != C_WORKSPACE) { 101 while (con->parent) {
106 con = con->parent; 102 con = con->parent;
107 } 103 }
108 } 104 }
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 4d0a22c7..cd6630e0 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -42,8 +42,8 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
43 } 43 }
44 // map absolute coords (0..1,0..1) to root container coords 44 // map absolute coords (0..1,0..1) to root container coords
45 float x = strtof(argv[1], NULL) / root_container.width; 45 float x = strtof(argv[1], NULL) / root->width;
46 float y = strtof(argv[2], NULL) / root_container.height; 46 float y = strtof(argv[2], NULL) / root->height;
47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); 47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);
48 cursor_send_pointer_motion(cursor, 0, true); 48 cursor_send_pointer_motion(cursor, 0, true);
49 } else { 49 } else {
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c
index 1844e917..d501584a 100644
--- a/sway/commands/show_marks.c
+++ b/sway/commands/show_marks.c
@@ -11,8 +11,8 @@
11#include "util.h" 11#include "util.h"
12 12
13static void rebuild_marks_iterator(struct sway_container *con, void *data) { 13static void rebuild_marks_iterator(struct sway_container *con, void *data) {
14 if (con->type == C_VIEW) { 14 if (con->view) {
15 view_update_marks_textures(con->sway_view); 15 view_update_marks_textures(con->view);
16 } 16 }
17} 17}
18 18
@@ -28,9 +28,9 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) {
28 root_for_each_container(rebuild_marks_iterator, NULL); 28 root_for_each_container(rebuild_marks_iterator, NULL);
29 } 29 }
30 30
31 for (int i = 0; i < root_container.children->length; ++i) { 31 for (int i = 0; i < root->outputs->length; ++i) {
32 struct sway_container *con = root_container.children->items[i]; 32 struct sway_output *output = root->outputs->items[i];
33 output_damage_whole(con->sway_output); 33 output_damage_whole(output);
34 } 34 }
35 35
36 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 36 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index 7d27e571..273905df 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
23 "Expected 'smart_gaps <on|off>' "); 23 "Expected 'smart_gaps <on|off>' ");
24 } 24 }
25 25
26 arrange_windows(&root_container); 26 arrange_root();
27 27
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 29}
diff --git a/sway/commands/split.c b/sway/commands/split.c
index a8eddf54..9a53f3d3 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -4,15 +4,21 @@
4#include "sway/tree/arrange.h" 4#include "sway/tree/arrange.h"
5#include "sway/tree/container.h" 5#include "sway/tree/container.h"
6#include "sway/tree/view.h" 6#include "sway/tree/view.h"
7#include "sway/tree/workspace.h"
7#include "sway/input/input-manager.h" 8#include "sway/input/input-manager.h"
8#include "sway/input/seat.h" 9#include "sway/input/seat.h"
9#include "log.h" 10#include "log.h"
10 11
11static struct cmd_results *do_split(int layout) { 12static struct cmd_results *do_split(int layout) {
12 struct sway_container *con = config->handler_context.current_container; 13 struct sway_container *con = config->handler_context.container;
13 struct sway_container *parent = container_split(con, layout); 14 struct sway_workspace *ws = config->handler_context.workspace;
14 container_create_notify(parent); 15 if (con) {
15 arrange_windows(parent->parent); 16 container_split(con, layout);
17 } else {
18 workspace_split(ws, layout);
19 }
20
21 arrange_workspace(ws);
16 22
17 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 23 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
18} 24}
@@ -29,10 +35,9 @@ struct cmd_results *cmd_split(int argc, char **argv) {
29 return do_split(L_HORIZ); 35 return do_split(L_HORIZ);
30 } else if (strcasecmp(argv[0], "t") == 0 || 36 } else if (strcasecmp(argv[0], "t") == 0 ||
31 strcasecmp(argv[0], "toggle") == 0) { 37 strcasecmp(argv[0], "toggle") == 0) {
32 struct sway_container *focused = 38 struct sway_container *focused = config->handler_context.container;
33 config->handler_context.current_container;
34 39
35 if (focused->parent->layout == L_VERT) { 40 if (focused && container_parent_layout(focused) == L_VERT) {
36 return do_split(L_HORIZ); 41 return do_split(L_HORIZ);
37 } else { 42 } else {
38 return do_split(L_VERT); 43 return do_split(L_VERT);
@@ -66,9 +71,9 @@ struct cmd_results *cmd_splitt(int argc, char **argv) {
66 return error; 71 return error;
67 } 72 }
68 73
69 struct sway_container *con = config->handler_context.current_container; 74 struct sway_container *con = config->handler_context.container;
70 75
71 if (con->parent->layout == L_VERT) { 76 if (con && container_parent_layout(con) == L_VERT) {
72 return do_split(L_HORIZ); 77 return do_split(L_HORIZ);
73 } else { 78 } else {
74 return do_split(L_VERT); 79 return do_split(L_VERT);
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
index 8692e08d..7995cdd6 100644
--- a/sway/commands/sticky.c
+++ b/sway/commands/sticky.c
@@ -15,8 +15,7 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
15 if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) { 15 if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
16 return error; 16 return error;
17 } 17 }
18 struct sway_container *container = 18 struct sway_container *container = config->handler_context.container;
19 config->handler_context.current_container;
20 if (!container_is_floating(container)) { 19 if (!container_is_floating(container)) {
21 return cmd_results_new(CMD_FAILURE, "sticky", 20 return cmd_results_new(CMD_FAILURE, "sticky",
22 "Can't set sticky on a tiled container"); 21 "Can't set sticky on a tiled container");
@@ -37,20 +36,16 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
37 container->is_sticky = wants_sticky; 36 container->is_sticky = wants_sticky;
38 37
39 if (wants_sticky) { 38 if (wants_sticky) {
40 // move container to focused workspace 39 // move container to active workspace
41 struct sway_container *output = container_parent(container, C_OUTPUT); 40 struct sway_workspace *active_workspace =
42 struct sway_seat *seat = input_manager_current_seat(input_manager); 41 output_get_active_workspace(container->workspace->output);
43 struct sway_container *focus = seat_get_focus_inactive(seat, output); 42 if (container->workspace != active_workspace) {
44 struct sway_container *focused_workspace = container_parent(focus, C_WORKSPACE); 43 struct sway_workspace *old_workspace = container->workspace;
45 struct sway_container *current_workspace = container_parent(container, C_WORKSPACE); 44 container_detach(container);
46 if (current_workspace != focused_workspace) { 45 workspace_add_floating(active_workspace, container);
47 container_remove_child(container); 46 container_handle_fullscreen_reparent(container);
48 workspace_add_floating(focused_workspace, container); 47 arrange_workspace(active_workspace);
49 container_handle_fullscreen_reparent(container, current_workspace); 48 workspace_consider_destroy(old_workspace);
50 arrange_windows(focused_workspace);
51 if (!container_reap_empty(current_workspace)) {
52 arrange_windows(current_workspace);
53 }
54 } 49 }
55 } 50 }
56 51
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index f25c43a1..a0ffbda8 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -4,6 +4,7 @@
4#include "config.h" 4#include "config.h"
5#include "log.h" 5#include "log.h"
6#include "sway/commands.h" 6#include "sway/commands.h"
7#include "sway/output.h"
7#include "sway/tree/arrange.h" 8#include "sway/tree/arrange.h"
8#include "sway/tree/root.h" 9#include "sway/tree/root.h"
9#include "sway/tree/view.h" 10#include "sway/tree/view.h"
@@ -43,27 +44,28 @@ static void swap_focus(struct sway_container *con1,
43 struct sway_container *con2, struct sway_seat *seat, 44 struct sway_container *con2, struct sway_seat *seat,
44 struct sway_container *focus) { 45 struct sway_container *focus) {
45 if (focus == con1 || focus == con2) { 46 if (focus == con1 || focus == con2) {
46 struct sway_container *ws1 = container_parent(con1, C_WORKSPACE); 47 struct sway_workspace *ws1 = con1->workspace;
47 struct sway_container *ws2 = container_parent(con2, C_WORKSPACE); 48 struct sway_workspace *ws2 = con2->workspace;
48 if (focus == con1 && (con2->parent->layout == L_TABBED 49 enum sway_container_layout layout1 = container_parent_layout(con1);
49 || con2->parent->layout == L_STACKED)) { 50 enum sway_container_layout layout2 = container_parent_layout(con2);
51 if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
50 if (workspace_is_visible(ws2)) { 52 if (workspace_is_visible(ws2)) {
51 seat_set_focus_warp(seat, con2, false, true); 53 seat_set_focus_warp(seat, &con2->node, false, true);
52 } 54 }
53 seat_set_focus(seat, ws1 != ws2 ? con2 : con1); 55 seat_set_focus(seat, ws1 != ws2 ? &con2->node : &con1->node);
54 } else if (focus == con2 && (con1->parent->layout == L_TABBED 56 } else if (focus == con2 && (layout1 == L_TABBED
55 || con1->parent->layout == L_STACKED)) { 57 || layout1 == L_STACKED)) {
56 if (workspace_is_visible(ws1)) { 58 if (workspace_is_visible(ws1)) {
57 seat_set_focus_warp(seat, con1, false, true); 59 seat_set_focus_warp(seat, &con1->node, false, true);
58 } 60 }
59 seat_set_focus(seat, ws1 != ws2 ? con1 : con2); 61 seat_set_focus(seat, ws1 != ws2 ? &con1->node : &con2->node);
60 } else if (ws1 != ws2) { 62 } else if (ws1 != ws2) {
61 seat_set_focus(seat, focus == con1 ? con2 : con1); 63 seat_set_focus(seat, focus == con1 ? &con2->node : &con1->node);
62 } else { 64 } else {
63 seat_set_focus(seat, focus); 65 seat_set_focus(seat, &focus->node);
64 } 66 }
65 } else { 67 } else {
66 seat_set_focus(seat, focus); 68 seat_set_focus(seat, &focus->node);
67 } 69 }
68} 70}
69 71
@@ -72,10 +74,6 @@ static void container_swap(struct sway_container *con1,
72 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { 74 if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
73 return; 75 return;
74 } 76 }
75 if (!sway_assert(con1->type >= C_CONTAINER && con2->type >= C_CONTAINER,
76 "Can only swap containers and views")) {
77 return;
78 }
79 if (!sway_assert(!container_has_ancestor(con1, con2) 77 if (!sway_assert(!container_has_ancestor(con1, con2)
80 && !container_has_ancestor(con2, con1), 78 && !container_has_ancestor(con2, con1),
81 "Cannot swap ancestor and descendant")) { 79 "Cannot swap ancestor and descendant")) {
@@ -87,10 +85,11 @@ static void container_swap(struct sway_container *con1,
87 return; 85 return;
88 } 86 }
89 87
90 wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); 88 wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu",
89 con1->node.id, con2->node.id);
91 90
92 int fs1 = con1->is_fullscreen; 91 bool fs1 = con1->is_fullscreen;
93 int fs2 = con2->is_fullscreen; 92 bool fs2 = con2->is_fullscreen;
94 if (fs1) { 93 if (fs1) {
95 container_set_fullscreen(con1, false); 94 container_set_fullscreen(con1, false);
96 } 95 }
@@ -99,13 +98,11 @@ static void container_swap(struct sway_container *con1,
99 } 98 }
100 99
101 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 100 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
102 struct sway_container *focus = seat_get_focus(seat); 101 struct sway_container *focus = seat_get_focused_container(seat);
103 struct sway_container *vis1 = container_parent( 102 struct sway_workspace *vis1 =
104 seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)), 103 output_get_active_workspace(con1->workspace->output);
105 C_WORKSPACE); 104 struct sway_workspace *vis2 =
106 struct sway_container *vis2 = container_parent( 105 output_get_active_workspace(con2->workspace->output);
107 seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)),
108 C_WORKSPACE);
109 106
110 char *stored_prev_name = NULL; 107 char *stored_prev_name = NULL;
111 if (prev_workspace_name) { 108 if (prev_workspace_name) {
@@ -115,10 +112,10 @@ static void container_swap(struct sway_container *con1,
115 swap_places(con1, con2); 112 swap_places(con1, con2);
116 113
117 if (!workspace_is_visible(vis1)) { 114 if (!workspace_is_visible(vis1)) {
118 seat_set_focus(seat, seat_get_focus_inactive(seat, vis1)); 115 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node));
119 } 116 }
120 if (!workspace_is_visible(vis2)) { 117 if (!workspace_is_visible(vis2)) {
121 seat_set_focus(seat, seat_get_focus_inactive(seat, vis2)); 118 seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node));
122 } 119 }
123 120
124 swap_focus(con1, con2, seat, focus); 121 swap_focus(con1, con2, seat, focus);
@@ -137,23 +134,22 @@ static void container_swap(struct sway_container *con1,
137} 134}
138 135
139static bool test_con_id(struct sway_container *container, void *con_id) { 136static bool test_con_id(struct sway_container *container, void *con_id) {
140 return container->id == (size_t)con_id; 137 return container->node.id == (size_t)con_id;
141} 138}
142 139
143static bool test_id(struct sway_container *container, void *id) { 140static bool test_id(struct sway_container *container, void *id) {
144#ifdef HAVE_XWAYLAND 141#ifdef HAVE_XWAYLAND
145 xcb_window_t *wid = id; 142 xcb_window_t *wid = id;
146 return (container->type == C_VIEW 143 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND
147 && container->sway_view->type == SWAY_VIEW_XWAYLAND 144 && container->view->wlr_xwayland_surface->window_id == *wid);
148 && container->sway_view->wlr_xwayland_surface->window_id == *wid);
149#else 145#else
150 return false; 146 return false;
151#endif 147#endif
152} 148}
153 149
154static bool test_mark(struct sway_container *container, void *mark) { 150static bool test_mark(struct sway_container *container, void *mark) {
155 if (container->type == C_VIEW && container->sway_view->marks->length) { 151 if (container->view && container->view->marks->length) {
156 return !list_seq_find(container->sway_view->marks, 152 return !list_seq_find(container->view->marks,
157 (int (*)(const void *, const void *))strcmp, mark); 153 (int (*)(const void *, const void *))strcmp, mark);
158 } 154 }
159 return false; 155 return false;
@@ -169,7 +165,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
169 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); 165 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
170 } 166 }
171 167
172 struct sway_container *current = config->handler_context.current_container; 168 struct sway_container *current = config->handler_context.container;
173 struct sway_container *other; 169 struct sway_container *other;
174 170
175 char *value = join_args(argv + 3, argc - 3); 171 char *value = join_args(argv + 3, argc - 3);
@@ -191,7 +187,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
191 if (!other) { 187 if (!other) {
192 error = cmd_results_new(CMD_FAILURE, "swap", 188 error = cmd_results_new(CMD_FAILURE, "swap",
193 "Failed to find %s '%s'", argv[2], value); 189 "Failed to find %s '%s'", argv[2], value);
194 } else if (current->type < C_CONTAINER || other->type < C_CONTAINER) { 190 } else if (!current) {
195 error = cmd_results_new(CMD_FAILURE, "swap", 191 error = cmd_results_new(CMD_FAILURE, "swap",
196 "Can only swap with containers and views"); 192 "Can only swap with containers and views");
197 } else if (container_has_ancestor(current, other) 193 } else if (container_has_ancestor(current, other)
@@ -211,9 +207,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
211 207
212 container_swap(current, other); 208 container_swap(current, other);
213 209
214 arrange_windows(current->parent); 210 arrange_node(node_get_parent(&current->node));
215 if (other->parent != current->parent) { 211 if (node_get_parent(&other->node) != node_get_parent(&current->node)) {
216 arrange_windows(other->parent); 212 arrange_node(node_get_parent(&other->node));
217 } 213 }
218 214
219 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 215 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c
index 3d1c578c..c9ffe8fa 100644
--- a/sway/commands/title_format.c
+++ b/sway/commands/title_format.c
@@ -11,13 +11,12 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
11 if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) { 11 if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct sway_container *container = 14 struct sway_container *container = config->handler_context.container;
15 config->handler_context.current_container; 15 if (!container->view) {
16 if (container->type != C_VIEW) {
17 return cmd_results_new(CMD_INVALID, "title_format", 16 return cmd_results_new(CMD_INVALID, "title_format",
18 "Only views can have a title_format"); 17 "Only views can have a title_format");
19 } 18 }
20 struct sway_view *view = container->sway_view; 19 struct sway_view *view = container->view;
21 char *format = join_args(argv, argc); 20 char *format = join_args(argv, argc);
22 if (view->title_format) { 21 if (view->title_format) {
23 free(view->title_format); 22 free(view->title_format);
diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c
index 62127c97..c6251dc8 100644
--- a/sway/commands/unmark.c
+++ b/sway/commands/unmark.c
@@ -9,9 +9,9 @@
9#include "stringop.h" 9#include "stringop.h"
10 10
11static void remove_all_marks_iterator(struct sway_container *con, void *data) { 11static void remove_all_marks_iterator(struct sway_container *con, void *data) {
12 if (con->type == C_VIEW) { 12 if (con->view) {
13 view_clear_marks(con->sway_view); 13 view_clear_marks(con->view);
14 view_update_marks_textures(con->sway_view); 14 view_update_marks_textures(con->view);
15 } 15 }
16} 16}
17 17
@@ -24,13 +24,12 @@ struct cmd_results *cmd_unmark(int argc, char **argv) {
24 // Determine the view 24 // Determine the view
25 struct sway_view *view = NULL; 25 struct sway_view *view = NULL;
26 if (config->handler_context.using_criteria) { 26 if (config->handler_context.using_criteria) {
27 struct sway_container *container = 27 struct sway_container *container = config->handler_context.container;
28 config->handler_context.current_container; 28 if (!container->view) {
29 if (container->type != C_VIEW) {
30 return cmd_results_new(CMD_INVALID, "unmark", 29 return cmd_results_new(CMD_INVALID, "unmark",
31 "Only views can have marks"); 30 "Only views can have marks");
32 } 31 }
33 view = container->sway_view; 32 view = container->view;
34 } 33 }
35 34
36 // Determine the mark 35 // Determine the mark
diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c
index bccb33fe..53c37d4d 100644
--- a/sway/commands/urgent.c
+++ b/sway/commands/urgent.c
@@ -11,13 +11,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) {
11 if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { 11 if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) {
12 return error; 12 return error;
13 } 13 }
14 struct sway_container *container = 14 struct sway_container *container = config->handler_context.container;
15 config->handler_context.current_container; 15 if (!container->view) {
16 if (container->type != C_VIEW) {
17 return cmd_results_new(CMD_INVALID, "urgent", 16 return cmd_results_new(CMD_INVALID, "urgent",
18 "Only views can be urgent"); 17 "Only views can be urgent");
19 } 18 }
20 struct sway_view *view = container->sway_view; 19 struct sway_view *view = container->view;
21 20
22 if (strcmp(argv[0], "allow") == 0) { 21 if (strcmp(argv[0], "allow") == 0) {
23 view->allow_request_urgent = true; 22 view->allow_request_urgent = true;
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index ceb4cd6e..f026a39d 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -58,7 +58,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
58 } 58 }
59 59
60 60
61 struct sway_container *ws = NULL; 61 struct sway_workspace *ws = NULL;
62 if (strcasecmp(argv[0], "number") == 0) { 62 if (strcasecmp(argv[0], "number") == 0) {
63 if (argc < 2) { 63 if (argc < 2) {
64 return cmd_results_new(CMD_INVALID, "workspace", 64 return cmd_results_new(CMD_INVALID, "workspace",
diff --git a/sway/config.c b/sway/config.c
index 8105722a..89701640 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -825,6 +825,6 @@ void config_update_font_height(bool recalculate) {
825 root_for_each_container(find_font_height_iterator, &recalculate); 825 root_for_each_container(find_font_height_iterator, &recalculate);
826 826
827 if (config->font_height != prev_max_height) { 827 if (config->font_height != prev_max_height) {
828 arrange_windows(&root_container); 828 arrange_root();
829 } 829 }
830} 830}
diff --git a/sway/config/bar.c b/sway/config/bar.c
index ae9383d6..f83b37d1 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -12,6 +12,7 @@
12#include <strings.h> 12#include <strings.h>
13#include <signal.h> 13#include <signal.h>
14#include "sway/config.h" 14#include "sway/config.h"
15#include "sway/output.h"
15#include "stringop.h" 16#include "stringop.h"
16#include "list.h" 17#include "list.h"
17#include "log.h" 18#include "log.h"
@@ -218,17 +219,6 @@ void invoke_swaybar(struct bar_config *bar) {
218 close(filedes[1]); 219 close(filedes[1]);
219} 220}
220 221
221static bool active_output(const char *name) {
222 struct sway_container *cont = NULL;
223 for (int i = 0; i < root_container.children->length; ++i) {
224 cont = root_container.children->items[i];
225 if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
226 return true;
227 }
228 }
229 return false;
230}
231
232void load_swaybars() { 222void load_swaybars() {
233 for (int i = 0; i < config->bars->length; ++i) { 223 for (int i = 0; i < config->bars->length; ++i) {
234 struct bar_config *bar = config->bars->items[i]; 224 struct bar_config *bar = config->bars->items[i];
@@ -236,7 +226,7 @@ void load_swaybars() {
236 if (bar->outputs) { 226 if (bar->outputs) {
237 for (int j = 0; j < bar->outputs->length; ++j) { 227 for (int j = 0; j < bar->outputs->length; ++j) {
238 char *o = bar->outputs->items[j]; 228 char *o = bar->outputs->items[j];
239 if (!strcmp(o, "*") || active_output(o)) { 229 if (!strcmp(o, "*") || output_by_name(o)) {
240 apply = true; 230 apply = true;
241 break; 231 break;
242 } 232 }
diff --git a/sway/config/output.c b/sway/config/output.c
index 65f09258..aa53fc46 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -174,21 +174,16 @@ void terminate_swaybg(pid_t pid) {
174 } 174 }
175} 175}
176 176
177void apply_output_config(struct output_config *oc, struct sway_container *output) { 177void apply_output_config(struct output_config *oc, struct sway_output *output) {
178 assert(output->type == C_OUTPUT); 178 struct wlr_output *wlr_output = output->wlr_output;
179
180 struct wlr_output_layout *output_layout =
181 root_container.sway_root->output_layout;
182 struct wlr_output *wlr_output = output->sway_output->wlr_output;
183 179
184 if (oc && oc->enabled == 0) { 180 if (oc && oc->enabled == 0) {
185 if (output->sway_output->bg_pid != 0) { 181 if (output->bg_pid != 0) {
186 terminate_swaybg(output->sway_output->bg_pid); 182 terminate_swaybg(output->bg_pid);
187 output->sway_output->bg_pid = 0; 183 output->bg_pid = 0;
188 } 184 }
189 output_begin_destroy(output); 185 output_disable(output);
190 wlr_output_layout_remove(root_container.sway_root->output_layout, 186 wlr_output_layout_remove(root->output_layout, wlr_output);
191 wlr_output);
192 return; 187 return;
193 } 188 }
194 189
@@ -213,21 +208,21 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
213 // Find position for it 208 // Find position for it
214 if (oc && (oc->x != -1 || oc->y != -1)) { 209 if (oc && (oc->x != -1 || oc->y != -1)) {
215 wlr_log(WLR_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); 210 wlr_log(WLR_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y);
216 wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y); 211 wlr_output_layout_add(root->output_layout, wlr_output, oc->x, oc->y);
217 } else { 212 } else {
218 wlr_output_layout_add_auto(output_layout, wlr_output); 213 wlr_output_layout_add_auto(root->output_layout, wlr_output);
219 } 214 }
220 215
221 int output_i; 216 int output_i;
222 for (output_i = 0; output_i < root_container.children->length; ++output_i) { 217 for (output_i = 0; output_i < root->outputs->length; ++output_i) {
223 if (root_container.children->items[output_i] == output) { 218 if (root->outputs->items[output_i] == output) {
224 break; 219 break;
225 } 220 }
226 } 221 }
227 222
228 if (oc && oc->background) { 223 if (oc && oc->background) {
229 if (output->sway_output->bg_pid != 0) { 224 if (output->bg_pid != 0) {
230 terminate_swaybg(output->sway_output->bg_pid); 225 terminate_swaybg(output->bg_pid);
231 } 226 }
232 227
233 wlr_log(WLR_DEBUG, "Setting background for output %d to %s", 228 wlr_log(WLR_DEBUG, "Setting background for output %d to %s",
@@ -249,8 +244,8 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
249 wlr_log(WLR_DEBUG, "-> %s", command); 244 wlr_log(WLR_DEBUG, "-> %s", command);
250 245
251 char *const cmd[] = { "sh", "-c", command, NULL }; 246 char *const cmd[] = { "sh", "-c", command, NULL };
252 output->sway_output->bg_pid = fork(); 247 output->bg_pid = fork();
253 if (output->sway_output->bg_pid == 0) { 248 if (output->bg_pid == 0) {
254 execvp(cmd[0], cmd); 249 execvp(cmd[0], cmd);
255 } else { 250 } else {
256 free(command); 251 free(command);
@@ -293,12 +288,11 @@ void apply_output_config_to_outputs(struct output_config *oc) {
293 bool wildcard = strcmp(oc->name, "*") == 0; 288 bool wildcard = strcmp(oc->name, "*") == 0;
294 char id[128]; 289 char id[128];
295 struct sway_output *sway_output; 290 struct sway_output *sway_output;
296 wl_list_for_each(sway_output, 291 wl_list_for_each(sway_output, &root->all_outputs, link) {
297 &root_container.sway_root->all_outputs, link) {
298 char *name = sway_output->wlr_output->name; 292 char *name = sway_output->wlr_output->name;
299 output_get_identifier(id, sizeof(id), sway_output); 293 output_get_identifier(id, sizeof(id), sway_output);
300 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { 294 if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
301 if (!sway_output->swayc) { 295 if (!sway_output->enabled) {
302 if (!oc->enabled) { 296 if (!oc->enabled) {
303 if (!wildcard) { 297 if (!wildcard) {
304 break; 298 break;
@@ -306,7 +300,7 @@ void apply_output_config_to_outputs(struct output_config *oc) {
306 continue; 300 continue;
307 } 301 }
308 302
309 output_enable(sway_output); 303 output_enable(sway_output, oc);
310 } 304 }
311 305
312 struct output_config *current = oc; 306 struct output_config *current = oc;
@@ -316,7 +310,7 @@ void apply_output_config_to_outputs(struct output_config *oc) {
316 current = tmp; 310 current = tmp;
317 } 311 }
318 } 312 }
319 apply_output_config(current, sway_output->swayc); 313 apply_output_config(current, sway_output);
320 314
321 if (!wildcard) { 315 if (!wildcard) {
322 // Stop looking if the output config isn't applicable to all 316 // Stop looking if the output config isn't applicable to all
@@ -354,8 +348,7 @@ static void default_output_config(struct output_config *oc,
354 348
355void create_default_output_configs(void) { 349void create_default_output_configs(void) {
356 struct sway_output *sway_output; 350 struct sway_output *sway_output;
357 wl_list_for_each(sway_output, 351 wl_list_for_each(sway_output, &root->all_outputs, link) {
358 &root_container.sway_root->all_outputs, link) {
359 char *name = sway_output->wlr_output->name; 352 char *name = sway_output->wlr_output->name;
360 struct output_config *oc = new_output_config(name); 353 struct output_config *oc = new_output_config(name);
361 default_output_config(oc, sway_output->wlr_output); 354 default_output_config(oc, sway_output->wlr_output);
diff --git a/sway/criteria.c b/sway/criteria.c
index feca904a..e5b308e0 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -9,6 +9,7 @@
9#include "sway/config.h" 9#include "sway/config.h"
10#include "sway/tree/root.h" 10#include "sway/tree/root.h"
11#include "sway/tree/view.h" 11#include "sway/tree/view.h"
12#include "sway/tree/workspace.h"
12#include "stringop.h" 13#include "stringop.h"
13#include "list.h" 14#include "list.h"
14#include "log.h" 15#include "log.h"
@@ -87,12 +88,12 @@ static int cmp_urgent(const void *_a, const void *_b) {
87 return 0; 88 return 0;
88} 89}
89 90
90static void find_urgent_iterator(struct sway_container *swayc, void *data) { 91static void find_urgent_iterator(struct sway_container *con, void *data) {
91 if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { 92 if (!con->view || !view_is_urgent(con->view)) {
92 return; 93 return;
93 } 94 }
94 list_t *urgent_views = data; 95 list_t *urgent_views = data;
95 list_add(urgent_views, swayc->sway_view); 96 list_add(urgent_views, con->view);
96} 97}
97 98
98static bool criteria_matches_view(struct criteria *criteria, 99static bool criteria_matches_view(struct criteria *criteria,
@@ -132,7 +133,7 @@ static bool criteria_matches_view(struct criteria *criteria,
132 } 133 }
133 134
134 if (criteria->con_id) { // Internal ID 135 if (criteria->con_id) { // Internal ID
135 if (!view->swayc || view->swayc->id != criteria->con_id) { 136 if (!view->container || view->container->node.id != criteria->con_id) {
136 return false; 137 return false;
137 } 138 }
138 } 139 }
@@ -174,13 +175,13 @@ static bool criteria_matches_view(struct criteria *criteria,
174#endif 175#endif
175 176
176 if (criteria->floating) { 177 if (criteria->floating) {
177 if (!container_is_floating(view->swayc)) { 178 if (!container_is_floating(view->container)) {
178 return false; 179 return false;
179 } 180 }
180 } 181 }
181 182
182 if (criteria->tiling) { 183 if (criteria->tiling) {
183 if (container_is_floating(view->swayc)) { 184 if (container_is_floating(view->container)) {
184 return false; 185 return false;
185 } 186 }
186 } 187 }
@@ -205,10 +206,7 @@ static bool criteria_matches_view(struct criteria *criteria,
205 } 206 }
206 207
207 if (criteria->workspace) { 208 if (criteria->workspace) {
208 if (!view->swayc) { 209 struct sway_workspace *ws = view->container->workspace;
209 return false;
210 }
211 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
212 if (!ws || strcmp(ws->name, criteria->workspace) != 0) { 210 if (!ws || strcmp(ws->name, criteria->workspace) != 0) {
213 return false; 211 return false;
214 } 212 }
@@ -237,9 +235,9 @@ struct match_data {
237static void criteria_get_views_iterator(struct sway_container *container, 235static void criteria_get_views_iterator(struct sway_container *container,
238 void *data) { 236 void *data) {
239 struct match_data *match_data = data; 237 struct match_data *match_data = data;
240 if (container->type == C_VIEW) { 238 if (!container->view) {
241 if (criteria_matches_view(match_data->criteria, container->sway_view)) { 239 if (criteria_matches_view(match_data->criteria, container->view)) {
242 list_add(match_data->matches, container->sway_view); 240 list_add(match_data->matches, container->view);
243 } 241 }
244 } 242 }
245} 243}
@@ -355,12 +353,12 @@ static enum criteria_token token_from_name(char *name) {
355 */ 353 */
356static char *get_focused_prop(enum criteria_token token) { 354static char *get_focused_prop(enum criteria_token token) {
357 struct sway_seat *seat = input_manager_current_seat(input_manager); 355 struct sway_seat *seat = input_manager_current_seat(input_manager);
358 struct sway_container *focus = seat_get_focus(seat); 356 struct sway_container *focus = seat_get_focused_container(seat);
359 357
360 if (!focus || focus->type != C_VIEW) { 358 if (!focus || !focus->view) {
361 return NULL; 359 return NULL;
362 } 360 }
363 struct sway_view *view = focus->sway_view; 361 struct sway_view *view = focus->view;
364 const char *value = NULL; 362 const char *value = NULL;
365 363
366 switch (token) { 364 switch (token) {
@@ -374,18 +372,15 @@ static char *get_focused_prop(enum criteria_token token) {
374 value = view_get_title(view); 372 value = view_get_title(view);
375 break; 373 break;
376 case T_WORKSPACE: 374 case T_WORKSPACE:
377 { 375 if (focus->workspace) {
378 struct sway_container *ws = container_parent(focus, C_WORKSPACE); 376 value = focus->workspace->name;
379 if (ws) {
380 value = ws->name;
381 }
382 } 377 }
383 break; 378 break;
384 case T_CON_ID: 379 case T_CON_ID:
385 if (view->swayc == NULL) { 380 if (view->container == NULL) {
386 return NULL; 381 return NULL;
387 } 382 }
388 size_t id = view->swayc->id; 383 size_t id = view->container->node.id;
389 size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; 384 size_t id_size = snprintf(NULL, 0, "%zu", id) + 1;
390 char *id_str = malloc(id_size); 385 char *id_str = malloc(id_size);
391 snprintf(id_str, id_size, "%zu", id); 386 snprintf(id_str, id_size, "%zu", id);
diff --git a/sway/debug-tree.c b/sway/debug-tree.c
index 2768cf58..2ed3c087 100644
--- a/sway/debug-tree.c
+++ b/sway/debug-tree.c
@@ -10,6 +10,7 @@
10#include "sway/server.h" 10#include "sway/server.h"
11#include "sway/tree/container.h" 11#include "sway/tree/container.h"
12#include "sway/tree/root.h" 12#include "sway/tree/root.h"
13#include "sway/tree/workspace.h"
13#include "cairo.h" 14#include "cairo.h"
14#include "config.h" 15#include "config.h"
15#include "pango.h" 16#include "pango.h"
@@ -32,28 +33,78 @@ static const char *layout_to_str(enum sway_container_layout layout) {
32 return "L_NONE"; 33 return "L_NONE";
33} 34}
34 35
35static int draw_container(cairo_t *cairo, struct sway_container *container, 36static char *get_string(struct sway_node *node) {
36 struct sway_container *focus, int x, int y) { 37 char *buffer = malloc(512);
38 switch (node->type) {
39 case N_ROOT:
40 snprintf(buffer, 512, "N_ROOT id:%zd %.fx%.f@%.f,%.f", node->id,
41 root->width, root->height, root->x, root->y);
42 break;
43 case N_OUTPUT:
44 snprintf(buffer, 512, "N_OUTPUT id:%zd '%s' %dx%d@%d,%d", node->id,
45 node->sway_output->wlr_output->name,
46 node->sway_output->wlr_output->width,
47 node->sway_output->wlr_output->height,
48 node->sway_output->wlr_output->lx,
49 node->sway_output->wlr_output->ly);
50 break;
51 case N_WORKSPACE:
52 snprintf(buffer, 512, "N_WORKSPACE id:%zd '%s' %s %dx%d@%.f,%.f",
53 node->id, node->sway_workspace->name,
54 layout_to_str(node->sway_workspace->layout),
55 node->sway_workspace->width, node->sway_workspace->height,
56 node->sway_workspace->x, node->sway_workspace->y);
57 break;
58 case N_CONTAINER:
59 snprintf(buffer, 512, "N_CONTAINER id:%zd '%s' %s %.fx%.f@%.f,%.f",
60 node->id, node->sway_container->title,
61 layout_to_str(node->sway_container->layout),
62 node->sway_container->width, node->sway_container->height,
63 node->sway_container->x, node->sway_container->y);
64 break;
65 }
66 return buffer;
67}
68
69static list_t *get_children(struct sway_node *node) {
70 switch (node->type) {
71 case N_ROOT:
72 return root->outputs;
73 case N_OUTPUT:
74 return node->sway_output->workspaces;
75 case N_WORKSPACE:
76 return node->sway_workspace->tiling;
77 case N_CONTAINER:
78 return node->sway_container->children;
79 }
80 return NULL;
81}
82
83static int draw_node(cairo_t *cairo, struct sway_node *node,
84 struct sway_node *focus, int x, int y) {
37 int text_width, text_height; 85 int text_width, text_height;
86 char *buffer = get_string(node);
38 get_text_size(cairo, "monospace", &text_width, &text_height, 87 get_text_size(cairo, "monospace", &text_width, &text_height,
39 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", 88 1, false, buffer);
40 container_type_to_str(container->type), container->id, container->name,
41 layout_to_str(container->layout),
42 container->width, container->height, container->x, container->y);
43 cairo_save(cairo); 89 cairo_save(cairo);
44 cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height); 90 cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height);
45 cairo_set_source_u32(cairo, 0xFFFFFFE0); 91 cairo_set_source_u32(cairo, 0xFFFFFFE0);
46 cairo_fill(cairo); 92 cairo_fill(cairo);
47 int height = text_height; 93 int height = text_height;
48 if (container->children) { 94 list_t *children = get_children(node);
49 for (int i = 0; i < container->children->length; ++i) { 95 if (children) {
50 struct sway_container *child = container->children->items[i]; 96 for (int i = 0; i < children->length; ++i) {
51 if (child->parent == container) { 97 // This is really dirty - the list contains specific structs but
98 // we're casting them as nodes. This works because node is the first
99 // item in each specific struct. This is acceptable because this is
100 // debug code.
101 struct sway_node *child = children->items[i];
102 if (node_get_parent(child) == node) {
52 cairo_set_source_u32(cairo, 0x000000FF); 103 cairo_set_source_u32(cairo, 0x000000FF);
53 } else { 104 } else {
54 cairo_set_source_u32(cairo, 0xFF0000FF); 105 cairo_set_source_u32(cairo, 0xFF0000FF);
55 } 106 }
56 height += draw_container(cairo, child, focus, x + 10, y + height); 107 height += draw_node(cairo, child, focus, x + 10, y + height);
57 } 108 }
58 } 109 }
59 cairo_set_source_u32(cairo, 0xFFFFFFE0); 110 cairo_set_source_u32(cairo, 0xFFFFFFE0);
@@ -61,13 +112,11 @@ static int draw_container(cairo_t *cairo, struct sway_container *container,
61 cairo_fill(cairo); 112 cairo_fill(cairo);
62 cairo_restore(cairo); 113 cairo_restore(cairo);
63 cairo_move_to(cairo, x, y); 114 cairo_move_to(cairo, x, y);
64 if (focus == container) { 115 if (focus == node) {
65 cairo_set_source_u32(cairo, 0x0000FFFF); 116 cairo_set_source_u32(cairo, 0x0000FFFF);
66 } 117 }
67 pango_printf(cairo, "monospace", 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", 118 pango_printf(cairo, "monospace", 1, false, buffer);
68 container_type_to_str(container->type), container->id, container->name, 119 free(buffer);
69 layout_to_str(container->layout),
70 container->width, container->height, container->x, container->y);
71 return height; 120 return height;
72} 121}
73 122
@@ -77,13 +126,13 @@ void update_debug_tree() {
77 } 126 }
78 127
79 int width = 640, height = 480; 128 int width = 640, height = 480;
80 for (int i = 0; i < root_container.children->length; ++i) { 129 for (int i = 0; i < root->outputs->length; ++i) {
81 struct sway_container *container = root_container.children->items[i]; 130 struct sway_output *output = root->outputs->items[i];
82 if (container->width > width) { 131 if (output->wlr_output->width > width) {
83 width = container->width; 132 width = output->wlr_output->width;
84 } 133 }
85 if (container->height > height) { 134 if (output->wlr_output->height > height) {
86 height = container->height; 135 height = output->wlr_output->height;
87 } 136 }
88 } 137 }
89 cairo_surface_t *surface = 138 cairo_surface_t *surface =
@@ -91,28 +140,22 @@ void update_debug_tree() {
91 cairo_t *cairo = cairo_create(surface); 140 cairo_t *cairo = cairo_create(surface);
92 PangoContext *pango = pango_cairo_create_context(cairo); 141 PangoContext *pango = pango_cairo_create_context(cairo);
93 142
94 struct sway_seat *seat = NULL; 143 struct sway_seat *seat = input_manager_current_seat(input_manager);
95 wl_list_for_each(seat, &input_manager->seats, link) { 144 struct sway_node *focus = seat_get_focus(seat);
96 break;
97 }
98 145
99 struct sway_container *focus = NULL;
100 if (seat != NULL) {
101 focus = seat_get_focus(seat);
102 }
103 cairo_set_source_u32(cairo, 0x000000FF); 146 cairo_set_source_u32(cairo, 0x000000FF);
104 draw_container(cairo, &root_container, focus, 0, 0); 147 draw_node(cairo, &root->node, focus, 0, 0);
105 148
106 cairo_surface_flush(surface); 149 cairo_surface_flush(surface);
107 struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); 150 struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend);
108 if (root_container.sway_root->debug_tree) { 151 if (root->debug_tree) {
109 wlr_texture_destroy(root_container.sway_root->debug_tree); 152 wlr_texture_destroy(root->debug_tree);
110 } 153 }
111 unsigned char *data = cairo_image_surface_get_data(surface); 154 unsigned char *data = cairo_image_surface_get_data(surface);
112 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); 155 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
113 struct wlr_texture *texture = wlr_texture_from_pixels(renderer, 156 struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
114 WL_SHM_FORMAT_ARGB8888, stride, width, height, data); 157 WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
115 root_container.sway_root->debug_tree = texture; 158 root->debug_tree = texture;
116 cairo_surface_destroy(surface); 159 cairo_surface_destroy(surface);
117 g_object_unref(pango); 160 g_object_unref(pango);
118 cairo_destroy(cairo); 161 cairo_destroy(cairo);
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
index 72650397..771b58fe 100644
--- a/sway/desktop/desktop.c
+++ b/sway/desktop/desktop.c
@@ -4,37 +4,32 @@
4 4
5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, 5void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
6 bool whole) { 6 bool whole) {
7 for (int i = 0; i < root_container.children->length; ++i) { 7 for (int i = 0; i < root->outputs->length; ++i) {
8 struct sway_container *cont = root_container.children->items[i]; 8 struct sway_output *output = root->outputs->items[i];
9 if (cont->type == C_OUTPUT) { 9 output_damage_surface(output, lx - output->wlr_output->lx,
10 output_damage_surface(cont->sway_output, 10 ly - output->wlr_output->ly, surface, whole);
11 lx - cont->current.swayc_x, ly - cont->current.swayc_y,
12 surface, whole);
13 }
14 } 11 }
15} 12}
16 13
17void desktop_damage_whole_container(struct sway_container *con) { 14void desktop_damage_whole_container(struct sway_container *con) {
18 for (int i = 0; i < root_container.children->length; ++i) { 15 for (int i = 0; i < root->outputs->length; ++i) {
19 struct sway_container *cont = root_container.children->items[i]; 16 struct sway_output *output = root->outputs->items[i];
20 if (cont->type == C_OUTPUT) { 17 output_damage_whole_container(output, con);
21 output_damage_whole_container(cont->sway_output, con);
22 }
23 } 18 }
24} 19}
25 20
26void desktop_damage_box(struct wlr_box *box) { 21void desktop_damage_box(struct wlr_box *box) {
27 for (int i = 0; i < root_container.children->length; ++i) { 22 for (int i = 0; i < root->outputs->length; ++i) {
28 struct sway_container *cont = root_container.children->items[i]; 23 struct sway_output *output = root->outputs->items[i];
29 output_damage_box(cont->sway_output, box); 24 output_damage_box(output, box);
30 } 25 }
31} 26}
32 27
33void desktop_damage_view(struct sway_view *view) { 28void desktop_damage_view(struct sway_view *view) {
34 desktop_damage_whole_container(view->swayc); 29 desktop_damage_whole_container(view->container);
35 struct wlr_box box = { 30 struct wlr_box box = {
36 .x = view->swayc->current.view_x - view->geometry.x, 31 .x = view->container->current.view_x - view->geometry.x,
37 .y = view->swayc->current.view_y - view->geometry.y, 32 .y = view->container->current.view_y - view->geometry.y,
38 .width = view->surface->current.width, 33 .width = view->surface->current.width,
39 .height = view->surface->current.height, 34 .height = view->surface->current.height,
40 }; 35 };
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index a4f7f928..7d254173 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -14,6 +14,7 @@
14#include "sway/output.h" 14#include "sway/output.h"
15#include "sway/server.h" 15#include "sway/server.h"
16#include "sway/tree/arrange.h" 16#include "sway/tree/arrange.h"
17#include "sway/tree/workspace.h"
17#include "log.h" 18#include "log.h"
18 19
19static void apply_exclusive(struct wlr_box *usable_area, 20static void apply_exclusive(struct wlr_box *usable_area,
@@ -176,7 +177,7 @@ void arrange_layers(struct sway_output *output) {
176 sizeof(struct wlr_box)) != 0) { 177 sizeof(struct wlr_box)) != 0) {
177 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); 178 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
178 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 179 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
179 arrange_output(output->swayc); 180 arrange_output(output);
180 } 181 }
181 182
182 // Arrange non-exlusive surfaces from top->bottom 183 // Arrange non-exlusive surfaces from top->bottom
@@ -256,7 +257,7 @@ static void unmap(struct sway_layer_surface *sway_layer) {
256 return; 257 return;
257 } 258 }
258 struct sway_output *output = wlr_output->data; 259 struct sway_output *output = wlr_output->data;
259 if (output == NULL || output->swayc == NULL) { 260 if (output == NULL) {
260 return; 261 return;
261 } 262 }
262 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, 263 output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
@@ -283,9 +284,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
283 wl_list_remove(&sway_layer->surface_commit.link); 284 wl_list_remove(&sway_layer->surface_commit.link);
284 if (sway_layer->layer_surface->output != NULL) { 285 if (sway_layer->layer_surface->output != NULL) {
285 struct sway_output *output = sway_layer->layer_surface->output->data; 286 struct sway_output *output = sway_layer->layer_surface->output->data;
286 if (output != NULL && output->swayc != NULL) { 287 if (output != NULL) {
287 arrange_layers(output); 288 arrange_layers(output);
288 arrange_windows(output->swayc); 289 arrange_output(output);
289 transaction_commit_dirty(); 290 transaction_commit_dirty();
290 } 291 }
291 wl_list_remove(&sway_layer->output_destroy.link); 292 wl_list_remove(&sway_layer->output_destroy.link);
@@ -332,23 +333,21 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
332 333
333 if (!layer_surface->output) { 334 if (!layer_surface->output) {
334 // Assign last active output 335 // Assign last active output
335 struct sway_container *output = NULL; 336 struct sway_output *output = NULL;
336 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 337 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
337 if (seat) { 338 if (seat) {
338 output = seat_get_focus_inactive(seat, &root_container); 339 struct sway_workspace *ws = seat_get_focused_workspace(seat);
340 output = ws->output;
339 } 341 }
340 if (!output) { 342 if (!output) {
341 if (!sway_assert(root_container.children->length, 343 if (!sway_assert(root->outputs->length,
342 "cannot auto-assign output for layer")) { 344 "cannot auto-assign output for layer")) {
343 wlr_layer_surface_close(layer_surface); 345 wlr_layer_surface_close(layer_surface);
344 return; 346 return;
345 } 347 }
346 output = root_container.children->items[0]; 348 output = root->outputs->items[0];
347 } 349 }
348 if (output->type != C_OUTPUT) { 350 layer_surface->output = output->wlr_output;
349 output = container_parent(output, C_OUTPUT);
350 }
351 layer_surface->output = output->sway_output->wlr_output;
352 } 351 }
353 352
354 struct sway_layer_surface *sway_layer = 353 struct sway_layer_surface *sway_layer =
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index c30e52a1..c182bad6 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -28,10 +28,10 @@
28#include "sway/tree/view.h" 28#include "sway/tree/view.h"
29#include "sway/tree/workspace.h" 29#include "sway/tree/workspace.h"
30 30
31struct sway_container *output_by_name(const char *name) { 31struct sway_output *output_by_name(const char *name) {
32 for (int i = 0; i < root_container.children->length; ++i) { 32 for (int i = 0; i < root->outputs->length; ++i) {
33 struct sway_container *output = root_container.children->items[i]; 33 struct sway_output *output = root->outputs->items[i];
34 if (strcasecmp(output->name, name) == 0) { 34 if (strcasecmp(output->wlr_output->name, name) == 0) {
35 return output; 35 return output;
36 } 36 }
37 } 37 }
@@ -98,8 +98,8 @@ static bool get_surface_box(struct surface_iterator_data *data,
98 wlr_box_rotated_bounds(&box, data->rotation, &rotated_box); 98 wlr_box_rotated_bounds(&box, data->rotation, &rotated_box);
99 99
100 struct wlr_box output_box = { 100 struct wlr_box output_box = {
101 .width = output->swayc->current.swayc_width, 101 .width = output->wlr_output->width,
102 .height = output->swayc->current.swayc_height, 102 .height = output->wlr_output->height,
103 }; 103 };
104 104
105 struct wlr_box intersection; 105 struct wlr_box intersection;
@@ -145,12 +145,12 @@ void output_view_for_each_surface(struct sway_output *output,
145 .user_iterator = iterator, 145 .user_iterator = iterator,
146 .user_data = user_data, 146 .user_data = user_data,
147 .output = output, 147 .output = output,
148 .ox = view->swayc->current.view_x - output->swayc->current.swayc_x 148 .ox = view->container->current.view_x - output->wlr_output->lx
149 - view->geometry.x, 149 - view->geometry.x,
150 .oy = view->swayc->current.view_y - output->swayc->current.swayc_y 150 .oy = view->container->current.view_y - output->wlr_output->ly
151 - view->geometry.y, 151 - view->geometry.y,
152 .width = view->swayc->current.view_width, 152 .width = view->container->current.view_width,
153 .height = view->swayc->current.view_height, 153 .height = view->container->current.view_height,
154 .rotation = 0, // TODO 154 .rotation = 0, // TODO
155 }; 155 };
156 156
@@ -164,12 +164,12 @@ void output_view_for_each_popup(struct sway_output *output,
164 .user_iterator = iterator, 164 .user_iterator = iterator,
165 .user_data = user_data, 165 .user_data = user_data,
166 .output = output, 166 .output = output,
167 .ox = view->swayc->current.view_x - output->swayc->current.swayc_x 167 .ox = view->container->current.view_x - output->wlr_output->lx
168 - view->geometry.x, 168 - view->geometry.x,
169 .oy = view->swayc->current.view_y - output->swayc->current.swayc_y 169 .oy = view->container->current.view_y - output->wlr_output->ly
170 - view->geometry.y, 170 - view->geometry.y,
171 .width = view->swayc->current.view_width, 171 .width = view->container->current.view_width,
172 .height = view->swayc->current.view_height, 172 .height = view->container->current.view_height,
173 .rotation = 0, // TODO 173 .rotation = 0, // TODO
174 }; 174 };
175 175
@@ -197,8 +197,8 @@ void output_unmanaged_for_each_surface(struct sway_output *output,
197 wl_list_for_each(unmanaged_surface, unmanaged, link) { 197 wl_list_for_each(unmanaged_surface, unmanaged, link) {
198 struct wlr_xwayland_surface *xsurface = 198 struct wlr_xwayland_surface *xsurface =
199 unmanaged_surface->wlr_xwayland_surface; 199 unmanaged_surface->wlr_xwayland_surface;
200 double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; 200 double ox = unmanaged_surface->lx - output->wlr_output->lx;
201 double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; 201 double oy = unmanaged_surface->ly - output->wlr_output->ly;
202 202
203 output_surface_for_each_surface(output, xsurface->surface, ox, oy, 203 output_surface_for_each_surface(output, xsurface->surface, ox, oy,
204 iterator, user_data); 204 iterator, user_data);
@@ -211,8 +211,8 @@ void output_drag_icons_for_each_surface(struct sway_output *output,
211 void *user_data) { 211 void *user_data) {
212 struct sway_drag_icon *drag_icon; 212 struct sway_drag_icon *drag_icon;
213 wl_list_for_each(drag_icon, drag_icons, link) { 213 wl_list_for_each(drag_icon, drag_icons, link) {
214 double ox = drag_icon->x - output->swayc->x; 214 double ox = drag_icon->x - output->wlr_output->lx;
215 double oy = drag_icon->y - output->swayc->y; 215 double oy = drag_icon->y - output->wlr_output->ly;
216 216
217 if (drag_icon->wlr_drag_icon->mapped) { 217 if (drag_icon->wlr_drag_icon->mapped) {
218 output_surface_for_each_surface(output, 218 output_surface_for_each_surface(output,
@@ -229,19 +229,13 @@ static void scale_box(struct wlr_box *box, float scale) {
229 box->height *= scale; 229 box->height *= scale;
230} 230}
231 231
232struct sway_container *output_get_active_workspace(struct sway_output *output) { 232struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
233 struct sway_seat *seat = input_manager_current_seat(input_manager); 233 struct sway_seat *seat = input_manager_current_seat(input_manager);
234 struct sway_container *focus = 234 struct sway_node *focus = seat_get_active_child(seat, &output->node);
235 seat_get_focus_inactive(seat, output->swayc);
236 if (!focus) { 235 if (!focus) {
237 // We've never been to this output before 236 return output->workspaces->items[0];
238 focus = output->swayc->current.children->items[0];
239 } 237 }
240 struct sway_container *workspace = focus; 238 return focus->sway_workspace;
241 if (workspace->type != C_WORKSPACE) {
242 workspace = container_parent(workspace, C_WORKSPACE);
243 }
244 return workspace;
245} 239}
246 240
247bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { 241bool output_has_opaque_overlay_layer_surface(struct sway_output *output) {
@@ -255,8 +249,8 @@ bool output_has_opaque_overlay_layer_surface(struct sway_output *output) {
255 struct sway_layer_surface *sway_layer_surface = 249 struct sway_layer_surface *sway_layer_surface =
256 layer_from_wlr_layer_surface(wlr_layer_surface); 250 layer_from_wlr_layer_surface(wlr_layer_surface);
257 pixman_box32_t output_box = { 251 pixman_box32_t output_box = {
258 .x2 = output->swayc->current.swayc_width, 252 .x2 = output->wlr_output->width,
259 .y2 = output->swayc->current.swayc_height, 253 .y2 = output->wlr_output->height,
260 }; 254 };
261 pixman_region32_t surface_opaque_box; 255 pixman_region32_t surface_opaque_box;
262 pixman_region32_init(&surface_opaque_box); 256 pixman_region32_init(&surface_opaque_box);
@@ -307,15 +301,15 @@ struct send_frame_done_data {
307 301
308static void send_frame_done_container_iterator(struct sway_container *con, 302static void send_frame_done_container_iterator(struct sway_container *con,
309 void *_data) { 303 void *_data) {
310 if (con->type != C_VIEW) { 304 if (!con->view) {
311 return; 305 return;
312 } 306 }
313 if (!view_is_visible(con->sway_view)) { 307 if (!view_is_visible(con->view)) {
314 return; 308 return;
315 } 309 }
316 310
317 struct send_frame_done_data *data = _data; 311 struct send_frame_done_data *data = _data;
318 output_view_for_each_surface(data->output, con->sway_view, 312 output_view_for_each_surface(data->output, con->view,
319 send_frame_done_iterator, data->when); 313 send_frame_done_iterator, data->when);
320} 314}
321 315
@@ -328,15 +322,14 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
328 .output = output, 322 .output = output,
329 .when = when, 323 .when = when,
330 }; 324 };
331 struct sway_container *workspace = output_get_active_workspace(output); 325 struct sway_workspace *workspace = output_get_active_workspace(output);
332 if (workspace->current.ws_fullscreen) { 326 if (workspace->current.fullscreen) {
333 send_frame_done_container_iterator( 327 send_frame_done_container_iterator(
334 workspace->current.ws_fullscreen, &data); 328 workspace->current.fullscreen, &data);
335 container_for_each_child(workspace->current.ws_fullscreen, 329 container_for_each_child(workspace->current.fullscreen,
336 send_frame_done_container_iterator, &data); 330 send_frame_done_container_iterator, &data);
337#ifdef HAVE_XWAYLAND 331#ifdef HAVE_XWAYLAND
338 send_frame_done_unmanaged(output, 332 send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when);
339 &root_container.sway_root->xwayland_unmanaged, when);
340#endif 333#endif
341 } else { 334 } else {
342 send_frame_done_layer(output, 335 send_frame_done_layer(output,
@@ -348,8 +341,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
348 send_frame_done_container_iterator, &data); 341 send_frame_done_container_iterator, &data);
349 342
350#ifdef HAVE_XWAYLAND 343#ifdef HAVE_XWAYLAND
351 send_frame_done_unmanaged(output, 344 send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when);
352 &root_container.sway_root->xwayland_unmanaged, when);
353#endif 345#endif
354 send_frame_done_layer(output, 346 send_frame_done_layer(output,
355 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when); 347 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when);
@@ -358,8 +350,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
358send_frame_overlay: 350send_frame_overlay:
359 send_frame_done_layer(output, 351 send_frame_done_layer(output,
360 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when); 352 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when);
361 send_frame_done_drag_icons(output, &root_container.sway_root->drag_icons, 353 send_frame_done_drag_icons(output, &root->drag_icons, when);
362 when);
363} 354}
364 355
365static void damage_handle_frame(struct wl_listener *listener, void *data) { 356static void damage_handle_frame(struct wl_listener *listener, void *data) {
@@ -391,7 +382,11 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
391} 382}
392 383
393void output_damage_whole(struct sway_output *output) { 384void output_damage_whole(struct sway_output *output) {
394 wlr_output_damage_add_whole(output->damage); 385 // The output can exist with no wlr_output if it's just been disconnected
386 // and the transaction to evacuate it has't completed yet.
387 if (output && output->wlr_output) {
388 wlr_output_damage_add_whole(output->damage);
389 }
395} 390}
396 391
397static void damage_surface_iterator(struct sway_output *output, 392static void damage_surface_iterator(struct sway_output *output,
@@ -446,14 +441,9 @@ void output_damage_surface(struct sway_output *output, double ox, double oy,
446 441
447static void output_damage_view(struct sway_output *output, 442static void output_damage_view(struct sway_output *output,
448 struct sway_view *view, bool whole) { 443 struct sway_view *view, bool whole) {
449 if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) {
450 return;
451 }
452
453 if (!view_is_visible(view)) { 444 if (!view_is_visible(view)) {
454 return; 445 return;
455 } 446 }
456
457 output_view_for_each_surface(output, view, damage_surface_iterator, &whole); 447 output_view_for_each_surface(output, view, damage_surface_iterator, &whole);
458} 448}
459 449
@@ -466,31 +456,29 @@ void output_damage_from_view(struct sway_output *output,
466void output_damage_box(struct sway_output *output, struct wlr_box *_box) { 456void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
467 struct wlr_box box; 457 struct wlr_box box;
468 memcpy(&box, _box, sizeof(struct wlr_box)); 458 memcpy(&box, _box, sizeof(struct wlr_box));
469 box.x -= output->swayc->current.swayc_x; 459 box.x -= output->wlr_output->lx;
470 box.y -= output->swayc->current.swayc_y; 460 box.y -= output->wlr_output->ly;
471 scale_box(&box, output->wlr_output->scale); 461 scale_box(&box, output->wlr_output->scale);
472 wlr_output_damage_add_box(output->damage, &box); 462 wlr_output_damage_add_box(output->damage, &box);
473} 463}
474 464
475static void output_damage_whole_container_iterator(struct sway_container *con, 465static void output_damage_whole_container_iterator(struct sway_container *con,
476 void *data) { 466 void *data) {
477 struct sway_output *output = data; 467 if (!sway_assert(con->view, "expected a view")) {
478
479 if (!sway_assert(con->type == C_VIEW, "expected a view")) {
480 return; 468 return;
481 } 469 }
482 470 struct sway_output *output = data;
483 output_damage_view(output, con->sway_view, true); 471 output_damage_view(output, con->view, true);
484} 472}
485 473
486void output_damage_whole_container(struct sway_output *output, 474void output_damage_whole_container(struct sway_output *output,
487 struct sway_container *con) { 475 struct sway_container *con) {
488 // Pad the box by 1px, because the width is a double and might be a fraction 476 // Pad the box by 1px, because the width is a double and might be a fraction
489 struct wlr_box box = { 477 struct wlr_box box = {
490 .x = con->current.swayc_x - output->wlr_output->lx - 1, 478 .x = con->current.con_x - output->wlr_output->lx - 1,
491 .y = con->current.swayc_y - output->wlr_output->ly - 1, 479 .y = con->current.con_y - output->wlr_output->ly - 1,
492 .width = con->current.swayc_width + 2, 480 .width = con->current.con_width + 2,
493 .height = con->current.swayc_height + 2, 481 .height = con->current.con_height + 2,
494 }; 482 };
495 scale_box(&box, output->wlr_output->scale); 483 scale_box(&box, output->wlr_output->scale);
496 wlr_output_damage_add_box(output->damage, &box); 484 wlr_output_damage_add_box(output->damage, &box);
@@ -499,44 +487,48 @@ void output_damage_whole_container(struct sway_output *output,
499static void damage_handle_destroy(struct wl_listener *listener, void *data) { 487static void damage_handle_destroy(struct wl_listener *listener, void *data) {
500 struct sway_output *output = 488 struct sway_output *output =
501 wl_container_of(listener, output, damage_destroy); 489 wl_container_of(listener, output, damage_destroy);
502 output_begin_destroy(output->swayc); 490 output_disable(output);
491 transaction_commit_dirty();
503} 492}
504 493
505static void handle_destroy(struct wl_listener *listener, void *data) { 494static void handle_destroy(struct wl_listener *listener, void *data) {
506 struct sway_output *output = wl_container_of(listener, output, destroy); 495 struct sway_output *output = wl_container_of(listener, output, destroy);
507 wl_signal_emit(&output->events.destroy, output); 496 wl_signal_emit(&output->events.destroy, output);
508 497
509 if (output->swayc) { 498 if (output->enabled) {
510 output_begin_destroy(output->swayc); 499 output_disable(output);
511 } 500 }
501 output_begin_destroy(output);
512 502
513 wl_list_remove(&output->link); 503 transaction_commit_dirty();
514 wl_list_remove(&output->destroy.link);
515 output->wlr_output->data = NULL;
516 free(output);
517
518 arrange_windows(&root_container);
519} 504}
520 505
521static void handle_mode(struct wl_listener *listener, void *data) { 506static void handle_mode(struct wl_listener *listener, void *data) {
522 struct sway_output *output = wl_container_of(listener, output, mode); 507 struct sway_output *output = wl_container_of(listener, output, mode);
523 arrange_layers(output); 508 arrange_layers(output);
524 arrange_windows(output->swayc); 509 arrange_output(output);
525 transaction_commit_dirty(); 510 transaction_commit_dirty();
526} 511}
527 512
528static void handle_transform(struct wl_listener *listener, void *data) { 513static void handle_transform(struct wl_listener *listener, void *data) {
529 struct sway_output *output = wl_container_of(listener, output, transform); 514 struct sway_output *output = wl_container_of(listener, output, transform);
530 arrange_layers(output); 515 arrange_layers(output);
531 arrange_windows(output->swayc); 516 arrange_output(output);
532 transaction_commit_dirty(); 517 transaction_commit_dirty();
533} 518}
534 519
520static void update_textures(struct sway_container *con, void *data) {
521 container_update_title_textures(con);
522 if (con->view) {
523 view_update_marks_textures(con->view);
524 }
525}
526
535static void handle_scale(struct wl_listener *listener, void *data) { 527static void handle_scale(struct wl_listener *listener, void *data) {
536 struct sway_output *output = wl_container_of(listener, output, scale); 528 struct sway_output *output = wl_container_of(listener, output, scale);
537 arrange_layers(output); 529 arrange_layers(output);
538 container_update_textures_recursive(output->swayc); 530 output_for_each_container(output, update_textures, NULL);
539 arrange_windows(output->swayc); 531 arrange_output(output);
540 transaction_commit_dirty(); 532 transaction_commit_dirty();
541} 533}
542 534
@@ -545,57 +537,27 @@ void handle_new_output(struct wl_listener *listener, void *data) {
545 struct wlr_output *wlr_output = data; 537 struct wlr_output *wlr_output = data;
546 wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 538 wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
547 539
548 struct sway_output *output = calloc(1, sizeof(struct sway_output)); 540 struct sway_output *output = output_create(wlr_output);
549 if (!output) { 541 if (!output) {
550 return; 542 return;
551 } 543 }
552 output->wlr_output = wlr_output;
553 wlr_output->data = output;
554 output->server = server; 544 output->server = server;
555 output->damage = wlr_output_damage_create(wlr_output); 545 output->damage = wlr_output_damage_create(wlr_output);
556
557 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
558 output->destroy.notify = handle_destroy; 546 output->destroy.notify = handle_destroy;
559 547
560 wl_list_insert(&root_container.sway_root->all_outputs, &output->link); 548 struct output_config *oc = output_find_config(output);
561
562 output_enable(output);
563}
564
565void output_enable(struct sway_output *output) {
566 struct wlr_output *wlr_output = output->wlr_output;
567
568 if (!sway_assert(output->swayc == NULL, "output is already enabled")) {
569 return;
570 }
571
572 output->swayc = output_create(output);
573 if (!output->swayc) {
574 // Output is disabled
575 return;
576 }
577 549
578 size_t len = sizeof(output->layers) / sizeof(output->layers[0]); 550 if (oc && oc->enabled) {
579 for (size_t i = 0; i < len; ++i) { 551 output_enable(output, oc);
580 wl_list_init(&output->layers[i]);
581 } 552 }
582 wl_signal_init(&output->events.destroy);
583 553
584 input_manager_configure_xcursor(input_manager); 554 transaction_commit_dirty();
555}
585 556
586 wl_signal_add(&wlr_output->events.mode, &output->mode); 557void output_add_listeners(struct sway_output *output) {
587 output->mode.notify = handle_mode; 558 output->mode.notify = handle_mode;
588 wl_signal_add(&wlr_output->events.transform, &output->transform);
589 output->transform.notify = handle_transform; 559 output->transform.notify = handle_transform;
590 wl_signal_add(&wlr_output->events.scale, &output->scale);
591 output->scale.notify = handle_scale; 560 output->scale.notify = handle_scale;
592
593 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
594 output->damage_frame.notify = damage_handle_frame; 561 output->damage_frame.notify = damage_handle_frame;
595 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
596 output->damage_destroy.notify = damage_handle_destroy; 562 output->damage_destroy.notify = damage_handle_destroy;
597
598 arrange_layers(output);
599 arrange_windows(&root_container);
600 transaction_commit_dirty();
601} 563}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 695213eb..99b2cf3d 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -193,10 +193,10 @@ static void render_view_toplevels(struct sway_view *view,
193 .alpha = alpha, 193 .alpha = alpha,
194 }; 194 };
195 // Render all toplevels without descending into popups 195 // Render all toplevels without descending into popups
196 double ox = 196 double ox = view->container->current.view_x -
197 view->swayc->current.view_x - output->wlr_output->lx - view->geometry.x; 197 output->wlr_output->lx - view->geometry.x;
198 double oy = 198 double oy = view->container->current.view_y -
199 view->swayc->current.view_y - output->wlr_output->ly - view->geometry.y; 199 output->wlr_output->ly - view->geometry.y;
200 output_surface_for_each_surface(output, view->surface, ox, oy, 200 output_surface_for_each_surface(output, view->surface, ox, oy,
201 render_surface_iterator, &data); 201 render_surface_iterator, &data);
202} 202}
@@ -229,17 +229,17 @@ static void render_saved_view(struct sway_view *view,
229 return; 229 return;
230 } 230 }
231 struct wlr_box box = { 231 struct wlr_box box = {
232 .x = view->swayc->current.view_x - output->swayc->current.swayc_x - 232 .x = view->container->current.view_x - output->wlr_output->lx -
233 view->saved_geometry.x, 233 view->saved_geometry.x,
234 .y = view->swayc->current.view_y - output->swayc->current.swayc_y - 234 .y = view->container->current.view_y - output->wlr_output->ly -
235 view->saved_geometry.y, 235 view->saved_geometry.y,
236 .width = view->saved_buffer_width, 236 .width = view->saved_buffer_width,
237 .height = view->saved_buffer_height, 237 .height = view->saved_buffer_height,
238 }; 238 };
239 239
240 struct wlr_box output_box = { 240 struct wlr_box output_box = {
241 .width = output->swayc->current.swayc_width, 241 .width = output->wlr_output->width,
242 .height = output->swayc->current.swayc_height, 242 .height = output->wlr_output->height,
243 }; 243 };
244 244
245 struct wlr_box intersection; 245 struct wlr_box intersection;
@@ -263,14 +263,14 @@ static void render_saved_view(struct sway_view *view,
263 */ 263 */
264static void render_view(struct sway_output *output, pixman_region32_t *damage, 264static void render_view(struct sway_output *output, pixman_region32_t *damage,
265 struct sway_container *con, struct border_colors *colors) { 265 struct sway_container *con, struct border_colors *colors) {
266 struct sway_view *view = con->sway_view; 266 struct sway_view *view = con->view;
267 if (view->saved_buffer) { 267 if (view->saved_buffer) {
268 render_saved_view(view, output, damage, view->swayc->alpha); 268 render_saved_view(view, output, damage, view->container->alpha);
269 } else { 269 } else {
270 render_view_toplevels(view, output, damage, view->swayc->alpha); 270 render_view_toplevels(view, output, damage, view->container->alpha);
271 } 271 }
272 272
273 if (view->swayc->current.using_csd) { 273 if (view->container->current.using_csd) {
274 return; 274 return;
275 } 275 }
276 276
@@ -283,7 +283,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
283 if (state->border_left) { 283 if (state->border_left) {
284 memcpy(&color, colors->child_border, sizeof(float) * 4); 284 memcpy(&color, colors->child_border, sizeof(float) * 4);
285 premultiply_alpha(color, con->alpha); 285 premultiply_alpha(color, con->alpha);
286 box.x = state->swayc_x; 286 box.x = state->con_x;
287 box.y = state->view_y; 287 box.y = state->view_y;
288 box.width = state->border_thickness; 288 box.width = state->border_thickness;
289 box.height = state->view_height; 289 box.height = state->view_height;
@@ -291,9 +291,12 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
291 render_rect(output->wlr_output, damage, &box, color); 291 render_rect(output->wlr_output, damage, &box, color);
292 } 292 }
293 293
294 list_t *siblings = container_get_current_siblings(con);
295 enum sway_container_layout layout =
296 container_current_parent_layout(con);
297
294 if (state->border_right) { 298 if (state->border_right) {
295 if (state->parent->current.children->length == 1 299 if (siblings->length == 1 && layout == L_HORIZ) {
296 && state->parent->current.layout == L_HORIZ) {
297 memcpy(&color, colors->indicator, sizeof(float) * 4); 300 memcpy(&color, colors->indicator, sizeof(float) * 4);
298 } else { 301 } else {
299 memcpy(&color, colors->child_border, sizeof(float) * 4); 302 memcpy(&color, colors->child_border, sizeof(float) * 4);
@@ -308,16 +311,15 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
308 } 311 }
309 312
310 if (state->border_bottom) { 313 if (state->border_bottom) {
311 if (state->parent->current.children->length == 1 314 if (siblings->length == 1 && layout == L_VERT) {
312 && con->current.parent->current.layout == L_VERT) {
313 memcpy(&color, colors->indicator, sizeof(float) * 4); 315 memcpy(&color, colors->indicator, sizeof(float) * 4);
314 } else { 316 } else {
315 memcpy(&color, colors->child_border, sizeof(float) * 4); 317 memcpy(&color, colors->child_border, sizeof(float) * 4);
316 } 318 }
317 premultiply_alpha(color, con->alpha); 319 premultiply_alpha(color, con->alpha);
318 box.x = state->swayc_x; 320 box.x = state->con_x;
319 box.y = state->view_y + state->view_height; 321 box.y = state->view_y + state->view_height;
320 box.width = state->swayc_width; 322 box.width = state->con_width;
321 box.height = state->border_thickness; 323 box.height = state->border_thickness;
322 scale_box(&box, output_scale); 324 scale_box(&box, output_scale);
323 render_rect(output->wlr_output, damage, &box, color); 325 render_rect(output->wlr_output, damage, &box, color);
@@ -344,12 +346,12 @@ static void render_titlebar(struct sway_output *output,
344 float color[4]; 346 float color[4];
345 struct sway_container_state *state = &con->current; 347 struct sway_container_state *state = &con->current;
346 float output_scale = output->wlr_output->scale; 348 float output_scale = output->wlr_output->scale;
347 enum sway_container_layout layout = state->parent->current.layout; 349 enum sway_container_layout layout = container_current_parent_layout(con);
348 list_t *children = state->parent->current.children; 350 list_t *children = container_get_current_siblings(con);
349 bool is_last_child = children->length == 0 || 351 bool is_last_child = children->length == 0 ||
350 children->items[children->length - 1] == con; 352 children->items[children->length - 1] == con;
351 double output_x = output->swayc->current.swayc_x; 353 double output_x = output->wlr_output->lx;
352 double output_y = output->swayc->current.swayc_y; 354 double output_y = output->wlr_output->ly;
353 355
354 // Single pixel bar above title 356 // Single pixel bar above title
355 memcpy(&color, colors->border, sizeof(float) * 4); 357 memcpy(&color, colors->border, sizeof(float) * 4);
@@ -366,7 +368,7 @@ static void render_titlebar(struct sway_output *output,
366 bool connects_sides = false; 368 bool connects_sides = false;
367 if (layout == L_HORIZ || layout == L_VERT || 369 if (layout == L_HORIZ || layout == L_VERT ||
368 (layout == L_STACKED && is_last_child)) { 370 (layout == L_STACKED && is_last_child)) {
369 if (con->type == C_VIEW) { 371 if (con->view) {
370 left_offset = state->border_left * state->border_thickness; 372 left_offset = state->border_left * state->border_thickness;
371 right_offset = state->border_right * state->border_thickness; 373 right_offset = state->border_right * state->border_thickness;
372 connects_sides = true; 374 connects_sides = true;
@@ -542,14 +544,22 @@ static void render_top_border(struct sway_output *output,
542 // Child border - top edge 544 // Child border - top edge
543 memcpy(&color, colors->child_border, sizeof(float) * 4); 545 memcpy(&color, colors->child_border, sizeof(float) * 4);
544 premultiply_alpha(color, con->alpha); 546 premultiply_alpha(color, con->alpha);
545 box.x = state->swayc_x; 547 box.x = state->con_x;
546 box.y = state->swayc_y; 548 box.y = state->con_y;
547 box.width = state->swayc_width; 549 box.width = state->con_width;
548 box.height = state->border_thickness; 550 box.height = state->border_thickness;
549 scale_box(&box, output_scale); 551 scale_box(&box, output_scale);
550 render_rect(output->wlr_output, output_damage, &box, color); 552 render_rect(output->wlr_output, output_damage, &box, color);
551} 553}
552 554
555struct parent_data {
556 enum sway_container_layout layout;
557 struct wlr_box box;
558 list_t *children;
559 bool focused;
560 struct sway_container *active_child;
561};
562
553static void render_container(struct sway_output *output, 563static void render_container(struct sway_output *output,
554 pixman_region32_t *damage, struct sway_container *con, bool parent_focused); 564 pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
555 565
@@ -559,14 +569,13 @@ static void render_container(struct sway_output *output,
559 * Wrap child views in borders and leave child containers borderless because 569 * Wrap child views in borders and leave child containers borderless because
560 * they'll apply their own borders to their children. 570 * they'll apply their own borders to their children.
561 */ 571 */
562static void render_container_simple(struct sway_output *output, 572static void render_containers_linear(struct sway_output *output,
563 pixman_region32_t *damage, struct sway_container *con, 573 pixman_region32_t *damage, struct parent_data *parent) {
564 bool parent_focused) { 574 for (int i = 0; i < parent->children->length; ++i) {
565 for (int i = 0; i < con->current.children->length; ++i) { 575 struct sway_container *child = parent->children->items[i];
566 struct sway_container *child = con->current.children->items[i]; 576
567 577 if (child->view) {
568 if (child->type == C_VIEW) { 578 struct sway_view *view = child->view;
569 struct sway_view *view = child->sway_view;
570 struct border_colors *colors; 579 struct border_colors *colors;
571 struct wlr_texture *title_texture; 580 struct wlr_texture *title_texture;
572 struct wlr_texture *marks_texture; 581 struct wlr_texture *marks_texture;
@@ -576,11 +585,11 @@ static void render_container_simple(struct sway_output *output,
576 colors = &config->border_colors.urgent; 585 colors = &config->border_colors.urgent;
577 title_texture = child->title_urgent; 586 title_texture = child->title_urgent;
578 marks_texture = view->marks_urgent; 587 marks_texture = view->marks_urgent;
579 } else if (state->focused || parent_focused) { 588 } else if (state->focused || parent->focused) {
580 colors = &config->border_colors.focused; 589 colors = &config->border_colors.focused;
581 title_texture = child->title_focused; 590 title_texture = child->title_focused;
582 marks_texture = view->marks_focused; 591 marks_texture = view->marks_focused;
583 } else if (con->current.focused_inactive_child == child) { 592 } else if (child == parent->active_child) {
584 colors = &config->border_colors.focused_inactive; 593 colors = &config->border_colors.focused_inactive;
585 title_texture = child->title_focused_inactive; 594 title_texture = child->title_focused_inactive;
586 marks_texture = view->marks_focused_inactive; 595 marks_texture = view->marks_focused_inactive;
@@ -590,10 +599,10 @@ static void render_container_simple(struct sway_output *output,
590 marks_texture = view->marks_unfocused; 599 marks_texture = view->marks_unfocused;
591 } 600 }
592 601
593 if (!view->swayc->current.using_csd) { 602 if (!view->container->current.using_csd) {
594 if (state->border == B_NORMAL) { 603 if (state->border == B_NORMAL) {
595 render_titlebar(output, damage, child, state->swayc_x, 604 render_titlebar(output, damage, child, state->con_x,
596 state->swayc_y, state->swayc_width, colors, 605 state->con_y, state->con_width, colors,
597 title_texture, marks_texture); 606 title_texture, marks_texture);
598 } else { 607 } else {
599 render_top_border(output, damage, child, colors); 608 render_top_border(output, damage, child, colors);
@@ -602,7 +611,7 @@ static void render_container_simple(struct sway_output *output,
602 render_view(output, damage, child, colors); 611 render_view(output, damage, child, colors);
603 } else { 612 } else {
604 render_container(output, damage, child, 613 render_container(output, damage, child,
605 parent_focused || child->current.focused); 614 parent->focused || child->current.focused);
606 } 615 }
607 } 616 }
608} 617}
@@ -610,22 +619,19 @@ static void render_container_simple(struct sway_output *output,
610/** 619/**
611 * Render a container's children using the L_TABBED layout. 620 * Render a container's children using the L_TABBED layout.
612 */ 621 */
613static void render_container_tabbed(struct sway_output *output, 622static void render_containers_tabbed(struct sway_output *output,
614 pixman_region32_t *damage, struct sway_container *con, 623 pixman_region32_t *damage, struct parent_data *parent) {
615 bool parent_focused) { 624 if (!parent->children->length) {
616 if (!con->current.children->length) {
617 return; 625 return;
618 } 626 }
619 struct sway_container_state *pstate = &con->current; 627 struct sway_container *current = parent->active_child;
620 struct sway_container *current = pstate->focused_inactive_child;
621 struct border_colors *current_colors = &config->border_colors.unfocused; 628 struct border_colors *current_colors = &config->border_colors.unfocused;
622 629 int tab_width = parent->box.width / parent->children->length;
623 int tab_width = (pstate->swayc_width) / pstate->children->length;
624 630
625 // Render tabs 631 // Render tabs
626 for (int i = 0; i < pstate->children->length; ++i) { 632 for (int i = 0; i < parent->children->length; ++i) {
627 struct sway_container *child = pstate->children->items[i]; 633 struct sway_container *child = parent->children->items[i];
628 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; 634 struct sway_view *view = child->view;
629 struct sway_container_state *cstate = &child->current; 635 struct sway_container_state *cstate = &child->current;
630 struct border_colors *colors; 636 struct border_colors *colors;
631 struct wlr_texture *title_texture; 637 struct wlr_texture *title_texture;
@@ -637,11 +643,11 @@ static void render_container_tabbed(struct sway_output *output,
637 colors = &config->border_colors.urgent; 643 colors = &config->border_colors.urgent;
638 title_texture = child->title_urgent; 644 title_texture = child->title_urgent;
639 marks_texture = view ? view->marks_urgent : NULL; 645 marks_texture = view ? view->marks_urgent : NULL;
640 } else if (cstate->focused || parent_focused) { 646 } else if (cstate->focused || parent->focused) {
641 colors = &config->border_colors.focused; 647 colors = &config->border_colors.focused;
642 title_texture = child->title_focused; 648 title_texture = child->title_focused;
643 marks_texture = view ? view->marks_focused : NULL; 649 marks_texture = view ? view->marks_focused : NULL;
644 } else if (child == pstate->focused_inactive_child) { 650 } else if (child == parent->active_child) {
645 colors = &config->border_colors.focused_inactive; 651 colors = &config->border_colors.focused_inactive;
646 title_texture = child->title_focused_inactive; 652 title_texture = child->title_focused_inactive;
647 marks_texture = view ? view->marks_focused_inactive : NULL; 653 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -651,14 +657,14 @@ static void render_container_tabbed(struct sway_output *output,
651 marks_texture = view ? view->marks_unfocused : NULL; 657 marks_texture = view ? view->marks_unfocused : NULL;
652 } 658 }
653 659
654 int x = cstate->swayc_x + tab_width * i; 660 int x = cstate->con_x + tab_width * i;
655 661
656 // Make last tab use the remaining width of the parent 662 // Make last tab use the remaining width of the parent
657 if (i == pstate->children->length - 1) { 663 if (i == parent->children->length - 1) {
658 tab_width = pstate->swayc_width - tab_width * i; 664 tab_width = parent->box.width - tab_width * i;
659 } 665 }
660 666
661 render_titlebar(output, damage, child, x, pstate->swayc_y, tab_width, 667 render_titlebar(output, damage, child, x, parent->box.y, tab_width,
662 colors, title_texture, marks_texture); 668 colors, title_texture, marks_texture);
663 669
664 if (child == current) { 670 if (child == current) {
@@ -667,33 +673,30 @@ static void render_container_tabbed(struct sway_output *output,
667 } 673 }
668 674
669 // Render surface and left/right/bottom borders 675 // Render surface and left/right/bottom borders
670 if (current->type == C_VIEW) { 676 if (current->view) {
671 render_view(output, damage, current, current_colors); 677 render_view(output, damage, current, current_colors);
672 } else { 678 } else {
673 render_container(output, damage, current, 679 render_container(output, damage, current,
674 parent_focused || current->current.focused); 680 parent->focused || current->current.focused);
675 } 681 }
676} 682}
677 683
678/** 684/**
679 * Render a container's children using the L_STACKED layout. 685 * Render a container's children using the L_STACKED layout.
680 */ 686 */
681static void render_container_stacked(struct sway_output *output, 687static void render_containers_stacked(struct sway_output *output,
682 pixman_region32_t *damage, struct sway_container *con, 688 pixman_region32_t *damage, struct parent_data *parent) {
683 bool parent_focused) { 689 if (!parent->children->length) {
684 if (!con->current.children->length) {
685 return; 690 return;
686 } 691 }
687 struct sway_container_state *pstate = &con->current; 692 struct sway_container *current = parent->active_child;
688 struct sway_container *current = pstate->focused_inactive_child;
689 struct border_colors *current_colors = &config->border_colors.unfocused; 693 struct border_colors *current_colors = &config->border_colors.unfocused;
690
691 size_t titlebar_height = container_titlebar_height(); 694 size_t titlebar_height = container_titlebar_height();
692 695
693 // Render titles 696 // Render titles
694 for (int i = 0; i < pstate->children->length; ++i) { 697 for (int i = 0; i < parent->children->length; ++i) {
695 struct sway_container *child = pstate->children->items[i]; 698 struct sway_container *child = parent->children->items[i];
696 struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; 699 struct sway_view *view = child->view;
697 struct sway_container_state *cstate = &child->current; 700 struct sway_container_state *cstate = &child->current;
698 struct border_colors *colors; 701 struct border_colors *colors;
699 struct wlr_texture *title_texture; 702 struct wlr_texture *title_texture;
@@ -705,11 +708,11 @@ static void render_container_stacked(struct sway_output *output,
705 colors = &config->border_colors.urgent; 708 colors = &config->border_colors.urgent;
706 title_texture = child->title_urgent; 709 title_texture = child->title_urgent;
707 marks_texture = view ? view->marks_urgent : NULL; 710 marks_texture = view ? view->marks_urgent : NULL;
708 } else if (cstate->focused || parent_focused) { 711 } else if (cstate->focused || parent->focused) {
709 colors = &config->border_colors.focused; 712 colors = &config->border_colors.focused;
710 title_texture = child->title_focused; 713 title_texture = child->title_focused;
711 marks_texture = view ? view->marks_focused : NULL; 714 marks_texture = view ? view->marks_focused : NULL;
712 } else if (child == pstate->focused_inactive_child) { 715 } else if (child == parent->active_child) {
713 colors = &config->border_colors.focused_inactive; 716 colors = &config->border_colors.focused_inactive;
714 title_texture = child->title_focused_inactive; 717 title_texture = child->title_focused_inactive;
715 marks_texture = view ? view->marks_focused_inactive : NULL; 718 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -719,9 +722,9 @@ static void render_container_stacked(struct sway_output *output,
719 marks_texture = view ? view->marks_unfocused : NULL; 722 marks_texture = view ? view->marks_unfocused : NULL;
720 } 723 }
721 724
722 int y = pstate->swayc_y + titlebar_height * i; 725 int y = parent->box.y + titlebar_height * i;
723 render_titlebar(output, damage, child, pstate->swayc_x, y, 726 render_titlebar(output, damage, child, parent->box.x, y,
724 pstate->swayc_width, colors, title_texture, marks_texture); 727 parent->box.width, colors, title_texture, marks_texture);
725 728
726 if (child == current) { 729 if (child == current) {
727 current_colors = colors; 730 current_colors = colors;
@@ -729,36 +732,69 @@ static void render_container_stacked(struct sway_output *output,
729 } 732 }
730 733
731 // Render surface and left/right/bottom borders 734 // Render surface and left/right/bottom borders
732 if (current->type == C_VIEW) { 735 if (current->view) {
733 render_view(output, damage, current, current_colors); 736 render_view(output, damage, current, current_colors);
734 } else { 737 } else {
735 render_container(output, damage, current, 738 render_container(output, damage, current,
736 parent_focused || current->current.focused); 739 parent->focused || current->current.focused);
737 } 740 }
738} 741}
739 742
740static void render_container(struct sway_output *output, 743static void render_containers(struct sway_output *output,
741 pixman_region32_t *damage, struct sway_container *con, 744 pixman_region32_t *damage, struct parent_data *parent) {
742 bool parent_focused) { 745 switch (parent->layout) {
743 switch (con->current.layout) {
744 case L_NONE: 746 case L_NONE:
745 case L_HORIZ: 747 case L_HORIZ:
746 case L_VERT: 748 case L_VERT:
747 render_container_simple(output, damage, con, parent_focused); 749 render_containers_linear(output, damage, parent);
748 break; 750 break;
749 case L_STACKED: 751 case L_STACKED:
750 render_container_stacked(output, damage, con, parent_focused); 752 render_containers_stacked(output, damage, parent);
751 break; 753 break;
752 case L_TABBED: 754 case L_TABBED:
753 render_container_tabbed(output, damage, con, parent_focused); 755 render_containers_tabbed(output, damage, parent);
754 break; 756 break;
755 } 757 }
756} 758}
757 759
760static void render_container(struct sway_output *output,
761 pixman_region32_t *damage, struct sway_container *con, bool focused) {
762 struct parent_data data = {
763 .layout = con->current.layout,
764 .box = {
765 .x = con->current.con_x,
766 .y = con->current.con_y,
767 .width = con->current.con_width,
768 .height = con->current.con_height,
769 },
770 .children = con->current.children,
771 .focused = focused,
772 .active_child = con->current.focused_inactive_child,
773 };
774 render_containers(output, damage, &data);
775}
776
777static void render_workspace(struct sway_output *output,
778 pixman_region32_t *damage, struct sway_workspace *ws, bool focused) {
779 struct parent_data data = {
780 .layout = ws->current.layout,
781 .box = {
782 .x = ws->current.x,
783 .y = ws->current.y,
784 .width = ws->current.width,
785 .height = ws->current.height,
786 },
787 .children = ws->current.tiling,
788 .focused = focused,
789 .active_child = ws->current.focused_inactive_child,
790 };
791 render_containers(output, damage, &data);
792}
793
758static void render_floating_container(struct sway_output *soutput, 794static void render_floating_container(struct sway_output *soutput,
759 pixman_region32_t *damage, struct sway_container *con) { 795 pixman_region32_t *damage, struct sway_container *con) {
760 if (con->type == C_VIEW) { 796 if (con->view) {
761 struct sway_view *view = con->sway_view; 797 struct sway_view *view = con->view;
762 struct border_colors *colors; 798 struct border_colors *colors;
763 struct wlr_texture *title_texture; 799 struct wlr_texture *title_texture;
764 struct wlr_texture *marks_texture; 800 struct wlr_texture *marks_texture;
@@ -777,10 +813,10 @@ static void render_floating_container(struct sway_output *soutput,
777 marks_texture = view->marks_unfocused; 813 marks_texture = view->marks_unfocused;
778 } 814 }
779 815
780 if (!view->swayc->current.using_csd) { 816 if (!view->container->current.using_csd) {
781 if (con->current.border == B_NORMAL) { 817 if (con->current.border == B_NORMAL) {
782 render_titlebar(soutput, damage, con, con->current.swayc_x, 818 render_titlebar(soutput, damage, con, con->current.con_x,
783 con->current.swayc_y, con->current.swayc_width, colors, 819 con->current.con_y, con->current.con_width, colors,
784 title_texture, marks_texture); 820 title_texture, marks_texture);
785 } else if (con->current.border != B_NONE) { 821 } else if (con->current.border != B_NONE) {
786 render_top_border(soutput, damage, con, colors); 822 render_top_border(soutput, damage, con, colors);
@@ -794,17 +830,15 @@ static void render_floating_container(struct sway_output *soutput,
794 830
795static void render_floating(struct sway_output *soutput, 831static void render_floating(struct sway_output *soutput,
796 pixman_region32_t *damage) { 832 pixman_region32_t *damage) {
797 for (int i = 0; i < root_container.current.children->length; ++i) { 833 for (int i = 0; i < root->outputs->length; ++i) {
798 struct sway_container *output = 834 struct sway_output *output = root->outputs->items[i];
799 root_container.current.children->items[i]; 835 for (int j = 0; j < output->current.workspaces->length; ++j) {
800 for (int j = 0; j < output->current.children->length; ++j) { 836 struct sway_workspace *ws = output->current.workspaces->items[j];
801 struct sway_container *ws = output->current.children->items[j];
802 if (!workspace_is_visible(ws)) { 837 if (!workspace_is_visible(ws)) {
803 continue; 838 continue;
804 } 839 }
805 list_t *floating = ws->current.ws_floating; 840 for (int k = 0; k < ws->current.floating->length; ++k) {
806 for (int k = 0; k < floating->length; ++k) { 841 struct sway_container *floater = ws->current.floating->items[k];
807 struct sway_container *floater = floating->items[k];
808 render_floating_container(soutput, damage, floater); 842 render_floating_container(soutput, damage, floater);
809 } 843 }
810 } 844 }
@@ -837,8 +871,8 @@ void output_render(struct sway_output *output, struct timespec *when,
837 pixman_region32_union_rect(damage, damage, 0, 0, width, height); 871 pixman_region32_union_rect(damage, damage, 0, 0, width, height);
838 } 872 }
839 873
840 struct sway_container *workspace = output_get_active_workspace(output); 874 struct sway_workspace *workspace = output_get_active_workspace(output);
841 struct sway_container *fullscreen_con = workspace->current.ws_fullscreen; 875 struct sway_container *fullscreen_con = workspace->current.fullscreen;
842 876
843 if (output_has_opaque_overlay_layer_surface(output)) { 877 if (output_has_opaque_overlay_layer_surface(output)) {
844 goto render_overlay; 878 goto render_overlay;
@@ -855,12 +889,11 @@ void output_render(struct sway_output *output, struct timespec *when,
855 } 889 }
856 890
857 // TODO: handle views smaller than the output 891 // TODO: handle views smaller than the output
858 if (fullscreen_con->type == C_VIEW) { 892 if (fullscreen_con->view) {
859 if (fullscreen_con->sway_view->saved_buffer) { 893 if (fullscreen_con->view->saved_buffer) {
860 render_saved_view(fullscreen_con->sway_view, 894 render_saved_view(fullscreen_con->view, output, damage, 1.0f);
861 output, damage, 1.0f);
862 } else { 895 } else {
863 render_view_toplevels(fullscreen_con->sway_view, 896 render_view_toplevels(fullscreen_con->view,
864 output, damage, 1.0f); 897 output, damage, 1.0f);
865 } 898 }
866 } else { 899 } else {
@@ -868,8 +901,7 @@ void output_render(struct sway_output *output, struct timespec *when,
868 fullscreen_con->current.focused); 901 fullscreen_con->current.focused);
869 } 902 }
870#ifdef HAVE_XWAYLAND 903#ifdef HAVE_XWAYLAND
871 render_unmanaged(output, damage, 904 render_unmanaged(output, damage, &root->xwayland_unmanaged);
872 &root_container.sway_root->xwayland_unmanaged);
873#endif 905#endif
874 } else { 906 } else {
875 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; 907 float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
@@ -886,31 +918,30 @@ void output_render(struct sway_output *output, struct timespec *when,
886 render_layer(output, damage, 918 render_layer(output, damage,
887 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); 919 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
888 920
889 render_container(output, damage, workspace, workspace->current.focused); 921 render_workspace(output, damage, workspace, workspace->current.focused);
890 render_floating(output, damage); 922 render_floating(output, damage);
891#ifdef HAVE_XWAYLAND 923#ifdef HAVE_XWAYLAND
892 render_unmanaged(output, damage, 924 render_unmanaged(output, damage, &root->xwayland_unmanaged);
893 &root_container.sway_root->xwayland_unmanaged);
894#endif 925#endif
895 render_layer(output, damage, 926 render_layer(output, damage,
896 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); 927 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
897 } 928 }
898 929
899 struct sway_seat *seat = input_manager_current_seat(input_manager); 930 struct sway_seat *seat = input_manager_current_seat(input_manager);
900 struct sway_container *focus = seat_get_focus(seat); 931 struct sway_container *focus = seat_get_focused_container(seat);
901 if (focus && focus->type == C_VIEW) { 932 if (focus && focus->view) {
902 render_view_popups(focus->sway_view, output, damage, focus->alpha); 933 render_view_popups(focus->view, output, damage, focus->alpha);
903 } 934 }
904 935
905render_overlay: 936render_overlay:
906 render_layer(output, damage, 937 render_layer(output, damage,
907 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); 938 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
908 render_drag_icons(output, damage, &root_container.sway_root->drag_icons); 939 render_drag_icons(output, damage, &root->drag_icons);
909 940
910renderer_end: 941renderer_end:
911 if (debug.render_tree) { 942 if (debug.render_tree) {
912 wlr_renderer_scissor(renderer, NULL); 943 wlr_renderer_scissor(renderer, NULL);
913 wlr_render_texture(renderer, root_container.sway_root->debug_tree, 944 wlr_render_texture(renderer, root->debug_tree,
914 wlr_output->transform_matrix, 0, 40, 1); 945 wlr_output->transform_matrix, 0, 40, 1);
915 } 946 }
916 if (debug.damage == DAMAGE_HIGHLIGHT) { 947 if (debug.damage == DAMAGE_HIGHLIGHT) {
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 145c5f92..b4eec933 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -12,6 +12,7 @@
12#include "sway/desktop/transaction.h" 12#include "sway/desktop/transaction.h"
13#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/tree/container.h" 14#include "sway/tree/container.h"
15#include "sway/tree/node.h"
15#include "sway/tree/view.h" 16#include "sway/tree/view.h"
16#include "sway/tree/workspace.h" 17#include "sway/tree/workspace.h"
17#include "list.h" 18#include "list.h"
@@ -27,8 +28,12 @@ struct sway_transaction {
27 28
28struct sway_transaction_instruction { 29struct sway_transaction_instruction {
29 struct sway_transaction *transaction; 30 struct sway_transaction *transaction;
30 struct sway_container *container; 31 struct sway_node *node;
31 struct sway_container_state state; 32 union {
33 struct sway_output_state *output_state;
34 struct sway_workspace_state *workspace_state;
35 struct sway_container_state *container_state;
36 };
32 uint32_t serial; 37 uint32_t serial;
33}; 38};
34 39
@@ -47,26 +52,24 @@ static void transaction_destroy(struct sway_transaction *transaction) {
47 for (int i = 0; i < transaction->instructions->length; ++i) { 52 for (int i = 0; i < transaction->instructions->length; ++i) {
48 struct sway_transaction_instruction *instruction = 53 struct sway_transaction_instruction *instruction =
49 transaction->instructions->items[i]; 54 transaction->instructions->items[i];
50 struct sway_container *con = instruction->container; 55 struct sway_node *node = instruction->node;
51 con->ntxnrefs--; 56 node->ntxnrefs--;
52 if (con->instruction == instruction) { 57 if (node->instruction == instruction) {
53 con->instruction = NULL; 58 node->instruction = NULL;
54 } 59 }
55 if (con->destroying && con->ntxnrefs == 0) { 60 if (node->destroying && node->ntxnrefs == 0) {
56 switch (con->type) { 61 switch (node->type) {
57 case C_ROOT: 62 case N_ROOT:
63 sway_assert(false, "Never reached");
58 break; 64 break;
59 case C_OUTPUT: 65 case N_OUTPUT:
60 output_destroy(con); 66 output_destroy(node->sway_output);
61 break; 67 break;
62 case C_WORKSPACE: 68 case N_WORKSPACE:
63 workspace_destroy(con); 69 workspace_destroy(node->sway_workspace);
64 break; 70 break;
65 case C_CONTAINER: 71 case N_CONTAINER:
66 case C_VIEW: 72 container_destroy(node->sway_container);
67 container_destroy(con);
68 break;
69 case C_TYPES:
70 break; 73 break;
71 } 74 }
72 } 75 }
@@ -80,22 +83,79 @@ static void transaction_destroy(struct sway_transaction *transaction) {
80 free(transaction); 83 free(transaction);
81} 84}
82 85
83static void copy_pending_state(struct sway_container *container, 86static void copy_output_state(struct sway_output *output,
84 struct sway_container_state *state) { 87 struct sway_transaction_instruction *instruction) {
88 struct sway_output_state *state =
89 calloc(1, sizeof(struct sway_output_state));
90 if (!state) {
91 wlr_log(WLR_ERROR, "Could not allocate output state");
92 return;
93 }
94 instruction->output_state = state;
95
96 state->workspaces = create_list();
97 list_cat(state->workspaces, output->workspaces);
98
99 state->active_workspace = output_get_active_workspace(output);
100}
101
102static void copy_workspace_state(struct sway_workspace *ws,
103 struct sway_transaction_instruction *instruction) {
104 struct sway_workspace_state *state =
105 calloc(1, sizeof(struct sway_workspace_state));
106 if (!state) {
107 wlr_log(WLR_ERROR, "Could not allocate workspace state");
108 return;
109 }
110 instruction->workspace_state = state;
111
112 state->fullscreen = ws->fullscreen;
113 state->x = ws->x;
114 state->y = ws->y;
115 state->width = ws->width;
116 state->height = ws->height;
117 state->layout = ws->layout;
118
119 state->output = ws->output;
120 state->floating = create_list();
121 state->tiling = create_list();
122 list_cat(state->floating, ws->floating);
123 list_cat(state->tiling, ws->tiling);
124
125 struct sway_seat *seat = input_manager_current_seat(input_manager);
126 state->focused = seat_get_focus(seat) == &ws->node;
127
128 // Set focused_inactive_child to the direct tiling child
129 struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws);
130 if (focus) {
131 while (focus->parent) {
132 focus = focus->parent;
133 }
134 }
135 state->focused_inactive_child = focus;
136}
137
138static void copy_container_state(struct sway_container *container,
139 struct sway_transaction_instruction *instruction) {
140 struct sway_container_state *state =
141 calloc(1, sizeof(struct sway_container_state));
142 if (!state) {
143 wlr_log(WLR_ERROR, "Could not allocate container state");
144 return;
145 }
146 instruction->container_state = state;
147
85 state->layout = container->layout; 148 state->layout = container->layout;
86 state->swayc_x = container->x; 149 state->con_x = container->x;
87 state->swayc_y = container->y; 150 state->con_y = container->y;
88 state->swayc_width = container->width; 151 state->con_width = container->width;
89 state->swayc_height = container->height; 152 state->con_height = container->height;
90 state->is_fullscreen = container->is_fullscreen; 153 state->is_fullscreen = container->is_fullscreen;
91 state->has_gaps = container->has_gaps;
92 state->current_gaps = container->current_gaps;
93 state->gaps_inner = container->gaps_inner;
94 state->gaps_outer = container->gaps_outer;
95 state->parent = container->parent; 154 state->parent = container->parent;
155 state->workspace = container->workspace;
96 156
97 if (container->type == C_VIEW) { 157 if (container->view) {
98 struct sway_view *view = container->sway_view; 158 struct sway_view *view = container->view;
99 state->view_x = view->x; 159 state->view_x = view->x;
100 state->view_y = view->y; 160 state->view_y = view->y;
101 state->view_width = view->width; 161 state->view_width = view->width;
@@ -107,50 +167,111 @@ static void copy_pending_state(struct sway_container *container,
107 state->border_right = view->border_right; 167 state->border_right = view->border_right;
108 state->border_bottom = view->border_bottom; 168 state->border_bottom = view->border_bottom;
109 state->using_csd = view->using_csd; 169 state->using_csd = view->using_csd;
110 } else if (container->type == C_WORKSPACE) {
111 state->ws_fullscreen = container->sway_workspace->fullscreen;
112 state->ws_floating = create_list();
113 state->children = create_list();
114 list_cat(state->ws_floating, container->sway_workspace->floating);
115 list_cat(state->children, container->children);
116 } else { 170 } else {
117 state->children = create_list(); 171 state->children = create_list();
118 list_cat(state->children, container->children); 172 list_cat(state->children, container->children);
119 } 173 }
120 174
121 struct sway_seat *seat = input_manager_current_seat(input_manager); 175 struct sway_seat *seat = input_manager_current_seat(input_manager);
122 state->focused = seat_get_focus(seat) == container; 176 state->focused = seat_get_focus(seat) == &container->node;
123 177
124 if (container->type == C_WORKSPACE) { 178 if (!container->view) {
125 // Set focused_inactive_child to the direct tiling child 179 struct sway_node *focus = seat_get_active_child(seat, &container->node);
126 struct sway_container *focus = 180 state->focused_inactive_child = focus ? focus->sway_container : NULL;
127 seat_get_focus_inactive_tiling(seat, container);
128 if (focus && focus->type > C_WORKSPACE) {
129 while (focus->parent->type != C_WORKSPACE) {
130 focus = focus->parent;
131 }
132 }
133 state->focused_inactive_child = focus;
134 } else if (container->type != C_VIEW) {
135 state->focused_inactive_child =
136 seat_get_active_child(seat, container);
137 } 181 }
138} 182}
139 183
140static void transaction_add_container(struct sway_transaction *transaction, 184static void transaction_add_node(struct sway_transaction *transaction,
141 struct sway_container *container) { 185 struct sway_node *node) {
142 struct sway_transaction_instruction *instruction = 186 struct sway_transaction_instruction *instruction =
143 calloc(1, sizeof(struct sway_transaction_instruction)); 187 calloc(1, sizeof(struct sway_transaction_instruction));
144 if (!sway_assert(instruction, "Unable to allocate instruction")) { 188 if (!sway_assert(instruction, "Unable to allocate instruction")) {
145 return; 189 return;
146 } 190 }
147 instruction->transaction = transaction; 191 instruction->transaction = transaction;
148 instruction->container = container; 192 instruction->node = node;
149 193
150 copy_pending_state(container, &instruction->state); 194 switch (node->type) {
195 case N_ROOT:
196 break;
197 case N_OUTPUT:
198 copy_output_state(node->sway_output, instruction);
199 break;
200 case N_WORKSPACE:
201 copy_workspace_state(node->sway_workspace, instruction);
202 break;
203 case N_CONTAINER:
204 copy_container_state(node->sway_container, instruction);
205 break;
206 }
151 207
152 list_add(transaction->instructions, instruction); 208 list_add(transaction->instructions, instruction);
153 container->ntxnrefs++; 209 node->ntxnrefs++;
210}
211
212static void apply_output_state(struct sway_output *output,
213 struct sway_output_state *state) {
214 output_damage_whole(output);
215 list_free(output->current.workspaces);
216 memcpy(&output->current, state, sizeof(struct sway_output_state));
217 output_damage_whole(output);
218}
219
220static void apply_workspace_state(struct sway_workspace *ws,
221 struct sway_workspace_state *state) {
222 output_damage_whole(ws->current.output);
223 list_free(ws->current.floating);
224 list_free(ws->current.tiling);
225 memcpy(&ws->current, state, sizeof(struct sway_workspace_state));
226 output_damage_whole(ws->current.output);
227}
228
229static void apply_container_state(struct sway_container *container,
230 struct sway_container_state *state) {
231 struct sway_view *view = container->view;
232 // Damage the old location
233 desktop_damage_whole_container(container);
234 if (view && view->saved_buffer) {
235 struct wlr_box box = {
236 .x = container->current.view_x - view->saved_geometry.x,
237 .y = container->current.view_y - view->saved_geometry.y,
238 .width = view->saved_buffer_width,
239 .height = view->saved_buffer_height,
240 };
241 desktop_damage_box(&box);
242 }
243
244 // There are separate children lists for each instruction state, the
245 // container's current state and the container's pending state
246 // (ie. con->children). The list itself needs to be freed here.
247 // Any child containers which are being deleted will be cleaned up in
248 // transaction_destroy().
249 list_free(container->current.children);
250
251 memcpy(&container->current, state, sizeof(struct sway_container_state));
252
253 if (view && view->saved_buffer) {
254 if (!container->node.destroying || container->node.ntxnrefs == 1) {
255 view_remove_saved_buffer(view);
256 }
257 }
258
259 // Damage the new location
260 desktop_damage_whole_container(container);
261 if (view && view->surface) {
262 struct wlr_surface *surface = view->surface;
263 struct wlr_box box = {
264 .x = container->current.view_x - view->geometry.x,
265 .y = container->current.view_y - view->geometry.y,
266 .width = surface->current.width,
267 .height = surface->current.height,
268 };
269 desktop_damage_box(&box);
270 }
271
272 if (!container->node.destroying) {
273 container_discover_outputs(container);
274 }
154} 275}
155 276
156/** 277/**
@@ -168,67 +289,36 @@ static void transaction_apply(struct sway_transaction *transaction) {
168 "(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60)); 289 "(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60));
169 } 290 }
170 291
171 // Apply the instruction state to the container's current state 292 // Apply the instruction state to the node's current state
172 for (int i = 0; i < transaction->instructions->length; ++i) { 293 for (int i = 0; i < transaction->instructions->length; ++i) {
173 struct sway_transaction_instruction *instruction = 294 struct sway_transaction_instruction *instruction =
174 transaction->instructions->items[i]; 295 transaction->instructions->items[i];
175 struct sway_container *container = instruction->container; 296 struct sway_node *node = instruction->node;
176
177 // Damage the old location
178 desktop_damage_whole_container(container);
179 if (container->type == C_VIEW && container->sway_view->saved_buffer) {
180 struct sway_view *view = container->sway_view;
181 struct wlr_box box = {
182 .x = container->current.view_x - view->saved_geometry.x,
183 .y = container->current.view_y - view->saved_geometry.y,
184 .width = view->saved_buffer_width,
185 .height = view->saved_buffer_height,
186 };
187 desktop_damage_box(&box);
188 }
189
190 // There are separate children lists for each instruction state, the
191 // container's current state and the container's pending state
192 // (ie. con->children). The list itself needs to be freed here.
193 // Any child containers which are being deleted will be cleaned up in
194 // transaction_destroy().
195 list_free(container->current.children);
196 list_free(container->current.ws_floating);
197
198 memcpy(&container->current, &instruction->state,
199 sizeof(struct sway_container_state));
200
201 if (container->type == C_VIEW && container->sway_view->saved_buffer) {
202 if (!container->destroying || container->ntxnrefs == 1) {
203 view_remove_saved_buffer(container->sway_view);
204 }
205 }
206 297
207 // Damage the new location 298 switch (node->type) {
208 desktop_damage_whole_container(container); 299 case N_ROOT:
209 if (container->type == C_VIEW && container->sway_view->surface) { 300 break;
210 struct sway_view *view = container->sway_view; 301 case N_OUTPUT:
211 struct wlr_surface *surface = view->surface; 302 apply_output_state(node->sway_output, instruction->output_state);
212 struct wlr_box box = { 303 break;
213 .x = container->current.view_x - view->geometry.x, 304 case N_WORKSPACE:
214 .y = container->current.view_y - view->geometry.y, 305 apply_workspace_state(node->sway_workspace,
215 .width = surface->current.width, 306 instruction->workspace_state);
216 .height = surface->current.height, 307 break;
217 }; 308 case N_CONTAINER:
218 desktop_damage_box(&box); 309 apply_container_state(node->sway_container,
310 instruction->container_state);
311 break;
219 } 312 }
220 313
221 container->instruction = NULL; 314 node->instruction = NULL;
222 if (container->type == C_CONTAINER || container->type == C_VIEW) {
223 container_discover_outputs(container);
224 }
225 } 315 }
226} 316}
227 317
228static void transaction_commit(struct sway_transaction *transaction); 318static void transaction_commit(struct sway_transaction *transaction);
229 319
230// Return true if both transactions operate on the same containers 320// Return true if both transactions operate on the same nodes
231static bool transaction_same_containers(struct sway_transaction *a, 321static bool transaction_same_nodes(struct sway_transaction *a,
232 struct sway_transaction *b) { 322 struct sway_transaction *b) {
233 if (a->instructions->length != b->instructions->length) { 323 if (a->instructions->length != b->instructions->length) {
234 return false; 324 return false;
@@ -236,7 +326,7 @@ static bool transaction_same_containers(struct sway_transaction *a,
236 for (int i = 0; i < a->instructions->length; ++i) { 326 for (int i = 0; i < a->instructions->length; ++i) {
237 struct sway_transaction_instruction *a_inst = a->instructions->items[i]; 327 struct sway_transaction_instruction *a_inst = a->instructions->items[i];
238 struct sway_transaction_instruction *b_inst = b->instructions->items[i]; 328 struct sway_transaction_instruction *b_inst = b->instructions->items[i];
239 if (a_inst->container != b_inst->container) { 329 if (a_inst->node != b_inst->node) {
240 return false; 330 return false;
241 } 331 }
242 } 332 }
@@ -267,7 +357,7 @@ static void transaction_progress_queue() {
267 while (server.transactions->length >= 2) { 357 while (server.transactions->length >= 2) {
268 struct sway_transaction *a = server.transactions->items[0]; 358 struct sway_transaction *a = server.transactions->items[0];
269 struct sway_transaction *b = server.transactions->items[1]; 359 struct sway_transaction *b = server.transactions->items[1];
270 if (transaction_same_containers(a, b)) { 360 if (transaction_same_nodes(a, b)) {
271 list_del(server.transactions, 0); 361 list_del(server.transactions, 0);
272 transaction_destroy(a); 362 transaction_destroy(a);
273 } else { 363 } else {
@@ -289,16 +379,18 @@ static int handle_timeout(void *data) {
289 return 0; 379 return 0;
290} 380}
291 381
292static bool should_configure(struct sway_container *con, 382static bool should_configure(struct sway_node *node,
293 struct sway_transaction_instruction *instruction) { 383 struct sway_transaction_instruction *instruction) {
294 if (con->type != C_VIEW) { 384 if (!node_is_view(node)) {
295 return false; 385 return false;
296 } 386 }
297 if (con->destroying) { 387 if (node->destroying) {
298 return false; 388 return false;
299 } 389 }
300 if (con->current.view_width == instruction->state.view_width && 390 struct sway_container_state *cstate = &node->sway_container->current;
301 con->current.view_height == instruction->state.view_height) { 391 struct sway_container_state *istate = instruction->container_state;
392 if (cstate->view_width == istate->view_width &&
393 cstate->view_height == istate->view_height) {
302 return false; 394 return false;
303 } 395 }
304 return true; 396 return true;
@@ -311,13 +403,13 @@ static void transaction_commit(struct sway_transaction *transaction) {
311 for (int i = 0; i < transaction->instructions->length; ++i) { 403 for (int i = 0; i < transaction->instructions->length; ++i) {
312 struct sway_transaction_instruction *instruction = 404 struct sway_transaction_instruction *instruction =
313 transaction->instructions->items[i]; 405 transaction->instructions->items[i];
314 struct sway_container *con = instruction->container; 406 struct sway_node *node = instruction->node;
315 if (should_configure(con, instruction)) { 407 if (should_configure(node, instruction)) {
316 instruction->serial = view_configure(con->sway_view, 408 instruction->serial = view_configure(node->sway_container->view,
317 instruction->state.view_x, 409 instruction->container_state->view_x,
318 instruction->state.view_y, 410 instruction->container_state->view_y,
319 instruction->state.view_width, 411 instruction->container_state->view_width,
320 instruction->state.view_height); 412 instruction->container_state->view_height);
321 ++transaction->num_waiting; 413 ++transaction->num_waiting;
322 414
323 // From here on we are rendering a saved buffer of the view, which 415 // From here on we are rendering a saved buffer of the view, which
@@ -325,14 +417,16 @@ static void transaction_commit(struct sway_transaction *transaction) {
325 // as soon as possible. Additionally, this is required if a view is 417 // as soon as possible. Additionally, this is required if a view is
326 // mapping and its default geometry doesn't intersect an output. 418 // mapping and its default geometry doesn't intersect an output.
327 struct timespec when; 419 struct timespec when;
328 wlr_surface_send_frame_done(con->sway_view->surface, &when); 420 wlr_surface_send_frame_done(
421 node->sway_container->view->surface, &when);
329 } 422 }
330 if (con->type == C_VIEW && !con->sway_view->saved_buffer) { 423 if (node_is_view(node) && !node->sway_container->view->saved_buffer) {
331 view_save_buffer(con->sway_view); 424 view_save_buffer(node->sway_container->view);
332 memcpy(&con->sway_view->saved_geometry, &con->sway_view->geometry, 425 memcpy(&node->sway_container->view->saved_geometry,
426 &node->sway_container->view->geometry,
333 sizeof(struct wlr_box)); 427 sizeof(struct wlr_box));
334 } 428 }
335 con->instruction = instruction; 429 node->instruction = instruction;
336 } 430 }
337 transaction->num_configures = transaction->num_waiting; 431 transaction->num_configures = transaction->num_waiting;
338 if (debug.txn_timings) { 432 if (debug.txn_timings) {
@@ -381,7 +475,7 @@ static void set_instruction_ready(
381 transaction, 475 transaction,
382 transaction->num_configures - transaction->num_waiting + 1, 476 transaction->num_configures - transaction->num_waiting + 1,
383 transaction->num_configures, ms, 477 transaction->num_configures, ms,
384 instruction->container->name); 478 instruction->node->sway_container->title);
385 } 479 }
386 480
387 // If the transaction has timed out then its num_waiting will be 0 already. 481 // If the transaction has timed out then its num_waiting will be 0 already.
@@ -390,41 +484,43 @@ static void set_instruction_ready(
390 wl_event_source_timer_update(transaction->timer, 0); 484 wl_event_source_timer_update(transaction->timer, 0);
391 } 485 }
392 486
393 instruction->container->instruction = NULL; 487 instruction->node->instruction = NULL;
394 transaction_progress_queue(); 488 transaction_progress_queue();
395} 489}
396 490
397void transaction_notify_view_ready_by_serial(struct sway_view *view, 491void transaction_notify_view_ready_by_serial(struct sway_view *view,
398 uint32_t serial) { 492 uint32_t serial) {
399 struct sway_transaction_instruction *instruction = view->swayc->instruction; 493 struct sway_transaction_instruction *instruction =
400 if (view->swayc->instruction->serial == serial) { 494 view->container->node.instruction;
495 if (instruction->serial == serial) {
401 set_instruction_ready(instruction); 496 set_instruction_ready(instruction);
402 } 497 }
403} 498}
404 499
405void transaction_notify_view_ready_by_size(struct sway_view *view, 500void transaction_notify_view_ready_by_size(struct sway_view *view,
406 int width, int height) { 501 int width, int height) {
407 struct sway_transaction_instruction *instruction = view->swayc->instruction; 502 struct sway_transaction_instruction *instruction =
408 if (instruction->state.view_width == width && 503 view->container->node.instruction;
409 instruction->state.view_height == height) { 504 if (instruction->container_state->view_width == width &&
505 instruction->container_state->view_height == height) {
410 set_instruction_ready(instruction); 506 set_instruction_ready(instruction);
411 } 507 }
412} 508}
413 509
414void transaction_commit_dirty(void) { 510void transaction_commit_dirty(void) {
415 if (!server.dirty_containers->length) { 511 if (!server.dirty_nodes->length) {
416 return; 512 return;
417 } 513 }
418 struct sway_transaction *transaction = transaction_create(); 514 struct sway_transaction *transaction = transaction_create();
419 if (!transaction) { 515 if (!transaction) {
420 return; 516 return;
421 } 517 }
422 for (int i = 0; i < server.dirty_containers->length; ++i) { 518 for (int i = 0; i < server.dirty_nodes->length; ++i) {
423 struct sway_container *container = server.dirty_containers->items[i]; 519 struct sway_node *node = server.dirty_nodes->items[i];
424 transaction_add_container(transaction, container); 520 transaction_add_node(transaction, node);
425 container->dirty = false; 521 node->dirty = false;
426 } 522 }
427 server.dirty_containers->length = 0; 523 server.dirty_nodes->length = 0;
428 524
429 list_add(server.transactions, transaction); 525 list_add(server.transactions, transaction);
430 526
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 587deb0f..e19d8e18 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -11,10 +11,12 @@
11#include "sway/desktop/transaction.h" 11#include "sway/desktop/transaction.h"
12#include "sway/input/input-manager.h" 12#include "sway/input/input-manager.h"
13#include "sway/input/seat.h" 13#include "sway/input/seat.h"
14#include "sway/output.h"
14#include "sway/server.h" 15#include "sway/server.h"
15#include "sway/tree/arrange.h" 16#include "sway/tree/arrange.h"
16#include "sway/tree/container.h" 17#include "sway/tree/container.h"
17#include "sway/tree/view.h" 18#include "sway/tree/view.h"
19#include "sway/tree/workspace.h"
18 20
19static const struct sway_view_child_impl popup_impl; 21static const struct sway_view_child_impl popup_impl;
20 22
@@ -52,15 +54,15 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
52 struct sway_view *view = popup->child.view; 54 struct sway_view *view = popup->child.view;
53 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; 55 struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
54 56
55 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 57 struct sway_output *output = view->container->workspace->output;
56 58
57 // the output box expressed in the coordinate system of the toplevel parent 59 // the output box expressed in the coordinate system of the toplevel parent
58 // of the popup 60 // of the popup
59 struct wlr_box output_toplevel_sx_box = { 61 struct wlr_box output_toplevel_sx_box = {
60 .x = output->x - view->x, 62 .x = output->wlr_output->lx - view->x,
61 .y = output->y - view->y, 63 .y = output->wlr_output->ly - view->y,
62 .width = output->width, 64 .width = output->wlr_output->width,
63 .height = output->height, 65 .height = output->wlr_output->height,
64 }; 66 };
65 67
66 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 68 wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
@@ -252,11 +254,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
252 struct sway_view *view = &xdg_shell_view->view; 254 struct sway_view *view = &xdg_shell_view->view;
253 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; 255 struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
254 256
255 if (!view->swayc) { 257 if (view->container->node.instruction) {
256 return;
257 }
258
259 if (view->swayc->instruction) {
260 wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry); 258 wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry);
261 transaction_notify_view_ready_by_serial(view, 259 transaction_notify_view_ready_by_serial(view,
262 xdg_surface->configure_serial); 260 xdg_surface->configure_serial);
@@ -265,7 +263,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
265 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 263 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
266 264
267 if ((new_geo.width != view->width || new_geo.height != view->height) && 265 if ((new_geo.width != view->width || new_geo.height != view->height) &&
268 container_is_floating(view->swayc)) { 266 container_is_floating(view->container)) {
269 // A floating view has unexpectedly sent a new size 267 // A floating view has unexpectedly sent a new size
270 desktop_damage_view(view); 268 desktop_damage_view(view);
271 view_update_size(view, new_geo.width, new_geo.height); 269 view_update_size(view, new_geo.width, new_geo.height);
@@ -319,10 +317,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
319 return; 317 return;
320 } 318 }
321 319
322 container_set_fullscreen(view->swayc, e->fullscreen); 320 container_set_fullscreen(view->container, e->fullscreen);
323 321
324 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 322 arrange_workspace(view->container->workspace);
325 arrange_windows(output);
326 transaction_commit_dirty(); 323 transaction_commit_dirty();
327} 324}
328 325
@@ -330,13 +327,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
330 struct sway_xdg_shell_view *xdg_shell_view = 327 struct sway_xdg_shell_view *xdg_shell_view =
331 wl_container_of(listener, xdg_shell_view, request_move); 328 wl_container_of(listener, xdg_shell_view, request_move);
332 struct sway_view *view = &xdg_shell_view->view; 329 struct sway_view *view = &xdg_shell_view->view;
333 if (!container_is_floating(view->swayc)) { 330 if (!container_is_floating(view->container)) {
334 return; 331 return;
335 } 332 }
336 struct wlr_xdg_toplevel_move_event *e = data; 333 struct wlr_xdg_toplevel_move_event *e = data;
337 struct sway_seat *seat = e->seat->seat->data; 334 struct sway_seat *seat = e->seat->seat->data;
338 if (e->serial == seat->last_button_serial) { 335 if (e->serial == seat->last_button_serial) {
339 seat_begin_move(seat, view->swayc, seat->last_button); 336 seat_begin_move(seat, view->container, seat->last_button);
340 } 337 }
341} 338}
342 339
@@ -344,13 +341,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
344 struct sway_xdg_shell_view *xdg_shell_view = 341 struct sway_xdg_shell_view *xdg_shell_view =
345 wl_container_of(listener, xdg_shell_view, request_resize); 342 wl_container_of(listener, xdg_shell_view, request_resize);
346 struct sway_view *view = &xdg_shell_view->view; 343 struct sway_view *view = &xdg_shell_view->view;
347 if (!container_is_floating(view->swayc)) { 344 if (!container_is_floating(view->container)) {
348 return; 345 return;
349 } 346 }
350 struct wlr_xdg_toplevel_resize_event *e = data; 347 struct wlr_xdg_toplevel_resize_event *e = data;
351 struct sway_seat *seat = e->seat->seat->data; 348 struct sway_seat *seat = e->seat->seat->data;
352 if (e->serial == seat->last_button_serial) { 349 if (e->serial == seat->last_button_serial) {
353 seat_begin_resize_floating(seat, view->swayc, 350 seat_begin_resize_floating(seat, view->container,
354 seat->last_button, e->edges); 351 seat->last_button, e->edges);
355 } 352 }
356} 353}
@@ -399,11 +396,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
399 view_map(view, view->wlr_xdg_surface->surface); 396 view_map(view, view->wlr_xdg_surface->surface);
400 397
401 if (xdg_surface->toplevel->client_pending.fullscreen) { 398 if (xdg_surface->toplevel->client_pending.fullscreen) {
402 container_set_fullscreen(view->swayc, true); 399 container_set_fullscreen(view->container, true);
403 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 400 arrange_workspace(view->container->workspace);
404 arrange_windows(ws);
405 } else { 401 } else {
406 arrange_windows(view->swayc->parent); 402 if (view->container->parent) {
403 arrange_container(view->container->parent);
404 } else {
405 arrange_workspace(view->container->workspace);
406 }
407 } 407 }
408 transaction_commit_dirty(); 408 transaction_commit_dirty();
409 409
@@ -440,8 +440,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
440 struct sway_xdg_shell_view *xdg_shell_view = 440 struct sway_xdg_shell_view *xdg_shell_view =
441 wl_container_of(listener, xdg_shell_view, destroy); 441 wl_container_of(listener, xdg_shell_view, destroy);
442 struct sway_view *view = &xdg_shell_view->view; 442 struct sway_view *view = &xdg_shell_view->view;
443 if (!sway_assert(view->swayc == NULL || view->swayc->destroying, 443 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
444 "Tried to destroy a mapped view")) {
445 return; 444 return;
446 } 445 }
447 wl_list_remove(&xdg_shell_view->destroy.link); 446 wl_list_remove(&xdg_shell_view->destroy.link);
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 175416f3..b23d4577 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -10,10 +10,12 @@
10#include "sway/desktop/transaction.h" 10#include "sway/desktop/transaction.h"
11#include "sway/input/input-manager.h" 11#include "sway/input/input-manager.h"
12#include "sway/input/seat.h" 12#include "sway/input/seat.h"
13#include "sway/output.h"
13#include "sway/server.h" 14#include "sway/server.h"
14#include "sway/tree/arrange.h" 15#include "sway/tree/arrange.h"
15#include "sway/tree/container.h" 16#include "sway/tree/container.h"
16#include "sway/tree/view.h" 17#include "sway/tree/view.h"
18#include "sway/tree/workspace.h"
17 19
18static const struct sway_view_child_impl popup_impl; 20static const struct sway_view_child_impl popup_impl;
19 21
@@ -51,15 +53,15 @@ static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) {
51 struct sway_view *view = popup->child.view; 53 struct sway_view *view = popup->child.view;
52 struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; 54 struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup;
53 55
54 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 56 struct sway_output *output = view->container->workspace->output;
55 57
56 // the output box expressed in the coordinate system of the toplevel parent 58 // the output box expressed in the coordinate system of the toplevel parent
57 // of the popup 59 // of the popup
58 struct wlr_box output_toplevel_sx_box = { 60 struct wlr_box output_toplevel_sx_box = {
59 .x = output->x - view->x, 61 .x = output->wlr_output->lx - view->x,
60 .y = output->y - view->y, 62 .y = output->wlr_output->ly - view->y,
61 .width = output->width, 63 .width = output->wlr_output->width,
62 .height = output->height, 64 .height = output->wlr_output->height,
63 }; 65 };
64 66
65 wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 67 wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
@@ -249,11 +251,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
249 struct sway_view *view = &xdg_shell_v6_view->view; 251 struct sway_view *view = &xdg_shell_v6_view->view;
250 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; 252 struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6;
251 253
252 if (!view->swayc) { 254 if (view->container->node.instruction) {
253 return;
254 }
255
256 if (view->swayc->instruction) {
257 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry); 255 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry);
258 transaction_notify_view_ready_by_serial(view, 256 transaction_notify_view_ready_by_serial(view,
259 xdg_surface_v6->configure_serial); 257 xdg_surface_v6->configure_serial);
@@ -262,7 +260,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
262 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo); 260 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo);
263 261
264 if ((new_geo.width != view->width || new_geo.height != view->height) && 262 if ((new_geo.width != view->width || new_geo.height != view->height) &&
265 container_is_floating(view->swayc)) { 263 container_is_floating(view->container)) {
266 // A floating view has unexpectedly sent a new size 264 // A floating view has unexpectedly sent a new size
267 desktop_damage_view(view); 265 desktop_damage_view(view);
268 view_update_size(view, new_geo.width, new_geo.height); 266 view_update_size(view, new_geo.width, new_geo.height);
@@ -316,10 +314,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
316 return; 314 return;
317 } 315 }
318 316
319 container_set_fullscreen(view->swayc, e->fullscreen); 317 container_set_fullscreen(view->container, e->fullscreen);
320 318
321 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 319 arrange_workspace(view->container->workspace);
322 arrange_windows(output);
323 transaction_commit_dirty(); 320 transaction_commit_dirty();
324} 321}
325 322
@@ -327,13 +324,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
327 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 324 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
328 wl_container_of(listener, xdg_shell_v6_view, request_move); 325 wl_container_of(listener, xdg_shell_v6_view, request_move);
329 struct sway_view *view = &xdg_shell_v6_view->view; 326 struct sway_view *view = &xdg_shell_v6_view->view;
330 if (!container_is_floating(view->swayc)) { 327 if (!container_is_floating(view->container)) {
331 return; 328 return;
332 } 329 }
333 struct wlr_xdg_toplevel_v6_move_event *e = data; 330 struct wlr_xdg_toplevel_v6_move_event *e = data;
334 struct sway_seat *seat = e->seat->seat->data; 331 struct sway_seat *seat = e->seat->seat->data;
335 if (e->serial == seat->last_button_serial) { 332 if (e->serial == seat->last_button_serial) {
336 seat_begin_move(seat, view->swayc, seat->last_button); 333 seat_begin_move(seat, view->container, seat->last_button);
337 } 334 }
338} 335}
339 336
@@ -341,13 +338,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
341 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 338 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
342 wl_container_of(listener, xdg_shell_v6_view, request_resize); 339 wl_container_of(listener, xdg_shell_v6_view, request_resize);
343 struct sway_view *view = &xdg_shell_v6_view->view; 340 struct sway_view *view = &xdg_shell_v6_view->view;
344 if (!container_is_floating(view->swayc)) { 341 if (!container_is_floating(view->container)) {
345 return; 342 return;
346 } 343 }
347 struct wlr_xdg_toplevel_v6_resize_event *e = data; 344 struct wlr_xdg_toplevel_v6_resize_event *e = data;
348 struct sway_seat *seat = e->seat->seat->data; 345 struct sway_seat *seat = e->seat->seat->data;
349 if (e->serial == seat->last_button_serial) { 346 if (e->serial == seat->last_button_serial) {
350 seat_begin_resize_floating(seat, view->swayc, 347 seat_begin_resize_floating(seat, view->container,
351 seat->last_button, e->edges); 348 seat->last_button, e->edges);
352 } 349 }
353} 350}
@@ -396,11 +393,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
396 view_map(view, view->wlr_xdg_surface_v6->surface); 393 view_map(view, view->wlr_xdg_surface_v6->surface);
397 394
398 if (xdg_surface->toplevel->client_pending.fullscreen) { 395 if (xdg_surface->toplevel->client_pending.fullscreen) {
399 container_set_fullscreen(view->swayc, true); 396 container_set_fullscreen(view->container, true);
400 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 397 arrange_workspace(view->container->workspace);
401 arrange_windows(ws);
402 } else { 398 } else {
403 arrange_windows(view->swayc->parent); 399 if (view->container->parent) {
400 arrange_container(view->container->parent);
401 } else {
402 arrange_workspace(view->container->workspace);
403 }
404 } 404 }
405 transaction_commit_dirty(); 405 transaction_commit_dirty();
406 406
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 94a30239..0d192b76 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -59,8 +59,7 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
59 wl_container_of(listener, surface, map); 59 wl_container_of(listener, surface, map);
60 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; 60 struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
61 61
62 wl_list_insert(root_container.sway_root->xwayland_unmanaged.prev, 62 wl_list_insert(root->xwayland_unmanaged.prev, &surface->link);
63 &surface->link);
64 63
65 wl_signal_add(&xsurface->surface->events.commit, &surface->commit); 64 wl_signal_add(&xsurface->surface->events.commit, &surface->commit);
66 surface->commit.notify = unmanaged_handle_commit; 65 surface->commit.notify = unmanaged_handle_commit;
@@ -90,11 +89,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
90 if (seat->wlr_seat->keyboard_state.focused_surface == 89 if (seat->wlr_seat->keyboard_state.focused_surface ==
91 xsurface->surface) { 90 xsurface->surface) {
92 // Restore focus 91 // Restore focus
93 struct sway_container *previous = 92 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
94 seat_get_focus_inactive(seat, &root_container);
95 if (previous) { 93 if (previous) {
96 // Hack to get seat to re-focus the return value of get_focus 94 // Hack to get seat to re-focus the return value of get_focus
97 seat_set_focus(seat, previous->parent); 95 seat_set_focus(seat, NULL);
98 seat_set_focus(seat, previous); 96 seat_set_focus(seat, previous);
99 } 97 }
100 } 98 }
@@ -299,7 +297,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
299 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; 297 struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
300 struct wlr_surface_state *state = &xsurface->surface->current; 298 struct wlr_surface_state *state = &xsurface->surface->current;
301 299
302 if (view->swayc->instruction) { 300 if (view->container->node.instruction) {
303 get_geometry(view, &view->geometry); 301 get_geometry(view, &view->geometry);
304 transaction_notify_view_ready_by_size(view, 302 transaction_notify_view_ready_by_size(view,
305 state->width, state->height); 303 state->width, state->height);
@@ -308,7 +306,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
308 get_geometry(view, &new_geo); 306 get_geometry(view, &new_geo);
309 307
310 if ((new_geo.width != view->width || new_geo.height != view->height) && 308 if ((new_geo.width != view->width || new_geo.height != view->height) &&
311 container_is_floating(view->swayc)) { 309 container_is_floating(view->container)) {
312 // A floating view has unexpectedly sent a new size 310 // A floating view has unexpectedly sent a new size
313 // eg. The Firefox "Save As" dialog when downloading a file 311 // eg. The Firefox "Save As" dialog when downloading a file
314 desktop_damage_view(view); 312 desktop_damage_view(view);
@@ -391,11 +389,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
391 view_map(view, xsurface->surface); 389 view_map(view, xsurface->surface);
392 390
393 if (xsurface->fullscreen) { 391 if (xsurface->fullscreen) {
394 container_set_fullscreen(view->swayc, true); 392 container_set_fullscreen(view->container, true);
395 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 393 arrange_workspace(view->container->workspace);
396 arrange_windows(ws);
397 } else { 394 } else {
398 arrange_windows(view->swayc->parent); 395 if (view->container->parent) {
396 arrange_container(view->container->parent);
397 } else {
398 arrange_workspace(view->container->workspace);
399 }
399 } 400 }
400 transaction_commit_dirty(); 401 transaction_commit_dirty();
401} 402}
@@ -411,13 +412,14 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
411 ev->width, ev->height); 412 ev->width, ev->height);
412 return; 413 return;
413 } 414 }
414 if (container_is_floating(view->swayc)) { 415 if (container_is_floating(view->container)) {
415 configure(view, view->swayc->current.view_x, 416 configure(view, view->container->current.view_x,
416 view->swayc->current.view_y, ev->width, ev->height); 417 view->container->current.view_y, ev->width, ev->height);
417 } else { 418 } else {
418 configure(view, view->swayc->current.view_x, 419 configure(view, view->container->current.view_x,
419 view->swayc->current.view_y, view->swayc->current.view_width, 420 view->container->current.view_y,
420 view->swayc->current.view_height); 421 view->container->current.view_width,
422 view->container->current.view_height);
421 } 423 }
422} 424}
423 425
@@ -429,10 +431,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
429 if (!xsurface->mapped) { 431 if (!xsurface->mapped) {
430 return; 432 return;
431 } 433 }
432 container_set_fullscreen(view->swayc, xsurface->fullscreen); 434 container_set_fullscreen(view->container, xsurface->fullscreen);
433 435
434 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 436 arrange_workspace(view->container->workspace);
435 arrange_windows(output);
436 transaction_commit_dirty(); 437 transaction_commit_dirty();
437} 438}
438 439
@@ -444,11 +445,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
444 if (!xsurface->mapped) { 445 if (!xsurface->mapped) {
445 return; 446 return;
446 } 447 }
447 if (!container_is_floating(view->swayc)) { 448 if (!container_is_floating(view->container)) {
448 return; 449 return;
449 } 450 }
450 struct sway_seat *seat = input_manager_current_seat(input_manager); 451 struct sway_seat *seat = input_manager_current_seat(input_manager);
451 seat_begin_move(seat, view->swayc, seat->last_button); 452 seat_begin_move(seat, view->container, seat->last_button);
452} 453}
453 454
454static void handle_request_resize(struct wl_listener *listener, void *data) { 455static void handle_request_resize(struct wl_listener *listener, void *data) {
@@ -459,12 +460,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
459 if (!xsurface->mapped) { 460 if (!xsurface->mapped) {
460 return; 461 return;
461 } 462 }
462 if (!container_is_floating(view->swayc)) { 463 if (!container_is_floating(view->container)) {
463 return; 464 return;
464 } 465 }
465 struct wlr_xwayland_resize_event *e = data; 466 struct wlr_xwayland_resize_event *e = data;
466 struct sway_seat *seat = input_manager_current_seat(input_manager); 467 struct sway_seat *seat = input_manager_current_seat(input_manager);
467 seat_begin_resize_floating(seat, view->swayc, seat->last_button, e->edges); 468 seat_begin_resize_floating(seat, view->container,
469 seat->last_button, e->edges);
468} 470}
469 471
470static void handle_request_activate(struct wl_listener *listener, void *data) { 472static void handle_request_activate(struct wl_listener *listener, void *data) {
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 00240e84..15993265 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -20,6 +20,7 @@
20#include "sway/layers.h" 20#include "sway/layers.h"
21#include "sway/output.h" 21#include "sway/output.h"
22#include "sway/tree/arrange.h" 22#include "sway/tree/arrange.h"
23#include "sway/tree/container.h"
23#include "sway/tree/root.h" 24#include "sway/tree/root.h"
24#include "sway/tree/view.h" 25#include "sway/tree/view.h"
25#include "sway/tree/workspace.h" 26#include "sway/tree/workspace.h"
@@ -50,15 +51,15 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output,
50} 51}
51 52
52/** 53/**
53 * Returns the container at the cursor's position. If there is a surface at that 54 * Returns the node at the cursor's position. If there is a surface at that
54 * location, it is stored in **surface (it may not be a view). 55 * location, it is stored in **surface (it may not be a view).
55 */ 56 */
56static struct sway_container *container_at_coords( 57static struct sway_node *node_at_coords(
57 struct sway_seat *seat, double lx, double ly, 58 struct sway_seat *seat, double lx, double ly,
58 struct wlr_surface **surface, double *sx, double *sy) { 59 struct wlr_surface **surface, double *sx, double *sy) {
59 // check for unmanaged views first 60 // check for unmanaged views first
60#ifdef HAVE_XWAYLAND 61#ifdef HAVE_XWAYLAND
61 struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; 62 struct wl_list *unmanaged = &root->xwayland_unmanaged;
62 struct sway_xwayland_unmanaged *unmanaged_surface; 63 struct sway_xwayland_unmanaged *unmanaged_surface;
63 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { 64 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
64 struct wlr_xwayland_surface *xsurface = 65 struct wlr_xwayland_surface *xsurface =
@@ -75,67 +76,64 @@ static struct sway_container *container_at_coords(
75 } 76 }
76#endif 77#endif
77 // find the output the cursor is on 78 // find the output the cursor is on
78 struct wlr_output_layout *output_layout =
79 root_container.sway_root->output_layout;
80 struct wlr_output *wlr_output = wlr_output_layout_output_at( 79 struct wlr_output *wlr_output = wlr_output_layout_output_at(
81 output_layout, lx, ly); 80 root->output_layout, lx, ly);
82 if (wlr_output == NULL) { 81 if (wlr_output == NULL) {
83 return NULL; 82 return NULL;
84 } 83 }
85 struct sway_output *output = wlr_output->data; 84 struct sway_output *output = wlr_output->data;
86 double ox = lx, oy = ly; 85 double ox = lx, oy = ly;
87 wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy); 86 wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
88 87
89 // find the focused workspace on the output for this seat 88 // find the focused workspace on the output for this seat
90 struct sway_container *ws = seat_get_focus_inactive(seat, output->swayc); 89 struct sway_workspace *ws = output_get_active_workspace(output);
91 if (ws && ws->type != C_WORKSPACE) {
92 ws = container_parent(ws, C_WORKSPACE);
93 }
94 if (!ws) {
95 return output->swayc;
96 }
97 90
98 if ((*surface = layer_surface_at(output, 91 if ((*surface = layer_surface_at(output,
99 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], 92 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
100 ox, oy, sx, sy))) { 93 ox, oy, sx, sy))) {
101 return ws; 94 return &ws->node;
102 } 95 }
103 if (ws->sway_workspace->fullscreen) { 96 if (ws->fullscreen) {
104 return tiling_container_at(ws->sway_workspace->fullscreen, lx, ly, 97 struct sway_container *con =
105 surface, sx, sy); 98 tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy);
99 if (con) {
100 return &con->node;
101 }
102 return NULL;
106 } 103 }
107 if ((*surface = layer_surface_at(output, 104 if ((*surface = layer_surface_at(output,
108 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], 105 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
109 ox, oy, sx, sy))) { 106 ox, oy, sx, sy))) {
110 return ws; 107 return &ws->node;
111 } 108 }
112 109
113 struct sway_container *c; 110 struct sway_container *c;
114 if ((c = container_at(ws, lx, ly, surface, sx, sy))) { 111 if ((c = container_at(ws, lx, ly, surface, sx, sy))) {
115 return c; 112 return &c->node;
116 } 113 }
117 114
118 if ((*surface = layer_surface_at(output, 115 if ((*surface = layer_surface_at(output,
119 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], 116 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
120 ox, oy, sx, sy))) { 117 ox, oy, sx, sy))) {
121 return ws; 118 return &ws->node;
122 } 119 }
123 if ((*surface = layer_surface_at(output, 120 if ((*surface = layer_surface_at(output,
124 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], 121 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
125 ox, oy, sx, sy))) { 122 ox, oy, sx, sy))) {
126 return ws; 123 return &ws->node;
127 } 124 }
128 125
129 c = seat_get_active_child(seat, output->swayc); 126 return &ws->node;
130 if (c) { 127}
131 return c;
132 }
133 if (!c && output->swayc->children->length) {
134 c = output->swayc->children->items[0];
135 return c;
136 }
137 128
138 return output->swayc; 129static struct sway_container *container_at_coords(struct sway_seat *seat,
130 double lx, double ly,
131 struct wlr_surface **surface, double *sx, double *sy) {
132 struct sway_node *node = node_at_coords(seat, lx, ly, surface, sx, sy);
133 if (node && node->type == N_CONTAINER) {
134 return node->sway_container;
135 }
136 return NULL;
139} 137}
140 138
141/** 139/**
@@ -160,13 +158,14 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
160 158
161 // Iterate the parents until we find one with the layout we want, 159 // Iterate the parents until we find one with the layout we want,
162 // then check if the child has siblings between it and the edge. 160 // then check if the child has siblings between it and the edge.
163 while (cont->type != C_OUTPUT) { 161 while (cont) {
164 if (cont->parent->layout == layout) { 162 if (container_parent_layout(cont) == layout) {
165 int index = list_find(cont->parent->children, cont); 163 list_t *siblings = container_get_siblings(cont);
164 int index = list_find(siblings, cont);
166 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { 165 if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {
167 return false; 166 return false;
168 } 167 }
169 if (index < cont->parent->children->length - 1 && 168 if (index < siblings->length - 1 &&
170 (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { 169 (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) {
171 return false; 170 return false;
172 } 171 }
@@ -178,10 +177,10 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
178 177
179static enum wlr_edges find_edge(struct sway_container *cont, 178static enum wlr_edges find_edge(struct sway_container *cont,
180 struct sway_cursor *cursor) { 179 struct sway_cursor *cursor) {
181 if (cont->type != C_VIEW) { 180 if (!cont->view) {
182 return WLR_EDGE_NONE; 181 return WLR_EDGE_NONE;
183 } 182 }
184 struct sway_view *view = cont->sway_view; 183 struct sway_view *view = cont->view;
185 if (view->border == B_NONE || !view->border_thickness || view->using_csd) { 184 if (view->border == B_NONE || !view->border_thickness || view->using_csd) {
186 return WLR_EDGE_NONE; 185 return WLR_EDGE_NONE;
187 } 186 }
@@ -219,7 +218,7 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont,
219static void handle_down_motion(struct sway_seat *seat, 218static void handle_down_motion(struct sway_seat *seat,
220 struct sway_cursor *cursor, uint32_t time_msec) { 219 struct sway_cursor *cursor, uint32_t time_msec) {
221 struct sway_container *con = seat->op_container; 220 struct sway_container *con = seat->op_container;
222 if (seat_is_input_allowed(seat, con->sway_view->surface)) { 221 if (seat_is_input_allowed(seat, con->view->surface)) {
223 double moved_x = cursor->cursor->x - seat->op_ref_lx; 222 double moved_x = cursor->cursor->x - seat->op_ref_lx;
224 double moved_y = cursor->cursor->y - seat->op_ref_ly; 223 double moved_y = cursor->cursor->y - seat->op_ref_ly;
225 double sx = seat->op_ref_con_lx + moved_x; 224 double sx = seat->op_ref_con_lx + moved_x;
@@ -260,8 +259,7 @@ static void calculate_floating_constraints(struct sway_container *con,
260 if (config->floating_maximum_width == -1) { // no maximum 259 if (config->floating_maximum_width == -1) { // no maximum
261 *max_width = INT_MAX; 260 *max_width = INT_MAX;
262 } else if (config->floating_maximum_width == 0) { // automatic 261 } else if (config->floating_maximum_width == 0) { // automatic
263 struct sway_container *ws = container_parent(con, C_WORKSPACE); 262 *max_width = con->workspace->width;
264 *max_width = ws->width;
265 } else { 263 } else {
266 *max_width = config->floating_maximum_width; 264 *max_width = config->floating_maximum_width;
267 } 265 }
@@ -269,8 +267,7 @@ static void calculate_floating_constraints(struct sway_container *con,
269 if (config->floating_maximum_height == -1) { // no maximum 267 if (config->floating_maximum_height == -1) { // no maximum
270 *max_height = INT_MAX; 268 *max_height = INT_MAX;
271 } else if (config->floating_maximum_height == 0) { // automatic 269 } else if (config->floating_maximum_height == 0) { // automatic
272 struct sway_container *ws = container_parent(con, C_WORKSPACE); 270 *max_height = con->workspace->height;
273 *max_height = ws->height;
274 } else { 271 } else {
275 *max_height = config->floating_maximum_height; 272 *max_height = config->floating_maximum_height;
276 } 273 }
@@ -314,9 +311,9 @@ static void handle_resize_floating_motion(struct sway_seat *seat,
314 height = fmax(min_height, fmin(height, max_height)); 311 height = fmax(min_height, fmin(height, max_height));
315 312
316 // Apply the view's min/max size 313 // Apply the view's min/max size
317 if (con->type == C_VIEW) { 314 if (con->view) {
318 double view_min_width, view_max_width, view_min_height, view_max_height; 315 double view_min_width, view_max_width, view_min_height, view_max_height;
319 view_get_constraints(con->sway_view, &view_min_width, &view_max_width, 316 view_get_constraints(con->view, &view_min_width, &view_max_width,
320 &view_min_height, &view_max_height); 317 &view_min_height, &view_max_height);
321 width = fmax(view_min_width, fmin(width, view_max_width)); 318 width = fmax(view_min_width, fmin(width, view_max_width));
322 height = fmax(view_min_height, fmin(height, view_max_height)); 319 height = fmax(view_min_height, fmin(height, view_max_height));
@@ -357,15 +354,15 @@ static void handle_resize_floating_motion(struct sway_seat *seat,
357 con->width += relative_grow_width; 354 con->width += relative_grow_width;
358 con->height += relative_grow_height; 355 con->height += relative_grow_height;
359 356
360 if (con->type == C_VIEW) { 357 if (con->view) {
361 struct sway_view *view = con->sway_view; 358 struct sway_view *view = con->view;
362 view->x += relative_grow_x; 359 view->x += relative_grow_x;
363 view->y += relative_grow_y; 360 view->y += relative_grow_y;
364 view->width += relative_grow_width; 361 view->width += relative_grow_width;
365 view->height += relative_grow_height; 362 view->height += relative_grow_height;
366 } 363 }
367 364
368 arrange_windows(con); 365 arrange_container(con);
369} 366}
370 367
371static void handle_resize_tiling_motion(struct sway_seat *seat, 368static void handle_resize_tiling_motion(struct sway_seat *seat,
@@ -435,44 +432,40 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
435 struct wlr_surface *surface = NULL; 432 struct wlr_surface *surface = NULL;
436 double sx, sy; 433 double sx, sy;
437 434
438 // Find the container beneath the pointer's previous position 435 // Find the node beneath the pointer's previous position
439 struct sway_container *prev_c = container_at_coords(seat, 436 struct sway_node *prev_node = node_at_coords(seat,
440 cursor->previous.x, cursor->previous.y, &surface, &sx, &sy); 437 cursor->previous.x, cursor->previous.y, &surface, &sx, &sy);
441 // Update the stored previous position 438 // Update the stored previous position
442 cursor->previous.x = cursor->cursor->x; 439 cursor->previous.x = cursor->cursor->x;
443 cursor->previous.y = cursor->cursor->y; 440 cursor->previous.y = cursor->cursor->y;
444 441
445 struct sway_container *c = container_at_coords(seat, 442 struct sway_node *node = node_at_coords(seat,
446 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 443 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
447 if (c && config->focus_follows_mouse && allow_refocusing) { 444 if (node && config->focus_follows_mouse && allow_refocusing) {
448 struct sway_container *focus = seat_get_focus(seat); 445 struct sway_node *focus = seat_get_focus(seat);
449 if (focus && c->type == C_WORKSPACE) { 446 if (focus && node->type == N_WORKSPACE) {
450 // Only follow the mouse if it would move to a new output 447 // Only follow the mouse if it would move to a new output
451 // Otherwise we'll focus the workspace, which is probably wrong 448 // Otherwise we'll focus the workspace, which is probably wrong
452 if (focus->type != C_OUTPUT) { 449 struct sway_output *focused_output = node_get_output(focus);
453 focus = container_parent(focus, C_OUTPUT); 450 struct sway_output *output = node_get_output(node);
454 } 451 if (output != focused_output) {
455 struct sway_container *output = c; 452 seat_set_focus_warp(seat, node, false, true);
456 if (output->type != C_OUTPUT) {
457 output = container_parent(c, C_OUTPUT);
458 }
459 if (output != focus) {
460 seat_set_focus_warp(seat, c, false, true);
461 } 453 }
462 } else if (c->type == C_VIEW) { 454 } else if (node->type == N_CONTAINER && node->sway_container->view) {
463 // Focus c if the following are true: 455 // Focus node if the following are true:
464 // - cursor is over a new view, i.e. entered a new window; and 456 // - cursor is over a new view, i.e. entered a new window; and
465 // - the new view is visible, i.e. not hidden in a stack or tab; and 457 // - the new view is visible, i.e. not hidden in a stack or tab; and
466 // - the seat does not have a keyboard grab 458 // - the seat does not have a keyboard grab
467 if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && 459 if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
468 c != prev_c && 460 node != prev_node &&
469 view_is_visible(c->sway_view)) { 461 view_is_visible(node->sway_container->view)) {
470 seat_set_focus_warp(seat, c, false, true); 462 seat_set_focus_warp(seat, node, false, true);
471 } else { 463 } else {
472 struct sway_container *next_focus = 464 struct sway_node *next_focus =
473 seat_get_focus_inactive(seat, &root_container); 465 seat_get_focus_inactive(seat, &root->node);
474 if (next_focus && next_focus->type == C_VIEW && 466 if (next_focus && next_focus->type == N_CONTAINER &&
475 view_is_visible(next_focus->sway_view)) { 467 node->sway_container->view &&
468 view_is_visible(next_focus->sway_container->view)) {
476 seat_set_focus_warp(seat, next_focus, false, true); 469 seat_set_focus_warp(seat, next_focus, false, true);
477 } 470 }
478 } 471 }
@@ -486,12 +479,12 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
486 if (client != cursor->image_client) { 479 if (client != cursor->image_client) {
487 cursor_set_image(cursor, "left_ptr", client); 480 cursor_set_image(cursor, "left_ptr", client);
488 } 481 }
489 } else if (c) { 482 } else if (node && node->type == N_CONTAINER) {
490 // Try a container's resize edge 483 // Try a node's resize edge
491 enum wlr_edges edge = find_resize_edge(c, cursor); 484 enum wlr_edges edge = find_resize_edge(node->sway_container, cursor);
492 if (edge == WLR_EDGE_NONE) { 485 if (edge == WLR_EDGE_NONE) {
493 cursor_set_image(cursor, "left_ptr", NULL); 486 cursor_set_image(cursor, "left_ptr", NULL);
494 } else if (container_is_floating(c)) { 487 } else if (container_is_floating(node->sway_container)) {
495 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); 488 cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);
496 } else { 489 } else {
497 if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { 490 if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) {
@@ -684,7 +677,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
684 // Handle tiling resize via border 677 // Handle tiling resize via border
685 if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && 678 if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED &&
686 !is_floating) { 679 !is_floating) {
687 seat_set_focus(seat, cont); 680 seat_set_focus(seat, &cont->node);
688 seat_begin_resize_tiling(seat, cont, button, edge); 681 seat_begin_resize_tiling(seat, cont, button, edge);
689 return; 682 return;
690 } 683 }
@@ -713,7 +706,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
713 image = "sw-resize"; 706 image = "sw-resize";
714 } 707 }
715 cursor_set_image(seat->cursor, image, NULL); 708 cursor_set_image(seat->cursor, image, NULL);
716 seat_set_focus(seat, cont); 709 seat_set_focus(seat, &cont->node);
717 seat_begin_resize_tiling(seat, cont, button, edge); 710 seat_begin_resize_tiling(seat, cont, button, edge);
718 return; 711 return;
719 } 712 }
@@ -725,7 +718,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
725 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 718 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
726 if (button == btn_move && state == WLR_BUTTON_PRESSED && 719 if (button == btn_move && state == WLR_BUTTON_PRESSED &&
727 (mod_pressed || on_titlebar)) { 720 (mod_pressed || on_titlebar)) {
728 while (cont->parent->type != C_WORKSPACE) { 721 while (cont->parent) {
729 cont = cont->parent; 722 cont = cont->parent;
730 } 723 }
731 seat_begin_move(seat, cont, button); 724 seat_begin_move(seat, cont, button);
@@ -747,7 +740,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
747 BTN_LEFT : BTN_RIGHT; 740 BTN_LEFT : BTN_RIGHT;
748 if (mod_pressed && button == btn_resize) { 741 if (mod_pressed && button == btn_resize) {
749 struct sway_container *floater = cont; 742 struct sway_container *floater = cont;
750 while (floater->parent->type != C_WORKSPACE) { 743 while (floater->parent) {
751 floater = floater->parent; 744 floater = floater->parent;
752 } 745 }
753 edge = 0; 746 edge = 0;
@@ -762,7 +755,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
762 755
763 // Handle mousedown on a container surface 756 // Handle mousedown on a container surface
764 if (surface && cont && state == WLR_BUTTON_PRESSED) { 757 if (surface && cont && state == WLR_BUTTON_PRESSED) {
765 seat_set_focus(seat, cont); 758 seat_set_focus(seat, &cont->node);
766 seat_pointer_notify_button(seat, time_msec, button, state); 759 seat_pointer_notify_button(seat, time_msec, button, state);
767 seat_begin_down(seat, cont, button, sx, sy); 760 seat_begin_down(seat, cont, button, sx, sy);
768 return; 761 return;
@@ -770,7 +763,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
770 763
771 // Handle clicking a container surface 764 // Handle clicking a container surface
772 if (cont) { 765 if (cont) {
773 seat_set_focus(seat, cont); 766 seat_set_focus(seat, &cont->node);
774 seat_pointer_notify_button(seat, time_msec, button, state); 767 seat_pointer_notify_button(seat, time_msec, button, state);
775 return; 768 return;
776 } 769 }
@@ -1025,8 +1018,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
1025 cursor->previous.y = wlr_cursor->y; 1018 cursor->previous.y = wlr_cursor->y;
1026 1019
1027 cursor->seat = seat; 1020 cursor->seat = seat;
1028 wlr_cursor_attach_output_layout(wlr_cursor, 1021 wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout);
1029 root_container.sway_root->output_layout);
1030 1022
1031 // input events 1023 // input events
1032 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); 1024 wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index c820e032..b4352c6a 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -293,12 +293,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)
293 struct sway_seat *seat; 293 struct sway_seat *seat;
294 wl_list_for_each(seat, &input_manager->seats, link) { 294 wl_list_for_each(seat, &input_manager->seats, link) {
295 seat_set_exclusive_client(seat, NULL); 295 seat_set_exclusive_client(seat, NULL);
296 struct sway_container *previous = seat_get_focus(seat); 296 struct sway_node *previous = seat_get_focus(seat);
297 if (previous) { 297 if (previous) {
298 wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous,
299 container_type_to_str(previous->type), previous->name);
300 // Hack to get seat to re-focus the return value of get_focus 298 // Hack to get seat to re-focus the return value of get_focus
301 seat_set_focus(seat, previous->parent); 299 seat_set_focus(seat, NULL);
302 seat_set_focus(seat, previous); 300 seat_set_focus(seat, previous);
303 } 301 }
304 } 302 }
@@ -369,10 +367,10 @@ struct sway_input_manager *input_manager_create(
369} 367}
370 368
371bool input_manager_has_focus(struct sway_input_manager *input, 369bool input_manager_has_focus(struct sway_input_manager *input,
372 struct sway_container *container) { 370 struct sway_node *node) {
373 struct sway_seat *seat = NULL; 371 struct sway_seat *seat = NULL;
374 wl_list_for_each(seat, &input->seats, link) { 372 wl_list_for_each(seat, &input->seats, link) {
375 if (seat_get_focus(seat) == container) { 373 if (seat_get_focus(seat) == node) {
376 return true; 374 return true;
377 } 375 }
378 } 376 }
@@ -381,10 +379,10 @@ bool input_manager_has_focus(struct sway_input_manager *input,
381} 379}
382 380
383void input_manager_set_focus(struct sway_input_manager *input, 381void input_manager_set_focus(struct sway_input_manager *input,
384 struct sway_container *container) { 382 struct sway_node *node) {
385 struct sway_seat *seat; 383 struct sway_seat *seat;
386 wl_list_for_each(seat, &input->seats, link) { 384 wl_list_for_each(seat, &input->seats, link) {
387 seat_set_focus(seat, container); 385 seat_set_focus(seat, node);
388 } 386 }
389} 387}
390 388
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 4b7c7893..2f7a3318 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -47,48 +47,36 @@ void seat_destroy(struct sway_seat *seat) {
47 seat_device_destroy(seat_device); 47 seat_device_destroy(seat_device);
48 } 48 }
49 sway_cursor_destroy(seat->cursor); 49 sway_cursor_destroy(seat->cursor);
50 wl_list_remove(&seat->new_container.link); 50 wl_list_remove(&seat->new_node.link);
51 wl_list_remove(&seat->new_drag_icon.link); 51 wl_list_remove(&seat->new_drag_icon.link);
52 wl_list_remove(&seat->link); 52 wl_list_remove(&seat->link);
53 wlr_seat_destroy(seat->wlr_seat); 53 wlr_seat_destroy(seat->wlr_seat);
54} 54}
55 55
56static struct sway_seat_container *seat_container_from_container( 56static struct sway_seat_node *seat_node_from_node(
57 struct sway_seat *seat, struct sway_container *con); 57 struct sway_seat *seat, struct sway_node *node);
58 58
59static void seat_container_destroy(struct sway_seat_container *seat_con) { 59static void seat_node_destroy(struct sway_seat_node *seat_node) {
60 struct sway_container *con = seat_con->container; 60 wl_list_remove(&seat_node->destroy.link);
61 struct sway_container *child = NULL; 61 wl_list_remove(&seat_node->link);
62 62 free(seat_node);
63 if (con->children != NULL) {
64 for (int i = 0; i < con->children->length; ++i) {
65 child = con->children->items[i];
66 struct sway_seat_container *seat_child =
67 seat_container_from_container(seat_con->seat, child);
68 seat_container_destroy(seat_child);
69 }
70 }
71
72 wl_list_remove(&seat_con->destroy.link);
73 wl_list_remove(&seat_con->link);
74 free(seat_con);
75} 63}
76 64
77/** 65/**
78 * Activate all views within this container recursively. 66 * Activate all views within this container recursively.
79 */ 67 */
80static void seat_send_activate(struct sway_container *con, 68static void seat_send_activate(struct sway_node *node, struct sway_seat *seat) {
81 struct sway_seat *seat) { 69 if (node_is_view(node)) {
82 if (con->type == C_VIEW) { 70 if (!seat_is_input_allowed(seat, node->sway_container->view->surface)) {
83 if (!seat_is_input_allowed(seat, con->sway_view->surface)) {
84 wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited"); 71 wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited");
85 return; 72 return;
86 } 73 }
87 view_set_activated(con->sway_view, true); 74 view_set_activated(node->sway_container->view, true);
88 } else { 75 } else {
89 for (int i = 0; i < con->children->length; ++i) { 76 list_t *children = node_get_children(node);
90 struct sway_container *child = con->children->items[i]; 77 for (int i = 0; i < children->length; ++i) {
91 seat_send_activate(child, seat); 78 struct sway_container *child = children->items[i];
79 seat_send_activate(&child->node, seat);
92 } 80 }
93 } 81 }
94} 82}
@@ -98,14 +86,15 @@ static void seat_send_activate(struct sway_container *con,
98 * If con is a container, set all child views as active and don't enable 86 * If con is a container, set all child views as active and don't enable
99 * keyboard input on any. 87 * keyboard input on any.
100 */ 88 */
101static void seat_send_focus(struct sway_container *con, 89static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
102 struct sway_seat *seat) { 90 seat_send_activate(node, seat);
103 seat_send_activate(con, seat);
104 91
105 if (con->type == C_VIEW 92 struct sway_view *view = node->type == N_CONTAINER ?
106 && seat_is_input_allowed(seat, con->sway_view->surface)) { 93 node->sway_container->view : NULL;
94
95 if (view && seat_is_input_allowed(seat, view->surface)) {
107#ifdef HAVE_XWAYLAND 96#ifdef HAVE_XWAYLAND
108 if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { 97 if (view->type == SWAY_VIEW_XWAYLAND) {
109 struct wlr_xwayland *xwayland = 98 struct wlr_xwayland *xwayland =
110 seat->input->server->xwayland.wlr_xwayland; 99 seat->input->server->xwayland.wlr_xwayland;
111 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 100 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
@@ -114,71 +103,67 @@ static void seat_send_focus(struct sway_container *con,
114 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); 103 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
115 if (keyboard) { 104 if (keyboard) {
116 wlr_seat_keyboard_notify_enter(seat->wlr_seat, 105 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
117 con->sway_view->surface, keyboard->keycodes, 106 view->surface, keyboard->keycodes,
118 keyboard->num_keycodes, &keyboard->modifiers); 107 keyboard->num_keycodes, &keyboard->modifiers);
119 } else { 108 } else {
120 wlr_seat_keyboard_notify_enter( 109 wlr_seat_keyboard_notify_enter(
121 seat->wlr_seat, con->sway_view->surface, NULL, 0, NULL); 110 seat->wlr_seat, view->surface, NULL, 0, NULL);
122 } 111 }
123 } 112 }
124} 113}
125 114
126void seat_focus_inactive_children_for_each(struct sway_seat *seat, 115void seat_for_each_node(struct sway_seat *seat,
127 struct sway_container *container, 116 void (*f)(struct sway_node *node, void *data), void *data) {
128 void (*f)(struct sway_container *container, void *data), void *data) { 117 struct sway_seat_node *current = NULL;
129 struct sway_seat_container *current = NULL;
130 wl_list_for_each(current, &seat->focus_stack, link) { 118 wl_list_for_each(current, &seat->focus_stack, link) {
131 if (current->container->parent == NULL) { 119 f(current->node, data);
132 continue;
133 }
134 if (current->container->parent == container) {
135 f(current->container, data);
136 }
137 } 120 }
138} 121}
139 122
140struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, 123struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
141 struct sway_container *ancestor) { 124 struct sway_node *ancestor) {
142 if (ancestor->type == C_VIEW) { 125 if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) {
143 return ancestor; 126 return ancestor->sway_container;
144 } 127 }
145 struct sway_seat_container *current; 128 struct sway_seat_node *current;
146 wl_list_for_each(current, &seat->focus_stack, link) { 129 wl_list_for_each(current, &seat->focus_stack, link) {
147 struct sway_container *con = current->container; 130 struct sway_node *node = current->node;
148 if (con->type == C_VIEW && container_has_ancestor(con, ancestor)) { 131 if (node->type == N_CONTAINER && node->sway_container->view &&
149 return con; 132 node_has_ancestor(node, ancestor)) {
133 return node->sway_container;
150 } 134 }
151 } 135 }
152 return NULL; 136 return NULL;
153} 137}
154 138
155static void handle_seat_container_destroy(struct wl_listener *listener, 139static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
156 void *data) { 140 struct sway_seat_node *seat_node =
157 struct sway_seat_container *seat_con = 141 wl_container_of(listener, seat_node, destroy);
158 wl_container_of(listener, seat_con, destroy); 142 struct sway_seat *seat = seat_node->seat;
159 struct sway_seat *seat = seat_con->seat; 143 struct sway_node *node = seat_node->node;
160 struct sway_container *con = seat_con->container; 144 struct sway_node *parent = node_get_parent(node);
161 struct sway_container *parent = con->parent; 145 struct sway_node *focus = seat_get_focus(seat);
162 struct sway_container *focus = seat_get_focus(seat);
163 146
164 bool set_focus = 147 bool set_focus =
165 focus != NULL && 148 focus != NULL &&
166 (focus == con || container_has_ancestor(focus, con)) && 149 (focus == node || node_has_ancestor(focus, node)) &&
167 con->type != C_WORKSPACE; 150 node->type == N_CONTAINER;
168 151
169 seat_container_destroy(seat_con); 152 seat_node_destroy(seat_node);
170 153
171 if (set_focus) { 154 if (set_focus) {
172 struct sway_container *next_focus = NULL; 155 struct sway_node *next_focus = NULL;
173 while (next_focus == NULL) { 156 while (next_focus == NULL) {
174 next_focus = seat_get_focus_inactive_view(seat, parent); 157 struct sway_container *con =
158 seat_get_focus_inactive_view(seat, parent);
159 next_focus = con ? &con->node : NULL;
175 160
176 if (next_focus == NULL && parent->type == C_WORKSPACE) { 161 if (next_focus == NULL && parent->type == N_WORKSPACE) {
177 next_focus = parent; 162 next_focus = parent;
178 break; 163 break;
179 } 164 }
180 165
181 parent = parent->parent; 166 parent = node_get_parent(parent);
182 } 167 }
183 168
184 // the structure change might have caused it to move up to the top of 169 // the structure change might have caused it to move up to the top of
@@ -191,39 +176,39 @@ static void handle_seat_container_destroy(struct wl_listener *listener,
191 } 176 }
192} 177}
193 178
194static struct sway_seat_container *seat_container_from_container( 179static struct sway_seat_node *seat_node_from_node(
195 struct sway_seat *seat, struct sway_container *con) { 180 struct sway_seat *seat, struct sway_node *node) {
196 if (con->type == C_ROOT || con->type == C_OUTPUT) { 181 if (node->type == N_ROOT || node->type == N_OUTPUT) {
197 // these don't get seat containers ever 182 // these don't get seat nodes ever
198 return NULL; 183 return NULL;
199 } 184 }
200 185
201 struct sway_seat_container *seat_con = NULL; 186 struct sway_seat_node *seat_node = NULL;
202 wl_list_for_each(seat_con, &seat->focus_stack, link) { 187 wl_list_for_each(seat_node, &seat->focus_stack, link) {
203 if (seat_con->container == con) { 188 if (seat_node->node == node) {
204 return seat_con; 189 return seat_node;
205 } 190 }
206 } 191 }
207 192
208 seat_con = calloc(1, sizeof(struct sway_seat_container)); 193 seat_node = calloc(1, sizeof(struct sway_seat_node));
209 if (seat_con == NULL) { 194 if (seat_node == NULL) {
210 wlr_log(WLR_ERROR, "could not allocate seat container"); 195 wlr_log(WLR_ERROR, "could not allocate seat node");
211 return NULL; 196 return NULL;
212 } 197 }
213 198
214 seat_con->container = con; 199 seat_node->node = node;
215 seat_con->seat = seat; 200 seat_node->seat = seat;
216 wl_list_insert(seat->focus_stack.prev, &seat_con->link); 201 wl_list_insert(seat->focus_stack.prev, &seat_node->link);
217 wl_signal_add(&con->events.destroy, &seat_con->destroy); 202 wl_signal_add(&node->events.destroy, &seat_node->destroy);
218 seat_con->destroy.notify = handle_seat_container_destroy; 203 seat_node->destroy.notify = handle_seat_node_destroy;
219 204
220 return seat_con; 205 return seat_node;
221} 206}
222 207
223static void handle_new_container(struct wl_listener *listener, void *data) { 208static void handle_new_node(struct wl_listener *listener, void *data) {
224 struct sway_seat *seat = wl_container_of(listener, seat, new_container); 209 struct sway_seat *seat = wl_container_of(listener, seat, new_node);
225 struct sway_container *con = data; 210 struct sway_node *node = data;
226 seat_container_from_container(seat, con); 211 seat_node_from_node(seat, node);
227} 212}
228 213
229static void drag_icon_damage_whole(struct sway_drag_icon *icon) { 214static void drag_icon_damage_whole(struct sway_drag_icon *icon) {
@@ -272,8 +257,7 @@ static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) {
272 drag_icon_damage_whole(icon); 257 drag_icon_damage_whole(icon);
273} 258}
274 259
275static void drag_icon_handle_destroy(struct wl_listener *listener, 260static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) {
276 void *data) {
277 struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); 261 struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy);
278 icon->wlr_drag_icon->data = NULL; 262 icon->wlr_drag_icon->data = NULL;
279 wl_list_remove(&icon->link); 263 wl_list_remove(&icon->link);
@@ -305,20 +289,29 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) {
305 icon->destroy.notify = drag_icon_handle_destroy; 289 icon->destroy.notify = drag_icon_handle_destroy;
306 wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); 290 wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy);
307 291
308 wl_list_insert(&root_container.sway_root->drag_icons, &icon->link); 292 wl_list_insert(&root->drag_icons, &icon->link);
309 293
310 drag_icon_update_position(icon); 294 drag_icon_update_position(icon);
311} 295}
312 296
313static void collect_focus_iter(struct sway_container *con, void *data) { 297static void collect_focus_iter(struct sway_node *node, void *data) {
314 struct sway_seat *seat = data; 298 struct sway_seat *seat = data;
315 struct sway_seat_container *seat_con = 299 struct sway_seat_node *seat_node = seat_node_from_node(seat, node);
316 seat_container_from_container(seat, con); 300 if (!seat_node) {
317 if (!seat_con) {
318 return; 301 return;
319 } 302 }
320 wl_list_remove(&seat_con->link); 303 wl_list_remove(&seat_node->link);
321 wl_list_insert(&seat->focus_stack, &seat_con->link); 304 wl_list_insert(&seat->focus_stack, &seat_node->link);
305}
306
307static void collect_focus_workspace_iter(struct sway_workspace *workspace,
308 void *data) {
309 collect_focus_iter(&workspace->node, data);
310}
311
312static void collect_focus_container_iter(struct sway_container *container,
313 void *data) {
314 collect_focus_iter(&container->node, data);
322} 315}
323 316
324struct sway_seat *seat_create(struct sway_input_manager *input, 317struct sway_seat *seat_create(struct sway_input_manager *input,
@@ -345,12 +338,11 @@ struct sway_seat *seat_create(struct sway_input_manager *input,
345 // init the focus stack 338 // init the focus stack
346 wl_list_init(&seat->focus_stack); 339 wl_list_init(&seat->focus_stack);
347 340
348 root_for_each_workspace(collect_focus_iter, seat); 341 root_for_each_workspace(collect_focus_workspace_iter, seat);
349 root_for_each_container(collect_focus_iter, seat); 342 root_for_each_container(collect_focus_container_iter, seat);
350 343
351 wl_signal_add(&root_container.sway_root->events.new_container, 344 wl_signal_add(&root->events.new_node, &seat->new_node);
352 &seat->new_container); 345 seat->new_node.notify = handle_new_node;
353 seat->new_container.notify = handle_new_container;
354 346
355 wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon); 347 wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon);
356 seat->new_drag_icon.notify = handle_new_drag_icon; 348 seat->new_drag_icon.notify = handle_new_drag_icon;
@@ -388,19 +380,11 @@ static void seat_apply_input_config(struct sway_seat *seat,
388 if (mapped_to_output != NULL) { 380 if (mapped_to_output != NULL) {
389 wlr_log(WLR_DEBUG, "Mapping input device %s to output %s", 381 wlr_log(WLR_DEBUG, "Mapping input device %s to output %s",
390 sway_device->input_device->identifier, mapped_to_output); 382 sway_device->input_device->identifier, mapped_to_output);
391 struct sway_container *output = NULL; 383 struct sway_output *output = output_by_name(mapped_to_output);
392 for (int i = 0; i < root_container.children->length; ++i) {
393 struct sway_container *_output = root_container.children->items[i];
394 if (strcasecmp(_output->name, mapped_to_output) == 0) {
395 output = _output;
396 break;
397 }
398 }
399 if (output) { 384 if (output) {
400 wlr_cursor_map_input_to_output(seat->cursor->cursor, 385 wlr_cursor_map_input_to_output(seat->cursor->cursor,
401 sway_device->input_device->wlr_device, 386 sway_device->input_device->wlr_device, output->wlr_output);
402 output->sway_output->wlr_output); 387 wlr_log(WLR_DEBUG, "Mapped to output %s", output->wlr_output->name);
403 wlr_log(WLR_DEBUG, "Mapped to output %s", output->name);
404 } 388 }
405 } 389 }
406} 390}
@@ -423,12 +407,12 @@ static void seat_configure_keyboard(struct sway_seat *seat,
423 sway_keyboard_configure(seat_device->keyboard); 407 sway_keyboard_configure(seat_device->keyboard);
424 wlr_seat_set_keyboard(seat->wlr_seat, 408 wlr_seat_set_keyboard(seat->wlr_seat,
425 seat_device->input_device->wlr_device); 409 seat_device->input_device->wlr_device);
426 struct sway_container *focus = seat_get_focus(seat); 410 struct sway_node *focus = seat_get_focus(seat);
427 if (focus && focus->type == C_VIEW) { 411 if (focus && node_is_view(focus)) {
428 // force notify reenter to pick up the new configuration 412 // force notify reenter to pick up the new configuration
429 wlr_seat_keyboard_clear_focus(seat->wlr_seat); 413 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
430 wlr_seat_keyboard_notify_enter(seat->wlr_seat, 414 wlr_seat_keyboard_notify_enter(seat->wlr_seat,
431 focus->sway_view->surface, wlr_keyboard->keycodes, 415 focus->sway_container->view->surface, wlr_keyboard->keycodes,
432 wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers); 416 wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers);
433 } 417 }
434} 418}
@@ -461,8 +445,7 @@ static struct sway_seat_device *seat_get_device(struct sway_seat *seat,
461 445
462void seat_configure_device(struct sway_seat *seat, 446void seat_configure_device(struct sway_seat *seat,
463 struct sway_input_device *input_device) { 447 struct sway_input_device *input_device) {
464 struct sway_seat_device *seat_device = 448 struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
465 seat_get_device(seat, input_device);
466 if (!seat_device) { 449 if (!seat_device) {
467 return; 450 return;
468 } 451 }
@@ -512,8 +495,7 @@ void seat_add_device(struct sway_seat *seat,
512 495
513void seat_remove_device(struct sway_seat *seat, 496void seat_remove_device(struct sway_seat *seat,
514 struct sway_input_device *input_device) { 497 struct sway_input_device *input_device) {
515 struct sway_seat_device *seat_device = 498 struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
516 seat_get_device(seat, input_device);
517 499
518 if (!seat_device) { 500 if (!seat_device) {
519 return; 501 return;
@@ -539,11 +521,9 @@ void seat_configure_xcursor(struct sway_seat *seat) {
539 } 521 }
540 } 522 }
541 523
542 for (int i = 0; i < root_container.children->length; ++i) { 524 for (int i = 0; i < root->outputs->length; ++i) {
543 struct sway_container *output_container = 525 struct sway_output *sway_output = root->outputs->items[i];
544 root_container.children->items[i]; 526 struct wlr_output *output = sway_output->wlr_output;
545 struct wlr_output *output =
546 output_container->sway_output->wlr_output;
547 bool result = 527 bool result =
548 wlr_xcursor_manager_load(seat->cursor->xcursor_manager, 528 wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
549 output->scale); 529 output->scale);
@@ -566,17 +546,20 @@ bool seat_is_input_allowed(struct sway_seat *seat,
566 return !seat->exclusive_client || seat->exclusive_client == client; 546 return !seat->exclusive_client || seat->exclusive_client == client;
567} 547}
568 548
549static void send_unfocus(struct sway_container *con, void *data) {
550 if (con->view) {
551 view_set_activated(con->view, false);
552 }
553}
554
569// Unfocus the container and any children (eg. when leaving `focus parent`) 555// Unfocus the container and any children (eg. when leaving `focus parent`)
570static void seat_send_unfocus(struct sway_container *container, 556static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) {
571 struct sway_seat *seat) { 557 wlr_seat_keyboard_clear_focus(seat->wlr_seat);
572 if (container->type == C_VIEW) { 558 if (node->type == N_WORKSPACE) {
573 wlr_seat_keyboard_clear_focus(seat->wlr_seat); 559 workspace_for_each_container(node->sway_workspace, send_unfocus, seat);
574 view_set_activated(container->sway_view, false);
575 } else { 560 } else {
576 for (int i = 0; i < container->children->length; ++i) { 561 send_unfocus(node->sway_container, seat);
577 struct sway_container *child = container->children->items[i]; 562 container_for_each_child(node->sway_container, send_unfocus, seat);
578 seat_send_unfocus(child, seat);
579 }
580 } 563 }
581} 564}
582 565
@@ -586,26 +569,23 @@ static int handle_urgent_timeout(void *data) {
586 return 0; 569 return 0;
587} 570}
588 571
589void seat_set_focus_warp(struct sway_seat *seat, 572void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node,
590 struct sway_container *container, bool warp, bool notify) { 573 bool warp, bool notify) {
591 if (seat->focused_layer) { 574 if (seat->focused_layer) {
592 return; 575 return;
593 } 576 }
594 577
595 struct sway_container *last_focus = seat_get_focus(seat); 578 struct sway_node *last_focus = seat_get_focus(seat);
596 if (last_focus == container) { 579 if (last_focus == node) {
597 return; 580 return;
598 } 581 }
599 582
600 struct sway_container *last_workspace = last_focus; 583 struct sway_workspace *last_workspace = seat_get_focused_workspace(seat);
601 if (last_workspace && last_workspace->type != C_WORKSPACE) {
602 last_workspace = container_parent(last_workspace, C_WORKSPACE);
603 }
604 584
605 if (container == NULL) { 585 if (node == NULL) {
606 // Close any popups on the old focus 586 // Close any popups on the old focus
607 if (last_focus->type == C_VIEW) { 587 if (node_is_view(last_focus)) {
608 view_close_popups(last_focus->sway_view); 588 view_close_popups(last_focus->sway_container->view);
609 } 589 }
610 seat_send_unfocus(last_focus, seat); 590 seat_send_unfocus(last_focus, seat);
611 seat->has_focus = false; 591 seat->has_focus = false;
@@ -613,69 +593,70 @@ void seat_set_focus_warp(struct sway_seat *seat,
613 return; 593 return;
614 } 594 }
615 595
616 struct sway_container *new_workspace = container; 596 struct sway_workspace *new_workspace = node->type == N_WORKSPACE ?
617 if (new_workspace->type != C_WORKSPACE) { 597 node->sway_workspace : node->sway_container->workspace;
618 new_workspace = container_parent(new_workspace, C_WORKSPACE); 598 struct sway_container *container = node->type == N_CONTAINER ?
619 } 599 node->sway_container : NULL;
620 600
621 if (last_workspace == new_workspace 601 // Deny setting focus to a view which is hidden by a fullscreen container
622 && last_workspace->sway_workspace->fullscreen 602 if (new_workspace && new_workspace->fullscreen && container &&
623 && !container_is_fullscreen_or_child(container)) { 603 !container_is_fullscreen_or_child(container)) {
624 return; 604 return;
625 } 605 }
626 606
627 struct sway_container *last_output = last_focus; 607 struct sway_output *last_output = last_workspace ?
628 if (last_output && last_output->type != C_OUTPUT) { 608 last_workspace->output : NULL;
629 last_output = container_parent(last_output, C_OUTPUT); 609 struct sway_output *new_output = new_workspace->output;
630 }
631 struct sway_container *new_output = container;
632 if (new_output->type != C_OUTPUT) {
633 new_output = container_parent(new_output, C_OUTPUT);
634 }
635 610
636 // find new output's old workspace, which might have to be removed if empty 611 // find new output's old workspace, which might have to be removed if empty
637 struct sway_container *new_output_last_ws = NULL; 612 struct sway_workspace *new_output_last_ws = NULL;
638 if (new_output && last_output != new_output) { 613 if (new_output && last_output != new_output) {
639 new_output_last_ws = seat_get_active_child(seat, new_output); 614 new_output_last_ws = output_get_active_workspace(new_output);
640 } 615 }
641 616
642 if (container->parent) { 617 // Put the container parents on the focus stack, then the workspace, then
643 struct sway_seat_container *seat_con = 618 // the focused container.
644 seat_container_from_container(seat, container); 619 if (container) {
645 if (seat_con == NULL) { 620 struct sway_container *parent = container->parent;
646 return;
647 }
648
649 // put all the ancestors of this container on top of the focus stack
650 struct sway_seat_container *parent =
651 seat_container_from_container(seat, container->parent);
652 while (parent) { 621 while (parent) {
653 wl_list_remove(&parent->link); 622 struct sway_seat_node *seat_node =
654 wl_list_insert(&seat->focus_stack, &parent->link); 623 seat_node_from_node(seat, &parent->node);
655 container_set_dirty(parent->container); 624 wl_list_remove(&seat_node->link);
656 625 wl_list_insert(&seat->focus_stack, &seat_node->link);
657 parent = seat_container_from_container(seat, 626 node_set_dirty(&parent->node);
658 parent->container->parent); 627 parent = parent->parent;
659 } 628 }
660 629 }
661 wl_list_remove(&seat_con->link); 630 if (new_workspace) {
662 wl_list_insert(&seat->focus_stack, &seat_con->link); 631 struct sway_seat_node *seat_node =
632 seat_node_from_node(seat, &new_workspace->node);
633 wl_list_remove(&seat_node->link);
634 wl_list_insert(&seat->focus_stack, &seat_node->link);
635 node_set_dirty(&new_workspace->node);
636 }
637 if (container) {
638 struct sway_seat_node *seat_node =
639 seat_node_from_node(seat, &container->node);
640 wl_list_remove(&seat_node->link);
641 wl_list_insert(&seat->focus_stack, &seat_node->link);
642 node_set_dirty(&container->node);
663 643
664 if (last_focus) { 644 if (last_focus) {
665 seat_send_unfocus(last_focus, seat); 645 seat_send_unfocus(last_focus, seat);
666 container_set_dirty(last_focus); 646 node_set_dirty(last_focus);
647 struct sway_node *last_parent = node_get_parent(last_focus);
648 if (last_parent) {
649 node_set_dirty(last_parent);
650 }
667 } 651 }
668 seat_send_focus(container, seat); 652 seat_send_focus(&container->node, seat);
669
670 container_set_dirty(container);
671 container_set_dirty(container->parent); // for focused_inactive_child
672 } 653 }
673 654
674 // emit ipc events 655 // emit ipc events
675 if (notify && new_workspace && last_workspace != new_workspace) { 656 if (notify && new_workspace && last_workspace != new_workspace) {
676 ipc_event_workspace(last_workspace, new_workspace, "focus"); 657 ipc_event_workspace(last_workspace, new_workspace, "focus");
677 } 658 }
678 if (container->type == C_VIEW) { 659 if (container && container->view) {
679 ipc_event_window(container, "focus"); 660 ipc_event_window(container, "focus");
680 } 661 }
681 662
@@ -684,14 +665,14 @@ void seat_set_focus_warp(struct sway_seat *seat,
684 } 665 }
685 666
686 // Close any popups on the old focus 667 // Close any popups on the old focus
687 if (last_focus && last_focus->type == C_VIEW) { 668 if (last_focus && node_is_view(last_focus)) {
688 view_close_popups(last_focus->sway_view); 669 view_close_popups(last_focus->sway_container->view);
689 } 670 }
690 671
691 // If urgent, either unset the urgency or start a timer to unset it 672 // If urgent, either unset the urgency or start a timer to unset it
692 if (container->type == C_VIEW && view_is_urgent(container->sway_view) && 673 if (container && container->view && view_is_urgent(container->view) &&
693 !container->sway_view->urgent_timer) { 674 !container->view->urgent_timer) {
694 struct sway_view *view = container->sway_view; 675 struct sway_view *view = container->view;
695 if (last_workspace && last_workspace != new_workspace && 676 if (last_workspace && last_workspace != new_workspace &&
696 config->urgent_timeout > 0) { 677 config->urgent_timeout > 0) {
697 view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, 678 view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop,
@@ -711,12 +692,15 @@ void seat_set_focus_warp(struct sway_seat *seat,
711 692
712 // If we've focused a floating container, bring it to the front. 693 // If we've focused a floating container, bring it to the front.
713 // We do this by putting it at the end of the floating list. 694 // We do this by putting it at the end of the floating list.
714 struct sway_container *floater = container; 695 if (container) {
715 while (floater->parent && floater->parent->type != C_WORKSPACE) { 696 struct sway_container *floater = container;
716 floater = floater->parent; 697 while (floater->parent) {
717 } 698 floater = floater->parent;
718 if (container_is_floating(floater)) { 699 }
719 list_move_to_end(floater->parent->sway_workspace->floating, floater); 700 if (container_is_floating(floater)) {
701 list_move_to_end(floater->workspace->floating, floater);
702 node_set_dirty(&floater->workspace->node);
703 }
720 } 704 }
721 705
722 if (last_focus) { 706 if (last_focus) {
@@ -727,11 +711,8 @@ void seat_set_focus_warp(struct sway_seat *seat,
727 if (config->mouse_warping && warp && new_output != last_output) { 711 if (config->mouse_warping && warp && new_output != last_output) {
728 double x = container->x + container->width / 2.0; 712 double x = container->x + container->width / 2.0;
729 double y = container->y + container->height / 2.0; 713 double y = container->y + container->height / 2.0;
730 struct wlr_output *wlr_output = 714 if (!wlr_output_layout_contains_point(root->output_layout,
731 new_output->sway_output->wlr_output; 715 new_output->wlr_output, seat->cursor->cursor->x,
732 if (!wlr_output_layout_contains_point(
733 root_container.sway_root->output_layout,
734 wlr_output, seat->cursor->cursor->x,
735 seat->cursor->cursor->y)) { 716 seat->cursor->cursor->y)) {
736 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y); 717 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
737 cursor_send_pointer_motion(seat->cursor, 0, true); 718 cursor_send_pointer_motion(seat->cursor, 0, true);
@@ -744,9 +725,8 @@ void seat_set_focus_warp(struct sway_seat *seat,
744 update_debug_tree(); 725 update_debug_tree();
745} 726}
746 727
747void seat_set_focus(struct sway_seat *seat, 728void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
748 struct sway_container *container) { 729 seat_set_focus_warp(seat, node, true, true);
749 seat_set_focus_warp(seat, container, true, true);
750} 730}
751 731
752void seat_set_focus_surface(struct sway_seat *seat, 732void seat_set_focus_surface(struct sway_seat *seat,
@@ -755,12 +735,11 @@ void seat_set_focus_surface(struct sway_seat *seat,
755 return; 735 return;
756 } 736 }
757 if (seat->has_focus && unfocus) { 737 if (seat->has_focus && unfocus) {
758 struct sway_container *focus = seat_get_focus(seat); 738 struct sway_node *focus = seat_get_focus(seat);
759 seat_send_unfocus(focus, seat); 739 seat_send_unfocus(focus, seat);
760 seat->has_focus = false; 740 seat->has_focus = false;
761 } 741 }
762 struct wlr_keyboard *keyboard = 742 struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
763 wlr_seat_get_keyboard(seat->wlr_seat);
764 if (keyboard) { 743 if (keyboard) {
765 wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, 744 wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface,
766 keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); 745 keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
@@ -773,11 +752,8 @@ void seat_set_focus_layer(struct sway_seat *seat,
773 struct wlr_layer_surface *layer) { 752 struct wlr_layer_surface *layer) {
774 if (!layer && seat->focused_layer) { 753 if (!layer && seat->focused_layer) {
775 seat->focused_layer = NULL; 754 seat->focused_layer = NULL;
776 struct sway_container *previous = 755 struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
777 seat_get_focus_inactive(seat, &root_container);
778 if (previous) { 756 if (previous) {
779 wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous,
780 container_type_to_str(previous->type), previous->name);
781 // Hack to get seat to re-focus the return value of get_focus 757 // Hack to get seat to re-focus the return value of get_focus
782 seat_set_focus(seat, NULL); 758 seat_set_focus(seat, NULL);
783 seat_set_focus(seat, previous); 759 seat_set_focus(seat, previous);
@@ -798,13 +774,9 @@ void seat_set_exclusive_client(struct sway_seat *seat,
798 seat->exclusive_client = client; 774 seat->exclusive_client = client;
799 // Triggers a refocus of the topmost surface layer if necessary 775 // Triggers a refocus of the topmost surface layer if necessary
800 // TODO: Make layer surface focus per-output based on cursor position 776 // TODO: Make layer surface focus per-output based on cursor position
801 for (int i = 0; i < root_container.children->length; ++i) { 777 for (int i = 0; i < root->outputs->length; ++i) {
802 struct sway_container *output = root_container.children->items[i]; 778 struct sway_output *output = root->outputs->items[i];
803 if (!sway_assert(output->type == C_OUTPUT, 779 arrange_layers(output);
804 "root container has non-output child")) {
805 continue;
806 }
807 arrange_layers(output->sway_output);
808 } 780 }
809 return; 781 return;
810 } 782 }
@@ -814,9 +786,9 @@ void seat_set_exclusive_client(struct sway_seat *seat,
814 } 786 }
815 } 787 }
816 if (seat->has_focus) { 788 if (seat->has_focus) {
817 struct sway_container *focus = seat_get_focus(seat); 789 struct sway_node *focus = seat_get_focus(seat);
818 if (focus->type == C_VIEW && wl_resource_get_client( 790 if (node_is_view(focus) && wl_resource_get_client(
819 focus->sway_view->surface->resource) != client) { 791 focus->sway_container->view->surface->resource) != client) {
820 seat_set_focus(seat, NULL); 792 seat_set_focus(seat, NULL);
821 } 793 }
822 } 794 }
@@ -837,79 +809,101 @@ void seat_set_exclusive_client(struct sway_seat *seat,
837 seat->exclusive_client = client; 809 seat->exclusive_client = client;
838} 810}
839 811
840struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, 812struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
841 struct sway_container *con) { 813 struct sway_node *node) {
842 if (con->type == C_WORKSPACE && !con->children->length && 814 if (node_is_view(node)) {
843 !con->sway_workspace->floating->length) { 815 return node;
844 return con;
845 }
846 if (con->type == C_VIEW) {
847 return con;
848 } 816 }
849 struct sway_seat_container *current; 817 struct sway_seat_node *current;
850 wl_list_for_each(current, &seat->focus_stack, link) { 818 wl_list_for_each(current, &seat->focus_stack, link) {
851 if (container_has_ancestor(current->container, con)) { 819 if (node_has_ancestor(current->node, node)) {
852 return current->container; 820 return current->node;
853 } 821 }
854 } 822 }
823 if (node->type == N_WORKSPACE) {
824 return node;
825 }
855 return NULL; 826 return NULL;
856} 827}
857 828
858struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, 829struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,
859 struct sway_container *ancestor) { 830 struct sway_workspace *workspace) {
860 if (ancestor->type == C_WORKSPACE && !ancestor->children->length) { 831 if (!workspace->tiling->length) {
861 return ancestor; 832 return NULL;
862 } 833 }
863 struct sway_seat_container *current; 834 struct sway_seat_node *current;
864 wl_list_for_each(current, &seat->focus_stack, link) { 835 wl_list_for_each(current, &seat->focus_stack, link) {
865 struct sway_container *con = current->container; 836 struct sway_node *node = current->node;
866 if (!container_is_floating_or_child(con) && 837 if (node->type == N_CONTAINER &&
867 container_has_ancestor(current->container, ancestor)) { 838 !container_is_floating_or_child(node->sway_container) &&
868 return con; 839 node->sway_container->workspace == workspace) {
840 return node->sway_container;
869 } 841 }
870 } 842 }
871 return NULL; 843 return NULL;
872} 844}
873 845
874struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, 846struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
875 struct sway_container *ancestor) { 847 struct sway_workspace *workspace) {
876 if (ancestor->type == C_WORKSPACE && 848 if (!workspace->floating->length) {
877 !ancestor->sway_workspace->floating->length) {
878 return NULL; 849 return NULL;
879 } 850 }
880 struct sway_seat_container *current; 851 struct sway_seat_node *current;
881 wl_list_for_each(current, &seat->focus_stack, link) { 852 wl_list_for_each(current, &seat->focus_stack, link) {
882 struct sway_container *con = current->container; 853 struct sway_node *node = current->node;
883 if (container_is_floating_or_child(con) && 854 if (node->type == N_CONTAINER &&
884 container_has_ancestor(current->container, ancestor)) { 855 container_is_floating_or_child(node->sway_container) &&
885 return con; 856 node->sway_container->workspace == workspace) {
857 return node->sway_container;
886 } 858 }
887 } 859 }
888 return NULL; 860 return NULL;
889} 861}
890 862
891struct sway_container *seat_get_active_child(struct sway_seat *seat, 863struct sway_node *seat_get_active_child(struct sway_seat *seat,
892 struct sway_container *parent) { 864 struct sway_node *parent) {
893 if (parent->type == C_VIEW) { 865 if (node_is_view(parent)) {
894 return parent; 866 return parent;
895 } 867 }
896 struct sway_seat_container *current; 868 struct sway_seat_node *current;
897 wl_list_for_each(current, &seat->focus_stack, link) { 869 wl_list_for_each(current, &seat->focus_stack, link) {
898 struct sway_container *con = current->container; 870 struct sway_node *node = current->node;
899 if (con->parent == parent) { 871 if (node_get_parent(node) == parent) {
900 return con; 872 return node;
901 } 873 }
902 } 874 }
903 return NULL; 875 return NULL;
904} 876}
905 877
906struct sway_container *seat_get_focus(struct sway_seat *seat) { 878struct sway_node *seat_get_focus(struct sway_seat *seat) {
907 if (!seat->has_focus) { 879 if (!seat->has_focus) {
908 return NULL; 880 return NULL;
909 } 881 }
910 struct sway_seat_container *current = 882 struct sway_seat_node *current =
911 wl_container_of(seat->focus_stack.next, current, link); 883 wl_container_of(seat->focus_stack.next, current, link);
912 return current->container; 884 return current->node;
885}
886
887struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) {
888 struct sway_node *focus = seat_get_focus(seat);
889 if (!focus) {
890 return NULL;
891 }
892 if (focus->type == N_CONTAINER) {
893 return focus->sway_container->workspace;
894 }
895 if (focus->type == N_WORKSPACE) {
896 return focus->sway_workspace;
897 }
898 return NULL; // unreachable
899}
900
901struct sway_container *seat_get_focused_container(struct sway_seat *seat) {
902 struct sway_node *focus = seat_get_focus(seat);
903 if (focus && focus->type == N_CONTAINER) {
904 return focus->sway_container;
905 }
906 return NULL;
913} 907}
914 908
915void seat_apply_config(struct sway_seat *seat, 909void seat_apply_config(struct sway_seat *seat,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 06cb7e11..9a955991 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -46,18 +46,18 @@ json_object *ipc_json_get_version() {
46 return version; 46 return version;
47} 47}
48 48
49static json_object *ipc_json_create_rect(struct sway_container *c) { 49static json_object *ipc_json_create_rect(struct wlr_box *box) {
50 json_object *rect = json_object_new_object(); 50 json_object *rect = json_object_new_object();
51 51
52 json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x)); 52 json_object_object_add(rect, "x", json_object_new_int(box->x));
53 json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y)); 53 json_object_object_add(rect, "y", json_object_new_int(box->y));
54 json_object_object_add(rect, "width", json_object_new_int((int32_t)c->width)); 54 json_object_object_add(rect, "width", json_object_new_int(box->width));
55 json_object_object_add(rect, "height", json_object_new_int((int32_t)c->height)); 55 json_object_object_add(rect, "height", json_object_new_int(box->height));
56 56
57 return rect; 57 return rect;
58} 58}
59 59
60static void ipc_json_describe_root(struct sway_container *root, json_object *object) { 60static void ipc_json_describe_root(struct sway_root *root, json_object *object) {
61 json_object_object_add(object, "type", json_object_new_string("root")); 61 json_object_object_add(object, "type", json_object_new_string("root"));
62 json_object_object_add(object, "layout", json_object_new_string("splith")); 62 json_object_object_add(object, "layout", json_object_new_string("splith"));
63} 63}
@@ -84,17 +84,13 @@ static const char *ipc_json_get_output_transform(enum wl_output_transform transf
84 return NULL; 84 return NULL;
85} 85}
86 86
87static void ipc_json_describe_output(struct sway_container *container, 87static void ipc_json_describe_output(struct sway_output *output,
88 json_object *object) { 88 json_object *object) {
89 struct wlr_output *wlr_output = container->sway_output->wlr_output; 89 struct wlr_output *wlr_output = output->wlr_output;
90 json_object_object_add(object, "type", 90 json_object_object_add(object, "type", json_object_new_string("output"));
91 json_object_new_string("output")); 91 json_object_object_add(object, "active", json_object_new_boolean(true));
92 json_object_object_add(object, "active", 92 json_object_object_add(object, "primary", json_object_new_boolean(false));
93 json_object_new_boolean(true)); 93 json_object_object_add(object, "layout", json_object_new_string("output"));
94 json_object_object_add(object, "primary",
95 json_object_new_boolean(false));
96 json_object_object_add(object, "layout",
97 json_object_new_string("output"));
98 json_object_object_add(object, "make", 94 json_object_object_add(object, "make",
99 json_object_new_string(wlr_output->make)); 95 json_object_new_string(wlr_output->make));
100 json_object_object_add(object, "model", 96 json_object_object_add(object, "model",
@@ -109,20 +105,9 @@ static void ipc_json_describe_output(struct sway_container *container,
109 json_object_new_string( 105 json_object_new_string(
110 ipc_json_get_output_transform(wlr_output->transform))); 106 ipc_json_get_output_transform(wlr_output->transform)));
111 107
112 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 108 struct sway_workspace *ws = output_get_active_workspace(output);
113 const char *ws = NULL;
114 if (seat) {
115 struct sway_container *focus =
116 seat_get_focus_inactive(seat, container);
117 if (focus && focus->type != C_WORKSPACE) {
118 focus = container_parent(focus, C_WORKSPACE);
119 }
120 if (focus) {
121 ws = focus->name;
122 }
123 }
124 json_object_object_add(object, "current_workspace", 109 json_object_object_add(object, "current_workspace",
125 json_object_new_string(ws)); 110 json_object_new_string(ws->name));
126 111
127 json_object *modes_array = json_object_new_array(); 112 json_object *modes_array = json_object_new_array();
128 struct wlr_output_mode *mode; 113 struct wlr_output_mode *mode;
@@ -161,60 +146,57 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
161 return object; 146 return object;
162} 147}
163 148
164static void ipc_json_describe_workspace(struct sway_container *workspace, 149static void ipc_json_describe_workspace(struct sway_workspace *workspace,
165 json_object *object) { 150 json_object *object) {
166 int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; 151 int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1;
167 152
168 json_object_object_add(object, "num", json_object_new_int(num)); 153 json_object_object_add(object, "num", json_object_new_int(num));
169 json_object_object_add(object, "output", workspace->parent ? 154 json_object_object_add(object, "output", workspace->output ?
170 json_object_new_string(workspace->parent->name) : NULL); 155 json_object_new_string(workspace->output->wlr_output->name) : NULL);
171 json_object_object_add(object, "type", json_object_new_string("workspace")); 156 json_object_object_add(object, "type", json_object_new_string("workspace"));
172 json_object_object_add(object, "urgent", 157 json_object_object_add(object, "urgent",
173 json_object_new_boolean(workspace->sway_workspace->urgent)); 158 json_object_new_boolean(workspace->urgent));
174 json_object_object_add(object, "representation", workspace->formatted_title ? 159 json_object_object_add(object, "representation", workspace->representation ?
175 json_object_new_string(workspace->formatted_title) : NULL); 160 json_object_new_string(workspace->representation) : NULL);
176 161
177 const char *layout = ipc_json_layout_description(workspace->layout); 162 const char *layout = ipc_json_layout_description(workspace->layout);
178 json_object_object_add(object, "layout", json_object_new_string(layout)); 163 json_object_object_add(object, "layout", json_object_new_string(layout));
179 164
180 // Floating 165 // Floating
181 json_object *floating_array = json_object_new_array(); 166 json_object *floating_array = json_object_new_array();
182 list_t *floating = workspace->sway_workspace->floating; 167 for (int i = 0; i < workspace->floating->length; ++i) {
183 for (int i = 0; i < floating->length; ++i) { 168 struct sway_container *floater = workspace->floating->items[i];
184 struct sway_container *floater = floating->items[i];
185 json_object_array_add(floating_array, 169 json_object_array_add(floating_array,
186 ipc_json_describe_container_recursive(floater)); 170 ipc_json_describe_node_recursive(&floater->node));
187 } 171 }
188 json_object_object_add(object, "floating_nodes", floating_array); 172 json_object_object_add(object, "floating_nodes", floating_array);
189} 173}
190 174
191static void ipc_json_describe_view(struct sway_container *c, json_object *object) { 175static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
192 json_object_object_add(object, "name", 176 json_object_object_add(object, "name",
193 c->name ? json_object_new_string(c->name) : NULL); 177 c->title ? json_object_new_string(c->title) : NULL);
194 json_object_object_add(object, "type", json_object_new_string("con")); 178 json_object_object_add(object, "type", json_object_new_string("con"));
195 179
196 if (c->type == C_VIEW) { 180 if (c->view) {
197 const char *app_id = view_get_app_id(c->sway_view); 181 const char *app_id = view_get_app_id(c->view);
198 json_object_object_add(object, "app_id", 182 json_object_object_add(object, "app_id",
199 app_id ? json_object_new_string(app_id) : NULL); 183 app_id ? json_object_new_string(app_id) : NULL);
200 184
201 const char *class = view_get_class(c->sway_view); 185 const char *class = view_get_class(c->view);
202 json_object_object_add(object, "class", 186 json_object_object_add(object, "class",
203 class ? json_object_new_string(class) : NULL); 187 class ? json_object_new_string(class) : NULL);
204 } 188 }
205 189
206 if (c->parent) { 190 json_object_object_add(object, "layout",
207 json_object_object_add(object, "layout", 191 json_object_new_string(ipc_json_layout_description(c->layout)));
208 json_object_new_string(ipc_json_layout_description(c->layout)));
209 }
210 192
211 bool urgent = c->type == C_VIEW ? 193 bool urgent = c->view ?
212 view_is_urgent(c->sway_view) : container_has_urgent_child(c); 194 view_is_urgent(c->view) : container_has_urgent_child(c);
213 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 195 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
214 196
215 if (c->type == C_VIEW) { 197 if (c->view) {
216 json_object *marks = json_object_new_array(); 198 json_object *marks = json_object_new_array();
217 list_t *view_marks = c->sway_view->marks; 199 list_t *view_marks = c->view->marks;
218 for (int i = 0; i < view_marks->length; ++i) { 200 for (int i = 0; i < view_marks->length; ++i) {
219 json_object_array_add(marks, json_object_new_string(view_marks->items[i])); 201 json_object_array_add(marks, json_object_new_string(view_marks->items[i]));
220 } 202 }
@@ -222,64 +204,97 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
222 } 204 }
223} 205}
224 206
225static void focus_inactive_children_iterator(struct sway_container *c, void *data) { 207struct focus_inactive_data {
226 json_object *focus = data; 208 struct sway_node *node;
227 json_object_array_add(focus, json_object_new_int(c->id)); 209 json_object *object;
228} 210};
229 211
230json_object *ipc_json_describe_container(struct sway_container *c) { 212static void focus_inactive_children_iterator(struct sway_node *node,
231 if (!(sway_assert(c, "Container must not be null."))) { 213 void *_data) {
232 return NULL; 214 struct focus_inactive_data *data = _data;
215 if (node_get_parent(node) == data->node) {
216 json_object_array_add(data->object, json_object_new_int(node->id));
233 } 217 }
218}
234 219
220json_object *ipc_json_describe_node(struct sway_node *node) {
235 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 221 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
236 bool focused = seat_get_focus(seat) == c; 222 bool focused = seat_get_focus(seat) == node;
237 223
238 json_object *object = json_object_new_object(); 224 json_object *object = json_object_new_object();
225 char *name = node_get_name(node);
239 226
240 json_object_object_add(object, "id", json_object_new_int((int)c->id)); 227 struct wlr_box box;
228 node_get_box(node, &box);
229 json_object_object_add(object, "id", json_object_new_int((int)node->id));
241 json_object_object_add(object, "name", 230 json_object_object_add(object, "name",
242 c->name ? json_object_new_string(c->name) : NULL); 231 name ? json_object_new_string(name) : NULL);
243 json_object_object_add(object, "rect", ipc_json_create_rect(c)); 232 json_object_object_add(object, "rect", ipc_json_create_rect(&box));
244 json_object_object_add(object, "focused", 233 json_object_object_add(object, "focused", json_object_new_boolean(focused));
245 json_object_new_boolean(focused));
246 234
247 json_object *focus = json_object_new_array(); 235 json_object *focus = json_object_new_array();
248 seat_focus_inactive_children_for_each(seat, c, 236 struct focus_inactive_data data = {
249 focus_inactive_children_iterator, focus); 237 .node = node,
238 .object = focus,
239 };
240 seat_for_each_node(seat, focus_inactive_children_iterator, &data);
250 json_object_object_add(object, "focus", focus); 241 json_object_object_add(object, "focus", focus);
251 242
252 switch (c->type) { 243 switch (node->type) {
253 case C_ROOT: 244 case N_ROOT:
254 ipc_json_describe_root(c, object); 245 ipc_json_describe_root(root, object);
255 break; 246 break;
256 case C_OUTPUT: 247 case N_OUTPUT:
257 ipc_json_describe_output(c, object); 248 ipc_json_describe_output(node->sway_output, object);
258 break; 249 break;
259 case C_CONTAINER: 250 case N_CONTAINER:
260 case C_VIEW: 251 ipc_json_describe_view(node->sway_container, object);
261 ipc_json_describe_view(c, object);
262 break; 252 break;
263 case C_WORKSPACE: 253 case N_WORKSPACE:
264 ipc_json_describe_workspace(c, object); 254 ipc_json_describe_workspace(node->sway_workspace, object);
265 break;
266 case C_TYPES:
267 default:
268 break; 255 break;
269 } 256 }
270 257
271 return object; 258 return object;
272} 259}
273 260
274json_object *ipc_json_describe_container_recursive(struct sway_container *c) { 261json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
275 json_object *object = ipc_json_describe_container(c); 262 json_object *object = ipc_json_describe_node(node);
276 int i; 263 int i;
277 264
278 json_object *children = json_object_new_array(); 265 json_object *children = json_object_new_array();
279 if (c->type != C_VIEW && c->children) { 266 switch (node->type) {
280 for (i = 0; i < c->children->length; ++i) { 267 case N_ROOT:
281 json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i])); 268 for (i = 0; i < root->outputs->length; ++i) {
269 struct sway_output *output = root->outputs->items[i];
270 json_object_array_add(children,
271 ipc_json_describe_node_recursive(&output->node));
272 }
273 break;
274 case N_OUTPUT:
275 for (i = 0; i < node->sway_output->workspaces->length; ++i) {
276 struct sway_workspace *ws = node->sway_output->workspaces->items[i];
277 json_object_array_add(children,
278 ipc_json_describe_node_recursive(&ws->node));
282 } 279 }
280 break;
281 case N_WORKSPACE:
282 for (i = 0; i < node->sway_workspace->tiling->length; ++i) {
283 struct sway_container *con = node->sway_workspace->tiling->items[i];
284 json_object_array_add(children,
285 ipc_json_describe_node_recursive(&con->node));
286 }
287 break;
288 case N_CONTAINER:
289 if (node->sway_container->children) {
290 for (i = 0; i < node->sway_container->children->length; ++i) {
291 struct sway_container *child =
292 node->sway_container->children->items[i];
293 json_object_array_add(children,
294 ipc_json_describe_node_recursive(&child->node));
295 }
296 }
297 break;
283 } 298 }
284 json_object_object_add(object, "nodes", children); 299 json_object_object_add(object, "nodes", children);
285 300
@@ -329,7 +344,7 @@ json_object *ipc_json_describe_seat(struct sway_seat *seat) {
329 } 344 }
330 345
331 json_object *object = json_object_new_object(); 346 json_object *object = json_object_new_object();
332 struct sway_container *focus = seat_get_focus(seat); 347 struct sway_node *focus = seat_get_focus(seat);
333 348
334 json_object_object_add(object, "name", 349 json_object_object_add(object, "name",
335 json_object_new_string(seat->wlr_seat->name)); 350 json_object_new_string(seat->wlr_seat->name));
@@ -470,13 +485,13 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) {
470 json_object_object_add(json, "colors", colors); 485 json_object_object_add(json, "colors", colors);
471 486
472 // Add outputs if defined 487 // Add outputs if defined
488 json_object *outputs = json_object_new_array();
473 if (bar->outputs && bar->outputs->length > 0) { 489 if (bar->outputs && bar->outputs->length > 0) {
474 json_object *outputs = json_object_new_array();
475 for (int i = 0; i < bar->outputs->length; ++i) { 490 for (int i = 0; i < bar->outputs->length; ++i) {
476 const char *name = bar->outputs->items[i]; 491 const char *name = bar->outputs->items[i];
477 json_object_array_add(outputs, json_object_new_string(name)); 492 json_object_array_add(outputs, json_object_new_string(name));
478 } 493 }
479 json_object_object_add(json, "outputs", outputs);
480 } 494 }
495 json_object_object_add(json, "outputs", outputs);
481 return json; 496 return json;
482} 497}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index fb5be27b..8ae265f6 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -33,6 +33,7 @@
33#include "sway/input/seat.h" 33#include "sway/input/seat.h"
34#include "sway/tree/root.h" 34#include "sway/tree/root.h"
35#include "sway/tree/view.h" 35#include "sway/tree/view.h"
36#include "sway/tree/workspace.h"
36#include "list.h" 37#include "list.h"
37#include "log.h" 38#include "log.h"
38#include "util.h" 39#include "util.h"
@@ -291,8 +292,8 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event)
291 } 292 }
292} 293}
293 294
294void ipc_event_workspace(struct sway_container *old, 295void ipc_event_workspace(struct sway_workspace *old,
295 struct sway_container *new, const char *change) { 296 struct sway_workspace *new, const char *change) {
296 if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { 297 if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {
297 return; 298 return;
298 } 299 }
@@ -301,14 +302,14 @@ void ipc_event_workspace(struct sway_container *old,
301 json_object_object_add(obj, "change", json_object_new_string(change)); 302 json_object_object_add(obj, "change", json_object_new_string(change));
302 if (old) { 303 if (old) {
303 json_object_object_add(obj, "old", 304 json_object_object_add(obj, "old",
304 ipc_json_describe_container_recursive(old)); 305 ipc_json_describe_node_recursive(&old->node));
305 } else { 306 } else {
306 json_object_object_add(obj, "old", NULL); 307 json_object_object_add(obj, "old", NULL);
307 } 308 }
308 309
309 if (new) { 310 if (new) {
310 json_object_object_add(obj, "current", 311 json_object_object_add(obj, "current",
311 ipc_json_describe_container_recursive(new)); 312 ipc_json_describe_node_recursive(&new->node));
312 } else { 313 } else {
313 json_object_object_add(obj, "current", NULL); 314 json_object_object_add(obj, "current", NULL);
314 } 315 }
@@ -325,7 +326,8 @@ void ipc_event_window(struct sway_container *window, const char *change) {
325 wlr_log(WLR_DEBUG, "Sending window::%s event", change); 326 wlr_log(WLR_DEBUG, "Sending window::%s event", change);
326 json_object *obj = json_object_new_object(); 327 json_object *obj = json_object_new_object();
327 json_object_object_add(obj, "change", json_object_new_string(change)); 328 json_object_object_add(obj, "change", json_object_new_string(change));
328 json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); 329 json_object_object_add(obj, "container",
330 ipc_json_describe_node_recursive(&window->node));
329 331
330 const char *json_string = json_object_to_json_string(obj); 332 const char *json_string = json_object_to_json_string(obj);
331 ipc_send_event(json_string, IPC_EVENT_WINDOW); 333 ipc_send_event(json_string, IPC_EVENT_WINDOW);
@@ -521,30 +523,20 @@ void ipc_client_disconnect(struct ipc_client *client) {
521 free(client); 523 free(client);
522} 524}
523 525
524static void ipc_get_workspaces_callback(struct sway_container *workspace, 526static void ipc_get_workspaces_callback(struct sway_workspace *workspace,
525 void *data) { 527 void *data) {
526 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 528 json_object *workspace_json = ipc_json_describe_node(&workspace->node);
527 return;
528 }
529 json_object *workspace_json = ipc_json_describe_container(workspace);
530 // override the default focused indicator because 529 // override the default focused indicator because
531 // it's set differently for the get_workspaces reply 530 // it's set differently for the get_workspaces reply
532 struct sway_seat *seat = 531 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
533 input_manager_get_default_seat(input_manager); 532 struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
534 struct sway_container *focused_ws = seat_get_focus(seat);
535 if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) {
536 focused_ws = container_parent(focused_ws, C_WORKSPACE);
537 }
538 bool focused = workspace == focused_ws; 533 bool focused = workspace == focused_ws;
539 json_object_object_del(workspace_json, "focused"); 534 json_object_object_del(workspace_json, "focused");
540 json_object_object_add(workspace_json, "focused", 535 json_object_object_add(workspace_json, "focused",
541 json_object_new_boolean(focused)); 536 json_object_new_boolean(focused));
542 json_object_array_add((json_object *)data, workspace_json); 537 json_object_array_add((json_object *)data, workspace_json);
543 538
544 focused_ws = seat_get_focus_inactive(seat, workspace->parent); 539 focused_ws = output_get_active_workspace(workspace->output);
545 if (focused_ws->type != C_WORKSPACE) {
546 focused_ws = container_parent(focused_ws, C_WORKSPACE);
547 }
548 bool visible = workspace == focused_ws; 540 bool visible = workspace == focused_ws;
549 json_object_object_add(workspace_json, "visible", 541 json_object_object_add(workspace_json, "visible",
550 json_object_new_boolean(visible)); 542 json_object_new_boolean(visible));
@@ -552,9 +544,9 @@ static void ipc_get_workspaces_callback(struct sway_container *workspace,
552 544
553static void ipc_get_marks_callback(struct sway_container *con, void *data) { 545static void ipc_get_marks_callback(struct sway_container *con, void *data) {
554 json_object *marks = (json_object *)data; 546 json_object *marks = (json_object *)data;
555 if (con->type == C_VIEW && con->sway_view->marks) { 547 if (con->view && con->view->marks) {
556 for (int i = 0; i < con->sway_view->marks->length; ++i) { 548 for (int i = 0; i < con->view->marks->length; ++i) {
557 char *mark = (char *)con->sway_view->marks->items[i]; 549 char *mark = (char *)con->view->marks->items[i];
558 json_object_array_add(marks, json_object_new_string(mark)); 550 json_object_array_add(marks, json_object_new_string(mark));
559 } 551 }
560 } 552 }
@@ -608,16 +600,14 @@ void ipc_client_handle_command(struct ipc_client *client) {
608 case IPC_GET_OUTPUTS: 600 case IPC_GET_OUTPUTS:
609 { 601 {
610 json_object *outputs = json_object_new_array(); 602 json_object *outputs = json_object_new_array();
611 for (int i = 0; i < root_container.children->length; ++i) { 603 for (int i = 0; i < root->outputs->length; ++i) {
612 struct sway_container *container = root_container.children->items[i]; 604 struct sway_output *output = root->outputs->items[i];
613 if (container->type == C_OUTPUT) { 605 json_object_array_add(outputs,
614 json_object_array_add(outputs, 606 ipc_json_describe_node(&output->node));
615 ipc_json_describe_container(container));
616 }
617 } 607 }
618 struct sway_output *output; 608 struct sway_output *output;
619 wl_list_for_each(output, &root_container.sway_root->all_outputs, link) { 609 wl_list_for_each(output, &root->all_outputs, link) {
620 if (!output->swayc) { 610 if (!output->enabled) {
621 json_object_array_add(outputs, 611 json_object_array_add(outputs,
622 ipc_json_describe_disabled_output(output)); 612 ipc_json_describe_disabled_output(output));
623 } 613 }
@@ -717,8 +707,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
717 707
718 case IPC_GET_TREE: 708 case IPC_GET_TREE:
719 { 709 {
720 json_object *tree = 710 json_object *tree = ipc_json_describe_node_recursive(&root->node);
721 ipc_json_describe_container_recursive(&root_container);
722 const char *json_string = json_object_to_json_string(tree); 711 const char *json_string = json_object_to_json_string(tree);
723 client_valid = 712 client_valid =
724 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); 713 ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));
diff --git a/sway/main.c b/sway/main.c
index 2f05dc38..fb4f0d8c 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -42,7 +42,6 @@ void sway_terminate(int exit_code) {
42} 42}
43 43
44void sig_handler(int signal) { 44void sig_handler(int signal) {
45 //close_views(&root_container);
46 sway_terminate(EXIT_SUCCESS); 45 sway_terminate(EXIT_SUCCESS);
47} 46}
48 47
@@ -395,7 +394,7 @@ int main(int argc, char **argv) {
395 394
396 wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION); 395 wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION);
397 396
398 root_create(); 397 root = root_create();
399 398
400 if (!server_init(&server)) { 399 if (!server_init(&server)) {
401 return 1; 400 return 1;
@@ -450,7 +449,8 @@ int main(int argc, char **argv) {
450 wlr_log(WLR_INFO, "Shutting down sway"); 449 wlr_log(WLR_INFO, "Shutting down sway");
451 450
452 server_fini(&server); 451 server_fini(&server);
453 root_destroy(); 452 root_destroy(root);
453 root = NULL;
454 454
455 if (config) { 455 if (config) {
456 free_config(config); 456 free_config(config);
diff --git a/sway/meson.build b/sway/meson.build
index c14e58dd..8891ebc0 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -151,6 +151,7 @@ sway_sources = files(
151 151
152 'tree/arrange.c', 152 'tree/arrange.c',
153 'tree/container.c', 153 'tree/container.c',
154 'tree/node.c',
154 'tree/root.c', 155 'tree/root.c',
155 'tree/view.c', 156 'tree/view.c',
156 'tree/workspace.c', 157 'tree/workspace.c',
diff --git a/sway/server.c b/sway/server.c
index 749365cb..09ebe83f 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -61,8 +61,7 @@ bool server_init(struct sway_server *server) {
61 server->new_output.notify = handle_new_output; 61 server->new_output.notify = handle_new_output;
62 wl_signal_add(&server->backend->events.new_output, &server->new_output); 62 wl_signal_add(&server->backend->events.new_output, &server->new_output);
63 63
64 wlr_xdg_output_manager_create(server->wl_display, 64 wlr_xdg_output_manager_create(server->wl_display, root->output_layout);
65 root_container.sway_root->output_layout);
66 65
67 server->idle = wlr_idle_create(server->wl_display); 66 server->idle = wlr_idle_create(server->wl_display);
68 server->idle_inhibit_manager_v1 = 67 server->idle_inhibit_manager_v1 =
@@ -131,7 +130,7 @@ bool server_init(struct sway_server *server) {
131 server->txn_timeout_ms = 200; 130 server->txn_timeout_ms = 200;
132 } 131 }
133 132
134 server->dirty_containers = create_list(); 133 server->dirty_nodes = create_list();
135 server->transactions = create_list(); 134 server->transactions = create_list();
136 135
137 input_manager = input_manager_create(server); 136 input_manager = input_manager_create(server);
@@ -145,7 +144,7 @@ void server_fini(struct sway_server *server) {
145#endif 144#endif
146 wl_display_destroy_clients(server->wl_display); 145 wl_display_destroy_clients(server->wl_display);
147 wl_display_destroy(server->wl_display); 146 wl_display_destroy(server->wl_display);
148 list_free(server->dirty_containers); 147 list_free(server->dirty_nodes);
149 list_free(server->transactions); 148 list_free(server->transactions);
150} 149}
151 150
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 92f20fcc..f86d4a74 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -166,29 +166,23 @@ void arrange_container(struct sway_container *container) {
166 if (config->reloading) { 166 if (config->reloading) {
167 return; 167 return;
168 } 168 }
169 if (container->type == C_VIEW) { 169 if (container->view) {
170 view_autoconfigure(container->sway_view); 170 view_autoconfigure(container->view);
171 container_set_dirty(container); 171 node_set_dirty(&container->node);
172 return;
173 }
174 if (!sway_assert(container->type == C_CONTAINER, "Expected a container")) {
175 return; 172 return;
176 } 173 }
177 struct wlr_box box; 174 struct wlr_box box;
178 container_get_box(container, &box); 175 container_get_box(container, &box);
179 arrange_children(container->children, container->layout, &box); 176 arrange_children(container->children, container->layout, &box);
180 container_set_dirty(container); 177 node_set_dirty(&container->node);
181} 178}
182 179
183void arrange_workspace(struct sway_container *workspace) { 180void arrange_workspace(struct sway_workspace *workspace) {
184 if (config->reloading) { 181 if (config->reloading) {
185 return; 182 return;
186 } 183 }
187 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 184 struct sway_output *output = workspace->output;
188 return; 185 struct wlr_box *area = &output->usable_area;
189 }
190 struct sway_container *output = workspace->parent;
191 struct wlr_box *area = &output->sway_output->usable_area;
192 wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", 186 wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d",
193 area->width, area->height, area->x, area->y); 187 area->width, area->height, area->x, area->y);
194 workspace_remove_gaps(workspace); 188 workspace_remove_gaps(workspace);
@@ -197,21 +191,20 @@ void arrange_workspace(struct sway_container *workspace) {
197 double prev_y = workspace->y; 191 double prev_y = workspace->y;
198 workspace->width = area->width; 192 workspace->width = area->width;
199 workspace->height = area->height; 193 workspace->height = area->height;
200 workspace->x = output->x + area->x; 194 workspace->x = output->wlr_output->lx + area->x;
201 workspace->y = output->y + area->y; 195 workspace->y = output->wlr_output->ly + area->y;
202 196
203 // Adjust any floating containers 197 // Adjust any floating containers
204 double diff_x = workspace->x - prev_x; 198 double diff_x = workspace->x - prev_x;
205 double diff_y = workspace->y - prev_y; 199 double diff_y = workspace->y - prev_y;
206 if (diff_x != 0 || diff_y != 0) { 200 if (diff_x != 0 || diff_y != 0) {
207 for (int i = 0; i < workspace->sway_workspace->floating->length; ++i) { 201 for (int i = 0; i < workspace->floating->length; ++i) {
208 struct sway_container *floater = 202 struct sway_container *floater = workspace->floating->items[i];
209 workspace->sway_workspace->floating->items[i];
210 container_floating_translate(floater, diff_x, diff_y); 203 container_floating_translate(floater, diff_x, diff_y);
211 double center_x = floater->x + floater->width / 2; 204 double center_x = floater->x + floater->width / 2;
212 double center_y = floater->y + floater->height / 2; 205 double center_y = floater->y + floater->height / 2;
213 struct wlr_box workspace_box; 206 struct wlr_box workspace_box;
214 container_get_box(workspace, &workspace_box); 207 workspace_get_box(workspace, &workspace_box);
215 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { 208 if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) {
216 container_floating_move_to_center(floater); 209 container_floating_move_to_center(floater);
217 } 210 }
@@ -219,43 +212,32 @@ void arrange_workspace(struct sway_container *workspace) {
219 } 212 }
220 213
221 workspace_add_gaps(workspace); 214 workspace_add_gaps(workspace);
222 container_set_dirty(workspace); 215 node_set_dirty(&workspace->node);
223 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, 216 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
224 workspace->x, workspace->y); 217 workspace->x, workspace->y);
225 if (workspace->sway_workspace->fullscreen) { 218 if (workspace->fullscreen) {
226 struct sway_container *fs = workspace->sway_workspace->fullscreen; 219 struct sway_container *fs = workspace->fullscreen;
227 fs->x = workspace->parent->x; 220 fs->x = output->wlr_output->lx;
228 fs->y = workspace->parent->y; 221 fs->y = output->wlr_output->ly;
229 fs->width = workspace->parent->width; 222 fs->width = output->wlr_output->width;
230 fs->height = workspace->parent->height; 223 fs->height = output->wlr_output->height;
231 arrange_container(fs); 224 arrange_container(fs);
232 } else { 225 } else {
233 struct wlr_box box; 226 struct wlr_box box;
234 container_get_box(workspace, &box); 227 workspace_get_box(workspace, &box);
235 arrange_children(workspace->children, workspace->layout, &box); 228 arrange_children(workspace->tiling, workspace->layout, &box);
236 arrange_floating(workspace->sway_workspace->floating); 229 arrange_floating(workspace->floating);
237 } 230 }
238} 231}
239 232
240void arrange_output(struct sway_container *output) { 233void arrange_output(struct sway_output *output) {
241 if (config->reloading) { 234 if (config->reloading) {
242 return; 235 return;
243 } 236 }
244 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 237 // Outputs have no pending x/y/width/height,
245 return; 238 // so all we do here is arrange the workspaces.
246 } 239 for (int i = 0; i < output->workspaces->length; ++i) {
247 const struct wlr_box *output_box = wlr_output_layout_get_box( 240 struct sway_workspace *workspace = output->workspaces->items[i];
248 root_container.sway_root->output_layout,
249 output->sway_output->wlr_output);
250 output->x = output_box->x;
251 output->y = output_box->y;
252 output->width = output_box->width;
253 output->height = output_box->height;
254 container_set_dirty(output);
255 wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f",
256 output->name, output->x, output->y);
257 for (int i = 0; i < output->children->length; ++i) {
258 struct sway_container *workspace = output->children->items[i];
259 arrange_workspace(workspace); 241 arrange_workspace(workspace);
260 } 242 }
261} 243}
@@ -264,37 +246,31 @@ void arrange_root(void) {
264 if (config->reloading) { 246 if (config->reloading) {
265 return; 247 return;
266 } 248 }
267 struct wlr_output_layout *output_layout =
268 root_container.sway_root->output_layout;
269 const struct wlr_box *layout_box = 249 const struct wlr_box *layout_box =
270 wlr_output_layout_get_box(output_layout, NULL); 250 wlr_output_layout_get_box(root->output_layout, NULL);
271 root_container.x = layout_box->x; 251 root->x = layout_box->x;
272 root_container.y = layout_box->y; 252 root->y = layout_box->y;
273 root_container.width = layout_box->width; 253 root->width = layout_box->width;
274 root_container.height = layout_box->height; 254 root->height = layout_box->height;
275 container_set_dirty(&root_container); 255 for (int i = 0; i < root->outputs->length; ++i) {
276 for (int i = 0; i < root_container.children->length; ++i) { 256 struct sway_output *output = root->outputs->items[i];
277 struct sway_container *output = root_container.children->items[i];
278 arrange_output(output); 257 arrange_output(output);
279 } 258 }
280} 259}
281 260
282void arrange_windows(struct sway_container *container) { 261void arrange_node(struct sway_node *node) {
283 switch (container->type) { 262 switch (node->type) {
284 case C_ROOT: 263 case N_ROOT:
285 arrange_root(); 264 arrange_root();
286 break; 265 break;
287 case C_OUTPUT: 266 case N_OUTPUT:
288 arrange_output(container); 267 arrange_output(node->sway_output);
289 break;
290 case C_WORKSPACE:
291 arrange_workspace(container);
292 break; 268 break;
293 case C_CONTAINER: 269 case N_WORKSPACE:
294 case C_VIEW: 270 arrange_workspace(node->sway_workspace);
295 arrange_container(container);
296 break; 271 break;
297 case C_TYPES: 272 case N_CONTAINER:
273 arrange_container(node->sway_container);
298 break; 274 break;
299 } 275 }
300} 276}
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 520b4566..0cb8d0a5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -24,97 +24,39 @@
24#include "log.h" 24#include "log.h"
25#include "stringop.h" 25#include "stringop.h"
26 26
27const char *container_type_to_str(enum sway_container_type type) { 27struct sway_container *container_create(struct sway_view *view) {
28 switch (type) {
29 case C_ROOT:
30 return "C_ROOT";
31 case C_OUTPUT:
32 return "C_OUTPUT";
33 case C_WORKSPACE:
34 return "C_WORKSPACE";
35 case C_CONTAINER:
36 return "C_CONTAINER";
37 case C_VIEW:
38 return "C_VIEW";
39 default:
40 return "C_UNKNOWN";
41 }
42}
43
44void container_create_notify(struct sway_container *container) {
45 if (container->type == C_VIEW) {
46 ipc_event_window(container, "new");
47 } else if (container->type == C_WORKSPACE) {
48 ipc_event_workspace(NULL, container, "init");
49 }
50 wl_signal_emit(&root_container.sway_root->events.new_container, container);
51}
52
53void container_update_textures_recursive(struct sway_container *con) {
54 if (con->type == C_CONTAINER || con->type == C_VIEW) {
55 container_update_title_textures(con);
56 }
57
58 if (con->type == C_VIEW) {
59 view_update_marks_textures(con->sway_view);
60 } else {
61 for (int i = 0; i < con->children->length; ++i) {
62 struct sway_container *child = con->children->items[i];
63 container_update_textures_recursive(child);
64 }
65
66 if (con->type == C_WORKSPACE) {
67 for (int i = 0; i < con->sway_workspace->floating->length; ++i) {
68 struct sway_container *floater =
69 con->sway_workspace->floating->items[i];
70 container_update_textures_recursive(floater);
71 }
72 }
73 }
74}
75
76struct sway_container *container_create(enum sway_container_type type) {
77 // next id starts at 1 because 0 is assigned to root_container in layout.c
78 static size_t next_id = 1;
79 struct sway_container *c = calloc(1, sizeof(struct sway_container)); 28 struct sway_container *c = calloc(1, sizeof(struct sway_container));
80 if (!c) { 29 if (!c) {
30 wlr_log(WLR_ERROR, "Unable to allocate sway_container");
81 return NULL; 31 return NULL;
82 } 32 }
83 c->id = next_id++; 33 node_init(&c->node, N_CONTAINER, c);
84 c->layout = L_NONE; 34 c->layout = L_NONE;
85 c->type = type; 35 c->view = view;
86 c->alpha = 1.0f; 36 c->alpha = 1.0f;
87 37
88 if (type != C_VIEW) { 38 if (!view) {
89 c->children = create_list(); 39 c->children = create_list();
90 c->current.children = create_list(); 40 c->current.children = create_list();
91 } 41 }
92 c->outputs = create_list(); 42 c->outputs = create_list();
93 43
94 wl_signal_init(&c->events.destroy); 44 wl_signal_init(&c->events.destroy);
95 45 wl_signal_emit(&root->events.new_node, &c->node);
96 c->has_gaps = false;
97 c->gaps_inner = 0;
98 c->gaps_outer = 0;
99 c->current_gaps = 0;
100 46
101 return c; 47 return c;
102} 48}
103 49
104void container_destroy(struct sway_container *con) { 50void container_destroy(struct sway_container *con) {
105 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, 51 if (!sway_assert(con->node.destroying,
106 "Expected a container or view")) {
107 return;
108 }
109 if (!sway_assert(con->destroying,
110 "Tried to free container which wasn't marked as destroying")) { 52 "Tried to free container which wasn't marked as destroying")) {
111 return; 53 return;
112 } 54 }
113 if (!sway_assert(con->ntxnrefs == 0, "Tried to free container " 55 if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container "
114 "which is still referenced by transactions")) { 56 "which is still referenced by transactions")) {
115 return; 57 return;
116 } 58 }
117 free(con->name); 59 free(con->title);
118 free(con->formatted_title); 60 free(con->formatted_title);
119 wlr_texture_destroy(con->title_focused); 61 wlr_texture_destroy(con->title_focused);
120 wlr_texture_destroy(con->title_focused_inactive); 62 wlr_texture_destroy(con->title_focused_inactive);
@@ -124,14 +66,14 @@ void container_destroy(struct sway_container *con) {
124 list_free(con->current.children); 66 list_free(con->current.children);
125 list_free(con->outputs); 67 list_free(con->outputs);
126 68
127 if (con->type == C_VIEW) { 69 if (con->view) {
128 struct sway_view *view = con->sway_view; 70 struct sway_view *view = con->view;
129 view->swayc = NULL; 71 view->container = NULL;
130 free(view->title_format); 72 free(view->title_format);
131 view->title_format = NULL; 73 view->title_format = NULL;
132 74
133 if (view->destroying) { 75 if (view->destroying) {
134 view_destroy(view); 76 view_destroy(con->view);
135 } 77 }
136 } 78 }
137 79
@@ -139,115 +81,57 @@ void container_destroy(struct sway_container *con) {
139} 81}
140 82
141void container_begin_destroy(struct sway_container *con) { 83void container_begin_destroy(struct sway_container *con) {
142 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, 84 if (con->view) {
143 "Expected a container or view")) {
144 return;
145 }
146
147 if (con->type == C_VIEW) {
148 ipc_event_window(con, "close"); 85 ipc_event_window(con, "close");
149 } 86 }
150 wl_signal_emit(&con->events.destroy, con); 87 wl_signal_emit(&con->node.events.destroy, &con->node);
151 88
152 container_end_mouse_operation(con); 89 container_end_mouse_operation(con);
153 90
154 con->destroying = true; 91 con->node.destroying = true;
155 container_set_dirty(con); 92 node_set_dirty(&con->node);
156 93
157 if (con->scratchpad) { 94 if (con->scratchpad) {
158 root_scratchpad_remove_container(con); 95 root_scratchpad_remove_container(con);
159 } 96 }
160 97
161 if (con->parent) { 98 if (con->parent || con->workspace) {
162 container_remove_child(con); 99 container_detach(con);
163 } 100 }
164} 101}
165 102
166struct sway_container *container_reap_empty(struct sway_container *con) { 103void container_reap_empty(struct sway_container *con) {
167 while (con && con->type == C_CONTAINER) { 104 if (con->view) {
168 struct sway_container *next = con->parent; 105 return;
169 if (con->children->length == 0) {
170 container_begin_destroy(con);
171 }
172 con = next;
173 } 106 }
174 if (con && con->type == C_WORKSPACE) { 107 struct sway_workspace *ws = con->workspace;
175 workspace_consider_destroy(con); 108 while (con) {
176 if (con->destroying) { 109 if (con->children->length) {
177 con = con->parent; 110 return;
178 } 111 }
112 struct sway_container *parent = con->parent;
113 container_begin_destroy(con);
114 con = parent;
179 } 115 }
180 return con; 116 workspace_consider_destroy(ws);
181} 117}
182 118
183struct sway_container *container_flatten(struct sway_container *container) { 119struct sway_container *container_flatten(struct sway_container *container) {
184 while (container->type == C_CONTAINER && container->children->length == 1) { 120 if (container->view) {
121 return NULL;
122 }
123 while (container && container->children->length == 1) {
185 struct sway_container *child = container->children->items[0]; 124 struct sway_container *child = container->children->items[0];
186 struct sway_container *parent = container->parent; 125 struct sway_container *parent = container->parent;
187 container_replace_child(container, child); 126 container_replace(container, child);
188 container_begin_destroy(container); 127 container_begin_destroy(container);
189 container = parent; 128 container = parent;
190 } 129 }
191 return container; 130 return container;
192} 131}
193 132
194static void container_close_func(struct sway_container *container, void *data) {
195 if (container->type == C_VIEW) {
196 view_close(container->sway_view);
197 }
198}
199
200struct sway_container *container_close(struct sway_container *con) {
201 if (!sway_assert(con != NULL,
202 "container_close called with a NULL container")) {
203 return NULL;
204 }
205
206 struct sway_container *parent = con->parent;
207
208 if (con->type == C_VIEW) {
209 view_close(con->sway_view);
210 } else if (con->type == C_CONTAINER) {
211 container_for_each_child(con, container_close_func, NULL);
212 } else if (con->type == C_WORKSPACE) {
213 workspace_for_each_container(con, container_close_func, NULL);
214 }
215
216 return parent;
217}
218
219struct sway_container *container_view_create(struct sway_container *sibling,
220 struct sway_view *sway_view) {
221 if (!sway_assert(sibling,
222 "container_view_create called with NULL sibling/parent")) {
223 return NULL;
224 }
225 const char *title = view_get_title(sway_view);
226 struct sway_container *swayc = container_create(C_VIEW);
227 wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s",
228 swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);
229 // Setup values
230 swayc->sway_view = sway_view;
231 swayc->width = 0;
232 swayc->height = 0;
233
234 if (sibling->type == C_WORKSPACE) {
235 // Case of focused workspace, just create as child of it
236 container_add_child(sibling, swayc);
237 } else {
238 // Regular case, create as sibling of current container
239 container_add_sibling(sibling, swayc);
240 }
241 container_create_notify(swayc);
242 return swayc;
243}
244
245struct sway_container *container_find_child(struct sway_container *container, 133struct sway_container *container_find_child(struct sway_container *container,
246 bool (*test)(struct sway_container *view, void *data), void *data) { 134 bool (*test)(struct sway_container *con, void *data), void *data) {
247 if (!sway_assert(container->type == C_CONTAINER ||
248 container->type == C_VIEW, "Expected a container or view")) {
249 return NULL;
250 }
251 if (!container->children) { 135 if (!container->children) {
252 return NULL; 136 return NULL;
253 } 137 }
@@ -264,46 +148,32 @@ struct sway_container *container_find_child(struct sway_container *container,
264 return NULL; 148 return NULL;
265} 149}
266 150
267struct sway_container *container_parent(struct sway_container *container, 151static void surface_at_view(struct sway_container *con, double lx, double ly,
268 enum sway_container_type type) {
269 if (!sway_assert(container, "container is NULL")) {
270 return NULL;
271 }
272 if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) {
273 return NULL;
274 }
275 do {
276 container = container->parent;
277 } while (container && container->type != type);
278 return container;
279}
280
281static void surface_at_view(struct sway_container *swayc, double lx, double ly,
282 struct wlr_surface **surface, double *sx, double *sy) { 152 struct wlr_surface **surface, double *sx, double *sy) {
283 if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { 153 if (!sway_assert(con->view, "Expected a view")) {
284 return; 154 return;
285 } 155 }
286 struct sway_view *sview = swayc->sway_view; 156 struct sway_view *view = con->view;
287 double view_sx = lx - sview->x + sview->geometry.x; 157 double view_sx = lx - view->x + view->geometry.x;
288 double view_sy = ly - sview->y + sview->geometry.y; 158 double view_sy = ly - view->y + view->geometry.y;
289 159
290 double _sx, _sy; 160 double _sx, _sy;
291 struct wlr_surface *_surface = NULL; 161 struct wlr_surface *_surface = NULL;
292 switch (sview->type) { 162 switch (view->type) {
293#ifdef HAVE_XWAYLAND 163#ifdef HAVE_XWAYLAND
294 case SWAY_VIEW_XWAYLAND: 164 case SWAY_VIEW_XWAYLAND:
295 _surface = wlr_surface_surface_at(sview->surface, 165 _surface = wlr_surface_surface_at(view->surface,
296 view_sx, view_sy, &_sx, &_sy); 166 view_sx, view_sy, &_sx, &_sy);
297 break; 167 break;
298#endif 168#endif
299 case SWAY_VIEW_XDG_SHELL_V6: 169 case SWAY_VIEW_XDG_SHELL_V6:
300 _surface = wlr_xdg_surface_v6_surface_at( 170 _surface = wlr_xdg_surface_v6_surface_at(
301 sview->wlr_xdg_surface_v6, 171 view->wlr_xdg_surface_v6,
302 view_sx, view_sy, &_sx, &_sy); 172 view_sx, view_sy, &_sx, &_sy);
303 break; 173 break;
304 case SWAY_VIEW_XDG_SHELL: 174 case SWAY_VIEW_XDG_SHELL:
305 _surface = wlr_xdg_surface_surface_at( 175 _surface = wlr_xdg_surface_surface_at(
306 sview->wlr_xdg_surface, 176 view->wlr_xdg_surface,
307 view_sx, view_sy, &_sx, &_sy); 177 view_sx, view_sy, &_sx, &_sy);
308 break; 178 break;
309 } 179 }
@@ -317,65 +187,72 @@ static void surface_at_view(struct sway_container *swayc, double lx, double ly,
317/** 187/**
318 * container_at for a container with layout L_TABBED. 188 * container_at for a container with layout L_TABBED.
319 */ 189 */
320static struct sway_container *container_at_tabbed(struct sway_container *parent, 190static struct sway_container *container_at_tabbed(struct sway_node *parent,
321 double lx, double ly, 191 double lx, double ly,
322 struct wlr_surface **surface, double *sx, double *sy) { 192 struct wlr_surface **surface, double *sx, double *sy) {
323 if (ly < parent->y || ly > parent->y + parent->height) { 193 struct wlr_box box;
194 node_get_box(parent, &box);
195 if (ly < box.y || ly > box.y + box.height) {
324 return NULL; 196 return NULL;
325 } 197 }
326 struct sway_seat *seat = input_manager_current_seat(input_manager); 198 struct sway_seat *seat = input_manager_current_seat(input_manager);
199 list_t *children = node_get_children(parent);
327 200
328 // Tab titles 201 // Tab titles
329 int title_height = container_titlebar_height(); 202 int title_height = container_titlebar_height();
330 if (ly < parent->y + title_height) { 203 if (ly < box.y + title_height) {
331 int tab_width = parent->width / parent->children->length; 204 int tab_width = box.width / children->length;
332 int child_index = (lx - parent->x) / tab_width; 205 int child_index = (lx - box.x) / tab_width;
333 if (child_index >= parent->children->length) { 206 if (child_index >= children->length) {
334 child_index = parent->children->length - 1; 207 child_index = children->length - 1;
335 } 208 }
336 struct sway_container *child = parent->children->items[child_index]; 209 struct sway_container *child = children->items[child_index];
337 return seat_get_focus_inactive(seat, child); 210 struct sway_node *node = seat_get_focus_inactive(seat, &child->node);
211 return node->sway_container;
338 } 212 }
339 213
340 // Surfaces 214 // Surfaces
341 struct sway_container *current = seat_get_active_child(seat, parent); 215 struct sway_node *current = seat_get_active_child(seat, parent);
342
343 return tiling_container_at(current, lx, ly, surface, sx, sy); 216 return tiling_container_at(current, lx, ly, surface, sx, sy);
344} 217}
345 218
346/** 219/**
347 * container_at for a container with layout L_STACKED. 220 * container_at for a container with layout L_STACKED.
348 */ 221 */
349static struct sway_container *container_at_stacked( 222static struct sway_container *container_at_stacked(struct sway_node *parent,
350 struct sway_container *parent, double lx, double ly, 223 double lx, double ly,
351 struct wlr_surface **surface, double *sx, double *sy) { 224 struct wlr_surface **surface, double *sx, double *sy) {
352 if (ly < parent->y || ly > parent->y + parent->height) { 225 struct wlr_box box;
226 node_get_box(parent, &box);
227 if (ly < box.y || ly > box.y + box.height) {
353 return NULL; 228 return NULL;
354 } 229 }
355 struct sway_seat *seat = input_manager_current_seat(input_manager); 230 struct sway_seat *seat = input_manager_current_seat(input_manager);
231 list_t *children = node_get_children(parent);
356 232
357 // Title bars 233 // Title bars
358 int title_height = container_titlebar_height(); 234 int title_height = container_titlebar_height();
359 int child_index = (ly - parent->y) / title_height; 235 int child_index = (ly - box.y) / title_height;
360 if (child_index < parent->children->length) { 236 if (child_index < children->length) {
361 struct sway_container *child = parent->children->items[child_index]; 237 struct sway_container *child = children->items[child_index];
362 return seat_get_focus_inactive(seat, child); 238 struct sway_node *node = seat_get_focus_inactive(seat, &child->node);
239 return node->sway_container;
363 } 240 }
364 241
365 // Surfaces 242 // Surfaces
366 struct sway_container *current = seat_get_active_child(seat, parent); 243 struct sway_node *current = seat_get_active_child(seat, parent);
367
368 return tiling_container_at(current, lx, ly, surface, sx, sy); 244 return tiling_container_at(current, lx, ly, surface, sx, sy);
369} 245}
370 246
371/** 247/**
372 * container_at for a container with layout L_HORIZ or L_VERT. 248 * container_at for a container with layout L_HORIZ or L_VERT.
373 */ 249 */
374static struct sway_container *container_at_linear(struct sway_container *parent, 250static struct sway_container *container_at_linear(struct sway_node *parent,
375 double lx, double ly, 251 double lx, double ly,
376 struct wlr_surface **surface, double *sx, double *sy) { 252 struct wlr_surface **surface, double *sx, double *sy) {
377 for (int i = 0; i < parent->children->length; ++i) { 253 list_t *children = node_get_children(parent);
378 struct sway_container *child = parent->children->items[i]; 254 for (int i = 0; i < children->length; ++i) {
255 struct sway_container *child = children->items[i];
379 struct wlr_box box = { 256 struct wlr_box box = {
380 .x = child->x, 257 .x = child->x,
381 .y = child->y, 258 .y = child->y,
@@ -383,7 +260,7 @@ static struct sway_container *container_at_linear(struct sway_container *parent,
383 .height = child->height, 260 .height = child->height,
384 }; 261 };
385 if (wlr_box_contains_point(&box, lx, ly)) { 262 if (wlr_box_contains_point(&box, lx, ly)) {
386 return tiling_container_at(child, lx, ly, surface, sx, sy); 263 return tiling_container_at(&child->node, lx, ly, surface, sx, sy);
387 } 264 }
388 } 265 }
389 return NULL; 266 return NULL;
@@ -391,12 +268,11 @@ static struct sway_container *container_at_linear(struct sway_container *parent,
391 268
392static struct sway_container *floating_container_at(double lx, double ly, 269static struct sway_container *floating_container_at(double lx, double ly,
393 struct wlr_surface **surface, double *sx, double *sy) { 270 struct wlr_surface **surface, double *sx, double *sy) {
394 for (int i = 0; i < root_container.children->length; ++i) { 271 for (int i = 0; i < root->outputs->length; ++i) {
395 struct sway_container *output = root_container.children->items[i]; 272 struct sway_output *output = root->outputs->items[i];
396 for (int j = 0; j < output->children->length; ++j) { 273 for (int j = 0; j < output->workspaces->length; ++j) {
397 struct sway_container *workspace = output->children->items[j]; 274 struct sway_workspace *ws = output->workspaces->items[j];
398 struct sway_workspace *ws = workspace->sway_workspace; 275 if (!workspace_is_visible(ws)) {
399 if (!workspace_is_visible(workspace)) {
400 continue; 276 continue;
401 } 277 }
402 // Items at the end of the list are on top, so iterate the list in 278 // Items at the end of the list are on top, so iterate the list in
@@ -410,7 +286,7 @@ static struct sway_container *floating_container_at(double lx, double ly,
410 .height = floater->height, 286 .height = floater->height,
411 }; 287 };
412 if (wlr_box_contains_point(&box, lx, ly)) { 288 if (wlr_box_contains_point(&box, lx, ly)) {
413 return tiling_container_at(floater, lx, ly, 289 return tiling_container_at(&floater->node, lx, ly,
414 surface, sx, sy); 290 surface, sx, sy);
415 } 291 }
416 } 292 }
@@ -419,25 +295,24 @@ static struct sway_container *floating_container_at(double lx, double ly,
419 return NULL; 295 return NULL;
420} 296}
421 297
422struct sway_container *tiling_container_at( 298struct sway_container *tiling_container_at(struct sway_node *parent,
423 struct sway_container *con, double lx, double ly, 299 double lx, double ly,
424 struct wlr_surface **surface, double *sx, double *sy) { 300 struct wlr_surface **surface, double *sx, double *sy) {
425 if (con->type == C_VIEW) { 301 if (node_is_view(parent)) {
426 surface_at_view(con, lx, ly, surface, sx, sy); 302 surface_at_view(parent->sway_container, lx, ly, surface, sx, sy);
427 return con; 303 return parent->sway_container;
428 } 304 }
429 if (!con->children->length) { 305 if (!node_get_children(parent)) {
430 return NULL; 306 return NULL;
431 } 307 }
432 308 switch (node_get_layout(parent)) {
433 switch (con->layout) {
434 case L_HORIZ: 309 case L_HORIZ:
435 case L_VERT: 310 case L_VERT:
436 return container_at_linear(con, lx, ly, surface, sx, sy); 311 return container_at_linear(parent, lx, ly, surface, sx, sy);
437 case L_TABBED: 312 case L_TABBED:
438 return container_at_tabbed(con, lx, ly, surface, sx, sy); 313 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
439 case L_STACKED: 314 case L_STACKED:
440 return container_at_stacked(con, lx, ly, surface, sx, sy); 315 return container_at_stacked(parent, lx, ly, surface, sx, sy);
441 case L_NONE: 316 case L_NONE:
442 return NULL; 317 return NULL;
443 } 318 }
@@ -472,19 +347,16 @@ static bool surface_is_popup(struct wlr_surface *surface) {
472 return false; 347 return false;
473} 348}
474 349
475struct sway_container *container_at(struct sway_container *workspace, 350struct sway_container *container_at(struct sway_workspace *workspace,
476 double lx, double ly, 351 double lx, double ly,
477 struct wlr_surface **surface, double *sx, double *sy) { 352 struct wlr_surface **surface, double *sx, double *sy) {
478 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
479 return NULL;
480 }
481 struct sway_container *c; 353 struct sway_container *c;
354 // Focused view's popups
482 struct sway_seat *seat = input_manager_current_seat(input_manager); 355 struct sway_seat *seat = input_manager_current_seat(input_manager);
483 struct sway_container *focus = 356 struct sway_container *focus = seat_get_focused_container(seat);
484 seat_get_focus_inactive(seat, &root_container);
485 bool is_floating = focus && container_is_floating_or_child(focus); 357 bool is_floating = focus && container_is_floating_or_child(focus);
486 // Focused view's popups 358 // Focused view's popups
487 if (focus && focus->type == C_VIEW) { 359 if (focus && focus->view) {
488 surface_at_view(focus, lx, ly, surface, sx, sy); 360 surface_at_view(focus, lx, ly, surface, sx, sy);
489 if (*surface && surface_is_popup(*surface)) { 361 if (*surface && surface_is_popup(*surface)) {
490 return focus; 362 return focus;
@@ -492,7 +364,7 @@ struct sway_container *container_at(struct sway_container *workspace,
492 *surface = NULL; 364 *surface = NULL;
493 } 365 }
494 // If focused is floating, focused view's non-popups 366 // If focused is floating, focused view's non-popups
495 if (focus && focus->type == C_VIEW && is_floating) { 367 if (focus && focus->view && is_floating) {
496 surface_at_view(focus, lx, ly, surface, sx, sy); 368 surface_at_view(focus, lx, ly, surface, sx, sy);
497 if (*surface) { 369 if (*surface) {
498 return focus; 370 return focus;
@@ -504,7 +376,7 @@ struct sway_container *container_at(struct sway_container *workspace,
504 return c; 376 return c;
505 } 377 }
506 // If focused is tiling, focused view's non-popups 378 // If focused is tiling, focused view's non-popups
507 if (focus && focus->type == C_VIEW && !is_floating) { 379 if (focus && focus->view && !is_floating) {
508 surface_at_view(focus, lx, ly, surface, sx, sy); 380 surface_at_view(focus, lx, ly, surface, sx, sy);
509 if (*surface) { 381 if (*surface) {
510 return focus; 382 return focus;
@@ -512,7 +384,7 @@ struct sway_container *container_at(struct sway_container *workspace,
512 *surface = NULL; 384 *surface = NULL;
513 } 385 }
514 // Tiling (non-focused) 386 // Tiling (non-focused)
515 if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { 387 if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) {
516 return c; 388 return c;
517 } 389 }
518 return NULL; 390 return NULL;
@@ -521,10 +393,6 @@ struct sway_container *container_at(struct sway_container *workspace,
521void container_for_each_child(struct sway_container *container, 393void container_for_each_child(struct sway_container *container,
522 void (*f)(struct sway_container *container, void *data), 394 void (*f)(struct sway_container *container, void *data),
523 void *data) { 395 void *data) {
524 if (!sway_assert(container->type == C_CONTAINER ||
525 container->type == C_VIEW, "Expected a container or view")) {
526 return;
527 }
528 if (container->children) { 396 if (container->children) {
529 for (int i = 0; i < container->children->length; ++i) { 397 for (int i = 0; i < container->children->length; ++i) {
530 struct sway_container *child = container->children->items[i]; 398 struct sway_container *child = container->children->items[i];
@@ -536,7 +404,7 @@ void container_for_each_child(struct sway_container *container,
536 404
537bool container_has_ancestor(struct sway_container *descendant, 405bool container_has_ancestor(struct sway_container *descendant,
538 struct sway_container *ancestor) { 406 struct sway_container *ancestor) {
539 while (descendant && descendant->type != C_ROOT) { 407 while (descendant) {
540 descendant = descendant->parent; 408 descendant = descendant->parent;
541 if (descendant == ancestor) { 409 if (descendant == ancestor) {
542 return true; 410 return true;
@@ -545,27 +413,10 @@ bool container_has_ancestor(struct sway_container *descendant,
545 return false; 413 return false;
546} 414}
547 415
548int container_count_descendants_of_type(struct sway_container *con,
549 enum sway_container_type type) {
550 int children = 0;
551 if (con->type == type) {
552 children++;
553 }
554 if (con->children) {
555 for (int i = 0; i < con->children->length; i++) {
556 struct sway_container *child = con->children->items[i];
557 children += container_count_descendants_of_type(child, type);
558 }
559 }
560 return children;
561}
562
563void container_damage_whole(struct sway_container *container) { 416void container_damage_whole(struct sway_container *container) {
564 for (int i = 0; i < root_container.children->length; ++i) { 417 for (int i = 0; i < root->outputs->length; ++i) {
565 struct sway_container *cont = root_container.children->items[i]; 418 struct sway_output *output = root->outputs->items[i];
566 if (cont->type == C_OUTPUT) { 419 output_damage_whole_container(output, container);
567 output_damage_whole_container(cont->sway_output, container);
568 }
569 } 420 }
570} 421}
571 422
@@ -582,10 +433,6 @@ struct sway_output *container_get_effective_output(struct sway_container *con) {
582 433
583static void update_title_texture(struct sway_container *con, 434static void update_title_texture(struct sway_container *con,
584 struct wlr_texture **texture, struct border_colors *class) { 435 struct wlr_texture **texture, struct border_colors *class) {
585 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
586 "Unexpected type %s", container_type_to_str(con->type))) {
587 return;
588 }
589 struct sway_output *output = container_get_effective_output(con); 436 struct sway_output *output = container_get_effective_output(con);
590 if (!output) { 437 if (!output) {
591 return; 438 return;
@@ -664,9 +511,10 @@ void container_calculate_title_height(struct sway_container *container) {
664 * An example tree representation is: V[Terminal, Firefox] 511 * An example tree representation is: V[Terminal, Firefox]
665 * If buffer is not NULL, also populate the buffer with the representation. 512 * If buffer is not NULL, also populate the buffer with the representation.
666 */ 513 */
667static size_t get_tree_representation(struct sway_container *parent, char *buffer) { 514size_t container_build_representation(enum sway_container_layout layout,
515 list_t *children, char *buffer) {
668 size_t len = 2; 516 size_t len = 2;
669 switch (parent->layout) { 517 switch (layout) {
670 case L_VERT: 518 case L_VERT:
671 lenient_strcat(buffer, "V["); 519 lenient_strcat(buffer, "V[");
672 break; 520 break;
@@ -683,17 +531,17 @@ static size_t get_tree_representation(struct sway_container *parent, char *buffe
683 lenient_strcat(buffer, "D["); 531 lenient_strcat(buffer, "D[");
684 break; 532 break;
685 } 533 }
686 for (int i = 0; i < parent->children->length; ++i) { 534 for (int i = 0; i < children->length; ++i) {
687 if (i != 0) { 535 if (i != 0) {
688 ++len; 536 ++len;
689 lenient_strcat(buffer, " "); 537 lenient_strcat(buffer, " ");
690 } 538 }
691 struct sway_container *child = parent->children->items[i]; 539 struct sway_container *child = children->items[i];
692 const char *identifier = NULL; 540 const char *identifier = NULL;
693 if (child->type == C_VIEW) { 541 if (child->view) {
694 identifier = view_get_class(child->sway_view); 542 identifier = view_get_class(child->view);
695 if (!identifier) { 543 if (!identifier) {
696 identifier = view_get_app_id(child->sway_view); 544 identifier = view_get_app_id(child->view);
697 } 545 }
698 } else { 546 } else {
699 identifier = child->formatted_title; 547 identifier = child->formatted_title;
@@ -711,25 +559,25 @@ static size_t get_tree_representation(struct sway_container *parent, char *buffe
711 return len; 559 return len;
712} 560}
713 561
714void container_notify_subtree_changed(struct sway_container *container) { 562void container_update_representation(struct sway_container *con) {
715 if (!container || container->type < C_WORKSPACE) { 563 if (!con->view) {
716 return; 564 size_t len = container_build_representation(con->layout,
717 } 565 con->children, NULL);
718 free(container->formatted_title); 566 free(con->formatted_title);
719 container->formatted_title = NULL; 567 con->formatted_title = calloc(len + 1, sizeof(char));
720 568 if (!sway_assert(con->formatted_title,
721 size_t len = get_tree_representation(container, NULL); 569 "Unable to allocate title string")) {
722 char *buffer = calloc(len + 1, sizeof(char)); 570 return;
723 if (!sway_assert(buffer, "Unable to allocate title string")) { 571 }
724 return; 572 container_build_representation(con->layout, con->children,
573 con->formatted_title);
574 container_calculate_title_height(con);
575 container_update_title_textures(con);
725 } 576 }
726 get_tree_representation(container, buffer); 577 if (con->parent) {
727 578 container_update_representation(con->parent);
728 container->formatted_title = buffer; 579 } else if (con->workspace) {
729 if (container->type != C_WORKSPACE) { 580 workspace_update_representation(con->workspace);
730 container_calculate_title_height(container);
731 container_update_title_textures(container);
732 container_notify_subtree_changed(container->parent);
733 } 581 }
734} 582}
735 583
@@ -738,11 +586,7 @@ size_t container_titlebar_height() {
738} 586}
739 587
740void container_init_floating(struct sway_container *con) { 588void container_init_floating(struct sway_container *con) {
741 if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER, 589 struct sway_workspace *ws = con->workspace;
742 "Expected a view or container")) {
743 return;
744 }
745 struct sway_container *ws = container_parent(con, C_WORKSPACE);
746 int min_width, min_height; 590 int min_width, min_height;
747 int max_width, max_height; 591 int max_width, max_height;
748 592
@@ -778,13 +622,13 @@ void container_init_floating(struct sway_container *con) {
778 max_height = config->floating_maximum_height; 622 max_height = config->floating_maximum_height;
779 } 623 }
780 624
781 if (con->type == C_CONTAINER) { 625 if (!con->view) {
782 con->width = max_width; 626 con->width = max_width;
783 con->height = max_height; 627 con->height = max_height;
784 con->x = ws->x + (ws->width - con->width) / 2; 628 con->x = ws->x + (ws->width - con->width) / 2;
785 con->y = ws->y + (ws->height - con->height) / 2; 629 con->y = ws->y + (ws->height - con->height) / 2;
786 } else { 630 } else {
787 struct sway_view *view = con->sway_view; 631 struct sway_view *view = con->view;
788 view->width = fmax(min_width, fmin(view->natural_width, max_width)); 632 view->width = fmax(min_width, fmin(view->natural_width, max_width));
789 view->height = fmax(min_height, fmin(view->natural_height, max_height)); 633 view->height = fmax(min_height, fmin(view->natural_height, max_height));
790 view->x = ws->x + (ws->width - view->width) / 2; 634 view->x = ws->x + (ws->width - view->width) / 2;
@@ -794,7 +638,7 @@ void container_init_floating(struct sway_container *con) {
794 view->border_top = view->border_bottom = true; 638 view->border_top = view->border_bottom = true;
795 view->border_left = view->border_right = true; 639 view->border_left = view->border_right = true;
796 640
797 container_set_geometry_from_floating_view(view->swayc); 641 container_set_geometry_from_floating_view(con);
798 } 642 }
799} 643}
800 644
@@ -804,32 +648,41 @@ void container_set_floating(struct sway_container *container, bool enable) {
804 } 648 }
805 649
806 struct sway_seat *seat = input_manager_current_seat(input_manager); 650 struct sway_seat *seat = input_manager_current_seat(input_manager);
807 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 651 struct sway_workspace *workspace = container->workspace;
808 652
809 if (enable) { 653 if (enable) {
810 struct sway_container *old_parent = container_remove_child(container); 654 struct sway_container *old_parent = container->parent;
655 container_detach(container);
811 workspace_add_floating(workspace, container); 656 workspace_add_floating(workspace, container);
812 container_init_floating(container); 657 container_init_floating(container);
813 if (container->type == C_VIEW) { 658 if (container->view) {
814 view_set_tiled(container->sway_view, false); 659 view_set_tiled(container->view, false);
660 }
661 if (old_parent) {
662 container_reap_empty(old_parent);
815 } 663 }
816 container_reap_empty(old_parent);
817 } else { 664 } else {
818 // Returning to tiled 665 // Returning to tiled
819 if (container->scratchpad) { 666 if (container->scratchpad) {
820 root_scratchpad_remove_container(container); 667 root_scratchpad_remove_container(container);
821 } 668 }
822 container_remove_child(container); 669 container_detach(container);
823 struct sway_container *reference = 670 struct sway_container *reference =
824 seat_get_focus_inactive_tiling(seat, workspace); 671 seat_get_focus_inactive_tiling(seat, workspace);
825 if (reference->type == C_VIEW) { 672 if (reference && reference->view) {
826 reference = reference->parent; 673 reference = reference->parent;
827 } 674 }
828 container_add_child(reference, container); 675 if (reference) {
829 container->width = container->parent->width; 676 container_add_child(reference, container);
830 container->height = container->parent->height; 677 container->width = reference->width;
831 if (container->type == C_VIEW) { 678 container->height = reference->height;
832 view_set_tiled(container->sway_view, true); 679 } else {
680 workspace_add_tiling(workspace, container);
681 container->width = workspace->width;
682 container->height = workspace->height;
683 }
684 if (container->view) {
685 view_set_tiled(container->view, true);
833 } 686 }
834 container->is_sticky = false; 687 container->is_sticky = false;
835 } 688 }
@@ -840,14 +693,13 @@ void container_set_floating(struct sway_container *container, bool enable) {
840} 693}
841 694
842void container_set_geometry_from_floating_view(struct sway_container *con) { 695void container_set_geometry_from_floating_view(struct sway_container *con) {
843 if (!sway_assert(con->type == C_VIEW, "Expected a view")) { 696 if (!sway_assert(con->view, "Expected a view")) {
844 return; 697 return;
845 } 698 }
846 if (!sway_assert(container_is_floating(con), 699 if (!sway_assert(container_is_floating(con), "Expected a floating view")) {
847 "Expected a floating view")) {
848 return; 700 return;
849 } 701 }
850 struct sway_view *view = con->sway_view; 702 struct sway_view *view = con->view;
851 size_t border_width = 0; 703 size_t border_width = 0;
852 size_t top = 0; 704 size_t top = 0;
853 705
@@ -861,12 +713,12 @@ void container_set_geometry_from_floating_view(struct sway_container *con) {
861 con->y = view->y - top; 713 con->y = view->y - top;
862 con->width = view->width + border_width * 2; 714 con->width = view->width + border_width * 2;
863 con->height = top + view->height + border_width; 715 con->height = top + view->height + border_width;
864 container_set_dirty(con); 716 node_set_dirty(&con->node);
865} 717}
866 718
867bool container_is_floating(struct sway_container *container) { 719bool container_is_floating(struct sway_container *container) {
868 return container->parent && container->parent->type == C_WORKSPACE && 720 return !container->parent && container->workspace &&
869 list_find(container->parent->sway_workspace->floating, container) != -1; 721 list_find(container->workspace->floating, container) != -1;
870} 722}
871 723
872void container_get_box(struct sway_container *container, struct wlr_box *box) { 724void container_get_box(struct sway_container *container, struct wlr_box *box) {
@@ -883,16 +735,16 @@ void container_floating_translate(struct sway_container *con,
883 double x_amount, double y_amount) { 735 double x_amount, double y_amount) {
884 con->x += x_amount; 736 con->x += x_amount;
885 con->y += y_amount; 737 con->y += y_amount;
886 if (con->type == C_VIEW) { 738 if (con->view) {
887 con->sway_view->x += x_amount; 739 con->view->x += x_amount;
888 con->sway_view->y += y_amount; 740 con->view->y += y_amount;
889 } else { 741 } else {
890 for (int i = 0; i < con->children->length; ++i) { 742 for (int i = 0; i < con->children->length; ++i) {
891 struct sway_container *child = con->children->items[i]; 743 struct sway_container *child = con->children->items[i];
892 container_floating_translate(child, x_amount, y_amount); 744 container_floating_translate(child, x_amount, y_amount);
893 } 745 }
894 } 746 }
895 container_set_dirty(con); 747 node_set_dirty(&con->node);
896} 748}
897 749
898/** 750/**
@@ -902,17 +754,16 @@ void container_floating_translate(struct sway_container *con,
902 * one, otherwise we'll choose whichever output is closest to the container's 754 * one, otherwise we'll choose whichever output is closest to the container's
903 * center. 755 * center.
904 */ 756 */
905struct sway_container *container_floating_find_output( 757struct sway_output *container_floating_find_output(struct sway_container *con) {
906 struct sway_container *con) {
907 double center_x = con->x + con->width / 2; 758 double center_x = con->x + con->width / 2;
908 double center_y = con->y + con->height / 2; 759 double center_y = con->y + con->height / 2;
909 struct sway_container *closest_output = NULL; 760 struct sway_output *closest_output = NULL;
910 double closest_distance = DBL_MAX; 761 double closest_distance = DBL_MAX;
911 for (int i = 0; i < root_container.children->length; ++i) { 762 for (int i = 0; i < root->outputs->length; ++i) {
912 struct sway_container *output = root_container.children->items[i]; 763 struct sway_output *output = root->outputs->items[i];
913 struct wlr_box output_box; 764 struct wlr_box output_box;
914 double closest_x, closest_y; 765 double closest_x, closest_y;
915 container_get_box(output, &output_box); 766 output_get_box(output, &output_box);
916 wlr_box_closest_point(&output_box, center_x, center_y, 767 wlr_box_closest_point(&output_box, center_x, center_y,
917 &closest_x, &closest_y); 768 &closest_x, &closest_y);
918 if (center_x == closest_x && center_y == closest_y) { 769 if (center_x == closest_x && center_y == closest_y) {
@@ -937,18 +788,18 @@ void container_floating_move_to(struct sway_container *con,
937 return; 788 return;
938 } 789 }
939 container_floating_translate(con, lx - con->x, ly - con->y); 790 container_floating_translate(con, lx - con->x, ly - con->y);
940 struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); 791 struct sway_workspace *old_workspace = con->workspace;
941 struct sway_container *new_output = container_floating_find_output(con); 792 struct sway_output *new_output = container_floating_find_output(con);
942 if (!sway_assert(new_output, "Unable to find any output")) { 793 if (!sway_assert(new_output, "Unable to find any output")) {
943 return; 794 return;
944 } 795 }
945 struct sway_container *new_workspace = 796 struct sway_workspace *new_workspace =
946 output_get_active_workspace(new_output->sway_output); 797 output_get_active_workspace(new_output);
947 if (old_workspace != new_workspace) { 798 if (old_workspace != new_workspace) {
948 container_remove_child(con); 799 container_detach(con);
949 workspace_add_floating(new_workspace, con); 800 workspace_add_floating(new_workspace, con);
950 arrange_windows(old_workspace); 801 arrange_workspace(old_workspace);
951 arrange_windows(new_workspace); 802 arrange_workspace(new_workspace);
952 workspace_detect_urgent(old_workspace); 803 workspace_detect_urgent(old_workspace);
953 workspace_detect_urgent(new_workspace); 804 workspace_detect_urgent(new_workspace);
954 } 805 }
@@ -959,22 +810,14 @@ void container_floating_move_to_center(struct sway_container *con) {
959 "Expected a floating container")) { 810 "Expected a floating container")) {
960 return; 811 return;
961 } 812 }
962 struct sway_container *ws = container_parent(con, C_WORKSPACE); 813 struct sway_workspace *ws = con->workspace;
963 double new_lx = ws->x + (ws->width - con->width) / 2; 814 double new_lx = ws->x + (ws->width - con->width) / 2;
964 double new_ly = ws->y + (ws->height - con->height) / 2; 815 double new_ly = ws->y + (ws->height - con->height) / 2;
965 container_floating_translate(con, new_lx - con->x, new_ly - con->y); 816 container_floating_translate(con, new_lx - con->x, new_ly - con->y);
966} 817}
967 818
968void container_set_dirty(struct sway_container *container) {
969 if (container->dirty) {
970 return;
971 }
972 container->dirty = true;
973 list_add(server.dirty_containers, container);
974}
975
976static bool find_urgent_iterator(struct sway_container *con, void *data) { 819static bool find_urgent_iterator(struct sway_container *con, void *data) {
977 return con->type == C_VIEW && view_is_urgent(con->sway_view); 820 return con->view && view_is_urgent(con->view);
978} 821}
979 822
980bool container_has_urgent_child(struct sway_container *container) { 823bool container_has_urgent_child(struct sway_container *container) {
@@ -991,12 +834,12 @@ void container_end_mouse_operation(struct sway_container *container) {
991} 834}
992 835
993static void set_fullscreen_iterator(struct sway_container *con, void *data) { 836static void set_fullscreen_iterator(struct sway_container *con, void *data) {
994 if (con->type != C_VIEW) { 837 if (!con->view) {
995 return; 838 return;
996 } 839 }
997 if (con->sway_view->impl->set_fullscreen) { 840 if (con->view->impl->set_fullscreen) {
998 bool *enable = data; 841 bool *enable = data;
999 con->sway_view->impl->set_fullscreen(con->sway_view, *enable); 842 con->view->impl->set_fullscreen(con->view, *enable);
1000 } 843 }
1001} 844}
1002 845
@@ -1005,9 +848,9 @@ void container_set_fullscreen(struct sway_container *container, bool enable) {
1005 return; 848 return;
1006 } 849 }
1007 850
1008 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 851 struct sway_workspace *workspace = container->workspace;
1009 if (enable && workspace->sway_workspace->fullscreen) { 852 if (enable && workspace->fullscreen) {
1010 container_set_fullscreen(workspace->sway_workspace->fullscreen, false); 853 container_set_fullscreen(workspace->fullscreen, false);
1011 } 854 }
1012 855
1013 set_fullscreen_iterator(container, &enable); 856 set_fullscreen_iterator(container, &enable);
@@ -1016,36 +859,32 @@ void container_set_fullscreen(struct sway_container *container, bool enable) {
1016 container->is_fullscreen = enable; 859 container->is_fullscreen = enable;
1017 860
1018 if (enable) { 861 if (enable) {
1019 workspace->sway_workspace->fullscreen = container; 862 workspace->fullscreen = container;
1020 container->saved_x = container->x; 863 container->saved_x = container->x;
1021 container->saved_y = container->y; 864 container->saved_y = container->y;
1022 container->saved_width = container->width; 865 container->saved_width = container->width;
1023 container->saved_height = container->height; 866 container->saved_height = container->height;
1024 867
1025 struct sway_seat *seat; 868 struct sway_seat *seat;
1026 struct sway_container *focus, *focus_ws; 869 struct sway_workspace *focus_ws;
1027 wl_list_for_each(seat, &input_manager->seats, link) { 870 wl_list_for_each(seat, &input_manager->seats, link) {
1028 focus = seat_get_focus(seat); 871 focus_ws = seat_get_focused_workspace(seat);
1029 if (focus) { 872 if (focus_ws) {
1030 focus_ws = focus;
1031 if (focus_ws->type != C_WORKSPACE) {
1032 focus_ws = container_parent(focus_ws, C_WORKSPACE);
1033 }
1034 if (focus_ws == workspace) { 873 if (focus_ws == workspace) {
1035 seat_set_focus(seat, container); 874 seat_set_focus(seat, &container->node);
1036 } 875 }
1037 } 876 }
1038 } 877 }
1039 } else { 878 } else {
1040 workspace->sway_workspace->fullscreen = NULL; 879 workspace->fullscreen = NULL;
1041 if (container_is_floating(container)) { 880 if (container_is_floating(container)) {
1042 container->x = container->saved_x; 881 container->x = container->saved_x;
1043 container->y = container->saved_y; 882 container->y = container->saved_y;
1044 container->width = container->saved_width; 883 container->width = container->saved_width;
1045 container->height = container->saved_height; 884 container->height = container->saved_height;
1046 struct sway_container *output = 885 struct sway_output *output =
1047 container_floating_find_output(container); 886 container_floating_find_output(container);
1048 if (!container_has_ancestor(container, output)) { 887 if (workspace->output != output) {
1049 container_floating_move_to_center(container); 888 container_floating_move_to_center(container);
1050 } 889 }
1051 } else { 890 } else {
@@ -1060,7 +899,7 @@ void container_set_fullscreen(struct sway_container *container, bool enable) {
1060} 899}
1061 900
1062bool container_is_floating_or_child(struct sway_container *container) { 901bool container_is_floating_or_child(struct sway_container *container) {
1063 while (container->parent && container->parent->type != C_WORKSPACE) { 902 while (container->parent) {
1064 container = container->parent; 903 container = container->parent;
1065 } 904 }
1066 return container_is_floating(container); 905 return container_is_floating(container);
@@ -1072,7 +911,7 @@ bool container_is_fullscreen_or_child(struct sway_container *container) {
1072 return true; 911 return true;
1073 } 912 }
1074 container = container->parent; 913 container = container->parent;
1075 } while (container && container->type != C_WORKSPACE); 914 } while (container);
1076 915
1077 return false; 916 return false;
1078} 917}
@@ -1090,42 +929,37 @@ static void surface_send_leave_iterator(struct wlr_surface *surface,
1090} 929}
1091 930
1092void container_discover_outputs(struct sway_container *con) { 931void container_discover_outputs(struct sway_container *con) {
1093 if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW,
1094 "Expected a container or view")) {
1095 return;
1096 }
1097 struct wlr_box con_box = { 932 struct wlr_box con_box = {
1098 .x = con->current.swayc_x, 933 .x = con->current.con_x,
1099 .y = con->current.swayc_y, 934 .y = con->current.con_y,
1100 .width = con->current.swayc_width, 935 .width = con->current.con_width,
1101 .height = con->current.swayc_height, 936 .height = con->current.con_height,
1102 }; 937 };
1103 struct sway_output *old_output = container_get_effective_output(con); 938 struct sway_output *old_output = container_get_effective_output(con);
1104 939
1105 for (int i = 0; i < root_container.children->length; ++i) { 940 for (int i = 0; i < root->outputs->length; ++i) {
1106 struct sway_container *output = root_container.children->items[i]; 941 struct sway_output *output = root->outputs->items[i];
1107 struct sway_output *sway_output = output->sway_output;
1108 struct wlr_box output_box; 942 struct wlr_box output_box;
1109 container_get_box(output, &output_box); 943 output_get_box(output, &output_box);
1110 struct wlr_box intersection; 944 struct wlr_box intersection;
1111 bool intersects = 945 bool intersects =
1112 wlr_box_intersection(&con_box, &output_box, &intersection); 946 wlr_box_intersection(&con_box, &output_box, &intersection);
1113 int index = list_find(con->outputs, sway_output); 947 int index = list_find(con->outputs, output);
1114 948
1115 if (intersects && index == -1) { 949 if (intersects && index == -1) {
1116 // Send enter 950 // Send enter
1117 wlr_log(WLR_DEBUG, "Con %p entered output %p", con, sway_output); 951 wlr_log(WLR_DEBUG, "Container %p entered output %p", con, output);
1118 if (con->type == C_VIEW) { 952 if (con->view) {
1119 view_for_each_surface(con->sway_view, 953 view_for_each_surface(con->view,
1120 surface_send_enter_iterator, sway_output->wlr_output); 954 surface_send_enter_iterator, output->wlr_output);
1121 } 955 }
1122 list_add(con->outputs, sway_output); 956 list_add(con->outputs, output);
1123 } else if (!intersects && index != -1) { 957 } else if (!intersects && index != -1) {
1124 // Send leave 958 // Send leave
1125 wlr_log(WLR_DEBUG, "Con %p left output %p", con, sway_output); 959 wlr_log(WLR_DEBUG, "Container %p left output %p", con, output);
1126 if (con->type == C_VIEW) { 960 if (con->view) {
1127 view_for_each_surface(con->sway_view, 961 view_for_each_surface(con->view,
1128 surface_send_leave_iterator, sway_output->wlr_output); 962 surface_send_leave_iterator, output->wlr_output);
1129 } 963 }
1130 list_del(con->outputs, index); 964 list_del(con->outputs, index);
1131 } 965 }
@@ -1135,17 +969,13 @@ void container_discover_outputs(struct sway_container *con) {
1135 double new_scale = new_output ? new_output->wlr_output->scale : -1; 969 double new_scale = new_output ? new_output->wlr_output->scale : -1;
1136 if (old_scale != new_scale) { 970 if (old_scale != new_scale) {
1137 container_update_title_textures(con); 971 container_update_title_textures(con);
1138 if (con->type == C_VIEW) { 972 if (con->view) {
1139 view_update_marks_textures(con->sway_view); 973 view_update_marks_textures(con->view);
1140 } 974 }
1141 } 975 }
1142} 976}
1143 977
1144void container_remove_gaps(struct sway_container *c) { 978void container_remove_gaps(struct sway_container *c) {
1145 if (!sway_assert(c->type == C_CONTAINER || c->type == C_VIEW,
1146 "Expected a container or view")) {
1147 return;
1148 }
1149 if (c->current_gaps == 0) { 979 if (c->current_gaps == 0) {
1150 return; 980 return;
1151 } 981 }
@@ -1158,25 +988,20 @@ void container_remove_gaps(struct sway_container *c) {
1158} 988}
1159 989
1160void container_add_gaps(struct sway_container *c) { 990void container_add_gaps(struct sway_container *c) {
1161 if (!sway_assert(c->type == C_CONTAINER || c->type == C_VIEW,
1162 "Expected a container or view")) {
1163 return;
1164 }
1165 if (c->current_gaps > 0) { 991 if (c->current_gaps > 0) {
1166 return; 992 return;
1167 } 993 }
1168 // Linear containers don't have gaps because it'd create double gaps 994 // Linear containers don't have gaps because it'd create double gaps
1169 if (c->type == C_CONTAINER && 995 if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) {
1170 c->layout != L_TABBED && c->layout != L_STACKED) {
1171 return; 996 return;
1172 } 997 }
1173 // Children of tabbed/stacked containers re-use the gaps of the container 998 // Children of tabbed/stacked containers re-use the gaps of the container
1174 enum sway_container_layout layout = c->parent->layout; 999 enum sway_container_layout layout = container_parent_layout(c);
1175 if (layout == L_TABBED || layout == L_STACKED) { 1000 if (layout == L_TABBED || layout == L_STACKED) {
1176 return; 1001 return;
1177 } 1002 }
1178 1003
1179 struct sway_container *ws = container_parent(c, C_WORKSPACE); 1004 struct sway_workspace *ws = c->workspace;
1180 1005
1181 c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; 1006 c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner;
1182 c->x += c->current_gaps; 1007 c->x += c->current_gaps;
@@ -1185,222 +1010,154 @@ void container_add_gaps(struct sway_container *c) {
1185 c->height -= 2 * c->current_gaps; 1010 c->height -= 2 * c->current_gaps;
1186} 1011}
1187 1012
1188int container_sibling_index(const struct sway_container *child) { 1013enum sway_container_layout container_parent_layout(struct sway_container *con) {
1189 return list_find(child->parent->children, child); 1014 if (con->parent) {
1015 return con->parent->layout;
1016 }
1017 return con->workspace->layout;
1190} 1018}
1191 1019
1192void container_handle_fullscreen_reparent(struct sway_container *con, 1020enum sway_container_layout container_current_parent_layout(
1193 struct sway_container *old_parent) { 1021 struct sway_container *con) {
1194 if (!con->is_fullscreen) { 1022 if (con->current.parent) {
1195 return; 1023 return con->current.parent->current.layout;
1196 } 1024 }
1197 struct sway_container *old_workspace = old_parent; 1025 return con->current.workspace->current.layout;
1198 if (old_workspace && old_workspace->type != C_WORKSPACE) { 1026}
1199 old_workspace = container_parent(old_workspace, C_WORKSPACE); 1027
1028list_t *container_get_siblings(const struct sway_container *container) {
1029 if (container->parent) {
1030 return container->parent->children;
1200 } 1031 }
1201 struct sway_container *new_workspace = container_parent(con, C_WORKSPACE); 1032 if (!container->workspace) {
1202 if (old_workspace == new_workspace) { 1033 return NULL;
1203 return;
1204 } 1034 }
1205 // Unmark the old workspace as fullscreen 1035 if (list_find(container->workspace->tiling, container) != -1) {
1206 if (old_workspace) { 1036 return container->workspace->tiling;
1207 old_workspace->sway_workspace->fullscreen = NULL;
1208 } 1037 }
1038 return container->workspace->floating;
1039}
1209 1040
1210 // Mark the new workspace as fullscreen 1041int container_sibling_index(const struct sway_container *child) {
1211 if (new_workspace->sway_workspace->fullscreen) { 1042 return list_find(container_get_siblings(child), child);
1212 container_set_fullscreen( 1043}
1213 new_workspace->sway_workspace->fullscreen, false);
1214 }
1215 new_workspace->sway_workspace->fullscreen = con;
1216 1044
1217 // Resize container to new output dimensions 1045list_t *container_get_current_siblings(struct sway_container *container) {
1218 struct sway_container *output = new_workspace->parent; 1046 if (container->current.parent) {
1219 con->x = output->x; 1047 return container->current.parent->current.children;
1220 con->y = output->y; 1048 }
1221 con->width = output->width; 1049 return container->current.workspace->current.tiling;
1222 con->height = output->height; 1050}
1223 1051
1224 if (con->type == C_VIEW) { 1052void container_handle_fullscreen_reparent(struct sway_container *con) {
1225 struct sway_view *view = con->sway_view; 1053 if (!con->is_fullscreen || con->workspace->fullscreen == con) {
1226 view->x = output->x; 1054 return;
1227 view->y = output->y;
1228 view->width = output->width;
1229 view->height = output->height;
1230 } else {
1231 arrange_windows(new_workspace);
1232 } 1055 }
1056 if (con->workspace->fullscreen) {
1057 container_set_fullscreen(con->workspace->fullscreen, false);
1058 }
1059 con->workspace->fullscreen = con;
1060
1061 arrange_workspace(con->workspace);
1062}
1063
1064static void set_workspace(struct sway_container *container, void *data) {
1065 container->workspace = container->parent->workspace;
1233} 1066}
1234 1067
1235void container_insert_child(struct sway_container *parent, 1068void container_insert_child(struct sway_container *parent,
1236 struct sway_container *child, int i) { 1069 struct sway_container *child, int i) {
1237 struct sway_container *old_parent = child->parent; 1070 if (child->workspace) {
1238 if (old_parent) { 1071 container_detach(child);
1239 container_remove_child(child);
1240 } 1072 }
1241 wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i);
1242 list_insert(parent->children, i, child); 1073 list_insert(parent->children, i, child);
1243 child->parent = parent; 1074 child->parent = parent;
1244 container_handle_fullscreen_reparent(child, old_parent); 1075 child->workspace = parent->workspace;
1076 container_for_each_child(child, set_workspace, NULL);
1077 container_handle_fullscreen_reparent(child);
1078 container_update_representation(parent);
1245} 1079}
1246 1080
1247struct sway_container *container_add_sibling(struct sway_container *fixed, 1081void container_add_sibling(struct sway_container *fixed,
1248 struct sway_container *active) { 1082 struct sway_container *active, int offset) {
1249 // TODO handle floating 1083 if (active->workspace) {
1250 struct sway_container *old_parent = NULL; 1084 container_detach(active);
1251 if (active->parent) { 1085 }
1252 old_parent = active->parent; 1086 list_t *siblings = container_get_siblings(fixed);
1253 container_remove_child(active); 1087 int index = list_find(siblings, fixed);
1254 } 1088 list_insert(siblings, index + offset, active);
1255 struct sway_container *parent = fixed->parent; 1089 active->parent = fixed->parent;
1256 int i = container_sibling_index(fixed); 1090 active->workspace = fixed->workspace;
1257 list_insert(parent->children, i + 1, active); 1091 container_for_each_child(active, set_workspace, NULL);
1258 active->parent = parent; 1092 container_handle_fullscreen_reparent(active);
1259 container_handle_fullscreen_reparent(active, old_parent); 1093 container_update_representation(active);
1260 return active->parent;
1261} 1094}
1262 1095
1263void container_add_child(struct sway_container *parent, 1096void container_add_child(struct sway_container *parent,
1264 struct sway_container *child) { 1097 struct sway_container *child) {
1265 wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", 1098 if (child->workspace) {
1266 child, child->type, child->width, child->height, 1099 container_detach(child);
1267 parent, parent->type, parent->width, parent->height); 1100 }
1268 struct sway_container *old_parent = child->parent;
1269 list_add(parent->children, child); 1101 list_add(parent->children, child);
1270 child->parent = parent; 1102 child->parent = parent;
1271 container_handle_fullscreen_reparent(child, old_parent); 1103 child->workspace = parent->workspace;
1272 if (old_parent) { 1104 container_for_each_child(child, set_workspace, NULL);
1273 container_set_dirty(old_parent); 1105 container_handle_fullscreen_reparent(child);
1274 } 1106 container_update_representation(parent);
1275 container_set_dirty(child); 1107 node_set_dirty(&child->node);
1108 node_set_dirty(&parent->node);
1276} 1109}
1277 1110
1278struct sway_container *container_remove_child(struct sway_container *child) { 1111void container_detach(struct sway_container *child) {
1279 if (child->is_fullscreen) { 1112 if (child->is_fullscreen) {
1280 struct sway_container *workspace = container_parent(child, C_WORKSPACE); 1113 child->workspace->fullscreen = NULL;
1281 workspace->sway_workspace->fullscreen = NULL;
1282 } 1114 }
1283 1115
1284 struct sway_container *parent = child->parent; 1116 struct sway_container *old_parent = child->parent;
1285 list_t *list = container_is_floating(child) ? 1117 struct sway_workspace *old_workspace = child->workspace;
1286 parent->sway_workspace->floating : parent->children; 1118 list_t *siblings = container_get_siblings(child);
1287 int index = list_find(list, child); 1119 int index = list_find(siblings, child);
1288 if (index != -1) { 1120 if (index != -1) {
1289 list_del(list, index); 1121 list_del(siblings, index);
1290 } 1122 }
1291 child->parent = NULL; 1123 child->parent = NULL;
1292 container_notify_subtree_changed(parent); 1124 child->workspace = NULL;
1293 1125 container_for_each_child(child, set_workspace, NULL);
1294 container_set_dirty(parent);
1295 container_set_dirty(child);
1296
1297 return parent;
1298}
1299
1300enum sway_container_layout container_get_default_layout(
1301 struct sway_container *con) {
1302 if (con->type != C_OUTPUT) {
1303 con = container_parent(con, C_OUTPUT);
1304 }
1305 1126
1306 if (!sway_assert(con != NULL, 1127 if (old_parent) {
1307 "container_get_default_layout must be called on an attached" 1128 container_update_representation(old_parent);
1308 " container below the root container")) { 1129 node_set_dirty(&old_parent->node);
1309 return 0;
1310 }
1311
1312 if (config->default_layout != L_NONE) {
1313 return config->default_layout;
1314 } else if (config->default_orientation != L_NONE) {
1315 return config->default_orientation;
1316 } else if (con->width >= con->height) {
1317 return L_HORIZ;
1318 } else { 1130 } else {
1319 return L_VERT; 1131 workspace_update_representation(old_workspace);
1132 node_set_dirty(&old_workspace->node);
1320 } 1133 }
1134 node_set_dirty(&child->node);
1321} 1135}
1322 1136
1323struct sway_container *container_replace_child(struct sway_container *child, 1137void container_replace(struct sway_container *container,
1324 struct sway_container *new_child) { 1138 struct sway_container *replacement) {
1325 struct sway_container *parent = child->parent; 1139 container_add_sibling(container, replacement, 1);
1326 if (parent == NULL) { 1140 container_detach(container);
1327 return NULL;
1328 }
1329
1330 list_t *list = container_is_floating(child) ?
1331 parent->sway_workspace->floating : parent->children;
1332 int i = list_find(list, child);
1333
1334 if (new_child->parent) {
1335 container_remove_child(new_child);
1336 }
1337 list->items[i] = new_child;
1338 new_child->parent = parent;
1339 child->parent = NULL;
1340
1341 // Set geometry for new child
1342 new_child->x = child->x;
1343 new_child->y = child->y;
1344 new_child->width = child->width;
1345 new_child->height = child->height;
1346
1347 // reset geometry for child
1348 child->width = 0;
1349 child->height = 0;
1350
1351 return parent;
1352} 1141}
1353 1142
1354struct sway_container *container_split(struct sway_container *child, 1143struct sway_container *container_split(struct sway_container *child,
1355 enum sway_container_layout layout) { 1144 enum sway_container_layout layout) {
1356 // TODO floating: cannot split a floating container 1145 struct sway_seat *seat = input_manager_get_default_seat(input_manager);
1357 if (!sway_assert(child, "child cannot be null")) { 1146 bool set_focus = (seat_get_focus(seat) == &child->node);
1358 return NULL;
1359 }
1360 if (child->type == C_WORKSPACE && child->children->length == 0) {
1361 // Special case: this just behaves like splitt
1362 child->prev_split_layout = child->layout;
1363 child->layout = layout;
1364 return child;
1365 }
1366
1367 struct sway_container *cont = container_create(C_CONTAINER);
1368
1369 wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child);
1370 1147
1371 cont->prev_split_layout = L_NONE; 1148 struct sway_container *cont = container_create(NULL);
1372 cont->width = child->width; 1149 cont->width = child->width;
1373 cont->height = child->height; 1150 cont->height = child->height;
1374 cont->x = child->x;
1375 cont->y = child->y;
1376 cont->current_gaps = child->current_gaps; 1151 cont->current_gaps = child->current_gaps;
1152 cont->layout = layout;
1377 1153
1378 struct sway_seat *seat = input_manager_get_default_seat(input_manager); 1154 container_replace(child, cont);
1379 bool set_focus = (seat_get_focus(seat) == child); 1155 container_add_child(cont, child);
1380
1381 if (child->type == C_WORKSPACE) {
1382 struct sway_container *workspace = child;
1383 while (workspace->children->length) {
1384 struct sway_container *ws_child = workspace->children->items[0];
1385 container_remove_child(ws_child);
1386 container_add_child(cont, ws_child);
1387 }
1388
1389 container_add_child(workspace, cont);
1390 enum sway_container_layout old_layout = workspace->layout;
1391 workspace->layout = layout;
1392 cont->layout = old_layout;
1393 } else {
1394 cont->layout = layout;
1395 container_replace_child(child, cont);
1396 container_add_child(cont, child);
1397 }
1398 1156
1399 if (set_focus) { 1157 if (set_focus) {
1400 seat_set_focus(seat, cont); 1158 seat_set_focus(seat, &cont->node);
1401 seat_set_focus(seat, child); 1159 seat_set_focus(seat, &child->node);
1402 } 1160 }
1403 1161
1404 container_notify_subtree_changed(cont);
1405 return cont; 1162 return cont;
1406} 1163}
diff --git a/sway/tree/node.c b/sway/tree/node.c
new file mode 100644
index 00000000..74661c1a
--- /dev/null
+++ b/sway/tree/node.c
@@ -0,0 +1,151 @@
1#define _POSIX_C_SOURCE 200809L
2#include "sway/output.h"
3#include "sway/server.h"
4#include "sway/tree/container.h"
5#include "sway/tree/node.h"
6#include "sway/tree/root.h"
7#include "sway/tree/workspace.h"
8#include "log.h"
9
10void node_init(struct sway_node *node, enum sway_node_type type, void *thing) {
11 static size_t next_id = 1;
12 node->id = next_id++;
13 node->type = type;
14 node->sway_root = thing;
15 wl_signal_init(&node->events.destroy);
16}
17
18const char *node_type_to_str(enum sway_node_type type) {
19 switch (type) {
20 case N_ROOT:
21 return "N_ROOT";
22 case N_OUTPUT:
23 return "N_OUTPUT";
24 case N_WORKSPACE:
25 return "N_WORKSPACE";
26 case N_CONTAINER:
27 return "N_CONTAINER";
28 }
29 return "";
30}
31
32void node_set_dirty(struct sway_node *node) {
33 if (node->dirty) {
34 return;
35 }
36 node->dirty = true;
37 list_add(server.dirty_nodes, node);
38}
39
40bool node_is_view(struct sway_node *node) {
41 return node->type == N_CONTAINER && node->sway_container->view;
42}
43
44char *node_get_name(struct sway_node *node) {
45 switch (node->type) {
46 case N_ROOT:
47 return "root";
48 case N_OUTPUT:
49 return node->sway_output->wlr_output->name;
50 case N_WORKSPACE:
51 return node->sway_workspace->name;
52 case N_CONTAINER:
53 return node->sway_container->title;
54 }
55 return NULL;
56}
57
58void node_get_box(struct sway_node *node, struct wlr_box *box) {
59 switch (node->type) {
60 case N_ROOT:
61 root_get_box(root, box);
62 break;
63 case N_OUTPUT:
64 output_get_box(node->sway_output, box);
65 break;
66 case N_WORKSPACE:
67 workspace_get_box(node->sway_workspace, box);
68 break;
69 case N_CONTAINER:
70 container_get_box(node->sway_container, box);
71 break;
72 }
73}
74
75struct sway_output *node_get_output(struct sway_node *node) {
76 switch (node->type) {
77 case N_CONTAINER:
78 return node->sway_container->workspace->output;
79 case N_WORKSPACE:
80 return node->sway_workspace->output;
81 case N_OUTPUT:
82 return node->sway_output;
83 case N_ROOT:
84 return NULL;
85 }
86 return NULL;
87}
88
89enum sway_container_layout node_get_layout(struct sway_node *node) {
90 switch (node->type) {
91 case N_CONTAINER:
92 return node->sway_container->layout;
93 case N_WORKSPACE:
94 return node->sway_workspace->layout;
95 case N_OUTPUT:
96 case N_ROOT:
97 return L_NONE;
98 }
99 return L_NONE;
100}
101
102struct sway_node *node_get_parent(struct sway_node *node) {
103 switch (node->type) {
104 case N_CONTAINER: {
105 struct sway_container *con = node->sway_container;
106 if (con->parent) {
107 return &con->parent->node;
108 }
109 if (con->workspace) {
110 return &con->workspace->node;
111 }
112 }
113 return NULL;
114 case N_WORKSPACE: {
115 struct sway_workspace *ws = node->sway_workspace;
116 if (ws->output) {
117 return &ws->output->node;
118 }
119 }
120 return NULL;
121 case N_OUTPUT:
122 return &root->node;
123 case N_ROOT:
124 return NULL;
125 }
126 return NULL;
127}
128
129list_t *node_get_children(struct sway_node *node) {
130 switch (node->type) {
131 case N_CONTAINER:
132 return node->sway_container->children;
133 case N_WORKSPACE:
134 return node->sway_workspace->tiling;
135 case N_OUTPUT:
136 case N_ROOT:
137 return NULL;
138 }
139 return NULL;
140}
141
142bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {
143 struct sway_node *parent = node_get_parent(node);
144 while (parent) {
145 if (parent == ancestor) {
146 return true;
147 }
148 parent = node_get_parent(parent);
149 }
150 return false;
151}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 6601220b..d72eb1a1 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -2,28 +2,31 @@
2#include <ctype.h> 2#include <ctype.h>
3#include <string.h> 3#include <string.h>
4#include <strings.h> 4#include <strings.h>
5#include <wlr/types/wlr_output_damage.h>
5#include "sway/ipc-server.h" 6#include "sway/ipc-server.h"
7#include "sway/layers.h"
6#include "sway/output.h" 8#include "sway/output.h"
7#include "sway/tree/arrange.h" 9#include "sway/tree/arrange.h"
8#include "sway/tree/output.h" 10#include "sway/tree/output.h"
9#include "sway/tree/workspace.h" 11#include "sway/tree/workspace.h"
10#include "log.h" 12#include "log.h"
13#include "util.h"
11 14
12static void restore_workspaces(struct sway_container *output) { 15static void restore_workspaces(struct sway_output *output) {
13 // Workspace output priority 16 // Workspace output priority
14 for (int i = 0; i < root_container.children->length; i++) { 17 for (int i = 0; i < root->outputs->length; i++) {
15 struct sway_container *other = root_container.children->items[i]; 18 struct sway_output *other = root->outputs->items[i];
16 if (other == output) { 19 if (other == output) {
17 continue; 20 continue;
18 } 21 }
19 22
20 for (int j = 0; j < other->children->length; j++) { 23 for (int j = 0; j < other->workspaces->length; j++) {
21 struct sway_container *ws = other->children->items[j]; 24 struct sway_workspace *ws = other->workspaces->items[j];
22 struct sway_container *highest = 25 struct sway_output *highest =
23 workspace_output_get_highest_available(ws, NULL); 26 workspace_output_get_highest_available(ws, NULL);
24 if (highest == output) { 27 if (highest == output) {
25 container_remove_child(ws); 28 workspace_detach(ws);
26 container_add_child(output, ws); 29 output_add_workspace(output, ws);
27 ipc_event_workspace(NULL, ws, "move"); 30 ipc_event_workspace(NULL, ws, "move");
28 j--; 31 j--;
29 } 32 }
@@ -31,111 +34,111 @@ static void restore_workspaces(struct sway_container *output) {
31 } 34 }
32 35
33 // Saved workspaces 36 // Saved workspaces
34 list_t *saved = root_container.sway_root->saved_workspaces; 37 for (int i = 0; i < root->saved_workspaces->length; ++i) {
35 for (int i = 0; i < saved->length; ++i) { 38 struct sway_workspace *ws = root->saved_workspaces->items[i];
36 struct sway_container *ws = saved->items[i]; 39 output_add_workspace(output, ws);
37 container_add_child(output, ws);
38 ipc_event_workspace(NULL, ws, "move"); 40 ipc_event_workspace(NULL, ws, "move");
39 } 41 }
40 saved->length = 0; 42 root->saved_workspaces->length = 0;
41 43
42 output_sort_workspaces(output); 44 output_sort_workspaces(output);
43} 45}
44 46
45struct sway_container *output_create( 47struct sway_output *output_create(struct wlr_output *wlr_output) {
46 struct sway_output *sway_output) { 48 struct sway_output *output = calloc(1, sizeof(struct sway_output));
47 const char *name = sway_output->wlr_output->name; 49 node_init(&output->node, N_OUTPUT, output);
48 char identifier[128]; 50 output->wlr_output = wlr_output;
49 output_get_identifier(identifier, sizeof(identifier), sway_output); 51 wlr_output->data = output;
50 52
51 struct output_config *oc = NULL, *all = NULL; 53 wl_signal_add(&wlr_output->events.destroy, &output->destroy);
52 for (int i = 0; i < config->output_configs->length; ++i) {
53 struct output_config *cur = config->output_configs->items[i];
54 54
55 if (strcasecmp(name, cur->name) == 0 || 55 wl_list_insert(&root->all_outputs, &output->link);
56 strcasecmp(identifier, cur->name) == 0) {
57 wlr_log(WLR_DEBUG, "Matched output config for %s", name);
58 oc = cur;
59 }
60 if (strcasecmp("*", cur->name) == 0) {
61 wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
62 all = cur;
63 }
64 56
65 if (oc && all) { 57 if (!wl_list_empty(&wlr_output->modes)) {
66 break; 58 struct wlr_output_mode *mode =
67 } 59 wl_container_of(wlr_output->modes.prev, mode, link);
68 } 60 wlr_output_set_mode(wlr_output, mode);
69 if (!oc) {
70 oc = all;
71 } 61 }
72 62
73 if (oc && !oc->enabled) { 63 output->workspaces = create_list();
74 return NULL; 64 output->current.workspaces = create_list();
75 }
76 65
77 struct sway_container *output = container_create(C_OUTPUT); 66 return output;
78 output->sway_output = sway_output; 67}
79 output->name = strdup(name);
80 if (output->name == NULL) {
81 output_begin_destroy(output);
82 return NULL;
83 }
84 68
69void output_enable(struct sway_output *output, struct output_config *oc) {
70 if (!sway_assert(!output->enabled, "output is already enabled")) {
71 return;
72 }
73 struct wlr_output *wlr_output = output->wlr_output;
74 output->enabled = true;
85 apply_output_config(oc, output); 75 apply_output_config(oc, output);
86 container_add_child(&root_container, output); 76 list_add(root->outputs, output);
87 load_swaybars();
88
89 struct wlr_box size;
90 wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
91 &size.height);
92 output->width = size.width;
93 output->height = size.height;
94 77
95 restore_workspaces(output); 78 restore_workspaces(output);
96 79
97 if (!output->children->length) { 80 if (!output->workspaces->length) {
98 // Create workspace 81 // Create workspace
99 char *ws_name = workspace_next_name(output->name); 82 char *ws_name = workspace_next_name(wlr_output->name);
100 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); 83 wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name);
101 struct sway_container *ws = workspace_create(output, ws_name); 84 struct sway_workspace *ws = workspace_create(output, ws_name);
102 // Set each seat's focus if not already set 85 // Set each seat's focus if not already set
103 struct sway_seat *seat = NULL; 86 struct sway_seat *seat = NULL;
104 wl_list_for_each(seat, &input_manager->seats, link) { 87 wl_list_for_each(seat, &input_manager->seats, link) {
105 if (!seat->has_focus) { 88 if (!seat->has_focus) {
106 seat_set_focus(seat, ws); 89 seat_set_focus(seat, &ws->node);
107 } 90 }
108 } 91 }
109 free(ws_name); 92 free(ws_name);
110 } 93 }
111 94
112 container_create_notify(output); 95 size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
113 return output; 96 for (size_t i = 0; i < len; ++i) {
97 wl_list_init(&output->layers[i]);
98 }
99 wl_signal_init(&output->events.destroy);
100
101 input_manager_configure_xcursor(input_manager);
102
103 wl_signal_add(&wlr_output->events.mode, &output->mode);
104 wl_signal_add(&wlr_output->events.transform, &output->transform);
105 wl_signal_add(&wlr_output->events.scale, &output->scale);
106 wl_signal_add(&output->damage->events.frame, &output->damage_frame);
107 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
108
109 output_add_listeners(output);
110
111 wl_signal_emit(&root->events.new_node, &output->node);
112
113 load_swaybars();
114
115 arrange_layers(output);
116 arrange_root();
114} 117}
115 118
116static void output_evacuate(struct sway_container *output) { 119static void output_evacuate(struct sway_output *output) {
117 if (!output->children->length) { 120 if (!output->workspaces->length) {
118 return; 121 return;
119 } 122 }
120 struct sway_container *fallback_output = NULL; 123 struct sway_output *fallback_output = NULL;
121 if (root_container.children->length > 1) { 124 if (root->outputs->length > 1) {
122 fallback_output = root_container.children->items[0]; 125 fallback_output = root->outputs->items[0];
123 if (fallback_output == output) { 126 if (fallback_output == output) {
124 fallback_output = root_container.children->items[1]; 127 fallback_output = root->outputs->items[1];
125 } 128 }
126 } 129 }
127 130
128 while (output->children->length) { 131 while (output->workspaces->length) {
129 struct sway_container *workspace = output->children->items[0]; 132 struct sway_workspace *workspace = output->workspaces->items[0];
130 133
131 container_remove_child(workspace); 134 workspace_detach(workspace);
132 135
133 if (workspace_is_empty(workspace)) { 136 if (workspace_is_empty(workspace)) {
134 workspace_begin_destroy(workspace); 137 workspace_begin_destroy(workspace);
135 continue; 138 continue;
136 } 139 }
137 140
138 struct sway_container *new_output = 141 struct sway_output *new_output =
139 workspace_output_get_highest_available(workspace, output); 142 workspace_output_get_highest_available(workspace, output);
140 if (!new_output) { 143 if (!new_output) {
141 new_output = fallback_output; 144 new_output = fallback_output;
@@ -143,39 +146,31 @@ static void output_evacuate(struct sway_container *output) {
143 146
144 if (new_output) { 147 if (new_output) {
145 workspace_output_add_priority(workspace, new_output); 148 workspace_output_add_priority(workspace, new_output);
146 container_add_child(new_output, workspace); 149 output_add_workspace(new_output, workspace);
147 output_sort_workspaces(new_output); 150 output_sort_workspaces(new_output);
148 ipc_event_workspace(NULL, workspace, "move"); 151 ipc_event_workspace(NULL, workspace, "move");
149 } else { 152 } else {
150 list_add(root_container.sway_root->saved_workspaces, workspace); 153 list_add(root->saved_workspaces, workspace);
151 } 154 }
152 } 155 }
153} 156}
154 157
155void output_destroy(struct sway_container *output) { 158void output_destroy(struct sway_output *output) {
156 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 159 if (!sway_assert(output->node.destroying,
160 "Tried to free output which wasn't marked as destroying")) {
157 return; 161 return;
158 } 162 }
159 if (!sway_assert(output->destroying, 163 if (!sway_assert(output->wlr_output == NULL,
160 "Tried to free output which wasn't marked as destroying")) { 164 "Tried to free output which still had a wlr_output")) {
161 return; 165 return;
162 } 166 }
163 if (!sway_assert(output->ntxnrefs == 0, "Tried to free output " 167 if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output "
164 "which is still referenced by transactions")) { 168 "which is still referenced by transactions")) {
165 return; 169 return;
166 } 170 }
167 free(output->name); 171 list_free(output->workspaces);
168 free(output->formatted_title); 172 list_free(output->current.workspaces);
169 wlr_texture_destroy(output->title_focused);
170 wlr_texture_destroy(output->title_focused_inactive);
171 wlr_texture_destroy(output->title_unfocused);
172 wlr_texture_destroy(output->title_urgent);
173 list_free(output->children);
174 list_free(output->current.children);
175 list_free(output->outputs);
176 free(output); 173 free(output);
177
178 // NOTE: We don't actually destroy the sway_output here
179} 174}
180 175
181static void untrack_output(struct sway_container *con, void *data) { 176static void untrack_output(struct sway_container *con, void *data) {
@@ -186,76 +181,131 @@ static void untrack_output(struct sway_container *con, void *data) {
186 } 181 }
187} 182}
188 183
189void output_begin_destroy(struct sway_container *output) { 184void output_disable(struct sway_output *output) {
190 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 185 if (!sway_assert(output->enabled, "Expected an enabled output")) {
191 return; 186 return;
192 } 187 }
193 wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); 188 wlr_log(WLR_DEBUG, "Disabling output '%s'", output->wlr_output->name);
194 wl_signal_emit(&output->events.destroy, output); 189 wl_signal_emit(&output->events.destroy, output);
195 190
196 output_evacuate(output); 191 output_evacuate(output);
197 192
198 output->destroying = true; 193 root_for_each_container(untrack_output, output);
199 container_set_dirty(output); 194
195 int index = list_find(root->outputs, output);
196 list_del(root->outputs, index);
200 197
201 root_for_each_container(untrack_output, output->sway_output); 198 wl_list_remove(&output->mode.link);
199 wl_list_remove(&output->transform.link);
200 wl_list_remove(&output->scale.link);
201 wl_list_remove(&output->damage_destroy.link);
202 wl_list_remove(&output->damage_frame.link);
202 203
203 wl_list_remove(&output->sway_output->mode.link); 204 output->enabled = false;
204 wl_list_remove(&output->sway_output->transform.link);
205 wl_list_remove(&output->sway_output->scale.link);
206 wl_list_remove(&output->sway_output->damage_destroy.link);
207 wl_list_remove(&output->sway_output->damage_frame.link);
208 205
209 output->sway_output->swayc = NULL; 206 arrange_root();
210 output->sway_output = NULL; 207}
211 208
212 if (output->parent) { 209void output_begin_destroy(struct sway_output *output) {
213 container_remove_child(output); 210 if (!sway_assert(!output->enabled, "Expected a disabled output")) {
211 return;
214 } 212 }
213 wlr_log(WLR_DEBUG, "Destroying output '%s'", output->wlr_output->name);
214
215 output->node.destroying = true;
216 node_set_dirty(&output->node);
217
218 wl_list_remove(&output->link);
219 wl_list_remove(&output->destroy.link);
220 output->wlr_output->data = NULL;
221 output->wlr_output = NULL;
215} 222}
216 223
217struct sway_container *output_from_wlr_output(struct wlr_output *output) { 224struct output_config *output_find_config(struct sway_output *output) {
218 if (output == NULL) { 225 const char *name = output->wlr_output->name;
226 char identifier[128];
227 output_get_identifier(identifier, sizeof(identifier), output);
228
229 struct output_config *oc = NULL, *all = NULL;
230 for (int i = 0; i < config->output_configs->length; ++i) {
231 struct output_config *cur = config->output_configs->items[i];
232
233 if (strcasecmp(name, cur->name) == 0 ||
234 strcasecmp(identifier, cur->name) == 0) {
235 wlr_log(WLR_DEBUG, "Matched output config for %s", name);
236 oc = cur;
237 }
238 if (strcasecmp("*", cur->name) == 0) {
239 wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
240 all = cur;
241 }
242
243 if (oc && all) {
244 break;
245 }
246 }
247 if (!oc) {
248 oc = all;
249 }
250
251 if (oc && !oc->enabled) {
219 return NULL; 252 return NULL;
220 } 253 }
221 for (int i = 0; i < root_container.children->length; ++i) { 254 return oc;
222 struct sway_container *o = root_container.children->items[i]; 255}
223 if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) { 256
224 return o; 257struct sway_output *output_from_wlr_output(struct wlr_output *output) {
225 } 258 return output->data;
259}
260
261struct sway_output *output_get_in_direction(struct sway_output *reference,
262 enum movement_direction direction) {
263 enum wlr_direction wlr_dir = 0;
264 if (!sway_assert(sway_dir_to_wlr(direction, &wlr_dir),
265 "got invalid direction: %d", direction)) {
266 return NULL;
226 } 267 }
227 return NULL; 268 int lx = reference->wlr_output->lx + reference->wlr_output->width / 2;
269 int ly = reference->wlr_output->ly + reference->wlr_output->height / 2;
270 struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(
271 root->output_layout, wlr_dir, reference->wlr_output, lx, ly);
272 if (!wlr_adjacent) {
273 return NULL;
274 }
275 return output_from_wlr_output(wlr_adjacent);
228} 276}
229 277
230void output_for_each_workspace(struct sway_container *output, 278void output_add_workspace(struct sway_output *output,
231 void (*f)(struct sway_container *con, void *data), void *data) { 279 struct sway_workspace *workspace) {
232 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 280 if (workspace->output) {
233 return; 281 workspace_detach(workspace);
234 } 282 }
235 for (int i = 0; i < output->children->length; ++i) { 283 list_add(output->workspaces, workspace);
236 struct sway_container *workspace = output->children->items[i]; 284 workspace->output = output;
285 node_set_dirty(&output->node);
286 node_set_dirty(&workspace->node);
287}
288
289void output_for_each_workspace(struct sway_output *output,
290 void (*f)(struct sway_workspace *ws, void *data), void *data) {
291 for (int i = 0; i < output->workspaces->length; ++i) {
292 struct sway_workspace *workspace = output->workspaces->items[i];
237 f(workspace, data); 293 f(workspace, data);
238 } 294 }
239} 295}
240 296
241void output_for_each_container(struct sway_container *output, 297void output_for_each_container(struct sway_output *output,
242 void (*f)(struct sway_container *con, void *data), void *data) { 298 void (*f)(struct sway_container *con, void *data), void *data) {
243 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 299 for (int i = 0; i < output->workspaces->length; ++i) {
244 return; 300 struct sway_workspace *workspace = output->workspaces->items[i];
245 }
246 for (int i = 0; i < output->children->length; ++i) {
247 struct sway_container *workspace = output->children->items[i];
248 workspace_for_each_container(workspace, f, data); 301 workspace_for_each_container(workspace, f, data);
249 } 302 }
250} 303}
251 304
252struct sway_container *output_find_workspace(struct sway_container *output, 305struct sway_workspace *output_find_workspace(struct sway_output *output,
253 bool (*test)(struct sway_container *con, void *data), void *data) { 306 bool (*test)(struct sway_workspace *ws, void *data), void *data) {
254 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { 307 for (int i = 0; i < output->workspaces->length; ++i) {
255 return NULL; 308 struct sway_workspace *workspace = output->workspaces->items[i];
256 }
257 for (int i = 0; i < output->children->length; ++i) {
258 struct sway_container *workspace = output->children->items[i];
259 if (test(workspace, data)) { 309 if (test(workspace, data)) {
260 return workspace; 310 return workspace;
261 } 311 }
@@ -263,14 +313,11 @@ struct sway_container *output_find_workspace(struct sway_container *output,
263 return NULL; 313 return NULL;
264} 314}
265 315
266struct sway_container *output_find_container(struct sway_container *output, 316struct sway_container *output_find_container(struct sway_output *output,
267 bool (*test)(struct sway_container *con, void *data), void *data) { 317 bool (*test)(struct sway_container *con, void *data), void *data) {
268 if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
269 return NULL;
270 }
271 struct sway_container *result = NULL; 318 struct sway_container *result = NULL;
272 for (int i = 0; i < output->children->length; ++i) { 319 for (int i = 0; i < output->workspaces->length; ++i) {
273 struct sway_container *workspace = output->children->items[i]; 320 struct sway_workspace *workspace = output->workspaces->items[i];
274 if ((result = workspace_find_container(workspace, test, data))) { 321 if ((result = workspace_find_container(workspace, test, data))) {
275 return result; 322 return result;
276 } 323 }
@@ -279,8 +326,8 @@ struct sway_container *output_find_container(struct sway_container *output,
279} 326}
280 327
281static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { 328static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
282 struct sway_container *a = *(void **)_a; 329 struct sway_workspace *a = *(void **)_a;
283 struct sway_container *b = *(void **)_b; 330 struct sway_workspace *b = *(void **)_b;
284 331
285 if (isdigit(a->name[0]) && isdigit(b->name[0])) { 332 if (isdigit(a->name[0]) && isdigit(b->name[0])) {
286 int a_num = strtol(a->name, NULL, 10); 333 int a_num = strtol(a->name, NULL, 10);
@@ -294,6 +341,27 @@ static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
294 return 0; 341 return 0;
295} 342}
296 343
297void output_sort_workspaces(struct sway_container *output) { 344void output_sort_workspaces(struct sway_output *output) {
298 list_stable_sort(output->children, sort_workspace_cmp_qsort); 345 list_stable_sort(output->workspaces, sort_workspace_cmp_qsort);
346}
347
348void output_get_box(struct sway_output *output, struct wlr_box *box) {
349 box->x = output->wlr_output->lx;
350 box->y = output->wlr_output->ly;
351 box->width = output->wlr_output->width;
352 box->height = output->wlr_output->height;
353}
354
355enum sway_container_layout output_get_default_layout(
356 struct sway_output *output) {
357 if (config->default_layout != L_NONE) {
358 return config->default_layout;
359 }
360 if (config->default_orientation != L_NONE) {
361 return config->default_orientation;
362 }
363 if (output->wlr_output->height > output->wlr_output->width) {
364 return L_VERT;
365 }
366 return L_HORIZ;
299} 367}
diff --git a/sway/tree/root.c b/sway/tree/root.c
index b42371de..ecc04ddb 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -14,54 +14,45 @@
14#include "log.h" 14#include "log.h"
15#include "util.h" 15#include "util.h"
16 16
17struct sway_container root_container; 17struct sway_root *root;
18 18
19static void output_layout_handle_change(struct wl_listener *listener, 19static void output_layout_handle_change(struct wl_listener *listener,
20 void *data) { 20 void *data) {
21 arrange_windows(&root_container); 21 arrange_root();
22 transaction_commit_dirty(); 22 transaction_commit_dirty();
23} 23}
24 24
25void root_create(void) { 25struct sway_root *root_create(void) {
26 root_container.id = 0; // normally assigned in new_swayc() 26 struct sway_root *root = calloc(1, sizeof(struct sway_root));
27 root_container.type = C_ROOT; 27 if (!root) {
28 root_container.layout = L_NONE; 28 wlr_log(WLR_ERROR, "Unable to allocate sway_root");
29 root_container.name = strdup("root"); 29 return NULL;
30 root_container.children = create_list(); 30 }
31 root_container.current.children = create_list(); 31 node_init(&root->node, N_ROOT, root);
32 wl_signal_init(&root_container.events.destroy); 32 root->output_layout = wlr_output_layout_create();
33 33 wl_list_init(&root->all_outputs);
34 root_container.sway_root = calloc(1, sizeof(*root_container.sway_root));
35 root_container.sway_root->output_layout = wlr_output_layout_create();
36 wl_list_init(&root_container.sway_root->all_outputs);
37#ifdef HAVE_XWAYLAND 34#ifdef HAVE_XWAYLAND
38 wl_list_init(&root_container.sway_root->xwayland_unmanaged); 35 wl_list_init(&root->xwayland_unmanaged);
39#endif 36#endif
40 wl_list_init(&root_container.sway_root->drag_icons); 37 wl_list_init(&root->drag_icons);
41 wl_signal_init(&root_container.sway_root->events.new_container); 38 wl_signal_init(&root->events.new_node);
42 root_container.sway_root->scratchpad = create_list(); 39 root->outputs = create_list();
43 root_container.sway_root->saved_workspaces = create_list(); 40 root->scratchpad = create_list();
44 41 root->saved_workspaces = create_list();
45 root_container.sway_root->output_layout_change.notify = 42
46 output_layout_handle_change; 43 root->output_layout_change.notify = output_layout_handle_change;
47 wl_signal_add(&root_container.sway_root->output_layout->events.change, 44 wl_signal_add(&root->output_layout->events.change,
48 &root_container.sway_root->output_layout_change); 45 &root->output_layout_change);
46 return root;
49} 47}
50 48
51void root_destroy(void) { 49void root_destroy(struct sway_root *root) {
52 // sway_root 50 wl_list_remove(&root->output_layout_change.link);
53 wl_list_remove(&root_container.sway_root->output_layout_change.link); 51 list_free(root->scratchpad);
54 list_free(root_container.sway_root->scratchpad); 52 list_free(root->saved_workspaces);
55 list_free(root_container.sway_root->saved_workspaces); 53 list_free(root->outputs);
56 wlr_output_layout_destroy(root_container.sway_root->output_layout); 54 wlr_output_layout_destroy(root->output_layout);
57 free(root_container.sway_root); 55 free(root);
58
59 // root_container
60 list_free(root_container.children);
61 list_free(root_container.current.children);
62 free(root_container.name);
63
64 memset(&root_container, 0, sizeof(root_container));
65} 56}
66 57
67void root_scratchpad_add_container(struct sway_container *con) { 58void root_scratchpad_add_container(struct sway_container *con) {
@@ -69,15 +60,21 @@ void root_scratchpad_add_container(struct sway_container *con) {
69 return; 60 return;
70 } 61 }
71 con->scratchpad = true; 62 con->scratchpad = true;
72 list_add(root_container.sway_root->scratchpad, con); 63 list_add(root->scratchpad, con);
73 64
74 struct sway_container *parent = con->parent; 65 struct sway_container *parent = con->parent;
66 struct sway_workspace *workspace = con->workspace;
75 container_set_floating(con, true); 67 container_set_floating(con, true);
76 container_remove_child(con); 68 container_detach(con);
77 arrange_windows(parent);
78 69
79 struct sway_seat *seat = input_manager_current_seat(input_manager); 70 struct sway_seat *seat = input_manager_current_seat(input_manager);
80 seat_set_focus(seat, seat_get_focus_inactive(seat, parent)); 71 if (parent) {
72 arrange_container(parent);
73 seat_set_focus(seat, seat_get_focus_inactive(seat, &parent->node));
74 } else {
75 arrange_workspace(workspace);
76 seat_set_focus(seat, seat_get_focus_inactive(seat, &workspace->node));
77 }
81} 78}
82 79
83void root_scratchpad_remove_container(struct sway_container *con) { 80void root_scratchpad_remove_container(struct sway_container *con) {
@@ -85,28 +82,25 @@ void root_scratchpad_remove_container(struct sway_container *con) {
85 return; 82 return;
86 } 83 }
87 con->scratchpad = false; 84 con->scratchpad = false;
88 int index = list_find(root_container.sway_root->scratchpad, con); 85 int index = list_find(root->scratchpad, con);
89 if (index != -1) { 86 if (index != -1) {
90 list_del(root_container.sway_root->scratchpad, index); 87 list_del(root->scratchpad, index);
91 } 88 }
92} 89}
93 90
94void root_scratchpad_show(struct sway_container *con) { 91void root_scratchpad_show(struct sway_container *con) {
95 struct sway_seat *seat = input_manager_current_seat(input_manager); 92 struct sway_seat *seat = input_manager_current_seat(input_manager);
96 struct sway_container *ws = seat_get_focus(seat); 93 struct sway_workspace *ws = seat_get_focused_workspace(seat);
97 if (ws->type != C_WORKSPACE) {
98 ws = container_parent(ws, C_WORKSPACE);
99 }
100 94
101 // If the current con or any of its parents are in fullscreen mode, we 95 // If the current con or any of its parents are in fullscreen mode, we
102 // first need to disable it before showing the scratchpad con. 96 // first need to disable it before showing the scratchpad con.
103 if (ws->sway_workspace->fullscreen) { 97 if (ws->fullscreen) {
104 container_set_fullscreen(ws->sway_workspace->fullscreen, false); 98 container_set_fullscreen(ws->fullscreen, false);
105 } 99 }
106 100
107 // Show the container 101 // Show the container
108 if (con->parent) { 102 if (con->workspace) {
109 container_remove_child(con); 103 container_detach(con);
110 } 104 }
111 workspace_add_floating(ws, con); 105 workspace_add_floating(ws, con);
112 106
@@ -115,7 +109,7 @@ void root_scratchpad_show(struct sway_container *con) {
115 double center_ly = con->y + con->height / 2; 109 double center_ly = con->y + con->height / 2;
116 110
117 struct wlr_box workspace_box; 111 struct wlr_box workspace_box;
118 container_get_box(ws, &workspace_box); 112 workspace_get_box(ws, &workspace_box);
119 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { 113 if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
120 // Maybe resize it 114 // Maybe resize it
121 if (con->width > ws->width || con->height > ws->height) { 115 if (con->width > ws->width || con->height > ws->height) {
@@ -128,23 +122,21 @@ void root_scratchpad_show(struct sway_container *con) {
128 container_floating_move_to(con, new_lx, new_ly); 122 container_floating_move_to(con, new_lx, new_ly);
129 } 123 }
130 124
131 arrange_windows(ws); 125 arrange_workspace(ws);
132 seat_set_focus(seat, seat_get_focus_inactive(seat, con)); 126 seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
133
134 container_set_dirty(con->parent);
135} 127}
136 128
137void root_scratchpad_hide(struct sway_container *con) { 129void root_scratchpad_hide(struct sway_container *con) {
138 struct sway_seat *seat = input_manager_current_seat(input_manager); 130 struct sway_seat *seat = input_manager_current_seat(input_manager);
139 struct sway_container *focus = seat_get_focus(seat); 131 struct sway_node *focus = seat_get_focus(seat);
140 struct sway_container *ws = container_parent(con, C_WORKSPACE); 132 struct sway_workspace *ws = con->workspace;
141 133
142 container_remove_child(con); 134 container_detach(con);
143 arrange_windows(ws); 135 arrange_workspace(ws);
144 if (con == focus) { 136 if (&con->node == focus) {
145 seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); 137 seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node));
146 } 138 }
147 list_move_to_end(root_container.sway_root->scratchpad, con); 139 list_move_to_end(root->scratchpad, con);
148} 140}
149 141
150struct pid_workspace { 142struct pid_workspace {
@@ -152,7 +144,7 @@ struct pid_workspace {
152 char *workspace; 144 char *workspace;
153 struct timespec time_added; 145 struct timespec time_added;
154 146
155 struct sway_container *output; 147 struct sway_output *output;
156 struct wl_listener output_destroy; 148 struct wl_listener output_destroy;
157 149
158 struct wl_list link; 150 struct wl_list link;
@@ -160,13 +152,13 @@ struct pid_workspace {
160 152
161static struct wl_list pid_workspaces; 153static struct wl_list pid_workspaces;
162 154
163struct sway_container *root_workspace_for_pid(pid_t pid) { 155struct sway_workspace *root_workspace_for_pid(pid_t pid) {
164 if (!pid_workspaces.prev && !pid_workspaces.next) { 156 if (!pid_workspaces.prev && !pid_workspaces.next) {
165 wl_list_init(&pid_workspaces); 157 wl_list_init(&pid_workspaces);
166 return NULL; 158 return NULL;
167 } 159 }
168 160
169 struct sway_container *ws = NULL; 161 struct sway_workspace *ws = NULL;
170 struct pid_workspace *pw = NULL; 162 struct pid_workspace *pw = NULL;
171 163
172 wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); 164 wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid);
@@ -219,16 +211,12 @@ void root_record_workspace_pid(pid_t pid) {
219 } 211 }
220 212
221 struct sway_seat *seat = input_manager_current_seat(input_manager); 213 struct sway_seat *seat = input_manager_current_seat(input_manager);
222 struct sway_container *ws = 214 struct sway_workspace *ws = seat_get_focused_workspace(seat);
223 seat_get_focus_inactive(seat, &root_container);
224 if (ws && ws->type != C_WORKSPACE) {
225 ws = container_parent(ws, C_WORKSPACE);
226 }
227 if (!ws) { 215 if (!ws) {
228 wlr_log(WLR_DEBUG, "Bailing out, no workspace"); 216 wlr_log(WLR_DEBUG, "Bailing out, no workspace");
229 return; 217 return;
230 } 218 }
231 struct sway_container *output = ws->parent; 219 struct sway_output *output = ws->output;
232 if (!output) { 220 if (!output) {
233 wlr_log(WLR_DEBUG, "Bailing out, no output"); 221 wlr_log(WLR_DEBUG, "Bailing out, no output");
234 return; 222 return;
@@ -255,30 +243,28 @@ void root_record_workspace_pid(pid_t pid) {
255 pw->pid = pid; 243 pw->pid = pid;
256 memcpy(&pw->time_added, &now, sizeof(struct timespec)); 244 memcpy(&pw->time_added, &now, sizeof(struct timespec));
257 pw->output_destroy.notify = pw_handle_output_destroy; 245 pw->output_destroy.notify = pw_handle_output_destroy;
258 wl_signal_add(&output->sway_output->wlr_output->events.destroy, 246 wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy);
259 &pw->output_destroy);
260 wl_list_insert(&pid_workspaces, &pw->link); 247 wl_list_insert(&pid_workspaces, &pw->link);
261} 248}
262 249
263void root_for_each_workspace(void (*f)(struct sway_container *con, void *data), 250void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
264 void *data) { 251 void *data) {
265 for (int i = 0; i < root_container.children->length; ++i) { 252 for (int i = 0; i < root->outputs->length; ++i) {
266 struct sway_container *output = root_container.children->items[i]; 253 struct sway_output *output = root->outputs->items[i];
267 output_for_each_workspace(output, f, data); 254 output_for_each_workspace(output, f, data);
268 } 255 }
269} 256}
270 257
271void root_for_each_container(void (*f)(struct sway_container *con, void *data), 258void root_for_each_container(void (*f)(struct sway_container *con, void *data),
272 void *data) { 259 void *data) {
273 for (int i = 0; i < root_container.children->length; ++i) { 260 for (int i = 0; i < root->outputs->length; ++i) {
274 struct sway_container *output = root_container.children->items[i]; 261 struct sway_output *output = root->outputs->items[i];
275 output_for_each_container(output, f, data); 262 output_for_each_container(output, f, data);
276 } 263 }
277 264
278 // Scratchpad 265 // Scratchpad
279 for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { 266 for (int i = 0; i < root->scratchpad->length; ++i) {
280 struct sway_container *container = 267 struct sway_container *container = root->scratchpad->items[i];
281 root_container.sway_root->scratchpad->items[i];
282 // If the container has a parent then it's visible on a workspace 268 // If the container has a parent then it's visible on a workspace
283 // and will have been iterated in the previous for loop. So we only 269 // and will have been iterated in the previous for loop. So we only
284 // iterate the hidden scratchpad containers here. 270 // iterate the hidden scratchpad containers here.
@@ -289,10 +275,10 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
289 } 275 }
290} 276}
291 277
292struct sway_container *root_find_output( 278struct sway_output *root_find_output(
293 bool (*test)(struct sway_container *con, void *data), void *data) { 279 bool (*test)(struct sway_output *output, void *data), void *data) {
294 for (int i = 0; i < root_container.children->length; ++i) { 280 for (int i = 0; i < root->outputs->length; ++i) {
295 struct sway_container *output = root_container.children->items[i]; 281 struct sway_output *output = root->outputs->items[i];
296 if (test(output, data)) { 282 if (test(output, data)) {
297 return output; 283 return output;
298 } 284 }
@@ -300,11 +286,11 @@ struct sway_container *root_find_output(
300 return NULL; 286 return NULL;
301} 287}
302 288
303struct sway_container *root_find_workspace( 289struct sway_workspace *root_find_workspace(
304 bool (*test)(struct sway_container *con, void *data), void *data) { 290 bool (*test)(struct sway_workspace *ws, void *data), void *data) {
305 struct sway_container *result = NULL; 291 struct sway_workspace *result = NULL;
306 for (int i = 0; i < root_container.children->length; ++i) { 292 for (int i = 0; i < root->outputs->length; ++i) {
307 struct sway_container *output = root_container.children->items[i]; 293 struct sway_output *output = root->outputs->items[i];
308 if ((result = output_find_workspace(output, test, data))) { 294 if ((result = output_find_workspace(output, test, data))) {
309 return result; 295 return result;
310 } 296 }
@@ -315,17 +301,16 @@ struct sway_container *root_find_workspace(
315struct sway_container *root_find_container( 301struct sway_container *root_find_container(
316 bool (*test)(struct sway_container *con, void *data), void *data) { 302 bool (*test)(struct sway_container *con, void *data), void *data) {
317 struct sway_container *result = NULL; 303 struct sway_container *result = NULL;
318 for (int i = 0; i < root_container.children->length; ++i) { 304 for (int i = 0; i < root->outputs->length; ++i) {
319 struct sway_container *output = root_container.children->items[i]; 305 struct sway_output *output = root->outputs->items[i];
320 if ((result = output_find_container(output, test, data))) { 306 if ((result = output_find_container(output, test, data))) {
321 return result; 307 return result;
322 } 308 }
323 } 309 }
324 310
325 // Scratchpad 311 // Scratchpad
326 for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { 312 for (int i = 0; i < root->scratchpad->length; ++i) {
327 struct sway_container *container = 313 struct sway_container *container = root->scratchpad->items[i];
328 root_container.sway_root->scratchpad->items[i];
329 if (!container->parent) { 314 if (!container->parent) {
330 if (test(container, data)) { 315 if (test(container, data)) {
331 return container; 316 return container;
@@ -337,3 +322,10 @@ struct sway_container *root_find_container(
337 } 322 }
338 return NULL; 323 return NULL;
339} 324}
325
326void root_get_box(struct sway_root *root, struct wlr_box *box) {
327 box->x = root->x;
328 box->y = root->y;
329 box->width = root->width;
330 box->height = root->height;
331}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 6bd0ef67..452c2bd1 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -33,6 +33,8 @@ void view_init(struct sway_view *view, enum sway_view_type type,
33 view->marks = create_list(); 33 view->marks = create_list();
34 view->allow_request_urgent = true; 34 view->allow_request_urgent = true;
35 wl_signal_init(&view->events.unmap); 35 wl_signal_init(&view->events.unmap);
36
37 view->container = container_create(view);
36} 38}
37 39
38void view_destroy(struct sway_view *view) { 40void view_destroy(struct sway_view *view) {
@@ -43,8 +45,8 @@ void view_destroy(struct sway_view *view) {
43 "Tried to free view which wasn't marked as destroying")) { 45 "Tried to free view which wasn't marked as destroying")) {
44 return; 46 return;
45 } 47 }
46 if (!sway_assert(view->swayc == NULL, 48 if (!sway_assert(view->container == NULL,
47 "Tried to free view which still has a swayc " 49 "Tried to free view which still has a container "
48 "(might have a pending transaction?)")) { 50 "(might have a pending transaction?)")) {
49 return; 51 return;
50 } 52 }
@@ -57,6 +59,7 @@ void view_destroy(struct sway_view *view) {
57 wlr_texture_destroy(view->marks_focused_inactive); 59 wlr_texture_destroy(view->marks_focused_inactive);
58 wlr_texture_destroy(view->marks_unfocused); 60 wlr_texture_destroy(view->marks_unfocused);
59 wlr_texture_destroy(view->marks_urgent); 61 wlr_texture_destroy(view->marks_urgent);
62 free(view->title_format);
60 63
61 if (view->impl->destroy) { 64 if (view->impl->destroy) {
62 view->impl->destroy(view); 65 view->impl->destroy(view);
@@ -65,23 +68,13 @@ void view_destroy(struct sway_view *view) {
65 } 68 }
66} 69}
67 70
68/**
69 * The view may or may not be involved in a transaction. For example, a view may
70 * unmap then attempt to destroy itself before we've applied the new layout. If
71 * an unmapping view is still involved in a transaction then it'll still have a
72 * swayc.
73 *
74 * If there's no transaction we can simply free the view. Otherwise the
75 * destroying flag will make the view get freed when the transaction is
76 * finished.
77 */
78void view_begin_destroy(struct sway_view *view) { 71void view_begin_destroy(struct sway_view *view) {
79 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { 72 if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
80 return; 73 return;
81 } 74 }
82 view->destroying = true; 75 view->destroying = true;
83 76
84 if (!view->swayc) { 77 if (!view->container) {
85 view_destroy(view); 78 view_destroy(view);
86 } 79 }
87} 80}
@@ -171,30 +164,27 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
171} 164}
172 165
173void view_autoconfigure(struct sway_view *view) { 166void view_autoconfigure(struct sway_view *view) {
174 if (!sway_assert(view->swayc, 167 struct sway_output *output = view->container->workspace->output;
175 "Called view_autoconfigure() on a view without a swayc")) {
176 return;
177 }
178
179 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
180 168
181 if (view->swayc->is_fullscreen) { 169 if (view->container->is_fullscreen) {
182 view->x = output->x; 170 view->x = output->wlr_output->lx;
183 view->y = output->y; 171 view->y = output->wlr_output->ly;
184 view->width = output->width; 172 view->width = output->wlr_output->width;
185 view->height = output->height; 173 view->height = output->wlr_output->height;
186 return; 174 return;
187 } 175 }
188 176
189 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 177 struct sway_workspace *ws = view->container->workspace;
190 178
191 int other_views = 0; 179 bool other_views = false;
192 if (config->hide_edge_borders == E_SMART) { 180 if (config->hide_edge_borders == E_SMART) {
193 struct sway_container *con = view->swayc; 181 struct sway_container *con = view->container;
194 while (con != output) { 182 while (con) {
195 if (con->layout != L_TABBED && con->layout != L_STACKED) { 183 enum sway_container_layout layout = container_parent_layout(con);
196 other_views += con->children ? con->children->length - 1 : 0; 184 if (layout != L_TABBED && layout != L_STACKED) {
197 if (other_views > 0) { 185 list_t *siblings = container_get_siblings(con);
186 if (siblings && siblings->length > 1) {
187 other_views = true;
198 break; 188 break;
199 } 189 }
200 } 190 }
@@ -202,7 +192,7 @@ void view_autoconfigure(struct sway_view *view) {
202 } 192 }
203 } 193 }
204 194
205 struct sway_container *con = view->swayc; 195 struct sway_container *con = view->container;
206 196
207 view->border_top = view->border_bottom = true; 197 view->border_top = view->border_bottom = true;
208 view->border_left = view->border_right = true; 198 view->border_left = view->border_right = true;
@@ -228,7 +218,8 @@ void view_autoconfigure(struct sway_view *view) {
228 // In a tabbed or stacked container, the swayc's y is the bottom of the 218 // In a tabbed or stacked container, the swayc's y is the bottom of the
229 // title area. We have to disable any top border because the title bar is 219 // title area. We have to disable any top border because the title bar is
230 // rendered by the parent. 220 // rendered by the parent.
231 if (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED) { 221 enum sway_container_layout layout = container_parent_layout(con);
222 if (layout == L_TABBED || layout == L_STACKED) {
232 view->border_top = false; 223 view->border_top = false;
233 } else { 224 } else {
234 y_offset = container_titlebar_height(); 225 y_offset = container_titlebar_height();
@@ -281,13 +272,16 @@ void view_set_activated(struct sway_view *view, bool activated) {
281} 272}
282 273
283void view_request_activate(struct sway_view *view) { 274void view_request_activate(struct sway_view *view) {
284 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 275 struct sway_workspace *ws = view->container->workspace;
276 if (!ws) { // hidden scratchpad container
277 return;
278 }
285 struct sway_seat *seat = input_manager_current_seat(input_manager); 279 struct sway_seat *seat = input_manager_current_seat(input_manager);
286 280
287 switch (config->focus_on_window_activation) { 281 switch (config->focus_on_window_activation) {
288 case FOWA_SMART: 282 case FOWA_SMART:
289 if (workspace_is_visible(ws)) { 283 if (workspace_is_visible(ws)) {
290 seat_set_focus(seat, view->swayc); 284 seat_set_focus(seat, &view->container->node);
291 } else { 285 } else {
292 view_set_urgent(view, true); 286 view_set_urgent(view, true);
293 } 287 }
@@ -296,7 +290,7 @@ void view_request_activate(struct sway_view *view) {
296 view_set_urgent(view, true); 290 view_set_urgent(view, true);
297 break; 291 break;
298 case FOWA_FOCUS: 292 case FOWA_FOCUS:
299 seat_set_focus(seat, view->swayc); 293 seat_set_focus(seat, &view->container->node);
300 break; 294 break;
301 case FOWA_NONE: 295 case FOWA_NONE:
302 break; 296 break;
@@ -331,23 +325,12 @@ void view_close_popups(struct sway_view *view) {
331} 325}
332 326
333void view_damage_from(struct sway_view *view) { 327void view_damage_from(struct sway_view *view) {
334 for (int i = 0; i < root_container.children->length; ++i) { 328 for (int i = 0; i < root->outputs->length; ++i) {
335 struct sway_container *cont = root_container.children->items[i]; 329 struct sway_output *output = root->outputs->items[i];
336 if (cont->type == C_OUTPUT) { 330 output_damage_from_view(output, view);
337 output_damage_from_view(cont->sway_output, view);
338 }
339 } 331 }
340} 332}
341 333
342static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
343 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
344
345 box->x = output->x + view->swayc->x;
346 box->y = output->y + view->swayc->y;
347 box->width = view->width;
348 box->height = view->height;
349}
350
351void view_for_each_surface(struct sway_view *view, 334void view_for_each_surface(struct sway_view *view,
352 wlr_surface_iterator_func_t iterator, void *user_data) { 335 wlr_surface_iterator_func_t iterator, void *user_data) {
353 if (!view->surface) { 336 if (!view->surface) {
@@ -396,11 +379,8 @@ static bool view_has_executed_criteria(struct sway_view *view,
396} 379}
397 380
398void view_execute_criteria(struct sway_view *view) { 381void view_execute_criteria(struct sway_view *view) {
399 if (!view->swayc) {
400 return;
401 }
402 struct sway_seat *seat = input_manager_current_seat(input_manager); 382 struct sway_seat *seat = input_manager_current_seat(input_manager);
403 struct sway_container *prior_focus = seat_get_focus(seat); 383 struct sway_node *prior_focus = seat_get_focus(seat);
404 list_t *criterias = criteria_for_view(view, CT_COMMAND); 384 list_t *criterias = criteria_for_view(view, CT_COMMAND);
405 for (int i = 0; i < criterias->length; i++) { 385 for (int i = 0; i < criterias->length; i++) {
406 struct criteria *criteria = criterias->items[i]; 386 struct criteria *criteria = criterias->items[i];
@@ -411,7 +391,7 @@ void view_execute_criteria(struct sway_view *view) {
411 } 391 }
412 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", 392 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
413 criteria->raw, view, criteria->cmdlist); 393 criteria->raw, view, criteria->cmdlist);
414 seat_set_focus(seat, view->swayc); 394 seat_set_focus(seat, &view->container->node);
415 list_add(view->executed_criteria, criteria); 395 list_add(view->executed_criteria, criteria);
416 struct cmd_results *res = execute_command(criteria->cmdlist, NULL); 396 struct cmd_results *res = execute_command(criteria->cmdlist, NULL);
417 if (res->status != CMD_SUCCESS) { 397 if (res->status != CMD_SUCCESS) {
@@ -423,19 +403,19 @@ void view_execute_criteria(struct sway_view *view) {
423 seat_set_focus(seat, prior_focus); 403 seat_set_focus(seat, prior_focus);
424} 404}
425 405
426static struct sway_container *select_workspace(struct sway_view *view) { 406static struct sway_workspace *select_workspace(struct sway_view *view) {
427 struct sway_seat *seat = input_manager_current_seat(input_manager); 407 struct sway_seat *seat = input_manager_current_seat(input_manager);
428 408
429 // Check if there's any `assign` criteria for the view 409 // Check if there's any `assign` criteria for the view
430 list_t *criterias = criteria_for_view(view, 410 list_t *criterias = criteria_for_view(view,
431 CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT); 411 CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT);
432 struct sway_container *ws = NULL; 412 struct sway_workspace *ws = NULL;
433 for (int i = 0; i < criterias->length; ++i) { 413 for (int i = 0; i < criterias->length; ++i) {
434 struct criteria *criteria = criterias->items[i]; 414 struct criteria *criteria = criterias->items[i];
435 if (criteria->type == CT_ASSIGN_OUTPUT) { 415 if (criteria->type == CT_ASSIGN_OUTPUT) {
436 struct sway_container *output = output_by_name(criteria->target); 416 struct sway_output *output = output_by_name(criteria->target);
437 if (output) { 417 if (output) {
438 ws = seat_get_active_child(seat, output); 418 ws = output_get_active_workspace(output);
439 break; 419 break;
440 } 420 }
441 } else { 421 } else {
@@ -484,20 +464,14 @@ static struct sway_container *select_workspace(struct sway_view *view) {
484 } 464 }
485 465
486 // Use the focused workspace 466 // Use the focused workspace
487 ws = seat_get_focus_inactive(seat, &root_container); 467 return seat_get_focused_workspace(seat);
488 if (ws->type != C_WORKSPACE) {
489 ws = container_parent(ws, C_WORKSPACE);
490 }
491 return ws;
492} 468}
493 469
494static bool should_focus(struct sway_view *view) { 470static bool should_focus(struct sway_view *view) {
495 struct sway_seat *seat = input_manager_current_seat(input_manager); 471 struct sway_seat *seat = input_manager_current_seat(input_manager);
496 struct sway_container *prev_focus = 472 struct sway_container *prev_con = seat_get_focused_container(seat);
497 seat_get_focus_inactive(seat, &root_container); 473 struct sway_workspace *prev_ws = seat_get_focused_workspace(seat);
498 struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? 474 struct sway_workspace *map_ws = view->container->workspace;
499 prev_focus : container_parent(prev_focus, C_WORKSPACE);
500 struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE);
501 475
502 // Views can only take focus if they are mapped into the active workspace 476 // Views can only take focus if they are mapped into the active workspace
503 if (prev_ws != map_ws) { 477 if (prev_ws != map_ws) {
@@ -506,10 +480,9 @@ static bool should_focus(struct sway_view *view) {
506 480
507 // If the view is the only one in the focused workspace, it'll get focus 481 // If the view is the only one in the focused workspace, it'll get focus
508 // regardless of any no_focus criteria. 482 // regardless of any no_focus criteria.
509 struct sway_container *parent = view->swayc->parent; 483 if (!view->container->parent && !prev_con) {
510 if (parent->type == C_WORKSPACE && prev_focus == parent) { 484 size_t num_children = view->container->workspace->tiling->length +
511 size_t num_children = parent->children->length + 485 view->container->workspace->floating->length;
512 parent->sway_workspace->floating->length;
513 if (num_children == 1) { 486 if (num_children == 1) {
514 return true; 487 return true;
515 } 488 }
@@ -529,16 +502,24 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
529 view->surface = wlr_surface; 502 view->surface = wlr_surface;
530 503
531 struct sway_seat *seat = input_manager_current_seat(input_manager); 504 struct sway_seat *seat = input_manager_current_seat(input_manager);
532 struct sway_container *ws = select_workspace(view); 505 struct sway_workspace *ws = select_workspace(view);
533 struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); 506 struct sway_node *node = seat_get_focus_inactive(seat, &ws->node);
507 struct sway_container *target_sibling = node->type == N_CONTAINER ?
508 node->sway_container : NULL;
534 509
535 // If we're about to launch the view into the floating container, then 510 // If we're about to launch the view into the floating container, then
536 // launch it as a tiled view in the root of the workspace instead. 511 // launch it as a tiled view in the root of the workspace instead.
537 if (container_is_floating(target_sibling)) { 512 if (target_sibling && container_is_floating(target_sibling)) {
538 target_sibling = target_sibling->parent; 513 target_sibling = NULL;
539 } 514 }
540 515
541 view->swayc = container_view_create(target_sibling, view); 516 view->container = container_create(view);
517 if (target_sibling) {
518 container_add_sibling(target_sibling, view->container, 1);
519 } else {
520 workspace_add_tiling(ws, view->container);
521 }
522 ipc_event_window(view->container, "new");
542 523
543 view_init_subsurfaces(view, wlr_surface); 524 view_init_subsurfaces(view, wlr_surface);
544 wl_signal_add(&wlr_surface->events.new_subsurface, 525 wl_signal_add(&wlr_surface->events.new_subsurface,
@@ -548,7 +529,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
548 if (view->impl->wants_floating && view->impl->wants_floating(view)) { 529 if (view->impl->wants_floating && view->impl->wants_floating(view)) {
549 view->border = config->floating_border; 530 view->border = config->floating_border;
550 view->border_thickness = config->floating_border_thickness; 531 view->border_thickness = config->floating_border_thickness;
551 container_set_floating(view->swayc, true); 532 container_set_floating(view->container, true);
552 } else { 533 } else {
553 view->border = config->border; 534 view->border = config->border;
554 view->border_thickness = config->border_thickness; 535 view->border_thickness = config->border_thickness;
@@ -556,11 +537,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
556 } 537 }
557 538
558 if (should_focus(view)) { 539 if (should_focus(view)) {
559 input_manager_set_focus(input_manager, view->swayc); 540 input_manager_set_focus(input_manager, &view->container->node);
560 } 541 }
561 542
562 view_update_title(view, false); 543 view_update_title(view, false);
563 container_notify_subtree_changed(view->swayc->parent); 544 container_update_representation(view->container);
564 view_execute_criteria(view); 545 view_execute_criteria(view);
565} 546}
566 547
@@ -574,17 +555,17 @@ void view_unmap(struct sway_view *view) {
574 view->urgent_timer = NULL; 555 view->urgent_timer = NULL;
575 } 556 }
576 557
577 bool was_fullscreen = view->swayc->is_fullscreen; 558 struct sway_container *parent = view->container->parent;
578 struct sway_container *parent = view->swayc->parent; 559 struct sway_workspace *ws = view->container->workspace;
579 container_begin_destroy(view->swayc); 560 container_begin_destroy(view->container);
580 struct sway_container *surviving_ancestor = container_reap_empty(parent); 561 if (parent) {
562 container_reap_empty(parent);
563 } else {
564 workspace_consider_destroy(ws);
565 }
581 566
582 // If the workspace wasn't reaped 567 if (!ws->node.destroying) {
583 if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) { 568 arrange_workspace(ws);
584 struct sway_container *ws = surviving_ancestor->type == C_WORKSPACE ?
585 surviving_ancestor :
586 container_parent(surviving_ancestor, C_WORKSPACE);
587 arrange_windows(was_fullscreen ? ws : surviving_ancestor);
588 workspace_detect_urgent(ws); 569 workspace_detect_urgent(ws);
589 } 570 }
590 571
@@ -593,15 +574,15 @@ void view_unmap(struct sway_view *view) {
593} 574}
594 575
595void view_update_size(struct sway_view *view, int width, int height) { 576void view_update_size(struct sway_view *view, int width, int height) {
596 if (!sway_assert(container_is_floating(view->swayc), 577 if (!sway_assert(container_is_floating(view->container),
597 "Expected a floating container")) { 578 "Expected a floating container")) {
598 return; 579 return;
599 } 580 }
600 view->width = width; 581 view->width = width;
601 view->height = height; 582 view->height = height;
602 view->swayc->current.view_width = width; 583 view->container->current.view_width = width;
603 view->swayc->current.view_height = height; 584 view->container->current.view_height = height;
604 container_set_geometry_from_floating_view(view->swayc); 585 container_set_geometry_from_floating_view(view->container);
605} 586}
606 587
607static void view_subsurface_create(struct sway_view *view, 588static void view_subsurface_create(struct sway_view *view,
@@ -670,27 +651,18 @@ void view_child_init(struct sway_view_child *child,
670 wl_signal_add(&view->events.unmap, &child->view_unmap); 651 wl_signal_add(&view->events.unmap, &child->view_unmap);
671 child->view_unmap.notify = view_child_handle_view_unmap; 652 child->view_unmap.notify = view_child_handle_view_unmap;
672 653
673 struct sway_container *output = child->view->swayc->parent; 654 struct sway_output *output = child->view->container->workspace->output;
674 if (output != NULL) { 655 wlr_surface_send_enter(child->surface, output->wlr_output);
675 if (output->type != C_OUTPUT) {
676 output = container_parent(output, C_OUTPUT);
677 }
678 wlr_surface_send_enter(child->surface, output->sway_output->wlr_output);
679 }
680 656
681 view_init_subsurfaces(child->view, surface); 657 view_init_subsurfaces(child->view, surface);
682 658
683 // TODO: only damage the whole child 659 // TODO: only damage the whole child
684 if (child->view->swayc) { 660 container_damage_whole(child->view->container);
685 container_damage_whole(child->view->swayc);
686 }
687} 661}
688 662
689void view_child_destroy(struct sway_view_child *child) { 663void view_child_destroy(struct sway_view_child *child) {
690 // TODO: only damage the whole child 664 // TODO: only damage the whole child
691 if (child->view->swayc) { 665 container_damage_whole(child->view->container);
692 container_damage_whole(child->view->swayc);
693 }
694 666
695 wl_list_remove(&child->surface_commit.link); 667 wl_list_remove(&child->surface_commit.link);
696 wl_list_remove(&child->surface_destroy.link); 668 wl_list_remove(&child->surface_destroy.link);
@@ -808,22 +780,20 @@ static char *escape_title(char *buffer) {
808} 780}
809 781
810void view_update_title(struct sway_view *view, bool force) { 782void view_update_title(struct sway_view *view, bool force) {
811 if (!view->swayc) {
812 return;
813 }
814 const char *title = view_get_title(view); 783 const char *title = view_get_title(view);
815 784
816 if (!force) { 785 if (!force) {
817 if (title && view->swayc->name && strcmp(title, view->swayc->name) == 0) { 786 if (title && view->container->title &&
787 strcmp(title, view->container->title) == 0) {
818 return; 788 return;
819 } 789 }
820 if (!title && !view->swayc->name) { 790 if (!title && !view->container->title) {
821 return; 791 return;
822 } 792 }
823 } 793 }
824 794
825 free(view->swayc->name); 795 free(view->container->title);
826 free(view->swayc->formatted_title); 796 free(view->container->formatted_title);
827 if (title) { 797 if (title) {
828 size_t len = parse_title_format(view, NULL); 798 size_t len = parse_title_format(view, NULL);
829 char *buffer = calloc(len + 1, sizeof(char)); 799 char *buffer = calloc(len + 1, sizeof(char));
@@ -836,25 +806,25 @@ void view_update_title(struct sway_view *view, bool force) {
836 buffer = escape_title(buffer); 806 buffer = escape_title(buffer);
837 } 807 }
838 808
839 view->swayc->name = strdup(title); 809 view->container->title = strdup(title);
840 view->swayc->formatted_title = buffer; 810 view->container->formatted_title = buffer;
841 } else { 811 } else {
842 view->swayc->name = NULL; 812 view->container->title = NULL;
843 view->swayc->formatted_title = NULL; 813 view->container->formatted_title = NULL;
844 } 814 }
845 container_calculate_title_height(view->swayc); 815 container_calculate_title_height(view->container);
846 config_update_font_height(false); 816 config_update_font_height(false);
847 817
848 // Update title after the global font height is updated 818 // Update title after the global font height is updated
849 container_update_title_textures(view->swayc); 819 container_update_title_textures(view->container);
850 820
851 ipc_event_window(view->swayc, "title"); 821 ipc_event_window(view->container, "title");
852} 822}
853 823
854static bool find_by_mark_iterator(struct sway_container *con, 824static bool find_by_mark_iterator(struct sway_container *con,
855 void *data) { 825 void *data) {
856 char *mark = data; 826 char *mark = data;
857 return con->type == C_VIEW && view_has_mark(con->sway_view, mark); 827 return con->view && view_has_mark(con->view, mark);
858} 828}
859 829
860struct sway_view *view_find_mark(char *mark) { 830struct sway_view *view_find_mark(char *mark) {
@@ -863,7 +833,7 @@ struct sway_view *view_find_mark(char *mark) {
863 if (!container) { 833 if (!container) {
864 return NULL; 834 return NULL;
865 } 835 }
866 return container->sway_view; 836 return container->view;
867} 837}
868 838
869bool view_find_and_unmark(char *mark) { 839bool view_find_and_unmark(char *mark) {
@@ -872,7 +842,7 @@ bool view_find_and_unmark(char *mark) {
872 if (!container) { 842 if (!container) {
873 return false; 843 return false;
874 } 844 }
875 struct sway_view *view = container->sway_view; 845 struct sway_view *view = container->view;
876 846
877 for (int i = 0; i < view->marks->length; ++i) { 847 for (int i = 0; i < view->marks->length; ++i) {
878 char *view_mark = view->marks->items[i]; 848 char *view_mark = view->marks->items[i];
@@ -888,10 +858,9 @@ bool view_find_and_unmark(char *mark) {
888} 858}
889 859
890void view_clear_marks(struct sway_view *view) { 860void view_clear_marks(struct sway_view *view) {
891 while (view->marks->length) { 861 list_foreach(view->marks, free);
892 list_del(view->marks, 0); 862 view->marks->length = 0;
893 ipc_event_window(view->swayc, "mark"); 863 ipc_event_window(view->container, "mark");
894 }
895} 864}
896 865
897bool view_has_mark(struct sway_view *view, char *mark) { 866bool view_has_mark(struct sway_view *view, char *mark) {
@@ -906,12 +875,13 @@ bool view_has_mark(struct sway_view *view, char *mark) {
906 875
907void view_add_mark(struct sway_view *view, char *mark) { 876void view_add_mark(struct sway_view *view, char *mark) {
908 list_add(view->marks, strdup(mark)); 877 list_add(view->marks, strdup(mark));
909 ipc_event_window(view->swayc, "mark"); 878 ipc_event_window(view->container, "mark");
910} 879}
911 880
912static void update_marks_texture(struct sway_view *view, 881static void update_marks_texture(struct sway_view *view,
913 struct wlr_texture **texture, struct border_colors *class) { 882 struct wlr_texture **texture, struct border_colors *class) {
914 struct sway_output *output = container_get_effective_output(view->swayc); 883 struct sway_output *output =
884 container_get_effective_output(view->container);
915 if (!output) { 885 if (!output) {
916 return; 886 return;
917 } 887 }
@@ -949,7 +919,7 @@ static void update_marks_texture(struct sway_view *view,
949 919
950 double scale = output->wlr_output->scale; 920 double scale = output->wlr_output->scale;
951 int width = 0; 921 int width = 0;
952 int height = view->swayc->title_height * scale; 922 int height = view->container->title_height * scale;
953 923
954 cairo_t *c = cairo_create(NULL); 924 cairo_t *c = cairo_create(NULL);
955 get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); 925 get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer);
@@ -994,44 +964,40 @@ void view_update_marks_textures(struct sway_view *view) {
994 &config->border_colors.unfocused); 964 &config->border_colors.unfocused);
995 update_marks_texture(view, &view->marks_urgent, 965 update_marks_texture(view, &view->marks_urgent,
996 &config->border_colors.urgent); 966 &config->border_colors.urgent);
997 container_damage_whole(view->swayc); 967 container_damage_whole(view->container);
998} 968}
999 969
1000bool view_is_visible(struct sway_view *view) { 970bool view_is_visible(struct sway_view *view) {
1001 if (!view->swayc || view->swayc->destroying) { 971 if (view->container->node.destroying) {
1002 return false; 972 return false;
1003 } 973 }
1004 struct sway_container *workspace = 974 struct sway_workspace *workspace = view->container->workspace;
1005 container_parent(view->swayc, C_WORKSPACE);
1006 if (!workspace) { 975 if (!workspace) {
1007 return false; 976 return false;
1008 } 977 }
1009 // Determine if view is nested inside a floating container which is sticky. 978 // Determine if view is nested inside a floating container which is sticky
1010 // A simple floating view will have this ancestry: 979 struct sway_container *floater = view->container;
1011 // C_VIEW -> floating -> workspace 980 while (floater->parent) {
1012 // A more complex ancestry could be:
1013 // C_VIEW -> C_CONTAINER (tabbed) -> floating -> workspace
1014 struct sway_container *floater = view->swayc;
1015 while (floater->parent->type != C_WORKSPACE
1016 && floater->parent->parent->type != C_WORKSPACE) {
1017 floater = floater->parent; 981 floater = floater->parent;
1018 } 982 }
1019 bool is_sticky = container_is_floating(floater) && floater->is_sticky; 983 bool is_sticky = container_is_floating(floater) && floater->is_sticky;
1020 // Check view isn't in a tabbed or stacked container on an inactive tab 984 // Check view isn't in a tabbed or stacked container on an inactive tab
1021 struct sway_seat *seat = input_manager_current_seat(input_manager); 985 struct sway_seat *seat = input_manager_current_seat(input_manager);
1022 struct sway_container *container = view->swayc; 986 struct sway_container *container = view->container;
1023 while (container->type != C_WORKSPACE) { 987 while (container) {
1024 if (container->parent->layout == L_TABBED || 988 enum sway_container_layout layout = container_parent_layout(container);
1025 container->parent->layout == L_STACKED) { 989 if (layout == L_TABBED || layout == L_STACKED) {
1026 if (seat_get_active_child(seat, container->parent) != container) { 990 struct sway_node *parent = container->parent ?
991 &container->parent->node : &container->workspace->node;
992 if (seat_get_active_child(seat, parent) != &container->node) {
1027 return false; 993 return false;
1028 } 994 }
1029 } 995 }
1030 container = container->parent; 996 container = container->parent;
1031 } 997 }
1032 // Check view isn't hidden by another fullscreen view 998 // Check view isn't hidden by another fullscreen view
1033 if (workspace->sway_workspace->fullscreen && 999 if (workspace->fullscreen &&
1034 !container_is_fullscreen_or_child(view->swayc)) { 1000 !container_is_fullscreen_or_child(view->container)) {
1035 return false; 1001 return false;
1036 } 1002 }
1037 // Check the workspace is visible 1003 // Check the workspace is visible
@@ -1047,7 +1013,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1047 } 1013 }
1048 if (enable) { 1014 if (enable) {
1049 struct sway_seat *seat = input_manager_current_seat(input_manager); 1015 struct sway_seat *seat = input_manager_current_seat(input_manager);
1050 if (seat_get_focus(seat) == view->swayc) { 1016 if (seat_get_focused_container(seat) == view->container) {
1051 return; 1017 return;
1052 } 1018 }
1053 clock_gettime(CLOCK_MONOTONIC, &view->urgent); 1019 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
@@ -1058,12 +1024,11 @@ void view_set_urgent(struct sway_view *view, bool enable) {
1058 view->urgent_timer = NULL; 1024 view->urgent_timer = NULL;
1059 } 1025 }
1060 } 1026 }
1061 container_damage_whole(view->swayc); 1027 container_damage_whole(view->container);
1062 1028
1063 ipc_event_window(view->swayc, "urgent"); 1029 ipc_event_window(view->container, "urgent");
1064 1030
1065 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 1031 workspace_detect_urgent(view->container->workspace);
1066 workspace_detect_urgent(ws);
1067} 1032}
1068 1033
1069bool view_is_urgent(struct sway_view *view) { 1034bool view_is_urgent(struct sway_view *view) {
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 1957d94f..38ee478e 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -12,128 +12,105 @@
12#include "sway/output.h" 12#include "sway/output.h"
13#include "sway/tree/arrange.h" 13#include "sway/tree/arrange.h"
14#include "sway/tree/container.h" 14#include "sway/tree/container.h"
15#include "sway/tree/node.h"
15#include "sway/tree/view.h" 16#include "sway/tree/view.h"
16#include "sway/tree/workspace.h" 17#include "sway/tree/workspace.h"
17#include "list.h" 18#include "list.h"
18#include "log.h" 19#include "log.h"
19#include "util.h" 20#include "util.h"
20 21
21struct sway_container *workspace_get_initial_output(const char *name) { 22struct sway_output *workspace_get_initial_output(const char *name) {
22 struct sway_container *parent;
23 // Search for workspace<->output pair 23 // Search for workspace<->output pair
24 int e = config->workspace_outputs->length;
25 for (int i = 0; i < config->workspace_outputs->length; ++i) { 24 for (int i = 0; i < config->workspace_outputs->length; ++i) {
26 struct workspace_output *wso = config->workspace_outputs->items[i]; 25 struct workspace_output *wso = config->workspace_outputs->items[i];
27 if (strcasecmp(wso->workspace, name) == 0) { 26 if (strcasecmp(wso->workspace, name) == 0) {
28 // Find output to use if it exists 27 // Find output to use if it exists
29 e = root_container.children->length; 28 struct sway_output *output = output_by_name(wso->output);
30 for (i = 0; i < e; ++i) { 29 if (output) {
31 parent = root_container.children->items[i]; 30 return output;
32 if (strcmp(parent->name, wso->output) == 0) {
33 return parent;
34 }
35 } 31 }
36 break; 32 break;
37 } 33 }
38 } 34 }
39 // Otherwise put it on the focused output 35 // Otherwise put it on the focused output
40 struct sway_seat *seat = input_manager_current_seat(input_manager); 36 struct sway_seat *seat = input_manager_current_seat(input_manager);
41 struct sway_container *focus = 37 struct sway_workspace *focus = seat_get_focused_workspace(seat);
42 seat_get_focus_inactive(seat, &root_container); 38 return focus->output;
43 parent = focus;
44 parent = container_parent(parent, C_OUTPUT);
45 return parent;
46} 39}
47 40
48struct sway_container *workspace_create(struct sway_container *output, 41struct sway_workspace *workspace_create(struct sway_output *output,
49 const char *name) { 42 const char *name) {
50 if (output == NULL) { 43 if (output == NULL) {
51 output = workspace_get_initial_output(name); 44 output = workspace_get_initial_output(name);
52 } 45 }
53 46
54 wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); 47 wlr_log(WLR_DEBUG, "Adding workspace %s for output %s", name,
55 struct sway_container *workspace = container_create(C_WORKSPACE); 48 output->wlr_output->name);
56
57 workspace->x = output->x;
58 workspace->y = output->y;
59 workspace->width = output->width;
60 workspace->height = output->height;
61 workspace->name = !name ? NULL : strdup(name);
62 workspace->prev_split_layout = L_NONE;
63 workspace->layout = container_get_default_layout(output);
64 49
65 struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); 50 struct sway_workspace *ws = calloc(1, sizeof(struct sway_workspace));
66 if (!swayws) { 51 if (!ws) {
52 wlr_log(WLR_ERROR, "Unable to allocate sway_workspace");
67 return NULL; 53 return NULL;
68 } 54 }
69 swayws->swayc = workspace; 55 node_init(&ws->node, N_WORKSPACE, ws);
70 swayws->floating = create_list(); 56 ws->x = output->wlr_output->lx;
71 swayws->output_priority = create_list(); 57 ws->y = output->wlr_output->ly;
72 workspace->sway_workspace = swayws; 58 ws->width = output->wlr_output->width;
73 workspace_output_add_priority(workspace, output); 59 ws->height = output->wlr_output->height;
74 60 ws->name = name ? strdup(name) : NULL;
75 container_add_child(output, workspace); 61 ws->prev_split_layout = L_NONE;
62 ws->layout = output_get_default_layout(output);
63 ws->floating = create_list();
64 ws->tiling = create_list();
65 ws->output_priority = create_list();
66 workspace_output_add_priority(ws, output);
67
68 output_add_workspace(output, ws);
76 output_sort_workspaces(output); 69 output_sort_workspaces(output);
77 container_create_notify(workspace);
78 70
79 return workspace; 71 ipc_event_workspace(NULL, ws, "init");
72 wl_signal_emit(&root->events.new_node, &ws->node);
73
74 return ws;
80} 75}
81 76
82void workspace_destroy(struct sway_container *workspace) { 77void workspace_destroy(struct sway_workspace *workspace) {
83 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 78 if (!sway_assert(workspace->node.destroying,
84 return;
85 }
86 if (!sway_assert(workspace->destroying,
87 "Tried to free workspace which wasn't marked as destroying")) { 79 "Tried to free workspace which wasn't marked as destroying")) {
88 return; 80 return;
89 } 81 }
90 if (!sway_assert(workspace->ntxnrefs == 0, "Tried to free workspace " 82 if (!sway_assert(workspace->node.ntxnrefs == 0, "Tried to free workspace "
91 "which is still referenced by transactions")) { 83 "which is still referenced by transactions")) {
92 return; 84 return;
93 } 85 }
94 // sway_workspace
95 struct sway_workspace *ws = workspace->sway_workspace;
96 list_foreach(ws->output_priority, free);
97 list_free(ws->output_priority);
98 list_free(ws->floating);
99 free(ws);
100 86
101 // swayc
102 free(workspace->name); 87 free(workspace->name);
103 free(workspace->formatted_title); 88 free(workspace->representation);
104 wlr_texture_destroy(workspace->title_focused); 89 list_foreach(workspace->output_priority, free);
105 wlr_texture_destroy(workspace->title_focused_inactive); 90 list_free(workspace->output_priority);
106 wlr_texture_destroy(workspace->title_unfocused); 91 list_free(workspace->floating);
107 wlr_texture_destroy(workspace->title_urgent); 92 list_free(workspace->tiling);
108 list_free(workspace->children); 93 list_free(workspace->current.floating);
109 list_free(workspace->current.children); 94 list_free(workspace->current.tiling);
110 list_free(workspace->outputs);
111 free(workspace); 95 free(workspace);
112} 96}
113 97
114void workspace_begin_destroy(struct sway_container *workspace) { 98void workspace_begin_destroy(struct sway_workspace *workspace) {
115 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
116 return;
117 }
118 wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name); 99 wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name);
119 wl_signal_emit(&workspace->events.destroy, workspace);
120 ipc_event_workspace(NULL, workspace, "empty"); // intentional 100 ipc_event_workspace(NULL, workspace, "empty"); // intentional
101 wl_signal_emit(&workspace->node.events.destroy, &workspace->node);
121 102
122 workspace->destroying = true; 103 if (workspace->output) {
123 container_set_dirty(workspace); 104 workspace_detach(workspace);
124
125 if (workspace->parent) {
126 container_remove_child(workspace);
127 } 105 }
106
107 workspace->node.destroying = true;
108 node_set_dirty(&workspace->node);
128} 109}
129 110
130void workspace_consider_destroy(struct sway_container *ws) { 111void workspace_consider_destroy(struct sway_workspace *ws) {
131 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { 112 if (ws->tiling->length == 0 && ws->floating->length == 0
132 return; 113 && output_get_active_workspace(ws->output) != ws) {
133 }
134 struct sway_seat *seat = input_manager_current_seat(input_manager);
135 if (ws->children->length == 0 && ws->sway_workspace->floating->length == 0
136 && seat_get_active_child(seat, ws->parent) != ws) {
137 workspace_begin_destroy(ws); 114 workspace_begin_destroy(ws);
138 } 115 }
139} 116}
@@ -272,59 +249,49 @@ char *workspace_next_name(const char *output_name) {
272 } 249 }
273 // As a fall back, get the current number of active workspaces 250 // As a fall back, get the current number of active workspaces
274 // and return that + 1 for the next workspace's name 251 // and return that + 1 for the next workspace's name
275 int ws_num = root_container.children->length; 252 int ws_num = root->outputs->length;
276 int l = snprintf(NULL, 0, "%d", ws_num); 253 int l = snprintf(NULL, 0, "%d", ws_num);
277 char *name = malloc(l + 1); 254 char *name = malloc(l + 1);
278 if (!sway_assert(name, "Cloud not allocate workspace name")) { 255 if (!sway_assert(name, "Could not allocate workspace name")) {
279 return NULL; 256 return NULL;
280 } 257 }
281 sprintf(name, "%d", ws_num++); 258 sprintf(name, "%d", ws_num++);
282 return name; 259 return name;
283} 260}
284 261
285static bool _workspace_by_number(struct sway_container *view, void *data) { 262static bool _workspace_by_number(struct sway_workspace *ws, void *data) {
286 if (view->type != C_WORKSPACE) {
287 return false;
288 }
289 char *name = data; 263 char *name = data;
290 char *view_name = view->name; 264 char *ws_name = ws->name;
291 while (isdigit(*name)) { 265 while (isdigit(*name)) {
292 if (*name++ != *view_name++) { 266 if (*name++ != *ws_name++) {
293 return false; 267 return false;
294 } 268 }
295 } 269 }
296 return !isdigit(*view_name); 270 return !isdigit(*ws_name);
297} 271}
298 272
299struct sway_container *workspace_by_number(const char* name) { 273struct sway_workspace *workspace_by_number(const char* name) {
300 return root_find_workspace(_workspace_by_number, (void *) name); 274 return root_find_workspace(_workspace_by_number, (void *) name);
301} 275}
302 276
303static bool _workspace_by_name(struct sway_container *view, void *data) { 277static bool _workspace_by_name(struct sway_workspace *ws, void *data) {
304 return (view->type == C_WORKSPACE) && 278 return strcasecmp(ws->name, data) == 0;
305 (strcasecmp(view->name, (char *) data) == 0);
306} 279}
307 280
308struct sway_container *workspace_by_name(const char *name) { 281struct sway_workspace *workspace_by_name(const char *name) {
309 struct sway_seat *seat = input_manager_current_seat(input_manager); 282 struct sway_seat *seat = input_manager_current_seat(input_manager);
310 struct sway_container *current_workspace = NULL, *current_output = NULL; 283 struct sway_workspace *current = seat_get_focused_workspace(seat);
311 struct sway_container *focus = seat_get_focus(seat);
312 if (focus) {
313 current_workspace = focus->type == C_WORKSPACE ?
314 focus : container_parent(focus, C_WORKSPACE);
315 current_output = container_parent(focus, C_OUTPUT);
316 }
317 284
318 if (strcmp(name, "prev") == 0) { 285 if (strcmp(name, "prev") == 0) {
319 return workspace_prev(current_workspace); 286 return workspace_prev(current);
320 } else if (strcmp(name, "prev_on_output") == 0) { 287 } else if (strcmp(name, "prev_on_output") == 0) {
321 return workspace_output_prev(current_output); 288 return workspace_output_prev(current);
322 } else if (strcmp(name, "next") == 0) { 289 } else if (strcmp(name, "next") == 0) {
323 return workspace_next(current_workspace); 290 return workspace_next(current);
324 } else if (strcmp(name, "next_on_output") == 0) { 291 } else if (strcmp(name, "next_on_output") == 0) {
325 return workspace_output_next(current_output); 292 return workspace_output_next(current);
326 } else if (strcmp(name, "current") == 0) { 293 } else if (strcmp(name, "current") == 0) {
327 return current_workspace; 294 return current;
328 } else if (strcasecmp(name, "back_and_forth") == 0) { 295 } else if (strcasecmp(name, "back_and_forth") == 0) {
329 return prev_workspace_name ? 296 return prev_workspace_name ?
330 root_find_workspace(_workspace_by_name, (void*)prev_workspace_name) 297 root_find_workspace(_workspace_by_name, (void*)prev_workspace_name)
@@ -339,97 +306,68 @@ struct sway_container *workspace_by_name(const char *name) {
339 * the end and beginning. If next is false, the previous workspace is returned, 306 * the end and beginning. If next is false, the previous workspace is returned,
340 * otherwise the next one is returned. 307 * otherwise the next one is returned.
341 */ 308 */
342static struct sway_container *workspace_output_prev_next_impl( 309static struct sway_workspace *workspace_output_prev_next_impl(
343 struct sway_container *output, int dir) { 310 struct sway_output *output, int dir) {
344 if (!output) {
345 return NULL;
346 }
347 if (!sway_assert(output->type == C_OUTPUT,
348 "Argument must be an output, is %d", output->type)) {
349 return NULL;
350 }
351
352 struct sway_seat *seat = input_manager_current_seat(input_manager); 311 struct sway_seat *seat = input_manager_current_seat(input_manager);
353 struct sway_container *focus = seat_get_focus_inactive(seat, output); 312 struct sway_workspace *workspace = seat_get_focused_workspace(seat);
354 struct sway_container *workspace = (focus->type == C_WORKSPACE ?
355 focus :
356 container_parent(focus, C_WORKSPACE));
357 313
358 int index = list_find(output->children, workspace); 314 int index = list_find(output->workspaces, workspace);
359 size_t new_index = wrap(index + dir, output->children->length); 315 size_t new_index = wrap(index + dir, output->workspaces->length);
360 return output->children->items[new_index]; 316 return output->workspaces->items[new_index];
361} 317}
362 318
363/** 319/**
364 * Get the previous or next workspace. If the first/last workspace on an output 320 * Get the previous or next workspace. If the first/last workspace on an output
365 * is active, proceed to the previous/next output's previous/next workspace. 321 * is active, proceed to the previous/next output's previous/next workspace.
366 */ 322 */
367static struct sway_container *workspace_prev_next_impl( 323static struct sway_workspace *workspace_prev_next_impl(
368 struct sway_container *workspace, int dir) { 324 struct sway_workspace *workspace, int dir) {
369 if (!workspace) { 325 struct sway_output *output = workspace->output;
370 return NULL; 326 int index = list_find(output->workspaces, workspace);
371 }
372 if (!sway_assert(workspace->type == C_WORKSPACE,
373 "Argument must be a workspace, is %d", workspace->type)) {
374 return NULL;
375 }
376
377 struct sway_container *output = workspace->parent;
378 int index = list_find(output->children, workspace);
379 int new_index = index + dir; 327 int new_index = index + dir;
380 328
381 if (new_index >= 0 && new_index < output->children->length) { 329 if (new_index >= 0 && new_index < output->workspaces->length) {
382 return output->children->items[index + dir]; 330 return output->workspaces->items[new_index];
383 } 331 }
384 332
385 // Look on a different output 333 // Look on a different output
386 int output_index = list_find(root_container.children, output); 334 int output_index = list_find(root->outputs, output);
387 new_index = wrap(output_index + dir, root_container.children->length); 335 new_index = wrap(output_index + dir, root->outputs->length);
388 output = root_container.children->items[new_index]; 336 output = root->outputs->items[new_index];
389 337
390 if (dir == 1) { 338 if (dir == 1) {
391 return output->children->items[0]; 339 return output->workspaces->items[0];
392 } else { 340 } else {
393 return output->children->items[output->children->length - 1]; 341 return output->workspaces->items[output->workspaces->length - 1];
394 } 342 }
395} 343}
396 344
397struct sway_container *workspace_output_next(struct sway_container *current) { 345struct sway_workspace *workspace_output_next(struct sway_workspace *current) {
398 return workspace_output_prev_next_impl(current, 1); 346 return workspace_output_prev_next_impl(current->output, 1);
399} 347}
400 348
401struct sway_container *workspace_next(struct sway_container *current) { 349struct sway_workspace *workspace_next(struct sway_workspace *current) {
402 return workspace_prev_next_impl(current, 1); 350 return workspace_prev_next_impl(current, 1);
403} 351}
404 352
405struct sway_container *workspace_output_prev(struct sway_container *current) { 353struct sway_workspace *workspace_output_prev(struct sway_workspace *current) {
406 return workspace_output_prev_next_impl(current, -1); 354 return workspace_output_prev_next_impl(current->output, -1);
407} 355}
408 356
409struct sway_container *workspace_prev(struct sway_container *current) { 357struct sway_workspace *workspace_prev(struct sway_workspace *current) {
410 return workspace_prev_next_impl(current, -1); 358 return workspace_prev_next_impl(current, -1);
411} 359}
412 360
413bool workspace_switch(struct sway_container *workspace, 361bool workspace_switch(struct sway_workspace *workspace,
414 bool no_auto_back_and_forth) { 362 bool no_auto_back_and_forth) {
415 if (!workspace) {
416 return false;
417 }
418 struct sway_seat *seat = input_manager_current_seat(input_manager); 363 struct sway_seat *seat = input_manager_current_seat(input_manager);
419 struct sway_container *focus = 364 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node);
420 seat_get_focus_inactive(seat, &root_container); 365 struct sway_workspace *active_ws = seat_get_focused_workspace(seat);
421 if (!seat || !focus) {
422 return false;
423 }
424 struct sway_container *active_ws = focus;
425 if (active_ws->type != C_WORKSPACE) {
426 active_ws = container_parent(focus, C_WORKSPACE);
427 }
428 366
429 if (!no_auto_back_and_forth && config->auto_back_and_forth 367 if (!no_auto_back_and_forth && config->auto_back_and_forth
430 && active_ws == workspace 368 && active_ws == workspace
431 && prev_workspace_name) { 369 && prev_workspace_name) {
432 struct sway_container *new_ws = workspace_by_name(prev_workspace_name); 370 struct sway_workspace *new_ws = workspace_by_name(prev_workspace_name);
433 workspace = new_ws ? 371 workspace = new_ws ?
434 new_ws : 372 new_ws :
435 workspace_create(NULL, prev_workspace_name); 373 workspace_create(NULL, prev_workspace_name);
@@ -447,21 +385,21 @@ bool workspace_switch(struct sway_container *workspace,
447 } 385 }
448 386
449 // Move sticky containers to new workspace 387 // Move sticky containers to new workspace
450 struct sway_container *next_output = workspace->parent; 388 struct sway_output *next_output = workspace->output;
451 struct sway_container *next_output_prev_ws = 389 struct sway_workspace *next_output_prev_ws =
452 seat_get_active_child(seat, next_output); 390 output_get_active_workspace(next_output);
453 list_t *floating = next_output_prev_ws->sway_workspace->floating;
454 bool has_sticky = false; 391 bool has_sticky = false;
455 if (workspace != next_output_prev_ws) { 392 if (workspace != next_output_prev_ws) {
456 for (int i = 0; i < floating->length; ++i) { 393 for (int i = 0; i < next_output_prev_ws->floating->length; ++i) {
457 struct sway_container *floater = floating->items[i]; 394 struct sway_container *floater =
395 next_output_prev_ws->floating->items[i];
458 if (floater->is_sticky) { 396 if (floater->is_sticky) {
459 has_sticky = true; 397 has_sticky = true;
460 container_remove_child(floater); 398 container_detach(floater);
461 workspace_add_floating(workspace, floater); 399 workspace_add_floating(workspace, floater);
462 if (floater == focus) { 400 if (&floater->node == focus) {
463 seat_set_focus(seat, NULL); 401 seat_set_focus(seat, NULL);
464 seat_set_focus(seat, floater); 402 seat_set_focus(seat, &floater->node);
465 } 403 }
466 --i; 404 --i;
467 } 405 }
@@ -470,9 +408,9 @@ bool workspace_switch(struct sway_container *workspace,
470 408
471 wlr_log(WLR_DEBUG, "Switching to workspace %p:%s", 409 wlr_log(WLR_DEBUG, "Switching to workspace %p:%s",
472 workspace, workspace->name); 410 workspace, workspace->name);
473 struct sway_container *next = seat_get_focus_inactive(seat, workspace); 411 struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node);
474 if (next == NULL) { 412 if (next == NULL) {
475 next = workspace; 413 next = &workspace->node;
476 } 414 }
477 if (has_sticky) { 415 if (has_sticky) {
478 // If there's a sticky container, we might be setting focus to the same 416 // If there's a sticky container, we might be setting focus to the same
@@ -483,35 +421,24 @@ bool workspace_switch(struct sway_container *workspace,
483 workspace_consider_destroy(active_ws); 421 workspace_consider_destroy(active_ws);
484 } 422 }
485 seat_set_focus(seat, next); 423 seat_set_focus(seat, next);
486 struct sway_container *output = container_parent(workspace, C_OUTPUT); 424 arrange_workspace(workspace);
487 arrange_windows(output);
488 return true; 425 return true;
489} 426}
490 427
491bool workspace_is_visible(struct sway_container *ws) { 428bool workspace_is_visible(struct sway_workspace *ws) {
492 if (ws->destroying) { 429 if (ws->node.destroying) {
493 return false; 430 return false;
494 } 431 }
495 struct sway_container *output = container_parent(ws, C_OUTPUT); 432 return output_get_active_workspace(ws->output) == ws;
496 struct sway_seat *seat = input_manager_current_seat(input_manager);
497 struct sway_container *focus = seat_get_focus_inactive(seat, output);
498 if (focus->type != C_WORKSPACE) {
499 focus = container_parent(focus, C_WORKSPACE);
500 }
501 return focus == ws;
502} 433}
503 434
504bool workspace_is_empty(struct sway_container *ws) { 435bool workspace_is_empty(struct sway_workspace *ws) {
505 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { 436 if (ws->tiling->length) {
506 return false;
507 }
508 if (ws->children->length) {
509 return false; 437 return false;
510 } 438 }
511 // Sticky views are not considered to be part of this workspace 439 // Sticky views are not considered to be part of this workspace
512 list_t *floating = ws->sway_workspace->floating; 440 for (int i = 0; i < ws->floating->length; ++i) {
513 for (int i = 0; i < floating->length; ++i) { 441 struct sway_container *floater = ws->floating->items[i];
514 struct sway_container *floater = floating->items[i];
515 if (!floater->is_sticky) { 442 if (!floater->is_sticky) {
516 return false; 443 return false;
517 } 444 }
@@ -523,20 +450,19 @@ static int find_output(const void *id1, const void *id2) {
523 return strcmp(id1, id2) ? 0 : 1; 450 return strcmp(id1, id2) ? 0 : 1;
524} 451}
525 452
526void workspace_output_raise_priority(struct sway_container *workspace, 453void workspace_output_raise_priority(struct sway_workspace *ws,
527 struct sway_container *old_output, struct sway_container *output) { 454 struct sway_output *old_output, struct sway_output *output) {
528 struct sway_workspace *ws = workspace->sway_workspace;
529
530 int old_index = list_seq_find(ws->output_priority, find_output, 455 int old_index = list_seq_find(ws->output_priority, find_output,
531 old_output->name); 456 old_output->wlr_output->name);
532 if (old_index < 0) { 457 if (old_index < 0) {
533 return; 458 return;
534 } 459 }
535 460
536 int new_index = list_seq_find(ws->output_priority, find_output, 461 int new_index = list_seq_find(ws->output_priority, find_output,
537 output->name); 462 output->wlr_output->name);
538 if (new_index < 0) { 463 if (new_index < 0) {
539 list_insert(ws->output_priority, old_index, strdup(output->name)); 464 list_insert(ws->output_priority, old_index,
465 strdup(output->wlr_output->name));
540 } else if (new_index > old_index) { 466 } else if (new_index > old_index) {
541 char *name = ws->output_priority->items[new_index]; 467 char *name = ws->output_priority->items[new_index];
542 list_del(ws->output_priority, new_index); 468 list_del(ws->output_priority, new_index);
@@ -544,29 +470,24 @@ void workspace_output_raise_priority(struct sway_container *workspace,
544 } 470 }
545} 471}
546 472
547void workspace_output_add_priority(struct sway_container *workspace, 473void workspace_output_add_priority(struct sway_workspace *workspace,
548 struct sway_container *output) { 474 struct sway_output *output) {
549 int index = list_seq_find(workspace->sway_workspace->output_priority, 475 int index = list_seq_find(workspace->output_priority,
550 find_output, output->name); 476 find_output, output->wlr_output->name);
551 if (index < 0) { 477 if (index < 0) {
552 list_add(workspace->sway_workspace->output_priority, 478 list_add(workspace->output_priority, strdup(output->wlr_output->name));
553 strdup(output->name));
554 } 479 }
555} 480}
556 481
557static bool _output_by_name(struct sway_container *output, void *data) { 482struct sway_output *workspace_output_get_highest_available(
558 return output->type == C_OUTPUT && strcasecmp(output->name, data) == 0; 483 struct sway_workspace *ws, struct sway_output *exclude) {
559} 484 for (int i = 0; i < ws->output_priority->length; i++) {
560 485 char *name = ws->output_priority->items[i];
561struct sway_container *workspace_output_get_highest_available( 486 if (exclude && strcasecmp(name, exclude->wlr_output->name) == 0) {
562 struct sway_container *ws, struct sway_container *exclude) {
563 for (int i = 0; i < ws->sway_workspace->output_priority->length; i++) {
564 char *name = ws->sway_workspace->output_priority->items[i];
565 if (exclude && strcasecmp(name, exclude->name) == 0) {
566 continue; 487 continue;
567 } 488 }
568 489
569 struct sway_container *output = root_find_output(_output_by_name, name); 490 struct sway_output *output = output_by_name(name);
570 if (output) { 491 if (output) {
571 return output; 492 return output;
572 } 493 }
@@ -576,49 +497,42 @@ struct sway_container *workspace_output_get_highest_available(
576} 497}
577 498
578static bool find_urgent_iterator(struct sway_container *con, void *data) { 499static bool find_urgent_iterator(struct sway_container *con, void *data) {
579 return con->type == C_VIEW && view_is_urgent(con->sway_view); 500 return con->view && view_is_urgent(con->view);
580} 501}
581 502
582void workspace_detect_urgent(struct sway_container *workspace) { 503void workspace_detect_urgent(struct sway_workspace *workspace) {
583 bool new_urgent = (bool)workspace_find_container(workspace, 504 bool new_urgent = (bool)workspace_find_container(workspace,
584 find_urgent_iterator, NULL); 505 find_urgent_iterator, NULL);
585 506
586 if (workspace->sway_workspace->urgent != new_urgent) { 507 if (workspace->urgent != new_urgent) {
587 workspace->sway_workspace->urgent = new_urgent; 508 workspace->urgent = new_urgent;
588 ipc_event_workspace(NULL, workspace, "urgent"); 509 ipc_event_workspace(NULL, workspace, "urgent");
589 container_damage_whole(workspace); 510 output_damage_whole(workspace->output);
590 } 511 }
591} 512}
592 513
593void workspace_for_each_container(struct sway_container *ws, 514void workspace_for_each_container(struct sway_workspace *ws,
594 void (*f)(struct sway_container *con, void *data), void *data) { 515 void (*f)(struct sway_container *con, void *data), void *data) {
595 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
596 return;
597 }
598 // Tiling 516 // Tiling
599 for (int i = 0; i < ws->children->length; ++i) { 517 for (int i = 0; i < ws->tiling->length; ++i) {
600 struct sway_container *container = ws->children->items[i]; 518 struct sway_container *container = ws->tiling->items[i];
601 f(container, data); 519 f(container, data);
602 container_for_each_child(container, f, data); 520 container_for_each_child(container, f, data);
603 } 521 }
604 // Floating 522 // Floating
605 for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { 523 for (int i = 0; i < ws->floating->length; ++i) {
606 struct sway_container *container = 524 struct sway_container *container = ws->floating->items[i];
607 ws->sway_workspace->floating->items[i];
608 f(container, data); 525 f(container, data);
609 container_for_each_child(container, f, data); 526 container_for_each_child(container, f, data);
610 } 527 }
611} 528}
612 529
613struct sway_container *workspace_find_container(struct sway_container *ws, 530struct sway_container *workspace_find_container(struct sway_workspace *ws,
614 bool (*test)(struct sway_container *con, void *data), void *data) { 531 bool (*test)(struct sway_container *con, void *data), void *data) {
615 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
616 return NULL;
617 }
618 struct sway_container *result = NULL; 532 struct sway_container *result = NULL;
619 // Tiling 533 // Tiling
620 for (int i = 0; i < ws->children->length; ++i) { 534 for (int i = 0; i < ws->tiling->length; ++i) {
621 struct sway_container *child = ws->children->items[i]; 535 struct sway_container *child = ws->tiling->items[i];
622 if (test(child, data)) { 536 if (test(child, data)) {
623 return child; 537 return child;
624 } 538 }
@@ -627,8 +541,8 @@ struct sway_container *workspace_find_container(struct sway_container *ws,
627 } 541 }
628 } 542 }
629 // Floating 543 // Floating
630 for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { 544 for (int i = 0; i < ws->floating->length; ++i) {
631 struct sway_container *child = ws->sway_workspace->floating->items[i]; 545 struct sway_container *child = ws->floating->items[i];
632 if (test(child, data)) { 546 if (test(child, data)) {
633 return child; 547 return child;
634 } 548 }
@@ -639,37 +553,76 @@ struct sway_container *workspace_find_container(struct sway_container *ws,
639 return NULL; 553 return NULL;
640} 554}
641 555
642struct sway_container *workspace_wrap_children(struct sway_container *ws) { 556struct sway_container *workspace_wrap_children(struct sway_workspace *ws) {
643 struct sway_container *middle = container_create(C_CONTAINER); 557 struct sway_container *middle = container_create(NULL);
644 middle->layout = ws->layout; 558 middle->layout = ws->layout;
645 while (ws->children->length) { 559 while (ws->tiling->length) {
646 struct sway_container *child = ws->children->items[0]; 560 struct sway_container *child = ws->tiling->items[0];
647 container_remove_child(child); 561 container_detach(child);
648 container_add_child(middle, child); 562 container_add_child(middle, child);
649 } 563 }
650 container_add_child(ws, middle); 564 workspace_add_tiling(ws, middle);
651 return middle; 565 return middle;
652} 566}
653 567
654void workspace_add_floating(struct sway_container *workspace, 568void workspace_detach(struct sway_workspace *workspace) {
655 struct sway_container *con) { 569 struct sway_output *output = workspace->output;
656 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { 570 int index = list_find(output->workspaces, workspace);
657 return; 571 if (index != -1) {
572 list_del(output->workspaces, index);
658 } 573 }
659 if (!sway_assert(con->parent == NULL, "Expected an orphan container")) { 574 workspace->output = NULL;
660 return; 575
576 node_set_dirty(&workspace->node);
577 node_set_dirty(&output->node);
578}
579
580static void set_workspace(struct sway_container *container, void *data) {
581 container->workspace = container->parent->workspace;
582}
583
584void workspace_add_tiling(struct sway_workspace *workspace,
585 struct sway_container *con) {
586 if (con->workspace) {
587 container_detach(con);
661 } 588 }
589 list_add(workspace->tiling, con);
590 con->workspace = workspace;
591 container_for_each_child(con, set_workspace, NULL);
592 container_handle_fullscreen_reparent(con);
593 workspace_update_representation(workspace);
594 node_set_dirty(&workspace->node);
595 node_set_dirty(&con->node);
596}
662 597
663 list_add(workspace->sway_workspace->floating, con); 598void workspace_add_floating(struct sway_workspace *workspace,
664 con->parent = workspace; 599 struct sway_container *con) {
665 container_set_dirty(workspace); 600 if (con->workspace) {
666 container_set_dirty(con); 601 container_detach(con);
602 }
603 list_add(workspace->floating, con);
604 con->workspace = workspace;
605 container_for_each_child(con, set_workspace, NULL);
606 container_handle_fullscreen_reparent(con);
607 node_set_dirty(&workspace->node);
608 node_set_dirty(&con->node);
667} 609}
668 610
669void workspace_remove_gaps(struct sway_container *ws) { 611void workspace_insert_tiling(struct sway_workspace *workspace,
670 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { 612 struct sway_container *con, int index) {
671 return; 613 if (con->workspace) {
614 container_detach(con);
672 } 615 }
616 list_insert(workspace->tiling, index, con);
617 con->workspace = workspace;
618 container_for_each_child(con, set_workspace, NULL);
619 container_handle_fullscreen_reparent(con);
620 workspace_update_representation(workspace);
621 node_set_dirty(&workspace->node);
622 node_set_dirty(&con->node);
623}
624
625void workspace_remove_gaps(struct sway_workspace *ws) {
673 if (ws->current_gaps == 0) { 626 if (ws->current_gaps == 0) {
674 return; 627 return;
675 } 628 }
@@ -681,15 +634,12 @@ void workspace_remove_gaps(struct sway_container *ws) {
681 ws->current_gaps = 0; 634 ws->current_gaps = 0;
682} 635}
683 636
684void workspace_add_gaps(struct sway_container *ws) { 637void workspace_add_gaps(struct sway_workspace *ws) {
685 if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
686 return;
687 }
688 if (ws->current_gaps > 0) { 638 if (ws->current_gaps > 0) {
689 return; 639 return;
690 } 640 }
691 bool should_apply = 641 bool should_apply =
692 config->edge_gaps || (config->smart_gaps && ws->children->length > 1); 642 config->edge_gaps || (config->smart_gaps && ws->tiling->length > 1);
693 if (!should_apply) { 643 if (!should_apply) {
694 return; 644 return;
695 } 645 }
@@ -708,3 +658,36 @@ void workspace_add_gaps(struct sway_container *ws) {
708 ws->width -= 2 * ws->current_gaps; 658 ws->width -= 2 * ws->current_gaps;
709 ws->height -= 2 * ws->current_gaps; 659 ws->height -= 2 * ws->current_gaps;
710} 660}
661
662struct sway_container *workspace_split(struct sway_workspace *workspace,
663 enum sway_container_layout layout) {
664 if (workspace->tiling->length == 0) {
665 workspace->prev_split_layout = workspace->layout;
666 workspace->layout = layout;
667 return NULL;
668 }
669
670 enum sway_container_layout old_layout = workspace->layout;
671 struct sway_container *middle = workspace_wrap_children(workspace);
672 workspace->layout = layout;
673 middle->layout = old_layout;
674
675 return middle;
676}
677
678void workspace_update_representation(struct sway_workspace *ws) {
679 size_t len = container_build_representation(ws->layout, ws->tiling, NULL);
680 free(ws->representation);
681 ws->representation = calloc(len + 1, sizeof(char));
682 if (!sway_assert(ws->representation, "Unable to allocate title string")) {
683 return;
684 }
685 container_build_representation(ws->layout, ws->tiling, ws->representation);
686}
687
688void workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box) {
689 box->x = workspace->x;
690 box->y = workspace->y;
691 box->width = workspace->width;
692 box->height = workspace->height;
693}