]> git.mdlowis.com Git - proto/labwc.git/commitdiff
action: add support for <prompt> in 'If' actions
authorConsolatis <35009135+Consolatis@users.noreply.github.com>
Fri, 20 Dec 2024 09:14:32 +0000 (10:14 +0100)
committerJohan Malm <johanmalm@users.noreply.github.com>
Sat, 9 Aug 2025 09:44:03 +0000 (10:44 +0100)
...and allow If Action without activator view.

For example:

    <action name="If">
      <prompt message="Toggle maximize?"/>
      <then>
        <action name="ToggleMaximize" />
      </then>
    </action>

Also revert the change in b9c84f9 that <else> branch is always taken when
no window is focused.

Co-Authored-by: johanmalm
Co-Authored-by: tokyo4j
include/action.h
src/action.c
src/server.c

index b8a271e99af71ac3c0f3cef932cdf004036db7f1..cb88d0d599f27d207278cea5090d0cafc865651f 100644 (file)
@@ -3,6 +3,7 @@
 #define LABWC_ACTION_H
 
 #include <stdbool.h>
+#include <sys/types.h>
 #include <wayland-util.h>
 
 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);
 
index 8d5eb5ac2e2659c028c169faa032f9f7de216bc6..657810bfb3028a24b90b4be8142df80dd9882a3b 100644 (file)
@@ -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;
        }
index a9e021dc7640c54df1497a3a1f58920810531b4b..e8befd55f0376c6d35893cf7599f72a27e566eae 100644 (file)
@@ -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,