diff options
Diffstat (limited to 'sway/commands/move.c')
-rw-r--r-- | sway/commands/move.c | 873 |
1 files changed, 407 insertions, 466 deletions
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 | ||
43 | static struct sway_container *output_in_direction(const char *direction_string, | 43 | static 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 | ||
84 | static void container_move_to(struct sway_container *container, | 82 | static 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 | */ | ||
99 | static 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; | 115 | static 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 | |||
163 | static 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 | |||
189 | static 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) { | 219 | static 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 | ||
173 | static 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 | ||
187 | static 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 | ||
203 | static 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 |
208 | static 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. */ |
216 | static void workspace_rejigger(struct sway_container *ws, | 257 | static 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 | ||
238 | static void move_out_of_tabs_stacks(struct sway_container *container, | 272 | static 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 | ||
264 | static void container_move(struct sway_container *container, | 305 | // Returns true if moved |
265 | enum movement_direction move_dir, int move_amt) { | 306 | static 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 | ||
490 | static struct cmd_results *cmd_move_container(struct sway_container *current, | 411 | static 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 | ||
662 | static void workspace_move_to_output(struct sway_container *workspace, | 600 | static 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 | ||
701 | static struct cmd_results *cmd_move_workspace(struct sway_container *current, | 630 | static 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 | ||
739 | static struct cmd_results *move_in_direction(struct sway_container *container, | 665 | static 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 | ||
805 | static struct cmd_results *move_to_position(struct sway_container *container, | 746 | static 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 | ||
892 | static struct cmd_results *move_to_scratchpad(struct sway_container *con) { | 832 | static 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 | } |