add notcurses_render_file() #491

This commit is contained in:
nick black 2020-07-05 07:14:07 -04:00
parent 8731b1191d
commit b6330d142b
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC
7 changed files with 79 additions and 28 deletions

View File

@ -1,6 +1,9 @@
This document attempts to list user-visible changes and any major internal This document attempts to list user-visible changes and any major internal
rearrangements of Notcurses. rearrangements of Notcurses.
* 1.6.1 (not yet released)
* Added `notcurses_render_file()` to dump last rendered frame to a `FILE*`.
* 1.6.0 (2020-07-04) * 1.6.0 (2020-07-04)
* Behavior has changed regarding use of the provided `FILE*` (which, when * Behavior has changed regarding use of the provided `FILE*` (which, when
`NULL`, is assumed to be `stdout`). Both Notcurses and `ncdirect` now `NULL`, is assumed to be `stdout`). Both Notcurses and `ncdirect` now

View File

@ -170,6 +170,10 @@ updated to reflect the changes:
// successful call to notcurses_render(). // successful call to notcurses_render().
int notcurses_render(struct notcurses* nc); int notcurses_render(struct notcurses* nc);
// Write the last rendered frame, in its entirety, to 'fp'. This is not valid
// until notcurses_render() has been successfully called at least once.
int notcurses_render_to_file(struct notcurses* nc, FILE* fp);
// Retrieve the contents of the specified cell as last rendered. The EGC is // Retrieve the contents of the specified cell as last rendered. The EGC is
// returned, or NULL on error. This EGC must be free()d by the caller. The // returned, or NULL on error. This EGC must be free()d by the caller. The
// attrword and channels are written to 'attrword' and 'channels', respectively. // attrword and channels are written to 'attrword' and 'channels', respectively.

View File

