From d7aac5461a3d8e9be5b97d95ccba598a92dcc215 Mon Sep 17 00:00:00 2001 From: nick black Date: Sun, 4 Jul 2021 09:45:52 -0300 Subject: [PATCH] reacquire cursor following init banner when using NCOPTION_PRESERVE_CURSOR, we want to reacquire the true cursor position after printing the init banner, rather than trying to figure out how many lines it made us scroll, and keeping that up to date. this improves the notcurses-info output on freebsd, where the compiler line was occupying more than one row. closes #1880. --- src/lib/direct.c | 76 ++++------------------------------------- src/lib/internal.h | 4 --- src/lib/notcurses.c | 18 ++++------ src/lib/render.c | 2 +- src/lib/termdesc.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ src/lib/termdesc.h | 11 ++++++ 6 files changed, 107 insertions(+), 87 deletions(-) diff --git a/src/lib/direct.c b/src/lib/direct.c index cb260d601..f6d59615d 100644 --- a/src/lib/direct.c +++ b/src/lib/direct.c @@ -49,70 +49,6 @@ int ncdirect_putstr(ncdirect* nc, uint64_t channels, const char* utf8){ return fprintf(nc->ttyfp, "%s", utf8); } -static int -cursor_yx_get(int ttyfd, const char* u7, int* y, int* x){ - if(tty_emit(u7, ttyfd)){ - return -1; - } - bool done = false; - enum { // what we expect now - CURSOR_ESC, // 27 (0x1b) - CURSOR_LSQUARE, - CURSOR_ROW, // delimited by a semicolon - CURSOR_COLUMN, - CURSOR_R, - } state = CURSOR_ESC; - int row = 0, column = 0; - char in; - while(read(ttyfd, &in, 1) == 1){ - bool valid = false; - switch(state){ - case CURSOR_ESC: valid = (in == NCKEY_ESC); state = CURSOR_LSQUARE; break; - case CURSOR_LSQUARE: valid = (in == '['); state = CURSOR_ROW; break; - case CURSOR_ROW: - if(isdigit(in)){ - row *= 10; - row += in - '0'; - valid = true; - }else if(in == ';'){ - state = CURSOR_COLUMN; - valid = true; - } - break; - case CURSOR_COLUMN: - if(isdigit(in)){ - column *= 10; - column += in - '0'; - valid = true; - }else if(in == 'R'){ - state = CURSOR_R; - valid = true; - } - break; - case CURSOR_R: default: // logical error, whoops - break; - } - if(!valid){ - fprintf(stderr, "Unexpected result from terminal: %d\n", in); - break; - } - if(state == CURSOR_R){ - done = true; - break; - } - } - if(!done){ - return -1; - } - if(y){ - *y = row; - } - if(x){ - *x = column; - } - return 0; -} - int ncdirect_cursor_up(ncdirect* nc, int num){ if(num < 0){ return -1; @@ -343,22 +279,22 @@ detect_cursor_inversion(ncdirect* n, const char* u7, int rows, int cols, int* y, // we only changed one, supposedly the number of rows. if we were on the // top row before, the reply is inverted. if(*y == 0){ - n->inverted_cursor = true; + n->tcache.inverted_cursor = true; } }else if(*y == newy){ // we only changed one, supposedly the number of columns. if we were on the // rightmost column before, the reply is inverted. if(*x == cols){ - n->inverted_cursor = true; + n->tcache.inverted_cursor = true; } }else{ // the row ought have decreased, and the column ought have increased. if it // went the other way, the reply is inverted. if(newy > *y && newx < *x){ - n->inverted_cursor = true; + n->tcache.inverted_cursor = true; } } - n->detected_cursor_inversion = true; + n->tcache.detected_cursor_inversion = true; return 0; } @@ -412,13 +348,13 @@ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){ if(!x){ x = &xval; } - if(!n->detected_cursor_inversion){ + if(!n->tcache.detected_cursor_inversion){ ret = detect_cursor_inversion_wrapper(n, u7, y, x); }else{ ret = cursor_yx_get(n->ctermfd, u7, y, x); } if(ret == 0){ - if(n->inverted_cursor){ + if(n->tcache.inverted_cursor){ int tmp = *y; *y = *x; *x = tmp; diff --git a/src/lib/internal.h b/src/lib/internal.h index 7dfd25f8a..835bc71ec 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -406,10 +406,6 @@ typedef struct ncdirect { tinfo tcache; // terminfo cache uint64_t channels; // current channels uint16_t stylemask; // current styles - // some terminals (e.g. kmscon) return cursor coordinates inverted from the - // typical order. we detect it the first time ncdirect_cursor_yx() is called. - bool detected_cursor_inversion; // have we performed inversion testing? - bool inverted_cursor; // does the terminal return inverted coordinates? bool initialized_readline; // have we initialized Readline? uint64_t flags; // copied in ncdirect_init() from param } ncdirect; diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 83cd8d875..5d06af78a 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -887,11 +887,9 @@ init_banner_warnings(const notcurses* nc, FILE* out){ // returns the number of lines printed. static int init_banner(const notcurses* nc){ - int liness = 0; if(!nc->suppress_banner){ char prefixbuf[BPREFIXSTRLEN + 1]; term_fg_palindex(nc, stdout, 50 % nc->tcache.caps.colors); - ++liness; fprintf(nc->ttyfp, "notcurses %s on %s %s\n", notcurses_version(), nc->tcache.termname ? nc->tcache.termname : "?", nc->tcache.termversion ? nc->tcache.termversion : ""); @@ -911,7 +909,6 @@ init_banner(const notcurses* nc){ sizeof(struct crender), nc->tcache.caps.colors); } const char* setaf; - liness += 3; if(nc->tcache.caps.rgb && (setaf = get_escape(&nc->tcache, ESCAPE_SETAF))){ ncfputc('+', nc->ttyfp); term_fg_rgb8(&nc->tcache, stdout, 0xe0, 0x60, 0x60); @@ -938,17 +935,14 @@ init_banner(const notcurses* nc){ #endif , curses_version()); ncvisual_printbanner(nc); - ++liness; - // don't go sending these to stderr; it would fuck up our line count, for - // one, and we don't know if we can meaningfully send escapes there. - liness += init_banner_warnings(nc, nc->ttyfp); + init_banner_warnings(nc, nc->ttyfp); const char* esc; if( (esc = get_escape(&nc->tcache, ESCAPE_SGR0)) || (esc = get_escape(&nc->tcache, ESCAPE_OP))){ term_emit(esc, nc->ttyfp, false); } } - return liness; + return 0; } // it's critical that we're using UTF-8 encoding if at all possible. since the @@ -1160,15 +1154,15 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){ goto err; } ret->rstate.x = ret->rstate.y = -1; - int bannerlines = init_banner(ret); + init_banner(ret); if(ncflush(ret->ttyfp)){ free_plane(ret->stdplane); goto err; } if(cursor_y >= 0 && cursor_x >= 0){ - cursor_y += bannerlines; - if(cursor_y >= ncplane_dim_y(ret->stdplane)){ - cursor_y = ncplane_dim_y(ret->stdplane) - 1; + if(!ret->suppress_banner && locate_cursor_early(ret, &cursor_y, &cursor_x)){ + free_plane(ret->stdplane); + goto err; } ncplane_cursor_move_yx(ret->stdplane, cursor_y, cursor_x); } diff --git a/src/lib/render.c b/src/lib/render.c index 4ed8b535f..5d303d947 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -881,7 +881,7 @@ static int rasterize_scrolls(ncpile* p, FILE* out){ //fprintf(stderr, "%d tardies to work off, by far the most in the class\n", p->scrolls); while(p->scrolls){ - if(fprintf(out, "\n") < 0){ + if(fprintf(out, "\v") < 0){ return -1; } --p->scrolls; diff --git a/src/lib/termdesc.c b/src/lib/termdesc.c index be43bc19c..aaca78285 100644 --- a/src/lib/termdesc.c +++ b/src/lib/termdesc.c @@ -726,3 +726,86 @@ char* termdesc_longterm(const tinfo* ti){ } return ret; } + +int cursor_yx_get(int ttyfd, const char* u7, int* y, int* x){ + if(tty_emit(u7, ttyfd)){ + return -1; + } + bool done = false; + enum { // what we expect now + CURSOR_ESC, // 27 (0x1b) + CURSOR_LSQUARE, + CURSOR_ROW, // delimited by a semicolon + CURSOR_COLUMN, + CURSOR_R, + } state = CURSOR_ESC; + int row = 0, column = 0; + int r; + char in; + do{ + while((r = read(ttyfd, &in, 1)) == 1){ + bool valid = false; + switch(state){ + case CURSOR_ESC: valid = (in == NCKEY_ESC); state = CURSOR_LSQUARE; break; + case CURSOR_LSQUARE: valid = (in == '['); state = CURSOR_ROW; break; + case CURSOR_ROW: + if(isdigit(in)){ + row *= 10; + row += in - '0'; + valid = true; + }else if(in == ';'){ + state = CURSOR_COLUMN; + valid = true; + } + break; + case CURSOR_COLUMN: + if(isdigit(in)){ + column *= 10; + column += in - '0'; + valid = true; + }else if(in == 'R'){ + state = CURSOR_R; + valid = true; + } + break; + case CURSOR_R: default: // logical error, whoops + break; + } + if(!valid){ + fprintf(stderr, "Unexpected result from terminal: %d\n", in); + break; + } + if(state == CURSOR_R){ + done = true; + break; + } + } + }while(!done && (r == 1 || (errno == EINTR || errno == EAGAIN || errno == EBUSY))); + if(!done){ + return -1; + } + if(y){ + *y = row; + } + if(x){ + *x = column; + } + return 0; +} + +int locate_cursor_early(struct notcurses* nc, int* cursor_y, int* cursor_x){ + const char* u7 = get_escape(&nc->tcache, ESCAPE_DSRCPR); + // FIXME ought check for cursor inversion! + int ret = cursor_yx_get(nc->ttyfd, u7, cursor_y, cursor_x); + if(ret == 0){ + if(nc->tcache.inverted_cursor){ + int tmp = *cursor_y; + *cursor_y = *cursor_x; + *cursor_x = tmp; + } + // we use 0-based coordinates, but known terminals use 1-based coordinates + --*cursor_y; + --*cursor_x; + } + return ret; +} diff --git a/src/lib/termdesc.h b/src/lib/termdesc.h index c364f9b40..6e9c7af44 100644 --- a/src/lib/termdesc.h +++ b/src/lib/termdesc.h @@ -161,6 +161,11 @@ typedef struct tinfo { int default_cols; // COLUMNS environment var / cols terminfo / 80 int linux_fb_fd; // linux framebuffer device fd + + // some terminals (e.g. kmscon) return cursor coordinates inverted from the + // typical order. we detect it the first time ncdirect_cursor_yx() is called. + bool detected_cursor_inversion; // have we performed inversion testing? + bool inverted_cursor; // does the terminal return inverted coordinates? } tinfo; // retrieve the terminfo(5)-style escape 'e' from tdesc (NULL if undefined). @@ -194,6 +199,12 @@ void free_terminfo_cache(tinfo* ti); // return a heap-allocated copy of termname + termversion char* termdesc_longterm(const tinfo* ti); +// get the cursor location early (after interrogate_terminfo(), but before +// handing control back to the user). blocking call. FIXME this should go away +// once we have proper input handling that separates out control sequences. +int locate_cursor_early(struct notcurses* nc, int* cursor_y, int* cursor_x); +int cursor_yx_get(int ttyfd, const char* u7, int* y, int* x); + #ifdef __cplusplus } #endif