cmd_move: allow for all i3 syntax options

This modifies cmd_move to allow for the syntax options allowed by i3.
The following syntaxes are supported:
- `move left|right|up|down [<amount> [px]]`
- `move [--no-auto-back-and-forth] [window|container] [to] workspace
   <name>|next|prev|next_on_output|prev_on_output|current|number <num>`
- `move [window|container] [to] output <name/id>|left|right|up|down`
- `move [window|container] [to] mark <mark>`
- `move workspace to [output] <name/id>|left|right|up|down`
- `move [window|container] [to] [absolute] position <x> [px] <y> [px]`
- `move [window|container] [to] [absolute] position center`
- `move [window|container] [to] position mouse|cursor|pointer`

This also allows retains the following syntax option that is not
supported by i3, but is supported in sway 1.0:
- `move workspace [to] output <name/id>|left|right|up|down`

The changes are:
- `window` and `container` are now optional
- `output` is now optional for `move workspace` when `to` is given

There is also stricter command checking now. If `absolute` or
`--no-auto-back-and-forth` are given for commands that do not support
them, it will be treated as invalid syntax instead of being silently
ignored.
master
Brian Ashworth 6 years ago committed by Drew DeVault
parent 5e925f06e0
commit 09595da0bc

@ -377,10 +377,11 @@ static bool container_move_in_direction(struct sway_container *container,
return false; return false;
} }
static struct cmd_results *cmd_move_container(int argc, char **argv) { static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if ((error = checkarg(argc, "move container/window", if ((error = checkarg(argc, "move container/window",
EXPECTED_AT_LEAST, 3))) { EXPECTED_AT_LEAST, 2))) {
return error; return error;
} }
@ -400,29 +401,6 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
"Can't move fullscreen global container"); "Can't move fullscreen global container");
} }
bool no_auto_back_and_forth = false;
while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
no_auto_back_and_forth = true;
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, expected_syntax);
}
++argv;
}
while (strcasecmp(argv[1], "--no-auto-back-and-forth") == 0) {
no_auto_back_and_forth = true;
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, expected_syntax);
}
argv++;
}
while (strcasecmp(argv[1], "to") == 0) {
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, expected_syntax);
}
argv++;
}
struct sway_seat *seat = config->handler_context.seat; struct sway_seat *seat = config->handler_context.seat;
struct sway_container *old_parent = container->parent; struct sway_container *old_parent = container->parent;
struct sway_workspace *old_ws = container->workspace; struct sway_workspace *old_ws = container->workspace;
@ -430,18 +408,18 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
struct sway_node *destination = NULL; struct sway_node *destination = NULL;
// determine destination // determine destination
if (strcasecmp(argv[1], "workspace") == 0) { if (strcasecmp(argv[0], "workspace") == 0) {
// move container to workspace x // move container to workspace x
struct sway_workspace *ws = NULL; struct sway_workspace *ws = NULL;
char *ws_name = NULL; char *ws_name = NULL;
if (strcasecmp(argv[2], "next") == 0 || if (strcasecmp(argv[1], "next") == 0 ||
strcasecmp(argv[2], "prev") == 0 || strcasecmp(argv[1], "prev") == 0 ||
strcasecmp(argv[2], "next_on_output") == 0 || strcasecmp(argv[1], "next_on_output") == 0 ||
strcasecmp(argv[2], "prev_on_output") == 0 || strcasecmp(argv[1], "prev_on_output") == 0 ||
strcasecmp(argv[2], "current") == 0) { strcasecmp(argv[1], "current") == 0) {
ws = workspace_by_name(argv[2]); ws = workspace_by_name(argv[1]);
} else if (strcasecmp(argv[2], "back_and_forth") == 0) { } else if (strcasecmp(argv[1], "back_and_forth") == 0) {
if (!(ws = workspace_by_name(argv[2]))) { if (!(ws = workspace_by_name(argv[1]))) {
if (seat->prev_workspace_name) { if (seat->prev_workspace_name) {
ws_name = strdup(seat->prev_workspace_name); ws_name = strdup(seat->prev_workspace_name);
} else { } else {
@ -450,19 +428,19 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
} }
} }
} else { } else {
if (strcasecmp(argv[2], "number") == 0) { if (strcasecmp(argv[1], "number") == 0) {
// move "container to workspace number x" // move [window|container] [to] "workspace number x"
if (argc < 4) { if (argc < 3) {
return cmd_results_new(CMD_INVALID, expected_syntax); return cmd_results_new(CMD_INVALID, expected_syntax);
} }
if (!isdigit(argv[3][0])) { if (!isdigit(argv[2][0])) {
return cmd_results_new(CMD_INVALID, return cmd_results_new(CMD_INVALID,
"Invalid workspace number '%s'", argv[3]); "Invalid workspace number '%s'", argv[2]);
} }
ws_name = join_args(argv + 3, argc - 3); ws_name = join_args(argv + 2, argc - 2);
ws = workspace_by_number(ws_name); ws = workspace_by_number(ws_name);
} else { } else {
ws_name = join_args(argv + 2, argc - 2); ws_name = join_args(argv + 1, argc - 1);
ws = workspace_by_name(ws_name); ws = workspace_by_name(ws_name);
} }
@ -497,19 +475,19 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
free(ws_name); free(ws_name);
struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws); struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws);
destination = dst ? &dst->node : &ws->node; destination = dst ? &dst->node : &ws->node;
} else if (strcasecmp(argv[1], "output") == 0) { } else if (strcasecmp(argv[0], "output") == 0) {
struct sway_output *new_output = output_in_direction(argv[2], struct sway_output *new_output = output_in_direction(argv[1],
old_output, container->x, container->y); old_output, container->x, container->y);
if (!new_output) { if (!new_output) {
return cmd_results_new(CMD_FAILURE, return cmd_results_new(CMD_FAILURE,
"Can't find output with name/direction '%s'", argv[2]); "Can't find output with name/direction '%s'", argv[1]);
} }
destination = seat_get_focus_inactive(seat, &new_output->node); destination = seat_get_focus_inactive(seat, &new_output->node);
} else if (strcasecmp(argv[1], "mark") == 0) { } else if (strcasecmp(argv[0], "mark") == 0) {
struct sway_container *dest_con = container_find_mark(argv[2]); struct sway_container *dest_con = container_find_mark(argv[1]);
if (dest_con == NULL) { if (dest_con == NULL) {
return cmd_results_new(CMD_FAILURE, return cmd_results_new(CMD_FAILURE,
"Mark '%s' not found", argv[2]); "Mark '%s' not found", argv[1]);
} }
destination = &dest_con->node; destination = &dest_con->node;
} else { } else {
@ -635,19 +613,17 @@ static void workspace_move_to_output(struct sway_workspace *workspace,
static struct cmd_results *cmd_move_workspace(int argc, char **argv) { static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 1))) {
return error; return error;
} }
while (strcasecmp(argv[1], "to") == 0) { if (strcasecmp(argv[0], "output") == 0) {
if (--argc < 3) { --argc; ++argv;
return cmd_results_new(CMD_INVALID, expected_syntax);
}
++argv;
} }
if (strcasecmp(argv[1], "output") != 0) { if (!argc) {
return cmd_results_new(CMD_INVALID, expected_syntax); return cmd_results_new(CMD_INVALID,
"Expected 'move workspace to [output] <output>'");
} }
struct sway_workspace *workspace = config->handler_context.workspace; struct sway_workspace *workspace = config->handler_context.workspace;
@ -658,11 +634,11 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
struct sway_output *old_output = workspace->output; struct sway_output *old_output = workspace->output;
int center_x = workspace->width / 2 + workspace->x, int center_x = workspace->width / 2 + workspace->x,
center_y = workspace->height / 2 + workspace->y; center_y = workspace->height / 2 + workspace->y;
struct sway_output *new_output = output_in_direction(argv[2], struct sway_output *new_output = output_in_direction(argv[0],
old_output, center_x, center_y); old_output, center_x, center_y);
if (!new_output) { if (!new_output) {
return cmd_results_new(CMD_FAILURE, return cmd_results_new(CMD_FAILURE,
"Can't find output with name/direction '%s'", argv[2]); "Can't find output with name/direction '%s'", argv[0]);
} }
workspace_move_to_output(workspace, new_output); workspace_move_to_output(workspace, new_output);
@ -675,9 +651,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
static struct cmd_results *cmd_move_in_direction( static struct cmd_results *cmd_move_in_direction(
enum wlr_direction direction, int argc, char **argv) { enum wlr_direction direction, int argc, char **argv) {
int move_amt = 10; int move_amt = 10;
if (argc > 1) { if (argc) {
char *inv; char *inv;
move_amt = (int)strtol(argv[1], &inv, 10); move_amt = (int)strtol(argv[0], &inv, 10);
if (*inv != '\0' && strcasecmp(inv, "px") != 0) { if (*inv != '\0' && strcasecmp(inv, "px") != 0) {
return cmd_results_new(CMD_FAILURE, "Invalid distance specified"); return cmd_results_new(CMD_FAILURE, "Invalid distance specified");
} }
@ -769,7 +745,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
} }
if (!argc) { if (!argc) {
return cmd_results_new(CMD_FAILURE, expected_position_syntax); return cmd_results_new(CMD_INVALID, expected_position_syntax);
} }
bool absolute = false; bool absolute = false;
@ -779,17 +755,20 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
++argv; ++argv;
} }
if (!argc) { if (!argc) {
return cmd_results_new(CMD_FAILURE, expected_position_syntax); return cmd_results_new(CMD_INVALID, expected_position_syntax);
} }
if (strcmp(argv[0], "position") == 0) { if (strcmp(argv[0], "position") == 0) {
--argc; --argc;
++argv; ++argv;
} }
if (!argc) { if (!argc) {
return cmd_results_new(CMD_FAILURE, expected_position_syntax); return cmd_results_new(CMD_INVALID, expected_position_syntax);
} }
if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 ||
strcmp(argv[0], "pointer") == 0) { strcmp(argv[0], "pointer") == 0) {
if (absolute) {
return cmd_results_new(CMD_INVALID, expected_position_syntax);
}
struct sway_seat *seat = config->handler_context.seat; struct sway_seat *seat = config->handler_context.seat;
if (!seat->cursor) { if (!seat->cursor) {
return cmd_results_new(CMD_FAILURE, "No cursor device"); return cmd_results_new(CMD_FAILURE, "No cursor device");
@ -875,6 +854,18 @@ static struct cmd_results *cmd_move_to_scratchpad(void) {
return cmd_results_new(CMD_SUCCESS, NULL); return cmd_results_new(CMD_SUCCESS, NULL);
} }
static const char expected_full_syntax[] = "Expected "
"'move left|right|up|down [<amount> [px]]'"
" or 'move [--no-auto-back-and-forth] [window|container] [to] workspace"
" <name>|next|prev|next_on_output|prev_on_output|current|(number <num>)'"
" or 'move [window|container] [to] output <name/id>|left|right|up|down'"
" or 'move [window|container] [to] mark <mark>'"
" or 'move [window|container] [to] scratchpad'"
" or 'move workspace to [output] <name/id>|left|right|up|down'"
" or 'move [window|container] [to] [absolute] position <x> [px] <y> [px]'"
" or 'move [window|container] [to] [absolute] position center'"
" or 'move [window|container] [to] position mouse|cursor|pointer'";
struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *cmd_move(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
@ -886,31 +877,55 @@ struct cmd_results *cmd_move(int argc, char **argv) {
} }
if (strcasecmp(argv[0], "left") == 0) { if (strcasecmp(argv[0], "left") == 0) {
return cmd_move_in_direction(WLR_DIRECTION_LEFT, argc, argv); return cmd_move_in_direction(WLR_DIRECTION_LEFT, --argc, ++argv);
} else if (strcasecmp(argv[0], "right") == 0) { } else if (strcasecmp(argv[0], "right") == 0) {
return cmd_move_in_direction(WLR_DIRECTION_RIGHT, argc, argv); return cmd_move_in_direction(WLR_DIRECTION_RIGHT, --argc, ++argv);
} else if (strcasecmp(argv[0], "up") == 0) { } else if (strcasecmp(argv[0], "up") == 0) {
return cmd_move_in_direction(WLR_DIRECTION_UP, argc, argv); return cmd_move_in_direction(WLR_DIRECTION_UP, --argc, ++argv);
} else if (strcasecmp(argv[0], "down") == 0) { } else if (strcasecmp(argv[0], "down") == 0) {
return cmd_move_in_direction(WLR_DIRECTION_DOWN, argc, argv); return cmd_move_in_direction(WLR_DIRECTION_DOWN, --argc, ++argv);
} else if ((strcasecmp(argv[0], "container") == 0 } else if (strcasecmp(argv[0], "workspace") == 0 && argc >= 2
|| strcasecmp(argv[0], "window") == 0) || && (strcasecmp(argv[1], "to") == 0 ||
(strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2 strcasecmp(argv[1], "output") == 0)) {
&& (strcasecmp(argv[1], "container") == 0 argc -= 2; argv += 2;
|| strcasecmp(argv[1], "window") == 0))) {
return cmd_move_container(argc, argv);
} else if (strcasecmp(argv[0], "workspace") == 0) {
return cmd_move_workspace(argc, argv); return cmd_move_workspace(argc, argv);
} else if (strcasecmp(argv[0], "scratchpad") == 0 }
|| (strcasecmp(argv[0], "to") == 0 && argc == 2
&& strcasecmp(argv[1], "scratchpad") == 0)) { bool no_auto_back_and_forth = false;
if (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
no_auto_back_and_forth = true;
--argc; ++argv;
}
if (strcasecmp(argv[0], "window") == 0 ||
strcasecmp(argv[0], "container") == 0) {
--argc; ++argv;
}
if (strcasecmp(argv[0], "to") == 0) {
--argc; ++argv;
}
if (!argc) {
return cmd_results_new(CMD_INVALID, expected_full_syntax);
}
// Only `move [window|container] [to] workspace` supports
// `--no-auto-back-and-forth` so treat others as invalid syntax
if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) {
return cmd_results_new(CMD_INVALID, expected_full_syntax);
}
if (strcasecmp(argv[0], "workspace") == 0 ||
strcasecmp(argv[0], "output") == 0 ||
strcasecmp(argv[0], "mark") == 0) {
return cmd_move_container(no_auto_back_and_forth, argc, argv);
} else if (strcasecmp(argv[0], "scratchpad") == 0) {
return cmd_move_to_scratchpad(); return cmd_move_to_scratchpad();
} else if (strcasecmp(argv[0], "position") == 0) { } else if (strcasecmp(argv[0], "position") == 0 ||
return cmd_move_to_position(argc, argv); (argc > 1 && strcasecmp(argv[0], "absolute") == 0 &&
} else if (strcasecmp(argv[0], "absolute") == 0) { strcasecmp(argv[1], "position") == 0)) {
return cmd_move_to_position(argc, argv); return cmd_move_to_position(argc, argv);
} else {
return cmd_results_new(CMD_INVALID, expected_syntax);
} }
return cmd_results_new(CMD_SUCCESS, NULL); return cmd_results_new(CMD_INVALID, expected_full_syntax);
} }

