Add wait-for -F and -v for events.

This commit is contained in:
Nicholas Marriott
2026-07-07 12:45:32 +01:00
parent 86e5746890
commit 3943584046
7 changed files with 144 additions and 88 deletions

View File

@@ -34,8 +34,8 @@ const struct cmd_entry cmd_wait_for_entry = {
.name = "wait-for",
.alias = "wait",
.args = { "ELSU", 1, 1, NULL },
.usage = "[-E|-L|-S|-U] channel",
.args = { "EF:LSUv", 1, 1, NULL },
.usage = "[-ELSUv] [-F format] channel",
.flags = 0,
.exec = cmd_wait_for_exec
@@ -49,6 +49,8 @@ struct wait_item {
struct wait_event_item {
struct cmdq_item *item;
struct events_sink *sink;
char *filter;
int verbose;
TAILQ_ENTRY(wait_event_item) entry;
};
static TAILQ_HEAD(, wait_event_item) wait_event_items =
@@ -84,7 +86,8 @@ static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *,
struct wait_channel *);
static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *,
struct wait_channel *);
static enum cmd_retval cmd_wait_for_event(struct cmdq_item *, const char *);
static enum cmd_retval cmd_wait_for_event(struct cmdq_item *, const char *,
struct args *);
static void cmd_wait_for_event_cb(const char *,
struct event_payload *, void *);
@@ -131,23 +134,14 @@ cmd_wait_for_remove(struct wait_channel *wc)
static enum cmd_retval
cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct args *args = cmd_get_args(self);
const char *name = args_string(args, 0);
struct wait_channel *wc, find;
if (args_has(args, 'E') &&
(args_has(args, 'L') || args_has(args, 'S') ||
args_has(args, 'U'))) {
cmdq_error(item, "-E cannot be used with -L, -S or -U");
return (CMD_RETURN_ERROR);
}
struct wait_channel *wc, find = { .name = name };
if (args_has(args, 'E'))
return (cmd_wait_for_event(item, name));
return (cmd_wait_for_event(item, name, args));
find.name = name;
wc = RB_FIND(wait_channels, &wait_channels, &find);
if (args_has(args, 'S'))
return (cmd_wait_for_signal(item, name, wc));
if (args_has(args, 'L'))
@@ -158,21 +152,61 @@ cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
}
static void
cmd_wait_for_event_cb(__unused const char *name,
__unused struct event_payload *ep, void *item_data)
cmd_wait_for_event_print(struct wait_event_item *wei, struct event_payload *ep)
{
struct event_payload_item *epi;
const char *key;
char *value;
epi = event_payload_first(ep);
while (epi != NULL) {
key = event_payload_item_name(epi);
if (*key != '_') {
value = event_payload_item_print(epi);
cmdq_print(wei->item, "%s=%s", key, value);
free(value);
}
epi = event_payload_next(epi);
}
}
static void
cmd_wait_for_event_cb(const char *name, struct event_payload *ep,
void *item_data)
{
struct wait_event_item *wei = item_data;
struct format_tree *ft;
char *expanded;
int flag;
if (wei->verbose)
cmd_wait_for_event_print(wei, ep);
if (wei->filter != NULL) {
ft = format_create(cmdq_get_client(wei->item), wei->item,
FORMAT_NONE, FORMAT_NOJOBS);
event_payload_add_formats(ep, ft, NULL);
expanded = format_expand(ft, wei->filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
if (!flag)
return;
}
TAILQ_REMOVE(&wait_event_items, wei, entry);
events_remove_sink(wei->sink);
cmdq_continue(wei->item);
free(wei->filter);
free(wei);
}
static enum cmd_retval
cmd_wait_for_event(struct cmdq_item *item, const char *name)
cmd_wait_for_event(struct cmdq_item *item, const char *name, struct args *args)
{
struct wait_event_item *wei;
const char *filter = args_get(args, 'F');
if (cmdq_get_client(item) == NULL) {
cmdq_error(item, "not able to wait");
@@ -181,6 +215,8 @@ cmd_wait_for_event(struct cmdq_item *item, const char *name)
wei = xcalloc(1, sizeof *wei);
wei->item = item;
wei->filter = (filter != NULL ? xstrdup(filter) : NULL);
wei->verbose = args_has(args, 'v');
wei->sink = events_add_sink(name, cmd_wait_for_event_cb, wei);
TAILQ_INSERT_TAIL(&wait_event_items, wei, entry);
@@ -302,6 +338,7 @@ cmd_wait_for_flush(void)
TAILQ_REMOVE(&wait_event_items, wei, entry);
events_remove_sink(wei->sink);
cmdq_continue(wei->item);
free(wei->filter);
free(wei);
}

View File

@@ -480,6 +480,41 @@ event_payload_print(struct event_payload *ep, const char *name)
return (event_payload_item_print(epi));
}
/* Add payload items as formats. */
void
event_payload_add_formats(struct event_payload *ep, struct format_tree *ft,
const char *prefix)
{
struct event_payload_item *epi;
char *name, *value;
const char *key;
if (prefix == NULL)
prefix = "";
RB_FOREACH(epi, event_payload_tree, &ep->items) {
key = epi->name;
if (*key == '_')
continue;
value = event_payload_item_print(epi);
xasprintf(&name, "%s%s", prefix, key);
format_add(ft, name, "%s", value);
free(name);
free(value);
if (epi->type == EVENT_PAYLOAD_SESSION) {
xasprintf(&name, "%s%s_name", prefix, key);
format_add(ft, name, "%s", epi->session->name);
free(name);
} else if (epi->type == EVENT_PAYLOAD_WINDOW) {
xasprintf(&name, "%s%s_name", prefix, key);
format_add(ft, name, "%s", epi->window->name);
free(name);
}
}
}
/* Get the first payload item. */
struct event_payload_item *
event_payload_first(struct event_payload *ep)

View File

@@ -96,6 +96,8 @@ events_fire(const char *name, struct event_payload *ep)
struct events_sink *es;
u_int generation = events_generation;
event_payload_set_string(ep, "event", "%s", name);
if (log_get_level() != 0)
event_payload_log(ep, "%s: %s: ", __func__, name);

63
hooks.c
View File

@@ -195,66 +195,6 @@ hooks_insert(struct cmdq_item *item, struct hooks_data *hd)
cmdq_free_state(state);
}
/* Add payload formats for a hook. */
static void
hooks_add_formats(struct format_tree *ft, const char *name,
struct event_payload *ep)
{
struct event_payload_item *epi;
struct client *c;
struct session *s;
struct window *w;
struct window_pane *wp;
char *fname, *value;
const char *key;
const char *svalue;
epi = event_payload_first(ep);
while (epi != NULL) {
key = event_payload_item_name(epi);
if (*key != '_') {
value = event_payload_item_print(epi);
xasprintf(&fname, "hook_%s", key);
format_add(ft, fname, "%s", value);
free(fname);
free(value);
}
epi = event_payload_next(epi);
}
format_add(ft, "hook", "%s", name);
c = event_payload_get_client(ep, "client");
if (c != NULL)
format_add(ft, "hook_client", "%s", c->name);
s = event_payload_get_session(ep, "session");
if (s != NULL) {
format_add(ft, "hook_session", "$%u", s->id);
format_add(ft, "hook_session_name", "%s", s->name);
}
w = event_payload_get_window(ep, "window");
if (w != NULL) {
format_add(ft, "hook_window", "@%u", w->id);
format_add(ft, "hook_window_name", "%s", w->name);
}
wp = event_payload_get_pane(ep, "pane");
if (wp != NULL)
format_add(ft, "hook_pane", "%%%u", wp->id);
else {
value = event_payload_print(ep, "pane");
if (value != NULL) {
format_add(ft, "hook_pane", "%s", value);
free(value);
}
}
svalue = event_payload_get_string(ep, "value");
if (svalue != NULL)
format_add(ft, "hook_value", "%s", svalue);
svalue = event_payload_get_string(ep, "last");
if (svalue != NULL)
format_add(ft, "hook_last", "%s", svalue);
}
/* Insert commands for a hook event. */
static void
hooks_insert_event(struct cmdq_item *item, const char *name,
@@ -269,7 +209,8 @@ hooks_insert_event(struct cmdq_item *item, const char *name,
c = event_payload_get_client(ep, "client");
ft = format_create(c, item, FORMAT_NONE, FORMAT_NOJOBS);
hooks_add_formats(ft, name, ep);
event_payload_add_formats(ep, ft, "hook_");
format_add(ft, "hook", "%s", name);
format_log_debug(ft, __func__);
memset(&hd, 0, sizeof hd);

View File

@@ -94,6 +94,34 @@ $TMUX set -g @wf_value 2 || fail "set @wf_value 2 failed"
wait_channel wf-late
wait "$late_pid" || fail "late wait-for -E command failed"
$TMUX set -g @filtered 0 || fail "set @filtered failed"
$TMUX wait-for -E -F '#{==:#{value},3}' @wf \; set -g @filtered 1 \; \
wait-for -S wf-filtered &
filtered_pid=$!
assert_unchanged @filtered 0 5
$TMUX set -g @wf_value unmatched || fail "set @wf_value unmatched failed"
assert_unchanged @filtered 0 5
$TMUX set -g @wf_value 3 || fail "set @wf_value 3 failed"
wait_channel wf-filtered
wait "$filtered_pid" || fail "filtered wait-for -E command failed"
verbose_file="$OUT/verbose"
$TMUX wait-for -E -v @wf \; wait-for -S wf-verbose >"$verbose_file" &
verbose_pid=$!
sleep 0.5
$TMUX set -g @wf_value 4 || fail "set @wf_value 4 failed"
wait_channel wf-verbose
wait "$verbose_pid" || fail "verbose wait-for -E command failed"
grep '^event=@wf$' "$verbose_file" >/dev/null ||
fail "verbose wait-for -E did not print event payload"
grep '^value=4$' "$verbose_file" >/dev/null ||
fail "verbose wait-for -E did not print value payload"
grep '^_hook_monitor=' "$verbose_file" >/dev/null &&
fail "verbose wait-for -E printed private payload"
$TMUX new -d -s wf2 || fail "new-session wf2 failed"
$TMUX wait-for -E window-renamed \; wait-for -S wf-renamed &

29
tmux.1
View File

@@ -8553,7 +8553,8 @@ or the current pane if omitted) after the command finishes.
If the command fails, the exit status is also displayed.
.Tg wait
.It Xo Ic wait\-for
.Op Fl E | L | S | U
.Op Fl ELSUv
.Op Fl F Ar format
.Ar channel | event\-name
.Xc
.D1 Pq alias: Ic wait
@@ -8562,8 +8563,14 @@ When used without options, prevents the client from exiting until woken using
.Fl S
with the same channel.
When
.Fl E
is used,
.Fl L
is used, the channel is locked and any clients that try to lock the same
channel are made to wait until the channel is unlocked with
.Ic wait\-for
.Fl U .
.Pp
With
.Fl E ,
.Nm
waits for the next event with the given name.
Events include hook and notification names, and user
@@ -8571,12 +8578,16 @@ Events include hook and notification names, and user
events generated by
.Ic set-hook
.Fl B .
When
.Fl L
is used, the channel is locked and any clients that try to lock the same
channel are made to wait until the channel is unlocked with
.Ic wait\-for
.Fl U .
If
.Fl F
is given,
.Ar format
must also be true.
If
.Fl v
is given, event payload keys are printed (whether or not
.Ar format
is true).
.El
.Sh EXIT MESSAGES
When a

2
tmux.h
View File

@@ -2697,6 +2697,8 @@ void event_payload_set_pointer(struct event_payload *, const char *, void *,
event_payload_free_cb, event_payload_print_cb);
const char *event_payload_get_string(struct event_payload *, const char *);
char *event_payload_print(struct event_payload *, const char *);
void event_payload_add_formats(struct event_payload *,
struct format_tree *, const char *);
struct event_payload_item *event_payload_first(struct event_payload *);
struct event_payload_item *event_payload_next(struct event_payload_item *);
const char *event_payload_item_name(struct event_payload_item *);