diff --git a/Makefile.am b/Makefile.am index d168d02bb..3cbd60b99 100644 --- a/Makefile.am +++ b/Makefile.am @@ -159,6 +159,7 @@ dist_tmux_SOURCES = \ control-notify.c \ control.c \ environ.c \ + events-payload.c \ events.c \ file.c \ format.c \ diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 81d471cbf..bd2d63268 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -85,8 +85,8 @@ static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *, 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 *, - struct events_type *, void *); +static void cmd_wait_for_event_cb(const char *, + struct event_payload *, void *); static struct wait_channel *cmd_wait_for_add(const char *); static void cmd_wait_for_remove(struct wait_channel *); @@ -158,8 +158,8 @@ 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 struct events_type *type, void *item_data) +cmd_wait_for_event_cb(__unused const char *name, + __unused struct event_payload *ep, void *item_data) { struct wait_event_item *wei = item_data; diff --git a/control-notify.c b/control-notify.c index 30ff44193..f010ff28d 100644 --- a/control-notify.c +++ b/control-notify.c @@ -31,33 +31,48 @@ /* Notify control clients that pane mode changed. */ static void -control_pane_mode_changed_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_pane_mode_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct notify_entry *ne = data; + struct window_pane *wp; struct client *c; + char *value; - TAILQ_FOREACH(c, &clients, entry) { - if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) - continue; - - control_write(c, "%%pane-mode-changed %%%u", ne->pane); + wp = event_payload_get_pane(ep, "pane"); + if (wp != NULL) { + TAILQ_FOREACH(c, &clients, entry) { + if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) + continue; + control_write(c, "%%pane-mode-changed %%%u", wp->id); + } + return; } + + value = event_payload_print(ep, "pane"); + if (value == NULL) + return; + TAILQ_FOREACH(c, &clients, entry) { + if (CONTROL_SHOULD_NOTIFY_CLIENT(c)) + control_write(c, "%%pane-mode-changed %s", value); + } + free(value); } /* Notify control clients that window layout changed. */ static void -control_window_layout_changed_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_window_layout_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct notify_entry *ne = data; struct client *c; struct session *s; struct winlink *wl; - struct window *w = ne->window; + struct window *w = event_payload_get_window(ep, "window"); const char *template; char *cp; + if (w == NULL) + return; + template = "%layout-change #{window_id} #{window_layout} " "#{window_visible_layout} #{window_raw_flags}"; @@ -83,14 +98,13 @@ control_window_layout_changed_cb(__unused const char *name, void *data, /* Notify control clients that window pane changed. */ static void -control_window_pane_changed_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_window_pane_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct notify_entry *ne = data; struct client *c; - struct window *w = ne->window; + struct window *w = event_payload_get_window(ep, "window"); - if (w->active == NULL) + if (w == NULL || w->active == NULL) return; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) @@ -103,14 +117,15 @@ control_window_pane_changed_cb(__unused const char *name, void *data, /* Notify control clients that a window was unlinked. */ static void -control_window_unlinked_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_window_unlinked_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct notify_entry *ne = data; struct client *c; struct session *cs; - struct window *w = ne->window; + struct window *w = event_payload_get_window(ep, "window"); + if (w == NULL) + return; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; @@ -125,14 +140,15 @@ control_window_unlinked_cb(__unused const char *name, void *data, /* Notify control clients that a window was linked. */ static void -control_window_linked_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_window_linked_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct notify_entry *ne = data; struct client *c; struct session *cs; - struct window *w = ne->window; + struct window *w = event_payload_get_window(ep, "window"); + if (w == NULL) + return; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; @@ -147,14 +163,15 @@ control_window_linked_cb(__unused const char *name, void *data, /* Notify control clients that a window was renamed. */ static void -control_window_renamed_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_window_renamed_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct notify_entry *ne = data; struct client *c; struct session *cs; - struct window *w = ne->window; + struct window *w = event_payload_get_window(ep, "window"); + if (w == NULL) + return; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; @@ -172,15 +189,14 @@ control_window_renamed_cb(__unused const char *name, void *data, /* Notify control clients that a client changed session. */ static void -control_client_session_changed_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_client_session_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct notify_entry *ne = data; - struct client *cc = ne->client; + struct client *cc = event_payload_get_client(ep, "client"); struct client *c; struct session *s; - if (cc->session == NULL) + if (cc == NULL || cc->session == NULL) return; s = cc->session; @@ -200,13 +216,14 @@ control_client_session_changed_cb(__unused const char *name, void *data, /* Notify control clients that a client detached. */ static void -control_client_detached_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_client_detached_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct notify_entry *ne = data; - struct client *cc = ne->client; + struct client *cc = event_payload_get_client(ep, "client"); struct client *c; + if (cc == NULL) + return; TAILQ_FOREACH(c, &clients, entry) { if (CONTROL_SHOULD_NOTIFY_CLIENT(c)) control_write(c, "%%client-detached %s", cc->name); @@ -215,13 +232,14 @@ control_client_detached_cb(__unused const char *name, void *data, /* Notify control clients that a session was renamed. */ static void -control_session_renamed_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_session_renamed_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct notify_entry *ne = data; - struct session *s = ne->session; + struct session *s = event_payload_get_session(ep, "session"); struct client *c; + if (s == NULL) + return; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; @@ -232,8 +250,8 @@ control_session_renamed_cb(__unused const char *name, void *data, /* Notify control clients that sessions changed. */ static void -control_session_created_cb(__unused const char *name, __unused void *data, - __unused struct events_type *type, __unused void *sink_data) +control_session_created_cb(__unused const char *name, + __unused struct event_payload *ep, __unused void *sink_data) { struct client *c; @@ -247,8 +265,8 @@ control_session_created_cb(__unused const char *name, __unused void *data, /* Notify control clients that sessions changed. */ static void -control_session_closed_cb(__unused const char *name, __unused void *data, - __unused struct events_type *type, __unused void *sink_data) +control_session_closed_cb(__unused const char *name, + __unused struct event_payload *ep, __unused void *sink_data) { struct client *c; @@ -262,11 +280,10 @@ control_session_closed_cb(__unused const char *name, __unused void *data, /* Notify control clients that the current window changed. */ static void -control_session_window_changed_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_session_window_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct notify_entry *ne = data; - struct session *s = ne->session; + struct session *s = event_payload_get_session(ep, "session"); struct client *c; /* @@ -274,7 +291,7 @@ control_session_window_changed_cb(__unused const char *name, void *data, * session has been destroyed (which sets curw to NULL) but is kept * alive by the notification's reference. Skip the notification. */ - if (s->curw == NULL) + if (s == NULL || s->curw == NULL) return; TAILQ_FOREACH(c, &clients, entry) { @@ -288,33 +305,37 @@ control_session_window_changed_cb(__unused const char *name, void *data, /* Notify control clients that a paste buffer changed. */ static void -control_paste_buffer_changed_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_paste_buffer_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct notify_entry *ne = data; + const char *pbname = event_payload_get_string(ep, "paste_buffer"); struct client *c; + if (pbname == NULL) + return; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; - control_write(c, "%%paste-buffer-changed %s", ne->pbname); + control_write(c, "%%paste-buffer-changed %s", pbname); } } /* Notify control clients that a paste buffer was deleted. */ static void -control_paste_buffer_deleted_cb(__unused const char *name, void *data, - __unused struct events_type *type, __unused void *sink_data) +control_paste_buffer_deleted_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct notify_entry *ne = data; + const char *pbname = event_payload_get_string(ep, "paste_buffer"); struct client *c; + if (pbname == NULL) + return; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; - control_write(c, "%%paste-buffer-deleted %s", ne->pbname); + control_write(c, "%%paste-buffer-deleted %s", pbname); } } diff --git a/events-payload.c b/events-payload.c new file mode 100644 index 000000000..d1c74928a --- /dev/null +++ b/events-payload.c @@ -0,0 +1,506 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2026 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* Event payload item. */ +struct event_payload_item { + char *name; + enum event_payload_type type; + + union { + char *string; + time_t time; + struct client *client; + struct session *session; + struct window *window; + u_int pane; + struct { + u_int session; + u_int window; + int idx; + } winlink; + struct { + void *ptr; + event_payload_free_cb free_cb; + event_payload_print_cb print_cb; + } pointer; + }; + + RB_ENTRY(event_payload_item) entry; +}; + +static int +event_payload_cmp(struct event_payload_item *epi1, + struct event_payload_item *epi2) +{ + return (strcmp(epi1->name, epi2->name)); +} +RB_HEAD(event_payload, event_payload_item); +RB_GENERATE_STATIC(event_payload, event_payload_item, entry, event_payload_cmp); + +/* Find an item. */ +static struct event_payload_item * +event_payload_find(struct event_payload *ep, const char *name) +{ + struct event_payload_item find = { .name = (char *)name }; + + return (RB_FIND(event_payload, ep, &find)); +} + +/* Free the value in an item. */ +static void +event_payload_free_value(struct event_payload_item *epi) +{ + switch (epi->type) { + case EVENT_PAYLOAD_STRING: + free(epi->string); + break; + case EVENT_PAYLOAD_CLIENT: + if (epi->client != NULL) + server_client_unref(epi->client); + break; + case EVENT_PAYLOAD_SESSION: + if (epi->session != NULL) + session_remove_ref(epi->session, __func__); + break; + case EVENT_PAYLOAD_WINDOW: + if (epi->window != NULL) + window_remove_ref(epi->window, __func__); + break; + case EVENT_PAYLOAD_POINTER: + if (epi->pointer.free_cb != NULL) + epi->pointer.free_cb(epi->pointer.ptr); + break; + case EVENT_PAYLOAD_TIME: + case EVENT_PAYLOAD_PANE: + case EVENT_PAYLOAD_WINLINK: + break; + } +} + +/* Set an item. */ +static void +event_payload_set_item(struct event_payload *ep, const char *name, + struct event_payload_item *new) +{ + struct event_payload_item *old; + + new->name = xstrdup(name); + old = RB_INSERT(event_payload, ep, new); + if (old != NULL) { + RB_REMOVE(event_payload, ep, old); + event_payload_free_value(old); + free(old->name); + free(old); + RB_INSERT(event_payload, ep, new); + } +} + +/* Create an event payload. */ +struct event_payload * +event_payload_create(void) +{ + struct event_payload *ep; + + ep = xcalloc(1, sizeof *ep); + RB_INIT(ep); + return (ep); +} + +/* Free an event payload. */ +void +event_payload_free(struct event_payload *ep) +{ + struct event_payload_item *epi, *epi1; + + if (ep != NULL) { + RB_FOREACH_SAFE(epi, event_payload, ep, epi1) { + RB_REMOVE(event_payload, ep, epi); + event_payload_free_value(epi); + free(epi->name); + free(epi); + } + free(ep); + } +} + +/* Set a string item. */ +void +event_payload_set_string(struct event_payload *ep, const char *name, + const char *fmt, ...) +{ + struct event_payload_item *epi; + va_list ap; + + va_start(ap, fmt); + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_STRING; + xvasprintf(&epi->string, fmt, ap); + event_payload_set_item(ep, name, epi); + + va_end(ap); +} + +/* Set a time item. */ +void +event_payload_set_time(struct event_payload *ep, const char *name, + time_t value) +{ + struct event_payload_item *epi; + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_TIME; + epi->time = value; + event_payload_set_item(ep, name, epi); +} + +/* Set a client item. */ +void +event_payload_set_client(struct event_payload *ep, const char *name, + struct client *c) +{ + struct event_payload_item *epi; + + if (c != NULL) + c->references++; + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_CLIENT; + epi->client = c; + event_payload_set_item(ep, name, epi); +} + +/* Set a session item. */ +void +event_payload_set_session(struct event_payload *ep, const char *name, + struct session *s) +{ + struct event_payload_item *epi; + + if (s != NULL) + session_add_ref(s, __func__); + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_SESSION; + epi->session = s; + event_payload_set_item(ep, name, epi); +} + +/* Set a window item. */ +void +event_payload_set_window(struct event_payload *ep, const char *name, + struct window *w) +{ + struct event_payload_item *epi; + + if (w != NULL) + window_add_ref(w, __func__); + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_WINDOW; + epi->window = w; + event_payload_set_item(ep, name, epi); +} + +/* Set a pane item. */ +void +event_payload_set_pane(struct event_payload *ep, const char *name, + struct window_pane *wp) +{ + struct event_payload_item *epi; + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_PANE; + epi->pane = wp->id; + event_payload_set_item(ep, name, epi); +} + +/* Set a winlink item. */ +void +event_payload_set_winlink(struct event_payload *ep, const char *name, + struct winlink *wl) +{ + struct event_payload_item *epi; + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_WINLINK; + epi->winlink.session = wl->session->id; + epi->winlink.window = wl->window->id; + epi->winlink.idx = wl->idx; + event_payload_set_item(ep, name, epi); +} + +/* Set a pointer item. */ +void +event_payload_set_pointer(struct event_payload *ep, const char *name, + void *ptr, event_payload_free_cb free_cb, event_payload_print_cb print_cb) +{ + struct event_payload_item *epi; + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_POINTER; + epi->pointer.ptr = ptr; + epi->pointer.free_cb = free_cb; + epi->pointer.print_cb = print_cb; + event_payload_set_item(ep, name, epi); +} + +/* Get a string item. */ +const char * +event_payload_get_string(struct event_payload *ep, const char *name) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_STRING) + return (NULL); + return (epi->string); +} + +/* Print a payload item. */ +static void +event_payload_add_item(struct event_payload_item *epi, struct evbuffer *evb) +{ + switch (epi->type) { + case EVENT_PAYLOAD_STRING: + evbuffer_add_printf(evb, "%s", epi->string); + break; + case EVENT_PAYLOAD_TIME: + evbuffer_add_printf(evb, "%lld", (long long)epi->time); + break; + case EVENT_PAYLOAD_CLIENT: + if (epi->client != NULL) + evbuffer_add_printf(evb, "%s", epi->client->name); + break; + case EVENT_PAYLOAD_SESSION: + if (epi->session != NULL) + evbuffer_add_printf(evb, "$%u", epi->session->id); + break; + case EVENT_PAYLOAD_WINDOW: + if (epi->window != NULL) + evbuffer_add_printf(evb, "@%u", epi->window->id); + break; + case EVENT_PAYLOAD_PANE: + evbuffer_add_printf(evb, "%%%u", epi->pane); + break; + case EVENT_PAYLOAD_WINLINK: + evbuffer_add_printf(evb, "$%u:%d", epi->winlink.session, + epi->winlink.idx); + break; + case EVENT_PAYLOAD_POINTER: + if (epi->pointer.print_cb != NULL) + epi->pointer.print_cb(epi->pointer.ptr, evb); + else + evbuffer_add_printf(evb, "%p", epi->pointer.ptr); + break; + } +} + +/* Print a payload item. */ +char * +event_payload_item_print(struct event_payload_item *epi) +{ + struct evbuffer *evb; + char *value = NULL; + size_t size; + + evb = evbuffer_new(); + if (evb == NULL) + fatalx("out of memory"); + event_payload_add_item(epi, evb); + if ((size = EVBUFFER_LENGTH(evb)) != 0) + value = xmemdup(EVBUFFER_DATA(evb), size); + else + value = xstrdup(""); + evbuffer_free(evb); + return (value); +} + +/* Print an item value. */ +char * +event_payload_print(struct event_payload *ep, const char *name) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL) + return (NULL); + return (event_payload_item_print(epi)); +} + +/* Get the first payload item. */ +struct event_payload_item * +event_payload_first(struct event_payload *ep) +{ + return (RB_MIN(event_payload, ep)); +} + +/* Get the next payload item. */ +struct event_payload_item * +event_payload_next(struct event_payload_item *epi) +{ + return (RB_NEXT(event_payload, , epi)); +} + +/* Get a payload item name. */ +const char * +event_payload_item_name(struct event_payload_item *epi) +{ + return (epi->name); +} + +/* Get a payload item type. */ +enum event_payload_type +event_payload_item_type(struct event_payload_item *epi) +{ + return (epi->type); +} + +/* Log a payload. */ +void +event_payload_log(struct event_payload *ep, const char *fmt, ...) +{ + struct event_payload_item *epi; + struct evbuffer *evb; + va_list ap; + char *prefix; + + va_start(ap, fmt); + xvasprintf(&prefix, fmt, ap); + va_end(ap); + + evb = evbuffer_new(); + if (evb == NULL) + fatalx("out of memory"); + if (ep != NULL) { + RB_FOREACH(epi, event_payload, ep) { + if (EVBUFFER_LENGTH(evb) != 0) + evbuffer_add_printf(evb, ", "); + evbuffer_add_printf(evb, "%s=", epi->name); + event_payload_add_item(epi, evb); + } + } + log_debug("%s%.*s", prefix, (int)EVBUFFER_LENGTH(evb), + (char *)EVBUFFER_DATA(evb)); + evbuffer_free(evb); + free(prefix); +} + +/* Get a time item. */ +int +event_payload_get_time(struct event_payload *ep, const char *name, + time_t *value) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_TIME) + return (0); + *value = epi->time; + return (1); +} + +/* Get a client item. */ +struct client * +event_payload_get_client(struct event_payload *ep, const char *name) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_CLIENT) + return (NULL); + return (epi->client); +} + +/* Get a session item. */ +struct session * +event_payload_get_session(struct event_payload *ep, const char *name) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_SESSION) + return (NULL); + return (epi->session); +} + +/* Get a window item. */ +struct window * +event_payload_get_window(struct event_payload *ep, const char *name) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_WINDOW) + return (NULL); + return (epi->window); +} + +/* Get a pane item. */ +struct window_pane * +event_payload_get_pane(struct event_payload *ep, const char *name) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_PANE) + return (NULL); + return (window_pane_find_by_id(epi->pane)); +} + +/* Get a winlink item. */ +struct winlink * +event_payload_get_winlink(struct event_payload *ep, const char *name) +{ + struct event_payload_item *epi; + struct session *s; + struct winlink *wl; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_WINLINK) + return (NULL); + s = session_find_by_id(epi->winlink.session); + if (s == NULL) + return (NULL); + wl = winlink_find_by_index(&s->windows, epi->winlink.idx); + if (wl == NULL || wl->window->id != epi->winlink.window) + return (NULL); + return (wl); +} + +/* Get a pointer item. */ +void * +event_payload_get_pointer(struct event_payload *ep, const char *name) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_POINTER) + return (NULL); + return (epi->pointer.ptr); +} diff --git a/events.c b/events.c index 5a64f05ef..25ea97d06 100644 --- a/events.c +++ b/events.c @@ -26,9 +26,6 @@ /* Event type metadata. */ 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,23 +93,15 @@ 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_get_client_cb get_client_cb) +events_add_event(const char *name) { 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; + if ((et = events_find_type(name)) != NULL) return (0); - } et = xcalloc(1, sizeof *et); 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); } @@ -147,45 +136,21 @@ events_remove_sink(struct events_sink *es) /* Fire an event. */ void -events_fire(const char *name, void *data) +events_fire(const char *name, struct event_payload *ep) { - struct events_type *et = events_find_type(name); struct events_sink *es; u_int generation = events_generation; + event_payload_log(ep, "%s: %s: ", __func__, name); + events_dispatching++; TAILQ_FOREACH(es, &events_sinks, entry) { if (es->dead || es->generation > generation) continue; if (strcmp(es->name, name) == 0) - es->cb(name, data, et, es->data); + es->cb(name, ep, es->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)); + event_payload_free(ep); } diff --git a/hooks.c b/hooks.c index df86f0b82..911ca6b34 100644 --- a/hooks.c +++ b/hooks.c @@ -37,10 +37,14 @@ struct hook_monitor { char *format; }; -/* Hook monitor event data passed through events_fire. */ -struct hook_monitor_event { - struct hook_monitor *hm; - struct monitor_change *change; +/* Hook command data built from an event payload. */ +struct hooks_data { + const char *name; + struct cmd_find_state fs; + struct format_tree *formats; + struct options *oo; + struct client *client; + int expand; }; /* Hook event sink registered for a notify event name. */ @@ -49,12 +53,12 @@ struct hooks_event { struct events_sink *sink; TAILQ_ENTRY(hooks_event) entry; }; -static TAILQ_HEAD(, hooks_event) hooks_events = - TAILQ_HEAD_INITIALIZER(hooks_events); +TAILQ_HEAD(hooks_events, hooks_event); +static struct hooks_events hooks_events = TAILQ_HEAD_INITIALIZER(hooks_events); /* Insert one hook command list. */ static struct cmdq_item * -hooks_insert_one(struct cmdq_item *item, struct notify_entry *ne, +hooks_insert_one(struct cmdq_item *item, struct hooks_data *hd, struct cmd_list *cmdlist, struct cmdq_state *state) { struct cmdq_item *new_item; @@ -64,7 +68,7 @@ hooks_insert_one(struct cmdq_item *item, struct notify_entry *ne, return (item); if (log_get_level() != 0) { s = cmd_list_print(cmdlist, 0); - log_debug("%s: hook %s is: %s", __func__, ne->name, s); + log_debug("%s: hook %s is: %s", __func__, hd->name, s); free(s); } new_item = cmdq_get_command(cmdlist, state); @@ -75,19 +79,19 @@ hooks_insert_one(struct cmdq_item *item, struct notify_entry *ne, /* Parse a hook command. */ static struct cmd_parse_result * -hooks_parse(struct notify_entry *ne, struct cmd_find_state *fs, +hooks_parse(struct hooks_data *hd, struct cmd_find_state *fs, const char *value) { struct cmd_parse_result *pr; struct format_tree *ft; char *expanded; - if (!ne->expand) + if (!hd->expand) return (cmd_parse_from_string(value, NULL)); - ft = format_create_defaults(NULL, ne->client, fs->s, fs->wl, fs->wp); - if (ne->formats != NULL) - format_merge(ft, ne->formats); + ft = format_create_defaults(NULL, hd->client, fs->s, fs->wl, fs->wp); + if (hd->formats != NULL) + format_merge(ft, hd->formats); expanded = format_expand(ft, value); format_free(ft); @@ -98,7 +102,7 @@ hooks_parse(struct notify_entry *ne, struct cmd_find_state *fs, /* Insert commands for a hook. */ static void -hooks_insert(struct cmdq_item *item, struct notify_entry *ne) +hooks_insert(struct cmdq_item *item, struct hooks_data *hd) { struct cmd_find_state fs; struct options *oo; @@ -109,72 +113,72 @@ hooks_insert(struct cmdq_item *item, struct notify_entry *ne) const char *value; struct cmd_parse_result *pr; - log_debug("%s: inserting hook %s", __func__, ne->name); + log_debug("%s: inserting hook %s", __func__, hd->name); cmd_find_clear_state(&fs, 0); - if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs)) + if (cmd_find_empty_state(&hd->fs) || !cmd_find_valid_state(&hd->fs)) cmd_find_from_nothing(&fs, 0); else - cmd_find_copy_state(&fs, &ne->fs); + cmd_find_copy_state(&fs, &hd->fs); - if (ne->oo != NULL) { - oo = ne->oo; - o = options_get_only(oo, ne->name); + if (hd->oo != NULL) { + oo = hd->oo; + o = options_get_only(oo, hd->name); } else { if (fs.s == NULL) oo = global_s_options; else oo = fs.s->options; - o = options_get(oo, ne->name); + o = options_get(oo, hd->name); if (o == NULL && fs.wp != NULL) { oo = fs.wp->options; - o = options_get(oo, ne->name); + o = options_get(oo, hd->name); } if (o == NULL && fs.wl != NULL) { oo = fs.wl->window->options; - o = options_get(oo, ne->name); + o = options_get(oo, hd->name); } } if (o == NULL) { - log_debug("%s: hook %s not found", __func__, ne->name); + log_debug("%s: hook %s not found", __func__, hd->name); return; } state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); - cmdq_add_formats(state, ne->formats); + cmdq_add_formats(state, hd->formats); - if (*ne->name == '@') { - value = options_get_string(oo, ne->name); - pr = hooks_parse(ne, &fs, value); + if (*hd->name == '@') { + value = options_get_string(oo, hd->name); + pr = hooks_parse(hd, &fs, value); switch (pr->status) { case CMD_PARSE_ERROR: log_debug("%s: can't parse hook %s: %s", __func__, - ne->name, pr->error); + hd->name, pr->error); free(pr->error); break; case CMD_PARSE_SUCCESS: - hooks_insert_one(item, ne, pr->cmdlist, state); + hooks_insert_one(item, hd, pr->cmdlist, state); break; } } else { a = options_array_first(o); while (a != NULL) { - if (ne->expand) { + if (hd->expand) { value = options_array_item_value(a)->string; - pr = hooks_parse(ne, &fs, value); + pr = hooks_parse(hd, &fs, value); switch (pr->status) { case CMD_PARSE_ERROR: if (pr->error != NULL) cmdq_error(item, "%s", pr->error); break; case CMD_PARSE_SUCCESS: - item = hooks_insert_one(item, ne, + item = hooks_insert_one(item, hd, pr->cmdlist, state); break; } } else { cmdlist = options_array_item_value(a)->cmdlist; - item = hooks_insert_one(item, ne, cmdlist, + item = hooks_insert_one(item, hd, cmdlist, state); } a = options_array_next(a); @@ -184,46 +188,155 @@ hooks_insert(struct cmdq_item *item, struct notify_entry *ne) 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); +} + +/* Find the best target state for a hook payload. */ +static void +hooks_find_state(struct event_payload *ep, struct cmd_find_state *fs) +{ + struct window_pane *wp; + struct winlink *wl; + struct window *w; + struct session *s; + struct client *c; + struct hook_monitor *hm; + + wp = event_payload_get_pane(ep, "pane"); + if (wp != NULL && cmd_find_from_pane(fs, wp, 0) == 0) + return; + + wl = event_payload_get_winlink(ep, "winlink"); + if (wl != NULL) { + if (wp != NULL && wp->window == wl->window) { + cmd_find_from_winlink_pane(fs, wl, wp, 0); + return; + } + cmd_find_from_winlink(fs, wl, 0); + return; + } + + s = event_payload_get_session(ep, "session"); + w = event_payload_get_window(ep, "window"); + if (s != NULL && w != NULL && + cmd_find_from_session_window(fs, s, w, 0) == 0) + return; + + if (w != NULL && cmd_find_from_window(fs, w, 0) == 0) + return; + + if (s != NULL && session_alive(s)) { + cmd_find_from_session(fs, s, 0); + return; + } + + c = event_payload_get_client(ep, "client"); + if (c != NULL && cmd_find_from_client(fs, c, 0) == 0) + return; + + hm = event_payload_get_pointer(ep, "_hook_monitor"); + if (hm != NULL && !cmd_find_empty_state(&hm->fs) && + cmd_find_valid_state(&hm->fs)) { + cmd_find_copy_state(fs, &hm->fs); + return; + } + + if (cmd_find_from_nothing(fs, 0) != 0) + cmd_find_clear_state(fs, 0); +} + /* Insert commands for a hook event. */ static void hooks_insert_event(struct cmdq_item *item, const char *name, - struct events_type *type, void *data, struct options *oo, int expand) + struct event_payload *ep, struct options *oo, int expand) { - struct cmd_find_state fs; - struct notify_entry ne; + struct hooks_data hd; 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); + c = event_payload_get_client(ep, "client"); ft = format_create(c, item, FORMAT_NONE, FORMAT_NOJOBS); - events_add_formats(type, data, ft); + hooks_add_formats(ft, name, ep); + format_log_debug(ft, __func__); - 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; + memset(&hd, 0, sizeof hd); + hd.name = name; + hooks_find_state(ep, &hd.fs); + hd.formats = ft; + hd.oo = oo; + hd.client = c; + hd.expand = expand; - hooks_insert(item, &ne); + hooks_insert(item, &hd); format_free(ft); } /* Handle an event for hooks. */ static void -hooks_event_cb(const char *name, void *data, struct events_type *type, +hooks_event_cb(const char *name, struct event_payload *ep, __unused void *sink_data) { - hooks_insert_event(cmdq_running(NULL), name, type, data, NULL, 0); + hooks_insert_event(cmdq_running(NULL), name, ep, NULL, 0); } /* Add a hook event sink. */ @@ -248,80 +361,18 @@ void hooks_run(struct cmdq_item *item, const char *name) { struct cmd_find_state *target = cmdq_get_target(item); - struct notify_entry ne; + struct hooks_data hd = { 0 }; - memset(&ne, 0, sizeof ne); + hd.name = name; + cmd_find_copy_state(&hd.fs, target); + hd.client = cmdq_get_client(item); - ne.name = name; - cmd_find_copy_state(&ne.fs, target); + hd.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); + format_add(hd.formats, "hook", "%s", name); + format_log_debug(hd.formats, __func__); - ne.client = cmdq_get_client(item); - ne.session = target->s; - ne.window = target->w; - ne.pane = (target->wp != NULL ? (int)target->wp->id : -1); - - ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); - format_add(ne.formats, "hook", "%s", name); - format_log_debug(ne.formats, __func__); - - hooks_insert(item, &ne); - format_free(ne.formats); -} - -/* Add formats for a hook monitor event. */ -static void -hooks_monitor_event_add_formats(void *data, struct format_tree *ft) -{ - struct hook_monitor_event *hme = data; - struct monitor_change *change = hme->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); -} - -/* Find state for a hook monitor event. */ -static int -hooks_monitor_event_find_state(void *data, struct cmd_find_state *fs) -{ - struct hook_monitor_event *hme = data; - struct hook_monitor *hm = hme->hm; - struct monitor_change *change = hme->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, &hm->fs); - return (1); -} - -/* Get client for a hook monitor event. */ -static struct client * -hooks_monitor_event_get_client(void *data) -{ - struct hook_monitor_event *hme = data; - struct monitor_change *change = hme->change; - - return (change->c); + hooks_insert(item, &hd); + format_free(hd.formats); } /* Free a hook monitor. */ @@ -356,27 +407,55 @@ hooks_monitor_remove(struct options *oo, const char *name) /* Handle a hook monitor event. */ static void -hooks_monitor_hook_cb(const char *name, void *data, struct events_type *type, +hooks_monitor_hook_cb(const char *name, struct event_payload *ep, void *sink_data) { - struct hook_monitor_event *hme = data; - struct hook_monitor *hm = sink_data; + struct hook_monitor *hm = sink_data; - if (hme->hm != hm) - return; - - hooks_insert_event(cmdq_running(NULL), name, type, data, hm->oo, 1); + if (event_payload_get_pointer(ep, "_hook_monitor") == hm) + hooks_insert_event(cmdq_running(NULL), name, ep, hm->oo, 1); } /* Fire a hook monitor event. */ static void hooks_monitor_cb(struct monitor_change *change, void *data) { - struct hook_monitor_event hme; + struct event_payload *ep; + struct winlink *wl = change->wl; + struct window_pane *wp = change->wp; - hme.hm = data; - hme.change = change; - events_fire(change->name, &hme); + ep = event_payload_create(); + event_payload_set_pointer(ep, "_hook_monitor", data, NULL, NULL); + + if (change->value != NULL) + event_payload_set_string(ep, "value", "%s", change->value); + else + event_payload_set_string(ep, "value", "%s", ""); + if (change->last != NULL) + event_payload_set_string(ep, "last", "%s", change->last); + else + event_payload_set_string(ep, "last", "%s", ""); + + if (change->c != NULL) + event_payload_set_client(ep, "client", change->c); + if (change->s != NULL) + event_payload_set_session(ep, "session", change->s); + if (wl != NULL) { + if (change->s == NULL) + event_payload_set_session(ep, "session", + wl->session); + event_payload_set_window(ep, "window", wl->window); + event_payload_set_string(ep, "window_index", "%d", + wl->idx); + event_payload_set_winlink(ep, "winlink", wl); + } + if (wp != NULL) { + event_payload_set_pane(ep, "pane", wp); + if (wl == NULL) + event_payload_set_window(ep, "window", wp->window); + } + + events_fire(change->name, ep); } /* Add a hook monitor. */ @@ -400,8 +479,7 @@ hooks_monitor_add(__unused struct cmdq_item *item, struct options *oo, hm->id = id; hm->format = xstrdup(format); hm->set = monitor_create_session(s, hooks_monitor_cb, hm); - events_add_event(name, hooks_monitor_event_add_formats, - hooks_monitor_event_find_state, hooks_monitor_event_get_client); + events_add_event(name); hm->sink = events_add_sink(name, hooks_monitor_hook_cb, hm); options_set_monitor_data(o, hm); monitor_add(hm->set, name, type, id, format, 0); diff --git a/notify.c b/notify.c index 70639e682..3fcc7573e 100644 --- a/notify.c +++ b/notify.c @@ -23,127 +23,46 @@ #include "tmux.h" -/* Add formats for a notify event. */ -static void -notify_add_formats(void *data, struct format_tree *ft) -{ - struct notify_entry *ne = data; - - if (ne->formats != NULL) - format_merge(ft, ne->formats); -} - -/* Find state for a notify event. */ -static int -notify_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); -} - -/* Get client for a notify event. */ -static struct client * -notify_get_client(void *data) -{ - struct notify_entry *ne = data; - - return (ne->client); -} - -/* Build a notify event type. */ -static void -notify_build_event(const char *name) -{ - events_add_event(name, notify_add_formats, notify_find_state, - notify_get_client); - hooks_add_event(name); -} +/* Queued notify event. */ +struct notify_event { + char *name; + struct event_payload *payload; +}; /* Fire a queued notify event. */ static enum cmd_retval notify_callback(__unused struct cmdq_item *item, void *data) { - struct notify_entry *ne = data; + struct notify_event *ne = data; log_debug("%s: %s", __func__, ne->name); - notify_build_event(ne->name); - events_fire(ne->name, ne); + events_add_event(ne->name); + hooks_add_event(ne->name); + events_fire(ne->name, ne->payload); + ne->payload = NULL; - if (ne->client != NULL) - server_client_unref(ne->client); - if (ne->session != NULL) - session_remove_ref(ne->session, __func__); - if (ne->window != NULL) - window_remove_ref(ne->window, __func__); - - if (ne->fs.s != NULL) - session_remove_ref(ne->fs.s, __func__); - - format_free(ne->formats); - free((void *)ne->name); - free((void *)ne->pbname); + free(ne->name); free(ne); - return (CMD_RETURN_NORMAL); } /* Queue a notify event. */ static void -notify_add(const char *name, struct cmd_find_state *fs, struct client *c, - struct session *s, struct window *w, struct window_pane *wp, - const char *pbname) +notify_add(const char *name, struct event_payload *ep) { - struct notify_entry *ne; + struct notify_event *ne; struct cmdq_item *item; item = cmdq_running(NULL); - if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS)) + if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS)) { + event_payload_free(ep); return; + } ne = xcalloc(1, sizeof *ne); ne->name = xstrdup(name); - - ne->client = c; - ne->session = s; - ne->window = w; - ne->pane = (wp != NULL ? (int)wp->id : -1); - ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL); - - ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); - format_add(ne->formats, "hook", "%s", name); - if (c != NULL) - format_add(ne->formats, "hook_client", "%s", c->name); - if (s != NULL) { - format_add(ne->formats, "hook_session", "$%u", s->id); - format_add(ne->formats, "hook_session_name", "%s", s->name); - } - if (w != NULL) { - format_add(ne->formats, "hook_window", "@%u", w->id); - format_add(ne->formats, "hook_window_name", "%s", w->name); - } - if (wp != NULL) { - format_add(ne->formats, "hook_pane", "%%%d", wp->id); - format_add(ne->formats, "hook_window", "@%u", wp->window->id); - format_add(ne->formats, "hook_window_name", "%s", - wp->window->name); - } - format_log_debug(ne->formats, __func__); - - if (c != NULL) - c->references++; - if (s != NULL) - session_add_ref(s, __func__); - if (w != NULL) - window_add_ref(w, __func__); - - cmd_find_copy_state(&ne->fs, fs); - if (ne->fs.s != NULL) /* cmd_find_valid_state needs session */ - session_add_ref(ne->fs.s, __func__); + ne->payload = ep; cmdq_append(NULL, cmdq_get_callback(notify_callback, ne)); } @@ -151,71 +70,79 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c, void notify_client(const char *name, struct client *c) { - struct cmd_find_state fs; + struct event_payload *ep; - cmd_find_from_client(&fs, c, 0); - notify_add(name, &fs, c, NULL, NULL, NULL, NULL); + ep = event_payload_create(); + event_payload_set_client(ep, "client", c); + notify_add(name, ep); } void notify_session(const char *name, struct session *s) { - struct cmd_find_state fs; + struct event_payload *ep; - if (session_alive(s)) - cmd_find_from_session(&fs, s, 0); - else - cmd_find_from_nothing(&fs, 0); - notify_add(name, &fs, NULL, s, NULL, NULL, NULL); + ep = event_payload_create(); + event_payload_set_session(ep, "session", s); + notify_add(name, ep); } void notify_winlink(const char *name, struct winlink *wl) { - struct cmd_find_state fs; + struct event_payload *ep; - cmd_find_from_winlink(&fs, wl, 0); - notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL); + ep = event_payload_create(); + event_payload_set_session(ep, "session", wl->session); + event_payload_set_window(ep, "window", wl->window); + event_payload_set_string(ep, "window_index", "%d", wl->idx); + /* XXX winlink is not refcounted; store session/window IDs and index. */ + event_payload_set_winlink(ep, "winlink", wl); + notify_add(name, ep); } void notify_session_window(const char *name, struct session *s, struct window *w) { - struct cmd_find_state fs; + struct event_payload *ep; - cmd_find_from_session_window(&fs, s, w, 0); - notify_add(name, &fs, NULL, s, w, NULL, NULL); + ep = event_payload_create(); + event_payload_set_session(ep, "session", s); + event_payload_set_window(ep, "window", w); + notify_add(name, ep); } void notify_window(const char *name, struct window *w) { - struct cmd_find_state fs; + struct event_payload *ep; - cmd_find_from_window(&fs, w, 0); - notify_add(name, &fs, NULL, NULL, w, NULL, NULL); + ep = event_payload_create(); + event_payload_set_window(ep, "window", w); + notify_add(name, ep); } void notify_pane(const char *name, struct window_pane *wp) { - struct cmd_find_state fs; + struct event_payload *ep; - cmd_find_from_pane(&fs, wp, 0); - notify_add(name, &fs, NULL, NULL, NULL, wp, NULL); + ep = event_payload_create(); + /* XXX window_pane is not refcounted; store pane ID for now. */ + event_payload_set_pane(ep, "pane", wp); + event_payload_set_window(ep, "window", wp->window); + notify_add(name, ep); } void notify_paste_buffer(const char *pbname, int deleted) { - struct cmd_find_state fs; + struct event_payload *ep; - cmd_find_clear_state(&fs, 0); - if (deleted) { - notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, - NULL, pbname); - } else { - notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, - NULL, pbname); - } + ep = event_payload_create(); + event_payload_set_string(ep, "paste_buffer", "%s", pbname); + if (deleted) + notify_add("paste-buffer-deleted", ep); + else + notify_add("paste-buffer-changed", ep); } diff --git a/tmux.h b/tmux.h index c4dd45ca3..e46f00b0a 100644 --- a/tmux.h +++ b/tmux.h @@ -49,8 +49,9 @@ struct cmdq_state; struct cmds; struct control_state; struct environ; +struct event_payload; +struct event_payload_item; struct events_sink; -struct events_type; struct format_job_tree; struct format_tree; struct hyperlinks_uri; @@ -2338,27 +2339,22 @@ struct monitor_change { }; typedef void (*monitor_cb)(struct monitor_change *, void *); -/* Events. */ -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 *); - -/* Data passed by notify producers to event sinks. */ -struct notify_entry { - const char *name; - struct cmd_find_state fs; - struct format_tree *formats; - struct options *oo; - - struct client *client; - struct session *session; - struct window *window; - int pane; - const char *pbname; - int expand; +/* Event payload type. */ +enum event_payload_type { + EVENT_PAYLOAD_STRING, + EVENT_PAYLOAD_TIME, + EVENT_PAYLOAD_CLIENT, + EVENT_PAYLOAD_SESSION, + EVENT_PAYLOAD_WINDOW, + EVENT_PAYLOAD_PANE, + EVENT_PAYLOAD_WINLINK, + EVENT_PAYLOAD_POINTER }; +/* Event payload callbacks. */ +typedef void (*event_payload_free_cb)(void *); +typedef void (*event_payload_print_cb)(void *, struct evbuffer *); + /* Key binding and key table. */ struct key_binding { key_code key; @@ -2668,25 +2664,62 @@ char *format_grid_hyperlink(struct grid *, u_int, u_int, struct screen *); char *format_grid_line(struct grid *, u_int); +/* events-payload.c */ +struct event_payload *event_payload_create(void); +void event_payload_free(struct event_payload *); +void printflike(2, 3) event_payload_log(struct event_payload *, const char *, + ...); +char *event_payload_item_print(struct event_payload_item *); +void printflike(3, 4) event_payload_set_string(struct event_payload *, + const char *, const char *, ...); +void event_payload_set_time(struct event_payload *, const char *, time_t); +void event_payload_set_client(struct event_payload *, const char *, + struct client *); +void event_payload_set_session(struct event_payload *, const char *, + struct session *); +void event_payload_set_window(struct event_payload *, const char *, + struct window *); +void event_payload_set_pane(struct event_payload *, const char *, + struct window_pane *); +void event_payload_set_winlink(struct event_payload *, const char *, + struct winlink *); +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 *); +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 *); +enum event_payload_type event_payload_item_type(struct event_payload_item *); +int event_payload_get_time(struct event_payload *, const char *, + time_t *); +struct client *event_payload_get_client(struct event_payload *, + const char *); +struct session *event_payload_get_session(struct event_payload *, + const char *); +struct window *event_payload_get_window(struct event_payload *, + const char *); +struct window_pane *event_payload_get_pane(struct event_payload *, + const char *); +struct winlink *event_payload_get_winlink(struct event_payload *, + const char *); +void *event_payload_get_pointer(struct event_payload *, + const char *); + /* events.c */ -int events_add_event(const char *, events_add_formats_cb, - events_find_state_cb, events_get_client_cb); +typedef void (*events_cb)(const char *, struct event_payload *, void *); +int events_add_event(const char *); 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 *); +void events_fire(const char *, struct event_payload *); /* format-draw.c */ -void format_draw(struct screen_write_ctx *, - const struct grid_cell *, u_int, const char *, - struct style_ranges *, int); -u_int format_width(const char *); -char *format_trim_left(const char *, u_int); -char *format_trim_right(const char *, u_int); +void format_draw(struct screen_write_ctx *, const struct grid_cell *, + u_int, const char *, struct style_ranges *, int); +u_int format_width(const char *); +char *format_trim_left(const char *, u_int); +char *format_trim_right(const char *, u_int); /* hooks.c */ void hooks_add_event(const char *);