aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
authorLibravatar Brian Ashworth <RedSoxFan@users.noreply.github.com>2018-08-06 11:47:00 -0400
committerLibravatar GitHub <noreply@github.com>2018-08-06 11:47:00 -0400
commit639f3368e101b697aaf3715b1213ea30766ff4ed (patch)
tree67dfb7bc19eb3dd27252d8b0f493436250b4fdea /sway
parentMove workspace moving code out of container_move_to (diff)
parentMerge pull request #2268 from emersion/server-decoration-borders (diff)
downloadsway-639f3368e101b697aaf3715b1213ea30766ff4ed.tar.gz
sway-639f3368e101b697aaf3715b1213ea30766ff4ed.tar.zst
sway-639f3368e101b697aaf3715b1213ea30766ff4ed.zip
Merge branch 'master' into workspace-move-to-output
Diffstat (limited to 'sway')
-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/decoration.c71
-rw-r--r--sway/desktop/xdg_shell.c19
-rw-r--r--sway/desktop/xdg_shell_v6.c25
-rw-r--r--sway/meson.build5
-rw-r--r--sway/server.c12
-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
14 files changed, 491 insertions, 186 deletions
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 1af98e1f..bb4a7124 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>
@@ -18,11 +19,11 @@
18#include "list.h" 19#include "list.h"
19#include "log.h" 20#include "log.h"
20 21
21static const char* expected_syntax = 22static const char *expected_syntax =
22 "Expected 'move <left|right|up|down> <[px] px>' or " 23 "Expected 'move <left|right|up|down> <[px] px>' or "
23 "'move <container|window> to workspace <name>' or " 24 "'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or "
24 "'move <container|window|workspace> to output <name|direction>' or " 25 "'move [--no-auto-back-and-forth] <container|window|workspace> [to] output <name|direction>' or "
25 "'move position mouse'"; 26 "'move <container|window> [to] mark <mark>'";
26 27
27static struct sway_container *output_in_direction(const char *direction, 28static struct sway_container *output_in_direction(const char *direction,
28 struct wlr_output *reference, int ref_lx, int ref_ly) { 29 struct wlr_output *reference, int ref_lx, int ref_ly) {
@@ -54,104 +55,140 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
54 int argc, char **argv) { 55 int argc, char **argv) {
55 struct cmd_results *error = NULL; 56 struct cmd_results *error = NULL;
56 if ((error = checkarg(argc, "move container/window", 57 if ((error = checkarg(argc, "move container/window",
57 EXPECTED_AT_LEAST, 4))) { 58 EXPECTED_AT_LEAST, 3))) {
58 return error; 59 return error;
59 } else if (strcasecmp(argv[1], "to") == 0 60 }
60 && strcasecmp(argv[2], "workspace") == 0) { 61
61 // move container to workspace x 62 if (current->type == C_WORKSPACE) {
62 if (current->type == C_WORKSPACE) { 63 if (current->children->length == 0) {
63 if (current->children->length == 0) {
64 return cmd_results_new(CMD_FAILURE, "move",
65 "Can't move an empty workspace");
66 }
67 current = container_wrap_children(current);
68 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
69 return cmd_results_new(CMD_FAILURE, "move", 64 return cmd_results_new(CMD_FAILURE, "move",
70 "Can only move containers and views."); 65 "Can't move an empty workspace");
71 } 66 }
72 struct sway_container *ws; 67 current = container_wrap_children(current);
73 char *ws_name = NULL; 68 } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
74 if (argc == 5 && strcasecmp(argv[3], "number") == 0) { 69 return cmd_results_new(CMD_FAILURE, "move",
75 // move "container to workspace number x" 70 "Can only move containers and views.");
76 ws_name = strdup(argv[4]); 71 }
77 ws = workspace_by_number(ws_name); 72
78 } else { 73 bool no_auto_back_and_forth = false;
79 ws_name = join_args(argv + 3, argc - 3); 74 while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
80 ws = workspace_by_name(ws_name); 75 no_auto_back_and_forth = true;
76 if (--argc < 3) {
77 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
78 }
79 ++argv;
80 }
81 while (strcasecmp(argv[1], "--no-auto-back-and-forth") == 0) {
82 no_auto_back_and_forth = true;
83 if (--argc < 3) {
84 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
81 } 85 }
86 argv++;
87 }
88
89 while (strcasecmp(argv[1], "to") == 0) {
90 if (--argc < 3) {
91 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
92 }
93 argv++;
94 }
82 95
83 if (config->auto_back_and_forth && prev_workspace_name) { 96 struct sway_container *old_parent = current->parent;
84 // auto back and forth move 97 struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
85 struct sway_container *curr_ws = container_parent(current, C_WORKSPACE); 98 struct sway_container *destination = NULL;
86 if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) { 99
87 // if target workspace is the current one 100 // determine destination
88 free(ws_name); 101 if (strcasecmp(argv[1], "workspace") == 0) {
89 ws_name = strdup(prev_workspace_name); 102 // move container to workspace x
103 struct sway_container *ws;
104 if (strcasecmp(argv[2], "next") == 0 ||
105 strcasecmp(argv[2], "prev") == 0 ||
106 strcasecmp(argv[2], "next_on_output") == 0 ||
107 strcasecmp(argv[2], "prev_on_output") == 0 ||
108 strcasecmp(argv[2], "back_and_forth") == 0 ||
109 strcasecmp(argv[2], "current") == 0) {
110 ws = workspace_by_name(argv[2]);
111 } else if (strcasecmp(argv[2], "back_and_forth") == 0) {
112 if (!(ws = workspace_by_name(argv[2]))) {
113 if (prev_workspace_name) {
114 ws = workspace_create(NULL, prev_workspace_name);
115 } else {
116 return cmd_results_new(CMD_FAILURE, "move",
117 "No workspace was previously active.");
118 }
119 }
120 } else {
121 char *ws_name = NULL;
122 if (strcasecmp(argv[2], "number") == 0) {
123 // move "container to workspace number x"
124 if (argc < 4) {
125 return cmd_results_new(CMD_INVALID, "move",
126 expected_syntax);
127 }
128 ws_name = strdup(argv[3]);
129 ws = workspace_by_number(ws_name);
130 } else {
131 ws_name = join_args(argv + 2, argc - 2);
90 ws = workspace_by_name(ws_name); 132 ws = workspace_by_name(ws_name);
91 } 133 }
92 }
93 134
94 if (!ws) { 135 if (!no_auto_back_and_forth && config->auto_back_and_forth &&
95 ws = workspace_create(NULL, ws_name); 136 prev_workspace_name) {
96 } 137 // auto back and forth move
97 free(ws_name); 138 if (old_ws->name && strcmp(old_ws->name, ws_name) == 0) {
98 struct sway_container *old_parent = current->parent; 139 // if target workspace is the current one
99 struct sway_container *old_ws = container_parent(current, C_WORKSPACE); 140 free(ws_name);
100 struct sway_container *destination = seat_get_focus_inactive( 141 ws_name = strdup(prev_workspace_name);
101 config->handler_context.seat, ws); 142 ws = workspace_by_name(ws_name);
102 container_move_to(current, destination); 143 }
103 struct sway_container *focus = seat_get_focus_inactive( 144 }
104 config->handler_context.seat, old_parent);
105 seat_set_focus_warp(config->handler_context.seat, focus, true, false);
106 container_reap_empty(old_parent);
107 container_reap_empty(destination->parent);
108
109 // TODO: Ideally we would arrange the surviving parent after reaping,
110 // but container_reap_empty does not return it, so we arrange the
111 // workspace instead.
112 arrange_windows(old_ws);
113 arrange_windows(destination->parent);
114 145
115 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 146 if (!ws) {
116 } else if (strcasecmp(argv[1], "to") == 0 147 ws = workspace_create(NULL, ws_name);
117 && strcasecmp(argv[2], "output") == 0) { 148 }
118 if (current->type == C_WORKSPACE) { 149 free(ws_name);
119 // TODO: Wrap children in a container and move that
120 return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
121 } else if (current->type != C_CONTAINER
122 && current->type != C_VIEW) {
123 return cmd_results_new(CMD_FAILURE, "move",
124 "Can only move containers and views.");
125 } 150 }
151 destination = seat_get_focus_inactive(config->handler_context.seat, ws);
152 } else if (strcasecmp(argv[1], "output") == 0) {
126 struct sway_container *source = container_parent(current, C_OUTPUT); 153 struct sway_container *source = container_parent(current, C_OUTPUT);
127 struct sway_container *destination = output_in_direction(argv[3], 154 struct sway_container *dest_output = output_in_direction(argv[2],
128 source->sway_output->wlr_output, current->x, current->y); 155 source->sway_output->wlr_output, current->x, current->y);
129 if (!destination) { 156 if (!dest_output) {
130 return cmd_results_new(CMD_FAILURE, "move workspace", 157 return cmd_results_new(CMD_FAILURE, "move workspace",
131 "Can't find output with name/direction '%s'", argv[3]); 158 "Can't find output with name/direction '%s'", argv[2]);
132 } 159 }
133 struct sway_container *focus = seat_get_focus_inactive( 160 destination = seat_get_focus_inactive(
134 config->handler_context.seat, destination); 161 config->handler_context.seat, dest_output);
135 if (!focus) { 162 if (!destination) {
136 // We've never been to this output before 163 // We've never been to this output before
137 focus = destination->children->items[0]; 164 destination = dest_output->children->items[0];
138 } 165 }
139 struct sway_container *old_parent = current->parent; 166 } else if (strcasecmp(argv[1], "mark") == 0) {
140 struct sway_container *old_ws = container_parent(current, C_WORKSPACE); 167 struct sway_view *dest_view = view_find_mark(argv[2]);
141 container_move_to(current, focus); 168 if (dest_view == NULL) {
142 seat_set_focus_warp(config->handler_context.seat, old_parent, true, false); 169 return cmd_results_new(CMD_FAILURE, "move",
143 container_reap_empty(old_parent); 170 "Mark '%s' not found", argv[2]);
144 container_reap_empty(focus->parent); 171 }
145 172 destination = dest_view->swayc;
146 // TODO: Ideally we would arrange the surviving parent after reaping, 173 } else {
147 // but container_reap_empty does not return it, so we arrange the 174 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
148 // workspace instead.
149 arrange_windows(old_ws);
150 arrange_windows(focus->parent);
151
152 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
153 } 175 }
154 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 176
177 // move container, arrange windows and return focus
178 container_move_to(current, destination);
179 struct sway_container *focus =
180 seat_get_focus_inactive(config->handler_context.seat, old_parent);
181 seat_set_focus_warp(config->handler_context.seat, focus, true, false);
182 container_reap_empty(old_parent);
183 container_reap_empty(destination->parent);
184
185 // TODO: Ideally we would arrange the surviving parent after reaping,
186 // but container_reap_empty does not return it, so we arrange the
187 // workspace instead.
188 arrange_windows(old_ws);
189 arrange_windows(destination->parent);
190
191 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
155} 192}
156 193
157static void workspace_move_to_output(struct sway_container *workspace, 194static void workspace_move_to_output(struct sway_container *workspace,
@@ -197,20 +234,29 @@ static void workspace_move_to_output(struct sway_container *workspace,
197static struct cmd_results *cmd_move_workspace(struct sway_container *current, 234static struct cmd_results *cmd_move_workspace(struct sway_container *current,
198 int argc, char **argv) { 235 int argc, char **argv) {
199 struct cmd_results *error = NULL; 236 struct cmd_results *error = NULL;
200 if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { 237 if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) {
201 return error; 238 return error;
202 } else if (strcasecmp(argv[1], "to") != 0 239 }
203 || strcasecmp(argv[2], "output") != 0) { 240
241 while (strcasecmp(argv[1], "to") == 0) {
242 if (--argc < 3) {
243 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
244 }
245 ++argv;
246 }
247
248 if (strcasecmp(argv[1], "output") != 0) {
204 return cmd_results_new(CMD_INVALID, "move", expected_syntax); 249 return cmd_results_new(CMD_INVALID, "move", expected_syntax);
205 } 250 }
251
206 struct sway_container *source = container_parent(current, C_OUTPUT); 252 struct sway_container *source = container_parent(current, C_OUTPUT);
207 int center_x = current->width / 2 + current->x, 253 int center_x = current->width / 2 + current->x,
208 center_y = current->height / 2 + current->y; 254 center_y = current->height / 2 + current->y;
209 struct sway_container *destination = output_in_direction(argv[3], 255 struct sway_container *destination = output_in_direction(argv[2],
210 source->sway_output->wlr_output, center_x, center_y); 256 source->sway_output->wlr_output, center_x, center_y);
211 if (!destination) { 257 if (!destination) {
212 return cmd_results_new(CMD_FAILURE, "move workspace", 258 return cmd_results_new(CMD_FAILURE, "move workspace",
213 "Can't find output with name/direction '%s'", argv[3]); 259 "Can't find output with name/direction '%s'", argv[2]);
214 } 260 }
215 if (current->type != C_WORKSPACE) { 261 if (current->type != C_WORKSPACE) {
216 current = container_parent(current, C_WORKSPACE); 262 current = container_parent(current, C_WORKSPACE);
@@ -284,9 +330,9 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
284 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 330 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
285} 331}
286 332
287static const char* expected_position_syntax = 333static const char *expected_position_syntax =
288 "Expected 'move [absolute] position <x> <y>' or " 334 "Expected 'move [absolute] position <x> [px] <y> [px]' or "
289 "'move [absolute] position mouse'"; 335 "'move [absolute] position center|mouse'";
290 336
291static struct cmd_results *move_to_position(struct sway_container *container, 337static struct cmd_results *move_to_position(struct sway_container *container,
292 int argc, char **argv) { 338 int argc, char **argv) {
@@ -321,10 +367,18 @@ static struct cmd_results *move_to_position(struct sway_container *container,
321 double ly = seat->cursor->cursor->y - container->height / 2; 367 double ly = seat->cursor->cursor->y - container->height / 2;
322 container_floating_move_to(container, lx, ly); 368 container_floating_move_to(container, lx, ly);
323 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 369 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
370 } else if (strcmp(argv[0], "center") == 0) {
371 struct sway_container *ws = container_parent(container, C_WORKSPACE);
372 double lx = ws->x + (ws->width - container->width) / 2;
373 double ly = ws->y + (ws->height - container->height) / 2;
374 container_floating_move_to(container, lx, ly);
375 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
324 } 376 }
325 if (argc != 2) { 377
378 if (argc < 2) {
326 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); 379 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
327 } 380 }
381
328 double lx, ly; 382 double lx, ly;
329 char *inv; 383 char *inv;
330 lx = (double)strtol(argv[0], &inv, 10); 384 lx = (double)strtol(argv[0], &inv, 10);
@@ -332,11 +386,22 @@ static struct cmd_results *move_to_position(struct sway_container *container,
332 return cmd_results_new(CMD_FAILURE, "move", 386 return cmd_results_new(CMD_FAILURE, "move",
333 "Invalid position specified"); 387 "Invalid position specified");
334 } 388 }
389 if (strcmp(argv[1], "px") == 0) {
390 --argc;
391 ++argv;
392 }
393
394 if (argc > 3) {
395 return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
396 }
397
335 ly = (double)strtol(argv[1], &inv, 10); 398 ly = (double)strtol(argv[1], &inv, 10);
336 if (*inv != '\0' && strcasecmp(inv, "px") != 0) { 399 if ((*inv != '\0' && strcasecmp(inv, "px") != 0) ||
400 (argc == 3 && strcmp(argv[2], "px") != 0)) {
337 return cmd_results_new(CMD_FAILURE, "move", 401 return cmd_results_new(CMD_FAILURE, "move",
338 "Invalid position specified"); 402 "Invalid position specified");
339 } 403 }
404
340 container_floating_move_to(container, lx, ly); 405 container_floating_move_to(container, lx, ly);
341 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 406 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
342} 407}
@@ -384,8 +449,11 @@ struct cmd_results *cmd_move(int argc, char **argv) {
384 return move_in_direction(current, MOVE_UP, argc, argv); 449 return move_in_direction(current, MOVE_UP, argc, argv);
385 } else if (strcasecmp(argv[0], "down") == 0) { 450 } else if (strcasecmp(argv[0], "down") == 0) {
386 return move_in_direction(current, MOVE_DOWN, argc, argv); 451 return move_in_direction(current, MOVE_DOWN, argc, argv);
387 } else if (strcasecmp(argv[0], "container") == 0 452 } else if ((strcasecmp(argv[0], "container") == 0
388 || strcasecmp(argv[0], "window") == 0) { 453 || strcasecmp(argv[0], "window") == 0) ||
454 (strcasecmp(argv[0], "--no-auto-back-and-forth") &&
455 (strcasecmp(argv[0], "container") == 0
456 || strcasecmp(argv[0], "window") == 0))) {
389 return cmd_move_container(current, argc, argv); 457 return cmd_move_container(current, argc, argv);
390 } else if (strcasecmp(argv[0], "workspace") == 0) { 458 } else if (strcasecmp(argv[0], "workspace") == 0) {
391 return cmd_move_workspace(current, argc, argv); 459 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/decoration.c b/sway/decoration.c
new file mode 100644
index 00000000..0e3e67ac
--- /dev/null
+++ b/sway/decoration.c
@@ -0,0 +1,71 @@
1#include <stdlib.h>
2#include "sway/decoration.h"
3#include "sway/server.h"
4#include "sway/tree/view.h"
5#include "log.h"
6
7static void server_decoration_handle_destroy(struct wl_listener *listener,
8 void *data) {
9 struct sway_server_decoration *deco =
10 wl_container_of(listener, deco, destroy);
11 wl_list_remove(&deco->destroy.link);
12 wl_list_remove(&deco->mode.link);
13 wl_list_remove(&deco->link);
14 free(deco);
15}
16
17static void server_decoration_handle_mode(struct wl_listener *listener,
18 void *data) {
19 struct sway_server_decoration *deco =
20 wl_container_of(listener, deco, mode);
21 struct sway_view *view =
22 view_from_wlr_surface(deco->wlr_server_decoration->surface);
23 if (view == NULL || view->surface != deco->wlr_server_decoration->surface) {
24 return;
25 }
26
27 switch (view->type) {
28 case SWAY_VIEW_XDG_SHELL_V6:;
29 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
30 (struct sway_xdg_shell_v6_view *)view;
31 xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode;
32 break;
33 case SWAY_VIEW_XDG_SHELL:;
34 struct sway_xdg_shell_view *xdg_shell_view =
35 (struct sway_xdg_shell_view *)view;
36 xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode;
37 break;
38 default:
39 break;
40 }
41}
42
43void handle_server_decoration(struct wl_listener *listener, void *data) {
44 struct wlr_server_decoration *wlr_deco = data;
45
46 struct sway_server_decoration *deco = calloc(1, sizeof(*deco));
47 if (deco == NULL) {
48 return;
49 }
50
51 deco->wlr_server_decoration = wlr_deco;
52
53 wl_signal_add(&wlr_deco->events.destroy, &deco->destroy);
54 deco->destroy.notify = server_decoration_handle_destroy;
55
56 wl_signal_add(&wlr_deco->events.mode, &deco->mode);
57 deco->mode.notify = server_decoration_handle_mode;
58
59 wl_list_insert(&server.decorations, &deco->link);
60}
61
62struct sway_server_decoration *decoration_from_surface(
63 struct wlr_surface *surface) {
64 struct sway_server_decoration *deco;
65 wl_list_for_each(deco, &server.decorations, link) {
66 if (deco->wlr_server_decoration->surface == surface) {
67 return deco;
68 }
69 }
70 return NULL;
71}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index b364663d..3b73f99c 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -6,6 +6,7 @@
6#include <wlr/types/wlr_xdg_shell.h> 6#include <wlr/types/wlr_xdg_shell.h>
7#include <wlr/util/edges.h> 7#include <wlr/util/edges.h>
8#include "log.h" 8#include "log.h"
9#include "sway/decoration.h"
9#include "sway/input/input-manager.h" 10#include "sway/input/input-manager.h"
10#include "sway/input/seat.h" 11#include "sway/input/seat.h"
11#include "sway/server.h" 12#include "sway/server.h"
@@ -170,6 +171,15 @@ static bool wants_floating(struct sway_view *view) {
170 || toplevel->parent; 171 || toplevel->parent;
171} 172}
172 173
174static bool has_client_side_decorations(struct sway_view *view) {
175 struct sway_xdg_shell_view *xdg_shell_view =
176 xdg_shell_view_from_view(view);
177 if (xdg_shell_view == NULL) {
178 return true;
179 }
180 return xdg_shell_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER;
181}
182
173static void for_each_surface(struct sway_view *view, 183static void for_each_surface(struct sway_view *view,
174 wlr_surface_iterator_func_t iterator, void *user_data) { 184 wlr_surface_iterator_func_t iterator, void *user_data) {
175 if (xdg_shell_view_from_view(view) == NULL) { 185 if (xdg_shell_view_from_view(view) == NULL) {
@@ -226,6 +236,7 @@ static const struct sway_view_impl view_impl = {
226 .set_tiled = set_tiled, 236 .set_tiled = set_tiled,
227 .set_fullscreen = set_fullscreen, 237 .set_fullscreen = set_fullscreen,
228 .wants_floating = wants_floating, 238 .wants_floating = wants_floating,
239 .has_client_side_decorations = has_client_side_decorations,
229 .for_each_surface = for_each_surface, 240 .for_each_surface = for_each_surface,
230 .for_each_popup = for_each_popup, 241 .for_each_popup = for_each_popup,
231 .close = _close, 242 .close = _close,
@@ -357,6 +368,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
357 view->natural_height = view->wlr_xdg_surface->surface->current.height; 368 view->natural_height = view->wlr_xdg_surface->surface->current.height;
358 } 369 }
359 370
371 struct sway_server_decoration *deco =
372 decoration_from_surface(xdg_surface->surface);
373 if (deco != NULL) {
374 xdg_shell_view->deco_mode = deco->wlr_server_decoration->mode;
375 } else {
376 xdg_shell_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
377 }
378
360 view_map(view, view->wlr_xdg_surface->surface); 379 view_map(view, view->wlr_xdg_surface->surface);
361 380
362 if (xdg_surface->toplevel->client_pending.fullscreen) { 381 if (xdg_surface->toplevel->client_pending.fullscreen) {
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index ffea03ad..a947fb35 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -4,14 +4,15 @@
4#include <stdlib.h> 4#include <stdlib.h>
5#include <wayland-server.h> 5#include <wayland-server.h>
6#include <wlr/types/wlr_xdg_shell_v6.h> 6#include <wlr/types/wlr_xdg_shell_v6.h>
7#include "log.h"
8#include "sway/decoration.h"
9#include "sway/input/input-manager.h"
10#include "sway/input/seat.h"
7#include "sway/server.h" 11#include "sway/server.h"
8#include "sway/tree/arrange.h" 12#include "sway/tree/arrange.h"
9#include "sway/tree/container.h" 13#include "sway/tree/container.h"
10#include "sway/tree/layout.h" 14#include "sway/tree/layout.h"
11#include "sway/tree/view.h" 15#include "sway/tree/view.h"
12#include "sway/input/seat.h"
13#include "sway/input/input-manager.h"
14#include "log.h"
15 16
16static const struct sway_view_child_impl popup_impl; 17static const struct sway_view_child_impl popup_impl;
17 18
@@ -166,6 +167,15 @@ static bool wants_floating(struct sway_view *view) {
166 || toplevel->parent; 167 || toplevel->parent;
167} 168}
168 169
170static bool has_client_side_decorations(struct sway_view *view) {
171 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
172 xdg_shell_v6_view_from_view(view);
173 if (xdg_shell_v6_view == NULL) {
174 return true;
175 }
176 return xdg_shell_v6_view->deco_mode != WLR_SERVER_DECORATION_MANAGER_MODE_SERVER;
177}
178
169static void for_each_surface(struct sway_view *view, 179static void for_each_surface(struct sway_view *view,
170 wlr_surface_iterator_func_t iterator, void *user_data) { 180 wlr_surface_iterator_func_t iterator, void *user_data) {
171 if (xdg_shell_v6_view_from_view(view) == NULL) { 181 if (xdg_shell_v6_view_from_view(view) == NULL) {
@@ -223,6 +233,7 @@ static const struct sway_view_impl view_impl = {
223 .set_tiled = set_tiled, 233 .set_tiled = set_tiled,
224 .set_fullscreen = set_fullscreen, 234 .set_fullscreen = set_fullscreen,
225 .wants_floating = wants_floating, 235 .wants_floating = wants_floating,
236 .has_client_side_decorations = has_client_side_decorations,
226 .for_each_surface = for_each_surface, 237 .for_each_surface = for_each_surface,
227 .for_each_popup = for_each_popup, 238 .for_each_popup = for_each_popup,
228 .close = _close, 239 .close = _close,
@@ -353,6 +364,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
353 view->natural_height = view->wlr_xdg_surface_v6->surface->current.height; 364 view->natural_height = view->wlr_xdg_surface_v6->surface->current.height;
354 } 365 }
355 366
367 struct sway_server_decoration *deco =
368 decoration_from_surface(xdg_surface->surface);
369 if (deco != NULL) {
370 xdg_shell_v6_view->deco_mode = deco->wlr_server_decoration->mode;
371 } else {
372 xdg_shell_v6_view->deco_mode = WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
373 }
374
356 view_map(view, view->wlr_xdg_surface_v6->surface); 375 view_map(view, view->wlr_xdg_surface_v6->surface);
357 376
358 if (xdg_surface->toplevel->client_pending.fullscreen) { 377 if (xdg_surface->toplevel->client_pending.fullscreen) {
diff --git a/sway/meson.build b/sway/meson.build
index c18fb6e2..2a457270 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -1,13 +1,14 @@
1sway_sources = files( 1sway_sources = files(
2 'main.c',
3 'server.c',
4 'commands.c', 2 'commands.c',
5 'config.c', 3 'config.c',
6 'criteria.c', 4 'criteria.c',
7 'debug-tree.c', 5 'debug-tree.c',
6 'decoration.c',
8 'ipc-json.c', 7 'ipc-json.c',
9 'ipc-server.c', 8 'ipc-server.c',
9 'main.c',
10 'security.c', 10 'security.c',
11 'server.c',
11 'swaynag.c', 12 'swaynag.c',
12 13
13 'desktop/desktop.c', 14 'desktop/desktop.c',
diff --git a/sway/server.c b/sway/server.c
index e8755360..e8dc63be 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -19,7 +19,6 @@
19#include <wlr/types/wlr_xcursor_manager.h> 19#include <wlr/types/wlr_xcursor_manager.h>
20#include <wlr/types/wlr_xdg_output.h> 20#include <wlr/types/wlr_xdg_output.h>
21#include <wlr/util/log.h> 21#include <wlr/util/log.h>
22// TODO WLR: make Xwayland optional
23#include "list.h" 22#include "list.h"
24#include "sway/config.h" 23#include "sway/config.h"
25#include "sway/desktop/idle_inhibit_v1.h" 24#include "sway/desktop/idle_inhibit_v1.h"
@@ -85,7 +84,6 @@ bool server_init(struct sway_server *server) {
85 &server->xdg_shell_surface); 84 &server->xdg_shell_surface);
86 server->xdg_shell_surface.notify = handle_xdg_shell_surface; 85 server->xdg_shell_surface.notify = handle_xdg_shell_surface;
87 86
88 // TODO make xwayland optional
89#ifdef HAVE_XWAYLAND 87#ifdef HAVE_XWAYLAND
90 server->xwayland.wlr_xwayland = 88 server->xwayland.wlr_xwayland =
91 wlr_xwayland_create(server->wl_display, server->compositor, true); 89 wlr_xwayland_create(server->wl_display, server->compositor, true);
@@ -109,11 +107,15 @@ bool server_init(struct sway_server *server) {
109 } 107 }
110#endif 108#endif
111 109
112 // TODO: Integration with sway borders 110 server->server_decoration_manager =
113 struct wlr_server_decoration_manager *deco_manager =
114 wlr_server_decoration_manager_create(server->wl_display); 111 wlr_server_decoration_manager_create(server->wl_display);
115 wlr_server_decoration_manager_set_default_mode( 112 wlr_server_decoration_manager_set_default_mode(
116 deco_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); 113 server->server_decoration_manager,
114 WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
115 wl_signal_add(&server->server_decoration_manager->events.new_decoration,
116 &server->server_decoration);
117 server->server_decoration.notify = handle_server_decoration;
118 wl_list_init(&server->decorations);
117 119
118 wlr_linux_dmabuf_v1_create(server->wl_display, renderer); 120 wlr_linux_dmabuf_v1_create(server->wl_display, renderer);
119 wlr_export_dmabuf_manager_v1_create(server->wl_display); 121 wlr_export_dmabuf_manager_v1_create(server->wl_display);
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 9485e675..20815654 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -843,7 +843,7 @@ struct sway_container *container_split(struct sway_container *child,
843 } 843 }
844 if (child->type == C_WORKSPACE && child->children->length == 0) { 844 if (child->type == C_WORKSPACE && child->children->length == 0) {
845 // Special case: this just behaves like splitt 845 // Special case: this just behaves like splitt
846 child->prev_layout = child->layout; 846 child->prev_split_layout = child->layout;
847 child->layout = layout; 847 child->layout = layout;
848 return child; 848 return child;
849 } 849 }
@@ -854,7 +854,7 @@ struct sway_container *container_split(struct sway_container *child,
854 854
855 remove_gaps(child); 855 remove_gaps(child);
856 856
857 cont->prev_layout = L_NONE; 857 cont->prev_split_layout = L_NONE;
858 cont->width = child->width; 858 cont->width = child->width;
859 cont->height = child->height; 859 cont->height = child->height;
860 cont->x = child->x; 860 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);