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.
This commit is contained in:
Brian Ashworth 2019-04-19 02:46:49 -04:00 committed by Drew DeVault
parent 5e925f06e0
commit 09595da0bc
2 changed files with 122 additions and 95 deletions

View file

@ -377,10 +377,11 @@ static bool container_move_in_direction(struct sway_container *container,
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;
if ((error = checkarg(argc, "move container/window",
EXPECTED_AT_LEAST, 3))) {
EXPECTED_AT_LEAST, 2))) {
return error;
}
@ -400,29 +401,6 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
"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_container *old_parent = container->parent;
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;
// determine destination
if (strcasecmp(argv[1], "workspace") == 0) {
if (strcasecmp(argv[0], "workspace") == 0) {
// move container to workspace x
struct sway_workspace *ws = NULL;
char *ws_name = NULL;
if (strcasecmp(argv[2], "next") == 0 ||
strcasecmp(argv[2], "prev") == 0 ||
strcasecmp(argv[2], "next_on_output") == 0 ||
strcasecmp(argv[2], "prev_on_output") == 0 ||
strcasecmp(argv[2], "current") == 0) {
ws = workspace_by_name(argv[2]);
} else if (strcasecmp(argv[2], "back_and_forth") == 0) {
if (!(ws = workspace_by_name(argv[2]))) {
if (strcasecmp(argv[1], "next") == 0 ||
strcasecmp(argv[1], "prev") == 0 ||
strcasecmp(argv[1], "next_on_output") == 0 ||
strcasecmp(argv[1], "prev_on_output") == 0 ||
strcasecmp(argv[1], "current") == 0) {
ws = workspace_by_name(argv[1]);
} else if (strcasecmp(argv[1], "back_and_forth") == 0) {
if (!(ws = workspace_by_name(argv[1]))) {
if (seat->prev_workspace_name) {
ws_name = strdup(seat->prev_workspace_name);
} else {
@ -450,19 +428,19 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
}
}
} else {
if (strcasecmp(argv[2], "number") == 0) {
// move "container to workspace number x"
if (argc < 4) {
if (strcasecmp(argv[1], "number") == 0) {
// move [window|container] [to] "workspace number x"
if (argc < 3) {
return cmd_results_new(CMD_INVALID, expected_syntax);
}
if (!isdigit(argv[3][0])) {
if (!isdigit(argv[2][0])) {
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);
} else {
ws_name = join_args(argv + 2, argc - 2);
ws_name = join_args(argv + 1, argc - 1);
ws = workspace_by_name(ws_name);
}
@ -497,19 +475,19 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) {
free(ws_name);
struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws);
destination = dst ? &dst->node : &ws->node;
} else if (strcasecmp(argv[1], "output") == 0) {
struct sway_output *new_output = output_in_direction(argv[2],
} else if (strcasecmp(argv[0], "output") == 0) {
struct sway_output *new_output = output_in_direction(argv[1],
old_output, container->x, container->y);
if (!new_output) {
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);
} else if (strcasecmp(argv[1], "mark") == 0) {
struct sway_container *dest_con = container_find_mark(argv[2]);
} else if (strcasecmp(argv[0], "mark") == 0) {
struct sway_container *dest_con = container_find_mark(argv[1]);
if (dest_con == NULL) {
return cmd_results_new(CMD_FAILURE,
"Mark '%s' not found", argv[2]);
"Mark '%s' not found", argv[1]);
}
destination = &dest_con->node;
} 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) {
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;
}
while (strcasecmp(argv[1], "to") == 0) {
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, expected_syntax);
}
++argv;
if (strcasecmp(argv[0], "output") == 0) {
--argc; ++argv;
}
if (strcasecmp(argv[1], "output") != 0) {
return cmd_results_new(CMD_INVALID, expected_syntax);
if (!argc) {
return cmd_results_new(CMD_INVALID,
"Expected 'move workspace to [output] <output>'");
}
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;
int center_x = workspace->width / 2 + workspace->x,
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);
if (!new_output) {
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);
@ -675,9 +651,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
static struct cmd_results *cmd_move_in_direction(
enum wlr_direction direction, int argc, char **argv) {
int move_amt = 10;
if (argc > 1) {
if (argc) {
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) {
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) {
return cmd_results_new(CMD_FAILURE, expected_position_syntax);
return cmd_results_new(CMD_INVALID, expected_position_syntax);
}
bool absolute = false;
@ -779,17 +755,20 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
++argv;
}
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) {
--argc;
++argv;
}
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 ||
strcmp(argv[0], "pointer") == 0) {
if (absolute) {
return cmd_results_new(CMD_INVALID, expected_position_syntax);
}
struct sway_seat *seat = config->handler_context.seat;
if (!seat->cursor) {
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);
}
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 *error = NULL;
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) {
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) {
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) {
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) {
return cmd_move_in_direction(WLR_DIRECTION_DOWN, argc, argv);
} else if ((strcasecmp(argv[0], "container") == 0
|| strcasecmp(argv[0], "window") == 0) ||
(strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2
&& (strcasecmp(argv[1], "container") == 0
|| strcasecmp(argv[1], "window") == 0))) {
return cmd_move_container(argc, argv);
} else if (strcasecmp(argv[0], "workspace") == 0) {
return cmd_move_in_direction(WLR_DIRECTION_DOWN, --argc, ++argv);
} else if (strcasecmp(argv[0], "workspace") == 0 && argc >= 2
&& (strcasecmp(argv[1], "to") == 0 ||
strcasecmp(argv[1], "output") == 0)) {
argc -= 2; argv += 2;
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)) {
return cmd_move_to_scratchpad();
} else if (strcasecmp(argv[0], "position") == 0) {
return cmd_move_to_position(argc, argv);
} else if (strcasecmp(argv[0], "absolute") == 0) {
return cmd_move_to_position(argc, argv);
} else {
return cmd_results_new(CMD_INVALID, expected_syntax);
}
return cmd_results_new(CMD_SUCCESS, NULL);
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();
} else if (strcasecmp(argv[0], "position") == 0 ||
(argc > 1 && strcasecmp(argv[0], "absolute") == 0 &&
strcasecmp(argv[1], "position") == 0)) {
return cmd_move_to_position(argc, argv);
}
return cmd_results_new(CMD_INVALID, expected_full_syntax);
}

View file

@ -187,34 +187,46 @@ set|plus|minus <amount>
*move* position cursor|mouse|pointer
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.
*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"
is optional and is used to match a workspace with the same number, even if
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
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
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.
*move* container|window|workspace [to] output <name>
Moves the focused container or workspace to the specified output.
*move* [container|window] [to] output <name-or-id>
Moves the focused container to the specified output.
*move* container|window|workspace [to] output up|right|down|left
Moves the focused container or workspace to next output in the specified
*move* [container|window] [to] output up|right|down|left
Moves the focused container to next output in the specified
direction.
*move* [to] scratchpad
Moves the focused window to the scratchpad.
*move* [container|window] [to] 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>
A no operation command that can be used to override default behaviour. The