Created
May 31, 2021 02:59
-
-
Save ahauser31/cce997dac24ab4d655d8c93355045162 to your computer and use it in GitHub Desktop.
Patch that introduces a hack to solve nerdfont icon cutoff in dwm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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