From c6520ab84c13786ff773aa13f80366b0a60a71f4 Mon Sep 17 00:00:00 2001 From: nick black Date: Sat, 15 Feb 2020 18:40:43 -0500 Subject: [PATCH] add notcurses_drop_planes, use from demo #346 --- README.md | 17 ++++-- doc/man/man3/notcurses_lines.3.md | 2 +- doc/man/man3/notcurses_ncplane.3.md | 7 ++- include/notcurses.h | 14 +++-- python/src/notcurses/build_notcurses.py | 1 + src/demo/demo.c | 13 ++--- src/demo/eagle.c | 4 +- src/lib/notcurses.c | 71 +++++++++++++++++++++++++ tests/fills.cpp | 22 ++++++++ 9 files changed, 134 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 156ab54e7..ea57c1999 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,9 @@ Utility functions operating on the toplevel `notcurses` object include: // Return the topmost ncplane, of which there is always at least one. struct ncplane* notcurses_top(struct notcurses* n); +// Destroy any ncplanes other than the stdplane. +void notcurses_drop_planes(struct notcurses* nc); + // Refresh our idea of the terminal's dimensions, reshaping the standard plane // if necessary. Without a call to this function following a terminal resize // (as signaled via SIGWINCH), notcurses_render() might not function properly. @@ -1109,9 +1112,14 @@ int ncplane_gradient(struct ncplane* n, const char* egc, uint32_t attrword, // Draw a gradient with its upper-left corner at the current cursor position, // having dimensions 'ylen'x'xlen'. See ncplane_gradient for more information. -int ncplane_gradient_sized(struct ncplane* n, const char* egc, - uint32_t attrword, uint64_t ul, uint64_t ur, - uint64_t ll, uint64_t lr, int ylen, int xlen); +static inline int +ncplane_gradient_sized(struct ncplane* n, const char* egc, uint32_t attrword, + uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, + int ylen, int xlen){ + int y, x; + ncplane_cursor_yx(n, &y, &x); + return ncplane_gradient(n, egc, attrword, ul, ur, ll, lr, y + ylen - 1, x + xlen - 1); +} ``` My 14 year-old self would never forgive me if we didn't have sweet palette fades. @@ -1201,7 +1209,8 @@ static inline unsigned ncplane_fg_alpha(const struct ncplane* nc){ return channels_fg_alpha(ncplane_channels(nc)); } -/ Extract 2 bits of background alpha from 'struct ncplane', shifted to LSBs. + +// Extract 2 bits of background alpha from 'struct ncplane', shifted to LSBs. static inline unsigned ncplane_bg_alpha(const struct ncplane* nc){ return channels_bg_alpha(ncplane_channels(nc)); diff --git a/doc/man/man3/notcurses_lines.3.md b/doc/man/man3/notcurses_lines.3.md index 8049e2fdc..473d2e27e 100644 --- a/doc/man/man3/notcurses_lines.3.md +++ b/doc/man/man3/notcurses_lines.3.md @@ -61,7 +61,7 @@ ncplane_box_sized(struct ncplane* n, const cell* ul, const cell* ur, **int ncplane_gradient(struct ncplane* n, const char* egc, uint32_t attrword, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ystop, int xstop);** -**int ncplane_gradient_sized(struct ncplane* n, const char* egc, uint32_t attrword, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen);** +**static inline int ncplane_gradient_sized(struct ncplane* n, const char* egc, uint32_t attrword, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen);** # DESCRIPTION diff --git a/doc/man/man3/notcurses_ncplane.3.md b/doc/man/man3/notcurses_ncplane.3.md index d17c5afe4..0e98d2991 100644 --- a/doc/man/man3/notcurses_ncplane.3.md +++ b/doc/man/man3/notcurses_ncplane.3.md @@ -116,6 +116,8 @@ notcurses_ncplane - operations on notcurses planes **int ncblit_rgba(struct ncplane* nc, int placey, int placex, int linesize, const unsigned char* data, int begy, int begx, int leny, int lenx);** +**void notcurses_drop_planes(struct notcurses* nc);** + ## DESCRIPTION Ncplanes are the fundamental drawing object of notcurses. All output functions @@ -132,6 +134,9 @@ anywhere. In addition to its framebuffer--a rectilinear matrix of cells * its position relative to the visible plane, and * its z-index. +**notcurses_drop_planes** destroys all ncplanes other than the stdplane. Any +references to such planes are, of course, invalidated. + # RETURN VALUES **ncplane_new(3)**, **ncplane_aligned(3)**, and **ncplane_dup(3)** all return a @@ -145,7 +150,7 @@ plane is the bottommost plane, NULL is returned. It cannot fail. Functions returning **int** return 0 on success, and non-zero on error. -All other functions either cannot fail (and return **void**). +All other functions cannot fail (and return **void**). # NOTES diff --git a/include/notcurses.h b/include/notcurses.h index eb725783f..59d7a2ffc 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -246,6 +246,9 @@ API int notcurses_render(struct notcurses* nc); // Return the topmost ncplane, of which there is always at least one. API struct ncplane* notcurses_top(struct notcurses* n); +// Destroy any ncplanes other than the stdplane. +API void notcurses_drop_planes(struct notcurses* nc); + // All input is currently taken from stdin, though this will likely change. We // attempt to read a single UTF8-encoded Unicode codepoint, *not* an entire // Extended Grapheme Cluster. It is also possible that we will read a special @@ -958,9 +961,14 @@ API int ncplane_gradient(struct ncplane* n, const char* egc, uint32_t attrword, // Draw a gradient with its upper-left corner at the current cursor position, // having dimensions 'ylen'x'xlen'. See ncplane_gradient for more information. -API int ncplane_gradient_sized(struct ncplane* n, const char* egc, - uint32_t attrword, uint64_t ul, uint64_t ur, - uint64_t ll, uint64_t lr, int ylen, int xlen); +static inline int +ncplane_gradient_sized(struct ncplane* n, const char* egc, uint32_t attrword, + uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, + int ylen, int xlen){ + int y, x; + ncplane_cursor_yx(n, &y, &x); + return ncplane_gradient(n, egc, attrword, ul, ur, ll, lr, y + ylen - 1, x + xlen - 1); +} // Erase every cell in the ncplane, resetting all attributes to normal, all // colors to the default color, and all cells to undrawn. All cells associated diff --git a/python/src/notcurses/build_notcurses.py b/python/src/notcurses/build_notcurses.py index 6f37db95b..9b7c421b1 100644 --- a/python/src/notcurses/build_notcurses.py +++ b/python/src/notcurses/build_notcurses.py @@ -87,6 +87,7 @@ int ncplane_set_base_cell(struct ncplane* ncp, const cell* c); int ncplane_set_base(struct ncplane* ncp, uint64_t channels, uint32_t attrword, const char* egc); int ncplane_base(struct ncplane* ncp, cell* c); struct ncplane* notcurses_top(struct notcurses* n); +void notcurses_drop_planes(struct notcurses* nc); int notcurses_refresh(struct notcurses* n); int notcurses_resize(struct notcurses* n, int* y, int* x); struct ncplane* ncplane_new(struct notcurses* nc, int rows, int cols, int yoff, int xoff, void* opaque); diff --git a/src/demo/demo.c b/src/demo/demo.c index 1fe330f53..ae6abf6c1 100644 --- a/src/demo/demo.c +++ b/src/demo/demo.c @@ -423,9 +423,6 @@ int main(int argc, char** argv){ if(notcurses_mouse_enable(nc)){ goto err; } - if(menu_create(nc) == NULL){ - goto err; - } if(input_dispatcher(nc)){ goto err; } @@ -444,13 +441,17 @@ int main(int argc, char** argv){ do{ restart_demos = false; interrupted = false; + notcurses_drop_planes(nc); + if(menu_create(nc) == NULL){ + goto err; + } if(ext_demos(nc, spec, ignore_failures) == NULL){ goto err; } + if(hud_destroy()){ // destroy here since notcurses_drop_planes will kill it + goto err; + } }while(restart_demos); - if(hud_destroy()){ - goto err; - } if(stop_input()){ goto err; } diff --git a/src/demo/eagle.c b/src/demo/eagle.c index ecce6c32d..b4b811d95 100644 --- a/src/demo/eagle.c +++ b/src/demo/eagle.c @@ -176,8 +176,8 @@ eagles(struct notcurses* nc){ continue; } e[i].yoff += random() % (2 + i) - 1; - if(e[i].yoff < 1){ - e[i].yoff = 1; + if(e[i].yoff < 0){ + e[i].yoff = 0; }else if(e[i].yoff + height >= truey){ e[i].yoff = truey - height - 1; } diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 37b08e674..c7dd7e907 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -1003,6 +1003,20 @@ int ncdirect_stop(ncdirect* nc){ return ret; } +void notcurses_drop_planes(notcurses* nc){ + ncplane* p = nc->top; + while(p){ + ncplane* tmp = p->z; + if(nc->stdscr == p){ + nc->top = p; + p->z = NULL; + }else{ + free_plane(p); + } + p = tmp; + } +} + int notcurses_stop(notcurses* nc){ int ret = 0; if(nc){ @@ -1996,3 +2010,60 @@ int ncplane_polyfill_yx(ncplane* n, int y, int x, const cell* c){ ncplane_unlock(n); return ret; } + +// calculate one of the channels of a gradient at a particular point. +static uint32_t +calc_gradient_channel(uint32_t ul, uint32_t ur, uint32_t ll, uint32_t lr, + int y, int x, int ylen, int xlen){ + return 0; +} + +// calculate both channels of a gradient at a particular point, storing them +// into `c`->channels. x and y ought be the location within the gradient. +static void +calc_gradient_channels(cell* c, uint64_t ul, uint64_t ur, uint64_t ll, + uint64_t lr, int y, int x, int ylen, int xlen){ + cell_set_fchannel(c, calc_gradient_channel(channels_fchannel(ul), + channels_fchannel(ur), + channels_fchannel(ll), + channels_fchannel(lr), + y, x, ylen, xlen)); + cell_set_bchannel(c, calc_gradient_channel(channels_fchannel(ul), + channels_fchannel(ur), + channels_fchannel(ll), + channels_fchannel(lr), + y, x, ylen, xlen)); +} + +int ncplane_gradient(ncplane* n, const char* egc, uint32_t attrword, + uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, + int ystop, int xstop){ + int yoff, xoff, ymax, xmax; + ncplane_cursor_yx(n, &yoff, &xoff); + // must be at least 1x1, with its upper-left corner at the current cursor + if(ystop < yoff){ + return -1; + } + if(xstop < xoff){ + return -1; + } + ncplane_dim_yx(n, &ymax, &xmax); + // must be within the ncplane + if(xstop >= xmax || ystop >= ymax){ + return -1; + } + const int xlen = xstop - xoff + 1; + const int ylen = ystop - ylen + 1; + for(int y = yoff ; y < ylen ; ++y){ + for(int x = xoff ; x < xlen ; ++x){ + cell* targc = ncplane_cell_ref_yx(n, y, x); + targc->channels = 0; + targc->attrword = 0; + if(cell_load(n, targc, egc) < 0){ + return -1; + } + calc_gradient_channels(&targc, ul, ur, ll, lr, y - yoff, x - xoff, ylen, xlen); + } + } + return 0; +} diff --git a/tests/fills.cpp b/tests/fills.cpp index 48c71d49c..f16a06471 100644 --- a/tests/fills.cpp +++ b/tests/fills.cpp @@ -29,6 +29,18 @@ TEST_CASE("Fills") { CHECK(0 > ncplane_polyfill_yx(n_, -1, 0, &c)); } + SUBCASE("PolyfillOnGlyph") { + cell c = CELL_SIMPLE_INITIALIZER('+'); + struct ncplane* pfn = ncplane_new(nc_, 4, 4, 0, 0, nullptr); + REQUIRE(nullptr != pfn); + CHECK(16 == ncplane_polyfill_yx(pfn, 0, 0, &c)); + CHECK(0 < ncplane_putc_yx(pfn, 0, 0, &c)); + // Trying to fill the origin ought fill zero cells + CHECK(0 == ncplane_polyfill_yx(pfn, 0, 0, &c)); + CHECK(0 == notcurses_render(nc_)); + CHECK(0 == ncplane_destroy(pfn)); + } + SUBCASE("PolyfillEmptyPlane") { cell c = CELL_SIMPLE_INITIALIZER('+'); struct ncplane* pfn = ncplane_new(nc_, 4, 4, 0, 0, nullptr); @@ -53,6 +65,16 @@ TEST_CASE("Fills") { CHECK(0 == ncplane_destroy(pfn)); } + SUBCASE("GradientMonochromatic") { + struct ncplane* pfn = ncplane_new(nc_, 4, 4, 0, 0, nullptr); + REQUIRE(nullptr != pfn); + uint64_t ul, ur, ll, lr; + ul = ur = ll = lr = 0; + CHECK(0 == ncplane_gradient(pfn, " ", 0, ul, ur, ll, lr, 3, 3)); + CHECK(0 == notcurses_render(nc_)); + CHECK(0 == ncplane_destroy(pfn)); + } + CHECK(0 == notcurses_stop(nc_)); CHECK(0 == fclose(outfp_));