move ttyfd into tinfo, unify calls to setupterm #2023

pull/2041/head
nick black 3 years ago
parent c86d285f5d
commit 6490099466
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -140,8 +140,8 @@ int ncdirect_clear(ncdirect* nc){
int ncdirect_dim_x(ncdirect* nc){
int x;
if(nc->ttyfd >= 0){
if(update_term_dimensions(nc->ttyfd, NULL, &x, &nc->tcache, 0) == 0){
if(nc->tcache.ttyfd >= 0){
if(update_term_dimensions(NULL, &x, &nc->tcache, 0) == 0){
return x;
}
}else{
@ -152,8 +152,8 @@ int ncdirect_dim_x(ncdirect* nc){
int ncdirect_dim_y(ncdirect* nc){
int y;
if(nc->ttyfd >= 0){
if(update_term_dimensions(nc->ttyfd, &y, NULL, &nc->tcache, 0) == 0){
if(nc->tcache.ttyfd >= 0){
if(update_term_dimensions(&y, NULL, &nc->tcache, 0) == 0){
return y;
}
}else{
@ -259,8 +259,8 @@ int ncdirect_cursor_move_yx(ncdirect* n, int y, int x){
if(y == -1){ // keep row the same, horizontal move only
if(hpa){
return term_emit(tiparm(hpa, x), n->ttyfp, false);
}else if(n->ttyfd >= 0 && u7){
if(cursor_yx_get(n->ttyfd, u7, &y, NULL)){
}else if(n->tcache.ttyfd >= 0 && u7){
if(cursor_yx_get(n->tcache.ttyfd, u7, &y, NULL)){
return -1;
}
}else{
@ -269,8 +269,8 @@ int ncdirect_cursor_move_yx(ncdirect* n, int y, int x){
}else if(x == -1){ // keep column the same, vertical move only
if(!vpa){
return term_emit(tiparm(vpa, y), n->ttyfp, false);
}else if(n->ttyfd >= 0 && u7){
if(cursor_yx_get(n->ttyfd, u7, NULL, &x)){
}else if(n->tcache.ttyfd >= 0 && u7){
if(cursor_yx_get(n->tcache.ttyfd, u7, NULL, &x)){
return -1;
}
}else{
@ -309,11 +309,11 @@ detect_cursor_inversion(ncdirect* n, const char* u7, int rows, int cols, int* y,
if(rows <= 1 || cols <= 1){ // FIXME can this be made to work in 1 dimension?
return -1;
}
if(cursor_yx_get(n->ttyfd, u7, y, x)){
if(cursor_yx_get(n->tcache.ttyfd, u7, y, x)){
return -1;
}
// do not use normal ncdirect_cursor_*() commands, because those go to ttyfp
// instead of ttyfd. since we always talk directly to the terminal, we need
// instead of tcache.ttyfd. since we always talk directly to the terminal, we need
// to move the cursor directly via the terminal.
const char* cuu = get_escape(&n->tcache, ESCAPE_CUU);
const char* cuf = get_escape(&n->tcache, ESCAPE_CUF);
@ -326,26 +326,26 @@ detect_cursor_inversion(ncdirect* n, const char* u7, int rows, int cols, int* y,
int movex;
int movey;
if(*x == cols && *y == 1){
if(tty_emit(tiparm(cud, 1), n->ttyfd)){
if(tty_emit(tiparm(cud, 1), n->tcache.ttyfd)){
return -1;
}
if(tty_emit(tiparm(cub, 1), n->ttyfd)){
if(tty_emit(tiparm(cub, 1), n->tcache.ttyfd)){
return -1;
}
movex = 1;
movey = -1;
}else{
if(tty_emit(tiparm(cuu, 1), n->ttyfd)){
if(tty_emit(tiparm(cuu, 1), n->tcache.ttyfd)){
return -1;
}
if(tty_emit(tiparm(cuf, 1), n->ttyfd)){
if(tty_emit(tiparm(cuf, 1), n->tcache.ttyfd)){
return -1;
}
movex = -1;
movey = 1;
}
int newy, newx;
if(cursor_yx_get(n->ttyfd, u7, &newy, &newx)){
if(cursor_yx_get(n->tcache.ttyfd, u7, &newy, &newx)){
return -1;
}
if(*x == cols && *y == 1){ // need to swap values, since we moved opposite
@ -354,10 +354,10 @@ detect_cursor_inversion(ncdirect* n, const char* u7, int rows, int cols, int* y,
*y = newy;
newy = 1;
}
if(tty_emit(tiparm(movex == 1 ? cuf : cub, 1), n->ttyfd)){
if(tty_emit(tiparm(movex == 1 ? cuf : cub, 1), n->tcache.ttyfd)){
return -1;
}
if(tty_emit(tiparm(movey == 1 ? cud : cuu, 1), n->ttyfd)){
if(tty_emit(tiparm(movey == 1 ? cud : cuu, 1), n->tcache.ttyfd)){
return -1;
}
if(*y == newy && *x == newx){
@ -388,7 +388,7 @@ detect_cursor_inversion(ncdirect* n, const char* u7, int rows, int cols, int* y,
static int
detect_cursor_inversion_wrapper(ncdirect* n, const char* u7, int* y, int* x){
// if we're not on a real terminal, there's no point in running this
if(n->ttyfd < 0){
if(n->tcache.ttyfd < 0){
return 0;
}
const int toty = ncdirect_dim_y(n);
@ -408,7 +408,7 @@ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){
#ifndef __MINGW64__ // FIXME
struct termios termio, oldtermios;
// this is only meaningful for real terminals
if(n->ttyfd < 0){
if(n->tcache.ttyfd < 0){
return -1;
}
const char* u7 = get_escape(&n->tcache, ESCAPE_U7);
@ -416,17 +416,18 @@ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){
fprintf(stderr, "Terminal doesn't support cursor reporting\n");
return -1;
}
if(tcgetattr(n->ttyfd, &termio)){
fprintf(stderr, "Couldn't get terminal info from %d (%s)\n", n->ttyfd, strerror(errno));
if(tcgetattr(n->tcache.ttyfd, &termio)){
fprintf(stderr, "Couldn't get terminal info from %d (%s)\n",
n->tcache.ttyfd, strerror(errno));
return -1;
}
memcpy(&oldtermios, &termio, sizeof(termio));
// we might already be in cbreak mode from ncdirect_init(), but just in case
// it got changed by the client code since then, duck into cbreak mode anew.
termio.c_lflag &= ~(ICANON | ECHO);
if(tcsetattr(n->ttyfd, TCSAFLUSH, &termio)){
if(tcsetattr(n->tcache.ttyfd, TCSAFLUSH, &termio)){
fprintf(stderr, "Couldn't put terminal into cbreak mode via %d (%s)\n",
n->ttyfd, strerror(errno));
n->tcache.ttyfd, strerror(errno));
return -1;
}
int ret, yval, xval;
@ -439,7 +440,7 @@ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){
if(!n->tcache.detected_cursor_inversion){
ret = detect_cursor_inversion_wrapper(n, u7, y, x);
}else{
ret = cursor_yx_get(n->ttyfd, u7, y, x);
ret = cursor_yx_get(n->tcache.ttyfd, u7, y, x);
}
if(ret == 0){
if(n->tcache.inverted_cursor){
@ -451,9 +452,9 @@ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){
--*y;
--*x;
}
if(tcsetattr(n->ttyfd, TCSANOW, &oldtermios)){
if(tcsetattr(n->tcache.ttyfd, TCSANOW, &oldtermios)){
fprintf(stderr, "Couldn't restore terminal mode on %d (%s)\n",
n->ttyfd, strerror(errno)); // don't return error for this
n->tcache.ttyfd, strerror(errno)); // don't return error for this
}
return ret;
#else
@ -814,13 +815,13 @@ ncdirect_stop_minimal(void* vnc){
}
ret |= reset_term_attributes(&nc->tcache, &f);
}
if(nc->ttyfd >= 0){
if(nc->tcache.ttyfd >= 0){
const char* cnorm = get_escape(&nc->tcache, ESCAPE_CNORM);
if(cnorm && tty_emit(cnorm, nc->ttyfd)){
if(cnorm && tty_emit(cnorm, nc->tcache.ttyfd)){
ret = -1;
}
ret |= tcsetattr(nc->ttyfd, TCSANOW, &nc->tcache.tpreserved);
ret |= close(nc->ttyfd);
ret |= tcsetattr(nc->tcache.ttyfd, TCSANOW, &nc->tcache.tpreserved);
ret |= close(nc->tcache.ttyfd);
}
ret |= ncdirect_flush(nc);
free_terminfo_cache(&nc->tcache);
@ -863,19 +864,9 @@ ncdirect* ncdirect_core_init(const char* termtype, FILE* outfp, uint64_t flags){
}else{
loglevel = NCLOGLEVEL_SILENT;
}
// we don't need a controlling tty for everything we do; allow a failure here
ret->ttyfd = get_tty_fd(ret->ttyfp);
// FIXME factor this out, and share it with rendered mode #2023
#ifndef __MINGW64__
int termerr;
if(setupterm(termtype, ret->ttyfd, &termerr)){
fprintf(stderr, "Terminfo error %d (see terminfo(3ncurses))\n", termerr);
goto err;
}
#endif
int cursor_y = -1;
int cursor_x = -1;
if(interrogate_terminfo(&ret->tcache, ret->ttyfd, utf8,
if(interrogate_terminfo(&ret->tcache, termtype, ret->ttyfp, utf8,
1, flags & NCDIRECT_OPTION_INHIBIT_CBREAK,
TERMINAL_UNKNOWN, &cursor_y, &cursor_x, NULL)){
goto err;
@ -899,13 +890,13 @@ ncdirect* ncdirect_core_init(const char* termtype, FILE* outfp, uint64_t flags){
goto err;
}
}
update_term_dimensions(ret->ttyfd, NULL, NULL, &ret->tcache, 0);
update_term_dimensions(NULL, NULL, &ret->tcache, 0);
ncdirect_set_styles(ret, 0);
return ret;
err:
if(ret->ttyfd >= 0){
(void)tcsetattr(ret->ttyfd, TCSANOW, &ret->tcache.tpreserved);
if(ret->tcache.ttyfd >= 0){
(void)tcsetattr(ret->tcache.ttyfd, TCSANOW, &ret->tcache.tpreserved);
}
drop_signals(ret);
free(ret);
@ -1452,7 +1443,7 @@ bool ncdirect_canget_cursor(const ncdirect* n){
if(get_escape(&n->tcache, ESCAPE_U7) == NULL){
return false;
}
if(n->ttyfd < 0){
if(n->tcache.ttyfd < 0){
return false;
}
return true;

@ -49,17 +49,17 @@ int cbreak_mode(int ttyfd, const struct termios* tpreserved){
// SIGINT (^C), SIGQUIT (^\), and SIGTSTP (^Z). They are enabled by default.
int notcurses_linesigs_disable(notcurses* n){
#ifndef __MINGW64__
if(n->ttyfd < 0){
if(n->tcache.ttyfd < 0){
return 0;
}
struct termios tios;
if(tcgetattr(n->ttyfd, &tios)){
logerror("Couldn't preserve terminal state for %d (%s)\n", n->ttyfd, strerror(errno));
if(tcgetattr(n->tcache.ttyfd, &tios)){
logerror("Couldn't preserve terminal state for %d (%s)\n", n->tcache.ttyfd, strerror(errno));
return -1;
}
tios.c_lflag &= ~ISIG;
if(tcsetattr(n->ttyfd, TCSANOW, &tios)){
logerror("Error disabling signals on %d (%s)\n", n->ttyfd, strerror(errno));
if(tcsetattr(n->tcache.ttyfd, TCSANOW, &tios)){
logerror("Error disabling signals on %d (%s)\n", n->tcache.ttyfd, strerror(errno));
return -1;
}
return 0;
@ -72,17 +72,17 @@ int notcurses_linesigs_disable(notcurses* n){
// SIGINT (^C), SIGQUIT (^\), and SIGTSTP (^Z), if disabled.
int notcurses_linesigs_enable(notcurses* n){
#ifndef __MINGW64__
if(n->ttyfd < 0){
if(n->tcache.ttyfd < 0){
return 0;
}
struct termios tios;
if(tcgetattr(n->ttyfd, &tios)){
logerror("Couldn't preserve terminal state for %d (%s)\n", n->ttyfd, strerror(errno));
if(tcgetattr(n->tcache.ttyfd, &tios)){
logerror("Couldn't preserve terminal state for %d (%s)\n", n->tcache.ttyfd, strerror(errno));
return -1;
}
tios.c_lflag |= ~ISIG;
if(tcsetattr(n->ttyfd, TCSANOW, &tios)){
logerror("Error disabling signals on %d (%s)\n", n->ttyfd, strerror(errno));
if(tcsetattr(n->tcache.ttyfd, TCSANOW, &tios)){
logerror("Error disabling signals on %d (%s)\n", n->tcache.ttyfd, strerror(errno));
return -1;
}
return 0;

@ -271,7 +271,6 @@ typedef struct nctabbed {
typedef struct ncdirect {
ncpalette palette; // 256-indexed palette can be used instead of/with RGB
FILE* ttyfp; // FILE* for output tty
int ttyfd; // fd for controlling terminal
tinfo tcache; // terminfo cache
uint64_t channels; // current channels
uint16_t stylemask; // current styles
@ -364,7 +363,6 @@ typedef struct notcurses {
ncstats stashed_stats; // retain across a context reset, for closing banner
FILE* ttyfp; // FILE* for writing rasterized data
int ttyfd; // file descriptor for controlling tty
FILE* renderfp; // debugging FILE* to which renderings are written
tinfo tcache; // terminfo cache
pthread_mutex_t pilelock; // guards pile list, locks resize in render
@ -913,8 +911,7 @@ int ncplane_resize_internal(ncplane* n, int keepy, int keepx,
int keepleny, int keeplenx, int yoff, int xoff,
int ylen, int xlen);
int update_term_dimensions(int fd, int* rows, int* cols, tinfo* tcache,
int margin_b);
int update_term_dimensions(int* rows, int* cols, tinfo* tcache, int margin_b);
ALLOC static inline void*
memdup(const void* src, size_t len){

@ -72,7 +72,7 @@ notcurses_stop_minimal(void* vnc){
}
ret |= mouse_disable(f);
ret |= reset_term_attributes(&nc->tcache, f);
if(nc->ttyfd >= 0){
if(nc->tcache.ttyfd >= 0){
if((esc = get_escape(&nc->tcache, ESCAPE_RMCUP))){
if(sprite_clear_all(&nc->tcache, f)){
ret = -1;
@ -81,7 +81,7 @@ notcurses_stop_minimal(void* vnc){
ret = -1;
}
}
ret |= tcsetattr(nc->ttyfd, TCSANOW, &nc->tcache.tpreserved);
ret |= tcsetattr(nc->tcache.ttyfd, TCSANOW, &nc->tcache.tpreserved);
}
if((esc = get_escape(&nc->tcache, ESCAPE_RMKX)) && fbuf_emit(f, esc)){
ret = -1;
@ -220,10 +220,9 @@ void ncplane_dim_yx(const ncplane* n, int* rows, int* cols){
// anyone calling this needs ensure the ncplane's framebuffer is updated
// to reflect changes in geometry. also called at startup for standard plane.
int update_term_dimensions(int fd, int* rows, int* cols, tinfo* tcache,
int margin_b){
int update_term_dimensions(int* rows, int* cols, tinfo* tcache, int margin_b){
// if we're not a real tty, we presumably haven't changed geometry, return
if(fd < 0){
if(tcache->ttyfd < 0){
if(rows){
*rows = tcache->default_rows;
}
@ -239,14 +238,14 @@ int update_term_dimensions(int fd, int* rows, int* cols, tinfo* tcache,
// FIXME
#ifndef __MINGW64__
struct winsize ws;
int i = ioctl(fd, TIOCGWINSZ, &ws);
int i = ioctl(tcache->ttyfd, TIOCGWINSZ, &ws);
if(i < 0){
logerror("TIOCGWINSZ failed on %d (%s)\n", fd, strerror(errno));
logerror("TIOCGWINSZ failed on %d (%s)\n", tcache->ttyfd, strerror(errno));
return -1;
}
if(ws.ws_row <= 0 || ws.ws_col <= 0){
logerror("Bogus return from TIOCGWINSZ on %d (%d/%d)\n",
fd, ws.ws_row, ws.ws_col);
tcache->ttyfd, ws.ws_row, ws.ws_col);
return -1;
}
int rowsafe;
@ -1123,21 +1122,6 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
// don't set loglevel until we've acquired the signal handler, lest we
// change the loglevel out from under a running instance
loglevel = opts->loglevel;
ret->ttyfd = get_tty_fd(ret->ttyfp);
#ifndef __MINGW64__
// windows doesn't really have a concept of terminfo. you might ssh into other
// machines, but they'll use the terminfo installed thereon (putty, etc.).
int termerr;
if(setupterm(opts->termtype, ret->ttyfd, &termerr) != OK){
logpanic("Terminfo error %d (see terminfo(3ncurses))\n", termerr);
drop_signals(ret);
fbuf_free(&ret->rstate.f);
pthread_mutex_destroy(&ret->stats.lock);
pthread_mutex_destroy(&ret->pilelock);
free(ret);
return NULL;
}
#endif
ret->rstate.logendy = -1;
ret->rstate.logendx = -1;
ret->rstate.x = ret->rstate.y = -1;
@ -1147,7 +1131,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
&ret->rstate.logendy : &fakecursory;
int* cursorx = opts->flags & NCOPTION_PRESERVE_CURSOR ?
&ret->rstate.logendx : &fakecursorx;
if(interrogate_terminfo(&ret->tcache, ret->ttyfd, utf8,
if(interrogate_terminfo(&ret->tcache, opts->termtype, ret->ttyfp, utf8,
opts->flags & NCOPTION_NO_ALTERNATE_SCREEN, 0,
opts->flags & NCOPTION_NO_FONT_CHANGES,
cursory, cursorx, &ret->stats)){
@ -1162,8 +1146,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
}
}
int dimy, dimx;
if(update_term_dimensions(ret->ttyfd, &dimy, &dimx, &ret->tcache,
ret->margin_b)){
if(update_term_dimensions(&dimy, &dimx, &ret->tcache, ret->margin_b)){
goto err;
}
if(ncvisual_init(ret->loglevel)){
@ -1199,7 +1182,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
goto err;
}
if(ret->rstate.logendy >= 0){ // if either is set, both are
if(!ret->suppress_banner && ret->ttyfd >= 0){
if(!ret->suppress_banner && ret->tcache.ttyfd >= 0){
if(locate_cursor(&ret->tcache, &ret->rstate.logendy, &ret->rstate.logendx)){
free_plane(ret->stdplane);
goto err;
@ -1214,7 +1197,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
}
// if not connected to an actual terminal, we're not going to try entering
// the alternate screen; we're not even going to bother clearing the screen.
if(ret->ttyfd >= 0){
if(ret->tcache.ttyfd >= 0){
if(!(opts->flags & NCOPTION_NO_ALTERNATE_SCREEN)){
const char* smcup = get_escape(&ret->tcache, ESCAPE_SMCUP);
if(smcup){
@ -1253,8 +1236,9 @@ err:
logpanic("Alas, you will not be going to space today.\n");
// FIXME looks like we have some memory leaks on this error path?
fbuf_free(&ret->rstate.f);
tcsetattr(ret->ttyfd, TCSANOW, &ret->tcache.tpreserved);
tcsetattr(ret->tcache.ttyfd, TCSANOW, &ret->tcache.tpreserved);
drop_signals(ret);
del_curterm(cur_term);
pthread_mutex_destroy(&ret->stats.lock);
pthread_mutex_destroy(&ret->pilelock);
free(ret);
@ -1319,8 +1303,8 @@ int notcurses_stop(notcurses* nc){
notcurses_drop_planes(nc);
free_plane(nc->stdplane);
}
if(nc->ttyfd >= 0){
ret |= close(nc->ttyfd);
if(nc->tcache.ttyfd >= 0){
ret |= close(nc->tcache.ttyfd);
}
egcpool_dump(&nc->pool);
free(nc->lastframe);

@ -23,7 +23,7 @@ notcurses_resize_internal(ncplane* pp, int* restrict rows, int* restrict cols){
int oldcols = pile->dimx;
*rows = oldrows;
*cols = oldcols;
if(update_term_dimensions(n->ttyfd, rows, cols, &n->tcache, n->margin_b)){
if(update_term_dimensions(rows, cols, &n->tcache, n->margin_b)){
return -1;
}
*rows -= n->margin_t + n->margin_b;
@ -1552,7 +1552,7 @@ int notcurses_cursor_enable(notcurses* nc, int y, int x){
if(nc->cursory == y && nc->cursorx == x){
return 0;
}
if(nc->ttyfd < 0){
if(nc->tcache.ttyfd < 0){
return -1;
}
fbuf f = {};
@ -1576,7 +1576,7 @@ int notcurses_cursor_enable(notcurses* nc, int y, int x){
return 0;
}
const char* cnorm = get_escape(&nc->tcache, ESCAPE_CNORM);
if(!cnorm || tty_emit(cnorm, nc->ttyfd)){
if(!cnorm || tty_emit(cnorm, nc->tcache.ttyfd)){
return -1;
}
nc->cursory = y;
@ -1589,10 +1589,10 @@ int notcurses_cursor_disable(notcurses* nc){
logerror("Cursor is not enabled\n");
return -1;
}
if(nc->ttyfd >= 0){
if(nc->tcache.ttyfd >= 0){
const char* cinvis = get_escape(&nc->tcache, ESCAPE_CIVIS);
if(cinvis){
if(!tty_emit(cinvis, nc->ttyfd) && !ncflush(nc->ttyfp)){
if(!tty_emit(cinvis, nc->tcache.ttyfd) && !ncflush(nc->ttyfp)){
nc->cursory = -1;
nc->cursorx = -1;
return 0;

@ -459,10 +459,8 @@ add_pushcolors_escapes(tinfo* ti, size_t* tablelen, size_t* tableused){
// though, so it's something of a worst-of-all-worlds deal where TERM still
// needs be correct, even though we identify the terminal. le sigh.
static int
apply_term_heuristics(tinfo* ti, const char* termname, int fd,
queried_terminals_e qterm,
size_t* tablelen, size_t* tableused,
bool* invertsixel){
apply_term_heuristics(tinfo* ti, const char* termname, queried_terminals_e qterm,
size_t* tablelen, size_t* tableused, bool* invertsixel){
if(!termname){
// setupterm interprets a missing/empty TERM variable as the special value “unknown”.
termname = "unknown";
@ -489,11 +487,11 @@ apply_term_heuristics(tinfo* ti, const char* termname, int fd,
return -1;
}
if(compare_versions(ti->termversion, "0.22.1") >= 0){
setup_kitty_bitmaps(ti, fd, KITTY_SELFREF);
setup_kitty_bitmaps(ti, ti->ttyfd, KITTY_SELFREF);
}else if(compare_versions(ti->termversion, "0.20.0") >= 0){
setup_kitty_bitmaps(ti, fd, KITTY_ANIMATION);
setup_kitty_bitmaps(ti, ti->ttyfd, KITTY_ANIMATION);
}else{
setup_kitty_bitmaps(ti, fd, KITTY_ALWAYS_SCROLLS);
setup_kitty_bitmaps(ti, ti->ttyfd, KITTY_ALWAYS_SCROLLS);
}
if(add_pushcolors_escapes(ti, tablelen, tableused)){
return -1;
@ -567,7 +565,7 @@ apply_term_heuristics(tinfo* ti, const char* termname, int fd,
termname = "iTerm2";
ti->caps.quadrants = true;
ti->caps.rgb = true;
setup_iterm_bitmaps(ti, fd);
setup_iterm_bitmaps(ti, ti->ttyfd);
}else if(qterm == TERMINAL_APPLE){
termname = "Terminal.app";
// no quadrants, no sextants, no rgb, but it does have braille
@ -659,20 +657,32 @@ macos_early_matches(const char* termname){
}
#endif
// termname is just the TERM environment variable. some details are not
// exposed via terminfo, and we must make heuristic decisions based on
// the detected terminal type, yuck :/.
// if |termtype| is not NULL, it is used to look up the terminfo database entry
// via setupterm(). the value of the TERM environment variable is otherwise
// (implicitly) used. some details are not exposed via terminfo, and we must
// make heuristic decisions based on the detected terminal type, yuck :/.
// the first thing we do is fire off any queries we have (XTSMGRAPHICS, etc.)
// with a trailing Device Attributes. all known terminals will reply to a
// Device Attributes, allowing us to get a negative response if our queries
// aren't supported by the terminal. we fire it off early because we have a
// full round trip before getting the reply, which is likely to pace init.
int interrogate_terminfo(tinfo* ti, int fd, unsigned utf8, unsigned noaltscreen,
unsigned nocbreak, unsigned nonewfonts,
int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned utf8,
unsigned noaltscreen, unsigned nocbreak, unsigned nonewfonts,
int* cursor_y, int* cursor_x, ncsharedstats* stats){
const char* tname = termname(); // longname() is also available
queried_terminals_e qterm = TERMINAL_UNKNOWN;
memset(ti, 0, sizeof(*ti));
// we don't need a controlling tty for everything we do; allow a failure here
ti->ttyfd = get_tty_fd(out);
#ifndef __MINGW64__
// windows doesn't really have a concept of terminfo. you might ssh into other
// machines, but they'll use the terminfo installed thereon (putty, etc.).
int termerr;
if(setupterm(termtype, ti->ttyfd, &termerr)){
logpanic("Terminfo error %d (see terminfo(3ncurses))\n", termerr);
return -1;
}
const char* tname = termname(); // longname() is also available
#endif
size_t tablelen = 0;
size_t tableused = 0;
#ifdef __APPLE__
@ -688,7 +698,7 @@ int interrogate_terminfo(tinfo* ti, int fd, unsigned utf8, unsigned noaltscreen,
ti->linux_fb_fd = -1;
ti->linux_fbuffer = MAP_FAILED;
// we might or might not program quadrants into the console font
if(is_linux_console(fd, nonewfonts, &ti->caps.quadrants)){
if(is_linux_console(ti->ttyfd, nonewfonts, &ti->caps.quadrants)){
qterm = TERMINAL_LINUX;
if(is_linux_framebuffer(ti)){
// FIXME set up pixel-drawing API for framebuffer #1369
@ -697,22 +707,25 @@ int interrogate_terminfo(tinfo* ti, int fd, unsigned utf8, unsigned noaltscreen,
#else
(void)nonewfonts;
#endif
if(fd >= 0){
if(ti->ttyfd >= 0){
#ifndef __MINGW64__
if(tcgetattr(fd, &ti->tpreserved)){
fprintf(stderr, "Couldn't preserve terminal state for %d (%s)\n", fd, strerror(errno));
if(tcgetattr(ti->ttyfd, &ti->tpreserved)){
fprintf(stderr, "Couldn't preserve terminal state for %d (%s)\n", ti->ttyfd, strerror(errno));
del_curterm(cur_term);
return -1;
}
// enter cbreak mode regardless of user preference until we've performed
// terminal interrogation. at that point, we might restore original mode.
if(cbreak_mode(fd, &ti->tpreserved)){
if(cbreak_mode(ti->ttyfd, &ti->tpreserved)){
del_curterm(cur_term);
return -1;
}
// if we already know our terminal (e.g. on the linux console), there's no
// need to send the identification queries. the controls are sufficient.
bool minimal = (qterm != TERMINAL_UNKNOWN);
if(send_initial_queries(fd, minimal)){
fprintf(stderr, "Error issuing terminal queries on %d\n", fd);
if(send_initial_queries(ti->ttyfd, minimal)){
fprintf(stderr, "Error issuing terminal queries on %d\n", ti->ttyfd);
del_curterm(cur_term);
return -1;
}
#endif
@ -839,8 +852,8 @@ int interrogate_terminfo(tinfo* ti, int fd, unsigned utf8, unsigned noaltscreen,
goto err;
}
if(nocbreak){
if(fd >= 0){
if(tcsetattr(fd, TCSANOW, &ti->tpreserved)){
if(ti->ttyfd >= 0){
if(tcsetattr(ti->ttyfd, TCSANOW, &ti->tpreserved)){
ncinputlayer_stop(&ti->input);
goto err;
}
@ -857,21 +870,20 @@ int interrogate_terminfo(tinfo* ti, int fd, unsigned utf8, unsigned noaltscreen,
}
}
bool invertsixel = false;
if(apply_term_heuristics(ti, tname, fd, qterm, &tablelen, &tableused,
&invertsixel)){
if(apply_term_heuristics(ti, tname, qterm, &tablelen, &tableused, &invertsixel)){
ncinputlayer_stop(&ti->input);
goto err;
}
build_supported_styles(ti);
if(ti->pixel_draw == NULL){
if(kittygraphs){
setup_kitty_bitmaps(ti, fd, KITTY_SELFREF);
setup_kitty_bitmaps(ti, ti->ttyfd, KITTY_SELFREF);
}
// our current sixel quantization algorithm requires at least 64 color
// registers. we make use of no more than 256. this needs to happen
// after heuristics, since the choice of sixel_init() depends on it.
if(ti->color_registers >= 64){
setup_sixel_bitmaps(ti, fd, invertsixel);
setup_sixel_bitmaps(ti, ti->ttyfd, invertsixel);
}
}
return 0;
@ -879,6 +891,7 @@ int interrogate_terminfo(tinfo* ti, int fd, unsigned utf8, unsigned noaltscreen,
err:
free(ti->esctable);
free(ti->termversion);
del_curterm(cur_term);
return -1;
}

@ -127,6 +127,7 @@ typedef struct ncinputlayer {
// can change over the program's life (don't cache them locally).
typedef struct tinfo {
uint16_t escindices[ESCAPE_MAX]; // table of 1-biased indices into esctable
int ttyfd; // connected to true terminal, might be -1
char* esctable; // packed table of escape sequences
nccapabilities caps; // exported to the user, when requested
unsigned pixy; // total pixel geometry, height
@ -216,16 +217,14 @@ term_supported_styles(const tinfo* ti){
return ti->supported_styles;
}
// load |ti| from the terminfo database, which must already have been
// initialized. set |utf8| if we've verified UTF8 output encoding.
// set |noaltscreen| to inhibit alternate screen detection. |fd| ought
// be connected to a terminal device, or -1 if no terminal is available.
// if already *certain* of the terminal type (basically, if it's the Linux
// console, identified via ioctl(2)s), pass it as qterm; otherwise use
// TERMINAL_UNKNOWN. |stats| may be NULL; either way, it will be handed to the
// input layer so that its stats can be recorded.
int interrogate_terminfo(tinfo* ti, int fd, unsigned utf8,
unsigned noaltscreen, unsigned nocbreak,
// prepare |ti| from the terminfo database and other sources. set |utf8| if
// we've verified UTF8 output encoding. set |noaltscreen| to inhibit alternate
// screen detection. |stats| may be NULL; either way, it will be handed to the
// input layer so that its stats can be recorded. if |termtype| is not NULL, it
// will be used to look up the terminfo database entry; the value of TERM is
// otherwise used.
int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out,
unsigned utf8, unsigned noaltscreen, unsigned nocbreak,
unsigned nonewfonts, int* cursor_y, int* cursor_x,
struct ncsharedstats* stats);

Loading…
Cancel
Save