diff --git a/doc/man/man3/notcurses.3.md b/doc/man/man3/notcurses.3.md index 9a96b055e..8674f3f9e 100644 --- a/doc/man/man3/notcurses.3.md +++ b/doc/man/man3/notcurses.3.md @@ -154,7 +154,7 @@ not itself perform any locking. * It is **not** safe to add, delete, or reorder ncplanes within a single pile from multiple threads. -Only one thread may call **notcurses_getc** or any other input-related thread +Only one thread may call **notcurses_get(3)** or any other input-related thread at a time, but it **is** safe to call for input while another thread renders. Since multiple threads can concurrently manipulate distinct ncplanes, peak diff --git a/doc/man/man3/notcurses_plane.3.md b/doc/man/man3/notcurses_plane.3.md index d4bd0c32b..ec24f07c0 100644 --- a/doc/man/man3/notcurses_plane.3.md +++ b/doc/man/man3/notcurses_plane.3.md @@ -396,9 +396,17 @@ 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 +When **ncplane_reparent_family** is used, all planes bound to the reparented plane are moved along with it. Their relative z-order is maintained. +## 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. + ## Scrolling All planes, including the standard plane, are created with scrolling disabled. @@ -503,7 +511,7 @@ It should not be used in new code. # BUGS **ncplane_at_yx** doesn't yet account for bitmap-based graphics (see -**notcurses_visual**). Whatever glyph-based contents existed on the plane when +**notcurses_visual(3)**). Whatever glyph-based contents existed on the plane when the bitmap was blitted will continue to be returned. When the alternate screen is not used (see **notcurses_init(3)**), the contents diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 5a1cf7cff..f11731552 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -1244,11 +1244,13 @@ typedef struct ncplane_options { // must both be positive. This plane is initially at the top of the z-buffer, // as if ncplane_move_top() had been called on it. The void* 'userptr' can be // retrieved (and reset) later. A 'name' can be set, used in debugging. -API ALLOC struct ncplane* ncplane_create(struct ncplane* n, const ncplane_options* nopts); +API ALLOC struct ncplane* ncplane_create(struct ncplane* n, const ncplane_options* nopts) + __attribute__ ((nonnull (1, 2))); // Same as ncplane_create(), but creates a new pile. The returned plane will // be the top, bottom, and root of this new pile. -API ALLOC struct ncplane* ncpile_create(struct notcurses* nc, const ncplane_options* nopts); +API ALLOC struct ncplane* ncpile_create(struct notcurses* nc, const ncplane_options* nopts) + __attribute__ ((nonnull (1, 2))); // Suitable for use as a 'resizecb', this will resize the plane to the visual // region's size. It is used for the standard plane. @@ -1296,7 +1298,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. Bound planes are *not* duplicated; the new +// be bound to the same parent (unless 'n' is a root plane, in which case the +// new plane will be bound to it). Bound planes are *not* duplicated; the new // plane is bound to the parent of 'n', but has no bound planes. API ALLOC struct ncplane* ncplane_dup(const struct ncplane* n, void* opaque) __attribute__ ((nonnull (1))); diff --git a/src/lib/automaton.c b/src/lib/automaton.c index 5e209998c..573e0b5bc 100644 --- a/src/lib/automaton.c +++ b/src/lib/automaton.c @@ -197,7 +197,6 @@ link_kleene(automaton* a, esctrie* e, unsigned follow){ e->trie[follow] = esctrie_idx(a, term); }else if(e->trie[i] == 0){ e->trie[i] = esctrie_idx(a, targ); - // FIXME travel to the ends and link targ there } } targ->kleene = esctrie_idx(a, targ); diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index d60e09b2d..54fc167a8 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -428,7 +428,7 @@ make_ncpile(notcurses* nc, ncplane* n){ // (as once more is n). ncplane* ncplane_new_internal(notcurses* nc, ncplane* n, const ncplane_options* nopts){ - if(nopts->flags >= (NCPLANE_OPTION_MARGINALIZED << 1u)){ + if(nopts->flags >= (NCPLANE_OPTION_FIXED << 1u)){ logwarn("Provided unsupported flags %016jx\n", (uintmax_t)nopts->flags); } if(nopts->flags & NCPLANE_OPTION_HORALIGNED || nopts->flags & NCPLANE_OPTION_VERALIGNED){ @@ -863,13 +863,16 @@ int ncplane_destroy(ncplane* ncp){ if( (*ncp->bprev = ncp->bnext) ){ ncp->bnext->bprev = ncp->bprev; } + }else if(ncp->bnext){ + ncp->bnext->bprev = NULL; } // recursively reparent our children to the plane to which we are bound. // this will extract each one from the sibling list. struct ncplane* bound = ncp->blist; while(bound){ struct ncplane* tmp = bound->bnext; - if(ncplane_reparent_family(bound, ncp->boundto) == NULL){ + ncplane* bindto = ((ncp == ncp->boundto) ? bound : ncp->boundto); + if(ncplane_reparent_family(bound, bindto) == NULL){ ret = -1; } bound = tmp; @@ -2568,19 +2571,21 @@ ncplane* ncplane_reparent(ncplane* n, ncplane* newparent){ // to be called before unbinding 'n' from old pile. static void unsplice_zaxis_recursive(ncplane* n){ + // might already have been unspliced, in which case ->above/->below are NULL if(ncplane_pile(n)->top == n){ ncplane_pile(n)->top = n->below; - }else{ + }else if(n->above){ n->above->below = n->below; } if(ncplane_pile(n)->bottom == n){ ncplane_pile(n)->bottom = n->above; - }else{ + }else if(n->below){ n->below->above = n->above; } for(ncplane* child = n->blist ; child ; child = child->bnext){ unsplice_zaxis_recursive(child); } + n->below = n->above = NULL; } // unsplice our sprixel from the pile's sprixellist, and then unsplice all @@ -2616,7 +2621,8 @@ unsplice_sprixels_recursive(ncplane* n, sprixel* prev){ // recursively splice 'n' and children into the z-axis, above 'n->boundto'. // handles 'n' == 'n->boundto'. to be called after binding 'n' into new pile. static void -splice_zaxis_recursive(ncplane* n){ +splice_zaxis_recursive(ncplane* n, ncpile* p){ + n->pile = p; if(n != n->boundto){ if((n->above = n->boundto->above) == NULL){ n->pile->top = n; @@ -2627,7 +2633,7 @@ splice_zaxis_recursive(ncplane* n){ n->boundto->above = n; } for(ncplane* child = n->blist ; child ; child = child->bnext){ - splice_zaxis_recursive(child); + splice_zaxis_recursive(child, p); } } @@ -2646,7 +2652,11 @@ ncplane* ncplane_reparent_family(ncplane* n, ncplane* newparent){ if( (*n->bprev = n->bnext) ){ n->bnext->bprev = n->bprev; } + }else if(n->bnext){ + n->bnext->bprev = NULL; } + n->bprev = NULL; + n->bnext = NULL; // ncplane_notcurses() goes through ncplane_pile(). since we're possibly // destroying piles below, get the notcurses reference early on. notcurses* nc = ncplane_notcurses(n); @@ -2658,15 +2668,17 @@ ncplane* ncplane_reparent_family(ncplane* n, ncplane* newparent){ } n->boundto = newparent; if(n == n->boundto){ // we're a new root plane + logdebug("reparenting new root plane %p\n", n); + unsplice_zaxis_recursive(n); n->bnext = NULL; n->bprev = NULL; - splice_zaxis_recursive(n); pthread_mutex_lock(&nc->pilelock); if(ncplane_pile(n)->top == NULL){ // did we just empty our pile? ncpile_destroy(ncplane_pile(n)); } make_ncpile(ncplane_notcurses(n), n); pthread_mutex_unlock(&nc->pilelock); + splice_zaxis_recursive(n, ncplane_pile(n)); }else{ // establish ourselves as a sibling of new parent's children if( (n->bnext = newparent->blist) ){ n->bnext->bprev = &n->bnext; @@ -2675,13 +2687,13 @@ ncplane* ncplane_reparent_family(ncplane* n, ncplane* newparent){ newparent->blist = n; // place it immediately above the new binding plane if crossing piles if(n->pile != ncplane_pile(n->boundto)){ - splice_zaxis_recursive(n); pthread_mutex_lock(&nc->pilelock); if(ncplane_pile(n)->top == NULL){ // did we just empty our pile? ncpile_destroy(ncplane_pile(n)); } n->pile = ncplane_pile(n->boundto); pthread_mutex_unlock(&nc->pilelock); + splice_zaxis_recursive(n, ncplane_pile(n)); } } if(s){ // must be on new plane, with sprixels to donate