t* dwm + patches
       
   URI git clone git://git.codevoid.de/dwm-sdk
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 5be974e0ca83cc0c67e9944550aa2832a3d14a7c
   DIR parent b00613e27fbfa2559bf6035e79efe16e65a49735
   URI Author: Stefan Hagen <sh+git[at]codevoid[dot]de>
       Date:   Thu, 18 Apr 2019 15:38:21 +0200
       
       Update to DWM 6.2
       
       Diffstat:
         M LICENSE                             |      17 +++++++++++------
         M Makefile                            |      47 +++++++++++++------------------
         M README                              |       3 ---
         M config.def.h                        |      58 ++++++++++++++-----------------
         M config.h                            |      23 +++++++++++++++++------
         M config.mk                           |      25 +++++++++++++++----------
         A drw.c                               |     436 +++++++++++++++++++++++++++++++
         A drw.h                               |      57 +++++++++++++++++++++++++++++++
         M dwm.1                               |      43 ++++++++++++-------------------
         M dwm.c                               |    1905 +++++++++++++++----------------
         A transient.c                         |      42 +++++++++++++++++++++++++++++++
         A util.c                              |      35 +++++++++++++++++++++++++++++++
         A util.h                              |       8 ++++++++
       
       13 files changed, 1618 insertions(+), 1081 deletions(-)
       ---
   DIR diff --git a/LICENSE b/LICENSE
       t@@ -1,17 +1,22 @@
        MIT/X Consortium License
        
       -© 2006-2011 Anselm R Garbe <anselm@garbe.us>
       -© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
       -© 2010-2011 Connor Lane Smith <cls@lubutu.com>
       +© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
        © 2006-2009 Jukka Salmi <jukka at salmi dot ch>
       -© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
       +© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
       +© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
        © 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
        © 2007-2009 Christof Musik <christof at sendfax dot de>
       -© 2009 Mate Nagy <mnagy at port70 dot net>
       +© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
        © 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
        © 2008 Martin Hurton <martin dot hurton at gmail dot com>
        © 2008 Neale Pickett <neale dot woozle dot org>
       -© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
       +© 2009 Mate Nagy <mnagy at port70 dot net>
       +© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
       +© 2010-2012 Connor Lane Smith <cls@lubutu.com>
       +© 2011 Christoph Lohmann <20h@r-36.net>
       +© 2015-2016 Quentin Rameau <quinq@fifth.space>
       +© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com>
       +© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de>
        
        Permission is hereby granted, free of charge, to any person obtaining a
        copy of this software and associated documentation files (the "Software"),
   DIR diff --git a/Makefile b/Makefile
       t@@ -3,7 +3,7 @@
        
        include config.mk
        
       -SRC = dwm.c
       +SRC = drw.c dwm.c util.c
        OBJ = ${SRC:.c=.o}
        
        all: options dwm
       t@@ -15,46 +15,37 @@ options:
                @echo "CC       = ${CC}"
        
        .c.o:
       -        @echo CC $<
       -        @${CC} -c ${CFLAGS} $<
       +        ${CC} -c ${CFLAGS} $<
        
        ${OBJ}: config.h config.mk
        
        config.h:
       -        @echo creating $@ from config.def.h
       -        @cp config.def.h $@
       +        cp config.def.h $@
        
        dwm: ${OBJ}
       -        @echo CC -o $@
       -        @${CC} -o $@ ${OBJ} ${LDFLAGS}
       +        ${CC} -o $@ ${OBJ} ${LDFLAGS}
        
        clean:
       -        @echo cleaning
       -        @rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
       +        rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
        
        dist: clean
       -        @echo creating dist tarball
       -        @mkdir -p dwm-${VERSION}
       -        @cp -R LICENSE Makefile README config.def.h config.mk \
       -                dwm.1 ${SRC} dwm-${VERSION}
       -        @tar -cf dwm-${VERSION}.tar dwm-${VERSION}
       -        @gzip dwm-${VERSION}.tar
       -        @rm -rf dwm-${VERSION}
       +        mkdir -p dwm-${VERSION}
       +        cp -R LICENSE Makefile README config.def.h config.mk\
       +                dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION}
       +        tar -cf dwm-${VERSION}.tar dwm-${VERSION}
       +        gzip dwm-${VERSION}.tar
       +        rm -rf dwm-${VERSION}
        
        install: all
       -        @echo installing executable file to ${DESTDIR}${PREFIX}/bin
       -        @mkdir -p ${DESTDIR}${PREFIX}/bin
       -        @cp -f dwm ${DESTDIR}${PREFIX}/bin
       -        @chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
       -        @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
       -        @mkdir -p ${DESTDIR}${MANPREFIX}/man1
       -        @sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
       -        @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
       +        mkdir -p ${DESTDIR}${PREFIX}/bin
       +        cp -f dwm ${DESTDIR}${PREFIX}/bin
       +        chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
       +        mkdir -p ${DESTDIR}${MANPREFIX}/man1
       +        sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
       +        chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
        
        uninstall:
       -        @echo removing executable file from ${DESTDIR}${PREFIX}/bin
       -        @rm -f ${DESTDIR}${PREFIX}/bin/dwm
       -        @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
       -        @rm -f ${DESTDIR}${MANPREFIX}/man1/dwm.1
       +        rm -f ${DESTDIR}${PREFIX}/bin/dwm\
       +                ${DESTDIR}${MANPREFIX}/man1/dwm.1
        
        .PHONY: all options clean dist install uninstall
   DIR diff --git a/README b/README
       t@@ -18,9 +18,6 @@ necessary as root):
        
            make clean install
        
       -If you are going to use the default bluegray color scheme it is highly
       -recommended to also install the bluegray files shipped in the dextra package.
       -
        
        Running dwm
        -----------
   DIR diff --git a/config.def.h b/config.def.h
       t@@ -1,37 +1,40 @@
        /* See LICENSE file for copyright and license details. */
        
        /* appearance */
       -static const char font[]            = "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*";
       -static const char normbordercolor[] = "#444444";
       -static const char normbgcolor[]     = "#222222";
       -static const char normfgcolor[]     = "#bbbbbb";
       -static const char selbordercolor[]  = "#005577";
       -static const char selbgcolor[]      = "#005577";
       -static const char selfgcolor[]      = "#eeeeee";
       -static const char floatnormbordercolor[] = "#005577";
       -static const char floatselbordercolor[]  = "#005577";
        static const unsigned int borderpx  = 1;        /* border pixel of windows */
       -static const unsigned int gappx     = 18;       /* gap pixel between windows */
        static const unsigned int snap      = 32;       /* snap pixel */
       -static const unsigned int systrayspacing = 2;   /* systray spacing */
       -static const Bool showsystray       = True;     /* False means no systray */
       -static const Bool showbar           = True;     /* False means no bar */
       -static const Bool topbar            = True;     /* False means bottom bar */
       -static const Bool extrabar          = True;     /* False means no extra bar */
       +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 dmenufont[]       = "monospace:size=10";
       +static const char col_gray1[]       = "#222222";
       +static const char col_gray2[]       = "#444444";
       +static const char col_gray3[]       = "#bbbbbb";
       +static const char col_gray4[]       = "#eeeeee";
       +static const char col_cyan[]        = "#005577";
       +static const char *colors[][3]      = {
       +        /*               fg         bg         border   */
       +        [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
       +        [SchemeSel]  = { col_gray4, col_cyan,  col_cyan  },
       +};
        
        /* tagging */
        static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
        
        static const Rule rules[] = {
       +        /* xprop(1):
       +         *        WM_CLASS(STRING) = instance, class
       +         *        WM_NAME(STRING) = title
       +         */
                /* class      instance    title       tags mask     isfloating   monitor */
       -        { "Gimp",     NULL,       NULL,       0,            True,        -1 },
       -        { "Firefox",  NULL,       NULL,       1 << 8,       False,       -1 },
       +        { "Gimp",     NULL,       NULL,       0,            1,           -1 },
       +        { "Firefox",  NULL,       NULL,       1 << 8,       0,           -1 },
        };
        
        /* layout(s) */
       -static const float mfact      = 0.55; /* factor of master area size [0.05..0.95] */
       -static const int nmaster      = 1;    /* number of clients in master area */
       -static const Bool resizehints = True; /* True means respect size hints in tiled resizals */
       +static const float mfact     = 0.55; /* factor of master area size [0.05..0.95] */
       +static const int nmaster     = 1;    /* number of clients in master area */
       +static const int resizehints = 1;    /* 1 means respect size hints in tiled resizals */
        
        static const Layout layouts[] = {
                /* symbol     arrange function */
       t@@ -52,18 +55,15 @@ static const Layout layouts[] = {
        #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
        
        /* commands */
       -static const char *dmenucmd[] = { "dmenu_run", "-fn", font, "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor, NULL };
       -static const char *termcmd[]  = { "uxterm", NULL };
       -static const char scratchpadname[] = "scratchpad";
       -static const char *scratchpadcmd[] = { "st", "-t", scratchpadname, "-g", "120x34", NULL };
       +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
       +static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
       +static const char *termcmd[]  = { "st", NULL };
        
        static Key keys[] = {
                /* modifier                     key        function        argument */
                { MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
                { MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },
       -    { MODKEY,                       XK_minus,  togglescratch,  {.v = scratchpadcmd } },
                { MODKEY,                       XK_b,      togglebar,      {0} },
       -        { MODKEY,                       XK_b,      toggleextrabar, {0} },
                { MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
                { MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
                { MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
       t@@ -84,10 +84,6 @@ static Key keys[] = {
                { MODKEY,                       XK_period, focusmon,       {.i = +1 } },
                { MODKEY|ShiftMask,             XK_comma,  tagmon,         {.i = -1 } },
                { MODKEY|ShiftMask,             XK_period, tagmon,         {.i = +1 } },
       -        { MODKEY,                       XK_Left,   viewtoleft,     {0} },
       -        { MODKEY,                       XK_Right,  viewtoright,    {0} },
       -        { MODKEY|ShiftMask,             XK_Left,   tagtoleft,      {0} },
       -        { MODKEY|ShiftMask,             XK_Right,  tagtoright,     {0} },
                TAGKEYS(                        XK_1,                      0)
                TAGKEYS(                        XK_2,                      1)
                TAGKEYS(                        XK_3,                      2)
       t@@ -101,7 +97,7 @@ static Key keys[] = {
        };
        
        /* button definitions */
       -/* click can be ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
       +/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
        static Button buttons[] = {
                /* click                event mask      button          function        argument */
                { ClkLtSymbol,          0,              Button1,        setlayout,      {0} },
   DIR diff --git a/config.h b/config.h
       t@@ -1,7 +1,7 @@
        /* See LICENSE file for copyright and license details. */
        
        /* appearance */
       -static const char font[]                 = "-xos4-terminus-medium-r-normal--16-160-72-72-c-80-iso8859-15";
       +static const char *fonts[]                 = { "-xos4-terminus-medium-r-normal--16-160-72-72-c-80-iso8859-15" };
        static const char normbgcolor[]          = "#181818"; // top bar bg
        static const char normfgcolor[]          = "#999999"; // top bar fg
        static const char selbgcolor[]           = "#181818"; // top bar selection bg
       t@@ -13,11 +13,20 @@ static const char floatselbordercolor[]  = "#FF0000"; // floating window border 
        static const unsigned int borderpx       = 1;         // border pixel of windows
        static const unsigned int gappx          = 8;         // gap pixel between windows
        static const unsigned int snap           = 8;         // snap pixel
       +static const unsigned int systraypinning = 0;         // 0: systray follows mouse, >0: pin systray to monitor X
        static const unsigned int systrayspacing = 2;         // systray spacing
       -static const Bool showsystray            = True;      // False means no systray
       -static const Bool showbar                = True;      // False means no bar
       -static const Bool topbar                 = True;      // False means bottom bar
       -static const Bool extrabar               = True;      // False means no extra bar
       +static const int systraypinningfailfirst = 1;         // 1 first monitor, 0 last monitor
       +static const int showsystray             = 1;         // 0 means no systray
       +static const int showbar                 = 1;         // False means no bar
       +static const int topbar                  = 1;         // False means bottom bar
       +static const int extrabar                = 1;         // False means no extra bar
       +
       +
       +static const char *colors[][3]      = {
       +        /*               fg         bg         border   */
       +        [SchemeNorm] = { normfgcolor, normbgcolor, normbordercolor },
       +        [SchemeSel]  = { selfgcolor, selbgcolor,  selbordercolor  },
       +};
        
        /* dmenu options */
        #define DMENUOPTS "-fn", "Terminus:size=12" , "-nb", normbgcolor, "-nf", normfgcolor, "-sb", selbgcolor, "-sf", selfgcolor
       t@@ -44,7 +53,7 @@ static const Rule rules[] = {
        /* layout(s) */
        static const float mfact      = 0.55;  /* factor of master area size [0.05..0.95] */
        static const int nmaster      = 1;     /* number of clients in master area */
       -static const Bool resizehints = False; /* True means respect size hints in tiled resizals */
       +static const int resizehints = 0;      /* True means respect size hints in tiled resizals */
        
        static const Layout layouts[] = {
                /* symbol     arrange function */
       t@@ -65,7 +74,9 @@ static const Layout layouts[] = {
        static const char scratchpadname[] = "scratchpad";
        
        /* commands */
       +static char dmenumon[2] = "0";
        static const char *dmenucmd[]    = { "dmenu_run", DMENUOPTS, NULL };
       +
        #ifdef __linux__
        static const char *scratchpadcmd[] = { "st","-t", scratchpadname, "-g", "120x34", "-e", "mksh", NULL };
        static const char *termcmd[]     = { "st", "-e", "mksh", NULL };
   DIR diff --git a/config.mk b/config.mk
       t@@ -1,5 +1,5 @@
        # dwm version
       -VERSION = 6.0
       +VERSION = 6.2
        
        # Customize below to fit your system
        
       t@@ -10,20 +10,25 @@ MANPREFIX = ${PREFIX}/share/man
        X11INC = /usr/X11R6/include
        X11LIB = /usr/X11R6/lib
        
       -# Xinerama
       -XINERAMALIBS = -L${X11LIB} -lXinerama
       +# Xinerama, comment if you don't want it
       +XINERAMALIBS  = -lXinerama
        XINERAMAFLAGS = -DXINERAMA
        
       +# freetype
       +FREETYPELIBS = -lfontconfig -lXft
       +FREETYPEINC = /usr/include/freetype2
       +# OpenBSD (uncomment)
       +#FREETYPEINC = ${X11INC}/freetype2
       +
        # includes and libs
       -INCS = -I. -I/usr/include -I${X11INC}
       -LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${XINERAMALIBS}
       +INCS = -I${X11INC} -I${FREETYPEINC}
       +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
        
        # flags
       -CPPFLAGS = -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
       -#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
       -CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
       -#LDFLAGS = -g ${LIBS}
       -LDFLAGS = -s ${LIBS}
       +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
       +#CFLAGS   = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
       +CFLAGS   = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
       +LDFLAGS  = ${LIBS}
        
        # Solaris
        #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
   DIR diff --git a/drw.c b/drw.c
       t@@ -0,0 +1,436 @@
       +/* See LICENSE file for copyright and license details. */
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xft/Xft.h>
       +
       +#include "drw.h"
       +#include "util.h"
       +
       +#define UTF_INVALID 0xFFFD
       +#define UTF_SIZ     4
       +#define FC_COLOR "color"
       +
       +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)
       +{
       +        Drw *drw = ecalloc(1, sizeof(Drw));
       +
       +        drw->dpy = dpy;
       +        drw->screen = screen;
       +        drw->root = root;
       +        drw->w = w;
       +        drw->h = h;
       +        drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
       +        drw->gc = XCreateGC(dpy, root, 0, NULL);
       +        XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
       +
       +        return drw;
       +}
       +
       +void
       +drw_resize(Drw *drw, unsigned int w, unsigned int h)
       +{
       +        if (!drw)
       +                return;
       +
       +        drw->w = w;
       +        drw->h = h;
       +        if (drw->drawable)
       +                XFreePixmap(drw->dpy, drw->drawable);
       +        drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
       +}
       +
       +void
       +drw_free(Drw *drw)
       +{
       +        XFreePixmap(drw->dpy, drw->drawable);
       +        XFreeGC(drw->dpy, drw->gc);
       +        free(drw);
       +}
       +
       +/* This function is an implementation detail. Library users should use
       + * drw_fontset_create instead.
       + */
       +static Fnt *
       +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
       +{
       +        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.");
       +        }
       +
       +        /* 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;
       +        }
       +
       +        font = ecalloc(1, sizeof(Fnt));
       +        font->xfont = xfont;
       +        font->pattern = pattern;
       +        font->h = xfont->ascent + xfont->descent;
       +        font->dpy = drw->dpy;
       +
       +        return font;
       +}
       +
       +static void
       +xfont_free(Fnt *font)
       +{
       +        if (!font)
       +                return;
       +        if (font->pattern)
       +                FcPatternDestroy(font->pattern);
       +        XftFontClose(font->dpy, font->xfont);
       +        free(font);
       +}
       +
       +Fnt*
       +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
       +{
       +        Fnt *cur, *ret = NULL;
       +        size_t i;
       +
       +        if (!drw || !fonts)
       +                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);
       +}
       +
       +void
       +drw_fontset_free(Fnt *font)
       +{
       +        if (font) {
       +                drw_fontset_free(font->next);
       +                xfont_free(font);
       +        }
       +}
       +
       +void
       +drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
       +{
       +        if (!drw || !dest || !clrname)
       +                return;
       +
       +        if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
       +                               DefaultColormap(drw->dpy, drw->screen),
       +                               clrname, dest))
       +                die("error, cannot allocate color '%s'", clrname);
       +}
       +
       +/* Wrapper to create color schemes. The caller has to call free(3) on the
       + * returned color scheme when done using it. */
       +Clr *
       +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
       +{
       +        size_t i;
       +        Clr *ret;
       +
       +        /* need at least two colors for a scheme */
       +        if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
       +                return NULL;
       +
       +        for (i = 0; i < clrcount; i++)
       +                drw_clr_create(drw, &ret[i], clrnames[i]);
       +        return ret;
       +}
       +
       +void
       +drw_setfontset(Drw *drw, Fnt *set)
       +{
       +        if (drw)
       +                drw->fonts = set;
       +}
       +
       +void
       +drw_setscheme(Drw *drw, Clr *scm)
       +{
       +        if (drw)
       +                drw->scheme = scm;
       +}
       +
       +void
       +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
       +{
       +        if (!drw || !drw->scheme)
       +                return;
       +        XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
       +        if (filled)
       +                XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
       +        else
       +                XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
       +}
       +
       +int
       +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
       +{
       +        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)
       +                return 0;
       +
       +        if (!render) {
       +                w = ~w;
       +        } else {
       +                XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
       +                XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
       +                d = XftDrawCreate(drw->dpy, drw->drawable,
       +                                  DefaultVisual(drw->dpy, drw->screen),
       +                                  DefaultColormap(drw->dpy, drw->screen));
       +                x += lpad;
       +                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;
       +                                }
       +                        }
       +                }
       +        }
       +        if (d)
       +                XftDrawDestroy(d);
       +
       +        return x + (render ? w : 0);
       +}
       +
       +void
       +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
       +{
       +        if (!drw)
       +                return;
       +
       +        XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
       +        XSync(drw->dpy, False);
       +}
       +
       +unsigned int
       +drw_fontset_getwidth(Drw *drw, const char *text)
       +{
       +        if (!drw || !drw->fonts || !text)
       +                return 0;
       +        return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
       +}
       +
       +void
       +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
       +{
       +        XGlyphInfo ext;
       +
       +        if (!font || !text)
       +                return;
       +
       +        XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
       +        if (w)
       +                *w = ext.xOff;
       +        if (h)
       +                *h = font->h;
       +}
       +
       +Cur *
       +drw_cur_create(Drw *drw, int shape)
       +{
       +        Cur *cur;
       +
       +        if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
       +                return NULL;
       +
       +        cur->cursor = XCreateFontCursor(drw->dpy, shape);
       +
       +        return cur;
       +}
       +
       +void
       +drw_cur_free(Drw *drw, Cur *cursor)
       +{
       +        if (!cursor)
       +                return;
       +
       +        XFreeCursor(drw->dpy, cursor->cursor);
       +        free(cursor);
       +}
   DIR diff --git a/drw.h b/drw.h
       t@@ -0,0 +1,57 @@
       +/* See LICENSE file for copyright and license details. */
       +
       +typedef struct {
       +        Cursor cursor;
       +} Cur;
       +
       +typedef struct Fnt {
       +        Display *dpy;
       +        unsigned int h;
       +        XftFont *xfont;
       +        FcPattern *pattern;
       +        struct Fnt *next;
       +} Fnt;
       +
       +enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */
       +typedef XftColor Clr;
       +
       +typedef struct {
       +        unsigned int w, h;
       +        Display *dpy;
       +        int screen;
       +        Window root;
       +        Drawable drawable;
       +        GC gc;
       +        Clr *scheme;
       +        Fnt *fonts;
       +} Drw;
       +
       +/* Drawable abstraction */
       +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
       +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_font