query: allow nested conditional actions

This commit is contained in:
Orfeas 2025-01-25 20:40:50 +02:00
parent b2623ce1e3
commit fb14ac9c0f

View file

@ -49,7 +49,8 @@ struct parser_state {
bool in_touch; bool in_touch;
bool in_libinput_category; bool in_libinput_category;
bool in_window_switcher_field; bool in_window_switcher_field;
bool in_window_rules; bool in_window_rule;
bool in_action;
bool in_action_query; bool in_action_query;
bool in_action_then_branch; bool in_action_then_branch;
bool in_action_else_branch; bool in_action_else_branch;
@ -60,14 +61,13 @@ struct parser_state {
struct touch_config_entry *current_touch; struct touch_config_entry *current_touch;
struct libinput_category *current_libinput_category; struct libinput_category *current_libinput_category;
const char *current_mouse_context; const char *current_mouse_context;
struct action *current_keybind_action;
struct action *current_mousebind_action;
struct region *current_region; struct region *current_region;
struct window_switcher_field *current_field; struct window_switcher_field *current_field;
struct window_rule *current_window_rule; struct window_rule *current_window_rule;
struct action *current_window_rule_action;
struct view_query *current_view_query; struct view_query *current_view_query;
struct action *current_child_action; struct action *current_inline_action;
struct wl_list *current_action_siblings;
struct action *current_action;
}; };
/* for backward compatibility of <mouse><scrollFactor> */ /* for backward compatibility of <mouse><scrollFactor> */
@ -279,10 +279,14 @@ fill_window_rule(char *nodename, char *content, struct parser_state *state)
state->current_window_rule->window_type = -1; // Window types are >= 0 state->current_window_rule->window_type = -1; // Window types are >= 0
wl_list_append(&rc.window_rules, &state->current_window_rule->link); wl_list_append(&rc.window_rules, &state->current_window_rule->link);
wl_list_init(&state->current_window_rule->actions); wl_list_init(&state->current_window_rule->actions);
state->current_action = NULL;
state->current_action_siblings = &state->current_window_rule->actions;
state->current_inline_action = NULL;
return; return;
} }
string_truncate_at_pattern(nodename, ".windowrule.windowrules"); string_truncate_at_pattern(nodename, ".windowrule.windowrules");
if (!content) { if (!content) {
/* nop */ /* nop */
} else if (!state->current_window_rule) { } else if (!state->current_window_rule) {
@ -328,16 +332,16 @@ fill_window_rule(char *nodename, char *content, struct parser_state *state)
/* Actions */ /* Actions */
} else if (!strcmp(nodename, "name.action")) { } else if (!strcmp(nodename, "name.action")) {
state->current_window_rule_action = action_create(content); state->current_inline_action = action_create(content);
if (state->current_window_rule_action) { if (state->current_inline_action) {
wl_list_append(&state->current_window_rule->actions, wl_list_append(&state->current_window_rule->actions,
&state->current_window_rule_action->link); &state->current_inline_action->link);
} }
} else if (!state->current_window_rule_action) { } else if (!state->current_inline_action) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. " wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content); "nodename: '%s' content: '%s'", nodename, content);
} else { } else {
action_arg_from_xml_node(state->current_window_rule_action, nodename, content); action_arg_from_xml_node(state->current_inline_action, nodename, content);
} }
} }
@ -399,15 +403,19 @@ fill_region(char *nodename, char *content, struct parser_state *state)
} }
static void static void
fill_action_query(char *nodename, char *content, struct action *action, struct parser_state *state) fill_action_query(char *nodename, char *content, struct parser_state *state)
{ {
if (!action) { if (!state->current_action) {
wlr_log(WLR_ERROR, "No parent action for query: %s=%s", nodename, content); wlr_log(WLR_ERROR, "No parent action for query: %s=%s", nodename, content);
return; return;
} }
string_truncate_at_pattern(nodename, ".windowrule.windowrules");
string_truncate_at_pattern(nodename, ".keybind.keyboard"); string_truncate_at_pattern(nodename, ".keybind.keyboard");
string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); string_truncate_at_pattern(nodename, ".mousebind.context.mouse");
string_truncate_at_pattern(nodename, ".then.action");
string_truncate_at_pattern(nodename, ".else.action");
string_truncate_at_pattern(nodename, ".none.action");
if (!strcasecmp(nodename, "query.action")) { if (!strcasecmp(nodename, "query.action")) {
state->current_view_query = NULL; state->current_view_query = NULL;
@ -420,10 +428,10 @@ fill_action_query(char *nodename, char *content, struct action *action, struct p
} }
if (!state->current_view_query) { if (!state->current_view_query) {
struct wl_list *queries = action_get_querylist(action, "query"); struct wl_list *queries = action_get_querylist(state->current_action, "query");
if (!queries) { if (!queries) {
action_arg_add_querylist(action, "query"); action_arg_add_querylist(state->current_action, "query");
queries = action_get_querylist(action, "query"); queries = action_get_querylist(state->current_action, "query");
} }
state->current_view_query = view_query_create(); state->current_view_query = view_query_create();
wl_list_append(queries, &state->current_view_query->link); wl_list_append(queries, &state->current_view_query->link);
@ -463,14 +471,9 @@ fill_action_query(char *nodename, char *content, struct action *action, struct p
} }
static void static void
fill_child_action(char *nodename, char *content, struct action *parent, fill_action(char *nodename, char *content, struct parser_state *state)
const char *branch_name, struct parser_state *state)
{ {
if (!parent) { string_truncate_at_pattern(nodename, ".windowrule.windowrules");
wlr_log(WLR_ERROR, "No parent action for branch: %s=%s", nodename, content);
return;
}
string_truncate_at_pattern(nodename, ".keybind.keyboard"); string_truncate_at_pattern(nodename, ".keybind.keyboard");
string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); string_truncate_at_pattern(nodename, ".mousebind.context.mouse");
string_truncate_at_pattern(nodename, ".then.action"); string_truncate_at_pattern(nodename, ".then.action");
@ -478,33 +481,60 @@ fill_child_action(char *nodename, char *content, struct action *parent,
string_truncate_at_pattern(nodename, ".none.action"); string_truncate_at_pattern(nodename, ".none.action");
if (!strcasecmp(nodename, "action")) { if (!strcasecmp(nodename, "action")) {
state->current_child_action = NULL;
}
if (!content) {
return; return;
} else if (!strcmp(nodename, "name.action")) {
state->current_action = action_create(content);
if (state->current_action) {
wl_list_append(state->current_action_siblings,
&state->current_action->link);
} }
} else if (!state->current_action) {
struct wl_list *siblings = action_get_actionlist(parent, branch_name);
if (!siblings) {
action_arg_add_actionlist(parent, branch_name);
siblings = action_get_actionlist(parent, branch_name);
}
if (!strcasecmp(nodename, "name.action")) {
if (!strcasecmp(content, "If") || !strcasecmp(content, "ForEach")) {
wlr_log(WLR_ERROR, "action '%s' cannot be a child action", content);
return;
}
state->current_child_action = action_create(content);
if (state->current_child_action) {
wl_list_append(siblings, &state->current_child_action->link);
}
} else if (!state->current_child_action) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. " wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content); "nodename: '%s' content: '%s'", nodename, content);
} else { } else {
action_arg_from_xml_node(state->current_child_action, nodename, content); action_arg_from_xml_node(state->current_action, nodename, content);
}
}
static void
fill_child_action(char *nodename, char *content, struct parser_state *state,
const char *branch_name)
{
string_truncate_at_pattern(nodename, ".windowrule.windowrules");
string_truncate_at_pattern(nodename, ".keybind.keyboard");
string_truncate_at_pattern(nodename, ".mousebind.context.mouse");
string_truncate_at_pattern(nodename, ".then.action");
string_truncate_at_pattern(nodename, ".else.action");
string_truncate_at_pattern(nodename, ".none.action");
if (!state->current_action) {
wlr_log(WLR_ERROR, "No parent action for branch: %s=%s", nodename, content);
return;
}
struct wl_list *siblings = action_get_actionlist(state->current_action, branch_name);
if (!siblings) {
action_arg_add_actionlist(state->current_action, branch_name);
siblings = action_get_actionlist(state->current_action, branch_name);
}
state->current_action_siblings = siblings;
if (!content) {
state->current_inline_action = NULL;
return;
}
if (!strcasecmp(nodename, "name.action")) {
state->current_inline_action = action_create(content);
if (state->current_inline_action) {
wl_list_append(state->current_action_siblings,
&state->current_inline_action->link);
}
} else if (!state->current_inline_action) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content);
} else {
action_arg_from_xml_node(state->current_inline_action, nodename, content);
} }
} }
@ -514,10 +544,10 @@ fill_keybind(char *nodename, char *content, struct parser_state *state)
if (!content) { if (!content) {
return; return;
} }
string_truncate_at_pattern(nodename, ".keybind.keyboard"); string_truncate_at_pattern(nodename, ".keybind.keyboard");
if (!strcmp(nodename, "key")) { if (!strcmp(nodename, "key")) {
state->current_keybind = keybind_create(content); state->current_keybind = keybind_create(content);
state->current_keybind_action = NULL;
/* /*
* If an invalid keybind has been provided, * If an invalid keybind has been provided,
* keybind_create() complains. * keybind_create() complains.
@ -526,6 +556,9 @@ fill_keybind(char *nodename, char *content, struct parser_state *state)
wlr_log(WLR_ERROR, "Invalid keybind: %s", content); wlr_log(WLR_ERROR, "Invalid keybind: %s", content);
return; return;
} }
state->current_action = NULL;
state->current_action_siblings = &state->current_keybind->actions;
state->current_inline_action = NULL;
} else if (!state->current_keybind) { } else if (!state->current_keybind) {
wlr_log(WLR_ERROR, "expect <keybind key=\"\"> element first. " wlr_log(WLR_ERROR, "expect <keybind key=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content); "nodename: '%s' content: '%s'", nodename, content);
@ -536,12 +569,12 @@ fill_keybind(char *nodename, char *content, struct parser_state *state)
} else if (!strcasecmp(nodename, "allowWhenLocked")) { } else if (!strcasecmp(nodename, "allowWhenLocked")) {
set_bool(content, &state->current_keybind->allow_when_locked); set_bool(content, &state->current_keybind->allow_when_locked);
} else if (!strcmp(nodename, "name.action")) { } else if (!strcmp(nodename, "name.action")) {
state->current_keybind_action = action_create(content); state->current_inline_action = action_create(content);
if (state->current_keybind_action) { if (state->current_inline_action) {
wl_list_append(&state->current_keybind->actions, wl_list_append(&state->current_keybind->actions,
&state->current_keybind_action->link); &state->current_inline_action->link);
} }
} else if (!state->current_keybind_action) { } else if (!state->current_inline_action) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. " wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content); "nodename: '%s' content: '%s'", nodename, content);
} else { } else {
@ -550,7 +583,7 @@ fill_keybind(char *nodename, char *content, struct parser_state *state)
* <region>, <direction> and so on. This is common to key- and * <region>, <direction> and so on. This is common to key- and
* mousebinds. * mousebinds.
*/ */
action_arg_from_xml_node(state->current_keybind_action, nodename, content); action_arg_from_xml_node(state->current_inline_action, nodename, content);
} }
} }
@ -574,7 +607,13 @@ fill_mousebind(char *nodename, char *content, struct parser_state *state)
wlr_log(WLR_INFO, "create mousebind for %s", wlr_log(WLR_INFO, "create mousebind for %s",
state->current_mouse_context); state->current_mouse_context);
state->current_mousebind = mousebind_create(state->current_mouse_context); state->current_mousebind = mousebind_create(state->current_mouse_context);
state->current_mousebind_action = NULL; if (!state->current_mousebind) {
wlr_log(WLR_ERROR, "Invalid mousebind: %s", content);
return;
}
state->current_action = NULL;
state->current_action_siblings = &state->current_mousebind->actions;
state->current_inline_action = NULL;
return; return;
} else if (!content) { } else if (!content) {
return; return;
@ -596,16 +635,16 @@ fill_mousebind(char *nodename, char *content, struct parser_state *state)
state->current_mousebind->mouse_event = state->current_mousebind->mouse_event =
mousebind_event_from_str(content); mousebind_event_from_str(content);
} else if (!strcmp(nodename, "name.action")) { } else if (!strcmp(nodename, "name.action")) {
state->current_mousebind_action = action_create(content); state->current_inline_action = action_create(content);
if (state->current_mousebind_action) { if (state->current_inline_action) {
wl_list_append(&state->current_mousebind->actions, wl_list_append(&state->current_mousebind->actions,
&state->current_mousebind_action->link); &state->current_inline_action->link);
} }
} else if (!state->current_mousebind_action) { } else if (!state->current_inline_action) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. " wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content); "nodename: '%s' content: '%s'", nodename, content);
} else { } else {
action_arg_from_xml_node(state->current_mousebind_action, nodename, content); action_arg_from_xml_node(state->current_inline_action, nodename, content);
} }
} }
@ -954,40 +993,29 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state)
if (state->in_usable_area_override) { if (state->in_usable_area_override) {
fill_usable_area_override(nodename, content, state); fill_usable_area_override(nodename, content, state);
} }
if (state->in_keybind) {
if ((state->in_keybind || state->in_mousebind ||
state->in_window_rule) && state->in_action) {
if (state->in_action_query) { if (state->in_action_query) {
fill_action_query(nodename, content, fill_action_query(nodename, content, state);
state->current_keybind_action, state);
} else if (state->in_action_then_branch) { } else if (state->in_action_then_branch) {
fill_child_action(nodename, content, fill_child_action(nodename, content, state, "then");
state->current_keybind_action, "then", state);
} else if (state->in_action_else_branch) { } else if (state->in_action_else_branch) {
fill_child_action(nodename, content, fill_child_action(nodename, content, state, "else");
state->current_keybind_action, "else", state);
} else if (state->in_action_none_branch) { } else if (state->in_action_none_branch) {
fill_child_action(nodename, content, fill_child_action(nodename, content, state, "none");
state->current_keybind_action, "none", state);
} else { } else {
fill_action(nodename, content, state);
}
return;
} else if (state->in_keybind) {
fill_keybind(nodename, content, state); fill_keybind(nodename, content, state);
} return;
} } else if (state->in_mousebind) {
if (state->in_mousebind) {
if (state->in_action_query) {
fill_action_query(nodename, content,
state->current_mousebind_action, state);
} else if (state->in_action_then_branch) {
fill_child_action(nodename, content,
state->current_mousebind_action, "then", state);
} else if (state->in_action_else_branch) {
fill_child_action(nodename, content,
state->current_mousebind_action, "else", state);
} else if (state->in_action_none_branch) {
fill_child_action(nodename, content,
state->current_mousebind_action, "none", state);
} else {
fill_mousebind(nodename, content, state); fill_mousebind(nodename, content, state);
return;
} }
}
if (state->in_touch) { if (state->in_touch) {
fill_touch(nodename, content, state); fill_touch(nodename, content, state);
return; return;
@ -1004,7 +1032,7 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state)
fill_window_switcher_field(nodename, content, state); fill_window_switcher_field(nodename, content, state);
return; return;
} }
if (state->in_window_rules) { if (state->in_window_rule) {
fill_window_rule(nodename, content, state); fill_window_rule(nodename, content, state);
return; return;
} }
@ -1352,10 +1380,22 @@ xml_tree_walk(xmlNode *node, struct parser_state *state)
state->in_window_switcher_field = false; state->in_window_switcher_field = false;
continue; continue;
} }
if (!strcasecmp((char *)n->name, "windowRules")) { if (!strcasecmp((char *)n->name, "windowRule")) {
state->in_window_rules = true; state->in_window_rule = true;
traverse(n, state); traverse(n, state);
state->in_window_rules = false; state->in_window_rule = false;
continue;
}
if (!strcasecmp((char *)n->name, "action")) {
struct parser_state new_state = *state;
new_state.current_action = NULL;
new_state.current_view_query = NULL;
new_state.in_action = true;
new_state.in_action_query = false;
new_state.in_action_then_branch = false;
new_state.in_action_else_branch = false;
new_state.in_action_none_branch = false;
traverse(n, &new_state);
continue; continue;
} }
if (!strcasecmp((char *)n->name, "query")) { if (!strcasecmp((char *)n->name, "query")) {