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;
+}