mirror of
https://github.com/labwc/labwc.git
synced 2025-10-29 05:40:24 -04:00
action: add support for <prompt> in 'If' actions
...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
This commit is contained in:
parent
32e308b5d5
commit
fba73a0036
3 changed files with 135 additions and 14 deletions
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
135
src/action.c
135
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;
|
||||
}
|
||||
|
|
|
|||
11
src/server.c
11
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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue