Merge pull request #2392 from ianyfan/commands

Fix commands: criteria, layout, move, workspace
This commit is contained in:
Drew DeVault 2018-08-06 11:30:14 -04:00 committed by GitHub
commit 1a8bee68b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 381 additions and 181 deletions

View file

@ -113,7 +113,7 @@ struct sway_container {
enum sway_container_type type; enum sway_container_type type;
enum sway_container_layout layout; enum sway_container_layout layout;
enum sway_container_layout prev_layout; enum sway_container_layout prev_split_layout;
bool is_sticky; bool is_sticky;

View file

@ -315,6 +315,11 @@ void view_update_title(struct sway_view *view, bool force);
*/ */
void view_execute_criteria(struct sway_view *view); void view_execute_criteria(struct sway_view *view);
/**
* Find any view that has the given mark and return it.
*/
struct sway_view *view_find_mark(char *mark);
/** /**
* Find any view that has the given mark and remove the mark from the view. * Find any view that has the given mark and remove the mark from the view.
* Returns true if it matched a view. * Returns true if it matched a view.

View file

@ -1,6 +1,7 @@
#ifndef _SWAY_WORKSPACE_H #ifndef _SWAY_WORKSPACE_H
#define _SWAY_WORKSPACE_H #define _SWAY_WORKSPACE_H
#include <stdbool.h>
#include "sway/tree/container.h" #include "sway/tree/container.h"
struct sway_view; struct sway_view;
@ -17,7 +18,8 @@ extern char *prev_workspace_name;
char *workspace_next_name(const char *output_name); char *workspace_next_name(const char *output_name);
bool workspace_switch(struct sway_container *workspace); bool workspace_switch(struct sway_container *workspace,
bool no_auto_back_and_forth);
struct sway_container *workspace_by_number(const char* name); struct sway_container *workspace_by_number(const char* name);

View file

@ -1,3 +1,4 @@
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include "sway/commands.h" #include "sway/commands.h"
@ -5,6 +6,26 @@
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "log.h" #include "log.h"
static bool parse_layout_string(char *s, enum sway_container_layout *ptr) {
if (strcasecmp(s, "splith") == 0) {
*ptr = L_HORIZ;
} else if (strcasecmp(s, "splitv") == 0) {
*ptr = L_VERT;
} else if (strcasecmp(s, "tabbed") == 0) {
*ptr = L_TABBED;
} else if (strcasecmp(s, "stacking") == 0) {
*ptr = L_STACKED;
} else {
return false;
}
return true;
}
static const char* expected_syntax =
"Expected 'layout default|tabbed|stacking|splitv|splith' or "
"'layout toggle [split|all]' or "
"'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'";
struct cmd_results *cmd_layout(int argc, char **argv) { struct cmd_results *cmd_layout(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
@ -21,35 +42,69 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
parent = parent->parent; parent = parent->parent;
} }
if (strcasecmp(argv[0], "default") == 0) { enum sway_container_layout prev = parent->layout;
parent->layout = parent->prev_layout; bool assigned_directly = parse_layout_string(argv[0], &parent->layout);
if (parent->layout == L_NONE) { if (!assigned_directly) {
parent->layout = container_get_default_layout(parent); if (strcasecmp(argv[0], "default") == 0) {
} parent->layout = parent->prev_split_layout;
} else { } else if (strcasecmp(argv[0], "toggle") == 0) {
if (parent->layout != L_TABBED && parent->layout != L_STACKED) { if (argc == 1) {
parent->prev_layout = parent->layout; parent->layout =
} parent->layout == L_STACKED ? L_TABBED :
parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED;
if (strcasecmp(argv[0], "splith") == 0) { } else if (argc == 2) {
parent->layout = L_HORIZ; if (strcasecmp(argv[1], "all") == 0) {
} else if (strcasecmp(argv[0], "splitv") == 0) { parent->layout =
parent->layout = L_VERT; parent->layout == L_HORIZ ? L_VERT :
} else if (strcasecmp(argv[0], "tabbed") == 0) { parent->layout == L_VERT ? L_STACKED :
parent->layout = L_TABBED; parent->layout == L_STACKED ? L_TABBED : L_HORIZ;
} else if (strcasecmp(argv[0], "stacking") == 0) { } else if (strcasecmp(argv[1], "split") == 0) {
parent->layout = L_STACKED; parent->layout =
} else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { parent->layout == L_HORIZ ? L_VERT :
if (parent->layout == L_HORIZ) { parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
parent->layout = L_VERT; } else {
return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
}
} else { } else {
parent->layout = L_HORIZ; enum sway_container_layout parsed_layout;
int curr = 1;
for (; curr < argc; curr++) {
bool valid = parse_layout_string(argv[curr], &parsed_layout);
if ((valid && parsed_layout == parent->layout) ||
(strcmp(argv[curr], "split") == 0 &&
(parent->layout == L_VERT || parent->layout == L_HORIZ))) {
break;
}
}
for (int i = curr + 1; i != curr; ++i) {
// cycle round to find next valid layout
if (i >= argc) {
i = 1;
}
if (parse_layout_string(argv[i], &parent->layout)) {
break;
} else if (strcmp(argv[i], "split") == 0) {
parent->layout =
parent->layout == L_HORIZ ? L_VERT :
parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
break;
} // invalid layout strings are silently ignored
}
} }
} else {
return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
} }
} }
if (parent->layout == L_NONE) {
container_notify_subtree_changed(parent); parent->layout = container_get_default_layout(parent);
arrange_windows(parent); }
if (prev != parent->layout) {
if (prev != L_TABBED && prev != L_STACKED) {
parent->prev_split_layout = prev;
}
container_notify_subtree_changed(parent);
arrange_windows(parent);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

View file

@ -1,4 +1,5 @@
#define _XOPEN_SOURCE 500 #define _XOPEN_SOURCE 500
#include <stdbool.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
@ -16,11 +17,11 @@
#include "stringop.h" #include "stringop.h"
#include "list.h" #include "list.h"
static const char* expected_syntax = static const char *expected_syntax =
"Expected 'move <left|right|up|down> <[px] px>' or " "Expected 'move <left|right|up|down> <[px] px>' or "
"'move <container|window> to workspace <name>' or " "'move [--no-auto-back-and-forth] <container|window> [to] workspace <name>' or "
"'move <container|window|workspace> to output <name|direction>' or " "'move [--no-auto-back-and-forth] <container|window|workspace> [to] output <name|direction>' or "
"'move position mouse'"; "'move <container|window> [to] mark <mark>'";
static struct sway_container *output_in_direction(const char *direction, static struct sway_container *output_in_direction(const char *direction,
struct wlr_output *reference, int ref_lx, int ref_ly) { 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,
int argc, char **argv) { 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, 4))) { EXPECTED_AT_LEAST, 3))) {
return error; return error;
} else if (strcasecmp(argv[1], "to") == 0 }
&& strcasecmp(argv[2], "workspace") == 0) {
// move container to workspace x
if (current->type == C_WORKSPACE) {
if (current->children->length == 0) {
return cmd_results_new(CMD_FAILURE, "move",
"Can't move an empty workspace");
}
current = container_wrap_children(current);
} else if (current->type != C_CONTAINER && current->type != C_VIEW) {
return cmd_results_new(CMD_FAILURE, "move",
"Can only move containers and views.");
}
struct sway_container *ws;
char *ws_name = NULL;
if (argc == 5 && strcasecmp(argv[3], "number") == 0) {
// move "container to workspace number x"
ws_name = strdup(argv[4]);
ws = workspace_by_number(ws_name);
} else {
ws_name = join_args(argv + 3, argc - 3);
ws = workspace_by_name(ws_name);
}
if (config->auto_back_and_forth && prev_workspace_name) { if (current->type == C_WORKSPACE) {
// auto back and forth move if (current->children->length == 0) {
struct sway_container *curr_ws = container_parent(current, C_WORKSPACE); return cmd_results_new(CMD_FAILURE, "move",
if (curr_ws->name && strcmp(curr_ws->name, ws_name) == 0) { "Can't move an empty workspace");
// if target workspace is the current one }
free(ws_name); current = container_wrap_children(current);
ws_name = strdup(prev_workspace_name); } else if (current->type != C_CONTAINER && current->type != C_VIEW) {
return cmd_results_new(CMD_FAILURE, "move",
"Can only move containers and views.");
}
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, "move", 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, "move", expected_syntax);
}
argv++;
}
while (strcasecmp(argv[1], "to") == 0) {
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
argv++;
}
struct sway_container *old_parent = current->parent;
struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
struct sway_container *destination = NULL;
// determine destination
if (strcasecmp(argv[1], "workspace") == 0) {
// move container to workspace x
struct sway_container *ws;
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], "back_and_forth") == 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 (prev_workspace_name) {
ws = workspace_create(NULL, prev_workspace_name);
} else {
return cmd_results_new(CMD_FAILURE, "move",
"No workspace was previously active.");
}
}
} else {
char *ws_name = NULL;
if (strcasecmp(argv[2], "number") == 0) {
// move "container to workspace number x"
if (argc < 4) {
return cmd_results_new(CMD_INVALID, "move",
expected_syntax);
}
ws_name = strdup(argv[3]);
ws = workspace_by_number(ws_name);
} else {
ws_name = join_args(argv + 2, argc - 2);
ws = workspace_by_name(ws_name); ws = workspace_by_name(ws_name);
} }
}
if (!ws) { if (!no_auto_back_and_forth && config->auto_back_and_forth &&
ws = workspace_create(NULL, ws_name); prev_workspace_name) {
} // auto back and forth move
free(ws_name); if (old_ws->name && strcmp(old_ws->name, ws_name) == 0) {
struct sway_container *old_parent = current->parent; // if target workspace is the current one
struct sway_container *old_ws = container_parent(current, C_WORKSPACE); free(ws_name);
struct sway_container *destination = seat_get_focus_inactive( ws_name = strdup(prev_workspace_name);
config->handler_context.seat, ws); ws = workspace_by_name(ws_name);
container_move_to(current, destination); }
struct sway_container *focus = seat_get_focus_inactive( }
config->handler_context.seat, old_parent);
seat_set_focus_warp(config->handler_context.seat, focus, true, false);
container_reap_empty(old_parent);
container_reap_empty(destination->parent);
// TODO: Ideally we would arrange the surviving parent after reaping, if (!ws) {
// but container_reap_empty does not return it, so we arrange the ws = workspace_create(NULL, ws_name);
// workspace instead. }
arrange_windows(old_ws); free(ws_name);
arrange_windows(destination->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if (strcasecmp(argv[1], "to") == 0
&& strcasecmp(argv[2], "output") == 0) {
if (current->type == C_WORKSPACE) {
// TODO: Wrap children in a container and move that
return cmd_results_new(CMD_FAILURE, "move", "Unimplemented");
} else if (current->type != C_CONTAINER
&& current->type != C_VIEW) {
return cmd_results_new(CMD_FAILURE, "move",
"Can only move containers and views.");
} }
destination = seat_get_focus_inactive(config->handler_context.seat, ws);
} else if (strcasecmp(argv[1], "output") == 0) {
struct sway_container *source = container_parent(current, C_OUTPUT); struct sway_container *source = container_parent(current, C_OUTPUT);
struct sway_container *destination = output_in_direction(argv[3], struct sway_container *dest_output = output_in_direction(argv[2],
source->sway_output->wlr_output, current->x, current->y); source->sway_output->wlr_output, current->x, current->y);
if (!destination) { if (!dest_output) {
return cmd_results_new(CMD_FAILURE, "move workspace", return cmd_results_new(CMD_FAILURE, "move workspace",
"Can't find output with name/direction '%s'", argv[3]); "Can't find output with name/direction '%s'", argv[2]);
} }
struct sway_container *focus = seat_get_focus_inactive( destination = seat_get_focus_inactive(
config->handler_context.seat, destination); config->handler_context.seat, dest_output);
if (!focus) { if (!destination) {
// We've never been to this output before // We've never been to this output before
focus = destination->children->items[0]; destination = dest_output->children->items[0];
} }
struct sway_container *old_parent = current->parent; } else if (strcasecmp(argv[1], "mark") == 0) {
struct sway_container *old_ws = container_parent(current, C_WORKSPACE); struct sway_view *dest_view = view_find_mark(argv[2]);
container_move_to(current, focus); if (dest_view == NULL) {
seat_set_focus_warp(config->handler_context.seat, old_parent, true, false); return cmd_results_new(CMD_FAILURE, "move",
container_reap_empty(old_parent); "Mark '%s' not found", argv[2]);
container_reap_empty(focus->parent); }
destination = dest_view->swayc;
// TODO: Ideally we would arrange the surviving parent after reaping, } else {
// but container_reap_empty does not return it, so we arrange the return cmd_results_new(CMD_INVALID, "move", expected_syntax);
// workspace instead.
arrange_windows(old_ws);
arrange_windows(focus->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
// move container, arrange windows and return focus
container_move_to(current, destination);
struct sway_container *focus =
seat_get_focus_inactive(config->handler_context.seat, old_parent);
seat_set_focus_warp(config->handler_context.seat, focus, true, false);
container_reap_empty(old_parent);
container_reap_empty(destination->parent);
// TODO: Ideally we would arrange the surviving parent after reaping,
// but container_reap_empty does not return it, so we arrange the
// workspace instead.
arrange_windows(old_ws);
arrange_windows(destination->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static struct cmd_results *cmd_move_workspace(struct sway_container *current, static struct cmd_results *cmd_move_workspace(struct sway_container *current,
int argc, char **argv) { int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) { if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) {
return error; return error;
} else if (strcasecmp(argv[1], "to") != 0 }
|| strcasecmp(argv[2], "output") != 0) {
while (strcasecmp(argv[1], "to") == 0) {
if (--argc < 3) {
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
}
++argv;
}
if (strcasecmp(argv[1], "output") != 0) {
return cmd_results_new(CMD_INVALID, "move", expected_syntax); return cmd_results_new(CMD_INVALID, "move", expected_syntax);
} }
struct sway_container *source = container_parent(current, C_OUTPUT); struct sway_container *source = container_parent(current, C_OUTPUT);
int center_x = current->width / 2 + current->x, int center_x = current->width / 2 + current->x,
center_y = current->height / 2 + current->y; center_y = current->height / 2 + current->y;
struct sway_container *destination = output_in_direction(argv[3], struct sway_container *destination = output_in_direction(argv[2],
source->sway_output->wlr_output, center_x, center_y); source->sway_output->wlr_output, center_x, center_y);
if (!destination) { if (!destination) {
return cmd_results_new(CMD_FAILURE, "move workspace", return cmd_results_new(CMD_FAILURE, "move workspace",
"Can't find output with name/direction '%s'", argv[3]); "Can't find output with name/direction '%s'", argv[2]);
} }
if (current->type != C_WORKSPACE) { if (current->type != C_WORKSPACE) {
current = container_parent(current, C_WORKSPACE); current = container_parent(current, C_WORKSPACE);
@ -242,9 +288,9 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static const char* expected_position_syntax = static const char *expected_position_syntax =
"Expected 'move [absolute] position <x> <y>' or " "Expected 'move [absolute] position <x> [px] <y> [px]' or "
"'move [absolute] position mouse'"; "'move [absolute] position center|mouse'";
static struct cmd_results *move_to_position(struct sway_container *container, static struct cmd_results *move_to_position(struct sway_container *container,
int argc, char **argv) { int argc, char **argv) {
@ -279,10 +325,18 @@ static struct cmd_results *move_to_position(struct sway_container *container,
double ly = seat->cursor->cursor->y - container->height / 2; double ly = seat->cursor->cursor->y - container->height / 2;
container_floating_move_to(container, lx, ly); container_floating_move_to(container, lx, ly);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if (strcmp(argv[0], "center") == 0) {
struct sway_container *ws = container_parent(container, C_WORKSPACE);
double lx = ws->x + (ws->width - container->width) / 2;
double ly = ws->y + (ws->height - container->height) / 2;
container_floating_move_to(container, lx, ly);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
if (argc != 2) {
if (argc < 2) {
return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax); return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
} }
double lx, ly; double lx, ly;
char *inv; char *inv;
lx = (double)strtol(argv[0], &inv, 10); lx = (double)strtol(argv[0], &inv, 10);
@ -290,11 +344,22 @@ static struct cmd_results *move_to_position(struct sway_container *container,
return cmd_results_new(CMD_FAILURE, "move", return cmd_results_new(CMD_FAILURE, "move",
"Invalid position specified"); "Invalid position specified");
} }
if (strcmp(argv[1], "px") == 0) {
--argc;
++argv;
}
if (argc > 3) {
return cmd_results_new(CMD_FAILURE, "move", expected_position_syntax);
}
ly = (double)strtol(argv[1], &inv, 10); ly = (double)strtol(argv[1], &inv, 10);
if (*inv != '\0' && strcasecmp(inv, "px") != 0) { if ((*inv != '\0' && strcasecmp(inv, "px") != 0) ||
(argc == 3 && strcmp(argv[2], "px") != 0)) {
return cmd_results_new(CMD_FAILURE, "move", return cmd_results_new(CMD_FAILURE, "move",
"Invalid position specified"); "Invalid position specified");
} }
container_floating_move_to(container, lx, ly); container_floating_move_to(container, lx, ly);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
@ -342,8 +407,11 @@ struct cmd_results *cmd_move(int argc, char **argv) {
return move_in_direction(current, MOVE_UP, argc, argv); return move_in_direction(current, MOVE_UP, argc, argv);
} else if (strcasecmp(argv[0], "down") == 0) { } else if (strcasecmp(argv[0], "down") == 0) {
return move_in_direction(current, MOVE_DOWN, argc, argv); return move_in_direction(current, MOVE_DOWN, argc, argv);
} else if (strcasecmp(argv[0], "container") == 0 } else if ((strcasecmp(argv[0], "container") == 0
|| strcasecmp(argv[0], "window") == 0) { || strcasecmp(argv[0], "window") == 0) ||
(strcasecmp(argv[0], "--no-auto-back-and-forth") &&
(strcasecmp(argv[0], "container") == 0
|| strcasecmp(argv[0], "window") == 0))) {
return cmd_move_container(current, argc, argv); return cmd_move_container(current, argc, argv);
} else if (strcasecmp(argv[0], "workspace") == 0) { } else if (strcasecmp(argv[0], "workspace") == 0) {
return cmd_move_workspace(current, argc, argv); return cmd_move_workspace(current, argc, argv);

View file

@ -61,6 +61,16 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
} }
char *new_name = join_args(argv + argn, argc - argn); char *new_name = join_args(argv + argn, argc - argn);
if (strcasecmp(new_name, "next") == 0 ||
strcasecmp(new_name, "prev") == 0 ||
strcasecmp(new_name, "next_on_output") == 0 ||
strcasecmp(new_name, "prev_on_output") == 0 ||
strcasecmp(new_name, "back_and_forth") == 0 ||
strcasecmp(new_name, "current") == 0) {
free(new_name);
return cmd_results_new(CMD_INVALID, "rename",
"Cannot use special workspace name '%s'", argv[argn]);
}
struct sway_container *tmp_workspace = workspace_by_name(new_name); struct sway_container *tmp_workspace = workspace_by_name(new_name);
if (tmp_workspace) { if (tmp_workspace) {
free(new_name); free(new_name);

View file

@ -17,17 +17,6 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
int output_location = -1; int output_location = -1;
struct sway_container *current_container = config->handler_context.current_container;
struct sway_container *old_workspace = NULL, *old_output = NULL;
if (current_container) {
if (current_container->type == C_WORKSPACE) {
old_workspace = current_container;
} else {
old_workspace = container_parent(current_container, C_WORKSPACE);
}
old_output = container_parent(current_container, C_OUTPUT);
}
for (int i = 0; i < argc; ++i) { for (int i = 0; i < argc; ++i) {
if (strcasecmp(argv[i], "output") == 0) { if (strcasecmp(argv[i], "output") == 0) {
output_location = i; output_location = i;
@ -57,29 +46,36 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
if (config->reading || !config->active) { if (config->reading || !config->active) {
return cmd_results_new(CMD_DEFER, "workspace", NULL); return cmd_results_new(CMD_DEFER, "workspace", NULL);
} }
bool no_auto_back_and_forth = false;
while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) {
no_auto_back_and_forth = true;
if ((error = checkarg(--argc, "workspace", EXPECTED_AT_LEAST, 1))) {
return error;
}
++argv;
}
struct sway_container *ws = NULL; struct sway_container *ws = NULL;
if (strcasecmp(argv[0], "number") == 0) { if (strcasecmp(argv[0], "number") == 0) {
if (argc < 2) {
cmd_results_new(CMD_INVALID, "workspace",
"Expected workspace number");
}
if (!(ws = workspace_by_number(argv[1]))) { if (!(ws = workspace_by_number(argv[1]))) {
char *name = join_args(argv + 1, argc - 1); char *name = join_args(argv + 1, argc - 1);
ws = workspace_create(NULL, name); ws = workspace_create(NULL, name);
free(name); free(name);
} }
} else if (strcasecmp(argv[0], "next") == 0) { } else if (strcasecmp(argv[0], "next") == 0 ||
ws = workspace_next(old_workspace); strcasecmp(argv[0], "prev") == 0 ||
} else if (strcasecmp(argv[0], "prev") == 0) { strcasecmp(argv[0], "next_on_output") == 0 ||
ws = workspace_prev(old_workspace); strcasecmp(argv[0], "prev_on_output") == 0 ||
} else if (strcasecmp(argv[0], "next_on_output") == 0) { strcasecmp(argv[0], "current") == 0) {
ws = workspace_output_next(old_output); ws = workspace_by_name(argv[0]);
} else if (strcasecmp(argv[0], "prev_on_output") == 0) {
ws = workspace_output_prev(old_output);
} else if (strcasecmp(argv[0], "back_and_forth") == 0) { } else if (strcasecmp(argv[0], "back_and_forth") == 0) {
// if auto_back_and_forth is enabled, workspace_switch will swap if (!(ws = workspace_by_name(argv[0])) && prev_workspace_name) {
// the workspaces. If we created prev_workspace here, workspace_switch
// would put us back on original workspace.
if (config->auto_back_and_forth) {
ws = old_workspace;
} else if (prev_workspace_name
&& !(ws = workspace_by_name(prev_workspace_name))) {
ws = workspace_create(NULL, prev_workspace_name); ws = workspace_create(NULL, prev_workspace_name);
} }
} else { } else {
@ -89,7 +85,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
} }
free(name); free(name);
} }
workspace_switch(ws); workspace_switch(ws, no_auto_back_and_forth);
} }
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

View file

@ -361,8 +361,17 @@ static char *get_focused_prop(enum criteria_token token) {
} }
} }
break; break;
case T_CON_ID: // These do not support __focused__ case T_CON_ID:
case T_CON_MARK: if (view->swayc == NULL) {
return NULL;
}
size_t id = view->swayc->id;
size_t id_size = snprintf(NULL, 0, "%zu", id) + 1;
char *id_str = malloc(id_size);
snprintf(id_str, id_size, "%zu", id);
value = id_str;
break;
case T_CON_MARK: // These do not support __focused__
case T_FLOATING: case T_FLOATING:
#ifdef HAVE_XWAYLAND #ifdef HAVE_XWAYLAND
case T_ID: case T_ID:
@ -425,7 +434,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
case T_CON_ID: case T_CON_ID:
criteria->con_id = strtoul(effective_value, &endptr, 10); criteria->con_id = strtoul(effective_value, &endptr, 10);
if (*endptr != 0) { if (*endptr != 0) {
error = strdup("The value for 'con_id' should be numeric"); error = strdup("The value for 'con_id' should be '__focused__' or numeric");
} }
break; break;
case T_CON_MARK: case T_CON_MARK:
@ -452,13 +461,18 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
criteria->tiling = true; criteria->tiling = true;
break; break;
case T_URGENT: case T_URGENT:
if (strcmp(effective_value, "latest") == 0) { if (strcmp(effective_value, "latest") == 0 ||
strcmp(effective_value, "newest") == 0 ||
strcmp(effective_value, "last") == 0 ||
strcmp(effective_value, "recent") == 0) {
criteria->urgent = 'l'; criteria->urgent = 'l';
} else if (strcmp(effective_value, "oldest") == 0) { } else if (strcmp(effective_value, "oldest") == 0 ||
strcmp(effective_value, "first") == 0) {
criteria->urgent = 'o'; criteria->urgent = 'o';
} else { } else {
error = error =
strdup("The value for 'urgent' must be 'latest' or 'oldest'"); strdup("The value for 'urgent' must be 'first', 'last', "
"'latest', 'newest', 'oldest' or 'recent'");
} }
break; break;
case T_WORKSPACE: case T_WORKSPACE:

View file

@ -84,6 +84,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
*floating* enable|disable|toggle *floating* enable|disable|toggle
Make focused view floating, non-floating, or the opposite of what it is now. Make focused view floating, non-floating, or the opposite of what it is now.
<criteria> *focus*
Moves focus to the container that matches the specified criteria.
*focus* up|right|down|left *focus* up|right|down|left
Moves focus to the next container in the specified direction. Moves focus to the next container in the specified direction.
@ -111,33 +114,53 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
*fullscreen* *fullscreen*
Toggles fullscreen for the focused view. Toggles fullscreen for the focused view.
*layout* splith|splitv|stacking|tabbed *layout* default|splith|splitv|stacking|tabbed
Sets the layout mode of the focused container. Sets the layout mode of the focused container.
*layout* toggle split *layout* toggle [split|all]
Switches the focused container between the splitv and splith layouts. Cycles the layout mode of the focused container though a preset list of
layouts. If no argument is given, then it cycles through stacking, tabbed
and the last split layout. If "split" is given, then it cycles through
splith and splitv. If "all" is given, then it cycles through every layout.
*move* left|right|up|down [<px>] *layout* toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...
Cycles the layout mode of the focused container through a list of layouts.
*move* left|right|up|down [<px> px]
Moves the focused container in the direction specified. If the container, Moves the focused container in the direction specified. If the container,
the optional _px_ argument specifies how many pixels to move the container. the optional _px_ argument specifies how many pixels to move the container.
If unspecified, the default is 10 pixels. Pixels are ignored when moving If unspecified, the default is 10 pixels. Pixels are ignored when moving
tiled containers. tiled containers.
*move* container|window to workspace <name> *move* [absolute] position <pos_x> [px] <pos_y> [px]
Moves the focused container to the specified workspace. Moves the focused container to the specified position.
*move* container|window to workspace prev|next *move* [absolute] position center|mouse
Moves the focused container to the previous or next workspace on this Moves the focused container to be centered on the workspace or mouse.
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] mark <mark>
Moves the focused container to the specified mark.
*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
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
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|workspace to output <name> *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. Moves the focused container or workspace to the specified output.
*move* container|window|workspace to output up|right|down|left *move* container|window|workspace [to] output up|right|down|left
Moves the focused container or workspace to next output in the specified Moves the focused container or workspace to next output in the specified
direction. direction.
@ -511,7 +534,7 @@ config after the others, or it will be matched instead of the others.
state. Using _allow_ or _deny_ controls the window's ability to set itself state. Using _allow_ or _deny_ controls the window's ability to set itself
as urgent. By default, windows are allowed to set their own urgency. as urgent. By default, windows are allowed to set their own urgency.
*workspace* [number] <name> *workspace* [--no-auto-back-and-forth] [number] <name>
Switches to the specified workspace. The string "number" is optional and is Switches to the specified workspace. The string "number" is optional and is
used to sort workspaces. used to sort workspaces.
@ -522,6 +545,9 @@ config after the others, or it will be matched instead of the others.
*workspace* prev\_on\_output|next\_on\_output *workspace* prev\_on\_output|next\_on\_output
Switches to the next workspace on the current output. Switches to the next workspace on the current output.
*workspace* back_and_forth
Switches to the previously focused workspace.
*workspace* <name> output <output> *workspace* <name> output <output>
Specifies that workspace _name_ should be shown on the specified _output_. Specifies that workspace _name_ should be shown on the specified _output_.
@ -582,7 +608,9 @@ The following attributes may be matched with:
the currently focused window. the currently focused window.
*con\_id* *con\_id*
Compare against the internal container ID, which you can find via IPC. Compare against the internal container ID, which you can find via IPC. If
value is \_\_focused\_\_, then the id must be the same as that of the
currently focused window.
*con\_mark* *con\_mark*
Compare against the window marks. Can be a regular expression. Compare against the window marks. Can be a regular expression.
@ -612,7 +640,8 @@ The following attributes may be matched with:
currently focused window. currently focused window.
*urgent* *urgent*
Compares the urgent state of the window. Can be "latest" or "oldest". Compares the urgent state of the window. Can be "first", "last", "latest",
"newest", "oldest" or "recent".
*window\_role* *window\_role*
Compare against the window role (WM\_WINDOW\_ROLE). Can be a regular Compare against the window role (WM\_WINDOW\_ROLE). Can be a regular

View file

@ -859,7 +859,7 @@ struct sway_container *container_split(struct sway_container *child,
} }
if (child->type == C_WORKSPACE && child->children->length == 0) { if (child->type == C_WORKSPACE && child->children->length == 0) {
// Special case: this just behaves like splitt // Special case: this just behaves like splitt
child->prev_layout = child->layout; child->prev_split_layout = child->layout;
child->layout = layout; child->layout = layout;
return child; return child;
} }
@ -870,7 +870,7 @@ struct sway_container *container_split(struct sway_container *child,
remove_gaps(child); remove_gaps(child);
cont->prev_layout = L_NONE; cont->prev_split_layout = L_NONE;
cont->width = child->width; cont->width = child->width;
cont->height = child->height; cont->height = child->height;
cont->x = child->x; cont->x = child->x;

View file

@ -1,5 +1,6 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <stdlib.h> #include <stdlib.h>
#include <strings.h>
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_buffer.h>
@ -456,7 +457,13 @@ static struct sway_container *select_workspace(struct sway_view *view) {
if (criteria->type == CT_ASSIGN_WORKSPACE) { if (criteria->type == CT_ASSIGN_WORKSPACE) {
ws = workspace_by_name(criteria->target); ws = workspace_by_name(criteria->target);
if (!ws) { if (!ws) {
ws = workspace_create(NULL, criteria->target); if (strcasecmp(criteria->target, "back_and_forth") == 0) {
if (prev_workspace_name) {
ws = workspace_create(NULL, prev_workspace_name);
}
} else {
ws = workspace_create(NULL, criteria->target);
}
} }
break; break;
} else { } else {
@ -891,6 +898,15 @@ static bool find_by_mark_iterator(struct sway_container *con,
return con->type == C_VIEW && view_has_mark(con->sway_view, mark); return con->type == C_VIEW && view_has_mark(con->sway_view, mark);
} }
struct sway_view *view_find_mark(char *mark) {
struct sway_container *container = container_find(&root_container,
find_by_mark_iterator, mark);
if (!container) {
return NULL;
}
return container->sway_view;
}
bool view_find_and_unmark(char *mark) { bool view_find_and_unmark(char *mark) {
struct sway_container *container = container_find(&root_container, struct sway_container *container = container_find(&root_container,
find_by_mark_iterator, mark); find_by_mark_iterator, mark);

View file

@ -59,7 +59,7 @@ struct sway_container *workspace_create(struct sway_container *output,
workspace->width = output->width; workspace->width = output->width;
workspace->height = output->height; workspace->height = output->height;
workspace->name = !name ? NULL : strdup(name); workspace->name = !name ? NULL : strdup(name);
workspace->prev_layout = L_NONE; workspace->prev_split_layout = L_NONE;
workspace->layout = container_get_default_layout(output); workspace->layout = container_get_default_layout(output);
struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace));
@ -250,6 +250,7 @@ struct sway_container *workspace_by_name(const char *name) {
current_workspace = container_parent(focus, C_WORKSPACE); current_workspace = container_parent(focus, C_WORKSPACE);
current_output = container_parent(focus, C_OUTPUT); current_output = container_parent(focus, C_OUTPUT);
} }
if (strcmp(name, "prev") == 0) { if (strcmp(name, "prev") == 0) {
return workspace_prev(current_workspace); return workspace_prev(current_workspace);
} else if (strcmp(name, "prev_on_output") == 0) { } else if (strcmp(name, "prev_on_output") == 0) {
@ -260,6 +261,9 @@ struct sway_container *workspace_by_name(const char *name) {
return workspace_output_next(current_output); return workspace_output_next(current_output);
} else if (strcmp(name, "current") == 0) { } else if (strcmp(name, "current") == 0) {
return current_workspace; return current_workspace;
} else if (strcasecmp(name, "back_and_forth") == 0) {
return prev_workspace_name ? container_find(&root_container,
_workspace_by_name, (void *)prev_workspace_name) : NULL;
} else { } else {
return container_find(&root_container, _workspace_by_name, return container_find(&root_container, _workspace_by_name,
(void *)name); (void *)name);
@ -364,7 +368,8 @@ struct sway_container *workspace_prev(struct sway_container *current) {
return workspace_prev_next_impl(current, false); return workspace_prev_next_impl(current, false);
} }
bool workspace_switch(struct sway_container *workspace) { bool workspace_switch(struct sway_container *workspace,
bool no_auto_back_and_forth) {
if (!workspace) { if (!workspace) {
return false; return false;
} }
@ -379,7 +384,7 @@ bool workspace_switch(struct sway_container *workspace) {
active_ws = container_parent(focus, C_WORKSPACE); active_ws = container_parent(focus, C_WORKSPACE);
} }
if (config->auto_back_and_forth if (!no_auto_back_and_forth && config->auto_back_and_forth
&& active_ws == workspace && active_ws == workspace
&& prev_workspace_name) { && prev_workspace_name) {
struct sway_container *new_ws = workspace_by_name(prev_workspace_name); struct sway_container *new_ws = workspace_by_name(prev_workspace_name);