Merge pull request #1149 from 4e554c4c/criteria

Improve criteria handling
This commit is contained in:
Drew DeVault 2017-04-06 06:47:26 -04:00 committed by GitHub
commit 3f40b61321
16 changed files with 116 additions and 53 deletions

View file

@ -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.
*/

View file

@ -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

View file

@ -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;
@ -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, ";");
@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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");
}

View file

@ -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);
}

View file

@ -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");
}

View file

@ -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;

View file

@ -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;
@ -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) {

View file

@ -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;
@ -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) {
@ -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;
@ -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;
@ -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) {
@ -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;

View file

@ -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) {
@ -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 {
@ -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 {

View file

@ -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) {

View file

@ -245,7 +245,7 @@ ect_cleanup:
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);
}
@ -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:
@ -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++;
@ -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;
}

View file

@ -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
@ -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**::