mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
* 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 #523
This commit is contained in:
parent
b264de6b8f
commit
fe8034b5e0
@ -5,7 +5,7 @@ name: debian-unstable
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: debian-build
|
- name: debian-build
|
||||||
image: dankamongmen/unstable_builder:2020-04-18e
|
image: dankamongmen/unstable_builder:2020-04-20a
|
||||||
commands:
|
commands:
|
||||||
- export LANG=en_US.UTF-8
|
- export LANG=en_US.UTF-8
|
||||||
- mkdir build
|
- mkdir build
|
||||||
@ -20,7 +20,7 @@ name: ubuntu-focal
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: ubuntu-build
|
- name: ubuntu-build
|
||||||
image: dankamongmen/focal:2020-04-18c
|
image: dankamongmen/focal:2020-04-20a
|
||||||
commands:
|
commands:
|
||||||
- export LANG=en_US.UTF-8
|
- export LANG=en_US.UTF-8
|
||||||
- mkdir build
|
- mkdir build
|
||||||
|
@ -14,10 +14,11 @@ include(GNUInstallDirs)
|
|||||||
###################### USER-SELECTABLE OPTIONS ###########################
|
###################### USER-SELECTABLE OPTIONS ###########################
|
||||||
option(DFSG_BUILD "DFSG build (no non-free media/code)" OFF)
|
option(DFSG_BUILD "DFSG build (no non-free media/code)" OFF)
|
||||||
option(USE_DOXYGEN "Build HTML cross reference with doxygen" OFF)
|
option(USE_DOXYGEN "Build HTML cross reference with doxygen" OFF)
|
||||||
option(USE_FFMPEG "Disable FFmpeg image/video support (recommended)" ON)
|
option(USE_FFMPEG "Disable FFmpeg image/video support" ON)
|
||||||
option(USE_NETWORK "Allow cargo to use the network" OFF)
|
option(USE_NETWORK "Allow cargo to use the network" OFF)
|
||||||
option(USE_PANDOC "Build man pages and HTML reference with pandoc" ON)
|
option(USE_PANDOC "Build man pages and HTML reference with pandoc" ON)
|
||||||
option(USE_PYTHON "Build Python wrappers" ON)
|
option(USE_PYTHON "Build Python wrappers" ON)
|
||||||
|
option(USE_QRCODEGEN "Disable libqrcodegen QR code support" ON)
|
||||||
option(USE_RUST "Build Rust wrappers (experimental)" OFF)
|
option(USE_RUST "Build Rust wrappers (experimental)" OFF)
|
||||||
option(USE_TESTS "Build doctest unit tests" ON)
|
option(USE_TESTS "Build doctest unit tests" ON)
|
||||||
############## END (additional) USER-SELECTABLE OPTIONS ##################
|
############## END (additional) USER-SELECTABLE OPTIONS ##################
|
||||||
@ -67,7 +68,6 @@ target_include_directories(notcurses-static
|
|||||||
)
|
)
|
||||||
target_link_libraries(notcurses
|
target_link_libraries(notcurses
|
||||||
PRIVATE
|
PRIVATE
|
||||||
Threads::Threads
|
|
||||||
"${TERMINFO_LIBRARIES}"
|
"${TERMINFO_LIBRARIES}"
|
||||||
"${LIBRT}"
|
"${LIBRT}"
|
||||||
PUBLIC
|
PUBLIC
|
||||||
@ -89,6 +89,11 @@ target_link_directories(notcurses-static
|
|||||||
"${TERMINFO_STATIC_LIBRARY_DIRS}"
|
"${TERMINFO_STATIC_LIBRARY_DIRS}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(${USE_QRCODEGEN})
|
||||||
|
target_link_libraries(notcurses PRIVATE qrcodegen)
|
||||||
|
target_link_libraries(notcurses-static PRIVATE qrcodegen)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(${USE_FFMPEG})
|
if(${USE_FFMPEG})
|
||||||
target_include_directories(notcurses
|
target_include_directories(notcurses
|
||||||
PUBLIC
|
PUBLIC
|
||||||
|
@ -113,8 +113,8 @@ that fine library.
|
|||||||
* (build) A C11 and a C++17 compiler
|
* (build) A C11 and a C++17 compiler
|
||||||
* (build) CMake 3.14.0+
|
* (build) CMake 3.14.0+
|
||||||
* (build+runtime) From NCURSES: terminfo 6.1+
|
* (build+runtime) From NCURSES: terminfo 6.1+
|
||||||
|
* (OPTIONAL) (build+runtime) From QR-Code-generator: [libqrcodegen](https://github.com/nayuki/QR-Code-generator) 1.5.0+
|
||||||
* (OPTIONAL) (build+runtime) From [FFMpeg](https://www.ffmpeg.org/): libswscale 5.0+, libavformat 57.0+, libavutil 56.0+
|
* (OPTIONAL) (build+runtime) From [FFMpeg](https://www.ffmpeg.org/): libswscale 5.0+, libavformat 57.0+, libavutil 56.0+
|
||||||
* (OPTIONAL) (build+runtime) [libqrcodegen](https://github.com/nayuki/QR-Code-generator) 1.5.0+
|
|
||||||
* (OPTIONAL) (testing) [Doctest](https://github.com/onqtam/doctest) 2.3.5+
|
* (OPTIONAL) (testing) [Doctest](https://github.com/onqtam/doctest) 2.3.5+
|
||||||
* (OPTIONAL) (documentation) [pandoc](https://pandoc.org/index.html) 1.19.2+
|
* (OPTIONAL) (documentation) [pandoc](https://pandoc.org/index.html) 1.19.2+
|
||||||
* (OPTIONAL) (python bindings): Python 3.7+, [CFFI](https://pypi.org/project/cffi/) 1.13.2+
|
* (OPTIONAL) (python bindings): Python 3.7+, [CFFI](https://pypi.org/project/cffi/) 1.13.2+
|
||||||
|
@ -32,6 +32,7 @@ The demonstrations include (see NOTES below):
|
|||||||
* (j)ungle—low-bandwidth color cycling reveals ancient ruins
|
* (j)ungle—low-bandwidth color cycling reveals ancient ruins
|
||||||
* (l)uigi—a dashing Apennine plumber in a world of fire
|
* (l)uigi—a dashing Apennine plumber in a world of fire
|
||||||
* (n)ormal—a normal map of a friend, with lambert shading
|
* (n)ormal—a normal map of a friend, with lambert shading
|
||||||
|
* (q)rcode—quick response codes (from ISO/IEC 18004:2015)
|
||||||
* (r)eel—demonstration of the ncreel high-level widget
|
* (r)eel—demonstration of the ncreel high-level widget
|
||||||
* (s)liders—a missing-piece puzzle made up of colorful blocks
|
* (s)liders—a missing-piece puzzle made up of colorful blocks
|
||||||
* (t)rans—an exploration of various transparencies
|
* (t)rans—an exploration of various transparencies
|
||||||
@ -88,7 +89,8 @@ non-free under the Debian Free Software Guidelines. As a result, the
|
|||||||
are unavailable through the Debian package.
|
are unavailable through the Debian package.
|
||||||
|
|
||||||
If notcurses is built without FFmpeg, the **chunli**, **eagle**,
|
If notcurses is built without FFmpeg, the **chunli**, **eagle**,
|
||||||
**outro**, **view**, and **xray** demos will be unavailable.
|
**outro**, **view**, and **xray** demos will be unavailable. If notcurses is
|
||||||
|
built without libqrcodegen, the **qrcode** demo will be unavailable.
|
||||||
|
|
||||||
If **notcurses-demo** is run in a terminal lacking the **can_change** terminfo
|
If **notcurses-demo** is run in a terminal lacking the **can_change** terminfo
|
||||||
capability, **jungle** will be skipped.
|
capability, **jungle** will be skipped.
|
||||||
|
@ -27,10 +27,15 @@ extern "C" {
|
|||||||
// Get a human-readable string describing the running notcurses version.
|
// Get a human-readable string describing the running notcurses version.
|
||||||
API const char* notcurses_version(void);
|
API const char* notcurses_version(void);
|
||||||
|
|
||||||
struct cell; // a coordinate on an ncplane: an EGC plus styling
|
|
||||||
struct ncplane; // a drawable notcurses surface, composed of cells
|
|
||||||
struct ncvisual; // a visual bit of multimedia opened with LibAV
|
|
||||||
struct notcurses; // notcurses state for a given terminal, composed of ncplanes
|
struct notcurses; // notcurses state for a given terminal, composed of ncplanes
|
||||||
|
struct ncplane; // a drawable notcurses surface, composed of cells
|
||||||
|
struct cell; // a coordinate on an ncplane: an EGC plus styling
|
||||||
|
struct ncvisual; // a visual bit of multimedia opened with LibAV
|
||||||
|
struct ncplot; // a histogram, bound to a plane
|
||||||
|
struct ncfdplane; // i/o wrapper to dump file descriptor to plane
|
||||||
|
struct ncsubproc; // ncfdplane wrapper with subprocess management
|
||||||
|
struct ncselector;// widget supporting selecting 1 from a list of options
|
||||||
|
struct ncmultiselector; // widget supporting selecting 0..n from n options
|
||||||
|
|
||||||
// Initialize a direct-mode notcurses context on the connected terminal at 'fp'.
|
// Initialize a direct-mode notcurses context on the connected terminal at 'fp'.
|
||||||
// 'fp' must be a tty. You'll usually want stdout. Direct mode supportes a
|
// 'fp' must be a tty. You'll usually want stdout. Direct mode supportes a
|
||||||
@ -878,6 +883,8 @@ API int notcurses_mouse_disable(struct notcurses* n);
|
|||||||
// NCKEY_RESIZE event has been read and you're not ready to render.
|
// NCKEY_RESIZE event has been read and you're not ready to render.
|
||||||
API int notcurses_refresh(struct notcurses* n, int* RESTRICT y, int* RESTRICT x);
|
API int notcurses_refresh(struct notcurses* n, int* RESTRICT y, int* RESTRICT x);
|
||||||
|
|
||||||
|
API struct notcurses* ncplane_notcurses(struct ncplane* n);
|
||||||
|
|
||||||
// Return the dimensions of this ncplane.
|
// Return the dimensions of this ncplane.
|
||||||
API void ncplane_dim_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x);
|
API void ncplane_dim_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x);
|
||||||
|
|
||||||
@ -2398,8 +2405,6 @@ typedef struct selector_options {
|
|||||||
uint64_t bgchannels; // background channels, used only in body
|
uint64_t bgchannels; // background channels, used only in body
|
||||||
} selector_options;
|
} selector_options;
|
||||||
|
|
||||||
struct ncselector;
|
|
||||||
|
|
||||||
API struct ncselector* ncselector_create(struct ncplane* n, int y, int x,
|
API struct ncselector* ncselector_create(struct ncplane* n, int y, int x,
|
||||||
const selector_options* opts);
|
const selector_options* opts);
|
||||||
|
|
||||||
@ -2474,8 +2479,6 @@ typedef struct multiselector_options {
|
|||||||
uint64_t bgchannels; // background channels, used only in body
|
uint64_t bgchannels; // background channels, used only in body
|
||||||
} multiselector_options;
|
} multiselector_options;
|
||||||
|
|
||||||
struct ncmultiselector;
|
|
||||||
|
|
||||||
API struct ncmultiselector* ncmultiselector_create(struct ncplane* n, int y, int x,
|
API struct ncmultiselector* ncmultiselector_create(struct ncplane* n, int y, int x,
|
||||||
const multiselector_options* opts);
|
const multiselector_options* opts);
|
||||||
|
|
||||||
@ -2649,6 +2652,53 @@ API int ncplot_set_sample(struct ncplot* n, uint64_t x, uint64_t y);
|
|||||||
|
|
||||||
API void ncplot_destroy(struct ncplot* n);
|
API void ncplot_destroy(struct ncplot* n);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// read from an fd until EOF (or beyond, if follow is set), invoking the user's
|
||||||
|
// callback each time. runs in its own context. on EOF or error, the finalizer
|
||||||
|
// callback will be invoked, and the user ought destroy the ncfdplane. the
|
||||||
|
// data is *not* guaranteed to be nul-terminated, and may contain arbitrary
|
||||||
|
// zeroes.
|
||||||
|
typedef struct ncfdplane_options {
|
||||||
|
void* curry; // parameter provided to callbacks
|
||||||
|
bool follow; // keep reading after hitting end? (think tail -f)
|
||||||
|
} ncfdplane_options;
|
||||||
|
|
||||||
|
// Create an ncfdplane around the fd 'fd'. Consider this function to take
|
||||||
|
// ownership of the file descriptor, which will be closed in ncfdplane_destroy().
|
||||||
|
API struct ncfdplane* ncfdplane_create(struct ncplane* n, const ncfdplane_options* opts,
|
||||||
|
int fd, ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn);
|
||||||
|
API struct ncplane* ncfdplane_plane(struct ncfdplane* n);
|
||||||
|
API int ncfdplane_destroy(struct ncfdplane* n);
|
||||||
|
|
||||||
|
typedef struct ncsubproc_options {
|
||||||
|
ncfdplane_options popts;
|
||||||
|
uint64_t restart_period; // restart this many seconds after an exit (watch)
|
||||||
|
} ncsubproc_options;
|
||||||
|
|
||||||
|
// see exec(2). p-types use $PATH. e-type passes environment vars.
|
||||||
|
API struct ncsubproc* ncsubproc_createv(struct ncplane* n, const ncsubproc_options* opts,
|
||||||
|
const char* bin, char* const arg[],
|
||||||
|
ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn);
|
||||||
|
API struct ncsubproc* ncsubproc_createvp(struct ncplane* n, const ncsubproc_options* opts,
|
||||||
|
const char* bin, char* const arg[],
|
||||||
|
ncfdplane_callback cbfxn, ncfdplane_done_cb donecbfxn);
|
||||||
|
API 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);
|
||||||
|
|
||||||
|
API int ncsubproc_destroy(struct ncsubproc* n);
|
||||||
|
|
||||||
|
// Draw a QR code at the current position on the plane. If there is insufficient
|
||||||
|
// room to draw the code here, or there is any other error, non-zero will be
|
||||||
|
// returned. Otherwise, the QR code "version" (size) is returned. The QR code
|
||||||
|
// is (version * 4 + 17) columns wide, and ⌈version * 4 + 17 / 2⌉ rows tall. If
|
||||||
|
// maxversion is not zero, it plays a hard limit on the QR code size. Though the
|
||||||
|
// max version of current QR codes is 40, greater values are allowed, for
|
||||||
|
// future compatability (provide 0 for no artificail bound).
|
||||||
|
API int ncplane_qrcode(struct ncplane* n, int maxversion, const void* data, size_t len);
|
||||||
|
|
||||||
#undef API
|
#undef API
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -20,14 +20,14 @@ static demoresult* results;
|
|||||||
static char *datadir = NOTCURSES_SHARE;
|
static char *datadir = NOTCURSES_SHARE;
|
||||||
|
|
||||||
// yes, these are in different orders in different configurations on purpose
|
// yes, these are in different orders in different configurations on purpose
|
||||||
// (since some transition into the next)
|
// (since some transition into the next). FIXME handle case sans libqrcodegen.
|
||||||
#ifndef USE_FFMPEG
|
#ifndef USE_FFMPEG
|
||||||
static const char DEFAULT_DEMO[] = "itfhbrgnswju";
|
static const char DEFAULT_DEMO[] = "itfhbrgnswjqu";
|
||||||
#else
|
#else
|
||||||
#ifdef DFSG_BUILD
|
#ifdef DFSG_BUILD
|
||||||
static const char DEFAULT_DEMO[] = "ixtfhbrgnswuo";
|
static const char DEFAULT_DEMO[] = "ixtfhbrgnswuqo";
|
||||||
#else
|
#else
|
||||||
static const char DEFAULT_DEMO[] = "ixethnbcgrwuvlfsjo";
|
static const char DEFAULT_DEMO[] = "ixethnbcgrwuvlfsjqo";
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ static struct {
|
|||||||
{ "normal", normal_demo, },
|
{ "normal", normal_demo, },
|
||||||
FREEFFMPEG("outro", outro),
|
FREEFFMPEG("outro", outro),
|
||||||
{ NULL, NULL, },
|
{ NULL, NULL, },
|
||||||
{ NULL, NULL, },
|
{ "qrcode", qrcode_demo, }, // is blank without USE_QRCODEGEN
|
||||||
{ "reel", reel_demo, },
|
{ "reel", reel_demo, },
|
||||||
{ "sliders", sliding_puzzle_demo, },
|
{ "sliders", sliding_puzzle_demo, },
|
||||||
{ "trans", trans_demo, },
|
{ "trans", trans_demo, },
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include <version.h>
|
|
||||||
#include <notcurses/notcurses.h>
|
#include <notcurses/notcurses.h>
|
||||||
|
#include <version.h>
|
||||||
#ifdef USE_FFMPEG
|
#ifdef USE_FFMPEG
|
||||||
#include <libavutil/pixdesc.h>
|
#include <libavutil/pixdesc.h>
|
||||||
#include <libavutil/avconfig.h>
|
#include <libavutil/avconfig.h>
|
||||||
@ -37,6 +37,7 @@ int witherworm_demo(struct notcurses* nc);
|
|||||||
int box_demo(struct notcurses* nc);
|
int box_demo(struct notcurses* nc);
|
||||||
int trans_demo(struct notcurses* nc);
|
int trans_demo(struct notcurses* nc);
|
||||||
int chunli_demo(struct notcurses* nc);
|
int chunli_demo(struct notcurses* nc);
|
||||||
|
int qrcode_demo(struct notcurses* nc);
|
||||||
int grid_demo(struct notcurses* nc);
|
int grid_demo(struct notcurses* nc);
|
||||||
int fallin_demo(struct notcurses* nc);
|
int fallin_demo(struct notcurses* nc);
|
||||||
int highcontrast_demo(struct notcurses* nc);
|
int highcontrast_demo(struct notcurses* nc);
|
||||||
|
@ -26652,7 +26652,7 @@ int jungle_demo(struct notcurses* nc){
|
|||||||
free(buf);
|
free(buf);
|
||||||
int iter = 0;
|
int iter = 0;
|
||||||
// don't try to run faster than, eh, 140Hz
|
// don't try to run faster than, eh, 140Hz
|
||||||
int64_t iterns = GIG / 140;
|
int64_t iterns = GIG / 100;
|
||||||
int64_t nsrunning;
|
int64_t nsrunning;
|
||||||
do{
|
do{
|
||||||
DEMO_RENDER(nc);
|
DEMO_RENDER(nc);
|
||||||
|
39
src/demo/qrcode.c
Normal file
39
src/demo/qrcode.c
Normal file
@ -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;
|
||||||
|
}
|
141
src/lib/fd.c
Normal file
141
src/lib/fd.c
Normal file
@ -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;
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
#include <qrcodegen/qrcodegen.h>
|
||||||
|
|
||||||
void ncplane_greyscale(ncplane *n){
|
void ncplane_greyscale(ncplane *n){
|
||||||
for(int y = 0 ; y < n->leny ; ++y){
|
for(int y = 0 ; y < n->leny ; ++y){
|
||||||
@ -588,3 +589,96 @@ int ncplane_rotate_ccw(ncplane* n){
|
|||||||
ret |= ncplane_destroy(newp);
|
ret |= ncplane_destroy(newp);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_QRCODEGEN
|
||||||
|
#define QR_BASE_SIZE 17
|
||||||
|
#define PER_QR_VERSION 4
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
qrcode_rows(int version){
|
||||||
|
return QR_BASE_SIZE + (version * PER_QR_VERSION / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
qrcode_cols(int version){
|
||||||
|
return QR_BASE_SIZE + (version * PER_QR_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ncplane_qrcode(ncplane* n, int maxversion, const void* data, size_t len){
|
||||||
|
const int MAX_QR_VERSION = 40; // QR library only supports up to 40
|
||||||
|
if(maxversion < 0){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(len == 0){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const int starty = n->y;
|
||||||
|
const int startx = n->x;
|
||||||
|
const int availx = n->lenx - startx;
|
||||||
|
const int availy = n->leny - starty;
|
||||||
|
if(availy < qrcode_rows(1)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(availx < qrcode_cols(1)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const int availsquare = availy * 2 < availx ? availy * 2 : availx;
|
||||||
|
const int roomforver = (availsquare - QR_BASE_SIZE) / 4;
|
||||||
|
if(maxversion == 0){
|
||||||
|
maxversion = roomforver;
|
||||||
|
}else if(maxversion > roomforver){
|
||||||
|
maxversion = roomforver;
|
||||||
|
}
|
||||||
|
if(maxversion > MAX_QR_VERSION){
|
||||||
|
maxversion = MAX_QR_VERSION;
|
||||||
|
}
|
||||||
|
const size_t bsize = qrcodegen_BUFFER_LEN_FOR_VERSION(maxversion);
|
||||||
|
if(bsize < len){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint8_t* src = malloc(bsize);
|
||||||
|
uint8_t* dst = malloc(bsize);
|
||||||
|
if(src == NULL || dst == NULL){
|
||||||
|
free(src);
|
||||||
|
free(dst);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(src, data, len);
|
||||||
|
int ret = -1;
|
||||||
|
if(qrcodegen_encodeBinary(src, len, dst, qrcodegen_Ecc_HIGH, 1, maxversion, qrcodegen_Mask_AUTO, true)){
|
||||||
|
ret = qrcodegen_getSize(dst);
|
||||||
|
for(int y = starty ; y < starty + (ret + 1) / 2 ; ++y){
|
||||||
|
for(int x = startx ; x < startx + ret ; ++x){
|
||||||
|
const bool top = qrcodegen_getModule(dst, x, y);
|
||||||
|
const bool bot = qrcodegen_getModule(dst, x, y + 1);
|
||||||
|
const char* egc;
|
||||||
|
if(top && bot){
|
||||||
|
egc = "█";
|
||||||
|
}else if(top){
|
||||||
|
egc = "▀";
|
||||||
|
}else if(bot){
|
||||||
|
egc = "▄";
|
||||||
|
}else{
|
||||||
|
egc = " ";
|
||||||
|
}
|
||||||
|
int sbytes;
|
||||||
|
if(ncplane_putegc_yx(n, y, x, egc, &sbytes) <= 0){
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(src);
|
||||||
|
free(dst);
|
||||||
|
return ret < 0 ? ret : (ret - QR_BASE_SIZE) / PER_QR_VERSION;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int ncplane_qrcode(ncplane* n, int maxversion, const void* data, size_t len){
|
||||||
|
(void)n;
|
||||||
|
(void)maxversion;
|
||||||
|
(void)data;
|
||||||
|
(void)len;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -187,6 +187,22 @@ typedef struct ncplot {
|
|||||||
bool detectdomain; // is domain detection in effect (stretch the domain)?
|
bool detectdomain; // is domain detection in effect (stretch the domain)?
|
||||||
} ncplot;
|
} ncplot;
|
||||||
|
|
||||||
|
typedef struct ncfdplane {
|
||||||
|
ncfdplane_callback cb; // invoked with fresh hot data
|
||||||
|
ncfdplane_done_cb donecb; // invoked on EOF (if !follow) or error
|
||||||
|
void* curry; // passed to the callbacks
|
||||||
|
int fd; // we take ownership of the fd, and close it
|
||||||
|
bool follow; // keep trying to read past the end (event-based)
|
||||||
|
ncplane* ncp; // bound ncplane
|
||||||
|
pthread_t tid; // thread servicing this i/o
|
||||||
|
bool destroyed; // set in ncfdplane_destroy() in our own context
|
||||||
|
} ncfdplane;
|
||||||
|
|
||||||
|
typedef struct ncsubproc {
|
||||||
|
ncfdplane* nfp;
|
||||||
|
pid_t pid; // subprocess
|
||||||
|
} ncsubproc;
|
||||||
|
|
||||||
typedef struct ncmenu {
|
typedef struct ncmenu {
|
||||||
ncplane* ncp;
|
ncplane* ncp;
|
||||||
int sectioncount; // must be positive
|
int sectioncount; // must be positive
|
||||||
|
@ -1210,14 +1210,9 @@ cell_obliterate(ncplane* n, cell* c){
|
|||||||
cell_init(c);
|
cell_init(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ncplane_putc_yx(ncplane* n, int y, int x, const cell* c){
|
// increment y by 1 and rotate the framebuffer up one line. x moves to 0.
|
||||||
// if scrolling is enabled, check *before ncplane_cursor_move_yx()* whether
|
static inline void
|
||||||
// we're past the end of the line, and move to the next line if so.
|
scroll_down(ncplane* n){
|
||||||
bool wide = cell_double_wide_p(c);
|
|
||||||
if(x == -1 && y == -1 && n->x + wide >= n->lenx){
|
|
||||||
if(!n->scrolling){
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
n->x = 0;
|
n->x = 0;
|
||||||
if(n->y == n->leny - 1){
|
if(n->y == n->leny - 1){
|
||||||
n->logrow = (n->logrow + 1) % n->leny;
|
n->logrow = (n->logrow + 1) % n->leny;
|
||||||
@ -1230,9 +1225,26 @@ int ncplane_putc_yx(ncplane* n, int y, int x, const cell* c){
|
|||||||
++n->y;
|
++n->y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ncplane_putc_yx(ncplane* n, int y, int x, const cell* c){
|
||||||
|
// if scrolling is enabled, check *before ncplane_cursor_move_yx()* whether
|
||||||
|
// we're past the end of the line, and move to the next line if so.
|
||||||
|
bool wide = cell_double_wide_p(c);
|
||||||
|
if(x == -1 && y == -1 && n->x + wide >= n->lenx){
|
||||||
|
if(!n->scrolling){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
scroll_down(n);
|
||||||
|
}
|
||||||
if(ncplane_cursor_move_yx(n, y, x)){
|
if(ncplane_cursor_move_yx(n, y, x)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if(c->gcluster == '\n'){
|
||||||
|
if(n->scrolling){
|
||||||
|
scroll_down(n);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
// A wide character obliterates anything to its immediate right (and marks
|
// A wide character obliterates anything to its immediate right (and marks
|
||||||
// that cell as wide). Any character placed atop one half of a wide character
|
// that cell as wide). Any character placed atop one half of a wide character
|
||||||
// obliterates the other half. Note that a wide char can thus obliterate two
|
// obliterates the other half. Note that a wide char can thus obliterate two
|
||||||
@ -1844,6 +1856,10 @@ void ncplane_translate(const ncplane* src, const ncplane* dst,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notcurses* ncplane_notcurses(ncplane* n){
|
||||||
|
return n->nc;
|
||||||
|
}
|
||||||
|
|
||||||
ncplane* ncplane_reparent(ncplane* n, ncplane* newparent){
|
ncplane* ncplane_reparent(ncplane* n, ncplane* newparent){
|
||||||
if(n == n->nc->stdscr){
|
if(n == n->nc->stdscr){
|
||||||
return NULL; // can't reparent standard plane
|
return NULL; // can't reparent standard plane
|
||||||
|
65
src/poc/fileroller.c
Normal file
65
src/poc/fileroller.c
Normal file
@ -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;
|
||||||
|
}
|
@ -22,19 +22,27 @@ int main(void){
|
|||||||
r = 0;
|
r = 0;
|
||||||
g = 0x80;
|
g = 0x80;
|
||||||
b = 0;
|
b = 0;
|
||||||
|
ncplane_set_bg_rgb(n, 0x40, 0x20, 0x40);
|
||||||
for(y = 0 ; y < dimy ; ++y){
|
for(y = 0 ; y < dimy ; ++y){
|
||||||
for(x = 0 ; x < dimx ; ++x){
|
for(x = 0 ; x < dimx ; ++x){
|
||||||
ncplane_set_fg_rgb(n, r, g, b);
|
if(ncplane_set_fg_rgb(n, r, g, b)){
|
||||||
ncplane_putsimple(n, 'x');
|
goto err;
|
||||||
|
}
|
||||||
|
if(ncplane_cursor_move_yx(n, y, x)){
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if(ncplane_putsimple(n, 'x') <= 0){
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
if(g % 2){
|
if(g % 2){
|
||||||
if(b-- == 0){
|
if(--b <= 0){
|
||||||
++g;
|
++g;
|
||||||
b = 0;
|
b = 0;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if(b++ >= 256){
|
if(++b >= 256){
|
||||||
++g;
|
++g;
|
||||||
b = 256;
|
b = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,4 +53,8 @@ int main(void){
|
|||||||
}
|
}
|
||||||
notcurses_stop(nc);
|
notcurses_stop(nc);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
err:
|
||||||
|
notcurses_stop(nc);
|
||||||
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
103
tests/fds.cpp
Normal file
103
tests/fds.cpp
Normal file
@ -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_));
|
||||||
|
}
|
@ -436,6 +436,14 @@ TEST_CASE("Fills") {
|
|||||||
ncplane_destroy(p2);
|
ncplane_destroy(p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_QRCODEGEN
|
||||||
|
SUBCASE("QRCodes") {
|
||||||
|
const char* qr = "a very simple qr code";
|
||||||
|
CHECK(0 < ncplane_qrcode(n_, 0, qr, strlen(qr)));
|
||||||
|
CHECK(0 == notcurses_render(nc_));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
CHECK(0 == notcurses_stop(nc_));
|
CHECK(0 == notcurses_stop(nc_));
|
||||||
CHECK(0 == fclose(outfp_));
|
CHECK(0 == fclose(outfp_));
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ handle_opts(const char** argv){
|
|||||||
// notcurses_stop()). so just whip up a new one, and free it immediately.
|
// notcurses_stop()). so just whip up a new one, and free it immediately.
|
||||||
static void
|
static void
|
||||||
reset_terminal(){
|
reset_terminal(){
|
||||||
|
// FIXME much more robust to just use termios here
|
||||||
notcurses_options nopts{};
|
notcurses_options nopts{};
|
||||||
nopts.inhibit_alternate_screen = true;
|
nopts.inhibit_alternate_screen = true;
|
||||||
auto nc = notcurses_init(&nopts, NULL);
|
auto nc = notcurses_init(&nopts, NULL);
|
||||||
|
@ -3,4 +3,5 @@
|
|||||||
#define notcurses_VERSION_PATCH "@notcurses_VERSION_PATCH@"
|
#define notcurses_VERSION_PATCH "@notcurses_VERSION_PATCH@"
|
||||||
#cmakedefine DFSG_BUILD
|
#cmakedefine DFSG_BUILD
|
||||||
#cmakedefine USE_FFMPEG
|
#cmakedefine USE_FFMPEG
|
||||||
|
#cmakedefine USE_QRCODEGEN
|
||||||
#define NOTCURSES_SHARE "@CMAKE_INSTALL_FULL_DATADIR@/notcurses"
|
#define NOTCURSES_SHARE "@CMAKE_INSTALL_FULL_DATADIR@/notcurses"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user