dankamongmen/iterm2complete
nick black 3 years ago committed by nick black
parent 07e50e27e9
commit f86daff3cc

@ -508,12 +508,22 @@ ncdirect_dump_plane(ncdirect* n, const ncplane* np, int xoff){
if(ncdirect_flush(n)){ if(ncdirect_flush(n)){
return -1; return -1;
} }
if(sprite_draw(&n->tcache, NULL, np->sprite, n->ttyfp, 0, xoff) < 0){ fbuf f = {};
if(fbuf_init_small(&f)){
return -1; return -1;
} }
if(sprite_commit(&n->tcache, n->ttyfp, np->sprite, true)){ if(sprite_draw(&n->tcache, NULL, np->sprite, &f, 0, xoff) < 0){
return -1; return -1;
} }
if(sprite_commit(&n->tcache, &f, np->sprite, true)){
fbuf_free(&f);
return -1;
}
if(fwrite(f.buf, f.used, 1, n->ttyfp) != 1 || fflush(n->ttyfp)){
fbuf_free(&f);
return -1;
}
fbuf_free(&f);
return 0; return 0;
} }
//fprintf(stderr, "rasterizing %dx%d+%d\n", dimy, dimx, xoff); //fprintf(stderr, "rasterizing %dx%d+%d\n", dimy, dimx, xoff);
@ -795,10 +805,13 @@ ncdirect_stop_minimal(void* vnc){
rl_deprep_terminal(); rl_deprep_terminal();
#endif #endif
} }
if(nc->tcache.pixel_shutdown){ fbuf f = {};
ret |= nc->tcache.pixel_shutdown(nc->ttyfp); if(fbuf_init_small(&f) == 0){
if(nc->tcache.pixel_shutdown){
ret |= nc->tcache.pixel_shutdown(&f);
}
ret |= reset_term_attributes(&nc->tcache, &f);
} }
ret |= reset_term_attributes(&nc->tcache, nc->ttyfp);
if(nc->ctermfd >= 0){ if(nc->ctermfd >= 0){
const char* cnorm = get_escape(&nc->tcache, ESCAPE_CNORM); const char* cnorm = get_escape(&nc->tcache, ESCAPE_CNORM);
if(cnorm && tty_emit(cnorm, nc->ctermfd)){ if(cnorm && tty_emit(cnorm, nc->ctermfd)){
@ -920,9 +933,9 @@ char* ncdirect_readline(ncdirect* n, const char* prompt){
} }
static inline int static inline int
ncdirect_style_emit(ncdirect* n, unsigned stylebits, FILE* out){ ncdirect_style_emit(ncdirect* n, unsigned stylebits, fbuf* f){
unsigned normalized = 0; unsigned normalized = 0;
int r = coerce_styles(out, &n->tcache, &n->stylemask, stylebits, &normalized); int r = coerce_styles(f, &n->tcache, &n->stylemask, stylebits, &normalized);
// sgr0 resets colors, so set them back up if not defaults and it was used // sgr0 resets colors, so set them back up if not defaults and it was used
if(normalized){ if(normalized){
// emitting an sgr resets colors. if we want to be default, that's no // emitting an sgr resets colors. if we want to be default, that's no
@ -964,9 +977,19 @@ int ncdirect_on_styles(ncdirect* n, unsigned stylebits){
return -1; return -1;
} }
uint32_t stylemask = n->stylemask | stylebits; uint32_t stylemask = n->stylemask | stylebits;
if(ncdirect_style_emit(n, stylemask, n->ttyfp)){ fbuf f = {};
if(fbuf_init_small(&f)){
return -1; return -1;
} }
if(ncdirect_style_emit(n, stylemask, &f)){
fbuf_free(&f);
return -1;
}
if(fwrite(f.buf, f.used, 1, n->ttyfp) != 1){
fbuf_free(&f);
return -1;
}
fbuf_free(&f);
return 0; return 0;
} }
@ -981,10 +1004,20 @@ unsigned ncdirect_styles(ncdirect* n){
// turn off any specified stylebits // turn off any specified stylebits
int ncdirect_off_styles(ncdirect* n, unsigned stylebits){ int ncdirect_off_styles(ncdirect* n, unsigned stylebits){
uint32_t stylemask = n->stylemask & ~stylebits; uint32_t stylemask = n->stylemask & ~stylebits;
if(ncdirect_style_emit(n, stylemask, n->ttyfp)){ fbuf f = {};
if(fbuf_init_small(&f)){
return -1; return -1;
} }
return 0; if(ncdirect_style_emit(n, stylemask, &f)){
fbuf_free(&f);
return -1;
}
if(fwrite(f.buf, f.used, 1, n->ttyfp) != 1){
fbuf_free(&f);
return -1;
}
fbuf_free(&f);
return -1;
} }
int ncdirect_styles_set(ncdirect* n, unsigned stylebits){ int ncdirect_styles_set(ncdirect* n, unsigned stylebits){
@ -997,9 +1030,18 @@ int ncdirect_set_styles(ncdirect* n, unsigned stylebits){
return -1; return -1;
} }
uint32_t stylemask = stylebits; uint32_t stylemask = stylebits;
if(ncdirect_style_emit(n, stylemask, n->ttyfp)){ fbuf f = {};
if(fbuf_init_small(&f)){
return -1;
}
if(ncdirect_style_emit(n, stylemask, &f)){
return -1;
}
if(fwrite(f.buf, f.used, 1, n->ttyfp) != 1){
fbuf_free(&f);
return -1; return -1;
} }
fbuf_free(&f);
return 0; return 0;
} }
@ -1341,15 +1383,20 @@ int ncdirect_stream(ncdirect* n, const char* filename, ncstreamcb streamer,
ncdirect_raster_frame(n, v, (vopts->flags & NCVISUAL_OPTION_HORALIGNED) ? vopts->x : 0); ncdirect_raster_frame(n, v, (vopts->flags & NCVISUAL_OPTION_HORALIGNED) ? vopts->x : 0);
if(lastid > -1){ if(lastid > -1){
if(n->tcache.pixel_remove){ if(n->tcache.pixel_remove){
if(n->tcache.pixel_remove(lastid, n->ttyfp)){ fbuf f = {};
fbuf_init_small(&f);
if(n->tcache.pixel_remove(lastid, &f) || fwrite(f.buf, f.used, 1, n->ttyfp) != 1){
fbuf_free(&f);
ncvisual_destroy(ncv); ncvisual_destroy(ncv);
return -1; return -1;
} }
fbuf_free(&f);
} }
} }
streamer(ncv, vopts, NULL, curry); streamer(ncv, vopts, NULL, curry);
lastid = thisid; lastid = thisid;
}while(ncvisual_decode(ncv) == 0); }while(ncvisual_decode(ncv) == 0);
ncdirect_flush(n);
ncvisual_destroy(ncv); ncvisual_destroy(ncv);
return 0; return 0;
} }

@ -139,6 +139,15 @@ fbuf_init_small(fbuf* f){
return fbuf_initgrow(f, 1); return fbuf_initgrow(f, 1);
} }
// prepare f with an initial buffer.
static inline int
fbuf_init_small(fbuf* f){
f->used = 0;
f->size = 0;
f->buf = NULL;
return fbuf_grow(f, 0);
}
// prepare f with an initial buffer. // prepare f with an initial buffer.
static inline int static inline int
fbuf_init(fbuf* f){ fbuf_init(fbuf* f){
@ -154,6 +163,12 @@ fbuf_reset(fbuf* f){
f->used = 0; f->used = 0;
} }
// reset usage, but don't shrink the buffer or anything
static inline void
fbuf_reset(fbuf* f){
f->used = 0;
}
static inline int static inline int
fbuf_putc(fbuf* f, char c){ fbuf_putc(fbuf* f, char c){
if(fbuf_grow(f, 1)){ if(fbuf_grow(f, 1)){
@ -219,7 +234,11 @@ fbuf_emit(fbuf* f, const char* esc){
return -1; return -1;
} }
if(fbuf_puts(f, esc) < 0){ if(fbuf_puts(f, esc) < 0){
<<<<<<< HEAD
//logerror("error emitting escape (%s)\n", strerror(errno)); //logerror("error emitting escape (%s)\n", strerror(errno));
=======
logerror("error emitting escape (%s)\n", strerror(errno));
>>>>>>> 3ddb9554d (merrrge)
return -1; return -1;
} }
return 0; return 0;

@ -107,12 +107,10 @@ typedef struct ncplane {
// current presentation state of the terminal. it is carried across render // current presentation state of the terminal. it is carried across render
// instances. initialize everything to 0 on a terminal reset / startup. // instances. initialize everything to 0 on a terminal reset / startup.
typedef struct rasterstate { typedef struct rasterstate {
// we assemble the encoded (rasterized) output in a POSIX memstream, and keep // we assemble the encoded (rasterized) output in an fbuf (a portable POSIX
// it around between uses. this could be a problem if it ever tremendously // memstream, basically), and keep it around between uses. this could be a
// spiked, but that's a highly unlikely situation. // problem if it ever tremendously spiked, but that seems unlikely?
char* mstream; // buffer for rasterizing memstream, see open_memstream(3) fbuf f; // buffer for preparing raster glyph/escape stream
FILE* mstreamfp;// FILE* for rasterizing memstream
size_t mstrsize;// size of rendering memstream
// the current cursor position. this is independent of whether the cursor is // the current cursor position. this is independent of whether the cursor is
// visible. it is the cell at which the next write will take place. this is // visible. it is the cell at which the next write will take place. this is
@ -464,7 +462,7 @@ void sigwinch_handler(int signo);
void init_lang(void); void init_lang(void);
int reset_term_attributes(const tinfo* ti, FILE* fp); int reset_term_attributes(const tinfo* ti, fbuf* f);
// if there were missing elements we wanted from terminfo, bitch about them here // if there were missing elements we wanted from terminfo, bitch about them here
@ -508,7 +506,7 @@ logical_to_virtual(const ncplane* n, int y){
return (y + n->logrow) % n->leny; return (y + n->logrow) % n->leny;
} }
int clear_and_home(notcurses* nc, tinfo* ti, FILE* fp, unsigned flush); int clear_and_home(notcurses* nc, tinfo* ti, fbuf* f);
static inline int static inline int
nfbcellidx(const ncplane* n, int row, int col){ nfbcellidx(const ncplane* n, int row, int col){
@ -704,7 +702,7 @@ sprixel* sprixel_recycle(ncplane* n);
// takes ownership of s on success. // takes ownership of s on success.
int sprixel_load(sprixel* spx, fbuf* f, int pixy, int pixx, int parse_start); int sprixel_load(sprixel* spx, fbuf* f, int pixy, int pixx, int parse_start);
int sprite_init(const tinfo* t, int fd); int sprite_init(const tinfo* t, int fd);
int sprite_clear_all(const tinfo* t, FILE* fp); int sprite_clear_all(const tinfo* t, fbuf* f);
// these three all use absolute coordinates // these three all use absolute coordinates
void sprixel_invalidate(sprixel* s, int y, int x); void sprixel_invalidate(sprixel* s, int y, int x);
void sprixel_movefrom(sprixel* s, int y, int x); void sprixel_movefrom(sprixel* s, int y, int x);
@ -731,17 +729,17 @@ sprite_scrub(const notcurses* n, const ncpile* p, sprixel* s){
// precondition: s->invalidated is SPRIXEL_INVALIDATED or SPRIXEL_MOVED. // precondition: s->invalidated is SPRIXEL_INVALIDATED or SPRIXEL_MOVED.
// returns -1 on error, or the number of bytes written. // returns -1 on error, or the number of bytes written.
static inline int static inline int
sprite_draw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out, sprite_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f,
int y, int x){ int y, int x){
//sprixel_debug(s, stderr); //sprixel_debug(s, stderr);
logdebug("sprixel %u state %d\n", s->id, s->invalidated); logdebug("sprixel %u state %d\n", s->id, s->invalidated);
return ti->pixel_draw(ti, p, s, out, y, x); return ti->pixel_draw(ti, p, s, f, y, x);
} }
// precondition: s->invalidated is SPRIXEL_MOVED or SPRIXEL_INVALIDATED // precondition: s->invalidated is SPRIXEL_MOVED or SPRIXEL_INVALIDATED
// returns -1 on error, or the number of bytes written. // returns -1 on error, or the number of bytes written.
static inline int static inline int
sprite_redraw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out, sprite_redraw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f,
int y, int x){ int y, int x){
//sprixel_debug(s, stderr); //sprixel_debug(s, stderr);
logdebug("sprixel %u state %d\n", s->id, s->invalidated); logdebug("sprixel %u state %d\n", s->id, s->invalidated);
@ -750,21 +748,21 @@ sprite_redraw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out,
// not emit it. we use sixel_maxy_pristine as a side channel to encode // not emit it. we use sixel_maxy_pristine as a side channel to encode
// this version information. // this version information.
bool noscroll = !ti->sixel_maxy_pristine; bool noscroll = !ti->sixel_maxy_pristine;
return ti->pixel_move(s, out, noscroll); return ti->pixel_move(s, f, noscroll);
}else{ }else{
return ti->pixel_draw(ti, p, s, out, y, x); return ti->pixel_draw(ti, p, s, f, y, x);
} }
} }
// present a loaded graphic. only defined for kitty. // present a loaded graphic. only defined for kitty.
static inline int static inline int
sprite_commit(tinfo* ti, FILE* out, sprixel* s, unsigned forcescroll){ sprite_commit(tinfo* ti, fbuf* f, sprixel* s, unsigned forcescroll){
if(ti->pixel_commit){ if(ti->pixel_commit){
// if we are kitty prior to 0.20.0, C=1 isn't available to us, and we must // if we are kitty prior to 0.20.0, C=1 isn't available to us, and we must
// not emit it. we use sixel_maxy_pristine as a side channel to encode // not emit it. we use sixel_maxy_pristine as a side channel to encode
// this version information. direct mode, meanwhile, sets forcescroll. // this version information. direct mode, meanwhile, sets forcescroll.
bool noscroll = !ti->sixel_maxy_pristine && !forcescroll; bool noscroll = !ti->sixel_maxy_pristine && !forcescroll;
if(ti->pixel_commit(out, s, noscroll) < 0){ if(ti->pixel_commit(f, s, noscroll) < 0){
return -1; return -1;
} }
} }
@ -1113,7 +1111,7 @@ int set_fd_nonblocking(int fd, unsigned state, unsigned* oldstate);
// perform any work (even following a clearerr()), despite returning 0 from // perform any work (even following a clearerr()), despite returning 0 from
// that point on. thus, after a fflush() error, even on EAGAIN and friends, // that point on. thus, after a fflush() error, even on EAGAIN and friends,
// you can't use the stream any further. doesn't this make fflush() pretty // you can't use the stream any further. doesn't this make fflush() pretty
// much useless? it sure would seem to, which is why we use a memstream for // much useless? it sure would seem to, which is why we use an fbuf for
// all our important I/O, which we then blit with blocking_write(). if you // all our important I/O, which we then blit with blocking_write(). if you
// care about your data, you'll do the same. // care about your data, you'll do the same.
static inline int static inline int
@ -1141,19 +1139,19 @@ term_emit(const char* seq, FILE* out, bool flush){
} }
static inline int static inline int
term_bg_palindex(const notcurses* nc, FILE* out, unsigned pal){ term_bg_palindex(const notcurses* nc, fbuf* f, unsigned pal){
const char* setab = get_escape(&nc->tcache, ESCAPE_SETAB); const char* setab = get_escape(&nc->tcache, ESCAPE_SETAB);
if(setab){ if(setab){
return term_emit(tiparm(setab, pal), out, false); return fbuf_emit(f, tiparm(setab, pal));
} }
return 0; return 0;
} }
static inline int static inline int
term_fg_palindex(const notcurses* nc, FILE* out, unsigned pal){ term_fg_palindex(const notcurses* nc, fbuf* f, unsigned pal){
const char* setaf = get_escape(&nc->tcache, ESCAPE_SETAF); const char* setaf = get_escape(&nc->tcache, ESCAPE_SETAF);
if(setaf){ if(setaf){
return term_emit(tiparm(setaf, pal), out, false); return fbuf_emit(f, tiparm(setaf, pal));
} }
return 0; return 0;
} }
@ -1162,7 +1160,7 @@ term_fg_palindex(const notcurses* nc, FILE* out, unsigned pal){
// if they are different, and we have the necessary capability, write the // if they are different, and we have the necessary capability, write the
// applicable terminfo entry to 'out'. returns -1 only on a true error. // applicable terminfo entry to 'out'. returns -1 only on a true error.
static int static int
term_setstyle(FILE* out, unsigned cur, unsigned targ, unsigned stylebit, term_setstyle(fbuf* f, unsigned cur, unsigned targ, unsigned stylebit,
const char* ton, const char* toff){ const char* ton, const char* toff){
int ret = 0; int ret = 0;
unsigned curon = cur & stylebit; unsigned curon = cur & stylebit;
@ -1170,11 +1168,11 @@ term_setstyle(FILE* out, unsigned cur, unsigned targ, unsigned stylebit,
if(curon != targon){ if(curon != targon){
if(targon){ if(targon){
if(ton){ if(ton){
ret = term_emit(ton, out, false); ret = fbuf_emit(f, ton);
} }
}else{ }else{
if(toff){ // how did this happen? we can turn it on, but not off? if(toff){ // how did this happen? we can turn it on, but not off?
ret = term_emit(toff, out, false); ret = fbuf_emit(f, toff);
} }
} }
} }
@ -1188,26 +1186,26 @@ term_setstyle(FILE* out, unsigned cur, unsigned targ, unsigned stylebit,
// required an sgr0 (which resets colors), normalized will be non-zero upon // required an sgr0 (which resets colors), normalized will be non-zero upon
// a successful return. // a successful return.
static inline int static inline int
coerce_styles(FILE* out, const tinfo* ti, uint16_t* curstyle, coerce_styles(fbuf* f, const tinfo* ti, uint16_t* curstyle,
uint16_t newstyle, unsigned* normalized){ uint16_t newstyle, unsigned* normalized){
*normalized = 0; // we never currently use sgr0 *normalized = 0; // we never currently use sgr0
int ret = 0; int ret = 0;
ret |= term_setstyle(out, *curstyle, newstyle, NCSTYLE_BOLD, ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_BOLD,
get_escape(ti, ESCAPE_BOLD), get_escape(ti, ESCAPE_NOBOLD)); get_escape(ti, ESCAPE_BOLD), get_escape(ti, ESCAPE_NOBOLD));
ret |= term_setstyle(out, *curstyle, newstyle, NCSTYLE_ITALIC, ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_ITALIC,
get_escape(ti, ESCAPE_SITM), get_escape(ti, ESCAPE_RITM)); get_escape(ti, ESCAPE_SITM), get_escape(ti, ESCAPE_RITM));
ret |= term_setstyle(out, *curstyle, newstyle, NCSTYLE_STRUCK, ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_STRUCK,
get_escape(ti, ESCAPE_SMXX), get_escape(ti, ESCAPE_RMXX)); get_escape(ti, ESCAPE_SMXX), get_escape(ti, ESCAPE_RMXX));
// underline and undercurl are exclusive. if we set one, don't go unsetting // underline and undercurl are exclusive. if we set one, don't go unsetting
// the other. // the other.
if(newstyle & NCSTYLE_UNDERLINE){ // turn on underline, or do nothing if(newstyle & NCSTYLE_UNDERLINE){ // turn on underline, or do nothing
ret |= term_setstyle(out, *curstyle, newstyle, NCSTYLE_UNDERLINE, ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_UNDERLINE,
get_escape(ti, ESCAPE_SMUL), get_escape(ti, ESCAPE_RMUL)); get_escape(ti, ESCAPE_SMUL), get_escape(ti, ESCAPE_RMUL));
}else if(newstyle & NCSTYLE_UNDERCURL){ // turn on undercurl, or do nothing }else if(newstyle & NCSTYLE_UNDERCURL){ // turn on undercurl, or do nothing
ret |= term_setstyle(out, *curstyle, newstyle, NCSTYLE_UNDERCURL, ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_UNDERCURL,
get_escape(ti, ESCAPE_SMULX), get_escape(ti, ESCAPE_SMULNOX)); get_escape(ti, ESCAPE_SMULX), get_escape(ti, ESCAPE_SMULNOX));
}else{ // turn off any underlining }else{ // turn off any underlining
ret |= term_setstyle(out, *curstyle, newstyle, NCSTYLE_UNDERCURL | NCSTYLE_UNDERLINE, ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_UNDERCURL | NCSTYLE_UNDERLINE,
NULL, get_escape(ti, ESCAPE_RMUL)); NULL, get_escape(ti, ESCAPE_RMUL));
} }
*curstyle = newstyle; *curstyle = newstyle;
@ -1230,10 +1228,9 @@ mouse_enable(FILE* out){
} }
static inline int static inline int
mouse_disable(FILE* out){ mouse_disable(fbuf* f){
return term_emit("\x1b[?" SET_BTN_EVENT_MOUSE ";" return fbuf_emit(f, "\x1b[?" SET_BTN_EVENT_MOUSE ";"
/*SET_FOCUS_EVENT_MOUSE ";" */SET_SGR_MODE_MOUSE "l", /*SET_FOCUS_EVENT_MOUSE ";" */SET_SGR_MODE_MOUSE "l");
out, true);
} }
// sync the drawing position to the specified location with as little overhead // sync the drawing position to the specified location with as little overhead
@ -1245,7 +1242,7 @@ mouse_disable(FILE* out){
// if hardcursorpos is non-zero, we always perform a cup. this is done when we // if hardcursorpos is non-zero, we always perform a cup. this is done when we
// don't know where the cursor currently is =]. // don't know where the cursor currently is =].
static inline int static inline int
goto_location(notcurses* nc, FILE* out, int y, int x){ goto_location(notcurses* nc, fbuf* f, int y, int x){
//fprintf(stderr, "going to %d/%d from %d/%d hard: %u\n", y, x, nc->rstate.y, nc->rstate.x, hardcursorpos); //fprintf(stderr, "going to %d/%d from %d/%d hard: %u\n", y, x, nc->rstate.y, nc->rstate.x, hardcursorpos);
int ret = 0; int ret = 0;
// if we don't have hpa, force a cup even if we're only 1 char away. the only // if we don't have hpa, force a cup even if we're only 1 char away. the only
@ -1256,13 +1253,13 @@ goto_location(notcurses* nc, FILE* out, int y, int x){
if(nc->rstate.x == x){ // needn't move shit if(nc->rstate.x == x){ // needn't move shit
return 0; return 0;
} }
if(term_emit(tiparm(hpa, x), out, false)){ if(fbuf_emit(f, tiparm(hpa, x))){
return -1; return -1;
} }
}else{ }else{
// cup is required, no need to verify existence // cup is required, no need to verify existence
const char* cup = get_escape(&nc->tcache, ESCAPE_CUP); const char* cup = get_escape(&nc->tcache, ESCAPE_CUP);
if(term_emit(tiparm(cup, y, x), out, false)){ if(fbuf_emit(f, tiparm(cup, y, x))){
return -1; return -1;
} }
} }
@ -1584,7 +1581,7 @@ int drop_signals(void* nc);
int block_signals(sigset_t* old_blocked_signals); int block_signals(sigset_t* old_blocked_signals);
int unblock_signals(const sigset_t* old_blocked_signals); int unblock_signals(const sigset_t* old_blocked_signals);
void ncvisual_printbanner(const notcurses* nc); void ncvisual_printbanner(fbuf* f);
// alpha comes to us 0--255, but we have only 3 alpha values to map them to // alpha comes to us 0--255, but we have only 3 alpha values to map them to
// (opaque, blended, and transparent). it's necessary that we display // (opaque, blended, and transparent). it's necessary that we display
@ -1668,7 +1665,7 @@ ncdirect_bg_palindex_p(const ncdirect* nc){
return ncchannels_bg_palindex_p(ncdirect_channels(nc)); return ncchannels_bg_palindex_p(ncdirect_channels(nc));
} }
int term_fg_rgb8(const tinfo* ti, FILE* out, unsigned r, unsigned g, unsigned b); int term_fg_rgb8(const tinfo* ti, fbuf* f, unsigned r, unsigned g, unsigned b);
const struct blitset* lookup_blitset(const tinfo* tcache, ncblitter_e setid, bool may_degrade); const struct blitset* lookup_blitset(const tinfo* tcache, ncblitter_e setid, bool may_degrade);
@ -1746,7 +1743,7 @@ resize_bitmap(const uint32_t* bmap, int srows, int scols, size_t sstride,
// prior to calling notcurses_core_init() (by notcurses_init()). // prior to calling notcurses_core_init() (by notcurses_init()).
typedef struct ncvisual_implementation { typedef struct ncvisual_implementation {
int (*visual_init)(int loglevel); int (*visual_init)(int loglevel);
void (*visual_printbanner)(const struct notcurses* nc); void (*visual_printbanner)(fbuf* f);
int (*visual_blit)(struct ncvisual* ncv, int rows, int cols, ncplane* n, int (*visual_blit)(struct ncvisual* ncv, int rows, int cols, ncplane* n,
const struct blitset* bset, const blitterargs* barg); const struct blitset* bset, const blitterargs* barg);
struct ncvisual* (*visual_create)(void); struct ncvisual* (*visual_create)(void);

@ -27,14 +27,14 @@ int iterm_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
} }
// spit out the control sequence and data. // spit out the control sequence and data.
int iterm_draw(const tinfo* ti, const ncpile *p, sprixel* s, FILE* out, int y, int x){ int iterm_draw(const tinfo* ti, const ncpile *p, sprixel* s, fbuf* f, int y, int x){
(void)ti; (void)ti;
if(p){ if(p){
if(goto_location(p->nc, out, y, x)){ if(goto_location(p->nc, f, y, x)){
return -1; return -1;
} }
} }
if(fwrite(s->glyph.buf, s->glyph.used, 1, out) != 1){ if(fbuf_putn(f, s->glyph.buf, s->glyph.used) < 0){
return -1; return -1;
} }
return s->glyph.used; return s->glyph.used;

@ -535,9 +535,9 @@ int kitty_wipe(sprixel* s, int ycell, int xcell){
return -1; return -1;
} }
int kitty_commit(FILE* fp, sprixel* s, unsigned noscroll){ int kitty_commit(fbuf* f, sprixel* s, unsigned noscroll){
loginfo("Committing Kitty graphic id %u\n", s->id); loginfo("Committing Kitty graphic id %u\n", s->id);
fprintf(fp, "\e_Ga=p,i=%u,p=1,q=2%s\e\\", s->id, noscroll ? ",C=1" : ""); fbuf_printf(f, "\e_Ga=p,i=%u,p=1,q=2%s\e\\", s->id, noscroll ? ",C=1" : "");
s->invalidated = SPRIXEL_QUIESCENT; s->invalidated = SPRIXEL_QUIESCENT;
return 0; return 0;
} }
@ -1055,9 +1055,9 @@ int kitty_blit_selfref(ncplane* n, int linesize, const void* data,
KITTY_SELFREF); KITTY_SELFREF);
} }
int kitty_remove(int id, FILE* out){ int kitty_remove(int id, fbuf* f){
loginfo("Removing graphic %u\n", id); loginfo("Removing graphic %u\n", id);
if(fprintf(out, "\e_Ga=d,d=i,i=%d\e\\", id) < 0){ if(fbuf_printf(f, "\e_Ga=d,d=i,i=%d\e\\", id) < 0){
return -1; return -1;
} }
return 0; return 0;
@ -1094,7 +1094,7 @@ int kitty_scrub(const ncpile* p, sprixel* s){
} }
// returns the number of bytes written // returns the number of bytes written
int kitty_draw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out, int kitty_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f,
int y, int x){ int y, int x){
(void)ti; (void)ti;
(void)p; (void)p;
@ -1108,7 +1108,7 @@ int kitty_draw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out,
int ret = s->glyph.used; int ret = s->glyph.used;
logdebug("Writing out %zub for %u\n", s->glyph.used, s->id); logdebug("Writing out %zub for %u\n", s->glyph.used, s->id);
if(ret){ if(ret){
if(fwrite(s->glyph.buf, s->glyph.used, 1, out) != 1){ if(fbuf_putn(f, s->glyph.buf, s->glyph.used) < 0){
ret = -1; ret = -1;
} }
} }
@ -1122,10 +1122,10 @@ int kitty_draw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out,
} }
// returns -1 on failure, 0 on success (move bytes do not count for sprixel stats) // returns -1 on failure, 0 on success (move bytes do not count for sprixel stats)
int kitty_move(sprixel* s, FILE* out, unsigned noscroll){ int kitty_move(sprixel* s, fbuf* f, unsigned noscroll){
int ret = 0; int ret = 0;
if(fprintf(out, "\e_Ga=p,i=%d,p=1,q=2%s\e\\", s->id, if(fbuf_printf(f, "\e_Ga=p,i=%d,p=1,q=2%s\e\\", s->id,
noscroll ? ",C=1" : "") < 0){ noscroll ? ",C=1" : "") < 0){
ret = -1; ret = -1;
} }
s->invalidated = SPRIXEL_QUIESCENT; s->invalidated = SPRIXEL_QUIESCENT;
@ -1133,14 +1133,17 @@ int kitty_move(sprixel* s, FILE* out, unsigned noscroll){
} }
// clears all kitty bitmaps // clears all kitty bitmaps
int kitty_clear_all(FILE* fp){ int kitty_clear_all(fbuf* f){
//fprintf(stderr, "KITTY UNIVERSAL ERASE\n"); //fprintf(stderr, "KITTY UNIVERSAL ERASE\n");
return term_emit("\e_Ga=d,q=2\e\\", fp, false); if(fbuf_putn(f, "\x1b_Ga=d,q=2\x1b\\", 12) < 0){
return -1;
}
return 0;
} }
int kitty_shutdown(FILE* fp){ int kitty_shutdown(fbuf* f){
// FIXME need to close off any open kitty bitmap emission, or we will // FIXME need to close off any open kitty bitmap emission, or we will
// lock up the terminal // lock up the terminal
(void)fp; (void)f;
return 0; return 0;
} }

@ -160,9 +160,9 @@ int fbcon_scrub(const struct ncpile* p, sprixel* s){
return sixel_scrub(p, s); return sixel_scrub(p, s);
} }
int fbcon_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, FILE* out, int y, int x){ int fbcon_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, fbuf* f, int y, int x){
(void)p; (void)p;
(void)out; // we don't write to the stream (void)f; // we don't write to the stream
int wrote = 0; int wrote = 0;
for(unsigned l = 0 ; l < (unsigned)s->pixy && l < ti->pixy ; ++l){ for(unsigned l = 0 ; l < (unsigned)s->pixy && l < ti->pixy ; ++l){
// FIXME pixel size isn't necessarily 4B, line isn't necessarily psize*pixx // FIXME pixel size isn't necessarily 4B, line isn't necessarily psize*pixx

@ -31,19 +31,19 @@ void notcurses_version_components(int* major, int* minor, int* patch, int* tweak
// reset the current colors, styles, and palette. called on startup (to purge // reset the current colors, styles, and palette. called on startup (to purge
// any preexisting styling) and shutdown (to not affect further programs). // any preexisting styling) and shutdown (to not affect further programs).
int reset_term_attributes(const tinfo* ti, FILE* fp){ int reset_term_attributes(const tinfo* ti, fbuf* f){
int ret = 0; int ret = 0;
const char* esc; const char* esc;
if((esc = get_escape(ti, ESCAPE_RESTORECOLORS)) && term_emit(esc, fp, false)){ if((esc = get_escape(ti, ESCAPE_RESTORECOLORS)) && fbuf_emit(f, esc)){
ret = -1; ret = -1;
} }
if((esc = get_escape(ti, ESCAPE_OP)) && term_emit(esc, fp, false)){ if((esc = get_escape(ti, ESCAPE_OP)) && fbuf_emit(f, esc)){
ret = -1; ret = -1;
} }
if((esc = get_escape(ti, ESCAPE_SGR0)) && term_emit(esc, fp, false)){ if((esc = get_escape(ti, ESCAPE_SGR0)) && fbuf_emit(f, esc)){
ret = -1; ret = -1;
} }
if((esc = get_escape(ti, ESCAPE_OC)) && term_emit(esc, fp, false)){ if((esc = get_escape(ti, ESCAPE_OC)) && fbuf_emit(f, esc)){
ret = -1; ret = -1;
} }
return ret; return ret;
@ -58,8 +58,8 @@ notcurses_stop_minimal(void* vnc){
notcurses* nc = vnc; notcurses* nc = vnc;
// collect output into the memstream buffer, and then dump it directly using // collect output into the memstream buffer, and then dump it directly using
// blocking_write(), to avoid problems with unreliable fflush(). // blocking_write(), to avoid problems with unreliable fflush().
FILE* out = nc->rstate.mstreamfp; fbuf* f = &nc->rstate.f;
fseeko(out, 0, SEEK_SET); fbuf_reset(f);
int ret = 0; int ret = 0;
ret |= drop_signals(nc); ret |= drop_signals(nc);
// be sure to write the restoration sequences *prior* to running rmcup, as // be sure to write the restoration sequences *prior* to running rmcup, as
@ -69,32 +69,29 @@ notcurses_stop_minimal(void* vnc){
// byte. if we leave an active escape open, it can lock up the terminal. // byte. if we leave an active escape open, it can lock up the terminal.
// we only want to do it when in the middle of a rasterization, though. FIXME // we only want to do it when in the middle of a rasterization, though. FIXME
if(nc->tcache.pixel_shutdown){ if(nc->tcache.pixel_shutdown){
ret |= nc->tcache.pixel_shutdown(out); ret |= nc->tcache.pixel_shutdown(f);
} }
ret |= mouse_disable(out); ret |= mouse_disable(f);
ret |= reset_term_attributes(&nc->tcache, out); ret |= reset_term_attributes(&nc->tcache, f);
if(nc->ttyfd >= 0){ if(nc->ttyfd >= 0){
if((esc = get_escape(&nc->tcache, ESCAPE_RMCUP))){ if((esc = get_escape(&nc->tcache, ESCAPE_RMCUP))){
if(sprite_clear_all(&nc->tcache, out)){ if(sprite_clear_all(&nc->tcache, f)){
ret = -1; ret = -1;
} }
if(term_emit(esc, out, false)){ if(fbuf_emit(f, esc)){
ret = -1; ret = -1;
} }
} }
ret |= tcsetattr(nc->ttyfd, TCSANOW, &nc->tcache.tpreserved); ret |= tcsetattr(nc->ttyfd, TCSANOW, &nc->tcache.tpreserved);
} }
if((esc = get_escape(&nc->tcache, ESCAPE_RMKX)) && term_emit(esc, out, false)){ if((esc = get_escape(&nc->tcache, ESCAPE_RMKX)) && fbuf_emit(f, esc)){
ret = -1; ret = -1;
} }
const char* cnorm = get_escape(&nc->tcache, ESCAPE_CNORM); const char* cnorm = get_escape(&nc->tcache, ESCAPE_CNORM);
if(cnorm && term_emit(cnorm, out, false)){ if(cnorm && fbuf_emit(f, cnorm)){
ret = -1; ret = -1;
} }
if(fflush(out)){ return blocking_write(fileno(nc->ttyfp), f->buf, f->used);
return -1;
}
return blocking_write(fileno(nc->ttyfp), nc->rstate.mstream, nc->rstate.mstrsize);
} }
// make a heap-allocated wchar_t expansion of the multibyte string at s // make a heap-allocated wchar_t expansion of the multibyte string at s
@ -895,38 +892,39 @@ init_banner_warnings(const notcurses* nc, FILE* out){
// unless the suppress_banner flag was set, print some version information and // unless the suppress_banner flag was set, print some version information and
// (if applicable) warnings to ttyfp. we are not yet on the alternate screen. // (if applicable) warnings to ttyfp. we are not yet on the alternate screen.
static int static int
init_banner(const notcurses* nc){ init_banner(const notcurses* nc, fbuf* f){
if(!nc->suppress_banner){ if(!nc->suppress_banner){
char prefixbuf[BPREFIXSTRLEN + 1]; char prefixbuf[BPREFIXSTRLEN + 1];
term_fg_palindex(nc, stdout, 50 % nc->tcache.caps.colors); term_fg_palindex(nc, f, 50 % nc->tcache.caps.colors);
fprintf(nc->ttyfp, "notcurses %s on %s %s\n", notcurses_version(), fbuf_printf(f, "notcurses %s on %s %s\n", notcurses_version(),
nc->tcache.termname ? nc->tcache.termname : "?", nc->tcache.termname ? nc->tcache.termname : "?",
nc->tcache.termversion ? nc->tcache.termversion : ""); nc->tcache.termversion ? nc->tcache.termversion : "");
term_fg_palindex(nc, stdout, nc->tcache.caps.colors <= 256 ? term_fg_palindex(nc, f, nc->tcache.caps.colors <= 256 ?
14 % nc->tcache.caps.colors : 0x2080e0); 14 % nc->tcache.caps.colors : 0x2080e0);
if(nc->tcache.cellpixy && nc->tcache.cellpixx){ if(nc->tcache.cellpixy && nc->tcache.cellpixx){
fprintf(nc->ttyfp, "%d rows (%dpx) %d cols (%dpx) %dx%d %zuB crend %u colors", fbuf_printf(f, "%d rows (%dpx) %d cols (%dpx) %dx%d %zuB crend %u colors",
nc->stdplane->leny, nc->tcache.cellpixy, nc->stdplane->leny, nc->tcache.cellpixy,
nc->stdplane->lenx, nc->tcache.cellpixx, nc->stdplane->lenx, nc->tcache.cellpixx,
nc->stdplane->leny * nc->tcache.cellpixy, nc->stdplane->leny * nc->tcache.cellpixy,
nc->stdplane->lenx * nc->tcache.cellpixx, nc->stdplane->lenx * nc->tcache.cellpixx,
sizeof(struct crender), nc->tcache.caps.colors); sizeof(struct crender), nc->tcache.caps.colors);
}else{ }else{
fprintf(nc->ttyfp, "%d rows %d cols (%sB) %zuB crend %d colors", fbuf_printf(f, "%d rows %d cols (%sB) %zuB crend %d colors",
nc->stdplane->leny, nc->stdplane->lenx, nc->stdplane->leny, nc->stdplane->lenx,
bprefix(nc->stats.s.fbbytes, 1, prefixbuf, 0), bprefix(nc->stats.s.fbbytes, 1, prefixbuf, 0),
sizeof(struct crender), nc->tcache.caps.colors); sizeof(struct crender), nc->tcache.caps.colors);
} }
const char* setaf; const char* setaf;
if(nc->tcache.caps.rgb && (setaf = get_escape(&nc->tcache, ESCAPE_SETAF))){ if(nc->tcache.caps.rgb && (setaf = get_escape(&nc->tcache, ESCAPE_SETAF))){
ncfputc('+', nc->ttyfp); fbuf_putc(f, '+');
term_fg_rgb8(&nc->tcache, stdout, 0xe0, 0x60, 0x60); term_fg_rgb8(&nc->tcache, f, 0xe0, 0x60, 0x60);
ncfputc('R', nc->ttyfp); fbuf_putc(f, '+');
term_fg_rgb8(&nc->tcache, stdout, 0x60, 0xe0, 0x60); fbuf_putc(f, 'r');
ncfputc('G', nc->ttyfp); term_fg_rgb8(&nc->tcache, f, 0x60, 0xe0, 0x60);
term_fg_rgb8(&nc->tcache, stdout, 0x20, 0x80, 0xff); fbuf_putc(f, 'g');
ncfputc('B', nc->ttyfp); term_fg_rgb8(&nc->tcache, f, 0x20, 0x80, 0xff);
term_fg_palindex(nc, nc->ttyfp, nc->tcache.caps.colors <= 256 ? fbuf_putc(f, 'b');
term_fg_palindex(nc, f, nc->tcache.caps.colors <= 256 ?
14 % nc->tcache.caps.colors : 0x2080e0); 14 % nc->tcache.caps.colors : 0x2080e0);
} }
fprintf(nc->ttyfp, "\n%s%s, %zuB %s cells\nterminfo from %s zlib %s\n", fprintf(nc->ttyfp, "\n%s%s, %zuB %s cells\nterminfo from %s zlib %s\n",
@ -951,12 +949,12 @@ init_banner(const notcurses* nc){
#error "No __BYTE_ORDER__ definition" #error "No __BYTE_ORDER__ definition"
#endif #endif
curses_version(), zlibVersion()); curses_version(), zlibVersion());
ncvisual_printbanner(nc); ncvisual_printbanner(f);
init_banner_warnings(nc, nc->ttyfp); init_banner_warnings(nc, f);
const char* esc; const char* esc;
if( (esc = get_escape(&nc->tcache, ESCAPE_SGR0)) || if( (esc = get_escape(&nc->tcache, ESCAPE_SGR0)) ||
(esc = get_escape(&nc->tcache, ESCAPE_OP))){ (esc = get_escape(&nc->tcache, ESCAPE_OP))){
term_emit(esc, nc->ttyfp, false); fbuf_emit(f, esc);
} }
} }
return 0; return 0;
@ -1050,8 +1048,9 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
return ret; return ret;
} }
ret->last_pile = NULL; ret->last_pile = NULL;
ret->rstate.mstream = NULL; ret->rstate.f.buf = NULL;
ret->rstate.mstreamfp = NULL; ret->rstate.f.used = 0;
ret->rstate.f.size = 0;
ret->loglevel = opts->loglevel; ret->loglevel = opts->loglevel;
if(!(opts->flags & NCOPTION_INHIBIT_SETLOCALE)){ if(!(opts->flags & NCOPTION_INHIBIT_SETLOCALE)){
init_lang(); init_lang();
@ -1102,9 +1101,9 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
free(ret); free(ret);
return NULL; return NULL;
} }
// mstreamfp is needed by notcurses_stop_minimal, so this must be done // the fbuf is needed by notcurses_stop_minimal, so this must be done
// before registering fatal signal handlers. // before registering fatal signal handlers.
if((ret->rstate.mstreamfp = open_memstream(&ret->rstate.mstream, &ret->rstate.mstrsize)) == NULL){ if(fbuf_init(&ret->rstate.f)){
pthread_mutex_destroy(&ret->pilelock); pthread_mutex_destroy(&ret->pilelock);
pthread_mutex_destroy(&ret->stats.lock); pthread_mutex_destroy(&ret->stats.lock);
free(ret); free(ret);
@ -1113,7 +1112,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
if(setup_signals(ret, (opts->flags & NCOPTION_NO_QUIT_SIGHANDLERS), if(setup_signals(ret, (opts->flags & NCOPTION_NO_QUIT_SIGHANDLERS),
(opts->flags & NCOPTION_NO_WINCH_SIGHANDLER), (opts->flags & NCOPTION_NO_WINCH_SIGHANDLER),
notcurses_stop_minimal)){ notcurses_stop_minimal)){
fclose(ret->rstate.mstreamfp); fbuf_free(&ret->rstate.f);
pthread_mutex_destroy(&ret->pilelock); pthread_mutex_destroy(&ret->pilelock);
pthread_mutex_destroy(&ret->stats.lock); pthread_mutex_destroy(&ret->stats.lock);
free(ret); free(ret);
@ -1126,7 +1125,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
if(setupterm(opts->termtype, ret->ttyfd, &termerr) != OK){ if(setupterm(opts->termtype, ret->ttyfd, &termerr) != OK){
logpanic("Terminfo error %d (see terminfo(3ncurses))\n", termerr); logpanic("Terminfo error %d (see terminfo(3ncurses))\n", termerr);
drop_signals(ret); drop_signals(ret);
fclose(ret->rstate.mstreamfp); fbuf_free(&ret->rstate.f);
pthread_mutex_destroy(&ret->stats.lock); pthread_mutex_destroy(&ret->stats.lock);
pthread_mutex_destroy(&ret->pilelock); pthread_mutex_destroy(&ret->pilelock);
free(ret); free(ret);
@ -1168,7 +1167,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
logerror("Couldn't create the initial plane (bad margins?)\n"); logerror("Couldn't create the initial plane (bad margins?)\n");
goto err; goto err;
} }
reset_term_attributes(&ret->tcache, ret->ttyfp); reset_term_attributes(&ret->tcache, &ret->rstate.f);
const char* smkx = get_escape(&ret->tcache, ESCAPE_SMKX); const char* smkx = get_escape(&ret->tcache, ESCAPE_SMKX);
if(smkx && term_emit(smkx, ret->ttyfp, false)){ if(smkx && term_emit(smkx, ret->ttyfp, false)){
free_plane(ret->stdplane); free_plane(ret->stdplane);
@ -1184,7 +1183,10 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
free_plane(ret->stdplane); free_plane(ret->stdplane);
goto err; goto err;
} }
init_banner(ret); ret->rstate.x = ret->rstate.y = -1;
init_banner(ret, &ret->rstate.f);
fwrite(ret->rstate.f.buf, ret->rstate.f.used, 1, ret->ttyfp);
fbuf_reset(&ret->rstate.f);
if(ncflush(ret->ttyfp)){ if(ncflush(ret->ttyfp)){
free_plane(ret->stdplane); free_plane(ret->stdplane);
goto err; goto err;
@ -1217,7 +1219,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
// perform an explicit clear since the alternate screen was requested // perform an explicit clear since the alternate screen was requested
// (smcup *might* clear, but who knows? and it might not have been // (smcup *might* clear, but who knows? and it might not have been
// available in any case). // available in any case).
if(clear_and_home(ret, &ret->tcache, ret->ttyfp, true)){ if(clear_and_home(ret, &ret->tcache, &ret->rstate.f)){
goto err; goto err;
} }
// no need to reestablish a preserved cursor -- that only affects the // no need to reestablish a preserved cursor -- that only affects the
@ -1227,8 +1229,14 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
// the sprite clear ought take place within the alternate screen, if it's // the sprite clear ought take place within the alternate screen, if it's
// being used. // being used.
if(!(opts->flags & NCOPTION_NO_CLEAR_BITMAPS)){ if(!(opts->flags & NCOPTION_NO_CLEAR_BITMAPS)){
if(sprite_clear_all(&ret->tcache, ret->ttyfp) || fflush(ret->ttyfp)){ if(sprite_clear_all(&ret->tcache, &ret->rstate.f)){
free_plane(ret->stdplane); goto err;
}
}
if(ret->rstate.f.used){
fwrite(ret->rstate.f.buf, ret->rstate.f.used, 1, ret->ttyfp);
fbuf_reset(&ret->rstate.f);
if(ncflush(ret->ttyfp)){
goto err; goto err;
} }
} }
@ -1237,10 +1245,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
err: err:
logpanic("Alas, you will not be going to space today.\n"); logpanic("Alas, you will not be going to space today.\n");
// FIXME looks like we have some memory leaks on this error path? // FIXME looks like we have some memory leaks on this error path?
if(ret->rstate.mstreamfp){ fbuf_free(&ret->rstate.f);
fclose(ret->rstate.mstreamfp);
}
free(ret->rstate.mstream);
tcsetattr(ret->ttyfd, TCSANOW, &ret->tcache.tpreserved); tcsetattr(ret->ttyfd, TCSANOW, &ret->tcache.tpreserved);
drop_signals(ret); drop_signals(ret);
pthread_mutex_destroy(&ret->stats.lock); pthread_mutex_destroy(&ret->stats.lock);
@ -1297,7 +1302,9 @@ int notcurses_stop(notcurses* nc){
printf("\v"); printf("\v");
--targy; --targy;
} }
goto_location(nc, stdout, targy, 0); fbuf_reset(&nc->rstate.f);
goto_location(nc, &nc->rstate.f, targy, 0);
fwrite(nc->rstate.f.buf, nc->rstate.f.used, 1, stdout);
fflush(stdout); fflush(stdout);
} }
ret |= set_fd_nonblocking(nc->tcache.input.infd, nc->stdio_blocking_save, NULL); ret |= set_fd_nonblocking(nc->tcache.input.infd, nc->stdio_blocking_save, NULL);
@ -1305,15 +1312,12 @@ int notcurses_stop(notcurses* nc){
notcurses_drop_planes(nc); notcurses_drop_planes(nc);
free_plane(nc->stdplane); free_plane(nc->stdplane);
} }
if(nc->rstate.mstreamfp){
fclose(nc->rstate.mstreamfp);
}
if(nc->ttyfd >= 0){ if(nc->ttyfd >= 0){
ret |= close(nc->ttyfd); ret |= close(nc->ttyfd);
} }
egcpool_dump(&nc->pool); egcpool_dump(&nc->pool);
free(nc->lastframe); free(nc->lastframe);
free(nc->rstate.mstream); fbuf_free(&nc->rstate.f);
// get any current stats loaded into stash_stats // get any current stats loaded into stash_stats
notcurses_stats_reset(nc, NULL); notcurses_stats_reset(nc, NULL);
if(!nc->suppress_banner){ if(!nc->suppress_banner){
@ -2226,9 +2230,19 @@ int notcurses_mouse_enable(notcurses* n){
// this seems to work (note difference in suffix, 'l' vs 'h'), but what about // this seems to work (note difference in suffix, 'l' vs 'h'), but what about
// the sequences 1000 etc? // the sequences 1000 etc?
int notcurses_mouse_disable(notcurses* n){ int notcurses_mouse_disable(notcurses* n){
if(mouse_disable(n->ttyfp)){ fbuf f = {};
if(fbuf_init_small(&f)){
return -1;
}
if(mouse_disable(&f)){
fbuf_free(&f);
return -1;
}
if(fwrite(f.buf, f.used, 1, n->ttyfp) != 1 || fflush(n->ttyfp) == EOF){
fbuf_free(&f);
return -1; return -1;
} }
fbuf_free(&f);
return 0; return 0;
} }

@ -542,19 +542,19 @@ int ncplane_mergedown_simple(ncplane* restrict src, ncplane* restrict dst){
// write the nccell's UTF-8 extended grapheme cluster to the provided FILE*. // write the nccell's UTF-8 extended grapheme cluster to the provided FILE*.
static int static int
term_putc(FILE* out, const egcpool* e, const nccell* c){ term_putc(fbuf* f, const egcpool* e, const nccell* c){
if(cell_simple_p(c)){ if(cell_simple_p(c)){
//fprintf(stderr, "[%.4s] %08x\n", (const char*)&c->gcluster, c->gcluster); } //fprintf(stderr, "[%.4s] %08x\n", (const char*)&c->gcluster, c->gcluster); }
// we must not have any 'cntrl' characters at this point // we must not have any 'cntrl' characters at this point
if(c->gcluster == 0){ if(c->gcluster == 0){
if(ncfputc(' ', out) == EOF){ if(fbuf_putc(f, ' ') < 0){
return -1; return -1;
} }
}else if(ncfputs((const char*)&c->gcluster, out) == EOF){ }else if(fbuf_puts(f, (const char*)&c->gcluster) == EOF){
return -1; return -1;
} }
}else{ }else{
if(ncfputs(egcpool_extended_gcluster(e, c), out) == EOF){ if(fbuf_puts(f, egcpool_extended_gcluster(e, c)) == EOF){
return -1; return -1;
} }
} }
@ -563,9 +563,9 @@ term_putc(FILE* out, const egcpool* e, const nccell* c){
// write any escape sequences necessary to set the desired style // write any escape sequences necessary to set the desired style
static inline int static inline int
term_setstyles(FILE* out, notcurses* nc, const nccell* c){ term_setstyles(fbuf* f, notcurses* nc, const nccell* c){
unsigned normalized = false; unsigned normalized = false;
int ret = coerce_styles(out, &nc->tcache, &nc->rstate.curattr, int ret = coerce_styles(f, &nc->tcache, &nc->rstate.curattr,
nccell_styles(c), &normalized); nccell_styles(c), &normalized);
if(normalized){ if(normalized){
nc->rstate.fgdefelidable = true; nc->rstate.fgdefelidable = true;
@ -598,15 +598,15 @@ static const char* const NUMBERS[] = {
"241;", "242;", "243;", "244;", "245;", "246;", "247;", "248;", "249;", "250;", "251;", "252;", "253;", "254;", "255;", }; "241;", "242;", "243;", "244;", "245;", "246;", "247;", "248;", "249;", "250;", "251;", "252;", "253;", "254;", "255;", };
static inline int static inline int
term_esc_rgb(FILE* out, bool foreground, unsigned r, unsigned g, unsigned b){ term_esc_rgb(fbuf* f, bool foreground, unsigned r, unsigned g, unsigned b){
// The correct way to do this is using tiparm+tputs, but doing so (at least // The correct way to do this is using tiparm+tputs, but doing so (at least
// as of terminfo 6.1.20191019) both emits ~3% more bytes for a run of 'rgb' // as of terminfo 6.1.20191019) both emits ~3% more bytes for a run of 'rgb'
// and gives rise to some inaccurate colors (possibly due to special handling // and gives rise to some inaccurate colors (possibly due to special handling
// of values < 256; I'm not at this time sure). So we just cons up our own. // of values < 256; I'm not at this time sure). So we just cons up our own.
/*if(esc == 4){ /*if(esc == 4){
return term_emit("setab", tiparm(nc->setab, (int)((r << 16u) | (g << 8u) | b)), out, false); return fbuf_emit(f, "setab", tiparm(nc->setab, (int)((r << 16u) | (g << 8u) | b)));
}else if(esc == 3){ }else if(esc == 3){
return term_emit("setaf", tiparm(nc->setaf, (int)((r << 16u) | (g << 8u) | b)), out, false); return fbuf_emit(f, "setaf", tiparm(nc->setaf, (int)((r << 16u) | (g << 8u) | b)));
}else{ }else{
return -1; return -1;
}*/ }*/
@ -638,14 +638,14 @@ term_esc_rgb(FILE* out, bool foreground, unsigned r, unsigned g, unsigned b){
} }
rgbbuf[offset++] = 'm'; rgbbuf[offset++] = 'm';
rgbbuf[offset] = '\0'; rgbbuf[offset] = '\0';
if(fwrite(rgbbuf, offset, 1, out) != 1){ if(fbuf_putn(f, rgbbuf, offset) < 0){
return -1; return -1;
} }
return 0; return 0;
} }
static inline int static inline int
term_bg_rgb8(const tinfo* ti, FILE* out, unsigned r, unsigned g, unsigned b){ term_bg_rgb8(const tinfo* ti, fbuf* f, unsigned r, unsigned g, unsigned b){
// We typically want to use tputs() and tiperm() to acquire and write the // We typically want to use tputs() and tiperm() to acquire and write the
// escapes, as these take into account terminal-specific delays, padding, // escapes, as these take into account terminal-specific delays, padding,
// etc. For the case of DirectColor, there is no suitable terminfo entry, but // etc. For the case of DirectColor, there is no suitable terminfo entry, but
@ -663,7 +663,7 @@ term_bg_rgb8(const tinfo* ti, FILE* out, unsigned r, unsigned g, unsigned b){
} }
} }
} }
return term_esc_rgb(out, false, r, g, b); return term_esc_rgb(f, false, r, g, b);
}else{ }else{
const char* setab = get_escape(ti, ESCAPE_SETAB); const char* setab = get_escape(ti, ESCAPE_SETAB);
if(setab){ if(setab){
@ -672,23 +672,23 @@ term_bg_rgb8(const tinfo* ti, FILE* out, unsigned r, unsigned g, unsigned b){
// a single screen, start... combining close ones? For 8-color mode, simple // a single screen, start... combining close ones? For 8-color mode, simple
// interpolation. I have no idea what to do for 88 colors. FIXME // interpolation. I have no idea what to do for 88 colors. FIXME
if(ti->caps.colors >= 256){ if(ti->caps.colors >= 256){
return term_emit(tiparm(setab, rgb_quantize_256(r, g, b)), out, false); return fbuf_emit(f, tiparm(setab, rgb_quantize_256(r, g, b)));
}else if(ti->caps.colors >= 8){ }else if(ti->caps.colors >= 8){
return term_emit(tiparm(setab, rgb_quantize_8(r, g, b)), out, false); return fbuf_emit(f, tiparm(setab, rgb_quantize_8(r, g, b)));
} }
} }
} }
return 0; return 0;
} }
int term_fg_rgb8(const tinfo* ti, FILE* out, unsigned r, unsigned g, unsigned b){ int term_fg_rgb8(const tinfo* ti, fbuf* f, unsigned r, unsigned g, unsigned b){
// We typically want to use tputs() and tiperm() to acquire and write the // We typically want to use tputs() and tiperm() to acquire and write the
// escapes, as these take into account terminal-specific delays, padding, // escapes, as these take into account terminal-specific delays, padding,
// etc. For the case of DirectColor, there is no suitable terminfo entry, but // etc. For the case of DirectColor, there is no suitable terminfo entry, but
// we're also in that case working with hopefully more robust terminals. // we're also in that case working with hopefully more robust terminals.
// If it doesn't work, eh, it doesn't work. Fuck the world; save yourself. // If it doesn't work, eh, it doesn't work. Fuck the world; save yourself.
if(ti->caps.rgb){ if(ti->caps.rgb){
return term_esc_rgb(out, true, r, g, b); return term_esc_rgb(f, true, r, g, b);
}else{ }else{
const char* setaf = get_escape(ti, ESCAPE_SETAF); const char* setaf = get_escape(ti, ESCAPE_SETAF);
if(setaf){ if(setaf){
@ -697,9 +697,9 @@ int term_fg_rgb8(const tinfo* ti, FILE* out, unsigned r, unsigned g, unsigned b)
// a single screen, start... combining close ones? For 8-color mode, simple // a single screen, start... combining close ones? For 8-color mode, simple
// interpolation. I have no idea what to do for 88 colors. FIXME // interpolation. I have no idea what to do for 88 colors. FIXME
if(ti->caps.colors >= 256){ if(ti->caps.colors >= 256){
return term_emit(tiparm(setaf, rgb_quantize_256(r, g, b)), out, false); return fbuf_emit(f, tiparm(setaf, rgb_quantize_256(r, g, b)));
}else if(ti->caps.colors >= 8){ }else if(ti->caps.colors >= 8){
return term_emit(tiparm(setaf, rgb_quantize_8(r, g, b)), out, false); return fbuf_emit(f, tiparm(setaf, rgb_quantize_8(r, g, b)));
} }
} }
} }
@ -707,7 +707,7 @@ int term_fg_rgb8(const tinfo* ti, FILE* out, unsigned r, unsigned g, unsigned b)
} }
static inline int static inline int
update_palette(notcurses* nc, FILE* out){ update_palette(notcurses* nc, fbuf* f){
if(nc->tcache.caps.can_change_colors){ if(nc->tcache.caps.can_change_colors){
const char* initc = get_escape(&nc->tcache, ESCAPE_INITC); const char* initc = get_escape(&nc->tcache, ESCAPE_INITC);
for(size_t damageidx = 0 ; damageidx < sizeof(nc->palette.chans) / sizeof(*nc->palette.chans) ; ++damageidx){ for(size_t damageidx = 0 ; damageidx < sizeof(nc->palette.chans) / sizeof(*nc->palette.chans) ; ++damageidx){
@ -719,7 +719,9 @@ update_palette(notcurses* nc, FILE* out){
r = r * 1000 / 255; r = r * 1000 / 255;
g = g * 1000 / 255; g = g * 1000 / 255;
b = b * 1000 / 255; b = b * 1000 / 255;
term_emit(tiparm(initc, damageidx, r, g, b), out, false); if(fbuf_emit(f, tiparm(initc, damageidx, r, g, b)) < 0){
return -1;
}
nc->palette_damage[damageidx] = false; nc->palette_damage[damageidx] = false;
} }
} }
@ -730,7 +732,7 @@ update_palette(notcurses* nc, FILE* out){
// at least one of the foreground and background are the default. emit the // at least one of the foreground and background are the default. emit the
// necessary return to default (if one is necessary), and update rstate. // necessary return to default (if one is necessary), and update rstate.
static inline int static inline int
raster_defaults(notcurses* nc, bool fgdef, bool bgdef, FILE* out){ raster_defaults(notcurses* nc, bool fgdef, bool bgdef, fbuf* f){
const char* op = get_escape(&nc->tcache, ESCAPE_OP); const char* op = get_escape(&nc->tcache, ESCAPE_OP);
if(op == NULL){ // if we don't have op, we don't have fgop/bgop if(op == NULL){ // if we don't have op, we don't have fgop/bgop
return 0; return 0;
@ -743,7 +745,7 @@ raster_defaults(notcurses* nc, bool fgdef, bool bgdef, FILE* out){
++nc->stats.s.defaultelisions; ++nc->stats.s.defaultelisions;
return 0; return 0;
}else if((mustsetfg && mustsetbg) || !fgop || !bgop){ }else if((mustsetfg && mustsetbg) || !fgop || !bgop){
if(term_emit(op, out, false)){ if(fbuf_emit(f, op)){
return -1; return -1;
} }
nc->rstate.fgdefelidable = true; nc->rstate.fgdefelidable = true;
@ -753,14 +755,14 @@ raster_defaults(notcurses* nc, bool fgdef, bool bgdef, FILE* out){
nc->rstate.fgpalelidable = false; nc->rstate.fgpalelidable = false;
nc->rstate.bgpalelidable = false; nc->rstate.bgpalelidable = false;
}else if(mustsetfg){ // if we reach here, we must have fgop }else if(mustsetfg){ // if we reach here, we must have fgop
if(term_emit(fgop, out, false)){ if(fbuf_emit(f, fgop)){
return -1; return -1;
} }
nc->rstate.fgdefelidable = true; nc->rstate.fgdefelidable = true;
nc->rstate.fgelidable = false; nc->rstate.fgelidable = false;
nc->rstate.fgpalelidable = false; nc->rstate.fgpalelidable = false;
}else{ // mustsetbg and !mustsetfg and bgop != NULL }else{ // mustsetbg and !mustsetfg and bgop != NULL
if(term_emit(bgop, out, false)){ if(fbuf_emit(f, bgop)){
return -1; return -1;
} }
nc->rstate.bgdefelidable = true; nc->rstate.bgdefelidable = true;
@ -773,13 +775,13 @@ raster_defaults(notcurses* nc, bool fgdef, bool bgdef, FILE* out){
// these are unlikely, so we leave it uninlined // these are unlikely, so we leave it uninlined
static int static int
emit_fg_palindex(notcurses* nc, FILE* out, const nccell* srccell){ emit_fg_palindex(notcurses* nc, fbuf* f, const nccell* srccell){
unsigned palfg = nccell_fg_palindex(srccell); unsigned palfg = nccell_fg_palindex(srccell);
// we overload lastr for the palette index; both are 8 bits // we overload lastr for the palette index; both are 8 bits
if(nc->rstate.fgpalelidable && nc->rstate.lastr == palfg){ if(nc->rstate.fgpalelidable && nc->rstate.lastr == palfg){
++nc->stats.s.fgelisions; ++nc->stats.s.fgelisions;
}else{ }else{
if(term_fg_palindex(nc, out, palfg)){ if(term_fg_palindex(nc, f, palfg)){
return -1; return -1;
} }
++nc->stats.s.fgemissions; ++nc->stats.s.fgemissions;
@ -792,12 +794,12 @@ emit_fg_palindex(notcurses* nc, FILE* out, const nccell* srccell){
} }
static int static int
emit_bg_palindex(notcurses* nc, FILE* out, const nccell* srccell){ emit_bg_palindex(notcurses* nc, fbuf* f, const nccell* srccell){
unsigned palbg = nccell_bg_palindex(srccell); unsigned palbg = nccell_bg_palindex(srccell);
if(nc->rstate.bgpalelidable && nc->rstate.lastbr == palbg){ if(nc->rstate.bgpalelidable && nc->rstate.lastbr == palbg){
++nc->stats.s.bgelisions; ++nc->stats.s.bgelisions;
}else{ }else{
if(term_bg_palindex(nc, out, palbg)){ if(term_bg_palindex(nc, f, palbg)){
return -1; return -1;
} }
++nc->stats.s.bgemissions; ++nc->stats.s.bgemissions;
@ -822,7 +824,7 @@ emit_bg_palindex(notcurses* nc, FILE* out, const nccell* srccell){
// are loaded, but old kitty graphics remain visible, and new/updated kitty // are loaded, but old kitty graphics remain visible, and new/updated kitty
// graphics are not yet visible, and they have not moved. // graphics are not yet visible, and they have not moved.
static int64_t static int64_t
clean_sprixels(notcurses* nc, ncpile* p, FILE* out){ clean_sprixels(notcurses* nc, ncpile* p, fbuf* f){
sprixel* s; sprixel* s;
sprixel** parent = &p->sprixelcache; sprixel** parent = &p->sprixelcache;
int64_t bytesemitted = 0; int64_t bytesemitted = 0;
@ -859,10 +861,10 @@ clean_sprixels(notcurses* nc, ncpile* p, FILE* out){
} }
// FIXME kitty graphics don't need the goto_location before a load, but only // FIXME kitty graphics don't need the goto_location before a load, but only
// before a presentation; we ought be able to eliminate this // before a presentation; we ought be able to eliminate this
if(goto_location(nc, out, y + nc->margin_t, x + nc->margin_l)){ if(goto_location(nc, f, y + nc->margin_t, x + nc->margin_l)){
return -1; return -1;
} }
int r = sprite_redraw(&nc->tcache, p, s, out, y + nc->margin_t, x + nc->margin_l); int r = sprite_redraw(&nc->tcache, p, s, f, y + nc->margin_t, x + nc->margin_l);
if(r < 0){ if(r < 0){
return -1; return -1;
} }
@ -882,11 +884,11 @@ clean_sprixels(notcurses* nc, ncpile* p, FILE* out){
// "%d tardies to work off, by far the most in the class!\n", p->scrolls // "%d tardies to work off, by far the most in the class!\n", p->scrolls
static int static int
rasterize_scrolls(ncpile* p, FILE* out){ rasterize_scrolls(ncpile* p, fbuf* f){
if(p->scrolls == 0){ if(p->scrolls == 0){
return 0; return 0;
} }
logdebug("Order-%d scroll\n", p->scrolls); logdebug("order-%d scroll\n", p->scrolls);
if(p->nc->rstate.logendy >= 0){ if(p->nc->rstate.logendy >= 0){
p->nc->rstate.logendy -= p->scrolls; p->nc->rstate.logendy -= p->scrolls;
if(p->nc->rstate.logendy < 0){ if(p->nc->rstate.logendy < 0){
@ -898,7 +900,7 @@ rasterize_scrolls(ncpile* p, FILE* out){
return -1; return -1;
} }
while(p->scrolls){ while(p->scrolls){
if(ncfputc('\n', out) < 0){ if(fbuf_putc(f, '\n') < 0){
return -1; return -1;
} }
--p->scrolls; --p->scrolls;
@ -916,7 +918,7 @@ rasterize_scrolls(ncpile* p, FILE* out){
// //
// don't account for sprixelemissions here, as they were already counted. // don't account for sprixelemissions here, as they were already counted.
static int64_t static int64_t
rasterize_sprixels(notcurses* nc, ncpile* p, FILE* out){ rasterize_sprixels(notcurses* nc, ncpile* p, fbuf* f){
int64_t bytesemitted = 0; int64_t bytesemitted = 0;
sprixel* s; sprixel* s;
sprixel** parent = &p->sprixelcache; sprixel** parent = &p->sprixelcache;
@ -926,7 +928,7 @@ rasterize_sprixels(notcurses* nc, ncpile* p, FILE* out){
//fprintf(stderr, "3 DRAWING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, y + nc->margin_t, x + nc->margin_l, s->n); //fprintf(stderr, "3 DRAWING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, y + nc->margin_t, x + nc->margin_l, s->n);
int y,x; int y,x;
ncplane_yx(s->n, &y, &x); ncplane_yx(s->n, &y, &x);
int r = sprite_draw(&nc->tcache, p, s, out, y + nc->margin_t, x + nc->margin_l); int r = sprite_draw(&nc->tcache, p, s, f, y + nc->margin_t, x + nc->margin_l);
if(r < 0){ if(r < 0){
return -1; return -1;
} }
@ -936,17 +938,17 @@ rasterize_sprixels(notcurses* nc, ncpile* p, FILE* out){
if(nc->tcache.pixel_commit){ if(nc->tcache.pixel_commit){
int y,x; int y,x;
ncplane_yx(s->n, &y, &x); ncplane_yx(s->n, &y, &x);
if(goto_location(nc, out, y + nc->margin_t, x + nc->margin_l)){ if(goto_location(nc, f, y + nc->margin_t, x + nc->margin_l)){
return -1; return -1;
} }
if(sprite_commit(&nc->tcache, out, s, false)){ if(sprite_commit(&nc->tcache, f, s, false)){
return -1; return -1;
} }
nc->rstate.hardcursorpos = true; nc->rstate.hardcursorpos = true;
} }
}else if(s->invalidated == SPRIXEL_HIDE){ }else if(s->invalidated == SPRIXEL_HIDE){
if(nc->tcache.pixel_remove){ if(nc->tcache.pixel_remove){
if(nc->tcache.pixel_remove(s->id, out) < 0){ if(nc->tcache.pixel_remove(s->id, f) < 0){
return -1; return -1;
} }
if( (*parent = s->next) ){ if( (*parent = s->next) ){
@ -973,7 +975,7 @@ rasterize_sprixels(notcurses* nc, ncpile* p, FILE* out){
// 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
rasterize_core(notcurses* nc, const ncpile* p, FILE* out, unsigned phase){ rasterize_core(notcurses* nc, const ncpile* p, fbuf* f, unsigned phase){
struct crender* rvec = p->crender; struct crender* rvec = p->crender;
// we only need to emit a coordinate if it was damaged. the damagemap is a // we only need to emit a coordinate if it was damaged. the damagemap is a
// bit per coordinate, one per struct crender. // bit per coordinate, one per struct crender.
@ -997,12 +999,12 @@ rasterize_core(notcurses* nc, const ncpile* p, FILE* out, unsigned phase){
// was not above a sprixel (and the cell is damaged). in the second // was not above a sprixel (and the cell is damaged). in the second
// phase, we draw everything that remains damaged. // phase, we draw everything that remains damaged.
++nc->stats.s.cellemissions; ++nc->stats.s.cellemissions;
if(goto_location(nc, out, y, x)){ if(goto_location(nc, f, y, x)){
return -1; return -1;
} }
// 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.
if(term_setstyles(out, nc, srccell)){ if(term_setstyles(f, nc, srccell)){
return -1; return -1;
} }
// if our cell has a default foreground *or* background, we can elide // if our cell has a default foreground *or* background, we can elide
@ -1013,7 +1015,7 @@ rasterize_core(notcurses* nc, const ncpile* p, FILE* out, unsigned phase){
bool nobackground = cell_nobackground_p(srccell); bool nobackground = cell_nobackground_p(srccell);
if((nccell_fg_default_p(srccell)) || (!nobackground && nccell_bg_default_p(srccell))){ if((nccell_fg_default_p(srccell)) || (!nobackground && nccell_bg_default_p(srccell))){
if(raster_defaults(nc, nccell_fg_default_p(srccell), if(raster_defaults(nc, nccell_fg_default_p(srccell),
!nobackground && nccell_bg_default_p(srccell), out)){ !nobackground && nccell_bg_default_p(srccell), f)){
return -1; return -1;
} }
} }
@ -1022,7 +1024,7 @@ rasterize_core(notcurses* nc, const ncpile* p, FILE* out, unsigned phase){
// * the previous was non-default, and matches what we have now, or // * the previous was non-default, and matches what we have now, or
// * we are a no-foreground glyph (iswspace() is true) // * we are a no-foreground glyph (iswspace() is true)
if(nccell_fg_palindex_p(srccell)){ // palette-indexed foreground if(nccell_fg_palindex_p(srccell)){ // palette-indexed foreground
if(emit_fg_palindex(nc, out, srccell)){ if(emit_fg_palindex(nc, f, srccell)){
return -1; return -1;
} }
}else if(!nccell_fg_default_p(srccell)){ // rgb foreground }else if(!nccell_fg_default_p(srccell)){ // rgb foreground
@ -1030,7 +1032,7 @@ rasterize_core(notcurses* nc, const ncpile* p, FILE* out, unsigned phase){
if(nc->rstate.fgelidable && nc->rstate.lastr == r && nc->rstate.lastg == g && nc->rstate.lastb == b){ if(nc->rstate.fgelidable && nc->rstate.lastr == r && nc->rstate.lastg == g && nc->rstate.lastb == b){
++nc->stats.s.fgelisions; ++nc->stats.s.fgelisions;
}else{ }else{
if(term_fg_rgb8(&nc->tcache, out, r, g, b)){ if(term_fg_rgb8(&nc->tcache, f, r, g, b)){
return -1; return -1;
} }
++nc->stats.s.fgemissions; ++nc->stats.s.fgemissions;
@ -1047,7 +1049,7 @@ rasterize_core(notcurses* nc, const ncpile* p, FILE* out, unsigned phase){
if(nobackground){ if(nobackground){
++nc->stats.s.bgelisions; ++nc->stats.s.bgelisions;
}else if(nccell_bg_palindex_p(srccell)){ // palette-indexed background }else if(nccell_bg_palindex_p(srccell)){ // palette-indexed background
if(emit_bg_palindex(nc, out, srccell)){ if(emit_bg_palindex(nc, f, srccell)){
return -1; return -1;
} }
}else if(!nccell_bg_default_p(srccell)){ // rgb background }else if(!nccell_bg_default_p(srccell)){ // rgb background
@ -1055,7 +1057,7 @@ rasterize_core(notcurses* nc, const ncpile* p, FILE* out, unsigned phase){
if(nc->rstate.bgelidable && nc->rstate.lastbr == br && nc->rstate.lastbg == bg && nc->rstate.lastbb == bb){ if(nc->rstate.bgelidable && nc->rstate.lastbr == br && nc->rstate.lastbg == bg && nc->rstate.lastbb == bb){
++nc->stats.s.bgelisions; ++nc->stats.s.bgelisions;
}else{ }else{
if(term_bg_rgb8(&nc->tcache, out, br, bg, bb)){ if(term_bg_rgb8(&nc->tcache, f, br, bg, bb)){
return -1; return -1;
} }
++nc->stats.s.bgemissions; ++nc->stats.s.bgemissions;
@ -1076,7 +1078,7 @@ rasterize_core(notcurses* nc, const ncpile* p, FILE* out, unsigned phase){
sprixel_invalidate(rvec[damageidx].sprixel, y, x); sprixel_invalidate(rvec[damageidx].sprixel, y, x);
} }
} }
if(term_putc(out, &nc->pool, srccell)){ if(term_putc(f, &nc->pool, srccell)){
return -1; return -1;
} }
rvec[damageidx].s.damaged = 0; rvec[damageidx].s.damaged = 0;
@ -1098,26 +1100,26 @@ rasterize_core(notcurses* nc, const ncpile* p, FILE* out, unsigned phase){
// assuming success, it is non-0 if application-synchronized updates are // assuming success, it is non-0 if application-synchronized updates are
// desired; in this case, a SUM footer is present at the end of the buffer. // desired; in this case, a SUM footer is present at the end of the buffer.
static int static int
notcurses_rasterize_inner(notcurses* nc, ncpile* p, FILE* out, unsigned* asu){ notcurses_rasterize_inner(notcurses* nc, ncpile* p, fbuf* f, unsigned* asu){
logdebug("pile %p ymax: %d xmax: %d\n", p, p->dimy + nc->margin_t, p->dimx + nc->margin_l); logdebug("pile %p ymax: %d xmax: %d\n", p, p->dimy + nc->margin_t, p->dimx + nc->margin_l);
// don't write a clearscreen. we only update things that have been changed. // don't write a clearscreen. we only update things that have been changed.
// we explicitly move the cursor at the beginning of each output line, so no // we explicitly move the cursor at the beginning of each output line, so no
// need to home it expliticly. // need to home it expliticly.
update_palette(nc, out); update_palette(nc, f);
if(rasterize_scrolls(p, out)){ if(rasterize_scrolls(p, f)){
return -1; return -1;
} }
logdebug("Sprixel phase 1\n"); logdebug("Sprixel phase 1\n");
int64_t sprixelbytes = clean_sprixels(nc, p, out); int64_t sprixelbytes = clean_sprixels(nc, p, f);
if(sprixelbytes < 0){ if(sprixelbytes < 0){
return -1; return -1;
} }
logdebug("Glyph phase 1\n"); logdebug("Glyph phase 1\n");
if(rasterize_core(nc, p, out, 0)){ if(rasterize_core(nc, p, f, 0)){
return -1; return -1;
} }
logdebug("Sprixel phase 2\n"); logdebug("Sprixel phase 2\n");
int64_t rasprixelbytes = rasterize_sprixels(nc, p, out); int64_t rasprixelbytes = rasterize_sprixels(nc, p, f);
if(rasprixelbytes < 0){ if(rasprixelbytes < 0){
return -1; return -1;
} }
@ -1126,19 +1128,15 @@ notcurses_rasterize_inner(notcurses* nc, ncpile* p, FILE* out, unsigned* asu){
nc->stats.s.sprixelbytes += sprixelbytes; nc->stats.s.sprixelbytes += sprixelbytes;
pthread_mutex_unlock(&nc->stats.lock); pthread_mutex_unlock(&nc->stats.lock);
logdebug("Glyph phase 2\n"); logdebug("Glyph phase 2\n");
if(rasterize_core(nc, p, out, 1)){ if(rasterize_core(nc, p, f, 1)){
return -1;
}
// need to flush before doing SUMode size check, to update mstrsize
if(ncflush(out)){
return -1; return -1;
} }
#define MIN_SUMODE_SIZE BUFSIZ #define MIN_SUMODE_SIZE BUFSIZ
if(*asu){ if(*asu){
if(nc->rstate.mstrsize >= MIN_SUMODE_SIZE){ if(nc->rstate.f.used >= MIN_SUMODE_SIZE){
const char* endasu = get_escape(&nc->tcache, ESCAPE_ESUM); const char* endasu = get_escape(&nc->tcache, ESCAPE_ESUM);
if(endasu){ if(endasu){
if(fprintf(out, "%s", endasu) < 0 || ncflush(out)){ if(fbuf_puts(f, endasu) < 0){
*asu = 0; *asu = 0;
} }
}else{ }else{
@ -1149,13 +1147,13 @@ notcurses_rasterize_inner(notcurses* nc, ncpile* p, FILE* out, unsigned* asu){
} }
} }
#undef MIN_SUMODE_SIZE #undef MIN_SUMODE_SIZE
return nc->rstate.mstrsize; return nc->rstate.f.used;
} }
// rasterize the rendered frame, and blockingly write it out to the terminal. // rasterize the rendered frame, and blockingly write it out to the terminal.
static int static int
raster_and_write(notcurses* nc, ncpile* p, FILE* out){ raster_and_write(notcurses* nc, ncpile* p, fbuf* f){
fseeko(out, 0, SEEK_SET); fbuf_reset(f);
// will we be using application-synchronized updates? if this comes back as // will we be using application-synchronized updates? if this comes back as
// non-zero, we are, and must emit the header. no SUM without a tty, and we // non-zero, we are, and must emit the header. no SUM without a tty, and we
// can't have the escape without being connected to one... // can't have the escape without being connected to one...
@ -1164,11 +1162,11 @@ raster_and_write(notcurses* nc, ncpile* p, FILE* out){
// if we have SUM support, emit a BSU speculatively. if we do so, but don't // if we have SUM support, emit a BSU speculatively. if we do so, but don't
// actually use an ESU, this BSUM must be skipped on write. // actually use an ESU, this BSUM must be skipped on write.
if(useasu){ if(useasu){
if(ncfputs(basu, out) == EOF){ if(fbuf_puts(f, basu) < 0){
return -1; return -1;
} }
} }
if(notcurses_rasterize_inner(nc, p, out, &useasu) < 0){ if(notcurses_rasterize_inner(nc, p, f, &useasu) < 0){
return -1; return -1;
} }
// if we loaded a BSU into the front, but don't actually want to use it, // if we loaded a BSU into the front, but don't actually want to use it,
@ -1184,19 +1182,19 @@ raster_and_write(notcurses* nc, ncpile* p, FILE* out){
int ret = 0; int ret = 0;
sigset_t oldmask; sigset_t oldmask;
block_signals(&oldmask); block_signals(&oldmask);
if(blocking_write(fileno(nc->ttyfp), nc->rstate.mstream + moffset, if(blocking_write(fileno(nc->ttyfp), nc->rstate.f.buf + moffset,
nc->rstate.mstrsize - moffset)){ nc->rstate.f.used - moffset)){
ret = -1; ret = -1;
} }
unblock_signals(&oldmask); unblock_signals(&oldmask);
//fprintf(stderr, "%lu/%lu %lu/%lu %lu/%lu %d\n", nc->stats.defaultelisions, nc->stats.defaultemissions, nc->stats.fgelisions, nc->stats.fgemissions, nc->stats.bgelisions, nc->stats.bgemissions, ret); //fprintf(stderr, "%lu/%lu %lu/%lu %lu/%lu %d\n", nc->stats.defaultelisions, nc->stats.defaultemissions, nc->stats.fgelisions, nc->stats.fgemissions, nc->stats.bgelisions, nc->stats.bgemissions, ret);
if(nc->renderfp){ if(nc->renderfp){
fprintf(nc->renderfp, "%s\n", nc->rstate.mstream); fprintf(nc->renderfp, "%s\n", nc->rstate.f.buf);
} }
if(ret < 0){ if(ret < 0){
return ret; return ret;
} }
return nc->rstate.mstrsize; return nc->rstate.f.used;
} }
// if the cursor is enabled, store its location and disable it. then, once done // if the cursor is enabled, store its location and disable it. then, once done
@ -1204,17 +1202,17 @@ raster_and_write(notcurses* nc, ncpile* p, FILE* out){
// during rasterization, we'll get grotesque flicker. 'out' is a memstream // during rasterization, we'll get grotesque flicker. 'out' is a memstream
// used to collect a buffer. // used to collect a buffer.
static inline int static inline int
notcurses_rasterize(notcurses* nc, ncpile* p, FILE* out){ notcurses_rasterize(notcurses* nc, ncpile* p, fbuf* f){
const int cursory = nc->cursory; const int cursory = nc->cursory;
const int cursorx = nc->cursorx; const int cursorx = nc->cursorx;
if(cursory >= 0){ // either both are good, or neither is if(cursory >= 0){ // either both are good, or neither is
notcurses_cursor_disable(nc); notcurses_cursor_disable(nc);
} }
int ret = raster_and_write(nc, p, out); int ret = raster_and_write(nc, p, f);
if(cursory >= 0){ if(cursory >= 0){
notcurses_cursor_enable(nc, cursory, cursorx); notcurses_cursor_enable(nc, cursory, cursorx);
}else if(nc->rstate.logendy >= 0){ }else if(nc->rstate.logendy >= 0){
goto_location(nc, nc->ttyfp, nc->rstate.logendy, nc->rstate.logendx); goto_location(nc, f, nc->rstate.logendy, nc->rstate.logendx);
fflush(nc->ttyfp); fflush(nc->ttyfp);
} }
nc->last_pile = p; nc->last_pile = p;
@ -1223,33 +1221,28 @@ notcurses_rasterize(notcurses* nc, ncpile* p, FILE* out){
// get the cursor to the upper-left corner by one means or another, clearing // get the cursor to the upper-left corner by one means or another, clearing
// the screen while doing so. // the screen while doing so.
int clear_and_home(notcurses* nc, tinfo* ti, FILE* fp, unsigned flush){ int clear_and_home(notcurses* nc, tinfo* ti, fbuf* f){
// clear clears the screen and homes the cursor by itself // clear clears the screen and homes the cursor by itself
const char* clearscr = get_escape(ti, ESCAPE_CLEAR); const char* clearscr = get_escape(ti, ESCAPE_CLEAR);
if(clearscr){ if(clearscr){
if(term_emit(clearscr, fp, flush) == 0){ if(fbuf_emit(f, clearscr) == 0){
goto success; goto success;
} }
} }
const ncplane* stdn = notcurses_stdplane_const(nc); const ncplane* stdn = notcurses_stdplane_const(nc);
// clearscr didn't fly. try scrolling everything off. first, go to the // clearscr didn't fly. try scrolling everything off. first, go to the
// bottom of the screen, then write N newlines. // bottom of the screen, then write N newlines.
if(goto_location(nc, fp, ncplane_dim_y(stdn) - 1, 0)){ if(goto_location(nc, f, ncplane_dim_y(stdn) - 1, 0)){
return -1; return -1;
} }
for(int y = 0 ; y < ncplane_dim_y(stdn) ; ++y){ for(int y = 0 ; y < ncplane_dim_y(stdn) ; ++y){
if(ncfputc('\n', fp) == EOF){ if(fbuf_putc(f, '\n') < 0){
return -1; return -1;
} }
} }
if(goto_location(nc, fp, 0, 0)){ if(goto_location(nc, f, 0, 0)){
return -1; return -1;
} }
if(flush){
if(ncflush(fp)){
return -1;
}
}
success: success:
nc->rstate.x = 0; nc->rstate.x = 0;
@ -1262,7 +1255,8 @@ int notcurses_refresh(notcurses* nc, int* restrict dimy, int* restrict dimx){
if(notcurses_resize(nc, dimy, dimx)){ if(notcurses_resize(nc, dimy, dimx)){
return -1; return -1;
} }
if(clear_and_home(nc, &nc->tcache, nc->ttyfp, true)){ fbuf_reset(&nc->rstate.f);
if(clear_and_home(nc, &nc->tcache, &nc->rstate.f)){
return -1; return -1;
} }
if(nc->lfdimx == 0 || nc->lfdimy == 0){ if(nc->lfdimx == 0 || nc->lfdimy == 0){
@ -1280,11 +1274,18 @@ 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){
p.crender[i].s.damaged = 1; p.crender[i].s.damaged = 1;
} }
int ret = notcurses_rasterize(nc, &p, nc->rstate.mstreamfp); int ret = notcurses_rasterize(nc, &p, &nc->rstate.f);
free(p.crender); free(p.crender);
if(ret < 0){ if(ret < 0){
return -1; return -1;
} }
if(nc->rstate.f.used){
fwrite(nc->rstate.f.buf, nc->rstate.f.used, 1, nc->ttyfp);
fbuf_reset(&nc->rstate.f);
if(ncflush(nc->ttyfp)){
return -1;
}
}
++nc->stats.s.refreshes; ++nc->stats.s.refreshes;
return 0; return 0;
} }
@ -1295,35 +1296,31 @@ int ncpile_render_to_file(ncplane* n, FILE* fp){
if(nc->lfdimx == 0 || nc->lfdimy == 0){ if(nc->lfdimx == 0 || nc->lfdimy == 0){
return 0; return 0;
} }
char* rastered = NULL; fbuf f = {};
size_t rastbytes = 0; if(fbuf_init(&f)){
FILE* out = open_memstream(&rastered, &rastbytes);
if(out == NULL){
return -1; return -1;
} }
const int count = (nc->lfdimx > p->dimx ? nc->lfdimx : p->dimx) * const int count = (nc->lfdimx > p->dimx ? nc->lfdimx : p->dimx) *
(nc->lfdimy > p->dimy ? nc->lfdimy : p->dimy); (nc->lfdimy > p->dimy ? nc->lfdimy : p->dimy);
p->crender = malloc(count * sizeof(*p->crender)); p->crender = malloc(count * sizeof(*p->crender));
if(p->crender == NULL){ if(p->crender == NULL){
fclose(out); fbuf_free(&f);
free(rastered);
return -1; return -1;
} }
init_rvec(p->crender, count); init_rvec(p->crender, count);
for(int i = 0 ; i < count ; ++i){ for(int i = 0 ; i < count ; ++i){
p->crender[i].s.damaged = 1; p->crender[i].s.damaged = 1;
} }
int ret = raster_and_write(nc, p, out); int ret = raster_and_write(nc, p, &f);
free(p->crender); free(p->crender);
if(ret > 0){ if(ret > 0){
if(fprintf(fp, "%s", rastered) == ret){ if(fwrite(f.buf, f.used, 1, fp) == 1){
ret = 0; ret = 0;
}else{ }else{
ret = -1; ret = -1;
} }
} }
fclose(out); fbuf_free(&f);
free(rastered);
return ret; return ret;
} }
@ -1370,7 +1367,7 @@ int ncpile_rasterize(ncplane* n){
const struct tinfo* ti = &ncplane_notcurses_const(n)->tcache; const struct tinfo* ti = &ncplane_notcurses_const(n)->tcache;
postpaint(ti, nc->lastframe, miny, minx, pile->crender, &nc->pool); postpaint(ti, nc->lastframe, miny, minx, pile->crender, &nc->pool);
clock_gettime(CLOCK_MONOTONIC, &rasterdone); clock_gettime(CLOCK_MONOTONIC, &rasterdone);
int bytes = notcurses_rasterize(nc, pile, nc->rstate.mstreamfp); int bytes = notcurses_rasterize(nc, pile, &nc->rstate.f);
// accepts -1 as an indication of failure // accepts -1 as an indication of failure
clock_gettime(CLOCK_MONOTONIC, &writedone); clock_gettime(CLOCK_MONOTONIC, &writedone);
pthread_mutex_lock(&nc->stats.lock); pthread_mutex_lock(&nc->stats.lock);
@ -1445,19 +1442,19 @@ int ncpile_render_to_buffer(ncplane* p, char** buf, size_t* buflen){
} }
notcurses* nc = ncplane_notcurses(p); notcurses* nc = ncplane_notcurses(p);
unsigned useasu = false; // no SUM with file unsigned useasu = false; // no SUM with file
fseeko(nc->rstate.mstreamfp, 0, SEEK_SET); fbuf_reset(&nc->rstate.f);
int bytes = notcurses_rasterize_inner(nc, ncplane_pile(p), nc->rstate.mstreamfp, &useasu); int bytes = notcurses_rasterize_inner(nc, ncplane_pile(p), &nc->rstate.f, &useasu);
pthread_mutex_lock(&nc->stats.lock); pthread_mutex_lock(&nc->stats.lock);
update_render_bytes(&nc->stats.s, bytes); update_render_bytes(&nc->stats.s, bytes);
pthread_mutex_unlock(&nc->stats.lock); pthread_mutex_unlock(&nc->stats.lock);
if(bytes < 0){ if(bytes < 0){
return -1; return -1;
} }
*buf = memdup(nc->rstate.mstreamfp, nc->rstate.mstrsize); *buf = memdup(nc->rstate.f.buf, nc->rstate.f.used);
if(buf == NULL){ if(buf == NULL){
return -1; return -1;
} }
*buflen = nc->rstate.mstrsize; *buflen = nc->rstate.f.used;
return 0; return 0;
} }
@ -1506,9 +1503,19 @@ int ncdirect_set_bg_rgb(ncdirect* nc, unsigned rgb){
&& ncchannels_bg_rgb(nc->channels) == rgb){ && ncchannels_bg_rgb(nc->channels) == rgb){
return 0; return 0;
} }
if(term_bg_rgb8(&nc->tcache, nc->ttyfp, (rgb & 0xff0000u) >> 16u, (rgb & 0xff00u) >> 8u, rgb & 0xffu)){ fbuf f = {};
if(fbuf_init_small(&f)){
return -1; return -1;
} }
if(term_bg_rgb8(&nc->tcache, &f, (rgb & 0xff0000u) >> 16u, (rgb & 0xff00u) >> 8u, rgb & 0xffu)){
fbuf_free(&f);
return -1;
}
if(fwrite(f.buf, f.used, 1, nc->ttyfp) != 1){
fbuf_free(&f);
return -1;
}
fbuf_free(&f);
ncchannels_set_bg_rgb(&nc->channels, rgb); ncchannels_set_bg_rgb(&nc->channels, rgb);
return 0; return 0;
} }
@ -1521,9 +1528,19 @@ int ncdirect_set_fg_rgb(ncdirect* nc, unsigned rgb){
&& ncchannels_fg_rgb(nc->channels) == rgb){ && ncchannels_fg_rgb(nc->channels) == rgb){
return 0; return 0;
} }
if(term_fg_rgb8(&nc->tcache, nc->ttyfp, (rgb & 0xff0000u) >> 16u, (rgb & 0xff00u) >> 8u, rgb & 0xffu)){ fbuf f = {};
if(fbuf_init_small(&f)){
return -1;
}
if(term_fg_rgb8(&nc->tcache, &f, (rgb & 0xff0000u) >> 16u, (rgb & 0xff00u) >> 8u, rgb & 0xffu)){
fbuf_free(&f);
return -1;
}
if(fwrite(f.buf, f.used, 1, nc->ttyfp) != 1){
fbuf_free(&f);
return -1; return -1;
} }
fbuf_free(&f);
ncchannels_set_fg_rgb(&nc->channels, rgb); ncchannels_set_fg_rgb(&nc->channels, rgb);
return 0; return 0;
} }
@ -1547,13 +1564,20 @@ int notcurses_cursor_enable(notcurses* nc, int y, int x){
if(nc->ttyfd < 0){ if(nc->ttyfd < 0){
return -1; return -1;
} }
fbuf f = {};
if(fbuf_init_small(&f)){
return -1;
}
// updates nc->rstate.cursor{y,x} // updates nc->rstate.cursor{y,x}
if(goto_location(nc, nc->ttyfp, y + nc->margin_t, x + nc->margin_l)){ if(goto_location(nc, &f, y + nc->margin_t, x + nc->margin_l)){
fbuf_free(&f);
return -1; return -1;
} }
if(ncflush(nc->ttyfp)){ if(fwrite(f.buf, f.used, 1, nc->ttyfp) != 1 || ncflush(nc->ttyfp)){
fbuf_free(&f);
return -1; return -1;
} }
fbuf_free(&f);
// if we were already positive, we're already visible, no need to write cnorm // if we were already positive, we're already visible, no need to write cnorm
if(nc->cursory >= 0 && nc->cursorx >= 0){ if(nc->cursory >= 0 && nc->cursorx >= 0){
nc->cursory = y; nc->cursory = y;

@ -916,7 +916,7 @@ int sixel_scrub(const ncpile* p, sprixel* s){
} }
// returns the number of bytes written // returns the number of bytes written
int sixel_draw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out, int sixel_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f,
int y, int x){ int y, int x){
(void)ti; (void)ti;
// if we've wiped or rebuilt any cells, effect those changes now, or else // if we've wiped or rebuilt any cells, effect those changes now, or else
@ -928,7 +928,7 @@ int sixel_draw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out,
s->wipes_outstanding = false; s->wipes_outstanding = false;
} }
if(p){ if(p){
if(goto_location(p->nc, out, y, x)){ if(goto_location(p->nc, f, y, x)){
return -1; return -1;
} }
if(s->invalidated == SPRIXEL_MOVED){ if(s->invalidated == SPRIXEL_MOVED){
@ -942,7 +942,7 @@ int sixel_draw(const tinfo* ti, const ncpile* p, sprixel* s, FILE* out,
} }
} }
} }
if(fwrite(s->glyph.buf, s->glyph.used, 1, out) != 1){ if(fbuf_putn(f, s->glyph.buf, s->glyph.used) < 0){
return -1; return -1;
} }
s->invalidated = SPRIXEL_QUIESCENT; s->invalidated = SPRIXEL_QUIESCENT;
@ -1015,8 +1015,8 @@ int sixel_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
// 80 (sixel scrolling) is enabled by default. 8452 is not. XTSAVE/XTRESTORE // 80 (sixel scrolling) is enabled by default. 8452 is not. XTSAVE/XTRESTORE
// would be better, where they're supported. // would be better, where they're supported.
int sixel_shutdown(FILE* fp){ int sixel_shutdown(fbuf* f){
return term_emit("\e[?8452l", fp, false); return fbuf_emit(f, "\e[?8452l");
} }
uint8_t* sixel_trans_auxvec(const tinfo* ti){ uint8_t* sixel_trans_auxvec(const tinfo* ti){

@ -205,11 +205,11 @@ int sprite_wipe(const notcurses* nc, sprixel* s, int ycell, int xcell){
return r; return r;
} }
int sprite_clear_all(const tinfo* t, FILE* fp){ int sprite_clear_all(const tinfo* t, fbuf* f){
if(t->pixel_clear_all == NULL){ if(t->pixel_clear_all == NULL){
return 0; return 0;
} }
return t->pixel_clear_all(fp); return t->pixel_clear_all(f);
} }
int sprite_init(const tinfo* t, int fd){ int sprite_init(const tinfo* t, int fd){

@ -174,24 +174,24 @@ int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec);
int kitty_rebuild_animation(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int kitty_rebuild_animation(sprixel* s, int ycell, int xcell, uint8_t* auxvec);
int kitty_rebuild_selfref(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int kitty_rebuild_selfref(sprixel* s, int ycell, int xcell, uint8_t* auxvec);
int sixel_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, int sixel_draw(const tinfo* ti, const struct ncpile *p, sprixel* s,
FILE* out, int y, int x); fbuf* f, int y, int x);
int kitty_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, int kitty_draw(const tinfo* ti, const struct ncpile *p, sprixel* s,
FILE* out, int y, int x); fbuf* f, int y, int x);
int iterm_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, int iterm_draw(const tinfo* ti, const struct ncpile *p, sprixel* s,
FILE* out, int y, int x); fbuf* f, int y, int x);
int kitty_move(sprixel* s, FILE* out, unsigned noscroll); int kitty_move(sprixel* s, fbuf* f, unsigned noscroll);
int sixel_scrub(const struct ncpile* p, sprixel* s); int sixel_scrub(const struct ncpile* p, sprixel* s);
int kitty_scrub(const struct ncpile* p, sprixel* s); int kitty_scrub(const struct ncpile* p, sprixel* s);
int fbcon_scrub(const struct ncpile* p, sprixel* s); int fbcon_scrub(const struct ncpile* p, sprixel* s);
int kitty_remove(int id, FILE* out); int kitty_remove(int id, fbuf* f);
int kitty_clear_all(FILE* fp); int kitty_clear_all(fbuf* f);
int sixel_init(const tinfo* t, int fd); int sixel_init(const tinfo* t, int fd);
int sixel_init_inverted(const tinfo* t, int fd); int sixel_init_inverted(const tinfo* t, int fd);
int kitty_shutdown(FILE* fp); int kitty_shutdown(fbuf* f);
int sixel_shutdown(FILE* fp); int sixel_shutdown(fbuf* f);
uint8_t* sixel_trans_auxvec(const struct tinfo* ti); uint8_t* sixel_trans_auxvec(const struct tinfo* ti);
uint8_t* kitty_trans_auxvec(const struct tinfo* ti); uint8_t* kitty_trans_auxvec(const struct tinfo* ti);
int kitty_commit(FILE* fp, sprixel* s, unsigned noscroll); int kitty_commit(fbuf* f, sprixel* s, unsigned noscroll);
int sixel_blit(struct ncplane* nc, int linesize, const void* data, int sixel_blit(struct ncplane* nc, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs); int leny, int lenx, const struct blitterargs* bargs);
int kitty_blit(struct ncplane* nc, int linesize, const void* data, int kitty_blit(struct ncplane* nc, int linesize, const void* data,
@ -205,7 +205,7 @@ int kitty_blit_selfref(struct ncplane* nc, int linesize, const void* data,
int fbcon_blit(struct ncplane* nc, int linesize, const void* data, int fbcon_blit(struct ncplane* nc, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs); int leny, int lenx, const struct blitterargs* bargs);
int fbcon_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, int fbcon_draw(const tinfo* ti, const struct ncpile *p, sprixel* s,
FILE* out, int y, int x); fbuf* f, int y, int x);
typedef enum { typedef enum {
// C=1 (disabling scrolling) was only introduced in 0.20.0, at the same // C=1 (disabling scrolling) was only introduced in 0.20.0, at the same

@ -12,6 +12,7 @@ extern "C" {
#include <pthread.h> #include <pthread.h>
#include <stdbool.h> #include <stdbool.h>
#include <notcurses/notcurses.h> #include <notcurses/notcurses.h>
#include "fbuf.h"
struct ncpile; struct ncpile;
struct sprixel; struct sprixel;
@ -150,17 +151,17 @@ typedef struct tinfo {
int (*pixel_wipe)(struct sprixel* s, int y, int x); int (*pixel_wipe)(struct sprixel* s, int y, int x);
// perform the inverse of pixel_wipe, restoring an annihilated sprixcell. // perform the inverse of pixel_wipe, restoring an annihilated sprixcell.
int (*pixel_rebuild)(struct sprixel* s, int y, int x, uint8_t* auxvec); int (*pixel_rebuild)(struct sprixel* s, int y, int x, uint8_t* auxvec);
int (*pixel_remove)(int id, FILE* out); // kitty only, issue actual delete command int (*pixel_remove)(int id, fbuf* f); // kitty only, issue actual delete command
int (*pixel_init)(const struct tinfo*, int fd); // called when support is detected int (*pixel_init)(const struct tinfo*, int fd); // called when support is detected
int (*pixel_draw)(const struct tinfo*, const struct ncpile* p, int (*pixel_draw)(const struct tinfo*, const struct ncpile* p,
struct sprixel* s, FILE* out, int y, int x); struct sprixel* s, fbuf* f, int y, int x);
// execute move (erase old graphic, place at new location) if non-NULL // execute move (erase old graphic, place at new location) if non-NULL
int (*pixel_move)(struct sprixel* s, FILE* out, unsigned noscroll); int (*pixel_move)(struct sprixel* s, fbuf* f, unsigned noscroll);
int (*pixel_scrub)(const struct ncpile* p, struct sprixel* s); int (*pixel_scrub)(const struct ncpile* p, struct sprixel* s);
int (*pixel_shutdown)(FILE* fp); // called during context shutdown int (*pixel_shutdown)(fbuf* f); // called during context shutdown
int (*pixel_clear_all)(FILE* fp); // called during context startup int (*pixel_clear_all)(fbuf* f); // called during context startup
// make a loaded graphic visible. only used with kitty. // make a loaded graphic visible. only used with kitty.
int (*pixel_commit)(FILE* fp, struct sprixel* s, unsigned noscroll); int (*pixel_commit)(fbuf* f, struct sprixel* s, unsigned noscroll);
uint8_t* (*pixel_trans_auxvec)(const struct tinfo* ti); // create tranparent auxvec uint8_t* (*pixel_trans_auxvec)(const struct tinfo* ti); // create tranparent auxvec
// sprixel parameters. there are several different sprixel protocols, of // sprixel parameters. there are several different sprixel protocols, of
// which we support sixel and kitty. the kitty protocol is used based // which we support sixel and kitty. the kitty protocol is used based

@ -24,9 +24,9 @@ int ncvisual_init(int logl){
return 0; return 0;
} }
void ncvisual_printbanner(const notcurses* nc){ void ncvisual_printbanner(fbuf* f){
if(visual_implementation.visual_printbanner){ if(visual_implementation.visual_printbanner){
visual_implementation.visual_printbanner(nc); visual_implementation.visual_printbanner(f);
} }
} }

@ -673,11 +673,11 @@ int ffmpeg_init(int logl){
return 0; return 0;
} }
void ffmpeg_printbanner(const notcurses* nc){ void ffmpeg_printbanner(fbuf* f){
fprintf(nc->ttyfp, "avformat %u.%u.%u avutil %u.%u.%u swscale %u.%u.%u\n", fbuf_printf(f, "avformat %u.%u.%u avutil %u.%u.%u swscale %u.%u.%u\n",
LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO, LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO,
LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO, LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO,
LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO); LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO);
} }
void ffmpeg_details_destroy(ncvisual_details* deets){ void ffmpeg_details_destroy(ncvisual_details* deets){

@ -4,8 +4,8 @@
#include "internal.h" #include "internal.h"
static void static void
printbanner(const notcurses* nc){ printbanner(fbuf* f){
fprintf(nc->ttyfp, "built without multimedia support\n"); fbuf_puts(f, "built without multimedia support\n");
} }
const ncvisual_implementation local_visual_implementation = { const ncvisual_implementation local_visual_implementation = {

@ -195,8 +195,10 @@ auto oiio_destroy(ncvisual* ncv) -> void {
} }
// FIXME would be nice to have OIIO::attributes("libraries") in here // FIXME would be nice to have OIIO::attributes("libraries") in here
void oiio_printbanner(const struct notcurses* nc){ void oiio_printbanner(fbuf* f){
fprintf(nc->ttyfp, "openimageio %s\n", OIIO_VERSION_STRING); fbuf_puts(f, "openimageio ");
fbuf_puts(f, OIIO_VERSION_STRING);
fbuf_putc(f, '\n');
} }
#endif #endif

@ -9,7 +9,7 @@ extern "C" {
int oiio_decode(ncvisual* nc); int oiio_decode(ncvisual* nc);
struct ncvisual_details* oiio_details_init(void); struct ncvisual_details* oiio_details_init(void);
void oiio_printbanner(const struct notcurses* nc); void oiio_printbanner(fbuf* f);
void oiio_details_seed(struct ncvisual* ncv); void oiio_details_seed(struct ncvisual* ncv);
int oiio_blit(ncvisual* ncv, int rows, int cols, int oiio_blit(ncvisual* ncv, int rows, int cols,
struct ncplane* n, const struct blitset* bset, struct ncplane* n, const struct blitset* bset,

Loading…
Cancel
Save