diff --git a/src/lib/internal.h b/src/lib/internal.h index d97b7d11e..3391856ce 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -57,8 +57,13 @@ struct esctrie; // A plane may be partially or wholly offscreen--this might occur if the // screen is resized, for example. Offscreen portions will not be rendered. // Accesses beyond the borders of a panel, however, are errors. +// +// The framebuffer 'fb' is a set of rows. For scrolling, we interpret it as a +// circular buffer of rows. 'logrow' is the index of the row at the logical top +// of the plane. typedef struct ncplane { cell* fb; // "framebuffer" of character cells + int logrow; // logical top row, starts at 0, add one for each scroll int x, y; // current cursor location within this plane int absx, absy; // origin of the plane relative to the screen int lenx, leny; // size of the plane, [0..len{x,y}) is addressable diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 309bbac45..7645e2485 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -286,6 +286,7 @@ ncplane_create(notcurses* nc, ncplane* n, int rows, int cols, p->leny = rows; p->lenx = cols; p->x = p->y = 0; + p->logrow = 0; if( (p->bound = n) ){ p->absx = xoff + n->absx; p->absy = yoff + n->absy; diff --git a/src/lib/render.c b/src/lib/render.c index 4ee0f7857..fb735b51e 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -233,6 +233,13 @@ lock_in_highcontrast(cell* targc, struct crender* crender){ } } +// taking in a desired true row of the plane, find the line within the +// plane's framebuffer, taking into account the current scrolling state. +static inline int +scrolled_row(const ncplane* p, int y){ + return (y + p->logrow) % p->leny; +} + // Paints a single ncplane into the provided scratch framebuffer 'fb', and // ultimately 'lastframe' (we can't always write directly into 'lastframe', // because we need build state to solve certain cells, and need compare their @@ -276,7 +283,7 @@ paint(ncplane* p, cell* lastframe, struct crender* rvec, continue; } struct crender* crender = &rvec[fbcellidx(absy, dstlenx, absx)]; - const cell* vis = &p->fb[nfbcellidx(p, y, x)]; + const cell* vis = &p->fb[nfbcellidx(p, scrolled_row(p, y), x)]; // if we never loaded any content into the cell (or obliterated it by // writing in a zero), use the plane's base cell. if(vis->gcluster == 0 && !cell_wide_right_p(vis)){