From d2c968b94888d47d385ff8b728bcbea2cd7bd5e9 Mon Sep 17 00:00:00 2001 From: nick black Date: Fri, 8 May 2020 22:33:39 -0400 Subject: [PATCH] notcurses_init(): interpret NULL as /dev/tty #571 --- NEWS.md | 3 +++ USAGE.md | 3 ++- doc/man/man3/notcurses_init.3.md | 14 ++++++++------ include/notcurses/notcurses.h | 3 ++- src/demo/demo.c | 2 +- src/lib/internal.h | 1 + src/lib/notcurses.c | 15 ++++++++++++--- 7 files changed, 29 insertions(+), 12 deletions(-) diff --git a/NEWS.md b/NEWS.md index b7d50974f..41e183520 100644 --- a/NEWS.md +++ b/NEWS.md @@ -13,6 +13,9 @@ rearrangements of Notcurses. notcurses*` instead of a `struct ncplane`, as they create their own planes. * `ncplane_set_channels()` and `ncplane_set_attr()` have been added to allow `ncplane` attributes to be set directly and in toto. + * `NULL` can now be passed as the `FILE*` argument to `notcurses_init()` and + `ncdirect_init()`. In this case, a new `FILE*` will be created using + `/dev/tty`. If the `FILE*` cannot be created, an error will be returned. * 1.3.4 (2020-05-07) * `notcurses_lex_margins()` has been added to lex margins expressed in either diff --git a/USAGE.md b/USAGE.md index 60ca5c6cb..355d45ea2 100644 --- a/USAGE.md +++ b/USAGE.md @@ -108,7 +108,8 @@ typedef struct notcurses_options { int notcurses_lex_margins(const char* op, notcurses_options* opts); // Initialize a notcurses context on the connected terminal at 'fp'. 'fp' must -// be a tty. You'll usually want stdout. Returns NULL on error, including any +// be a tty. You'll usually want stdout. NULL can be supplied for 'fp', in +// which case /dev/tty will be opened. Returns NULL on error, including any // failure initializing terminfo. struct notcurses* notcurses_init(const notcurses_options* opts, FILE* fp); diff --git a/doc/man/man3/notcurses_init.3.md b/doc/man/man3/notcurses_init.3.md index edfc96770..b75a36609 100644 --- a/doc/man/man3/notcurses_init.3.md +++ b/doc/man/man3/notcurses_init.3.md @@ -29,14 +29,16 @@ typedef struct notcurses_options { # DESCRIPTION -**notcurses_init** prepares the **FILE** provided as **fp** (which must be -attached to a terminal) for cursor-addressable (multiline) mode. The +**notcurses_init** prepares the terminal for cursor-addressable (multiline) +mode. The **FILE** provided as **fp** must be writable and attached to a +terminal, or **NULL**. If it is **NULL**, **/dev/tty** will be opened. The **struct notcurses_option** passed as **opts** controls behavior. Only one instance should be associated with a given terminal at a time, though it is no -problem to have multiple instances in a given process. On success, a pointer to -a valid **struct notcurses** is returned. **NULL** is returned on failure. -Before the process exits, **notcurses_stop(3)** should be called to reset the -terminal and free up resources. +problem to have multiple instances in a given process. + +On success, a pointer to a valid **struct notcurses** is returned. **NULL** is +returned on failure. Before the process exits, **notcurses_stop(3)** should be +called to reset the terminal and free up resources. An appropriate **terminfo(5)** entry must exist for the terminal. This entry is usually selected using the value of the **TERM** environment variable (see diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 5c22e72a5..f57e32609 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -793,7 +793,8 @@ typedef struct notcurses_options { API int notcurses_lex_margins(const char* op, notcurses_options* opts); // Initialize a notcurses context on the connected terminal at 'fp'. 'fp' must -// be a tty. You'll usually want stdout. Returns NULL on error, including any +// be a tty. You'll usually want stdout. NULL can be supplied for 'fp', in +// which case /dev/tty will be opened. Returns NULL on error, including any // failure initializing terminfo. API struct notcurses* notcurses_init(const notcurses_options* opts, FILE* fp); diff --git a/src/demo/demo.c b/src/demo/demo.c index d08f414e8..6b588aafc 100644 --- a/src/demo/demo.c +++ b/src/demo/demo.c @@ -447,7 +447,7 @@ int main(int argc, char** argv){ struct timespec starttime; clock_gettime(CLOCK_MONOTONIC, &starttime); struct notcurses* nc; - if((nc = notcurses_init(&nopts, stdout)) == NULL){ + if((nc = notcurses_init(&nopts, NULL)) == NULL){ return EXIT_FAILURE; } if(notcurses_mouse_enable(nc)){ diff --git a/src/lib/internal.h b/src/lib/internal.h index e3c5c5ed7..71d647a51 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -332,6 +332,7 @@ typedef struct notcurses { palette256 palette; // 256-indexed palette can be used instead of/with RGB bool palette_damage[NCPALETTESIZE]; struct esctrie* inputescapes; // trie of input escapes -> ncspecial_keys + bool ownttyfp; // do we own ttyfp (and thus must close it?) } notcurses; void sigwinch_handler(int signo); diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index cff3381c7..dbb97fea1 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -815,9 +815,6 @@ ncdirect* ncdirect_init(const char* termtype, FILE* outfp){ } notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ - if(outfp == NULL){ - outfp = stdout; - } notcurses_options defaultopts; memset(&defaultopts, 0, sizeof(defaultopts)); if(!opts){ @@ -838,6 +835,14 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ if(ret == NULL){ return ret; } + bool own_outfp = false; + if(outfp == NULL){ + if((outfp = fopen("/dev/tty", "wbe")) == NULL){ + free(ret); + return NULL; + } + own_outfp = true; + } ret->margin_t = opts->margin_t; ret->margin_b = opts->margin_b; ret->margin_l = opts->margin_l; @@ -847,6 +852,7 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ reset_stats(&ret->stats); reset_stats(&ret->stashstats); ret->ttyfp = outfp; + ret->ownttyfp = own_outfp; ret->renderfp = opts->renderfp; ret->inputescapes = NULL; ret->ttyinfp = stdin; // FIXME @@ -1007,6 +1013,9 @@ int notcurses_stop(notcurses* nc){ free(nc->rstate.mstream); input_free_esctrie(&nc->inputescapes); stash_stats(nc); + if(nc->ownttyfp){ + ret |= fclose(nc->ttyfp); + } if(!nc->suppress_banner){ if(nc->stashstats.renders){ char totalbuf[BPREFIXSTRLEN + 1];