O(1) z-axis moves #623

Replace the singly-linked z-axis with a doubly-linked list,
and reimplement all z-axis moves as O(1) functions.
Eliminate ncplane_move_{above/below}_unsafe(), as there are no
longer unsafe moves.
This commit is contained in:
nick black 2020-05-20 15:32:27 -04:00
parent a288c2e654
commit 37a4114f42
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC
13 changed files with 163 additions and 177 deletions

View File

@ -1,6 +1,11 @@
This document attempts to list user-visible changes and any major internal This document attempts to list user-visible changes and any major internal
rearrangements of Notcurses. rearrangements of Notcurses.
* 1.4.2.4 (2020-05-20)
* Removed `ncplane_move_above_unsafe()` and `ncplane_move_below_unsafe()`;
all z-axis moves are now safe. Z-axis moves are all now O(1), rather
than the previous O(N).
* 1.4.2.3 (2020-05-17) * 1.4.2.3 (2020-05-17)
* Added `notcurses_canutf8()`, to verify use of UTF-8 encoding. * Added `notcurses_canutf8()`, to verify use of UTF-8 encoding.
* Fixed bug in `ncvisual_from_plane()` when invoked on the standard plane. * Fixed bug in `ncvisual_from_plane()` when invoked on the standard plane.

View File

@ -549,7 +549,6 @@ struct ncplane* ncplane_reparent(struct ncplane* n, struct ncplane* newparent);
// Duplicate an existing ncplane. The new plane will have the same geometry, // Duplicate an existing ncplane. The new plane will have the same geometry,
// will duplicate all content, and will start with the same rendering state. // 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.
struct ncplane* ncplane_dup(struct ncplane* n, void* opaque); struct ncplane* ncplane_dup(struct ncplane* n, void* opaque);
// Merge the ncplane 'src' down onto the ncplane 'dst'. This is most rigorously // Merge the ncplane 'src' down onto the ncplane 'dst'. This is most rigorously
@ -709,13 +708,17 @@ int ncplane_base(struct ncplane* ncp, cell* c);
```c ```c
// Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or bottom. // Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or bottom.
int ncplane_move_top(struct ncplane* n); void ncplane_move_top(struct ncplane* n);
int ncplane_move_bottom(struct ncplane* n); void ncplane_move_bottom(struct ncplane* n);
// Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'. // Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'.
// Returns non-zero if 'n' is already in the desired location. 'n' and
// 'below' must not be the same plane.
int ncplane_move_below(struct ncplane* restrict n, struct ncplane* restrict below); int ncplane_move_below(struct ncplane* restrict n, struct ncplane* restrict below);
// Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'. // Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'.
// Returns non-zero if 'n' is already in the desired location. 'n' and
// 'above' must not be the same plane.
int ncplane_move_above(struct ncplane* restrict n, struct ncplane* restrict above); int ncplane_move_above(struct ncplane* restrict n, struct ncplane* restrict above);
// Return the ncplane below this one, or NULL if this is at the stack's bottom. // Return the ncplane below this one, or NULL if this is at the stack's bottom.

View File

@ -36,9 +36,9 @@ notcurses_plane - operations on ncplanes
**int ncplane_move_bottom(struct ncplane* n);** **int ncplane_move_bottom(struct ncplane* n);**
**int ncplane_move_above(struct ncplane* n, struct ncplane* above);** **int ncplane_move_above(struct ncplane* restrict n, struct ncplane* restrict above);**
**int ncplane_move_below(struct ncplane* n, struct ncplane* below);** **int ncplane_move_below(struct ncplane* restrict n, struct ncplane* restrict below);**
**struct ncplane* ncplane_below(struct ncplane* n);** **struct ncplane* ncplane_below(struct ncplane* n);**

View File

