From 0ace779cde0bc6c68a2cbc66986162e7383f622c Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 15 Nov 2016 14:02:32 +0000 Subject: [PATCH] Initial attempt to make use of left and right margins if the terminal supports them (that is, if it advertises itself as a VT420 - probably just xterm). These are the vertical equivalent of the scroll region and allow much faster scrolling of panes that do not take up the full width of the terminal. --- server-client.c | 1 + tmux.h | 14 ++++++++ tty-keys.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ tty.c | 89 ++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 187 insertions(+), 11 deletions(-) diff --git a/server-client.c b/server-client.c index b92bada5..29f3e08b 100644 --- a/server-client.c +++ b/server-client.c @@ -1004,6 +1004,7 @@ server_client_reset_state(struct client *c) return; tty_region(&c->tty, 0, c->tty.sy - 1); + tty_margin(&c->tty, 0, c->tty.sx - 1); status = options_get_number(oo, "status"); if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) diff --git a/tmux.h b/tmux.h index 50527c10..e2d49962 100644 --- a/tmux.h +++ b/tmux.h @@ -1098,6 +1098,9 @@ struct tty { u_int rlower; u_int rupper; + u_int rleft; + u_int rright; + char *termname; struct tty_term *term; @@ -1118,6 +1121,15 @@ struct tty { int flags; int term_flags; + enum { + TTY_VT100, + TTY_VT101, + TTY_VT102, + TTY_VT220, + TTY_VT320, + TTY_VT420, + TTY_UNKNOWN + } term_type; struct mouse_event mouse; int mouse_drag_flag; @@ -1653,6 +1665,7 @@ void tty_attributes(struct tty *, const struct grid_cell *, const struct window_pane *); void tty_reset(struct tty *); void tty_region(struct tty *, u_int, u_int); +void tty_margin(struct tty *, u_int, u_int); void tty_cursor(struct tty *, u_int, u_int); void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode1(struct tty *, enum tty_code_code, int); @@ -1677,6 +1690,7 @@ void tty_draw_line(struct tty *, const struct window_pane *, struct screen *, int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); +void tty_set_type(struct tty *, int); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); diff --git a/tty-keys.c b/tty-keys.c index 174b008e..802bbbdc 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -44,6 +44,8 @@ static int tty_keys_next1(struct tty *, const char *, size_t, key_code *, size_t *, int); static void tty_keys_callback(int, short, void *); static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); +static int tty_keys_device_attributes(struct tty *, const char *, size_t, + size_t *); /* Default raw keys. */ struct tty_default_key_raw { @@ -537,6 +539,17 @@ tty_keys_next(struct tty *tty) return (0); log_debug("keys are %zu (%.*s)", len, (int)len, buf); + /* Is this a device attributes response? */ + switch (tty_keys_device_attributes(tty, buf, len, &size)) { + case 0: /* yes */ + key = KEYC_UNKNOWN; + goto complete_key; + case -1: /* no, or not valid */ + break; + case 1: /* partial */ + goto partial_key; + } + /* Is this a mouse key press? */ switch (tty_keys_mouse(tty, buf, len, &size)) { case 0: /* yes */ @@ -815,3 +828,84 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (0); } + +/* + * Handle device attributes input. Returns 0 for success, -1 for failure, 1 for + * partial. + */ +static int +tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, + size_t *size) +{ + u_int i, a, b; + char tmp[64], *endptr; + const char *s; + + *size = 0; + + /* First three bytes are always \033[?. */ + if (buf[0] != '\033') + return (-1); + if (len == 1) + return (1); + if (buf[1] != '[') + return (-1); + if (len == 2) + return (1); + if (buf[2] != '?') + return (-1); + if (len == 3) + return (1); + + /* Copy the rest up to a 'c'. */ + for (i = 0; i < (sizeof tmp) - 1 && buf[3 + i] != 'c'; i++) { + if (3 + i == len) + return (1); + tmp[i] = buf[3 + i]; + } + if (i == (sizeof tmp) - 1) + return (-1); + tmp[i] = '\0'; + *size = 4 + i; + + /* Convert version numbers. */ + a = strtoul(tmp, &endptr, 10); + if (*endptr == ';') { + b = strtoul(endptr + 1, &endptr, 10); + if (*endptr != '\0' && *endptr != ';') + b = 0; + } else + a = b = 0; + + s = "UNKNOWN"; + switch (a) { + case 1: + if (b == 2) { + tty_set_type(tty, TTY_VT100); + s = "VT100"; + } else if (b == 0) { + tty_set_type(tty, TTY_VT101); + s = "VT101"; + } + break; + case 6: + tty_set_type(tty, TTY_VT102); + s = "VT102"; + break; + case 62: + tty_set_type(tty, TTY_VT220); + s = "VT220"; + break; + case 63: + tty_set_type(tty, TTY_VT320); + s = "VT320"; + break; + case 64: + tty_set_type(tty, TTY_VT420); + s = "VT420"; + break; + } + log_debug("received DA %.*s (%s)", (int)*size, buf, s); + + return (0); +} diff --git a/tty.c b/tty.c index 34270625..fb7b6f8c 100644 --- a/tty.c +++ b/tty.c @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -54,6 +55,7 @@ static void tty_colours_bg(struct tty *, const struct grid_cell *); static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); +static void tty_margin_pane(struct tty *, const struct tty_ctx *); static int tty_large_region(struct tty *, const struct tty_ctx *); static int tty_fake_bce(const struct tty *, const struct window_pane *, u_int); @@ -70,6 +72,8 @@ static void tty_default_attributes(struct tty *, const struct window_pane *, #define tty_use_acs(tty) \ (tty_term_has((tty)->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8)) +#define tty_use_margin(tty) \ + ((tty)->term_type == TTY_VT420) #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) @@ -110,7 +114,9 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) tty->ccolour = xstrdup(""); tty->flags = 0; + tty->term_flags = 0; + tty->term_type = TTY_UNKNOWN; return (0); } @@ -138,8 +144,8 @@ tty_resize(struct tty *tty) tty->cx = UINT_MAX; tty->cy = UINT_MAX; - tty->rupper = UINT_MAX; - tty->rlower = UINT_MAX; + tty->rupper = tty->rleft = UINT_MAX; + tty->rlower = tty->rright = UINT_MAX; /* * If the terminal has been started, reset the actual scroll region and @@ -148,13 +154,15 @@ tty_resize(struct tty *tty) if (tty->flags & TTY_STARTED) { tty_cursor(tty, 0, 0); tty_region(tty, 0, tty->sy - 1); + tty_margin(tty, 0, tty->sx - 1); } return (1); } int -tty_set_size(struct tty *tty, u_int sx, u_int sy) { +tty_set_size(struct tty *tty, u_int sx, u_int sy) +{ if (sx == tty->sx && sy == tty->sy) return (0); tty->sx = sx; @@ -248,13 +256,14 @@ tty_start_tty(struct tty *tty) tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } + tty_puts(tty, "\033[c"); } tty->cx = UINT_MAX; tty->cy = UINT_MAX; - tty->rlower = UINT_MAX; - tty->rupper = UINT_MAX; + tty->rupper = tty->rleft = UINT_MAX; + tty->rlower = tty->rright = UINT_MAX; tty->mode = MODE_CURSOR; @@ -315,6 +324,8 @@ tty_stop_tty(struct tty *tty) } } + if (tty_use_margin(tty)) + tty_raw(tty, "\033[?69l"); /* DECLRMM */ tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); setblocking(tty->fd, 1); @@ -352,6 +363,15 @@ tty_free(struct tty *tty) free(tty->termname); } +void +tty_set_type(struct tty *tty, int type) +{ + tty->term_type = type; + + if (tty_use_margin(tty)) + tty_puts(tty, "\033[?69h"); /* DECLRMM */ +} + void tty_raw(struct tty *tty, const char *s) { @@ -835,6 +855,7 @@ tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, ctx->wp, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_margin_pane(tty, ctx); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num); @@ -854,6 +875,7 @@ tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, ctx->wp, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_margin_pane(tty, ctx); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num); @@ -930,6 +952,7 @@ tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) tty_attributes(tty, &grid_default_cell, ctx->wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_margin_pane(tty, ctx); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); tty_putcode(tty, TTYC_RI); @@ -943,7 +966,7 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy != ctx->orlower) return; - if (!tty_pane_full_width(tty, ctx) || + if ((!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR)) { if (tty_large_region(tty, ctx)) @@ -954,17 +977,30 @@ tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) } /* - * If this line wrapped naturally (ctx->num is nonzero), don't do - * anything - the cursor can just be moved to the last cell and wrap - * naturally. + * If this line wrapped naturally (ctx->num is nonzero) and we are not + * using margins, don't do anything - the cursor can just be moved + * to the last cell and wrap naturally. */ - if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP)) + if (!tty_use_margin(tty) && + ctx->num != 0 && + !(tty->term->flags & TERM_EARLYWRAP)) { return; + } tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); - tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + tty_margin_pane(tty, ctx); + + /* + * If we want to wrap a pane, the cursor needs to be exactly on the + * right of the region. But if the pane isn't on the right, it may be + * off the edge - if so, move the cursor back to the right. + */ + if (ctx->xoff + ctx->ocx > tty->rright) + tty_cursor(tty, tty->rright, ctx->yoff + ctx->ocy); + else + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_putc(tty, '\n'); } @@ -979,6 +1015,7 @@ tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, wp, ctx->bg); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + tty_margin_pane(tty, ctx); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_pane_full_width(tty, ctx) && @@ -1014,6 +1051,7 @@ tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + tty_margin_pane(tty, ctx); tty_cursor_pane(tty, ctx, 0, 0); if (tty_pane_full_width(tty, ctx) && @@ -1043,6 +1081,7 @@ tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) tty_default_attributes(tty, wp, ctx->bg); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + tty_margin_pane(tty, ctx); tty_cursor_pane(tty, ctx, 0, 0); if (tty_pane_full_width(tty, ctx) && @@ -1073,6 +1112,7 @@ tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + tty_margin_pane(tty, ctx); for (j = 0; j < screen_size_y(s); j++) { tty_cursor_pane(tty, ctx, 0, j); @@ -1090,6 +1130,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) if (ctx->ocy == ctx->orlower) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_margin_pane(tty, ctx); /* Is the cursor in the very last position? */ width = ctx->cell->data.width; @@ -1250,6 +1291,32 @@ tty_region(struct tty *tty, u_int rupper, u_int rlower) tty_cursor(tty, 0, 0); } +/* Set margin inside pane. */ +static void +tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) +{ + tty_margin(tty, ctx->xoff, ctx->xoff + ctx->wp->sx - 1); +} + +/* Set margin at absolute position. */ +void +tty_margin(struct tty *tty, u_int rleft, u_int rright) +{ + char s[64]; + + if (!tty_use_margin(tty)) + return; + if (tty->rleft == rleft && tty->rright == rright) + return; + + tty->rleft = rleft; + tty->rright = rright; + + snprintf(s, sizeof s, "\033[%u;%us", rleft + 1, rright + 1); + tty_puts(tty, s); + tty_cursor(tty, 0, 0); +} + /* Move cursor inside pane. */ static void tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)