Here diacritics will now combine, but not stack (no terminal does proper
stacking). The diacritic was added to the glyph struct so that it won't
occupy a seperate column (matching its presentation).

specbuf is doubled in size to handle 2 specs per glyph (base char, and its
combiner, if any).

REP behavior is consistant with xterm when there was a diacritic part of
the last character, which is that only base is repeated.
---
There is for sure a better way of dealing with the 'badcombining' thing, but I'm
not very familiar with X(ft). Anyone here with more insight? To repro:

 - Delete the badcombining test (x.c:977)
 - Compile st with "Liberation Mono" font, notice diacritics are off by 1 along 
x
(it's trying to back-up for us?)

 - Compile st with "SourceCodePro" font, notice diacritics are correct

Please let me know what yall think!

Have a test string (notice 'o\xcc\x81' (U+006F, U+0301) is the decomposed form
of 'รณ' (U+00F3)):

    printf 'Bo\xcc\x81br k*rwa ja p*erdole jakie bydle\xcc\xa8! 
w\xcc\x82a\xe2\x83\x97?\numlaut replacing: o\xcc\x83\xcc\x88!\numlaut replacing 
the previous line: o\xcc\x83  \n\xcc\x88'

Regards,
Vally.

 TODO |  3 +--
 st.c | 47 +++++++++++++++++++++++++++++++------
 st.h |  1 +
 x.c  | 75 +++++++++++++++++++++++++++++++++++++++++++++---------------
 4 files changed, 99 insertions(+), 27 deletions(-)

diff --git a/TODO b/TODO
index 5f74cd5..c2a528c 100644
--- a/TODO
+++ b/TODO
@@ -10,8 +10,7 @@ code & interface
 
 drawing
 -------
-* add diacritics support to xdraws()
-       * switch to a suckless font drawing library
+* switch to a suckless font drawing library
 * make the font cache simpler
 * add better support for brightening of the upper colors
 
diff --git a/st.c b/st.c
index 03b9bc8..c82a990 100644
--- a/st.c
+++ b/st.c
@@ -189,6 +189,7 @@ static void tscrollup(int, int);
 static void tscrolldown(int, int);
 static void tsetattr(const int *, int);
 static void tsetchar(Rune, const Glyph *, int, int);
+static void tsetdiacritic(Rune, const Glyph *, int, int);
 static void tsetdirt(int, int);
 static void tsetscroll(int, int);
 static void tswapscreen(void);
@@ -412,7 +413,7 @@ tlinelen(int y)
        if (term.line[y][i - 1].mode & ATTR_WRAP)
                return i;
 
-       while (i > 0 && term.line[y][i - 1].u == ' ')
+       while (i > 0 && term.line[y][i - 1].u == ' ' && term.line[y][i - 1].d 
== '\0')
                --i;
 
        return i;
@@ -591,7 +592,7 @@ getsel(void)
        if (sel.ob.x == -1)
                return NULL;
 
-       bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
+       bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ * 2;
        ptr = str = xmalloc(bufsize);
 
        /* append every set & selected glyph to the selection */
@@ -609,7 +610,7 @@ getsel(void)
                        lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
                }
                last = &term.line[y][MIN(lastx, linelen-1)];
-               while (last >= gp && last->u == ' ')
+               while (last >= gp && last->u == ' ' && last->d == '\0')
                        --last;
 
                for ( ; gp <= last; ++gp) {
@@ -617,6 +618,8 @@ getsel(void)
                                continue;
 
                        ptr += utf8encode(gp->u, ptr);
+                       if (gp->d)
+                               ptr += utf8encode(gp->d, ptr);
                }
 
                /*
@@ -1220,6 +1223,28 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
        term.line[y][x].u = u;
 }
 
+void
+tsetdiacritic(Rune d, const Glyph * attr, int x, int y)
+{
+       Rune u;
+       if (x > 0) {
+               --x;
+       } else if (y > 0) {
+               --y;
+               x = tlinelen(y);
+               if (x > 0)
+                       --x;
+       }
+       if (x > 0 && (term.line[y][x].mode & ATTR_WDUMMY)) {
+               --x;
+       }
+       term.dirty[y] = 1;
+       u = term.line[y][x].u;
+       term.line[y][x] = *attr;
+       term.line[y][x].u = u; /* *attr stomps on u */
+       term.line[y][x].d = d;
+}
+
 void
 tclearregion(int x1, int y1, int x2, int y2)
 {
@@ -1246,6 +1271,7 @@ tclearregion(int x1, int y1, int x2, int y2)
                        gp->bg = term.c.attr.bg;
                        gp->mode = 0;
                        gp->u = ' ';
+                       gp->d = '\0';
                }
        }
 }
