From e00a1a3e2656bdb1aa2b4bb10edf30a3c864adf1 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 13 Jul 2026 13:01:14 +0000 Subject: [PATCH] Add some additional simple events. --- cmd-break-pane.c | 9 ++++--- cmd-join-pane.c | 3 ++- cmd-swap-pane.c | 9 ++++++- input.c | 8 +++--- options-table.c | 10 ++++++- resize.c | 22 +++++++++++++-- server-client.c | 69 +++++++++++++++++++++++++++++++++++++++++++++--- session.c | 51 ++++++++++++++++++++++++++++++++--- spawn.c | 6 +++-- tmux.1 | 18 ++++++++++++- tmux.h | 4 ++- window.c | 30 +++++++++++++++++++-- 12 files changed, 216 insertions(+), 23 deletions(-) diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 8d85c57b2..dcc5a6af4 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-break-pane.c,v 1.72 2026/07/10 13:38:45 nicm Exp $ */ +/* $OpenBSD: cmd-break-pane.c,v 1.73 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -98,9 +98,9 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) struct session *src_s = source->s; struct session *dst_s = target->s; struct window_pane *wp = source->wp; - struct window *w = wl->window; + struct window *w = wl->window, *old_w = w; char *cause, *cp; - int idx = target->idx, before; + int idx = target->idx, before, old_idx = wl->idx; const char *template, *name = args_get(args, 'n'); if (args_has(args, 'W')) @@ -137,6 +137,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) wl = winlink_find_by_window(&dst_s->windows, w); if (wl == NULL) return (CMD_RETURN_ERROR); + window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx); goto out; } if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { @@ -175,6 +176,8 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) 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__); + events_fire_window("window-created", w); + window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx); if (!args_has(args, 'd')) { session_select(dst_s, wl->idx); cmd_find_from_session(current, dst_s, 0); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 01fbfbe5b..927ccda43 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-join-pane.c,v 1.70 2026/07/13 10:03:27 nicm Exp $ */ +/* $OpenBSD: cmd-join-pane.c,v 1.71 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2011 George Nachman @@ -506,6 +506,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) } else server_status_session(dst_s); + window_fire_pane_moved(src_wp, src_w, src_wl->idx, dst_w, dst_idx); if (window_count_panes(src_w, 1) == 0) server_kill_window(src_w, 1); else diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index da7ea3706..b2e53f315 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-swap-pane.c,v 1.53 2026/07/10 13:38:45 nicm Exp $ */ +/* $OpenBSD: cmd-swap-pane.c,v 1.54 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -68,11 +68,14 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; + int src_idx, dst_idx; dst_w = target->wl->window; dst_wp = target->wp; + dst_idx = target->wl->idx; src_w = source->wl->window; src_wp = source->wp; + src_idx = source->wl->idx; if (window_push_zoom(dst_w, 0, args_has(args, 'Z'))) server_redraw_window(dst_w); @@ -180,6 +183,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) redraw_invalidate_scene(dst_w); server_redraw_window(dst_w); + if (src_w != dst_w) { + window_fire_pane_moved(src_wp, src_w, src_idx, dst_w, dst_idx); + window_fire_pane_moved(dst_wp, dst_w, dst_idx, src_w, src_idx); + } events_fire_window("window-layout-changed", src_w); if (src_w != dst_w) events_fire_window("window-layout-changed", dst_w); diff --git a/input.c b/input.c index fe7297dae..46e7b91aa 100644 --- a/input.c +++ b/input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: input.c,v 1.266 2026/07/10 15:20:06 nicm Exp $ */ +/* $OpenBSD: input.c,v 1.267 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1316,8 +1316,10 @@ input_c0_dispatch(struct input_ctx *ictx) case '\000': /* NUL */ break; case '\007': /* BEL */ - if (wp != NULL) + if (wp != NULL) { + events_fire_pane("pane-bell", wp); alerts_queue(wp->window, WINDOW_BELL); + } break; case '\010': /* BS */ screen_write_backspace(sctx); @@ -2180,7 +2182,7 @@ input_csi_dispatch_winops(struct input_ctx *ictx) screen_pop_title(sctx->s); if (wp == NULL) break; - events_fire_pane("pane-title-changed", wp); + input_fire_pane_title_changed(wp, sctx->s->title); server_redraw_window_borders(w); server_status_window(w); break; diff --git a/options-table.c b/options-table.c index 49cf9730f..3e8dd5b46 100644 --- a/options-table.c +++ b/options-table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: options-table.c,v 1.233 2026/07/10 15:20:06 nicm Exp $ */ +/* $OpenBSD: options-table.c,v 1.234 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -1925,6 +1925,8 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("alert-silence", ""), OPTIONS_TABLE_HOOK("client-active", ""), OPTIONS_TABLE_HOOK("client-attached", ""), + OPTIONS_TABLE_HOOK("client-created", ""), + OPTIONS_TABLE_HOOK("client-closed", ""), OPTIONS_TABLE_HOOK("client-detached", ""), OPTIONS_TABLE_HOOK("client-focus-in", ""), OPTIONS_TABLE_HOOK("client-focus-out", ""), @@ -1934,6 +1936,7 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_HOOK("client-dark-theme", ""), OPTIONS_TABLE_HOOK("command-error", ""), OPTIONS_TABLE_HOOK("marked-pane-changed", ""), + OPTIONS_TABLE_PANE_HOOK("pane-bell", ""), OPTIONS_TABLE_PANE_HOOK("pane-command-finished", ""), OPTIONS_TABLE_PANE_HOOK("pane-command-started", ""), OPTIONS_TABLE_PANE_HOOK("pane-created", ""), @@ -1944,6 +1947,7 @@ const struct options_table_entry options_table[] = { 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-moved", ""), OPTIONS_TABLE_PANE_HOOK("pane-prompt-closed", ""), OPTIONS_TABLE_PANE_HOOK("pane-prompt-opened", ""), OPTIONS_TABLE_PANE_HOOK("pane-resized", ""), @@ -1952,8 +1956,12 @@ const struct options_table_entry options_table[] = { OPTIONS_TABLE_PANE_HOOK("pane-title-changed", ""), OPTIONS_TABLE_HOOK("session-closed", ""), OPTIONS_TABLE_HOOK("session-created", ""), + OPTIONS_TABLE_HOOK("session-added-to-group", ""), OPTIONS_TABLE_HOOK("session-renamed", ""), + OPTIONS_TABLE_HOOK("session-removed-from-group", ""), OPTIONS_TABLE_HOOK("session-window-changed", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-created", ""), + OPTIONS_TABLE_WINDOW_HOOK("window-closed", ""), OPTIONS_TABLE_WINDOW_HOOK("window-layout-changed", ""), OPTIONS_TABLE_HOOK("window-linked", ""), OPTIONS_TABLE_WINDOW_HOOK("window-pane-changed", ""), diff --git a/resize.c b/resize.c index c8257cec0..1d83e4b70 100644 --- a/resize.c +++ b/resize.c @@ -1,4 +1,4 @@ -/* $OpenBSD: resize.c,v 1.56 2026/07/10 13:38:45 nicm Exp $ */ +/* $OpenBSD: resize.c,v 1.57 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -22,9 +22,27 @@ #include "tmux.h" +static void +resize_fire_window_resized(struct window *w, u_int old_sx, u_int old_sy) +{ + 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_uint(ep, "width", w->sx); + event_payload_set_uint(ep, "height", w->sy); + event_payload_set_uint(ep, "old_width", old_sx); + event_payload_set_uint(ep, "old_height", old_sy); + events_fire("window-resized", ep); +} + void resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) { + u_int old_sx = w->sx, old_sy = w->sy; int zoomed; /* Check size limits. */ @@ -61,7 +79,7 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel) tty_update_window_offset(w); server_redraw_window(w); events_fire_window("window-layout-changed", w); - events_fire_window("window-resized", w); + resize_fire_window_resized(w, old_sx, old_sy); w->flags &= ~WINDOW_RESIZE; } diff --git a/server-client.c b/server-client.c index 86360de20..971b8d1aa 100644 --- a/server-client.c +++ b/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.486 2026/07/10 13:38:45 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.487 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -407,6 +407,62 @@ server_client_attached_lost(struct client *c) } } +/* Fire client session changed. */ +static void +server_client_fire_session_changed(struct client *c, struct session *old) +{ + 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); + event_payload_set_session(ep, "new_session", fs.s); + } + if (old != NULL) + event_payload_set_session(ep, "old_session", old); + 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("client-session-changed", ep); +} + +/* Fire client resized. */ +static void +server_client_fire_resized(struct client *c, u_int old_sx, u_int old_sy) +{ + 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); + event_payload_set_uint(ep, "width", c->tty.sx); + event_payload_set_uint(ep, "height", c->tty.sy); + event_payload_set_uint(ep, "old_width", old_sx); + event_payload_set_uint(ep, "old_height", old_sy); + events_fire("client-resized", ep); +} + /* Set client session. */ void server_client_set_session(struct client *c, struct session *s) @@ -433,7 +489,7 @@ server_client_set_session(struct client *c, struct session *s) alerts_check_session(s); tty_update_client_offset(c); status_timer_start(c); - events_fire_client("client-session-changed", c); + server_client_fire_session_changed(c, old); server_redraw_client(c); } @@ -470,6 +526,8 @@ server_client_lost(struct client *c) server_client_attached_lost(c); events_fire_client("client-detached", c); } + if (c->name != NULL && (c->flags & (CLIENT_CONTROL|CLIENT_TERMINAL))) + events_fire_client("client-closed", c); if (c->flags & CLIENT_CONTROL) control_stop(c); @@ -2425,6 +2483,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) struct client *c = arg; ssize_t datalen; struct session *s; + u_int old_sx, old_sy; if (c->flags & CLIENT_DEAD) return; @@ -2463,6 +2522,8 @@ server_client_dispatch(struct imsg *imsg, void *arg) if (c->flags & CLIENT_CONTROL) break; server_client_update_latest(c); + old_sx = c->tty.sx; + old_sy = c->tty.sy; tty_resize(&c->tty); tty_repeat_requests(&c->tty, 0); recalculate_sizes(); @@ -2472,7 +2533,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) c->overlay_resize(c, c->overlay_data); server_redraw_client(c); if (c->session != NULL) - events_fire_client("client-resized", c); + server_client_fire_resized(c, old_sx, old_sy); break; case MSG_EXITING: if (datalen != 0) @@ -2774,6 +2835,8 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) close(c->out_fd); c->out_fd = -1; } + if (c->flags & (CLIENT_CONTROL|CLIENT_TERMINAL)) + events_fire_client("client-created", c); /* If pasting has taken too long, turn it off. */ if (c->flags & (CLIENT_BRACKETPASTING|CLIENT_ASSUMEPASTING) && diff --git a/session.c b/session.c index 9c23c4b50..f51df3bbd 100644 --- a/session.c +++ b/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.105 2026/07/10 13:38:45 nicm Exp $ */ +/* $OpenBSD: session.c,v 1.106 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -473,6 +473,29 @@ session_last(struct session *s) return (session_set_current(s, wl)); } +/* Fire session window changed. */ +static void +session_fire_window_changed(struct session *s, struct winlink *wl, + struct winlink *old) +{ + 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", s); + event_payload_set_window(ep, "window", wl->window); + event_payload_set_window(ep, "new_window", wl->window); + event_payload_set_int(ep, "window_index", wl->idx); + event_payload_set_int(ep, "new_window_index", wl->idx); + if (old != NULL) { + event_payload_set_window(ep, "old_window", old->window); + event_payload_set_int(ep, "old_window_index", old->idx); + } + events_fire("session-window-changed", ep); +} + /* Set current winlink to wl .*/ int session_set_current(struct session *s, struct winlink *wl) @@ -495,7 +518,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); - events_fire_session("session-window-changed", s); + session_fire_window_changed(s, wl, old); return (0); } @@ -542,12 +565,33 @@ session_group_new(const char *name) return (sg); } +/* Fire session group changed. */ +static void +session_group_fire(const char *name, struct session_group *sg, + 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); + event_payload_set_string(ep, "group", "%s", sg->name); + event_payload_set_uint(ep, "group_size", session_group_count(sg)); + events_fire(name, ep); +} + /* Add a session to a session group. */ void session_group_add(struct session_group *sg, struct session *s) { - if (session_group_contains(s) == NULL) + if (session_group_contains(s) == NULL) { TAILQ_INSERT_TAIL(&sg->sessions, s, gentry); + session_group_fire("session-added-to-group", sg, s); + } } /* Remove a session from its group and destroy the group if empty. */ @@ -558,6 +602,7 @@ session_group_remove(struct session *s) if ((sg = session_group_contains(s)) == NULL) return; + session_group_fire("session-removed-from-group", sg, s); TAILQ_REMOVE(&sg->sessions, s, gentry); if (TAILQ_EMPTY(&sg->sessions)) { RB_REMOVE(session_groups, &session_groups, sg); diff --git a/spawn.c b/spawn.c index 21fc25937..5d020b297 100644 --- a/spawn.c +++ b/spawn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: spawn.c,v 1.47 2026/07/10 13:38:45 nicm Exp $ */ +/* $OpenBSD: spawn.c,v 1.48 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2019 Nicholas Marriott @@ -231,8 +231,10 @@ spawn_window(struct spawn_context *sc, char **cause) session_select(s, sc->wl->idx); /* Fire notification if new window. */ - if (~sc->flags & SPAWN_RESPAWN) + if (~sc->flags & SPAWN_RESPAWN) { + events_fire_window("window-created", w); events_fire_winlink("window-linked", sc->wl); + } session_group_synchronize_from(s); return (sc->wl); diff --git a/tmux.1 b/tmux.1 index 01d654a25..3b4b228f6 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1133 2026/07/13 10:03:27 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1134 2026/07/13 13:01:14 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -6356,6 +6356,10 @@ See Run when a client becomes the latest active client of its session. .It client\-attached Run when a client is attached. +.It client\-closed +Run when a client is closed. +.It client\-created +Run when a client is created. .It client\-detached Run when a client is detached. .It client\-focus\-in @@ -6374,6 +6378,8 @@ Run when a client switches to a dark theme. Run when a command fails. .It marked\-pane\-changed Run when the marked pane is set or cleared. +.It pane\-bell +Run when a pane receives a bell. .It pane\-command\-finished Run when an OSC 133 command finishes in a pane. The event payload includes @@ -6407,6 +6413,8 @@ Run when a pane changes mode. Run when a pane enters a mode. .It pane\-mode\-exited Run when a pane exits a mode. +.It pane\-moved +Run when a pane is moved to another window. .It pane\-prompt\-closed Run when a prompt in a pane is closed. .It pane\-prompt\-opened @@ -6425,10 +6433,18 @@ Run when a pane title is changed. Run when a session is closed. .It session\-created Run when a new session is created. +.It session\-added\-to\-group +Run when a session is added to a session group. +.It session\-removed\-from\-group +Run when a session is removed from a session group. .It session\-renamed Run when a session is renamed. .It session\-window\-changed Run when a session changes its active window. +.It window\-closed +Run when a window is closed. +.It window\-created +Run when a window is created. .It window\-layout\-changed Run when a window layout is changed. .It window\-linked diff --git a/tmux.h b/tmux.h index ea357d08c..db6d4cf4e 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1398 2026/07/13 10:29:17 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1399 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -3624,6 +3624,8 @@ int window_has_floating_panes(struct window *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *, int); +void window_fire_pane_moved(struct window_pane *, struct window *, + int, struct window *, int); void window_update_focus(struct window *); void window_pane_update_focus(struct window_pane *); void window_redraw_active_switch(struct window *, diff --git a/window.c b/window.c index f6e1a1f23..ca2eca389 100644 --- a/window.c +++ b/window.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window.c,v 1.360 2026/07/10 15:20:06 nicm Exp $ */ +/* $OpenBSD: window.c,v 1.361 2026/07/13 13:01:14 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -130,6 +130,29 @@ window_fire_pane_changed(struct window *w, struct window_pane *wp, events_fire("window-pane-changed", ep); } +void +window_fire_pane_moved(struct window_pane *wp, struct window *old_w, + int old_idx, struct window *new_w, int new_idx) +{ + 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", new_w); + event_payload_set_window(ep, "old_window", old_w); + event_payload_set_window(ep, "new_window", new_w); + if (old_idx != -1) + event_payload_set_int(ep, "old_window_index", old_idx); + if (new_idx != -1) { + event_payload_set_int(ep, "window_index", new_idx); + event_payload_set_int(ep, "new_window_index", new_idx); + } + events_fire("pane-moved", ep); +} + static void window_fire_pane_mode_changed(const char *name, struct window_pane *wp, const char *previous, const char *current, int entered) @@ -490,6 +513,9 @@ window_add_ref(struct window *w, const char *from) void window_remove_ref(struct window *w, const char *from) { + if (w->references == 1) + events_fire_window("window-closed", w); + w->references--; log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references); @@ -972,7 +998,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; - events_fire_window("window-pane-changed", w); + window_fire_pane_changed(w, w->active, wp); window_update_focus(w); } }