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.
This commit is contained in:
nick black 2021-07-04 09:45:52 -03:00
parent 5451ecd890
commit d7aac5461a
6 changed files with 107 additions and 87 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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