aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-08-06 11:30:14 -0400
committerLibravatar GitHub <noreply@github.com>2018-08-06 11:30:14 -0400
commit1a8bee68b40f641f57adc97d0f2ffcf5e2ed544d (patch)
tree3df803a95c0a13f4dc9b627db505ba50d5e6a6d2
parentMerge pull request #2422 from ggreer/compiler-errors (diff)
parentcommands: fix workspace edge cases (diff)
downloadsway-1a8bee68b40f641f57adc97d0f2ffcf5e2ed544d.tar.gz
sway-1a8bee68b40f641f57adc97d0f2ffcf5e2ed544d.tar.zst
sway-1a8bee68b40f641f57adc97d0f2ffcf5e2ed544d.zip
Merge pull request #2392 from ianyfan/commands
Fix commands: criteria, layout, move, workspace
-rw-r--r--include/sway/tree/container.h2
-rw-r--r--include/sway/tree/view.h5
-rw-r--r--include/sway/tree/workspace.h4
-rw-r--r--sway/commands/layout.c105
-rw-r--r--sway/commands/move.c262
-rw-r--r--sway/commands/rename.c10
-rw-r--r--sway/commands/workspace.c50
-rw-r--r--sway/criteria.c26
-rw-r--r--sway/sway.5.scd59
-rw-r--r--sway/tree/layout.c4
-rw-r--r--sway/tree/view.c18
-rw-r--r--sway/tree/workspace.c11
12 files changed, 378 insertions, 178 deletions
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 44ff9f7d..16a180f8 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -113,7 +113,7 @@ struct sway_container {
113 113
114 enum sway_container_type type; 114 enum sway_container_type type;
115 enum sway_container_layout layout; 115 enum sway_container_layout layout;
116 enum sway_container_layout prev_layout; 116 enum sway_container_layout prev_split_layout;
117 117
118 bool is_sticky; 118 bool is_sticky;
119 119
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 37fd02bc..4a3f01e7 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -316,6 +316,11 @@ void view_update_title(struct sway_view *view, bool force);
316void view_execute_criteria(struct sway_view *view); 316void view_execute_criteria(struct sway_view *view);
317 317
318/** 318/**
319 * Find any view that has the given mark and return it.
320 */
321struct sway_view *view_find_mark(char *mark);
322
323/**
319 * Find any view that has the given mark and remove the mark from the view. 324 * Find any view that has the given mark and remove the mark from the view.
320 * Returns true if it matched a view. 325 * Returns true if it matched a view.
321 */ 326 */
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index 3337f2c8..239cbbdb 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -1,6 +1,7 @@
1#ifndef _SWAY_WORKSPACE_H 1#ifndef _SWAY_WORKSPACE_H
2#define _SWAY_WORKSPACE_H 2#define _SWAY_WORKSPACE_H
3 3
4#include <stdbool.h>
4#include "sway/tree/container.h" 5#include "sway/tree/container.h"
5 6
6struct sway_view; 7struct sway_view;
@@ -17,7 +18,8 @@ extern char *prev_workspace_name;
17 18
18char *workspace_next_name(const char *output_name); 19char *workspace_next_name(const char *output_name);
19 20
20bool workspace_switch(struct sway_container *workspace); 21bool workspace_switch(struct sway_container *workspace,
22 bool no_auto_back_and_forth);
21 23
22struct sway_container *workspace_by_number(const char* name); 24struct sway_container *workspace_by_number(const char* name);
23 25
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index c446f1f9..f4e4dda9 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -1,3 +1,4 @@
1#include <stdbool.h>
1#include <string.h> 2#include <string.h>
2#include <strings.h> 3#include <strings.h>
3#include "sway/commands.h" 4#include "sway/commands.h"
@@ -5,6 +6,26 @@
5#include "sway/tree/container.h" 6#include "sway/tree/container.h"
6#include "log.h" 7#include "log.h"
7 8
9static bool parse_layout_string(char *s, enum sway_container_layout *ptr) {
10 if (strcasecmp(s, "splith") == 0) {
11 *ptr = L_HORIZ;
12 } else if (strcasecmp(s, "splitv") == 0) {
13 *ptr = L_VERT;
14 } else if (strcasecmp(s, "tabbed") == 0) {
15 *ptr = L_TABBED;
16 } else if (strcasecmp(s, "stacking") == 0) {
17 *ptr = L_STACKED;
18 } else {
19 return false;
20 }
21 return true;
22}
23
24static const char* expected_syntax =
25 "Expected 'layout default|tabbed|stacking|splitv|splith' or "
26 "'layout toggle [split|all]' or "
27 "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'";
28
8struct cmd_results *cmd_layout(int argc, char **argv) { 29struct cmd_results *cmd_layout(int argc, char **argv) {
9 struct cmd_results *error = NULL; 30 struct cmd_results *error = NULL;
10 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { 31 if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
@@ -21,35 +42,69 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
21 parent = parent->parent; 42 parent = parent->parent;
22 } 43 }
23 44
24 if (strcasecmp(argv[0], "default") == 0) { 45 enum sway_container_layout prev = parent->layout;
25 parent->layout = parent->prev_layout; 46 bool assigned_directly = parse_layout_string(argv[0], &parent->layout);
26 if (parent->layout == L_NONE) { 47 if (!assigned_directly) {
27 parent->layout = container_get_default_layout(parent); 48 if (strcasecmp(argv[0], "default") == 0) {
28 } 49 parent->layout = parent->prev_split_layout;
29 } else { 50 } else if (strcasecmp(argv[0], "toggle") == 0) {
30 if (parent->layout != L_TABBED && parent->layout != L_STACKED) { 51 if (argc == 1) {
31 parent->prev_layout = parent->layout; 52 parent->layout =
32 } 53 parent->layout == L_STACKED ? L_TABBED :
33 54 parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED;
34 if (strcasecmp(argv[0], "splith") == 0) { 55 } else if (argc == 2) {
35 parent->layout = L_HORIZ; 56 if (strcasecmp(argv[1], "all") == 0) {
36 } else if (strcasecmp(argv[0], "splitv") == 0) { 57 parent->layout =
37 parent->layout = L_VERT; 58 parent->layout == L_HORIZ ? L_VERT :
38 } else if (strcasecmp(argv[0], "tabbed") == 0) { 59 parent->layout == L_VERT ? L_STACKED :
39 parent->layout = L_TABBED; 60 parent->layout == L_STACKED ? L_TABBED : L_HORIZ;
40 } else if (strcasecmp(argv[0], "stacking") == 0) { 61 } else if (strcasecmp(argv[1], "split") == 0) {
41 parent->layout = L_STACKED; 62 parent->layout =
42 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { 63 parent->layout == L_HORIZ ? L_VERT :
43 if (parent->layout == L_HORIZ) { 64 parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
44 parent->layout = L_VERT; 65 } else {
66 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
67 }
45 } else { 68 } else {
46 parent->layout = L_HORIZ; 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 }
47 } 93 }
94 } else {
95 return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
48 } 96 }
49 } 97 }
50 98 if (parent->layout == L_NONE) {
51 container_notify_subtree_changed(parent); 99 parent->layout = container_get_default_layout(parent);
52 arrange_windows(parent); 100 }
101 if (prev != parent->layout) {
102 if (prev != L_TABBED && prev != L_STACKED) {
103 parent->prev_split_layout = prev;
104 }
105 container_notify_subtree_changed(parent);
106 arrange_windows(parent);
107 }
53 108
54 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 109 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
55} 110}
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 841da4c4..af3dc538 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -1,4 +1,5 @@
1#define _XOPEN_SOURCE 500 1#define _XOPEN_SOURCE 500
2#include <stdbool.h>
2#include <string.h> 3#include <string.h>
3#include <strings.h> 4#include <strings.h>
4#include <wlr/types/wlr_cursor.h> 5#include <wlr/types/wlr_cursor.h>
@@ -16,11 +17,11 @@
16#include "stringop.h" 17#include "stringop.h"
17#include "list.h" 18#include "list.h"
18 19
19static const char* expected_syntax = 20static const char *expected_syntax =
20 "Expected 'move <left|right|up|down> <[px] px>' or " 21 "Expected 'move <left|right|up|down> <[px] px>' or "
21 "'move <container|window> to workspace <name>' or " 22 "'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or "
22 "'move <container|window|workspace> to output <name|direction>' or " 23 "'move [--no-auto-back-and-forth] <container|window|workspace> [to] output <name|direction>' or "
23 "'move position mouse'"; 24 "'move <container|window> [to] mark <mark>'";
24 25
25static struct sway_container *output_in_direction(const char *direction, 26static struct sway_container *output_in_direction(const char *direction,
26 struct wlr_output *reference, int ref_lx, int ref_ly) { 27 struct wlr_output *reference, int ref_lx, int ref_ly) {
@@ -52,123 +53,168 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
52 int argc, char **argv) { 53 int argc, char **argv) {
53 struct cmd_results *error = NULL; 54 struct cmd_results *error = NULL;
54 if ((error = checkarg(argc, "move container/window", 55 if ((error = checkarg(argc, "move container/window",
55 EXPECTED_AT_LEAST, 4))) { 56 EXPECTED_AT_LEAST, 3))) {
56 return error; 57 return error;
57 } else if (strcasecmp(argv[1], "to") == 0 58 }
58 && strcasecmp(argv[2], "workspace") == 0) { 59
59 // move container to workspace x 60 if (current->type == C_WORKSPACE) {
60 if (current->type == C_WORKSPACE) { 61 if (current->children->length == 0) {
61 if (current->children->length == 0) {
62 return cmd_results_new(CMD_FAILURE, "move",
63 "Can't move an empty workspace");
64 }
65 current = container_wrap_children(current);
66 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
67 return cmd_results_new(CMD_FAILURE, "move", 62 return cmd_results_new(CMD_FAILURE, "move",
68 "Can only move containers and views."); 63 "Can't move an empty workspace");
69 } 64 }
70 struct sway_container *ws; 65 current = container_wrap_children(current);
71 char *ws_name = NULL; 66 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
72 if (argc == 5 && strcasecmp(argv[3], "number") == 0) { 67 return cmd_results_new(CMD_FAILURE, "move",
73 // move "container to workspace number x" 68 "Can only move containers and views.");
74 ws_name = strdup(argv[4]); 69 }
75 ws = workspace_by_number(ws_name); 70
76 } else { 71 bool no_auto_back_and_forth = false;
77 ws_name = join_args(argv + 3, argc - 3); 72 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
78 ws = workspace_by_name(ws_name); 73 no_auto_back_and_forth = true;
74 if (--argc < 3) {
75 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
76 }
77 ++argv;
78 }
79 while (strcasecmp(argv[1], "--no-auto-back-and-forth") == 0) {
80 no_auto_back_and_forth = true;
81 if (--argc < 3) {
82 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
79 } 83 }
84 argv++;
85 }
86
87 while (strcasecmp(argv[1], "to") == 0) {
88 if (--argc < 3) {
89 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
90 }
91 argv++;
92 }
80 93
81 if (config->auto_back_and_forth && prev_workspace_name) { 94 struct sway_container *old_parent = current->parent;
82 // auto back and forth move 95 struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
83 struct sway_container *curr_ws = container_parent(current, C_WORKSPACE); 96 struct sway_container *destination = NULL;
84 if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) { 97
85 // if target workspace is the current one 98 // determine destination
86 free(ws_name); 99 if (strcasecmp(argv[1], "workspace") == 0) {
87 ws_name = strdup(prev_workspace_name); 100 // move container to workspace x
101 struct sway_container *ws;
102 if (strcasecmp(argv[2], "next") == 0 ||
103 strcasecmp(argv[2], "prev") == 0 ||
104 strcasecmp(argv[2], "next_on_output") == 0 ||
105 strcasecmp(argv[2], "prev_on_output") == 0 ||
106 strcasecmp(argv[2], "back_and_forth") == 0 ||
107 strcasecmp(argv[2], "current") == 0) {
108 ws = workspace_by_name(argv[2]);
109 } else if (strcasecmp(argv[2], "back_and_forth") == 0) {
110 if (!(ws = workspace_by_name(argv[2]))) {
111 if (prev_workspace_name) {
112 ws = workspace_create(NULL, prev_workspace_name);
113 } else {
114 return cmd_results_new(CMD_FAILURE, "move",
115 "No workspace was previously active.");
116 }
117 }
118 } else {
119 char *ws_name = NULL;
120 if (strcasecmp(argv[2], "number") == 0) {
121 // move "container to workspace number x"
122 if (argc < 4) {
123 return cmd_results_new(CMD_INVALID, "move",
124 expected_syntax);
125 }
126 ws_name = strdup(argv[3]);
127 ws = workspace_by_number(ws_name);
128 } else {
129 ws_name = join_args(argv + 2, argc - 2);
88 ws = workspace_by_name(ws_name); 130 ws = workspace_by_name(ws_name);
89 } 131 }
90 }
91 132
92 if (!ws) { 133 if (!no_auto_back_and_forth && config->auto_back_and_forth &&
93 ws = workspace_create(NULL, ws_name); 134 prev_workspace_name) {
94 } 135 // auto back and forth move
95 free(ws_name); 136 if (old_ws->name && strcmp(old_ws->name, ws_name) == 0) {
96 struct sway_container *old_parent = current->parent; 137 // if target workspace is the current one
97 struct sway_container *old_ws = container_parent(current, C_WORKSPACE); 138 free(ws_name);
98 struct sway_container *destination = seat_get_focus_inactive( 139 ws_name = strdup(prev_workspace_name);
99 config->handler_context.seat, ws); 140 ws = workspace_by_name(ws_name);
100 container_move_to(current, destination); 141 }
101 struct sway_container *focus = seat_get_focus_inactive( 142 }
102 config->handler_context.seat, old_parent);
103 seat_set_focus_warp(config->handler_context.seat, focus, true, false);
104 container_reap_empty(old_parent);
105 container_reap_empty(destination->parent);
106
107 // TODO: Ideally we would arrange the surviving parent after reaping,
108 // but container_reap_empty does not return it, so we arrange the
109 // workspace instead.
110 arrange_windows(old_ws);
111 arrange_windows(destination->parent);
112 143
113 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 144 if (!ws) {
114 } else if (strcasecmp(argv[1], "to") == 0 145 ws = workspace_create(NULL, ws_name);
115 && strcasecmp(argv[2], "output") == 0) { 146 }
116 if (current->type == C_WORKSPACE) { 147 free(ws_name);
117 // TODO: Wrap children in a container and move that
118 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
119 } else if (current->type != C_CONTAINER
120 && current->type != C_VIEW) {
121 return cmd_results_new(CMD_FAILURE, "move",
122 "Can only move containers and views.");
123 } 148 }
149 destination = seat_get_focus_inactive(config->handler_context.seat, ws);
150 } else if (strcasecmp(argv[1], "output") == 0) {
124 struct sway_container *source = container_parent(current, C_OUTPUT); 151 struct sway_container *source = container_parent(current, C_OUTPUT);
125 struct sway_container *destination = output_in_direction(argv[3], 152 struct sway_container *dest_output = output_in_direction(argv[2],
126 source->sway_output->wlr_output, current->x, current->y); 153 source->sway_output->wlr_output, current->x, current->y);
127 if (!destination) { 154 if (!dest_output) {
128 return cmd_results_new(CMD_FAILURE, "move workspace", 155 return cmd_results_new(CMD_FAILURE, "move workspace",
129 "Can't find output with name/direction '%s'", argv[3]); 156 "Can't find output with name/direction '%s'", argv[2]);
130 } 157 }
131 struct sway_container *focus = seat_get_focus_inactive( 158 destination = seat_get_focus_inactive(
132 config->handler_context.seat, destination); 159 config->handler_context.seat, dest_output);
133 if (!focus) { 160 if (!destination) {
134 // We've never been to this output before 161 // We've never been to this output before
135 focus = destination->children->items[0]; 162 destination = dest_output->children->items[0];
136 } 163 }
137 struct sway_container *old_parent = current->parent; 164 } else if (strcasecmp(argv[1], "mark") == 0) {
138 struct sway_container *old_ws = container_parent(current, C_WORKSPACE); 165 struct sway_view *dest_view = view_find_mark(argv[2]);
139 container_move_to(current, focus); 166 if (dest_view == NULL) {
140 seat_set_focus_warp(config->handler_context.seat, old_parent, true, false); 167 return cmd_results_new(CMD_FAILURE, "move",
141 container_reap_empty(old_parent); 168 "Mark '%s' not found", argv[2]);
142 container_reap_empty(focus->parent); 169 }
143 170 destination = dest_view->swayc;
144 // TODO: Ideally we would arrange the surviving parent after reaping, 171 } else {
145 // but container_reap_empty does not return it, so we arrange the 172 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
146 // workspace instead.
147 arrange_windows(old_ws);
148 arrange_windows(focus->parent);
149
150 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
151 } 173 }
152 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 174
175 // move container, arrange windows and return focus
176 container_move_to(current, destination);
177 struct sway_container *focus =
178 seat_get_focus_inactive(config->handler_context.seat, old_parent);
179 seat_set_focus_warp(config->handler_context.seat, focus, true, false);
180 container_reap_empty(old_parent);
181 container_reap_empty(destination->parent);
182
183 // TODO: Ideally we would arrange the surviving parent after reaping,
184 // but container_reap_empty does not return it, so we arrange the
185 // workspace instead.
186 arrange_windows(old_ws);
187 arrange_windows(destination->parent);
188
189 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
153} 190}
154 191
155static struct cmd_results *cmd_move_workspace(struct sway_container *current, 192static struct cmd_results *cmd_move_workspace(struct sway_container *current,
156 int argc, char **argv) { 193 int argc, char **argv) {
157 struct cmd_results *error = NULL; 194 struct cmd_results *error = NULL;
158 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { 195 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) {
159 return error; 196 return error;
160 } else if (strcasecmp(argv[1], "to") != 0 197 }
161 || strcasecmp(argv[2], "output") != 0) { 198
199 while (strcasecmp(argv[1], "to") == 0) {
200 if (--argc < 3) {
201 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
202 }
203 ++argv;
204 }
205
206 if (strcasecmp(argv[1], "output") != 0) {
162 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 207 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
163 } 208 }
209
164 struct sway_container *source = container_parent(current, C_OUTPUT); 210 struct sway_container *source = container_parent(current, C_OUTPUT);
165 int center_x = current->width / 2 + current->x, 211 int center_x = current->width / 2 + current->x,
166 center_y = current->height / 2 + current->y; 212 center_y = current->height / 2 + current->y;
167 struct sway_container *destination = output_in_direction(argv[3], 213 struct sway_container *destination = output_in_direction(argv[2],
168 source->sway_output->wlr_output, center_x, center_y); 214 source->sway_output->wlr_output, center_x, center_y);
169 if (!destination) { 215 if (!destination) {
170 return cmd_results_new(CMD_FAILURE, "move workspace", 216 return cmd_results_new(CMD_FAILURE, "move workspace",
171 "Can't find output with name/direction '%s'", argv[3]); 217 "Can't find output with name/direction '%s'", argv[2]);
172 } 218 }
173 if (current->type != C_WORKSPACE) { 219 if (current->type != C_WORKSPACE) {
174 current = container_parent(current, C_WORKSPACE); 220 current = container_parent(current, C_WORKSPACE);
@@ -242,9 +288,9 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
242 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 288 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
243} 289}
244 290
245static const char* expected_position_syntax = 291static const char *expected_position_syntax =
246 "Expected 'move [absolute] position <x> <y>' or " 292 "Expected 'move [absolute] position <x> [px] <y> [px]' or "
247 "'move [absolute] position mouse'"; 293 "'move [absolute] position center|mouse'";
248 294
249static struct cmd_results *move_to_position(struct sway_container *container, 295static struct cmd_results *move_to_position(struct sway_container *container,
250 int argc, char **argv) { 296 int argc, char **argv) {
@@ -279,10 +325,18 @@ static struct cmd_results *move_to_position(struct sway_container *container,
279 double ly = seat->cursor->cursor->y - container->height / 2; 325 double ly = seat->cursor->cursor->y - container->height / 2;
280 container_floating_move_to(container, lx, ly); 326 container_floating_move_to(container, lx, ly);
281 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 327 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
328 } else if (strcmp(argv[0], "center") == 0) {
329 struct sway_container *ws = container_parent(container, C_WORKSPACE);
330 double lx = ws->x + (ws->width - container->width) / 2;
331 double ly = ws->y + (ws->height - container->height) / 2;
332 container_floating_move_to(container, lx, ly);
333 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
282 } 334 }
283 if (argc != 2) { 335
336 if (argc < 2) {
284 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); 337 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
285 } 338 }
339
286 double lx, ly; 340 double lx, ly;
287 char *inv; 341 char *inv;
288 lx = (double)strtol(argv[0], &inv, 10); 342 lx = (double)strtol(argv[0], &inv, 10);
@@ -290,11 +344,22 @@ static struct cmd_results *move_to_position(struct sway_container *container,
290 return cmd_results_new(CMD_FAILURE, "move", 344 return cmd_results_new(CMD_FAILURE, "move",
291 "Invalid position specified"); 345 "Invalid position specified");
292 } 346 }
347 if (strcmp(argv[1], "px") == 0) {
348 --argc;
349 ++argv;
350 }
351
352 if (argc > 3) {
353 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
354 }
355
293 ly = (double)strtol(argv[1], &inv, 10); 356 ly = (double)strtol(argv[1], &inv, 10);
294 if (*inv != '\0' && strcasecmp(inv, "px") != 0) { 357 if ((*inv != '\0' && strcasecmp(inv, "px") != 0) ||
358 (argc == 3 && strcmp(argv[2], "px") != 0)) {
295 return cmd_results_new(CMD_FAILURE, "move", 359 return cmd_results_new(CMD_FAILURE, "move",
296 "Invalid position specified"); 360 "Invalid position specified");
297 } 361 }
362
298 container_floating_move_to(container, lx, ly); 363 container_floating_move_to(container, lx, ly);
299 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 364 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
300} 365}
@@ -342,8 +407,11 @@ struct cmd_results *cmd_move(int argc, char **argv) {
342 return move_in_direction(current, MOVE_UP, argc, argv); 407 return move_in_direction(current, MOVE_UP, argc, argv);
343 } else if (strcasecmp(argv[0], "down") == 0) { 408 } else if (strcasecmp(argv[0], "down") == 0) {
344 return move_in_direction(current, MOVE_DOWN, argc, argv); 409 return move_in_direction(current, MOVE_DOWN, argc, argv);
345 } else if (strcasecmp(argv[0], "container") == 0 410 } else if ((strcasecmp(argv[0], "container") == 0
346 || strcasecmp(argv[0], "window") == 0) { 411 || strcasecmp(argv[0], "window") == 0) ||
412 (strcasecmp(argv[0], "--no-auto-back-and-forth") &&
413 (strcasecmp(argv[0], "container") == 0
414 || strcasecmp(argv[0], "window") == 0))) {
347 return cmd_move_container(current, argc, argv); 415 return cmd_move_container(current, argc, argv);
348 } else if (strcasecmp(argv[0], "workspace") == 0) { 416 } else if (strcasecmp(argv[0], "workspace") == 0) {
349 return cmd_move_workspace(current, argc, argv); 417 return cmd_move_workspace(current, argc, argv);
diff --git a/sway/commands/rename.c b/sway/commands/rename.c
index a380ff9c..c6952bbb 100644
--- a/sway/commands/rename.c
+++ b/sway/commands/rename.c
@@ -61,6 +61,16 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
61 } 61 }
62 62
63 char *new_name = join_args(argv + argn, argc - argn); 63 char *new_name = join_args(argv + argn, argc - argn);
64 if (strcasecmp(new_name, "next") == 0 ||
65 strcasecmp(new_name, "prev") == 0 ||
66 strcasecmp(new_name, "next_on_output") == 0 ||
67 strcasecmp(new_name, "prev_on_output") == 0 ||
68 strcasecmp(new_name, "back_and_forth") == 0 ||
69 strcasecmp(new_name, "current") == 0) {
70 free(new_name);
71 return cmd_results_new(CMD_INVALID, "rename",
72 "Cannot use special workspace name '%s'", argv[argn]);
73 }
64 struct sway_container *tmp_workspace = workspace_by_name(new_name); 74 struct sway_container *tmp_workspace = workspace_by_name(new_name);
65 if (tmp_workspace) { 75 if (tmp_workspace) {
66 free(new_name); 76 free(new_name);
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index e8b37182..f5558bb4 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -17,17 +17,6 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
17 17
18 int output_location = -1; 18 int output_location = -1;
19 19
20 struct sway_container *current_container = config->handler_context.current_container;
21 struct sway_container *old_workspace = NULL, *old_output = NULL;
22 if (current_container) {
23 if (current_container->type == C_WORKSPACE) {
24 old_workspace = current_container;
25 } else {
26 old_workspace = container_parent(current_container, C_WORKSPACE);
27 }
28 old_output = container_parent(current_container, C_OUTPUT);
29 }
30
31 for (int i = 0; i < argc; ++i) { 20 for (int i = 0; i < argc; ++i) {
32 if (strcasecmp(argv[i], "output") == 0) { 21 if (strcasecmp(argv[i], "output") == 0) {
33 output_location = i; 22 output_location = i;
@@ -57,29 +46,36 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
57 if (config->reading || !config->active) { 46 if (config->reading || !config->active) {
58 return cmd_results_new(CMD_DEFER, "workspace", NULL); 47 return cmd_results_new(CMD_DEFER, "workspace", NULL);
59 } 48 }
49
50 bool no_auto_back_and_forth = false;
51 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
52 no_auto_back_and_forth = true;
53 if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) {
54 return error;
55 }
56 ++argv;
57 }
58
59
60 struct sway_container *ws = NULL; 60 struct sway_container *ws = NULL;
61 if (strcasecmp(argv[0], "number") == 0) { 61 if (strcasecmp(argv[0], "number") == 0) {
62 if (argc < 2) {
63 cmd_results_new(CMD_INVALID, "workspace",
64 "Expected workspace number");
65 }
62 if (!(ws = workspace_by_number(argv[1]))) { 66 if (!(ws = workspace_by_number(argv[1]))) {
63 char *name = join_args(argv + 1, argc - 1); 67 char *name = join_args(argv + 1, argc - 1);
64 ws = workspace_create(NULL, name); 68 ws = workspace_create(NULL, name);
65 free(name); 69 free(name);
66 } 70 }
67 } else if (strcasecmp(argv[0], "next") == 0) { 71 } else if (strcasecmp(argv[0], "next") == 0 ||
68 ws = workspace_next(old_workspace); 72 strcasecmp(argv[0], "prev") == 0 ||
69 } else if (strcasecmp(argv[0], "prev") == 0) { 73 strcasecmp(argv[0], "next_on_output") == 0 ||
70 ws = workspace_prev(old_workspace); 74 strcasecmp(argv[0], "prev_on_output") == 0 ||
71 } else if (strcasecmp(argv[0], "next_on_output") == 0) { 75 strcasecmp(argv[0], "current") == 0) {
72 ws = workspace_output_next(old_output); 76 ws = workspace_by_name(argv[0]);
73 } else if (strcasecmp(argv[0], "prev_on_output") == 0) {
74 ws = workspace_output_prev(old_output);
75 } else if (strcasecmp(argv[0], "back_and_forth") == 0) { 77 } else if (strcasecmp(argv[0], "back_and_forth") == 0) {
76 // if auto_back_and_forth is enabled, workspace_switch will swap 78 if (!(ws = workspace_by_name(argv[0])) && prev_workspace_name) {
77 // the workspaces. If we created prev_workspace here, workspace_switch
78 // would put us back on original workspace.
79 if (config->auto_back_and_forth) {
80 ws = old_workspace;
81 } else if (prev_workspace_name
82 && !(ws = workspace_by_name(prev_workspace_name))) {
83 ws = workspace_create(NULL, prev_workspace_name); 79 ws = workspace_create(NULL, prev_workspace_name);
84 } 80 }
85 } else { 81 } else {
@@ -89,7 +85,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
89 } 85 }
90 free(name); 86 free(name);
91 } 87 }
92 workspace_switch(ws); 88 workspace_switch(ws, no_auto_back_and_forth);
93 } 89 }
94 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 90 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
95} 91}
diff --git a/sway/criteria.c b/sway/criteria.c
index 39d300ea..9077aa9b 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -361,8 +361,17 @@ static char *get_focused_prop(enum criteria_token token) {
361 } 361 }
362 } 362 }
363 break; 363 break;
364 case T_CON_ID: // These do not support __focused__ 364 case T_CON_ID:
365 case T_CON_MARK: 365 if (view->swayc == NULL) {
366 return NULL;
367 }
368 size_t id = view->swayc->id;
369 size_t id_size = snprintf(NULL, 0, "%zu", id) + 1;
370 char *id_str = malloc(id_size);
371 snprintf(id_str, id_size, "%zu", id);
372 value = id_str;
373 break;
374 case T_CON_MARK: // These do not support __focused__
366 case T_FLOATING: 375 case T_FLOATING:
367#ifdef HAVE_XWAYLAND 376#ifdef HAVE_XWAYLAND
368 case T_ID: 377 case T_ID:
@@ -425,7 +434,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
425 case T_CON_ID: 434 case T_CON_ID:
426 criteria->con_id = strtoul(effective_value, &endptr, 10); 435 criteria->con_id = strtoul(effective_value, &endptr, 10);
427 if (*endptr != 0) { 436 if (*endptr != 0) {
428 error = strdup("The value for 'con_id' should be numeric"); 437 error = strdup("The value for 'con_id' should be '__focused__' or numeric");
429 } 438 }
430 break; 439 break;
431 case T_CON_MARK: 440 case T_CON_MARK:
@@ -452,13 +461,18 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
452 criteria->tiling = true; 461 criteria->tiling = true;
453 break; 462 break;
454 case T_URGENT: 463 case T_URGENT:
455 if (strcmp(effective_value, "latest") == 0) { 464 if (strcmp(effective_value, "latest") == 0 ||
465 strcmp(effective_value, "newest") == 0 ||
466 strcmp(effective_value, "last") == 0 ||
467 strcmp(effective_value, "recent") == 0) {
456 criteria->urgent = 'l'; 468 criteria->urgent = 'l';
457 } else if (strcmp(effective_value, "oldest") == 0) { 469 } else if (strcmp(effective_value, "oldest") == 0 ||
470 strcmp(effective_value, "first") == 0) {
458 criteria->urgent = 'o'; 471 criteria->urgent = 'o';
459 } else { 472 } else {
460 error = 473 error =
461 strdup("The value for 'urgent' must be 'latest' or 'oldest'"); 474 strdup("The value for 'urgent' must be 'first', 'last', "
475 "'latest', 'newest', 'oldest' or 'recent'");
462 } 476 }
463 break; 477 break;
464 case T_WORKSPACE: 478 case T_WORKSPACE:
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 82df38e3..73a01152 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -84,6 +84,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
84*floating* enable|disable|toggle 84*floating* enable|disable|toggle
85 Make focused view floating, non-floating, or the opposite of what it is now. 85 Make focused view floating, non-floating, or the opposite of what it is now.
86 86
87<criteria> *focus*
88 Moves focus to the container that matches the specified criteria.
89
87*focus* up|right|down|left 90*focus* up|right|down|left
88 Moves focus to the next container in the specified direction. 91 Moves focus to the next container in the specified direction.
89 92
@@ -111,33 +114,53 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
111*fullscreen* 114*fullscreen*
112 Toggles fullscreen for the focused view. 115 Toggles fullscreen for the focused view.
113 116
114*layout* splith|splitv|stacking|tabbed 117*layout* default|splith|splitv|stacking|tabbed
115 Sets the layout mode of the focused container. 118 Sets the layout mode of the focused container.
116 119
117*layout* toggle split 120*layout* toggle [split|all]
118 Switches the focused container between the splitv and splith layouts. 121 Cycles the layout mode of the focused container though a preset list of
122 layouts. If no argument is given, then it cycles through stacking, tabbed
123 and the last split layout. If "split" is given, then it cycles through
124 splith and splitv. If "all" is given, then it cycles through every layout.
125
126*layout* toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...
127 Cycles the layout mode of the focused container through a list of layouts.
119 128
120*move* left|right|up|down [<px>] 129*move* left|right|up|down [<px> px]
121 Moves the focused container in the direction specified. If the container, 130 Moves the focused container in the direction specified. If the container,
122 the optional _px_ argument specifies how many pixels to move the container. 131 the optional _px_ argument specifies how many pixels to move the container.
123 If unspecified, the default is 10 pixels. Pixels are ignored when moving 132 If unspecified, the default is 10 pixels. Pixels are ignored when moving
124 tiled containers. 133 tiled containers.
125 134
126*move* container|window to workspace <name> 135*move* [absolute] position <pos_x> [px] <pos_y> [px]
127 Moves the focused container to the specified workspace. 136 Moves the focused container to the specified position.
128 137
129*move* container|window to workspace prev|next 138*move* [absolute] position center|mouse
130 Moves the focused container to the previous or next workspace on this 139 Moves the focused container to be centered on the workspace or mouse.
131 output, or if no workspaces remain, the previous or next output. 140
141*move* container|window [to] mark <mark>
142 Moves the focused container to the specified mark.
143
144*move* [--no-auto-back-and-forth] container|window [to] workspace [number] <name>
145 Moves the focused container to the specified workspace. The string "number"
146 is optional and is used to match a workspace with the same number, even if
147 it has a different name.
132 148
133*move* container|window to workspace prev\_on\_output|next\_on\_output 149*move* container|window [to] workspace prev|next|current
150 Moves the focused container to the previous, next or current workspace on
151 this output, or if no workspaces remain, the previous or next output.
152
153*move* container|window [to] workspace prev\_on\_output|next\_on\_output
134 Moves the focused container to the previous or next workspace on this 154 Moves the focused container to the previous or next workspace on this
135 output, wrapping around if already at the first or last workspace. 155 output, wrapping around if already at the first or last workspace.
136 156
137*move* container|window|workspace to output <name> 157*move* container|window [to] workspace back_and_forth
158 Moves the focused container to previously focused workspace.
159
160*move* container|window|workspace [to] output <name>
138 Moves the focused container or workspace to the specified output. 161 Moves the focused container or workspace to the specified output.
139 162
140*move* container|window|workspace to output up|right|down|left 163*move* container|window|workspace [to] output up|right|down|left
141 Moves the focused container or workspace to next output in the specified 164 Moves the focused container or workspace to next output in the specified
142 direction. 165 direction.
143 166
@@ -511,7 +534,7 @@ config after the others, or it will be matched instead of the others.
511 state. Using _allow_ or _deny_ controls the window's ability to set itself 534 state. Using _allow_ or _deny_ controls the window's ability to set itself
512 as urgent. By default, windows are allowed to set their own urgency. 535 as urgent. By default, windows are allowed to set their own urgency.
513 536
514*workspace* [number] <name> 537*workspace* [--no-auto-back-and-forth] [number] <name>
515 Switches to the specified workspace. The string "number" is optional and is 538 Switches to the specified workspace. The string "number" is optional and is
516 used to sort workspaces. 539 used to sort workspaces.
517 540
@@ -522,6 +545,9 @@ config after the others, or it will be matched instead of the others.
522*workspace* prev\_on\_output|next\_on\_output 545*workspace* prev\_on\_output|next\_on\_output
523 Switches to the next workspace on the current output. 546 Switches to the next workspace on the current output.
524 547
548*workspace* back_and_forth
549 Switches to the previously focused workspace.
550
525*workspace* <name> output <output> 551*workspace* <name> output <output>
526 Specifies that workspace _name_ should be shown on the specified _output_. 552 Specifies that workspace _name_ should be shown on the specified _output_.
527 553
@@ -582,7 +608,9 @@ The following attributes may be matched with:
582 the currently focused window. 608 the currently focused window.
583 609
584*con\_id* 610*con\_id*
585 Compare against the internal container ID, which you can find via IPC. 611 Compare against the internal container ID, which you can find via IPC. If
612 value is \_\_focused\_\_, then the id must be the same as that of the
613 currently focused window.
586 614
587*con\_mark* 615*con\_mark*
588 Compare against the window marks. Can be a regular expression. 616 Compare against the window marks. Can be a regular expression.
@@ -612,7 +640,8 @@ The following attributes may be matched with:
612 currently focused window. 640 currently focused window.
613 641
614*urgent* 642*urgent*
615 Compares the urgent state of the window. Can be "latest" or "oldest". 643 Compares the urgent state of the window. Can be "first", "last", "latest",
644 "newest", "oldest" or "recent".
616 645
617*window\_role* 646*window\_role*
618 Compare against the window role (WM\_WINDOW\_ROLE). Can be a regular 647 Compare against the window role (WM\_WINDOW\_ROLE). Can be a regular
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 07de9664..28cdc71e 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -859,7 +859,7 @@ struct sway_container *container_split(struct sway_container *child,
859 } 859 }
860 if (child->type == C_WORKSPACE && child->children->length == 0) { 860 if (child->type == C_WORKSPACE && child->children->length == 0) {
861 // Special case: this just behaves like splitt 861 // Special case: this just behaves like splitt
862 child->prev_layout = child->layout; 862 child->prev_split_layout = child->layout;
863 child->layout = layout; 863 child->layout = layout;
864 return child; 864 return child;
865 } 865 }
@@ -870,7 +870,7 @@ struct sway_container *container_split(struct sway_container *child,
870 870
871 remove_gaps(child); 871 remove_gaps(child);
872 872
873 cont->prev_layout = L_NONE; 873 cont->prev_split_layout = L_NONE;
874 cont->width = child->width; 874 cont->width = child->width;
875 cont->height = child->height; 875 cont->height = child->height;
876 cont->x = child->x; 876 cont->x = child->x;
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 9465b3a1..faaa53a1 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1,5 +1,6 @@
1#define _POSIX_C_SOURCE 200809L 1#define _POSIX_C_SOURCE 200809L
2#include <stdlib.h> 2#include <stdlib.h>
3#include <strings.h>
3#include <wayland-server.h> 4#include <wayland-server.h>
4#include <wlr/render/wlr_renderer.h> 5#include <wlr/render/wlr_renderer.h>
5#include <wlr/types/wlr_buffer.h> 6#include <wlr/types/wlr_buffer.h>
@@ -456,7 +457,13 @@ static struct sway_container *select_workspace(struct sway_view *view) {
456 if (criteria->type == CT_ASSIGN_WORKSPACE) { 457 if (criteria->type == CT_ASSIGN_WORKSPACE) {
457 ws = workspace_by_name(criteria->target); 458 ws = workspace_by_name(criteria->target);
458 if (!ws) { 459 if (!ws) {
459 ws = workspace_create(NULL, criteria->target); 460 if (strcasecmp(criteria->target, "back_and_forth") == 0) {
461 if (prev_workspace_name) {
462 ws = workspace_create(NULL, prev_workspace_name);
463 }
464 } else {
465 ws = workspace_create(NULL, criteria->target);
466 }
460 } 467 }
461 break; 468 break;
462 } else { 469 } else {
@@ -891,6 +898,15 @@ static bool find_by_mark_iterator(struct sway_container *con,
891 return con->type == C_VIEW && view_has_mark(con->sway_view, mark); 898 return con->type == C_VIEW && view_has_mark(con->sway_view, mark);
892} 899}
893 900
901struct sway_view *view_find_mark(char *mark) {
902 struct sway_container *container = container_find(&root_container,
903 find_by_mark_iterator, mark);
904 if (!container) {
905 return NULL;
906 }
907 return container->sway_view;
908}
909
894bool view_find_and_unmark(char *mark) { 910bool view_find_and_unmark(char *mark) {
895 struct sway_container *container = container_find(&root_container, 911 struct sway_container *container = container_find(&root_container,
896 find_by_mark_iterator, mark); 912 find_by_mark_iterator, mark);
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index cc225e79..3fcad631 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -59,7 +59,7 @@ struct sway_container *workspace_create(struct sway_container *output,
59 workspace->width = output->width; 59 workspace->width = output->width;
60 workspace->height = output->height; 60 workspace->height = output->height;
61 workspace->name = !name ? NULL : strdup(name); 61 workspace->name = !name ? NULL : strdup(name);
62 workspace->prev_layout = L_NONE; 62 workspace->prev_split_layout = L_NONE;
63 workspace->layout = container_get_default_layout(output); 63 workspace->layout = container_get_default_layout(output);
64 64
65 struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); 65 struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace));
@@ -250,6 +250,7 @@ struct sway_container *workspace_by_name(const char *name) {
250 current_workspace = container_parent(focus, C_WORKSPACE); 250 current_workspace = container_parent(focus, C_WORKSPACE);
251 current_output = container_parent(focus, C_OUTPUT); 251 current_output = container_parent(focus, C_OUTPUT);
252 } 252 }
253
253 if (strcmp(name, "prev") == 0) { 254 if (strcmp(name, "prev") == 0) {
254 return workspace_prev(current_workspace); 255 return workspace_prev(current_workspace);
255 } else if (strcmp(name, "prev_on_output") == 0) { 256 } else if (strcmp(name, "prev_on_output") == 0) {
@@ -260,6 +261,9 @@ struct sway_container *workspace_by_name(const char *name) {
260 return workspace_output_next(current_output); 261 return workspace_output_next(current_output);
261 } else if (strcmp(name, "current") == 0) { 262 } else if (strcmp(name, "current") == 0) {
262 return current_workspace; 263 return current_workspace;
264 } else if (strcasecmp(name, "back_and_forth") == 0) {
265 return prev_workspace_name ? container_find(&root_container,
266 _workspace_by_name, (void *)prev_workspace_name) : NULL;
263 } else { 267 } else {
264 return container_find(&root_container, _workspace_by_name, 268 return container_find(&root_container, _workspace_by_name,
265 (void *)name); 269 (void *)name);
@@ -364,7 +368,8 @@ struct sway_container *workspace_prev(struct sway_container *current) {
364 return workspace_prev_next_impl(current, false); 368 return workspace_prev_next_impl(current, false);
365} 369}
366 370
367bool workspace_switch(struct sway_container *workspace) { 371bool workspace_switch(struct sway_container *workspace,
372 bool no_auto_back_and_forth) {
368 if (!workspace) { 373 if (!workspace) {
369 return false; 374 return false;
370 } 375 }
@@ -379,7 +384,7 @@ bool workspace_switch(struct sway_container *workspace) {
379 active_ws = container_parent(focus, C_WORKSPACE); 384 active_ws = container_parent(focus, C_WORKSPACE);
380 } 385 }
381 386
382 if (config->auto_back_and_forth 387 if (!no_auto_back_and_forth && config->auto_back_and_forth
383 && active_ws == workspace 388 && active_ws == workspace
384 && prev_workspace_name) { 389 && prev_workspace_name) {
385 struct sway_container *new_ws = workspace_by_name(prev_workspace_name); 390 struct sway_container *new_ws = workspace_by_name(prev_workspace_name);