>From c48056710a4ca6a6e52be60b0165313f9461f663 Mon Sep 17 00:00:00 2001
From: Marius Iacob <[email protected]>
Date: Tue, 28 Apr 2020 17:18:32 +0300
Subject: [PATCH] Using pango markup for status text

Use a single font. Removed some code utf8 code from drw.
Created for pango 1.44. Older versions might not have getter
for font height, ascent + descent can be used instead.
All texts are rendered with pango but only status is with
markup. Doubled stext size (in case a lot of markup is used).
MIN/MAX is already defined (didn't redefine them).
---
 config.def.h |   2 +-
 config.mk    |   4 +-
 drw.c        | 303 +++++++++++++--------------------------------------
 drw.h        |  17 ++-
 dwm.c        |  28 ++---
 util.h       |   4 +
 6 files changed, 106 insertions(+), 252 deletions(-)

diff --git a/config.def.h b/config.def.h
index 1c0b587..d201ae6 100644
--- a/config.def.h
+++ b/config.def.h
@@ -5,7 +5,7 @@ static const unsigned int borderpx  = 1;        /*
border pixel of windows */
 static const unsigned int snap      = 32;       /* snap pixel */
 static const int showbar            = 1;        /* 0 means no bar */
 static const int topbar             = 1;        /* 0 means bottom bar */
-static const char *fonts[]          = { "monospace:size=10" };
+static const char font[]            = "monospace 10";
 static const char dmenufont[]       = "monospace:size=10";
 static const char col_gray1[]       = "#222222";
 static const char col_gray2[]       = "#444444";
diff --git a/config.mk b/config.mk
index 7084c33..b5c7e12 100644
--- a/config.mk
+++ b/config.mk
@@ -21,8 +21,8 @@ FREETYPEINC = /usr/include/freetype2
 #FREETYPEINC = ${X11INC}/freetype2

 # includes and libs
-INCS = -I${X11INC} -I${FREETYPEINC}
-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+INCS = -I${X11INC} -I${FREETYPEINC} `pkg-config --cflags xft pango pangoxft`
+LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} `pkg-config
--libs xft pango pangoxft`

 # flags
 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L
-DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
diff --git a/drw.c b/drw.c
index 8fd1ca4..6d1b64e 100644
--- a/drw.c
+++ b/drw.c
@@ -4,62 +4,12 @@
 #include <string.h>
 #include <X11/Xlib.h>
 #include <X11/Xft/Xft.h>
+#include <pango/pango.h>
+#include <pango/pangoxft.h>

 #include "drw.h"
 #include "util.h"

-#define UTF_INVALID 0xFFFD
-#define UTF_SIZ     4
-
-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};
-static const long utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,
0x800,  0x10000};
-static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF,
0xFFFF, 0x10FFFF};
-
-static long
-utf8decodebyte(const char c, size_t *i)
-{
- for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
- if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
- return (unsigned char)c & ~utfmask[*i];
- return 0;
-}
-
-static size_t
-utf8validate(long *u, size_t i)
-{
- if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
- *u = UTF_INVALID;
- for (i = 1; *u > utfmax[i]; ++i)
- ;
- return i;
-}
-
-static size_t
-utf8decode(const char *c, long *u, size_t clen)
-{
- size_t i, j, len, type;
- long udecoded;
-
- *u = UTF_INVALID;
- if (!clen)
- return 0;
- udecoded = utf8decodebyte(c[0], &len);
- if (!BETWEEN(len, 1, UTF_SIZ))
- return 1;
- for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
- udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
- if (type)
- return j;
- }
- if (j < len)
- return 0;
- *u = udecoded;
- utf8validate(u, len);
-
- return len;
-}
-
 Drw *
 drw_create(Display *dpy, int screen, Window root, unsigned int w,
unsigned int h)
 {
@@ -99,58 +49,37 @@ drw_free(Drw *drw)
 }

 /* This function is an implementation detail. Library users should use
- * drw_fontset_create instead.
+ * drw_font_create instead.
  */
 static Fnt *
-xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
+xfont_create(Drw *drw, const char *fontname)
 {
  Fnt *font;
- XftFont *xfont = NULL;
- FcPattern *pattern = NULL;
-
- if (fontname) {
- /* Using the pattern found at font->xfont->pattern does not yield the
- * same substitution results as using the pattern returned by
- * FcNameParse; using the latter results in the desired fallback
- * behaviour whereas the former just results in missing-character
- * rectangles being drawn, at least with some fonts. */
- if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
- fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
- return NULL;
- }
- if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
- fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
- XftFontClose(drw->dpy, xfont);
- return NULL;
- }
- } else if (fontpattern) {
- if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
- fprintf(stderr, "error, cannot load font from pattern.\n");
- return NULL;
- }
- } else {
- die("no font specified.");
- }
+ PangoFontMap *fontmap;
+ PangoContext *context;
+ PangoFontDescription *desc;
+ PangoFontMetrics *metrics;

