diff --git a/NEWS.md b/NEWS.md index 20e6fcfb3..1040f75a5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,10 @@ rearrangements of Notcurses. * 2.1.8 (not yet released): * The `notcurses-tetris` binary has been renamed `nctetris`. * The new function `channel_set_palindex()` has been added. + * `NCDIRECT_OPTION_NO_READLINE` has been removed after a short life. + * `ncdirect_readline()` has been added. The first time used, it initializes + Readline. Readline will be destroyed by ncdirect_stop() if it was ever + initialized. * 2.1.7 (2021-01-21): * Notcurses has been split into two libraries, `notcurses-core` and diff --git a/TERMINALS.md b/TERMINALS.md index a9bf8804f..2be22e1dc 100644 --- a/TERMINALS.md +++ b/TERMINALS.md @@ -29,7 +29,7 @@ relies on the font. Patches to correct/complete this table are very welcome! | mlterm | ❌ |? |`TERM=mlterm-256color` | Do not set `COLORTERM`. `mlterm-direct` gives strange results. | | PuTTY | ❌ |❌ |`TERM=putty-256color` `COLORTERM=24bit` | | | rxvt | ? |? | | | -| Sakura | ✅ |? |`TERM=vte-256color` `COLORTERM=24bit` | No terminfo entry? | +| Sakura | ✅ |? |`TERM=vte-256color` `COLORTERM=24bit` | VTE-derived, no terminfo entry. | | GNU Screen | ❌ |? |`TERM=screen.OLDTERM` | Must be compiled with `--enable-256color`. `TERM` should typically be `screen.` suffixed by the appropriate `TERM` value for the true connected terminal, e.g. `screen.vte-256color`. See below. | | st ("suckless") | ✅ |? |`TERM=st-256color` `COLORTERM=24bit` | | | Terminator | ? |? | ? | | diff --git a/USAGE.md b/USAGE.md index 5e2e48d29..799f7e3cd 100644 --- a/USAGE.md +++ b/USAGE.md @@ -334,11 +334,16 @@ struct ncdirect* ncdirect_core_init(const char* termtype, FILE* fp, uint64_t fla // prior to notcurses_init(), you should not set this bit. Even if you are // invoking setlocale(), this behavior shouldn't be an issue unless you're // doing something weird (setting a locale not based on LANG). -#define NCDIRECT_OPTION_INHIBIT_SETLOCALE 0x0001ull +#define NCDIRECT_OPTION_INHIBIT_SETLOCALE 0x0001ull // *Don't* place the terminal into cbreak mode (see tcgetattr(3)). By default, // echo and line buffering are turned off. -#define NCDIRECT_OPTION_INHIBIT_CBREAK 0x0002ull +#define NCDIRECT_OPTION_INHIBIT_CBREAK 0x0002ull + +// We typically install a signal handler for SIG{INT, SEGV, ABRT, QUIT} that +// restores the screen, and then calls the old signal handler. Set to inhibit +// registration of these signal handlers. Chosen to match fullscreen mode. +#define NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS 0x0008ull // Release 'nc' and any associated resources. 0 on success, non-0 on failure. int ncdirect_stop(struct ncdirect* nc); @@ -348,6 +353,12 @@ This context must be destroyed using `ncdirect_stop()`. The following functions are available for direct mode: ```c +// Read a (heap-allocated) line of text using the Readline library Initializes +// Readline the first time it's called. For input to be echoed to the terminal, +// it is necessary that NCDIRECT_OPTION_INHIBIT_CBREAK be provided to +// ncdirect_init(). Returns NULL on error. +API char* ncdirect_readline(struct ncdirect* nc, const char* prompt); + int ncdirect_fg_rgb(struct ncdirect* nc, unsigned rgb); int ncdirect_bg_rgb(struct ncdirect* nc, unsigned rgb); diff --git a/doc/examples/src/directmode.md b/doc/examples/src/directmode.md index 6b98c8ff8..080a11c01 100644 --- a/doc/examples/src/directmode.md +++ b/doc/examples/src/directmode.md @@ -7,7 +7,7 @@ rendering; output is intended to appear immediately (subject to buffering). It is still necessary to have a valid `TERM` environment variable identifying a valid terminfo database entry for the running terminal. -The authoritative reference for direct mode is the `notcurses_directmode(3)` +The authoritative reference for direct mode is the `notcurses_direct(3)` man page. Enter direct mode with a call to `ncdirect_init()`. It takes three arguments: diff --git a/doc/man/index.html b/doc/man/index.html index 0a07987cf..668dfcd6e 100644 --- a/doc/man/index.html +++ b/doc/man/index.html @@ -44,7 +44,7 @@ notcurses_cell—operations on nccell objects
notcurses_channels—operations on the channel type
notcurses_core—linking against a minimal Notcurses
- notcurses_directmode—minimal notcurses instances for styling text
+ notcurses_direct—minimal notcurses instances for styling text
notcurses_fade—fading and pulsing for ncplanes
notcurses_fds—dumping file descriptors/subprocesses to ncplanes
notcurses_init—initialization
diff --git a/doc/man/man3/notcurses.3.md b/doc/man/man3/notcurses.3.md index d7a11cbcd..1a3c0b6bd 100644 --- a/doc/man/man3/notcurses.3.md +++ b/doc/man/man3/notcurses.3.md @@ -47,7 +47,7 @@ for the actual terminal must be available. **ncdirect_init(3)** makes available a very restricted subset of Notcurses functionality. This subset is intended to be interleaved with user-generated output, and is limited to coloring and styling. Direct mode is -documented in **notcurses_directmode(3)**. +documented in **notcurses_direct(3)**. ## Output @@ -165,7 +165,7 @@ order to turn most error returns into exceptions. **notcurses_capabilities(3)**, **notcurses_cell(3)**, **notcurses_channels(3)**, -**notcurses_directmode(3)**, +**notcurses_direct(3)**, **notcurses_fade(3)**, **notcurses_fds(3)**, **notcurses_init(3)**, diff --git a/doc/man/man3/notcurses_directmode.3.md b/doc/man/man3/notcurses_direct.3.md similarity index 92% rename from doc/man/man3/notcurses_directmode.3.md rename to doc/man/man3/notcurses_direct.3.md index 6fc19c310..46db0950d 100644 --- a/doc/man/man3/notcurses_directmode.3.md +++ b/doc/man/man3/notcurses_direct.3.md @@ -1,10 +1,10 @@ -% ncdirect_init(3) +% notcurses_direct(3) % nick black % v2.1.7 # NAME -ncdirect_init - minimal notcurses instances for styling text +notcurses_direct - minimal notcurses instances for styling text # SYNOPSIS @@ -13,7 +13,6 @@ ncdirect_init - minimal notcurses instances for styling text #define NCDIRECT_OPTION_INHIBIT_SETLOCALE 0x0001ull #define NCDIRECT_OPTION_INHIBIT_CBREAK 0x0002ull -#define NCDIRECT_OPTION_NO_READLINE 0x0004ull #define NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS 0x0008ull ``` @@ -85,6 +84,8 @@ ncdirect_init - minimal notcurses instances for styling text **int ncdirect_render_image(struct ncdirect* ***n***, const char* ***filename***, ncblitter_e ***blitter***, ncscale_e ***scale***);** +**char* ncdirect_readline(struct ncdirect* ***n***, const char* ***prompt***);** + # DESCRIPTION **ncdirect_init** prepares the **FILE** provided as **fp** for colorizing and @@ -108,10 +109,6 @@ The following flags are defined: will place the terminal into cbreak mode (i.e. disabling echo and line buffering; see **tcgetattr(3)**). -* **NCDIRECT_OPTION_NO_READLINE**: Unless this flag is set, **ncdirect_init** - will initialize GNU Readline so that it can be safely used together with - direct mode. With this flag, no calls are made to GNU Readline. - * **NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS**: A signal handler will usually be installed for **SIGINT**, **SIGQUIT**, **SIGSEGV**, **SIGTERM**, and **SIGABRT**, cleaning up the terminal on such exceptions. With this flag, @@ -141,6 +138,12 @@ output stream, taking effect immediately. Attempting to e.g. move up while on the top row will return 0, but have no effect. +**ncdirect_readline** uses the Readline library to read a (heap-allocated) +line of arbitrary length, supporting line-editing controls. For more +information, consult **readline(3)**. If you want input echoed to the +terminal while using **ncdirect_readline**, **NCDIRECT_OPTION_INHIBIT_CBREAK** +must be supplied to **ncdirect_init**. + # RETURN VALUES **ncdirect_init** returns **NULL** on failure. Otherwise, the return value diff --git a/include/notcurses/direct.h b/include/notcurses/direct.h index 19dc9f93f..46f3f9a7d 100644 --- a/include/notcurses/direct.h +++ b/include/notcurses/direct.h @@ -25,11 +25,6 @@ typedef struct ncplane ncdirectv; // echo and input's line buffering are turned off. #define NCDIRECT_OPTION_INHIBIT_CBREAK 0x0002ull -// We typically initialize the GNU Readline library in a way that works with -// ncdirect. If you intend no use of GNU Readline, this flag will inhibit any -// such setup/teardown. -#define NCDIRECT_OPTION_NO_READLINE 0x0004ull - // We typically install a signal handler for SIG{INT, SEGV, ABRT, QUIT} that // restores the screen, and then calls the old signal handler. Set to inhibit // registration of these signal handlers. Chosen to match fullscreen mode. @@ -48,6 +43,12 @@ API struct ncdirect* ncdirect_init(const char* termtype, FILE* fp, uint64_t flag // allowing for a svelter binary. Link with notcurses-core if this is used. API struct ncdirect* ncdirect_core_init(const char* termtype, FILE* fp, uint64_t flags); +// Read a (heap-allocated) line of text using the Readline library Initializes +// Readline the first time it's called. For input to be echoed to the terminal, +// it is necessary that NCDIRECT_OPTION_INHIBIT_CBREAK be provided to +// ncdirect_init(). Returns NULL on error. +API char* ncdirect_readline(struct ncdirect* nc, const char* prompt); + // Direct mode. This API can be used to colorize and stylize output generated // outside of notcurses, without ever calling notcurses_render(). These should // not be intermixed with standard Notcurses rendering. diff --git a/src/lib/direct.cpp b/src/lib/direct.cpp index 36c78e833..8c18ff2bd 100644 --- a/src/lib/direct.cpp +++ b/src/lib/direct.cpp @@ -309,7 +309,7 @@ detect_cursor_inversion_wrapper(ncdirect* n, int* y, int* x){ // no terminfo capability for this. dangerous--it involves writing controls to // the terminal, and then reading a response. many things can distupt this -// non-atomic procedure. +// non-atomic procedure, leading to unexpected results. a garbage function. int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){ struct termios termio, oldtermios; // this is only meaningful for real terminals @@ -321,7 +321,7 @@ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){ return -1; } memcpy(&oldtermios, &termio, sizeof(termio)); - // we should already be in cbreak mode from ncdirect_init(), but just in case + // 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->ctermfd, TCSAFLUSH, &termio)){ @@ -597,7 +597,7 @@ static int ncdirect_stop_minimal(void* vnc){ ncdirect* nc = static_cast(vnc); int ret = drop_signals(nc); - if(!(nc->flags & NCDIRECT_OPTION_NO_READLINE)){ + if(nc->initialized_readline){ rl_deprep_terminal(); } if(nc->tcache.op && term_emit("op", nc->tcache.op, nc->ttyfp, true)){ @@ -673,13 +673,7 @@ ncdirect* ncdirect_core_init(const char* termtype, FILE* outfp, uint64_t flags){ if(interrogate_terminfo(&ret->tcache, shortname_term)){ goto err; } - ret->channels = 0; ncdirect_set_styles(ret, 0); - if(!(flags & NCDIRECT_OPTION_NO_READLINE)){ - rl_outstream = stderr; - rl_instream = stdin; - rl_prep_terminal(1); // 1 == read 8-bit input - } return ret; err: @@ -701,6 +695,16 @@ int ncdirect_stop(ncdirect* nc){ return ret; } +char* ncdirect_readline(ncdirect* n, const char* prompt){ + if(!n->initialized_readline){ + rl_outstream = n->ttyfp; + rl_instream = stdin; + rl_prep_terminal(1); // 1 == read 8-bit input + n->initialized_readline = true; + } + return readline(prompt); +} + static inline int ncdirect_style_emit(ncdirect* n, unsigned stylebits, FILE* out){ int r = -1; diff --git a/src/lib/internal.h b/src/lib/internal.h index ed83f3f62..ab3bd5213 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -310,6 +310,7 @@ typedef struct ncdirect { // 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/poc/hilodirect.c b/src/poc/hilodirect.c new file mode 100644 index 000000000..1004cb172 --- /dev/null +++ b/src/poc/hilodirect.c @@ -0,0 +1,39 @@ +#include +#include + +int main(void){ + srand(time(NULL)); // gross + long guess, secret = random(); + struct ncdirect* n = ncdirect_core_init(NULL, stdout, NCDIRECT_OPTION_INHIBIT_CBREAK); + if(n == NULL){ + return EXIT_FAILURE; + } + int r = 0; + do{ + if(!(r |= (ncdirect_set_fg_default(n)))){ + if(!(r |= (printf("Guess the long: ") < 0))){ + if(!fflush(stdout)){ + int rargs = scanf("%ld", &guess); // super shitty to the max + if(rargs != 1){ + fprintf(stderr, "Die, infidel!\n"); + ncdirect_stop(n); + return EXIT_FAILURE; + } + int offoom = labs(__builtin_clzl(guess) - __builtin_clzl(secret)); + if(guess > secret){ + r |= ncdirect_set_fg_rgb8(n, 0x40, 0x80, offoom * 6); + r |= (printf("\tLOL jabronies guess %ld. Too high!\n", guess) < 0); + }else if(guess < secret){ + r |= ncdirect_set_fg_rgb8(n, offoom * 6, 0x80, 0x40); + r |= (printf("\tSpineless worm! %ld? Too low!\n", guess) < 0); + } + } + } + } + }while(guess != secret && !r); + if(r || printf("You enjoy 20/20 vision into the minds of antimen!\n") < 0){ + ncdirect_stop(n); + return EXIT_FAILURE; + } + return ncdirect_stop(n) ? EXIT_FAILURE : EXIT_SUCCESS; +}