@ -187,34 +187,46 @@ set|plus|minus <amount>
*move* position cursor|mouse|pointer *move* position cursor|mouse|pointer
Moves the focused container to be centered on the cursor. Moves the focused container to be centered on the cursor.
*move* container|window [to] mark <mark> *move* [container|window] [to] mark <mark>
Moves the focused container to the specified mark. Moves the focused container to the specified mark.
*move* [--no-auto-back-and-forth] container|window [to] workspace [number] <name> *move* [--no-auto-back-and-forth] [container|window] [to] workspace [number] <name>
Moves the focused container to the specified workspace. The string "number" Moves the focused container to the specified workspace. The string "number"
is optional and is used to match a workspace with the same number, even if is optional and is used to match a workspace with the same number, even if
it has a different name. it has a different name.
*move* container|window [to] workspace prev|next|current *move* [container|window] [to] workspace prev|next|current
Moves the focused container to the previous, next or current workspace on Moves the focused container to the previous, next or current workspace on
this output, or if no workspaces remain, the previous or next output. this output, or if no workspaces remain, the previous or next output.
*move* container|window [to] workspace prev_on_output|next_on_output *move* [container|window] [to] workspace prev_on_output|next_on_output
Moves the focused container to the previous or next workspace on this Moves the focused container to the previous or next workspace on this
output, wrapping around if already at the first or last workspace. output, wrapping around if already at the first or last workspace.
*move* container|window [to] workspace back_and_forth *move* [container|window] [to] workspace back_and_forth
Moves the focused container to previously focused workspace. Moves the focused container to previously focused workspace.
*move* container|window|workspace [to] output <name> *move* [container|window] [to] output <name-or-id>
Moves the focused container or workspace to the specified output. Moves the focused container to the specified output.
*move* container|window|workspace [to] output up|right|down|left *move* [container|window] [to] output up|right|down|left
Moves the focused container or workspace to next output in the specified Moves the focused container to next output in the specified
direction. direction.
*move* [to] scratchpad *move* [container|window] [to] scratchpad
Moves the focused window to the scratchpad. Moves the focused container to the scratchpad.
*move* workspace [to] output <name-or-id>
Moves the focused workspace to the specified output.
*move* workspace to [output] <name-or-id>
Moves the focused workspace to the specified output.
*move* workspace [to] output up|right|down|left
Moves the focused workspace to next output in the specified direction.
*move* workspace to [output] up|right|down|left
Moves the focused workspace to next output in the specified direction.
*nop* <comment> *nop* <comment>
A no operation command that can be used to override default behaviour. The A no operation command that can be used to override default behaviour. The

Loading…
Cancel
Save