Skip to content

Commit

Permalink
Merge pull request #1149 from 4e554c4c/criteria
Browse files Browse the repository at this point in the history
Improve criteria handling
  • Loading branch information
ddevault authored Apr 6, 2017
2 parents 7d43a76 + 069d37f commit 3f40b61
Show file tree
Hide file tree
Showing 16 changed files with 116 additions and 53 deletions.
3 changes: 3 additions & 0 deletions include/sway/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include <wlc/wlc.h>
#include "config.h"

// Container that a called command should act upon. Only valid in command functions.
extern swayc_t *current_container;

/**
* Indicates the result of a command's execution.
*/
Expand Down
3 changes: 3 additions & 0 deletions include/sway/criteria.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ char *extract_crit_tokens(list_t *tokens, const char *criteria);
// been set with `for_window` commands and have an associated cmdlist.
list_t *criteria_for(swayc_t *cont);

// Returns a list of all containers that match the given list of tokens.
list_t *container_for(list_t *tokens);

#endif
79 changes: 49 additions & 30 deletions sway/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ struct cmd_handler {

int sp_index = 0;

swayc_t *current_container = NULL;

// Returns error object, or NULL if check succeeds.
struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
struct cmd_results *error = NULL;
Expand Down Expand Up @@ -371,42 +373,37 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
char *head = exec;
char *cmdlist;
char *cmd;
char *criteria __attribute__((unused));
list_t *containers = NULL;

head = exec;
do {
// Extract criteria (valid for this command list only).
criteria = NULL;
if (*head == '[') {
++head;
criteria = argsep(&head, "]");
char *criteria_string = argsep(&head, "]");
if (head) {
++head;
// TODO handle criteria
list_t *tokens = create_list();
char *error;

if ((error = extract_crit_tokens(tokens, criteria_string))) {
results = cmd_results_new(CMD_INVALID, criteria_string,
"Can't parse criteria string: %s", error);
free(error);
free(tokens);
goto cleanup;
}
containers = container_for(tokens);

free(tokens);
} else {
if (!results) {
results = cmd_results_new(CMD_INVALID, criteria, "Unmatched [");
results = cmd_results_new(CMD_INVALID, criteria_string, "Unmatched [");
}
goto cleanup;
}
// Skip leading whitespace
head += strspn(head, whitespace);

// TODO: it will yield unexpected results to execute commands
// (on any view) that where meant for certain views only.
if (!results) {
int len = strlen(criteria) + strlen(head) + 4;
char *tmp = malloc(len);
if (tmp) {
snprintf(tmp, len, "[%s] %s", criteria, head);
} else {
sway_log(L_DEBUG, "Unable to allocate criteria string for cmd result");
}
results = cmd_results_new(CMD_INVALID, tmp,
"Can't handle criteria string: Refusing to execute command");
free(tmp);
}
goto cleanup;
}
// Split command list
cmdlist = argsep(&head, ";");
Expand Down Expand Up @@ -450,21 +447,43 @@ struct cmd_results *handle_command(char *_exec, enum command_context context) {
free_argv(argc, argv);
goto cleanup;
}
struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) {
free_argv(argc, argv);
if (results) {
free_cmd_results(results);
int i = 0;
do {
if (!containers) {
current_container = get_focused_container(&root_container);
} else if (containers->length == 0) {
break;
} else {
current_container = (swayc_t *)containers->items[i];
}
results = res;
goto cleanup;
}
sway_log(L_INFO, "Running on container '%s'", current_container->name);

struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) {
free_argv(argc, argv);
if (results) {
free_cmd_results(results);
}
results = res;
goto cleanup;
}
free_cmd_results(res);
++i;
} while(containers && i < containers->length);

free_argv(argc, argv);
free_cmd_results(res);
} while(cmdlist);

if (containers) {
list_free(containers);
containers = NULL;
}
} while(head);
cleanup:
free(exec);
if (containers) {
free(containers);
}
if (!results) {
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
Expand Down
2 changes: 1 addition & 1 deletion sway/commands/border.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
"Expected 'border <normal|pixel|none|toggle> [<n>]");
}

swayc_t *view = get_focused_view(&root_container);
swayc_t *view = current_container;
enum swayc_border_types border = view->border_type;
int thickness = view->border_thickness;

