rcxml: rewrite action parser

This commit rewrites the nested action parser into append_actions() which
is used by following commits. At this point, it's not used yet and parsing
"If" action is temporarily disabled.
This commit is contained in:
tokyo4j 2025-04-12 00:57:03 +09:00 committed by Johan Malm
parent 8881841098
commit 9462457cc2

View file

@ -432,112 +432,141 @@ 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(struct action *action, xmlNode *node, struct view_query *query)
{ {
if (!action) { xmlNode *child;
wlr_log(WLR_ERROR, "No parent action for query: %s=%s", nodename, content); char *key, *content;
return; LAB_XML_FOR_EACH(node, child, key, content) {
} if (!strcasecmp(key, "identifier")) {
xstrdup_replace(query->identifier, content);
string_truncate_at_pattern(nodename, ".keybind.keyboard"); } else if (!strcasecmp(key, "title")) {
string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); xstrdup_replace(query->title, content);
} else if (!strcmp(key, "type")) {
if (!strcasecmp(nodename, "query.action")) { query->window_type = parse_window_type(content);
state->current_view_query = NULL; } else if (!strcasecmp(key, "sandboxEngine")) {
} xstrdup_replace(query->sandbox_engine, content);
} else if (!strcasecmp(key, "sandboxAppId")) {
string_truncate_at_pattern(nodename, ".query.action"); xstrdup_replace(query->sandbox_app_id, content);
} else if (!strcasecmp(key, "shaded")) {
if (!content) { query->shaded = parse_three_state(content);
return; } else if (!strcasecmp(key, "maximized")) {
} query->maximized = view_axis_parse(content);
} else if (!strcasecmp(key, "iconified")) {
if (!state->current_view_query) { query->iconified = parse_three_state(content);
struct wl_list *queries = action_get_querylist(action, "query"); } else if (!strcasecmp(key, "focused")) {
if (!queries) { query->focused = parse_three_state(content);
action_arg_add_querylist(action, "query"); } else if (!strcasecmp(key, "omnipresent")) {
queries = action_get_querylist(action, "query"); query->omnipresent = parse_three_state(content);
} else if (!strcasecmp(key, "tiled")) {
query->tiled = view_edge_parse(content);
} else if (!strcasecmp(key, "tiled_region")) {
xstrdup_replace(query->tiled_region, content);
} else if (!strcasecmp(key, "desktop")) {
xstrdup_replace(query->desktop, content);
} else if (!strcasecmp(key, "decoration")) {
query->decoration = ssd_mode_parse(content);
} else if (!strcasecmp(key, "monitor")) {
xstrdup_replace(query->monitor, content);
} }
state->current_view_query = view_query_create();
wl_list_append(queries, &state->current_view_query->link);
}
if (!strcasecmp(nodename, "identifier")) {
xstrdup_replace(state->current_view_query->identifier, content);
} else if (!strcasecmp(nodename, "title")) {
xstrdup_replace(state->current_view_query->title, content);
} else if (!strcmp(nodename, "type")) {
state->current_view_query->window_type = parse_window_type(content);
} else if (!strcasecmp(nodename, "sandboxEngine")) {
xstrdup_replace(state->current_view_query->sandbox_engine, content);
} else if (!strcasecmp(nodename, "sandboxAppId")) {
xstrdup_replace(state->current_view_query->sandbox_app_id, content);
} else if (!strcasecmp(nodename, "shaded")) {
state->current_view_query->shaded = parse_three_state(content);
} else if (!strcasecmp(nodename, "maximized")) {
state->current_view_query->maximized = view_axis_parse(content);
} else if (!strcasecmp(nodename, "iconified")) {
state->current_view_query->iconified = parse_three_state(content);
} else if (!strcasecmp(nodename, "focused")) {
state->current_view_query->focused = parse_three_state(content);
} else if (!strcasecmp(nodename, "omnipresent")) {
state->current_view_query->omnipresent = parse_three_state(content);
} else if (!strcasecmp(nodename, "tiled")) {
state->current_view_query->tiled = view_edge_parse(content);
} else if (!strcasecmp(nodename, "tiled_region")) {
xstrdup_replace(state->current_view_query->tiled_region, content);
} else if (!strcasecmp(nodename, "desktop")) {
xstrdup_replace(state->current_view_query->desktop, content);
} else if (!strcasecmp(nodename, "decoration")) {
state->current_view_query->decoration = ssd_mode_parse(content);
} else if (!strcasecmp(nodename, "monitor")) {
xstrdup_replace(state->current_view_query->monitor, content);
} }
} }
static void append_actions(xmlNode *node, struct wl_list *list);
static void static void
fill_child_action(char *nodename, char *content, struct action *parent, parse_action_args(xmlNode *node, struct action *action)
const char *branch_name, struct parser_state *state)
{ {
if (!parent) { xmlNode *child;
wlr_log(WLR_ERROR, "No parent action for branch: %s=%s", nodename, content); char *key, *content;
return; LAB_XML_FOR_EACH(node, child, key, content) {
} if (!strcasecmp(key, "query")) {
struct wl_list *querylist =
string_truncate_at_pattern(nodename, ".keybind.keyboard"); action_get_querylist(action, "query");
string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); if (!querylist) {
string_truncate_at_pattern(nodename, ".then.action"); action_arg_add_querylist(action, "query");
string_truncate_at_pattern(nodename, ".else.action"); querylist = action_get_querylist(action, "query");
string_truncate_at_pattern(nodename, ".none.action"); }
struct view_query *query = view_query_create();
if (!strcasecmp(nodename, "action")) { fill_action_query(action, child, query);
state->current_child_action = NULL; wl_list_append(querylist, &query->link);
} } else if (!strcasecmp(key, "then")) {
struct wl_list *actions =
if (!content) { action_get_actionlist(action, "then");
return; if (!actions) {
} action_arg_add_actionlist(action, "then");
actions = action_get_actionlist(action, "then");
struct wl_list *siblings = action_get_actionlist(parent, branch_name); }
if (!siblings) { append_actions(child, actions);
action_arg_add_actionlist(parent, branch_name); } else if (!strcasecmp(key, "else")) {
siblings = action_get_actionlist(parent, branch_name); struct wl_list *actions =
} action_get_actionlist(action, "else");
if (!actions) {
if (!strcasecmp(nodename, "name.action")) { action_arg_add_actionlist(action, "else");
if (!strcasecmp(content, "If") || !strcasecmp(content, "ForEach")) { actions = action_get_actionlist(action, "else");
wlr_log(WLR_ERROR, "action '%s' cannot be a child action", content); }
return; append_actions(child, actions);
} else if (!strcasecmp(key, "none")) {
struct wl_list *actions =
action_get_actionlist(action, "none");
if (!actions) {
action_arg_add_actionlist(action, "none");
actions = action_get_actionlist(action, "none");
}
append_actions(child, actions);
} else if (!strcasecmp(key, "name")) {
/* Ignore <action name=""> */
} else if (lab_xml_node_is_leaf(child)) {
/* Handle normal action args */
char buffer[256];
char *node_name = nodename(child, buffer, sizeof(buffer));
action_arg_from_xml_node(action, node_name, content);
} else {
/* Handle nested args like <position><x> in ShowMenu */
parse_action_args(child, action);
} }
state->current_child_action = action_create(content); }
if (state->current_child_action) { }
wl_list_append(siblings, &state->current_child_action->link);
static struct action *
parse_action(xmlNode *node)
{
char name[256];
struct action *action = NULL;
if (lab_xml_get_string(node, "name", name, sizeof(name))) {
action = action_create(name);
}
if (!action) {
return NULL;
}
parse_action_args(node, action);
return action;
}
static void
append_actions(xmlNode *node, struct wl_list *list)
{
xmlNode *child;
char *key, *content;
LAB_XML_FOR_EACH(node, child, key, content) {
if (strcasecmp(key, "action")) {
continue;
}
if (lab_xml_node_is_leaf(child)) {
/*
* A mousebind contains two types of "action" nodes:
* <mousebind button="Left" action="Click">
* <action name="Close" />
* </mousebind>
* The first node (action="Click") is skipped.
*/
continue;
}
struct action *action = parse_action(child);
if (action) {
wl_list_append(list, &action->link);
} }
} else if (!state->current_child_action) {
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
"nodename: '%s' content: '%s'", nodename, content);
} else {
action_arg_from_xml_node(state->current_child_action, nodename, content);
} }
} }
@ -1055,38 +1084,10 @@ entry(xmlNode *node, char *nodename, char *content, struct parser_state *state)
fill_usable_area_override(nodename, content, state); fill_usable_area_override(nodename, content, state);
} }
if (state->in_keybind) { if (state->in_keybind) {
if (state->in_action_query) { fill_keybind(nodename, content, state);
fill_action_query(nodename, content,
state->current_keybind_action, state);
} else if (state->in_action_then_branch) {
fill_child_action(nodename, content,
state->current_keybind_action, "then", state);
} else if (state->in_action_else_branch) {
fill_child_action(nodename, content,
state->current_keybind_action, "else", state);
} else if (state->in_action_none_branch) {
fill_child_action(nodename, content,
state->current_keybind_action, "none", state);
} else {
fill_keybind(nodename, content, state);
}
} }
if (state->in_mousebind) { if (state->in_mousebind) {
if (state->in_action_query) { fill_mousebind(nodename, content, state);
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);
}
} }
if (state->in_touch) { if (state->in_touch) {
fill_touch(nodename, content, state); fill_touch(nodename, content, state);