Higher planes obliterate bisected wide glyphs #158 (#243)

* higher planes stomp wide glyphs
* broken unit test
* develop out widestomp PoC
* fix notcurses_at_yx()
* fix up dig_visible_cell() return value
* refuse wide glyph on last column #242
* set adjacent cell wide when rendering #158
* xray: eliminate weird color flicker
* witherworm: don't eat wide glyphs
* unit test for boxed glyph
* uniblock: no need to emit so many U+200Es
* witherworm: remove wide glyph hack
pull/247/head
Nick Black 5 years ago committed by nick black
parent 48177b8474
commit 2fbc94e41c
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -382,6 +382,11 @@ API void notcurses_stats(struct notcurses* nc, ncstats* stats);
// Reset all cumulative stats (immediate ones, such as fbbytes, are not reset).
API void notcurses_reset_stats(struct notcurses* nc, ncstats* stats);
// Retrieve the cell at the specified location on the specified plane, returning
// it in 'c'. This copy is safe to use until the ncplane is destroyed/erased.
// Returns the length of the EGC in bytes.
API char* notcurses_at_yx(struct notcurses* nc, int y, int x, cell* c);
// Resize the specified ncplane. The four parameters 'keepy', 'keepx',
// 'keepleny', and 'keeplenx' define a subset of the ncplane to keep,
// unchanged. This may be a section of size 0, though none of these four
@ -559,8 +564,8 @@ API int ncplane_putegc(struct ncplane* n, const char* gclust, uint32_t attr,
// Call ncplane_putegc() after successfully moving to y, x.
static inline int
ncplane_putegc_yx(struct ncplane* n, int y, int x, const char* gclust, uint32_t attr,
uint64_t channels, int* sbytes){
ncplane_putegc_yx(struct ncplane* n, int y, int x, const char* gclust,
uint32_t attr, uint64_t channels, int* sbytes){
if(ncplane_cursor_move_yx(n, y, x)){
return -1;
}
@ -1365,6 +1370,18 @@ cell_double_wide_p(const cell* c){
return (c->channels & CELL_WIDEASIAN_MASK);
}
// Is this the right half of a wide character?
static inline bool
cell_wide_right_p(const cell* c){
return cell_double_wide_p(c) && c->gcluster == 0;
}
// Is this the left half of a wide character?
static inline bool
cell_wide_left_p(const cell* c){
return cell_double_wide_p(c) && c->gcluster;
}
// Is the cell simple (a lone ASCII character, encoded as such)?
static inline bool
cell_simple_p(const cell* c){

@ -257,8 +257,7 @@ panelreel_demo_core(struct notcurses* nc, int efd, tabletctx** tctxs){
.circular = true,
.min_supported_cols = 8,
.min_supported_rows = 5,
.bordermask = NCBOXMASK_BOTTOM | NCBOXMASK_TOP |
NCBOXMASK_LEFT | NCBOXMASK_RIGHT,
.bordermask = 0,
.borderchan = 0,
.tabletchan = 0,
.focusedchan = 0,

@ -28,9 +28,9 @@ draw_block(struct ncplane* nn, uint32_t blockstart){
cell_set_fg_rgb(&vl, 255, 255, 255);
cell_set_bg_rgb(&hl, 0, 0, 0);
cell_set_bg_rgb(&vl, 0, 0, 0);
if(ncplane_box_sized(nn, &ul, &ur, &ll, &lr, &hl, &vl,
BLOCKSIZE / CHUNKSIZE + 2,
(CHUNKSIZE * 2) + 2, 0)){
int dimx, dimy;
ncplane_dim_yx(nn, &dimy, &dimx);
if(ncplane_box_sized(nn, &ul, &ur, &ll, &lr, &hl, &vl, dimy, dimx, 0)){
return -1;
}
cell_release(nn, &ul); cell_release(nn, &ur); cell_release(nn, &hl);
@ -59,25 +59,15 @@ draw_block(struct ncplane* nn, uint32_t blockstart){
}
utf8arr[bwc] = '\0';
}else{ // don't dump non-printing codepoints
utf8arr[0] = ' ';
utf8arr[1] = 0xe2;
utf8arr[2] = 0x80;
utf8arr[3] = 0x8e;
utf8arr[4] = '\0';
}
if(cell_load(nn, &c, utf8arr) < 0){ // FIXME check full len was eaten?
return -1;;
strcpy(utf8arr, " ");
}
cell_set_fg_rgb(&c, 0xad + z * 2, 0xd8, 0xe6 - z * 2);
cell_set_bg_rgb(&c, 8 * chunk, 8 * chunk + z, 8 * chunk);
if(ncplane_putc(nn, &c) < 0){
if(ncplane_putstr(nn, utf8arr) < 0){
return -1;
}
if(wcwidth(w[0]) < 2 || !iswprint(w[0])){
if(cell_load(nn, &c, " ") < 0){
return -1;
}
if(ncplane_putc(nn, &c) < 0){
if(ncplane_putsimple(nn, ' ') < 0){
return -1;
}
}

@ -44,7 +44,7 @@ wall_p(const struct ncplane* n, const cell* c){
// the closer the coordinate is (lower distance), the more we lighten the cell
static inline int
lighten(struct ncplane* n, cell* c, int distance){
if(c->gcluster == 0){ // don't blow away wide characters
if(cell_wide_right_p(c)){ // not really a character
return 0;
}
unsigned r, g, b;
@ -63,16 +63,10 @@ lighten(struct ncplane* n, cell* c, int distance){
static void
surrounding_cells(struct ncplane* n, cell* cells, int y, int x){
ncplane_at_yx(n, y - 1, x - 1, &cells[0]);
ncplane_at_yx(n, y - 1, x, &cells[1]);
ncplane_at_yx(n, y - 1, x + 1, &cells[2]);
// FIXME rewrite all these using ncplane_at_yx()
if(ncplane_cursor_move_yx(n, y - 1, x - 1) == 0){
ncplane_at_cursor(n, &cells[0]);
}
if(ncplane_cursor_move_yx(n, y - 1, x) == 0){
ncplane_at_cursor(n, &cells[1]);
}
if(ncplane_cursor_move_yx(n, y - 1, x + 1) == 0){
ncplane_at_cursor(n, &cells[2]);
}
if(ncplane_cursor_move_yx(n, y, x - 1) == 0){
ncplane_at_cursor(n, &cells[7]);
}
@ -186,7 +180,6 @@ static int
snakey_top(struct notcurses* nc, snake* s){
struct ncplane* n = notcurses_stdplane(nc);
surrounding_cells(n, s->lightup, s->y, s->x);
ncplane_cursor_move_yx(n, s->y, s->x);
if(lightup_surrounding_cells(n, s->lightup, s->y, s->x)){
return -1;
}
@ -656,7 +649,7 @@ int witherworm_demo(struct notcurses* nc){
return -1;
}
if(i){
uint64_t delay = demodelay.tv_sec * 1000000000 + demodelay.tv_nsec;
uint64_t delay = timespec_to_ns(&demodelay);
delay /= screens;
struct timespec tv;
if(delay > GIG){

@ -1,16 +1,16 @@
#include "demo.h"
static const char* leg[] = {
" 88 88 88 88 88 88 88 ",
" \"\" 88 88 88 88 \"\" \"\" ,d ",
" 88 88 88 88 88 ",
" ,adPPYYba, 8b,dPPYba, 88 ,adPPYba, 88 ,d8 88,dPPYba, 88 ,adPPYYba, ,adPPYba, 88 ,d8 88 ,adPPYba, 88 8b,dPPYba, MM88MMM ",
" \"\" `Y8 88P' `\"8a 88 a8\" \"\" 88 ,a8\" 88P' \"8a 88 \"\" `Y8 a8\" \"\" 88 ,a8\" 88 a8\" \"8a 88 88P' `\"8a 88 ",
" ,adPPPPP88 88 88 88 8b 8888[ 88 d8 88 ,adPPPPP88 8b 8888[ 88 8b d8 88 88 88 88 ",
" 88, ,88 88 88 88 \"8a, ,aa 88`\"Yba, 88b, ,a8\" 88 88, ,88 \"8a, ,aa 88`\"Yba, 88 \"8a, ,a8\" 88 88 88 88, ",
" `\"8bbdP\"Y8 88 88 88 `\"Ybbd8\"' 88 `Y8a 8Y\"Ybbd8\"' 88 `\"8bbdP\"Y8 `\"Ybbd8\"' 88 `Y8a 88 `\"YbbdP\"' 88 88 88 \"Y888 ",
" ,88 ",
" 888P ",
" 88 88 88 88 88 88 88 ",
" \"\" 88 88 88 88 \"\" \"\" ,d ",
" 88 88 88 88 88 ",
",adPPYYba, 8b,dPPYba, 88 ,adPPYba, 88 ,d8 88,dPPYba, 88 ,adPPYYba, ,adPPYba, 88 ,d8 88 ,adPPYba, 88 8b,dPPYba, MM88MMM ",
"\"\" `Y8 88P' `\"8a 88 a8\" \"\" 88 ,a8\" 88P' \"8a 88 \"\" `Y8 a8\" \"\" 88 ,a8\" 88 a8\" \"8a 88 88P' `\"8a 88 ",
",adPPPPP88 88 88 88 8b 8888[ 88 d8 88 ,adPPPPP88 8b 8888[ 88 8b d8 88 88 88 88 ",
"88, ,88 88 88 88 \"8a, ,aa 88`\"Yba, 88b, ,a8\" 88 88, ,88 \"8a, ,aa 88`\"Yba, 88 \"8a, ,a8\" 88 88 88 88, ",
"`\"8bbdP\"Y8 88 88 88 `\"Ybbd8\"' 88 `Y8a 8Y\"Ybbd8\"' 88 `\"8bbdP\"Y8 `\"Ybbd8\"' 88 `Y8a 88 `\"YbbdP\"' 88 88 88 \"Y888 ",
" ,88 ",
" 888P ",
};
static int
@ -34,7 +34,6 @@ perframecb(struct notcurses* nc, struct ncvisual* ncv __attribute__ ((unused)),
*(struct ncplane**)vnewplane = n;
}
ncplane_dim_yx(n, &dimy, &dimx);
y = 0;
cell c = CELL_SIMPLE_INITIALIZER(' ');
cell_set_fg_alpha(&c, CELL_ALPHA_TRANSPARENT);
cell_set_bg_alpha(&c, CELL_ALPHA_TRANSPARENT);
@ -43,48 +42,49 @@ perframecb(struct notcurses* nc, struct ncvisual* ncv __attribute__ ((unused)),
ncplane_set_bg_alpha(n, CELL_ALPHA_BLEND);
// fg/bg rgbs are set within loop
int x = dimx - frameno;
for(size_t l = 0 ; l < sizeof(leg) / sizeof(*leg) ; ++l, ++y){
int r = startr;
int g = startg - (l * 0x8);
int b = startb;
ncplane_set_bg_rgb(n, l * 0x4, 0x20, l * 0x4);
int xoff = x;
while(xoff + (int)strlen(leg[l]) <= 0){
xoff += strlen(leg[l]);
}
do{
ncplane_set_fg_rgb(n, r, g, b);
int len = dimx - xoff;
if(xoff < 0){
len = strlen(leg[l]) + xoff;
}else if(xoff == 0){
int t = startr;
startr = startg;
startg = startb;
startb = t;
int r = startr;
int g = startg;
int b = startb;
const size_t llen = strlen(leg[0]);
do{
if(x + (int)llen <= 0){
x += llen;
}else{
int len = dimx - x;
if(x < 0){
len = llen + x;
}
if(len > (int)strlen(leg[l])){
len = strlen(leg[l]);
if(len > (int)llen){
len = llen;
}
if(len > dimx){
len = dimx;
}
int stroff = 0;
if(xoff < 0){
stroff = -xoff;
xoff = 0;
if(x < 0){
stroff = -x;
x = 0;
}
ncplane_printf_yx(n, y, xoff, "%*.*s", len, len, leg[l] + stroff);
xoff += len;
int t = r;
r = g;
g = b;
b = t;
}while(xoff < dimx);
}
for(size_t l = 0 ; l < sizeof(leg) / sizeof(*leg) ; ++l, ++y){
if(ncplane_set_fg_rgb(n, r - 0xc * l, g - 0xc * l, b - 0xc * l)){
return -1;
}
if(ncplane_set_bg_rgb(n, (l + 1) * 0x8, 0x20, (l + 1) * 0x8)){
return -1;
}
if(ncplane_printf_yx(n, l, x, "%*.*s", len, len, leg[l] + stroff) != (int)len){
return -1;
}
}
x += len;
}
int t = r;
r = g;
g = b;
b = t;
}while(x < dimx);
++frameno;
demo_render(nc);
// FIXME we'll need some delay here
return 0;
}

@ -213,19 +213,26 @@ fbcellidx(const ncplane* n, int row, int col){
// copy the UTF8-encoded EGC out of the cell, whether simple or complex. the
// result is not tied to the ncplane, and persists across erases / destruction.
static inline char*
cell_egc_copy(const ncplane* n, const cell* c){
pool_egc_copy(const egcpool* e, const cell* c){
char* ret;
if(cell_simple_p(c)){
if( (ret = (char*)malloc(2)) ){ // cast required for c++ unit tests
if( (ret = (char*)malloc(2)) ){
ret[0] = c->gcluster;
ret[1] = '\0';
}
}else{
ret = strdup(cell_extended_gcluster(n, c));
ret = strdup(egcpool_extended_gcluster(e, c));
}
return ret;
}
// copy the UTF8-encoded EGC out of the cell, whether simple or complex. the
// result is not tied to the ncplane, and persists across erases / destruction.
static inline char*
cell_egc_copy(const ncplane* n, const cell* c){
return pool_egc_copy(&n->pool, c);
}
// For our first attempt, O(1) uniform conversion from 8-bit r/g/b down to
// ~2.4-bit 6x6x6 cube + greyscale (assumed on entry; I know no way to
// even semi-portably recover the palette) proceeds via: map each 8-bit to
@ -280,6 +287,11 @@ extended_gcluster(const ncplane* n, const cell* c){
cell* ncplane_cell_ref_yx(ncplane* n, int y, int x);
static inline void
cell_set_wide(cell* c){
c->channels |= CELL_WIDEASIAN_MASK;
}
#define NANOSECS_IN_SEC 1000000000
static inline uint64_t

@ -1025,11 +1025,6 @@ ncplane_cursor_stuck(const ncplane* n){
return (n->x == n->lenx && n->y == n->leny);
}
static inline void
cell_set_wide(cell* c){
c->channels |= CELL_WIDEASIAN_MASK;
}
static inline void
cell_obliterate(ncplane* n, cell* c){
cell_release(n, c);
@ -1043,6 +1038,10 @@ int ncplane_putc(ncplane* n, const cell* c){
return -1;
}
bool wide = cell_double_wide_p(c);
if(wide && (n->x + 1 == n->lenx)){
ncplane_unlock(n);
return -1;
}
// A wide character obliterates anything to its immediate right (and marks
// that cell as wide). Any character placed atop one half of a wide character
// obliterates the other half. Note that a wide char can thus obliterate two
@ -1061,12 +1060,19 @@ int ncplane_putc(ncplane* n, const cell* c){
ncplane_unlock(n);
return -1;
}
int cols = 1 + wide;
int cols = 1;
if(wide){
++cols;
cell* rtarg = &n->fb[fbcellidx(n, n->y, n->x + 1)];
cell_release(n, rtarg);
cell_init(rtarg);
cell_set_wide(rtarg);
}
if(wide){ // must set our right wide, and check for further damage
if(n->x < n->lenx - 1){ // check to our right
cell* candidate = &n->fb[fbcellidx(n, n->y, n->x + 1)];
if(n->x < n->lenx - 2){
if(cell_double_wide_p(candidate) && targ->gcluster){ // left half
if(cell_wide_left_p(candidate)){
cell_obliterate(n, &n->fb[fbcellidx(n, n->y, n->x + 2)]);
}
}

@ -140,8 +140,18 @@ reshape_shadow_fb(notcurses* nc){
// whichever one occurs at the top with a non-transparent α (α < 2). To effect
// tail recursion, though, we instead write first, and then recurse, blending
// as we descend. α == 0 is opaque. α == 2 is fully transparent.
//
// It is useful to know how deep our glyph came from (the depth of the return
// value), so it will be recorded in 'previousz'. It is useful to know this
// value's relation to the previous cell, so the previous value is provided as
// input to 'previousz', and when we set 'previousz' in this function, we use
// the positive depth to indicate that the return value was above the previous
// plane, and a negative depth to indicate that the return value was equal to or
// below the previous plane. Relative depths are valid only within the context
// of a single render. 'previousz' must be non-negative on input.
static inline ncplane*
dig_visible_cell(cell* c, int y, int x, ncplane* p){
dig_visible_cell(cell* c, int y, int x, ncplane* p, int* previousz){
int depth = 1;
unsigned fgblends = 0;
unsigned bgblends = 0;
// once we decide on our glyph, it cannot be changed by anything below, so
@ -188,26 +198,40 @@ dig_visible_cell(cell* c, int y, int x, ncplane* p){
// if everything's locked in, we're done
if((glyphplane && cell_fg_alpha(c) == CELL_ALPHA_OPAQUE &&
cell_bg_alpha(c) == CELL_ALPHA_OPAQUE)){
if(depth < *previousz){
*previousz = depth;
}else{
*previousz = -depth;
}
return glyphplane;
}
}
}
p = p->z;
++depth;
}
// if we have a background set, but no glyph selected, load a space so that
// the background will be printed
if(c->gcluster == 0){
cell_load_simple(NULL, c, ' ');
}
if(depth < *previousz){
*previousz = depth;
}else{
*previousz = -depth;
}
return glyphplane;
}
static inline ncplane*
visible_cell(cell* c, int y, int x, ncplane* n){
visible_cell(cell* c, int y, int x, ncplane* n, int* previousz){
cell_init(c);
cell_set_fg_alpha(c, CELL_ALPHA_TRANSPARENT);
cell_set_bg_alpha(c, CELL_ALPHA_TRANSPARENT);
return dig_visible_cell(c, y, x, n);
if(*previousz < 0){
*previousz = -*previousz;
}
return dig_visible_cell(c, y, x, n, previousz);
}
// write the cell's UTF-8 grapheme cluster to the provided FILE*. returns the
@ -470,22 +494,48 @@ notcurses_render_internal(notcurses* nc){
// cursor movement with cup if we only elided one or two. set to INT_MAX
// whenever we're on a new line.
int needmove = INT_MAX;
// track the depth of our glyph, to see if we need need to stomp a wide
// glyph we're following.
int depth = 0;
// are we in the right half of a wide glyph? if so, we don't typically emit
// anything, *BUT* we must handle higher planes bisecting our wide glyph.
bool inright = false;
for(x = 0 ; x < nc->stdscr->lenx ; ++x){
unsigned r, g, b, br, bg, bb;
ncplane* p;
cell c; // no need to initialize
p = visible_cell(&c, y, x, nc->top);
p = visible_cell(&c, y, x, nc->top, &depth);
// don't try to print a wide character on the last column; it'll instead
// be printed on the next line. they probably shouldn't be admitted, but
// we can end up with one due to a resize.
// FIXME but...print what, exactly, instead?
// be printed on the next line. they aren't output, but we can end up
// with one due to a resize. FIXME but...print what, exactly, instead?
if((x + 1 >= nc->stdscr->lenx && cell_double_wide_p(&c))){
continue; // needmove will be reset as we restart the line
}
//fprintf(stderr, "%d %d depth: %d %d\n", y, x, depth, inright);
if(depth > 0){ // we are above the previous source plane
if(inright){ // wipe out the character to the left
cell* prev = &nc->lastframe[fbcellidx(nc->stdscr, y, x - 1)];
pool_release(&nc->pool, prev);
cell_init(prev);
// FIXME technically we need rerun the visible cell search...? gross
cell_load_simple(NULL, prev, ' ');
// FIXME this space will be the wrong color, methinks?
term_emit("cup", tiparm(nc->cup, y, x - 1), out, false);
fputc(' ', out);
inright = false;
//fprintf(stderr, "WENT BACK NOW FOR %c\n", c.gcluster);
}
}
// lastframe has already been sized to match the current size, so no need
// to check whether we're within its bounds. just check the cell.
if(nc->lastframe){
cell* oldcell = &nc->lastframe[fbcellidx(nc->stdscr, y, x)];
if(inright){
cell_set_wide(oldcell);
inright = false;
continue;
}
// check the damage map
if(cellcmp_and_dupfar(&nc->pool, oldcell, p, &c) == 0){
// no need to emit a cell; what we rendered appears to already be
// here. no updates are performed to elision state nor lastframe.
@ -498,7 +548,9 @@ notcurses_render_internal(notcurses* nc){
++needmove;
}
++nc->stats.cellelisions;
++x;
inright = !inright;
}else{
inright = false;
}
continue;
}
@ -531,7 +583,6 @@ notcurses_render_internal(notcurses* nc){
// * we are a partial glyph, and the previous was default on both, or
// * we are a no-foreground glyph, and the previous was default background, or
// * we are a no-background glyph, and the previous was default foreground
bool noforeground = cell_noforeground_p(&c);
bool nobackground = cell_nobackground_p(p, &c);
if((!noforeground && cell_fg_default_p(&c)) || (!nobackground && cell_bg_default_p(&c))){
@ -583,11 +634,9 @@ notcurses_render_internal(notcurses* nc){
++nc->stats.bgelisions;
}
}
// fprintf(stderr, "[%02d/%02d] 0x%02x 0x%02x 0x%02x %p\n", y, x, r, g, b, p);
//fprintf(stderr, "[%02d/%02d] 0x%02x 0x%02x 0x%02x %p\n", y, x, r, g, b, p);
term_putc(out, p, &c);
if(cell_double_wide_p(&c)){
++x;
}
inright = cell_double_wide_p(&c);
}
}
ret |= fflush(out);
@ -627,7 +676,9 @@ char* notcurses_at_yx(notcurses* nc, int y, int x, cell* c){
if(x >= 0 || x < nc->lfdimx){
const cell* srccell = &nc->lastframe[y * nc->lfdimx + x];
memcpy(c, srccell, sizeof(*c)); // unsafe copy of gcluster
egc = cell_egc_copy(nc->stdscr, srccell);
//fprintf(stderr, "COPYING: %d from %p\n", c->gcluster, &nc->pool);
egc = pool_egc_copy(&nc->pool, srccell);
c->gcluster = 0; // otherwise cell_release() will blow up
}
}
}

@ -0,0 +1,75 @@
#include <cstdlib>
#include <clocale>
#include <unistd.h>
#include <notcurses.h>
constexpr auto DELAY = 1;
// dump two wide glyphs, then create a new plane and drop it atop them
int stomper(struct notcurses* nc, struct ncplane* nn){
ncplane_move_yx(nn, 0, 1);
notcurses_render(nc);
sleep(DELAY);
// first wide glyph gone, second present
ncplane_move_yx(nn, 1, 0);
notcurses_render(nc);
sleep(DELAY);
// second wide glyph gone, first present
ncplane_move_yx(nn, 2, 2);
notcurses_render(nc);
sleep(DELAY);
ncplane_move_yx(nn, 4, 0);
notcurses_render(nc);
sleep(DELAY);
ncplane_move_yx(nn, 5, 1);
notcurses_render(nc);
sleep(DELAY);
ncplane_move_yx(nn, 6, 2);
notcurses_render(nc);
sleep(DELAY);
return 0;
}
int main(void){
setlocale(LC_ALL, "");
notcurses_options opts{};
struct notcurses* nc = notcurses_init(&opts, stdout);
struct ncplane* n = notcurses_stdplane(nc);
// first, a 2x1 with "AB"
struct ncplane* nn = ncplane_new(nc, 1, 2, 1, 16, nullptr);
ncplane_set_fg_rgb(nn, 0xc0, 0x80, 0xc0);
ncplane_set_bg_rgb(nn, 0x20, 0x00, 0x20);
ncplane_putstr(nn, "AB");
ncplane_set_fg_rgb(n, 0x80, 0xc0, 0x80);
ncplane_set_bg_rgb(n, 0x00, 0x40, 0x00);
ncplane_putstr(n, "\xe5\xbd\xa2\xe5\x85\xa8");
ncplane_putstr_yx(n, 1, 0, "\xe5\xbd\xa2\xe5\x85\xa8");
ncplane_putstr_yx(n, 2, 0, "\xe5\xbd\xa2\xe5\x85\xa8");
ncplane_putstr_yx(n, 3, 0, "\xe5\xbd\xa2\xe5\x85\xa8");
ncplane_putstr_yx(n, 4, 0, "abcdef");
ncplane_putstr_yx(n, 5, 0, "abcdef");
ncplane_putstr_yx(n, 6, 0, "abcdef");
ncplane_putstr_yx(n, 7, 0, "abcdef");
notcurses_render(nc);
sleep(1);
stomper(nc, nn);
if(ncplane_putstr_yx(nn, 0, 0, "\xe5\xbd\xa1") <= 0){
notcurses_stop(nc);
return EXIT_FAILURE;
}
stomper(nc, nn);
notcurses_stop(nc);
return EXIT_SUCCESS;
}

@ -1,4 +1,5 @@
#include <array>
#include <unistd.h>
#include <cstdlib>
#include <notcurses.h>
#include "internal.h"
@ -120,8 +121,8 @@ TEST_CASE("NCPlane") {
CHECK(0 == notcurses_render(nc_));
}
// Verify we can emit a wide character, and it advances the cursor
SUBCASE("EmitWchar") {
// Verify we can emit a wchar_t, and it advances the cursor
SUBCASE("EmitWcharT") {
const wchar_t* w = L"";
int sbytes = 0;
CHECK(0 < ncplane_putwegc(n_, w, 0, 0, &sbytes));
@ -132,6 +133,36 @@ TEST_CASE("NCPlane") {
CHECK(0 == notcurses_render(nc_));
}
// Verify we can emit a wide character, and it advances the cursor
SUBCASE("EmitWideAsian") {
const char* w = "\u5168";
int sbytes = 0;
CHECK(0 < ncplane_putegc(n_, w, 0, 0, &sbytes));
int x, y;
ncplane_cursor_yx(n_, &y, &x);
CHECK(0 == y);
CHECK(2 == x);
CHECK(0 == notcurses_render(nc_));
}
// Verify a wide character is rejected on the last column
SUBCASE("EmitWideAsian") {
const char* w = "\u5168";
int sbytes = 0;
int dimx;
ncplane_dim_yx(n_, NULL, &dimx);
CHECK(0 < ncplane_putegc_yx(n_, 0, dimx - 3, w, 0, 0, &sbytes));
int x, y;
ncplane_cursor_yx(n_, &y, &x);
CHECK(0 == y);
CHECK(dimx - 1 == x);
// now it ought be rejected
CHECK(0 > ncplane_putegc(n_, w, 0, 0, &sbytes));
CHECK(0 == y);
CHECK(dimx - 1 == x);
CHECK(0 == notcurses_render(nc_));
}
// Verify we can emit a multibyte string, and it advances the cursor
SUBCASE("EmitStr") {
const char s[] = "Σιβυλλα τι θελεις; respondebat illa: αποθανειν θελω.";
@ -860,6 +891,32 @@ TEST_CASE("NCPlane") {
CHECK(0 == notcurses_render(nc_));
}
SUBCASE("BoxedWideGlyph") {
struct ncplane* ncp = ncplane_new(nc_, 3, 4, 0, 0, nullptr);
REQUIRE(ncp);
int dimx, dimy;
ncplane_dim_yx(n_, &dimy, &dimx);
CHECK(0 == ncplane_rounded_box_sized(ncp, 0, 0, 3, 4, 0));
CHECK(0 < ncplane_putegc_yx(ncp, 1, 1, "\xf0\x9f\xa6\x82", 0, 0, NULL));
CHECK(0 == notcurses_render(nc_));
cell c = CELL_TRIVIAL_INITIALIZER;
REQUIRE(0 < ncplane_at_yx(ncp, 1, 0, &c));
CHECK(!strcmp(cell_extended_gcluster(ncp, &c), ""));
cell_release(ncp, &c);
char* egc = notcurses_at_yx(nc_, 1, 0, &c);
REQUIRE(egc);
CHECK(!strcmp(egc, ""));
free(egc);
REQUIRE(0 < ncplane_at_yx(ncp, 1, 3, &c));
CHECK(!strcmp(cell_extended_gcluster(ncp, &c), ""));
cell_release(ncp, &c);
egc = notcurses_at_yx(nc_, 1, 3, &c);
REQUIRE(egc);
CHECK(!strcmp(egc, ""));
free(egc);
CHECK(0 == ncplane_destroy(ncp));
}
CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));

@ -0,0 +1,133 @@
#include <cstdlib>
#include <iostream>
#include "internal.h"
#include "main.h"
TEST_CASE("RenderTest") {
if(getenv("TERM") == nullptr){
return;
}
notcurses_options nopts{};
nopts.inhibit_alternate_screen = true;
nopts.suppress_banner = true;
FILE* outfp_ = fopen("/dev/tty", "wb");
REQUIRE(outfp_);
struct notcurses* nc_ = notcurses_init(&nopts, outfp_);
REQUIRE(nc_);
struct ncplane* n_ = notcurses_stdplane(nc_);
REQUIRE(n_);
// If an ncplane is moved atop the right half of a wide glyph, the entire
// glyph should be oblitrated.
SUBCASE("PlaneStompsWideGlyph"){
cell c = CELL_TRIVIAL_INITIALIZER;
char* egc;
// print two wide glyphs on the standard plane
int y, x;
ncplane_yx(n_, &y, &x);
CHECK(0 == y);
CHECK(0 == x);
CHECK(3 == ncplane_putstr(n_, "\xe5\x85\xa8"));
ncplane_cursor_yx(n_, &y, &x);
CHECK(0 == y);
CHECK(2 == x);
CHECK(3 == ncplane_putstr(n_, "\xe5\xbd\xa2"));
ncplane_cursor_yx(n_, &y, &x);
CHECK(0 == y);
CHECK(4 == x);
CHECK(!notcurses_render(nc_));
// should be wide char 1
REQUIRE(3 == ncplane_at_yx(n_, 0, 0, &c));
egc = cell_egc_copy(n_, &c);
REQUIRE(egc);
CHECK(!strcmp("\xe5\x85\xa8", egc));
CHECK(cell_double_wide_p(&c));
free(egc);
egc = notcurses_at_yx(nc_, 0, 0, &c);
REQUIRE(egc);
CHECK(!strcmp("\xe5\x85\xa8", egc));
CHECK(cell_double_wide_p(&c));
free(egc);
cell_init(&c);
// should be wide char 1 right side
REQUIRE(0 == ncplane_at_yx(n_, 0, 1, &c));
egc = cell_egc_copy(n_, &c);
REQUIRE(egc);
CHECK(!strcmp("", egc));
CHECK(cell_double_wide_p(&c));
free(egc);
egc = notcurses_at_yx(nc_, 0, 1, &c);
REQUIRE(egc);
CHECK(!strcmp("", egc));
CHECK(cell_double_wide_p(&c));
free(egc);
cell_init(&c);
// should be wide char 2
REQUIRE(3 == ncplane_at_yx(n_, 0, 2, &c));
egc = cell_egc_copy(n_, &c);
REQUIRE(egc);
CHECK(!strcmp("\xe5\xbd\xa2", egc));
CHECK(cell_double_wide_p(&c));
free(egc);
egc = notcurses_at_yx(nc_, 0, 2, &c);
REQUIRE(egc);
fprintf(stderr, "HAVE [%s] WANT \xe5\xbd\xa2\n", egc);
CHECK(!strcmp("\xe5\xbd\xa2", egc));
CHECK(cell_double_wide_p(&c));
free(egc);
cell_init(&c);
// should be wide char 2 right side
CHECK(0 == ncplane_at_yx(n_, 0, 3, &c));
egc = cell_egc_copy(n_, &c);
REQUIRE(egc);
CHECK(!strcmp("", egc));
CHECK(cell_double_wide_p(&c));
free(egc);
egc = notcurses_at_yx(nc_, 0, 3, &c);
REQUIRE(egc);
CHECK(!strcmp("", egc));
CHECK(cell_double_wide_p(&c));
free(egc);
cell_init(&c);
struct ncplane* n = ncplane_new(nc_, 1, 2, 0, 1, nullptr);
REQUIRE(n);
CHECK(0 < ncplane_putstr(n, "AB"));
CHECK(!notcurses_render(nc_));
// should be nothing, having been stomped
egc = notcurses_at_yx(nc_, 0, 0, &c);
REQUIRE(egc);
CHECK(!strcmp(" ", egc));
free(egc);
cell_init(&c);
// should be character from higher plane
egc = notcurses_at_yx(nc_, 0, 1, &c);
REQUIRE(egc);
CHECK(!strcmp("A", egc));
free(egc);
cell_init(&c);
egc = notcurses_at_yx(nc_, 0, 2, &c);
REQUIRE(egc);
CHECK(!strcmp("B", egc));
free(egc);
cell_init(&c);
// should be nothing, having been stomped
egc = notcurses_at_yx(nc_, 0, 3, &c);
REQUIRE(egc);
CHECK(!strcmp(" ", egc));
free(egc);
cell_init(&c);
CHECK(!ncplane_destroy(n));
}
CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));
}
Loading…
Cancel
Save