@@ -2088,9 +2114,12 @@ tdumpline(int n)
 
        bp = &term.line[n][0];
        end = &bp[MIN(tlinelen(n), term.col) - 1];
-       if (bp != end || bp->u != ' ') {
-               for ( ; bp <= end; ++bp)
+       if (bp != end || bp->u != ' ' || bp->d != '\0') {
+               for ( ; bp <= end; ++bp) {
                        tprinter(buf, utf8encode(bp->u, buf));
+                       if (bp->d)
+                               tprinter(buf, utf8encode(bp->d, buf));
+               }
        }
        tprinter("\n", 1);
 }
@@ -2493,8 +2522,12 @@ check_control_code:
                gp = &term.line[term.c.y][term.c.x];
        }
 
-       tsetchar(u, &term.c.attr, term.c.x, term.c.y);
-       term.lastc = u;
+       if (width > 0) {
+               tsetchar(u, &term.c.attr, term.c.x, term.c.y);
+               term.lastc = u;
+       } else {
+               tsetdiacritic(u, &term.c.attr, term.c.x, term.c.y);
+       }
 
        if (width == 2) {
                gp->mode |= ATTR_WIDE;
diff --git a/st.h b/st.h
index fd3b0d8..cddccde 100644
--- a/st.h
+++ b/st.h
@@ -62,6 +62,7 @@ typedef uint_least32_t Rune;
 #define Glyph Glyph_
 typedef struct {
        Rune u;           /* character code */
+       Rune d;           /* diacritic */
        ushort mode;      /* attribute flags */
        uint32_t fg;      /* foreground  */
        uint32_t bg;      /* background  */
diff --git a/x.c b/x.c
index d73152b..c23ff8e 100644
--- a/x.c
+++ b/x.c
@@ -125,6 +125,7 @@ typedef struct {
        int descent;
        int badslant;
        int badweight;
+       int badcombining;
        short lbearing;
        short rbearing;
        XftFont *match;
@@ -142,7 +143,7 @@ typedef struct {
 
 static inline ushort sixd_to_16bit(int);
 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, 
int);
-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int);
+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int, int);
 static void xdrawglyph(Glyph, int, int);
 static void xclear(int, int, int, int);
 static int xgeommasktogravity(int);
@@ -757,7 +758,7 @@ xresize(int col, int row)
        xclear(0, 0, win.w, win.h);
 
        /* resize to new width */
-       xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
+       xw.specbuf = xrealloc(xw.specbuf, col * 2 * sizeof(GlyphFontSpec));
 }
 
 ushort
@@ -977,6 +978,16 @@ xloadfont(Font *f, FcPattern *pattern)
        f->height = f->ascent + f->descent;
        f->width = DIVCEIL(extents.xOff, strlen(ascii_printable));
 
+       /*
+        * Test xoff to see if the font is trying to
+        * help us position diacritics along x.
+        */
+       XftTextExtentsUtf8(xw.dpy, f->match,
+               (const FcChar8 *) "a\xcc\x81",
+               strlen("a\xcc\x81"), &extents);
+
+       f->badcombining = (extents.xOff <= f->width) ? 1 : 0;
+
        return 0;
 }
 
@@ -1188,7 +1199,7 @@ xinit(int cols, int rows)
        XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
 
        /* font spec buffer */
-       xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
+       xw.specbuf = xmalloc(cols * 2 * sizeof(GlyphFontSpec));
 
        /* Xft rendering context */
        xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
@@ -1256,11 +1267,11 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
-       int i, f, numspecs = 0;
+       int i, f, numspecs = 0, ondiacritic = 0;
 
        for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
                /* Fetch rune and mode for current glyph. */
