Convert notify.c to events.

This commit is contained in:
Nicholas Marriott
2026-07-05 14:34:00 +01:00
parent b347209610
commit a30875c207
6 changed files with 350 additions and 82 deletions

View File

@@ -86,7 +86,7 @@ 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 void cmd_wait_for_event_cb(const char *, void *,
const struct events_type *, void *);
struct events_type *, void *);
static struct wait_channel *cmd_wait_for_add(const char *);
static void cmd_wait_for_remove(struct wait_channel *);
@@ -159,7 +159,7 @@ cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
static void
cmd_wait_for_event_cb(__unused const char *name, __unused void *data,
__unused const struct events_type *type, void *item_data)
__unused struct events_type *type, void *item_data)
{
struct wait_event_item *wei = item_data;

View File

@@ -28,6 +28,7 @@ struct events_type {
char *name;
events_add_formats_cb add_formats_cb;
events_find_state_cb find_state_cb;
events_get_client_cb get_client_cb;
RB_ENTRY(events_type) entry;
};
@@ -96,13 +97,14 @@ events_free_dead(void)
/* Add an event type. */
int
events_add_event(const char *name, events_add_formats_cb add_formats_cb,
events_find_state_cb find_state_cb)
events_find_state_cb find_state_cb, events_get_client_cb get_client_cb)
{
struct events_type *et;
if ((et = events_find_type(name)) != NULL) {
et->add_formats_cb = add_formats_cb;
et->find_state_cb = find_state_cb;
et->get_client_cb = get_client_cb;
return (0);
}
@@ -110,6 +112,7 @@ events_add_event(const char *name, events_add_formats_cb add_formats_cb,
et->name = xstrdup(name);
et->add_formats_cb = add_formats_cb;
et->find_state_cb = find_state_cb;
et->get_client_cb = get_client_cb;
RB_INSERT(events_types, &events_types, et);
return (0);
}
@@ -160,3 +163,29 @@ events_fire(const char *name, void *data)
if (--events_dispatching == 0)
events_free_dead();
}
/* Add event formats. */
void
events_add_formats(struct events_type *et, void *data, struct format_tree *ft)
{
if (et != NULL && et->add_formats_cb != NULL)
et->add_formats_cb(data, ft);
}
/* Find event state. */
int
events_find_state(struct events_type *et, void *data, struct cmd_find_state *fs)
{
if (et == NULL || et->find_state_cb == NULL)
return (0);
return (et->find_state_cb(data, fs));
}
/* Get event client. */
struct client *
events_get_client(struct events_type *et, void *data)
{
if (et == NULL || et->get_client_cb == NULL)
return (NULL);
return (et->get_client_cb(data));
}

365
notify.c
View File

