From 37286aa707ad978527844af39d31e446629bbd53 Mon Sep 17 00:00:00 2001 From: nick black Date: Fri, 9 Jul 2021 09:21:17 -0400 Subject: [PATCH] work around strange kitty issue for now #1910 --- src/lib/internal.h | 51 ++++++++++++++++++++++++++++++++++++++++++---- src/lib/kitty.c | 7 ++++--- src/lib/render.c | 48 ++----------------------------------------- src/lib/sixel.c | 5 ++++- src/lib/sprite.h | 4 ++-- src/lib/termdesc.h | 3 ++- 6 files changed, 61 insertions(+), 57 deletions(-) diff --git a/src/lib/internal.h b/src/lib/internal.h index f23fc83ee..5f8d53832 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -674,16 +674,18 @@ sprite_scrub(const notcurses* n, const ncpile* p, sprixel* s){ // precondition: s->invalidated is SPRIXEL_INVALIDATED or SPRIXEL_MOVED. // returns -1 on error, or the number of bytes written. static inline int -sprite_draw(const notcurses* n, const ncpile* p, sprixel* s, FILE* out){ +sprite_draw(const notcurses* n, const ncpile* p, sprixel* s, FILE* out, + int y, int x){ //sprixel_debug(s, stderr); logdebug("Sprixel %u state %d\n", s->id, s->invalidated); - return n->tcache.pixel_draw(p, s, out); + return n->tcache.pixel_draw(p, s, out, y, x); } // precondition: s->invalidated is SPRIXEL_MOVED or SPRIXEL_INVALIDATED // returns -1 on error, or the number of bytes written. static inline int -sprite_redraw(const notcurses* n, const ncpile* p, sprixel* s, FILE* out){ +sprite_redraw(const notcurses* n, const ncpile* p, sprixel* s, FILE* out, + int y, int x){ //sprixel_debug(s, stderr); logdebug("Sprixel %u state %d\n", s->id, s->invalidated); if(s->invalidated == SPRIXEL_MOVED && n->tcache.pixel_move){ @@ -693,7 +695,7 @@ sprite_redraw(const notcurses* n, const ncpile* p, sprixel* s, FILE* out){ bool noscroll = !n->tcache.sixel_maxy_pristine; return n->tcache.pixel_move(s, out, noscroll); }else{ - return n->tcache.pixel_draw(p, s, out); + return n->tcache.pixel_draw(p, s, out, y, x); } } @@ -1171,6 +1173,47 @@ mouse_disable(FILE* out){ out, true); } +// 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. +// 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 +static inline int +goto_location(notcurses* nc, FILE* out, int y, int x){ +//fprintf(stderr, "going to %d/%d from %d/%d hard: %u\n", y, x, nc->rstate.y, nc->rstate.x, hardcursorpos); + int ret = 0; + // if we don't have hpa, force a cup even if we're only 1 char away. the only + // terminal i know supporting cup sans hpa is vt100, and vt100 can suck it. + // 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; + } + const char* cuf1 = get_escape(&nc->tcache, ESCAPE_CUF1); + if(x == nc->rstate.x + 1 && cuf1){ + ret = term_emit(cuf1, out, false); + }else{ + ret = term_emit(tiparm(hpa, x), out, false); + } + }else{ + // cup is required, no need to verify existence + ret = term_emit(tiparm(get_escape(&nc->tcache, ESCAPE_CUP), y, x), out, false); + nc->rstate.hardcursorpos = 0; + } + nc->rstate.x = x; + nc->rstate.y = y; + if(nc->rstate.logendy >= 0){ + if(y > nc->rstate.logendy || (y == nc->rstate.logendy && x > nc->rstate.logendx)){ + nc->rstate.logendy = y; + nc->rstate.logendx = x; + } + } + return ret; +} + // how many edges need touch a corner for it to be printed? static inline unsigned box_corner_needs(unsigned ctlword){ diff --git a/src/lib/kitty.c b/src/lib/kitty.c index f69f6b328..ae503b140 100644 --- a/src/lib/kitty.c +++ b/src/lib/kitty.c @@ -562,7 +562,9 @@ write_kitty_data(FILE* fp, int linesize, int leny, int lenx, int cols, // q=2 has been able to go on chunks other than the last chunk since // 2021-03, but there's no harm in this small bit of backwards compat. if(totalout == 0){ - *parse_start = fprintf(fp, "\e_Gf=32,s=%d,v=%d,i=%d,p=1,a=t,%s;", + // FIXME this move to 1;1 is ridiculous, but see + // https://github.com/dankamongmen/notcurses/issues/1910 =[ + *parse_start = fprintf(fp, "\e[1;1H\e_Gf=32,s=%d,v=%d,i=%d,p=1,a=t,%s;", lenx, leny, sprixelid, chunks ? "m=1" : "q=2"); }else{ fprintf(fp, "\e_G%sm=%d;", chunks ? "" : "q=2,", chunks ? 1 : 0); @@ -830,7 +832,7 @@ int kitty_scrub(const ncpile* p, sprixel* s){ } // returns the number of bytes written -int kitty_draw(const ncpile* p, sprixel* s, FILE* out){ +int kitty_draw(const ncpile* p, sprixel* s, FILE* out, int y, int x){ (void)p; bool animated = false; if(s->mstreamfp){ // active animation @@ -852,7 +854,6 @@ int kitty_draw(const ncpile* p, sprixel* s, FILE* out){ free(s->glyph); s->glyph = NULL; s->glyphlen = 0; - //s->invalidated = SPRIXEL_QUIESCENT; s->invalidated = SPRIXEL_LOADED; }else{ s->invalidated = SPRIXEL_LOADED; diff --git a/src/lib/render.c b/src/lib/render.c index 6b7359b96..05eb0a3d0 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -717,47 +717,6 @@ update_palette(notcurses* nc, FILE* out){ return 0; } -// 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. -// 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 -static inline int -goto_location(notcurses* nc, FILE* out, int y, int x){ -//fprintf(stderr, "going to %d/%d from %d/%d hard: %u\n", y, x, nc->rstate.y, nc->rstate.x, hardcursorpos); - int ret = 0; - // if we don't have hpa, force a cup even if we're only 1 char away. the only - // terminal i know supporting cup sans hpa is vt100, and vt100 can suck it. - // 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; - } - const char* cuf1 = get_escape(&nc->tcache, ESCAPE_CUF1); - if(x == nc->rstate.x + 1 && cuf1){ - ret = term_emit(cuf1, out, false); - }else{ - ret = term_emit(tiparm(hpa, x), out, false); - } - }else{ - // cup is required, no need to verify existence - ret = term_emit(tiparm(get_escape(&nc->tcache, ESCAPE_CUP), y, x), out, false); - nc->rstate.hardcursorpos = 0; - } - nc->rstate.x = x; - nc->rstate.y = y; - if(nc->rstate.logendy >= 0){ - if(y > nc->rstate.logendy || (y == nc->rstate.logendy && x > nc->rstate.logendx)){ - nc->rstate.logendy = y; - nc->rstate.logendx = x; - } - } - return ret; -} - // at least one of the foreground and background are the default. emit the // necessary return to default (if one is necessary), and update rstate. static inline int @@ -893,7 +852,7 @@ clean_sprixels(notcurses* nc, ncpile* p, FILE* out){ if(goto_location(nc, out, y + nc->margin_t, x + nc->margin_l)){ return -1; } - int r = sprite_redraw(nc, p, s, out); + int r = sprite_redraw(nc, p, s, out, y + nc->margin_t, x + nc->margin_l); if(r < 0){ return -1; } @@ -952,10 +911,7 @@ rasterize_sprixels(notcurses* nc, ncpile* p, FILE* out){ //fprintf(stderr, "3 DRAWING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, y + nc->margin_t, x + nc->margin_l, s->n); int y,x; ncplane_yx(s->n, &y, &x); - if(goto_location(nc, out, y + nc->margin_t, x + nc->margin_l)){ - return -1; - } - int r = sprite_draw(nc, p, s, out); + int r = sprite_draw(nc, p, s, out, y + nc->margin_t, x + nc->margin_l); if(r < 0){ return -1; } diff --git a/src/lib/sixel.c b/src/lib/sixel.c index 2baa95835..bd7d65145 100644 --- a/src/lib/sixel.c +++ b/src/lib/sixel.c @@ -827,7 +827,7 @@ int sixel_scrub(const ncpile* p, sprixel* s){ } // returns the number of bytes written -int sixel_draw(const ncpile* p, sprixel* s, FILE* out){ +int sixel_draw(const ncpile* p, sprixel* s, FILE* out, int y, int x){ // if we've wiped or rebuilt any cells, effect those changes now, or else // we'll get flicker when we move to the new location. if(s->wipes_outstanding){ @@ -836,6 +836,9 @@ int sixel_draw(const ncpile* p, sprixel* s, FILE* out){ } s->wipes_outstanding = false; } + if(goto_location(p->nc, out, y, x)){ + return -1; + } if(s->invalidated == SPRIXEL_MOVED){ for(int yy = s->movedfromy ; yy < s->movedfromy + s->dimy && yy < p->dimy ; ++yy){ for(int xx = s->movedfromx ; xx < s->movedfromx + s->dimx && xx < p->dimx ; ++xx){ diff --git a/src/lib/sprite.h b/src/lib/sprite.h index 040f4479d..c2aa14da3 100644 --- a/src/lib/sprite.h +++ b/src/lib/sprite.h @@ -167,9 +167,9 @@ int kitty_wipe_animation(sprixel* s, int ycell, int xcell); int sixel_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int kitty_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int kitty_rebuild_animation(sprixel* s, int ycell, int xcell, uint8_t* auxvec); -int kitty_draw(const struct ncpile *p, sprixel* s, FILE* out); +int kitty_draw(const struct ncpile *p, sprixel* s, FILE* out, int y, int x); int kitty_move(sprixel* s, FILE* out, unsigned noscroll); -int sixel_draw(const struct ncpile *p, sprixel* s, FILE* out); +int sixel_draw(const struct ncpile *p, sprixel* s, FILE* out, int y, int x); int sixel_scrub(const struct ncpile* p, sprixel* s); int kitty_scrub(const struct ncpile* p, sprixel* s); int kitty_remove(int id, FILE* out); diff --git a/src/lib/termdesc.h b/src/lib/termdesc.h index 9af58d851..eb5563252 100644 --- a/src/lib/termdesc.h +++ b/src/lib/termdesc.h @@ -130,7 +130,8 @@ typedef struct tinfo { int (*pixel_rebuild)(struct sprixel* s, int y, int x, uint8_t* auxvec); int (*pixel_remove)(int id, FILE* out); // kitty only, issue actual delete command int (*pixel_init)(const struct tinfo*, int fd); // called when support is detected - int (*pixel_draw)(const struct ncpile* p, struct sprixel* s, FILE* out); + int (*pixel_draw)(const struct ncpile* p, struct sprixel* s, FILE* out, + int y, int x); // execute move (erase old graphic, place at new location) if non-NULL int (*pixel_move)(struct sprixel* s, FILE* out, unsigned noscroll); int (*pixel_scrub)(const struct ncpile* p, struct sprixel* s);