|
|
|
@ -190,19 +190,128 @@ static bool generate_regex(pcre **regex, char *value) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum criteria_token {
|
|
|
|
|
T_APP_ID,
|
|
|
|
|
T_CLASS,
|
|
|
|
|
T_CON_ID,
|
|
|
|
|
T_CON_MARK,
|
|
|
|
|
T_FLOATING,
|
|
|
|
|
T_ID,
|
|
|
|
|
T_INSTANCE,
|
|
|
|
|
T_TILING,
|
|
|
|
|
T_TITLE,
|
|
|
|
|
T_URGENT,
|
|
|
|
|
T_WINDOW_ROLE,
|
|
|
|
|
T_WINDOW_TYPE,
|
|
|
|
|
T_WORKSPACE,
|
|
|
|
|
|
|
|
|
|
T_INVALID,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static enum criteria_token token_from_name(char *name) {
|
|
|
|
|
if (strcmp(name, "app_id") == 0) {
|
|
|
|
|
return T_APP_ID;
|
|
|
|
|
} else if (strcmp(name, "class") == 0) {
|
|
|
|
|
return T_CLASS;
|
|
|
|
|
} else if (strcmp(name, "con_id") == 0) {
|
|
|
|
|
return T_CON_ID;
|
|
|
|
|
} else if (strcmp(name, "con_mark") == 0) {
|
|
|
|
|
return T_CON_MARK;
|
|
|
|
|
} else if (strcmp(name, "id") == 0) {
|
|
|
|
|
return T_ID;
|
|
|
|
|
} else if (strcmp(name, "instance") == 0) {
|
|
|
|
|
return T_INSTANCE;
|
|
|
|
|
} else if (strcmp(name, "title") == 0) {
|
|
|
|
|
return T_TITLE;
|
|
|
|
|
} else if (strcmp(name, "urgent") == 0) {
|
|
|
|
|
return T_URGENT;
|
|
|
|
|
} else if (strcmp(name, "window_role") == 0) {
|
|
|
|
|
return T_WINDOW_ROLE;
|
|
|
|
|
} else if (strcmp(name, "window_type") == 0) {
|
|
|
|
|
return T_WINDOW_TYPE;
|
|
|
|
|
} else if (strcmp(name, "workspace") == 0) {
|
|
|
|
|
return T_WORKSPACE;
|
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
|
struct sway_seat *seat = input_manager_current_seat(input_manager);
|
|
|
|
|
struct sway_container *focus = seat_get_focus(seat);
|
|
|
|
|
|
|
|
|
|
if (!focus || focus->type != C_VIEW) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
struct sway_view *view = focus->sway_view;
|
|
|
|
|
const char *value = NULL;
|
|
|
|
|
|
|
|
|
|
switch (token) {
|
|
|
|
|
case T_APP_ID:
|
|
|
|
|
value = view_get_app_id(view);
|
|
|
|
|
break;
|
|
|
|
|
case T_CLASS:
|
|
|
|
|
value = view_get_class(view);
|
|
|
|
|
break;
|
|
|
|
|
case T_INSTANCE:
|
|
|
|
|
value = view_get_instance(view);
|
|
|
|
|
break;
|
|
|
|
|
case T_TITLE:
|
|
|
|
|
value = view_get_class(view);
|
|
|
|
|
break;
|
|
|
|
|
case T_WINDOW_ROLE:
|
|
|
|
|
value = view_get_class(view);
|
|
|
|
|
break;
|
|
|
|
|
case T_WORKSPACE:
|
|
|
|
|
{
|
|
|
|
|
struct sway_container *ws = container_parent(focus, C_WORKSPACE);
|
|
|
|
|
if (ws) {
|
|
|
|
|
value = ws->name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case T_CON_ID: // These do not support __focused__
|
|
|
|
|
case T_CON_MARK:
|
|
|
|
|
case T_FLOATING:
|
|
|
|
|
case T_ID:
|
|
|
|
|
case T_TILING:
|
|
|
|
|
case T_URGENT:
|
|
|
|
|
case T_WINDOW_TYPE:
|
|
|
|
|
case T_INVALID:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (value) {
|
|
|
|
|
return strdup(value);
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool parse_token(struct criteria *criteria, char *name, char *value) {
|
|
|
|
|
enum criteria_token token = token_from_name(name);
|
|
|
|
|
if (token == T_INVALID) {
|
|
|
|
|
const char *fmt = "Token '%s' is not recognized";
|
|
|
|
|
int len = strlen(fmt) + strlen(name) - 1;
|
|
|
|
|
error = malloc(len);
|
|
|
|
|
snprintf(error, len, fmt, name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *effective_value = NULL;
|
|
|
|
|
if (value && strcmp(value, "__focused__") == 0) {
|
|
|
|
|
effective_value = get_focused_prop(token);
|
|
|
|
|
} else if (value) {
|
|
|
|
|
effective_value = strdup(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Require value, unless token is floating or tiled
|
|
|
|
|
if (!value && (strcmp(name, "title") == 0
|
|
|
|
|
|| strcmp(name, "app_id") == 0
|
|
|
|
|
|| strcmp(name, "class") == 0
|
|
|
|
|
|| strcmp(name, "instance") == 0
|
|
|
|
|
|| strcmp(name, "con_id") == 0
|
|
|
|
|
|| strcmp(name, "con_mark") == 0
|
|
|
|
|
|| strcmp(name, "window_role") == 0
|
|
|
|
|
|| strcmp(name, "window_type") == 0
|
|
|
|
|
|| strcmp(name, "id") == 0
|
|
|
|
|
|| strcmp(name, "urgent") == 0
|
|
|
|
|
|| strcmp(name, "workspace") == 0)) {
|
|
|
|
|
if (!effective_value && token != T_FLOATING && token != T_TILING) {
|
|
|
|
|
const char *fmt = "Token '%s' requires a value";
|
|
|
|
|
int len = strlen(fmt) + strlen(name) - 1;
|
|
|
|
|
error = malloc(len);
|
|
|
|
@ -210,53 +319,64 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(name, "title") == 0) {
|
|
|
|
|
generate_regex(&criteria->title, value);
|
|
|
|
|
} else if (strcmp(name, "app_id") == 0) {
|
|
|
|
|
generate_regex(&criteria->app_id, value);
|
|
|
|
|
} else if (strcmp(name, "class") == 0) {
|
|
|
|
|
generate_regex(&criteria->class, value);
|
|
|
|
|
} else if (strcmp(name, "instance") == 0) {
|
|
|
|
|
generate_regex(&criteria->instance, value);
|
|
|
|
|
} else if (strcmp(name, "con_id") == 0) {
|
|
|
|
|
char *endptr;
|
|
|
|
|
criteria->con_id = strtoul(value, &endptr, 10);
|
|
|
|
|
char *endptr = NULL;
|
|
|
|
|
switch (token) {
|
|
|
|
|
case T_TITLE:
|
|
|
|
|
generate_regex(&criteria->title, effective_value);
|
|
|
|
|
break;
|
|
|
|
|
case T_APP_ID:
|
|
|
|
|
generate_regex(&criteria->app_id, effective_value);
|
|
|
|
|
break;
|
|
|
|
|
case T_CLASS:
|
|
|
|
|
generate_regex(&criteria->class, effective_value);
|
|
|
|
|
break;
|
|
|
|
|
case T_INSTANCE:
|
|
|
|
|
generate_regex(&criteria->instance, effective_value);
|
|
|
|
|
break;
|
|
|
|
|
case T_CON_ID:
|
|
|
|
|
criteria->con_id = strtoul(effective_value, &endptr, 10);
|
|
|
|
|
if (*endptr != 0) {
|
|
|
|
|
error = strdup("The value for 'con_id' should be numeric");
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(name, "con_mark") == 0) {
|
|
|
|
|
generate_regex(&criteria->con_mark, value);
|
|
|
|
|
} else if (strcmp(name, "window_role") == 0) {
|
|
|
|
|
generate_regex(&criteria->window_role, value);
|
|
|
|
|
} else if (strcmp(name, "window_type") == 0) {
|
|
|
|
|
break;
|
|
|
|
|
case T_CON_MARK:
|
|
|
|
|
generate_regex(&criteria->con_mark, effective_value);
|
|
|
|
|
break;
|
|
|
|
|
case T_WINDOW_ROLE:
|
|
|
|
|
generate_regex(&criteria->window_role, effective_value);
|
|
|
|
|
break;
|
|
|
|
|
case T_WINDOW_TYPE:
|
|
|
|
|
// TODO: This is a string but will be stored as an enum or integer
|
|
|
|
|
} else if (strcmp(name, "id") == 0) {
|
|
|
|
|
char *endptr;
|
|
|
|
|
criteria->id = strtoul(value, &endptr, 10);
|
|
|
|
|
break;
|
|
|
|
|
case T_ID:
|
|
|
|
|
criteria->id = strtoul(effective_value, &endptr, 10);
|
|
|
|
|
if (*endptr != 0) {
|
|
|
|
|
error = strdup("The value for 'id' should be numeric");
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(name, "floating") == 0) {
|
|
|
|
|
break;
|
|
|
|
|
case T_FLOATING:
|
|
|
|
|
criteria->floating = true;
|
|
|
|
|
} else if (strcmp(name, "tiling") == 0) {
|
|
|
|
|
break;
|
|
|
|
|
case T_TILING:
|
|
|
|
|
criteria->tiling = true;
|
|
|
|
|
} else if (strcmp(name, "urgent") == 0) {
|
|
|
|
|
if (strcmp(value, "latest") == 0) {
|
|
|
|
|
break;
|
|
|
|
|
case T_URGENT:
|
|
|
|
|
if (strcmp(effective_value, "latest") == 0) {
|
|
|
|
|
criteria->urgent = 'l';
|
|
|
|
|
} else if (strcmp(value, "oldest") == 0) {
|
|
|
|
|
} else if (strcmp(effective_value, "oldest") == 0) {
|
|
|
|
|
criteria->urgent = 'o';
|
|
|
|
|
} else {
|
|
|
|
|
error =
|
|
|
|
|
strdup("The value for 'urgent' must be 'latest' or 'oldest'");
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(name, "workspace") == 0) {
|
|
|
|
|
criteria->workspace = strdup(value);
|
|
|
|
|
} else {
|
|
|
|
|
const char *fmt = "Token '%s' is not recognized";
|
|
|
|
|
int len = strlen(fmt) + strlen(name) - 1;
|
|
|
|
|
error = malloc(len);
|
|
|
|
|
snprintf(error, len, fmt, name);
|
|
|
|
|
break;
|
|
|
|
|
case T_WORKSPACE:
|
|
|
|
|
criteria->workspace = strdup(effective_value);
|
|
|
|
|
break;
|
|
|
|
|
case T_INVALID:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
free(effective_value);
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
return false;
|
|
|
|
|