@ -271,14 +271,14 @@ namespace ncpp
return error_guard (ncplane_move_yx (plane, y, x), -1); return error_guard (ncplane_move_yx (plane, y, x), -1);
} }
bool move_top () const NOEXCEPT_MAYBE void move_top () noexcept
{ {
return error_guard (ncplane_move_top (plane), -1); ncplane_move_top (plane);
} }
bool move_bottom () const NOEXCEPT_MAYBE void move_bottom () noexcept
{ {
return error_guard (ncplane_move_bottom (plane), -1); ncplane_move_bottom (plane);
} }
bool move_below (Plane &below) const NOEXCEPT_MAYBE bool move_below (Plane &below) const NOEXCEPT_MAYBE
@ -294,19 +294,6 @@ namespace ncpp
return move_below (*below); return move_below (*below);
} }
bool move_below_unsafe (Plane &below) const NOEXCEPT_MAYBE
{
return error_guard (ncplane_move_below_unsafe (plane, below.plane), -1);
}
bool move_below_unsafe (Plane *below) const
{
if (below == nullptr)
throw invalid_argument ("'below' must be a valid pointer");
return move_below_unsafe (*below);
}
bool move_above (Plane &above) const NOEXCEPT_MAYBE bool move_above (Plane &above) const NOEXCEPT_MAYBE
{ {
return error_guard (ncplane_move_above (plane, above.plane), -1); return error_guard (ncplane_move_above (plane, above.plane), -1);
@ -320,19 +307,6 @@ namespace ncpp
return move_above (*above); return move_above (*above);
} }
bool move_above_unsafe (Plane &above) const NOEXCEPT_MAYBE
{
return error_guard (ncplane_move_above_unsafe (plane, above.plane), -1);
}
bool move_above_unsafe (Plane *above) const
{
if (above == nullptr)
throw invalid_argument ("'above' must be a valid pointer");
return move_above (*above);
}
bool mergedown (Plane &dst) const bool mergedown (Plane &dst) const
{ {
return mergedown (&dst); return mergedown (&dst);

View File

@ -1124,32 +1124,20 @@ API int ncplane_move_yx(struct ncplane* n, int y, int x);
API void ncplane_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x); API void ncplane_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x);
// Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or bottom. // Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or bottom.
API int ncplane_move_top(struct ncplane* n); API void ncplane_move_top(struct ncplane* n);
API int ncplane_move_bottom(struct ncplane* n); API void ncplane_move_bottom(struct ncplane* n);
// Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'. // Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'.
API int ncplane_move_above_unsafe(struct ncplane* RESTRICT n, // Returns non-zero if 'n' is already in the desired location. 'n' and
const struct ncplane* RESTRICT above); // 'above' must not be the same plane.
API int ncplane_move_above(struct ncplane* RESTRICT n,
static inline int struct ncplane* RESTRICT above);
ncplane_move_above(struct ncplane* n, struct ncplane* above){
if(n == above){
return -1;
}
return ncplane_move_above_unsafe(n, above);
}
// Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'. // Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'.
API int ncplane_move_below_unsafe(struct ncplane* RESTRICT n, // Returns non-zero if 'n' is already in the desired location. 'n' and
const struct ncplane* RESTRICT below); // 'below' must not be the same plane.
API int ncplane_move_below(struct ncplane* RESTRICT n,
static inline int struct ncplane* RESTRICT below);
ncplane_move_below(struct ncplane* n, struct ncplane* below){
if(n == below){
return -1;
}
return ncplane_move_below_unsafe(n, below);
}
// Return the plane below this one, or NULL if this is at the bottom. // Return the plane below this one, or NULL if this is at the bottom.
API struct ncplane* ncplane_below(struct ncplane* n); API struct ncplane* ncplane_below(struct ncplane* n);

View File