- /* Do not allow using color fonts. This is a workaround for a BadLength
- * error from Xft with color glyphs. Modelled on the Xterm workaround. See
- * https://bugzilla.redhat.com/show_bug.cgi?id=1498269
- * https://lists.suckless.org/dev/1701/30932.html
- * 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;
+
+ if (!fontname) {
+ die("no font specified.");
  }

  font = ecalloc(1, sizeof(Fnt));
- font->xfont = xfont;
- font->pattern = pattern;
- font->h = xfont->ascent + xfont->descent;
  font->dpy = drw->dpy;

+ fontmap = pango_xft_get_font_map(drw->dpy, drw->screen);
+ context = pango_font_map_create_context(fontmap);
+ desc = pango_font_description_from_string(fontname);
+ font->layout = pango_layout_new(context);
+ pango_layout_set_font_description(font->layout, desc);
+
+ metrics = pango_context_get_metrics(context, desc,
pango_language_from_string ("en-us"));
+ font->h = pango_font_metrics_get_height(metrics) / PANGO_SCALE;
+
+ pango_font_metrics_unref(metrics);
+ g_object_unref(context);
+
  return font;
 }

@@ -159,35 +88,28 @@ xfont_free(Fnt *font)
 {
  if (!font)
  return;
- if (font->pattern)
- FcPatternDestroy(font->pattern);
- XftFontClose(font->dpy, font->xfont);
+ if (font->layout)
+ g_object_unref(font->layout);
  free(font);
 }

 Fnt*
-drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
+drw_font_create(Drw* drw, const char font[])
 {
- Fnt *cur, *ret = NULL;
- size_t i;
+ Fnt *fnt = NULL;

- if (!drw || !fonts)
+ if (!drw || !font)
  return NULL;

- for (i = 1; i <= fontcount; i++) {
- if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
- cur->next = ret;
- ret = cur;
- }
- }
- return (drw->fonts = ret);
+ fnt = xfont_create(drw, font);
+
+ return (drw->font = fnt);
 }

 void
-drw_fontset_free(Fnt *font)
+drw_font_free(Fnt *font)
 {
  if (font) {
- drw_fontset_free(font->next);
  xfont_free(font);
  }
 }
@@ -221,13 +143,6 @@ drw_scm_create(Drw *drw, const char *clrnames[],
size_t clrcount)
  return ret;
 }

-void
-drw_setfontset(Drw *drw, Fnt *set)
-{
- if (drw)
- drw->fonts = set;
-}
-
 void
 drw_setscheme(Drw *drw, Clr *scm)
 {
@@ -248,24 +163,16 @@ drw_rect(Drw *drw, int x, int y, unsigned int w,
unsigned int h, int filled, int
 }

 int
-drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h,
unsigned int lpad, const char *text, int invert)
+drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h,
unsigned int lpad, const char *text, int invert, Bool markup)
 {
  char buf[1024];
  int ty;
  unsigned int ew;
  XftDraw *d = NULL;
- Fnt *usedfont, *curfont, *nextfont;
  size_t i, len;
- int utf8strlen, utf8charlen, render = x || y || w || h;
- long utf8codepoint = 0;
- const char *utf8str;
- FcCharSet *fccharset;
- FcPattern *fcpattern;
- FcPattern *match;
- XftResult result;
- int charexists = 0;
-
- if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
+ int render = x || y || w || h;
+
+ if (!drw || (render && !drw->scheme) || !text || !drw->font)
  return 0;

  if (!render) {
@@ -280,98 +187,37 @@ drw_text(Drw *drw, int x, int y, unsigned int w,
unsigned int h, unsigned int lp
  w -= lpad;
  }

- usedfont = drw->fonts;
- while (1) {
- utf8strlen = 0;
- utf8str = text;
- nextfont = NULL;
- while (*text) {
- utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
- for (curfont = drw->fonts; curfont; curfont = curfont->next) {
- charexists = charexists || XftCharExists(drw->dpy, curfont->xfont,
utf8codepoint);
- if (charexists) {
- if (curfont == usedfont) {
- utf8strlen += utf8charlen;
- text += utf8charlen;
- } else {
- nextfont = curfont;
- }
- break;
- }
- }
-
- if (!charexists || nextfont)
- break;
- else
- charexists = 0;
- }
-
- if (utf8strlen) {
- 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);
-
- if (len) {
- memcpy(buf, utf8str, len);
- buf[len] = '\0';
- if (len < utf8strlen)
- for (i = len; i && i > len - 3; buf[--i] = '.')
- ; /* NOP */
-
- 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);
- }
- x += ew;
- w -= ew;
- }
- }
-
- if (!*text) {
- break;
- } else if (nextfont) {
- charexists = 0;
- usedfont = nextfont;
- } else {
- /* Regardless of whether or not a fallback font is found, the
- * character must be drawn. */
- charexists = 1;
-
- fccharset = FcCharSetCreate();
- FcCharSetAddChar(fccharset, utf8codepoint);
-
- if (!drw->fonts->pattern) {
- /* Refer to the comment in xfont_create for more information. */
- die("the first font in the cache must be loaded from a font string.");
- }
-
- fcpattern = FcPatternDuplicate(drw->fonts->pattern);
- FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
- FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
- FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
-
- FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
- FcDefaultSubstitute(fcpattern);
- match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
-
- FcCharSetDestroy(fccharset);
- FcPatternDestroy(fcpattern);
-
- if (match) {
- usedfont = xfont_create(drw, NULL, match);
- if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
- for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
- ; /* NOP */
- curfont->next = usedfont;
- } else {
- xfont_free(usedfont);
- usedfont = drw->fonts;
- }
+ len = strlen(text);
+
+ if (len) {
+ drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
+ /* shorten text if necessary */
+ for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--)
+ drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
+
+ if (len) {
+ memcpy(buf, text, len);
+ buf[len] = '\0';
+ if (len < strlen(text))
+ for (i = len; i && i > len - 3; buf[--i] = '.')
+ ; /* NOP */
+
+ if (render) {
+ ty = y + (h - drw->font->h) / 2;
+ if(markup)
+ pango_layout_set_markup(drw->font->layout, buf, len);
+ else
+ pango_layout_set_text(drw->font->layout, buf, len);
+ pango_xft_render_layout(d, &drw->scheme[invert ? ColBg : ColFg],
+ drw->font->layout, x * PANGO_SCALE, ty * PANGO_SCALE);
+ if(markup) /* clear markup attributes */
+ pango_layout_set_attributes(drw->font->layout, NULL);
  }
+ x += ew;
+ w -= ew;
  }
  }
