From fba73a003652917799dd3d26165a52ea686b7f93 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:14:32 +0100 Subject: [PATCH] action: add support for in 'If' actions ...and allow If Action without activator view. For example: Also revert the change in b9c84f9 that branch is always taken when no window is focused. Co-Authored-by: johanmalm Co-Authored-by: tokyo4j --- include/action.h | 3 ++ src/action.c | 135 +++++++++++++++++++++++++++++++++++++++++++---- src/server.c | 11 ++-- 3 files changed, 135 insertions(+), 14 deletions(-) diff --git a/include/action.h b/include/action.h index b8a271e9..cb88d0d5 100644 --- a/include/action.h +++ b/include/action.h @@ -3,6 +3,7 @@ #define LABWC_ACTION_H #include +#include #include struct view; @@ -47,6 +48,8 @@ bool actions_contain_toggle_keybinds(struct wl_list *action_list); void actions_run(struct view *activator, struct server *server, struct wl_list *actions, struct cursor_context *ctx); +bool action_check_prompt_result(pid_t pid, int exit_code); + void action_free(struct action *action); void action_list_free(struct wl_list *action_list); diff --git a/src/action.c b/src/action.c index 8d5eb5ac..657810bf 100644 --- a/src/action.c +++ b/src/action.c @@ -493,6 +493,11 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char goto cleanup; } break; + case ACTION_TYPE_IF: + if (!strcmp(argument, "message.prompt")) { + action_arg_add_str(action, "message.prompt", content); + } + goto cleanup; } wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s'", @@ -775,16 +780,115 @@ view_for_action(struct view *activator, struct server *server, } } +struct action_prompt { + /* Set when created */ + struct server *server; + struct action *action; + struct view *view; + + /* Set when executed */ + pid_t pid; + + struct { + struct wl_listener destroy; + } on_view; + struct wl_list link; +}; + +static struct wl_list prompts = WL_LIST_INIT(&prompts); + +static void +action_prompt_destroy(struct action_prompt *prompt) +{ + wl_list_remove(&prompt->on_view.destroy.link); + wl_list_remove(&prompt->link); + free(prompt); +} + +static void +handle_view_destroy(struct wl_listener *listener, void *data) +{ + struct action_prompt *prompt = wl_container_of(listener, prompt, on_view.destroy); + wl_list_remove(&prompt->on_view.destroy.link); + wl_list_init(&prompt->on_view.destroy.link); + prompt->view = NULL; +} + +static void +action_prompt_create(struct view *view, struct server *server, struct action *action) +{ + char *command = strdup_printf("labnag -m \"%s\" -Z \"%s\" : -Z \"%s\" :", + action_get_str(action, "message.prompt", "Choose wisely"), + _("Yes"), _("No")); + + int pipe_fd; + pid_t prompt_pid = spawn_piped(command, &pipe_fd); + if (prompt_pid < 0) { + wlr_log(WLR_ERROR, "Failed to create action prompt"); + goto cleanup; + } + /* FIXME: closing stdout might confuse clients */ + close(pipe_fd); + + struct action_prompt *prompt = znew(*prompt); + prompt->server = server; + prompt->action = action; + prompt->view = view; + prompt->pid = prompt_pid; + if (view) { + prompt->on_view.destroy.notify = handle_view_destroy; + wl_signal_add(&view->events.destroy, &prompt->on_view.destroy); + } else { + /* Allows removing during destroy */ + wl_list_init(&prompt->on_view.destroy.link); + } + + wl_list_insert(&prompts, &prompt->link); + +cleanup: + free(command); +} + +bool +action_check_prompt_result(pid_t pid, int exit_code) +{ + struct action_prompt *prompt, *tmp; + wl_list_for_each_safe(prompt, tmp, &prompts, link) { + if (prompt->pid != pid) { + continue; + } + + wlr_log(WLR_INFO, "Found pending prompt for exit code %d", exit_code); + struct wl_list *actions = NULL; + if (exit_code == 0) { + wlr_log(WLR_INFO, "Selected the 'then' branch"); + actions = action_get_actionlist(prompt->action, "then"); + } else { + wlr_log(WLR_INFO, "Selected the 'else' branch"); + actions = action_get_actionlist(prompt->action, "else"); + } + if (actions) { + wlr_log(WLR_INFO, "Running actions"); + actions_run(prompt->view, prompt->server, + actions, /*cursor_ctx*/ NULL); + } else { + wlr_log(WLR_INFO, "No actions for selected branch"); + } + action_prompt_destroy(prompt); + return true; + } + return false; +} + static bool match_queries(struct view *view, struct action *action) { + assert(view); + struct wl_list *queries = action_get_querylist(action, "query"); if (!queries) { return true; } - if (!view) { - return false; - } /* All queries are OR'ed */ struct view_query *query; @@ -1205,14 +1309,23 @@ run_action(struct view *view, struct server *server, struct action *action, break; } case ACTION_TYPE_IF: { - struct wl_list *actions; - if (match_queries(view, action)) { - actions = action_get_actionlist(action, "then"); - } else { - actions = action_get_actionlist(action, "else"); - } - if (actions) { - actions_run(view, server, actions, ctx); + /* At least one of the queries was matched or there was no query */ + if (action_get_str(action, "message.prompt", NULL)) { + /* + * We delay the selection and execution of the + * branch until we get a response from the user. + */ + action_prompt_create(view, server, action); + } else if (view) { + struct wl_list *actions; + if (match_queries(view, action)) { + actions = action_get_actionlist(action, "then"); + } else { + actions = action_get_actionlist(action, "else"); + } + if (actions) { + actions_run(view, server, actions, ctx); + } } break; } diff --git a/src/server.c b/src/server.c index a9e021dc..e8befd55 100644 --- a/src/server.c +++ b/src/server.c @@ -46,6 +46,7 @@ #endif #include "drm-lease-v1-protocol.h" +#include "action.h" #include "common/macros.h" #include "common/scaled-scene-buffer.h" #include "config/rcxml.h" @@ -160,9 +161,11 @@ handle_sigchld(int signal, void *data) const char *signame; switch (info.si_code) { case CLD_EXITED: - wlr_log(info.si_status == 0 ? WLR_DEBUG : WLR_ERROR, - "spawned child %ld exited with %d", - (long)info.si_pid, info.si_status); + if (!action_check_prompt_result(info.si_pid, info.si_status)) { + wlr_log(info.si_status == 0 ? WLR_DEBUG : WLR_ERROR, + "spawned child %ld exited with %d", + (long)info.si_pid, info.si_status); + } break; case CLD_KILLED: case CLD_DUMPED: @@ -171,6 +174,8 @@ handle_sigchld(int signal, void *data) "spawned child %ld terminated with signal %d (%s)", (long)info.si_pid, info.si_status, signame ? signame : "unknown"); + /* Allow cleanup of killed prompt */ + action_check_prompt_result(info.si_pid, -info.si_status); break; default: wlr_log(WLR_ERROR, -- 2.52.0