Wide character support.
authorChristoph Lohmann <20h@r-36.net>
Sat, 7 Sep 2013 10:41:36 +0000 (12:41 +0200)
committerChristoph Lohmann <20h@r-36.net>
Sat, 7 Sep 2013 10:41:36 +0000 (12:41 +0200)
Thanks "Eon S. Jeon" <esjeon@hyunmu.am>!

TODO
st.c

diff --git a/TODO b/TODO
index 794b71b..0c538e5 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,7 +1,6 @@
 vt emulation
 ------------
 
-* wide-character support in conjunction with fallback xft code 
 * double-height support
 
 code & interface
diff --git a/st.c b/st.c
index 0fa0c86..96d45bf 100644 (file)
--- a/st.c
+++ b/st.c
@@ -27,6 +27,7 @@
 #include <X11/keysym.h>
 #include <X11/Xft/Xft.h>
 #include <fontconfig/fontconfig.h>
+#include <wchar.h>
 
 #include "arg.h"
 
@@ -96,6 +97,8 @@ enum glyph_attribute {
        ATTR_ITALIC    = 16,
        ATTR_BLINK     = 32,
        ATTR_WRAP      = 64,
+       ATTR_WIDE      = 128,
+       ATTR_WDUMMY    = 256,
 };
 
 enum cursor_movement {
@@ -165,7 +168,7 @@ typedef unsigned short ushort;
 
 typedef struct {
        char c[UTF_SIZ]; /* character code */
-       uchar mode;      /* attribute flags */
+       ushort mode;      /* attribute flags */
        ulong fg;        /* foreground  */
        ulong bg;        /* background  */
 } Glyph;
@@ -719,8 +722,13 @@ selsnap(int mode, int *x, int *y, int direction) {
                                }
                        }
 
+                       if(term.line[*y][*x+direction].mode & ATTR_WDUMMY) {
+                               *x += direction;
+                               continue;
+                       }
+
                        if(strchr(worddelimiters,
-                                       term.line[*y][*x + direction].c[0])) {
+                                       term.line[*y][*x+direction].c[0])) {
                                break;
                        }
 
