action: add If and ForEach actions

Add If and ForEach actions as described in OpenBox specification.

Limitations:

- If and ForEach cannot contain nested If and ForEach.
This commit is contained in:
Consus 2023-08-28 19:14:04 +03:00 committed by Johan Malm
parent 12f6a8975a
commit 22e3be40e5
4 changed files with 307 additions and 3 deletions

View file

@ -7,6 +7,7 @@
#include <unistd.h>
#include <wlr/util/log.h>
#include "action.h"
#include "common/array-size.h"
#include "common/list.h"
#include "common/mem.h"
#include "common/parse-bool.h"
@ -24,6 +25,8 @@ enum action_arg_type {
LAB_ACTION_ARG_STR = 0,
LAB_ACTION_ARG_BOOL,
LAB_ACTION_ARG_INT,
LAB_ACTION_ARG_QUERY_LIST,
LAB_ACTION_ARG_ACTION_LIST,
};
struct action_arg {
@ -48,6 +51,11 @@ struct action_arg_int {
int value;
};
struct action_arg_list {
struct action_arg base;
struct wl_list value;
};
enum action_type {
ACTION_TYPE_INVALID = 0,
ACTION_TYPE_NONE,
@ -82,6 +90,8 @@ enum action_type {
ACTION_TYPE_SNAP_TO_REGION,
ACTION_TYPE_TOGGLE_KEYBINDS,
ACTION_TYPE_FOCUS_OUTPUT,
ACTION_TYPE_IF,
ACTION_TYPE_FOR_EACH,
};
const char *action_names[] = {
@ -118,6 +128,8 @@ const char *action_names[] = {
"SnapToRegion",
"ToggleKeybinds",
"FocusOutput",
"If",
"ForEach",
NULL
};
@ -158,6 +170,30 @@ action_arg_add_int(struct action *action, const char *key, int value)
wl_list_append(&action->args, &arg->base.link);
}
static void
action_arg_add_list(struct action *action, const char *key, enum action_arg_type type)
{
assert(action);
assert(key);
struct action_arg_list *arg = znew(*arg);
arg->base.type = type;
arg->base.key = xstrdup(key);
wl_list_init(&arg->value);
wl_list_append(&action->args, &arg->base.link);
}
void
action_arg_add_querylist(struct action *action, const char *key)
{
action_arg_add_list(action, key, LAB_ACTION_ARG_QUERY_LIST);
}
void
action_arg_add_actionlist(struct action *action, const char *key)
{
action_arg_add_list(action, key, LAB_ACTION_ARG_ACTION_LIST);
}
static void *
action_get_arg(struct action *action, const char *key, enum action_arg_type type)
{
@ -193,6 +229,20 @@ action_get_int(struct action *action, const char *key, int default_value)
return arg ? arg->value : default_value;
}
struct wl_list *
action_get_querylist(struct action *action, const char *key)
{
struct action_arg_list *arg = action_get_arg(action, key, LAB_ACTION_ARG_QUERY_LIST);
return arg ? &arg->value : NULL;
}
struct wl_list *
action_get_actionlist(struct action *action, const char *key)
{
struct action_arg_list *arg = action_get_arg(action, key, LAB_ACTION_ARG_ACTION_LIST);
return arg ? &arg->value : NULL;
}
void
action_arg_from_xml_node(struct action *action, const char *nodename, const char *content)
{
@ -327,6 +377,20 @@ actions_contain_toggle_keybinds(struct wl_list *action_list)
return false;
}
static bool
action_list_is_valid(struct wl_list *actions)
{
assert(actions);
struct action *action;
wl_list_for_each(action, actions, link) {
if (!action_is_valid(action)) {
return false;
}
}
return true;
}
/* Checks for *required* arguments */
bool
action_is_valid(struct action *action)
@ -356,6 +420,19 @@ action_is_valid(struct action *action)
case ACTION_TYPE_FOCUS_OUTPUT:
arg_name = "output";
break;
case ACTION_TYPE_IF:
case ACTION_TYPE_FOR_EACH:
; /* works around "a label can only be part of a statement" */
static const char * const branches[] = { "then", "else" };
for (size_t i = 0; i < ARRAY_SIZE(branches); i++) {
struct wl_list *children = action_get_actionlist(action, branches[i]);
if (children && !action_list_is_valid(children)) {
wlr_log(WLR_ERROR, "Invalid action in %s '%s' branch",
action_names[action->type], branches[i]);
return false;
}
}
return true;
default:
/* No arguments required */
return true;
@ -381,6 +458,15 @@ action_free(struct action *action)
if (arg->type == LAB_ACTION_ARG_STR) {
struct action_arg_str *str_arg = (struct action_arg_str *)arg;
zfree(str_arg->value);
} else if (arg->type == LAB_ACTION_ARG_ACTION_LIST) {
struct action_arg_list *list_arg = (struct action_arg_list *)arg;
action_list_free(&list_arg->value);
} else if (arg->type == LAB_ACTION_ARG_QUERY_LIST) {
struct action_arg_list *list_arg = (struct action_arg_list *)arg;
struct view_query *elm, *next;
wl_list_for_each_safe(elm, next, &list_arg->value, link) {
view_query_free(elm);
}
}
zfree(arg);
}
@ -466,6 +552,31 @@ view_for_action(struct view *activator, struct server *server,
}
}
static void
run_if_action(struct view *view, struct server *server, struct action *action)
{
struct view_query *query;
struct wl_list *queries, *actions;
const char *branch = "then";
queries = action_get_querylist(action, "query");
if (queries) {
branch = "else";
/* All queries are OR'ed */
wl_list_for_each(query, queries, link) {
if (view_matches_query(view, query)) {
branch = "then";
break;
}
}
}
actions = action_get_actionlist(action, branch);
if (actions) {
actions_run(view, server, actions, 0);
}
}
void
actions_run(struct view *activator, struct server *server,
struct wl_list *actions, uint32_t resize_edges)
@ -693,6 +804,23 @@ actions_run(struct view *activator, struct server *server,
desktop_focus_output(output_from_name(server, output_name));
}
break;
case ACTION_TYPE_IF:
if (view) {
run_if_action(view, server, action);
}
break;
case ACTION_TYPE_FOR_EACH:
{
struct wl_array views;
struct view **item;
wl_array_init(&views);
view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE);
wl_array_for_each(item, &views) {
run_if_action(*item, server, action);
}
wl_array_release(&views);
}
break;
case ACTION_TYPE_INVALID:
wlr_log(WLR_ERROR, "Not executing unknown action");
break;