@@ -54,6 +54,17 @@ struct notify_monitor_event {
struct monitor_change *change;
};
struct notify_event_entry {
const char *name;
void (*control_cb)(struct notify_entry *);
};
static void notify_event_hook_cb(const char *, void *, struct events_type *,
void *);
static void notify_event_control_cb(const char *, void *,
struct events_type *, void *);
static void notify_register_events(void);
static struct cmdq_item *
notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne,
struct cmd_list *cmdlist, struct cmdq_state *state)
@@ -183,43 +194,285 @@ notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
cmdq_free_state(state);
}
static void
notify_event_insert_hook(struct cmdq_item *item, const char *name,
struct events_type *type, void *data, struct options *oo, int expand)
{
struct cmd_find_state fs;
struct notify_entry ne;
struct format_tree *ft;
struct client *c;
if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS))
return;
cmd_find_clear_state(&fs, 0);
if (!events_find_state(type, data, &fs) ||
cmd_find_empty_state(&fs) || !cmd_find_valid_state(&fs))
cmd_find_from_nothing(&fs, 0);
c = events_get_client(type, data);
ft = format_create(c, item, FORMAT_NONE, FORMAT_NOJOBS);
events_add_formats(type, data, ft);
memset(&ne, 0, sizeof ne);
ne.name = name;
cmd_find_copy_state(&ne.fs, &fs);
ne.formats = ft;
ne.oo = oo;
ne.client = c;
ne.expand = expand;
notify_insert_hook(item, &ne);
format_free(ft);
}
static void
notify_event_add_formats(void *data, struct format_tree *ft)
{
struct notify_entry *ne = data;
if (ne->formats != NULL)
format_merge(ft, ne->formats);
}
static int
notify_event_find_state(void *data, struct cmd_find_state *fs)
{
struct notify_entry *ne = data;
if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
return (0);
cmd_find_copy_state(fs, &ne->fs);
return (1);
}
static struct client *
notify_event_get_client(void *data)
{
struct notify_entry *ne = data;
return (ne->client);
}
static void
notify_monitor_event_add_formats(void *data, struct format_tree *ft)
{
struct notify_monitor_event *nme = data;
struct monitor_change *change = nme->change;
struct window *w;
format_add(ft, "hook", "%s", change->name);
format_add(ft, "hook_value", "%s",
change->value == NULL ? "" : change->value);
format_add(ft, "hook_last", "%s",
change->last == NULL ? "" : change->last);
if (change->s != NULL) {
format_add(ft, "hook_session", "$%u", change->s->id);
format_add(ft, "hook_session_name", "%s", change->s->name);
}
if (change->wl != NULL) {
w = change->wl->window;
format_add(ft, "hook_window", "@%u", w->id);
format_add(ft, "hook_window_name", "%s", w->name);
format_add(ft, "hook_window_index", "%d", change->wl->idx);
}
if (change->wp != NULL)
format_add(ft, "hook_pane", "%%%u", change->wp->id);
}
static int
notify_monitor_event_find_state(void *data, struct cmd_find_state *fs)
{
struct notify_monitor_event *nme = data;
struct notify_monitor *nhm = nme->nhm;
struct monitor_change *change = nme->change;
if (change->wp != NULL && change->wl != NULL)
cmd_find_from_winlink_pane(fs, change->wl, change->wp, 0);
else if (change->wl != NULL)
cmd_find_from_winlink(fs, change->wl, 0);
else if (change->s != NULL)
cmd_find_from_session(fs, change->s, 0);
else
cmd_find_copy_state(fs, &nhm->fs);
return (1);
}
static struct client *
notify_monitor_event_get_client(void *data)
{
struct notify_monitor_event *nme = data;
struct monitor_change *change = nme->change;
return (change->c);
}
static void
notify_control_pane_mode_changed(struct notify_entry *ne)
{
control_notify_pane_mode_changed(ne->pane);
}
static void
notify_control_window_layout_changed(struct notify_entry *ne)
{
control_notify_window_layout_changed(ne->window);
}
static void
notify_control_window_pane_changed(struct notify_entry *ne)
{
control_notify_window_pane_changed(ne->window);
}
static void
notify_control_window_unlinked(struct notify_entry *ne)
{
control_notify_window_unlinked(ne->session, ne->window);
}
static void
notify_control_window_linked(struct notify_entry *ne)
{
control_notify_window_linked(ne->session, ne->window);
}
static void
notify_control_window_renamed(struct notify_entry *ne)
{
control_notify_window_renamed(ne->window);
}
static void
notify_control_client_session_changed(struct notify_entry *ne)
{
control_notify_client_session_changed(ne->client);
}
static void
notify_control_client_detached(struct notify_entry *ne)
{
control_notify_client_detached(ne->client);
}
static void
notify_control_session_renamed(struct notify_entry *ne)
{
control_notify_session_renamed(ne->session);
}
static void
notify_control_session_created(struct notify_entry *ne)
{
control_notify_session_created(ne->session);
}
static void
notify_control_session_closed(struct notify_entry *ne)
{
control_notify_session_closed(ne->session);
}
static void
notify_control_session_window_changed(struct notify_entry *ne)
{
control_notify_session_window_changed(ne->session);
}
static void
notify_control_paste_buffer_changed(struct notify_entry *ne)
{
control_notify_paste_buffer_changed(ne->pbname);
}
static void
notify_control_paste_buffer_deleted(struct notify_entry *ne)
{
control_notify_paste_buffer_deleted(ne->pbname);
}
static struct notify_event_entry notify_events[] = {
{ "alert-activity", NULL },
{ "alert-bell", NULL },
{ "alert-silence", NULL },
{ "client-active", NULL },
{ "client-attached", NULL },
{ "client-dark-theme", NULL },
{ "client-focus-in", NULL },
{ "client-focus-out", NULL },
{ "client-light-theme", NULL },
{ "client-resized", NULL },
{ "pane-mode-changed", notify_control_pane_mode_changed },
{ "pane-died", NULL },
{ "pane-exited", NULL },
{ "pane-focus-in", NULL },
{ "pane-focus-out", NULL },
{ "pane-set-clipboard", NULL },
{ "pane-title-changed", NULL },
{ "window-layout-changed", notify_control_window_layout_changed },
{ "window-pane-changed", notify_control_window_pane_changed },
{ "window-resized", NULL },
{ "window-unlinked", notify_control_window_unlinked },
{ "window-linked", notify_control_window_linked },
{ "window-renamed", notify_control_window_renamed },
{ "client-session-changed", notify_control_client_session_changed },
{ "client-detached", notify_control_client_detached },
{ "session-renamed", notify_control_session_renamed },
{ "session-created", notify_control_session_created },
{ "session-closed", notify_control_session_closed },
{ "session-window-changed", notify_control_session_window_changed },
{ "paste-buffer-changed", notify_control_paste_buffer_changed },
{ "paste-buffer-deleted", notify_control_paste_buffer_deleted }
};
static void
notify_event_hook_cb(const char *name, void *data, struct events_type *type,
__unused void *sink_data)
{
notify_event_insert_hook(cmdq_running(NULL), name, type, data, NULL, 0);
}
static void
notify_event_control_cb(__unused const char *name, void *data,
__unused struct events_type *type, void *sink_data)
{
struct notify_event_entry *nee = sink_data;
nee->control_cb(data);
}
static void
notify_register_events(void)
{
static int done;
u_int i;
if (done)
return;
done = 1;
for (i = 0; i < nitems(notify_events); i++) {
events_add_event(notify_events[i].name, notify_event_add_formats,
notify_event_find_state, notify_event_get_client);
events_add_sink(notify_events[i].name, notify_event_hook_cb,
NULL);
if (notify_events[i].control_cb != NULL) {
events_add_sink(notify_events[i].name,
notify_event_control_cb, &notify_events[i]);
}
}
}
static enum cmd_retval
notify_callback(struct cmdq_item *item, void *data)
notify_callback(__unused struct cmdq_item *item, void *data)
{
struct notify_entry *ne = data;
log_debug("%s: %s", __func__, ne->name);
if (strcmp(ne->name, "pane-mode-changed") == 0)
control_notify_pane_mode_changed(ne->pane);
if (strcmp(ne->name, "window-layout-changed") == 0)
control_notify_window_layout_changed(ne->window);
if (strcmp(ne->name, "window-pane-changed") == 0)
control_notify_window_pane_changed(ne->window);
if (strcmp(ne->name, "window-unlinked") == 0)
control_notify_window_unlinked(ne->session, ne->window);
if (strcmp(ne->name, "window-linked") == 0)
control_notify_window_linked(ne->session, ne->window);
if (strcmp(ne->name, "window-renamed") == 0)
control_notify_window_renamed(ne->window);
if (strcmp(ne->name, "client-session-changed") == 0)
control_notify_client_session_changed(ne->client);
if (strcmp(ne->name, "client-detached") == 0)
control_notify_client_detached(ne->client);
if (strcmp(ne->name, "session-renamed") == 0)
control_notify_session_renamed(ne->session);
if (strcmp(ne->name, "session-created") == 0)
control_notify_session_created(ne->session);
if (strcmp(ne->name, "session-closed") == 0)
control_notify_session_closed(ne->session);
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
if (strcmp(ne->name, "paste-buffer-changed") == 0)
control_notify_paste_buffer_changed(ne->pbname);
if (strcmp(ne->name, "paste-buffer-deleted") == 0)
control_notify_paste_buffer_deleted(ne->pbname);
notify_insert_hook(item, ne);
notify_register_events();
events_fire(ne->name, ne);
if (ne->client != NULL)
server_client_unref(ne->client);
@@ -268,57 +521,16 @@ notify_monitor_remove(struct options *oo, const char *name)
}
static void
notify_monitor_hook_cb(__unused const char *name, void *data,
__unused const struct events_type *type, void *sink_data)
notify_monitor_hook_cb(const char *name, void *data, struct events_type *type,
void *sink_data)
{
struct notify_monitor_event *nme = data;
struct notify_monitor *nhm = sink_data;
struct monitor_change *change = nme->change;
struct notify_entry ne;
struct cmdq_item *item;
struct window *w;
if (nme->nhm != nhm)
return;
item = cmdq_running(NULL);
if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS))
return;
memset(&ne, 0, sizeof ne);
ne.name = change->name;
ne.oo = nhm->oo;
ne.client = change->c;
ne.expand = 1;
if (change->wp != NULL && change->wl != NULL)
cmd_find_from_winlink_pane(&ne.fs, change->wl, change->wp, 0);
else if (change->wl != NULL)
cmd_find_from_winlink(&ne.fs, change->wl, 0);
else if (change->s != NULL)
cmd_find_from_session(&ne.fs, change->s, 0);
else
cmd_find_copy_state(&ne.fs, &nhm->fs);
ne.formats = format_create(change->c, item, FORMAT_NONE, FORMAT_NOJOBS);
format_add(ne.formats, "hook", "%s", change->name);
format_add(ne.formats, "hook_value", "%s", change->value);
format_add(ne.formats, "hook_last", "%s",
change->last == NULL ? "" : change->last);
if (change->s != NULL) {
format_add(ne.formats, "hook_session", "$%u", change->s->id);
format_add(ne.formats, "hook_session_name", "%s", change->s->name);
}
if (change->wl != NULL) {
w = change->wl->window;
format_add(ne.formats, "hook_window", "@%u", w->id);
format_add(ne.formats, "hook_window_name", "%s", w->name);
format_add(ne.formats, "hook_window_index", "%d", change->wl->idx);
}
if (change->wp != NULL) {
format_add(ne.formats, "hook_pane", "%%%u", change->wp->id);
}
notify_insert_hook(item, &ne);
format_free(ne.formats);
notify_event_insert_hook(cmdq_running(NULL), name, type, data, nhm->oo, 1);
}
static void
@@ -351,7 +563,8 @@ notify_monitor_add(__unused struct cmdq_item *item, struct options *oo,
nhm->id = id;
nhm->format = xstrdup(format);
nhm->set = monitor_create_session(s, notify_monitor_cb, nhm);
events_add_event(name, NULL, NULL);
events_add_event(name, notify_monitor_event_add_formats,
notify_monitor_event_find_state, notify_monitor_event_get_client);
nhm->sink = events_add_sink(name, notify_monitor_hook_cb, nhm);
options_set_monitor_data(o, nhm);
monitor_add(nhm->set, name, type, id, format, 0);

View File

@@ -94,4 +94,20 @@ $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 new -d -s wf2 || fail "new-session wf2 failed"
$TMUX wait-for -E window-renamed \; wait-for -S wf-renamed &
renamed_pid=$!
sleep 0.5
$TMUX rename-window -t wf2:0 renamed || fail "rename-window failed"
wait_channel wf-renamed
wait "$renamed_pid" || fail "wait-for -E window-renamed failed"
$TMUX set-hook -g window-renamed 'wait-for -S wf-hook-renamed' ||
fail "set-hook window-renamed failed"
$TMUX rename-window -t wf2:0 renamed-again ||
fail "rename-window renamed-again failed"
wait_channel wf-hook-renamed
exit 0

5
tmux.1
View File

@@ -8562,6 +8562,11 @@ When
is used,
.Nm
waits for the next event with the given name.
Events include hook and notification names, and user
.Ql @
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

11
tmux.h
View File

@@ -2339,10 +2339,10 @@ struct monitor_change {
typedef void (*monitor_cb)(struct monitor_change *, void *);
/* Events. */
typedef void (*events_cb)(const char *, void *, const struct events_type *,
void *);
typedef void (*events_cb)(const char *, void *, struct events_type *, void *);
typedef void (*events_add_formats_cb)(void *, struct format_tree *);
typedef int (*events_find_state_cb)(void *, struct cmd_find_state *);
typedef struct client *(*events_get_client_cb)(void *);
/* Key binding and key table. */
struct key_binding {
@@ -2655,10 +2655,15 @@ char *format_grid_line(struct grid *, u_int);
/* events.c */
int events_add_event(const char *, events_add_formats_cb,
events_find_state_cb);
events_find_state_cb, events_get_client_cb);
struct events_sink *events_add_sink(const char *, events_cb, void *);
void events_remove_sink(struct events_sink *);
void events_fire(const char *, void *);
void events_add_formats(struct events_type *, void *,
struct format_tree *);
int events_find_state(struct events_type *, void *,
struct cmd_find_state *);
struct client *events_get_client(struct events_type *, void *);
/* format-draw.c */
void format_draw(struct screen_write_ctx *,