@ -14,6 +14,8 @@ notcurses_render - sync the physical display to the virtual ncplanes
**char* notcurses_at_yx(struct notcurses* nc, int yoff, int xoff, uint32_t* attrword, uint64_t* channels);** **char* notcurses_at_yx(struct notcurses* nc, int yoff, int xoff, uint32_t* attrword, uint64_t* channels);**
**int notcurses_render_to_file(struct notcurses* nc, FILE* fp);**
# DESCRIPTION # DESCRIPTION
**notcurses_render** syncs the physical display to the context's prepared **notcurses_render** syncs the physical display to the context's prepared
@ -25,7 +27,7 @@ render (see notcurses_stats(3)), and screen geometry is refreshed (similarly to
While **notcurses_render** is called, you **must not call any other functions While **notcurses_render** is called, you **must not call any other functions
on the same notcurses context**, with the one exception of **notcurses_getc** on the same notcurses context**, with the one exception of **notcurses_getc**
(and its input-related helpers). (and its input-related helpers; see **notcurses_input(3)**.).
A render operation consists of two logical phases: generation of the rendered A render operation consists of two logical phases: generation of the rendered
scene, and blitting this scene to the terminal (these two phases might actually scene, and blitting this scene to the terminal (these two phases might actually
@ -86,8 +88,9 @@ purposes of color blending.
**notcurses(3)**, **notcurses(3)**,
**notcurses_cell(3)**, **notcurses_cell(3)**,
**notcurses_plane(3)**, **notcurses_input(3)**,
**notcurses_output(3)**, **notcurses_output(3)**,
**notcurses_plane(3)**,
**notcurses_refresh(3)**, **notcurses_refresh(3)**,
**notcurses_stats(3)**, **notcurses_stats(3)**,
**console_codes(4)**, **console_codes(4)**,

View File

@ -993,6 +993,10 @@ API int notcurses_stop(struct notcurses* nc);
// successful call to notcurses_render(). // successful call to notcurses_render().
API int notcurses_render(struct notcurses* nc); API int notcurses_render(struct notcurses* nc);
// Write the last rendered frame, in its entirety, to 'fp'. This is not valid
// until notcurses_render() has been successfully called at least once.
API int notcurses_render_to_file(struct notcurses* nc, FILE* fp);
// Return the topmost ncplane, of which there is always at least one. // Return the topmost ncplane, of which there is always at least one.
API struct ncplane* notcurses_top(struct notcurses* n); API struct ncplane* notcurses_top(struct notcurses* n);

View File

@ -85,6 +85,7 @@ struct notcurses* notcurses_init(const notcurses_options*, FILE*);
int notcurses_lex_margins(const char* op, notcurses_options* opts); int notcurses_lex_margins(const char* op, notcurses_options* opts);
int notcurses_stop(struct notcurses*); int notcurses_stop(struct notcurses*);
int notcurses_render(struct notcurses*); int notcurses_render(struct notcurses*);
int notcurses_render_to_file(struct notcurses* nc, FILE* fp);
struct ncplane* notcurses_stdplane(struct notcurses*); struct ncplane* notcurses_stdplane(struct notcurses*);
const struct ncplane* notcurses_stdplane_const(const struct notcurses* nc); const struct ncplane* notcurses_stdplane_const(const struct notcurses* nc);
int ncplane_set_base_cell(struct ncplane* ncp, const cell* c); int ncplane_set_base_cell(struct ncplane* ncp, const cell* c);

View File

@ -35,14 +35,14 @@ unicode1emoji1(struct ncplane* title, int y){
if(n == NULL){ if(n == NULL){
return NULL; return NULL;
} }
ncplane_putstr_yx(n, 1, 1, "\u2764 (\u2764\ufe0f) \u2709 (\u2709\ufe0f)" ncplane_putstr_yx(n, 1, 1, "\u2764 \u2764\ufe0f \u2709 \u2709\ufe0f"
"\u270f (\u270f\ufe0f) \u2712 (\u2712\ufe0f)" "\u270f \u270f\ufe0f \u2712 \u2712\ufe0f"
"\u2195 (\u2195\ufe0f) \u2194 (\u2194\ufe0f)" "\u2195 \u2195\ufe0f \u2194 \u2194\ufe0f"
"\u2716 (\u2716\ufe0f) \u2733 (\u2733\ufe0f)" "\u2716 \u2716\ufe0f \u2733 \u2733\ufe0f"
"\u2734 (\u2734\ufe0f) \u2747 (\u2747\ufe0f)"); "\u2734 \u2734\ufe0f \u2747 \u2747\ufe0f");
ncplane_putstr_yx(n, 2, 1, "\u2660 (\u2660\ufe0f) \u2665 (\u2665\ufe0f)" ncplane_putstr_yx(n, 2, 1, "\u2660 \u2660\ufe0f \u2665 \u2665\ufe0f"
"\u2666 (\u2666\ufe0f) \u2663 (\u2663\ufe0f)" "\u2666 \u2666\ufe0f \u2663 \u2663\ufe0f"
"\u260e (\u260e\ufe0f) \u27a1 (\u27a1\ufe0f)"); "\u260e \u260e\ufe0f \u27a1 \u27a1\ufe0f");
return n; return n;
} }
@ -54,20 +54,20 @@ unicode52(struct ncplane* title, int y){
if(n == NULL){ if(n == NULL){
return NULL; return NULL;
} }
ncplane_putstr_yx(n, 1, 1, "\u26ea (\u26ea\ufe0f) \u26f2 (\u26f2\ufe0f)" ncplane_putstr_yx(n, 1, 1, "\u26ea \u26ea\ufe0f \u26f2 \u26f2\ufe0f"
"\u26fa (\u26fa\ufe0f) \u2668 (\u2668\ufe0f)" "\u26fa \u26fa\ufe0f \u2668 \u2668\ufe0f"
"\u26fd (\u26fd\ufe0f) \u2693 (\u2693\ufe0f)" "\u26fd \u26fd\ufe0f \u2693 \u2693\ufe0f"
"\u26f5 (\u26f5\ufe0f) \u2600 (\u2600\ufe0f)"); "\u26f5 \u26f5\ufe0f \u2600 \u2600\ufe0f");
ncplane_putstr_yx(n, 2, 1, "\u26c5 (\u26c5\ufe0f) \u2614 (\u2614\ufe0f)" ncplane_putstr_yx(n, 2, 1, "\u26c5 \u26c5\ufe0f \u2614 \u2614\ufe0f"
"\u26a1 (\u26a1\ufe0f) \u26c4 (\u26c4\ufe0f)" "\u26a1 \u26a1\ufe0f \u26c4 \u26c4\ufe0f"
"\u26be (\u26b3\ufe0f) \u26d4 (\u26d4\ufe0f)" "\u26be \u26b3\ufe0f \u26d4 \u26d4\ufe0f"
"\u2b06 (\u2b06\ufe0f) \u2b07 (\u2b07\ufe0f)"); "\u2b06 \u2b06\ufe0f \u2b07 \u2b07\ufe0f");
ncplane_putstr_yx(n, 3, 1, "\u2b05 (\u2b05\ufe0f) \u26ce (\u26ce\ufe0f)" ncplane_putstr_yx(n, 3, 1, "\u2b05 \u2b05\ufe0f \u26ce \u26ce\ufe0f"
"\u203c (\u203c\ufe0f) \u2049 (\u2049\ufe0f)" "\u203c \u203c\ufe0f \u2049 \u2049\ufe0f"
"\xf0\x9f\x85\xbf (\xf0\x9f\x85\xbf\ufe0f)" "\xf0\x9f\x85\xbf \xf0\x9f\x85\xbf\ufe0f"
"\xf0\x9f\x88\xaf (\xf0\x9f\x88\xaf\ufe0f)" "\xf0\x9f\x88\xaf \xf0\x9f\x88\xaf\ufe0f"
"\xf0\x9f\x88\x9a (\xf0\x9f\x88\x9a\ufe0f)" "\xf0\x9f\x88\x9a \xf0\x9f\x88\x9a\ufe0f"
"\u3299 (\u3299\ufe0f)"); "\u3299 \u3299\ufe0f");
return n; return n;
} }

View File

@ -865,8 +865,7 @@ stage_cursor(notcurses* nc, FILE* out, int y, int x){
// lastframe has *not yet been written to the screen*, i.e. it's only about to // lastframe has *not yet been written to the screen*, i.e. it's only about to
// *become* the last frame rasterized. // *become* the last frame rasterized.
static int static int
notcurses_rasterize(notcurses* nc, const struct crender* rvec){ notcurses_rasterize(notcurses* nc, const struct crender* rvec, FILE* out){
FILE* out = nc->rstate.mstreamfp;
int ret = 0; int ret = 0;
int y, x; int y, x;
fseeko(out, 0, SEEK_SET); fseeko(out, 0, SEEK_SET);
@ -1078,7 +1077,7 @@ int notcurses_refresh(notcurses* nc, int* restrict dimy, int* restrict dimx){
for(int i = 0 ; i < count ; ++i){ for(int i = 0 ; i < count ; ++i){
rvec[i].damaged = true; rvec[i].damaged = true;
} }
int ret = notcurses_rasterize(nc, rvec); int ret = notcurses_rasterize(nc, rvec, nc->rstate.mstreamfp);
free(rvec); free(rvec);
if(ret < 0){ if(ret < 0){
return -1; return -1;
@ -1086,6 +1085,43 @@ int notcurses_refresh(notcurses* nc, int* restrict dimy, int* restrict dimx){
return 0; return 0;
} }
int notcurses_render_to_file(struct notcurses* nc, FILE* fp){
if(nc->lfdimx == 0 || nc->lfdimy == 0){
return 0;
}
char* rastered = NULL;
size_t rastbytes = 0;
FILE* out = open_memstream(&rastered, &rastbytes);
if(out == NULL){
return -1;
}
const int count = (nc->lfdimx > nc->stdscr->lenx ? nc->lfdimx : nc->stdscr->lenx) *
(nc->lfdimy > nc->stdscr->leny ? nc->lfdimy : nc->stdscr->leny);
struct crender* rvec = malloc(count * sizeof(*rvec));
if(rvec == NULL){
fclose(out);
free(rastered);
return -1;
}
memset(rvec, 0, count * sizeof(*rvec));
for(int i = 0 ; i < count ; ++i){
rvec[i].damaged = true;
}
int ret = notcurses_rasterize(nc, rvec, out);
free(rvec);
if(ret > 0){
if(fprintf(fp, "%s", rastered) == ret){
ret = 0;
}else{
ret = -1;
}
}
fclose(out);
free(rastered);
return 0;
}
// We execute the painter's algorithm, starting from our topmost plane. The // We execute the painter's algorithm, starting from our topmost plane. The
// damagevector should be all zeros on input. On success, it will reflect // damagevector should be all zeros on input. On success, it will reflect
// which cells were changed. We solve for each coordinate's cell by walking // which cells were changed. We solve for each coordinate's cell by walking
@ -1123,7 +1159,7 @@ int notcurses_render(notcurses* nc){
struct crender* crender = malloc(crenderlen); struct crender* crender = malloc(crenderlen);
memset(crender, 0, crenderlen); memset(crender, 0, crenderlen);
if(notcurses_render_internal(nc, crender) == 0){ if(notcurses_render_internal(nc, crender) == 0){
bytes = notcurses_rasterize(nc, crender); bytes = notcurses_rasterize(nc, crender, nc->rstate.mstreamfp);
} }
free(crender); free(crender);
clock_gettime(CLOCK_MONOTONIC, &done); clock_gettime(CLOCK_MONOTONIC, &done);