From 6fb5c1e5128286100ec8220e854446cc150da9fd Mon Sep 17 00:00:00 2001 From: nick black Date: Thu, 3 Dec 2020 00:25:29 -0500 Subject: [PATCH] Fill out some API holes, improve some comments - Disambiguate the docs for ncplane_erase() - Add ncpile_top(), ncpile_bottom() - Refuse attempts to modify the standard plane's resizecb - Copy alignment and resizecb in ncplane_dup() - Add cell_load_egc32() --- NEWS.md | 7 +++++++ USAGE.md | 16 ++++++++++++++++ doc/man/man3/notcurses_cell.3.md | 2 ++ doc/man/man3/notcurses_plane.3.md | 10 +++++++++- include/notcurses/notcurses.h | 30 +++++++++++++++++++++++++----- src/lib/notcurses.c | 20 ++++++++++++++++---- 6 files changed, 75 insertions(+), 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index fb66c9c61..73721841a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,13 @@ This document attempts to list user-visible changes and any major internal rearrangements of Notcurses. +* 2.0.10 (not yet released) + * `ncpile_top()` and `ncpile_bottom()` have been added, returning the top + or bottommost plane, respectively, of the pile containing their argument. + * Added `cell_load_egc32()`, allowing a cell to be released and then reloaded + with a UTF-8 EGC of up to 4 bytes, passed as a `uint32_t` (as opposed to a + `const char *`). + * 2.0.9 (2020-12-01) * `ncmenu`s now automatically expand or shrink to match their binding plane. diff --git a/USAGE.md b/USAGE.md index 13387e460..fb78c7789 100644 --- a/USAGE.md +++ b/USAGE.md @@ -884,6 +884,12 @@ struct ncplane* ncplane_below(struct ncplane* n); // Return the ncplane above this one, or NULL if this is at the stack's top. struct ncplane* ncplane_above(struct ncplane* n); + +// Return the topmost plane of the pile containing 'n'. +struct ncplane* ncpile_top(struct ncplane* n); + +// Return the bottommost plane of the pile containing 'n'. +struct ncplane* ncpile_bottom(struct ncplane* n); ``` Each plane holds a user pointer which can be retrieved and set (or ignored). In @@ -1747,6 +1753,7 @@ cell_double_wide_p(const cell* c){ return (c->channels & CELL_WIDEASIAN_MASK); } +// Load a 7-bit char 'ch' into the cell 'c'. static inline int cell_load_char(struct ncplane* n, cell* c, char ch){ cell_release(n, c); @@ -1755,6 +1762,15 @@ cell_load_char(struct ncplane* n, cell* c, char ch){ return 1; } +// Load a UTF-8 encoded EGC of up to 4 bytes into the cell 'c'. +static inline int +cell_load_egc32(struct ncplane* n, cell* c, uint32_t egc){ + cell_release(n, c); + c->channels &= ~(CELL_WIDEASIAN_MASK | CELL_NOBACKGROUND_MASK); + c->gcluster = htole(egc); + return 1; +} + // return a pointer to the NUL-terminated EGC referenced by 'c'. this pointer // is invalidated by any further operation on the plane 'n', so...watch out! const char* cell_extended_gcluster(const struct ncplane* n, const cell* c); diff --git a/doc/man/man3/notcurses_cell.3.md b/doc/man/man3/notcurses_cell.3.md index 04007c78e..9f1ec8bb3 100644 --- a/doc/man/man3/notcurses_cell.3.md +++ b/doc/man/man3/notcurses_cell.3.md @@ -76,6 +76,8 @@ typedef struct cell { **int cell_load_char(struct ncplane* ***n***, cell* ***c***, char ***ch***);** +**int cell_load_egc32(struct ncplane* ***n***, cell* ***c***, uint32_t ***egc***);** + **char* cell_extract(const struct ncplane* ***n***, const cell* ***c***, uint16_t* ***stylemask***, uint64_t* ***channels***);** **uint32_t cell_bchannel(const cell* ***c***);** diff --git a/doc/man/man3/notcurses_plane.3.md b/doc/man/man3/notcurses_plane.3.md index 868f61476..5ae752ab7 100644 --- a/doc/man/man3/notcurses_plane.3.md +++ b/doc/man/man3/notcurses_plane.3.md @@ -33,6 +33,10 @@ typedef struct ncplane_options { **struct ncplane* notcurses_bottom(struct notcurses* ***n***);** +**struct ncplane* ncpile_top(struct ncplane* ***n***);** + +**struct ncplane* ncpile_bottom(struct ncplane* ***n***);** + **struct ncplane* ncplane_reparent(struct ncplane* ***n***, struct ncplane* ***newparent***);** **struct ncplane* ncplane_reparent_family(struct ncplane* ***n***, struct ncplane* ***newparent***);** @@ -238,7 +242,7 @@ of the rendering region. Only those cells where **src** intersects with **dst** might see changes. It is an error to merge a plane onto itself. **ncplane_erase** zeroes out every cell of the plane, dumps the egcpool, and -homes the cursor. The base cell is preserved. +homes the cursor. The base cell is preserved, as are the active attributes. When a plane is resized (whether by **ncplane_resize**, **SIGWINCH**, or any other mechanism), a depth-first recursion is performed on its children. @@ -304,6 +308,10 @@ plane is the bottommost plane, NULL is returned. It cannot fail. **ncplane_set_scrolling** returns **true** if scrolling was previously enabled, and **false** otherwise. +**ncpile_top** and **ncpile_bottom** return the topmost and bottommost planes, +respectively, of the pile containing their argument. **notcurses_top** and +**notcurses_bottom** do the same for the standard pile. + **ncplane_at_yx** and **ncplane_at_cursor** return a heap-allocated copy of the EGC at the relevant cell, or NULL if the cell is invalid. The caller should free this result. **ncplane_at_yx_cell** and **ncplane_at_cursor_cell** instead load diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 0d9aa9fe1..b2627c6e6 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -730,11 +730,23 @@ cellcmp(const struct ncplane* n1, const cell* RESTRICT c1, static inline int cell_load_char(struct ncplane* n, cell* c, char ch){ cell_release(n, c); + // FIXME don't allow non-printing garbage c->channels &= ~(CELL_WIDEASIAN_MASK | CELL_NOBACKGROUND_MASK); c->gcluster = htole((uint32_t)ch); return 1; } +// Load a UTF-8 encoded EGC of up to 4 bytes into the cell 'c'. +static inline int +cell_load_egc32(struct ncplane* n, cell* c, uint32_t egc){ + cell_release(n, c); + // FIXME don't allow non-printing garbage, nor invalid forms + // FIXME this might well be a wide egc, augh + c->channels &= ~(CELL_WIDEASIAN_MASK | CELL_NOBACKGROUND_MASK); + c->gcluster = htole(egc); + return 1; +} + // These log levels consciously map cleanly to those of libav; Notcurses itself // does not use this full granularity. The log level does not affect the opening // and closing banners, which can be disabled via the notcurses_option struct's @@ -844,6 +856,12 @@ API struct notcurses* notcurses_init(const notcurses_options* opts, FILE* fp); // Destroy a Notcurses context. API int notcurses_stop(struct notcurses* nc); +// Return the topmost plane of the pile containing 'n'. +API struct ncplane* ncpile_top(struct ncplane* n); + +// Return the bottommost plane of the pile containing 'n'. +API struct ncplane* ncpile_bottom(struct ncplane* n); + // Renders the pile of which 'n' is a part. Rendering this pile again will blow // away the render. To actually write out the render, call ncpile_rasterize(). API int ncpile_render(struct ncplane* n); @@ -1068,6 +1086,7 @@ API struct ncplane* ncplane_new(struct ncplane* n, int rows, int cols, int y, in API int ncplane_resize_realign(struct ncplane* n); // Replace the ncplane's existing resizecb with 'resizecb' (which may be NULL). +// The standard plane's resizecb may not be changed. API void ncplane_set_resizecb(struct ncplane* n, int(*resizecb)(struct ncplane*)); // Returns the ncplane's current resize callback. @@ -1088,7 +1107,8 @@ API struct ncplane* ncplane_reparent_family(struct ncplane* n, struct ncplane* n // Duplicate an existing ncplane. The new plane will have the same geometry, // will duplicate all content, and will start with the same rendering state. // The new plane will be immediately above the old one on the z axis, and will -// be bound to the same parent. +// be bound to the same parent. Bound planes are *not* duplicated; the new +// plane is bound to the parent of 'n', but has no bound planes. API struct ncplane* ncplane_dup(const struct ncplane* n, void* opaque); // provided a coordinate relative to the origin of 'src', map it to the same @@ -1812,10 +1832,10 @@ API int ncplane_mergedown(const struct ncplane* RESTRICT src, int begsrcy, int begsrcx, int leny, int lenx, int dsty, int dstx); -// 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 -// with this ncplane is invalidated, and must not be used after the call, -// *excluding* the base cell. The cursor is homed. +// Erase every cell in the ncplane (each cell is initialized to the null glyph +// and the default channels/styles). All cells associated with this ncplane are +// invalidated, and must not be used after the call, *excluding* the base cell. +// The cursor is homed. The plane's active attributes are unaffected. API void ncplane_erase(struct ncplane* n); // Extract the 32-bit background channel from a cell. diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 1420fe1ae..180c53652 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -502,8 +502,6 @@ inline int ncplane_cursor_move_yx(ncplane* n, int y, int x){ ncplane* ncplane_dup(const ncplane* n, void* opaque){ int dimy = n->leny; int dimx = n->lenx; - uint16_t attr = ncplane_styles(n); - uint64_t chan = ncplane_channels(n); // if we're duping the standard plane, we need adjust for marginalia const struct notcurses* nc = ncplane_notcurses_const(n); const int placey = n->absy - nc->margin_t; @@ -515,6 +513,8 @@ ncplane* ncplane_dup(const ncplane* n, void* opaque){ .cols = dimx, .userptr = opaque, .name = n->name, + .resizecb = ncplane_resizecb(n), + .flags = 0, }; ncplane* newn = ncplane_create(n->boundto, &nopts); if(newn){ @@ -526,8 +526,9 @@ ncplane* ncplane_dup(const ncplane* n, void* opaque){ ncplane_destroy(newn); return NULL; } - newn->stylemask = attr; - newn->channels = chan; + newn->align = n->align; + newn->stylemask = ncplane_styles(n); + newn->channels = ncplane_channels(n); memmove(newn->fb, n->fb, sizeof(*n->fb) * dimx * dimy); // we dupd the egcpool, so just dup the goffset newn->basecell = n->basecell; @@ -2036,6 +2037,14 @@ ncplane* notcurses_bottom(notcurses* n){ return ncplane_pile(n->stdplane)->bottom; } +ncplane* ncpile_top(ncplane* n){ + return ncplane_pile(n)->top; +} + +ncplane* ncpile_bottom(ncplane* n){ + return ncplane_pile(n)->bottom; +} + ncplane* ncplane_below(ncplane* n){ return n->below; } @@ -2168,6 +2177,9 @@ const ncplane* ncplane_parent_const(const ncplane* n){ } void ncplane_set_resizecb(ncplane* n, int(*resizecb)(ncplane*)){ + if(n == notcurses_stdplane(ncplane_notcurses(n))){ + return; + } n->resizecb = resizecb; }