st

fork of suckless's simple terminal
Index Commits Files Refs README LICENSE
commit 210dda9570095443bac887c2bfcd75f2bcc23780
parent a4358a1fbd1c71269129404c9af4f539b2d7627c
Author: Christoph Lohmann <20h@r-36.net>
Date:   Sat,  7 Sep 2013 12:41:36 +0200

Wide character support.

Thanks "Eon S. Jeon" <esjeon@hyunmu.am>!

Diffstat:
MTODO | 1-
Mst.c | 84++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
2 files changed, 68 insertions(+), 17 deletions(-)
diff --git 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
@@ -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);