@@ -932,7 +940,7 @@ selcopy(void) {
                                /* nothing */;
 
                        for(x = 0; gp <= last; x++, ++gp) {
-                               if(!selected(x, y))
+                               if(!selected(x, y) || (gp->mode & ATTR_WDUMMY))
                                        continue;
 
                                size = utf8size(gp->c);
@@ -1533,6 +1541,16 @@ tsetchar(char *c, Glyph *attr, int x, int y) {
                }
        }
 
+       if(term.line[y][x].mode & ATTR_WIDE) {
+               if(x+1 < term.col) {
+                       term.line[y][x+1].c[0] = ' ';
+                       term.line[y][x+1].mode &= ~ATTR_WDUMMY;
+               }
+       } else if(term.line[y][x].mode & ATTR_WDUMMY) {
+               term.line[y][x-1].c[0] = ' ';
+               term.line[y][x-1].mode &= ~ATTR_WIDE;
+       }
+
        term.dirty[y] = 1;
        term.line[y][x] = *attr;
        memcpy(term.line[y][x].c, c, UTF_SIZ);
@@ -2222,6 +2240,15 @@ void
 tputc(char *c, int len) {
        uchar ascii = *c;
        bool control = ascii < '\x20' || ascii == 0177;
+       long u8char;
+       int width;
+
+       if(len == 1) {
+               width = 1;
+       } else {
+               utf8decode(c, &u8char);
+               width = wcwidth(u8char);
+       }
 
        if(iofd != -1) {
                if(xwrite(iofd, c, len) < 0) {
@@ -2469,9 +2496,20 @@ tputc(char *c, int len) {
                        (term.col - term.c.x - 1) * sizeof(Glyph));
        }
 
+       if(term.c.x+width > term.col)
+               tnewline(1);
+
        tsetchar(c, &term.c.attr, term.c.x, term.c.y);
-       if(term.c.x+1 < term.col) {
-               tmoveto(term.c.x+1, term.c.y);
+
+       if(width == 2) {
+               term.line[term.c.y][term.c.x].mode |= ATTR_WIDE;
+               if(term.c.x+1 < term.col) {
+                       term.line[term.c.y][term.c.x+1].c[0] = '\0';
+                       term.line[term.c.y][term.c.x+1].mode = ATTR_WDUMMY;
+               }
+       }
+       if(term.c.x+width < term.col) {
+               tmoveto(term.c.x+width, term.c.y);
        } else {
                term.c.state |= CURSOR_WRAPNEXT;
        }
@@ -3173,7 +3211,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                                xp, winy + frc[i].font->ascent,
                                (FcChar8 *)u8c, u8cblen);
 
-               xp += xw.cw;
+               xp += xw.cw * wcwidth(u8char);
        }
 
        /*
@@ -3193,18 +3231,27 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 void
 xdrawcursor(void) {
        static int oldx = 0, oldy = 0;
-       int sl;
+       int sl, width, curx;
        Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs};
 
        LIMIT(oldx, 0, term.col-1);
        LIMIT(oldy, 0, term.row-1);
 
+       curx = term.c.x;
+
+       /* adjust position if in dummy */
+       if(term.line[oldy][oldx].mode & ATTR_WDUMMY)
+               oldx--;
+       if(term.line[term.c.y][curx].mode & ATTR_WDUMMY)
+               curx--;
+
        memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ);
 
        /* remove the old cursor */
        sl = utf8size(term.line[oldy][oldx].c);
+       width = (term.line[oldy][oldx].mode & ATTR_WIDE)? 2 : 1;
        xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx,
-                       oldy, 1, sl);
+                       oldy, width, sl);
 
        /* draw the new one */
        if(!(IS_SET(MODE_HIDE))) {
@@ -3216,26 +3263,28 @@ xdrawcursor(void) {
                        }
 
                        sl = utf8size(g.c);
-                       xdraws(g.c, g, term.c.x, term.c.y, 1, sl);
+                       width = (term.line[term.c.y][curx].mode & ATTR_WIDE)\
+                               ? 2 : 1;
+                       xdraws(g.c, g, term.c.x, term.c.y, width, sl);
                } else {
                        XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                       borderpx + term.c.x * xw.cw,
+                                       borderpx + curx * xw.cw,
                                        borderpx + term.c.y * xw.ch,
                                        xw.cw - 1, 1);
                        XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                       borderpx + term.c.x * xw.cw,
+                                       borderpx + curx * xw.cw,
                                        borderpx + term.c.y * xw.ch,
                                        1, xw.ch - 1);
                        XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                       borderpx + (term.c.x + 1) * xw.cw - 1,
+                                       borderpx + (curx + 1) * xw.cw - 1,
                                        borderpx + term.c.y * xw.ch,
                                        1, xw.ch - 1);
                        XftDrawRect(xw.draw, &dc.col[defaultcs],
-                                       borderpx + term.c.x * xw.cw,
+                                       borderpx + curx * xw.cw,
                                        borderpx + (term.c.y + 1) * xw.ch - 1,
                                        xw.cw, 1);
                }
-               oldx = term.c.x, oldy = term.c.y;
+               oldx = curx, oldy = term.c.y;
        }
 }
 
@@ -3284,6 +3333,7 @@ drawregion(int x1, int y1, int x2, int y2) {
        Glyph base, new;
        char buf[DRAW_BUF_SIZ];
        bool ena_sel = sel.ob.x != -1;
+       long u8char;
 
        if(sel.alt ^ IS_SET(MODE_ALTSCREEN))
                ena_sel = 0;
@@ -3301,6 +3351,8 @@ drawregion(int x1, int y1, int x2, int y2) {
                ic = ib = ox = 0;
                for(x = x1; x < x2; x++) {
                        new = term.line[y][x];
+                       if(new.mode == ATTR_WDUMMY)
+                               continue;
                        if(ena_sel && selected(x, y))
                                new.mode ^= ATTR_REVERSE;
                        if(ib > 0 && (ATTRCMP(base, new)
@@ -3313,10 +3365,10 @@ drawregion(int x1, int y1, int x2, int y2) {
                                base = new;
                        }
 
-                       sl = utf8size(new.c);
+                       sl = utf8decode(new.c, &u8char);
                        memcpy(buf+ib, new.c, sl);
                        ib += sl;
-                       ++ic;
+                       ic += (new.mode & ATTR_WIDE)? 2 : 1;
                }
                if(ib > 0)
                        xdraws(buf, base, ox, y, ic, ib);