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.
pull/1328/head
nick black 3 years ago
parent 5afa3f14a9
commit b3569b6aef
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -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

@ -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 | ? |? | ? | |

@ -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);

@ -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:

@ -44,7 +44,7 @@
<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_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_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/>

@ -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)**,

@ -1,10 +1,10 @@
% ncdirect_init(3)
% notcurses_direct(3)
% nick black <nickblack@linux.com>
% 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

@ -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.

@ -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<ncdirect*>(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;

@ -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;

@ -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;
}
Loading…
Cancel
Save