aboutsummaryrefslogtreecommitdiffstats
path: root/sway/commands
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/commands
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/commands')
-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
25 files changed, 838 insertions, 915 deletions
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",