+
  if (d)
  XftDrawDestroy(d);

@@ -389,24 +235,29 @@ drw_map(Drw *drw, Window win, int x, int y,
unsigned int w, unsigned int h)
 }

 unsigned int
-drw_fontset_getwidth(Drw *drw, const char *text)
+drw_font_getwidth(Drw *drw, const char *text, Bool markup)
 {
- if (!drw || !drw->fonts || !text)
+ if (!drw || !drw->font || !text)
  return 0;
- return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
+ return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup);
 }

 void
-drw_font_getexts(Fnt *font, const char *text, unsigned int len,
unsigned int *w, unsigned int *h)
+drw_font_getexts(Fnt *font, const char *text, unsigned int len,
unsigned int *w, unsigned int *h, Bool markup)
 {
- XGlyphInfo ext;
-
  if (!font || !text)
  return;

- XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
+ PangoRectangle r;
+ if(markup)
+ pango_layout_set_markup(font->layout, text, len);
+ else
+ pango_layout_set_text(font->layout, text, len);
+ pango_layout_get_extents(font->layout, 0, &r);
+ if(markup) /* clear markup attributes */
+ pango_layout_set_attributes(font->layout, NULL);
  if (w)
- *w = ext.xOff;
+ *w = r.width / PANGO_SCALE;
  if (h)
  *h = font->h;
 }
diff --git a/drw.h b/drw.h
index 4bcd5ad..3d3a906 100644
--- a/drw.h
+++ b/drw.h
@@ -7,9 +7,7 @@ typedef struct {
 typedef struct Fnt {
  Display *dpy;
  unsigned int h;
- XftFont *xfont;
- FcPattern *pattern;
- struct Fnt *next;
+ PangoLayout *layout;
 } Fnt;

 enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */
@@ -23,7 +21,7 @@ typedef struct {
  Drawable drawable;
  GC gc;
  Clr *scheme;
- Fnt *fonts;
+ Fnt *font;
 } Drw;

 /* Drawable abstraction */
@@ -32,10 +30,10 @@ void drw_resize(Drw *drw, unsigned int w, unsigned int h);
 void drw_free(Drw *drw);

 /* Fnt abstraction */
-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);
+Fnt *drw_font_create(Drw* drw, const char font[]);
+void drw_font_free(Fnt* set);
+unsigned int drw_font_getwidth(Drw *drw, const char *text, Bool markup);
+void drw_font_getexts(Fnt *font, const char *text, unsigned int len,
unsigned int *w, unsigned int *h, Bool markup);

 /* Colorscheme abstraction */
 void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
