diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 3aef7b212..81d471cbf 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -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; diff --git a/events.c b/events.c index 4dc00b503..b12085db5 100644 --- a/events.c +++ b/events.c @@ -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)); +} diff --git a/notify.c b/notify.c index c24c73dd8..ff846ef4d 100644 --- a/notify.c +++ b/notify.c @@ -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, ¬ify_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); diff --git a/regress/wait-for-E.sh b/regress/wait-for-E.sh index 11029a445..d86ed69d7 100644 --- a/regress/wait-for-E.sh +++ b/regress/wait-for-E.sh @@ -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 diff --git a/tmux.1 b/tmux.1 index 4c90b4495..e414b95ad 100644 --- a/tmux.1 +++ b/tmux.1 @@ -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 diff --git a/tmux.h b/tmux.h index 2ef24b412..e54c7c3b9 100644 --- a/tmux.h +++ b/tmux.h @@ -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 *,