@@ -406,18 +406,18 @@ typedef uint16_t uintattr_t;
406406 * `nech`, and `cech` via `tb_set_cell_ex`. `ech` is only valid when `nech>0`,
407407 * otherwise `ch` is used.
408408 *
409- * For non-single-width codepoints, given `N =wcwidth(ch)/wcswidth( ech)`:
409+ * For non-single-width codepoints, given `W =wcwidth(ch || ech)`:
410410 *
411- * when `N= =0`: termbox forces a single-width cell. Callers should avoid this
411+ * when `W< =0`: termbox forces a single-width cell. Callers should avoid this
412412 * if aiming to render text accurately. Callers may use
413- * `tb_set_cell_ex` or `tb_print*` to render `N ==0` combining
413+ * `tb_set_cell_ex` or `tb_print*` to render `W ==0` combining
414414 * characters.
415415 *
416- * when `N>1 `: termbox zeroes out the following `N -1` cells and skips sending
417- * them to the tty. So, e.g., if the caller sets `x=0,y=0` to an
418- * `N ==2` codepoint, the caller's next set should be at `x=2,y=0`.
416+ * when `W>=2 `: termbox zeroes out the following `W -1` cells and skips sending
417+ * them to the tty. So, e.g., if the caller sets `x=0,y=0` to a
418+ * `W ==2` codepoint, the caller's next set should be at `x=2,y=0`.
419419 * Anything set at `x=1,y=0` will be ignored. If there are not
420- * enough columns remaining on the line to render `N` width , spaces
420+ * enough columns remaining on the line to render `W` cells , spaces
421421 * are sent instead.
422422 *
423423 * See `tb_present` for implementation.
@@ -2329,7 +2329,7 @@ static int bytebuf_flush(struct bytebuf *b, int fd);
23292329static int bytebuf_reserve (struct bytebuf * b , size_t sz );
23302330static int bytebuf_free (struct bytebuf * b );
23312331static int tb_iswprint_ex (uint32_t ch , int * width );
2332- static int tb_wcswidth (uint32_t * ch , size_t nch );
2332+ static int tb_cluster_width (uint32_t * ch , size_t nch );
23332333
23342334int tb_init (void ) {
23352335 return tb_init_file ("/dev/tty" );
@@ -2424,12 +2424,12 @@ int tb_present(void) {
24242424 {
24252425#ifdef TB_OPT_EGC
24262426 if (back -> nech > 0 )
2427- w = tb_wcswidth (back -> ech , back -> nech );
2427+ w = tb_cluster_width (back -> ech , back -> nech );
24282428 else
24292429#endif
24302430 w = tb_wcwidth ((wchar_t )back -> ch );
24312431 }
2432- if (w < 1 ) w = 1 ; // wcwidth qreturns -1 for invalid codepoints
2432+ if (w < 1 ) w = 1 ; // wcwidth returns -1 for invalid codepoints
24332433
24342434 if (cell_cmp (back , front ) != 0 ) {
24352435 cell_copy (front , back );
@@ -4237,27 +4237,35 @@ int tb_iswprint(uint32_t ch) {
42374237}
42384238
42394239int tb_wcwidth (uint32_t ch ) {
4240+ int w ;
42404241#ifdef TB_OPT_LIBC_WCHAR
4241- return wcwidth ((wchar_t )ch );
4242+ w = wcwidth ((wchar_t )ch );
42424243#else
4243- return tb_wcswidth ( & ch , 1 );
4244+ tb_iswprint_ex ( ch , & w );
42444245#endif
4246+ return w ;
42454247}
42464248
4247- static int tb_wcswidth (uint32_t * ch , size_t nch ) {
4248- #ifdef TB_OPT_LIBC_WCHAR
4249- return wcswidth ((wchar_t * )ch , nch );
4250- #else
4251- int sw = 0 ;
4249+ static int tb_cluster_width (uint32_t * ch , size_t nch ) {
4250+ int wmax = -1 ;
4251+ int vs15 = 0 , vs16 = 0 , ri = 0 , zwj = 0 ;
42524252 size_t i = 0 ;
42534253 for (i = 0 ; i < nch ; i ++ ) {
4254- int w ;
4255- tb_iswprint_ex (ch [i ], & w );
4256- if (w < 0 ) return -1 ;
4257- sw += w ;
4254+ uint32_t c = ch [i ];
4255+ switch (c ) {
4256+ case 0xfe0e : ++ vs15 ; break ;
4257+ case 0xfe0f : ++ vs16 ; break ;
4258+ case 0x200d : ++ zwj ; break ;
4259+ default : if (c >= 0x1f1e6 && c <= 0x1f1ff ) ++ ri ;
4260+ }
4261+ int w = tb_wcwidth (c );
4262+ if (w > wmax ) wmax = w ;
42584263 }
4259- return sw ;
4260- #endif
4264+ if (wmax >= 1 ) {
4265+ if (vs15 ) return 1 ;
4266+ else if (vs16 || zwj || ri >= 2 ) return 2 ;
4267+ }
4268+ return wmax ;
42614269}
42624270
42634271static int tb_iswprint_ex (uint32_t ch , int * w ) {
0 commit comments