@@ -46,12 +44,11 @@ Cur *drw_cur_create(Drw *drw, int shape);
 void drw_cur_free(Drw *drw, Cur *cursor);

 /* Drawing context manipulation */
-void drw_setfontset(Drw *drw, Fnt *set);
 void drw_setscheme(Drw *drw, Clr *scm);

 /* Drawing functions */
 void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h,
int filled, int invert);
-int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h,
unsigned int lpad, const char *text, int invert);
+int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h,
unsigned int lpad, const char *text, int invert, Bool markup);

 /* Map functions */
 void drw_map(Drw *drw, Window win, int x, int y, unsigned int w,
unsigned int h);
diff --git a/dwm.c b/dwm.c
index 9fd0286..cc180c4 100644
--- a/dwm.c
+++ b/dwm.c
@@ -40,6 +40,7 @@
 #include <X11/extensions/Xinerama.h>
 #endif /* XINERAMA */
 #include <X11/Xft/Xft.h>
+#include <pango/pango.h>

 #include "drw.h"
 #include "util.h"
@@ -55,7 +56,8 @@
 #define WIDTH(X)                ((X)->w + 2 * (X)->bw)
 #define HEIGHT(X)               ((X)->h + 2 * (X)->bw)
 #define TAGMASK                 ((1 << LENGTH(tags)) - 1)
-#define TEXTW(X)                (drw_fontset_getwidth(drw, (X)) + lrpad)
+#define TEXTW(X)                (drw_font_getwidth(drw, (X), False) + lrpad)
+#define TEXTWM(X)                (drw_font_getwidth(drw, (X), True) + lrpad)

 /* enums */
 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
@@ -237,7 +239,7 @@ static void zoom(const Arg *arg);

 /* variables */
 static const char broken[] = "broken";
-static char stext[256];
+static char stext[512];
 static int screen;
 static int sw, sh;           /* X display screen geometry width, height */
 static int bh, blw = 0;      /* bar geometry */
@@ -440,7 +442,7 @@ buttonpress(XEvent *e)
  arg.ui = 1 << i;
  } else if (ev->x < x + blw)
  click = ClkLtSymbol;
- else if (ev->x > selmon->ww - TEXTW(stext))
+ else if (ev->x > selmon->ww - TEXTWM(stext))
  click = ClkStatusText;
  else
  click = ClkWinTitle;
@@ -697,16 +699,16 @@ void
 drawbar(Monitor *m)
 {
  int x, w, tw = 0;
- int boxs = drw->fonts->h / 9;
- int boxw = drw->fonts->h / 6 + 2;
+ int boxs = drw->font->h / 9;
+ int boxw = drw->font->h / 6 + 2;
  unsigned int i, occ = 0, urg = 0;
  Client *c;

  /* draw status first so it can be overdrawn by tags later */
  if (m == selmon) { /* status is only drawn on selected monitor */
  drw_setscheme(drw, scheme[SchemeNorm]);
- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
+ tw = TEXTWM(stext) - lrpad + 2; /* 2px right padding */
+ drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0, True);
  }

  for (c = m->clients; c; c = c->next) {
@@ -718,7 +720,7 @@ drawbar(Monitor *m)
  for (i = 0; i < LENGTH(tags); i++) {
  w = TEXTW(tags[i]);
  drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel
: SchemeNorm]);
- drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i, False);
  if (occ & 1 << i)
  drw_rect(drw, x + boxs, boxs, boxw, boxw,
  m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
@@ -727,12 +729,12 @@ drawbar(Monitor *m)
  }
  w = blw = TEXTW(m->ltsymbol);
  drw_setscheme(drw, scheme[SchemeNorm]);
