criteria: fix __focused__ when no focus or unset

This fixes the behavior of `__focused__` when there is no focused view
to match i3's behavior of successfully matching no views instead of
returning an error of a missing value. It also applies the same logic
when a token is not applicable (or unset) for a view such as `app_id`
for a focused xwayland view or `class` for a focused xdg-shell view.

This adds an `autofail` boolean to `struct criteria`. If it is set to
`true`, then `criteria_matches_view` will immediately bail out as a
no match. If `autofail` is set, the criteria will also not be
considered empty by `criteria_is_empty`.

To set this new `autofail` property, `get_focused_prop` will now take
in a boolean pointer of the same name. If `__focused__` is supported
for the token and there is no focused view or the focused view does not
have a value for the token, then the boolean will be set to true. In
`parse_token`, the boolean value will be checked and if set to true,
then `criteria->autofail` will be set to true and `parse_token` will
bail successfully. Tokens will still be parsed to make sure the whole
criteria is syntactically valid, which is also why
`&criteria->autofail` is not passed to `get_focused_prop` and a local
boolean is declared in `parse_token`.
This commit is contained in:
Brian Ashworth 2019-03-23 03:49:29 -04:00 committed by emersion
parent 8cd7f0171a
commit 7d2076cbff
2 changed files with 49 additions and 21 deletions

View file

@ -20,6 +20,7 @@ struct criteria {
char *cmdlist; char *cmdlist;
char *target; // workspace or output name for `assign` criteria char *target; // workspace or output name for `assign` criteria
bool autofail; // __focused__ while no focus or n/a for focused view
pcre *title; pcre *title;
pcre *shell; pcre *shell;
pcre *app_id; pcre *app_id;

View file

@ -16,7 +16,8 @@
#include "config.h" #include "config.h"
bool criteria_is_empty(struct criteria *criteria) { bool criteria_is_empty(struct criteria *criteria) {
return !criteria->title return !criteria->autofail
&& !criteria->title
&& !criteria->shell && !criteria->shell
&& !criteria->app_id && !criteria->app_id
&& !criteria->con_mark && !criteria->con_mark
@ -98,6 +99,10 @@ 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) {
return false;
}
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 || regex_cmp(title, criteria->title) != 0) {
@ -366,50 +371,66 @@ static enum criteria_token token_from_name(char *name) {
* using criteria via IPC. Using __focused__ in config is not useful because * using criteria via IPC. Using __focused__ in config is not useful because
* criteria is only executed once per view. * criteria is only executed once per view.
*/ */
static char *get_focused_prop(enum criteria_token token) { static char *get_focused_prop(enum criteria_token token, bool *autofail) {
struct sway_seat *seat = input_manager_current_seat(); struct sway_seat *seat = input_manager_current_seat();
struct sway_container *focus = seat_get_focused_container(seat); struct sway_container *focus = seat_get_focused_container(seat);
if (!focus || !focus->view) { struct sway_view *view = focus ? focus->view : NULL;
return NULL;
}
struct sway_view *view = focus->view;
const char *value = NULL; const char *value = NULL;
switch (token) { switch (token) {
case T_APP_ID: case T_APP_ID:
value = view_get_app_id(view); *autofail = true;
if (view) {
value = view_get_app_id(view);
}
break; break;
case T_SHELL: case T_SHELL:
value = view_get_shell(view); *autofail = true;
if (view) {
value = view_get_shell(view);
}
break; break;
case T_TITLE: case T_TITLE:
value = view_get_title(view); *autofail = true;
if (view) {
value = view_get_title(view);
}
break; break;
case T_WORKSPACE: case T_WORKSPACE:
if (focus->workspace) { *autofail = true;
if (focus && focus->workspace) {
value = focus->workspace->name; value = focus->workspace->name;
} }
break; break;
case T_CON_ID: case T_CON_ID:
if (view->container == NULL) { *autofail = true;
return NULL; 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;
} }
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; break;
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
case T_CLASS: case T_CLASS:
value = view_get_class(view); *autofail = true;
if (view) {
value = view_get_class(view);
}
break; break;
case T_INSTANCE: case T_INSTANCE:
value = view_get_instance(view); *autofail = true;
if (view) {
value = view_get_instance(view);
}
break; break;
case T_WINDOW_ROLE: case T_WINDOW_ROLE:
value = view_get_window_role(view); *autofail = true;
if (view) {
value = view_get_window_role(view);
}
break; break;
case T_WINDOW_TYPE: // These do not support __focused__ case T_WINDOW_TYPE: // These do not support __focused__
case T_ID: case T_ID:
@ -419,6 +440,7 @@ static char *get_focused_prop(enum criteria_token token) {
case T_TILING: case T_TILING:
case T_URGENT: case T_URGENT:
case T_INVALID: case T_INVALID:
*autofail = false;
break; break;
} }
if (value) { if (value) {
@ -439,7 +461,12 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
char *effective_value = NULL; char *effective_value = NULL;
if (value && strcmp(value, "__focused__") == 0) { if (value && strcmp(value, "__focused__") == 0) {
effective_value = get_focused_prop(token); bool autofail = false;
effective_value = get_focused_prop(token, &autofail);
if (!effective_value && autofail) {
criteria->autofail = true;
return true;
}
} else if (value) { } else if (value) {
effective_value = strdup(value); effective_value = strdup(value);
} }