-               rune = glyphs[i].u;
+               rune = ondiacritic ? glyphs[i].d : glyphs[i].u;
                mode = glyphs[i].mode;
 
                /* Skip dummy wide-character spacing. */
@@ -1291,10 +1302,19 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
                if (glyphidx) {
                        specs[numspecs].font = font->match;
                        specs[numspecs].glyph = glyphidx;
-                       specs[numspecs].x = (short)xp;
+                       specs[numspecs].x = (short)(xp + (
+                               (ondiacritic && font->badcombining) ?
+                                       runewidth :
+                                       0.0));
                        specs[numspecs].y = (short)yp;
-                       xp += runewidth;
                        numspecs++;
+                       if (!ondiacritic && glyphs[i].d) {
+                               ondiacritic = 1;
+                               --i;
+                       } else {
+                               ondiacritic = 0;
+                               xp += runewidth;
+                       }
                        continue;
                }
 
@@ -1365,18 +1385,32 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
 
                specs[numspecs].font = frc[f].font;
                specs[numspecs].glyph = glyphidx;
-               specs[numspecs].x = (short)xp;
+               /*
+                * TODO: fallback font didn't come from xloadfont,
+                * don't know if it's badcombining.
+                */
+               specs[numspecs].x = (short)(xp + (
+                       (ondiacritic && font->badcombining) ?
+                               runewidth :
+                               0.0));
                specs[numspecs].y = (short)yp;
-               xp += runewidth;
                numspecs++;
+               if (!ondiacritic && glyphs[i].d) {
+                       ondiacritic = 1;
+                       --i;
+               } else {
+                       ondiacritic = 0;
+                       xp += runewidth;
+               }
        }
 
        return numspecs;
 }
 
 void
-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, 
int y)
+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int numspecs, 
int numdiacritics, int x, int y)
 {
+       int len = numspecs - numdiacritics;
        int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
        int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
            width = charlen * win.cw;
@@ -1492,7 +1526,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph 
base, int len, int x, i
        XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
 
        /* Render the glyphs. */
-       XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
+       XftDrawGlyphFontSpec(xw.draw, fg, specs, numspecs);
 
        /* Render underline and strikethrough. */
        if (base.mode & ATTR_UNDERLINE) {
@@ -1513,10 +1547,10 @@ void
 xdrawglyph(Glyph g, int x, int y)
 {
        int numspecs;
-       XftGlyphFontSpec spec;
+       XftGlyphFontSpec specs[2];
 
-       numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
-       xdrawglyphfontspecs(&spec, g, numspecs, x, y);
+       numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
+       xdrawglyphfontspecs(specs, g, numspecs, g.d ? 1 : 0, x, y);
 }
 
 void
@@ -1657,12 +1691,12 @@ xstartdraw(void)
 void
 xdrawline(Line line, int x1, int y1, int x2)
 {
-       int i, x, ox, numspecs;
+       int i, x, ox, numspecs, numdiacritics;
        Glyph base, new;
        XftGlyphFontSpec *specs = xw.specbuf;
 
        numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
-       i = ox = 0;
+       i = ox = numdiacritics = 0;
        for (x = x1; x < x2 && i < numspecs; x++) {
                new = line[x];
                if (new.mode == ATTR_WDUMMY)
@@ -1670,19 +1704,24 @@ xdrawline(Line line, int x1, int y1, int x2)
                if (selected(x, y1))
                        new.mode ^= ATTR_REVERSE;
                if (i > 0 && ATTRCMP(base, new)) {
-                       xdrawglyphfontspecs(specs, base, i, ox, y1);
+                       xdrawglyphfontspecs(specs, base, i, numdiacritics, ox, 
y1);
                        specs += i;
                        numspecs -= i;
                        i = 0;
+                       numdiacritics = 0;
                }
                if (i == 0) {
                        ox = x;
                        base = new;
                }
                i++;
+               if (new.d) {
+                       i++;
+                       numdiacritics++;
+               }
        }
        if (i > 0)
-               xdrawglyphfontspecs(specs, base, i, ox, y1);
+               xdrawglyphfontspecs(specs, base, i, numdiacritics, ox, y1);
 }
 
 void
-- 
2.49.0



Reply via email to