From 27ab96b284e71ed82c1a8930dd900260fd1583f0 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 27 Jul 2026 19:15:58 +0000 Subject: [PATCH] Store count of how many times a hook is fired and the last time. --- cmd-show-options.c | 29 ++++++++++++++++++++++++++- hooks.c | 50 +++++++++++++++++++++++++++++++++------------- monitor.c | 30 +++++++++++++++++++++++++++- options.c | 24 +++++++++++++++++++++- tmux.1 | 6 ++++-- tmux.h | 9 ++++++++- 6 files changed, 128 insertions(+), 20 deletions(-) diff --git a/cmd-show-options.c b/cmd-show-options.c index 0b3329f7c..910aaf348 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-show-options.c,v 1.75 2026/07/22 20:12:58 nicm Exp $ */ +/* $OpenBSD: cmd-show-options.c,v 1.76 2026/07/27 19:15:58 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -191,6 +191,9 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, const char *name = options_name(o); const char *template = args_get(args, 'F'); char *value, *line; + struct timeval tv = { 0 }; + u_int fire_count; + time_t fire_time; int is_hook = 0, is_user = 0; int has_value = 1; const struct options_table_entry *oe = options_table_entry(o); @@ -233,6 +236,16 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item, format_add(ft, "option_is_hook", "%d", is_hook); format_add(ft, "option_is_user", "%d", is_user); format_add(ft, "option_has_value", "%d", has_value); + if (cmd_get_entry(self) == &cmd_show_hooks_entry) { + fire_count = options_get_fire_count(o); + format_add(ft, "hook_fire_count", "%u", fire_count); + + fire_time = options_get_fire_time(o); + if (fire_time != 0) { + tv.tv_sec = fire_time; + format_add_tv(ft, "hook_fire_time", &tv); + } + } if (array_key != NULL) { format_add(ft, "option_array_key", "%s", array_key); format_add(ft, "option_has_array_key", "1"); @@ -257,6 +270,9 @@ cmd_show_hooks_print_monitor(struct cmd *self, struct cmdq_item *item, enum monitor_type type; const char *template = args_get(args, 'F'), *format; char *value, *target, *line; + struct timeval tv = { 0 }; + u_int fire_count; + time_t fire_time; int id; value = hooks_monitor_to_string(o); @@ -299,8 +315,19 @@ cmd_show_hooks_print_monitor(struct cmd *self, struct cmdq_item *item, format_add(ft, "option_has_value", "%d", 1); format_add(ft, "option_array_key", "%s", ""); format_add(ft, "option_has_array_key", "0"); + format_add(ft, "hook_monitor_target", "%s", target); format_add(ft, "hook_monitor_format", "%s", format); + + fire_count = hooks_monitor_get_fire_count(o); + format_add(ft, "hook_fire_count", "%u", fire_count); + + fire_time = hooks_monitor_get_fire_time(o); + if (fire_time != 0) { + tv.tv_sec = fire_time; + format_add_tv(ft, "hook_fire_time", &tv); + } + line = format_expand(ft, template); format_free(ft); diff --git a/hooks.c b/hooks.c index bd0d1d732..acc11704d 100644 --- a/hooks.c +++ b/hooks.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hooks.c,v 1.15 2026/07/22 20:12:58 nicm Exp $ */ +/* $OpenBSD: hooks.c,v 1.16 2026/07/27 19:15:58 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -25,7 +25,7 @@ #include "tmux.h" /* Hook monitor state owned by an option entry. */ -struct hook_monitor { +struct hooks_monitor { struct options *oo; struct monitor_set *set; @@ -143,6 +143,7 @@ hooks_insert(struct cmdq_item *item, struct hooks_data *hd) log_debug("%s: hook %s not found", __func__, hd->name); return; } + options_hook_fired(o); if (item == NULL) state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); @@ -233,7 +234,7 @@ hooks_event_cb(const char *name, struct event_payload *ep, { struct cmdq_item *item; - if (event_payload_get_pointer(ep, "_hook_monitor") != NULL) + if (event_payload_get_pointer(ep, "_hooks_monitor") != NULL) return; item = event_payload_get_pointer(ep, "_cmdq_item"); @@ -324,7 +325,7 @@ hooks_run(struct cmdq_item *item, const char *name) void hooks_monitor_free(void *data) { - struct hook_monitor *hm = data; + struct hooks_monitor *hm = data; events_remove_sink(hm->sink); monitor_destroy(hm->set); @@ -337,7 +338,7 @@ void hooks_monitor_remove(struct options *oo, const char *name) { struct options_entry *o; - struct hook_monitor *hm; + struct hooks_monitor *hm; o = options_get_only(oo, name); if (o == NULL) @@ -355,9 +356,9 @@ static void hooks_monitor_hook_cb(const char *name, struct event_payload *ep, void *sink_data) { - struct hook_monitor *hm = sink_data; + struct hooks_monitor *hm = sink_data; - if (event_payload_get_pointer(ep, "_hook_monitor") == hm) + if (event_payload_get_pointer(ep, "_hooks_monitor") == hm) hooks_insert_event(cmdq_running(NULL), name, ep, hm->oo, 1); } @@ -365,14 +366,14 @@ hooks_monitor_hook_cb(const char *name, struct event_payload *ep, static void hooks_monitor_cb(struct monitor_change *change, void *data) { - struct hook_monitor *hm = data; + struct hooks_monitor *hm = data; struct event_payload *ep; struct winlink *wl = change->wl; struct window_pane *wp = change->wp; struct cmd_find_state fs; ep = event_payload_create(); - event_payload_set_pointer(ep, "_hook_monitor", data, NULL, NULL); + event_payload_set_pointer(ep, "_hooks_monitor", data, NULL, NULL); cmd_find_clear_state(&fs, 0); if (wl != NULL && wp != NULL && wp->window == wl->window) @@ -402,8 +403,7 @@ hooks_monitor_cb(struct monitor_change *change, void *data) 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_session(ep, "session", wl->session); event_payload_set_window(ep, "window", wl->window); event_payload_set_int(ep, "window_index", wl->idx); } @@ -423,7 +423,7 @@ hooks_monitor_add(__unused struct cmdq_item *item, struct options *oo, int flags, struct cmd_find_state *fs, struct session *s) { struct options_entry *o; - struct hook_monitor *hm; + struct hooks_monitor *hm; hooks_monitor_remove(oo, name); o = options_get_only(oo, name); @@ -446,7 +446,7 @@ hooks_monitor_add(__unused struct cmdq_item *item, struct options *oo, char * hooks_monitor_to_string(struct options_entry *o) { - struct hook_monitor *hm = options_get_monitor_data(o); + struct hooks_monitor *hm = options_get_monitor_data(o); const char *name = options_name(o); char *s; @@ -478,7 +478,7 @@ int hooks_monitor_get(struct options_entry *o, enum monitor_type *type, int *id, const char **format) { - struct hook_monitor *hm = options_get_monitor_data(o); + struct hooks_monitor *hm = options_get_monitor_data(o); if (hm == NULL) return (0); @@ -487,3 +487,25 @@ hooks_monitor_get(struct options_entry *o, enum monitor_type *type, int *id, *format = hm->format; return (1); } + +/* Get hook monitor firing count. */ +u_int +hooks_monitor_get_fire_count(struct options_entry *o) +{ + struct hooks_monitor *hm = options_get_monitor_data(o); + + if (hm == NULL) + return (0); + return (monitor_get_fire_count(hm->set, options_name(o))); +} + +/* Get hook monitor firing time. */ +time_t +hooks_monitor_get_fire_time(struct options_entry *o) +{ + struct hooks_monitor *hm = options_get_monitor_data(o); + + if (hm == NULL) + return (0); + return (monitor_get_fire_time(hm->set, options_name(o))); +} diff --git a/monitor.c b/monitor.c index d6338e878..ff0c6e5ed 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.6 2026/07/10 15:20:06 nicm Exp $ */ +/* $OpenBSD: monitor.c,v 1.7 2026/07/27 19:15:58 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott @@ -59,6 +59,9 @@ struct monitor_item { struct monitor_panes panes; struct monitor_windows windows; + u_int fire_count; + time_t fire_time; + RB_ENTRY(monitor_item) entry; }; RB_HEAD(monitor_items, monitor_item); @@ -180,6 +183,9 @@ monitor_report(struct monitor_set *ms, struct monitor_item *me, log_debug("%s: %s changed to %s", __func__, me->name, value); + me->fire_count++; + me->fire_time = current_time; + change.name = me->name; change.value = value; change.last = last; @@ -659,3 +665,25 @@ monitor_remove(struct monitor_set *ms, const char *name) if (RB_EMPTY(&ms->items) && evtimer_initialized(&ms->timer)) evtimer_del(&ms->timer); } + +/* Get subscription firing count. */ +u_int +monitor_get_fire_count(struct monitor_set *ms, const char *name) +{ + struct monitor_item *me, find = { .name = (char *)name }; + + if ((me = RB_FIND(monitor_items, &ms->items, &find)) == NULL) + return (0); + return (me->fire_count); +} + +/* Get subscription firing time. */ +time_t +monitor_get_fire_time(struct monitor_set *ms, const char *name) +{ + struct monitor_item *me, find = { .name = (char *)name }; + + if ((me = RB_FIND(monitor_items, &ms->items, &find)) == NULL) + return (0); + return (me->fire_time); +} diff --git a/options.c b/options.c index c3d948020..fa10d8a7d 100644 --- a/options.c +++ b/options.c @@ -1,4 +1,4 @@ -/* $OpenBSD: options.c,v 1.91 2026/07/23 09:38:27 nicm Exp $ */ +/* $OpenBSD: options.c,v 1.92 2026/07/27 19:15:58 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -107,7 +107,10 @@ struct options_entry { int cached; struct style style; + void *monitor_data; + u_int fire_count; + time_t fire_time; RB_ENTRY(options_entry) entry; }; @@ -440,6 +443,25 @@ options_set_monitor_data(struct options_entry *o, void *data) o->monitor_data = data; } +void +options_hook_fired(struct options_entry *o) +{ + o->fire_count++; + o->fire_time = current_time; +} + +u_int +options_get_fire_count(struct options_entry *o) +{ + return (o->fire_count); +} + +time_t +options_get_fire_time(struct options_entry *o) +{ + return (o->fire_time); +} + const struct options_table_entry * options_table_entry(struct options_entry *o) { diff --git a/tmux.1 b/tmux.1 index 52b74c9e5..b12a9fbdc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1149 2026/07/23 09:38:27 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1150 2026/07/27 19:15:58 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 23 2026 $ +.Dd $Mdocdate: July 27 2026 $ .Dt TMUX 1 .Os .Sh NAME @@ -7283,6 +7283,8 @@ The following variables are available, where appropriate: .It Li "history_size" Ta "" Ta "Size of history in lines" .It Li "hook" Ta "" Ta "Name of running hook, if any" .It Li "hook_client" Ta "" Ta "Name of client where hook was run, if any" +.It Li "hook_fire_count" Ta "" Ta "Number of times hook has fired" +.It Li "hook_fire_time" Ta "" Ta "Time hook last fired" .It Li "hook_last" Ta "" Ta "Previous value for a monitor hook" .It Li "hook_monitor_format" Ta "" Ta "Format for a monitor hook" .It Li "hook_monitor_target" Ta "" Ta "Target for a monitor hook" diff --git a/tmux.h b/tmux.h index 368813635..0f3969a1c 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1415 2026/07/23 09:38:27 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1416 2026/07/27 19:15:58 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -2745,6 +2745,8 @@ void hooks_monitor_free(void *); char *hooks_monitor_to_string(struct options_entry *); int hooks_monitor_get(struct options_entry *, enum monitor_type *, int *, const char **); +u_int hooks_monitor_get_fire_count(struct options_entry *); +time_t hooks_monitor_get_fire_time(struct options_entry *); /* options.c */ struct options *options_create(struct options *); @@ -2762,6 +2764,9 @@ const char *options_name(struct options_entry *); struct options *options_owner(struct options_entry *); void *options_get_monitor_data(struct options_entry *); void options_set_monitor_data(struct options_entry *, void *); +void options_hook_fired(struct options_entry *); +u_int options_get_fire_count(struct options_entry *); +time_t options_get_fire_time(struct options_entry *); const struct options_table_entry *options_table_entry(struct options_entry *); struct options_entry *options_get_only(struct options *, const char *); struct options_entry *options_get(struct options *, const char *); @@ -3957,6 +3962,8 @@ int monitor_parse(const char *, char **, enum monitor_type *, int *, void monitor_add(struct monitor_set *, const char *, enum monitor_type, int, const char *, int); void monitor_remove(struct monitor_set *, const char *); +u_int monitor_get_fire_count(struct monitor_set *, const char *); +time_t monitor_get_fire_time(struct monitor_set *, const char *); /* control.c */ void control_discard(struct client *);