- x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
+ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0, False);

  if ((w = m->ww - tw - x) > bh) {
  if (m->sel) {
  drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
- drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
+ drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0, False);
  if (m->sel->isfloating)
  drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
  } else {
@@ -1543,10 +1545,10 @@ setup(void)
  sh = DisplayHeight(dpy, screen);
  root = RootWindow(dpy, screen);
  drw = drw_create(dpy, screen, root, sw, sh);
- if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+ if (!drw_font_create(drw, font))
  die("no fonts could be loaded.");
- lrpad = drw->fonts->h;
- bh = drw->fonts->h + 2;
+ lrpad = drw->font->h;
+ bh = drw->font->h + 2;
  updategeom();
  /* init atoms */
  utf8string = XInternAtom(dpy, "UTF8_STRING", False);
diff --git a/util.h b/util.h
index f633b51..531ab25 100644
--- a/util.h
+++ b/util.h
@@ -1,7 +1,11 @@
 /* See LICENSE file for copyright and license details. */

+#ifndef MAX
 #define MAX(A, B)               ((A) > (B) ? (A) : (B))
+#endif
+#ifndef MIN
 #define MIN(A, B)               ((A) < (B) ? (A) : (B))
+#endif
 #define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))

 void die(const char *fmt, ...);
-- 
2.26.2
From c48056710a4ca6a6e52be60b0165313f9461f663 Mon Sep 17 00:00:00 2001
From: Marius Iacob <[email protected]>
Date: Tue, 28 Apr 2020 17:18:32 +0300
Subject: [PATCH] Using pango markup for status text

Use a single font. Removed some code utf8 code from drw.
Created for pango 1.44. Older versions might not have getter
for font height, ascent + descent can be used instead.
All texts are rendered with pango but only status is with
markup. Doubled stext size (in case a lot of markup is used).
MIN/MAX is already defined (didn't redefine them).
---
 config.def.h |   2 +-
 config.mk    |   4 +-
 drw.c        | 303 +++++++++++++--------------------------------------
 drw.h        |  17 ++-
 dwm.c        |  28 ++---
 util.h       |   4 +
 6 files changed, 106 insertions(+), 252 deletions(-)

diff --git a/config.def.h b/config.def.h
index 1c0b587..d201ae6 100644
--- a/config.def.h
+++ b/config.def.h
@@ -5,7 +5,7 @@ static const unsigned int borderpx  = 1;        /* border pixel of windows */
 static const unsigned int snap      = 32;       /* snap pixel */
 static const int showbar            = 1;        /* 0 means no bar */
 static const int topbar             = 1;        /* 0 means bottom bar */
-static const char *fonts[]          = { "monospace:size=10" };
+static const char font[]            = "monospace 10";
 static const char dmenufont[]       = "monospace:size=10";
 static const char col_gray1[]       = "#222222";
 static const char col_gray2[]       = "#444444";
diff --git a/config.mk b/config.mk
index 7084c33..b5c7e12 100644
--- a/config.mk
+++ b/config.mk
@@ -21,8 +21,8 @@ FREETYPEINC = /usr/include/freetype2
 #FREETYPEINC = ${X11INC}/freetype2
 
 # includes and libs
-INCS = -I${X11INC} -I${FREETYPEINC}
-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+INCS = -I${X11INC} -I${FREETYPEINC} `pkg-config --cflags xft pango pangoxft`
+LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} `pkg-config --libs xft pango pangoxft`
 
 # flags
 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
diff --git a/drw.c b/drw.c
index 8fd1ca4..6d1b64e 100644
--- a/drw.c
+++ b/drw.c
@@ -4,62 +4,12 @@
 #include <string.h>
 #include <X11/Xlib.h>
 #include <X11/Xft/Xft.h>
+#include <pango/pango.h>
+#include <pango/pangoxft.h>
 
 #include "drw.h"
 #include "util.h"
 
-#define UTF_INVALID 0xFFFD
-#define UTF_SIZ     4
-
-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};
-static const long utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
-static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
-
-static long
-utf8decodebyte(const char c, size_t *i)
-{
-	for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
-		if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
-			return (unsigned char)c & ~utfmask[*i];
-	return 0;
-}
-
-static size_t
-utf8validate(long *u, size_t i)
-{
-	if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
-		*u = UTF_INVALID;
-	for (i = 1; *u > utfmax[i]; ++i)
-		;
-	return i;
-}
-
-static size_t
-utf8decode(const char *c, long *u, size_t clen)
-{
-	size_t i, j, len, type;
-	long udecoded;
-
-	*u = UTF_INVALID;
-	if (!clen)
-		return 0;
-	udecoded = utf8decodebyte(c[0], &len);
-	if (!BETWEEN(len, 1, UTF_SIZ))
-		return 1;
-	for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
-		udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
-		if (type)
-			return j;
-	}
-	if (j < len)
-		return 0;
-	*u = udecoded;
-	utf8validate(u, len);
-
-	return len;
-}
-
 Drw *
 drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
 {
@@ -99,58 +49,37 @@ drw_free(Drw *drw)
 }
 
 /* This function is an implementation detail. Library users should use
- * drw_fontset_create instead.
+ * drw_font_create instead.
  */
 static Fnt *
-xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
+xfont_create(Drw *drw, const char *fontname)
 {
 	Fnt *font;
-	XftFont *xfont = NULL;
-	FcPattern *pattern = NULL;
-
-	if (fontname) {
-		/* Using the pattern found at font->xfont->pattern does not yield the
-		 * same substitution results as using the pattern returned by
-		 * FcNameParse; using the latter results in the desired fallback
-		 * behaviour whereas the former just results in missing-character
-		 * rectangles being drawn, at least with some fonts. */
-		if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
-			fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
-			return NULL;
-		}
-		if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
-			fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
-			XftFontClose(drw->dpy, xfont);
-			return NULL;
-		}
-	} else if (fontpattern) {
-		if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
-			fprintf(stderr, "error, cannot load font from pattern.\n");
-			return NULL;
-		}
-	} else {
-		die("no font specified.");
-	}
+	PangoFontMap *fontmap;
+	PangoContext *context;
+	PangoFontDescription *desc;
+	PangoFontMetrics *metrics;
 
