notcurses_fds(3)
π
notcurses_fds β the notcurses man page, bestie edition
okay so picture this: it's notcurses_fds(3), and it's all about dumping file descriptors and subprocesses onto planes. yes queen, that's literally the whole assignment. π
β¨ SYNOPSIS β¨
#include <notcurses/notcurses.h>
struct ncplane;
struct ncfdplane;
struct ncsubproc;
typedef struct ncfdplane_options {
void* curry; // parameter provided to callbacks
bool follow; // keep reading after hitting end?
} ncfdplane_options;
typedef struct ncsubproc_options {
void* curry; // parameter provided to callbacks
uint64_t restart_period; // restart after exit
} ncsubproc_options;
typedef int(ncfdplane_callback)(struct ncfdplane n, const void* buf,
size_t s, void* curry);
typedef int(ncfdplane_done_cb)(struct ncfdplane n, int fderrno, void*
curry);
struct ncfdplane* ncfdplane_create(struct ncplane* n, const
ncfdplane_options* opts, int fd, ncfdplane_callback cbfxn,
ncfdplane_done_cb donecbfxn);
struct ncplane* ncfdplane_plane(struct ncfdplane* n);
int ncfdplane_destroy(struct ncfdplane* n);
struct ncsubproc* ncsubproc_createv(struct ncplane* n, const
ncsubproc_options* opts, const char* bin, char* const arg[],
ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn);
struct ncsubproc* ncsubproc_createvp(struct ncplane* n, const
ncsubproc_options* opts, const char* bin, char* const arg[],
ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn);
struct ncsubproc* ncsubproc_createvpe(struct ncplane* n, const
ncsubproc_options* opts, const char* bin, char* const arg[], char* const
env[], ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn);
struct ncplane* ncsubproc_plane(struct ncsubproc* n);
int ncsubproc_destroy(struct ncsubproc* n);
πΈ DESCRIPTION πΈ
so these widgets? they make a file descriptor get read all the way until EOF, and then everything that comes out gets written to a scrolling ncplane. slay. π₯ and like, the reading happens in a notcurses-managed context β but bestie, the particulars of that context are not defined and you should not depend on them, that's a hard no. that context is gonna invoke the callbacks you provided with the data it read. essentially, these are just portable interfaces to asynchronous reading, with ncsubproc also doing subprocess management on top. iconic behavior honestly. π
if ncsubproc_destroy gets called before the subprocess has exited, it will be sent a SIGKILL. π yeah. and if ncsubproc_destroy or ncfdplane_destroy is called while a callback is being invoked, the destroy function will block until the callback is done being invoked. no cap. if a user callback returns non-0, the calling object will destroy itself. and if a user callback calls the relevant destroy function itself, the thread will exit as if non-0 had been returned, and the ncsubproc's resources will at that time be reclaimed.
β οΈ okay this part is the real one: it is essential that the destroy function be called once and only once, whether it's from within the thread's context, or external to that context. one. and. only. once. periodt. π
π¦ NOTES π¦
ncsubproc makes use of pidfds and pidfd_send_signal(2), and thus makes reliable use of signals β meaning it will never target a process other than the true subprocess. genuinely so reassuring, we love a responsible king. π«Ά
π RETURN VALUES π
(empty, bestie β the original page says nothing here, so neither do i π)
β SEE ALSO β
π AUTHORS π
nick black β nickblack@linux.com β¨
okay that's the whole page, mwah π this one's v3.0.17 of the manual, straight from the notcurses_fds(3) source β go read the code bestie, it's cute. π
π¦ where this page comes from
- alpine 3.24: notcurses-doc 3.0.17-r0 Β· Apache-2.0
- ubuntu resolute: libnotcurses-core-dev 3.0.17+dfsg-3 Β· Apache-2.0
- ubuntu stonking: libnotcurses-core-dev 3.0.17+dfsg-3 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms