diff --git a/src/lib/direct.cpp b/src/lib/direct.cpp index 08c8fc795..6a971b8e4 100644 --- a/src/lib/direct.cpp +++ b/src/lib/direct.cpp @@ -426,7 +426,12 @@ ncdirect* ncdirect_init(const char* termtype, FILE* outfp){ ret->ttyfp = outfp; memset(&ret->palette, 0, sizeof(ret->palette)); // we don't need a controlling tty for everything we do; allow a failure here - ret->ctermfd = get_controlling_tty(ret->ttyfp); + if((ret->ctermfd = get_controlling_tty(ret->ttyfp)) >= 0){ + if(cbreak_mode(ret->ctermfd, &ret->tpreserved)){ + delete(ret); + return nullptr; + } + } int termerr; if(setupterm(termtype, ret->ctermfd, &termerr) != OK){ fprintf(stderr, "Terminfo error %d (see terminfo(3ncurses))\n", termerr); @@ -468,6 +473,7 @@ int ncdirect_stop(ncdirect* nc){ if(nc->tcache.cnorm && tty_emit("cnorm", nc->tcache.cnorm, nc->ctermfd)){ ret = -1; } + tcsetattr(nc->ctermfd, TCSANOW, &nc->tpreserved); ret |= close(nc->ctermfd); } delete(nc); diff --git a/src/lib/internal.h b/src/lib/internal.h index 61cafd712..daaf55c06 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -261,6 +261,7 @@ typedef struct ncdirect { uint16_t stylemask; // current styles bool fgdefault, bgdefault; // are FG/BG currently using default colors? bool utf8; // are we using utf-8 encoding, as hoped? + struct termios tpreserved; // terminal state upon entry } ncdirect; typedef struct notcurses { @@ -966,6 +967,8 @@ cellcmp_and_dupfar(egcpool* dampool, cell* damcell, return 1; } +int cbreak_mode(int ttyfd, struct termios* tpreserved); + #ifdef __cplusplus } #endif diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 010fbd37c..8fbf252af 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -819,6 +819,28 @@ get_tty_fd(notcurses* nc, FILE* ttyfp){ return fd; } +int cbreak_mode(int ttyfd, struct termios* tpreserved){ + if(tcgetattr(ttyfd, tpreserved)){ + fprintf(stderr, "Couldn't preserve terminal state for %d (%s)\n", ttyfd, strerror(errno)); + return -1; + } + // assume it's not a true terminal (e.g. we might be redirected to a file) + struct termios modtermios; + memcpy(&modtermios, tpreserved, sizeof(modtermios)); + // see termios(3). disabling ECHO and ICANON means input will not be echoed + // to the screen, input is made available without enter-based buffering, and + // line editing is disabled. since we have not gone into raw mode, ctrl+c + // etc. still have their typical effects. ICRNL maps return to 13 (Ctrl+M) + // instead of 10 (Ctrl+J). + modtermios.c_lflag &= (~ECHO & ~ICANON); + modtermios.c_iflag &= (~ICRNL); + if(tcsetattr(ttyfd, TCSANOW, &modtermios)){ + fprintf(stderr, "Error disabling echo / canonical on %d (%s)\n", ttyfd, strerror(errno)); + return -1; + } + return 0; +} + notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ notcurses_options defaultopts; memset(&defaultopts, 0, sizeof(defaultopts)); @@ -893,27 +915,9 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ is_linux_console(ret, !!(opts->flags & NCOPTION_NO_FONT_CHANGES)); notcurses_mouse_disable(ret); if(ret->ttyfd >= 0){ - if(tcgetattr(ret->ttyfd, &ret->tpreserved)){ - logerror(ret, "Couldn't preserve terminal state for %d (%s)\n", ret->ttyfd, strerror(errno)); + if(cbreak_mode(ret->ttyfd, &ret->tpreserved)){ free(ret); return NULL; - // assume it's not a true terminal (e.g. we might be redirected to a file) - }else{ - struct termios modtermios; - memcpy(&modtermios, &ret->tpreserved, sizeof(modtermios)); - // see termios(3). disabling ECHO and ICANON means input will not be echoed - // to the screen, input is made available without enter-based buffering, and - // line editing is disabled. since we have not gone into raw mode, ctrl+c - // etc. still have their typical effects. ICRNL maps return to 13 (Ctrl+M) - // instead of 10 (Ctrl+J). - modtermios.c_lflag &= (~ECHO & ~ICANON); - modtermios.c_iflag &= (~ICRNL); - if(tcsetattr(ret->ttyfd, TCSANOW, &modtermios)){ - fprintf(stderr, "Error disabling echo / canonical on %d (%s)\n", - ret->ttyfd, strerror(errno)); - free(ret); - return NULL; - } } } if(setup_signals(ret,