diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c index 3c09d1c4..80be0165 100644 --- a/cmd-capture-pane.c +++ b/cmd-capture-pane.c @@ -31,8 +31,8 @@ enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmd_ctx *); const struct cmd_entry cmd_capture_pane_entry = { "capture-pane", "capturep", - "b:E:pS:t:", 0, 0, - "[-p] [-b buffer-index] [-E end-line] [-S start-line]" + "eb:E:pS:t:", 0, 0, + "[-ep] [-b buffer-index] [-E end-line] [-S start-line]" CMD_TARGET_PANE_USAGE, 0, NULL, @@ -46,12 +46,13 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_ctx *ctx) struct args *args = self->args; struct client *c = ctx->cmdclient; struct window_pane *wp; - char *buf, *line, *cause; + char *buf, *line, *cause; struct screen *s; struct grid *gd; - int buffer, n; + int buffer, n, with_codes; u_int i, limit, top, bottom, tmp; - size_t len, linelen; + size_t len, linelen; + struct grid_cell *gc; if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); @@ -89,16 +90,19 @@ cmd_capture_pane_exec(struct cmd *self, struct cmd_ctx *ctx) top = tmp; } + gc = NULL; + with_codes = args_has(args, 'e'); for (i = top; i <= bottom; i++) { - line = grid_string_cells(s->grid, 0, i, screen_size_x(s)); - linelen = strlen(line); + line = grid_string_cells(s->grid, 0, i, screen_size_x(s), + &gc, with_codes); + linelen = strlen(line); - buf = xrealloc(buf, 1, len + linelen + 1); - memcpy(buf + len, line, linelen); - len += linelen; - buf[len++] = '\n'; + buf = xrealloc(buf, 1, len + linelen + 1); + memcpy(buf + len, line, linelen); + len += linelen; + buf[len++] = '\n'; - free(line); + free(line); } if (args_has(args, 'p')) { diff --git a/grid-view.c b/grid-view.c index b4355413..baaddbe0 100644 --- a/grid-view.c +++ b/grid-view.c @@ -234,5 +234,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) px = grid_view_x(gd, px); py = grid_view_y(gd, py); - return (grid_string_cells(gd, px, py, nx)); + return (grid_string_cells(gd, px, py, nx, NULL, 0)); } diff --git a/grid.c b/grid.c index 3bf02aeb..3c32f224 100644 --- a/grid.c +++ b/grid.c @@ -74,6 +74,10 @@ void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, u_int); void grid_reflow_move(struct grid *, u_int *, struct grid_line *); +size_t grid_string_cells_fg(const struct grid_cell *, int *); +size_t grid_string_cells_bg(const struct grid_cell *, int *); +void grid_string_cells_code(const struct grid_cell *, + const struct grid_cell *, char *, size_t); /* Create a new grid. */ struct grid * @@ -392,18 +396,186 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) } } +/* Get ANSI foreground sequence. */ +size_t +grid_string_cells_fg(const struct grid_cell *gc, int *values) +{ + size_t n; + + n = 0; + if (gc->flags & GRID_FLAG_FG256) { + values[n++] = 38; + values[n++] = 5; + values[n++] = gc->fg; + } else { + switch (gc->fg) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + values[n++] = gc->fg + 30; + break; + case 8: + values[n++] = 39; + break; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + values[n++] = gc->fg; + break; + } + } + return (n); +} + +/* Get ANSI background sequence. */ +size_t +grid_string_cells_bg(const struct grid_cell *gc, int *values) +{ + size_t n; + + n = 0; + if (gc->flags & GRID_FLAG_BG256) { + values[n++] = 48; + values[n++] = 5; + values[n++] = gc->bg; + } else { + switch (gc->bg) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + values[n++] = gc->bg + 40; + break; + case 8: + values[n++] = 49; + break; + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + values[n++] = gc->bg - 10; + break; + } + } + return (n); +} + +/* + * Returns ANSI code to set particular attributes (colour, bold and so on) + * given a current state. The output buffer must be able to hold at least 57 + * bytes. + */ +void +grid_string_cells_code(const struct grid_cell *lastgc, + const struct grid_cell *gc, char *buf, size_t len) +{ + int oldc[16], newc[16], s[32]; + size_t noldc, nnewc, n, i; + u_int attr = gc->attr; + u_int lastattr = lastgc->attr; + char tmp[64]; + + struct { + u_int mask; + u_int code; + } attrs[] = { + { GRID_ATTR_BRIGHT, 1 }, + { GRID_ATTR_DIM, 2 }, + { GRID_ATTR_ITALICS, 3 }, + { GRID_ATTR_UNDERSCORE, 4 }, + { GRID_ATTR_BLINK, 5 }, + { GRID_ATTR_REVERSE, 7 }, + { GRID_ATTR_HIDDEN, 8 } + }; + n = 0; + + /* If any attribute is removed, begin with 0. */ + for (i = 0; i < nitems(attrs); i++) { + if (!(attr & attrs[i].mask) && (lastattr & attrs[i].mask)) { + s[n++] = 0; + break; + } + } + /* For each attribute that is newly set, add its code. */ + for (i = 0; i < nitems(attrs); i++) { + if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask)) + s[n++] = attrs[i].code; + } + + /* If the foreground c changed, append its parameters. */ + nnewc = grid_string_cells_fg(gc, newc); + noldc = grid_string_cells_fg(lastgc, oldc); + if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) { + for (i = 0; i < nnewc; i++) + s[n++] = newc[i]; + } + + /* If the background c changed, append its parameters. */ + nnewc = grid_string_cells_bg(gc, newc); + noldc = grid_string_cells_bg(lastgc, oldc); + if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) { + for (i = 0; i < nnewc; i++) + s[n++] = newc[i]; + } + + /* If there are any parameters, append an SGR code. */ + *buf = '\0'; + if (n > 0) { + strlcat(buf, "\033[", len); + for (i = 0; i < n; i++) { + if (i + 1 < n) + xsnprintf(tmp, sizeof tmp, "%d;", s[i]); + else + xsnprintf(tmp, sizeof tmp, "%d", s[i]); + strlcat(buf, tmp, len); + } + strlcat(buf, "m", len); + } + + /* Append shift in/shift out if needed. */ + if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) + strlcat(buf, "\016", len); /* SO */ + if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) + strlcat(buf, "\017", len); /* SI */ +} + /* Convert cells into a string. */ char * -grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) +grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, + struct grid_cell **lastgc, int with_codes) { const struct grid_cell *gc; + static struct grid_cell lastgc1; struct utf8_data ud; - char *buf; - size_t len, off; + char *buf, code[128]; + size_t len, off, codelen; u_int xx; GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + if (*lastgc == NULL) { + memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); + *lastgc = &lastgc1; + } + len = 128; buf = xmalloc(len); off = 0; @@ -414,11 +586,22 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) continue; grid_cell_get(gc, &ud); - while (len < off + ud.size + 1) { + if (with_codes) { + grid_string_cells_code(*lastgc, gc, code, sizeof code); + codelen = strlen(code); + memcpy(*lastgc, gc, sizeof *gc); + } else + codelen = 0; + + while (len < off + ud.size + codelen + 1) { buf = xrealloc(buf, 2, len); len *= 2; } + if (codelen != 0) { + memcpy(buf + off, code, codelen); + off += codelen; + } memcpy(buf + off, ud.data, ud.size); off += ud.size; } diff --git a/tmux.1 b/tmux.1 index 98561f93..c1380ffd 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1040,6 +1040,7 @@ By default, it uses the format but a different format may be specified with .Fl F . .It Xo Ic capture-pane +.Op Fl e .Op Fl p .Op Fl b Ar buffer-index .Op Fl E Ar end-line @@ -1053,6 +1054,9 @@ If is given, the output goes to stdout, otherwise to the buffer specified with .Fl b or a new buffer if omitted. +If +.Fl e +is given, the output includes escape sequences for text and background attributes. .Pp .Fl S and diff --git a/tmux.h b/tmux.h index df47cdb3..8b3d4e14 100644 --- a/tmux.h +++ b/tmux.h @@ -1973,7 +1973,8 @@ void grid_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_clear_lines(struct grid *, u_int, u_int); void grid_move_lines(struct grid *, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int); -char *grid_string_cells(struct grid *, u_int, u_int, u_int); +char *grid_string_cells(struct grid *, u_int, u_int, u_int, + struct grid_cell **, int); void grid_duplicate_lines( struct grid *, u_int, struct grid *, u_int, u_int); u_int grid_reflow(struct grid *, struct grid *, u_int);