@ -134,10 +134,10 @@ int ncplane_move_yx(struct ncplane* n, int y, int x);
void ncplane_yx(struct ncplane* n, int* y, int* x); void ncplane_yx(struct ncplane* n, int* y, int* x);
void ncplane_dim_yx(const struct ncplane* n, int* rows, int* cols); void ncplane_dim_yx(const struct ncplane* n, int* rows, int* cols);
int ncplane_putc_yx(struct ncplane* n, int y, int x, const cell* c); int ncplane_putc_yx(struct ncplane* n, int y, int x, const cell* c);
int ncplane_move_top(struct ncplane* n); void ncplane_move_top(struct ncplane* n);
int ncplane_move_bottom(struct ncplane* n); void ncplane_move_bottom(struct ncplane* n);
int ncplane_move_below(struct ncplane* n, struct ncplane* below); int ncplane_move_below(struct ncplane* restrict n, struct ncplane* restrict below);
int ncplane_move_above(struct ncplane* n, struct ncplane* above); int ncplane_move_above(struct ncplane* restrict n, struct ncplane* restrict above);
struct ncplane* ncplane_below(struct ncplane* n); struct ncplane* ncplane_below(struct ncplane* n);
char* notcurses_at_yx(struct notcurses* nc, int yoff, int xoff, uint32_t* attrword, uint64_t* channels); char* notcurses_at_yx(struct notcurses* nc, int yoff, int xoff, uint32_t* attrword, uint64_t* channels);
char* ncplane_at_cursor(struct ncplane* n, uint32_t* attrword, uint64_t* channels); char* ncplane_at_cursor(struct ncplane* n, uint32_t* attrword, uint64_t* channels);

View File

