Skip to content

Instantly share code, notes, and snippets.

@ahauser31
Created May 31, 2021 02:59
Show Gist options
  • Save ahauser31/cce997dac24ab4d655d8c93355045162 to your computer and use it in GitHub Desktop.
Save ahauser31/cce997dac24ab4d655d8c93355045162 to your computer and use it in GitHub Desktop.
Patch that introduces a hack to solve nerdfont icon cutoff in dwm
diff --git a/config.mk b/config.mk
index 7084c33..1b29d76 100644
--- a/config.mk
+++ b/config.mk
@@ -15,7 +15,7 @@ XINERAMALIBS = -lXinerama
XINERAMAFLAGS = -DXINERAMA
# freetype
-FREETYPELIBS = -lfontconfig -lXft
+FREETYPELIBS = -lfontconfig -lXft `pkg-config --libs freetype2`
FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment)
#FREETYPEINC = ${X11INC}/freetype2
diff --git a/drw.c b/drw.c
index 4cdbcbe..e061598 100644
--- a/drw.c
+++ b/drw.c
@@ -10,6 +10,7 @@
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
+#define NUM_LOCAL 1024
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
@@ -140,11 +141,11 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
* https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
* and lots more all over the internet.
*/
- FcBool iscol;
- if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
- XftFontClose(drw->dpy, xfont);
- return NULL;
- }
+ /* FcBool iscol; */
+ /* if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { */
+ /* XftFontClose(drw->dpy, xfont); */
+ /* return NULL; */
+ /* } */
font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont;
@@ -265,6 +266,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
FcPattern *match;
XftResult result;
int charexists = 0;
+ GlyphWidths gw;
if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
return 0;
@@ -308,10 +310,15 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
}
if (utf8strlen) {
- drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
+ gw = drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
/* shorten text if necessary */
- for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
- drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
+ for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) {
+ if (gw.nglyphs) {
+ gw.nglyphs = 0;
+ free(gw.glyphwidths);
+ }
+ gw = drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
+ }
if (len) {
memcpy(buf, utf8str, len);
@@ -322,12 +329,17 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
if (render) {
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
- XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
- usedfont->xfont, x, ty, (XftChar8 *)buf, len);
+ XftPatchedDrawStringUtf8(drw->dpy, d, &drw->scheme[invert ? ColBg : ColFg], usedfont->xfont, x, ty, (XftChar8 *)buf, len, gw);
+ /* XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], usedfont->xfont, x, ty, (XftChar8 *)buf, len); */
}
x += ew;
w -= ew;
}
+
+ if (gw.nglyphs) {
+ gw.nglyphs = 0;
+ free(gw.glyphwidths);
+ }
}
if (!*text) {
@@ -397,19 +409,28 @@ drw_fontset_getwidth(Drw *drw, const char *text)
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
}
-void
+GlyphWidths
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{
XGlyphInfo ext;
+ GlyphWidths res;
+
+ res.nglyphs = 0;
+ res.glyphwidths = NULL;
if (!font || !text)
- return;
+ return res;
- XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
if (w)
+ {
+ res = XftPatchedTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
*w = ext.xOff;
- if (h)
+ } else if (h) {
+ XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
*h = font->h;
+ }
+
+ return res;
}
Cur *
@@ -434,3 +455,122 @@ drw_cur_free(Drw *drw, Cur *cursor)
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}
+
+void
+XftPatchedDrawStringUtf8 (Display *dpy, XftDraw *draw, XftColor *color, XftFont *pub, int x, int y, FcChar8 *string, int len, GlyphWidths gw)
+{
+ FT_UInt *glyphs, glyphs_local[NUM_LOCAL];
+ FcChar32 ucs4;
+ int l, i = 0, size = NUM_LOCAL, deltax = 0, len_orig = len;
+ FT_UInt *glyph;
+ FcChar8 *string_orig = string;
+
+ glyphs = glyphs_local;
+ while (len && (l = FcUtf8ToUcs4 (string, &ucs4, len)) > 0) {
+ if (i == size)
+ break; /* This hack doesn't support strings longer than NUM_LOCAL */
+ glyphs[i++] = XftCharIndex (dpy, pub, ucs4);
+ string += l;
+ len -= l;
+ }
+
+ if (gw.nglyphs && (gw.nglyphs == i))
+ {
+ /* Patched drawing */
+ for (l = 0; l < i; l++) {
+ glyph = &glyphs[l];
+ XftDrawGlyphs (draw, color, pub, x + deltax, y, glyph, 1);
+ deltax += gw.glyphwidths[l];
+ }
+ } else {
+ /* Default drawing */
+ XftDrawStringUtf8(draw, color, pub, x, y, string_orig, len_orig);
+ }
+}
+
+GlyphWidths
+XftPatchedGlyphExtents (Display *dpy, XftFont *pub, FT_UInt *glyphs, int nglyphs, XGlyphInfo *extents)
+{
+ int advancex, width, glyphwidth, bitmap, n = nglyphs, x = 0, i = 0;
+ int *glyphwidths;
+ FT_UInt *g = glyphs;
+ FT_UInt glyph;
+ FT_Face face;
+ FT_Error error;
+ GlyphWidths res;
+
+ /* Calculate the length "correctly" */
+ /* This is a gross oversimplification of what Xft does and may not always work */
+ face = XftLockFace (pub);
+ glyphwidths = (int *)calloc(nglyphs, sizeof(int));
+ while (n--) {
+ glyph = *g++;
+ bitmap = 0;
+ if ((error = FT_Load_Glyph(face, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_NO_BITMAP))) {
+ FT_Load_Glyph(face, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_BITMAP_METRICS_ONLY);
+ bitmap = 1;
+ }
+
+ advancex = face->glyph->advance.x >> 6;
+ if (bitmap)
+ glyphwidth = advancex; /* Bitmaps (emoji) have unscaled width */
+ else {
+ width = face->glyph->metrics.width >> 6;
+ glyphwidth = (advancex > width ? advancex : width);
+ }
+
+ x += glyphwidth;
+ *(glyphwidths + i) = glyphwidth;
+ i++;
+ fprintf(stderr, "Glyph = %u, width = %d, acv = %d\n", (unsigned int)glyph, glyphwidth, advancex);
+ }
+ XftUnlockFace (pub);
+
+ /* Call original glyph extents function , as xft does more than get the extents */
+ XftGlyphExtents(dpy, pub, glyphs, nglyphs, extents) ;
+
+ /* Check if our x is longer than the xOff calculated by the unpatched function */
+ extents->xOff = extents->xOff < (short)x ? (short)x : extents->xOff;
+
+ res.glyphwidths = glyphwidths;
+ res.nglyphs = nglyphs;
+ return res;
+}
+
+GlyphWidths
+XftPatchedTextExtentsUtf8 (Display *dpy, XftFont *pub, FcChar8 *string, int len, XGlyphInfo *extents)
+{
+ FT_UInt *glyphs, *glyphs_new, glyphs_local[NUM_LOCAL];
+ FcChar32 ucs4;
+ int l, size, i = 0;
+ GlyphWidths res;
+
+ glyphs = glyphs_local;
+ size = NUM_LOCAL;
+ res.nglyphs = 0;
+ res.glyphwidths = NULL;
+ while (len && (l = FcUtf8ToUcs4 (string, &ucs4, len)) > 0) {
+ if (i == size) {
+ glyphs_new = malloc ((size_t)size * 2 * sizeof (FT_UInt));
+ if (!glyphs_new) {
+ if (glyphs != glyphs_local)
+ free (glyphs);
+ memset (extents, '\0', sizeof (XGlyphInfo));
+ return res;
+ }
+ memcpy (glyphs_new, glyphs, (size_t)size * sizeof (FT_UInt));
+ size *= 2;
+ if (glyphs != glyphs_local)
+ free (glyphs);
+ glyphs = glyphs_new;
+ }
+ glyphs[i++] = XftCharIndex (dpy, pub, ucs4);
+ string += l;
+ len -= l;
+ }
+ res = XftPatchedGlyphExtents (dpy, pub, glyphs, i, extents);
+ if (glyphs != glyphs_local)
+ free (glyphs);
+
+ return res;
+}
diff --git a/drw.h b/drw.h
index 4bcd5ad..0b2d009 100644
--- a/drw.h
+++ b/drw.h
@@ -1,5 +1,10 @@
/* See LICENSE file for copyright and license details. */
+typedef struct {
+ int *glyphwidths;
+ int nglyphs;
+} GlyphWidths;
+
typedef struct {
Cursor cursor;
} Cur;
@@ -35,7 +40,7 @@ void drw_free(Drw *drw);
Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
void drw_fontset_free(Fnt* set);
unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
-void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
+GlyphWidths drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
/* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
@@ -55,3 +60,8 @@ int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned in
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
+
+/* Patched drawing functions */
+void XftPatchedDrawStringUtf8 (Display *dpy, XftDraw *draw, XftColor *color, XftFont *pub, int x, int y, FcChar8 *string, int len, GlyphWidths gw);
+GlyphWidths XftPatchedGlyphExtents (Display *dpy, XftFont *pub, FT_UInt *glyphs, int nglyphs, XGlyphInfo *extents);
+GlyphWidths XftPatchedTextExtentsUtf8 (Display *dpy, XftFont *pub, FcChar8 *string, int len, XGlyphInfo *extents);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment