* first draft of ncsubproc spec * qrcode first draft #24 * demo: add qrcode demo skeleton, entries #24 * qrcode demo #24 * fedora python build changes from @dcantrell * ncplane_qrcode() works #24 * add some flash to the qrcode demo #24 * drone: use newest builders * fix up rgb PoC * drop jungle demo to 100Hz target * add fd.c * ncfd skeletons * more ncfdplane #514 * ncfdp i/o loop * ncfp: improve uinit test, write core * firm up ncfdplane #514 * fileroller PoC #514 * ncplane: allow '\n' in stream when scrolling #523pull/531/head
parent
b264de6b8f
commit
fe8034b5e0
@ -0,0 +1,39 @@
|
||||
#include "demo.h"
|
||||
#include <sys/random.h>
|
||||
|
||||
int qrcode_demo(struct notcurses* nc){
|
||||
#ifdef USE_QRCODEGEN
|
||||
char data[128];
|
||||
int dimy, dimx;
|
||||
struct ncplane* n = notcurses_stddim_yx(nc, &dimy, &dimx);
|
||||
for(int i = 0 ; i < 1024 ; ++i){
|
||||
ncplane_erase(n);
|
||||
size_t len = random() % sizeof(data) + 1;
|
||||
ssize_t got = getrandom(data, len, 0);
|
||||
if(got < 0 || (size_t)got != len){
|
||||
return -1;
|
||||
}
|
||||
if(ncplane_cursor_move_yx(n, 0, 0)){
|
||||
return -1;
|
||||
}
|
||||
if(ncplane_qrcode(n, 0, data, len) <= 0){
|
||||
return -1;
|
||||
}
|
||||
if(ncplane_cursor_move_yx(n, 0, 0)){
|
||||
return -1;
|
||||
}
|
||||
uint64_t tl = 0, bl = 0, br = 0, tr = 0;
|
||||
channels_set_fg_rgb(&tl, random() % 255 + 1, random() % 255 + 1, random() % 255 + 1);
|
||||
channels_set_fg_rgb(&tr, random() % 255 + 1, random() % 255 + 1, random() % 255 + 1);
|
||||
channels_set_fg_rgb(&bl, random() % 255 + 1, random() % 255 + 1, random() % 255 + 1);
|
||||
channels_set_fg_rgb(&br, random() % 255 + 1, random() % 255 + 1, random() % 255 + 1);
|
||||
if(ncplane_stain(n, dimy - 1, dimx - 1, tl, tr, bl, br) <= 0){
|
||||
return -1;
|
||||
}
|
||||
DEMO_RENDER(nc);
|
||||
}
|
||||
#else
|
||||
DEMO_RENDER(nc);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include "internal.h"
|
||||
|
||||
// release the memory and fd, but don't join the thread (since we might be
|
||||
// getting called within the thread's context, on a callback).
|
||||
static int
|
||||
ncfdplane_destroy_inner(ncfdplane* n){
|
||||
int ret = close(n->fd);
|
||||
free(n);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *
|
||||
ncfdplane_thread(void* vncfp){
|
||||
ncfdplane* ncfp = vncfp;
|
||||
char* buf = malloc(BUFSIZ);
|
||||
ssize_t r;
|
||||
while((r = read(ncfp->fd, buf, BUFSIZ)) >= 0){
|
||||
if(r == 0){
|
||||
break;
|
||||
}
|
||||
if( (r = ncfp->cb(ncfp, buf, r, ncfp->curry)) ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(r <= 0){
|
||||
ncfp->donecb(ncfp, r == 0 ? 0 : errno, ncfp->curry);
|
||||
}
|
||||
free(buf);
|
||||
if(ncfp->destroyed){
|
||||
ncfdplane_destroy_inner(ncfp);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ncfdplane* ncfdplane_create(ncplane* n, const ncfdplane_options* opts, int fd,
|
||||
ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn){
|
||||
if(fd < 0 || !cbfxn || !donecbfxn){
|
||||
return NULL;
|
||||
}
|
||||
ncfdplane* ret = malloc(sizeof(*ret));
|
||||
if(ret){
|
||||
ret->cb = cbfxn;
|
||||
ret->donecb = donecbfxn;
|
||||
ret->follow = opts->follow;
|
||||
ret->ncp = n;
|
||||
ret->destroyed = false;
|
||||
ncplane_set_scrolling(ret->ncp, true);
|
||||
ret->fd = fd;
|
||||
ret->curry = opts->curry;
|
||||
if(pthread_create(&ret->tid, NULL, ncfdplane_thread, ret)){
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ncplane* ncfdplane_plane(ncfdplane* n){
|
||||
return n->ncp;
|
||||
}
|
||||
|
||||
int ncfdplane_destroy(ncfdplane* n){
|
||||
int ret = 0;
|
||||
if(n){
|
||||
if(pthread_equal(pthread_self(), n->tid)){
|
||||
n->destroyed = true; // ncfdplane_destroy_inner() is called on thread exit
|
||||
}else{
|
||||
void* vret = NULL;
|
||||
pthread_cancel(n->tid);
|
||||
ret |= pthread_join(n->tid, &vret);
|
||||
ret |= ncfdplane_destroy_inner(n);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ncsubproc* ncsubproc_createv(ncplane* n, const ncsubproc_options* opts,
|
||||
const char* bin, char* const arg[],
|
||||
ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn){
|
||||
if(!cbfxn || !donecbfxn){
|
||||
return NULL;
|
||||
}
|
||||
int fd = -1;
|
||||
ncsubproc* ret = malloc(sizeof(*ret));
|
||||
if(ret){
|
||||
// FIXME launch process, create ncfdplane with pipe
|
||||
if((ret->nfp = ncfdplane_create(n, &opts->popts, fd, cbfxn, donecbfxn)) == NULL){
|
||||
// FIXME kill process
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ncsubproc* ncsubproc_createvp(ncplane* n, const ncsubproc_options* opts,
|
||||
const char* bin, char* const arg[],
|
||||
ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn){
|
||||
if(!cbfxn || !donecbfxn){
|
||||
return NULL;
|
||||
}
|
||||
int fd = -1;
|
||||
ncsubproc* ret = malloc(sizeof(*ret));
|
||||
if(ret){
|
||||
// FIXME launch process, create ncfdplane with pipe
|
||||
if((ret->nfp = ncfdplane_create(n, &opts->popts, fd, cbfxn, donecbfxn)) == NULL){
|
||||
// FIXME kill process
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ncsubproc* ncsubproc_createvpe(ncplane* n, const ncsubproc_options* opts,
|
||||
const char* bin, char* const arg[], char* const env[],
|
||||
ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn){
|
||||
if(!cbfxn || !donecbfxn){
|
||||
return NULL;
|
||||
}
|
||||
int fd = -1;
|
||||
ncsubproc* ret = malloc(sizeof(*ret));
|
||||
if(ret){
|
||||
// FIXME launch process, create ncfdplane with pipe
|
||||
if((ret->nfp = ncfdplane_create(n, &opts->popts, fd, cbfxn, donecbfxn)) == NULL){
|
||||
// FIXME kill process
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ncsubproc_destroy(ncsubproc* n){
|
||||
if(n){
|
||||
free(n);
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <notcurses/notcurses.h>
|
||||
|
||||
static bool fddone;
|
||||
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static int
|
||||
cb(struct ncfdplane* ncfd, const void* data, size_t len, void* curry){
|
||||
int ret = -1;
|
||||
if(ncplane_putstr(ncfdplane_plane(ncfd), data) >= 0){
|
||||
if(!notcurses_render(ncplane_notcurses(ncfdplane_plane(ncfd)))){
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
(void)len;
|
||||
(void)curry;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
eofcb(struct ncfdplane* ncfd, int nerrno, void* curry){
|
||||
(void)nerrno;
|
||||
(void)curry;
|
||||
pthread_mutex_lock(&lock);
|
||||
fddone = true;
|
||||
pthread_mutex_unlock(&lock);
|
||||
pthread_cond_signal(&cond);
|
||||
return ncfdplane_destroy(ncfd);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv){
|
||||
setlocale(LC_ALL, "");
|
||||
notcurses_options opts = {};
|
||||
opts.inhibit_alternate_screen = true;
|
||||
struct notcurses* nc = notcurses_init(&opts, stdout);
|
||||
struct ncplane* n = notcurses_stdplane(nc);
|
||||
int ret = -1;
|
||||
while(*++argv){
|
||||
int fd = open(*argv, O_RDONLY|O_CLOEXEC);
|
||||
if(fd < 0){
|
||||
fprintf(stderr, "Couldn't open %s (%s)\n", *argv, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
ncfdplane_options nopts = {};
|
||||
struct ncfdplane* ncfp = ncfdplane_create(n, &nopts, fd, cb, eofcb);
|
||||
pthread_mutex_lock(&lock);
|
||||
while(!fddone){
|
||||
pthread_cond_wait(&cond, &lock);
|
||||
}
|
||||
fddone = false;
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
|
||||
done:
|
||||
if(notcurses_stop(nc) || ret){
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
#include "main.h"
|
||||
#include <cerrno>
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <condition_variable>
|
||||
|
||||
std::mutex lock;
|
||||
std::condition_variable cond;
|
||||
bool inline_cancelled = false;
|
||||
bool outofline_cancelled = false;
|
||||
|
||||
int testfdcb(struct ncfdplane* ncfd, const void* buf, size_t s, void* curry){
|
||||
struct ncplane* n = ncfdplane_plane(ncfd);
|
||||
lock.lock();
|
||||
if(ncplane_putstr(n, static_cast<const char*>(buf)) <= 0){
|
||||
lock.unlock();
|
||||
return -1;
|
||||
}
|
||||
lock.unlock();
|
||||
(void)curry;
|
||||
(void)s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int testfdeof(struct ncfdplane* n, int fderrno, void* curry){
|
||||
lock.lock();
|
||||
outofline_cancelled = true;
|
||||
lock.unlock();
|
||||
cond.notify_one();
|
||||
(void)curry;
|
||||
(void)n;
|
||||
(void)fderrno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int testfdeofdestroys(struct ncfdplane* n, int fderrno, void* curry){
|
||||
lock.lock();
|
||||
inline_cancelled = true;
|
||||
int ret = ncfdplane_destroy(n);
|
||||
lock.unlock();
|
||||
cond.notify_one();
|
||||
(void)curry;
|
||||
(void)fderrno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// test ncfdplanes and ncsubprocs
|
||||
TEST_CASE("FdsAndSubprocs") {
|
||||
if(getenv("TERM") == nullptr){
|
||||
return;
|
||||
}
|
||||
notcurses_options nopts{};
|
||||
nopts.inhibit_alternate_screen = true;
|
||||
nopts.suppress_banner = true;
|
||||
FILE* outfp_ = fopen("/dev/tty", "wb");
|
||||
REQUIRE(outfp_);
|
||||
struct notcurses* nc_ = notcurses_init(&nopts, outfp_);
|
||||
REQUIRE(nc_);
|
||||
struct ncplane* n_ = notcurses_stdplane(nc_);
|
||||
REQUIRE(n_);
|
||||
REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
|
||||
|
||||
// destroy the ncfdplane outside of its own context
|
||||
SUBCASE("FdPlaneDestroyOffline") {
|
||||
REQUIRE(!outofline_cancelled);
|
||||
ncfdplane_options opts{};
|
||||
int fd = open("/etc/sysctl.conf", O_RDONLY|O_CLOEXEC);
|
||||
REQUIRE(0 <= fd);
|
||||
auto ncfdp = ncfdplane_create(n_, &opts, fd, testfdcb, testfdeof);
|
||||
REQUIRE(ncfdp);
|
||||
std::unique_lock<std::mutex> lck(lock);
|
||||
CHECK(0 == notcurses_render(nc_));
|
||||
while(!outofline_cancelled){
|
||||
cond.wait(lck);
|
||||
}
|
||||
CHECK(0 == ncfdplane_destroy(ncfdp));
|
||||
CHECK(0 == notcurses_render(nc_));
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
// destroy the ncfdplane within its own context, i.e. from the eof callback
|
||||
SUBCASE("FdPlaneDestroyInline") {
|
||||
REQUIRE(!inline_cancelled);
|
||||
ncfdplane_options opts{};
|
||||
opts.curry = n_;
|
||||
int fd = open("/etc/sysctl.conf", O_RDONLY|O_CLOEXEC);
|
||||
REQUIRE(0 <= fd);
|
||||
auto ncfdp = ncfdplane_create(n_, &opts, fd, testfdcb, testfdeofdestroys);
|
||||
REQUIRE(ncfdp);
|
||||
std::unique_lock<std::mutex> lck(lock);
|
||||
CHECK(0 == notcurses_render(nc_));
|
||||
while(!inline_cancelled){
|
||||
cond.wait(lck);
|
||||
}
|
||||
CHECK(0 == notcurses_render(nc_));
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
CHECK(0 == notcurses_stop(nc_));
|
||||
CHECK(0 == fclose(outfp_));
|
||||
}
|
Loading…
Reference in New Issue