diff --git a/src/lib/egcpool.h b/src/lib/egcpool.h index e544fadab..706e73d9a 100644 --- a/src/lib/egcpool.h +++ b/src/lib/egcpool.h @@ -1,6 +1,7 @@ #ifndef NOTCURSES_EGCPOOL #define NOTCURSES_EGCPOOL +#include #include #include #include @@ -29,15 +30,36 @@ egcpool_init(egcpool* p){ int egcpool_grow(egcpool* pool, size_t len); -// FIXME needs to loop on wcwidth() == 0 +// Eat an EGC from the UTF-8 string input. This consists of extracting a +// multibyte via mbtowc, then continuing to extract any which have zero +// width until hitting another spacing character or a NUL terminator. Writes +// the number of columns occupied to '*colcount'. Returns the number of bytes +// consumed, not including any NUL terminator. Note that neither the number +// of bytes nor columns is necessarily equivalent to the number of decoded code +// points. Such are the ways of Unicode. static inline size_t -utf8_gce_len(const char* gcluster){ +utf8_gce_len(const char* gcluster, int* colcount){ + size_t ret = 0; + *colcount = 0; wchar_t wc; - int r = mbtowc(&wc, gcluster, MB_CUR_MAX); - if(r <= 0){ - return 0; // will cascade into error in egcpool_stash() - } - return r; + int r; + do{ + r = mbtowc(&wc, gcluster, MB_CUR_MAX); + if(r < 0){ + return -1; + }else if(r){ + int cols = wcwidth(wc); + if(cols){ + if(*colcount){ // this must be starting a new EGC, exit and do not claim + break; + } + *colcount += cols; + } + ret += r; + gcluster += r; + } + }while(r); + return ret; } // if we're inserting a EGC of |len| bytes, ought we proactively realloc? @@ -57,10 +79,11 @@ egcpool_alloc_justified(const egcpool* pool, size_t len){ // stash away the provided UTF8, NUL-terminated grapheme cluster. the cluster // should not be less than 2 bytes (such a cluster should be directly stored in // the cell). returns -1 on error, and otherwise a non-negative 24-bit offset. -// The number of bytes copied is stored to |*ulen|. +// The number of bytes copied is stored to '*ulen'. The number of presentation +// columns is stored to '*cols'. static inline int -egcpool_stash(egcpool* pool, const char* egc, size_t* ulen){ - size_t len = utf8_gce_len(egc) + 1; // count the NUL terminator +egcpool_stash(egcpool* pool, const char* egc, size_t* ulen, int* cols){ + size_t len = utf8_gce_len(egc, cols) + 1; // count the NUL terminator if(len <= 2){ // should never be empty, nor a single byte + NUL return -1; } diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 33c05974e..fe316a266 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -602,15 +602,19 @@ term_putc(const notcurses* nc, const ncplane* n, const cell* c){ } static void -advance_cursor(ncplane* n){ +advance_cursor(ncplane* n, int cols){ if(n->y == n->leny){ if(n->x == n->lenx){ return; // stuck! } } - if(++n->x == n->lenx){ - n->x = 0; - ++n->y; + if((n->x += cols) >= n->lenx){ + if(n->y == n->leny){ + n->x = n->lenx; + }else{ + n->x -= n->lenx; + ++n->y; + } } } @@ -683,7 +687,9 @@ cell_duplicate(ncplane* n, cell* targ, const cell* c){ return !!c->gcluster; } size_t ulen; - int eoffset = egcpool_stash(&n->pool, extended_gcluster(n, c), &ulen); + int cols; + // FIXME insert colcount into cell... + int eoffset = egcpool_stash(&n->pool, extended_gcluster(n, c), &ulen, &cols); if(eoffset < 0){ return -1; } @@ -697,7 +703,7 @@ int ncplane_putc(ncplane* n, const cell* c){ } cell* targ = &n->fb[fbcellidx(n, n->y, n->x)]; int ret = cell_duplicate(n, targ, c); - advance_cursor(n); + advance_cursor(n, 1); // FIXME return ret; } @@ -732,7 +738,8 @@ int cell_load(ncplane* n, cell* c, const char* gcluster){ return !!c->gcluster; } size_t ulen; - int eoffset = egcpool_stash(&n->pool, gcluster, &ulen); + int cols; + int eoffset = egcpool_stash(&n->pool, gcluster, &ulen, &cols); if(eoffset < 0){ return -1; }