|
|
@ -16,8 +16,7 @@
|
|
|
|
#include "config.h"
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
|
|
bool criteria_is_empty(struct criteria *criteria) {
|
|
|
|
bool criteria_is_empty(struct criteria *criteria) {
|
|
|
|
return !criteria->autofail
|
|
|
|
return !criteria->title
|
|
|
|
&& !criteria->title
|
|
|
|
|
|
|
|
&& !criteria->shell
|
|
|
|
&& !criteria->shell
|
|
|
|
&& !criteria->app_id
|
|
|
|
&& !criteria->app_id
|
|
|
|
&& !criteria->con_mark
|
|
|
|
&& !criteria->con_mark
|
|
|
@ -35,16 +34,64 @@ bool criteria_is_empty(struct criteria *criteria) {
|
|
|
|
&& !criteria->workspace;
|
|
|
|
&& !criteria->workspace;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The error pointer is used for parsing functions, and saves having to pass it
|
|
|
|
|
|
|
|
// as an argument in several places.
|
|
|
|
|
|
|
|
char *error = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Returns error string on failure or NULL otherwise.
|
|
|
|
|
|
|
|
static bool generate_regex(pcre **regex, char *value) {
|
|
|
|
|
|
|
|
const char *reg_err;
|
|
|
|
|
|
|
|
int offset;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!*regex) {
|
|
|
|
|
|
|
|
const char *fmt = "Regex compilation for '%s' failed: %s";
|
|
|
|
|
|
|
|
int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3;
|
|
|
|
|
|
|
|
error = malloc(len);
|
|
|
|
|
|
|
|
snprintf(error, len, fmt, value, reg_err);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool pattern_create(struct pattern **pattern, char *value) {
|
|
|
|
|
|
|
|
*pattern = calloc(1, sizeof(struct pattern));
|
|
|
|
|
|
|
|
if (!*pattern) {
|
|
|
|
|
|
|
|
sway_log(SWAY_ERROR, "Failed to allocate pattern");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (strcmp(value, "__focused__") == 0) {
|
|
|
|
|
|
|
|
(*pattern)->match_type = PATTERN_FOCUSED;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
(*pattern)->match_type = PATTERN_PCRE;
|
|
|
|
|
|
|
|
if (!generate_regex(&(*pattern)->regex, value)) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void pattern_destroy(struct pattern *pattern) {
|
|
|
|
|
|
|
|
if (pattern) {
|
|
|
|
|
|
|
|
if (pattern->regex) {
|
|
|
|
|
|
|
|
pcre_free(pattern->regex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
free(pattern);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void criteria_destroy(struct criteria *criteria) {
|
|
|
|
void criteria_destroy(struct criteria *criteria) {
|
|
|
|
pcre_free(criteria->title);
|
|
|
|
pattern_destroy(criteria->title);
|
|
|
|
pcre_free(criteria->shell);
|
|
|
|
pattern_destroy(criteria->shell);
|
|
|
|
pcre_free(criteria->app_id);
|
|
|
|
pattern_destroy(criteria->app_id);
|
|
|
|
#if HAVE_XWAYLAND
|
|
|
|
#if HAVE_XWAYLAND
|
|
|
|
pcre_free(criteria->class);
|
|
|
|
pattern_destroy(criteria->class);
|
|
|
|
pcre_free(criteria->instance);
|
|
|
|
pattern_destroy(criteria->instance);
|
|
|
|
pcre_free(criteria->window_role);
|
|
|
|
pattern_destroy(criteria->window_role);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
pcre_free(criteria->con_mark);
|
|
|
|
pattern_destroy(criteria->con_mark);
|
|
|
|
free(criteria->workspace);
|
|
|
|
free(criteria->workspace);
|
|
|
|
free(criteria->cmdlist);
|
|
|
|
free(criteria->cmdlist);
|
|
|
|
free(criteria->raw);
|
|
|
|
free(criteria->raw);
|
|
|
@ -99,36 +146,75 @@ static void find_urgent_iterator(struct sway_container *con, void *data) {
|
|
|
|
|
|
|
|
|
|
|
|
static bool criteria_matches_view(struct criteria *criteria,
|
|
|
|
static bool criteria_matches_view(struct criteria *criteria,
|
|
|
|
struct sway_view *view) {
|
|
|
|
struct sway_view *view) {
|
|
|
|
if (criteria->autofail) {
|
|
|
|
struct sway_seat *seat = input_manager_current_seat();
|
|
|
|
return false;
|
|
|
|
struct sway_container *focus = seat_get_focused_container(seat);
|
|
|
|
}
|
|
|
|
struct sway_view *focused = focus ? focus->view : NULL;
|
|
|
|
|
|
|
|
|
|
|
|
if (criteria->title) {
|
|
|
|
if (criteria->title) {
|
|
|
|
const char *title = view_get_title(view);
|
|
|
|
const char *title = view_get_title(view);
|
|
|
|
if (!title || regex_cmp(title, criteria->title) != 0) {
|
|
|
|
if (!title) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (criteria->title->match_type) {
|
|
|
|
|
|
|
|
case PATTERN_FOCUSED:
|
|
|
|
|
|
|
|
if (focused && strcmp(title, view_get_title(focused))) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PATTERN_PCRE:
|
|
|
|
|
|
|
|
if (regex_cmp(title, criteria->title->regex) != 0) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (criteria->shell) {
|
|
|
|
if (criteria->shell) {
|
|
|
|
const char *shell = view_get_shell(view);
|
|
|
|
const char *shell = view_get_shell(view);
|
|
|
|
if (!shell || regex_cmp(shell, criteria->shell) != 0) {
|
|
|
|
if (!shell) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (criteria->shell->match_type) {
|
|
|
|
|
|
|
|
case PATTERN_FOCUSED:
|
|
|
|
|
|
|
|
if (focused && strcmp(shell, view_get_shell(focused))) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PATTERN_PCRE:
|
|
|
|
|
|
|
|
if (regex_cmp(shell, criteria->shell->regex) != 0) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (criteria->app_id) {
|
|
|
|
if (criteria->app_id) {
|
|
|
|
const char *app_id = view_get_app_id(view);
|
|
|
|
const char *app_id = view_get_app_id(view);
|
|
|
|
if (!app_id || regex_cmp(app_id, criteria->app_id) != 0) {
|
|
|
|
if (!app_id) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (criteria->app_id->match_type) {
|
|
|
|
|
|
|
|
case PATTERN_FOCUSED:
|
|
|
|
|
|
|
|
if (focused && strcmp(app_id, view_get_app_id(focused))) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PATTERN_PCRE:
|
|
|
|
|
|
|
|
if (regex_cmp(app_id, criteria->app_id->regex) != 0) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (criteria->con_mark) {
|
|
|
|
if (criteria->con_mark) {
|
|
|
|
bool exists = false;
|
|
|
|
bool exists = false;
|
|
|
|
struct sway_container *con = view->container;
|
|
|
|
struct sway_container *con = view->container;
|
|
|
|
for (int i = 0; i < con->marks->length; ++i) {
|
|
|
|
for (int i = 0; i < con->marks->length; ++i) {
|
|
|
|
if (regex_cmp(con->marks->items[i], criteria->con_mark) == 0) {
|
|
|
|
if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) {
|
|
|
|
exists = true;
|
|
|
|
exists = true;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -154,23 +240,62 @@ static bool criteria_matches_view(struct criteria *criteria,
|
|
|
|
|
|
|
|
|
|
|
|
if (criteria->class) {
|
|
|
|
if (criteria->class) {
|
|
|
|
const char *class = view_get_class(view);
|
|
|
|
const char *class = view_get_class(view);
|
|
|
|
if (!class || regex_cmp(class, criteria->class) != 0) {
|
|
|
|
if (!class) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (criteria->class->match_type) {
|
|
|
|
|
|
|
|
case PATTERN_FOCUSED:
|
|
|
|
|
|
|
|
if (focused && strcmp(class, view_get_class(focused))) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PATTERN_PCRE:
|
|
|
|
|
|
|
|
if (regex_cmp(class, criteria->class->regex) != 0) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (criteria->instance) {
|
|
|
|
if (criteria->instance) {
|
|
|
|
const char *instance = view_get_instance(view);
|
|
|
|
const char *instance = view_get_instance(view);
|
|
|
|
if (!instance || regex_cmp(instance, criteria->instance) != 0) {
|
|
|
|
if (!instance) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (criteria->instance->match_type) {
|
|
|
|
|
|
|
|
case PATTERN_FOCUSED:
|
|
|
|
|
|
|
|
if (focused && strcmp(instance, view_get_instance(focused))) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PATTERN_PCRE:
|
|
|
|
|
|
|
|
if (regex_cmp(instance, criteria->instance->regex) != 0) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (criteria->window_role) {
|
|
|
|
if (criteria->window_role) {
|
|
|
|
const char *role = view_get_window_role(view);
|
|
|
|
const char *window_role = view_get_window_role(view);
|
|
|
|
if (!role || regex_cmp(role, criteria->window_role) != 0) {
|
|
|
|
if (!window_role) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (criteria->window_role->match_type) {
|
|
|
|
|
|
|
|
case PATTERN_FOCUSED:
|
|
|
|
|
|
|
|
if (focused && strcmp(window_role, view_get_window_role(focused))) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PATTERN_PCRE:
|
|
|
|
|
|
|
|
if (regex_cmp(window_role, criteria->window_role->regex) != 0) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (criteria->window_type != ATOM_LAST) {
|
|
|
|
if (criteria->window_type != ATOM_LAST) {
|
|
|
@ -213,9 +338,23 @@ static bool criteria_matches_view(struct criteria *criteria,
|
|
|
|
|
|
|
|
|
|
|
|
if (criteria->workspace) {
|
|
|
|
if (criteria->workspace) {
|
|
|
|
struct sway_workspace *ws = view->container->workspace;
|
|
|
|
struct sway_workspace *ws = view->container->workspace;
|
|
|
|
if (!ws || regex_cmp(ws->name, criteria->workspace) != 0) {
|
|
|
|
if (!ws) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (criteria->workspace->match_type) {
|
|
|
|
|
|
|
|
case PATTERN_FOCUSED:
|
|
|
|
|
|
|
|
if (focused &&
|
|
|
|
|
|
|
|
strcmp(ws->name, focused->container->workspace->name)) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PATTERN_PCRE:
|
|
|
|
|
|
|
|
if (regex_cmp(ws->name, criteria->workspace->regex) != 0) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
@ -258,28 +397,6 @@ list_t *criteria_get_views(struct criteria *criteria) {
|
|
|
|
return matches;
|
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The error pointer is used for parsing functions, and saves having to pass it
|
|
|
|
|
|
|
|
// as an argument in several places.
|
|
|
|
|
|
|
|
char *error = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Returns error string on failure or NULL otherwise.
|
|
|
|
|
|
|
|
static bool generate_regex(pcre **regex, char *value) {
|
|
|
|
|
|
|
|
const char *reg_err;
|
|
|
|
|
|
|
|
int offset;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!*regex) {
|
|
|
|
|
|
|
|
const char *fmt = "Regex compilation for '%s' failed: %s";
|
|
|
|
|
|
|
|
int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3;
|
|
|
|
|
|
|
|
error = malloc(len);
|
|
|
|
|
|
|
|
snprintf(error, len, fmt, value, reg_err);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if HAVE_XWAYLAND
|
|
|
|
#if HAVE_XWAYLAND
|
|
|
|
static enum atom_name parse_window_type(const char *type) {
|
|
|
|
static enum atom_name parse_window_type(const char *type) {
|
|
|
|
if (strcasecmp(type, "normal") == 0) {
|
|
|
|
if (strcasecmp(type, "normal") == 0) {
|
|
|
@ -363,92 +480,6 @@ static enum criteria_token token_from_name(char *name) {
|
|
|
|
return T_INVALID;
|
|
|
|
return T_INVALID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Get a property of the focused view.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Note that we are taking the focused view at the time of criteria parsing, not
|
|
|
|
|
|
|
|
* at the time of execution. This is because __focused__ only makes sense when
|
|
|
|
|
|
|
|
* using criteria via IPC. Using __focused__ in config is not useful because
|
|
|
|
|
|
|
|
* criteria is only executed once per view.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
static char *get_focused_prop(enum criteria_token token, bool *autofail) {
|
|
|
|
|
|
|
|
struct sway_seat *seat = input_manager_current_seat();
|
|
|
|
|
|
|
|
struct sway_container *focus = seat_get_focused_container(seat);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct sway_view *view = focus ? focus->view : NULL;
|
|
|
|
|
|
|
|
const char *value = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch (token) {
|
|
|
|
|
|
|
|
case T_APP_ID:
|
|
|
|
|
|
|
|
*autofail = true;
|
|
|
|
|
|
|
|
if (view) {
|
|
|
|
|
|
|
|
value = view_get_app_id(view);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_SHELL:
|
|
|
|
|
|
|
|
*autofail = true;
|
|
|
|
|
|
|
|
if (view) {
|
|
|
|
|
|
|
|
value = view_get_shell(view);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_TITLE:
|
|
|
|
|
|
|
|
*autofail = true;
|
|
|
|
|
|
|
|
if (view) {
|
|
|
|
|
|
|
|
value = view_get_title(view);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_WORKSPACE:
|
|
|
|
|
|
|
|
*autofail = true;
|
|
|
|
|
|
|
|
if (focus && focus->workspace) {
|
|
|
|
|
|
|
|
value = focus->workspace->name;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CON_ID:
|
|
|
|
|
|
|
|
*autofail = true;
|
|
|
|
|
|
|
|
if (view && view->container) {
|
|
|
|
|
|
|
|
size_t id = view->container->node.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;
|
|
|
|
|
|
|
|
#if HAVE_XWAYLAND
|
|
|
|
|
|
|
|
case T_CLASS:
|
|
|
|
|
|
|
|
*autofail = true;
|
|
|
|
|
|
|
|
if (view) {
|
|
|
|
|
|
|
|
value = view_get_class(view);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_INSTANCE:
|
|
|
|
|
|
|
|
*autofail = true;
|
|
|
|
|
|
|
|
if (view) {
|
|
|
|
|
|
|
|
value = view_get_instance(view);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_WINDOW_ROLE:
|
|
|
|
|
|
|
|
*autofail = true;
|
|
|
|
|
|
|
|
if (view) {
|
|
|
|
|
|
|
|
value = view_get_window_role(view);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_WINDOW_TYPE: // These do not support __focused__
|
|
|
|
|
|
|
|
case T_ID:
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
case T_CON_MARK:
|
|
|
|
|
|
|
|
case T_FLOATING:
|
|
|
|
|
|
|
|
case T_TILING:
|
|
|
|
|
|
|
|
case T_URGENT:
|
|
|
|
|
|
|
|
case T_INVALID:
|
|
|
|
|
|
|
|
*autofail = false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
|
|
|
|
return strdup(value);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool parse_token(struct criteria *criteria, char *name, char *value) {
|
|
|
|
static bool parse_token(struct criteria *criteria, char *name, char *value) {
|
|
|
|
enum criteria_token token = token_from_name(name);
|
|
|
|
enum criteria_token token = token_from_name(name);
|
|
|
|
if (token == T_INVALID) {
|
|
|
|
if (token == T_INVALID) {
|
|
|
@ -459,20 +490,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *effective_value = NULL;
|
|
|
|
|
|
|
|
if (value && strcmp(value, "__focused__") == 0) {
|
|
|
|
|
|
|
|
bool autofail = false;
|
|
|
|
|
|
|
|
effective_value = get_focused_prop(token, &autofail);
|
|
|
|
|
|
|
|
if (!effective_value && autofail) {
|
|
|
|
|
|
|
|
criteria->autofail = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (value) {
|
|
|
|
|
|
|
|
effective_value = strdup(value);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Require value, unless token is floating or tiled
|
|
|
|
// Require value, unless token is floating or tiled
|
|
|
|
if (!effective_value && token != T_FLOATING && token != T_TILING) {
|
|
|
|
if (!value && token != T_FLOATING && token != T_TILING) {
|
|
|
|
const char *fmt = "Token '%s' requires a value";
|
|
|
|
const char *fmt = "Token '%s' requires a value";
|
|
|
|
int len = strlen(fmt) + strlen(name) - 1;
|
|
|
|
int len = strlen(fmt) + strlen(name) - 1;
|
|
|
|
error = malloc(len);
|
|
|
|
error = malloc(len);
|
|
|
@ -483,41 +502,48 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
|
|
|
|
char *endptr = NULL;
|
|
|
|
char *endptr = NULL;
|
|
|
|
switch (token) {
|
|
|
|
switch (token) {
|
|
|
|
case T_TITLE:
|
|
|
|
case T_TITLE:
|
|
|
|
generate_regex(&criteria->title, effective_value);
|
|
|
|
pattern_create(&criteria->title, value);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_SHELL:
|
|
|
|
case T_SHELL:
|
|
|
|
generate_regex(&criteria->shell, effective_value);
|
|
|
|
pattern_create(&criteria->shell, value);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_APP_ID:
|
|
|
|
case T_APP_ID:
|
|
|
|
generate_regex(&criteria->app_id, effective_value);
|
|
|
|
pattern_create(&criteria->app_id, value);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_CON_ID:
|
|
|
|
case T_CON_ID:
|
|
|
|
criteria->con_id = strtoul(effective_value, &endptr, 10);
|
|
|
|
if (strcmp(value, "__focused__") == 0) {
|
|
|
|
if (*endptr != 0) {
|
|
|
|
struct sway_seat *seat = input_manager_current_seat();
|
|
|
|
error = strdup("The value for 'con_id' should be '__focused__' or numeric");
|
|
|
|
struct sway_container *focus = seat_get_focused_container(seat);
|
|
|
|
|
|
|
|
struct sway_view *view = focus ? focus->view : NULL;
|
|
|
|
|
|
|
|
criteria->con_id = view ? view->container->node.id : 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
criteria->con_id = strtoul(value, &endptr, 10);
|
|
|
|
|
|
|
|
if (*endptr != 0) {
|
|
|
|
|
|
|
|
error = strdup("The value for 'con_id' should be '__focused__' or numeric");
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_CON_MARK:
|
|
|
|
case T_CON_MARK:
|
|
|
|
generate_regex(&criteria->con_mark, effective_value);
|
|
|
|
pattern_create(&criteria->con_mark, value);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
#if HAVE_XWAYLAND
|
|
|
|
#if HAVE_XWAYLAND
|
|
|
|
case T_CLASS:
|
|
|
|
case T_CLASS:
|
|
|
|
generate_regex(&criteria->class, effective_value);
|
|
|
|
pattern_create(&criteria->class, value);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_ID:
|
|
|
|
case T_ID:
|
|
|
|
criteria->id = strtoul(effective_value, &endptr, 10);
|
|
|
|
criteria->id = strtoul(value, &endptr, 10);
|
|
|
|
if (*endptr != 0) {
|
|
|
|
if (*endptr != 0) {
|
|
|
|
error = strdup("The value for 'id' should be numeric");
|
|
|
|
error = strdup("The value for 'id' should be numeric");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_INSTANCE:
|
|
|
|
case T_INSTANCE:
|
|
|
|
generate_regex(&criteria->instance, effective_value);
|
|
|
|
pattern_create(&criteria->instance, value);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_WINDOW_ROLE:
|
|
|
|
case T_WINDOW_ROLE:
|
|
|
|
generate_regex(&criteria->window_role, effective_value);
|
|
|
|
pattern_create(&criteria->window_role, value);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_WINDOW_TYPE:
|
|
|
|
case T_WINDOW_TYPE:
|
|
|
|
criteria->window_type = parse_window_type(effective_value);
|
|
|
|
criteria->window_type = parse_window_type(value);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
case T_FLOATING:
|
|
|
|
case T_FLOATING:
|
|
|
@ -527,13 +553,13 @@ 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(value, "latest") == 0 ||
|
|
|
|
strcmp(effective_value, "newest") == 0 ||
|
|
|
|
strcmp(value, "newest") == 0 ||
|
|
|
|
strcmp(effective_value, "last") == 0 ||
|
|
|
|
strcmp(value, "last") == 0 ||
|
|
|
|
strcmp(effective_value, "recent") == 0) {
|
|
|
|
strcmp(value, "recent") == 0) {
|
|
|
|
criteria->urgent = 'l';
|
|
|
|
criteria->urgent = 'l';
|
|
|
|
} else if (strcmp(effective_value, "oldest") == 0 ||
|
|
|
|
} else if (strcmp(value, "oldest") == 0 ||
|
|
|
|
strcmp(effective_value, "first") == 0) {
|
|
|
|
strcmp(value, "first") == 0) {
|
|
|
|
criteria->urgent = 'o';
|
|
|
|
criteria->urgent = 'o';
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
error =
|
|
|
|
error =
|
|
|
@ -542,12 +568,11 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_WORKSPACE:
|
|
|
|
case T_WORKSPACE:
|
|
|
|
generate_regex(&criteria->workspace, effective_value);
|
|
|
|
pattern_create(&criteria->workspace, value);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case T_INVALID:
|
|
|
|
case T_INVALID:
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(effective_value);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
if (error) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|