-	/* Do not allow using color fonts. This is a workaround for a BadLength
-	 * error from Xft with color glyphs. Modelled on the Xterm workaround. See
-	 * https://bugzilla.redhat.com/show_bug.cgi?id=1498269
-	 * https://lists.suckless.org/dev/1701/30932.html
-	 * 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;
+
+	if (!fontname) {
+		die("no font specified.");
 	}
 
 	font = ecalloc(1, sizeof(Fnt));
-	font->xfont = xfont;
-	font->pattern = pattern;
-	font->h = xfont->ascent + xfont->descent;
 	font->dpy = drw->dpy;
 
+	fontmap = pango_xft_get_font_map(drw->dpy, drw->screen);
+	context = pango_font_map_create_context(fontmap);
+	desc = pango_font_description_from_string(fontname);
+	font->layout = pango_layout_new(context);
+	pango_layout_set_font_description(font->layout, desc);
+
+	metrics = pango_context_get_metrics(context, desc, pango_language_from_string ("en-us"));
+	font->h = pango_font_metrics_get_height(metrics) / PANGO_SCALE;
+
+	pango_font_metrics_unref(metrics);
+	g_object_unref(context);
+
 	return font;
 }
 
@@ -159,35 +88,28 @@ xfont_free(Fnt *font)
 {
 	if (!font)
 		return;
-	if (font->pattern)
-		FcPatternDestroy(font->pattern);
-	XftFontClose(font->dpy, font->xfont);
+	if (font->layout)
+		g_object_unref(font->layout);
 	free(font);
 }
 
 Fnt*
-drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
+drw_font_create(Drw* drw, const char font[])
 {
-	Fnt *cur, *ret = NULL;
-	size_t i;
+	Fnt *fnt = NULL;
 
-	if (!drw || !fonts)
+	if (!drw || !font)
 		return NULL;
 
-	for (i = 1; i <= fontcount; i++) {
-		if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
-			cur->next = ret;
-			ret = cur;
-		}
-	}
-	return (drw->fonts = ret);
+	fnt = xfont_create(drw, font);
+
+	return (drw->font = fnt);
 }
 
 void
-drw_fontset_free(Fnt *font)
+drw_font_free(Fnt *font)
 {
 	if (font) {
-		drw_fontset_free(font->next);
 		xfont_free(font);
 	}
 }
@@ -221,13 +143,6 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
 	return ret;
 }
 
-void
-drw_setfontset(Drw *drw, Fnt *set)
-{
-	if (drw)
-		drw->fonts = set;
-}
-
 void
 drw_setscheme(Drw *drw, Clr *scm)
 {
@@ -248,24 +163,16 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
 }
 
 int
-drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
+drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup)
 {
 	char buf[1024];
 	int ty;
 	unsigned int ew;
 	XftDraw *d = NULL;
-	Fnt *usedfont, *curfont, *nextfont;
 	size_t i, len;
-	int utf8strlen, utf8charlen, render = x || y || w || h;
-	long utf8codepoint = 0;
-	const char *utf8str;
-	FcCharSet *fccharset;
-	FcPattern *fcpattern;
-	FcPattern *match;
-	XftResult result;
-	int charexists = 0;
-
-	if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
+	int render = x || y || w || h;
+
+	if (!drw || (render && !drw->scheme) || !text || !drw->font)
 		return 0;
 
 	if (!render) {
@@ -280,98 +187,37 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
 		w -= lpad;
 	}
 
-	usedfont = drw->fonts;
-	while (1) {
-		utf8strlen = 0;
-		utf8str = text;
-		nextfont = NULL;
-		while (*text) {
-			utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
-			for (curfont = drw->fonts; curfont; curfont = curfont->next) {
-				charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
-				if (charexists) {
-					if (curfont == usedfont) {
-						utf8strlen += utf8charlen;
-						text += utf8charlen;
-					} else {
-						nextfont = curfont;
-					}
-					break;
-				}
-			}
-
-			if (!charexists || nextfont)
-				break;
-			else
-				charexists = 0;
-		}
-
-		if (utf8strlen) {
-			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);
-
-			if (len) {
-				memcpy(buf, utf8str, len);
-				buf[len] = '\0';
-				if (len < utf8strlen)
-					for (i = len; i && i > len - 3; buf[--i] = '.')
-						; /* NOP */
-
-				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);
-				}
-				x += ew;
-				w -= ew;
-			}
-		}
-
-		if (!*text) {
-			break;
-		} else if (nextfont) {
-			charexists = 0;
-			usedfont = nextfont;
-		} else {
-			/* Regardless of whether or not a fallback font is found, the
-			 * character must be drawn. */
-			charexists = 1;
-
-			fccharset = FcCharSetCreate();
-			FcCharSetAddChar(fccharset, utf8codepoint);
-
-			if (!drw->fonts->pattern) {
-				/* Refer to the comment in xfont_create for more information. */
-				die("the first font in the cache must be loaded from a font string.");
-			}
-
-			fcpattern = FcPatternDuplicate(drw->fonts->pattern);
-			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
-			FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
-			FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
-
-			FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
-			FcDefaultSubstitute(fcpattern);
-			match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
-
-			FcCharSetDestroy(fccharset);
-			FcPatternDestroy(fcpattern);
-
-			if (match) {
-				usedfont = xfont_create(drw, NULL, match);
-				if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
-					for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
-						; /* NOP */
-					curfont->next = usedfont;
-				} else {
-					xfont_free(usedfont);
-					usedfont = drw->fonts;
-				}
+	len = strlen(text);
+
+	if (len) {
+		drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
+		/* shorten text if necessary */
+		for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--)
+			drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
+
+		if (len) {
+			memcpy(buf, text, len);
+			buf[len] = '\0';
+			if (len < strlen(text))
+				for (i = len; i && i > len - 3; buf[--i] = '.')
+					; /* NOP */
+
+			if (render) {
+				ty = y + (h - drw->font->h) / 2;
+				if(markup)
+					pango_layout_set_markup(drw->font->layout, buf, len);
+				else
+					pango_layout_set_text(drw->font->layout, buf, len);
+				pango_xft_render_layout(d, &drw->scheme[invert ? ColBg : ColFg],
+					drw->font->layout, x * PANGO_SCALE, ty * PANGO_SCALE);
+				if(markup) /* clear markup attributes */
+					pango_layout_set_attributes(drw->font->layout, NULL);
 			}
+			x += ew;
+			w -= ew;
 		}
 	}
