diff --git a/Makefile b/Makefile index 165d0d47c..d73312013 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD$ +# $OpenBSD: Makefile,v 1.120 2026/07/10 13:38:45 nicm Exp $ PROG= tmux SRCS= alerts.c \ @@ -76,6 +76,8 @@ SRCS= alerts.c \ control-notify.c \ control.c \ environ.c \ + events.c \ + events-payload.c \ file.c \ format.c \ fuzzy.c \ @@ -83,6 +85,7 @@ SRCS= alerts.c \ grid-reader.c \ grid-view.c \ grid.c \ + hooks.c \ hyperlinks.c \ input-keys.c \ input.c \ @@ -97,7 +100,6 @@ SRCS= alerts.c \ mode-tree.c \ monitor.c \ names.c \ - notify.c \ options-table.c \ options.c \ paste.c \ diff --git a/alerts.c b/alerts.c index a2b4d6541..824fb7381 100644 --- a/alerts.c +++ b/alerts.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: alerts.c,v 1.35 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott @@ -206,7 +206,7 @@ alerts_check_bell(struct window *w) } if (!alerts_action_applies(wl, "bell-action")) continue; - notify_winlink("alert-bell", wl); + events_fire_winlink("alert-bell", wl); if (s->flags & SESSION_ALERTED) continue; @@ -242,7 +242,7 @@ alerts_check_activity(struct window *w) } if (!alerts_action_applies(wl, "activity-action")) continue; - notify_winlink("alert-activity", wl); + events_fire_winlink("alert-activity", wl); if (s->flags & SESSION_ALERTED) continue; @@ -278,7 +278,7 @@ alerts_check_silence(struct window *w) } if (!alerts_action_applies(wl, "silence-action")) continue; - notify_winlink("alert-silence", wl); + events_fire_winlink("alert-silence", wl); if (s->flags & SESSION_ALERTED) continue; diff --git a/arguments.c b/arguments.c index cab40aa6e..baafd6d52 100644 --- a/arguments.c +++ b/arguments.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: arguments.c,v 1.66 2026/06/26 09:54:56 nicm Exp $ */ /* * Copyright (c) 2010 Nicholas Marriott diff --git a/attributes.c b/attributes.c index b0c9ef518..d853b8db0 100644 --- a/attributes.c +++ b/attributes.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: attributes.c,v 1.13 2026/07/02 08:51:05 nicm Exp $ */ /* * Copyright (c) 2009 Joshua Elsasser diff --git a/cfg.c b/cfg.c index 450ab88fb..7a79a6025 100644 --- a/cfg.c +++ b/cfg.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cfg.c,v 1.89 2026/06/25 11:39:11 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/client.c b/client.c index fcc49c395..1b7fdb043 100644 --- a/client.c +++ b/client.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: client.c,v 1.165 2025/04/25 12:25:32 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 32b1e0cbd..050c7ca38 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-attach-session.c,v 1.91 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -163,7 +163,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); - notify_client("client-attached", c); + events_fire_client("client-attached", c); c->flags |= CLIENT_ATTACHED; } diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 01f8e9614..0d7636ff7 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-bind-key.c,v 1.47 2025/04/09 07:03:04 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 3891534a0..8d85c57b2 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-break-pane.c,v 1.72 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -80,7 +80,7 @@ cmd_break_pane_float(struct cmdq_item *item, struct args *args, window_set_active_pane(w, wp, 1); layout_fix_offsets(w); layout_fix_panes(w, NULL); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); return (CMD_RETURN_NORMAL); @@ -99,7 +99,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) struct session *dst_s = target->s; struct window_pane *wp = source->wp; struct window *w = wl->window; - char *newname, *cause, *cp; + char *cause, *cp; int idx = target->idx, before; const char *template, *name = args_get(args, 'n'); @@ -151,6 +151,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) layout_close_pane(wp); w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel); + window_add_ref(w, __func__); options_set_parent(wp->options, w->options); wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED); TAILQ_INSERT_HEAD(&w->panes, wp, entry); @@ -158,12 +159,11 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) w->active = wp; w->latest = tc; - if (name == NULL) { - newname = default_window_name(w); - window_set_name(w, newname, 0); - free(newname); - } else { - window_set_name(w, name, 0); + free(w->name); + if (name == NULL) + w->name = default_window_name(w); + else { + w->name = clean_name(name, 0); options_set_number(w->options, "automatic-rename", 0); } @@ -174,6 +174,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) if (idx == -1) idx = -1 - options_get_number(dst_s->options, "base-index"); wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ + window_remove_ref(w, __func__); if (!args_has(args, 'd')) { session_select(dst_s, wl->idx); cmd_find_from_session(current, dst_s, 0); diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index febf29697..d83590f90 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-capture-pane.c,v 1.67 2026/07/02 10:34:14 nicm Exp $ */ /* * Copyright (c) 2009 Jonathan Alvarado diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 4a6bd152b..f9ce7d3b7 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-choose-tree.c,v 1.57 2026/06/26 14:40:30 nicm Exp $ */ /* * Copyright (c) 2012 Thomas Adam diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index 045ef0b12..ac1117347 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-command-prompt.c,v 1.75 2026/06/25 11:39:11 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 54bc9a2a1..fda3af772 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-confirm-before.c,v 1.61 2026/06/24 10:55:39 nicm Exp $ */ /* * Copyright (c) 2009 Tiago Cunha diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c index cfa60f59f..051cb58a2 100644 --- a/cmd-copy-mode.c +++ b/cmd-copy-mode.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-copy-mode.c,v 1.53 2026/07/08 08:07:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-detach-client.c b/cmd-detach-client.c index b5a4cf1c1..82ce9a00b 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-detach-client.c,v 1.39 2026/05/22 15:22:43 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-display-menu.c b/cmd-display-menu.c index 97973b8bd..c2d83ff27 100644 --- a/cmd-display-menu.c +++ b/cmd-display-menu.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-display-menu.c,v 1.51 2026/07/07 09:45:09 nicm Exp $ */ /* * Copyright (c) 2019 Nicholas Marriott diff --git a/cmd-display-message.c b/cmd-display-message.c index 3138d9223..f786006c3 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-display-message.c,v 1.65 2026/02/23 08:46:57 nicm Exp $ */ /* * Copyright (c) 2009 Tiago Cunha diff --git a/cmd-display-panes.c b/cmd-display-panes.c index f8991dfe1..176238a1f 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-display-panes.c,v 1.56 2026/06/25 16:32:42 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/cmd-find-window.c b/cmd-find-window.c index 560998370..28a8949cd 100644 --- a/cmd-find-window.c +++ b/cmd-find-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-find-window.c,v 1.56 2023/12/27 20:42:01 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/cmd-find.c b/cmd-find.c index 7ce714583..5173591f9 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-find.c,v 1.86 2026/07/02 08:47:25 nicm Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott diff --git a/cmd-has-session.c b/cmd-has-session.c deleted file mode 100644 index a873b2046..000000000 --- a/cmd-has-session.c +++ /dev/null @@ -1,46 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2007 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 MIND, 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 "tmux.h" - -/* - * Cause client to report an error and exit with 1 if session doesn't exist. - */ - -enum cmd_retval cmd_has_session_exec(struct cmd *, struct cmd_q *); - -const struct cmd_entry cmd_has_session_entry = { - "has-session", "has", - "t:", 0, 0, - CMD_TARGET_SESSION_USAGE, - 0, - cmd_has_session_exec -}; - -enum cmd_retval -cmd_has_session_exec(struct cmd *self, struct cmd_q *cmdq) -{ - struct args *args = self->args; - - if (cmd_find_session(cmdq, args_get(args, 't'), 0) == NULL) - return (CMD_RETURN_ERROR); - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-if-shell.c b/cmd-if-shell.c index f0c3637cc..49b92a996 100644 --- a/cmd-if-shell.c +++ b/cmd-if-shell.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-if-shell.c,v 1.86 2025/08/01 09:05:51 nicm Exp $ */ /* * Copyright (c) 2009 Tiago Cunha diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 122505d81..1b290a01e 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-join-pane.c,v 1.69 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2011 George Nachman @@ -187,7 +187,7 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl, lc->g.yoff = yoff; layout_fix_panes(w, NULL); } - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); return (CMD_RETURN_NORMAL); @@ -257,7 +257,7 @@ cmd_join_pane_move(struct cmdq_item *item, struct args *args, lc->g.xoff = xoff; lc->g.yoff = yoff; layout_fix_panes(w, NULL); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); } @@ -359,7 +359,7 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl, else TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); return (CMD_RETURN_NORMAL); @@ -398,7 +398,7 @@ cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w, window_set_active_pane(w, wp, 1); layout_fix_offsets(w); layout_fix_panes(w, NULL); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); return (CMD_RETURN_NORMAL); @@ -502,8 +502,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) if (window_count_panes(src_w, 1) == 0) server_kill_window(src_w, 1); else - notify_window("window-layout-changed", src_w); - notify_window("window-layout-changed", dst_w); + events_fire_window("window-layout-changed", src_w); + events_fire_window("window-layout-changed", dst_w); return (CMD_RETURN_NORMAL); } diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 836551f01..f634a7cd7 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-kill-pane.c,v 1.34 2026/06/09 21:22:22 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 7bb79e3d4..8b70be3d8 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-kill-server.c,v 1.20 2021/08/21 10:22:39 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-kill-session.c b/cmd-kill-session.c index 4bf36d785..7edd2a30a 100644 --- a/cmd-kill-session.c +++ b/cmd-kill-session.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-kill-session.c,v 1.31 2026/06/09 12:57:40 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-kill-window.c b/cmd-kill-window.c index 0253605f1..e7a6ec32e 100644 --- a/cmd-kill-window.c +++ b/cmd-kill-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-kill-window.c,v 1.30 2026/06/09 12:57:40 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 347c69341..82ee58947 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-list-buffers.c,v 1.41 2026/02/27 08:25:12 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-list-clients.c b/cmd-list-clients.c index ae235d83c..77f3912ca 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-list-clients.c,v 1.42 2026/02/27 08:25:12 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-list-commands.c b/cmd-list-commands.c index 0b0f912d8..5e9c4430c 100644 --- a/cmd-list-commands.c +++ b/cmd-list-commands.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-list-commands.c,v 1.15 2026/02/24 08:22:13 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 7aa38c61e..e8617c2e3 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-list-keys.c,v 1.78 2026/07/01 13:12:17 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-list-panes.c b/cmd-list-panes.c index 094ca850c..54c8db7d3 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-list-panes.c,v 1.40 2026/06/01 14:01:09 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index 79a1ab620..07a622979 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-list-sessions.c,v 1.36 2026/02/27 08:25:12 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 927976398..40f8c2db5 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-list-windows.c,v 1.50 2026/02/27 08:25:12 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-list.c b/cmd-list.c deleted file mode 100644 index 82ffe55c0..000000000 --- a/cmd-list.c +++ /dev/null @@ -1,103 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2009 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 MIND, 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 "tmux.h" - -static u_int cmd_list_next_group = 1; - -struct cmd_list * -cmd_list_new(void) -{ - struct cmd_list *cmdlist; - - cmdlist = xcalloc(1, sizeof *cmdlist); - cmdlist->references = 1; - cmdlist->group = cmd_list_next_group++; - TAILQ_INIT(&cmdlist->list); - return (cmdlist); -} - -void -cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) -{ - cmd->group = cmdlist->group; - TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); -} - -void -cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) -{ - struct cmd *cmd, *cmd1; - - TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) { - TAILQ_REMOVE(&from->list, cmd, qentry); - TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); - } - cmdlist->group = cmd_list_next_group++; -} - -void -cmd_list_free(struct cmd_list *cmdlist) -{ - struct cmd *cmd, *cmd1; - - if (--cmdlist->references != 0) - return; - - TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) { - TAILQ_REMOVE(&cmdlist->list, cmd, qentry); - cmd_free(cmd); - } - - free(cmdlist); -} - -char * -cmd_list_print(struct cmd_list *cmdlist, int escaped) -{ - struct cmd *cmd; - char *buf, *this; - size_t len; - - len = 1; - buf = xcalloc(1, len); - - TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { - this = cmd_print(cmd); - - len += strlen(this) + 4; - buf = xrealloc(buf, len); - - strlcat(buf, this, len); - if (TAILQ_NEXT(cmd, qentry) != NULL) { - if (escaped) - strlcat(buf, " \\; ", len); - else - strlcat(buf, " ; ", len); - } - - free(this); - } - - return (buf); -} diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c index 82ac2fd47..df35b82c1 100644 --- a/cmd-load-buffer.c +++ b/cmd-load-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-load-buffer.c,v 1.66 2025/10/28 07:32:26 nicm Exp $ */ /* * Copyright (c) 2009 Tiago Cunha diff --git a/cmd-lock-server.c b/cmd-lock-server.c index bd61dcffd..1feac03f8 100644 --- a/cmd-lock-server.c +++ b/cmd-lock-server.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-lock-server.c,v 1.31 2021/08/21 10:22:39 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/cmd-move-window.c b/cmd-move-window.c index 4b90e70f8..1cd4f10a8 100644 --- a/cmd-move-window.c +++ b/cmd-move-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-move-window.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/cmd-new-session.c b/cmd-new-session.c index ba02c72d4..56d4f9779 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-new-session.c,v 1.152 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -325,7 +325,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) session_group_synchronize_to(s); session_select(s, RB_MIN(winlinks, &s->windows)->idx); } - notify_session("session-created", s); + events_fire_session("session-created", s); /* * Set the client to the new session. If a command client exists, it is diff --git a/cmd-new-window.c b/cmd-new-window.c index a54aaaa20..d1e2ab4e5 100644 --- a/cmd-new-window.c +++ b/cmd-new-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-new-window.c,v 1.103 2026/07/08 08:07:42 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-parse.y b/cmd-parse.y index 0b32f5d34..29b7780e0 100644 --- a/cmd-parse.y +++ b/cmd-parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-parse.y,v 1.58 2026/04/27 12:31:11 nicm Exp $ */ /* * Copyright (c) 2019 Nicholas Marriott diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 03fcb0d40..fbe598b04 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-paste-buffer.c,v 1.44 2026/03/04 07:19:32 tb Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 13fe679e3..f18added6 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-pipe-pane.c,v 1.63 2026/04/28 08:47:55 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/cmd-queue.c b/cmd-queue.c index a6dfc5925..463d82b3f 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-queue.c,v 1.120 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2013 Nicholas Marriott @@ -351,74 +351,53 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) /* Insert a hook. */ void -cmdq_insert_hook(struct session *s, struct cmdq_item *item, +cmdq_insert_hook(__unused struct session *s, struct cmdq_item *item, struct cmd_find_state *current, const char *fmt, ...) { - struct cmdq_state *state = item->state; struct cmd *cmd = item->cmd; struct args *args = cmd_get_args(cmd); struct args_entry *ae; struct args_value *av; - struct options *oo; + struct event_payload *ep; va_list ap; char *name, tmp[32], flag, *arguments; u_int i; const char *value; - struct cmdq_item *new_item; - struct cmdq_state *new_state; - struct options_entry *o; - struct options_array_item *a; - struct cmd_list *cmdlist; if (item->state->flags & CMDQ_STATE_NOHOOKS) return; - if (s == NULL) - oo = global_s_options; - else - oo = s->options; va_start(ap, fmt); xvasprintf(&name, fmt, ap); va_end(ap); - o = options_get(oo, name); - if (o == NULL) { - free(name); - return; - } - log_debug("running hook %s (parent %p)", name, item); - - /* - * The hooks get a new state because they should not update the current - * target or formats for any subsequent commands. - */ - new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS); - cmdq_add_format(new_state, "hook", "%s", name); + ep = event_payload_create(); + if (current != NULL) + event_payload_set_target(ep, current); + event_payload_set_pointer(ep, "_cmdq_item", item, NULL, NULL); arguments = args_print(args); - cmdq_add_format(new_state, "hook_arguments", "%s", arguments); + event_payload_set_string(ep, "arguments", "%s", arguments); free(arguments); for (i = 0; i < args_count(args); i++) { - xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i); - cmdq_add_format(new_state, tmp, "%s", args_string(args, i)); + xsnprintf(tmp, sizeof tmp, "argument_%u", i); + event_payload_set_string(ep, tmp, "%s", args_string(args, i)); } flag = args_first(args, &ae); while (flag != 0) { value = args_get(args, flag); - if (value == NULL) { - xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag); - cmdq_add_format(new_state, tmp, "1"); - } else { - xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag); - cmdq_add_format(new_state, tmp, "%s", value); - } + xsnprintf(tmp, sizeof tmp, "flag_%c", flag); + if (value == NULL) + event_payload_set_string(ep, tmp, "1"); + else + event_payload_set_string(ep, tmp, "%s", value); i = 0; av = args_first_value(args, flag); while (av != NULL) { - xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i); - cmdq_add_format(new_state, tmp, "%s", av->string); + xsnprintf(tmp, sizeof tmp, "flag_%c_%u", flag, i); + event_payload_set_string(ep, tmp, "%s", av->string); i++; av = args_next_value(av); } @@ -426,20 +405,7 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item, flag = args_next(&ae); } - a = options_array_first(o); - while (a != NULL) { - cmdlist = options_array_item_value(a)->cmdlist; - if (cmdlist != NULL) { - new_item = cmdq_get_command(cmdlist, new_state); - if (item != NULL) - item = cmdq_insert_after(item, new_item); - else - item = cmdq_append(NULL, new_item); - } - a = options_array_next(a); - } - - cmdq_free_state(new_state); + events_fire(name, ep); free(name); } diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 6f22da83e..da2a27976 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-refresh-client.c,v 1.54 2026/07/05 08:24:00 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-rename-session.c b/cmd-rename-session.c index b399483b8..35719c564 100644 --- a/cmd-rename-session.c +++ b/cmd-rename-session.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-rename-session.c,v 1.39 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -49,6 +49,8 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); struct session *s = target->s; + struct event_payload *ep; + struct cmd_find_state fs; char *newname, *tmp; tmp = format_single_from_target(item, args_string(args, 0)); @@ -69,13 +71,20 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } + ep = event_payload_create(); + cmd_find_from_session(&fs, s, 0); + event_payload_set_target(ep, &fs); + event_payload_set_session(ep, "session", s); + event_payload_set_string(ep, "old_name", "%s", s->name); + event_payload_set_string(ep, "new_name", "%s", newname); + RB_REMOVE(sessions, &sessions, s); free(s->name); s->name = newname; RB_INSERT(sessions, &sessions, s); server_status_session(s); - notify_session("session-renamed", s); + events_fire("session-renamed", ep); return (CMD_RETURN_NORMAL); } diff --git a/cmd-rename-window.c b/cmd-rename-window.c index 8ab9b658a..f55f8a18f 100644 --- a/cmd-rename-window.c +++ b/cmd-rename-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-rename-window.c,v 1.30 2026/06/29 18:17:28 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c index 3ce805115..01ddf424a 100644 --- a/cmd-resize-pane.c +++ b/cmd-resize-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-resize-pane.c,v 1.67 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -182,7 +182,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) if (lc->parent != NULL) layout_fix_offsets(w); layout_fix_panes(w, NULL); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); return (CMD_RETURN_NORMAL); diff --git a/cmd-resize-window.c b/cmd-resize-window.c index c420451cf..a89750607 100644 --- a/cmd-resize-window.c +++ b/cmd-resize-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-resize-window.c,v 1.10 2023/06/30 13:19:32 nicm Exp $ */ /* * Copyright (c) 2018 Nicholas Marriott diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index bf058a267..66a5a0be4 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-respawn-pane.c,v 1.40 2026/07/03 16:09:49 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index e67757e1b..e10469649 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-respawn-window.c,v 1.51 2026/07/03 16:09:49 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c index c7459a9ae..d62fd0fa4 100644 --- a/cmd-rotate-window.c +++ b/cmd-rotate-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-rotate-window.c,v 1.34 2026/06/22 08:47:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 6114f68a1..b440828fb 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-run-shell.c,v 1.91 2026/06/01 08:27:37 nicm Exp $ */ /* * Copyright (c) 2009 Tiago Cunha diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c index d4266231f..903016c5d 100644 --- a/cmd-save-buffer.c +++ b/cmd-save-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-save-buffer.c,v 1.57 2025/10/28 07:32:26 nicm Exp $ */ /* * Copyright (c) 2009 Tiago Cunha diff --git a/cmd-select-layout.c b/cmd-select-layout.c index 417797472..741dc63f4 100644 --- a/cmd-select-layout.c +++ b/cmd-select-layout.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-select-layout.c,v 1.43 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -139,7 +139,7 @@ changed: free(oldlayout); recalculate_sizes(); server_redraw_window(w); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); return (CMD_RETURN_NORMAL); error: diff --git a/cmd-select-pane.c b/cmd-select-pane.c index d146ea6cf..91a36cddb 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-select-pane.c,v 1.76 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -80,6 +80,69 @@ cmd_select_pane_redraw(struct window *w) } } +static enum cmd_retval +cmd_select_pane_marked_pane(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct event_payload *ep; + struct cmd_find_state fs; + struct winlink *wl = target->wl; + struct window_pane *wp = target->wp, *lwp = NULL, *mwp; + struct session *s = target->s; + + if (args_has(args, 'm') && !window_pane_is_visible(wp)) + return (CMD_RETURN_NORMAL); + if (server_check_marked()) + lwp = marked_pane.wp; + + if (args_has(args, 'M') || server_is_marked(s, wl, wp)) + server_clear_marked(); + else + server_set_marked(s, wl, wp); + mwp = marked_pane.wp; + + ep = event_payload_create(); + if (mwp != NULL) + cmd_find_from_pane(&fs, mwp, 0); + else if (lwp != NULL) + cmd_find_from_pane(&fs, lwp, 0); + else + cmd_find_from_pane(&fs, wp, 0); + event_payload_set_target(ep, &fs); + if (mwp != NULL) { + event_payload_set_pane(ep, "pane", mwp); + event_payload_set_pane(ep, "new_pane", mwp); + event_payload_set_window(ep, "window", mwp->window); + } else if (lwp != NULL) { + event_payload_set_pane(ep, "pane", lwp); + event_payload_set_window(ep, "window", lwp->window); + } else { + event_payload_set_pane(ep, "pane", wp); + event_payload_set_window(ep, "window", wp->window); + } + if (lwp != NULL) + event_payload_set_pane(ep, "old_pane", lwp); + event_payload_set_int(ep, "marked", mwp != NULL); + events_fire("marked-pane-changed", ep); + + if (lwp != NULL) { + lwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED); + server_redraw_window_borders(lwp->window); + server_status_window(lwp->window); + } + if (mwp != NULL) { + mwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED); + server_redraw_window_borders(mwp->window); + server_status_window(mwp->window); + } + if (window_pane_is_floating(wp)) { + window_redraw_active_switch(wp->window, wp); + window_set_active_pane(wp->window, wp, 1); + } + return (CMD_RETURN_NORMAL); +} + static enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) { @@ -88,10 +151,12 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); struct client *c = cmdq_get_client(item); + struct event_payload *ep; + struct cmd_find_state fs; struct winlink *wl = target->wl; struct window *w = wl->window; struct session *s = target->s; - struct window_pane *wp = target->wp, *activewp, *lastwp, *markedwp; + struct window_pane *wp = target->wp, *activewp, *lastwp; struct options *oo = wp->options; char *title; const char *style; @@ -134,38 +199,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'm') || args_has(args, 'M')) { - if (args_has(args, 'm') && !window_pane_is_visible(wp)) - return (CMD_RETURN_NORMAL); - if (server_check_marked()) - lastwp = marked_pane.wp; - else - lastwp = NULL; - - if (args_has(args, 'M') || server_is_marked(s, wl, wp)) - server_clear_marked(); - else - server_set_marked(s, wl, wp); - markedwp = marked_pane.wp; - - if (lastwp != NULL) { - lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED| - PANE_THEMECHANGED); - server_redraw_window_borders(lastwp->window); - server_status_window(lastwp->window); - } - if (markedwp != NULL) { - markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED| - PANE_THEMECHANGED); - server_redraw_window_borders(markedwp->window); - server_status_window(markedwp->window); - } - if (window_pane_is_floating(wp)) { - window_redraw_active_switch(w, wp); - window_set_active_pane(w, wp, 1); - } - return (CMD_RETURN_NORMAL); - } + if (args_has(args, 'm') || args_has(args, 'M')) + return (cmd_select_pane_marked_pane(self, item)); style = args_get(args, 'P'); if (style != NULL) { @@ -218,7 +253,13 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'T')) { title = format_single_from_target(item, args_get(args, 'T')); if (screen_set_title(&wp->base, title, 0)) { - notify_pane("pane-title-changed", wp); + ep = event_payload_create(); + cmd_find_from_pane(&fs, wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_pane(ep, "pane", wp); + event_payload_set_window(ep, "window", wp->window); + event_payload_set_string(ep, "new_title", "%s", title); + events_fire("pane-title-changed", ep); server_redraw_window_borders(wp->window); server_status_window(wp->window); } diff --git a/cmd-select-window.c b/cmd-select-window.c index 4aca3e60e..32b4821bf 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-select-window.c,v 1.30 2021/08/21 10:22:39 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 6be3f2ead..06043bddb 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-send-keys.c,v 1.81 2026/06/11 19:13:34 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/cmd-send-prefix.c b/cmd-send-prefix.c deleted file mode 100644 index 04556507b..000000000 --- a/cmd-send-prefix.c +++ /dev/null @@ -1,57 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2007 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 MIND, 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 "tmux.h" - -/* - * Send prefix key as a key. - */ - -enum cmd_retval cmd_send_prefix_exec(struct cmd *, struct cmd_ctx *); - -const struct cmd_entry cmd_send_prefix_entry = { - "send-prefix", NULL, - "2t:", 0, 0, - "[-2] " CMD_TARGET_PANE_USAGE, - 0, - NULL, - NULL, - cmd_send_prefix_exec -}; - -enum cmd_retval -cmd_send_prefix_exec(struct cmd *self, struct cmd_ctx *ctx) -{ - struct args *args = self->args; - struct session *s; - struct window_pane *wp; - int key; - - if (cmd_find_pane(ctx, args_get(args, 't'), &s, &wp) == NULL) - return (CMD_RETURN_ERROR); - - if (args_has(args, '2')) - key = options_get_number(&s->options, "prefix2"); - else - key = options_get_number(&s->options, "prefix"); - window_pane_key(wp, s, key); - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-server-access.c b/cmd-server-access.c index 895e69417..e85a278a8 100644 --- a/cmd-server-access.c +++ b/cmd-server-access.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-server-access.c,v 1.6 2026/06/09 12:58:40 nicm Exp $ */ /* * Copyright (c) 2021 Dallas Lyons diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c index eef2978e2..6e640fec6 100644 --- a/cmd-set-buffer.c +++ b/cmd-set-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-set-buffer.c,v 1.37 2026/02/15 17:43:26 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-set-environment.c b/cmd-set-environment.c index 78c32452b..9a6552cb5 100644 --- a/cmd-set-environment.c +++ b/cmd-set-environment.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-set-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/cmd-set-option.c b/cmd-set-option.c index 1b6de1e86..158cd5fac 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-set-option.c,v 1.145 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -31,6 +31,8 @@ static enum args_parse_type cmd_set_option_args_parse(struct args *, u_int, char **); static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *); +static enum cmd_retval cmd_set_hook_event_exec(struct cmd *, + struct cmdq_item *); static enum cmd_retval cmd_set_hook_monitor_exec(struct cmdq_item *, struct args *, int); @@ -64,8 +66,8 @@ const struct cmd_entry cmd_set_hook_entry = { .name = "set-hook", .alias = NULL, - .args = { "agpRt:uB:w", 0, 2, cmd_set_option_args_parse }, - .usage = "[-agpRuw] [-B name:what:format] " CMD_TARGET_PANE_USAGE " " + .args = { "agpERt:uB:w", 0, 2, cmd_set_option_args_parse }, + .usage = "[-agpERuw] [-B name:what:format] " CMD_TARGET_PANE_USAGE " " "[hook] [command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, @@ -85,12 +87,58 @@ cmd_set_option_args_parse(struct args *args, u_int idx, return (ARGS_PARSE_STRING); } +static enum cmd_retval +cmd_set_hook_event_exec(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct event_payload *ep; + struct client *c; + char *argument; + + if (args_count(args) == 0) { + cmdq_error(item, "missing argument"); + return (CMD_RETURN_ERROR); + } + if (args_count(args) != 1) { + cmdq_error(item, "too many arguments"); + return (CMD_RETURN_ERROR); + } + + argument = format_single_from_target(item, args_string(args, 0)); + if (*argument != '@') { + cmdq_error(item, "event name must start with @"); + free(argument); + return (CMD_RETURN_ERROR); + } + + ep = event_payload_create(); + event_payload_set_target(ep, target); + c = cmdq_get_client(item); + if (c != NULL) + event_payload_set_client(ep, "client", c); + if (target->s != NULL) + event_payload_set_session(ep, "session", target->s); + if (target->w != NULL) + event_payload_set_window(ep, "window", target->w); + if (target->wl != NULL) + event_payload_set_int(ep, "window_index", target->wl->idx); + else if (target->idx != -1) + event_payload_set_int(ep, "window_index", target->idx); + if (target->wp != NULL) + event_payload_set_pane(ep, "pane", target->wp); + events_fire(argument, ep); + free(argument); + return (CMD_RETURN_NORMAL); +} + static enum cmd_retval cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window) { struct cmd_find_state *target = cmdq_get_target(item), fs; struct options *oo; struct options_entry *o; + struct session *s = NULL; char *cause = NULL, *name = NULL, *format = NULL; char *expanded = NULL, *newvalue = NULL; const char *value, *old; @@ -130,7 +178,7 @@ cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window) cmd_find_copy_state(&fs, target); if (args_has(args, 'u')) { - notify_monitor_remove(oo, name); + hooks_monitor_remove(oo, name); goto out; } @@ -152,7 +200,11 @@ cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window) } } - notify_monitor_add(item, oo, name, type, id, format, &fs, target->s); + if (oo != global_options && + oo != global_s_options && + oo != global_w_options) + s = target->s; + hooks_monitor_add(item, oo, name, type, id, format, &fs, s); out: free(newvalue); @@ -185,6 +237,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) int scope; window = (cmd_get_entry(self) == &cmd_set_window_option_entry); + if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'E')) + return (cmd_set_hook_event_exec(self, item)); if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'B')) return (cmd_set_hook_monitor_exec(item, args, window)); if (args_count(args) == 0) { @@ -197,7 +251,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) /* If set-hook -R, fire the hook straight away. */ if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { - notify_hook(item, argument); + hooks_run(item, argument); free(argument); return (CMD_RETURN_NORMAL); } @@ -289,6 +343,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) goto fail; } options_set_string(oo, name, append, "%s", value); + if (cmd_get_entry(self) == &cmd_set_hook_entry) + hooks_add_event(name); } else if (array_key == NULL && !options_is_array(parent)) { error = options_from_string(oo, options_table_entry(parent), options_table_entry(parent)->name, value, diff --git a/cmd-show-buffer.c b/cmd-show-buffer.c deleted file mode 100644 index 0af82972c..000000000 --- a/cmd-show-buffer.c +++ /dev/null @@ -1,112 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2007 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 MIND, 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 "tmux.h" - -/* - * Show a paste buffer. - */ - -enum cmd_retval cmd_show_buffer_exec(struct cmd *, struct cmd_ctx *); - -const struct cmd_entry cmd_show_buffer_entry = { - "show-buffer", "showb", - "b:", 0, 0, - CMD_BUFFER_USAGE, - 0, - NULL, - NULL, - cmd_show_buffer_exec -}; - -enum cmd_retval -cmd_show_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) -{ - struct args *args = self->args; - struct session *s; - struct paste_buffer *pb; - int buffer; - char *in, *buf, *ptr, *cause; - size_t size, len; - u_int width; - - if ((s = cmd_find_session(ctx, NULL, 0)) == NULL) - return (CMD_RETURN_ERROR); - - if (!args_has(args, 'b')) { - if ((pb = paste_get_top(&global_buffers)) == NULL) { - ctx->error(ctx, "no buffers"); - return (CMD_RETURN_ERROR); - } - } else { - buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); - if (cause != NULL) { - ctx->error(ctx, "buffer %s", cause); - free(cause); - return (CMD_RETURN_ERROR); - } - - pb = paste_get_index(&global_buffers, buffer); - if (pb == NULL) { - ctx->error(ctx, "no buffer %d", buffer); - return (CMD_RETURN_ERROR); - } - } - - size = pb->size; - if (size > SIZE_MAX / 4 - 1) - size = SIZE_MAX / 4 - 1; - in = xmalloc(size * 4 + 1); - strvisx(in, pb->data, size, VIS_OCTAL|VIS_TAB); - - width = s->sx; - if (ctx->cmdclient != NULL) - width = ctx->cmdclient->tty.sx; - - buf = xmalloc(width + 1); - len = 0; - - ptr = in; - do { - buf[len++] = *ptr++; - - if (len == width || buf[len - 1] == '\n') { - if (buf[len - 1] == '\n') - len--; - buf[len] = '\0'; - - ctx->print(ctx, "%s", buf); - len = 0; - } - } while (*ptr != '\0'); - - if (len != 0) { - buf[len] = '\0'; - ctx->print(ctx, "%s", buf); - } - free(buf); - - free(in); - - return (CMD_RETURN_NORMAL); -} diff --git a/cmd-show-environment.c b/cmd-show-environment.c index 3440c33e4..692f09c32 100644 --- a/cmd-show-environment.c +++ b/cmd-show-environment.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-show-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/cmd-show-messages.c b/cmd-show-messages.c index 7c656fa99..f9e198c4c 100644 --- a/cmd-show-messages.c +++ b/cmd-show-messages.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-show-messages.c,v 1.37 2025/11/18 08:37:54 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/cmd-show-options.c b/cmd-show-options.c index bff0634fe..308912e00 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-show-options.c,v 1.73 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -216,7 +216,7 @@ cmd_show_hooks_print_monitor(struct cmdq_item *item, struct options_entry *o) { char *value; - value = notify_monitor_to_string(o); + value = hooks_monitor_to_string(o); if (value == NULL) return; cmdq_print(item, "%s", value); diff --git a/cmd-show-prompt-history.c b/cmd-show-prompt-history.c index 741d22588..5b6af76fb 100644 --- a/cmd-show-prompt-history.c +++ b/cmd-show-prompt-history.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-show-prompt-history.c,v 1.5 2026/06/25 11:39:11 nicm Exp $ */ /* * Copyright (c) 2021 Anindya Mukherjee diff --git a/cmd-source-file.c b/cmd-source-file.c index cbd53960b..e5dfba543 100644 --- a/cmd-source-file.c +++ b/cmd-source-file.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-source-file.c,v 1.62 2025/11/18 08:42:09 nicm Exp $ */ /* * Copyright (c) 2008 Tiago Cunha diff --git a/cmd-split-window.c b/cmd-split-window.c index 09cfbfa86..64fa6b877 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-split-window.c,v 1.141 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -84,6 +84,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct window *w = wl->window; struct window_pane *wp = target->wp, *new_wp = NULL; struct layout_cell *lc = NULL; + struct event_payload *ep; struct cmd_find_state fs; int input, empty, is_floating, flags = 0; const char *template, *style, *value; @@ -218,7 +219,13 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'T')) { title = format_single_from_target(item, args_get(args, 'T')); screen_set_title(&new_wp->base, title, 0); - notify_pane("pane-title-changed", new_wp); + ep = event_payload_create(); + cmd_find_from_pane(&fs, new_wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_pane(ep, "pane", new_wp); + event_payload_set_window(ep, "window", new_wp->window); + event_payload_set_string(ep, "new_title", "%s", title); + events_fire("pane-title-changed", ep); free(title); } diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 7f5160e65..da7ea3706 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-swap-pane.c,v 1.53 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -180,9 +180,9 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) redraw_invalidate_scene(dst_w); server_redraw_window(dst_w); - notify_window("window-layout-changed", src_w); + events_fire_window("window-layout-changed", src_w); if (src_w != dst_w) - notify_window("window-layout-changed", dst_w); + events_fire_window("window-layout-changed", dst_w); out: if (window_pop_zoom(src_w)) diff --git a/cmd-swap-window.c b/cmd-swap-window.c index d31353982..980da0395 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-swap-window.c,v 1.29 2025/10/30 13:52:08 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 9865ac52c..177107b47 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-switch-client.c,v 1.74 2026/05/22 15:22:43 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 6d91d7cca..f537fb576 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-unbind-key.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/cmd-wait-for.c b/cmd-wait-for.c index 8a6aa259e..0e4130fcd 100644 --- a/cmd-wait-for.c +++ b/cmd-wait-for.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd-wait-for.c,v 1.23 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2013 Nicholas Marriott @@ -34,8 +34,8 @@ const struct cmd_entry cmd_wait_for_entry = { .name = "wait-for", .alias = "wait", - .args = { "LSU", 1, 1, NULL }, - .usage = "[-L|-S|-U] channel", + .args = { "EF:LSUlvw:", 1, 1, NULL }, + .usage = "[-ELSUlv] [-F format] [-w waiter] name", .flags = 0, .exec = cmd_wait_for_exec @@ -46,6 +46,17 @@ struct wait_item { TAILQ_ENTRY(wait_item) entry; }; +struct wait_event_item { + struct cmdq_item *item; + struct events_sink *sink; + char *name; + char *filter; + int verbose; + TAILQ_ENTRY(wait_event_item) entry; +}; +static TAILQ_HEAD(, wait_event_item) wait_event_items = + TAILQ_HEAD_INITIALIZER(wait_event_items); + struct wait_channel { const char *name; int locked; @@ -76,9 +87,28 @@ static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *, struct wait_channel *); static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *, struct wait_channel *); +static enum cmd_retval cmd_wait_for_event(struct cmdq_item *, const char *, + struct args *); +static void cmd_wait_for_event_cb(const char *, + struct event_payload *, void *); +static void cmd_wait_for_event_free(struct wait_event_item *); +static enum cmd_retval cmd_wait_for_event_list(struct cmdq_item *, + const char *); +static enum cmd_retval cmd_wait_for_event_wake(struct cmdq_item *, + const char *, struct args *); +static enum cmd_retval cmd_wait_for_list(struct cmdq_item *, + struct wait_channel *); +static enum cmd_retval cmd_wait_for_wake(struct cmdq_item *, const char *, + struct args *, struct wait_channel *); static struct wait_channel *cmd_wait_for_add(const char *); static void cmd_wait_for_remove(struct wait_channel *); +static void cmd_wait_for_remove_empty( + struct wait_channel *); +static const char *cmd_wait_for_item_client_name( + struct cmdq_item *); +static const char *cmd_wait_for_client_name( + struct wait_event_item *); static struct wait_channel * cmd_wait_for_add(const char *name) @@ -117,16 +147,53 @@ cmd_wait_for_remove(struct wait_channel *wc) free(wc); } +static void +cmd_wait_for_remove_empty(struct wait_channel *wc) +{ + if (wc->locked || wc->woken) + return; + if (!TAILQ_EMPTY(&wc->waiters) || !TAILQ_EMPTY(&wc->lockers)) + return; + + log_debug("remove empty wait channel %s", wc->name); + + RB_REMOVE(wait_channels, &wait_channels, wc); + + free((void *)wc->name); + free(wc); +} + +static const char * +cmd_wait_for_item_client_name(struct cmdq_item *item) +{ + struct client *c = cmdq_get_client(item); + + if (c == NULL || c->name == NULL) + return (""); + return (c->name); +} + +static const char * +cmd_wait_for_client_name(struct wait_event_item *wei) +{ + return (cmd_wait_for_item_client_name(wei->item)); +} + static enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); + struct args *args = cmd_get_args(self); const char *name = args_string(args, 0); - struct wait_channel *wc, find; + struct wait_channel *wc, find = { .name = name }; + + if (args_has(args, 'E')) + return (cmd_wait_for_event(item, name, args)); - find.name = name; wc = RB_FIND(wait_channels, &wait_channels, &find); - + if (args_has(args, 'l')) + return (cmd_wait_for_list(item, wc)); + if (args_has(args, 'w')) + return (cmd_wait_for_wake(item, name, args, wc)); if (args_has(args, 'S')) return (cmd_wait_for_signal(item, name, wc)); if (args_has(args, 'L')) @@ -136,6 +203,179 @@ cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item) return (cmd_wait_for_wait(item, name, wc)); } +static void +cmd_wait_for_event_print(struct wait_event_item *wei, struct event_payload *ep) +{ + struct event_payload_item *epi; + const char *key; + char *value; + + epi = event_payload_first(ep); + while (epi != NULL) { + key = event_payload_item_name(epi); + if (*key != '_') { + value = event_payload_item_print(epi); + cmdq_print(wei->item, "%s=%s", key, value); + free(value); + } + epi = event_payload_next(epi); + } +} + +static void +cmd_wait_for_event_cb(__unused const char *name, struct event_payload *ep, + void *item_data) +{ + struct wait_event_item *wei = item_data; + struct format_tree *ft; + char *expanded; + int flag; + + if (wei->verbose) + cmd_wait_for_event_print(wei, ep); + + if (wei->filter != NULL) { + ft = format_create(cmdq_get_client(wei->item), wei->item, + FORMAT_NONE, FORMAT_NOJOBS); + event_payload_add_formats(ep, ft, NULL); + expanded = format_expand(ft, wei->filter); + flag = format_true(expanded); + free(expanded); + format_free(ft); + + if (!flag) + return; + } + + TAILQ_REMOVE(&wait_event_items, wei, entry); + cmdq_continue(wei->item); + cmd_wait_for_event_free(wei); +} + +static void +cmd_wait_for_event_free(struct wait_event_item *wei) +{ + events_remove_sink(wei->sink); + free(wei->name); + free(wei->filter); + free(wei); +} + +static enum cmd_retval +cmd_wait_for_event(struct cmdq_item *item, const char *name, struct args *args) +{ + struct wait_event_item *wei; + const char *filter = args_get(args, 'F'); + + if (!hooks_valid_event_name(name)) { + cmdq_error(item, "invalid event: %s", name); + return (CMD_RETURN_ERROR); + } + if (args_has(args, 'l')) + return (cmd_wait_for_event_list(item, name)); + if (args_has(args, 'w')) + return (cmd_wait_for_event_wake(item, name, args)); + + if (cmdq_get_client(item) == NULL) { + cmdq_error(item, "not able to wait"); + return (CMD_RETURN_ERROR); + } + + wei = xcalloc(1, sizeof *wei); + wei->item = item; + wei->name = xstrdup(name); + wei->filter = (filter != NULL ? xstrdup(filter) : NULL); + wei->verbose = args_has(args, 'v'); + wei->sink = events_add_sink(name, cmd_wait_for_event_cb, wei); + TAILQ_INSERT_TAIL(&wait_event_items, wei, entry); + + return (CMD_RETURN_WAIT); +} + +static enum cmd_retval +cmd_wait_for_event_list(struct cmdq_item *item, const char *name) +{ + struct wait_event_item *wei; + + TAILQ_FOREACH(wei, &wait_event_items, entry) { + if (strcmp(wei->name, name) == 0) + cmdq_print(item, "%s", cmd_wait_for_client_name(wei)); + } + + return (CMD_RETURN_NORMAL); +} + +static enum cmd_retval +cmd_wait_for_event_wake(struct cmdq_item *item, const char *name, + struct args *args) +{ + struct wait_event_item *wei, *wei1; + const char *client_name = args_get(args, 'w'); + + TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) { + if (strcmp(wei->name, name) != 0) + continue; + if (strcmp(cmd_wait_for_client_name(wei), client_name) != 0) + continue; + + TAILQ_REMOVE(&wait_event_items, wei, entry); + cmdq_continue(wei->item); + cmd_wait_for_event_free(wei); + return (CMD_RETURN_NORMAL); + } + + cmdq_error(item, "waiter %s not found", client_name); + return (CMD_RETURN_ERROR); +} + +static enum cmd_retval +cmd_wait_for_list(struct cmdq_item *item, struct wait_channel *wc) +{ + struct wait_item *wi; + + if (wc == NULL) + return (CMD_RETURN_NORMAL); + + TAILQ_FOREACH(wi, &wc->waiters, entry) + cmdq_print(item, "%s", cmd_wait_for_item_client_name(wi->item)); + TAILQ_FOREACH(wi, &wc->lockers, entry) + cmdq_print(item, "%s", cmd_wait_for_item_client_name(wi->item)); + + return (CMD_RETURN_NORMAL); +} + +static enum cmd_retval +cmd_wait_for_wake(__unused struct cmdq_item *item, const char *name, + struct args *args, struct wait_channel *wc) +{ + struct wait_item *wi, *wi1; + const char *client_name = args_get(args, 'w'); + + if (wc != NULL) { + TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { + name = cmd_wait_for_item_client_name(wi->item); + if (strcmp(name, client_name) != 0) + continue; + cmdq_continue(wi->item); + TAILQ_REMOVE(&wc->waiters, wi, entry); + free(wi); + cmd_wait_for_remove_empty(wc); + return (CMD_RETURN_NORMAL); + } + TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) { + name = cmd_wait_for_item_client_name(wi->item); + if (strcmp(name, client_name) != 0) + continue; + cmdq_continue(wi->item); + TAILQ_REMOVE(&wc->lockers, wi, entry); + free(wi); + cmd_wait_for_remove_empty(wc); + return (CMD_RETURN_NORMAL); + } + } + return (CMD_RETURN_NORMAL); +} + static enum cmd_retval cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name, struct wait_channel *wc) @@ -245,6 +485,13 @@ cmd_wait_for_flush(void) { struct wait_channel *wc, *wc1; struct wait_item *wi, *wi1; + struct wait_event_item *wei, *wei1; + + TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) { + TAILQ_REMOVE(&wait_event_items, wei, entry); + cmdq_continue(wei->item); + cmd_wait_for_event_free(wei); + } RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { diff --git a/cmd.c b/cmd.c index a2d3fad70..b9b8f1a4a 100644 --- a/cmd.c +++ b/cmd.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: cmd.c,v 1.185 2026/06/26 14:40:30 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/colour.c b/colour.c index f563fd5aa..411d77921 100644 --- a/colour.c +++ b/colour.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: colour.c,v 1.35 2026/07/06 14:29:10 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/control-notify.c b/control-notify.c index e85660297..6fec1683f 100644 --- a/control-notify.c +++ b/control-notify.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: control-notify.c,v 1.37 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2012 Nicholas Marriott @@ -23,32 +23,56 @@ #include "tmux.h" +/* Should this client be sent events? */ #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ - ((c) != NULL && ((c)->flags & CLIENT_CONTROL) && \ + ((c) != NULL && \ + ((c)->flags & CLIENT_CONTROL) && \ (~(c)->flags & CLIENT_EXIT) && \ (c)->control_state != NULL) -void -control_notify_pane_mode_changed(int pane) +/* Notify control clients that pane mode changed. */ +static void +control_pane_mode_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct client *c; + 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", 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); } -void -control_notify_window_layout_changed(struct window *w) +/* Notify control clients that window layout changed. */ +static void +control_window_layout_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct client *c; - struct session *s; - struct winlink *wl; - const char *template; - char *cp; + struct client *c; + struct session *s; + struct winlink *wl; + 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}"; @@ -73,12 +97,15 @@ control_notify_window_layout_changed(struct window *w) free(cp); } -void -control_notify_window_pane_changed(struct window *w) +/* Notify control clients that window pane changed. */ +static void +control_window_pane_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { struct client *c; + 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)) @@ -89,12 +116,17 @@ control_notify_window_pane_changed(struct window *w) } } -void -control_notify_window_unlinked(__unused struct session *s, struct window *w) +/* Notify control clients that a window was unlinked. */ +static void +control_window_unlinked_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct client *c; - struct session *cs; + struct client *c; + struct session *cs; + 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; @@ -107,12 +139,17 @@ control_notify_window_unlinked(__unused struct session *s, struct window *w) } } -void -control_notify_window_linked(__unused struct session *s, struct window *w) +/* Notify control clients that a window was linked. */ +static void +control_window_linked_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct client *c; - struct session *cs; + struct client *c; + struct session *cs; + 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,12 +162,17 @@ control_notify_window_linked(__unused struct session *s, struct window *w) } } -void -control_notify_window_renamed(struct window *w) +/* Notify control clients that a window was renamed. */ +static void +control_window_renamed_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct client *c; - struct session *cs; + struct client *c; + struct session *cs; + 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; @@ -146,13 +188,16 @@ control_notify_window_renamed(struct window *w) } } -void -control_notify_client_session_changed(struct client *cc) +/* Notify control clients that a client changed session. */ +static void +control_client_session_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { - struct client *c; - struct session *s; + 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; @@ -170,22 +215,32 @@ control_notify_client_session_changed(struct client *cc) } } -void -control_notify_client_detached(struct client *cc) +/* Notify control clients that a client detached. */ +static void +control_client_detached_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct client *c; + 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); } } -void -control_notify_session_renamed(struct session *s) +/* Notify control clients that a session was renamed. */ +static void +control_session_renamed_cb(__unused const char *name, struct event_payload *ep, + __unused void *sink_data) { - struct client *c; + 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; @@ -194,8 +249,10 @@ control_notify_session_renamed(struct session *s) } } -void -control_notify_session_created(__unused struct session *s) +/* Notify control clients that sessions changed. */ +static void +control_session_created_cb(__unused const char *name, + __unused struct event_payload *ep, __unused void *sink_data) { struct client *c; @@ -207,8 +264,10 @@ control_notify_session_created(__unused struct session *s) } } -void -control_notify_session_closed(__unused struct session *s) +/* Notify control clients that sessions changed. */ +static void +control_session_closed_cb(__unused const char *name, + __unused struct event_payload *ep, __unused void *sink_data) { struct client *c; @@ -220,9 +279,12 @@ control_notify_session_closed(__unused struct session *s) } } -void -control_notify_session_window_changed(struct session *s) +/* Notify control clients that the current window changed. */ +static void +control_session_window_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_data) { + struct session *s = event_payload_get_session(ep, "session"); struct client *c; /* @@ -230,7 +292,7 @@ control_notify_session_window_changed(struct session *s) * 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) { @@ -242,28 +304,68 @@ control_notify_session_window_changed(struct session *s) } } -void -control_notify_paste_buffer_changed(const char *name) +/* Notify control clients that a paste buffer changed. */ +static void +control_paste_buffer_changed_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_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", name); + control_write(c, "%%paste-buffer-changed %s", pbname); } } -void -control_notify_paste_buffer_deleted(const char *name) +/* Notify control clients that a paste buffer was deleted. */ +static void +control_paste_buffer_deleted_cb(__unused const char *name, + struct event_payload *ep, __unused void *sink_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", name); + control_write(c, "%%paste-buffer-deleted %s", pbname); } } + +/* Build control event sinks. */ +void +control_build_events(void) +{ + /* Control event sink callbacks. */ + static struct { + const char *name; + events_cb cb; + } events[] = { + { "pane-mode-changed", control_pane_mode_changed_cb }, + { "window-layout-changed", control_window_layout_changed_cb }, + { "window-pane-changed", control_window_pane_changed_cb }, + { "window-unlinked", control_window_unlinked_cb }, + { "window-linked", control_window_linked_cb }, + { "window-renamed", control_window_renamed_cb }, + { "client-session-changed", control_client_session_changed_cb }, + { "client-detached", control_client_detached_cb }, + { "session-renamed", control_session_renamed_cb }, + { "session-created", control_session_created_cb }, + { "session-closed", control_session_closed_cb }, + { "session-window-changed", control_session_window_changed_cb }, + { "paste-buffer-changed", control_paste_buffer_changed_cb }, + { "paste-buffer-deleted", control_paste_buffer_deleted_cb } + }; + u_int i; + + for (i = 0; i < nitems(events); i++) + events_add_sink(events[i].name, events[i].cb, NULL); +} diff --git a/control.c b/control.c index c1cc79e25..220267b6c 100644 --- a/control.c +++ b/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: control.c,v 1.60 2026/07/10 07:25:05 nicm Exp $ */ /* * Copyright (c) 2012 Nicholas Marriott diff --git a/environ.c b/environ.c index f9bc8cab3..e4f94fd4b 100644 --- a/environ.c +++ b/environ.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: environ.c,v 1.31 2026/06/13 20:07:30 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/events-payload.c b/events-payload.c new file mode 100644 index 000000000..444836cf3 --- /dev/null +++ b/events-payload.c @@ -0,0 +1,672 @@ +/* $OpenBSD: events-payload.c,v 1.1 2026/07/10 13:38:45 nicm Exp $ */ + +/* + * 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; + int number; + u_int unsigned_number; + struct client *client; + struct session *session; + struct window *window; + struct window_pane *pane; + struct { + void *ptr; + event_payload_free_cb free_cb; + event_payload_print_cb print_cb; + } pointer; + }; + + RB_ENTRY(event_payload_item) entry; +}; + +RB_HEAD(event_payload_tree, event_payload_item); + +struct event_payload { + struct event_payload_tree items; + struct cmd_find_state target; +}; + +static int +event_payload_cmp(struct event_payload_item *epi1, + struct event_payload_item *epi2) +{ + return (strcmp(epi1->name, epi2->name)); +} +RB_GENERATE_STATIC(event_payload_tree, 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_tree, &ep->items, &find)); +} + +/* Free the target in a payload. */ +static void +event_payload_free_target(struct event_payload *ep) +{ + struct cmd_find_state *target = &ep->target; + + if (target->s != NULL) + session_remove_ref(target->s, __func__); + if (target->w != NULL) + window_remove_ref(target->w, __func__); + if (target->wp != NULL) + window_pane_remove_ref(target->wp, __func__); + + cmd_find_clear_state(target, 0); +} + +/* 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: + server_client_unref(epi->client); + break; + case EVENT_PAYLOAD_SESSION: + session_remove_ref(epi->session, __func__); + break; + case EVENT_PAYLOAD_WINDOW: + window_remove_ref(epi->window, __func__); + break; + case EVENT_PAYLOAD_PANE: + window_pane_remove_ref(epi->pane, __func__); + break; + case EVENT_PAYLOAD_POINTER: + if (epi->pointer.free_cb != NULL) + epi->pointer.free_cb(epi->pointer.ptr); + break; + case EVENT_PAYLOAD_INT: + case EVENT_PAYLOAD_UINT: + case EVENT_PAYLOAD_TIME: + 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_tree, &ep->items, new); + if (old != NULL) { + RB_REMOVE(event_payload_tree, &ep->items, old); + event_payload_free_value(old); + free(old->name); + free(old); + RB_INSERT(event_payload_tree, &ep->items, new); + } +} + +/* Create an event payload. */ +struct event_payload * +event_payload_create(void) +{ + struct event_payload *ep; + + ep = xcalloc(1, sizeof *ep); + RB_INIT(&ep->items); + cmd_find_clear_state(&ep->target, 0); + 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_tree, &ep->items, epi1) { + RB_REMOVE(event_payload_tree, &ep->items, epi); + event_payload_free_value(epi); + free(epi->name); + free(epi); + } + event_payload_free_target(ep); + free(ep); + } +} + +/* Set the target. */ +void +event_payload_set_target(struct event_payload *ep, struct cmd_find_state *fs) +{ + struct cmd_find_state *target = &ep->target; + + event_payload_free_target(ep); + + if (fs->s != NULL) { + session_add_ref(fs->s, __func__); + target->s = fs->s; + } + if (fs->wl != NULL) { + target->idx = fs->wl->idx; + if (target->s == NULL) { + session_add_ref(fs->wl->session, __func__); + target->s = fs->wl->session; + } + } else + target->idx = -1; + if (fs->w != NULL) { + window_add_ref(fs->w, __func__); + target->w = fs->w; + } else if (fs->wl != NULL) { + window_add_ref(fs->wl->window, __func__); + target->w = fs->wl->window; + } + if (fs->wp != NULL) { + window_pane_add_ref(fs->wp, __func__); + target->wp = fs->wp; + } +} + +/* Get the target. */ +int +event_payload_get_target(struct event_payload *ep, struct cmd_find_state *fs) +{ + struct cmd_find_state *t = &ep->target; + struct winlink *wl = NULL; + int flags = fs->flags; + + if (t->idx != -1 && + t->s != NULL && + t->w != NULL && + session_alive(t->s)) { + wl = winlink_find_by_index(&t->s->windows, t->idx); + if (wl != NULL && wl->window != t->w) + wl = NULL; + } + + cmd_find_clear_state(fs, flags); + fs->s = t->s; + fs->w = t->w; + fs->wp = t->wp; + fs->wl = wl; + fs->idx = (wl != NULL ? wl->idx : -1); + if (cmd_find_valid_state(fs)) + return (1); + + if (wl != NULL && + t->wp != NULL && + window_has_pane(wl->window, t->wp)) { + cmd_find_from_winlink_pane(fs, wl, t->wp, flags); + if (cmd_find_valid_state(fs)) + return (1); + } + + if (t->wp != NULL && + cmd_find_from_pane(fs, t->wp, flags) == 0 && + cmd_find_valid_state(fs)) + return (1); + + if (wl != NULL) { + cmd_find_from_winlink(fs, wl, flags); + if (cmd_find_valid_state(fs)) + return (1); + } + + if (t->s != NULL && + t->w != NULL && + session_alive(t->s) && + cmd_find_from_session_window(fs, t->s, t->w, flags) == 0 && + cmd_find_valid_state(fs)) + return (1); + + if (t->s != NULL && session_alive(t->s)) { + cmd_find_from_session(fs, t->s, flags); + if (cmd_find_valid_state(fs)) + return (1); + } + + if (cmd_find_from_nothing(fs, flags) == 0) + return (1); + + cmd_find_clear_state(fs, flags); + return (0); +} + +/* 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 number item. */ +void +event_payload_set_int(struct event_payload *ep, const char *name, int value) +{ + struct event_payload_item *epi; + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_INT; + epi->number = value; + event_payload_set_item(ep, name, epi); +} + +/* Set an unsigned number item. */ +void +event_payload_set_uint(struct event_payload *ep, const char *name, u_int value) +{ + struct event_payload_item *epi; + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_UINT; + epi->unsigned_number = 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; + + 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; + + 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; + + 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; + + window_pane_add_ref(wp, __func__); + + epi = xcalloc(1, sizeof *epi); + epi->type = EVENT_PAYLOAD_PANE; + epi->pane = wp; + 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_INT: + evbuffer_add_printf(evb, "%d", epi->number); + break; + case EVENT_PAYLOAD_UINT: + evbuffer_add_printf(evb, "%u", epi->unsigned_number); + break; + case EVENT_PAYLOAD_CLIENT: + evbuffer_add_printf(evb, "%s", epi->client->name); + break; + case EVENT_PAYLOAD_SESSION: + evbuffer_add_printf(evb, "$%u", epi->session->id); + break; + case EVENT_PAYLOAD_WINDOW: + evbuffer_add_printf(evb, "@%u", epi->window->id); + break; + case EVENT_PAYLOAD_PANE: + evbuffer_add_printf(evb, "%%%u", epi->pane->id); + 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)); +} + +/* Add payload items as formats. */ +void +event_payload_add_formats(struct event_payload *ep, struct format_tree *ft, + const char *prefix) +{ + struct event_payload_item *epi; + char *name, *value; + const char *key; + + if (prefix == NULL) + prefix = ""; + + RB_FOREACH(epi, event_payload_tree, &ep->items) { + key = epi->name; + if (*key == '_') + continue; + + value = event_payload_item_print(epi); + xasprintf(&name, "%s%s", prefix, key); + format_add(ft, name, "%s", value); + free(name); + free(value); + + if (epi->type == EVENT_PAYLOAD_SESSION) { + xasprintf(&name, "%s%s_name", prefix, key); + format_add(ft, name, "%s", epi->session->name); + free(name); + } else if (epi->type == EVENT_PAYLOAD_WINDOW) { + xasprintf(&name, "%s%s_name", prefix, key); + format_add(ft, name, "%s", epi->window->name); + free(name); + } + } +} + +/* Get the first payload item. */ +struct event_payload_item * +event_payload_first(struct event_payload *ep) +{ + return (RB_MIN(event_payload_tree, &ep->items)); +} + +/* Get the next payload item. */ +struct event_payload_item * +event_payload_next(struct event_payload_item *epi) +{ + return (RB_NEXT(event_payload_tree, , 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_tree, &ep->items) { + 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. */ +time_t +event_payload_get_time(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_TIME) + return (0); + return (epi->time); +} + +/* Get a number item. */ +int +event_payload_get_int(struct event_payload *ep, const char *name, int *value) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_INT) + return (-1); + *value = epi->number; + return (0); +} + +/* Get an unsigned number item. */ +int +event_payload_get_uint(struct event_payload *ep, const char *name, u_int *value) +{ + struct event_payload_item *epi; + + epi = event_payload_find(ep, name); + if (epi == NULL || epi->type != EVENT_PAYLOAD_UINT) + return (-1); + *value = epi->unsigned_number; + return (0); +} + +/* 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 (epi->pane); +} + +/* 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 new file mode 100644 index 000000000..8d6e3eed1 --- /dev/null +++ b/events.c @@ -0,0 +1,199 @@ +/* $OpenBSD: events.c,v 1.1 2026/07/10 13:38:45 nicm Exp $ */ + +/* + * 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 "tmux.h" + +/* Event sink. */ +struct events_sink { + char *name; + events_cb cb; + void *data; + int dead; + u_int generation; + + TAILQ_ENTRY(events_sink) entry; +}; + +TAILQ_HEAD(events_sinks, events_sink); +static struct events_sinks events_sinks = TAILQ_HEAD_INITIALIZER(events_sinks); + +static u_int events_dispatching; +static u_int events_generation; + +/* Free an event sink. */ +static void +events_free_sink(struct events_sink *es) +{ + TAILQ_REMOVE(&events_sinks, es, entry); + free(es->name); + free(es); +} + +/* Free dead event sinks. */ +static void +events_free_dead(void) +{ + struct events_sink *es, *es1; + + TAILQ_FOREACH_SAFE(es, &events_sinks, entry, es1) { + if (es->dead) + events_free_sink(es); + } +} + +/* Add an event sink. */ +struct events_sink * +events_add_sink(const char *name, events_cb cb, void *data) +{ + struct events_sink *es; + + es = xcalloc(1, sizeof *es); + es->name = xstrdup(name); + es->cb = cb; + es->data = data; + es->generation = ++events_generation; + + TAILQ_INSERT_TAIL(&events_sinks, es, entry); + return (es); +} + +/* Remove an event sink. */ +void +events_remove_sink(struct events_sink *es) +{ + if (es != NULL && !es->dead) { + if (events_dispatching != 0) + es->dead = 1; + else + events_free_sink(es); + } +} + +/* Fire an event. */ +void +events_fire(const char *name, struct event_payload *ep) +{ + struct events_sink *es; + u_int generation = events_generation; + + event_payload_set_string(ep, "event", "%s", name); + + if (log_get_level() != 0) + event_payload_log(ep, "%s: %s: ", __func__, name); + + events_dispatching++; + TAILQ_FOREACH(es, &events_sinks, entry) { + if (es->dead || es->generation > generation) + continue; + if (strcmp(es->name, name) == 0) + es->cb(name, ep, es->data); + } + if (--events_dispatching == 0) + events_free_dead(); + event_payload_free(ep); +} + +/* Fire a client event. */ +void +events_fire_client(const char *name, struct client *c) +{ + struct event_payload *ep; + struct cmd_find_state fs; + + ep = event_payload_create(); + cmd_find_from_client(&fs, c, 0); + event_payload_set_target(ep, &fs); + event_payload_set_client(ep, "client", c); + if (fs.s != NULL) + event_payload_set_session(ep, "session", fs.s); + if (fs.w != NULL) + event_payload_set_window(ep, "window", fs.w); + if (fs.wl != NULL) + event_payload_set_int(ep, "window_index", fs.wl->idx); + else if (fs.idx != -1) + event_payload_set_int(ep, "window_index", fs.idx); + if (fs.wp != NULL) + event_payload_set_pane(ep, "pane", fs.wp); + events_fire(name, ep); +} + +/* Fire a session event. */ +void +events_fire_session(const char *name, struct session *s) +{ + struct event_payload *ep; + struct cmd_find_state fs; + + ep = event_payload_create(); + if (session_alive(s)) { + cmd_find_from_session(&fs, s, 0); + event_payload_set_target(ep, &fs); + } + event_payload_set_session(ep, "session", s); + events_fire(name, ep); +} + +/* Fire a window event. */ +void +events_fire_window(const char *name, struct window *w) +{ + struct event_payload *ep; + struct cmd_find_state fs; + + ep = event_payload_create(); + cmd_find_from_window(&fs, w, 0); + event_payload_set_target(ep, &fs); + event_payload_set_window(ep, "window", w); + events_fire(name, ep); +} + +/* Fire a pane event. */ +void +events_fire_pane(const char *name, struct window_pane *wp) +{ + struct event_payload *ep; + struct cmd_find_state fs; + + ep = event_payload_create(); + cmd_find_from_pane(&fs, wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_pane(ep, "pane", wp); + event_payload_set_window(ep, "window", wp->window); + events_fire(name, ep); +} + +/* Fire a winlink event. */ +void +events_fire_winlink(const char *name, struct winlink *wl) +{ + struct event_payload *ep; + struct cmd_find_state fs; + + ep = event_payload_create(); + cmd_find_from_winlink(&fs, wl, 0); + event_payload_set_target(ep, &fs); + 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); + events_fire(name, ep); +} diff --git a/file.c b/file.c index 38a692e0d..a9c393c04 100644 --- a/file.c +++ b/file.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: file.c,v 1.20 2026/05/17 10:54:01 nicm Exp $ */ /* * Copyright (c) 2019 Nicholas Marriott diff --git a/format-draw.c b/format-draw.c index 47bf192e6..ea1da421a 100644 --- a/format-draw.c +++ b/format-draw.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: format-draw.c,v 1.33 2026/06/29 17:08:52 nicm Exp $ */ /* * Copyright (c) 2019 Nicholas Marriott diff --git a/format.c b/format.c index 7ec4f06b9..da374d580 100644 --- a/format.c +++ b/format.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: format.c,v 1.402 2026/07/09 06:33:19 nicm Exp $ */ /* * Copyright (c) 2011 Nicholas Marriott diff --git a/fuzzy.c b/fuzzy.c index 00fc90687..83d34715c 100644 --- a/fuzzy.c +++ b/fuzzy.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: fuzzy.c,v 1.1 2026/06/26 14:40:30 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott diff --git a/grid-reader.c b/grid-reader.c index 421c25181..9b40def6c 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: grid-reader.c,v 1.10 2026/05/17 13:12:21 nicm Exp $ */ /* * Copyright (c) 2020 Anindya Mukherjee diff --git a/grid-view.c b/grid-view.c index 655ec9e46..86317526f 100644 --- a/grid-view.c +++ b/grid-view.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: grid-view.c,v 1.38 2026/01/22 08:55:01 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/grid.c b/grid.c index aabc3cf3f..878c01bab 100644 --- a/grid.c +++ b/grid.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: grid.c,v 1.153 2026/07/02 08:51:05 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/hooks.c b/hooks.c new file mode 100644 index 000000000..0ac64b8bd --- /dev/null +++ b/hooks.c @@ -0,0 +1,461 @@ +/* $OpenBSD: hooks.c,v 1.12 2026/07/10 13:38:45 nicm Exp $ */ + +/* + * Copyright (c) 2026 Nicholas Marriott + * Copyright (c) 2012 George Nachman + * + * 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 MIND, 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 "tmux.h" + +/* Hook monitor state owned by an option entry. */ +struct hook_monitor { + struct options *oo; + + struct monitor_set *set; + struct events_sink *sink; + struct cmd_find_state fs; + + enum monitor_type type; + int id; + char *format; +}; + +/* 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. */ +struct hooks_event { + char *name; + struct events_sink *sink; + TAILQ_ENTRY(hooks_event) entry; +}; +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 hooks_data *hd, + struct cmd_list *cmdlist, struct cmdq_state *state) +{ + struct cmdq_item *new_item; + char *s; + + if (cmdlist == NULL) + return (item); + if (log_get_level() != 0) { + s = cmd_list_print(cmdlist, 0); + log_debug("%s: hook %s is: %s", __func__, hd->name, s); + free(s); + } + new_item = cmdq_get_command(cmdlist, state); + if (item != NULL) + return (cmdq_insert_after(item, new_item)); + return (cmdq_append(NULL, new_item)); +} + +/* Parse a hook command. */ +static struct cmd_parse_result * +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 (!hd->expand) + return (cmd_parse_from_string(value, NULL)); + + 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); + + pr = cmd_parse_from_string(expanded, NULL); + free(expanded); + return (pr); +} + +/* Insert commands for a hook. */ +static void +hooks_insert(struct cmdq_item *item, struct hooks_data *hd) +{ + struct cmd_find_state fs; + struct options *oo; + struct cmdq_state *state; + struct options_entry *o; + struct options_array_item *a; + struct cmd_list *cmdlist; + const char *value; + struct cmd_parse_result *pr; + + log_debug("%s: inserting hook %s", __func__, hd->name); + + cmd_find_clear_state(&fs, 0); + 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, &hd->fs); + + 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, hd->name); + if (o == NULL && fs.wp != NULL) { + oo = fs.wp->options; + o = options_get(oo, hd->name); + } + if (o == NULL && fs.wl != NULL) { + oo = fs.wl->window->options; + o = options_get(oo, hd->name); + } + } + if (o == NULL) { + log_debug("%s: hook %s not found", __func__, hd->name); + return; + } + + if (item == NULL) + state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); + else { + state = cmdq_new_state(&fs, cmdq_get_event(item), + CMDQ_STATE_NOHOOKS); + } + cmdq_add_formats(state, hd->formats); + + 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__, + hd->name, pr->error); + free(pr->error); + break; + case CMD_PARSE_SUCCESS: + hooks_insert_one(item, hd, pr->cmdlist, state); + break; + } + } else { + a = options_array_first(o); + while (a != NULL) { + if (hd->expand) { + value = options_array_item_value(a)->string; + 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, hd, + pr->cmdlist, state); + break; + } + } else { + cmdlist = options_array_item_value(a)->cmdlist; + item = hooks_insert_one(item, hd, cmdlist, + state); + } + a = options_array_next(a); + } + } + + cmdq_free_state(state); +} + +/* Insert commands for a hook event. */ +static void +hooks_insert_event(struct cmdq_item *item, const char *name, + struct event_payload *ep, struct options *oo, int expand) +{ + struct hooks_data hd; + struct format_tree *ft; + struct client *c; + + if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS)) + return; + + c = event_payload_get_client(ep, "client"); + ft = format_create(c, item, FORMAT_NONE, FORMAT_NOJOBS); + event_payload_add_formats(ep, ft, "hook_"); + format_add(ft, "hook", "%s", name); + format_log_debug(ft, __func__); + + memset(&hd, 0, sizeof hd); + hd.name = name; + cmd_find_clear_state(&hd.fs, 0); + event_payload_get_target(ep, &hd.fs); + hd.formats = ft; + hd.oo = oo; + hd.client = c; + hd.expand = expand; + + hooks_insert(item, &hd); + format_free(ft); +} + +/* Handle an event for hooks. */ +static void +hooks_event_cb(const char *name, struct event_payload *ep, + __unused void *sink_data) +{ + struct cmdq_item *item; + + if (event_payload_get_pointer(ep, "_hook_monitor") != NULL) + return; + + item = event_payload_get_pointer(ep, "_cmdq_item"); + if (item != NULL) { + hooks_insert_event(item, name, ep, NULL, 0); + return; + } + + item = cmdq_running(NULL); + if (item == NULL || (~cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS)) + hooks_insert_event(NULL, name, ep, NULL, 0); +} + +/* Add a hook event sink. */ +void +hooks_add_event(const char *name) +{ + struct hooks_event *he; + + TAILQ_FOREACH(he, &hooks_events, entry) { + if (strcmp(he->name, name) == 0) + return; + } + + he = xcalloc(1, sizeof *he); + he->name = xstrdup(name); + he->sink = events_add_sink(name, hooks_event_cb, NULL); + TAILQ_INSERT_TAIL(&hooks_events, he, entry); +} + +/* Return if an event name can be fired through the hooks path. */ +int +hooks_valid_event_name(const char *name) +{ + const struct options_table_entry *oe; + + if (*name == '@') + return (1); + oe = options_search(name); + return (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK)); +} + +/* Add hook event sinks for all built-in hooks. */ +void +hooks_build_events(void) +{ + const struct options_table_entry *oe; + + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->flags & OPTIONS_TABLE_IS_HOOK) + hooks_add_event(oe->name); + } +} + +/* Run a hook immediately. */ +void +hooks_run(struct cmdq_item *item, const char *name) +{ + struct cmd_find_state *target = cmdq_get_target(item); + struct hooks_data hd = { 0 }; + + hd.name = name; + cmd_find_copy_state(&hd.fs, target); + hd.client = cmdq_get_client(item); + + hd.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); + format_add(hd.formats, "hook", "%s", name); + format_log_debug(hd.formats, __func__); + + hooks_insert(item, &hd); + format_free(hd.formats); +} + +/* Free a hook monitor. */ +void +hooks_monitor_free(void *data) +{ + struct hook_monitor *hm = data; + + events_remove_sink(hm->sink); + monitor_destroy(hm->set); + free(hm->format); + free(hm); +} + +/* Remove a hook monitor. */ +void +hooks_monitor_remove(struct options *oo, const char *name) +{ + struct options_entry *o; + struct hook_monitor *hm; + + o = options_get_only(oo, name); + if (o == NULL) + return; + + hm = options_get_monitor_data(o); + if (hm != NULL) { + options_set_monitor_data(o, NULL); + hooks_monitor_free(hm); + } +} + +/* Handle a hook monitor event. */ +static void +hooks_monitor_hook_cb(const char *name, struct event_payload *ep, + void *sink_data) +{ + struct hook_monitor *hm = sink_data; + + 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 *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); + + cmd_find_clear_state(&fs, 0); + if (wl != NULL && wp != NULL && wp->window == wl->window) + cmd_find_from_winlink_pane(&fs, wl, wp, 0); + else if (wl != NULL) + cmd_find_from_winlink(&fs, wl, 0); + else if (wp != NULL) + cmd_find_from_pane(&fs, wp, 0); + else if (change->s != NULL) + cmd_find_from_session(&fs, change->s, 0); + else + cmd_find_copy_state(&fs, &hm->fs); + event_payload_set_target(ep, &fs); + + 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_int(ep, "window_index", wl->idx); + } + 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. */ +void +hooks_monitor_add(__unused struct cmdq_item *item, struct options *oo, + const char *name, enum monitor_type type, int id, const char *format, + struct cmd_find_state *fs, struct session *s) +{ + struct options_entry *o; + struct hook_monitor *hm; + + hooks_monitor_remove(oo, name); + o = options_get_only(oo, name); + if (o == NULL) + o = options_set_string(oo, name, 0, "%s", ""); + + hm = xcalloc(1, sizeof *hm); + hm->oo = oo; + cmd_find_copy_state(&hm->fs, fs); + hm->type = type; + hm->id = id; + hm->format = xstrdup(format); + hm->set = monitor_create_session(s, hooks_monitor_cb, hm); + 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); +} + +/* Convert a hook monitor to its value. */ +char * +hooks_monitor_to_string(struct options_entry *o) +{ + struct hook_monitor *hm = options_get_monitor_data(o); + const char *name = options_name(o); + char *s; + + if (hm == NULL) + return (NULL); + + switch (hm->type) { + case MONITOR_SESSION: + xasprintf(&s, "%s::%s", name, hm->format); + break; + case MONITOR_PANE: + xasprintf(&s, "%s:%%%d:%s", name, hm->id, hm->format); + break; + case MONITOR_ALL_PANES: + xasprintf(&s, "%s:%%*:%s", name, hm->format); + break; + case MONITOR_WINDOW: + xasprintf(&s, "%s:@%d:%s", name, hm->id, hm->format); + break; + case MONITOR_ALL_WINDOWS: + xasprintf(&s, "%s:@*:%s", name, hm->format); + break; + } + return (s); +} diff --git a/hyperlinks.c b/hyperlinks.c index a71827272..1820fdad2 100644 --- a/hyperlinks.c +++ b/hyperlinks.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: hyperlinks.c,v 1.5 2026/06/29 16:44:06 nicm Exp $ */ /* * Copyright (c) 2021 Will diff --git a/input-keys.c b/input-keys.c index 277c9e71e..3208a33c3 100644 --- a/input-keys.c +++ b/input-keys.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: input-keys.c,v 1.114 2026/06/15 21:47:01 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/input.c b/input.c index 2438ea504..bfab97a7b 100644 --- a/input.c +++ b/input.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: input.c,v 1.265 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -174,6 +174,8 @@ static void input_osc_110(struct input_ctx *, const char *); static void input_osc_111(struct input_ctx *, const char *); static void input_osc_112(struct input_ctx *, const char *); static void input_osc_133(struct input_ctx *, const char *); +static void input_fire_pane_title_changed(struct window_pane *, + const char *); /* Transition entry/exit handlers. */ static void input_clear(struct input_ctx *); @@ -210,6 +212,21 @@ static int input_end_bel(struct input_ctx *); /* Command table comparison function. */ static int input_table_compare(const void *, const void *); +static void +input_fire_pane_title_changed(struct window_pane *wp, const char *title) +{ + struct event_payload *ep; + struct cmd_find_state fs; + + ep = event_payload_create(); + cmd_find_from_pane(&fs, wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_pane(ep, "pane", wp); + event_payload_set_window(ep, "window", wp->window); + event_payload_set_string(ep, "new_title", "%s", title); + events_fire("pane-title-changed", ep); +} + /* Command table entry. */ struct input_table_entry { int ch; @@ -2161,7 +2178,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx) screen_pop_title(sctx->s); if (wp == NULL) break; - notify_pane("pane-title-changed", wp); + events_fire_pane("pane-title-changed", wp); server_redraw_window_borders(w); server_status_window(w); break; @@ -2663,7 +2680,7 @@ input_exit_osc(struct input_ctx *ictx) if (wp != NULL && options_get_number(wp->options, "allow-set-title") && screen_set_title(sctx->s, p, 1)) { - notify_pane("pane-title-changed", wp); + input_fire_pane_title_changed(wp, p); server_redraw_window_borders(wp->window); server_status_window(wp->window); } @@ -2741,7 +2758,7 @@ input_exit_apc(struct input_ctx *ictx) if (wp != NULL && options_get_number(wp->options, "allow-set-title") && screen_set_title(sctx->s, ictx->input_buf, 1)) { - notify_pane("pane-title-changed", wp); + input_fire_pane_title_changed(wp, ictx->input_buf); server_redraw_window_borders(wp->window); server_status_window(wp->window); } @@ -3252,7 +3269,7 @@ input_osc_52(struct input_ctx *ictx, const char *p) screen_write_start_pane(&ctx, wp, NULL); screen_write_setselection(&ctx, clip, out, outlen); screen_write_stop(&ctx); - notify_pane("pane-set-clipboard", wp); + events_fire_pane("pane-set-clipboard", wp); paste_add(NULL, out, outlen); } } diff --git a/job.c b/job.c index 5ad179ffb..8400ea584 100644 --- a/job.c +++ b/job.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: job.c,v 1.74 2025/09/08 11:21:56 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/key-bindings.c b/key-bindings.c index c73fd7c33..92d417c5b 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: key-bindings.c,v 1.186 2026/07/07 12:30:36 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/key-string.c b/key-string.c index 3df9da4b9..ac7a1bf89 100644 --- a/key-string.c +++ b/key-string.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: key-string.c,v 1.77 2026/03/31 11:46:43 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/layout-custom.c b/layout-custom.c index 65fb3780b..9df0448ff 100644 --- a/layout-custom.c +++ b/layout-custom.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: layout-custom.c,v 1.37 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2010 Nicholas Marriott @@ -286,7 +286,7 @@ layout_parse(struct window *w, const char *layout, char **cause) recalculate_sizes(); layout_print_cell(tiled_lc, __func__, 0); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); return (0); diff --git a/layout-set.c b/layout-set.c index 2f164b01c..7e944d32f 100644 --- a/layout-set.c +++ b/layout-set.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: layout-set.c,v 1.39 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -183,7 +183,7 @@ layout_set_even(struct window *w, enum layout_type type) layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); } @@ -292,7 +292,7 @@ layout_set_main_h(struct window *w) layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); } @@ -389,7 +389,7 @@ layout_set_main_h_mirrored(struct window *w) layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); } @@ -486,7 +486,7 @@ layout_set_main_v(struct window *w) layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); } @@ -584,7 +584,7 @@ layout_set_main_v_mirrored(struct window *w) layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); } @@ -706,6 +706,6 @@ layout_set_tiled(struct window *w) layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lcroot->g.sx, lcroot->g.sy, -1, -1); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); server_redraw_window(w); } diff --git a/layout.c b/layout.c index 507dd63c1..177d2f4cf 100644 --- a/layout.c +++ b/layout.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: layout.c,v 1.93 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -957,7 +957,7 @@ layout_resize_layout(struct window *w, struct layout_cell *lc, /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w, NULL); - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); } /* Resize a single pane within the layout. */ @@ -1502,7 +1502,7 @@ layout_close_pane(struct window_pane *wp) layout_fix_offsets(w); layout_fix_panes(w, NULL); } - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); } /* Spread out cells inside a parent cell. */ diff --git a/log.c b/log.c index ede6e2577..e596088b2 100644 --- a/log.c +++ b/log.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: log.c,v 1.33 2026/05/17 16:02:33 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/menu.c b/menu.c index 257452fd7..ec7b9781b 100644 --- a/menu.c +++ b/menu.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: menu.c,v 1.68 2026/07/07 09:45:09 nicm Exp $ */ /* * Copyright (c) 2019 Nicholas Marriott diff --git a/mode-key.c b/mode-key.c deleted file mode 100644 index 4245d173b..000000000 --- a/mode-key.c +++ /dev/null @@ -1,261 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2008 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 MIND, 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 "tmux.h" - -/* - * Mode keys. These are the key bindings used when editing (status prompt), and - * in the modes. They are split into two sets of three tables, one set of three - * for vi and the other for emacs key bindings. The three tables are for - * editing, for menu-like modes (choice, more), and for copy modes (copy, - * scroll). - * - * The fixed tables of struct mode_key_entry below are the defaults: they are - * built into a tree of struct mode_key_binding by mode_key_init_trees, which - * can then be modified. - * - * vi command mode is handled by having a mode flag in the struct which allows - * two sets of bindings to be swapped between. A couple of editing commands - * (any matching MODEKEYEDIT_SWITCHMODE*) are special-cased to do this. - */ - -/* Command to string mapping. */ -struct mode_key_cmdstr { - enum mode_key_cmd cmd; - const char *name; -}; - -/* Entry in the default mode key tables. */ -struct mode_key_entry { - key_code key; - enum mode_key_cmd cmd; -}; - -/* Choice keys command strings. */ -static const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { - { MODEKEYCHOICE_BACKSPACE, "backspace" }, - { MODEKEYCHOICE_BOTTOMLINE, "bottom-line"}, - { MODEKEYCHOICE_CANCEL, "cancel" }, - { MODEKEYCHOICE_CHOOSE, "choose" }, - { MODEKEYCHOICE_DOWN, "down" }, - { MODEKEYCHOICE_ENDOFLIST, "end-of-list"}, - { MODEKEYCHOICE_PAGEDOWN, "page-down" }, - { MODEKEYCHOICE_PAGEUP, "page-up" }, - { MODEKEYCHOICE_SCROLLDOWN, "scroll-down" }, - { MODEKEYCHOICE_SCROLLUP, "scroll-up" }, - { MODEKEYCHOICE_STARTNUMBERPREFIX, "start-number-prefix" }, - { MODEKEYCHOICE_STARTOFLIST, "start-of-list"}, - { MODEKEYCHOICE_TOPLINE, "top-line"}, - { MODEKEYCHOICE_TREE_COLLAPSE, "tree-collapse" }, - { MODEKEYCHOICE_TREE_COLLAPSE_ALL, "tree-collapse-all" }, - { MODEKEYCHOICE_TREE_EXPAND, "tree-expand" }, - { MODEKEYCHOICE_TREE_EXPAND_ALL, "tree-expand-all" }, - { MODEKEYCHOICE_TREE_TOGGLE, "tree-toggle" }, - { MODEKEYCHOICE_UP, "up" }, - - { 0, NULL } -}; - -/* vi choice selection keys. */ -static const struct mode_key_entry mode_key_vi_choice[] = { - { '0' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '1' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '2' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '3' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '4' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '5' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '6' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '7' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '8' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '9' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '\002' /* C-b */, MODEKEYCHOICE_PAGEUP }, - { '\003' /* C-c */, MODEKEYCHOICE_CANCEL }, - { '\005' /* C-e */, MODEKEYCHOICE_SCROLLDOWN }, - { '\006' /* C-f */, MODEKEYCHOICE_PAGEDOWN }, - { '\031' /* C-y */, MODEKEYCHOICE_SCROLLUP }, - { '\n', MODEKEYCHOICE_CHOOSE }, - { '\r', MODEKEYCHOICE_CHOOSE }, - { 'j', MODEKEYCHOICE_DOWN }, - { 'k', MODEKEYCHOICE_UP }, - { 'q', MODEKEYCHOICE_CANCEL }, - { KEYC_HOME, MODEKEYCHOICE_STARTOFLIST }, - { 'g', MODEKEYCHOICE_STARTOFLIST }, - { 'H', MODEKEYCHOICE_TOPLINE }, - { 'L', MODEKEYCHOICE_BOTTOMLINE }, - { 'G', MODEKEYCHOICE_ENDOFLIST }, - { KEYC_END, MODEKEYCHOICE_ENDOFLIST }, - { KEYC_BSPACE, MODEKEYCHOICE_BACKSPACE }, - { KEYC_DOWN | KEYC_CTRL, MODEKEYCHOICE_SCROLLDOWN }, - { KEYC_DOWN, MODEKEYCHOICE_DOWN }, - { KEYC_NPAGE, MODEKEYCHOICE_PAGEDOWN }, - { KEYC_PPAGE, MODEKEYCHOICE_PAGEUP }, - { KEYC_UP | KEYC_CTRL, MODEKEYCHOICE_SCROLLUP }, - { KEYC_UP, MODEKEYCHOICE_UP }, - { ' ', MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_LEFT, MODEKEYCHOICE_TREE_COLLAPSE }, - { KEYC_RIGHT, MODEKEYCHOICE_TREE_EXPAND }, - { KEYC_LEFT | KEYC_CTRL, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, - { KEYC_RIGHT | KEYC_CTRL, MODEKEYCHOICE_TREE_EXPAND_ALL }, - { KEYC_MOUSEDOWN1_PANE, MODEKEYCHOICE_CHOOSE }, - { KEYC_MOUSEDOWN3_PANE, MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_WHEELUP_PANE, MODEKEYCHOICE_UP }, - { KEYC_WHEELDOWN_PANE, MODEKEYCHOICE_DOWN }, - - { KEYC_NONE, -1 } -}; -struct mode_key_tree mode_key_tree_vi_choice; - -/* emacs choice selection keys. */ -static const struct mode_key_entry mode_key_emacs_choice[] = { - { '0' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '1' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '2' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '3' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '4' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '5' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '6' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '7' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '8' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '9' | KEYC_ESCAPE, MODEKEYCHOICE_STARTNUMBERPREFIX }, - { '\003' /* C-c */, MODEKEYCHOICE_CANCEL }, - { '\016' /* C-n */, MODEKEYCHOICE_DOWN }, - { '\020' /* C-p */, MODEKEYCHOICE_UP }, - { '\026' /* C-v */, MODEKEYCHOICE_PAGEDOWN }, - { '\033' /* Escape */, MODEKEYCHOICE_CANCEL }, - { '\n', MODEKEYCHOICE_CHOOSE }, - { '\r', MODEKEYCHOICE_CHOOSE }, - { 'q', MODEKEYCHOICE_CANCEL }, - { 'v' | KEYC_ESCAPE, MODEKEYCHOICE_PAGEUP }, - { KEYC_HOME, MODEKEYCHOICE_STARTOFLIST }, - { '<' | KEYC_ESCAPE, MODEKEYCHOICE_STARTOFLIST }, - { 'R' | KEYC_ESCAPE, MODEKEYCHOICE_TOPLINE }, - { '>' | KEYC_ESCAPE, MODEKEYCHOICE_ENDOFLIST }, - { KEYC_END, MODEKEYCHOICE_ENDOFLIST }, - { KEYC_BSPACE, MODEKEYCHOICE_BACKSPACE }, - { KEYC_DOWN | KEYC_CTRL, MODEKEYCHOICE_SCROLLDOWN }, - { KEYC_DOWN, MODEKEYCHOICE_DOWN }, - { KEYC_NPAGE, MODEKEYCHOICE_PAGEDOWN }, - { KEYC_PPAGE, MODEKEYCHOICE_PAGEUP }, - { KEYC_UP | KEYC_CTRL, MODEKEYCHOICE_SCROLLUP }, - { KEYC_UP, MODEKEYCHOICE_UP }, - { ' ', MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_LEFT, MODEKEYCHOICE_TREE_COLLAPSE }, - { KEYC_RIGHT, MODEKEYCHOICE_TREE_EXPAND }, - { KEYC_LEFT | KEYC_CTRL, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, - { KEYC_RIGHT | KEYC_CTRL, MODEKEYCHOICE_TREE_EXPAND_ALL }, - { KEYC_MOUSEDOWN1_PANE, MODEKEYCHOICE_CHOOSE }, - { KEYC_MOUSEDOWN3_PANE, MODEKEYCHOICE_TREE_TOGGLE }, - { KEYC_WHEELUP_PANE, MODEKEYCHOICE_UP }, - { KEYC_WHEELDOWN_PANE, MODEKEYCHOICE_DOWN }, - - { KEYC_NONE, -1 } -}; -struct mode_key_tree mode_key_tree_emacs_choice; - -/* Table mapping key table names to default settings and trees. */ -static const struct mode_key_table mode_key_tables[] = { - { "vi-choice", mode_key_cmdstr_choice, - &mode_key_tree_vi_choice, mode_key_vi_choice }, - { "emacs-choice", mode_key_cmdstr_choice, - &mode_key_tree_emacs_choice, mode_key_emacs_choice }, - - { NULL, NULL, NULL, NULL } -}; - -RB_GENERATE(mode_key_tree, mode_key_binding, entry, mode_key_cmp); - -int -mode_key_cmp(struct mode_key_binding *mbind1, struct mode_key_binding *mbind2) -{ - if (mbind1->key < mbind2->key) - return (-1); - if (mbind1->key > mbind2->key) - return (1); - return (0); -} - -const char * -mode_key_tostring(const struct mode_key_cmdstr *cmdstr, enum mode_key_cmd cmd) -{ - for (; cmdstr->name != NULL; cmdstr++) { - if (cmdstr->cmd == cmd) - return (cmdstr->name); - } - return (NULL); -} - -enum mode_key_cmd -mode_key_fromstring(const struct mode_key_cmdstr *cmdstr, const char *name) -{ - for (; cmdstr->name != NULL; cmdstr++) { - if (strcasecmp(cmdstr->name, name) == 0) - return (cmdstr->cmd); - } - return (MODEKEY_NONE); -} - -const struct mode_key_table * -mode_key_findtable(const char *name) -{ - const struct mode_key_table *mtab; - - for (mtab = mode_key_tables; mtab->name != NULL; mtab++) { - if (strcasecmp(name, mtab->name) == 0) - return (mtab); - } - return (NULL); -} - -void -mode_key_init_trees(void) -{ - const struct mode_key_table *mtab; - const struct mode_key_entry *ment; - struct mode_key_binding *mbind; - - for (mtab = mode_key_tables; mtab->name != NULL; mtab++) { - RB_INIT(mtab->tree); - for (ment = mtab->table; ment->key != KEYC_NONE; ment++) { - mbind = xmalloc(sizeof *mbind); - mbind->key = ment->key; - mbind->cmd = ment->cmd; - RB_INSERT(mode_key_tree, mtab->tree, mbind); - } - } -} - -void -mode_key_init(struct mode_key_data *mdata, struct mode_key_tree *mtree) -{ - mdata->tree = mtree; -} - -enum mode_key_cmd -mode_key_lookup(struct mode_key_data *mdata, key_code key) -{ - struct mode_key_binding *mbind, mtmp; - - mtmp.key = key; - if ((mbind = RB_FIND(mode_key_tree, mdata->tree, &mtmp)) == NULL) - return (MODEKEY_OTHER); - return (mbind->cmd); -} diff --git a/mode-tree.c b/mode-tree.c index 59d9e4fa9..ef859b15e 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: mode-tree.c,v 1.99 2026/07/06 12:08:52 nicm Exp $ */ /* * Copyright (c) 2017 Nicholas Marriott diff --git a/monitor.c b/monitor.c index 6d1a747fd..514af4e3c 100644 --- a/monitor.c +++ b/monitor.c @@ -1,7 +1,7 @@ -/* $OpenBSD$ */ +/* $OpenBSD: monitor.c,v 1.5 2026/07/10 13:38:45 nicm Exp $ */ /* - * Copyright (c) 2026 Nicholas Marriott + * 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 diff --git a/names.c b/names.c index cee310366..aa7e6006e 100644 --- a/names.c +++ b/names.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: names.c,v 1.48 2026/06/29 18:17:28 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/notify.c b/notify.c deleted file mode 100644 index 50bb55022..000000000 --- a/notify.c +++ /dev/null @@ -1,516 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2012 George Nachman - * - * 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 MIND, 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 "tmux.h" - -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; -}; - -struct notify_monitor { - struct options *oo; - - struct monitor_set *set; - struct cmd_find_state fs; - - enum monitor_type type; - int id; - char *format; -}; - -static struct cmdq_item * -notify_insert_one_hook(struct cmdq_item *item, struct notify_entry *ne, - struct cmd_list *cmdlist, struct cmdq_state *state) -{ - struct cmdq_item *new_item; - char *s; - - if (cmdlist == NULL) - return (item); - if (log_get_level() != 0) { - s = cmd_list_print(cmdlist, 0); - log_debug("%s: hook %s is: %s", __func__, ne->name, s); - free(s); - } - new_item = cmdq_get_command(cmdlist, state); - if (item != NULL) - return (cmdq_insert_after(item, new_item)); - return (cmdq_append(NULL, new_item)); -} - -static struct cmd_parse_result * -notify_parse_hook(struct notify_entry *ne, struct cmd_find_state *fs, - const char *value) -{ - struct cmd_parse_result *pr; - struct format_tree *ft; - char *expanded; - - if (!ne->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); - expanded = format_expand(ft, value); - format_free(ft); - - pr = cmd_parse_from_string(expanded, NULL); - free(expanded); - return (pr); -} - -static void -notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) -{ - struct cmd_find_state fs; - struct options *oo; - struct cmdq_state *state; - struct options_entry *o; - struct options_array_item *a; - struct cmd_list *cmdlist; - const char *value; - struct cmd_parse_result *pr; - - log_debug("%s: inserting hook %s", __func__, ne->name); - - cmd_find_clear_state(&fs, 0); - if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs)) - cmd_find_from_nothing(&fs, 0); - else - cmd_find_copy_state(&fs, &ne->fs); - - if (ne->oo != NULL) { - oo = ne->oo; - o = options_get_only(oo, ne->name); - } else { - if (fs.s == NULL) - oo = global_s_options; - else - oo = fs.s->options; - o = options_get(oo, ne->name); - if (o == NULL && fs.wp != NULL) { - oo = fs.wp->options; - o = options_get(oo, ne->name); - } - if (o == NULL && fs.wl != NULL) { - oo = fs.wl->window->options; - o = options_get(oo, ne->name); - } - } - if (o == NULL) { - log_debug("%s: hook %s not found", __func__, ne->name); - return; - } - - state = cmdq_new_state(&fs, NULL, CMDQ_STATE_NOHOOKS); - cmdq_add_formats(state, ne->formats); - - if (*ne->name == '@') { - value = options_get_string(oo, ne->name); - pr = notify_parse_hook(ne, &fs, value); - switch (pr->status) { - case CMD_PARSE_ERROR: - log_debug("%s: can't parse hook %s: %s", __func__, - ne->name, pr->error); - free(pr->error); - break; - case CMD_PARSE_SUCCESS: - notify_insert_one_hook(item, ne, pr->cmdlist, state); - break; - } - } else { - a = options_array_first(o); - while (a != NULL) { - if (ne->expand) { - value = options_array_item_value(a)->string; - pr = notify_parse_hook(ne, &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 = notify_insert_one_hook(item, ne, - pr->cmdlist, state); - break; - } - } else { - cmdlist = options_array_item_value(a)->cmdlist; - item = notify_insert_one_hook(item, ne, cmdlist, - state); - } - a = options_array_next(a); - } - } - - cmdq_free_state(state); -} - -static enum cmd_retval -notify_callback(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); - - 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); - - return (CMD_RETURN_NORMAL); -} - -void -notify_monitor_free(void *data) -{ - struct notify_monitor *nhm = data; - - monitor_destroy(nhm->set); - free(nhm->format); - free(nhm); -} - -void -notify_monitor_remove(struct options *oo, const char *name) -{ - struct options_entry *o; - struct notify_monitor *nhm; - - o = options_get_only(oo, name); - if (o == NULL) - return; - - nhm = options_get_monitor_data(o); - if (nhm != NULL) { - options_set_monitor_data(o, NULL); - notify_monitor_free(nhm); - } -} - -static void -notify_monitor_cb(struct monitor_change *change, void *data) -{ - struct notify_monitor *nhm = data; - struct notify_entry ne; - struct cmdq_item *item; - struct window *w; - - 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); -} - -void -notify_monitor_add(__unused struct cmdq_item *item, struct options *oo, - const char *name, enum monitor_type type, int id, const char *format, - struct cmd_find_state *fs, struct session *s) -{ - struct options_entry *o; - struct notify_monitor *nhm; - - notify_monitor_remove(oo, name); - o = options_get_only(oo, name); - if (o == NULL) - o = options_set_string(oo, name, 0, "%s", ""); - - nhm = xcalloc(1, sizeof *nhm); - nhm->oo = oo; - cmd_find_copy_state(&nhm->fs, fs); - nhm->type = type; - nhm->id = id; - nhm->format = xstrdup(format); - nhm->set = monitor_create_session(s, notify_monitor_cb, nhm); - options_set_monitor_data(o, nhm); - monitor_add(nhm->set, name, type, id, format, 0); -} - -/* Convert a hook monitor to its value. */ -char * -notify_monitor_to_string(struct options_entry *o) -{ - struct notify_monitor *nhm = options_get_monitor_data(o); - const char *name = options_name(o); - char *s; - - if (nhm == NULL) - return (NULL); - - switch (nhm->type) { - case MONITOR_SESSION: - xasprintf(&s, "%s::%s", name, nhm->format); - break; - case MONITOR_PANE: - xasprintf(&s, "%s:%%%d:%s", name, nhm->id, nhm->format); - break; - case MONITOR_ALL_PANES: - xasprintf(&s, "%s:%%*:%s", name, nhm->format); - break; - case MONITOR_WINDOW: - xasprintf(&s, "%s:@%d:%s", name, nhm->id, nhm->format); - break; - case MONITOR_ALL_WINDOWS: - xasprintf(&s, "%s:@*:%s", name, nhm->format); - break; - } - return (s); -} - -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) -{ - struct notify_entry *ne; - struct cmdq_item *item; - - item = cmdq_running(NULL); - if (item != NULL && (cmdq_get_flags(item) & CMDQ_STATE_NOHOOKS)) - 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__); - - cmdq_append(NULL, cmdq_get_callback(notify_callback, ne)); -} - -void -notify_hook(struct cmdq_item *item, const char *name) -{ - struct cmd_find_state *target = cmdq_get_target(item); - struct notify_entry ne; - - memset(&ne, 0, sizeof ne); - - ne.name = name; - cmd_find_copy_state(&ne.fs, target); - - 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__); - - notify_insert_hook(item, &ne); - format_free(ne.formats); -} - -void -notify_client(const char *name, struct client *c) -{ - struct cmd_find_state fs; - - cmd_find_from_client(&fs, c, 0); - notify_add(name, &fs, c, NULL, NULL, NULL, NULL); -} - -void -notify_session(const char *name, struct session *s) -{ - struct cmd_find_state fs; - - 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); -} - -void -notify_winlink(const char *name, struct winlink *wl) -{ - struct cmd_find_state fs; - - cmd_find_from_winlink(&fs, wl, 0); - notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL); -} - -void -notify_session_window(const char *name, struct session *s, struct window *w) -{ - struct cmd_find_state fs; - - cmd_find_from_session_window(&fs, s, w, 0); - notify_add(name, &fs, NULL, s, w, NULL, NULL); -} - -void -notify_window(const char *name, struct window *w) -{ - struct cmd_find_state fs; - - cmd_find_from_window(&fs, w, 0); - notify_add(name, &fs, NULL, NULL, w, NULL, NULL); -} - -void -notify_pane(const char *name, struct window_pane *wp) -{ - struct cmd_find_state fs; - - cmd_find_from_pane(&fs, wp, 0); - notify_add(name, &fs, NULL, NULL, NULL, wp, NULL); -} - -void -notify_paste_buffer(const char *pbname, int deleted) -{ - struct cmd_find_state fs; - - 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); - } -} diff --git a/options-table.c b/options-table.c index b1ab705d5..082ed06ff 100644 --- a/options-table.c +++ b/options-table.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: options-table.c,v 1.232 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -1933,11 +1933,18 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("client-light-theme", ""), OPTIONS_TABLE_HOOK("client-dark-theme", ""), OPTIONS_TABLE_HOOK("command-error", ""), + OPTIONS_TABLE_HOOK("marked-pane-changed", ""), + OPTIONS_TABLE_PANE_HOOK("pane-created", ""), OPTIONS_TABLE_PANE_HOOK("pane-died", ""), OPTIONS_TABLE_PANE_HOOK("pane-exited", ""), OPTIONS_TABLE_PANE_HOOK("pane-focus-in", ""), OPTIONS_TABLE_PANE_HOOK("pane-focus-out", ""), OPTIONS_TABLE_PANE_HOOK("pane-mode-changed", ""), + OPTIONS_TABLE_PANE_HOOK("pane-mode-entered", ""), + OPTIONS_TABLE_PANE_HOOK("pane-mode-exited", ""), + OPTIONS_TABLE_PANE_HOOK("pane-prompt-closed", ""), + OPTIONS_TABLE_PANE_HOOK("pane-prompt-opened", ""), + OPTIONS_TABLE_PANE_HOOK("pane-resized", ""), OPTIONS_TABLE_PANE_HOOK("pane-set-clipboard", ""), OPTIONS_TABLE_PANE_HOOK("pane-title-changed", ""), OPTIONS_TABLE_HOOK("session-closed", ""), @@ -1949,6 +1956,8 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""), OPTIONS_TABLE_WINDOW_HOOK("window-renamed", ""), OPTIONS_TABLE_WINDOW_HOOK("window-resized", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-unzoomed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-zoomed", ""), OPTIONS_TABLE_HOOK("window-unlinked", ""), { .name = NULL } diff --git a/options.c b/options.c index 3b81ed57d..b5238bc69 100644 --- a/options.c +++ b/options.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: options.c,v 1.90 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -410,7 +410,7 @@ options_remove(struct options_entry *o) else options_value_free(o, &o->value); if (o->monitor_data != NULL) - notify_monitor_free(o->monitor_data); + hooks_monitor_free(o->monitor_data); RB_REMOVE(options_tree, &oo->tree, o); free((void *)o->name); free(o); diff --git a/paste.c b/paste.c index 6b8a1cb47..0b9069f92 100644 --- a/paste.c +++ b/paste.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: paste.c,v 1.53 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -44,6 +44,16 @@ static int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *); RB_GENERATE_STATIC(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); +static void +paste_fire_event(const char *name, const char *pbname) +{ + struct event_payload *ep; + + ep = event_payload_create(); + event_payload_set_string(ep, "paste_buffer", "%s", pbname); + events_fire(name, ep); +} + static int paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b) { @@ -138,7 +148,7 @@ paste_get_name(const char *name) void paste_free(struct paste_buffer *pb) { - notify_paste_buffer(pb->name, 1); + paste_fire_event("paste-buffer-deleted", pb->name); RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_time_tree, &paste_by_time, pb); @@ -196,7 +206,7 @@ paste_add(const char *prefix, char *data, size_t size) RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); - notify_paste_buffer(pb->name, 0); + paste_fire_event("paste-buffer-changed", pb->name); } /* Rename a paste buffer. */ @@ -254,8 +264,8 @@ paste_rename(const char *oldname, const char *newname, char **cause) RB_INSERT(paste_name_tree, &paste_by_name, pb); - notify_paste_buffer(oldname, 1); - notify_paste_buffer(pb->name, 0); + paste_fire_event("paste-buffer-deleted", oldname); + paste_fire_event("paste-buffer-changed", pb->name); return (0); } @@ -312,7 +322,7 @@ paste_set(char *data, size_t size, const char *name, char **cause) RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); - notify_paste_buffer(pb->name, 0); + paste_fire_event("paste-buffer-changed", pb->name); return (0); } @@ -325,7 +335,7 @@ paste_replace(struct paste_buffer *pb, char *data, size_t size) pb->data = data; pb->size = size; - notify_paste_buffer(pb->name, 0); + paste_fire_event("paste-buffer-changed", pb->name); } /* Convert start of buffer into a nice string. */ diff --git a/popup.c b/popup.c index 913c6c527..c43b46d0a 100644 --- a/popup.c +++ b/popup.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: popup.c,v 1.75 2026/07/09 07:32:58 nicm Exp $ */ /* * Copyright (c) 2020 Nicholas Marriott diff --git a/proc.c b/proc.c index 21a747525..194371236 100644 --- a/proc.c +++ b/proc.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: proc.c,v 1.31 2026/06/08 21:38:19 nicm Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott diff --git a/procname.c b/procname.c index 7c0872fd0..0ef484fb8 100644 --- a/procname.c +++ b/procname.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: procname.c,v 1.20 2022/02/22 17:35:01 deraadt Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/prompt-history.c b/prompt-history.c index ac99bdb95..ad1ab4047 100644 --- a/prompt-history.c +++ b/prompt-history.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: prompt-history.c,v 1.1 2026/06/25 11:39:11 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott diff --git a/prompt.c b/prompt.c index 1ab447ebe..6187a7a12 100644 --- a/prompt.c +++ b/prompt.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: prompt.c,v 1.4 2026/06/26 14:40:30 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott diff --git a/regsub.c b/regsub.c index 9e33a88ab..c49c72bb9 100644 --- a/regsub.c +++ b/regsub.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: regsub.c,v 1.10 2026/07/08 11:04:51 nicm Exp $ */ /* * Copyright (c) 2019 Nicholas Marriott diff --git a/resize.c b/resize.c index 19a940d61..c8257cec0 100644 --- a/resize.c +++ b/resize.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: resize.c,v 1.56 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -60,8 +60,8 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) tty_update_window_offset(w); server_redraw_window(w); - notify_window("window-layout-changed", w); - notify_window("window-resized", w); + events_fire_window("window-layout-changed", w); + events_fire_window("window-resized", w); w->flags &= ~WINDOW_RESIZE; } diff --git a/screen-redraw.c b/screen-redraw.c index 70a830e6a..10a5acaa9 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: screen-redraw.c,v 1.150 2026/07/09 07:32:58 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott diff --git a/screen-write.c b/screen-write.c index 4d4f90900..a7a16149c 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: screen-write.c,v 1.283 2026/07/09 07:35:05 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/screen.c b/screen.c index 8b988adee..4c754360c 100644 --- a/screen.c +++ b/screen.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: screen.c,v 1.105 2026/06/29 18:17:28 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/server-acl.c b/server-acl.c index db95a3c6b..03e758af5 100644 --- a/server-acl.c +++ b/server-acl.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: server-acl.c,v 1.3 2026/06/08 21:38:19 nicm Exp $ */ /* * Copyright (c) 2021 Holland Schutte, Jayson Morberg diff --git a/server-client.c b/server-client.c index 8344ae190..86360de20 100644 --- a/server-client.c +++ b/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: server-client.c,v 1.486 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -433,7 +433,7 @@ server_client_set_session(struct client *c, struct session *s) alerts_check_session(s); tty_update_client_offset(c); status_timer_start(c); - notify_client("client-session-changed", c); + events_fire_client("client-session-changed", c); server_redraw_client(c); } @@ -468,7 +468,7 @@ server_client_lost(struct client *c) if (c->flags & CLIENT_ATTACHED) { server_client_attached_lost(c); - notify_client("client-detached", c); + events_fire_client("client-detached", c); } if (c->flags & CLIENT_CONTROL) @@ -1288,7 +1288,7 @@ server_client_update_latest(struct client *c) if (options_get_number(w->options, "window-size") == WINDOW_SIZE_LATEST) recalculate_size(w, 0); - notify_client("client-active", c); + events_fire_client("client-active", c); } /* Get repeat time. */ @@ -2472,7 +2472,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) c->overlay_resize(c, c->overlay_data); server_redraw_client(c); if (c->session != NULL) - notify_client("client-resized", c); + events_fire_client("client-resized", c); break; case MSG_EXITING: if (datalen != 0) @@ -3087,10 +3087,10 @@ server_client_report_theme(struct client *c, enum client_theme theme) if (theme == THEME_LIGHT) { c->theme = THEME_LIGHT; - notify_client("client-light-theme", c); + events_fire_client("client-light-theme", c); } else { c->theme = THEME_DARK; - notify_client("client-dark-theme", c); + events_fire_client("client-dark-theme", c); } /* diff --git a/server-fn.c b/server-fn.c index 7560e9da2..e3823dcbb 100644 --- a/server-fn.c +++ b/server-fn.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: server-fn.c,v 1.147 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -30,6 +30,31 @@ #include "tmux.h" static void server_destroy_session_group(struct session *); +static void server_fire_pane_exit(const char *, struct window_pane *); + +static void +server_fire_pane_exit(const char *name, struct window_pane *wp) +{ + struct event_payload *ep; + struct cmd_find_state fs; + int status = wp->status; + const char *signame; + + if (WIFSIGNALED(status)) + signame = sig2name(WTERMSIG(status)); + + ep = event_payload_create(); + cmd_find_from_pane(&fs, wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_pane(ep, "pane", wp); + event_payload_set_window(ep, "window", wp->window); + if (WIFEXITED(status)) + event_payload_set_int(ep, "exit_status", WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + event_payload_set_string(ep, "exit_signal", "%s", signame); + event_payload_set_int(ep, "exit_success", status == 0); + events_fire(name, ep); +} void server_redraw_client(struct client *c) @@ -203,6 +228,7 @@ server_kill_window(struct window *w, int renumber) struct session *s, *s1; struct winlink *wl; + window_add_ref(w, __func__); RB_FOREACH_SAFE(s, sessions, &sessions, s1) { if (!session_has(s, w)) continue; @@ -220,6 +246,7 @@ server_kill_window(struct window *w, int renumber) server_renumber_session(s); } recalculate_sizes(); + window_remove_ref(w, __func__); } void @@ -273,8 +300,7 @@ server_link_window(struct session *src, struct winlink *srcwl, * Can't use session_detach as it will destroy session * if this makes it empty. */ - notify_session_window("window-unlinked", dst, - dstwl->window); + events_fire_winlink("window-unlinked", dstwl); dstwl->flags &= ~WINLINK_ALERTFLAGS; winlink_stack_remove(&dst->lastw, dstwl); winlink_remove(&dst->windows, dstwl); @@ -348,7 +374,7 @@ server_destroy_pane(struct window_pane *wp, int notify) gettimeofday(&wp->dead_time, NULL); if (notify) - notify_pane("pane-died", wp); + server_fire_pane_exit("pane-died", wp); s = options_get_string(wp->options, "remain-on-exit-format"); if (*s != '\0') { @@ -371,7 +397,7 @@ server_destroy_pane(struct window_pane *wp, int notify) } if (notify) - notify_pane("pane-exited", wp); + server_fire_pane_exit("pane-exited", wp); server_unzoom_window(w); server_client_remove_pane(wp); diff --git a/server.c b/server.c index edabc8a79..6fe7f2c7b 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: server.c,v 1.214 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -212,6 +212,8 @@ server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base, TAILQ_INIT(&clients); RB_INIT(&sessions); key_bindings_init(); + control_build_events(); + hooks_build_events(); TAILQ_INIT(&message_log); gettimeofday(&start_time, NULL); diff --git a/session.c b/session.c index 41909d2a6..9c23c4b50 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: session.c,v 1.105 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -208,7 +208,7 @@ session_destroy(struct session *s, int notify, const char *from) RB_REMOVE(sessions, &sessions, s); if (notify) - notify_session("session-closed", s); + events_fire_session("session-closed", s); free(s->tio); @@ -221,7 +221,7 @@ session_destroy(struct session *s, int notify, const char *from) winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw)); while (!RB_EMPTY(&s->windows)) { wl = RB_ROOT(&s->windows); - notify_session_window("window-unlinked", s, wl->window); + events_fire_winlink("window-unlinked", wl); winlink_remove(&s->windows, wl); } @@ -332,7 +332,7 @@ session_attach(struct session *s, struct window *w, int idx, char **cause) } wl->session = s; winlink_set_window(wl, w); - notify_session_window("window-linked", s, w); + events_fire_winlink("window-linked", wl); session_group_synchronize_from(s); return (wl); @@ -348,7 +348,7 @@ session_detach(struct session *s, struct winlink *wl) session_next(s, 0); wl->flags &= ~WINLINK_ALERTFLAGS; - notify_session_window("window-unlinked", s, wl->window); + events_fire_winlink("window-unlinked", wl); winlink_stack_remove(&s->lastw, wl); winlink_remove(&s->windows, wl); @@ -495,7 +495,7 @@ session_set_current(struct session *s, struct winlink *wl) winlink_clear_flags(wl); window_update_activity(wl->window); tty_update_window_offset(wl->window); - notify_session("session-window-changed", s); + events_fire_session("session-window-changed", s); return (0); } @@ -659,7 +659,7 @@ session_group_synchronize1(struct session *target, struct session *s) wl2 = winlink_add(&s->windows, wl->idx); wl2->session = s; winlink_set_window(wl2, wl->window); - notify_session_window("window-linked", s, wl2->window); + events_fire_winlink("window-linked", wl2); wl2->flags |= wl->flags & WINLINK_ALERTFLAGS; } @@ -685,7 +685,7 @@ session_group_synchronize1(struct session *target, struct session *s) wl = RB_ROOT(&old_windows); wl2 = winlink_find_by_window_id(&s->windows, wl->window->id); if (wl2 == NULL) - notify_session_window("window-unlinked", s, wl->window); + events_fire_winlink("window-unlinked", wl); winlink_remove(&old_windows, wl); } } diff --git a/sort.c b/sort.c index c416b8425..31f6e82f1 100644 --- a/sort.c +++ b/sort.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: sort.c,v 1.9 2026/06/29 07:45:09 nicm Exp $ */ /* * Copyright (c) 2026 Dane Jensen diff --git a/spawn.c b/spawn.c index 09eec948d..21fc25937 100644 --- a/spawn.c +++ b/spawn.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: spawn.c,v 1.47 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2019 Nicholas Marriott @@ -73,6 +73,45 @@ spawn_log(const char *from, struct spawn_context *sc) log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); } +static void +spawn_fire_pane_created(struct spawn_context *sc, struct window_pane *wp) +{ + struct event_payload *ep; + struct cmd_find_state fs; + char *cmd = NULL; + const char *cwd = wp->cwd; + + ep = event_payload_create(); + cmd_find_from_winlink_pane(&fs, sc->wl, wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_session(ep, "session", sc->s); + event_payload_set_window(ep, "window", wp->window); + event_payload_set_int(ep, "window_index", sc->wl->idx); + event_payload_set_pane(ep, "pane", wp); + + if (wp->argc != 0) + cmd = cmd_stringify_argv(wp->argc, wp->argv); + if (cmd != NULL && *cmd != '\0') + event_payload_set_string(ep, "pane_command", "%s", cmd); + else if (wp->shell != NULL) + event_payload_set_string(ep, "pane_command", "%s", wp->shell); + free(cmd); + + if (cwd != NULL) + event_payload_set_string(ep, "pane_current_path", "%s", cwd); + + if (sc->flags & SPAWN_EMPTY) + event_payload_set_int(ep, "created_empty", 1); + else + event_payload_set_int(ep, "created_empty", 0); + if (sc->flags & SPAWN_RESPAWN) + event_payload_set_int(ep, "created_respawn", 1); + else + event_payload_set_int(ep, "created_respawn", 0); + + events_fire("pane-created", ep); +} + struct winlink * spawn_window(struct spawn_context *sc, char **cause) { @@ -133,7 +172,7 @@ spawn_window(struct spawn_context *sc, char **cause) * if this makes it empty. */ wl->flags &= ~WINLINK_ALERTFLAGS; - notify_session_window("window-unlinked", s, wl->window); + events_fire_winlink("window-unlinked", wl); winlink_stack_remove(&s->lastw, wl); winlink_remove(&s->windows, wl); @@ -193,7 +232,7 @@ spawn_window(struct spawn_context *sc, char **cause) /* Fire notification if new window. */ if (~sc->flags & SPAWN_RESPAWN) - notify_session_window("window-linked", s, w); + events_fire_winlink("window-linked", sc->wl); session_group_synchronize_from(s); return (sc->wl); @@ -499,8 +538,8 @@ complete: sigprocmask(SIG_SETMASK, &oldset, NULL); window_pane_set_event(new_wp); - environ_free(child); + spawn_fire_pane_created(sc, new_wp); if (sc->flags & SPAWN_RESPAWN) return (new_wp); @@ -511,7 +550,7 @@ complete: window_set_active_pane(w, new_wp, 1); } if (~sc->flags & SPAWN_NONOTIFY) - notify_window("window-layout-changed", w); + events_fire_window("window-layout-changed", w); return (new_wp); } diff --git a/status.c b/status.c index 8f7c209e1..2f3c8f11e 100644 --- a/status.c +++ b/status.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: status.c,v 1.273 2026/07/06 14:29:10 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/style.c b/style.c index 2ca5e37af..55d08fe75 100644 --- a/style.c +++ b/style.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: style.c,v 1.47 2026/06/29 17:08:52 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/tmux-protocol.h b/tmux-protocol.h index 3cf00c094..d823ce7e2 100644 --- a/tmux-protocol.h +++ b/tmux-protocol.h @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: tmux-protocol.h,v 1.2 2023/01/06 07:09:27 nicm Exp $ */ /* * Copyright (c) 2021 Nicholas Marriott diff --git a/tmux.1 b/tmux.1 index 50d398b7c..215878dea 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD$ +.\" $OpenBSD: tmux.1,v 1.1131 2026/07/10 13:38:45 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$ +.Dd $Mdocdate: July 10 2026 $ .Dt TMUX 1 .Os .Sh NAME @@ -6323,11 +6323,7 @@ For example, this could be used to write to a log file: set\-hook \-g command\-error "run\-shell \\"echo 'a tmux command failed' >>/tmp/log\\"" .Ed .Pp -All the notifications listed in the -.Sx CONTROL MODE -section are hooks (without any arguments), except -.Ic %exit . -The following additional hooks are available: +The following hooks are available in addition to after hooks: .Bl -tag -width "XXXXXXXXXXXXXXXXXXXXXX" .It alert\-activity Run when a window has activity. @@ -6346,11 +6342,11 @@ Run when a client becomes the latest active client of its session. .It client\-attached Run when a client is attached. .It client\-detached -Run when a client is detached +Run when a client is detached. .It client\-focus\-in -Run when focus enters a client +Run when focus enters a client. .It client\-focus\-out -Run when focus exits a client +Run when focus exits a client. .It client\-resized Run when a client is resized. .It client\-session\-changed @@ -6361,6 +6357,10 @@ Run when a client switches to a light theme. Run when a client switches to a dark theme. .It command\-error Run when a command fails. +.It marked\-pane\-changed +Run when the marked pane is set or cleared. +.It pane\-created +Run when a pane is created or respawned. .It pane\-died Run when the program running in a pane exits, but .Ic remain\-on\-exit @@ -6375,20 +6375,38 @@ option is on. Run when the focus exits a pane, if the .Ic focus\-events option is on. +.It pane\-mode\-changed +Run when a pane changes mode. +.It pane\-mode\-entered +Run when a pane enters a mode. +.It pane\-mode\-exited +Run when a pane exits a mode. +.It pane\-prompt\-closed +Run when a prompt in a pane is closed. +.It pane\-prompt\-opened +Run when a prompt is opened in a pane. +.It pane\-resized +Run when a pane is resized. .It pane\-set\-clipboard Run when the terminal clipboard is set using the .Xr xterm 1 escape sequence. -.It session\-created -Run when a new session created. +.It pane\-title\-changed +Run when a pane title is changed. .It session\-closed -Run when a session closed. +Run when a session is closed. +.It session\-created +Run when a new session is created. .It session\-renamed Run when a session is renamed. +.It session\-window\-changed +Run when a session changes its active window. .It window\-layout\-changed Run when a window layout is changed. .It window\-linked Run when a window is linked into a session. +.It window\-pane\-changed +Run when a window changes its active pane. .It window\-renamed Run when a window is renamed. .It window\-resized @@ -6396,6 +6414,10 @@ Run when a window is resized. This may be after the .Ar client\-resized hook is run. +.It window\-unzoomed +Run when a window is unzoomed. +.It window\-zoomed +Run when a window is zoomed. .It window\-unlinked Run when a window is unlinked from a session. .El @@ -6403,13 +6425,15 @@ Run when a window is unlinked from a session. Hooks are managed with these commands: .Bl -tag -width Ds .It Xo Ic set\-hook -.Op Fl agpRuw +.Op Fl agpERuw .Op Fl B Ar name:what:format .Op Fl t Ar target\-pane .Ar hook\-name .Op Ar command .Xc Without +.Fl E +or .Fl R , sets (or with .Fl u @@ -6450,6 +6474,14 @@ the subscription named by is removed. .Pp With +.Fl E , +fire the user event named +.Ar hook\-name . +.Ar hook\-name +must begin with +.Ql @ . +.Pp +With .Fl R , run .Ar hook\-name @@ -8560,8 +8592,10 @@ or the current pane if omitted) after the command finishes. If the command fails, the exit status is also displayed. .Tg wait .It Xo Ic wait\-for -.Op Fl L | S | U -.Ar channel +.Op Fl ELSUlv +.Op Fl F Ar format +.Op Fl w Ar waiter +.Ar name .Xc .D1 Pq alias: Ic wait When used without options, prevents the client from exiting until woken using @@ -8574,6 +8608,41 @@ is used, the channel is locked and any clients that try to lock the same channel are made to wait until the channel is unlocked with .Ic wait\-for .Fl U . +.Pp +With +.Fl E , +.Nm +waits for the next event with +.Ar name . +Events include hook and notification names, and user +.Ql @ +events generated by +.Ic set-hook +.Fl E +or +.Ic set-hook +.Fl B . +If +.Fl F +is given, +.Ar format +must also be true. +If +.Fl v +is given, event payload keys are printed (whether or not +.Ar format +is true). +.Pp +.Fl l +list the waiters for +.Ar name +and +.Fl w +wakes +.Ar waiter +on +.Ar name +immediately. .El .Sh EXIT MESSAGES When a diff --git a/tmux.c b/tmux.c index c08110c95..8a90c3ff9 100644 --- a/tmux.c +++ b/tmux.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: tmux.c,v 1.221 2026/06/29 18:17:28 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/tmux.h b/tmux.h index b153310cf..076f4bf7c 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: tmux.h,v 1.1394 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -48,6 +48,9 @@ struct cmdq_state; struct cmds; struct control_state; struct environ; +struct event_payload; +struct event_payload_item; +struct events_sink; struct format_job_tree; struct format_tree; struct hyperlinks_uri; @@ -2302,6 +2305,23 @@ struct monitor_change { }; typedef void (*monitor_cb)(struct monitor_change *, void *); +/* Event payload type. */ +enum event_payload_type { + EVENT_PAYLOAD_STRING, + EVENT_PAYLOAD_TIME, + EVENT_PAYLOAD_INT, + EVENT_PAYLOAD_UINT, + EVENT_PAYLOAD_CLIENT, + EVENT_PAYLOAD_SESSION, + EVENT_PAYLOAD_WINDOW, + EVENT_PAYLOAD_PANE, + 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; @@ -2611,29 +2631,78 @@ char *format_grid_hyperlink(struct grid *, u_int, u_int, struct screen *); char *format_grid_line(struct grid *, u_int); -/* 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); +/* 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 event_payload_set_target(struct event_payload *, + struct cmd_find_state *); +int event_payload_get_target(struct event_payload *, + struct cmd_find_state *); +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_int(struct event_payload *, const char *, int); +void event_payload_set_uint(struct event_payload *, const char *, u_int); +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_pointer(struct event_payload *, const char *, void *, + event_payload_free_cb, event_payload_print_cb); +const char *event_payload_get_string(struct event_payload *, const char *); +char *event_payload_print(struct event_payload *, const char *); +void event_payload_add_formats(struct event_payload *, + struct format_tree *, const char *); +struct event_payload_item *event_payload_first(struct event_payload *); +struct event_payload_item *event_payload_next(struct event_payload_item *); +const char *event_payload_item_name(struct event_payload_item *); +enum event_payload_type event_payload_item_type(struct event_payload_item *); +time_t event_payload_get_time(struct event_payload *, const char *); +int event_payload_get_int(struct event_payload *, const char *, int *); +int event_payload_get_uint(struct event_payload *, const char *, u_int *); +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 *); +void *event_payload_get_pointer(struct event_payload *, const char *); -/* notify.c */ -void notify_hook(struct cmdq_item *, const char *); -void notify_monitor_add(struct cmdq_item *, struct options *, +/* events.c */ +typedef void (*events_cb)(const char *, struct event_payload *, void *); +struct events_sink *events_add_sink(const char *, events_cb, void *); +void events_remove_sink(struct events_sink *); +void events_fire(const char *, struct event_payload *); +void events_fire_client(const char *, struct client *); +void events_fire_session(const char *, struct session *); +void events_fire_window(const char *, struct window *); +void events_fire_pane(const char *, struct window_pane *); +void events_fire_winlink(const char *, struct winlink *); + +/* 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); + +/* hooks.c */ +void hooks_add_event(const char *); +int hooks_valid_event_name(const char *); +void hooks_build_events(void); +void hooks_run(struct cmdq_item *, const char *); +void hooks_monitor_add(struct cmdq_item *, struct options *, const char *, enum monitor_type, int, const char *, struct cmd_find_state *, struct session *); -void notify_monitor_remove(struct options *, const char *); -void notify_monitor_free(void *); -char *notify_monitor_to_string(struct options_entry *); -void notify_client(const char *, struct client *); -void notify_session(const char *, struct session *); -void notify_winlink(const char *, struct winlink *); -void notify_session_window(const char *, struct session *, struct window *); -void notify_window(const char *, struct window *); -void notify_pane(const char *, struct window_pane *); -void notify_paste_buffer(const char *, int); +void hooks_monitor_remove(struct options *, const char *); +void hooks_monitor_free(void *); +char *hooks_monitor_to_string(struct options_entry *); /* options.c */ struct options *options_create(struct options *); @@ -3855,20 +3924,7 @@ void control_add_sub(struct client *, const char *, enum monitor_type, int, void control_remove_sub(struct client *, const char *); /* control-notify.c */ -void control_notify_pane_mode_changed(int); -void control_notify_window_layout_changed(struct window *); -void control_notify_window_pane_changed(struct window *); -void control_notify_window_unlinked(struct session *, struct window *); -void control_notify_window_linked(struct session *, struct window *); -void control_notify_window_renamed(struct window *); -void control_notify_client_session_changed(struct client *); -void control_notify_client_detached(struct client *); -void control_notify_session_renamed(struct session *); -void control_notify_session_created(struct session *); -void control_notify_session_closed(struct session *); -void control_notify_session_window_changed(struct session *); -void control_notify_paste_buffer_changed(const char *); -void control_notify_paste_buffer_deleted(const char *); +void control_build_events(void); /* session.c */ extern struct sessions sessions; diff --git a/tty-acs.c b/tty-acs.c index 3dab31b6f..eedb79c2f 100644 --- a/tty-acs.c +++ b/tty-acs.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: tty-acs.c,v 1.13 2023/08/08 07:19:48 nicm Exp $ */ /* * Copyright (c) 2010 Nicholas Marriott diff --git a/tty-draw.c b/tty-draw.c index 825d81e49..d47be66bc 100644 --- a/tty-draw.c +++ b/tty-draw.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: tty-draw.c,v 1.14 2026/06/19 10:38:29 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott diff --git a/tty-features.c b/tty-features.c index ea6769dc6..614c63f62 100644 --- a/tty-features.c +++ b/tty-features.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: tty-features.c,v 1.40 2026/07/01 06:17:58 nicm Exp $ */ /* * Copyright (c) 2020 Nicholas Marriott diff --git a/tty-keys.c b/tty-keys.c index d6a2610ea..61e541165 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: tty-keys.c,v 1.210 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1008,10 +1008,10 @@ complete_key: if (key == KEYC_FOCUS_OUT) { c->flags &= ~CLIENT_FOCUSED; window_update_focus(c->session->curw->window); - notify_client("client-focus-out", c); + events_fire_client("client-focus-out", c); } else if (key == KEYC_FOCUS_IN) { c->flags |= CLIENT_FOCUSED; - notify_client("client-focus-in", c); + events_fire_client("client-focus-in", c); window_update_focus(c->session->curw->window); } diff --git a/tty-term.c b/tty-term.c index bea34fc42..c8ceb2e56 100644 --- a/tty-term.c +++ b/tty-term.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: tty-term.c,v 1.106 2026/06/13 09:17:29 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/tty.c b/tty.c index ce5012191..66ca0d6cf 100644 --- a/tty.c +++ b/tty.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: tty.c,v 1.476 2026/06/26 11:36:22 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/utf8-combined.c b/utf8-combined.c index 923342b43..02abc3518 100644 --- a/utf8-combined.c +++ b/utf8-combined.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: utf8-combined.c,v 1.10 2026/06/09 11:49:36 nicm Exp $ */ /* * Copyright (c) 2023 Nicholas Marriott diff --git a/utf8.c b/utf8.c index 4d6ebcbd8..e634b43f1 100644 --- a/utf8.c +++ b/utf8.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: utf8.c,v 1.71 2026/05/12 09:37:25 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott diff --git a/window-border.c b/window-border.c index 5420f4b97..0419015f4 100644 --- a/window-border.c +++ b/window-border.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window-border.c,v 1.1 2026/06/22 08:47:46 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott diff --git a/window-buffer.c b/window-buffer.c index 6798e2027..f3116f8da 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window-buffer.c,v 1.49 2026/06/26 08:10:49 nicm Exp $ */ /* * Copyright (c) 2017 Nicholas Marriott diff --git a/window-client.c b/window-client.c index 5a8c71862..64e38cb81 100644 --- a/window-client.c +++ b/window-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window-client.c,v 1.46 2026/07/04 22:09:06 nicm Exp $ */ /* * Copyright (c) 2017 Nicholas Marriott diff --git a/window-clock.c b/window-clock.c index cf7040009..acd4382b3 100644 --- a/window-clock.c +++ b/window-clock.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window-clock.c,v 1.34 2026/06/25 16:32:42 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott diff --git a/window-copy.c b/window-copy.c index 8b117be92..997cb8888 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window-copy.c,v 1.413 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -5764,7 +5764,7 @@ window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, screen_write_setselection(&ctx, "", buf, len); screen_write_stop(&ctx); wp->flags |= redraw; - notify_pane("pane-set-clipboard", wp); + events_fire_pane("pane-set-clipboard", wp); } if (set_paste) @@ -5849,7 +5849,7 @@ window_copy_append_selection(struct window_mode_entry *wme) screen_write_start_pane(&ctx, wp, NULL); screen_write_setselection(&ctx, "", buf, len); screen_write_stop(&ctx); - notify_pane("pane-set-clipboard", wp); + events_fire_pane("pane-set-clipboard", wp); } pb = paste_get_top(&bufname); diff --git a/window-customize.c b/window-customize.c index 09da37f2e..9f84540ed 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window-customize.c,v 1.30 2026/07/06 14:40:57 nicm Exp $ */ /* * Copyright (c) 2020 Nicholas Marriott diff --git a/window-switch.c b/window-switch.c index 99ea19985..d458f30af 100644 --- a/window-switch.c +++ b/window-switch.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window-switch.c,v 1.1 2026/06/26 14:40:30 nicm Exp $ */ /* * Copyright (c) 2026 Nicholas Marriott diff --git a/window-tree.c b/window-tree.c index 2d15eb536..1b19602fe 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window-tree.c,v 1.93 2026/07/04 22:09:06 nicm Exp $ */ /* * Copyright (c) 2017 Nicholas Marriott diff --git a/window-visible.c b/window-visible.c index f46967ec7..360e4fc88 100644 --- a/window-visible.c +++ b/window-visible.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window-visible.c,v 1.4 2026/06/29 19:03:34 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott diff --git a/window.c b/window.c index 109f002ff..198296da4 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: window.c,v 1.359 2026/07/10 13:38:45 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -75,8 +75,8 @@ static struct window_pane *window_pane_create(struct window *, u_int, u_int, static void window_pane_destroy(struct window_pane *); static void window_pane_free(struct window_pane *); static void window_pane_scrollbar_timer(int, short, void *); -static void window_pane_full_size_offset(struct window_pane *wp, - int *xoff, int *yoff, u_int *sx, u_int *sy); +static void window_pane_full_size_offset(struct window_pane *, int *, int *, + u_int *, u_int *); RB_GENERATE(windows, window, entry, window_cmp); RB_GENERATE(winlinks, winlink, entry, winlink_cmp); @@ -88,6 +88,7 @@ struct window_pane_prompt { status_prompt_input_cb inputcb; prompt_free_cb freecb; void *data; + enum prompt_type type; }; int @@ -96,6 +97,78 @@ window_cmp(struct window *w1, struct window *w2) return (w1->id - w2->id); } +static void +window_fire_renamed(struct window *w, const char *old_name) +{ + struct event_payload *ep; + struct cmd_find_state fs; + + ep = event_payload_create(); + cmd_find_from_window(&fs, w, 0); + event_payload_set_target(ep, &fs); + event_payload_set_window(ep, "window", w); + event_payload_set_string(ep, "old_name", "%s", old_name); + event_payload_set_string(ep, "new_name", "%s", w->name); + events_fire("window-renamed", ep); +} + +static void +window_fire_pane_changed(struct window *w, struct window_pane *wp, + struct window_pane *lastwp) +{ + struct event_payload *ep; + struct cmd_find_state fs; + + ep = event_payload_create(); + cmd_find_from_pane(&fs, wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_window(ep, "window", w); + event_payload_set_pane(ep, "pane", wp); + event_payload_set_pane(ep, "new_pane", wp); + if (lastwp != NULL) + event_payload_set_pane(ep, "old_pane", lastwp); + events_fire("window-pane-changed", ep); +} + +static void +window_fire_pane_mode_changed(const char *name, struct window_pane *wp, + const char *previous, const char *current, int entered) +{ + struct event_payload *ep; + struct cmd_find_state fs; + + ep = event_payload_create(); + cmd_find_from_pane(&fs, wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_pane(ep, "pane", wp); + event_payload_set_window(ep, "window", wp->window); + + if (current != NULL) + event_payload_set_string(ep, "current_mode", "%s", current); + if (previous != NULL) + event_payload_set_string(ep, "previous_mode", "%s", previous); + event_payload_set_int(ep, "mode_entered", entered); + + events_fire(name, ep); +} + +static void +window_fire_pane_prompt(const char *name, struct window_pane *wp, + enum prompt_type type) +{ + struct event_payload *ep; + struct cmd_find_state fs; + const char *type_string = prompt_type_string(type); + + ep = event_payload_create(); + cmd_find_from_pane(&fs, wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_pane(ep, "pane", wp); + event_payload_set_window(ep, "window", wp->window); + event_payload_set_string(ep, "prompt_type", "%s", type_string); + events_fire(name, ep); +} + int winlink_cmp(struct winlink *wl1, struct winlink *wl2) { @@ -446,13 +519,15 @@ window_pane_remove_ref(struct window_pane *wp, const char *from) void window_set_name(struct window *w, const char *new_name, int untrusted) { - char *name; + char *last, *name; name = clean_name(new_name, untrusted); if (name != NULL) { + last = xstrdup(w->name); free(w->name); w->name = name; - notify_window("window-renamed", w); + window_fire_renamed(w, last); + free(last); } } @@ -553,13 +628,13 @@ window_pane_update_focus(struct window_pane *wp) log_debug("%s: %%%u focus out", __func__, wp->id); if (wp->base.mode & MODE_FOCUSON) bufferevent_write(wp->event, "\033[O", 3); - notify_pane("pane-focus-out", wp); + events_fire_pane("pane-focus-out", wp); wp->flags &= ~PANE_FOCUSED; } else if (focused && (~wp->flags & PANE_FOCUSED)) { log_debug("%s: %%%u focus in", __func__, wp->id); if (wp->base.mode & MODE_FOCUSON) bufferevent_write(wp->event, "\033[I", 3); - notify_pane("pane-focus-in", wp); + events_fire_pane("pane-focus-in", wp); wp->flags |= PANE_FOCUSED; } else log_debug("%s: %%%u focus unchanged", __func__, wp->id); @@ -595,7 +670,7 @@ window_set_active_pane(struct window *w, struct window_pane *wp, int notify) server_redraw_window(w); if (notify) - notify_window("window-pane-changed", w); + window_fire_pane_changed(w, w->active, lastwp); return (1); } @@ -785,7 +860,8 @@ window_zoom(struct window_pane *wp) w->saved_layout_root = w->layout_root; layout_init(w, wp); w->flags |= WINDOW_ZOOMED; - notify_window("window-layout-changed", w); + events_fire_window("window-zoomed", w); + events_fire_window("window-layout-changed", w); redraw_invalidate_scene(w); return (0); @@ -811,8 +887,10 @@ window_unzoom(struct window *w, int notify) } layout_fix_panes(w, NULL); - if (notify) - notify_window("window-layout-changed", w); + if (notify) { + events_fire_window("window-unzoomed", w); + events_fire_window("window-layout-changed", w); + } redraw_invalidate_scene(w); return (0); @@ -894,7 +972,7 @@ window_lost_pane(struct window *w, struct window_pane *wp) if (w->active != NULL) { window_pane_stack_remove(&w->last_panes, w->active); w->active->flags |= PANE_CHANGED; - notify_window("window-pane-changed", w); + events_fire_window("window-pane-changed", w); window_update_focus(w); } } @@ -1223,9 +1301,9 @@ window_pane_destroy(struct window_pane *wp) window_pane_wait_finish(wp); spawn_editor_finish(wp); - window_pane_clear_prompt(wp); RB_REMOVE(window_pane_tree, &all_window_panes, wp); wp->flags |= PANE_DESTROYED; + window_pane_clear_prompt(wp); window_pane_free_modes(wp); screen_write_clear_dirty(wp); @@ -1352,6 +1430,8 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_mode_entry *wme; struct window_pane_resize *r; + struct event_payload *ep; + struct cmd_find_state fs; if (sx == wp->sx && sy == wp->sy) return; @@ -1374,6 +1454,17 @@ window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); + + ep = event_payload_create(); + cmd_find_from_pane(&fs, wp, 0); + event_payload_set_target(ep, &fs); + event_payload_set_pane(ep, "pane", wp); + event_payload_set_window(ep, "window", wp->window); + event_payload_set_uint(ep, "width", sx); + event_payload_set_uint(ep, "height", sy); + event_payload_set_uint(ep, "old_width", r->osx); + event_payload_set_uint(ep, "old_height", r->osy); + events_fire("pane-resized", ep); } int @@ -1383,9 +1474,13 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, { struct window_mode_entry *wme; struct window *w = wp->window; + const char *name = mode->name, *p = NULL; - if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode) - return (1); + if (!TAILQ_EMPTY(&wp->modes)) { + if (TAILQ_FIRST(&wp->modes)->mode == mode) + return (1); + p = TAILQ_FIRST(&wp->modes)->mode->name; + } TAILQ_FOREACH(wme, &wp->modes, entry) { if (wme->mode == mode) @@ -1411,7 +1506,9 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, server_redraw_window_borders(wp->window); server_status_window(wp->window); - notify_pane("pane-mode-changed", wp); + + window_fire_pane_mode_changed("pane-mode-entered", wp, p, name, 1); + window_fire_pane_mode_changed("pane-mode-changed", wp, p, name, 1); return (0); } @@ -1422,11 +1519,13 @@ window_pane_reset_mode(struct window_pane *wp) struct window_mode_entry *wme, *next; struct window *w = wp->window; int kill; + const char *name, *p; if (TAILQ_EMPTY(&wp->modes)) return; wme = TAILQ_FIRST(&wp->modes); + p = wme->mode->name; kill = wme->kill; TAILQ_REMOVE(&wp->modes, wme, entry); wme->mode->free(wme); @@ -1443,13 +1542,16 @@ window_pane_reset_mode(struct window_pane *wp) if (next->mode->resize != NULL) next->mode->resize(next, wp->sx, wp->sy); } + name = (next == NULL ? NULL : next->mode->name); wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED); layout_fix_panes(w, NULL); server_redraw_window_borders(wp->window); server_status_window(wp->window); - notify_pane("pane-mode-changed", wp); + + window_fire_pane_mode_changed("pane-mode-exited", wp, p, name, 0); + window_fire_pane_mode_changed("pane-mode-changed", wp, p, name, 0); if (kill) server_kill_pane(wp); @@ -1512,6 +1614,7 @@ window_pane_set_prompt(struct window_pane *wp, struct client *c, wpp->inputcb = inputcb; wpp->freecb = freecb; wpp->data = data; + wpp->type = type; memset(&pd, 0, sizeof pd); prompt_set_options(&pd, s); @@ -1529,19 +1632,28 @@ window_pane_set_prompt(struct window_pane *wp, struct client *c, wp->flags |= PANE_REDRAW; prompt_incremental_start(wp->prompt); + window_fire_pane_prompt("pane-prompt-opened", wp, type); } /* Close a pane prompt. */ void window_pane_clear_prompt(struct window_pane *wp) { - struct prompt *prompt = wp->prompt; + struct prompt *prompt = wp->prompt; + struct window_pane_prompt *wpp = wp->prompt_data; + enum prompt_type type = PROMPT_TYPE_INVALID; - if (prompt == NULL) - return; - wp->prompt = NULL; - prompt_free(prompt); - wp->flags |= PANE_REDRAW; + if (prompt != NULL) { + if (wpp != NULL) + type = wpp->type; + + wp->prompt = NULL; + prompt_free(prompt); + wp->flags |= PANE_REDRAW; + + if (~wp->flags & PANE_DESTROYED) + window_fire_pane_prompt("pane-prompt-closed", wp, type); + } } /* Does this pane have an open prompt? */ diff --git a/xmalloc.c b/xmalloc.c index 14e805ed9..b60b9dbee 100644 --- a/xmalloc.c +++ b/xmalloc.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: xmalloc.c,v 1.14 2026/06/18 10:56:22 nicm Exp $ */ /* * Author: Tatu Ylonen diff --git a/xmalloc.h b/xmalloc.h index 93a4155e2..c9257b438 100644 --- a/xmalloc.h +++ b/xmalloc.h @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: xmalloc.h,v 1.5 2026/06/18 10:56:22 nicm Exp $ */ /* * Author: Tatu Ylonen diff --git a/xterm-keys.c b/xterm-keys.c deleted file mode 100644 index b10c10db7..000000000 --- a/xterm-keys.c +++ /dev/null @@ -1,252 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2009 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 MIND, 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 "tmux.h" - -/* - * xterm-style function keys append one of the following values before the last - * character: - * - * 2 Shift - * 3 Alt - * 4 Shift + Alt - * 5 Ctrl - * 6 Shift + Ctrl - * 7 Alt + Ctrl - * 8 Shift + Alt + Ctrl - * - * Rather than parsing them, just match against a table. - * - * There are three forms for F1-F4 (\\033O_P and \\033O1;_P and \\033[1;_P). - * We accept any but always output the latter (it comes first in the table). - */ - -static int xterm_keys_match(const char *, const char *, size_t, size_t *, - key_code *); -static int xterm_keys_modifiers(const char *, size_t, size_t *, - key_code *); - -struct xterm_keys_entry { - key_code key; - const char *template; -}; - -static const struct xterm_keys_entry xterm_keys_table[] = { - { KEYC_F1, "\033[1;_P" }, - { KEYC_F1, "\033O1;_P" }, - { KEYC_F1, "\033O_P" }, - { KEYC_F2, "\033[1;_Q" }, - { KEYC_F2, "\033O1;_Q" }, - { KEYC_F2, "\033O_Q" }, - { KEYC_F3, "\033[1;_R" }, - { KEYC_F3, "\033O1;_R" }, - { KEYC_F3, "\033O_R" }, - { KEYC_F4, "\033[1;_S" }, - { KEYC_F4, "\033O1;_S" }, - { KEYC_F4, "\033O_S" }, - { KEYC_F5, "\033[15;_~" }, - { KEYC_F6, "\033[17;_~" }, - { KEYC_F7, "\033[18;_~" }, - { KEYC_F8, "\033[19;_~" }, - { KEYC_F9, "\033[20;_~" }, - { KEYC_F10, "\033[21;_~" }, - { KEYC_F11, "\033[23;_~" }, - { KEYC_F12, "\033[24;_~" }, - { KEYC_UP, "\033[1;_A" }, - { KEYC_DOWN, "\033[1;_B" }, - { KEYC_RIGHT, "\033[1;_C" }, - { KEYC_LEFT, "\033[1;_D" }, - { KEYC_HOME, "\033[1;_H" }, - { KEYC_END, "\033[1;_F" }, - { KEYC_PPAGE, "\033[5;_~" }, - { KEYC_NPAGE, "\033[6;_~" }, - { KEYC_IC, "\033[2;_~" }, - { KEYC_DC, "\033[3;_~" }, - - { '!', "\033[27;_;33~" }, - { '#', "\033[27;_;35~" }, - { '(', "\033[27;_;40~" }, - { ')', "\033[27;_;41~" }, - { '+', "\033[27;_;43~" }, - { ',', "\033[27;_;44~" }, - { '-', "\033[27;_;45~" }, - { '.', "\033[27;_;46~" }, - { '0', "\033[27;_;48~" }, - { '1', "\033[27;_;49~" }, - { '2', "\033[27;_;50~" }, - { '3', "\033[27;_;51~" }, - { '4', "\033[27;_;52~" }, - { '5', "\033[27;_;53~" }, - { '6', "\033[27;_;54~" }, - { '7', "\033[27;_;55~" }, - { '8', "\033[27;_;56~" }, - { '9', "\033[27;_;57~" }, - { ':', "\033[27;_;58~" }, - { ';', "\033[27;_;59~" }, - { '<', "\033[27;_;60~" }, - { '=', "\033[27;_;61~" }, - { '>', "\033[27;_;62~" }, - { '?', "\033[27;_;63~" }, - { '\'', "\033[27;_;39~" }, - { '\r', "\033[27;_;13~" }, - { '\t', "\033[27;_;9~" }, -}; - -/* - * Match key against buffer, treating _ as a wildcard. Return -1 for no match, - * 0 for match, 1 if the end of the buffer is reached (need more data). - */ -static int -xterm_keys_match(const char *template, const char *buf, size_t len, - size_t *size, key_code *modifiers) -{ - size_t pos; - int retval; - - *modifiers = 0; - - if (len == 0) - return (0); - - pos = 0; - do { - if (*template == '_') { - retval = xterm_keys_modifiers(buf, len, &pos, - modifiers); - if (retval != 0) - return (retval); - continue; - } - if (buf[pos] != *template) - return (-1); - pos++; - } while (*++template != '\0' && pos != len); - - if (*template != '\0') /* partial */ - return (1); - - *size = pos; - return (0); -} - -/* Find modifiers from buffer. */ -static int -xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, - key_code *modifiers) -{ - u_int flags; - - if (len - *pos < 2) - return (1); - - if (buf[*pos] < '0' || buf[*pos] > '9') - return (-1); - flags = buf[(*pos)++] - '0'; - if (buf[*pos] >= '0' && buf[*pos] <= '9') - flags = (flags * 10) + (buf[(*pos)++] - '0'); - flags -= 1; - - *modifiers = 0; - if (flags & 1) - *modifiers |= KEYC_SHIFT; - if (flags & 2) - *modifiers |= KEYC_ESCAPE; - if (flags & 4) - *modifiers |= KEYC_CTRL; - if (flags & 8) - *modifiers |= KEYC_ESCAPE; - return (0); -} - -/* - * Lookup key from a buffer against the table. Returns 0 for found (and the - * key), -1 for not found, 1 for partial match. - */ -int -xterm_keys_find(const char *buf, size_t len, size_t *size, key_code *key) -{ - const struct xterm_keys_entry *entry; - u_int i; - int matched; - key_code modifiers; - - for (i = 0; i < nitems(xterm_keys_table); i++) { - entry = &xterm_keys_table[i]; - - matched = xterm_keys_match(entry->template, buf, len, size, - &modifiers); - if (matched == -1) - continue; - if (matched == 0) - *key = (entry->key|modifiers|KEYC_XTERM); - return (matched); - } - return (-1); -} - -/* Lookup a key number from the table. */ -char * -xterm_keys_lookup(key_code key) -{ - const struct xterm_keys_entry *entry; - u_int i; - key_code modifiers; - char *out; - - modifiers = 1; - if (key & KEYC_SHIFT) - modifiers += 1; - if (key & KEYC_ESCAPE) - modifiers += 2; - if (key & KEYC_CTRL) - modifiers += 4; - - /* - * If the key has no modifiers, return NULL and let it fall through to - * the normal lookup. - */ - if (modifiers == 1) - return (NULL); - - /* - * If this has the escape modifier, but was not originally an xterm - * key, it may be a genuine escape + key. So don't pass it through as - * an xterm key or programs like vi may be confused. - */ - if ((key & (KEYC_ESCAPE|KEYC_XTERM)) == KEYC_ESCAPE) - return (NULL); - - /* Otherwise, find the key in the table. */ - key &= KEYC_MASK_KEY; - for (i = 0; i < nitems(xterm_keys_table); i++) { - entry = &xterm_keys_table[i]; - if (key == entry->key) - break; - } - if (i == nitems(xterm_keys_table)) - return (NULL); - - /* Copy the template and replace the modifier. */ - out = xstrdup(entry->template); - out[strcspn(out, "_")] = '0' + modifiers; - return (out); -}