mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
* notcurses: set up lastframe #189 * render: o(1) take no prisoners damage detection #189
This commit is contained in:
parent
8b1b2ebdf0
commit
9b81de3789
@ -6,6 +6,7 @@ notcurses-demo \- Show off some notcurses features
|
|||||||
[ \fB\-p \fIpath \fR]
|
[ \fB\-p \fIpath \fR]
|
||||||
[ \fB\-d \fIdelaymult \fR]
|
[ \fB\-d \fIdelaymult \fR]
|
||||||
[ \fB\-k \fR]
|
[ \fB\-k \fR]
|
||||||
|
[ \fB\-c \fR]
|
||||||
[ \fB\-h / \fB\-\-help \fR]
|
[ \fB\-h / \fB\-\-help \fR]
|
||||||
.IR demospec
|
.IR demospec
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
@ -20,6 +21,9 @@ Apply a (floating-point) multiplier to the standard delay of 1s.
|
|||||||
Inhibit use of the alternate screen. Necessary if you want the output left
|
Inhibit use of the alternate screen. Necessary if you want the output left
|
||||||
on your terminal after the program exits.
|
on your terminal after the program exits.
|
||||||
.TP
|
.TP
|
||||||
|
.BR \-c
|
||||||
|
Do not attempt to seed the PRNG. This is useful when benchmarking.
|
||||||
|
.TP
|
||||||
.BR \-h ", " \-\-help
|
.BR \-h ", " \-\-help
|
||||||
Print a usage message, and exit with success.
|
Print a usage message, and exit with success.
|
||||||
.TP
|
.TP
|
||||||
@ -45,7 +49,7 @@ contains a set of text-based demonstrations of capabilities from the notcurses l
|
|||||||
.P
|
.P
|
||||||
(s)liders—a missing-piece puzzle made up of colorful blocks
|
(s)liders—a missing-piece puzzle made up of colorful blocks
|
||||||
.P
|
.P
|
||||||
(b)leachworm—a great Nothing slowly robs the world of color
|
(w)hiteworm—a great Nothing slowly robs the world of color
|
||||||
.P
|
.P
|
||||||
(v)iew—images and a video are rendered as text
|
(v)iew—images and a video are rendered as text
|
||||||
.P
|
.P
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
#include <notcurses.h>
|
#include <notcurses.h>
|
||||||
#include "demo.h"
|
#include "demo.h"
|
||||||
|
|
||||||
|
// ansi terminal definition-4-life
|
||||||
|
static const int MIN_SUPPORTED_ROWS = 25;
|
||||||
|
static const int MIN_SUPPORTED_COLS = 80;
|
||||||
|
|
||||||
static const char DEFAULT_DEMO[] = "iemlubgswvpo";
|
static const char DEFAULT_DEMO[] = "iemlubgswvpo";
|
||||||
static char datadir[PATH_MAX] = "/usr/share/notcurses"; // FIXME
|
static char datadir[PATH_MAX] = "/usr/share/notcurses"; // FIXME
|
||||||
|
|
||||||
@ -46,11 +50,12 @@ struct timespec demodelay = {
|
|||||||
static void
|
static void
|
||||||
usage(const char* exe, int status){
|
usage(const char* exe, int status){
|
||||||
FILE* out = status == EXIT_SUCCESS ? stdout : stderr;
|
FILE* out = status == EXIT_SUCCESS ? stdout : stderr;
|
||||||
fprintf(out, "usage: %s [ -h ] [ -k ] [ -d mult ] [ -f renderfile ] demospec\n", exe);
|
fprintf(out, "usage: %s [ -h ] [ -k ] [ -d mult ] [ -c ] [ -f renderfile ] demospec\n", exe);
|
||||||
fprintf(out, " -h: this message\n");
|
fprintf(out, " -h: this message\n");
|
||||||
fprintf(out, " -k: keep screen; do not switch to alternate\n");
|
fprintf(out, " -k: keep screen; do not switch to alternate\n");
|
||||||
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, " -c: constant PRNG seed, useful for benchmarking\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, " b: run box\n");
|
fprintf(out, " b: run box\n");
|
||||||
fprintf(out, " e: run eagles\n");
|
fprintf(out, " e: run eagles\n");
|
||||||
@ -63,7 +68,7 @@ usage(const char* exe, int status){
|
|||||||
fprintf(out, " s: run shuffle\n");
|
fprintf(out, " s: run shuffle\n");
|
||||||
fprintf(out, " u: run uniblock\n");
|
fprintf(out, " u: run uniblock\n");
|
||||||
fprintf(out, " v: run view\n");
|
fprintf(out, " v: run view\n");
|
||||||
fprintf(out, " w: run bleachworm\n");
|
fprintf(out, " w: run witherworm\n");
|
||||||
exit(status);
|
exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +188,7 @@ ext_demos(struct notcurses* nc, const char* demos){
|
|||||||
case 'l': ret = luigi_demo(nc); break;
|
case 'l': ret = luigi_demo(nc); break;
|
||||||
case 'v': ret = view_demo(nc); break;
|
case 'v': ret = view_demo(nc); break;
|
||||||
case 'e': ret = eagle_demo(nc); break;
|
case 'e': ret = eagle_demo(nc); break;
|
||||||
case 'w': ret = bleachworm_demo(nc); break;
|
case 'w': ret = witherworm_demo(nc); break;
|
||||||
case 'p': ret = panelreel_demo(nc); break;
|
case 'p': ret = panelreel_demo(nc); break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Unknown demo specification: %c\n", *demos);
|
fprintf(stderr, "Unknown demo specification: %c\n", *demos);
|
||||||
@ -208,13 +213,17 @@ ext_demos(struct notcurses* nc, const char* demos){
|
|||||||
// if it's NULL, there were valid options, but no spec.
|
// if it's NULL, there were valid options, but no spec.
|
||||||
static const char*
|
static const char*
|
||||||
handle_opts(int argc, char** argv, notcurses_options* opts){
|
handle_opts(int argc, char** argv, notcurses_options* opts){
|
||||||
|
bool constant_seed = false;
|
||||||
int c;
|
int c;
|
||||||
memset(opts, 0, sizeof(*opts));
|
memset(opts, 0, sizeof(*opts));
|
||||||
while((c = getopt(argc, argv, "hkd:f:p:")) != EOF){
|
while((c = getopt(argc, argv, "hckd:f:p:")) != EOF){
|
||||||
switch(c){
|
switch(c){
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(*argv, EXIT_SUCCESS);
|
usage(*argv, EXIT_SUCCESS);
|
||||||
break;
|
break;
|
||||||
|
case 'c':
|
||||||
|
constant_seed = true;
|
||||||
|
break;
|
||||||
case 'k':
|
case 'k':
|
||||||
opts->inhibit_alternate_screen = true;
|
opts->inhibit_alternate_screen = true;
|
||||||
break;
|
break;
|
||||||
@ -244,6 +253,9 @@ handle_opts(int argc, char** argv, notcurses_options* opts){
|
|||||||
usage(*argv, EXIT_FAILURE);
|
usage(*argv, EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(!constant_seed){
|
||||||
|
srand(time(NULL)); // a classic blunder lol
|
||||||
|
}
|
||||||
const char* demos = argv[optind];
|
const char* demos = argv[optind];
|
||||||
return demos;
|
return demos;
|
||||||
}
|
}
|
||||||
@ -252,7 +264,6 @@ handle_opts(int argc, char** argv, notcurses_options* opts){
|
|||||||
int main(int argc, char** argv){
|
int main(int argc, char** argv){
|
||||||
struct notcurses* nc;
|
struct notcurses* nc;
|
||||||
notcurses_options nopts;
|
notcurses_options nopts;
|
||||||
struct ncplane* ncp;
|
|
||||||
if(!setlocale(LC_ALL, "")){
|
if(!setlocale(LC_ALL, "")){
|
||||||
fprintf(stderr, "Couldn't set locale based on user preferences\n");
|
fprintf(stderr, "Couldn't set locale based on user preferences\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@ -267,8 +278,9 @@ int main(int argc, char** argv){
|
|||||||
if((nc = notcurses_init(&nopts, stdout)) == NULL){
|
if((nc = notcurses_init(&nopts, stdout)) == NULL){
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
if((ncp = notcurses_stdplane(nc)) == NULL){
|
int dimx, dimy;
|
||||||
fprintf(stderr, "Couldn't get standard plane\n");
|
notcurses_term_dim_yx(nc, &dimy, &dimx);
|
||||||
|
if(dimy < MIN_SUPPORTED_ROWS || dimx < MIN_SUPPORTED_COLS){
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
// no one cares about the leaderscreen. 1s max.
|
// no one cares about the leaderscreen. 1s max.
|
||||||
@ -306,6 +318,10 @@ int main(int argc, char** argv){
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
notcurses_term_dim_yx(nc, &dimy, &dimx);
|
||||||
notcurses_stop(nc);
|
notcurses_stop(nc);
|
||||||
|
if(dimy < MIN_SUPPORTED_ROWS || dimx < MIN_SUPPORTED_COLS){
|
||||||
|
fprintf(stderr, "At least an 80x25 terminal is required (current: %dx%d)\n", dimx, dimy);
|
||||||
|
}
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ extern struct timespec demodelay;
|
|||||||
char* find_data(const char* datum);
|
char* find_data(const char* datum);
|
||||||
|
|
||||||
int unicodeblocks_demo(struct notcurses* nc);
|
int unicodeblocks_demo(struct notcurses* nc);
|
||||||
int bleachworm_demo(struct notcurses* nc);
|
int witherworm_demo(struct notcurses* nc);
|
||||||
int box_demo(struct notcurses* nc);
|
int box_demo(struct notcurses* nc);
|
||||||
int maxcolor_demo(struct notcurses* nc);
|
int maxcolor_demo(struct notcurses* nc);
|
||||||
int grid_demo(struct notcurses* nc);
|
int grid_demo(struct notcurses* nc);
|
||||||
|
@ -160,81 +160,112 @@ lightup_surrounding_cells(struct ncplane* n, const cell* cells, int y, int x){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct snake {
|
||||||
|
cell lightup[13];
|
||||||
|
int x, y;
|
||||||
|
uint64_t channels;
|
||||||
|
int prevx, prevy;
|
||||||
|
} snake;
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_snake(snake* s, int dimy, int dimx){
|
||||||
|
for(size_t i = 0 ; i < sizeof(s->lightup) / sizeof(*s->lightup) ; ++i){
|
||||||
|
cell_init(&s->lightup[i]);
|
||||||
|
}
|
||||||
|
// start it in the lower center of the screen
|
||||||
|
s->x = (random() % (dimx / 2)) + (dimx / 4);
|
||||||
|
s->y = (random() % (dimy * 2 / 3)) + (dimy / 3);
|
||||||
|
s->channels = 0;
|
||||||
|
channels_set_fg_rgb(&s->channels, 255, 255, 255);
|
||||||
|
channels_set_bg_rgb(&s->channels, 20, 20, 20);
|
||||||
|
s->prevx = 0;
|
||||||
|
s->prevy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
snakey_top(struct notcurses* nc, snake* s){
|
||||||
|
struct ncplane* n = notcurses_stdplane(nc);
|
||||||
|
get_surrounding_cells(n, s->lightup, s->y, s->x);
|
||||||
|
ncplane_cursor_move_yx(n, s->y, s->x);
|
||||||
|
if(lightup_surrounding_cells(n, s->lightup, s->y, s->x)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
snakey(struct notcurses* nc, snake* s, int dimy, int dimx, const struct timespec* iterdelay){
|
||||||
|
struct ncplane* n = notcurses_stdplane(nc);
|
||||||
|
int oldy, oldx;
|
||||||
|
clock_nanosleep(CLOCK_MONOTONIC, 0, iterdelay, NULL);
|
||||||
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
do{ // force a move
|
||||||
|
oldy = s->y;
|
||||||
|
oldx = s->x;
|
||||||
|
// FIXME he ought be weighted to avoid light; he's a snake after all
|
||||||
|
int direction = random() % 4;
|
||||||
|
switch(direction){
|
||||||
|
case 0: --s->y; break;
|
||||||
|
case 1: ++s->x; break;
|
||||||
|
case 2: ++s->y; break;
|
||||||
|
case 3: --s->x; break;
|
||||||
|
}
|
||||||
|
// keep him away from the sides due to width irregularities
|
||||||
|
if(s->x < (dimx / 4)){
|
||||||
|
s->x = dimx / 4;
|
||||||
|
}else if(s->x >= dimx * 3 / 4){
|
||||||
|
s->x = dimx * 3 / 4;
|
||||||
|
}
|
||||||
|
if(s->y < 0){
|
||||||
|
s->y = 0;
|
||||||
|
}else if(s->y >= dimy){
|
||||||
|
s->y = dimy - 1;
|
||||||
|
}
|
||||||
|
ncplane_cursor_move_yx(n, s->y, s->x);
|
||||||
|
ncplane_at_cursor(n, &c);
|
||||||
|
// don't allow the snake into the summary zone (test for walls)
|
||||||
|
if(wall_p(n, &c)){
|
||||||
|
s->x = oldx;
|
||||||
|
s->y = oldy;
|
||||||
|
}
|
||||||
|
}while((oldx == s->x && oldy == s->y) || (s->x == s->prevx && s->y == s->prevy));
|
||||||
|
s->prevy = oldy;
|
||||||
|
s->prevx = oldx;
|
||||||
|
cell_release(n, &c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// each snake wanders around aimlessly, prohibited from entering the summary
|
// each snake wanders around aimlessly, prohibited from entering the summary
|
||||||
// section. it ought light up the cells around it; to do this, we keep an array
|
// section. it ought light up the cells around it; to do this, we keep an array
|
||||||
// of 13 cells with the original colors, which we tune up for the duration of
|
// of 13 cells with the original colors, which we tune up for the duration of
|
||||||
// our colocality (unless they're summary area walls).
|
// our colocality (unless they're summary area walls).
|
||||||
static void *
|
static void *
|
||||||
snake_thread(void* vnc){
|
snake_thread(void* vnc){
|
||||||
|
const int snakecount = 3; // FIXME base count off area
|
||||||
struct notcurses* nc = vnc;
|
struct notcurses* nc = vnc;
|
||||||
struct ncplane* n = notcurses_stdplane(nc);
|
struct ncplane* n = notcurses_stdplane(nc);
|
||||||
cell lightup[13];
|
|
||||||
size_t i;
|
|
||||||
for(i = 0 ; i < sizeof(lightup) / sizeof(*lightup) ; ++i){
|
|
||||||
cell_init(&lightup[i]);
|
|
||||||
}
|
|
||||||
int dimy, dimx;
|
int dimy, dimx;
|
||||||
ncplane_dim_yx(n, &dimy, &dimx);
|
ncplane_dim_yx(n, &dimy, &dimx);
|
||||||
int x, y;
|
snake snakes[snakecount];
|
||||||
// start it in the lower center of the screen
|
for(int s = 0 ; s < snakecount ; ++s){
|
||||||
x = (random() % (dimx / 2)) + (dimx / 4);
|
init_snake(&snakes[s], dimy, dimx);
|
||||||
y = (random() % (dimy / 2)) + (dimy / 2);
|
}
|
||||||
cell head = CELL_TRIVIAL_INITIALIZER;
|
|
||||||
uint64_t channels = 0;
|
|
||||||
channels_set_fg_rgb(&channels, 255, 255, 255);
|
|
||||||
channels_set_bg_rgb(&channels, 20, 20, 20);
|
|
||||||
cell_prime(n, &head, "א", 0, channels);
|
|
||||||
cell c = CELL_TRIVIAL_INITIALIZER;
|
|
||||||
struct timespec iterdelay = { .tv_sec = 0, .tv_nsec = 1000000000ul / 20, };
|
struct timespec iterdelay = { .tv_sec = 0, .tv_nsec = 1000000000ul / 20, };
|
||||||
int prevx = 0, prevy = 0;
|
|
||||||
while(true){
|
while(true){
|
||||||
pthread_testcancel();
|
pthread_testcancel();
|
||||||
get_surrounding_cells(n, lightup, y, x);
|
for(int s = 0 ; s < snakecount ; ++s){
|
||||||
ncplane_cursor_move_yx(n, y, x);
|
if(snakey_top(nc, &snakes[s])){
|
||||||
ncplane_at_cursor(n, &c);
|
return NULL;
|
||||||
if(lightup_surrounding_cells(n, lightup, y, x)){
|
}
|
||||||
|
}
|
||||||
|
if(notcurses_render(nc)){
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
notcurses_render(nc);
|
for(int s = 0 ; s < snakecount ; ++s){
|
||||||
int oldy, oldx;
|
if(snakey(nc, &snakes[s], dimy, dimx, &iterdelay)){
|
||||||
clock_nanosleep(CLOCK_MONOTONIC, 0, &iterdelay, NULL);
|
return NULL;
|
||||||
do{ // force a move
|
|
||||||
oldy = y;
|
|
||||||
oldx = x;
|
|
||||||
// FIXME he ought be weighted to avoid light; he's a snake after all
|
|
||||||
int direction = random() % 4;
|
|
||||||
switch(direction){
|
|
||||||
case 0: --y; break;
|
|
||||||
case 1: ++x; break;
|
|
||||||
case 2: ++y; break;
|
|
||||||
case 3: --x; break;
|
|
||||||
}
|
}
|
||||||
// keep him away from the sides due to width irregularities
|
}
|
||||||
if(x < (dimx / 4)){
|
|
||||||
x = dimx / 4;
|
|
||||||
}else if(x >= dimx * 3 / 4){
|
|
||||||
x = dimx * 3 / 4;
|
|
||||||
}
|
|
||||||
if(y < 0){
|
|
||||||
y = 0;
|
|
||||||
}else if(y >= dimy){
|
|
||||||
y = dimy - 1;
|
|
||||||
}
|
|
||||||
ncplane_cursor_move_yx(n, y, x);
|
|
||||||
ncplane_at_cursor(n, &c);
|
|
||||||
// don't allow the snake into the summary zone (test for walls)
|
|
||||||
if(wall_p(n, &c)){
|
|
||||||
x = oldx;
|
|
||||||
y = oldy;
|
|
||||||
}
|
|
||||||
}while((oldx == x && oldy == y) || (x == prevx && y == prevy));
|
|
||||||
prevy = oldy;
|
|
||||||
prevx = oldx;
|
|
||||||
}
|
|
||||||
cell_release(n, &head); // FIXME won't be released when cancelled
|
|
||||||
cell_release(n, &c); // FIXME won't be released when cancelled
|
|
||||||
for(i = 0 ; i < sizeof(lightup) / sizeof(*lightup) ; ++i){
|
|
||||||
cell_release(n, &lightup[i]);
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -306,7 +337,7 @@ message(struct ncplane* n, int maxy, int maxx, int num, int total,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Much of this text comes from http://kermitproject.org/utf8.html
|
// Much of this text comes from http://kermitproject.org/utf8.html
|
||||||
int bleachworm_demo(struct notcurses* nc){
|
int witherworm_demo(struct notcurses* nc){
|
||||||
static const char* strs[] = {
|
static const char* strs[] = {
|
||||||
"Война и мир",
|
"Война и мир",
|
||||||
"Бра́тья Карама́зовы",
|
"Бра́тья Карама́зовы",
|
||||||
@ -537,8 +568,8 @@ int bleachworm_demo(struct notcurses* nc){
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
const char** s;
|
const char** s;
|
||||||
const int steps[] = { 0x100, 0x100, 0x40000, 0x10001, };
|
const int steps[] = { 0x10040, 0x100, 0x100, 0x10001, };
|
||||||
const int starts[] = { 0x004000, 0x000040, 0x010101, 0x400040, };
|
const int starts[] = { 0x10101, 0x004000, 0x000040, 0x400040, };
|
||||||
|
|
||||||
struct ncplane* n = notcurses_stdplane(nc);
|
struct ncplane* n = notcurses_stdplane(nc);
|
||||||
size_t i;
|
size_t i;
|
@ -230,6 +230,12 @@ egcpool_dump(egcpool* pool){
|
|||||||
pool->poolused = 0;
|
pool->poolused = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const char*
|
||||||
|
egcpool_extended_gcluster(const egcpool* pool, const cell* c){
|
||||||
|
uint32_t idx = cell_egc_idx(c);
|
||||||
|
return pool->pool + idx;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -134,7 +134,6 @@ int ncplane_fadein(ncplane* n, const struct timespec* ts){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ncplane_updamage(n);
|
|
||||||
notcurses_render(n->nc);
|
notcurses_render(n->nc);
|
||||||
uint64_t nextwake = (iter + 1) * nanosecs_step + startns;
|
uint64_t nextwake = (iter + 1) * nanosecs_step + startns;
|
||||||
struct timespec sleepspec;
|
struct timespec sleepspec;
|
||||||
@ -204,7 +203,6 @@ int ncplane_fadeout(ncplane* n, const struct timespec* ts){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ncplane_updamage(n);
|
|
||||||
cell* c = &n->defcell;
|
cell* c = &n->defcell;
|
||||||
if(!cell_fg_default_p(c)){
|
if(!cell_fg_default_p(c)){
|
||||||
channels_get_fg_rgb(pp.channels[pp.cols * y], &r, &g, &b);
|
channels_get_fg_rgb(pp.channels[pp.cols * y], &r, &g, &b);
|
||||||
|
@ -51,7 +51,6 @@ typedef struct ncplane {
|
|||||||
uint32_t attrword; // same deal as in a cell
|
uint32_t attrword; // same deal as in a cell
|
||||||
void* userptr; // slot for the user to stick some opaque pointer
|
void* userptr; // slot for the user to stick some opaque pointer
|
||||||
cell defcell; // cell written anywhere that fb[i].gcluster == 0
|
cell defcell; // cell written anywhere that fb[i].gcluster == 0
|
||||||
unsigned char* damage;// damage map, one per row
|
|
||||||
struct notcurses* nc; // notcurses object of which we are a part
|
struct notcurses* nc; // notcurses object of which we are a part
|
||||||
} ncplane;
|
} ncplane;
|
||||||
|
|
||||||
@ -80,20 +79,32 @@ typedef struct ncvisual {
|
|||||||
|
|
||||||
typedef struct notcurses {
|
typedef struct notcurses {
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
int ttyfd; // file descriptor for controlling tty, from opts->ttyfp
|
ncplane* top; // the contents of our topmost plane (initially entire screen)
|
||||||
FILE* ttyfp; // FILE* for controlling tty, from opts->ttyfp
|
ncplane* stdscr;// aliases some plane from the z-buffer, covers screen
|
||||||
FILE* ttyinfp; // FILE* for processing input
|
|
||||||
unsigned char* damage; // damage map (row granularity)
|
// we keep a copy of the last rendered frame. this facilitates O(1)
|
||||||
|
// notcurses_at_yx() and O(1) damage detection (at the cost of some memory).
|
||||||
|
cell* lastframe;// last rendered framebuffer, NULL until first render
|
||||||
|
int lfdimx; // dimensions of lastframe, unchanged by screen resize
|
||||||
|
int lfdimy; // lfdimx/lfdimy are 0 until first render
|
||||||
|
egcpool pool; // duplicate EGCs into this pool
|
||||||
|
|
||||||
|
// we assemble the encoded output in a POSIX memstream, and keep it around
|
||||||
|
// between uses. this could be a problem if it ever tremendously spiked, but
|
||||||
|
// that's a highly unlikely situation.
|
||||||
char* mstream; // buffer for rendering memstream, see open_memstream(3)
|
char* mstream; // buffer for rendering memstream, see open_memstream(3)
|
||||||
FILE* mstreamfp;// FILE* for rendering memstream
|
FILE* mstreamfp;// FILE* for rendering memstream
|
||||||
size_t mstrsize;// size of rendering memstream
|
size_t mstrsize;// size of rendering memstream
|
||||||
int colors; // number of colors usable for this screen
|
|
||||||
ncstats stats; // some statistics across the lifetime of the notcurses ctx
|
ncstats stats; // some statistics across the lifetime of the notcurses ctx
|
||||||
ncstats stashstats; // cumulative stats, unaffected by notcurses_reset_stats()
|
ncstats stashstats; // cumulative stats, unaffected by notcurses_reset_stats()
|
||||||
|
|
||||||
// We verify that some terminfo capabilities exist. These needn't be checked
|
// We verify that some terminfo capabilities exist. These needn't be checked
|
||||||
// before further use; just use tiparm() directly.
|
// before further use; just use tiparm() directly.
|
||||||
|
int colors; // number of colors terminfo reported usable for this screen
|
||||||
char* cup; // move cursor
|
char* cup; // move cursor
|
||||||
bool RGBflag; // terminfo-reported "RGB" flag for 24bpc directcolor
|
char* cuf; // move n cells right
|
||||||
|
char* cuf1; // move 1 cell right
|
||||||
char* civis; // hide cursor
|
char* civis; // hide cursor
|
||||||
// These might be NULL, and we can more or less work without them. Check!
|
// These might be NULL, and we can more or less work without them. Check!
|
||||||
char* clearscr; // erase screen and home cursor
|
char* clearscr; // erase screen and home cursor
|
||||||
@ -117,12 +128,15 @@ typedef struct notcurses {
|
|||||||
char* italoff; // CELL_STYLE_ITALIC (disable)
|
char* italoff; // CELL_STYLE_ITALIC (disable)
|
||||||
char* smkx; // enter keypad transmit mode (keypad_xmit)
|
char* smkx; // enter keypad transmit mode (keypad_xmit)
|
||||||
char* rmkx; // leave keypad transmit mode (keypad_local)
|
char* rmkx; // leave keypad transmit mode (keypad_local)
|
||||||
|
bool RGBflag; // terminfo-reported "RGB" flag for 24bpc directcolor
|
||||||
|
bool CCCflag; // terminfo-reported "CCC" flag for palette set capability
|
||||||
|
|
||||||
|
int ttyfd; // file descriptor for controlling tty, from opts->ttyfp
|
||||||
|
FILE* ttyfp; // FILE* for controlling tty, from opts->ttyfp
|
||||||
|
FILE* ttyinfp; // FILE* for processing input
|
||||||
|
FILE* renderfp; // debugging FILE* to which renderings are written
|
||||||
struct termios tpreserved; // terminal state upon entry
|
struct termios tpreserved; // terminal state upon entry
|
||||||
bool suppress_banner; // from notcurses_options
|
bool suppress_banner; // from notcurses_options
|
||||||
bool CCCflag; // terminfo-reported "CCC" flag for palette set capability
|
|
||||||
ncplane* top; // the contents of our topmost plane (initially entire screen)
|
|
||||||
ncplane* stdscr;// aliases some plane from the z-buffer, covers screen
|
|
||||||
FILE* renderfp; // debugging FILE* to which renderings are written
|
|
||||||
unsigned char inputbuf[BUFSIZ];
|
unsigned char inputbuf[BUFSIZ];
|
||||||
// we keep a wee ringbuffer of input queued up for delivery. if
|
// we keep a wee ringbuffer of input queued up for delivery. if
|
||||||
// inputbuf_occupied == sizeof(inputbuf), there is no room. otherwise, data
|
// inputbuf_occupied == sizeof(inputbuf), there is no room. otherwise, data
|
||||||
@ -161,19 +175,6 @@ fbcellidx(const ncplane* n, int row, int col){
|
|||||||
return row * n->lenx + col;
|
return row * n->lenx + col;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set all elements of a damage map true or false
|
|
||||||
static inline void
|
|
||||||
flash_damage_map(unsigned char* damage, int count, bool val){
|
|
||||||
if(val){
|
|
||||||
memset(damage, 0xff, sizeof(*damage) * count);
|
|
||||||
}else{
|
|
||||||
memset(damage, 0, sizeof(*damage) * count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark all lines of the notcurses object touched by this plane as damaged
|
|
||||||
void ncplane_updamage(ncplane* n);
|
|
||||||
|
|
||||||
// For our first attempt, O(1) uniform conversion from 8-bit r/g/b down to
|
// For our first attempt, O(1) uniform conversion from 8-bit r/g/b down to
|
||||||
// ~2.4-bit 6x6x6 cube + greyscale (assumed on entry; I know no way to
|
// ~2.4-bit 6x6x6 cube + greyscale (assumed on entry; I know no way to
|
||||||
// even semi-portably recover the palette) proceeds via: map each 8-bit to
|
// even semi-portably recover the palette) proceeds via: map each 8-bit to
|
||||||
@ -223,8 +224,7 @@ term_emit(const char* name __attribute__ ((unused)), const char* seq,
|
|||||||
|
|
||||||
static inline const char*
|
static inline const char*
|
||||||
extended_gcluster(const ncplane* n, const cell* c){
|
extended_gcluster(const ncplane* n, const cell* c){
|
||||||
uint32_t idx = cell_egc_idx(c);
|
return egcpool_extended_gcluster(&n->pool, c);
|
||||||
return n->pool.pool + idx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NANOSECS_IN_SEC 1000000000
|
#define NANOSECS_IN_SEC 1000000000
|
||||||
|
@ -354,7 +354,6 @@ int ncvisual_render(const ncvisual* ncv, int begy, int begx, int leny, int lenx)
|
|||||||
cell_release(ncv->ncp, &c);
|
cell_release(ncv->ncp, &c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flash_damage_map(ncv->ncp->damage + ncv->placey, y - ncv->placey, true);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,9 +262,7 @@ term_verify_seq(char** gseq, const char* name){
|
|||||||
static void
|
static void
|
||||||
free_plane(ncplane* p){
|
free_plane(ncplane* p){
|
||||||
if(p){
|
if(p){
|
||||||
ncplane_updamage(p);
|
|
||||||
egcpool_dump(&p->pool);
|
egcpool_dump(&p->pool);
|
||||||
free(p->damage);
|
|
||||||
free(p->fb);
|
free(p->fb);
|
||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
@ -287,13 +285,6 @@ ncplane_create(notcurses* nc, int rows, int cols, int yoff, int xoff){
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memset(p->fb, 0, fbsize);
|
memset(p->fb, 0, fbsize);
|
||||||
p->damage = malloc(sizeof(*p->damage) * rows);
|
|
||||||
if(p->damage == NULL){
|
|
||||||
free(p->fb);
|
|
||||||
free(p);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
flash_damage_map(p->damage, rows, false);
|
|
||||||
p->userptr = NULL;
|
p->userptr = NULL;
|
||||||
p->leny = rows;
|
p->leny = rows;
|
||||||
p->lenx = cols;
|
p->lenx = cols;
|
||||||
@ -341,8 +332,7 @@ ncplane* notcurses_newplane(notcurses* nc, int rows, int cols,
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
// can be used on stdscr, unlike ncplane_resize() which prohibits it. sets all
|
// can be used on stdscr, unlike ncplane_resize() which prohibits it.
|
||||||
// members of the plane's damage map to damaged.
|
|
||||||
static int
|
static int
|
||||||
ncplane_resize_internal(ncplane* n, int keepy, int keepx, int keepleny,
|
ncplane_resize_internal(ncplane* n, int keepy, int keepx, int keepleny,
|
||||||
int keeplenx, int yoff, int xoff, int ylen, int xlen){
|
int keeplenx, int yoff, int xoff, int ylen, int xlen){
|
||||||
@ -377,19 +367,6 @@ ncplane_resize_internal(ncplane* n, int keepy, int keepx, int keepleny,
|
|||||||
if(fb == NULL){
|
if(fb == NULL){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// if we're the standard plane, the updamage can be charged at the end. it's
|
|
||||||
// unsafe to call now anyway, because if we shrank, the notcurses damage map
|
|
||||||
// has already been shrunk down
|
|
||||||
if(n != n->nc->stdscr){
|
|
||||||
ncplane_updamage(n); // damage any lines we were on
|
|
||||||
}
|
|
||||||
unsigned char* tmpdamage;
|
|
||||||
if((tmpdamage = realloc(n->damage, sizeof(*n->damage) * ylen)) == NULL){
|
|
||||||
free(fb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
n->damage = tmpdamage;
|
|
||||||
flash_damage_map(n->damage, ylen, true);
|
|
||||||
// update the cursor, if it would otherwise be off-plane
|
// update the cursor, if it would otherwise be off-plane
|
||||||
if(n->y >= ylen){
|
if(n->y >= ylen){
|
||||||
n->y = ylen - 1;
|
n->y = ylen - 1;
|
||||||
@ -411,7 +388,6 @@ ncplane_resize_internal(ncplane* n, int keepy, int keepx, int keepleny,
|
|||||||
n->lenx = xlen;
|
n->lenx = xlen;
|
||||||
n->leny = ylen;
|
n->leny = ylen;
|
||||||
free(preserved);
|
free(preserved);
|
||||||
ncplane_updamage(n); // damage any lines we're now on
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// we currently have maxy rows of maxx cells each. we will be keeping rows
|
// we currently have maxy rows of maxx cells each. we will be keeping rows
|
||||||
@ -448,7 +424,6 @@ ncplane_resize_internal(ncplane* n, int keepy, int keepx, int keepleny,
|
|||||||
n->lenx = xlen;
|
n->lenx = xlen;
|
||||||
n->leny = ylen;
|
n->leny = ylen;
|
||||||
free(preserved);
|
free(preserved);
|
||||||
ncplane_updamage(n); // damage any lines we're now on
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,19 +464,6 @@ int notcurses_resize(notcurses* n, int* rows, int* cols){
|
|||||||
if(keepx > oldcols){
|
if(keepx > oldcols){
|
||||||
keepx = oldcols;
|
keepx = oldcols;
|
||||||
}
|
}
|
||||||
unsigned char* tmpdamage;
|
|
||||||
if((tmpdamage = malloc(sizeof(*n->damage) * *rows)) == NULL){
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(oldcols < *cols){ // all are busted if rows got bigger
|
|
||||||
free(n->damage);
|
|
||||||
flash_damage_map(tmpdamage, *rows, true);
|
|
||||||
}else if(oldrows <= *rows){ // new rows are pre-busted, old are straight
|
|
||||||
memcpy(tmpdamage, n->damage, oldrows * sizeof(*tmpdamage));
|
|
||||||
flash_damage_map(tmpdamage + oldrows, *rows - oldrows, true);
|
|
||||||
free(n->damage);
|
|
||||||
}
|
|
||||||
n->damage = tmpdamage;
|
|
||||||
if(ncplane_resize_internal(n->stdscr, 0, 0, keepy, keepx, 0, 0, *rows, *cols)){
|
if(ncplane_resize_internal(n->stdscr, 0, 0, keepy, keepx, 0, 0, *rows, *cols)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -593,6 +555,8 @@ interrogate_terminfo(notcurses* nc, const notcurses_options* opts){
|
|||||||
term_verify_seq(&nc->clearscr, "clear");
|
term_verify_seq(&nc->clearscr, "clear");
|
||||||
term_verify_seq(&nc->cleareol, "el");
|
term_verify_seq(&nc->cleareol, "el");
|
||||||
term_verify_seq(&nc->clearbol, "el1");
|
term_verify_seq(&nc->clearbol, "el1");
|
||||||
|
term_verify_seq(&nc->cuf, "cuf"); // n non-destructive spaces
|
||||||
|
term_verify_seq(&nc->cuf1, "cuf1"); // non-destructive space
|
||||||
if(prep_special_keys(nc)){
|
if(prep_special_keys(nc)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -725,6 +689,10 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){
|
|||||||
ret->ttyinfp = stdin; // FIXME
|
ret->ttyinfp = stdin; // FIXME
|
||||||
ret->mstream = NULL;
|
ret->mstream = NULL;
|
||||||
ret->mstrsize = 0;
|
ret->mstrsize = 0;
|
||||||
|
ret->lastframe = NULL;
|
||||||
|
ret->lfdimy = 0;
|
||||||
|
ret->lfdimx = 0;
|
||||||
|
egcpool_init(&ret->pool);
|
||||||
if(make_nonblocking(ret->ttyinfp)){
|
if(make_nonblocking(ret->ttyinfp)){
|
||||||
free(ret);
|
free(ret);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -783,16 +751,10 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){
|
|||||||
free_plane(ret->top);
|
free_plane(ret->top);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if((ret->damage = malloc(sizeof(*ret->damage) * ret->stdscr->leny)) == NULL){
|
|
||||||
free_plane(ret->top);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
if((ret->mstreamfp = open_memstream(&ret->mstream, &ret->mstrsize)) == NULL){
|
if((ret->mstreamfp = open_memstream(&ret->mstream, &ret->mstrsize)) == NULL){
|
||||||
free(ret->damage);
|
|
||||||
free_plane(ret->top);
|
free_plane(ret->top);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
flash_damage_map(ret->damage, ret->stdscr->leny, false);
|
|
||||||
// term_emit("clear", ret->clear, ret->ttyfp, false);
|
// term_emit("clear", ret->clear, ret->ttyfp, false);
|
||||||
ret->suppress_banner = opts->suppress_bannner;
|
ret->suppress_banner = opts->suppress_bannner;
|
||||||
if(!opts->suppress_bannner){
|
if(!opts->suppress_bannner){
|
||||||
@ -856,10 +818,11 @@ int notcurses_stop(notcurses* nc){
|
|||||||
nc->top = p->z;
|
nc->top = p->z;
|
||||||
free_plane(p);
|
free_plane(p);
|
||||||
}
|
}
|
||||||
free(nc->damage);
|
|
||||||
if(nc->mstreamfp){
|
if(nc->mstreamfp){
|
||||||
fclose(nc->mstreamfp);
|
fclose(nc->mstreamfp);
|
||||||
}
|
}
|
||||||
|
egcpool_dump(&nc->pool);
|
||||||
|
free(nc->lastframe);
|
||||||
free(nc->mstream);
|
free(nc->mstream);
|
||||||
input_free_esctrie(&nc->inputescapes);
|
input_free_esctrie(&nc->inputescapes);
|
||||||
ret |= pthread_mutex_destroy(&nc->lock);
|
ret |= pthread_mutex_destroy(&nc->lock);
|
||||||
@ -955,7 +918,6 @@ int ncplane_set_default(ncplane* ncp, const cell* c){
|
|||||||
if(ret < 0){
|
if(ret < 0){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ncplane_updamage(ncp);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -991,7 +953,6 @@ int ncplane_move_above_unsafe(ncplane* restrict n, ncplane* restrict above){
|
|||||||
*an = n->z; // splice n out
|
*an = n->z; // splice n out
|
||||||
n->z = above; // attach above below n
|
n->z = above; // attach above below n
|
||||||
*aa = n; // spline n in above
|
*aa = n; // spline n in above
|
||||||
ncplane_updamage(n); // conservative (we might not actually be visible)
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1004,7 +965,6 @@ int ncplane_move_below_unsafe(ncplane* restrict n, ncplane* restrict below){
|
|||||||
*an = n->z; // splice n out
|
*an = n->z; // splice n out
|
||||||
n->z = below->z; // reattach subbelow list to n
|
n->z = below->z; // reattach subbelow list to n
|
||||||
below->z = n; // splice n in below
|
below->z = n; // splice n in below
|
||||||
ncplane_updamage(n); // conservative (we might not actually be visible)
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1016,7 +976,6 @@ int ncplane_move_top(ncplane* n){
|
|||||||
*an = n->z; // splice n out
|
*an = n->z; // splice n out
|
||||||
n->z = n->nc->top;
|
n->z = n->nc->top;
|
||||||
n->nc->top = n;
|
n->nc->top = n;
|
||||||
ncplane_updamage(n);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1032,7 +991,6 @@ int ncplane_move_bottom(ncplane* n){
|
|||||||
}
|
}
|
||||||
*an = n;
|
*an = n;
|
||||||
n->z = NULL;
|
n->z = NULL;
|
||||||
ncplane_updamage(n);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1062,24 +1020,6 @@ ncplane_cursor_stuck(const ncplane* n){
|
|||||||
return (n->x == n->lenx && n->y == n->leny);
|
return (n->x == n->lenx && n->y == n->leny);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cell_duplicate(ncplane* n, cell* targ, const cell* c){
|
|
||||||
cell_release(n, targ);
|
|
||||||
targ->attrword = c->attrword;
|
|
||||||
targ->channels = c->channels;
|
|
||||||
if(cell_simple_p(c)){
|
|
||||||
targ->gcluster = c->gcluster;
|
|
||||||
return !!c->gcluster;
|
|
||||||
}
|
|
||||||
size_t ulen = strlen(extended_gcluster(n, c));
|
|
||||||
// fprintf(stderr, "[%s] (%zu)\n", extended_gcluster(n, c), strlen(extended_gcluster(n, c)));
|
|
||||||
int eoffset = egcpool_stash(&n->pool, extended_gcluster(n, c), ulen);
|
|
||||||
if(eoffset < 0){
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
targ->gcluster = eoffset + 0x80;
|
|
||||||
return ulen;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
cell_set_wide(cell* c){
|
cell_set_wide(cell* c){
|
||||||
c->channels |= CELL_WIDEASIAN_MASK;
|
c->channels |= CELL_WIDEASIAN_MASK;
|
||||||
@ -1129,7 +1069,6 @@ int ncplane_putc(ncplane* n, const cell* c){
|
|||||||
cell_release(n, candidate);
|
cell_release(n, candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n->damage[n->y] = true;
|
|
||||||
advance_cursor(n, cols);
|
advance_cursor(n, cols);
|
||||||
ncplane_unlock(n);
|
ncplane_unlock(n);
|
||||||
return cols;
|
return cols;
|
||||||
@ -1174,13 +1113,6 @@ int ncplane_cursor_at(const ncplane* n, cell* c, char** gclust){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cell_release(ncplane* n, cell* c){
|
|
||||||
if(!cell_simple_p(c)){
|
|
||||||
egcpool_release(&n->pool, cell_egc_idx(c));
|
|
||||||
c->gcluster = 0; // don't subject ourselves to double-release problems
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int cell_load(ncplane* n, cell* c, const char* gcluster){
|
int cell_load(ncplane* n, cell* c, const char* gcluster){
|
||||||
cell_release(n, c);
|
cell_release(n, c);
|
||||||
int bytes;
|
int bytes;
|
||||||
@ -1536,33 +1468,12 @@ int ncplane_box(ncplane* n, const cell* ul, const cell* ur,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark all lines of the notcurses object touched by this plane as damaged
|
|
||||||
void ncplane_updamage(ncplane* n){
|
|
||||||
int drangelow = n->absy;
|
|
||||||
int drangehigh = n->absy + n->leny;
|
|
||||||
if(drangehigh > n->nc->stdscr->leny){
|
|
||||||
drangehigh = n->nc->stdscr->leny;
|
|
||||||
}
|
|
||||||
if(drangelow < n->nc->stdscr->absy){
|
|
||||||
drangelow = n->nc->stdscr->absy;
|
|
||||||
}
|
|
||||||
if(drangelow > n->nc->stdscr->absy + n->nc->stdscr->leny - 1){
|
|
||||||
drangelow = n->nc->stdscr->absy + n->nc->stdscr->leny - 1;
|
|
||||||
}
|
|
||||||
flash_damage_map(n->nc->damage + drangelow, drangehigh - drangelow, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ncplane_move_yx(ncplane* n, int y, int x){
|
int ncplane_move_yx(ncplane* n, int y, int x){
|
||||||
if(n == n->nc->stdscr){
|
if(n == n->nc->stdscr){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ncplane_updamage(n); // damage any lines we are currently on
|
|
||||||
bool movedy = n->absy != y;
|
|
||||||
n->absy = y;
|
n->absy = y;
|
||||||
n->absx = x;
|
n->absx = x;
|
||||||
if(movedy){
|
|
||||||
ncplane_updamage(n);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1602,7 +1513,6 @@ void ncplane_erase(ncplane* n){
|
|||||||
egcpool_init(&n->pool);
|
egcpool_init(&n->pool);
|
||||||
cell_load(n, &n->defcell, egc);
|
cell_load(n, &n->defcell, egc);
|
||||||
free(egc);
|
free(egc);
|
||||||
ncplane_updamage(n);
|
|
||||||
ncplane_unlock(n);
|
ncplane_unlock(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
172
src/lib/render.c
172
src/lib/render.c
@ -1,4 +1,5 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
@ -98,24 +99,33 @@ prep_optimized_palette(notcurses* nc, FILE* out __attribute__ ((unused))){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// reshape the shadow framebuffer to match the stdplane's dimensions, throwing
|
// reshape the shadow framebuffer to match the stdplane's dimensions, throwing
|
||||||
// away the old one.
|
// away the old one.
|
||||||
static int
|
static int
|
||||||
reshape_shadow_fb(notcurses* nc){
|
reshape_shadow_fb(notcurses* nc){
|
||||||
const size_t size = sizeof(nc->shadowbuf) * nc->stdscr->leny * nc->stdscr->lenx;
|
if(nc->lfdimx == nc->stdscr->lenx && nc->lfdimy == nc->stdscr->leny){
|
||||||
cell* fb = malloc(size);
|
return 0; // no change
|
||||||
|
}
|
||||||
|
const size_t size = sizeof(*nc->lastframe) * nc->stdscr->leny * nc->stdscr->lenx;
|
||||||
|
cell* fb = realloc(nc->lastframe, size);
|
||||||
if(fb == NULL){
|
if(fb == NULL){
|
||||||
|
free(nc->lastframe);
|
||||||
|
nc->lastframe = NULL;
|
||||||
|
nc->lfdimx = 0;
|
||||||
|
nc->lfdimy = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
free(nc->shadowbuf);
|
nc->lastframe = fb;
|
||||||
nc->shadowbuf = fb;
|
// FIXME more memset()tery than we need, both wasting work and wrecking
|
||||||
nc->shadowy = nc->stdscr->leny;
|
// damage detection for the upcoming render
|
||||||
nc->shadowx = nc->stdscr->lenx;
|
memset(nc->lastframe, 0, size);
|
||||||
|
nc->lastframe = fb;
|
||||||
|
nc->lfdimy = nc->stdscr->leny;
|
||||||
|
nc->lfdimx = nc->stdscr->lenx;
|
||||||
memset(fb, 0, size);
|
memset(fb, 0, size);
|
||||||
|
egcpool_dump(&nc->pool);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// Find the topmost cell for this coordinate by walking down the z-buffer,
|
// Find the topmost cell for this coordinate by walking down the z-buffer,
|
||||||
// looking for an intersecting ncplane. Once we've found one, check it for
|
// looking for an intersecting ncplane. Once we've found one, check it for
|
||||||
@ -135,9 +145,8 @@ reshape_shadow_fb(notcurses* nc){
|
|||||||
// whichever one occurs at the top with a non-transparent α (α < 3). To effect
|
// whichever one occurs at the top with a non-transparent α (α < 3). To effect
|
||||||
// tail recursion, though, we instead write first, and then recurse, blending
|
// tail recursion, though, we instead write first, and then recurse, blending
|
||||||
// as we descend. α <= 0 is opaque. α >= 3 is fully transparent.
|
// as we descend. α <= 0 is opaque. α >= 3 is fully transparent.
|
||||||
static ncplane*
|
static inline ncplane*
|
||||||
dig_visible_cell(cell* c, int y, int x, ncplane* p, int falpha, int balpha,
|
dig_visible_cell(cell* c, int y, int x, ncplane* p, int falpha, int balpha){
|
||||||
bool* damage){
|
|
||||||
// once we decide on our glyph, it cannot be changed by anything below, so
|
// once we decide on our glyph, it cannot be changed by anything below, so
|
||||||
// lock in this plane for the actual cell return.
|
// lock in this plane for the actual cell return.
|
||||||
ncplane* glyphplane = NULL;
|
ncplane* glyphplane = NULL;
|
||||||
@ -164,20 +173,12 @@ dig_visible_cell(cell* c, int y, int x, ncplane* p, int falpha, int balpha,
|
|||||||
c->attrword = vis->attrword;
|
c->attrword = vis->attrword;
|
||||||
cell_set_fchannel(c, cell_get_fchannel(vis)); // FIXME blend it in
|
cell_set_fchannel(c, cell_get_fchannel(vis)); // FIXME blend it in
|
||||||
falpha -= (CELL_ALPHA_TRANSPARENT - nalpha); // FIXME blend it in
|
falpha -= (CELL_ALPHA_TRANSPARENT - nalpha); // FIXME blend it in
|
||||||
if(p->damage[poffy]){
|
|
||||||
*damage = true;
|
|
||||||
p->damage[poffy] = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(balpha > 0 && (nalpha = cell_get_bg_alpha(vis)) < CELL_ALPHA_TRANSPARENT){
|
if(balpha > 0 && (nalpha = cell_get_bg_alpha(vis)) < CELL_ALPHA_TRANSPARENT){
|
||||||
cell_set_bchannel(c, cell_get_bchannel(vis)); // FIXME blend it in
|
cell_set_bchannel(c, cell_get_bchannel(vis)); // FIXME blend it in
|
||||||
balpha -= (CELL_ALPHA_TRANSPARENT - nalpha);
|
balpha -= (CELL_ALPHA_TRANSPARENT - nalpha);
|
||||||
if(p->damage[poffy]){
|
|
||||||
*damage = true;
|
|
||||||
p->damage[poffy] = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if((falpha <= 0 && balpha <= 0) || !p->z){ // done!
|
if((falpha <= 0 && balpha <= 0) || !p->z){ // done!
|
||||||
return glyphplane ? glyphplane : p;
|
return glyphplane ? glyphplane : p;
|
||||||
@ -191,10 +192,9 @@ dig_visible_cell(cell* c, int y, int x, ncplane* p, int falpha, int balpha,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline ncplane*
|
static inline ncplane*
|
||||||
visible_cell(cell* c, int y, int x, ncplane* n, bool* damage){
|
visible_cell(cell* c, int y, int x, ncplane* n){
|
||||||
cell_init(c);
|
cell_init(c);
|
||||||
return dig_visible_cell(c, y, x, n, CELL_ALPHA_TRANSPARENT,
|
return dig_visible_cell(c, y, x, n, CELL_ALPHA_TRANSPARENT, CELL_ALPHA_TRANSPARENT);
|
||||||
CELL_ALPHA_TRANSPARENT, damage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the cell's UTF-8 grapheme cluster to the provided FILE*. returns the
|
// write the cell's UTF-8 grapheme cluster to the provided FILE*. returns the
|
||||||
@ -385,6 +385,74 @@ term_fg_rgb8(notcurses* nc, FILE* out, unsigned r, unsigned g, unsigned b){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
pool_release(egcpool* pool, cell* c){
|
||||||
|
if(!cell_simple_p(c)){
|
||||||
|
egcpool_release(pool, cell_egc_idx(c));
|
||||||
|
c->gcluster = 0; // don't subject ourselves to double-release problems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cell_release(ncplane* n, cell* c){
|
||||||
|
pool_release(&n->pool, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate one cell onto another, possibly crossing ncplanes.
|
||||||
|
static inline int
|
||||||
|
cell_duplicate_far(egcpool* tpool, cell* targ, const ncplane* splane, const cell* c){
|
||||||
|
pool_release(tpool, targ);
|
||||||
|
targ->attrword = c->attrword;
|
||||||
|
targ->channels = c->channels;
|
||||||
|
if(cell_simple_p(c)){
|
||||||
|
targ->gcluster = c->gcluster;
|
||||||
|
return !!c->gcluster;
|
||||||
|
}
|
||||||
|
size_t ulen = strlen(extended_gcluster(splane, c));
|
||||||
|
// fprintf(stderr, "[%s] (%zu)\n", extended_gcluster(n, c), strlen(extended_gcluster(n, c)));
|
||||||
|
int eoffset = egcpool_stash(tpool, extended_gcluster(splane, c), ulen);
|
||||||
|
if(eoffset < 0){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
targ->gcluster = eoffset + 0x80;
|
||||||
|
return ulen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate one cell onto another when they share a plane. Convenience wrapper.
|
||||||
|
int cell_duplicate(ncplane* n, cell* targ, const cell* c){
|
||||||
|
return cell_duplicate_far(&n->pool, targ, n, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the heart of damage detection. compare two cells (from two different planes)
|
||||||
|
// for equality. if they are equal, return 0. otherwise, dup the second onto
|
||||||
|
// the first and return non-zero.
|
||||||
|
static int
|
||||||
|
cellcmp_and_dupfar(egcpool* dampool, cell* damcell, const ncplane* srcplane,
|
||||||
|
const cell* srccell){
|
||||||
|
if(damcell->attrword == srccell->attrword){
|
||||||
|
if(damcell->channels == srccell->channels){
|
||||||
|
bool damsimple = cell_simple_p(damcell);
|
||||||
|
bool srcsimple = cell_simple_p(srccell);
|
||||||
|
if(damsimple == srcsimple){
|
||||||
|
if(damsimple){
|
||||||
|
if(damcell->gcluster == srccell->gcluster){
|
||||||
|
return 0; // simple match
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
const char* damegc = egcpool_extended_gcluster(dampool, damcell);
|
||||||
|
const char* srcegc = extended_gcluster(srcplane, srccell);
|
||||||
|
assert(strcmp(damegc, "三体"));
|
||||||
|
assert(strcmp(srcegc, "三体"));
|
||||||
|
if(strcmp(damegc, srcegc) == 0){
|
||||||
|
return 0; // EGC match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell_duplicate_far(dampool, damcell, srcplane, srccell);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
notcurses_render_internal(notcurses* nc){
|
notcurses_render_internal(notcurses* nc){
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -405,40 +473,55 @@ notcurses_render_internal(notcurses* nc){
|
|||||||
// cells and the current cell uses no defaults, or if both the current and
|
// cells and the current cell uses no defaults, or if both the current and
|
||||||
// the last used both defaults.
|
// the last used both defaults.
|
||||||
bool fgelidable = false, bgelidable = false, defaultelidable = false;
|
bool fgelidable = false, bgelidable = false, defaultelidable = false;
|
||||||
/*if(nc->stdscr->leny != nc->shadowy || nc->stdscr->lenx != nc->shadowx){
|
// if this fails, struggle bravely on. we can live without a lastframe.
|
||||||
reshape_shadow_fb(nc);
|
reshape_shadow_fb(nc);
|
||||||
}*/
|
|
||||||
for(y = 0 ; y < nc->stdscr->leny ; ++y){
|
for(y = 0 ; y < nc->stdscr->leny ; ++y){
|
||||||
bool linedamaged = false; // have we repositioned the cursor to start line?
|
// how many characters have we elided? it's not worthwhile to invoke a
|
||||||
bool newdamage = nc->damage[y];
|
// cursor movement with cup if we only elided one or two. set to INT_MAX
|
||||||
// fprintf(stderr, "nc->damage[%d] (%p) = %u\n", y, nc->damage + y, nc->damage[y]);
|
// whenever we're on a new line.
|
||||||
if(newdamage){
|
int needmove = INT_MAX;
|
||||||
nc->damage[y] = 0;
|
|
||||||
}
|
|
||||||
// move to the beginning of the line, in case our accounting was befouled
|
|
||||||
// by wider- (or narrower-) than-reported characters
|
|
||||||
for(x = 0 ; x < nc->stdscr->lenx ; ++x){
|
for(x = 0 ; x < nc->stdscr->lenx ; ++x){
|
||||||
unsigned r, g, b, br, bg, bb;
|
unsigned r, g, b, br, bg, bb;
|
||||||
ncplane* p;
|
ncplane* p;
|
||||||
cell c; // no need to initialize
|
cell c; // no need to initialize
|
||||||
p = visible_cell(&c, y, x, nc->top, &newdamage);
|
p = visible_cell(&c, y, x, nc->top);
|
||||||
assert(p);
|
|
||||||
// don't try to print a wide character on the last column; it'll instead
|
// don't try to print a wide character on the last column; it'll instead
|
||||||
// be printed on the next line. they probably shouldn't be admitted, but
|
// be printed on the next line. they probably shouldn't be admitted, but
|
||||||
// we can end up with one due to a resize.
|
// we can end up with one due to a resize.
|
||||||
|
// FIXME but...print what, exactly, instead?
|
||||||
if((x + 1 >= nc->stdscr->lenx && cell_double_wide_p(&c))){
|
if((x + 1 >= nc->stdscr->lenx && cell_double_wide_p(&c))){
|
||||||
continue;
|
continue; // needmove will be reset as we restart the line
|
||||||
}
|
}
|
||||||
if(!linedamaged){
|
// lastframe has already been sized to match the current size, so no need
|
||||||
if(newdamage){
|
// to check whether we're within its bounds. just check the cell.
|
||||||
term_emit("cup", tiparm(nc->cup, y, x), out, false);
|
if(nc->lastframe){
|
||||||
nc->stats.cellelisions += x;
|
cell* oldcell = &nc->lastframe[fbcellidx(nc->stdscr, y, x)];
|
||||||
nc->stats.cellemissions += (nc->stdscr->lenx - x);
|
if(cellcmp_and_dupfar(&nc->pool, oldcell, p, &c) == 0){
|
||||||
linedamaged = true;
|
// no need to emit a cell; what we rendered appears to already be
|
||||||
}else{
|
// here. no updates are performed to elision state nor lastframe.
|
||||||
|
++nc->stats.cellelisions;
|
||||||
|
if(needmove < INT_MAX){
|
||||||
|
++needmove;
|
||||||
|
}
|
||||||
|
if(cell_double_wide_p(&c)){
|
||||||
|
if(needmove < INT_MAX){
|
||||||
|
++needmove;
|
||||||
|
}
|
||||||
|
++nc->stats.cellelisions;
|
||||||
|
++x;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
++nc->stats.cellemissions;
|
||||||
|
if(needmove > 8){ // FIXME cuf and cuf1 aren't guaranteed!
|
||||||
|
term_emit("cup", tiparm(nc->cup, y, x), out, false);
|
||||||
|
}else if(needmove > 1){
|
||||||
|
term_emit("cuf", tiparm(nc->cuf, needmove), out, false);
|
||||||
|
}else if(needmove){
|
||||||
|
term_emit("cuf1", tiparm(nc->cuf1), out, false);
|
||||||
|
}
|
||||||
|
needmove = 0;
|
||||||
// set the style. this can change the color back to the default; if it
|
// set the style. this can change the color back to the default; if it
|
||||||
// does, we need update our elision possibilities.
|
// does, we need update our elision possibilities.
|
||||||
bool normalized;
|
bool normalized;
|
||||||
@ -498,9 +581,6 @@ notcurses_render_internal(notcurses* nc){
|
|||||||
++x;
|
++x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(linedamaged == false){
|
|
||||||
nc->stats.cellelisions += x;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ret |= fflush(out);
|
ret |= fflush(out);
|
||||||
fflush(nc->ttyfp);
|
fflush(nc->ttyfp);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user