Place ncneofetch properly (#2415)

* ncplane_polyfill_inner: plug memory leak on error paths
* [progbar_create] memory leak on error path
* ncvisual_polyfill_core: memory leaks on error paths
* notcurses_early_init: memory leak on error path
* [demo] fix summary table for ASCII #2402
* [sliders] make it work in ASCII #2402
* add ncplane_ascii_box() #2402
* [notcurses-info] use capabilities accessors for blitters
pull/2427/head
nick black 3 years ago committed by GitHub
parent 771f2e1fb6
commit caaeec5094
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,6 @@
# 3.14.0 introduced NAME_WLE
cmake_minimum_required(VERSION 3.14.0)
project(notcurses VERSION 2.4.99
project(notcurses VERSION 2.4.999
DESCRIPTION "Blingful UI for modern terminal emulators"
HOMEPAGE_URL "https://nick-black.com/dankwiki/index.php/notcurses"
LANGUAGES C)

@ -1,6 +1,6 @@
% notcurses-pydemo(1)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -49,7 +49,7 @@ except ImportError:
setup(
name="notcurses",
version="2.4.99",
version="2.4.999",
packages=['notcurses'],
scripts=['notcurses-pydemo', 'ncdirect-pydemo'],
package_dir={'': 'src'},

@ -38,7 +38,7 @@ PROJECT_NAME = Notcurses
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2.4.99
PROJECT_NUMBER = 2.4.999
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

@ -38,7 +38,7 @@
</a>
</div>
<iframe align="right" width="560" height="315" src="https://www.youtube.com/embed/dcjkezf1ARY" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<h2><a href="https://nick-black.com/dankwiki/index.php/Notcurses">notcurses</a> manual pages (<a href="https://github.com/dankamongmen/notcurses/releases/tag/v2.4.99">v2.4.99</a>)</h2>
<h2><a href="https://nick-black.com/dankwiki/index.php/Notcurses">notcurses</a> manual pages (<a href="https://github.com/dankamongmen/notcurses/releases/tag/v2.4.999">v2.4.999</a>)</h2>
<a href="notcurses.3.html">notcurses(3)</a>—a blingful TUI library<br/>
<h3>Executables (section 1)</h3>
<a href="ncls.1.html">ncls</a>—list files, displaying multimedia along with them<br/>

@ -1,6 +1,6 @@
% ncls(1)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% ncneofetch(1)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% ncplayer(1)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% nctetris(1)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses-demo(1)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses-info(1)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses-input(1)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses-tester(1)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_capabilities(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_cell(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_channels(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_core(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_direct(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_fade(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_fds(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_init(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_input(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_lines(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_menu(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_metric(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_multiselector(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_output(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_palette(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_pile(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_plane(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_plot(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_reader(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_reel(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_refresh(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_render(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_selector(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_stats(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_stdplane(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_stop(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,5 +1,5 @@
% notcurses_tabbed(3)
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_tree(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_util(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME

@ -1,6 +1,6 @@
% notcurses_visual(3)
% nick black <nickblack@linux.com>
% v2.4.99
% v2.4.999
# NAME
notcurses_visual - notcurses multimedia

@ -2951,6 +2951,22 @@ ncplane_double_box(struct ncplane* n, uint16_t styles, uint64_t channels,
return ret;
}
static inline int
ncplane_ascii_box(struct ncplane* n, uint16_t styles, uint64_t channels,
unsigned ylen, unsigned xlen, unsigned ctlword){
int ret = 0;
nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
nccell ll = NCCELL_TRIVIAL_INITIALIZER, lr = NCCELL_TRIVIAL_INITIALIZER;
nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
if((ret = nccells_ascii_box(n, styles, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ylen, xlen, ctlword);
}
nccell_release(n, &ul); nccell_release(n, &ur);
nccell_release(n, &ll); nccell_release(n, &lr);
nccell_release(n, &hl); nccell_release(n, &vl);
return ret;
}
static inline int
ncplane_perimeter_double(struct ncplane* n, uint16_t stylemask,
uint64_t channels, unsigned ctlword){

@ -38,7 +38,7 @@ if environ.get('LDFLAGS') is None:
setup(
name="notcurses",
version="2.4.99",
version="2.4.999",
packages=['notcurses'],
ext_modules=[
Extension(

@ -326,21 +326,29 @@ handle_opts(int argc, char** argv, notcurses_options* opts, FILE** json_output){
}
static int
table_segment_color(struct ncplane* n, const char* str, const char* delim, unsigned color){
table_segment_color(struct ncplane* n, const char* str, const char* delim,
const char* ascdelim, unsigned color){
ncplane_set_fg_rgb(n, color);
if(ncplane_putstr(n, str) < 0){
return -1;
}
ncplane_set_fg_rgb8(n, 178, 102, 255);
if(ncplane_putstr(n, delim) < 0){
return -1;
if(notcurses_canutf8(ncplane_notcurses(n))){
if(ncplane_putstr(n, delim) < 0){
return -1;
}
}else{
if(ncplane_putstr(n, ascdelim) < 0){
return -1;
}
}
return 0;
}
static int
table_segment(struct ncplane* n, const char* str, const char* delim){
return table_segment_color(n, str, delim, 0xffffff);
table_segment(struct ncplane* n, const char* str, const char* delim,
const char* ascdelim){
return table_segment_color(n, str, delim, ascdelim, 0xffffff);
}
static int
@ -387,14 +395,21 @@ summary_table(struct notcurses* nc, const char* spec, bool canimage, bool canvid
// FIXME this shouldn't be necessary, but without it, late in 2.4.x we
// stopped printing the table header. see #2389.
notcurses_render(nc);
table_segment(n, " runtime", "");
table_segment(n, " frames", "");
table_segment(n, "output(B)", "");
table_segment(n, " FPS", "");
table_segment(n, "%r", "");
table_segment(n, "%a", "");
table_segment(n, "%w", "");
table_segment(n, "TheoFPS", "\n══╤════════╤════════╪═══════╪═════════╪═══════╪══╪══╪══╪═══════╣\n");
const char* sep;
if(notcurses_canutf8(ncplane_notcurses(n))){
sep = u8"";
}else{
sep = "|";
}
table_segment(n, " runtime", "", "|");
table_segment(n, " frames", "", "|");
table_segment(n, "output(B)", "", "|");
table_segment(n, " FPS", "", "|");
table_segment(n, "%r", "", "|");
table_segment(n, "%a", "", "|");
table_segment(n, "%w", "", "|");
table_segment(n, "TheoFPS", "\n══╤════════╤════════╪═══════╪═════════╪═══════╪══╪══╪══╪═══════╣\n",
"|\n--+--------+--------+-------+---------+-------+--+--+--+-------|\n");
char timebuf[NCPREFIXSTRLEN + 1];
char tfpsbuf[NCPREFIXSTRLEN + 1];
char totalbuf[NCBPREFIXSTRLEN + 1];
@ -423,21 +438,24 @@ summary_table(struct notcurses* nc, const char* spec, bool canimage, bool canvid
ncplane_set_fg_rgb(n, rescolor);
ncplane_printf(n, "%2llu", (unsigned long long)(i + 1)); // windows has %zu problems
ncplane_set_fg_rgb8(n, 178, 102, 255);
ncplane_putwc(n, L'');
ncplane_putegc(n, sep, NULL);
ncplane_set_fg_rgb(n, rescolor);
ncplane_printf(n, "%8s", demos[results[i].selector - 'a'].name);
ncplane_set_fg_rgb8(n, 178, 102, 255);
ncplane_printf(n, "│%*ss│%7" PRIu64 "│%*s│%7.1f│%2" PRId64 "│%2" PRId64 "│%2" PRId64 "│%*s║",
NCPREFIXFMT(timebuf), results[i].stats.renders, NCBPREFIXFMT(totalbuf),
ncplane_printf(n, "%s%*ss%s%7" PRIu64 "%s%*s%s%7.1f%s%2" PRId64 "%s%2" PRId64 "%s%2" PRId64 "%s%*s%s",
sep, NCPREFIXFMT(timebuf), sep,
results[i].stats.renders, sep,
NCBPREFIXFMT(totalbuf), sep,
results[i].timens ?
results[i].stats.renders / ((double)results[i].timens / NANOSECS_IN_SEC) : 0.0,
results[i].stats.renders / ((double)results[i].timens / NANOSECS_IN_SEC) : 0.0, sep,
(results[i].timens ?
results[i].stats.render_ns * 100 / results[i].timens : 0),
results[i].stats.render_ns * 100 / results[i].timens : 0), sep,
(results[i].timens ?
results[i].stats.raster_ns * 100 / results[i].timens : 0),
results[i].stats.raster_ns * 100 / results[i].timens : 0), sep,
(results[i].timens ?
results[i].stats.writeout_ns * 100 / results[i].timens : 0),
NCPREFIXFMT(tfpsbuf));
results[i].stats.writeout_ns * 100 / results[i].timens : 0), sep,
NCPREFIXFMT(tfpsbuf),
notcurses_canutf8(ncplane_notcurses(n)) ? "" : "|");
ncplane_set_fg_rgb(n, rescolor);
ncplane_printf(n, "%s\n", results[i].result < 0 ? "FAILED" :
results[i].result > 0 ? "ABORTED" :
@ -452,12 +470,13 @@ summary_table(struct notcurses* nc, const char* spec, bool canimage, bool canvid
}
ncqprefix(nsdelta, NANOSECS_IN_SEC, timebuf, 0);
ncbprefix(totalbytes, 1, totalbuf, 0);
table_segment(n, "", "══╧════════╧════════╪═══════╪═════════╪═══════╧══╧══╧══╧═══════╝\n");
table_segment(n, "", "══╧════════╧════════╪═══════╪═════════╪═══════╧══╧══╧══╧═══════╝\n",
"--+--------+--------+-------+---------+-------+--+--+--+-------+\n");
ncplane_putstr(n, " ");
table_printf(n, "", "%*ss", NCPREFIXFMT(timebuf));
table_printf(n, "", "%7lu", totalframes);
table_printf(n, "", "%*s", NCBPREFIXFMT(totalbuf));
//table_printf(nc, "│", "%7.1f", nsdelta ? totalframes / ((double)nsdelta / NANOSECS_IN_SEC) : 0);
table_printf(n, sep, "%*ss", NCPREFIXFMT(timebuf));
table_printf(n, sep, "%7lu", totalframes);
table_printf(n, sep, "%*s", NCBPREFIXFMT(totalbuf));
//table_printf(nc, sep, "%7.1f", nsdelta ? totalframes / ((double)nsdelta / NANOSECS_IN_SEC) : 0);
ncplane_putchar(n, '\n');
ncplane_set_fg_rgb8(n, 0xfe, 0x20, 0x76); // PANTONE Strong Red C + 3x0x20
#ifdef DFSG_BUILD

@ -104,10 +104,17 @@ fill_chunk(struct ncplane* n, int idx){
ncchannel_set_rgb8(&ur, g, b, r);
ncchannel_set_rgb8(&ll, b, r, g);
int ret = 0;
if(ncplane_gradient2x1(n, -1, -1, 0, 0, ul, ur, ll, lr) <= 0){
ret = -1;
if(notcurses_canutf8(ncplane_notcurses(n))){
if(ncplane_gradient2x1(n, -1, -1, 0, 0, ul, ur, ll, lr) <= 0){
ret = -1;
}
ret |= ncplane_double_box(n, 0, channels, maxy - 1, maxx - 1, 0);
}else{
if(ncplane_gradient(n, -1, -1, 0, 0, " ", NCSTYLE_NONE, ul, ur, ll, lr) <= 0){
ret = -1;
}
ret |= ncplane_ascii_box(n, 0, channels, maxy - 1, maxx - 1, 0);
}
ret |= ncplane_double_box(n, 0, channels, maxy - 1, maxx - 1, 0);
if(maxx >= 4 && maxy >= 3){
// don't zero-index to viewer
ret |= (ncplane_printf_yx(n, (maxy - 1) / 2, (maxx - 1) / 2, "%02d", idx + 1) < 0);
@ -122,9 +129,15 @@ draw_bounding_box(struct ncplane* n, int yoff, int xoff, int chunky, int chunkx)
ncchannels_set_fg_rgb8(&channels, 180, 80, 180);
//channels_set_bg_rgb8(&channels, 0, 0, 0);
ncplane_cursor_move_yx(n, yoff, xoff);
ret = ncplane_rounded_box(n, 0, channels,
if(notcurses_canutf8(ncplane_notcurses(n))){
ret = ncplane_rounded_box(n, 0, channels,
CHUNKS_VERT * chunky + yoff + 1,
CHUNKS_HORZ * chunkx + xoff + 1, 0);
}else{
ret = ncplane_ascii_box(n, 0, channels,
CHUNKS_VERT * chunky + yoff + 1,
CHUNKS_HORZ * chunkx + xoff + 1, 0);
}
return ret;
}

@ -447,11 +447,11 @@ tinfo_debug_styles(const notcurses* nc, struct ncplane* n, const char* indent){
tinfo_debug_cap(n, "el", get_escape(ti, ESCAPE_EL));
finish_line(n);
ncplane_putstr(n, indent);
tinfo_debug_cap(n, "utf8", ti->caps.utf8);
tinfo_debug_cap(n, "2x1", ti->caps.halfblocks);
tinfo_debug_cap(n, "2x2", ti->caps.quadrants);
tinfo_debug_cap(n, "3x2", ti->caps.sextants);
tinfo_debug_cap(n, "4x2", ti->caps.braille);
tinfo_debug_cap(n, "utf8", notcurses_canutf8(nc));
tinfo_debug_cap(n, "2x1", notcurses_canhalfblock(nc));
tinfo_debug_cap(n, "2x2", notcurses_canquadrant(nc));
tinfo_debug_cap(n, "3x2", notcurses_cansextant(nc));
tinfo_debug_cap(n, "4x2", notcurses_canbraille(nc));
tinfo_debug_cap(n, "img", notcurses_canopen_images(nc));
tinfo_debug_cap(n, "vid", notcurses_canopen_videos(nc));
tinfo_debug_cap(n, "indn", get_escape(ti, ESCAPE_INDN));

@ -405,11 +405,11 @@ int ncdirect_cursor_pop(ncdirect* n){
}
static inline int
ncdirect_align(struct ncdirect* n, ncalign_e align, int c){
ncdirect_align(struct ncdirect* n, ncalign_e align, unsigned c){
if(align == NCALIGN_LEFT){
return 0;
}
int cols = ncdirect_dim_x(n);
unsigned cols = ncdirect_dim_x(n);
if(c > cols){
return 0;
}
@ -974,7 +974,10 @@ char* ncdirect_readline(ncdirect* n, const char* prompt){
if(fprintf(n->ttyfp, "%s", prompt) < 0){
return NULL;
}
int dimx = ncdirect_dim_x(n);
unsigned dimx = ncdirect_dim_x(n);
if(dimx == 0){
return NULL;
}
// FIXME what if we're reading from redirected input, not a terminal?
unsigned y, xstart;
if(cursor_yx_get(n, u7, &y, &xstart)){

@ -26,8 +26,9 @@ ncplane_polyfill_inner(ncplane* n, unsigned y, unsigned x, const nccell* c, cons
return -1;
}
int ret = 0;
struct topolyfill* s;
do{
struct topolyfill* s = stack;
s = stack;
stack = stack->next;
y = s->y;
x = s->x;
@ -37,34 +38,42 @@ ncplane_polyfill_inner(ncplane* n, unsigned y, unsigned x, const nccell* c, cons
if(strcmp(glust, filltarg) == 0){
++ret;
if(nccell_duplicate(n, cur, c) < 0){
// FIXME need free stack!
return -1;
goto err;
}
//fprintf(stderr, "blooming from %d/%d ret: %d\n", y, x, ret);
if(y){
if(create_polyfill_op(y - 1, x, &stack) == NULL){
return -1;
goto err;
}
}
if(y + 1 < n->leny){
if(create_polyfill_op(y + 1, x, &stack) == NULL){
return -1;
goto err;
}
}
if(x){
if(create_polyfill_op(y, x - 1, &stack) == NULL){
return -1;
goto err;
}
}
if(x + 1 < n->lenx){
if(create_polyfill_op(y, x + 1, &stack) == NULL){
return -1;
goto err;
}
}
}
free(s);
}while(stack);
return ret;
err:
free(s);
while(stack){
struct topolyfill* tmp = stack->next;
free(stack);
stack = tmp;
}
return -1;
}
// at the initial step only, invalid ystart, xstart is an error, so explicitly check.

@ -402,6 +402,7 @@ program_line_drawing_chars(int fd, struct unimapdesc* map){
for(size_t widx = 0 ; widx < wcslen(s->ws) ; ++widx){
if(!found[widx]){
if(add_to_map(map, s->ws[widx], fontidx)){
free(found);
return -1;
}
++toadd;

@ -1053,6 +1053,7 @@ notcurses_early_init(const struct notcurses_options* opts, FILE* fp, unsigned* u
}
if(opts->termtype){
if(putenv_term(opts->termtype)){
free(ret);
return NULL;
}
}

@ -23,6 +23,7 @@ ncprogbar* ncprogbar_create(ncplane* n, const ncprogbar_options* opts){
ret->retrograde = opts->flags & NCPROGBAR_OPTION_RETROGRADE;
if(ncplane_set_widget(n, ret, (void(*)(void*))ncprogbar_destroy)){
ncplane_destroy(n);
free(ret);
return NULL;
}
return ret;

@ -1256,8 +1256,9 @@ ncvisual_polyfill_core(ncvisual* n, unsigned y, unsigned x, uint32_t rgba, uint3
stack->x = x;
stack->next = NULL;
int ret = 0;
struct topolyfill* s;
do{
struct topolyfill* s = stack;
s = stack;
stack = s->next;
y = s->y;
x = s->x;
@ -1268,29 +1269,37 @@ ncvisual_polyfill_core(ncvisual* n, unsigned y, unsigned x, uint32_t rgba, uint3
*pixel = rgba;
if(y){
if(create_polyfill_op(y - 1, x, &stack) == NULL){
// FIXME need free stack!
return -1;
goto err;
}
}
if(y + 1 < n->pixy){
if(create_polyfill_op(y + 1, x, &stack) == NULL){
return -1;
goto err;
}
}
if(x){
if(create_polyfill_op(y, x - 1, &stack) == NULL){
return -1;
goto err;
}
}
if(x + 1 < n->pixx){
if(create_polyfill_op(y, x + 1, &stack) == NULL){
return -1;
goto err;
}
}
}
free(s);
}while(stack);
return ret;
err:
free(s);
while(stack){
s = stack->next;
free(stack);
stack = s;
}
return -1;
}
int ncvisual_polyfill_yx(ncvisual* n, unsigned y, unsigned x, uint32_t rgba){

@ -3,7 +3,7 @@
<!-- need 4.9.0+ for SPDX license expressions -->
<metadata minClientVersion="4.9.0">
<id>Notcurses</id>
<version>2.4.99</version>
<version>2.4.999</version>
<title>Notcurses TUI/CLI library</title>
<description>Library for blingful TUIs and character graphics</description>
<authors>nick black</authors>

Loading…
Cancel
Save