+
 	if (d)
 		XftDrawDestroy(d);
 
@@ -389,24 +235,29 @@ drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
 }
 
 unsigned int
-drw_fontset_getwidth(Drw *drw, const char *text)
+drw_font_getwidth(Drw *drw, const char *text, Bool markup)
 {
-	if (!drw || !drw->fonts || !text)
+	if (!drw || !drw->font || !text)
 		return 0;
-	return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
+	return drw_text(drw, 0, 0, 0, 0, 0, text, 0, markup);
 }
 
 void
-drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
+drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup)
 {
-	XGlyphInfo ext;
-
 	if (!font || !text)
 		return;
 
-	XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
+	PangoRectangle r;
+	if(markup)
+		pango_layout_set_markup(font->layout, text, len);
+	else
+		pango_layout_set_text(font->layout, text, len);
+	pango_layout_get_extents(font->layout, 0, &r);
+	if(markup) /* clear markup attributes */
+		pango_layout_set_attributes(font->layout, NULL);
 	if (w)
-		*w = ext.xOff;
+		*w = r.width / PANGO_SCALE;
 	if (h)
 		*h = font->h;
 }
diff --git a/drw.h b/drw.h
index 4bcd5ad..3d3a906 100644
--- a/drw.h
+++ b/drw.h
@@ -7,9 +7,7 @@ typedef struct {
 typedef struct Fnt {
 	Display *dpy;
 	unsigned int h;
-	XftFont *xfont;
-	FcPattern *pattern;
-	struct Fnt *next;
+	PangoLayout *layout;
 } Fnt;
 
 enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */
@@ -23,7 +21,7 @@ typedef struct {
 	Drawable drawable;
 	GC gc;
 	Clr *scheme;
-	Fnt *fonts;
+	Fnt *font;
 } Drw;
 
 /* Drawable abstraction */
@@ -32,10 +30,10 @@ void drw_resize(Drw *drw, unsigned int w, unsigned int h);
 void drw_free(Drw *drw);
 
 /* Fnt abstraction */
-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);
+Fnt *drw_font_create(Drw* drw, const char font[]);
+void drw_font_free(Fnt* set);
+unsigned int drw_font_getwidth(Drw *drw, const char *text, Bool markup);
+void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h, Bool markup);
 
 /* Colorscheme abstraction */
 void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
@@ -46,12 +44,11 @@ Cur *drw_cur_create(Drw *drw, int shape);
 void drw_cur_free(Drw *drw, Cur *cursor);
 
 /* Drawing context manipulation */
