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:
|
||||
- name: debian-build
|
||||
image: dankamongmen/unstable_builder:2020-04-18e
|
||||
image: dankamongmen/unstable_builder:2020-04-20a
|
||||
commands:
|
||||
- export LANG=en_US.UTF-8
|
||||
- mkdir build
|
||||
@ -20,7 +20,7 @@ name: ubuntu-focal
|
||||
|
||||
steps:
|
||||
- name: ubuntu-build
|
||||
image: dankamongmen/focal:2020-04-18c
|
||||
image: dankamongmen/focal:2020-04-20a
|
||||
commands:
|
||||
- export LANG=en_US.UTF-8
|
||||
- mkdir build
|
||||
|
@ -14,10 +14,11 @@ include(GNUInstallDirs)
|
||||
###################### USER-SELECTABLE OPTIONS ###########################
|
||||
option(DFSG_BUILD "DFSG build (no non-free media/code)" 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_PANDOC "Build man pages and HTML reference with pandoc" 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_TESTS "Build doctest unit tests" ON)
|
||||
############## END (additional) USER-SELECTABLE OPTIONS ##################
|
||||
@ -67,7 +68,6 @@ target_include_directories(notcurses-static
|
||||
)
|
||||
target_link_libraries(notcurses
|
||||
PRIVATE
|
||||
Threads::Threads
|
||||
"${TERMINFO_LIBRARIES}"
|
||||
"${LIBRT}"
|
||||
PUBLIC
|
||||
@ -89,6 +89,11 @@ target_link_directories(notcurses-static
|
||||
"${TERMINFO_STATIC_LIBRARY_DIRS}"
|
||||
)
|
||||
|
||||
if(${USE_QRCODEGEN})
|
||||
target_link_libraries(notcurses PRIVATE qrcodegen)
|
||||
target_link_libraries(notcurses-static PRIVATE qrcodegen)
|
||||
endif()
|
||||
|
||||
if(${USE_FFMPEG})
|
||||
target_include_directories(notcurses
|
||||
PUBLIC
|
||||
|
@ -113,8 +113,8 @@ that fine library.
|
||||
* (build) A C11 and a C++17 compiler
|
||||
* (build) CMake 3.14.0+
|
||||
* (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) [libqrcodegen](https://github.com/nayuki/QR-Code-generator) 1.5.0+
|
||||
* (OPTIONAL) (testing) [Doctest](https://github.com/onqtam/doctest) 2.3.5+
|
||||
* (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+
|
||||
|
@ -32,6 +32,7 @@ The demonstrations include (see NOTES below):
|
||||
* (j)ungle—low-bandwidth color cycling reveals ancient ruins
|
||||
* (l)uigi—a dashing Apennine plumber in a world of fire
|
||||
* (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
|
||||
* (s)liders—a missing-piece puzzle made up of colorful blocks
|
||||
* (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.
|
||||
|
||||
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
|
||||
capability, **jungle** will be skipped.
|
||||
|
@ -27,10 +27,15 @@ extern "C" {
|
||||
// Get a human-readable string describing the running notcurses version.
|
||||
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 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'.
|
||||
// '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.
|
||||
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.
|
||||
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
|
||||
} selector_options;
|
||||
|
||||
struct ncselector;
|
||||
|
||||
API struct ncselector* ncselector_create(struct ncplane* n, int y, int x,
|
||||
const selector_options* opts);
|
||||
|
||||
@ -2474,8 +2479,6 @@ typedef struct multiselector_options {
|
||||
uint64_t bgchannels; // background channels, used only in body
|
||||
} multiselector_options;
|
||||
|
||||
struct ncmultiselector;
|
||||
|
||||
API struct ncmultiselector* ncmultiselector_create(struct ncplane* n, int y, int x,
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -20,14 +20,14 @@ static demoresult* results;
|
||||
static char *datadir = NOTCURSES_SHARE;
|
||||
|
||||
// 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
|
||||
static const char DEFAULT_DEMO[] = "itfhbrgnswju";
|
||||
static const char DEFAULT_DEMO[] = "itfhbrgnswjqu";
|
||||
#else
|
||||
#ifdef DFSG_BUILD
|
||||
static const char DEFAULT_DEMO[] = "ixtfhbrgnswuo";
|
||||
static const char DEFAULT_DEMO[] = "ixtfhbrgnswuqo";
|
||||
#else
|
||||
static const char DEFAULT_DEMO[] = "ixethnbcgrwuvlfsjo";
|
||||
static const char DEFAULT_DEMO[] = "ixethnbcgrwuvlfsjqo";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -113,7 +113,7 @@ static struct {
|
||||
{ "normal", normal_demo, },
|
||||
FREEFFMPEG("outro", outro),
|
||||
{ NULL, NULL, },
|
||||
{ NULL, NULL, },
|
||||
{ "qrcode", qrcode_demo, }, // is blank without USE_QRCODEGEN
|
||||
{ "reel", reel_demo, },
|
||||
{ "sliders", sliding_puzzle_demo, },
|
||||
{ "trans", trans_demo, },
|
||||
|
@ -6,8 +6,8 @@
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <stdatomic.h>
|
||||
#include <version.h>
|
||||
#include <notcurses/notcurses.h>
|
||||
#include <version.h>
|
||||
#ifdef USE_FFMPEG
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <libavutil/avconfig.h>
|
||||
@ -37,6 +37,7 @@ int witherworm_demo(struct notcurses* nc);
|
||||
int box_demo(struct notcurses* nc);
|
||||
int trans_demo(struct notcurses* nc);
|
||||
int chunli_demo(struct notcurses* nc);
|
||||
int qrcode_demo(struct notcurses* nc);
|
||||
int grid_demo(struct notcurses* nc);
|
||||
int fallin_demo(struct notcurses* nc);
|
||||
int highcontrast_demo(struct notcurses* nc);
|
||||
|
@ -26652,7 +26652,7 @@ int jungle_demo(struct notcurses* nc){
|
||||
free(buf);
|
||||
int iter = 0;
|
||||
// don't try to run faster than, eh, 140Hz
|
||||
int64_t iterns = GIG / 140;
|
||||
int64_t iterns = GIG / 100;
|
||||
int64_t nsrunning;
|
||||
do{
|
||||
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 <qrcodegen/qrcodegen.h>
|
||||
|
||||
void ncplane_greyscale(ncplane *n){
|
||||
for(int y = 0 ; y < n->leny ; ++y){
|
||||
@ -588,3 +589,96 @@ int ncplane_rotate_ccw(ncplane* n){
|
||||
ret |= ncplane_destroy(newp);
|
||||
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)?
|
||||
} 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 {
|
||||
ncplane* ncp;
|
||||
int sectioncount; // must be positive
|
||||
|
@ -1210,14 +1210,9 @@ cell_obliterate(ncplane* n, cell* c){
|
||||
cell_init(c);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// increment y by 1 and rotate the framebuffer up one line. x moves to 0.
|
||||
static inline void
|
||||
scroll_down(ncplane* n){
|
||||
n->x = 0;
|
||||
if(n->y == n->leny - 1){
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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)){
|
||||
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
|
||||
// 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
|
||||
@ -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){
|
||||
if(n == n->nc->stdscr){
|
||||
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;
|
||||
g = 0x80;
|
||||
b = 0;
|
||||
ncplane_set_bg_rgb(n, 0x40, 0x20, 0x40);
|
||||
for(y = 0 ; y < dimy ; ++y){
|
||||
for(x = 0 ; x < dimx ; ++x){
|
||||
ncplane_set_fg_rgb(n, r, g, b);
|
||||
ncplane_putsimple(n, 'x');
|
||||
if(ncplane_set_fg_rgb(n, r, g, b)){
|
||||
goto err;
|
||||
}
|
||||
if(ncplane_cursor_move_yx(n, y, x)){
|
||||
goto err;
|
||||
}
|
||||
if(ncplane_putsimple(n, 'x') <= 0){
|
||||
goto err;
|
||||
}
|
||||
if(g % 2){
|
||||
if(b-- == 0){
|
||||
if(--b <= 0){
|
||||
++g;
|
||||
b = 0;
|
||||
}
|
||||
}else{
|
||||
if(b++ >= 256){
|
||||
if(++b >= 256){
|
||||
++g;
|
||||
b = 256;
|
||||
b = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,4 +53,8 @@ int main(void){
|
||||
}
|
||||
notcurses_stop(nc);
|
||||
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);
|
||||
}
|
||||
|
||||
#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 == fclose(outfp_));
|
||||
|
||||
|
@ -48,6 +48,7 @@ handle_opts(const char** argv){
|
||||
// notcurses_stop()). so just whip up a new one, and free it immediately.
|
||||
static void
|
||||
reset_terminal(){
|
||||
// FIXME much more robust to just use termios here
|
||||
notcurses_options nopts{};
|
||||
nopts.inhibit_alternate_screen = true;
|
||||
auto nc = notcurses_init(&nopts, NULL);
|
||||
|
@ -3,4 +3,5 @@
|
||||
#define notcurses_VERSION_PATCH "@notcurses_VERSION_PATCH@"
|
||||
#cmakedefine DFSG_BUILD
|
||||
#cmakedefine USE_FFMPEG
|
||||
#cmakedefine USE_QRCODEGEN
|
||||
#define NOTCURSES_SHARE "@CMAKE_INSTALL_FULL_DATADIR@/notcurses"
|
||||
|
Loading…
x
Reference in New Issue
Block a user