mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
Port of panelreels, unit tests, and panelreel-demo from outcurses #52. Doesn't completely work yet, but is surprisingly close!
This commit is contained in:
parent
326e93fee1
commit
426f632300
@ -13,6 +13,7 @@ configure_file(tools/version.h.in include/version.h)
|
|||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
pkg_check_modules(TERMINFO REQUIRED tinfo>=6.1)
|
pkg_check_modules(TERMINFO REQUIRED tinfo>=6.1)
|
||||||
pkg_check_modules(AVUTIL REQUIRED libavutil>=56.0)
|
pkg_check_modules(AVUTIL REQUIRED libavutil>=56.0)
|
||||||
pkg_check_modules(AVFORMAT REQUIRED libavformat>=57.0)
|
pkg_check_modules(AVFORMAT REQUIRED libavformat>=57.0)
|
||||||
@ -62,6 +63,7 @@ target_include_directories(notcurses-demo PRIVATE include)
|
|||||||
target_link_libraries(notcurses-demo
|
target_link_libraries(notcurses-demo
|
||||||
PRIVATE
|
PRIVATE
|
||||||
notcurses
|
notcurses
|
||||||
|
Threads::Threads
|
||||||
)
|
)
|
||||||
target_compile_options(notcurses-demo
|
target_compile_options(notcurses-demo
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
@ -143,15 +143,16 @@ API int notcurses_render(struct notcurses* nc);
|
|||||||
//
|
//
|
||||||
// In the case of a valid read, a positive value is returned corresponding to
|
// In the case of a valid read, a positive value is returned corresponding to
|
||||||
// the number of bytes in the UTF-8 character, or '1' for all specials keys.
|
// the number of bytes in the UTF-8 character, or '1' for all specials keys.
|
||||||
// 0 is returned only by notcurses_getc(), to indicate that no input was
|
// 0 is returned to indicate that no input was available, but only by
|
||||||
// available. Otherwise (including on EOF) -1 is returned.
|
// notcurses_getc(). Otherwise (including on EOF) -1 is returned.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NCKEY_INVALID,
|
NCKEY_INVALID,
|
||||||
NCKEY_RESIZE,
|
NCKEY_RESIZE, // generated interally in response to SIGWINCH
|
||||||
NCKEY_UP,
|
NCKEY_UP,
|
||||||
NCKEY_RIGHT,
|
NCKEY_RIGHT,
|
||||||
NCKEY_DOWN,
|
NCKEY_DOWN,
|
||||||
NCKEY_LEFT,
|
NCKEY_LEFT,
|
||||||
|
NCKEY_DC, // delete
|
||||||
// FIXME...
|
// FIXME...
|
||||||
} ncspecial_key;
|
} ncspecial_key;
|
||||||
|
|
||||||
@ -660,13 +661,10 @@ typedef struct panelreel_options {
|
|||||||
// focused and non-focused tablets can have different styles. you can instead
|
// focused and non-focused tablets can have different styles. you can instead
|
||||||
// draw your own borders, or forgo borders entirely.
|
// draw your own borders, or forgo borders entirely.
|
||||||
unsigned bordermask; // bitfield; 1s will not be drawn (see bordermaskbits)
|
unsigned bordermask; // bitfield; 1s will not be drawn (see bordermaskbits)
|
||||||
uint32_t borderattr; // attributes used for panelreel border, no color!
|
cell borderattr; // attributes used for panelreel border
|
||||||
int borderpair; // extended color pair for panelreel border
|
|
||||||
unsigned tabletmask; // bitfield; same as bordermask but for tablet borders
|
unsigned tabletmask; // bitfield; same as bordermask but for tablet borders
|
||||||
uint32_t tabletattr; // attributes used for tablet borders, no color!
|
cell tabletattr; // attributes used for tablet borders
|
||||||
int tabletpair; // extended color pair for tablet borders
|
cell focusedattr; // attributes used for focused tablet borders, no color!
|
||||||
uint32_t focusedattr;// attributes used for focused tablet borders, no color!
|
|
||||||
int focusedpair; // extended color pair for focused tablet borders
|
|
||||||
} panelreel_options;
|
} panelreel_options;
|
||||||
|
|
||||||
struct tablet;
|
struct tablet;
|
||||||
@ -679,9 +677,9 @@ struct panelreel;
|
|||||||
// and columns can be enforced via popts. efd, if non-negative, is an eventfd
|
// and columns can be enforced via popts. efd, if non-negative, is an eventfd
|
||||||
// that ought be written to whenever panelreel_touch() updates a tablet (this
|
// that ought be written to whenever panelreel_touch() updates a tablet (this
|
||||||
// is useful in the case of nonblocking input).
|
// is useful in the case of nonblocking input).
|
||||||
struct panelreel* panelreel_create(struct ncplane* nc,
|
API struct panelreel* panelreel_create(struct ncplane* nc,
|
||||||
const panelreel_options* popts,
|
const panelreel_options* popts,
|
||||||
int efd);
|
int efd);
|
||||||
|
|
||||||
// Tablet draw callback, provided a ncplane the first column that may be used,
|
// Tablet draw callback, provided a ncplane the first column that may be used,
|
||||||
// the first row that may be used, the first column that may not be used, the
|
// the first row that may be used, the first column that may not be used, the
|
||||||
@ -698,7 +696,7 @@ struct panelreel* panelreel_create(struct ncplane* nc,
|
|||||||
// Returns the number of lines of output, which ought be less than or equal to
|
// Returns the number of lines of output, which ought be less than or equal to
|
||||||
// maxy - begy, and non-negative (negative values might be used in the future).
|
// maxy - begy, and non-negative (negative values might be used in the future).
|
||||||
typedef int (*tabletcb)(struct ncplane* p, int begx, int begy, int maxx,
|
typedef int (*tabletcb)(struct ncplane* p, int begx, int begy, int maxx,
|
||||||
int maxy, bool cliptop);
|
int maxy, bool cliptop, void* curry);
|
||||||
|
|
||||||
// Add a new tablet to the provided panelreel, having the callback object
|
// Add a new tablet to the provided panelreel, having the callback object
|
||||||
// opaque. Neither, either, or both of after and before may be specified. If
|
// opaque. Neither, either, or both of after and before may be specified. If
|
||||||
@ -707,44 +705,45 @@ typedef int (*tabletcb)(struct ncplane* p, int begx, int begy, int maxx,
|
|||||||
// specified tablet. If both are specifid, the tablet will be added to the
|
// specified tablet. If both are specifid, the tablet will be added to the
|
||||||
// resulting location, assuming it is valid (after->next == before->prev); if
|
// resulting location, assuming it is valid (after->next == before->prev); if
|
||||||
// it is not valid, or there is any other error, NULL will be returned.
|
// it is not valid, or there is any other error, NULL will be returned.
|
||||||
struct tablet* panelreel_add(struct panelreel* pr, struct tablet* after,
|
API struct tablet* panelreel_add(struct panelreel* pr, struct tablet* after,
|
||||||
struct tablet *before, tabletcb cb, void* opaque);
|
struct tablet *before, tabletcb cb,
|
||||||
|
void* opaque);
|
||||||
|
|
||||||
// Return the number of tablets.
|
// Return the number of tablets.
|
||||||
int panelreel_tabletcount(const struct panelreel* pr);
|
API int panelreel_tabletcount(const struct panelreel* pr);
|
||||||
|
|
||||||
// Indicate that the specified tablet has been updated in a way that would
|
// 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
|
// change its display. This will trigger some non-negative number of callbacks
|
||||||
// (though not in the caller's context).
|
// (though not in the caller's context).
|
||||||
int panelreel_touch(struct panelreel* pr, struct tablet* t);
|
API int panelreel_touch(struct panelreel* pr, struct tablet* t);
|
||||||
|
|
||||||
// Delete the tablet specified by t from the panelreel specified by pr. Returns
|
// Delete the tablet specified by t from the panelreel specified by pr. Returns
|
||||||
// -1 if the tablet cannot be found.
|
// -1 if the tablet cannot be found.
|
||||||
int panelreel_del(struct panelreel* pr, struct tablet* t);
|
API int panelreel_del(struct panelreel* pr, struct tablet* t);
|
||||||
|
|
||||||
// Delete the active tablet. Returns -1 if there are no tablets.
|
// Delete the active tablet. Returns -1 if there are no tablets.
|
||||||
int panelreel_del_focused(struct panelreel* pr);
|
API int panelreel_del_focused(struct panelreel* pr);
|
||||||
|
|
||||||
// Move to the specified location within the containing WINDOW.
|
// Move to the specified location within the containing WINDOW.
|
||||||
int panelreel_move(struct panelreel* pr, int x, int y);
|
API int panelreel_move(struct panelreel* pr, int x, int y);
|
||||||
|
|
||||||
// Redraw the panelreel in its entirety, for instance after
|
// Redraw the panelreel in its entirety, for instance after
|
||||||
// clearing the screen due to external corruption, or a SIGWINCH.
|
// clearing the screen due to external corruption, or a SIGWINCH.
|
||||||
int panelreel_redraw(struct panelreel* pr);
|
API int panelreel_redraw(struct panelreel* pr);
|
||||||
|
|
||||||
// Return the focused tablet, if any tablets are present. This is not a copy;
|
// 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.
|
// be careful to use it only for the duration of a critical section.
|
||||||
struct tablet* panelreel_focused(struct panelreel* pr);
|
API struct tablet* panelreel_focused(struct panelreel* pr);
|
||||||
|
|
||||||
// Change focus to the next tablet, if one exists
|
// Change focus to the next tablet, if one exists
|
||||||
struct tablet* panelreel_next(struct panelreel* pr);
|
API struct tablet* panelreel_next(struct panelreel* pr);
|
||||||
|
|
||||||
// Change focus to the previous tablet, if one exists
|
// Change focus to the previous tablet, if one exists
|
||||||
struct tablet* panelreel_prev(struct panelreel* pr);
|
API struct tablet* panelreel_prev(struct panelreel* pr);
|
||||||
|
|
||||||
// Destroy a panelreel allocated with panelreel_create(). Does not destroy the
|
// Destroy a panelreel allocated with panelreel_create(). Does not destroy the
|
||||||
// underlying WINDOW. Returns non-zero on failure.
|
// underlying WINDOW. Returns non-zero on failure.
|
||||||
int panelreel_destroy(struct panelreel* pr);
|
API int panelreel_destroy(struct panelreel* pr);
|
||||||
|
|
||||||
#undef API
|
#undef API
|
||||||
|
|
||||||
|
@ -24,12 +24,13 @@ usage(const char* exe, int status){
|
|||||||
fprintf(out, " -d: delay multiplier (float)\n");
|
fprintf(out, " -d: delay multiplier (float)\n");
|
||||||
fprintf(out, " -f: render to file in addition to stdout\n");
|
fprintf(out, " -f: render to file in addition to stdout\n");
|
||||||
fprintf(out, "all demos are run if no specification is provided\n");
|
fprintf(out, "all demos are run if no specification is provided\n");
|
||||||
fprintf(out, " i: run intro\n");
|
|
||||||
fprintf(out, " s: run shuffle\n");
|
|
||||||
fprintf(out, " u: run uniblock\n");
|
|
||||||
fprintf(out, " m: run maxcolor\n");
|
|
||||||
fprintf(out, " b: run box\n");
|
fprintf(out, " b: run box\n");
|
||||||
fprintf(out, " g: run grid\n");
|
fprintf(out, " g: run grid\n");
|
||||||
|
fprintf(out, " i: run intro\n");
|
||||||
|
fprintf(out, " m: run maxcolor\n");
|
||||||
|
fprintf(out, " p: run panelreels\n");
|
||||||
|
fprintf(out, " s: run shuffle\n");
|
||||||
|
fprintf(out, " u: run uniblock\n");
|
||||||
fprintf(out, " v: run view\n");
|
fprintf(out, " v: run view\n");
|
||||||
fprintf(out, " w: run widecolors\n");
|
fprintf(out, " w: run widecolors\n");
|
||||||
exit(status);
|
exit(status);
|
||||||
@ -142,6 +143,7 @@ ext_demos(struct notcurses* nc, const char* demos){
|
|||||||
case 'g': ret = grid_demo(nc); break;
|
case 'g': ret = grid_demo(nc); break;
|
||||||
case 'v': ret = view_demo(nc); break;
|
case 'v': ret = view_demo(nc); break;
|
||||||
case 'w': ret = widecolor_demo(nc); break;
|
case 'w': ret = widecolor_demo(nc); break;
|
||||||
|
case 'p': ret = panelreel_demo(nc); break;
|
||||||
}
|
}
|
||||||
if(ret){
|
if(ret){
|
||||||
return ret;
|
return ret;
|
||||||
@ -209,7 +211,7 @@ int main(int argc, char** argv){
|
|||||||
if(argv[optind] != NULL){
|
if(argv[optind] != NULL){
|
||||||
usage(*argv, EXIT_FAILURE);
|
usage(*argv, EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
demos = "isumbgwv";
|
demos = "isumbgwvp";
|
||||||
}
|
}
|
||||||
if((nc = notcurses_init(&nopts)) == NULL){
|
if((nc = notcurses_init(&nopts)) == NULL){
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
@ -20,6 +20,7 @@ int maxcolor_demo(struct notcurses* nc);
|
|||||||
int grid_demo(struct notcurses* nc);
|
int grid_demo(struct notcurses* nc);
|
||||||
int sliding_puzzle_demo(struct notcurses* nc);
|
int sliding_puzzle_demo(struct notcurses* nc);
|
||||||
int view_demo(struct notcurses* nc);
|
int view_demo(struct notcurses* nc);
|
||||||
|
int panelreel_demo(struct notcurses* nc);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
349
src/demo/panelreel.c
Normal file
349
src/demo/panelreel.c
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <notcurses.h>
|
||||||
|
#include <sys/eventfd.h>
|
||||||
|
#include "demo.h"
|
||||||
|
|
||||||
|
// FIXME ought just be an unordered_map
|
||||||
|
typedef struct tabletctx {
|
||||||
|
pthread_t tid;
|
||||||
|
struct panelreel* pr;
|
||||||
|
struct tablet* t;
|
||||||
|
int lines;
|
||||||
|
unsigned rgb;
|
||||||
|
unsigned id;
|
||||||
|
struct tabletctx* next;
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
} tabletctx;
|
||||||
|
|
||||||
|
static void
|
||||||
|
kill_tablet(tabletctx** tctx){
|
||||||
|
tabletctx* t = *tctx;
|
||||||
|
if(t){
|
||||||
|
if(pthread_cancel(t->tid)){
|
||||||
|
fprintf(stderr, "Warning: error sending pthread_cancel (%s)\n", strerror(errno));
|
||||||
|
}
|
||||||
|
if(pthread_join(t->tid, NULL)){
|
||||||
|
fprintf(stderr, "Warning: error joining pthread (%s)\n", strerror(errno));
|
||||||
|
}
|
||||||
|
panelreel_del(t->pr, t->t);
|
||||||
|
*tctx = t->next;
|
||||||
|
pthread_mutex_destroy(&t->lock);
|
||||||
|
free(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
kill_active_tablet(struct panelreel* pr, tabletctx** tctx){
|
||||||
|
struct tablet* focused = panelreel_focused(pr);
|
||||||
|
tabletctx* t;
|
||||||
|
while( (t = *tctx) ){
|
||||||
|
if(t->t == focused){
|
||||||
|
*tctx = t->next; // pull it out of the list
|
||||||
|
t->next = NULL; // finish splicing it out
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tctx = &t->next;
|
||||||
|
}
|
||||||
|
if(t == NULL){
|
||||||
|
return -1; // wasn't present in our list, wacky
|
||||||
|
}
|
||||||
|
kill_tablet(&t);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need write in reverse order (since only the bottom will be seen, if we're
|
||||||
|
// partially off-screen), but also leave unused space at the end (since
|
||||||
|
// wresize() only keeps the top and left on a shrink).
|
||||||
|
static int
|
||||||
|
tabletup(struct ncplane* w, int begx, int begy, int maxx, int maxy,
|
||||||
|
tabletctx* tctx, int rgb){
|
||||||
|
char cchbuf[2];
|
||||||
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
int y, idx;
|
||||||
|
idx = tctx->lines;
|
||||||
|
if(maxy - begy > tctx->lines){
|
||||||
|
maxy -= (maxy - begy - tctx->lines);
|
||||||
|
}
|
||||||
|
/*fprintf(stderr, "-OFFSET BY %d (%d->%d)\n", maxy - begy - tctx->lines,
|
||||||
|
maxy, maxy - (maxy - begy - tctx->lines));*/
|
||||||
|
for(y = maxy ; y >= begy ; --y, rgb += 16){
|
||||||
|
ncplane_cursor_move_yx(w, y, begx);
|
||||||
|
snprintf(cchbuf, sizeof(cchbuf) / sizeof(*cchbuf), "%x", idx % 16);
|
||||||
|
cell_load(w, &c, cchbuf);
|
||||||
|
cell_set_fg(&c, (rgb >> 16u) % 0xffu, (rgb >> 8u) % 0xffu, rgb % 0xffu);
|
||||||
|
int x;
|
||||||
|
for(x = begx ; x <= maxx ; ++x){
|
||||||
|
// lower-right corner always returns an error unless scrollok() is used
|
||||||
|
ncplane_putc(w, &c);
|
||||||
|
}
|
||||||
|
if(--idx == 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fprintf(stderr, "tabletup done%s at %d (%d->%d)\n", idx == 0 ? " early" : "", y, begy, maxy);
|
||||||
|
return tctx->lines - idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tabletdown(struct ncplane* w, int begx, int begy, int maxx, int maxy,
|
||||||
|
tabletctx* tctx, unsigned rgb){
|
||||||
|
char cchbuf[2];
|
||||||
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
int y;
|
||||||
|
for(y = begy ; y <= maxy ; ++y, rgb += 16){
|
||||||
|
if(y - begy >= tctx->lines){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ncplane_cursor_move_yx(w, y, begx);
|
||||||
|
snprintf(cchbuf, sizeof(cchbuf) / sizeof(*cchbuf), "%x", y % 16);
|
||||||
|
cell_load(w, &c, cchbuf);
|
||||||
|
cell_set_fg(&c, (rgb >> 16u) % 0xffu, (rgb >> 8u) % 0xffu, rgb % 0xffu);
|
||||||
|
int x;
|
||||||
|
for(x = begx ; x <= maxx ; ++x){
|
||||||
|
// lower-right corner always returns an error unless scrollok() is used
|
||||||
|
ncplane_putc(w, &c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return y - begy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tabletdraw(struct ncplane* p, int begx, int begy, int maxx, int maxy,
|
||||||
|
bool cliptop, void* vtabletctx){
|
||||||
|
int err = 0;
|
||||||
|
tabletctx* tctx = vtabletctx;
|
||||||
|
pthread_mutex_lock(&tctx->lock);
|
||||||
|
unsigned rgb = tctx->rgb;
|
||||||
|
int ll;
|
||||||
|
if(cliptop){
|
||||||
|
ll = tabletup(p, begx, begy, maxx, maxy, tctx, rgb);
|
||||||
|
}else{
|
||||||
|
ll = tabletdown(p, begx, begy, maxx, maxy, tctx, rgb);
|
||||||
|
}
|
||||||
|
ncplane_fg_rgb8(p, 242, 242, 242);
|
||||||
|
if(ll){
|
||||||
|
int summaryy = begy;
|
||||||
|
if(cliptop){
|
||||||
|
if(ll == maxy - begy + 1){
|
||||||
|
summaryy = ll - 1;
|
||||||
|
}else{
|
||||||
|
summaryy = ll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err |= ncplane_cursor_move_yx(p, summaryy, begx);
|
||||||
|
ncplane_printf(p, "[#%u %d line%s %u/%u] ", tctx->id, tctx->lines,
|
||||||
|
tctx->lines == 1 ? "" : "s", begy, maxy);
|
||||||
|
}
|
||||||
|
/*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);*/
|
||||||
|
pthread_mutex_unlock(&tctx->lock);
|
||||||
|
assert(0 == err);
|
||||||
|
return ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each tablet has an associated thread which will periodically send update
|
||||||
|
// events for its tablet.
|
||||||
|
static void*
|
||||||
|
tablet_thread(void* vtabletctx){
|
||||||
|
static int MINSECONDS = 0;
|
||||||
|
tabletctx* tctx = vtabletctx;
|
||||||
|
while(true){
|
||||||
|
struct timespec ts;
|
||||||
|
ts.tv_sec = random() % 3 + MINSECONDS;
|
||||||
|
ts.tv_nsec = random() % 1000000000;
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
int action = random() % 5;
|
||||||
|
pthread_mutex_lock(&tctx->lock);
|
||||||
|
if(action < 2){
|
||||||
|
if((tctx->lines -= (action + 1)) < 1){
|
||||||
|
tctx->lines = 1;
|
||||||
|
}
|
||||||
|
panelreel_touch(tctx->pr, tctx->t);
|
||||||
|
}else if(action > 2){
|
||||||
|
if((tctx->lines += (action - 2)) < 1){
|
||||||
|
tctx->lines = 1;
|
||||||
|
}
|
||||||
|
panelreel_touch(tctx->pr, tctx->t);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&tctx->lock);
|
||||||
|
}
|
||||||
|
return tctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static tabletctx*
|
||||||
|
new_tabletctx(struct panelreel* pr, unsigned *id){
|
||||||
|
tabletctx* tctx = malloc(sizeof(*tctx));
|
||||||
|
if(tctx == NULL){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_init(&tctx->lock, NULL);
|
||||||
|
tctx->pr = pr;
|
||||||
|
tctx->lines = random() % 10 + 1; // FIXME a nice gaussian would be swell
|
||||||
|
tctx->rgb = random() % (1u << 24u);
|
||||||
|
tctx->id = ++*id;
|
||||||
|
if((tctx->t = panelreel_add(pr, NULL, NULL, tabletdraw, tctx)) == NULL){
|
||||||
|
pthread_mutex_destroy(&tctx->lock);
|
||||||
|
free(tctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(pthread_create(&tctx->tid, NULL, tablet_thread, tctx)){
|
||||||
|
pthread_mutex_destroy(&tctx->lock);
|
||||||
|
free(tctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return tctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_input(struct notcurses* nc, struct panelreel* pr, int efd,
|
||||||
|
cell* c, ncspecial_key* special){
|
||||||
|
struct pollfd fds[2] = {
|
||||||
|
{ .fd = STDIN_FILENO, .events = POLLIN, .revents = 0, },
|
||||||
|
{ .fd = efd, .events = POLLIN, .revents = 0, },
|
||||||
|
};
|
||||||
|
int key = -1;
|
||||||
|
int pret;
|
||||||
|
notcurses_render(nc);
|
||||||
|
do{
|
||||||
|
pret = poll(fds, sizeof(fds) / sizeof(*fds), -1);
|
||||||
|
if(pret < 0){
|
||||||
|
fprintf(stderr, "Error polling on stdin/eventfd (%s)\n", strerror(errno));
|
||||||
|
}else{
|
||||||
|
if(fds[0].revents & POLLIN){
|
||||||
|
key = notcurses_getc(nc, c, special);
|
||||||
|
if(key < 0){
|
||||||
|
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 < 0){
|
||||||
|
panelreel_redraw(pr);
|
||||||
|
notcurses_render(nc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}while(key < 0);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct panelreel*
|
||||||
|
panelreel_demo_core(struct notcurses* nc, int efd, tabletctx** tctxs){
|
||||||
|
bool done = false;
|
||||||
|
int x = 4, y = 4;
|
||||||
|
panelreel_options popts = {
|
||||||
|
.infinitescroll = true,
|
||||||
|
.circular = true,
|
||||||
|
.min_supported_cols = 8,
|
||||||
|
.min_supported_rows = 5,
|
||||||
|
.borderattr = CELL_TRIVIAL_INITIALIZER,
|
||||||
|
.tabletattr = CELL_TRIVIAL_INITIALIZER,
|
||||||
|
.focusedattr = CELL_TRIVIAL_INITIALIZER,
|
||||||
|
.toff = y,
|
||||||
|
.loff = x,
|
||||||
|
.roff = 0,
|
||||||
|
.boff = 0,
|
||||||
|
};
|
||||||
|
cell_set_fg(&popts.focusedattr, 58, 150, 221);
|
||||||
|
cell_set_fg(&popts.tabletattr, 19, 161, 14);
|
||||||
|
cell_set_fg(&popts.borderattr, 136, 23, 152);
|
||||||
|
struct ncplane* w = notcurses_stdplane(nc);
|
||||||
|
struct panelreel* pr = panelreel_create(w, &popts, efd);
|
||||||
|
if(pr == NULL){
|
||||||
|
fprintf(stderr, "Error creating panelreel\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// Press a for a new panel above the current, c for a new one below the
|
||||||
|
// current, and b for a new block at arbitrary placement. q quits.
|
||||||
|
ncplane_fg_rgb8(w, 58, 150, 221);
|
||||||
|
ncplane_cursor_move_yx(w, 1, 1);
|
||||||
|
ncplane_printf(w, "a, b, c create tablets, DEL deletes, q quits.");
|
||||||
|
// FIXME clrtoeol();
|
||||||
|
unsigned id = 0;
|
||||||
|
do{
|
||||||
|
ncplane_styles_set(w, 0);
|
||||||
|
ncplane_fg_rgb8(w, 197, 15, 31);
|
||||||
|
int count = panelreel_tabletcount(pr);
|
||||||
|
ncplane_cursor_move_yx(w, 2, 2);
|
||||||
|
ncplane_printf(w, "%d tablet%s", count, count == 1 ? "" : "s");
|
||||||
|
// FIXME wclrtoeol(w);
|
||||||
|
ncplane_fg_rgb8(w, 0, 55, 218);
|
||||||
|
ncspecial_key special = NCKEY_INVALID;
|
||||||
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
if(handle_input(nc, pr, efd, &c, &special) < 0){
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// FIXME clrtoeol();
|
||||||
|
struct tabletctx* newtablet = NULL;
|
||||||
|
if(cell_simple_p(&c)){
|
||||||
|
if(c.gcluster){
|
||||||
|
switch(c.gcluster){
|
||||||
|
case 'p': sleep(60); exit(EXIT_FAILURE); break;
|
||||||
|
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(panelreel_move(pr, x, y)){ ++x; } break;
|
||||||
|
case 'l': ++x; if(panelreel_move(pr, x, y)){ --x; } break;
|
||||||
|
case 'k': panelreel_prev(pr); break;
|
||||||
|
case 'j': panelreel_next(pr); break;
|
||||||
|
case 'q': done = true; break;
|
||||||
|
default:
|
||||||
|
ncplane_cursor_move_yx(w, 3, 2);
|
||||||
|
ncplane_printf(w, "Unknown keycode (%d)\n", c.gcluster);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
switch(special){
|
||||||
|
case NCKEY_LEFT: --x; if(panelreel_move(pr, x, y)){ ++x; } break;
|
||||||
|
case NCKEY_RIGHT: ++x; if(panelreel_move(pr, x, y)){ --x; } break;
|
||||||
|
case NCKEY_UP: panelreel_prev(pr); break;
|
||||||
|
case NCKEY_DOWN: panelreel_next(pr); break;
|
||||||
|
case NCKEY_DC: kill_active_tablet(pr, tctxs); break;
|
||||||
|
default:
|
||||||
|
ncplane_cursor_move_yx(w, 3, 2);
|
||||||
|
ncplane_printf(w, "Unknown special (%d)\n", special);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell_release(w, &c);
|
||||||
|
if(newtablet){
|
||||||
|
newtablet->next = *tctxs;
|
||||||
|
*tctxs = newtablet;
|
||||||
|
}
|
||||||
|
//panelreel_validate(w, pr); // do what, if not assert()ing? FIXME
|
||||||
|
}while(!done);
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelreel_demo(struct notcurses* nc){
|
||||||
|
tabletctx* tctxs = NULL;
|
||||||
|
int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||||
|
if(efd < 0){
|
||||||
|
fprintf(stderr, "Error creating eventfd (%s)\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
struct panelreel* pr;
|
||||||
|
if((pr = panelreel_demo_core(nc, efd, &tctxs)) == NULL){
|
||||||
|
close(efd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/*fadeout(w, FADE_MILLISECONDS);*/
|
||||||
|
while(tctxs){
|
||||||
|
kill_tablet(&tctxs);
|
||||||
|
}
|
||||||
|
close(efd);
|
||||||
|
if(panelreel_destroy(pr)){
|
||||||
|
fprintf(stderr, "Error destroying panelreel\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(efd);
|
||||||
|
return 0;
|
||||||
|
}
|
@ -85,12 +85,12 @@ int widecolor_demo(struct notcurses* nc){
|
|||||||
"⑥ И҆ речѐ гдⷭ҇ь: сѐ, ро́дъ є҆ди́нъ, и҆ ѹ҆стнѣ̀ є҆ди҄нѣ всѣ́хъ, и҆ сїѐ нача́ша твори́ти: и҆ нн҃ѣ не ѡ҆скꙋдѣ́ютъ ѿ ни́хъ всѧ҄, є҆ли҄ка а́҆ще восхотѧ́тъ твори́ти.",
|
"⑥ И҆ речѐ гдⷭ҇ь: сѐ, ро́дъ є҆ди́нъ, и҆ ѹ҆стнѣ̀ є҆ди҄нѣ всѣ́хъ, и҆ сїѐ нача́ша твори́ти: и҆ нн҃ѣ не ѡ҆скꙋдѣ́ютъ ѿ ни́хъ всѧ҄, є҆ли҄ка а́҆ще восхотѧ́тъ твори́ти.",
|
||||||
"⑦ Ⱂⱃⰻⰻⰴⱑⱅⰵ ⰺ ⰺⰸⱎⰵⰴⱎⰵ ⱄⰿⱑⱄⰻⰿⱏ ⰺⰿⱏ ⱅⱆ ⱔⰸⱏⰹⰽⰻ ⰺⱈⱏ · ⰴⰰ ⱀⰵ ⱆⱄⰾⱏⰹⱎⰰⱅⱏ ⰽⱁⰶⰴⱁ ⰴⱃⱆⰳⰰ ⱄⰲⱁⰵⰳⱁ ⁖⸏",
|
"⑦ Ⱂⱃⰻⰻⰴⱑⱅⰵ ⰺ ⰺⰸⱎⰵⰴⱎⰵ ⱄⰿⱑⱄⰻⰿⱏ ⰺⰿⱏ ⱅⱆ ⱔⰸⱏⰹⰽⰻ ⰺⱈⱏ · ⰴⰰ ⱀⰵ ⱆⱄⰾⱏⰹⱎⰰⱅⱏ ⰽⱁⰶⰴⱁ ⰴⱃⱆⰳⰰ ⱄⰲⱁⰵⰳⱁ ⁖⸏",
|
||||||
"काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम्",
|
"काचं शक्नोम्यत्तुम् । नोपहिनस्ति माम्",
|
||||||
|
*/
|
||||||
"kācaṃ śaknomyattum; nopahinasti mām",
|
"kācaṃ śaknomyattum; nopahinasti mām",
|
||||||
"ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει",
|
"ὕαλον ϕαγεῖν δύναμαι· τοῦτο οὔ με βλάπτει",
|
||||||
"Μπορῶ νὰ φάω σπασμένα γυαλιὰ χωρὶς νὰ πάθω τίποτα",
|
"Μπορῶ νὰ φάω σπασμένα γυαλιὰ χωρὶς νὰ πάθω τίποτα",
|
||||||
"Vitrum edere possum; mihi non nocet",
|
"Vitrum edere possum; mihi non nocet",
|
||||||
"🚬🌿💉💊☢☣🔫💣⚔🤜🤛🧠🦹🤺🏋️,🦔🐧🐣🦆🦢🦜🦉🐊🐸🦕 🦖🐬🐙🦂🦠🦀",
|
// "🚬🌿💉💊☢☣🔫💣⚔🤜🤛🧠🦹🤺🏋️,🦔🐧🐣🦆🦢🦜🦉🐊🐸🦕 🦖🐬🐙🦂🦠🦀",
|
||||||
*/
|
|
||||||
"Je puis mangier del voirre. Ne me nuit",
|
"Je puis mangier del voirre. Ne me nuit",
|
||||||
"Je peux manger du verre, ça ne me fait pas mal",
|
"Je peux manger du verre, ça ne me fait pas mal",
|
||||||
"Pòdi manjar de veire, me nafrariá pas",
|
"Pòdi manjar de veire, me nafrariá pas",
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include "egcpool.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -18,7 +19,35 @@ struct AVCodec;
|
|||||||
struct AVCodecParameters;
|
struct AVCodecParameters;
|
||||||
struct AVPacket;
|
struct AVPacket;
|
||||||
struct SwsContext;
|
struct SwsContext;
|
||||||
struct ncplane;
|
|
||||||
|
// A plane is memory for some rectilinear virtual window, plus current cursor
|
||||||
|
// state for that window. A notcurses context describes a single terminal, and
|
||||||
|
// has a z-order of planes (I see no advantage to maintaining a poset, and we
|
||||||
|
// instead just use a list, top-to-bottom). Every cell on the terminal is part
|
||||||
|
// of at least one plane, and at least one plane covers the entirety of the
|
||||||
|
// terminal (this plane is created during initialization).
|
||||||
|
//
|
||||||
|
// Functions update these virtual planes over a series of API calls. Eventually,
|
||||||
|
// notcurses_render() is called. We then do a depth buffer blit of updated
|
||||||
|
// cells. A cell is updated if the topmost plane including that cell updates it,
|
||||||
|
// not simply if any plane updates it.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
typedef struct ncplane {
|
||||||
|
cell* fb; // "framebuffer" of character cells
|
||||||
|
int x, y; // current 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
|
||||||
|
struct ncplane* z; // plane below us
|
||||||
|
egcpool pool; // attached storage pool for UTF-8 EGCs
|
||||||
|
uint64_t channels; // works the same way as cells
|
||||||
|
uint32_t attrword; // same deal as in a cell
|
||||||
|
void* userptr; // slot for the user to stick some opaque pointer
|
||||||
|
cell background; // cell written anywhere that fb[i].gcluster == 0
|
||||||
|
struct notcurses* nc; // notcurses object of which we are a part
|
||||||
|
} ncplane;
|
||||||
|
|
||||||
typedef struct ncvisual {
|
typedef struct ncvisual {
|
||||||
struct AVFormatContext* fmtctx;
|
struct AVFormatContext* fmtctx;
|
||||||
@ -31,7 +60,7 @@ typedef struct ncvisual {
|
|||||||
struct SwsContext* swsctx;
|
struct SwsContext* swsctx;
|
||||||
int packet_outstanding;
|
int packet_outstanding;
|
||||||
int dstwidth, dstheight;
|
int dstwidth, dstheight;
|
||||||
struct ncplane* ncp;
|
ncplane* ncp;
|
||||||
} ncvisual;
|
} ncvisual;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -26,35 +26,6 @@
|
|||||||
|
|
||||||
#define ESC "\x1b"
|
#define ESC "\x1b"
|
||||||
|
|
||||||
// A plane is memory for some rectilinear virtual window, plus current cursor
|
|
||||||
// state for that window. A notcurses context describes a single terminal, and
|
|
||||||
// has a z-order of planes (I see no advantage to maintaining a poset, and we
|
|
||||||
// instead just use a list, top-to-bottom). Every cell on the terminal is part
|
|
||||||
// of at least one plane, and at least one plane covers the entirety of the
|
|
||||||
// terminal (this plane is created during initialization).
|
|
||||||
//
|
|
||||||
// Functions update these virtual planes over a series of API calls. Eventually,
|
|
||||||
// notcurses_render() is called. We then do a depth buffer blit of updated
|
|
||||||
// cells. A cell is updated if the topmost plane including that cell updates it,
|
|
||||||
// not simply if any plane updates it.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
typedef struct ncplane {
|
|
||||||
cell* fb; // "framebuffer" of character cells
|
|
||||||
int x, y; // current 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
|
|
||||||
struct ncplane* z; // plane below us
|
|
||||||
egcpool pool; // attached storage pool for UTF-8 EGCs
|
|
||||||
uint64_t channels; // works the same way as cells
|
|
||||||
uint32_t attrword; // same deal as in a cell
|
|
||||||
void* userptr; // slot for the user to stick some opaque pointer
|
|
||||||
cell background; // cell written anywhere that fb[i].gcluster == 0
|
|
||||||
struct notcurses* nc; // notcurses object of which we are a part
|
|
||||||
} ncplane;
|
|
||||||
|
|
||||||
typedef struct ncstats {
|
typedef struct ncstats {
|
||||||
uint64_t renders; // number of notcurses_render() runs
|
uint64_t renders; // number of notcurses_render() runs
|
||||||
uint64_t renders_ns; // number of nanoseconds spent in notcurses_render()
|
uint64_t renders_ns; // number of nanoseconds spent in notcurses_render()
|
||||||
@ -1445,8 +1416,12 @@ void ncplane_move_yx(ncplane* n, int y, int x){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ncplane_yx(const ncplane* n, int* y, int* x){
|
void ncplane_yx(const ncplane* n, int* y, int* x){
|
||||||
*y = n->absy;
|
if(y){
|
||||||
*x = n->absx;
|
*y = n->absy;
|
||||||
|
}
|
||||||
|
if(x){
|
||||||
|
*x = n->absx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the UTF8-encoded EGC out of the cell, whether simple or complex. the
|
// copy the UTF8-encoded EGC out of the cell, whether simple or complex. the
|
||||||
|
816
src/lib/panelreel.c
Normal file
816
src/lib/panelreel.c
Normal file
@ -0,0 +1,816 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "notcurses.h"
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
// Tablets are the toplevel entitites within a panelreel. Each corresponds to
|
||||||
|
// a single, distinct ncplane.
|
||||||
|
typedef struct tablet {
|
||||||
|
ncplane* p; // visible panel, NULL when offscreen
|
||||||
|
struct tablet* next;
|
||||||
|
struct tablet* prev;
|
||||||
|
tabletcb cbfxn; // application callback to draw tablet
|
||||||
|
void* curry; // application data provided to cbfxn
|
||||||
|
} tablet;
|
||||||
|
|
||||||
|
// The visible screen can be reconstructed from three 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)
|
||||||
|
typedef struct panelreel {
|
||||||
|
ncplane* p; // ncplane this panelreel occupies, under tablets
|
||||||
|
panelreel_options popts; // copied in panelreel_create()
|
||||||
|
// 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).
|
||||||
|
int efd; // eventfd, signaled in panelreel_touch() if >= 0
|
||||||
|
tablet* tablets;
|
||||||
|
// these values could all be derived at any time, but keeping them computed
|
||||||
|
// makes other things easier, or saves us time (at the cost of complexity).
|
||||||
|
int tabletcount; // could be derived, but we keep it o(1)
|
||||||
|
// last direction in which we moved. positive if we moved down ("next"),
|
||||||
|
// negative if we moved up ("prev"), 0 for non-linear operation. we start
|
||||||
|
// 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;
|
||||||
|
} panelreel;
|
||||||
|
|
||||||
|
// Returns the starting coordinates (relative to the screen) of the specified
|
||||||
|
// window, and its length. End is (begx + lenx - 1, begy + leny - 1).
|
||||||
|
static inline void
|
||||||
|
window_coordinates(const ncplane* w, int* begy, int* begx, int* leny, int* lenx){
|
||||||
|
ncplane_yx(w, begy, begx);
|
||||||
|
ncplane_dim_yx(w, leny, lenx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME compatability wrapper for libpanel
|
||||||
|
int wresize(ncplane* n, int leny, int lenx){
|
||||||
|
int y, x;
|
||||||
|
ncplane_yx(n, &y, &x);
|
||||||
|
int dimy, dimx;
|
||||||
|
ncplane_dim_yx(n, &dimy, &dimx);
|
||||||
|
return ncplane_resize(n, 0, 0, dimy, dimx, y, x, leny, lenx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bchrs: 6-element array of wide border characters + attributes FIXME
|
||||||
|
static int
|
||||||
|
draw_borders(ncplane* w, unsigned nobordermask, const cell* attr,
|
||||||
|
bool cliphead, bool clipfoot){
|
||||||
|
int begx, begy, 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;
|
||||||
|
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(ncplane_rounded_box_cells(w, &ul, &ur, &ll, &lr, &hl, &vl)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "drawing borders %d/%d->%d/%d, mask: %04x, clipping: %c%c\n",
|
||||||
|
begx, begy, maxx, maxy, nobordermask,
|
||||||
|
cliphead ? 'T' : 't', clipfoot ? 'F' : 'f');
|
||||||
|
ul.attrword = attr->attrword; ul.channels = attr->channels;
|
||||||
|
ur.attrword = attr->attrword; ur.channels = attr->channels;
|
||||||
|
ll.attrword = attr->attrword; ll.channels = attr->channels;
|
||||||
|
lr.attrword = attr->attrword; lr.channels = attr->channels;
|
||||||
|
hl.attrword = attr->attrword; hl.channels = attr->channels;
|
||||||
|
vl.attrword = attr->attrword; vl.channels = attr->channels;
|
||||||
|
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.
|
||||||
|
if(!(nobordermask & BORDERMASK_TOP)){
|
||||||
|
ret |= ncplane_cursor_move_yx(w, begy, begx);
|
||||||
|
ncplane_putc(w, &ul);
|
||||||
|
ncplane_hline(w, &hl, lenx - 2);
|
||||||
|
ncplane_putc(w, &ur);
|
||||||
|
}else{
|
||||||
|
if(!(nobordermask & BORDERMASK_LEFT)){
|
||||||
|
ret |= ncplane_cursor_move_yx(w, begy, begx);
|
||||||
|
ncplane_putc(w, &ul);
|
||||||
|
}
|
||||||
|
if(!(nobordermask & BORDERMASK_RIGHT)){
|
||||||
|
ret |= ncplane_cursor_move_yx(w, begy, maxx);
|
||||||
|
ncplane_putc(w, &ur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int y;
|
||||||
|
for(y = begy + !cliphead ; y < maxy + !!clipfoot ; ++y){
|
||||||
|
if(!(nobordermask & BORDERMASK_LEFT)){
|
||||||
|
ret |= ncplane_cursor_move_yx(w, y, begx);
|
||||||
|
ncplane_putc(w, &vl);
|
||||||
|
}
|
||||||
|
if(!(nobordermask & BORDERMASK_RIGHT)){
|
||||||
|
ret |= ncplane_cursor_move_yx(w, y, maxx);
|
||||||
|
ncplane_putc(w, &vl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!clipfoot){
|
||||||
|
if(!(nobordermask & BORDERMASK_BOTTOM)){
|
||||||
|
ret |= ncplane_cursor_move_yx(w, maxy, begx);
|
||||||
|
ncplane_putc(w, &ll);
|
||||||
|
ncplane_hline(w, &hl, lenx - 2);
|
||||||
|
ncplane_putc(w, &lr);
|
||||||
|
}else{
|
||||||
|
if(!(nobordermask & BORDERMASK_LEFT)){
|
||||||
|
ret |= ncplane_cursor_move_yx(w, maxy, begx);
|
||||||
|
ret |= ncplane_putc(w, &ll);
|
||||||
|
}
|
||||||
|
if(!(nobordermask & BORDERMASK_RIGHT)){
|
||||||
|
// mvwadd_wch returns error if we print to the lowermost+rightmost
|
||||||
|
// character cell. maybe we can make this go away with scrolling controls
|
||||||
|
// at setup? until then, don't check for error here FIXME.
|
||||||
|
ret |= ncplane_cursor_move_yx(w, maxy, maxx);
|
||||||
|
ret |= ncplane_putc(w, &lr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draws the border (if one should be drawn) around the panelreel, and enforces
|
||||||
|
// any provided restrictions on visible window size.
|
||||||
|
static int
|
||||||
|
draw_panelreel_borders(const panelreel* pr){
|
||||||
|
int begx, begy;
|
||||||
|
int maxx, maxy;
|
||||||
|
window_coordinates(pr->p, &begy, &begx, &maxy, &maxx);
|
||||||
|
assert(begy >= 0 && begx >= 0);
|
||||||
|
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 < pr->popts.min_supported_rows){
|
||||||
|
return 0; // no room
|
||||||
|
}
|
||||||
|
if(begy >= maxy || maxy - begy + 1 < pr->popts.min_supported_cols){
|
||||||
|
return 0; // no room
|
||||||
|
}
|
||||||
|
return draw_borders(pr->p, pr->popts.bordermask, &pr->popts.borderattr, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the starting and ending coordinates available for occupation by
|
||||||
|
// the tablet, relative to the panelreel's ncplane. Returns non-zero if the
|
||||||
|
// tablet cannot be made visible as specified. If this is the focused tablet
|
||||||
|
// (direction == 0), it can take the entire reel -- frontiery is only a
|
||||||
|
// suggestion in this case -- so give it the full breadth.
|
||||||
|
static int
|
||||||
|
tablet_columns(const panelreel* pr, int* begx, int* begy, int* lenx, int* leny,
|
||||||
|
int frontiery, int direction){
|
||||||
|
window_coordinates(pr->p, begy, begx, leny, lenx);
|
||||||
|
int maxy = *leny + *begy - 1;
|
||||||
|
int begindraw = *begy + !(pr->popts.bordermask & BORDERMASK_TOP);
|
||||||
|
// FIXME i think this fails to account for an absent panelreel bottom?
|
||||||
|
int enddraw = maxy - !(pr->popts.bordermask & BORDERMASK_TOP);
|
||||||
|
if(direction){
|
||||||
|
if(frontiery < begindraw){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(frontiery > enddraw){
|
||||||
|
// fprintf(stderr, "FRONTIER: %d ENDDRAW: %d\n", frontiery, enddraw);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// account for the panelreel borders
|
||||||
|
if(direction <= 0 && !(pr->popts.bordermask & BORDERMASK_TOP)){
|
||||||
|
++*begy;
|
||||||
|
--*leny;
|
||||||
|
}
|
||||||
|
if(direction >= 0 && !(pr->popts.bordermask & BORDERMASK_BOTTOM)){
|
||||||
|
--*leny;
|
||||||
|
}
|
||||||
|
if(!(pr->popts.bordermask & BORDERMASK_LEFT)){
|
||||||
|
++*begx;
|
||||||
|
--*lenx;
|
||||||
|
}
|
||||||
|
if(!(pr->popts.bordermask & BORDERMASK_RIGHT)){
|
||||||
|
--*lenx;
|
||||||
|
}
|
||||||
|
// at this point, our coordinates describe the largest possible tablet for
|
||||||
|
// this panelreel. this is the correct solution for the focused tablet. other
|
||||||
|
// tablets can only grow in one of two directions, so tighten them up.
|
||||||
|
if(direction > 0){
|
||||||
|
*leny -= (frontiery - *begy);
|
||||||
|
*begy = frontiery;
|
||||||
|
}else if(direction < 0){
|
||||||
|
*leny = frontiery - *begy + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the specified tablet, if possible. A direction less than 0 means we're
|
||||||
|
// laying out towards the top. Greater than zero means towards the bottom. 0
|
||||||
|
// means this is the focused tablet, always the first one to be drawn.
|
||||||
|
// frontiery is the line on which we're placing the tablet (in the case of the
|
||||||
|
// focused window, this is only an ideal, subject to change). For direction
|
||||||
|
// greater than or equal to 0, it's the top line of the tablet. For direction
|
||||||
|
// less than 0, it's the bottom line. Gives the tablet all possible space to
|
||||||
|
// work with (i.e. up to the edge we're approaching, or the entire panel for
|
||||||
|
// the focused tablet). If the callback uses less space, shrinks the panel back
|
||||||
|
// down before displaying it. Destroys any panel if it ought be hidden.
|
||||||
|
// Returns 0 if the tablet was able to be wholly rendered, non-zero otherwise.
|
||||||
|
static int
|
||||||
|
panelreel_draw_tablet(const panelreel* pr, tablet* t, int frontiery,
|
||||||
|
int direction){
|
||||||
|
int lenx, leny, begy, begx;
|
||||||
|
ncplane* fp = t->p;
|
||||||
|
if(tablet_columns(pr, &begx, &begy, &lenx, &leny, frontiery, direction)){
|
||||||
|
//fprintf(stderr, "no room: %p:%p base %d/%d len %d/%d\n", t, fp, begx, begy, lenx, leny);
|
||||||
|
// fprintf(stderr, "FRONTIER DONE!!!!!!\n");
|
||||||
|
if(fp){
|
||||||
|
// fprintf(stderr, "HIDING %p at frontier %d (dir %d) with %d\n", t, frontiery, direction, leny);
|
||||||
|
ncplane_destroy(fp);
|
||||||
|
t->p = NULL;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// 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 = notcurses_newplane(pr->p->nc, leny + 1, lenx, begy, begx, NULL);
|
||||||
|
if((fp = t->p) == NULL){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
int trueby, truebx;
|
||||||
|
ncplane_yx(fp, &trueby, &truebx);
|
||||||
|
int truey, truex;
|
||||||
|
ncplane_dim_yx(fp, &truey, &truex);
|
||||||
|
if(truey != leny){
|
||||||
|
// fprintf(stderr, "RESIZE TRUEY: %d BEGY: %d LENY: %d\n", truey, begy, leny);
|
||||||
|
if(wresize(fp, leny, truex)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
truey = leny;
|
||||||
|
}
|
||||||
|
if(begy != trueby){
|
||||||
|
ncplane_move_yx(fp, begy, begx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(wresize(fp, leny, lenx)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
bool cliphead = false;
|
||||||
|
bool clipfoot = false;
|
||||||
|
// We pass the coordinates in which the callback may freely write. That's
|
||||||
|
// the full width (minus tablet borders), and the full range of open space
|
||||||
|
// in the direction we're moving. We're not passing *lenghts* to the callback,
|
||||||
|
// but *coordinates* within the window--everywhere save tabletborders.
|
||||||
|
int cby = 0, cbx = 0, cbmaxy = leny, cbmaxx = lenx;
|
||||||
|
--cbmaxy;
|
||||||
|
--cbmaxx;
|
||||||
|
// If we're drawing up, we'll always have a bottom border unless it's masked
|
||||||
|
if(direction < 0 && !(pr->popts.tabletmask & BORDERMASK_BOTTOM)){
|
||||||
|
--cbmaxy;
|
||||||
|
}
|
||||||
|
// If we're drawing down, we'll always have a top border unless it's masked
|
||||||
|
if(direction >= 0 && !(pr->popts.tabletmask & BORDERMASK_TOP)){
|
||||||
|
++cby;
|
||||||
|
}
|
||||||
|
// Adjust the x-bounds for side borders, which we always have if unmasked
|
||||||
|
cbmaxx -= !(pr->popts.tabletmask & BORDERMASK_RIGHT);
|
||||||
|
cbx += !(pr->popts.tabletmask & BORDERMASK_LEFT);
|
||||||
|
bool cbdir = direction < 0 ? true : false;
|
||||||
|
// fprintf(stderr, "calling! lenx/leny: %d/%d cbx/cby: %d/%d cbmaxx/cbmaxy: %d/%d dir: %d\n",
|
||||||
|
// lenx, leny, cbx, cby, cbmaxx, cbmaxy, direction);
|
||||||
|
int ll = t->cbfxn(fp, cbx, cby, cbmaxx, cbmaxy, cbdir, t->curry);
|
||||||
|
//fprintf(stderr, "RETURNRETURNRETURN %p %d (%d, %d, %d) DIR %d\n",
|
||||||
|
// t, ll, cby, cbmaxy, leny, direction);
|
||||||
|
if(ll != leny){
|
||||||
|
if(ll == leny - 1){ // only has one border visible (partially off-screen)
|
||||||
|
++ll; // account for that border
|
||||||
|
wresize(fp, ll, lenx);
|
||||||
|
if(direction < 0){
|
||||||
|
cliphead = true;
|
||||||
|
ncplane_move_yx(fp, begy + leny - ll, begx);
|
||||||
|
// fprintf(stderr, "MOVEDOWN CLIPPED RESIZED (-1) from %d to %d\n", leny, ll);
|
||||||
|
}else{
|
||||||
|
clipfoot = true;
|
||||||
|
// fprintf(stderr, "RESIZED (-1) from %d to %d\n", leny, ll);
|
||||||
|
}
|
||||||
|
}else{ // both borders are visible
|
||||||
|
ll += 2; // account for both borders
|
||||||
|
// fprintf(stderr, "RESIZING (-2) from %d to %d\n", leny, ll);
|
||||||
|
wresize(fp, ll, lenx);
|
||||||
|
if(direction < 0){
|
||||||
|
// fprintf(stderr, "MOVEDOWN UNCLIPPED (skip %d)\n", leny - ll);
|
||||||
|
ncplane_move_yx(fp, begy + leny - ll, begx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The focused tablet will have been resized properly above, but it might
|
||||||
|
// be out of position (the focused tablet ought move as little as possible).
|
||||||
|
// Move it back to the frontier, or the nearest line above if it has grown.
|
||||||
|
if(direction == 0){
|
||||||
|
if(leny - frontiery + 1 < ll){
|
||||||
|
//fprintf(stderr, "frontieryIZING ADJ %d %d %d %d NEW %d\n", cbmaxy, leny,
|
||||||
|
// frontiery, ll, frontiery - ll + 1);
|
||||||
|
int dontcarex;
|
||||||
|
ncplane_yx(pr->p, &frontiery, &dontcarex);
|
||||||
|
frontiery += (leny - ll + 1);
|
||||||
|
}
|
||||||
|
ncplane_move_yx(fp, frontiery, begx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw_borders(fp, pr->popts.tabletmask,
|
||||||
|
direction == 0 ? &pr->popts.focusedattr : &pr->popts.tabletattr,
|
||||||
|
cliphead, clipfoot);
|
||||||
|
return cliphead || clipfoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw and size the focused tablet, which must exist (pr->tablets may not be
|
||||||
|
// NULL). it can occupy the entire panelreel.
|
||||||
|
static int
|
||||||
|
draw_focused_tablet(const panelreel* pr){
|
||||||
|
int pbegy, pbegx, plenx, pleny; // panelreel window coordinates
|
||||||
|
window_coordinates(pr->p, &pbegy, &pbegx, &pleny, &plenx);
|
||||||
|
int fulcrum;
|
||||||
|
if(pr->tablets->p == NULL){
|
||||||
|
if(pr->last_traveled_direction >= 0){
|
||||||
|
fulcrum = pleny + pbegy - !(pr->popts.bordermask & BORDERMASK_BOTTOM);
|
||||||
|
}else{
|
||||||
|
fulcrum = pbegy + !(pr->popts.bordermask & BORDERMASK_TOP);
|
||||||
|
}
|
||||||
|
}else{ // focused was already present. want to stay where we are, if possible
|
||||||
|
int dontcarex;
|
||||||
|
ncplane_yx(pr->tablets->p, &fulcrum, &dontcarex);
|
||||||
|
// FIXME ugh can't we just remember the previous fulcrum?
|
||||||
|
int prevfulcrum;
|
||||||
|
ncplane_yx(pr->tablets->prev->p, &prevfulcrum, &dontcarex);
|
||||||
|
int nextfulcrum;
|
||||||
|
ncplane_yx(pr->tablets->next->p, &nextfulcrum, &dontcarex);
|
||||||
|
if(pr->last_traveled_direction > 0 && fulcrum < prevfulcrum){
|
||||||
|
fulcrum = pleny + pbegy - !(pr->popts.bordermask & BORDERMASK_BOTTOM);
|
||||||
|
}else if(pr->last_traveled_direction < 0 && fulcrum > nextfulcrum){
|
||||||
|
fulcrum = pbegy + !(pr->popts.bordermask & BORDERMASK_TOP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//fprintf(stderr, "PR dims: %d/%d + %d/%d fulcrum: %d\n", pbegy, pbegx, pleny, plenx, fulcrum);
|
||||||
|
panelreel_draw_tablet(pr, pr->tablets, fulcrum, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move down below the focused tablet, filling up the reel to the bottom.
|
||||||
|
// returns the last tablet drawn.
|
||||||
|
static tablet*
|
||||||
|
draw_following_tablets(const panelreel* pr, const tablet* otherend){
|
||||||
|
int wmaxy, wbegy, wbegx, wlenx, wleny; // working tablet window coordinates
|
||||||
|
tablet* working = pr->tablets;
|
||||||
|
int frontiery;
|
||||||
|
// move down past the focused tablet, filling up the reel to the bottom
|
||||||
|
while(working->next != otherend || otherend->p == NULL){
|
||||||
|
window_coordinates(working->p, &wbegy, &wbegx, &wleny, &wlenx);
|
||||||
|
wmaxy = wbegy + wleny - 1;
|
||||||
|
frontiery = wmaxy + 2;
|
||||||
|
//fprintf(stderr, "EASTBOUND AND DOWN: %d %d\n", frontiery, wmaxy + 2);
|
||||||
|
working = working->next;
|
||||||
|
panelreel_draw_tablet(pr, working, frontiery, 1);
|
||||||
|
if(working->p == NULL){ // FIXME might be more to hide
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FIXME keep going forward, hiding those no longer visible
|
||||||
|
return working;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move up above the focused tablet, filling up the reel to the top.
|
||||||
|
// returns the last tablet drawn.
|
||||||
|
static tablet*
|
||||||
|
draw_previous_tablets(const panelreel* pr, const tablet* otherend){
|
||||||
|
int wbegy, wbegx, wlenx, wleny; // working tablet window coordinates
|
||||||
|
tablet* upworking = pr->tablets;
|
||||||
|
int frontiery;
|
||||||
|
while(upworking->prev != otherend || otherend->p == NULL){
|
||||||
|
window_coordinates(upworking->p, &wbegy, &wbegx, &wleny, &wlenx);
|
||||||
|
//fprintf(stderr, "MOVIN' ON UP: %d %d\n", frontiery, wbegy - 2);
|
||||||
|
frontiery = wbegy - 2;
|
||||||
|
upworking = upworking->prev;
|
||||||
|
panelreel_draw_tablet(pr, upworking, frontiery, -1);
|
||||||
|
if(upworking->p){
|
||||||
|
window_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;
|
||||||
|
}
|
||||||
|
if(upworking == otherend){
|
||||||
|
otherend = otherend->prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FIXME keep going backwards, hiding those no longer visible
|
||||||
|
return upworking;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all tablets must be visible (valid ->p), and at least one tablet must exist
|
||||||
|
static tablet*
|
||||||
|
find_topmost(panelreel* pr){
|
||||||
|
tablet* t = pr->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.
|
||||||
|
static int
|
||||||
|
panelreel_arrange_denormalized(panelreel* pr){
|
||||||
|
// 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(pr->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!
|
||||||
|
tablet* topmost = find_topmost(pr);
|
||||||
|
int wbegy, wbegx, wleny, wlenx;
|
||||||
|
window_coordinates(pr->p, &wbegy, &wbegx, &wleny, &wlenx);
|
||||||
|
int frontiery = wbegy + !(pr->popts.bordermask & BORDERMASK_TOP);
|
||||||
|
if(pr->last_traveled_direction >= 0){
|
||||||
|
ncplane_yx(pr->tablets->prev->p, &fromline, NULL);
|
||||||
|
if(fromline > nowline){ // keep the order we had
|
||||||
|
topmost = topmost->next;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
ncplane_yx(pr->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);
|
||||||
|
tablet* t = topmost;
|
||||||
|
do{
|
||||||
|
int broken;
|
||||||
|
if(t == pr->tablets){
|
||||||
|
broken = panelreel_draw_tablet(pr, t, frontiery, 0);
|
||||||
|
}else{
|
||||||
|
broken = panelreel_draw_tablet(pr, t, frontiery, 1);
|
||||||
|
}
|
||||||
|
if(t->p == NULL || broken){
|
||||||
|
pr->all_visible = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int dontcarex, basey;
|
||||||
|
ncplane_dim_yx(t->p, &frontiery, &dontcarex);
|
||||||
|
ncplane_yx(t->p, &basey, &dontcarex);
|
||||||
|
frontiery += basey + 1;
|
||||||
|
}while((t = t->next) != topmost);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// the reel above and below. If we moved down to get here, do the tablets above
|
||||||
|
// first. If we moved up, do the tablets below. This ensures tablets stay in
|
||||||
|
// place relative to the new focus; they could otherwise pivot around the new
|
||||||
|
// focus, if we're not filling out the reel.
|
||||||
|
//
|
||||||
|
// This can still leave a gap plus a partially-onscreen tablet FIXME
|
||||||
|
static int
|
||||||
|
panelreel_arrange(panelreel* pr){
|
||||||
|
tablet* focused = pr->tablets;
|
||||||
|
if(focused == NULL){
|
||||||
|
return 0; // if none are focused, none exist
|
||||||
|
}
|
||||||
|
// 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(pr->all_visible){
|
||||||
|
return panelreel_arrange_denormalized(pr);
|
||||||
|
}
|
||||||
|
draw_focused_tablet(pr);
|
||||||
|
tablet* otherend = focused;
|
||||||
|
if(pr->last_traveled_direction >= 0){
|
||||||
|
otherend = draw_previous_tablets(pr, otherend);
|
||||||
|
otherend = draw_following_tablets(pr, otherend);
|
||||||
|
}else{
|
||||||
|
otherend = draw_following_tablets(pr, otherend);
|
||||||
|
otherend = draw_previous_tablets(pr, otherend);
|
||||||
|
}
|
||||||
|
// FIXME move them up to plug any holes in original direction?
|
||||||
|
fprintf(stderr, "DONE ARRANGING\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelreel_redraw(panelreel* pr){
|
||||||
|
fprintf(stderr, "--------> BEGIN REDRAW <--------\n");
|
||||||
|
int ret = 0;
|
||||||
|
if(draw_panelreel_borders(pr)){
|
||||||
|
return -1; // enforces specified dimensional minima
|
||||||
|
}
|
||||||
|
ret |= panelreel_arrange(pr);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
validate_panelreel_opts(ncplane* w, const panelreel_options* popts){
|
||||||
|
if(w == NULL){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!popts->infinitescroll){
|
||||||
|
if(popts->circular){
|
||||||
|
return false; // can't set circular without infinitescroll
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const unsigned fullmask = BORDERMASK_LEFT |
|
||||||
|
BORDERMASK_RIGHT |
|
||||||
|
BORDERMASK_TOP |
|
||||||
|
BORDERMASK_BOTTOM;
|
||||||
|
if(popts->bordermask > fullmask){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(popts->tabletmask > fullmask){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
panelreel* panelreel_create(ncplane* w, const panelreel_options* popts, int efd){
|
||||||
|
panelreel* pr;
|
||||||
|
|
||||||
|
if(!validate_panelreel_opts(w, popts)){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if((pr = malloc(sizeof(*pr))) == NULL){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pr->efd = efd;
|
||||||
|
pr->tablets = NULL;
|
||||||
|
pr->tabletcount = 0;
|
||||||
|
pr->all_visible = true;
|
||||||
|
pr->last_traveled_direction = -1; // draw down after the initial tablet
|
||||||
|
memcpy(&pr->popts, popts, sizeof(*popts));
|
||||||
|
int maxx, maxy, wx, wy;
|
||||||
|
window_coordinates(w, &wy, &wx, &maxy, &maxx);
|
||||||
|
--maxy;
|
||||||
|
--maxx;
|
||||||
|
int ylen, xlen;
|
||||||
|
ylen = maxy - popts->boff - popts->toff + 1;
|
||||||
|
if(ylen < 0){
|
||||||
|
ylen = maxy - popts->toff;
|
||||||
|
if(ylen < 0){
|
||||||
|
ylen = 0; // but this translates to a full-screen window...FIXME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xlen = maxx - popts->roff - popts->loff + 1;
|
||||||
|
if(xlen < 0){
|
||||||
|
xlen = maxx - popts->loff;
|
||||||
|
if(xlen < 0){
|
||||||
|
xlen = 0; // FIXME see above...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((pr->p = notcurses_newplane(w->nc, ylen, xlen, popts->toff + wy, popts->loff + wx, NULL)) == NULL){
|
||||||
|
free(pr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(panelreel_redraw(pr)){
|
||||||
|
ncplane_destroy(pr->p);
|
||||||
|
free(pr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we've just added a new tablet. it need be inserted at the correct place in
|
||||||
|
// the reel. this will naturally fall out of things if the panelreel is full; we
|
||||||
|
// can just call panelreel_redraw(). otherwise, we need make ourselves at least
|
||||||
|
// minimally visible, to satisfy the preconditions of
|
||||||
|
// panelreel_arrange_denormalized(). this function, and approach, is shit.
|
||||||
|
// FIXME get rid of nc param here
|
||||||
|
static tablet*
|
||||||
|
insert_new_panel(struct notcurses* nc, panelreel* pr, tablet* t){
|
||||||
|
if(!pr->all_visible){
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
int wbegy, wbegx, wleny, wlenx; // params of PR
|
||||||
|
window_coordinates(pr->p, &wbegy, &wbegx, &wleny, &wlenx);
|
||||||
|
// are we the only tablet?
|
||||||
|
int begx, begy, lenx, leny, frontiery;
|
||||||
|
if(t->prev == t){
|
||||||
|
frontiery = wbegy + !(pr->popts.bordermask & BORDERMASK_TOP);
|
||||||
|
if(tablet_columns(pr, &begx, &begy, &lenx, &leny, frontiery, 1)){
|
||||||
|
pr->all_visible = false;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "newwin: %d/%d + %d/%d\n", begy, begx, leny, lenx);
|
||||||
|
if((t->p = notcurses_newplane(nc, leny, lenx, begy, begx, NULL)) == NULL){
|
||||||
|
pr->all_visible = false;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "created first tablet!\n");
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// we're not the only tablet, alas.
|
||||||
|
// our new window needs to be after our prev
|
||||||
|
int dontcarex;
|
||||||
|
ncplane_yx(t->prev->p, &frontiery, &dontcarex);
|
||||||
|
int dimprevy, dimprevx;
|
||||||
|
ncplane_dim_yx(t->prev->p, &dimprevy, &dimprevx);
|
||||||
|
frontiery += dimprevy + 2;
|
||||||
|
frontiery += 2;
|
||||||
|
if(tablet_columns(pr, &begx, &begy, &lenx, &leny, frontiery, 1)){
|
||||||
|
pr->all_visible = false;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
if((t->p = notcurses_newplane(nc, 2, lenx, begy, begx, NULL)) == NULL){
|
||||||
|
pr->all_visible = false;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
// FIXME push the other ones down by 4
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
tablet* panelreel_add(panelreel* pr, tablet* after, tablet *before,
|
||||||
|
tabletcb cbfxn, void* opaque){
|
||||||
|
tablet* t;
|
||||||
|
if(after && before){
|
||||||
|
if(after->prev != before || before->next != after){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}else if(!after && !before){
|
||||||
|
// This way, without user interaction or any specification, new tablets are
|
||||||
|
// inserted at the "end" relative to the focus. The first one to be added
|
||||||
|
// gets and keeps the focus. New ones will go on the bottom, until we run
|
||||||
|
// out of space. New tablets are then created off-screen.
|
||||||
|
before = pr->tablets;
|
||||||
|
}
|
||||||
|
if((t = malloc(sizeof(*t))) == NULL){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "--------->NEW TABLET %p\n", t);
|
||||||
|
if(after){
|
||||||
|
t->next = after->next;
|
||||||
|
after->next = t;
|
||||||
|
t->prev = after;
|
||||||
|
t->next->prev = t;
|
||||||
|
}else if(before){
|
||||||
|
t->prev = before->prev;
|
||||||
|
before->prev = t;
|
||||||
|
t->next = before;
|
||||||
|
t->prev->next = t;
|
||||||
|
}else{ // we're the first tablet
|
||||||
|
t->prev = t->next = t;
|
||||||
|
pr->tablets = t;
|
||||||
|
}
|
||||||
|
t->cbfxn = cbfxn;
|
||||||
|
t->curry = opaque;
|
||||||
|
++pr->tabletcount;
|
||||||
|
t->p = NULL;
|
||||||
|
// if we have room, it needs become visible immediately, in the proper place,
|
||||||
|
// lest we invalidate the preconditions of panelreel_arrange_denormalized().
|
||||||
|
insert_new_panel(pr->p->nc, pr, t);
|
||||||
|
panelreel_redraw(pr); // don't return failure; tablet was still created...
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelreel_del_focused(panelreel* pr){
|
||||||
|
return panelreel_del(pr, pr->tablets);
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelreel_del(panelreel* pr, struct tablet* t){
|
||||||
|
if(pr == NULL || t == NULL){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
t->prev->next = t->next;
|
||||||
|
if(pr->tablets == t){
|
||||||
|
if((pr->tablets = t->next) == t){
|
||||||
|
pr->tablets = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t->next->prev = t->prev;
|
||||||
|
if(t->p){
|
||||||
|
ncplane_destroy(t->p);
|
||||||
|
}
|
||||||
|
free(t);
|
||||||
|
--pr->tabletcount;
|
||||||
|
panelreel_redraw(pr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelreel_destroy(panelreel* preel){
|
||||||
|
int ret = 0;
|
||||||
|
if(preel){
|
||||||
|
tablet* t = preel->tablets;
|
||||||
|
while(t){
|
||||||
|
t->prev->next = NULL;
|
||||||
|
tablet* tmp = t->next;
|
||||||
|
panelreel_del(preel, t);
|
||||||
|
t = tmp;
|
||||||
|
}
|
||||||
|
free(preel);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelreel_tabletcount(const panelreel* preel){
|
||||||
|
return preel->tabletcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelreel_touch(panelreel* pr, tablet* t){
|
||||||
|
(void)t; // FIXME make these more granular eventually
|
||||||
|
int ret = 0;
|
||||||
|
if(pr->efd >= 0){
|
||||||
|
uint64_t val = 1;
|
||||||
|
if(write(pr->efd, &val, sizeof(val)) != sizeof(val)){
|
||||||
|
fprintf(stderr, "Error writing to eventfd %d (%s)\n",
|
||||||
|
pr->efd, strerror(errno));
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to some position relative to the current position
|
||||||
|
static int
|
||||||
|
move_tablet(ncplane* p, int deltax, int deltay){
|
||||||
|
int oldx, oldy;
|
||||||
|
ncplane_yx(p, &oldy, &oldx);
|
||||||
|
int x = oldx + deltax;
|
||||||
|
int y = oldy + deltay;
|
||||||
|
ncplane_move_yx(p, y, x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tablet* panelreel_focused(panelreel* pr){
|
||||||
|
return pr->tablets;
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelreel_move(panelreel* preel, int x, int y){
|
||||||
|
ncplane* w = preel->p;
|
||||||
|
int oldx, oldy;
|
||||||
|
ncplane_yx(w, &oldy, &oldx);
|
||||||
|
const int deltax = x - oldx;
|
||||||
|
const int deltay = y - oldy;
|
||||||
|
if(move_tablet(preel->p, deltax, deltay)){
|
||||||
|
ncplane_move_yx(preel->p, oldy, oldx);
|
||||||
|
panelreel_redraw(preel);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(preel->tablets){
|
||||||
|
tablet* t = preel->tablets;
|
||||||
|
do{
|
||||||
|
if(t->p == NULL){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
move_tablet(t->p, deltax, deltay);
|
||||||
|
}while((t = t->prev) != preel->tablets);
|
||||||
|
if(t != preel->tablets){ // don't repeat if we covered all tablets
|
||||||
|
for(t = preel->tablets->next ; t != preel->tablets ; t = t->next){
|
||||||
|
if(t->p == NULL){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
move_tablet(t->p, deltax, deltay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panelreel_redraw(preel);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tablet* panelreel_next(panelreel* pr){
|
||||||
|
if(pr->tablets){
|
||||||
|
pr->tablets = pr->tablets->next;
|
||||||
|
fprintf(stderr, "---------------> moved to next, %p to %p <----------\n",
|
||||||
|
pr->tablets->prev, pr->tablets);
|
||||||
|
pr->last_traveled_direction = 1;
|
||||||
|
}
|
||||||
|
panelreel_redraw(pr);
|
||||||
|
return pr->tablets;
|
||||||
|
}
|
||||||
|
|
||||||
|
tablet* panelreel_prev(panelreel* pr){
|
||||||
|
if(pr->tablets){
|
||||||
|
pr->tablets = pr->tablets->prev;
|
||||||
|
fprintf(stderr, "----------------> moved to prev, %p to %p <----------\n",
|
||||||
|
pr->tablets->next, pr->tablets);
|
||||||
|
pr->last_traveled_direction = -1;
|
||||||
|
}
|
||||||
|
panelreel_redraw(pr);
|
||||||
|
return pr->tablets;
|
||||||
|
}
|
233
tests/panelreel.cpp
Normal file
233
tests/panelreel.cpp
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
#include "main.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class PanelReelTest : public :: testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
setlocale(LC_ALL, nullptr);
|
||||||
|
if(getenv("TERM") == nullptr){
|
||||||
|
GTEST_SKIP();
|
||||||
|
}
|
||||||
|
notcurses_options nopts{};
|
||||||
|
nopts.inhibit_alternate_screen = true;
|
||||||
|
nopts.retain_cursor = true;
|
||||||
|
nopts.pass_through_esc = true;
|
||||||
|
nopts.outfp = stdin;
|
||||||
|
nc_ = notcurses_init(&nopts);
|
||||||
|
ASSERT_NE(nullptr, nc_);
|
||||||
|
n_ = notcurses_stdplane(nc_);
|
||||||
|
ASSERT_NE(nullptr, n_);
|
||||||
|
ASSERT_EQ(0, ncplane_cursor_move_yx(n_, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
if(nc_){
|
||||||
|
EXPECT_EQ(0, notcurses_stop(nc_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct notcurses* nc_{};
|
||||||
|
struct ncplane* n_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, InitLinear) {
|
||||||
|
panelreel_options p = { };
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, InitLinearInfinite) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.infinitescroll = true;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, InitCircular) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.infinitescroll = true;
|
||||||
|
p.circular = true;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
ASSERT_EQ(0, panelreel_destroy(pr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// circular is not allowed to be true when infinitescroll is false
|
||||||
|
TEST_F(PanelReelTest, FiniteCircleRejected) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.infinitescroll = false;
|
||||||
|
p.circular = true;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_EQ(nullptr, pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We ought be able to invoke panelreel_next() and panelreel_prev() safely,
|
||||||
|
// even if there are no tablets. They both ought return nullptr.
|
||||||
|
TEST_F(PanelReelTest, MovementWithoutTablets) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.infinitescroll = false;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
EXPECT_EQ(nullptr, panelreel_next(pr));
|
||||||
|
// EXPECT_EQ(0, panelreel_validate(n_, pr));
|
||||||
|
EXPECT_EQ(nullptr, panelreel_prev(pr));
|
||||||
|
// EXPECT_EQ(0, panelreel_validate(n_, pr));
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelcb(struct ncplane* p, int begx, int begy, int maxx, int maxy,
|
||||||
|
bool cliptop, void* curry){
|
||||||
|
EXPECT_NE(nullptr, p);
|
||||||
|
EXPECT_LT(begx, maxx);
|
||||||
|
EXPECT_LT(begy, maxy);
|
||||||
|
EXPECT_EQ(nullptr, curry);
|
||||||
|
EXPECT_FALSE(cliptop);
|
||||||
|
// FIXME verify geometry is as expected
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, OneTablet) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.infinitescroll = false;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
struct tablet* t = panelreel_add(pr, nullptr, nullptr, panelcb, nullptr);
|
||||||
|
ASSERT_NE(nullptr, t);
|
||||||
|
// EXPECT_EQ(0, panelreel_validate(n_, pr));
|
||||||
|
EXPECT_EQ(0, panelreel_del(pr, t));
|
||||||
|
// EXPECT_EQ(0, panelreel_validate(n_, pr));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, MovementWithOneTablet) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.infinitescroll = false;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
struct tablet* t = panelreel_add(pr, nullptr, nullptr, panelcb, nullptr);
|
||||||
|
ASSERT_NE(nullptr, t);
|
||||||
|
// EXPECT_EQ(0, panelreel_validate(n_, pr));
|
||||||
|
EXPECT_NE(nullptr, panelreel_next(pr));
|
||||||
|
// EXPECT_EQ(0, panelreel_validate(n_, pr));
|
||||||
|
EXPECT_NE(nullptr, panelreel_prev(pr));
|
||||||
|
// EXPECT_EQ(0, panelreel_validate(n_, pr));
|
||||||
|
EXPECT_EQ(0, panelreel_del(pr, t));
|
||||||
|
// EXPECT_EQ(0, panelreel_validate(n_, pr));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, DeleteActiveTablet) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.infinitescroll = false;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
struct tablet* t = panelreel_add(pr, nullptr, nullptr, panelcb, nullptr);
|
||||||
|
ASSERT_NE(nullptr, t);
|
||||||
|
EXPECT_EQ(0, panelreel_del_focused(pr));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, NoBorder) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.bordermask = BORDERMASK_LEFT | BORDERMASK_RIGHT |
|
||||||
|
BORDERMASK_TOP | BORDERMASK_BOTTOM;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, BadBorderBitsRejected) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.bordermask = BORDERMASK_LEFT * 2;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_EQ(nullptr, pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, NoTabletBorder) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.tabletmask = BORDERMASK_LEFT | BORDERMASK_RIGHT |
|
||||||
|
BORDERMASK_TOP | BORDERMASK_BOTTOM;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, BadTabletBorderBitsRejected) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.tabletmask = BORDERMASK_LEFT * 2;
|
||||||
|
struct panelreel* pr = panelreel_create(n_, &p, -1);
|
||||||
|
ASSERT_EQ(nullptr, pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Make a target window occupying all but a containing perimeter of the
|
||||||
|
// specified WINDOW (which will usually be n_).
|
||||||
|
struct ncpanel* make_targwin(struct ncpanel* w) {
|
||||||
|
cchar_t cc;
|
||||||
|
int cpair = COLOR_GREEN;
|
||||||
|
EXPECT_EQ(OK, setcchar(&cc, L"W", 0, 0, &cpair));
|
||||||
|
int x, y, xx, yy;
|
||||||
|
getbegyx(w, y, x);
|
||||||
|
getmaxyx(w, yy, xx);
|
||||||
|
yy -= 2;
|
||||||
|
xx -= 2;
|
||||||
|
++x;
|
||||||
|
++y;
|
||||||
|
WINDOW* ww = subwin(w, yy, xx, y, x);
|
||||||
|
EXPECT_NE(nullptr, ww);
|
||||||
|
PANEL* p = new_panel(ww);
|
||||||
|
EXPECT_NE(nullptr, p);
|
||||||
|
EXPECT_EQ(OK, wbkgrnd(ww, &cc));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, InitWithinSubwin) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.loff = 1;
|
||||||
|
p.roff = 1;
|
||||||
|
p.toff = 1;
|
||||||
|
p.boff = 1;
|
||||||
|
EXPECT_EQ(0, clear());
|
||||||
|
PANEL* base = make_targwin(n_);
|
||||||
|
ASSERT_NE(nullptr, base);
|
||||||
|
WINDOW* basew = panel_window(base);
|
||||||
|
ASSERT_NE(nullptr, basew);
|
||||||
|
struct panelreel* pr = panelreel_create(basew, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
EXPECT_EQ(0, panelreel_validate(basew, pr));
|
||||||
|
ASSERT_EQ(0, panelreel_destroy(pr));
|
||||||
|
EXPECT_EQ(OK, del_panel(base));
|
||||||
|
EXPECT_EQ(OK, delwin(basew));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, SubwinNoPanelreelBorders) {
|
||||||
|
panelreel_options p{};
|
||||||
|
p.loff = 1;
|
||||||
|
p.roff = 1;
|
||||||
|
p.toff = 1;
|
||||||
|
p.boff = 1;
|
||||||
|
p.bordermask = BORDERMASK_LEFT | BORDERMASK_RIGHT |
|
||||||
|
BORDERMASK_TOP | BORDERMASK_BOTTOM;
|
||||||
|
EXPECT_EQ(0, clear());
|
||||||
|
PANEL* base = make_targwin(n_);
|
||||||
|
ASSERT_NE(nullptr, base);
|
||||||
|
WINDOW* basew = panel_window(base);
|
||||||
|
ASSERT_NE(nullptr, basew);
|
||||||
|
struct panelreel* pr = panelreel_create(basew, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
EXPECT_EQ(0, panelreel_validate(basew, pr));
|
||||||
|
ASSERT_EQ(0, panelreel_destroy(pr));
|
||||||
|
EXPECT_EQ(OK, del_panel(base));
|
||||||
|
EXPECT_EQ(OK, delwin(basew));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PanelReelTest, SubwinNoOffsetGeom) {
|
||||||
|
panelreel_options p{};
|
||||||
|
EXPECT_EQ(0, clear());
|
||||||
|
PANEL* base = make_targwin(n_);
|
||||||
|
ASSERT_NE(nullptr, base);
|
||||||
|
WINDOW* basew = panel_window(base);
|
||||||
|
ASSERT_NE(nullptr, basew);
|
||||||
|
struct panelreel* pr = panelreel_create(basew, &p, -1);
|
||||||
|
ASSERT_NE(nullptr, pr);
|
||||||
|
EXPECT_EQ(0, panelreel_validate(basew, pr));
|
||||||
|
ASSERT_EQ(0, panelreel_destroy(pr));
|
||||||
|
EXPECT_EQ(OK, del_panel(base));
|
||||||
|
EXPECT_EQ(OK, delwin(basew));
|
||||||
|
}
|
||||||
|
*/
|
Loading…
x
Reference in New Issue
Block a user