Add formats and events for OSC 133 commmands, as well as a -T flag to

set-hook -B to only fire when the format is true.
This commit is contained in:
nicm
2026-07-10 15:20:06 +00:00
parent d29aa12117
commit 512255b9a7
9 changed files with 263 additions and 29 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-set-option.c,v 1.145 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: cmd-set-option.c,v 1.146 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -66,8 +66,8 @@ const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "agpERt:uB:w", 0, 2, cmd_set_option_args_parse },
.usage = "[-agpERuw] [-B name:what:format] " CMD_TARGET_PANE_USAGE " "
.args = { "agpERTt:uB:w", 0, 2, cmd_set_option_args_parse },
.usage = "[-agpERTuw] [-B name:what:format] " CMD_TARGET_PANE_USAGE " "
"[hook] [command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -143,7 +143,7 @@ cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window)
char *expanded = NULL, *newvalue = NULL;
const char *value, *old;
enum monitor_type type;
int id, scope;
int id, scope, flags = 0;
if (args_count(args) > 1) {
cmdq_error(item, "too many arguments");
@@ -204,7 +204,9 @@ cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window)
oo != global_s_options &&
oo != global_w_options)
s = target->s;
hooks_monitor_add(item, oo, name, type, id, format, &fs, s);
if (args_has(args, 'T'))
flags |= MONITOR_NOTIFY_TRUE;
hooks_monitor_add(item, oo, name, type, id, format, flags, &fs, s);
out:
free(newvalue);

123
format.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: format.c,v 1.402 2026/07/09 06:33:19 nicm Exp $ */
/* $OpenBSD: format.c,v 1.403 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2173,6 +2173,106 @@ format_cb_pane_dead_time(struct format_tree *ft)
return (NULL);
}
/* Callback for pane_last_output_time. */
static void *
format_cb_pane_last_output_time(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
static struct timeval tv;
if (wp != NULL && wp->last_output_time != 0) {
tv.tv_sec = wp->last_output_time;
tv.tv_usec = 0;
return (&tv);
}
return (NULL);
}
/* Callback for pane_last_prompt_time. */
static void *
format_cb_pane_last_prompt_time(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
static struct timeval tv;
if (wp != NULL && wp->last_prompt_time != 0) {
tv.tv_sec = wp->last_prompt_time;
tv.tv_usec = 0;
return (&tv);
}
return (NULL);
}
/* Callback for pane_command_start_time. */
static void *
format_cb_pane_command_start_time(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
static struct timeval tv;
if (wp != NULL && wp->cmd_start_time != 0) {
tv.tv_sec = wp->cmd_start_time;
tv.tv_usec = 0;
return (&tv);
}
return (NULL);
}
/* Callback for pane_command_end_time. */
static void *
format_cb_pane_command_end_time(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
static struct timeval tv;
if (wp != NULL && wp->cmd_end_time != 0) {
tv.tv_sec = wp->cmd_end_time;
tv.tv_usec = 0;
return (&tv);
}
return (NULL);
}
/* Callback for pane_command_running. */
static void *
format_cb_pane_command_running(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
if (wp != NULL)
return (format_printf("%d", !!(wp->flags & PANE_CMDRUNNING)));
return (NULL);
}
/* Callback for pane_command_duration. */
static void *
format_cb_pane_command_duration(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
time_t end;
if (wp == NULL || wp->cmd_start_time == 0)
return (NULL);
if (wp->flags & PANE_CMDRUNNING)
end = time(NULL);
else
end = wp->cmd_end_time;
if (end < wp->cmd_start_time)
end = wp->cmd_start_time;
return (format_printf("%lld", (long long)(end - wp->cmd_start_time)));
}
/* Callback for pane_command_status. */
static void *
format_cb_pane_command_status(struct format_tree *ft)
{
struct window_pane *wp = ft->wp;
if (wp != NULL && wp->cmd_status != -1)
return (format_printf("%d", wp->cmd_status));
return (NULL);
}
/* Callback for pane_format. */
static void *
format_cb_pane_format(struct format_tree *ft)
@@ -3485,6 +3585,21 @@ static const struct format_table_entry format_table[] = {
{ "pane_bottom", FORMAT_TABLE_STRING,
format_cb_pane_bottom
},
{ "pane_command_duration", FORMAT_TABLE_STRING,
format_cb_pane_command_duration
},
{ "pane_command_end_time", FORMAT_TABLE_TIME,
format_cb_pane_command_end_time
},
{ "pane_command_running", FORMAT_TABLE_STRING,
format_cb_pane_command_running
},
{ "pane_command_start_time", FORMAT_TABLE_TIME,
format_cb_pane_command_start_time
},
{ "pane_command_status", FORMAT_TABLE_STRING,
format_cb_pane_command_status
},
{ "pane_current_command", FORMAT_TABLE_STRING,
format_cb_current_command
},
@@ -3536,6 +3651,12 @@ static const struct format_table_entry format_table[] = {
{ "pane_last", FORMAT_TABLE_STRING,
format_cb_pane_last
},
{ "pane_last_output_time", FORMAT_TABLE_TIME,
format_cb_pane_last_output_time
},
{ "pane_last_prompt_time", FORMAT_TABLE_TIME,
format_cb_pane_last_prompt_time
},
{ "pane_left", FORMAT_TABLE_STRING,
format_cb_pane_left
},

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: hooks.c,v 1.12 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: hooks.c,v 1.13 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -407,7 +407,7 @@ hooks_monitor_cb(struct monitor_change *change, void *data)
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)
int flags, struct cmd_find_state *fs, struct session *s)
{
struct options_entry *o;
struct hook_monitor *hm;
@@ -426,7 +426,7 @@ hooks_monitor_add(__unused struct cmdq_item *item, struct options *oo,
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);
monitor_add(hm->set, name, type, id, format, flags);
}
/* Convert a hook monitor to its value. */