Expand Down
2 changes: 1 addition & 1 deletion sway/commands/floating.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
return error;
}
swayc_t *view = get_focused_container(&root_container);
swayc_t *view = current_container;
bool wants_floating;
if (strcasecmp(argv[0], "enable") == 0) {
wants_floating = true;
Expand Down
3 changes: 3 additions & 0 deletions sway/commands/focus.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
}
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if (argc == 0) {
set_focused_container(current_container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
return error;
}
Expand Down
2 changes: 1 addition & 1 deletion sway/commands/fullscreen.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
return error;
}
swayc_t *container = get_focused_view(&root_container);
swayc_t *container = current_container;
if(container->type != C_VIEW){
return cmd_results_new(CMD_INVALID, "fullscreen", "Only views can fullscreen");
}
Expand Down
2 changes: 1 addition & 1 deletion sway/commands/kill.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ struct cmd_results *cmd_kill(int argc, char **argv) {
if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");

swayc_t *container = get_focused_container(&root_container);
swayc_t *container = current_container;
close_views(container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
2 changes: 1 addition & 1 deletion sway/commands/layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
return error;
}
swayc_t *parent = get_focused_container(&root_container);
swayc_t *parent = current_container;
if (parent->is_floating) {
return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows");
}
Expand Down
4 changes: 2 additions & 2 deletions sway/commands/mark.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
struct cmd_results *cmd_mark(int argc, char **argv) {
struct cmd_results *error = NULL;
if (config->reading) return cmd_results_new(CMD_FAILURE, "mark", "Can't be used in config file.");
if ((error = checkarg(argc, "floating", EXPECTED_AT_LEAST, 1))) {
if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
return error;
}

swayc_t *view = get_focused_container(&root_container);
swayc_t *view = current_container;
bool add = false;
bool toggle = false;

Expand Down
4 changes: 2 additions & 2 deletions sway/commands/move.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct cmd_results *cmd_move(int argc, char **argv) {
"'move <container|window> to workspace <name>' or "
"'move <container|window|workspace> to output <name|direction>' or "
"'move position mouse'";
swayc_t *view = get_focused_container(&root_container);
swayc_t *view = current_container;

if (argc == 2 || (argc == 3 && strcasecmp(argv[2], "px") == 0 )) {
char *inv;
Expand Down Expand Up @@ -125,7 +125,7 @@ struct cmd_results *cmd_move(int argc, char **argv) {
if (view->type != C_CONTAINER && view->type != C_VIEW) {
return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views.");
}
swayc_t *view = get_focused_container(&root_container);
swayc_t *view = current_container;
int i;
for (i = 0; i < scratchpad->length; i++) {
if (scratchpad->items[i] == view) {
Expand Down
12 changes: 6 additions & 6 deletions sway/commands/resize.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ enum resize_dim_types {
};

static bool set_size_floating(int new_dimension, bool use_width) {
swayc_t *view = get_focused_float(swayc_active_workspace());
swayc_t *view = current_container;
if (view) {
if (use_width) {
int current_width = view->width;
Expand Down Expand Up @@ -50,7 +50,7 @@ static bool set_size_floating(int new_dimension, bool use_width) {
}

static bool resize_floating(int amount, bool use_width) {
swayc_t *view = get_focused_float(swayc_active_workspace());
swayc_t *view = current_container;

if (view) {
if (use_width) {
Expand All @@ -64,7 +64,7 @@ static bool resize_floating(int amount, bool use_width) {
}

static bool resize_tiled(int amount, bool use_width) {
swayc_t *container = get_focused_view(swayc_active_workspace());
swayc_t *container = current_container;
swayc_t *parent = container->parent;
int idx_focused = 0;
bool use_major = false;
Expand Down Expand Up @@ -199,7 +199,7 @@ static bool resize_tiled(int amount, bool use_width) {

static bool set_size_tiled(int amount, bool use_width) {
int desired;
swayc_t *focused = get_focused_view(swayc_active_workspace());
swayc_t *focused = current_container;

if (use_width) {
desired = amount - focused->width;
Expand All @@ -211,7 +211,7 @@ static bool set_size_tiled(int amount, bool use_width) {
}

static bool set_size(int dimension, bool use_width) {
swayc_t *focused = get_focused_view_include_floating(swayc_active_workspace());
swayc_t *focused = current_container;

if (focused) {
if (focused->is_floating) {
Expand All @@ -225,7 +225,7 @@ static bool set_size(int dimension, bool use_width) {
}

static bool resize(int dimension, bool use_width, enum resize_dim_types dim_type) {
swayc_t *focused = get_focused_view_include_floating(swayc_active_workspace());
swayc_t *focused = current_container;

// translate "10 ppt" (10%) to appropriate # of pixels in case we need it
float ppt_dim = (float)dimension / 100;
Expand Down
6 changes: 3 additions & 3 deletions sway/commands/split.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ static struct cmd_results *_do_split(int argc, char **argv, int layout) {
if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
return error;
}
swayc_t *focused = get_focused_container(&root_container);
swayc_t *focused = current_container;

// Case of floating window, don't split
if (focused->is_floating) {
Expand Down Expand Up @@ -66,7 +66,7 @@ struct cmd_results *cmd_split(int argc, char **argv) {
} else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) {
_do_split(argc - 1, argv + 1, L_HORIZ);
} else if (strcasecmp(argv[0], "t") == 0 || strcasecmp(argv[0], "toggle") == 0) {
swayc_t *focused = get_focused_container(&root_container);
swayc_t *focused = current_container;
if (focused->parent->layout == L_VERT) {
_do_split(argc - 1, argv + 1, L_HORIZ);
} else {
Expand All @@ -89,7 +89,7 @@ struct cmd_results *cmd_splith(int argc, char **argv) {
}

struct cmd_results *cmd_splitt(int argc, char **argv) {
swayc_t *focused = get_focused_container(&root_container);
swayc_t *focused = current_container;
if (focused->parent->layout == L_VERT) {
return _do_split(argc, argv, L_HORIZ);
} else {
Expand Down
2 changes: 1 addition & 1 deletion sway/commands/unmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "stringop.h"

struct cmd_results *cmd_unmark(int argc, char **argv) {
swayc_t *view = get_focused_container(&root_container);
swayc_t *view = current_container;

if (view->marks) {
if (argc) {
Expand Down
27 changes: 24 additions & 3 deletions sway/criteria.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ char *extract_crit_tokens(list_t *tokens, const char * const criteria) {
return error;
}

int regex_cmp(const char *item, const regex_t *regex) {
static int regex_cmp(const char *item, const regex_t *regex) {
return regexec(regex, item, 0, NULL, 0);
}

Expand All @@ -272,7 +272,10 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
break;
case CRIT_CON_MARK:
if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) {
++matches;
// Make sure it isn't matching the NUL string
if ((strcmp(crit->raw, "") == 0) == (list_seq_find(cont->marks, (int (*)(const void *, const void *))strcmp, "") != -1)) {
++matches;
}
}
break;
case CRIT_ID:
Expand All @@ -285,7 +288,7 @@ static bool criteria_test(swayc_t *cont, list_t *tokens) {
case CRIT_INSTANCE:
if (!cont->instance) {
// ignore
} else if (strcmp(crit->raw, "focused") == 0) {
} else if (crit_is_focused(crit->raw)) {
swayc_t *focused = get_focused_view(&root_container);
if (focused->instance && strcmp(cont->instance, focused->instance) == 0) {
matches++;
Expand Down Expand Up @@ -373,3 +376,21 @@ list_t *criteria_for(swayc_t *cont) {
}
return matches;
}

struct list_tokens {
list_t *list;
list_t *tokens;
};

static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) {
if (criteria_test(container, list_tokens->tokens)) {
list_add(list_tokens->list, container);
}
}
list_t *container_for(list_t *tokens) {
struct list_tokens list_tokens = (struct list_tokens){create_list(), tokens};

container_map(&root_container, (void (*)(swayc_t *, void *))container_match_add, &list_tokens);

return list_tokens.list;
}
16 changes: 15 additions & 1 deletion sway/sway.5.txt
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ The default colors are:
If smart_gaps are _on_ then gaps will only be enabled if a workspace has more
than one child container.

**mark** <--add|--replace> <--toggle> <identifier>::
**mark** \<--add|--replace> \<--toggle> <identifier>::
Marks are arbitrary labels that can be used to identify certain windows and
then jump to them at a later time. By default, the **mark** command sets
_identifier_ as the only mark on a window. By specifying _--add_, mark will
Expand Down Expand Up @@ -426,6 +426,20 @@ The string contains one or more (space separated) attribute/value pairs and they
are used by some commands filter which views to execute actions on. All attributes
must match for the criteria string to match.

Criteria may be used with either the **for_window** or **assign** commands to
specify operations to perform on new views. A criteria may also be used to
perform specific commands (ones that normally act upon one window) on all views
that match that criteria. For example:

Focus on a window with the mark "IRC":
[con_mark="IRC"] focus

Kill all windows with the title "Emacs":
[class="Emacs"] kill

Mark all Firefox windows with "Browser":
[class="Firefox"] mark Browser

Currently supported attributes:

**class**::
Expand Down

0 comments on commit 3f40b61

Please sign in to comment.