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 1dff03f50..893ab803f 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