87
input.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: input.c,v 1.265 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: input.c,v 1.266 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1034,6 +1034,8 @@ input_parse_pane(struct window_pane *wp)
size_t new_size;
new_data = window_pane_get_new_data(wp, &wp->offset, &new_size);
if (new_size != 0)
wp->last_output_time = time(NULL);
input_parse_buffer(wp, new_data, new_size);
window_pane_update_used_data(wp, &wp->offset, new_size);
}
@@ -3126,7 +3128,8 @@ input_osc_12(struct input_ctx *ictx, const char *p)
c = ictx->ctx.s->ccolour;
if (c == -1)
c = ictx->ctx.s->default_ccolour;
input_osc_colour_reply(ictx, 1, 12, 0, c, ictx->input_end);
input_osc_colour_reply(ictx, 1, 12, 0, c,
ictx->input_end);
}
return;
}
@@ -3146,24 +3149,92 @@ input_osc_112(struct input_ctx *ictx, const char *p)
screen_set_cursor_colour(ictx->ctx.s, -1);
}
/* Fire an OSC 133 command event. */
static void
input_fire_command_event(struct window_pane *wp, const char *name)
{
struct event_payload *ep;
struct cmd_find_state fs;
time_t tstart = wp->cmd_start_time, end;
time_t tend = wp->cmd_end_time;
ep = event_payload_create();
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
if (fs.s != NULL)
event_payload_set_session(ep, "session", fs.s);
if (fs.wl != NULL)
event_payload_set_int(ep, "window_index", fs.wl->idx);
event_payload_set_window(ep, "window", wp->window);
event_payload_set_pane(ep, "pane", wp);
if (wp->cmd_status != -1)
event_payload_set_int(ep, "command_status", wp->cmd_status);
if (tstart != 0)
event_payload_set_time(ep, "command_start_time", tstart);
if (tend != 0)
event_payload_set_time(ep, "command_end_time", tend);
if (tstart != 0) {
if (wp->flags & PANE_CMDRUNNING)
end = time(NULL);
else
end = tend;
if (end < tstart)
end = tstart;
end -= tstart;
event_payload_set_uint(ep, "command_duration", end);
}
events_fire(name, ep);
}
/* Handle the OSC 133 sequence. */
static void
input_osc_133(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
struct grid *gd = ictx->ctx.s->grid;
u_int line = ictx->ctx.s->cy + gd->hsize;
struct grid_line *gl;
struct grid_line *gl = NULL;
const char *errstr;
int status;
if (line > gd->hsize + gd->sy - 1)
return;
gl = grid_get_line(gd, line);
if (line <= gd->hsize + gd->sy - 1)
gl = grid_get_line(gd, line);
switch (*p) {
case 'A':
gl->flags |= GRID_LINE_START_PROMPT;
if (gl != NULL)
gl->flags |= GRID_LINE_START_PROMPT;
if (wp != NULL) {
wp->last_prompt_time = time(NULL);
events_fire_pane("pane-shell-prompt", wp);
}
break;
case 'C':
gl->flags |= GRID_LINE_START_OUTPUT;
if (gl != NULL)
gl->flags |= GRID_LINE_START_OUTPUT;
if (wp != NULL) {
wp->cmd_start_time = time(NULL);
wp->cmd_end_time = 0;
wp->flags |= PANE_CMDRUNNING;
wp->cmd_status = -1;
input_fire_command_event(wp, "pane-command-started");
}
break;
case 'D':
if (wp != NULL) {
wp->cmd_end_time = time(NULL);
wp->flags &= ~PANE_CMDRUNNING;
wp->cmd_status = -1;
if (p[1] == ';' && p[2] != '\0') {
status = strtonum(p + 2, 0, INT_MAX, &errstr);
if (errstr == NULL)
wp->cmd_status = status;
}
input_fire_command_event(wp, "pane-command-finished");
}
break;
}
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: monitor.c,v 1.5 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: monitor.c,v 1.6 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -53,7 +53,7 @@ struct monitor_item {
enum monitor_type type;
u_int id;
u_int flags;
int flags;
char *last;
struct monitor_panes panes;
@@ -198,7 +198,9 @@ monitor_check_value(struct monitor_set *ms, struct monitor_item *me,
{
if (*last == NULL) {
*last = value;
if (me->flags & MONITOR_NOTIFY_INITIAL)
if ((me->flags & MONITOR_NOTIFY_INITIAL) &&
((~me->flags & MONITOR_NOTIFY_TRUE) ||
format_true(value)))
monitor_report(ms, me, s, wl, wp, value, NULL);
return;
}
@@ -208,7 +210,8 @@ monitor_check_value(struct monitor_set *ms, struct monitor_item *me,
return;
}
monitor_report(ms, me, s, wl, wp, value, *last);
if ((~me->flags & MONITOR_NOTIFY_TRUE) || format_true(value))
monitor_report(ms, me, s, wl, wp, value, *last);
free(*last);
*last = value;
}
@@ -621,7 +624,7 @@ fail:
/* Add a subscription. */
void
monitor_add(struct monitor_set *ms, const char *name, enum monitor_type type,
int id, const char *format, u_int flags)
int id, const char *format, int flags)
{
struct monitor_item *me, find = { .name = (char *)name };
struct timeval tv = { .tv_sec = 1 };

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: options-table.c,v 1.232 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: options-table.c,v 1.233 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1934,6 +1934,8 @@ 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-command-finished", ""),
OPTIONS_TABLE_PANE_HOOK("pane-command-started", ""),
OPTIONS_TABLE_PANE_HOOK("pane-created", ""),
OPTIONS_TABLE_PANE_HOOK("pane-died", ""),
OPTIONS_TABLE_PANE_HOOK("pane-exited", ""),
@@ -1946,6 +1948,7 @@ const struct options_table_entry options_table[] = {
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-shell-prompt", ""),
OPTIONS_TABLE_PANE_HOOK("pane-title-changed", ""),
OPTIONS_TABLE_HOOK("session-closed", ""),
OPTIONS_TABLE_HOOK("session-created", ""),

29
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1131 2026/07/10 13:38:45 nicm Exp $
.\" $OpenBSD: tmux.1,v 1.1132 2026/07/10 15:20:06 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
@@ -6359,6 +6359,17 @@ 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\-command\-finished
Run when an OSC 133 command finishes in a pane.
The event payload includes
.Ql hook_command_start_time ,
.Ql hook_command_end_time ,
.Ql hook_command_duration
and
.Ql hook_command_status
if present.
.It pane\-command\-started
Run when an OSC 133 command starts in a pane.
.It pane\-created
Run when a pane is created or respawned.
.It pane\-died
@@ -6391,6 +6402,8 @@ Run when a pane is resized.
Run when the terminal clipboard is set using the
.Xr xterm 1
escape sequence.
.It pane\-shell\-prompt
Run when an OSC 133 shell prompt starts in a pane.
.It pane\-title\-changed
Run when a pane title is changed.
.It session\-closed
@@ -6425,7 +6438,7 @@ 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 agpERuw
.Op Fl agpERTuw
.Op Fl B Ar name:what:format
.Op Fl t Ar target\-pane
.Ar hook\-name
@@ -6456,6 +6469,11 @@ is the hook to run,
selects the session, pane, all panes, window, or all windows, and
.Ar format
is expanded once a second.
With
.Fl T ,
the hook is run only when
.Ar format
is true.
For monitor hooks,
.Ar name
must begin with
@@ -7191,6 +7209,11 @@ The following variables are available, where appropriate:
.It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window"
.It Li "pane_bg" Ta "" Ta "Pane background colour"
.It Li "pane_bottom" Ta "" Ta "Bottom of pane"
.It Li "pane_command_duration" Ta "" Ta "Current or most recent OSC 133 command duration in seconds"
.It Li "pane_command_end_time" Ta "" Ta "Time most recent OSC 133 command ended"
.It Li "pane_command_running" Ta "" Ta "1 if an OSC 133 command is running"
.It Li "pane_command_start_time" Ta "" Ta "Time current or most recent OSC 133 command started"
.It Li "pane_command_status" Ta "" Ta "Exit status from most recent OSC 133 command"
.It Li "pane_current_command" Ta "" Ta "Current command if available"
.It Li "pane_current_path" Ta "" Ta "Current path if available"
.It Li "pane_dead" Ta "" Ta "1 if pane is dead"
@@ -7208,6 +7231,8 @@ The following variables are available, where appropriate:
.It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled"
.It Li "pane_key_mode" Ta "" Ta "Extended key reporting mode in this pane"
.It Li "pane_last" Ta "" Ta "1 if last pane"
.It Li "pane_last_output_time" Ta "" Ta "Time pane last produced output"
.It Li "pane_last_prompt_time" Ta "" Ta "Time most recent OSC 133 prompt began"
.It Li "pane_left" Ta "" Ta "Left of pane"
.It Li "pane_marked" Ta "" Ta "1 if this is the marked pane"
.It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set"

14
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1394 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: tmux.h,v 1.1395 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1267,6 +1267,7 @@ struct window_pane {
#define PANE_UNSEENCHANGES 0x4000
#define PANE_REDRAWSCROLLBAR 0x8000
#define PANE_DESTROYED 0x10000
#define PANE_CMDRUNNING 0x20000
bitstr_t *sync_dirty;
u_int sync_dirty_size;
@@ -1289,6 +1290,12 @@ struct window_pane {
struct cmdq_item *wait_item; /* new-pane -W: waiting for pane exit */
struct spawn_editor_state *editor;
time_t last_output_time;
time_t last_prompt_time;
time_t cmd_start_time;
time_t cmd_end_time;
int cmd_status;
int fd;
struct bufferevent *event;
@@ -2293,6 +2300,7 @@ enum monitor_type {
MONITOR_ALL_WINDOWS
};
#define MONITOR_NOTIFY_INITIAL 0x1
#define MONITOR_NOTIFY_TRUE 0x2
struct monitor_change {
const char *name;
const char *value;
@@ -2699,7 +2707,7 @@ 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 *);
int, struct cmd_find_state *, struct session *);
void hooks_monitor_remove(struct options *, const char *);
void hooks_monitor_free(void *);
char *hooks_monitor_to_string(struct options_entry *);
@@ -3901,7 +3909,7 @@ void monitor_destroy(struct monitor_set *);
int monitor_parse(const char *, char **, enum monitor_type *, int *,
char **);
void monitor_add(struct monitor_set *, const char *, enum monitor_type, int,
const char *, u_int);
const char *, int);
void monitor_remove(struct monitor_set *, const char *);
/* control.c */

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window.c,v 1.359 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD: window.c,v 1.360 2026/07/10 15:20:06 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1177,6 +1177,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp->window = w;
wp->options = options_create(w->options);
wp->flags = PANE_STYLECHANGED;
wp->cmd_status = -1;
wp->id = next_window_pane_id++;
RB_INSERT(window_pane_tree, &all_window_panes, wp);