-void drw_setfontset(Drw *drw, Fnt *set);
 void drw_setscheme(Drw *drw, Clr *scm);
 
 /* Drawing functions */
 void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
-int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
+int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup);
 
 /* Map functions */
 void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
diff --git a/dwm.c b/dwm.c
index 9fd0286..cc180c4 100644
--- a/dwm.c
+++ b/dwm.c
@@ -40,6 +40,7 @@
 #include <X11/extensions/Xinerama.h>
 #endif /* XINERAMA */
 #include <X11/Xft/Xft.h>
+#include <pango/pango.h>
 
 #include "drw.h"
 #include "util.h"
@@ -55,7 +56,8 @@
 #define WIDTH(X)                ((X)->w + 2 * (X)->bw)
 #define HEIGHT(X)               ((X)->h + 2 * (X)->bw)
 #define TAGMASK                 ((1 << LENGTH(tags)) - 1)
-#define TEXTW(X)                (drw_fontset_getwidth(drw, (X)) + lrpad)
+#define TEXTW(X)                (drw_font_getwidth(drw, (X), False) + lrpad)
+#define TEXTWM(X)                (drw_font_getwidth(drw, (X), True) + lrpad)
 
 /* enums */
 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
@@ -237,7 +239,7 @@ static void zoom(const Arg *arg);
 
 /* variables */
 static const char broken[] = "broken";
-static char stext[256];
+static char stext[512];
 static int screen;
 static int sw, sh;           /* X display screen geometry width, height */
 static int bh, blw = 0;      /* bar geometry */
@@ -440,7 +442,7 @@ buttonpress(XEvent *e)
 			arg.ui = 1 << i;
 		} else if (ev->x < x + blw)
 			click = ClkLtSymbol;
-		else if (ev->x > selmon->ww - TEXTW(stext))
+		else if (ev->x > selmon->ww - TEXTWM(stext))
 			click = ClkStatusText;
 		else
 			click = ClkWinTitle;
@@ -697,16 +699,16 @@ void
 drawbar(Monitor *m)
 {
 	int x, w, tw = 0;
-	int boxs = drw->fonts->h / 9;
-	int boxw = drw->fonts->h / 6 + 2;
+	int boxs = drw->font->h / 9;
+	int boxw = drw->font->h / 6 + 2;
 	unsigned int i, occ = 0, urg = 0;
 	Client *c;
 
 	/* draw status first so it can be overdrawn by tags later */
 	if (m == selmon) { /* status is only drawn on selected monitor */
 		drw_setscheme(drw, scheme[SchemeNorm]);
-		tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
-		drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
+		tw = TEXTWM(stext) - lrpad + 2; /* 2px right padding */
+		drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0, True);
 	}
 
 	for (c = m->clients; c; c = c->next) {
@@ -718,7 +720,7 @@ drawbar(Monitor *m)
 	for (i = 0; i < LENGTH(tags); i++) {
 		w = TEXTW(tags[i]);
 		drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
-		drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
+		drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i, False);
 		if (occ & 1 << i)
 			drw_rect(drw, x + boxs, boxs, boxw, boxw,
 				m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
@@ -727,12 +729,12 @@ drawbar(Monitor *m)
 	}
 	w = blw = TEXTW(m->ltsymbol);
 	drw_setscheme(drw, scheme[SchemeNorm]);
-	x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
+	x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0, False);
 
 	if ((w = m->ww - tw - x) > bh) {
 		if (m->sel) {
 			drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
-			drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
+			drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0, False);
 			if (m->sel->isfloating)
 				drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
 		} else {
@@ -1543,10 +1545,10 @@ setup(void)
 	sh = DisplayHeight(dpy, screen);
 	root = RootWindow(dpy, screen);
 	drw = drw_create(dpy, screen, root, sw, sh);
-	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+	if (!drw_font_create(drw, font))
 		die("no fonts could be loaded.");
-	lrpad = drw->fonts->h;
-	bh = drw->fonts->h + 2;
+	lrpad = drw->font->h;
+	bh = drw->font->h + 2;
 	updategeom();
 	/* init atoms */
 	utf8string = XInternAtom(dpy, "UTF8_STRING", False);
diff --git a/util.h b/util.h
index f633b51..531ab25 100644
--- a/util.h
+++ b/util.h
@@ -1,7 +1,11 @@
 /* See LICENSE file for copyright and license details. */
 
+#ifndef MAX
 #define MAX(A, B)               ((A) > (B) ? (A) : (B))
+#endif
+#ifndef MIN
 #define MIN(A, B)               ((A) < (B) ? (A) : (B))
+#endif
 #define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))
 
 void die(const char *fmt, ...);
-- 
2.26.2

Reply via email to