@ -214,7 +214,7 @@ int main(void){
if(!nc.mouse_enable()){ if(!nc.mouse_enable()){
return EXIT_FAILURE; return EXIT_FAILURE;
} }
auto n = nc.get_stdplane(&dimy, &dimx); std::unique_ptr<Plane*> n = std::make_unique<Plane*>(nc.get_stdplane(&dimy, &dimx));
ncpp::Plane pplane{PLOTHEIGHT, dimx, dimy - PLOTHEIGHT, 0, nullptr}; ncpp::Plane pplane{PLOTHEIGHT, dimx, dimy - PLOTHEIGHT, 0, nullptr};
struct ncplot_options popts{}; struct ncplot_options popts{};
// FIXME would be nice to switch over to exponential at some level // FIXME would be nice to switch over to exponential at some level
@ -227,14 +227,14 @@ int main(void){
if(!plot){ if(!plot){
return EXIT_FAILURE; return EXIT_FAILURE;
} }
n->set_fg_rgb(0x00, 0x00, 0x00); (*n)->set_fg_rgb(0x00, 0x00, 0x00);
n->set_bg_rgb(0xbb, 0x64, 0xbb); (*n)->set_bg_rgb(0xbb, 0x64, 0xbb);
n->styles_on(CellStyle::Underline); (*n)->styles_on(CellStyle::Underline);
if(n->putstr(0, NCAlign::Center, "mash keys, yo. give that mouse some waggle! ctrl+d exits.") <= 0){ if((*n)->putstr(0, NCAlign::Center, "mash keys, yo. give that mouse some waggle! ctrl+d exits.") <= 0){
return EXIT_FAILURE; return EXIT_FAILURE;
} }
n->styles_set(CellStyle::None); (*n)->styles_set(CellStyle::None);
n->set_bg_default(); (*n)->set_bg_default();
if(!nc.render()){ if(!nc.render()){
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -262,35 +262,35 @@ int main(void){
} }
mtx.unlock(); mtx.unlock();
} }
if(!n->cursor_move(y, 0)){ if(!(*n)->cursor_move(y, 0)){
break; break;
} }
n->set_fg_rgb(0xd0, 0xd0, 0xd0); (*n)->set_fg_rgb(0xd0, 0xd0, 0xd0);
n->printf("%c%c%c ", ni.alt ? 'A' : 'a', ni.ctrl ? 'C' : 'c', (*n)->printf("%c%c%c ", ni.alt ? 'A' : 'a', ni.ctrl ? 'C' : 'c',
ni.shift ? 'S' : 's'); ni.shift ? 'S' : 's');
if(r < 0x80){ if(r < 0x80){
n->set_fg_rgb(128, 250, 64); (*n)->set_fg_rgb(128, 250, 64);
if(n->printf("ASCII: [0x%02x (%03d)] '%lc'", r, r, if((*n)->printf("ASCII: [0x%02x (%03d)] '%lc'", r, r,
(wchar_t)(iswprint(r) ? r : printutf8(r))) < 0){ (wchar_t)(iswprint(r) ? r : printutf8(r))) < 0){
break; break;
} }
}else{ }else{
if(nckey_supppuab_p(r)){ if(nckey_supppuab_p(r)){
n->set_fg_rgb(250, 64, 128); (*n)->set_fg_rgb(250, 64, 128);
if(n->printf("Special: [0x%02x (%02d)] '%s'", r, r, nckeystr(r)) < 0){ if((*n)->printf("Special: [0x%02x (%02d)] '%s'", r, r, nckeystr(r)) < 0){
break; break;
} }
if(NCKey::IsMouse(r)){ if(NCKey::IsMouse(r)){
if(n->printf(-1, NCAlign::Right, " x: %d y: %d", ni.x, ni.y) < 0){ if((*n)->printf(-1, NCAlign::Right, " x: %d y: %d", ni.x, ni.y) < 0){
break; break;
} }
} }
}else{ }else{
n->set_fg_rgb(64, 128, 250); (*n)->set_fg_rgb(64, 128, 250);
n->printf("Unicode: [0x%08x] '%lc'", r, (wchar_t)r); (*n)->printf("Unicode: [0x%08x] '%lc'", r, (wchar_t)r);
} }
} }
if(!dim_rows(n)){ if(!dim_rows(*n)){
break; break;
} }
const uint64_t sec = (timenow_to_ns() - start) / NANOSECS_IN_SEC; const uint64_t sec = (timenow_to_ns() - start) / NANOSECS_IN_SEC;

View File

@ -2,14 +2,22 @@
void notcurses_debug(notcurses* nc, FILE* debugfp){ void notcurses_debug(notcurses* nc, FILE* debugfp){
const ncplane* n = nc->top; const ncplane* n = nc->top;
const ncplane* prev = NULL;
int planeidx = 0; int planeidx = 0;
fprintf(debugfp, "*************************** notcurses debug state *****************************\n"); fprintf(debugfp, "*************************** notcurses debug state *****************************\n");
while(n){ while(n){
fprintf(debugfp, "%04d off y: %3d x: %3d geom y: %3d x: %3d curs y: %3d x: %3d %s %p\n", fprintf(debugfp, "%04d off y: %3d x: %3d geom y: %3d x: %3d curs y: %3d x: %3d %s %p\n",
planeidx, n->absy, n->absx, n->leny, n->lenx, n->y, n->x, planeidx, n->absy, n->absx, n->leny, n->lenx, n->y, n->x,
n == notcurses_stdplane_const(nc) ? "std" : " ", n); n == notcurses_stdplane_const(nc) ? "std" : " ", n);
n = n->z; if(n->above != prev){
fprintf(stderr, " WARNING: expected ->above %p, got %p\n", prev, n->above);
}
prev = n;
n = n->below;
++planeidx; ++planeidx;
} }
if(nc->bottom != prev){
fprintf(stderr, " WARNING: expected ->bottom %p, got %p\n", prev, nc->bottom);
}
fprintf(debugfp, "*******************************************************************************\n"); fprintf(debugfp, "*******************************************************************************\n");
} }

View File

@ -65,7 +65,8 @@ typedef struct ncplane {
int x, y; // current cursor location within this plane int x, y; // current cursor location within this plane
int absx, absy; // origin of the plane relative to the screen int absx, absy; // origin of the plane relative to the screen
int lenx, leny; // size of the plane, [0..len{x,y}) is addressable int lenx, leny; // size of the plane, [0..len{x,y}) is addressable
struct ncplane* z; // plane below us struct ncplane* above;// plane above us, NULL if we're on top
struct ncplane* below;// plane below us, NULL if we're on bottom
struct ncplane* bnext;// next in the bound list of plane to which we are bound struct ncplane* bnext;// next in the bound list of plane to which we are bound
struct ncplane* blist;// head of our own bound list, if any struct ncplane* blist;// head of our own bound list, if any
struct ncplane* bound;// plane to which we are bound, if any struct ncplane* bound;// plane to which we are bound, if any
@ -267,7 +268,8 @@ typedef struct ncdirect {
} ncdirect; } ncdirect;
typedef struct notcurses { typedef struct notcurses {
ncplane* top; // the contents of our topmost plane (initially entire screen) ncplane* top; // topmost plane, never NULL
ncplane* bottom;// bottommost plane, never NULL
ncplane* stdscr;// aliases some plane from the z-buffer, covers screen ncplane* stdscr;// aliases some plane from the z-buffer, covers screen
// the style state of the terminal is carried across render runs // the style state of the terminal is carried across render runs

View File

@ -291,7 +291,12 @@ ncplane_create(notcurses* nc, ncplane* n, int rows, int cols,
cell_init(&p->basecell); cell_init(&p->basecell);
p->blist = NULL; p->blist = NULL;
p->userptr = opaque; p->userptr = opaque;
p->z = nc->top; p->above = NULL;
if( (p->below = nc->top) ){ // always happens save initial plane
nc->top->above = p;
}else{
nc->bottom = p;
}
nc->top = p; nc->top = p;
p->nc = nc; p->nc = nc;
nc->stats.fbbytes += fbsize; nc->stats.fbbytes += fbsize;
@ -368,7 +373,6 @@ ncplane* ncplane_dup(const ncplane* n, void* opaque){
ncplane_cursor_move_yx(newn, n->y, n->x); ncplane_cursor_move_yx(newn, n->y, n->x);
newn->attrword = attr; newn->attrword = attr;
newn->channels = chan; newn->channels = chan;
ncplane_move_above_unsafe(newn, n);
memmove(newn->fb, n->fb, sizeof(*n->fb) * dimx * dimy); memmove(newn->fb, n->fb, sizeof(*n->fb) * dimx * dimy);
// we dupd the egcpool, so just dup the goffset // we dupd the egcpool, so just dup the goffset
newn->basecell = n->basecell; newn->basecell = n->basecell;
@ -481,21 +485,6 @@ fprintf(stderr, "Can't resize standard plane\n");
yoff, xoff, ylen, xlen); yoff, xoff, ylen, xlen);
} }
// find the pointer on the z-index referencing the specified plane. writing to
// this pointer will remove the plane (and everything below it) from the stack.
static ncplane**
find_above_ncplane(const ncplane* n){
notcurses* nc = n->nc;
ncplane** above = &nc->top;
while(*above){
if(*above == n){
return above;
}
above = &((*above)->z);
}
return NULL;
}
int ncplane_destroy(ncplane* ncp){ int ncplane_destroy(ncplane* ncp){
if(ncp == NULL){ if(ncp == NULL){
return 0; return 0;
@ -503,11 +492,16 @@ int ncplane_destroy(ncplane* ncp){
if(ncp->nc->stdscr == ncp){ if(ncp->nc->stdscr == ncp){
return -1; return -1;
} }
ncplane** above = find_above_ncplane(ncp); if(ncp->above){
if(above == NULL){ ncp->above->below = ncp->below;
return -1; }else{
ncp->nc->top = ncp->below;
}
if(ncp->below){
ncp->below->above = ncp->above;
}else{
ncp->nc->bottom = ncp->above;
} }
*above = ncp->z; // splice it out of the list
free_plane(ncp); free_plane(ncp);
return 0; return 0;
} }
@ -806,11 +800,13 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){
goto err; goto err;
} }
int dimy, dimx; int dimy, dimx;
update_term_dimensions(ret->ttyfd, &dimy, &dimx); if(update_term_dimensions(ret->ttyfd, &dimy, &dimx)){
goto err;
}
char* shortname_term = termname(); char* shortname_term = termname();
char* longname_term = longname(); char* longname_term = longname();
if(!opts->suppress_banner){ if(!opts->suppress_banner){
fprintf(stderr, "Term: %dx%d %s (%s)\n", dimx, dimy, fprintf(stderr, "Term: %dx%d %s (%s)\n", dimy, dimx,
shortname_term ? shortname_term : "?", shortname_term ? shortname_term : "?",
longname_term ? longname_term : "?"); longname_term ? longname_term : "?");
} }
@ -826,7 +822,7 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){
term_verify_seq(&ret->tcache.smcup, "smcup"); term_verify_seq(&ret->tcache.smcup, "smcup");
term_verify_seq(&ret->tcache.rmcup, "rmcup"); term_verify_seq(&ret->tcache.rmcup, "rmcup");
} }
ret->top = ret->stdscr = NULL; ret->bottom = ret->top = ret->stdscr = NULL;
if(ncvisual_init(ffmpeg_log_level(opts->loglevel))){ if(ncvisual_init(ffmpeg_log_level(opts->loglevel))){
goto err; goto err;
} }
@ -867,11 +863,8 @@ err:
void notcurses_drop_planes(notcurses* nc){ void notcurses_drop_planes(notcurses* nc){
ncplane* p = nc->top; ncplane* p = nc->top;
while(p){ while(p){
ncplane* tmp = p->z; ncplane* tmp = p->below;
if(nc->stdscr == p){ if(nc->stdscr != p){
nc->top = p;
p->z = NULL;
}else{
free_plane(p); free_plane(p);
} }
p = tmp; p = tmp;
@ -883,9 +876,9 @@ int notcurses_stop(notcurses* nc){
if(nc){ if(nc){
ret |= notcurses_stop_minimal(nc); ret |= notcurses_stop_minimal(nc);
while(nc->top){ while(nc->top){
ncplane* p = nc->top; ncplane* p = nc->top->below;
nc->top = p->z; free_plane(nc->top);
free_plane(p); nc->top = p;
} }
if(nc->rstate.mstreamfp){ if(nc->rstate.mstreamfp){
fclose(nc->rstate.mstreamfp); fclose(nc->rstate.mstreamfp);
@ -1047,76 +1040,88 @@ const char* cell_extended_gcluster(const ncplane* n, const cell* c){
} }
// 'n' ends up above 'above' // 'n' ends up above 'above'
int ncplane_move_above_unsafe(ncplane* restrict n, const ncplane* restrict above){ int ncplane_move_above(ncplane* restrict n, ncplane* restrict above){
if(n->z == above){ if(n == above){
return 0;
}
ncplane** an = find_above_ncplane(n);
if(an == NULL){
return -1; return -1;
} }
ncplane** aa = find_above_ncplane(above); if(n->below != above){
if(aa == NULL){ // splice out 'n'
return -1; if(n->below){
n->below->above = n->above;
}else{
n->nc->bottom = n->above;
}
if(n->above){
n->above->below = n->below;
}else{
n->nc->top = n->below;
}
if(above->above){
above->above->below = n;
}else{
n->nc->top = n;
}
above->above = n;
n->below = above;
} }
ncplane* deconst_above = *aa;
*an = n->z; // splice n out
n->z = deconst_above; // attach above below n
*aa = n; // spline n in above
return 0; return 0;
} }
// 'n' ends up below 'below' // 'n' ends up below 'below'
int ncplane_move_below_unsafe(ncplane* restrict n, const ncplane* restrict below){ int ncplane_move_below(ncplane* restrict n, ncplane* restrict below){
if(below->z == n){ if(n == below){
return 0; return -1;
} }
ncplane* deconst_below = NULL; if(n->above != below){
ncplane** an = &n->nc->top; if(n->below){
// go down, looking for n and below. if we find below, mark it. if we n->below->above = n->above;
// find n, break from the loop. }else{
while(*an){ n->nc->bottom = n->above;
if(*an == below){
deconst_below = *an;
}else if(*an == n){
*an = n->z; // splice n out
} }
if(n->above){
n->above->below = n->below;
}else{
n->nc->top = n->below;
}
if(below->below){
below->below->above = n;
}else{
n->nc->bottom = n;
}
below->below = n;
n->above = below;
} }
if(an == NULL){
return -1;
}
while(deconst_below != below){
deconst_below = deconst_below->z;
}
n->z = deconst_below->z; // reattach subbelow list to n
deconst_below->z = n; // splice n in below
return 0; return 0;
} }
int ncplane_move_top(ncplane* n){ void ncplane_move_top(ncplane* n){
ncplane** an = find_above_ncplane(n); if(n->above){
if(an == NULL){ if( (n->above->below = n->below) ){
return -1; n->below->above = n->above;
}else{
n->nc->bottom = n->above;
}
n->above = NULL;
if( (n->below = n->nc->top) ){
n->below->above = n;
}
n->nc->top = n;
} }
*an = n->z; // splice n out
n->z = n->nc->top;
n->nc->top = n;
return 0;
} }
int ncplane_move_bottom(ncplane* n){ void ncplane_move_bottom(ncplane* n){
ncplane** an = find_above_ncplane(n); if(n->below){
if(an == NULL){ if( (n->below->above = n->above) ){
return -1; n->above->below = n->below;
}else{
n->nc->top = n->below;
}
n->below = NULL;
if( (n->above = n->nc->bottom) ){
n->above->below = n;
}
n->nc->bottom = n;
} }
*an = n->z; // splice n out
an = &n->nc->top;
while(*an){
an = &(*an)->z;
}
*an = n;
n->z = NULL;
return 0;
} }
void ncplane_cursor_yx(const ncplane* n, int* y, int* x){ void ncplane_cursor_yx(const ncplane* n, int* y, int* x){
@ -1694,7 +1699,7 @@ ncplane* notcurses_top(notcurses* n){
} }
ncplane* ncplane_below(ncplane* n){ ncplane* ncplane_below(ncplane* n){
return n->z; return n->below;
} }
// FIXME this clears the screen for some reason! what's up? // FIXME this clears the screen for some reason! what's up?

View File

@ -1084,7 +1084,7 @@ notcurses_render_internal(notcurses* nc, struct crender* rvec){
free(fb); free(fb);
return -1; return -1;
} }
p = p->z; p = p->below;
} }
postpaint(fb, nc->lastframe, dimy, dimx, rvec, &nc->pool); postpaint(fb, nc->lastframe, dimy, dimx, rvec, &nc->pool);
free(fb); free(fb);

View File

@ -20,6 +20,7 @@ auto main() -> int {
opts.physrows = dimy / 8; opts.physrows = dimy / 8;
opts.physcols = dimx / 2; opts.physcols = dimx / 2;
opts.egc = strdup(""); opts.egc = strdup("");
// FIXME c++ is crashing
//ncpp::Reader nr(nc, 0, 0, &opts); //ncpp::Reader nr(nc, 0, 0, &opts);
auto nr = ncreader_create(*n, 2, 2, &opts); auto nr = ncreader_create(*n, 2, 2, &opts);
if(nr == nullptr){ if(nr == nullptr){

View File

@ -21,11 +21,11 @@ TEST_CASE("ZAxis") {
// if you want to move the plane which is already top+bottom to either, go ahead // if you want to move the plane which is already top+bottom to either, go ahead
SUBCASE("StdPlaneOnanism") { SUBCASE("StdPlaneOnanism") {
CHECK(0 == ncplane_move_top(n_)); ncplane_move_top(n_);
struct ncplane* top = notcurses_top(nc_); struct ncplane* top = notcurses_top(nc_);
CHECK(n_ == top); CHECK(n_ == top);
CHECK(!ncplane_below(top)); CHECK(!ncplane_below(top));
CHECK(0 == ncplane_move_bottom(n_)); ncplane_move_bottom(n_);
CHECK(!ncplane_below(n_)); CHECK(!ncplane_below(n_));
} }
@ -57,7 +57,7 @@ TEST_CASE("ZAxis") {
CHECK(np == top); CHECK(np == top);
CHECK(n_ == ncplane_below(top)); CHECK(n_ == ncplane_below(top));
CHECK(!ncplane_below(n_)); CHECK(!ncplane_below(n_));
CHECK(!ncplane_move_top(np)); ncplane_move_top(np);
// verify it // verify it
top = notcurses_top(nc_); top = notcurses_top(nc_);
CHECK(np == top); CHECK(np == top);
@ -73,7 +73,7 @@ TEST_CASE("ZAxis") {
CHECK(np == top); CHECK(np == top);
CHECK(n_ == ncplane_below(top)); CHECK(n_ == ncplane_below(top));
CHECK(!ncplane_below(n_)); CHECK(!ncplane_below(n_));
CHECK(!ncplane_move_bottom(np)); ncplane_move_bottom(np);
top = notcurses_top(nc_); top = notcurses_top(nc_);
CHECK(n_ == top); CHECK(n_ == top);
CHECK(np == ncplane_below(top)); CHECK(np == ncplane_below(top));