From 9501644ab19b07d773eb4f543c5fb408c51bad6d Mon Sep 17 00:00:00 2001 From: nick black Date: Wed, 27 Oct 2021 20:00:50 -0400 Subject: [PATCH] [render] gratuitous hpa only on plane changes #2199 --- NEWS.md | 7 ++----- src/lib/in.c | 9 +++++++-- src/lib/internal.h | 15 +++++++++++---- src/lib/kitty.c | 2 +- src/lib/notcurses.c | 4 ++-- src/lib/render.c | 22 +++++++--------------- src/lib/sixel.c | 2 +- src/lib/stats.c | 10 +++++----- 8 files changed, 36 insertions(+), 35 deletions(-) diff --git a/NEWS.md b/NEWS.md index 832f470b64..d9f9333692 100644 --- a/NEWS.md +++ b/NEWS.md @@ -196,7 +196,7 @@ rearrangements of Notcurses. * Added `ncplane_moverel()`. * Documented `ncplane_move_yx()` in `notcurses_plane.3`, and removed the false comment that "passing -1 as a coordinate will hold that axis - constant" from `USGAE.md` and `notcurses.h`. This has never been true. + constant" from `USAGE.md` and `notcurses.h`. This has never been true. * Added `ncdirect_putegc()` to perform Unicode segmentation. It returns the number of columns consumed, and makes available the number of bytes used by the EGC. @@ -588,10 +588,7 @@ rearrangements of Notcurses. * Add new function `ncpile_render()`, which renders the pile containing the specified plane to the specified buffer. Add new function `ncpile_rasterize()` to rasterize the specified buffer to output. - * Added `NCSTYLE_STRUCK` for strikethrough. Note that this is not supported - by terminfo, and we instead just hardcode the control sequence. Use at your - own risk! If your terminal doesn't support this control sequence, behavior - is undefined. + * Added `NCSTYLE_STRUCK` for strikethrough. * 2.0.7 (2020-11-21) * The `horiz` union of `ncplane_options` has been discarded; the `int x` diff --git a/src/lib/in.c b/src/lib/in.c index b6e24cfd31..9b41b6abd9 100644 --- a/src/lib/in.c +++ b/src/lib/in.c @@ -1035,6 +1035,8 @@ da2_cb(inputctx* ictx){ if(pv == 0){ return 2; } + // modern XTerm replies to XTVERSION, but older versions require extracting + // the version from secondary DA if(ictx->initdata->qterm == TERMINAL_XTERM){ if(ictx->initdata->version == NULL){ char ver[8]; @@ -1217,11 +1219,14 @@ tcap_cb(inputctx* ictx){ // 'TN' (Terminal Name) if(strncasecmp(str, "544e=", 5) == 0){ const char* tn = str + 5; + // FIXME clean this crap up if(strcasecmp(tn, "6D6C7465726D") == 0){ ictx->initdata->qterm = TERMINAL_MLTERM; + }else if(strcasecmp(tn, "787465726d") == 0){ + ictx->initdata->qterm = TERMINAL_XTERM; // "xterm" }else if(strcasecmp(tn, "787465726d2d6b69747479") == 0){ - ictx->initdata->qterm = TERMINAL_KITTY; - }else if(strcasecmp(tn, "787465726D2D323536636F6C6F72") == 0){ + ictx->initdata->qterm = TERMINAL_KITTY; // "xterm-kitty" + }else if(strcasecmp(tn, "787465726d2d323536636f6c6f72") == 0){ ictx->initdata->qterm = TERMINAL_XTERM; // "xterm-256color" }else{ logdebug("unknown terminal name %s\n", tn); diff --git a/src/lib/internal.h b/src/lib/internal.h index 5a7cc9c5ea..2393c3ba27 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -128,6 +128,8 @@ typedef struct rasterstate { // modified by: output, cursor moves, clearing the screen (during refresh). int y, x; + const ncplane* lastsrcp; // last source plane (we emit hpa on plane changes) + unsigned lastr; // foreground rgb, overloaded for palindexed fg unsigned lastg; unsigned lastb; @@ -1148,13 +1150,14 @@ mouse_disable(tinfo* ti, fbuf* f){ // sync the drawing position to the specified location with as little overhead // as possible (with nothing, if already at the right location). we prefer // absolute horizontal moves (hpa) to relative ones, in the rare event that -// our understanding of our horizontal location is faulty. +// our understanding of our horizontal location is faulty. if we're moving from +// one plane to another, we emit an hpa no matter what. // FIXME fall back to synthesized moves in the absence of capabilities (i.e. // textronix lacks cup; fake it with horiz+vert moves) // if hardcursorpos is non-zero, we always perform a cup. this is done when we // don't know where the cursor currently is =]. static inline int -goto_location(notcurses* nc, fbuf* f, int y, int x){ +goto_location(notcurses* nc, fbuf* f, int y, int x, const ncplane* srcp){ //fprintf(stderr, "going to %d/%d from %d/%d hard: %u\n", y, x, nc->rstate.y, nc->rstate.x, nc->rstate.hardcursorpos); int ret = 0; // if we don't have hpa, force a cup even if we're only 1 char away. the only @@ -1162,8 +1165,11 @@ goto_location(notcurses* nc, fbuf* f, int y, int x){ // you can't use cuf for backwards moves anyway; again, vt100 can suck it. const char* hpa = get_escape(&nc->tcache, ESCAPE_HPA); if(nc->rstate.y == y && hpa && !nc->rstate.hardcursorpos){ // only need move x - if(nc->rstate.x == x){ // needn't move shit - return 0; + if(nc->rstate.x == x){ + if(nc->rstate.lastsrcp == srcp){ + return 0; // needn't move shit + } + ++nc->stats.s.hpa_gratuitous; } if(fbuf_emit(f, tiparm(hpa, x))){ return -1; @@ -1182,6 +1188,7 @@ goto_location(notcurses* nc, fbuf* f, int y, int x){ nc->rstate.x = x; nc->rstate.y = y; nc->rstate.hardcursorpos = 0; + nc->rstate.lastsrcp = srcp; return ret; } diff --git a/src/lib/kitty.c b/src/lib/kitty.c index 7f66f93af5..c13071e2a9 100644 --- a/src/lib/kitty.c +++ b/src/lib/kitty.c @@ -1183,7 +1183,7 @@ int kitty_move(sprixel* s, fbuf* f, unsigned noscroll, int yoff, int xoff){ const int targx = s->n->absx; logdebug("moving %u to %d %d\n", s->id, targy, targx); int ret = 0; - if(goto_location(ncplane_notcurses(s->n), f, targy + yoff, targx + xoff)){ + if(goto_location(ncplane_notcurses(s->n), f, targy + yoff, targx + xoff, s->n)){ ret = -1; }else if(fbuf_printf(f, "\e_Ga=p,i=%d,p=1,q=2%s\e\\", s->id, noscroll ? ",C=1" : "") < 0){ diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index fe22c12309..b90eaf9830 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -1093,7 +1093,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){ // the u7 led the queries so that we would get a cursor position // unaffected by any query spill (unconsumed control sequences). move // us back to that location, in case there was any such spillage. - if(goto_location(ret, &ret->rstate.f, *cursory, *cursorx)){ + if(goto_location(ret, &ret->rstate.f, *cursory, *cursorx, NULL)){ goto err; } } @@ -1227,7 +1227,7 @@ int notcurses_stop(notcurses* nc){ fbuf_putc(&nc->rstate.f, '\n'); --targy; } - goto_location(nc, &nc->rstate.f, targy, 0); + goto_location(nc, &nc->rstate.f, targy, 0, NULL); fbuf_finalize(&nc->rstate.f, stdout); } if(nc->stdplane){ diff --git a/src/lib/render.c b/src/lib/render.c index fa10696896..05e786da27 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -976,7 +976,7 @@ rasterize_scrolls(const ncpile* p, fbuf* f){ if(p->nc->tcache.pixel_scroll){ p->nc->tcache.pixel_scroll(p, &p->nc->tcache, scrolls); } - if(goto_location(p->nc, f, p->dimy, 0)){ + if(goto_location(p->nc, f, p->dimy, 0, NULL)){ return -1; } // terminals advertising 'bce' will scroll in the current background color; @@ -1018,7 +1018,7 @@ rasterize_sprixels(notcurses* nc, ncpile* p, fbuf* f){ if(nc->tcache.pixel_commit){ int y, x; ncplane_abs_yx(s->n, &y, &x); - if(goto_location(nc, f, y + nc->margin_t, x + nc->margin_l)){ + if(goto_location(nc, f, y + nc->margin_t, x + nc->margin_l, NULL)){ return -1; } if(sprite_commit(&nc->tcache, f, s, false)){ @@ -1107,7 +1107,7 @@ rasterize_core(notcurses* nc, const ncpile* p, fbuf* f, unsigned phase){ // was not above a sprixel (and the cell is damaged). in the second // phase, we draw everything that remains damaged. ++nc->stats.s.cellemissions; - if(goto_location(nc, f, y, x)){ + if(goto_location(nc, f, y, x, rvec[damageidx].p)){ return -1; } // set the style. this can change the color back to the default; if it @@ -1322,7 +1322,7 @@ notcurses_rasterize(notcurses* nc, ncpile* p, fbuf* f){ if(cursory >= 0){ notcurses_cursor_enable(nc, cursory, cursorx); }else if(nc->rstate.logendy >= 0){ - goto_location(nc, f, nc->rstate.logendy, nc->rstate.logendx); + goto_location(nc, f, nc->rstate.logendy, nc->rstate.logendx, NULL); if(fbuf_flush(f, nc->ttyfp)){ ret = -1; } @@ -1341,18 +1341,10 @@ int clear_and_home(notcurses* nc, tinfo* ti, fbuf* f){ goto success; } } - const ncplane* stdn = notcurses_stdplane_const(nc); - // clearscr didn't fly. try scrolling everything off. first, go to the - // bottom of the screen, then write N newlines. - if(goto_location(nc, f, ncplane_dim_y(stdn) - 1, 0)){ + if(emit_scrolls(ti, ncplane_dim_y(notcurses_stdplane_const(nc)), f)){ return -1; } - for(int y = 0 ; y < ncplane_dim_y(stdn) ; ++y){ - if(fbuf_putc(f, '\n') < 0){ - return -1; - } - } - if(goto_location(nc, f, 0, 0)){ + if(goto_location(nc, f, 0, 0, NULL)){ return -1; } @@ -1682,7 +1674,7 @@ int notcurses_cursor_enable(notcurses* nc, int y, int x){ return -1; } // updates nc->rstate.cursor{y,x} - if(goto_location(nc, &f, y + nc->margin_t, x + nc->margin_l)){ + if(goto_location(nc, &f, y + nc->margin_t, x + nc->margin_l, nc->rstate.lastsrcp)){ fbuf_free(&f); return -1; } diff --git a/src/lib/sixel.c b/src/lib/sixel.c index bdfdfd24f7..90b815e9fe 100644 --- a/src/lib/sixel.c +++ b/src/lib/sixel.c @@ -1010,7 +1010,7 @@ int sixel_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f, if(p){ const int targy = s->n->absy + yoff; const int targx = s->n->absx + xoff; - if(goto_location(p->nc, f, targy, targx)){ + if(goto_location(p->nc, f, targy, targx, NULL)){ return -1; } if(s->invalidated == SPRIXEL_MOVED){ diff --git a/src/lib/stats.c b/src/lib/stats.c index 1e51f0ce6a..559d41fa0b 100644 --- a/src/lib/stats.c +++ b/src/lib/stats.c @@ -204,10 +204,11 @@ void summarize_stats(notcurses* nc){ 1, minbuf, 1), bprefix(stats->renders ? stats->render_bytes / stats->renders : 0, 1, avgbuf, 1); bprefix(stats->render_max_bytes, 1, maxbuf, 1), - fprintf(stderr, "%s%sB (%sB min, %sB avg, %sB max) %"PRIu64" input%s" NL, + fprintf(stderr, "%s%sB (%sB min, %sB avg, %sB max) %"PRIu64" input%s Ghpa: %"PRIu64 NL, clreol, totalbuf, minbuf, avgbuf, maxbuf, stats->input_events, - stats->input_events == 1 ? "" : "s"); + stats->input_events == 1 ? "" : "s", + stats->hpa_gratuitous); } fprintf(stderr, "%s%"PRIu64" failed render%s, %"PRIu64" failed raster%s, %" PRIu64" refresh%s, %"PRIu64" input error%s" NL, @@ -216,14 +217,13 @@ void summarize_stats(notcurses* nc){ stats->refreshes, stats->refreshes == 1 ? "" : "es", stats->input_errors, stats->input_errors == 1 ? "" : "s"); fprintf(stderr, "%sRGB emits:elides: def %"PRIu64":%"PRIu64" fg %"PRIu64":%" - PRIu64" bg %"PRIu64":%"PRIu64" Ghpa: %"PRIu64 NL, + PRIu64" bg %"PRIu64":%"PRIu64 NL, clreol, stats->defaultemissions, stats->defaultelisions, stats->fgemissions, stats->fgelisions, stats->bgemissions, - stats->bgelisions, - stats->hpa_gratuitous); + stats->bgelisions); fprintf(stderr, "%sCell emits:elides: %"PRIu64":%"PRIu64" (%.2f%%) %.2f%% %.2f%% %.2f%%" NL, clreol, stats->cellemissions, stats->cellelisions, (stats->cellemissions + stats->cellelisions) == 0 ? 0 :