notcurses_plane(3)
๐ notcurses_plane(3): operations on ncplanes ๐
okay bestie, welcome to notcurses_plane(3) โ the manual page where Notcurses explains the absolute main-character object of its whole drawing system. this is part 1 of 3, so we are not rushing, we are not skipping, and we are definitely not leaving out a single flag. ๐
๐ธ NAME ๐ธ
notcurses_plane โ operations on ncplanes
โจ SYNOPSIS โจ
first things first, you gotta include the header, obviously:
#include <notcurses/notcurses.h>
then we have the plane option bits. do not lose these, they are important:
#define NCPLANE_OPTION_HORALIGNED 0x0001ull
#define NCPLANE_OPTION_VERALIGNED 0x0002ull
#define NCPLANE_OPTION_MARGINALIZED 0x0004ull
#define NCPLANE_OPTION_FIXED 0x0008ull
#define NCPLANE_OPTION_AUTOGROW 0x0010ull
#define NCPLANE_OPTION_VSCROLL 0x0020ull
and here is the options struct, with every comment exactly where it belongs:
typedef struct ncplane_options {
int y; // vertical placement relative to parent plane
int x; // horizontal placement relative to parent plane
int rows; // number of rows, must be positive
int cols; // number of columns, must be positive
void* userptr; // user curry, may be NULL
const char* name; // name (used only for debugging), may be NULL
int (*resizecb)(struct ncplane*); // called on parent resize
uint64_t flags; // closure over NCPLANE_OPTION_*
unsigned margin_b, margin_r; // bottom and right margins
} ncplane_options;
style bits, slay:
#define NCSTYLE_MASK 0xffffu
#define NCSTYLE_ITALIC 0x0010u
#define NCSTYLE_UNDERLINE 0x0008u
#define NCSTYLE_UNDERCURL 0x0004u
#define NCSTYLE_BOLD 0x0002u
#define NCSTYLE_STRUCK 0x0001u
#define NCSTYLE_NONE 0
and now the function buffet. yes, it is a lot. no, we are not skipping any of them:
struct ncplane* ncplane_create(struct ncplane* n, const ncplane_options* nopts);
struct ncplane* ncpile_create(struct notcurses* n, const ncplane_options* nopts);
struct ncplane* ncplane_reparent(struct ncplane* n, struct ncplane* newparent);
struct ncplane* ncplane_reparent_family(struct ncplane* n, struct ncplane* newparent);
int ncplane_descendant_p(const struct ncplane* n, const struct ncplane* ancestor);
int ncplane_resize_realign(struct ncplane* n);
int ncplane_resize_maximize(struct ncplane* n);
int ncplane_resize_marginalized(struct ncplane* n);
int ncplane_resize_placewithin(struct ncplane* n);
void ncplane_set_resizecb(struct ncplane* n, int(resizecb)(struct ncplane));
int (ncplane_resizecb(const struct ncplane n**))(struct ncplane*);**
struct ncplane* ncplane_dup(struct ncplane* n, void* opaque);
int ncplane_resize(struct ncplane* n, int keepy, int keepx, int keepleny, int keeplenx, int yoff, int xoff, int ylen, int xlen);
int ncplane_move_yx(struct ncplane* n, int y, int x);
int ncplane_move_rel(struct ncplane* n, int y, int x);
void ncplane_yx(const struct ncplane* n, int* restrict y, int* restrict x);
int ncplane_y(const struct ncplane* n);
int ncplane_x(const struct ncplane* n);
void ncplane_abs_yx(const struct ncplane* n, int* y, int* x);
int ncplane_abs_y(const struct ncplane* n);
int ncplane_abs_x(const struct ncplane* n);
struct ncplane* ncplane_parent(struct ncplane* n);
const struct ncplane* ncplane_parent_const(const struct ncplane* n);
int ncplane_set_base_cell(struct ncplane* ncp, const nccell* c);
int ncplane_set_base(struct ncplane* ncp, const char* egc, uint16_t stylemask, uint64_t channels);
int ncplane_base(struct ncplane* ncp, nccell* c);
static inline void ncplane_move_top(struct ncplane* n);
static inline void ncplane_move_bottom(struct ncplane* n);
void ncplane_move_family_top(struct ncplane* n);
void ncplane_move_family_bottom(struct ncplane* n);
int ncplane_move_above(struct ncplane* restrict n, struct ncplane* restrict targ);
int ncplane_move_below(struct ncplane* restrict n, struct ncplane* restrict targ);
int ncplane_move_family_above(struct ncplane* restrict n, struct ncplane* restrict targ);
int ncplane_move_family_below(struct ncplane* restrict n, struct ncplane* restrict targ);
struct ncplane* ncplane_below(struct ncplane* n);
struct ncplane* ncplane_above(struct ncplane* n);
char* ncplane_at_cursor(struct ncplane* n, uint16_t* stylemask, uint64_t* channels);
int ncplane_at_cursor_cell(struct ncplane* n, nccell* c);
char* ncplane_at_yx(const struct ncplane* n, int y, int x, uint16_t* stylemask, uint64_t* channels);
int ncplane_at_yx_cell(struct ncplane* n, int y, int x, nccell* c);
uint32_t* ncplane_as_rgba(const struct ncplane* nc, ncblitter_e blit, unsigned begy, unsigned begx, unsigned leny, unsigned lenx, unsigned* pxdimy, unsigned* pxdimx);
char* ncplane_contents(const struct ncplane* nc, int begy, int begx, unsigned leny, unsigned lenx);
void* ncplane_set_userptr(struct ncplane* n, void* opaque);
void* ncplane_userptr(struct ncplane* n);
void ncplane_dim_yx(const struct ncplane* n, unsigned* restrict rows, unsigned* restrict cols);
static inline unsigned ncplane_dim_y(const struct ncplane* n);
static inline unsigned ncplane_dim_x(const struct ncplane* n);
void ncplane_cursor_yx(const struct ncplane* n, unsigned* restrict y, unsigned* restrict x);
unsigned ncplane_cursor_y(const struct ncplane* n);
unsigned ncplane_cursor_x(const struct ncplane* n);
void ncplane_translate(const struct ncplane* src, const struct ncplane* dst, int* restrict y, int* restrict x);
bool ncplane_translate_abs(const struct ncplane* n, int* restrict y, int* restrict x);
uint64_t ncplane_channels(const struct ncplane* n);
void ncplane_set_channels(struct ncplane* nc, uint64_t channels);
static inline unsigned ncplane_bchannel(struct ncplane* nc);
static inline unsigned ncplane_fchannel(struct ncplane* nc);
uint64_t ncplane_set_bchannel(struct ncplane* nc, uint32_t channel);
uint64_t ncplane_set_fchannel(struct ncplane* nc, uint32_t channel);
static inline unsigned ncplane_fg_rgb(struct ncplane* nc);
static inline unsigned ncplane_bg_rgb(struct ncplane* nc);
int ncplane_set_fg_rgb(struct ncplane* n, uint32_t channel);
int ncplane_set_bg_rgb(struct ncplane* n, uint32_t channel);
static inline unsigned ncplane_fg_alpha(struct ncplane* nc);
static inline unsigned ncplane_bg_alpha(struct ncplane* nc);
int ncplane_set_fg_alpha(struct ncplane* n, unsigned alpha);
int ncplane_set_bg_alpha(struct ncplane* n, unsigned alpha);
static inline unsigned ncplane_fg_rgb8(struct ncplane* n, unsigned* r, unsigned* g, unsigned* b);
static inline unsigned ncplane_bg_rgb8(struct ncplane* n, unsigned* r, unsigned* g, unsigned* b);
int ncplane_set_fg_rgb8(struct ncplane* n, unsigned r, unsigned g, unsigned b);
int ncplane_set_bg_rgb8(struct ncplane* n, unsigned r, unsigned g, unsigned b);
void ncplane_set_fg_rgb8_clipped(struct ncplane* n, int r, int g, int b);
void ncplane_set_bg_rgb8_clipped(struct ncplane* n, int r, int g, int b);
void ncplane_set_fg_default(struct ncplane* n);
void ncplane_set_bg_default(struct ncplane* n);
int ncplane_set_fg_palindex(struct ncplane* n, unsigned idx);
int ncplane_set_bg_palindex(struct ncplane* n, unsigned idx);
uint16_t ncplane_styles(const struct ncplane* n);
void ncplane_set_styles(struct ncplane* n, unsigned stylebits);
void ncplane_on_styles(struct ncplane* n, unsigned stylebits);
void ncplane_off_styles(struct ncplane* n, unsigned stylebits);
void ncplane_greyscale(struct ncplane* n);
int ncplane_blit_bgrx(struct ncplane* nc, int placey, int placex, int linesize, ncblitter_e blitter, const unsigned char* data, int begy, int begx, int leny, int lenx);
int ncplane_blit_rgba(struct ncplane* nc, int placey, int placex, int linesize, ncblitter_e blitter, const unsigned char* data, int begy, int begx, int leny, int lenx);
int ncplane_destroy(struct ncplane* n);
int ncplane_family_destroy(struct ncplane n);
void notcurses_drop_planes(struct notcurses* nc);
int ncplane_mergedown(struct ncplane* src, struct ncplane* dst, int begsrcy, int begsrcx, unsigned leny, unsigned lenx, int dsty, int dstx);
int ncplane_mergedown_simple(struct ncplane* restrict src, struct ncplane* restrict dst);
void ncplane_erase(struct ncplane* n);
int ncplane_erase_region(struct ncplane* n, int ystart, int xstart, int ylen, int xlen);
bool ncplane_set_scrolling(struct ncplane* n, unsigned scrollp);
bool ncplane_scrolling_p(const struct ncplane* n);
bool ncplane_set_autogrow(struct ncplane* n, unsigned growp);
bool ncplane_autogrow_p(const struct ncplane* n);
int ncplane_scrollup(struct ncplane* n, int r);
int ncplane_scrollup_child(struct ncplane* n, const struct ncplane* child);
int ncplane_rotate_cw(struct ncplane* n);
int ncplane_rotate_ccw(struct ncplane* n);
void ncplane_pixel_geom(const struct notcurses* n, unsigned* restrict pxy, unsigned* restrict pxx, unsigned* restrict celldimy, unsigned* restrict celldimx, unsigned* restrict maxbmapy, unsigned* restrict maxbmapx);
int ncplane_set_name(struct ncplane* n, const char* name);
char* ncplane_name(const struct ncplane* n);
๐ฆ DESCRIPTION ๐ฆ
okay so ncplanes are the fundamental drawing object of Notcurses. every output function takes a struct ncplane as an argument. they can be any size, and they can be placed anywhere. truly flexible icon behavior. ๐
besides its framebuffer โ which is a rectilinear matrix of nccells (go peek at notcurses_cell(3)) โ an ncplane is defined by:
- a base
nccell, used for any cell on the plane without a glyph, - the
egcpoolbacking itsnccells, - a current cursor location,
- a current style, foreground channel, and background channel,
- its geometry,
- a configured user pointer,
- position relative to the standard plane,
- the plane, if any, to which it is bound,
- the next plane bound by the plane to which it is bound,
- the head of the list of its bound planes,
- its resize methodology,
- whether a
sprixel(seenotcurses_visual(3)) is associated, - its z-index, and
- a name (used only for debugging).
new planes can be created with ncplane_create. if a plane is bound to another plane, its x and y coordinates are relative to the plane to which it is bound, and if that parent plane moves, all its bound planes move along with it. and listen: when a plane is destroyed, all planes bound to it โ directly or transitively โ are destroyed too. so yeah, the family tree gets taken out together. ๐ญ
if the NCPLANE_OPTION_HORALIGNED flag is provided, x is interpreted as an ncalign_e rather than an absolute position. if the NCPLANE_OPTION_VERALIGNED flag is provided, y is interpreted as an ncalign_e rather than an absolute position. either way, all positions are relative to the parent plane. and ncplane_resize_realign should usually be used together with these flags, so that the plane is automatically realigned upon a resize of its parent. alignment stays cute even when the parent decides to resize, periodt. โจ
okay so if you pass NCPLANE_OPTION_MARGINALIZED? you literally cannot pass NCPLANE_OPTION_HORALIGNED or NCPLANE_OPTION_VERALIGNED alongside it, and rows and cols both have to be 0. Instead, y and x get read as your top and left margins, and margin_b and margin_r get read as your bottom and right margins. The plane then claims the maximum space possible, subject to its parent planes and those margins. It can't shrink below 1x1 โ the margins are best-effort, bestie, not a promise ๐
ncplane_resize_marginalized should usually be used together with this flag, so that the plane is automatically resized. the way that works is chef's kiss
ncplane_reparentโ detaches the planenfrom any plane it's bound to, and binds it tonewparent. its children get reparented to its previous parent. the standard plane cannot be reparented โ don't even try. ifnewparentisNULL, the plane becomes the root plane of a new, unrendered stack. and whenncplane_reparent_familyis used, all planes bound tonmove along with it during the reparenting operation. see Piles below, iykyk ๐ซถncplane_destroyโ destroys a particular ncplane, and after that it must not be used again, periodt.notcurses_drop_planesdestroys all ncplanes other than the stdplane. any references to such planes are, of course, invalidated. > it's undefined to destroy a plane concurrently with any other operation involving that plane, or any operation involving the z-axis. that's a ๐ not a vibe.ncplane_move_yxโ moves a plane relative to its parent plane's origin. if the plane has no parent, the move is relative to the rendering area. it can go off-screen entirely (in which case it will not be visible following rasterization) or just partially off-screen. both are valid, no cap โจncplane_cursor_move_yxโ a plane has a virtual cursor; this sets its new position. specifying-1as one or both coordinates will hold that axis constant. you may move a cursor relatively to its current position withncplane_cursor_move_rel. unless coordinates are specified for a call, action takes place at the plane's virtual cursor, which automatically moves along with output. grab the current virtual cursor location withncplane_cursor_yx.ncplane_yxโ returns the coordinates of the specified plane's origin, relative to the plane to which it is bound. either or both ofyandxmay beNULL.ncplane_yandncplane_xlet you retrieve a single component of this location.ncplane_abs_yxreturns the coordinates of the specified plane's origin relative to its pile. pick your flavor ๐ncplane_translateโ translates coordinates expressed relative to the planesrc, and writes the coordinates of that cell relative todst. the cell need not intersect withdst, though this will yield coordinates which are invalid for writing or reading ondst. ifdstisNULL, it's taken to refer to the standard plane.ncplane_translate_abstakes coordinates expressed relative to the standard plane, and returns coordinates relative todst, returningfalseif the coordinates are invalid fordst.ncplane_mergedownโ writes todstthe frame that would be rendered if onlysrcanddstexisted on the z-axis, anddstrepresented the entirety of the rendering region. only those cells wheresrcintersects withdstmight see changes. it is an error to merge a plane onto itself โ mirror check failed, bestie ๐ncplane_eraseโ zeroes out every cell of the plane, dumps the egcpool, and homes the cursor. the base cell is preserved, as are the active attributes. (yes, base cell survives. we love a resilient icon ๐)ncplane_erase_regionโ does the same for a subregion of the plane. for this one: supply0forylenand/orxlento erase through that dimension, starting at the specified point. supply-1forystartand/orxstartto use the cursor's current position along that axis for a starting point. negativeylenandxlenmove up and to the left from the starting coordinate; positiveylenandxlenmove down and to the right from same. see BUGS below ๐- resizing in general โ when a plane is resized (whether by
ncplane_resize,SIGWINCH, or any other mechanism), a depth-first recursion is performed on its children. each child plane having a non-NULLresizecbwill see that callback invoked following resizing of its parent's plane. if it returns non-zero, the resizing cascade terminates, returning non-zero. otherwise, resizing proceeds recursively. it's giving cascade ๐ ncplane_move_top,ncplane_move_bottomโ extract their argumentnfrom the z-axis, and reinsert it at the top or bottom, respectively, of its pile. both areO(1), slay.ncplane_move_family_topandncplane_move_family_bottomdo the same, and move any bound descendants along with the plane. ordering among the plane and its descendants will be maintained.ncplane_move_above,ncplane_move_belowโ move the argumentnabove or below, respectively, the argumenttarg. both operate inO(1).ncplane_at_yx,ncplane_at_yx_cellโ retrieve the contents of the plane at the specified coordinate. the content is returned as it will be used during rendering, and thus integrates any base cell as appropriate. if called on the secondary columns of a wide glyph,ncplane_at_yxreturns the EGC, and thus cannot be used to distinguish between primary and secondary columns.ncplane_at_yx_cell, however, preserves this info: retrieving a secondary column of a wide glyph withncplane_at_yx_cellwill fill in thenccellargument such thatnccell_extended_gcluster(3)returns an empty string, andnccell_wide_right_p(3)returnstrue.ncplane_set_nameโ sets the plane's name, freeing any old name.namemay beNULL.ncplane_set_nameduplicates the provided name internally (so it's yours to free, icon behaviour ๐).
a little family-order example so it Actually Lands โ assume a pile with A at the top of its z-axis, followed by B, C, D, and E, where E is bound to C:
- calling
ncplane_move_family_toponCresults in the orderCEABD - calling
ncplane_move_family_bottomonCresults in the orderABDCE - calling
ncplane_move_family_toporncplane_move_toponEresults inEABCD - calling
ncplane_move_family_bottomonEis a no-op
those last two functions are O(N) on the number of planes in the pile, btw. math is math ๐งฎ
and hey โ ifncplane_at_yxis invoked upon a sprixel plane, the control sequence will be returned for any valid coordinates (note that this may be quite large ๐ญ). this does not apply toncplane_at_yx_cell, which will return an error.
๐ธ Base cells ๐ธ
each plane has a base cell, initialized to all zeroes. when rendering, the cells of the plane are examined in turn, and each cell has three independent rendering elements โ its EGC, its foreground channel, and its background channel. any default channel is replaced with the corresponding channel from that plane's base cell. the way that works is lowkey iconic.
ncplane_erase has no effect on the base cell. so calling ncplane_erase on a plane whose base cell is a purple A results (for rendering purposes) in a plane made up entirely of purple As. obsessed ๐
ncplane_set_base_cellโ uses thenccellc(which must be bound to the ncplanencp, and must be the firstnccellof a multicolumn sequence) to set the base cell.ncplane_set_baseโ does the same withegc,stylemask, andchannels.
๐ฆ Piles ๐ฆ
a single Notcurses context is made up of one or more piles. a pile is a set of one or more ncplanes, including the partial orderings made up of their binding and z-axis pointers. a pile has a top and bottom ncplane (this might be a single plane), and one or more root planes โ planes which are bound to themselves. multiple threads can concurrently operate on distinct piles, even changing one while rendering another. multitasking queen behaviour ๐
each plane is part of one and only one pile. by default, a plane is part of the same pile containing that plane to which it is bound. if ncpile_create is used in the place of ncplane_create, the returned plane becomes the root plane, top, and bottom of a new pile โ and as a root plane, it is bound to itself. a new pile can also be created by reparenting a plane to itself, though if the plane is already a root plane, this is a no-op. ngl, that's a relief.
when a plane is moved to a different pile (whether new or preexisting), any planes which were bound to it are rebound to its previous parent. if the plane was a root plane of some pile, any bound planes become root planes. the new plane is placed immediately atop its new parent on its new pile's z-axis. when ncplane_reparent_family is used, all planes bound to the reparented plane are moved along with it, and their relative z-order is maintained. fr fr, order preserved.
more info lives in notcurses_pile(3). go read it, bestie ๐ซถ
๐ง Binding ๐ง
the planes of a pile make up a directed acyclic forest. planes bound to themselves make up the root planes of the pile. every plane is either a root plane, or bound to some other plane in its pile. a plane and its descendants make up a family. when a plane is moved using ncplane_move_yx, its family is moved along with it. family sticks together, always ๐
โญ Scrolling โญ
all planes, including the standard plane, are created with scrolling disabled. control scrolling on a per-plane basis with ncplane_set_scrolling.
attempting to print past the end of a line will stop at the plane boundary, and indicate an error. on a plane 10 columns wide and two rows high, printing "0123456789" at the origin should succeed, but printing "01234567890" will by default fail at the eleventh character. in either case, the cursor will be left at location 0x10; it must be moved before further printing can take place.
if scrolling is enabled, the first row will be filled with 01234546789, the second row will have 0 written to its first column, and the cursor will end up at 1x1. (yes, that's the digits exactly as they come out. no, we're not fixing it. ๐ญ)
note that it is still an error to manually attempt to move the cursor off-plane, or to specify off-plane output.
box-drawing does not result in scrolling; attempting to draw a 2x11 box on our 2x10 plane will result in an error and no output. no partial box for you, sorry ๐
when scrolling is enabled, and output takes place while the cursor is past the end of the last row, the first row is discarded, all other rows are moved up, the last row is cleared, and output begins at the beginning of the last row. this does not take place until output is generated (i.e. it is possible to fill a plane when scrolling is enabled). so it's lazy, but like, in a good way โจ
creating a plane with the NCPLANE_OPTION_VSCROLL flag is equivalent to immediately calling ncplane_set_scrolling on that plane with an argument of true.
by default, planes bound to a scrolling plane will scroll along with it, if they intersect the plane. this can be disabled by creating them with the NCPLANE_OPTION_FIXED flag. stay put, bestie ๐ซถ
๐ฑ Autogrow ๐ฑ
so normally? once your output hits the right boundary of a plane, you literally cannot place more output unless you move the cursor first. periodt. if scrolling is enabled, the cursor auto-moves down and to the left in this case, but once it reaches the bottom-right corner of the plane, it is impossible to place more output without a scrolling event. that's just the rules, bestie.
if autogrow is in play, the plane gets automatically enlarged to accommodate output. glow-up behavior โจ. if scrolling is disabled, growth takes place to the right; otherwise it takes place at the bottom. and the plane only grows in one dimension โ no diagonal main-character growth. also: autogrow cannot be enabled for the standard plane, sorry not sorry.
creating a plane with the NCPLANE_OPTION_AUTOGROW flag is equivalent to immediately calling ncplane_set_autogrow on that plane with an argument of true. same energy, different syntax ๐
.
๐ผ๏ธ Bitmaps ๐ผ๏ธ
ncplane_pixel_geom retrieves pixel geometry details. pxy and pxx return the size of the plane in pixels. celldimy and celldimx return the size of a cell in pixels (these ought be the same across planes, so if they're not, side-eye ๐). maxbmapy and maxbmapx describe the largest bitmap which can be displayed in the plane. any parameter (save n) may be NULL, so pass only what you need ๐ซถ.
when a plane is blitted to using ncvisual_blit and NCBLIT_PIXEL (see notcurses_visual(3)), it ceases to accept cell-based output. the sprixel will remain associated until a new sprixel is blitted to the plane, the plane is resized, the plane is erased, or the plane is destroyed. those are the exits, no cap.
the base cell of a sprixelated plane has no effect; if the sprixel is not even multiples of the cell geometry, the "excess plane" is ignored during rendering. not its moment, honestly.
๐ RETURN VALUES ๐
ncplane_createandncplane_dupโ return a newstruct ncplaneon success, orNULLon failure.ncplane_userptrโ returns the configured user pointer for thencplane, and cannot fail.ncplane_belowโ returns the plane below the specifiedncplane. if the provided plane is the bottommost plane,NULLis returned. it cannot fail.ncplane_set_scrollingโ returnstrueif scrolling was previously enabled, andfalseotherwise.ncplane_at_yxandncplane_at_cursorโ return a heap-allocated copy of the EGC at the relevant cell, orNULLif the cell is invalid. the caller should free this result ๐งน.ncplane_at_yx_cellandncplane_at_cursor_cellโ instead load these values into annccell, which is invalidated if the associated plane is destroyed. the caller should release thisnccellwithnccell_release.ncplane_as_rgbaโ returns a heap-allocated array ofuint32_tvalues, each representing a single RGBA pixel, orNULLon failure.ncplane_erase_regionโ returns-1ifystartorxstartare less than-1, or outside the plane.ncplane_cursor_move_yxโ returns-1if the coordinates are beyond the dimensions of the specified plane (except for the special value-1).ncplane_cursor_move_relโ returns-1if the coordinates are beyond the dimensions of the specified plane.ncplane_nameโ returns a heap-allocated copy of the plane's name, orNULLif it has no name (or on error).- functions returning
intreturn0on success, and non-zero on error. - all other functions cannot fail (and return
void).
๐ NOTES ๐
no notes were given here in the original, bestie โ it's a clean slate, moving right along โจ.
๐ BUGS ๐
ncplane_at_yx doesn't yet account for bitmap-based graphics (see notcurses_visual(3)). whatever glyph-based contents existed on the plane when the bitmap was blitted will continue to be returned. awkward, but true ๐.
when the alternate screen is not used (see notcurses_init(3)), the contents of the terminal at startup remain present until obliterated on a cell-by-cell basis. ncplane_erase and ncplane_erase_region cannot be used to clear the terminal of startup content. if you want the screen cleared on startup, but do not want to use (or rely on) the alternate screen, use something like:
ncplane_set_base(notcurses_stdplane(nc), " ", 0, 0);
notcurses_render(nc);
or simply:
notcurses_refresh(nc);
๐ SEE ALSO ๐
notcurses(3)notcurses_capabilities(3)notcurses_cell(3)notcurses_output(3)notcurses_pile(3)notcurses_stdplane(3)notcurses_visual(3)
๐ AUTHORS ๐
the icon, the legend: nick black nickblack@linux.com ๐ซถ.
that's the whole page, bestie โ source/version v3.0.17, page notcurses_plane(3), signing off with maximum sparkle ๐โจ๐
๐ฆ where this page comes from
- alpine 3.24: notcurses-doc 3.0.17-r0 ยท Apache-2.0
- ubuntu resolute: libnotcurses-core-dev 3.0.17+dfsg-3 ยท Apache-2.0
- ubuntu stonking: libnotcurses-core-dev 3.0.17+dfsg-3 ยท Apache-2.0
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms