diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index d83590f90..baf6fc62d 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd-capture-pane.c,v 1.67 2026/07/02 10:34:14 nicm Exp $ */ +/* $OpenBSD: cmd-capture-pane.c,v 1.68 2026/07/20 11:16:33 nicm Exp $ */ /* * Copyright (c) 2009 Jonathan Alvarado @@ -135,6 +135,7 @@ cmd_capture_pane_grid(struct window_pane *wp, size_t *len) struct screen *s = &wp->base; struct grid *gd = s->grid; struct grid_line *gl; + struct osc133_data *od; char *buf = xstrdup(""), *line; char p[11]; u_int yy, xx, total = gd->hsize + gd->sy; @@ -150,9 +151,20 @@ cmd_capture_pane_grid(struct window_pane *wp, size_t *len) snprintf(p, sizeof p, "-"); else snprintf(p, sizeof p, "%u", yy - gd->hsize); - xasprintf(&line, "\tL %u (%s) flags=%s[%x] %u/%u\n", yy, - p, grid_line_flags_string(gl->flags), gl->flags, - gl->cellused, gl->cellsize); + od = &gl->osc133_data; + if (gl->flags & GRID_LINE_OSC133_FLAGS) { + xasprintf(&line, "\tL %u (%s) flags=%s[%x] " + "%u/%u osc133=%u,%u,%u,%u,%u\n", yy, p, + grid_line_flags_string(gl->flags), gl->flags, + gl->cellused, gl->cellsize, od->prompt_col, + od->cmd_col, od->out_start_col, + od->out_end_col, od->exit_status); + } else { + xasprintf(&line, "\tL %u (%s) flags=%s[%x] " + "%u/%u\n", yy, p, + grid_line_flags_string(gl->flags), gl->flags, + gl->cellused, gl->cellsize); + } buf = cmd_capture_pane_append(buf, len, line, strlen(line)); free(line); diff --git a/grid.c b/grid.c index 878c01bab..fa0c94ffe 100644 --- a/grid.c +++ b/grid.c @@ -1,4 +1,4 @@ -/* $OpenBSD: grid.c,v 1.153 2026/07/02 08:51:05 nicm Exp $ */ +/* $OpenBSD: grid.c,v 1.154 2026/07/20 11:16:33 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -194,6 +194,25 @@ grid_get_line(struct grid *gd, u_int line) return (&gd->linedata[line]); } +/* Get line time. */ +time_t +grid_line_time(const struct grid_line *gl) +{ + if (gl->time == 0) + return (0); + return (start_time.tv_sec + gl->time - 1); +} + +/* Set line time. */ +static void +grid_line_set_time(struct grid_line *gl) +{ + if (current_time == 0) + gl->time = 0; + else + gl->time = current_time - start_time.tv_sec + 1; +} + /* Adjust number of lines. */ void grid_adjust_lines(struct grid *gd, u_int lines) @@ -435,7 +454,7 @@ grid_scroll_history(struct grid *gd, u_int bg) gd->hscrolled++; grid_compact_line(&gd->linedata[gd->hsize]); - gd->linedata[gd->hsize].time = current_time; + grid_line_set_time(&gd->linedata[gd->hsize]); gd->hsize++; gd->scroll_added++; } @@ -477,7 +496,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg) /* Move the line into the history. */ memcpy(gl_history, gl_upper, sizeof *gl_history); - gl_history->time = current_time; + grid_line_set_time(gl_history); /* Then move the region up and clear the bottom line. */ memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); @@ -1650,8 +1669,14 @@ grid_line_flags_string(int flags) strlcat(s, "DEAD,", sizeof s); if (flags & GRID_LINE_START_PROMPT) strlcat(s, "START_PROMPT,", sizeof s); + if (flags & GRID_LINE_SECOND_PROMPT) + strlcat(s, "SECOND_PROMPT,", sizeof s); + if (flags & GRID_LINE_START_COMMAND) + strlcat(s, "START_COMMAND,", sizeof s); if (flags & GRID_LINE_START_OUTPUT) strlcat(s, "START_OUTPUT,", sizeof s); + if (flags & GRID_LINE_END_OUTPUT) + strlcat(s, "END_OUTPUT,", sizeof s); if (flags & GRID_LINE_HYPERLINK) strlcat(s, "HYPERLINK,", sizeof s); if (*s == '\0') diff --git a/input.c b/input.c index 66f65e01c..ecf07dd61 100644 --- a/input.c +++ b/input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: input.c,v 1.268 2026/07/13 15:03:03 nicm Exp $ */ +/* $OpenBSD: input.c,v 1.269 2026/07/20 11:16:33 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -174,8 +174,6 @@ 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 *); @@ -212,21 +210,6 @@ 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; @@ -809,6 +792,22 @@ input_stop_utf8(struct input_ctx *ictx) ictx->utf8started = 0; } +/* Fire a title changed event. */ +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); +} + /* * Timer - if this expires then have been waiting for a terminator for too * long, so reset to ground. @@ -3155,6 +3154,35 @@ input_osc_112(struct input_ctx *ictx, const char *p) screen_set_cursor_colour(ictx->ctx.s, -1); } +/* Parse the OSC 133 D exit status. */ +static int +input_osc_133_exit_status(const char *p) +{ + const char *end; + char *copy; + const char *errstr; + long long status; + + if (p[1] != ';' || p[2] == '\0' || strchr(p + 2, '=') == p + 2) + return (0); + end = strchr(p + 2, ';'); + if (end == p + 2) + return (0); + if (end == NULL) + copy = xstrdup(p + 2); + else + copy = xstrndup(p + 2, end - (p + 2)); + if (strchr(copy, '=') != NULL) { + free(copy); + return (0); + } + status = strtonum(copy, 0, 255, &errstr); + free(copy); + if (errstr != NULL) + return (255); + return (status); +} + /* Fire an OSC 133 command event. */ static void input_fire_command_event(struct window_pane *wp, const char *name) @@ -3200,27 +3228,51 @@ 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 screen *s = ictx->ctx.s; + struct grid *gd = s->grid; + u_int line = s->cy + gd->hsize; struct grid_line *gl = NULL; - const char *errstr; + const char *cp; int status; - if (line <= gd->hsize + gd->sy - 1) + if (line < gd->hsize + gd->sy) gl = grid_get_line(gd, line); switch (*p) { case 'A': - if (gl != NULL) + case 'N': + if (gl != NULL) { + memset(&gl->osc133_data, 0, sizeof gl->osc133_data); + gl->osc133_data.prompt_col = s->cx; gl->flags |= GRID_LINE_START_PROMPT; + } if (wp != NULL) { wp->last_prompt_time = time(NULL); events_fire_pane("pane-shell-prompt", wp); } break; + case 'P': + if (gl != NULL) { + cp = strstr(p, ";k=s"); + if (cp != NULL && (cp[4] == ';' || cp[4] == '\0')) + gl->flags |= GRID_LINE_SECOND_PROMPT; + else + gl->flags |= GRID_LINE_START_PROMPT; + gl->osc133_data.prompt_col = s->cx; + } + break; + case 'B': + case 'I': + if (gl != NULL) { + gl->flags |= GRID_LINE_START_COMMAND; + gl->osc133_data.cmd_col = s->cx; + } + break; case 'C': - if (gl != NULL) + if (gl != NULL) { gl->flags |= GRID_LINE_START_OUTPUT; + gl->osc133_data.out_start_col = s->cx; + } if (wp != NULL) { wp->cmd_start_time = time(NULL); wp->cmd_end_time = 0; @@ -3230,17 +3282,18 @@ input_osc_133(struct input_ctx *ictx, const char *p) } break; case 'D': + status = input_osc_133_exit_status(p); 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; - } + wp->cmd_status = status; input_fire_command_event(wp, "pane-command-finished"); } + if (gl != NULL) { + gl->flags |= GRID_LINE_END_OUTPUT; + gl->osc133_data.out_end_col = s->cx; + gl->osc133_data.exit_status = status; + } break; } } diff --git a/screen-write.c b/screen-write.c index a7a16149c..1ba5bed70 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1,4 +1,4 @@ -/* $OpenBSD: screen-write.c,v 1.283 2026/07/09 07:35:05 nicm Exp $ */ +/* $OpenBSD: screen-write.c,v 1.284 2026/07/20 11:16:33 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1576,16 +1576,19 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) struct grid_line *gl; u_int sx = screen_size_x(s); struct screen_write_citem *ci = ctx->item; + struct osc133_data od; u_int flags; gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) return; - flags = gl->flags & (GRID_LINE_START_PROMPT|GRID_LINE_START_OUTPUT); + flags = gl->flags & GRID_LINE_OSC133_FLAGS; + memcpy(&od, &gl->osc133_data, sizeof od); grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); gl = grid_get_line(s->grid, s->grid->hsize + s->cy); gl->flags |= flags; + memcpy(&gl->osc133_data, &od, sizeof gl->osc133_data); screen_write_collect_clear(ctx, s->cy, 1); ci->x = 0; diff --git a/tmux.h b/tmux.h index a9739285c..597d169b7 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1410 2026/07/19 19:53:11 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1411 2026/07/20 11:16:33 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -800,8 +800,19 @@ struct colour_palette { #define GRID_LINE_EXTENDED 0x2 #define GRID_LINE_DEAD 0x4 #define GRID_LINE_START_PROMPT 0x8 -#define GRID_LINE_START_OUTPUT 0x10 -#define GRID_LINE_HYPERLINK 0x20 +#define GRID_LINE_SECOND_PROMPT 0x10 +#define GRID_LINE_START_COMMAND 0x20 +#define GRID_LINE_START_OUTPUT 0x40 +#define GRID_LINE_END_OUTPUT 0x80 +#define GRID_LINE_HYPERLINK 0x100 + +/* All OSC 133 flags. */ +#define GRID_LINE_OSC133_FLAGS \ + (GRID_LINE_START_PROMPT| \ + GRID_LINE_SECOND_PROMPT| \ + GRID_LINE_START_COMMAND| \ + GRID_LINE_START_OUTPUT| \ + GRID_LINE_END_OUTPUT) /* Grid string flags. */ #define GRID_STRING_WITH_SEQUENCES 0x1 @@ -870,17 +881,27 @@ struct grid_cell_entry { u_char flags; } __packed; +/* OSC 133 data for a grid line. */ +struct osc133_data { + u_short prompt_col; + u_short cmd_col; + u_short out_start_col; + u_short out_end_col; + u_char exit_status; +}; + /* Grid line. */ struct grid_line { struct grid_cell_entry *celldata; - u_int cellused; - u_int cellsize; - struct grid_extd_entry *extddata; + + u_short cellused; + u_short cellsize; u_int extdsize; - int flags; - time_t time; + u_int time; + struct osc133_data osc133_data; + u_short flags; }; /* Entire grid of cells. */ @@ -3405,6 +3426,7 @@ int grid_compare(struct grid *, struct grid *); const char *grid_line_flags_string(int); const char *grid_cell_flags_string(int); const char *grid_cell_attr_string(int); +time_t grid_line_time(const struct grid_line *); void grid_collect_history(struct grid *, int); void grid_remove_history(struct grid *, u_int ); void grid_scroll_history(struct grid *, u_int); diff --git a/window-copy.c b/window-copy.c index a6909a8d4..4a8009b14 100644 --- a/window-copy.c +++ b/window-copy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-copy.c,v 1.421 2026/07/14 17:17:18 nicm Exp $ */ +/* $OpenBSD: window-copy.c,v 1.422 2026/07/20 11:16:33 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1107,9 +1107,11 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) u_int hsize = screen_hsize(data->backing); u_int position, limit; struct grid_line *gl; + time_t t; gl = grid_get_line(data->backing->grid, hsize - data->oy); - format_add(ft, "top_line_time", "%llu", (unsigned long long)gl->time); + t = grid_line_time(gl); + format_add(ft, "top_line_time", "%llu", (unsigned long long)t); format_add(ft, "scroll_position", "%d", data->oy); if (window_copy_line_number_is_absolute(wme)) {