Initialize Readline on demand

Eliminate NCDIRECT_OPTION_NO_READLINE (it was only introduced in
Notcurses 2.1.6). Add a new function, ncdirect_readline(). Upon
first call to this function, initialize libreadline. Destroy
libreadline in ncdirect_stop() iff we initialized it. Add
hilodirect guessing game PoC from #1325. Rename
notcurses_directmode.3 to notcurses_direct.3. Closes #1326.
This commit is contained in:
nick black 2021-02-03 00:18:10 -05:00
parent 5afa3f14a9
commit b3569b6aef
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC
11 changed files with 91 additions and 28 deletions

View File

@ -4,6 +4,10 @@ rearrangements of Notcurses.
* 2.1.8 (not yet released): * 2.1.8 (not yet released):
* The `notcurses-tetris` binary has been renamed `nctetris`. * The `notcurses-tetris` binary has been renamed `nctetris`.
* The new function `channel_set_palindex()` has been added. * 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): * 2.1.7 (2021-01-21):
* Notcurses has been split into two libraries, `notcurses-core` and * Notcurses has been split into two libraries, `notcurses-core` and

View File

@ -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. | | mlterm | ❌ |? |`TERM=mlterm-256color` | Do not set `COLORTERM`. `mlterm-direct` gives strange results. |
| PuTTY | ❌ |❌ |`TERM=putty-256color` `COLORTERM=24bit` | | | PuTTY | ❌ |❌ |`TERM=putty-256color` `COLORTERM=24bit` | |
| rxvt | ? |? | | | | 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. | | 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` | | | st ("suckless") | ✅ |? |`TERM=st-256color` `COLORTERM=24bit` | |
| Terminator | ? |? | ? | | | Terminator | ? |? | ? | |

View File

@ -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 // 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 // invoking setlocale(), this behavior shouldn't be an issue unless you're
// doing something weird (setting a locale not based on LANG). // 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, // *Don't* place the terminal into cbreak mode (see tcgetattr(3)). By default,
// echo and line buffering are turned off. // 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. // Release 'nc' and any associated resources. 0 on success, non-0 on failure.
int ncdirect_stop(struct ncdirect* nc); 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: are available for direct mode:
```c ```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_fg_rgb(struct ncdirect* nc, unsigned rgb);
int ncdirect_bg_rgb(struct ncdirect* nc, unsigned rgb); int ncdirect_bg_rgb(struct ncdirect* nc, unsigned rgb);

View File

@ -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 is still necessary to have a valid `TERM` environment variable identifying a
valid terminfo database entry for the running terminal. 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. man page.
Enter direct mode with a call to `ncdirect_init()`. It takes three arguments: Enter direct mode with a call to `ncdirect_init()`. It takes three arguments:

View File

@ -44,7 +44,7 @@
<a href="notcurses_cell.3.html">notcurses_cell</a>—operations on <tt>nccell</tt> objects<br/> <a href="notcurses_cell.3.html">notcurses_cell</a>—operations on <tt>nccell</tt> objects<br/>
<a href="notcurses_channels.3.html">notcurses_channels</a>—operations on the <tt>channel</tt> type<br/> <a href="notcurses_channels.3.html">notcurses_channels</a>—operations on the <tt>channel</tt> type<br/>
<a href="notcurses_core.3.html">notcurses_core</a>—linking against a minimal Notcurses<br/> <a href="notcurses_core.3.html">notcurses_core</a>—linking against a minimal Notcurses<br/>
<a href="notcurses_directmode.3.html">notcurses_directmode</a>—minimal notcurses instances for styling text<br/> <a href="notcurses_direct.3.html">notcurses_direct</a>—minimal notcurses instances for styling text<br/>
<a href="notcurses_fade.3.html">notcurses_fade</a>—fading and pulsing for <tt>ncplane</tt>s<br/> <a href="notcurses_fade.3.html">notcurses_fade</a>—fading and pulsing for <tt>ncplane</tt>s<br/>
<a href="notcurses_fds.3.html">notcurses_fds</a>—dumping file descriptors/subprocesses to <tt>ncplane</tt>s<br/> <a href="notcurses_fds.3.html">notcurses_fds</a>—dumping file descriptors/subprocesses to <tt>ncplane</tt>s<br/>
<a href="notcurses_init.3.html">notcurses_init</a>—initialization<br/> <a href="notcurses_init.3.html">notcurses_init</a>—initialization<br/>

View File

@ -47,7 +47,7 @@ for the actual terminal must be available.
**ncdirect_init(3)** makes available a very restricted subset of **ncdirect_init(3)** makes available a very restricted subset of
Notcurses functionality. This subset is intended to be interleaved with Notcurses functionality. This subset is intended to be interleaved with
user-generated output, and is limited to coloring and styling. Direct mode is user-generated output, and is limited to coloring and styling. Direct mode is
documented in **notcurses_directmode(3)**. documented in **notcurses_direct(3)**.
## Output ## Output
@ -165,7 +165,7 @@ order to turn most error returns into exceptions.
**notcurses_capabilities(3)**, **notcurses_capabilities(3)**,
**notcurses_cell(3)**, **notcurses_cell(3)**,
**notcurses_channels(3)**, **notcurses_channels(3)**,
**notcurses_directmode(3)**, **notcurses_direct(3)**,
**notcurses_fade(3)**, **notcurses_fade(3)**,
**notcurses_fds(3)**, **notcurses_fds(3)**,
**notcurses_init(3)**, **notcurses_init(3)**,

View File

@ -1,10 +1,10 @@
% ncdirect_init(3) % notcurses_direct(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.1.7 % v2.1.7
# NAME # NAME
ncdirect_init - minimal notcurses instances for styling text notcurses_direct - minimal notcurses instances for styling text
# SYNOPSIS # SYNOPSIS
@ -13,7 +13,6 @@ ncdirect_init - minimal notcurses instances for styling text
#define NCDIRECT_OPTION_INHIBIT_SETLOCALE 0x0001ull #define NCDIRECT_OPTION_INHIBIT_SETLOCALE 0x0001ull
#define NCDIRECT_OPTION_INHIBIT_CBREAK 0x0002ull #define NCDIRECT_OPTION_INHIBIT_CBREAK 0x0002ull
#define NCDIRECT_OPTION_NO_READLINE 0x0004ull
#define NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS 0x0008ull #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***);** **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 # DESCRIPTION
**ncdirect_init** prepares the **FILE** provided as **fp** for colorizing and **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 will place the terminal into cbreak mode (i.e. disabling echo and line
buffering; see **tcgetattr(3)**). 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 * **NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS**: A signal handler will usually be
installed for **SIGINT**, **SIGQUIT**, **SIGSEGV**, **SIGTERM**, and installed for **SIGINT**, **SIGQUIT**, **SIGSEGV**, **SIGTERM**, and
**SIGABRT**, cleaning up the terminal on such exceptions. With this flag, **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 Attempting to e.g. move up while on the top row will return 0, but have no
effect. 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 # RETURN VALUES
**ncdirect_init** returns **NULL** on failure. Otherwise, the return value **ncdirect_init** returns **NULL** on failure. Otherwise, the return value

View File

@ -25,11 +25,6 @@ typedef struct ncplane ncdirectv;
// echo and input's line buffering are turned off. // echo and input's line buffering are turned off.
#define NCDIRECT_OPTION_INHIBIT_CBREAK 0x0002ull #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 // 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 // restores the screen, and then calls the old signal handler. Set to inhibit
// registration of these signal handlers. Chosen to match fullscreen mode. // 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. // 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); 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 // Direct mode. This API can be used to colorize and stylize output generated
// outside of notcurses, without ever calling notcurses_render(). These should // outside of notcurses, without ever calling notcurses_render(). These should
// not be intermixed with standard Notcurses rendering. // not be intermixed with standard Notcurses rendering.

View File

@ -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 // no terminfo capability for this. dangerous--it involves writing controls to
// the terminal, and then reading a response. many things can distupt this // 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){ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){
struct termios termio, oldtermios; struct termios termio, oldtermios;
// this is only meaningful for real terminals // this is only meaningful for real terminals
@ -321,7 +321,7 @@ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){
return -1; return -1;
} }
memcpy(&oldtermios, &termio, sizeof(termio)); 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. // it got changed by the client code since then, duck into cbreak mode anew.
termio.c_lflag &= ~(ICANON | ECHO); termio.c_lflag &= ~(ICANON | ECHO);
if(tcsetattr(n->ctermfd, TCSAFLUSH, &termio)){ if(tcsetattr(n->ctermfd, TCSAFLUSH, &termio)){
@ -597,7 +597,7 @@ static int
ncdirect_stop_minimal(void* vnc){ ncdirect_stop_minimal(void* vnc){
ncdirect* nc = static_cast<ncdirect*>(vnc); ncdirect* nc = static_cast<ncdirect*>(vnc);
int ret = drop_signals(nc); int ret = drop_signals(nc);
if(!(nc->flags & NCDIRECT_OPTION_NO_READLINE)){ if(nc->initialized_readline){
rl_deprep_terminal(); rl_deprep_terminal();
} }
if(nc->tcache.op && term_emit("op", nc->tcache.op, nc->ttyfp, true)){ 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)){ if(interrogate_terminfo(&ret->tcache, shortname_term)){
goto err; goto err;
} }
ret->channels = 0;
ncdirect_set_styles(ret, 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; return ret;
err: err:
@ -701,6 +695,16 @@ int ncdirect_stop(ncdirect* nc){
return ret; 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 static inline int
ncdirect_style_emit(ncdirect* n, unsigned stylebits, FILE* out){ ncdirect_style_emit(ncdirect* n, unsigned stylebits, FILE* out){
int r = -1; int r = -1;

View File

@ -310,6 +310,7 @@ typedef struct ncdirect {
// typical order. we detect it the first time ncdirect_cursor_yx() is called. // typical order. we detect it the first time ncdirect_cursor_yx() is called.
bool detected_cursor_inversion; // have we performed inversion testing? bool detected_cursor_inversion; // have we performed inversion testing?
bool inverted_cursor; // does the terminal return inverted coordinates? 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 uint64_t flags; // copied in ncdirect_init() from param
} ncdirect; } ncdirect;

39
src/poc/hilodirect.c Normal file
View File

@ -0,0 +1,39 @@
#include <limits.h>
#include <notcurses/direct.h>
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;
}