Tons of work on ncreel (#776)

Tons of work on ncreel (#627, #749, #694)
Improve reel demo: get input wired up once more, avoid the FPS demo at bottom, print pointer and line count in each tablet, use new ncreel API. Improve notcurses-ncreel explorer: kill memory leaks (#694), draw tablets better, use new ncreel API. Fix bug in ncreel core where cruft could be left on the screen, via a very gross brute force algorithm. I'll likely come back and make this a bit less ghastly in the future #749. Remove weird one-off input system from ncreel, residue from outcurses. Make some of the normalizing changes speced out in #627

* ncreel: give each tablet an index, and print it #749
* reel: eliminate FIXME + param to insert_tabler() #749
* ncreel: label tablets with their adress to correlate against debugging logs #749
* more terminal environment variable notes
* TERMS.md: add Sakura, st
* ncreel: move legend out of reel proper
* ncreel_options: dump min/max_supported_rows/cols #627
* ncreel: remove weird one-off input layer #627
* ncreel: add ncreel_offer_input()
* reel demo: call demo_getc()
* reel demo: rig up input to demo main
* ncreel: drop ncreel_del_focused(), properly bind tablets
* reel demo: don't free up necessary plane
* ncreel: don't pull absolute locations of tablets
* ncreel: place tablets correctly in boundrel
* reel demo: add back support for left/right
* reel demo: restore thread movement
* ncreel: remove a great deal of complexity
* reel demo: stay out of FPS graph's way
* ncreel: give each tablet an index, and print it #749
* reel: eliminate FIXME + param to insert_tabler() #749
* ncreel: label tablets with their adress to correlate against debugging logs #749
* ncreel: move legend out of reel proper
* ncreel_options: dump min/max_supported_rows/cols #627
* ncreel: remove weird one-off input layer #627
* ncreel: add ncreel_offer_input()
* reel demo: call demo_getc()
* reel demo: rig up input to demo main
* ncreel: drop ncreel_del_focused(), properly bind tablets
* reel demo: don't free up necessary plane
* ncreel: don't pull absolute locations of tablets
* ncreel: place tablets correctly in boundrel
* reel demo: add back support for left/right
* reel demo: restore thread movement
* ncreel: remove a great deal of complexity
* reel demo: stay out of FPS graph's way
* reel: tighten up reel following redraw
* reel: fix upper-left corner of topless perimeter
* ncreel: print linecount, return clipped value
* reel: draw focused tablet relative to reel
* reel: brute force decruftification, how embarrassing #749
This commit is contained in:
Nick Black 2020-07-09 02:02:23 -04:00 committed by GitHub
parent 3918ab6999
commit 162f9910c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 362 additions and 522 deletions

View File

@ -6,7 +6,9 @@ rearrangements of Notcurses.
* The `ncreel` widget has been overhauled to bring it in line with the
others (`ncreel` began life in another project, predating Notcurses).
The `toff`, `boff`, `roff`, and `loff` fields of `ncreel_options` have
been purged.
been purged, as have `min_` and `max_supported_rows` and `_cols`. There
is no longer any need to provide a pipe/eventfd. `ncreel_touch()`,
`ncreel_del_focused()`, and `ncreel_move()` have been removed.
* 1.6.0 (2020-07-04)
* Behavior has changed regarding use of the provided `FILE*` (which, when

View File

@ -1886,20 +1886,6 @@ configured instead.
// scrolling gestures, trackballs, scrollwheels, touchpads, and verbal commands.
typedef struct ncreel_options {
// require this many rows and columns (including borders). otherwise, a
// message will be displayed stating that a larger terminal is necessary, and
// input will be queued. if 0, no minimum will be enforced. may not be
// negative. note that ncreel_create() does not return error if given a
// plane smaller than these minima; it instead patiently waits for the
// screen to get bigger.
int min_supported_cols;
int min_supported_rows;
// use no more than this many rows and columns (including borders). may not be
// less than the corresponding minimum. 0 means no maximum.
int max_supported_cols;
int max_supported_rows;
// is scrolling infinite (can one move down or up forever, or is an end
// reached?). if true, 'circular' specifies how to handle the special case of
// an incompletely-filled reel.
@ -1927,13 +1913,8 @@ struct nctablet;
struct ncreel;
// Create an ncreel according to the provided specifications. Returns NULL on
// failure. 'nc' must be a valid plane, to which offsets are relative. Note that
// there might not be enough room for the specified offsets, in which case the
// ncreel will be clipped on the bottom and right. A minimum number of rows
// and columns can be enforced via popts. efd, if non-negative, is an eventfd
// that ought be written to whenever ncreel_touch() updates a tablet (this
// is useful in the case of nonblocking input).
struct ncreel* ncreel_create(struct ncplane* nc, const ncreel_options* popts, int efd);
// failure. 'nc' must be a valid plane.
struct ncreel* ncreel_create(struct ncplane* nc, const ncreel_options* popts);
// Returns the ncplane on which this ncreel lives.
struct ncplane* ncreel_plane(struct ncreel* pr);
@ -1969,21 +1950,10 @@ struct nctablet* ncreel_add(struct ncreel* pr, struct nctablet* after,
// Return the number of tablets.
int ncreel_tabletcount(const struct ncreel* pr);
// Indicate that the specified tablet has been updated in a way that would
// change its display. This will trigger some non-negative number of callbacks
// (though not in the caller's context).
int ncreel_touch(struct ncreel* pr, struct nctablet* t);
// Delete the tablet specified by t from the ncreel specified by pr. Returns
// -1 if the tablet cannot be found.
int ncreel_del(struct ncreel* pr, struct nctablet* t);
// Delete the active tablet. Returns -1 if there are no tablets.
int ncreel_del_focused(struct ncreel* pr);
// Move to the specified location.
int ncreel_move(struct ncreel* pr, int y, int x);
// Redraw the ncreel in its entirety.
int ncreel_redraw(struct ncreel* pr);

View File

@ -15,22 +15,6 @@ notcurses_reel - high-level widget for hierarchical data
#define NCREEL_OPTION_CIRCULAR 0x0002
typedef struct ncreel_options {
// require this many rows and columns (including borders).
// otherwise, a message will be displayed stating that a
// larger terminal is necessary, and input will be queued.
// if 0, no minimum will be enforced. may not be negative.
// note that ncreel_create() does not return error if
// given a WINDOW smaller than these minima; it instead
// patiently waits for the screen to get bigger.
int min_supported_cols;
int min_supported_rows;
// use no more than this many rows and columns (including
// borders). may not be less than the corresponding minimum.
// 0 means no maximum.
int max_supported_cols;
int max_supported_rows;
// notcurses can draw a border around the ncreel, and also
// around the component tablets. inhibit borders by setting all
// valid bits in the masks. partially inhibit borders by setting
@ -48,7 +32,7 @@ typedef struct ncreel_options {
} ncreel_options;
```
**struct ncreel* ncreel_create(struct ncplane* nc, const ncreel_options* popts, int efd);**
**struct ncreel* ncreel_create(struct ncplane* nc, const ncreel_options* popts);**
**struct ncplane* ncreel_plane(struct ncreel* nr);**
@ -58,14 +42,8 @@ typedef struct ncreel_options {
**int ncreel_tabletcount(const struct ncreel* nr);**
**int ncreel_touch(struct ncreel* nr, struct nctablet* t);**
**int ncreel_del(struct ncreel* nr, struct nctablet* t);**
**int ncreel_del_focused(struct ncreel* nr);**
**int ncreel_move(struct ncreel* nr, int y, int x);**
**int ncreel_redraw(struct ncreel* nr);**
**struct nctablet* ncreel_focused(struct ncreel* nr);**

View File

@ -884,9 +884,9 @@ namespace ncpp
return static_cast<T*>(get_userptr ());
}
NcReel* ncreel_create (const ncreel_options *popts = nullptr, int efd = -1) const
NcReel* ncreel_create (const ncreel_options *popts = nullptr) const
{
return new NcReel (this, popts, efd);
return new NcReel (this, popts);
}
// Some Cell APIs go here since they act on individual panels even though it may seem weird at points (e.g.

View File

@ -15,27 +15,27 @@ namespace ncpp
public:
static ncreel_options default_options;
explicit NcReel (Plane &plane, const ncreel_options *popts = nullptr, int efd = -1)
: NcReel (static_cast<Plane const&>(plane), popts, efd)
explicit NcReel (Plane &plane, const ncreel_options *popts = nullptr)
: NcReel (static_cast<Plane const&>(plane), popts)
{}
explicit NcReel (Plane const&plane, const ncreel_options *popts = nullptr, int efd = -1)
explicit NcReel (Plane const&plane, const ncreel_options *popts = nullptr)
: Root (Utilities::get_notcurses_cpp (plane))
{
common_init (Utilities::to_ncplane (plane), popts, efd);
common_init (Utilities::to_ncplane (plane), popts);
}
explicit NcReel (Plane *plane, const ncreel_options *popts = nullptr, int efd = -1)
: NcReel (static_cast<const Plane*>(plane), popts, efd)
explicit NcReel (Plane *plane, const ncreel_options *popts = nullptr)
: NcReel (static_cast<const Plane*>(plane), popts)
{}
explicit NcReel (const Plane *plane, const ncreel_options *popts = nullptr, int efd = -1)
explicit NcReel (const Plane *plane, const ncreel_options *popts = nullptr)
: Root (Utilities::get_notcurses_cpp (plane))
{
if (plane == nullptr)
throw invalid_argument ("'plane' must be a valid pointer");
common_init (Utilities::to_ncplane (plane), popts, efd);
common_init (Utilities::to_ncplane (plane), popts);
}
~NcReel ()
@ -74,16 +74,6 @@ namespace ncpp
return ncreel_tabletcount (reel);
}
bool touch (NcTablet *t) const NOEXCEPT_MAYBE
{
return error_guard (ncreel_touch (reel, get_tablet (t)), -1);
}
bool touch (NcTablet &t) const NOEXCEPT_MAYBE
{
return touch (&t);
}
bool del (NcTablet *t) const NOEXCEPT_MAYBE
{
return error_guard (ncreel_del (reel, get_tablet (t)), -1);
@ -94,16 +84,6 @@ namespace ncpp
return del (&t);
}
bool del_focused () const NOEXCEPT_MAYBE
{
return error_guard (ncreel_del_focused (reel), -1);
}
bool move (int x, int y) const NOEXCEPT_MAYBE
{
return error_guard (ncreel_move (reel, x, y), -1);
}
bool redraw () const NOEXCEPT_MAYBE
{
return error_guard (ncreel_redraw (reel), -1);
@ -147,9 +127,9 @@ namespace ncpp
return t->get_tablet ();
}
void common_init (ncplane *plane, const ncreel_options *popts = nullptr, int efd = -1)
void common_init (ncplane *plane, const ncreel_options *popts = nullptr)
{
reel = ncreel_create (plane, popts == nullptr ? &default_options : popts, efd);
reel = ncreel_create (plane, popts == nullptr ? &default_options : popts);
if (reel == nullptr)
throw init_error ("Notcurses failed to create a new ncreel");
}

View File

@ -2496,20 +2496,6 @@ API int ncblit_bgrx(const void* data, int linesize,
#define NCREEL_OPTION_CIRCULAR 0x0002ull
typedef struct ncreel_options {
// require this many rows and columns (including borders). otherwise, a
// message will be displayed stating that a larger terminal is necessary, and
// input will be queued. if 0, no minimum will be enforced. may not be
// negative. note that ncreel_create() does not return error if given an
// ncplane smaller than these minima; it instead patiently waits for the
// screen to get bigger.
int min_supported_cols;
int min_supported_rows;
// use no more than this many rows and columns (including borders). may not be
// less than the corresponding minimum. 0 means no maximum.
int max_supported_cols;
int max_supported_rows;
// notcurses can draw a border around the ncreel, and also around the
// component tablets. inhibit borders by setting all valid bits in the masks.
// partially inhibit borders by setting individual bits in the masks. the
@ -2528,14 +2514,9 @@ typedef struct ncreel_options {
struct nctablet;
struct ncreel;
// Create an ncreel according to the provided specifications. Returns NULL on
// failure. nc must be a valid ncplane*, to which offsets are relative. Note that
// there might not be enough room for the specified offsets, in which case the
// ncreel will be clipped on the bottom and right. A minimum number of rows
// and columns can be enforced via popts. efd, if non-negative, is an eventfd
// that ought be written to whenever ncreel_touch() updates a tablet (this
// is useful in the case of nonblocking input).
API struct ncreel* ncreel_create(struct ncplane* nc, const ncreel_options* popts, int efd);
// Take over the ncplane 'nc' and use it to draw a reel according to 'popts'.
// The plane will be destroyed by ncreel_destroy(); this transfers ownership.
API struct ncreel* ncreel_create(struct ncplane* nc, const ncreel_options* popts);
// Returns the ncplane on which this ncreel lives.
API struct ncplane* ncreel_plane(struct ncreel* pr);
@ -2572,24 +2553,21 @@ API struct nctablet* ncreel_add(struct ncreel* pr, struct nctablet* after,
// Return the number of nctablets in the ncreel.
API int ncreel_tabletcount(const struct ncreel* pr);
// Indicate that the specified nctablet has been updated in a way that would
// change its display. This will trigger some non-negative number of callbacks
// (though not in the caller's context).
API int ncreel_touch(struct ncreel* pr, struct nctablet* t);
// Delete the tablet specified by t from the ncreel specified by pr. Returns
// -1 if the tablet cannot be found.
API int ncreel_del(struct ncreel* pr, struct nctablet* t);
// Delete the active tablet. Returns -1 if there are no tablets.
API int ncreel_del_focused(struct ncreel* pr);
// Move to the specified location.
API int ncreel_move(struct ncreel* pr, int y, int x);
// Redraw the ncreel in its entirety.
API int ncreel_redraw(struct ncreel* pr);
// Offer the input to the ncreel. If it's relevant, this function returns
// true, and the input ought not be processed further. If it's irrelevant to
// the reel, false is returned. Relevant inputs include:
// * a mouse click on a tablet (focuses tablet)
// * a mouse scrollwheel event (rolls reel)
// * up, down, pgup, or pgdown (navigates among items)
API bool ncreel_offer_input(struct ncreel* n, const struct ncinput* nc);
// Return the focused tablet, if any tablets are present. This is not a copy;
// be careful to use it only for the duration of a critical section.
API struct nctablet* ncreel_focused(struct ncreel* pr);

View File

@ -385,10 +385,6 @@ bool ncmenu_offer_input(struct ncmenu* n, const struct ncinput* nc);
int ncmenu_destroy(struct ncmenu* n);
const char* ncmetric(uintmax_t val, unsigned decimal, char* buf, int omitdec, unsigned mult, int uprefix);
typedef struct ncreel_options {
int min_supported_cols;
int min_supported_rows;
int max_supported_cols;
int max_supported_rows;
unsigned bordermask;
uint64_t borderchan;
unsigned tabletmask;
@ -397,15 +393,12 @@ typedef struct ncreel_options {
uint64_t bgchannel;
unsigned flags; // bitfield over NCREEL_OPTION_*
} ncreel_options;
struct ncreel* ncreel_create(struct ncplane* nc, const ncreel_options* popts, int efd);
struct ncreel* ncreel_create(struct ncplane* nc, const ncreel_options* popts);
struct ncplane* ncreel_plane(struct ncreel* pr);
typedef int (*tabletcb)(struct nctablet* t, int begx, int begy, int maxx, int maxy, bool cliptop);
struct nctablet* ncreel_add(struct ncreel* pr, struct nctablet* after, struct nctablet* before, tabletcb cb, void* opaque);
int ncreel_tabletcount(const struct ncreel* pr);
int ncreel_touch(struct ncreel* pr, struct nctablet* t);
int ncreel_del(struct ncreel* pr, struct nctablet* t);
int ncreel_del_focused(struct ncreel* pr);
int ncreel_move(struct ncreel* pr, int x, int y);
int ncreel_redraw(struct ncreel* pr);
struct nctablet* ncreel_focused(struct ncreel* pr);
struct nctablet* ncreel_next(struct ncreel* pr);

View File

@ -84,13 +84,15 @@ char32_t demo_getc(struct notcurses* nc, const struct timespec* ts, ncinput* ni)
if(id == 'L' && q->ni.ctrl){
notcurses_refresh(nc, NULL, NULL);
}else{
handoff = false;
handoff = true;
}
}
}else if(id == 'L' && q->ni.ctrl){
notcurses_refresh(nc, NULL, NULL);
}else{
handoff = false;
if(id == 'L' && q->ni.ctrl){
notcurses_refresh(nc, NULL, NULL);
}else{
handoff = true;
}
}
}
if(handoff && ni){

View File

@ -3547,7 +3547,7 @@ makegroup(struct ncplane* title, int y, const char* emoji, const char* name){
struct ncplane*
maketitle(struct ncplane* std){
struct ncplane* title = ncplane_aligned(std, 3, 74, 1, NCALIGN_CENTER, NULL);
struct ncplane* title = ncplane_aligned(std, 3, 74, 2, NCALIGN_CENTER, NULL);
if(title == NULL){
return NULL;
}

View File

@ -10,6 +10,8 @@
#define INITIAL_TABLET_COUNT 4
static pthread_mutex_t renderlock = PTHREAD_MUTEX_INITIALIZER;
// FIXME ought just be an unordered_map
typedef struct tabletctx {
pthread_t tid;
@ -151,9 +153,7 @@ tabletdraw(struct nctablet* t, int begx, int begy, int maxx, int maxy, bool clip
}
ncplane_styles_off(p, NCSTYLE_BOLD);
}
/*fprintf(stderr, " \\--> callback for %d, %d lines (%d/%d -> %d/%d) dir: %s wrote: %d ret: %d\n", tctx->id,
tctx->lines, begy, begx, maxy, maxx,
cliptop ? "up" : "down", ll, err);*/
//fprintf(stderr, " \\--> callback for %d, %d lines (%d/%d -> %d/%d) dir: %s wrote: %d\n", tctx->id, tctx->lines, begy, begx, maxy, maxx, cliptop ? "up" : "down", ll);
pthread_mutex_unlock(&tctx->lock);
return ll;
}
@ -175,14 +175,18 @@ tablet_thread(void* vtabletctx){
if((tctx->lines -= (action + 1)) < 1){
tctx->lines = 1;
}
ncreel_touch(tctx->pr, tctx->t);
}else if(action > 2){
if((tctx->lines += (action - 2)) < 1){
tctx->lines = 1;
}
ncreel_touch(tctx->pr, tctx->t);
}
pthread_mutex_unlock(&tctx->lock);
pthread_mutex_lock(&renderlock);
if(nctablet_ncplane(tctx->t)){
ncreel_redraw(tctx->pr);
demo_render(ncplane_notcurses(nctablet_ncplane(tctx->t)));
}
pthread_mutex_unlock(&renderlock);
}
return tctx;
}
@ -212,80 +216,32 @@ new_tabletctx(struct ncreel* pr, unsigned *id){
}
static wchar_t
handle_input(struct notcurses* nc, struct ncreel* pr, int efd,
const struct timespec* deadline){
struct pollfd fds[2] = {
{ .fd = demo_input_fd(), .events = POLLIN, .revents = 0, },
{ .fd = efd, .events = POLLIN, .revents = 0, },
};
sigset_t sset;
sigemptyset(&sset);
wchar_t key = -1;
int pret;
DEMO_RENDER(nc);
handle_input(struct notcurses* nc, const struct timespec* deadline,
ncinput* ni){
int64_t deadlinens = timespec_to_ns(deadline);
do{
struct timespec pollspec, cur;
clock_gettime(CLOCK_MONOTONIC, &cur);
int64_t curns = timespec_to_ns(&cur);
if(curns > deadlinens){
return 0;
}
ns_to_timespec(curns - deadlinens, &pollspec);
pret = ppoll(fds, sizeof(fds) / sizeof(*fds), &pollspec, &sset);
if(pret == 0){
return 0;
}else if(pret < 0){
if(errno != EINTR){
fprintf(stderr, "Error polling on stdin/eventfd (%s)\n", strerror(errno));
return (wchar_t)-1;
}
}else{
if(fds[0].revents & POLLIN){
uint64_t eventcount;
if(read(fds[0].fd, &eventcount, sizeof(eventcount)) > 0){
key = demo_getc_nblock(nc, NULL);
if(key == (wchar_t)-1){
return -1;
}
}
}
if(fds[1].revents & POLLIN){
uint64_t val;
if(read(efd, &val, sizeof(val)) != sizeof(val)){
fprintf(stderr, "Error reading from eventfd %d (%s)\n", efd, strerror(errno));
}else if(key == (wchar_t)-1){
ncreel_redraw(pr);
DEMO_RENDER(nc);
}
}
}
}while(key == (wchar_t)-1);
return key;
}
static int
close_pipes(int* pipes){
if(close(pipes[0]) | close(pipes[1])){ // intentional, avoid short-circuiting
return -1;
struct timespec pollspec, cur;
clock_gettime(CLOCK_MONOTONIC, &cur);
int64_t curns = timespec_to_ns(&cur);
if(curns > deadlinens){
return 0;
}
return 0;
ns_to_timespec(deadlinens - curns, &pollspec);
wchar_t r = demo_getc(nc, &pollspec, ni);
return r;
}
static int
ncreel_demo_core(struct notcurses* nc, int efdr, int efdw){
ncreel_demo_core(struct notcurses* nc){
tabletctx* tctxs = NULL;
bool aborted = false;
int x = 8, y = 4;
int dimy, dimx;
struct ncplane* std = notcurses_stddim_yx(nc, &dimy, &dimx);
struct ncplane* w = ncplane_new(nc, dimy - 8, dimx - 16, y, x, NULL);
struct ncplane* w = ncplane_new(nc, dimy - 12, dimx - 16, y, x, NULL);
if(w == NULL){
return -1;
}
ncreel_options popts = {
.min_supported_cols = 8,
.min_supported_rows = 5,
.bordermask = 0,
.borderchan = 0,
.tabletchan = 0,
@ -307,7 +263,7 @@ ncreel_demo_core(struct notcurses* nc, int efdr, int efdw){
ncplane_destroy(w);
return -1;
}
struct ncreel* pr = ncreel_create(w, &popts, efdw);
struct ncreel* pr = ncreel_create(w, &popts);
if(pr == NULL){
ncplane_destroy(w);
return -1;
@ -317,7 +273,7 @@ ncreel_demo_core(struct notcurses* nc, int efdr, int efdw){
ncplane_styles_on(std, NCSTYLE_BOLD | NCSTYLE_ITALIC);
ncplane_set_fg_rgb(std, 58, 150, 221);
ncplane_set_bg_default(std);
ncplane_printf_yx(std, 1, 1, "a, b, c create tablets, DEL deletes.");
ncplane_printf_yx(std, 1, 2, "a, b, c create tablets, DEL deletes.");
ncplane_styles_off(std, NCSTYLE_BOLD | NCSTYLE_ITALIC);
// FIXME clrtoeol();
struct timespec deadline;
@ -346,7 +302,17 @@ ncreel_demo_core(struct notcurses* nc, int efdr, int efdw){
// FIXME wclrtoeol(w);
ncplane_set_fg_rgb(std, 0, 55, 218);
wchar_t rw;
if((rw = handle_input(nc, pr, efdr, &deadline)) == (wchar_t)-1){
ncinput ni;
pthread_mutex_lock(&renderlock);
ncreel_redraw(pr);
int renderret;
renderret = demo_render(nc);
pthread_mutex_unlock(&renderlock);
if(renderret){
ncreel_destroy(pr);
return renderret;
}
if((rw = handle_input(nc, &deadline, &ni)) == (wchar_t)-1){
break;
}
// FIXME clrtoeol();
@ -355,15 +321,19 @@ ncreel_demo_core(struct notcurses* nc, int efdr, int efdw){
case 'a': newtablet = new_tabletctx(pr, &id); break;
case 'b': newtablet = new_tabletctx(pr, &id); break;
case 'c': newtablet = new_tabletctx(pr, &id); break;
case 'h': --x; if(ncreel_move(pr, x, y)){ ++x; } break;
case 'l': ++x; if(ncreel_move(pr, x, y)){ --x; } break;
case 'k': ncreel_prev(pr); break;
case 'j': ncreel_next(pr); break;
case 'q': aborted = true; break;
case NCKEY_LEFT: --x; if(ncreel_move(pr, x, y)){ ++x; } break;
case NCKEY_RIGHT: ++x; if(ncreel_move(pr, x, y)){ --x; } break;
case NCKEY_UP: ncreel_prev(pr); break;
case NCKEY_DOWN: ncreel_next(pr); break;
case NCKEY_LEFT:
ncplane_yx(ncreel_plane(pr), &y, &x);
ncplane_move_yx(ncreel_plane(pr), y, x - 1);
break;
case NCKEY_RIGHT:
ncplane_yx(ncreel_plane(pr), &y, &x);
ncplane_move_yx(ncreel_plane(pr), y, x + 1);
break;
case NCKEY_DEL: kill_active_tablet(pr, &tctxs); break;
case NCKEY_RESIZE: notcurses_render(nc); break;
default: ncplane_printf_yx(std, 3, 2, "Unknown keycode (0x%x)\n", rw); break;
@ -390,15 +360,7 @@ ncreel_demo_core(struct notcurses* nc, int efdr, int efdw){
}
int reel_demo(struct notcurses* nc){
int pipes[2];
ncplane_greyscale(notcurses_stdplane(nc));
// freebsd doesn't have eventfd :/
if(pipe2(pipes, O_CLOEXEC | O_NONBLOCK)){
fprintf(stderr, "Error creating pipe (%s)\n", strerror(errno));
return -1;
}
int ret = ncreel_demo_core(nc, pipes[0], pipes[1]);
close_pipes(pipes);
DEMO_RENDER(nc);
int ret = ncreel_demo_core(nc);
return ret;
}

View File

@ -16,14 +16,52 @@ typedef struct nctablet {
void* curry; // application data provided to cbfxn
} nctablet;
// The visible screen can be reconstructed from three things:
// The visible screen can be reconstructed from four things:
// * which tablet is focused (pointed at by tablets)
// * which row the focused tablet starts at (derived from focused window)
// * the list of tablets (available from the focused tablet)
// * from which direction we arrived at the focused window
// Things which can happen between ncreel_redraw() calls:
// * new focused tablet added (only when no tablets exist)
// * new unfocused tablet added
// * tablet removed (may be focused)
// * tablets may grow or shrink
// * focus can change
// On tablet remove:
// * destroy plane, remove from list
// * if tablet was focused, change focus
// On tablet add:
// * add to list (do not create plane)
// * if no tablet existed, change focus to new tablet
// On focus change:
// * change focus, update travel direction
// On redraw:
// * if no tablets, we're done (deleted planes are already gone)
// * resize focused tablet to maximum
// * call back for focused tablet redraw
// * shrink focused tablet if applicable
// * place focused tablet according to:
// * the focused tablet should be wholly visible, or if not, use all space
// * the focused tablet should be as close to its old position as possible.
// * if focused tablet was not wholly on screen, it goes to the side
// corresponding to the direction of movement.
// * if out of space or tablets, we're done
// FIXME *maybe* just draw up followed by down, rather than direction of travel?
// * walk the list in the direction of travel, foc->focw
// * designate tablet against the walk as 'focagainst', might be NULL
// * if focagainst || focagainst, focw only through edge, otherwise
// * focw can be as large as all remaining space
// * if there is space, draw what we can of next tablet
// * move the focused tablet againt the direction of travel if necessary
// * prefer the space in the direction of walking
// * last tablet drawn is 'backstop'
// * if out of space or tablets, we're done
// * walk the list in the direction against travel, foc->focw
// * if focw == backstop, we're done
// * draw through edge
typedef struct ncreel {
ncplane* p; // ncplane this ncreel occupies, under tablets
ncplane* p; // ncplane this ncreel occupies, under tablets
ncreel_options ropts; // copied in ncreel_create()
int efd; // eventfd/pipe, signaled in ncreel_touch()
// doubly-linked list, a circular one when infinity scrolling is in effect.
// points at the focused tablet (when at least one tablet exists, one must be
// focused), which might be anywhere on the screen (but is always visible).
@ -36,16 +74,12 @@ typedef struct ncreel {
// drawing unfocused tablets opposite the direction of our last movement, so
// that movement in an unfilled reel doesn't reorient our tablets.
int last_traveled_direction;
// are all of our tablets currently visible? our arrangement algorithm works
// differently when the reel is not completely filled. ideally we'd unite the
// two modes, but for now, check this bool and take one of two paths.
bool all_visible;
} ncreel;
// Returns the starting coordinates (relative to the screen) of the specified
// window, and its length. End is (begx + lenx - 1, begy + leny - 1).
// tablet, and its length. End is (begx + lenx - 1, begy + leny - 1).
static inline void
window_coordinates(ncplane* w, int* begy, int* begx, int* leny, int* lenx){
tablet_coordinates(ncplane* w, int* begy, int* begx, int* leny, int* lenx){
ncplane_yx(w, begy, begx);
ncplane_dim_yx(w, leny, lenx);
}
@ -64,45 +98,43 @@ assert(lenx > 0);
static int
draw_borders(ncplane* w, unsigned mask, uint64_t channel,
bool cliphead, bool clipfoot){
int begx, begy, lenx, leny;
int lenx, leny;
int ret = 0;
window_coordinates(w, &begy, &begx, &leny, &lenx);
begx = 0;
begy = 0;
int maxx = begx + lenx - 1;
int maxy = begy + leny - 1;
ncplane_dim_yx(w, &leny, &lenx);
int maxx = lenx - 1;
int maxy = leny - 1;
cell ul, ur, ll, lr, hl, vl;
cell_init(&ul); cell_init(&ur); cell_init(&hl);
cell_init(&ll); cell_init(&lr); cell_init(&vl);
if(cells_rounded_box(w, 0, channel, &ul, &ur, &ll, &lr, &hl, &vl)){
return -1;
}
/*fprintf(stderr, "drawing borders %p %d/%d->%d/%d, mask: %04x, clipping: %c%c\n",
w, begx, begy, maxx, maxy, mask,
/*fprintf(stderr, "drawing borders %p ->%d/%d, mask: %04x, clipping: %c%c\n",
w, maxx, maxy, mask,
cliphead ? 'T' : 't', clipfoot ? 'F' : 'f');*/
if(!cliphead){
// lenx - begx + 1 is the number of columns we have, but drop 2 due to
// corners. we thus want lenx - begx - 1 horizontal lines.
// lenx is the number of columns we have, but drop 2 due to
// corners. we thus want lenx horizontal lines.
if(!(mask & NCBOXMASK_TOP)){
ret |= ncplane_cursor_move_yx(w, begy, begx);
ncplane_home(w);
ncplane_putc(w, &ul);
ncplane_hline(w, &hl, lenx - 2);
ncplane_putc(w, &ur);
}else{
if(!(mask & NCBOXMASK_LEFT)){
ret |= ncplane_cursor_move_yx(w, begy, begx);
ncplane_home(w);
ncplane_putc(w, &ul);
}
if(!(mask & NCBOXMASK_RIGHT)){
ret |= ncplane_cursor_move_yx(w, begy, maxx);
ncplane_cursor_move_yx(w, 0, lenx - 1);
ncplane_putc(w, &ur);
}
}
}
int y;
for(y = begy + !cliphead ; y < maxy + !!clipfoot ; ++y){
for(y = !cliphead ; y < maxy + !!clipfoot ; ++y){
if(!(mask & NCBOXMASK_LEFT)){
ret |= ncplane_cursor_move_yx(w, y, begx);
ret |= ncplane_cursor_move_yx(w, y, 0);
ncplane_putc(w, &vl);
}
if(!(mask & NCBOXMASK_RIGHT)){
@ -112,13 +144,13 @@ draw_borders(ncplane* w, unsigned mask, uint64_t channel,
}
if(!clipfoot){
if(!(mask & NCBOXMASK_BOTTOM)){
ret |= ncplane_cursor_move_yx(w, maxy, begx);
ret |= ncplane_cursor_move_yx(w, maxy, 0);
ncplane_putc(w, &ll);
ncplane_hline(w, &hl, lenx - 2);
ncplane_putc(w, &lr);
}else{
if(!(mask & NCBOXMASK_LEFT)){
if(ncplane_cursor_move_yx(w, maxy, begx) || ncplane_putc(w, &ll) < 0){
if(ncplane_cursor_move_yx(w, maxy, 0) || ncplane_putc(w, &ll) < 0){
ret = -1;
}
}
@ -134,8 +166,8 @@ draw_borders(ncplane* w, unsigned mask, uint64_t channel,
}
cell_release(w, &ul); cell_release(w, &ur); cell_release(w, &hl);
cell_release(w, &ll); cell_release(w, &lr); cell_release(w, &vl);
// fprintf(stderr, "||--borders %d %d %d %d clip: %c%c ret: %d\n",
// begx, begy, maxx, maxy, cliphead ? 'y' : 'n', clipfoot ? 'y' : 'n', ret);
// fprintf(stderr, "||--borders %d %d clip: %c%c ret: %d\n",
// maxx, maxy, cliphead ? 'y' : 'n', clipfoot ? 'y' : 'n', ret);
return ret;
}
@ -143,18 +175,11 @@ draw_borders(ncplane* w, unsigned mask, uint64_t channel,
// any provided restrictions on visible window size.
static int
draw_ncreel_borders(const ncreel* nr){
int begx, begy;
int maxx, maxy;
window_coordinates(nr->p, &begy, &begx, &maxy, &maxx);
ncplane_dim_yx(nr->p, &maxy, &maxx);
assert(maxy >= 0 && maxx >= 0);
--maxx; // last column we can safely write to
--maxy; // last line we can safely write to
if(begx >= maxx || maxx - begx + 1 < nr->ropts.min_supported_rows){
return 0; // no room
}
if(begy >= maxy || maxy - begy + 1 < nr->ropts.min_supported_cols){
return 0; // no room
}
return draw_borders(nr->p, nr->ropts.bordermask, nr->ropts.borderchan, false, false);
}
@ -166,7 +191,9 @@ draw_ncreel_borders(const ncreel* nr){
static int
tablet_columns(const ncreel* nr, int* begx, int* begy, int* lenx, int* leny,
int frontiery, int direction){
window_coordinates(nr->p, begy, begx, leny, lenx);
*begy = 0;
*begx = 0;
ncplane_dim_yx(nr->p, leny, lenx);
int maxy = *leny + *begy - 1;
int begindraw = *begy + !(nr->ropts.bordermask & NCBOXMASK_TOP);
int enddraw = maxy - !(nr->ropts.bordermask & NCBOXMASK_TOP);
@ -236,7 +263,7 @@ ncreel_draw_tablet(const ncreel* nr, nctablet* t, int frontiery,
//fprintf(stderr, "tplacement: %p:%p base %d/%d len %d/%d\n", t, fp, begx, begy, lenx, leny);
//fprintf(stderr, "DRAWING %p at frontier %d (dir %d) with %d\n", t, frontiery, direction, leny);
if(fp == NULL){ // create a panel for the tablet
t->p = ncplane_new(nr->p->nc, leny + 1, lenx, begy, begx, NULL);
t->p = ncplane_bound(nr->p, leny + 1, lenx, begy, begx, NULL);
if((fp = t->p) == NULL){
return -1;
}
@ -336,8 +363,9 @@ ncreel_draw_tablet(const ncreel* nr, nctablet* t, int frontiery,
// NULL). it can occupy the entire ncreel.
static int
draw_focused_tablet(const ncreel* nr){
int pbegy, pbegx, plenx, pleny; // ncreel window coordinates
window_coordinates(nr->p, &pbegy, &pbegx, &pleny, &plenx);
int pbegy, plenx, pleny; // ncreel window coordinates
ncplane_dim_yx(nr->p, &pleny, &plenx);
pbegy = 0;
int fulcrum;
if(nr->tablets->p == NULL){
if(nr->last_traveled_direction >= 0){
@ -345,6 +373,7 @@ draw_focused_tablet(const ncreel* nr){
}else{
fulcrum = pbegy + !(nr->ropts.bordermask & NCBOXMASK_TOP);
}
//fprintf(stderr, "LTD: %d placing new at %d\n", nr->last_traveled_direction, fulcrum);
}else{ // focused was already present. want to stay where we are, if possible
ncplane_yx(nr->tablets->p, &fulcrum, NULL);
// FIXME ugh can't we just remember the previous fulcrum?
@ -365,6 +394,7 @@ draw_focused_tablet(const ncreel* nr){
}
}
}
//fprintf(stderr, "existing: %p %d placing at %d\n", nr->tablets, nr->last_traveled_direction, fulcrum);
}
//fprintf(stderr, "PR dims: %d/%d + %d/%d fulcrum: %d\n", pbegy, pbegx, pleny, plenx, fulcrum);
ncreel_draw_tablet(nr, nr->tablets, fulcrum, 0 /* nr->last_traveled_direction*/);
@ -382,7 +412,7 @@ draw_following_tablets(const ncreel* nr, const nctablet* otherend){
do{
//fprintf(stderr, "following otherend: %p ->p: %p\n", otherend, otherend->p);
// modify frontier based off the one we're at
window_coordinates(working->p, &wbegy, &wbegx, &wleny, &wlenx);
tablet_coordinates(working->p, &wbegy, &wbegx, &wleny, &wlenx);
wmaxy = wbegy + wleny - 1;
frontiery = wmaxy + 2;
//fprintf(stderr, "EASTBOUND AND DOWN: %p->%p %d %d\n", working, working->next, frontiery, wmaxy + 2);
@ -409,14 +439,14 @@ draw_previous_tablets(const ncreel* nr, const nctablet* otherend){
nctablet* upworking = nr->tablets;
int frontiery;
// modify frontier based off the one we're at
window_coordinates(upworking->p, &wbegy, &wbegx, &wleny, &wlenx);
tablet_coordinates(upworking->p, &wbegy, &wbegx, &wleny, &wlenx);
frontiery = wbegy - 2;
while(upworking->prev != otherend || otherend->p == NULL){
//fprintf(stderr, "MOVIN' ON UP: %p->%p %d %d\n", upworking, upworking->prev, frontiery, wbegy - 2);
upworking = upworking->prev;
ncreel_draw_tablet(nr, upworking, frontiery, -1);
if(upworking->p){
window_coordinates(upworking->p, &wbegy, &wbegx, &wleny, &wlenx);
tablet_coordinates(upworking->p, &wbegy, &wbegx, &wleny, &wlenx);
//fprintf(stderr, "new up coords: %d/%d + %d/%d, %d\n", wbegy, wbegx, wleny, wlenx, frontiery);
frontiery = wbegy - 2;
}else{
@ -430,78 +460,131 @@ draw_previous_tablets(const ncreel* nr, const nctablet* otherend){
return upworking;
}
// all tablets must be visible (valid ->p), and at least one tablet must exist
static nctablet*
find_topmost(ncreel* nr){
nctablet* t = nr->tablets;
int curline;
ncplane_yx(t->p, &curline, NULL);
int trialline;
ncplane_yx(t->prev->p, &trialline, NULL);
while(trialline < curline){
t = t->prev;
curline = trialline;
ncplane_yx(t->prev->p, &trialline, NULL);
}
// fprintf(stderr, "topmost: %p @ %d\n", t, curline);
return t;
}
// all the tablets are believed to be wholly visible. in this case, we only want
// to fill up the necessary top rows, even if it means moving everything up at
// the end. large gaps should always be at the bottom to avoid ui discontinuity.
// this must only be called if we actually have at least one tablet. note that
// as a result of this function, we might not longer all be wholly visible.
// good god almighty, this is some fucking garbage.
// run at the end of redraw, this aligns the top tablet with the top
// of the reel. we prefer empty space at the bottom (FIXME but not
// really -- we ought prefer space away from the last direction of
// movement. rather than this postprocessing, draw things to the
// right places!).
static int
ncreel_arrange_denormalized(ncreel* nr){
//fprintf(stderr, "denormalized devolution (are we men?)\n");
// we'll need the starting line of the tablet which just lost focus, and the
// starting line of the tablet which just gained focus.
int fromline, nowline;
ncplane_yx(nr->tablets->p, &nowline, NULL);
// we've moved to the next or previous tablet. either we were not at the end,
// in which case we can just move the focus, or we were at the end, in which
// case we need bring the target tablet to our end, and draw in the direction
// opposite travel (a single tablet is a trivial case of the latter case).
// how do we know whether we were at the end? if the new line is not in the
// direction of movement relative to the old one, of course!
nctablet* topmost = find_topmost(nr);
int wbegy, wbegx, wleny, wlenx;
window_coordinates(nr->p, &wbegy, &wbegx, &wleny, &wlenx);
int frontiery = wbegy + !(nr->ropts.bordermask & NCBOXMASK_TOP);
if(nr->last_traveled_direction >= 0){
ncplane_yx(nr->tablets->prev->p, &fromline, NULL);
if(fromline > nowline){ // keep the order we had
topmost = topmost->next;
}
}else{
ncplane_yx(nr->tablets->next->p, &fromline, NULL);
if(fromline < nowline){ // keep the order we had
topmost = topmost->prev;
}
}
//fprintf(stderr, "gotta draw 'em all FROM: %d NOW: %d!\n", fromline, nowline);
nctablet* t = topmost;
do{
int broken;
if(t == nr->tablets){
broken = ncreel_draw_tablet(nr, t, frontiery, 0);
}else{
broken = ncreel_draw_tablet(nr, t, frontiery, 1);
}
if(t->p == NULL || broken){
nr->all_visible = false;
tighten_reel(ncreel* r){
nctablet* top = r->tablets;
nctablet* cur = top;
int ytop = INT_MAX;
while(cur){
if(cur->p == NULL){
break;
}
int basey;
ncplane_dim_yx(t->p, &frontiery, NULL);
ncplane_yx(t->p, &basey, NULL);
frontiery += basey + 1;
}while((t = t->next) != topmost);
int cury;
ncplane_yx(cur->p, &cury, NULL);
if(cury >= ytop){
break;
}
ytop = cury;
top = cur;
cur = cur->prev;
}
int expected = !(r->ropts.bordermask & NCBOXMASK_TOP);
cur = top;
while(cur){
if(cur->p == NULL){
break;
}
int cury, curx;
ncplane_yx(cur->p, &cury, &curx);
if(cury != expected){
if(ncplane_move_yx(cur->p, expected, curx)){
return -1;
}
}else{
break;
}
int ylen;
ncplane_dim_yx(cur->p, &ylen, NULL);
expected += ylen + 1;
cur = cur->next;
if(cur == top){
break;
}
}
return 0;
}
// debugging
bool ncreel_validate(const ncreel* n){
if(n->tablets == NULL){
return true;
}
const nctablet* t = n->tablets;
int cury = -1;
bool wentaround = false;
do{
const ncplane* np = t->p;
if(np){
int y, x;
ncplane_yx(np, &y, &x);
//fprintf(stderr, "forvart: %p (%p) @ %d\n", t, np, y);
if(y < cury){
if(wentaround){
return false;
}
wentaround = true;
}else if(y == cury){
return false;
}
cury = y;
}
}while((t = t->next) != n->tablets);
cury = INT_MAX;
wentaround = false;
do{
const ncplane* np = t->p;
if(np){
int y, x;
ncplane_yx(np, &y, &x);
//fprintf(stderr, "backwards: %p (%p) @ %d\n", t, np, y);
if(y > cury){
if(wentaround){
return false;
}
wentaround = true;
}else if(y == cury){
return false;
}
cury = y;
}
}while((t = t->prev) != n->tablets);
return true;
}
// destroy all existing tablet planes pursuant to redraw
static void
clean_reel(ncreel* r){
nctablet* focused = r->tablets;
nctablet* cur = focused;
if(r->tablets){
cur = r->tablets->next;
while(cur && cur != r->tablets){// && cur->p){
//fprintf(stderr, "CLEANING: %p (%p)\n", cur, cur->p);
if(cur->p){
ncplane_destroy(cur->p);
cur->p = NULL;
}
cur = cur->next;
}
//fprintf(stderr, "done clean next, %p %p %p\n", cur, r->tablets, cur ? cur->p : NULL);
cur = r->tablets->prev;
while(cur && cur != r->tablets){// && cur->p){
//fprintf(stderr, "CLEANING: %p (%p)\n", cur, cur->p);
if(cur->p){
ncplane_destroy(cur->p);
cur->p = NULL;
}
cur = cur->prev;
}
}
//fprintf(stderr, "done clean prev, %p %p %p\n", cur, r->tablets, cur ? cur->p : NULL);
}
// Arrange the panels, starting with the focused window, wherever it may be.
// If necessary, resize it to the full size of the reel--focus has its
// privileges. We then work in the opposite direction of travel, filling out
@ -522,17 +605,10 @@ int ncreel_redraw(ncreel* nr){
return 0; // if none are focused, none exist
}
//fprintf(stderr, "focused %p!\n", focused);
// FIXME we special-cased this because i'm dumb and couldn't think of a more
// elegant way to do this. we keep 'all_visible' as boolean state to avoid
// having to do an o(n) iteration each round, but this is still grotesque, and
// feels fragile...
if(nr->all_visible){
//fprintf(stderr, "all are visible!\n");
return ncreel_arrange_denormalized(nr);
}
//fprintf(stderr, "drawing focused tablet %p dir: %d!\n", focused, nr->last_traveled_direction);
draw_focused_tablet(nr);
//fprintf(stderr, "drew focused tablet %p dir: %d!\n", focused, nr->last_traveled_direction);
clean_reel(nr);
nctablet* otherend = focused;
if(nr->last_traveled_direction >= 0){
otherend = draw_previous_tablets(nr, otherend);
@ -543,7 +619,11 @@ int ncreel_redraw(ncreel* nr){
otherend = draw_previous_tablets(nr, otherend);
draw_following_tablets(nr, otherend);
}
tighten_reel(nr);
//fprintf(stderr, "DONE ARRANGING\n");
if(!ncreel_validate(nr)){
return -1;
}
return 0;
}
@ -579,7 +659,7 @@ ncplane* ncreel_plane(ncreel* nr){
return nr->p;
}
ncreel* ncreel_create(ncplane* w, const ncreel_options* ropts, int efd){
ncreel* ncreel_create(ncplane* w, const ncreel_options* ropts){
ncreel* nr;
if(!validate_ncreel_opts(w, ropts)){
@ -588,35 +668,11 @@ ncreel* ncreel_create(ncplane* w, const ncreel_options* ropts, int efd){
if((nr = malloc(sizeof(*nr))) == NULL){
return NULL;
}
nr->efd = efd;
nr->tablets = NULL;
nr->tabletcount = 0;
nr->all_visible = true;
nr->last_traveled_direction = -1; // draw down after the initial tablet
memcpy(&nr->ropts, ropts, sizeof(*ropts));
int maxx, maxy, wx, wy;
window_coordinates(w, &wy, &wx, &maxy, &maxx);
--maxy;
--maxx;
int ylen, xlen;
ylen = maxy + 1;
if(ylen < 0){
ylen = maxy;
if(ylen < 0){
ylen = 0; // but this translates to a full-screen window...FIXME
}
}
xlen = maxx + 1;
if(xlen < 0){
xlen = maxx;
if(xlen < 0){
xlen = 0; // FIXME see above...
}
}
if((nr->p = ncplane_new(w->nc, ylen, xlen, wy, wx, NULL)) == NULL){
free(nr);
return NULL;
}
nr->p = w;
ncplane_set_base(nr->p, "", 0, ropts->bgchannel);
if(ncreel_redraw(nr)){
ncplane_destroy(nr->p);
@ -626,54 +682,6 @@ ncreel* ncreel_create(ncplane* w, const ncreel_options* ropts, int efd){
return nr;
}
// we've just added a new tablet. it needs be inserted at the correct place in
// the reel. this will naturally fall out of things if the ncreel is full; we
// can just call ncreel_redraw(). otherwise, we need make ourselves at least
// minimally visible, to satisfy the preconditions of
// ncreel_arrange_denormalized(). this function, and approach, is shit.
// FIXME get rid of nc param here
static nctablet*
insert_new_panel(struct notcurses* nc, ncreel* nr, nctablet* t){
if(!nr->all_visible){
return t;
}
int wbegy, wbegx, wleny, wlenx; // params of PR
window_coordinates(nr->p, &wbegy, &wbegx, &wleny, &wlenx);
// are we the only tablet?
int begx, begy, lenx, leny, frontiery;
if(t->prev == t){
frontiery = wbegy + !(nr->ropts.bordermask & NCBOXMASK_TOP);
if(tablet_columns(nr, &begx, &begy, &lenx, &leny, frontiery, 1)){
nr->all_visible = false;
return t;
}
// fprintf(stderr, "newwin: %d/%d + %d/%d\n", begy, begx, leny, lenx);
if((t->p = ncplane_new(nc, leny, lenx, begy, begx, NULL)) == NULL){
nr->all_visible = false;
return t;
}
return t;
}
// we're not the only tablet, alas.
// our new window needs to be after our prev
ncplane_yx(t->prev->p, &frontiery, NULL);
int dimprevy, dimprevx;
ncplane_dim_yx(t->prev->p, &dimprevy, &dimprevx);
frontiery += dimprevy + 2;
frontiery += 2;
if(tablet_columns(nr, &begx, &begy, &lenx, &leny, frontiery, 1)){
nr->all_visible = false;
return t;
}
// fprintf(stderr, "newwin: %d/%d + %d/%d\n", begy, begx, 2, lenx);
if((t->p = ncplane_new(nc, 2, lenx, begy, begx, NULL)) == NULL){
nr->all_visible = false;
return t;
}
// FIXME push the other ones down by 4
return t;
}
nctablet* ncreel_add(ncreel* nr, nctablet* after, nctablet *before,
tabletcb cbfxn, void* opaque){
nctablet* t;
@ -710,17 +718,9 @@ nctablet* ncreel_add(ncreel* nr, nctablet* after, nctablet *before,
t->curry = opaque;
++nr->tabletcount;
t->p = NULL;
// if we have room, it needs become visible immediately, in the proper place,
// lest we invalidate the preconditions of ncreel_arrange_denormalized().
insert_new_panel(nr->p->nc, nr, t);
ncreel_redraw(nr); // don't return failure; tablet was still created...
return t;
}
int ncreel_del_focused(ncreel* nr){
return ncreel_del(nr, nr->tablets);
}
int ncreel_del(ncreel* nr, struct nctablet* t){
if(nr == NULL || t == NULL){
return -1;
@ -737,7 +737,6 @@ int ncreel_del(ncreel* nr, struct nctablet* t){
}
free(t);
--nr->tabletcount;
ncreel_redraw(nr);
return 0;
}
@ -762,64 +761,10 @@ int ncreel_tabletcount(const ncreel* nreel){
return nreel->tabletcount;
}
int ncreel_touch(ncreel* nr, nctablet* t){
(void)t; // FIXME make these more granular eventually
int ret = 0;
if(nr->efd >= 0){
uint64_t val = 1;
if(write(nr->efd, &val, sizeof(val)) != sizeof(val)){
// fprintf(stderr, "Error writing to eventfd %d (%s)\n", nr->efd, strerror(errno));
ret = -1;
}
}
return ret;
}
// Move to some position relative to the current position
static int
move_tablet(ncplane* p, int deltay, int deltax){
int oldx, oldy;
ncplane_yx(p, &oldy, &oldx);
int x = oldx + deltax;
int y = oldy + deltay;
ncplane_move_yx(p, y, x);
return 0;
}
nctablet* ncreel_focused(ncreel* nr){
return nr->tablets;
}
int ncreel_move(ncreel* nreel, int y, int x){
ncplane* w = nreel->p;
int oldx, oldy;
ncplane_yx(w, &oldy, &oldx);
const int deltax = x - oldx;
const int deltay = y - oldy;
if(ncplane_move_yx(nreel->p, y, x)){
return -1;
}
if(nreel->tablets){
nctablet* t = nreel->tablets;
do{
if(t->p == NULL){
break;
}
move_tablet(t->p, deltay, deltax);
}while((t = t->prev) != nreel->tablets);
if(t != nreel->tablets){ // don't repeat if we covered all tablets
for(t = nreel->tablets->next ; t != nreel->tablets ; t = t->next){
if(t->p == NULL){
break;
}
move_tablet(t->p, deltay, deltax);
}
}
}
ncreel_redraw(nreel);
return 0;
}
nctablet* ncreel_next(ncreel* nr){
if(nr->tablets){
nr->tablets = nr->tablets->next;
@ -827,7 +772,6 @@ nctablet* ncreel_next(ncreel* nr){
// nr->tablets->prev, nr->tablets);
nr->last_traveled_direction = 1;
}
ncreel_redraw(nr);
return nr->tablets;
}
@ -838,6 +782,23 @@ nctablet* ncreel_prev(ncreel* nr){
// nr->tablets->next, nr->tablets);
nr->last_traveled_direction = -1;
}
ncreel_redraw(nr);
return nr->tablets;
}
bool ncreel_offer_input(ncreel* n, const ncinput* nc){
if(nc->id == NCKEY_UP){
ncreel_prev(n);
return true;
}else if(nc->id == NCKEY_DOWN){
ncreel_next(n);
return true;
}else if(nc->id == NCKEY_SCROLL_UP){
ncreel_prev(n);
return true;
}else if(nc->id == NCKEY_SCROLL_DOWN){
ncreel_next(n);
return true;
}
// FIXME there are a few more
return false;
}

View File

@ -5,10 +5,6 @@
using namespace ncpp;
ncreel_options NcReel::default_options = {
/* min_supported_cols */ 0,
/* min_supported_rows */ 0,
/* max_supported_cols */ 0,
/* max_supported_rows */ 0,
/* bordermask */ NCBox::MaskBottom | NCBox::MaskTop | NCBox::MaskRight | NCBox::MaskLeft,
/* borderchan */ 0,
/* tabletmask */ 0,

View File

@ -14,16 +14,22 @@ class TabletCtx {
public:
TabletCtx() :
lines(rand() % 5 + 3),
rgb(rand() % 0x1000000) {}
rgb(rand() % 0x1000000),
idx(++class_idx) {}
int getLines() const {
return lines;
}
int getIdx() const {
return idx;
}
unsigned getRGB() const {
return rgb;
}
private:
int lines;
unsigned rgb;
int idx;
inline static int class_idx = 0;
};
int tabletfxn(struct nctablet* _t, int begx, int begy, int maxx, int maxy,
@ -31,6 +37,7 @@ int tabletfxn(struct nctablet* _t, int begx, int begy, int maxx, int maxy,
(void)begx;
(void)begy;
(void)maxx;
(void)maxy;
(void)cliptop;
NcTablet *t = NcTablet::map_tablet (_t);
Plane* p = t->get_plane();
@ -40,7 +47,10 @@ int tabletfxn(struct nctablet* _t, int begx, int begy, int maxx, int maxy,
c.set_bg(tctx->getRGB());
p->set_base_cell(c);
p->release(c);
return tctx->getLines() > maxy - begy ? maxy - begy : tctx->getLines();
p->set_bg(0xffffff);
p->set_fg(0x000000);
p->printf(1, 1, "%d %p lines: %d", tctx->getIdx(), _t, tctx->getLines());
return tctx->getLines();
}
void usage(const char* argv0, std::ostream& c, int status){
@ -80,62 +90,65 @@ void parse_args(int argc, char** argv, struct notcurses_options* opts,
opts->flags |= NCOPTION_SUPPRESS_BANNERS;
}
int runreels(NotCurses& nc, ncreel_options& nopts){
std::unique_ptr<Plane> nstd(nc.get_stdplane());
int runreels(struct notcurses* nc, ncreel_options* nopts){
int dimy, dimx;
nstd->get_dim(&dimy, &dimx);
auto n = std::make_shared<Plane>(dimy - 1, dimx, 1, 0);
auto nstd = notcurses_stddim_yx(nc, &dimy, &dimx);
if(ncplane_putstr_aligned(nstd, 0, NCALIGN_CENTER, "(a)dd (d)el (q)uit") <= 0){
return -1;
}
auto n = ncplane_new(nc, dimy - 1, dimx, 1, 0, nullptr);
if(!n){
return -1;
}
if(!n->set_fg_rgb(0xb1, 0x1b, 0xb1)){
if(ncplane_set_fg_rgb(n, 0xb1, 0x1b, 0xb1)){
return -1;
}
if(n->putstr(0, NCAlign::Center, "(a)dd (d)el (q)uit") <= 0){
return -1;
}
channels_set_fg(&nopts.focusedchan, 0xffffff);
channels_set_bg(&nopts.focusedchan, 0x00c080);
channels_set_fg(&nopts.borderchan, 0x00c080);
std::shared_ptr<NcReel> nr(n->ncreel_create(&nopts));
if(!nr || !nc.render()){
channels_set_fg(&nopts->focusedchan, 0xffffff);
channels_set_bg(&nopts->focusedchan, 0x00c080);
channels_set_fg(&nopts->borderchan, 0x00c080);
auto nr = ncreel_create(n, nopts);
if(!nr || notcurses_render(nc)){
return -1;
}
int y, x;
char32_t key;
while((key = nc.getc(true)) != (char32_t)-1){
ncinput ni;
while((key = notcurses_getc_blocking(nc, &ni)) != (char32_t)-1){
switch(key){
case 'q':
return 0;
case 'a':{
auto tctx = new TabletCtx();
nr->add(nullptr, nullptr, tabletfxn, tctx);
ncreel_add(nr, nullptr, nullptr, tabletfxn, tctx);
break;
}
case 'd':
nr->del_focused();
ncreel_del(nr, ncreel_focused(nr));
break;
case '*':
notcurses_debug(nc, stderr);
break;
case NCKEY_LEFT:
nr->get_plane()->get_yx(&y, &x);
nr->move(y, x - 1);
ncplane_yx(ncreel_plane(nr), &y, &x);
ncplane_move_yx(ncreel_plane(nr), y, x - 1);
break;
case NCKEY_RIGHT:
nr->get_plane()->get_yx(&y, &x);
nr->move(y, x + 1);
ncplane_yx(ncreel_plane(nr), &y, &x);
ncplane_move_yx(ncreel_plane(nr), y, x + 1);
break;
case NCKEY_UP:
nr->prev();
ncreel_prev(nr);
break;
case NCKEY_DOWN:
nr->next();
ncreel_next(nr);
break;
default:
break;
}
if(!nc.render()){
if(ncreel_redraw(nr)){
break;
}
if(notcurses_render(nc)){
break;
}
}
@ -149,8 +162,13 @@ int main(int argc, char** argv){
notcurses_options ncopts{};
ncreel_options nopts{};
parse_args(argc, argv, &ncopts, &nopts);
NotCurses nc(ncopts);
int r = runreels(nc, nopts);
nc.stop();
auto nc = notcurses_init(&ncopts, NULL);
if(nc == nullptr){
return EXIT_FAILURE;
}
int r = runreels(nc, &nopts);
if(notcurses_stop(nc)){
return EXIT_FAILURE;
}
return r ? EXIT_FAILURE : EXIT_SUCCESS;
}

View File

@ -22,21 +22,21 @@ TEST_CASE("Reels") {
SUBCASE("InitLinear") {
ncreel_options r = { };
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
}
SUBCASE("InitLinearInfinite") {
ncreel_options r{};
r.flags = NCREEL_OPTION_INFINITESCROLL;
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
}
SUBCASE("InitCircular") {
ncreel_options r{};
r.flags = NCREEL_OPTION_INFINITESCROLL | NCREEL_OPTION_CIRCULAR;
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
REQUIRE(0 == ncreel_destroy(nr));
}
@ -45,7 +45,7 @@ TEST_CASE("Reels") {
SUBCASE("FiniteCircleRejected") {
ncreel_options r{};
r.flags = NCREEL_OPTION_CIRCULAR;
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(!nr);
}
@ -53,7 +53,7 @@ TEST_CASE("Reels") {
// even if there are no tablets. They both ought return nullptr.
SUBCASE("MovementWithoutTablets") {
ncreel_options r{};
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
CHECK(!ncreel_next(nr));
// CHECK_EQ(0, ncreel_validate(n_, pr));
@ -63,7 +63,7 @@ TEST_CASE("Reels") {
SUBCASE("OneTablet") {
ncreel_options r{};
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
struct nctablet* t = ncreel_add(nr, nullptr, nullptr, panelcb, nullptr);
REQUIRE(t);
@ -74,7 +74,7 @@ TEST_CASE("Reels") {
SUBCASE("MovementWithOneTablet") {
ncreel_options r{};
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
struct nctablet* t = ncreel_add(nr, nullptr, nullptr, panelcb, nullptr);
REQUIRE(t);
@ -89,25 +89,25 @@ TEST_CASE("Reels") {
SUBCASE("DeleteActiveTablet") {
ncreel_options r{};
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
struct nctablet* t = ncreel_add(nr, nullptr, nullptr, panelcb, nullptr);
REQUIRE(t);
CHECK(0 == ncreel_del_focused(nr));
CHECK(0 == ncreel_del(nr, ncreel_focused(nr)));
}
SUBCASE("NoBorder") {
ncreel_options r{};
r.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT |
NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
}
SUBCASE("BadBorderBitsRejected") {
ncreel_options r{};
r.bordermask = NCBOXMASK_LEFT * 2;
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(!nr);
}
@ -115,28 +115,28 @@ TEST_CASE("Reels") {
ncreel_options r{};
r.tabletmask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT |
NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
}
SUBCASE("NoTopBottomBorder") {
ncreel_options r{};
r.bordermask = NCBOXMASK_TOP | NCBOXMASK_BOTTOM;
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
}
SUBCASE("NoSideBorders") {
ncreel_options r{};
r.bordermask = NCBOXMASK_LEFT | NCBOXMASK_RIGHT;
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
}
SUBCASE("BadTabletBorderBitsRejected") {
ncreel_options r{};
r.tabletmask = NCBOXMASK_LEFT * 2;
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(!nr);
}
@ -173,7 +173,7 @@ TEST_CASE("Reels") {
REQUIRE_NE(nullptr, base);
WINDOW* basew = panel_window(base);
REQUIRE_NE(nullptr, basew);
struct ncreel* nr = ncreel_create(basew, &r, -1);
struct ncreel* nr = ncreel_create(basew, &r);
REQUIRE_NE(nullptr, pr);
CHECK_EQ(0, ncreel_validate(basew, pr));
REQUIRE_EQ(0, ncreel_destroy(nr));
@ -194,7 +194,7 @@ TEST_CASE("Reels") {
REQUIRE_NE(nullptr, base);
WINDOW* basew = panel_window(base);
REQUIRE_NE(nullptr, basew);
struct ncreel* nr = ncreel_create(basew, &r, -1);
struct ncreel* nr = ncreel_create(basew, &r);
REQUIRE_NE(nullptr, pr);
CHECK_EQ(0, ncreel_validate(basew, pr));
REQUIRE_EQ(0, ncreel_destroy(nr));
@ -209,7 +209,7 @@ TEST_CASE("Reels") {
REQUIRE_NE(nullptr, base);
WINDOW* basew = panel_window(base);
REQUIRE_NE(nullptr, basew);
struct ncreel* nr = ncreel_create(basew, &r, -1);
struct ncreel* nr = ncreel_create(basew, &r);
REQUIRE_NE(nullptr, pr);
CHECK_EQ(0, ncreel_validate(basew, pr));
REQUIRE_EQ(0, ncreel_destroy(nr));
@ -221,7 +221,7 @@ TEST_CASE("Reels") {
SUBCASE("TransparentBackground") {
ncreel_options r{};
channels_set_bg_alpha(&r.bgchannel, 3);
struct ncreel* nr = ncreel_create(n_, &r, -1);
struct ncreel* nr = ncreel_create(n_, &r);
REQUIRE(nr);
// FIXME
}