diff --git a/include/notcurses.h b/include/notcurses.h index a43b95ad8..ccf6ca652 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -297,13 +297,20 @@ cell_set_style(cell* c, unsigned stylebits){ ((stylebits & 0xffff) << 16u); } -// Add the specified styles to the cell's existing spec. +// Get the style bits, shifted over into the LSBs. +static inline unsigned +cell_get_style(const cell* c){ + return (c->attrword & ~CELL_STYLE_MASK) >> 16u; +} + +// Add the specified styles (in the LSBs) to the cell's existing spec, whether +// they're actively supported or not. static inline void cell_enable_styles(cell* c, unsigned stylebits){ c->attrword |= ((stylebits & 0xffff) << 16u); } -// Remove the specified styles from the cell's existing spec. +// Remove the specified styles (in the LSBs) from the cell's existing spec. static inline void cell_disable_styles(cell* c, unsigned stylebits){ c->attrword &= ~((stylebits & 0xffff) << 16u); @@ -334,11 +341,12 @@ cell_rgb_blue(uint32_t rgb){ return (rgb & 0xffull); } -#define CELL_FGDEFAULT_MASK 0x4000000000000000ull -#define CELL_FG_MASK 0x00ffffff00000000ull -#define CELL_WIDEASIAN_MASK 0x2000000000000000ull -#define CELL_BGDEFAULT_MASK 0x0000000040000000ull -#define CELL_BG_MASK 0x0000000000ffffffull +#define CELL_INHERITSTYLE_MASK 0x8000000000000000ull +#define CELL_FGDEFAULT_MASK 0x4000000000000000ull +#define CELL_WIDEASIAN_MASK 0x2000000000000000ull +#define CELL_FG_MASK 0x00ffffff00000000ull +#define CELL_BGDEFAULT_MASK 0x0000000040000000ull +#define CELL_BG_MASK 0x0000000000ffffffull static inline void cell_rgb_set_fg(uint64_t* channels, unsigned r, unsigned g, unsigned b){ @@ -382,21 +390,32 @@ cell_get_bg(const cell* c, unsigned* r, unsigned* g, unsigned* b){ *b = cell_rgb_blue(cell_bg_rgb(c->channels)); } +// does the cell passively retain the styling of the previously-rendered cell? +static inline bool +cell_inherits_style(const cell* c){ + return (c->channels & CELL_INHERITSTYLE_MASK); +} + +// is the cell using the terminal's default foreground color for its foreground? static inline bool cell_fg_default_p(const cell* c){ return !(c->channels & CELL_FGDEFAULT_MASK); } +// is the cell using the terminal's default background color for its background? static inline bool cell_bg_default_p(const cell* c){ return !(c->channels & CELL_BGDEFAULT_MASK); } +// does the cell contain an East Asian Wide codepoint? static inline bool cell_double_wide_p(const cell* c){ return (c->channels & CELL_WIDEASIAN_MASK); } +// get the offset into the egcpool for this cell's EGC. returns meaningless and +// unsafe results if called on a simple cell. static inline uint32_t cell_egc_idx(const cell* c){ return c->gcluster - 0x80; diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index e5925f792..f64b726a6 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -76,6 +76,7 @@ typedef struct notcurses { char* dim; // WA_DIM char* bold; // WA_BOLD char* italics; // WA_ITALIC + char* italoff; // WA_ITALIC (disable) struct termios tpreserved; // terminal state upon entry bool RGBflag; // terminfo-reported "RGB" flag for 24bpc directcolor ncplane* top; // the contents of our topmost plane (initially entire screen) @@ -425,6 +426,7 @@ interrogate_terminfo(notcurses* nc, const notcurses_options* opts){ term_verify_seq(&nc->dim, "dim"); term_verify_seq(&nc->bold, "bold"); term_verify_seq(&nc->italics, "sitm"); + term_verify_seq(&nc->italoff, "ritm"); term_verify_seq(&nc->op, "op"); term_verify_seq(&nc->clear, "clear"); // Some terminals cannot combine certain styles with colors. Don't advertise @@ -732,6 +734,51 @@ advance_cursor(ncplane* n, int cols){ } } +// check the current and target style bitmasks against the specified 'stylebit'. +// if they are different, and we have the necessary capability, write the +// applicable terminfo entry to 'out'. returns -1 only on a true error. +static int +term_setstyle(FILE* out, unsigned cur, unsigned targ, unsigned stylebit, + const char* ton, const char* toff){ + int ret = 0; + unsigned curon = cur & stylebit; + unsigned targon = targ & stylebit; + if(curon != targon){ + if(targon){ + if(ton){ + ret = term_emit(ton, out, false); + } + }else{ + if(toff){ // how did this happen? we can turn it on, but not off? + ret = term_emit(toff, out, false); + } + } + } + if(ret < 0){ + return -1; + } + return 0; +} + +// write any escape sequences necessary to set the desired style +static int +term_setstyles(const notcurses* nc, FILE* out, uint32_t curattr, const cell* c){ + if(cell_inherits_style(c)){ + return 0; // change nothing + } + uint32_t cellattr = cell_get_style(c); + if(cellattr == curattr){ + return 0; // happy agreement, change nothing + } + int ret = 0; + ret |= term_setstyle(out, curattr, cellattr, WA_ITALIC, nc->italics, nc->italoff); + /*ret |= term_setstyle(out, curattr, cellattr, WA_BOLD, nc->bold, nc->boldoff); + ret |= term_setstyle(out, curattr, cellattr, WA_UNDERLINE, nc->uline, nc->ulineoff); + ret |= term_setstyle(out, curattr, cellattr, WA_BLINK, nc->blink, nc->blinkoff);*/ + // FIXME a few others + return ret; +} + // FIXME this needs to keep an invalidation bitmap, rather than blitting the // world every time int notcurses_render(notcurses* nc){ @@ -745,6 +792,7 @@ int notcurses_render(notcurses* nc){ if(out == NULL){ return -1; } + uint32_t curattr = 0; // current attributes set (does not include colors) term_emit(nc->clear, out, false); for(y = 0 ; y < nc->stdscr->leny ; ++y){ // FIXME previous line could have ended halfway through multicol. what happens? @@ -767,6 +815,7 @@ int notcurses_render(notcurses* nc){ cell_get_bg(c, &br, &bg, &bb); term_bg_rgb8(nc, out, br, bg, bb); } + term_setstyles(nc, out, curattr, c); // FIXME what to do if we're at the last cell, and it's wide? // fprintf(stderr, "[%02d/%02d] ", y, x); term_putc(out, nc->stdscr, c);