tUse libdraw: add Xft and fallback-fonts support to graphics lib - dmenu - Dmenu fork with xft fonts.
 (HTM) git clone git://r-36.net/dmenu
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 4b1fecd44e8376594c418663351fcb30c4e841de
 (DIR) parent 13a529ce63364544bdc851dfd5d3aa2ef8740914
 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
       Date:   Mon,  4 May 2015 21:54:46 +0200
       
       Use libdraw: add Xft and fallback-fonts support to graphics lib
       
       - libdraw, util: add drw.{c,h}, util.{c,h} and update code.
       - libdraw: fix drw_rect(): use w and h parameter.
       - libdraw: print errstr if last character in string was ":" (sbase).
       - libdraw: drw_clr_free() allow valid free(NULL).
       - config.def.h: set default font to monospace.
       - cleanup() on exit.
       - LICENSE: update license string for dmenu -v to 2015.
       - LICENSE: add myself to LICENSE
       
       Diffstat:
         LICENSE                             |       1 +
         Makefile                            |      15 ++++++++-------
         config.def.h                        |      10 ++++++----
         config.mk                           |       4 ++--
         dmenu.c                             |     241 +++++++++++++++++++------------
         draw.c                              |     177 -------------------------------
         draw.h                              |      35 -------------------------------
         drw.c                               |     413 +++++++++++++++++++++++++++++++
         drw.h                               |      74 +++++++++++++++++++++++++++++++
         util.c                              |      23 +++++++++++++++++++++++
         util.h                              |       7 +++++++
       
       11 files changed, 684 insertions(+), 316 deletions(-)
       ---
 (DIR) diff --git a/LICENSE b/LICENSE
       t@@ -7,6 +7,7 @@ MIT/X Consortium License
        © 2009 Evan Gates <evan.gates@gmail.com>
        © 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
        © 2006-2007 Michał Janeczek <janeczek at gmail dot com>
       +© 2014-2015 Hiltjo Posthuma <hiltjo@codemadness.org>
        
        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 = dmenu.c draw.c stest.c
       +SRC = drw.c dmenu.c stest.c util.c
        OBJ = ${SRC:.c=.o}
        
        all: options dmenu stest
       t@@ -15,18 +15,18 @@ options:
                @echo "CC       = ${CC}"
        
        .c.o:
       -        @echo CC -c $<
       -        @${CC} -c $< ${CFLAGS}
       +        @echo CC $<
       +        @${CC} -c ${CFLAGS} $<
        
        config.h:
                @echo creating $@ from config.def.h
                @cp config.def.h $@
        
       -${OBJ}: config.h config.mk draw.h
       +${OBJ}: config.h config.mk drw.h
        
       -dmenu: dmenu.o draw.o
       +dmenu: dmenu.o drw.o util.o
                @echo CC -o $@
       -        @${CC} -o $@ dmenu.o draw.o ${LDFLAGS}
       +        @${CC} -o $@ dmenu.o drw.o util.o ${LDFLAGS}
        
        stest: stest.o
                @echo CC -o $@
       t@@ -39,7 +39,8 @@ clean:
        dist: clean
                @echo creating dist tarball
                @mkdir -p dmenu-${VERSION}
       -        @cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_path dmenu_run stest.1 ${SRC} dmenu-${VERSION}
       +        @cp LICENSE Makefile README config.mk dmenu.1 drw.h util.h dmenu_path \
       +                dmenu_run stest.1 ${SRC} dmenu-${VERSION}
                @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
                @gzip dmenu-${VERSION}.tar
                @rm -rf dmenu-${VERSION}
 (DIR) diff --git a/config.def.h b/config.def.h
       t@@ -4,8 +4,11 @@
        /* Default settings; can be overrided by command line. */
        
        static Bool topbar = True;                  /* -b  option; if False, dmenu appears at bottom */
       -static const char *font = NULL;             /* -fn option; default X11 font or font set      */
       -static const char *prompt = NULL;           /* -p  option; prompt to the elft of input field */
       +/* -fn option overrides fonts[0]; default X11 font or font set */
       +static const char *fonts[] = {
       +        "monospace:size=10"
       +};
       +static const char *prompt      = NULL;      /* -p  option; prompt to the elft of input field */
        static const char *normbgcolor = "#222222"; /* -nb option; normal background                 */
        static const char *normfgcolor = "#bbbbbb"; /* -nf option; normal foreground                 */
        static const char *selbgcolor  = "#005577"; /* -sb option; selected background               */
       t@@ -13,5 +16,4 @@ static const char *selfgcolor  = "#eeeeee"; /* -sf option; selected foreground  
        static const char *outbgcolor  = "#00ffff";
        static const char *outfgcolor  = "#000000";
        /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
       -static unsigned int lines = 0;
       -
       +static unsigned int lines      = 0;
 (DIR) diff --git a/config.mk b/config.mk
       t@@ -13,8 +13,8 @@ XINERAMALIBS  = -lXinerama
        XINERAMAFLAGS = -DXINERAMA
        
        # includes and libs
       -INCS = -I${X11INC}
       -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS}
       +INCS = -I${X11INC} -I/usr/include/freetype2
       +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} -lfontconfig -lXft
        
        # flags
        CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
 (DIR) diff --git a/dmenu.c b/dmenu.c
       t@@ -1,5 +1,6 @@
        /* See LICENSE file for copyright and license details. */
        #include <ctype.h>
       +#include <locale.h>
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
       t@@ -11,12 +12,20 @@
        #ifdef XINERAMA
        #include <X11/extensions/Xinerama.h>
        #endif
       -#include "draw.h"
       +#include <X11/Xft/Xft.h>
        
       +#include "drw.h"
       +#include "util.h"
       +
       +/* macros */
        #define INTERSECT(x,y,w,h,r)  (MAX(0, MIN((x)+(w),(r).x_org+(r).width)  - MAX((x),(r).x_org)) \
                                     * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
       -#define MIN(a,b)              ((a) < (b) ? (a) : (b))
       -#define MAX(a,b)              ((a) > (b) ? (a) : (b))
       +#define LENGTH(X)             (sizeof X / sizeof X[0])
       +#define TEXTNW(X,N)           (drw_font_getexts_width(drw->fonts[0], (X), (N)))
       +#define TEXTW(X)              (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
       +
       +/* enums */
       +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
        
        typedef struct Item Item;
        struct Item {
       t@@ -28,6 +37,7 @@ struct Item {
        static void appenditem(Item *item, Item **list, Item **last);
        static void calcoffsets(void);
        static char *cistrstr(const char *s, const char *sub);
       +static void cleanup(void);
        static void drawmenu(void);
        static void grabkeyboard(void);
        static void insert(const char *str, ssize_t n);
       t@@ -44,11 +54,7 @@ static char text[BUFSIZ] = "";
        static int bh, mw, mh;
        static int inputw, promptw;
        static size_t cursor = 0;
       -static unsigned long normcol[ColLast];
       -static unsigned long selcol[ColLast];
       -static unsigned long outcol[ColLast];
        static Atom clip, utf8;
       -static DC *dc;
        static Item *items = NULL;
        static Item *matches, *matchend;
        static Item *prev, *curr, *next, *sel;
       t@@ -56,6 +62,13 @@ static Window win;
        static XIC xic;
        static int mon = -1;
        
       +static ClrScheme scheme[SchemeLast];
       +static Display *dpy;
       +static int screen;
       +static Window root;
       +static Drw *drw;
       +static int sw, sh; /* X display screen geometry width, height */
       +
        #include "config.h"
        
        static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
       t@@ -69,8 +82,8 @@ main(int argc, char *argv[]) {
                for(i = 1; i < argc; i++)
                        /* these options take no arguments */
                        if(!strcmp(argv[i], "-v")) {      /* prints version information */
       -                        puts("dmenu-"VERSION", © 2006-2014 dmenu engineers, see LICENSE for details");
       -                        exit(EXIT_SUCCESS);
       +                        puts("dmenu-"VERSION", © 2006-2015 dmenu engineers, see LICENSE for details");
       +                        exit(0);
                        }
                        else if(!strcmp(argv[i], "-b"))   /* appears at the bottom of the screen */
                                topbar = False;
       t@@ -90,7 +103,7 @@ main(int argc, char *argv[]) {
                        else if(!strcmp(argv[i], "-p"))   /* adds prompt to left of input field */
                                prompt = argv[++i];
                        else if(!strcmp(argv[i], "-fn"))  /* font or font set */
       -                        font = argv[++i];
       +                        fonts[0] = argv[++i];
                        else if(!strcmp(argv[i], "-nb"))  /* normal background color */
                                normbgcolor = argv[++i];
                        else if(!strcmp(argv[i], "-nf"))  /* normal foreground color */
       t@@ -102,8 +115,19 @@ main(int argc, char *argv[]) {
                        else
                                usage();
        
       -        dc = initdc();
       -        initfont(dc, font);
       +        if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
       +                fputs("warning: no locale support\n", stderr);
       +        if(!(dpy = XOpenDisplay(NULL)))
       +                die("dwm: cannot open display\n");
       +        screen = DefaultScreen(dpy);
       +        root = RootWindow(dpy, screen);
       +        sw = DisplayWidth(dpy, screen);
       +        sh = DisplayHeight(dpy, screen);
       +        drw = drw_create(dpy, screen, root, sw, sh);
       +        drw_load_fonts(drw, fonts, LENGTH(fonts));
       +        if(!drw->fontcount)
       +                die("No fonts could be loaded.\n");
       +        drw_setscheme(drw, &scheme[SchemeNorm]);
        
                if(fast) {
                        grabkeyboard();
       t@@ -138,16 +162,30 @@ calcoffsets(void) {
                if(lines > 0)
                        n = lines * bh;
                else
       -                n = mw - (promptw + inputw + textw(dc, "<") + textw(dc, ">"));
       +                n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
                /* calculate which items will begin the next page and previous page */
                for(i = 0, next = curr; next; next = next->right)
       -                if((i += (lines > 0) ? bh : MIN(textw(dc, next->text), n)) > n)
       +                if((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n)
                                break;
                for(i = 0, prev = curr; prev && prev->left; prev = prev->left)
       -                if((i += (lines > 0) ? bh : MIN(textw(dc, prev->left->text), n)) > n)
       +                if((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n)
                                break;
        }
        
       +void
       +cleanup(void) {
       +        XUngrabKey(dpy, AnyKey, AnyModifier, root);
       +        drw_clr_free(scheme[SchemeNorm].bg);
       +        drw_clr_free(scheme[SchemeNorm].fg);
       +        drw_clr_free(scheme[SchemeSel].fg);
       +        drw_clr_free(scheme[SchemeSel].bg);
       +        drw_clr_free(scheme[SchemeOut].fg);
       +        drw_clr_free(scheme[SchemeOut].bg);
       +        drw_free(drw);
       +        XSync(dpy, False);
       +        XCloseDisplay(dpy);
       +}
       +
        char *
        cistrstr(const char *s, const char *sub) {
                size_t len;
       t@@ -162,50 +200,69 @@ void
        drawmenu(void) {
                int curpos;
                Item *item;
       +        int x = 0, y = 0, h = bh, w;
        
       -        dc->x = 0;
       -        dc->y = 0;
       -        dc->h = bh;
       -        drawrect(dc, 0, 0, mw, mh, True, BG(dc, normcol));
       +        drw_setscheme(drw, &scheme[SchemeNorm]);
       +        drw_rect(drw, 0, 0, mw, mh, True, 1, 1);
        
                if(prompt && *prompt) {
       -                dc->w = promptw;
       -                drawtext(dc, prompt, selcol);
       -                dc->x = dc->w;
       +                drw_setscheme(drw, &scheme[SchemeSel]);
       +                drw_text(drw, x, 0, promptw, bh, prompt, 1);
       +                x += promptw;
                }
                /* draw input field */
       -        dc->w = (lines > 0 || !matches) ? mw - dc->x : inputw;
       -        drawtext(dc, text, normcol);
       -        if((curpos = textnw(dc, text, cursor) + dc->h/2 - 2) < dc->w)
       -                drawrect(dc, curpos, 2, 1, dc->h - 4, True, FG(dc, normcol));
       +        w = (lines > 0 || !matches) ? mw - x : inputw;
       +        drw_setscheme(drw, &scheme[SchemeNorm]);
       +        drw_text(drw, x, 0, w, bh, text, 0);
       +
       +        if((curpos = TEXTNW(text, cursor) + bh/2 - 2) < w) {
       +                drw_setscheme(drw, &scheme[SchemeNorm]);
       +                drw_rect(drw, x + curpos + 2, 2, 1, bh - 4, 1, 1, 0);
       +        }
        
                if(lines > 0) {
                        /* draw vertical list */
       -                dc->w = mw - dc->x;
       +                w = mw - x;
                        for(item = curr; item != next; item = item->right) {
       -                        dc->y += dc->h;
       -                        drawtext(dc, item->text, (item == sel) ? selcol :
       -                                                 (item->out)   ? outcol : normcol);
       +                        y += h;
       +                        if(item == sel)
       +                                drw_setscheme(drw, &scheme[SchemeSel]);
       +                        else if(item->out)
       +                                drw_setscheme(drw, &scheme[SchemeOut]);
       +                        else
       +                                drw_setscheme(drw, &scheme[SchemeNorm]);
       +
       +                        drw_text(drw, x, y, w, bh, item->text, 0);
                        }
                }
                else if(matches) {
                        /* draw horizontal list */
       -                dc->x += inputw;
       -                dc->w = textw(dc, "<");
       -                if(curr->left)
       -                        drawtext(dc, "<", normcol);
       +                x += inputw;
       +                w = TEXTW("<");
       +                if(curr->left) {
       +                        drw_setscheme(drw, &scheme[SchemeNorm]);
       +                        drw_text(drw, x, 0, w, bh, "<", 0);
       +                }
                        for(item = curr; item != next; item = item->right) {
       -                        dc->x += dc->w;
       -                        dc->w = MIN(textw(dc, item->text), mw - dc->x - textw(dc, ">"));
       -                        drawtext(dc, item->text, (item == sel) ? selcol :
       -                                                 (item->out)   ? outcol : normcol);
       +                        x += w;
       +                        w = MIN(TEXTW(item->text), mw - x - TEXTW(">"));
       +
       +                        if(item == sel)
       +                                drw_setscheme(drw, &scheme[SchemeSel]);
       +                        else if(item->out)
       +                                drw_setscheme(drw, &scheme[SchemeOut]);
       +                        else
       +                                drw_setscheme(drw, &scheme[SchemeNorm]);
       +                        drw_text(drw, x, 0, w, bh, item->text, 0);
       +                }
       +                w = TEXTW(">");
       +                x = mw - w;
       +                if(next) {
       +                        drw_setscheme(drw, &scheme[SchemeNorm]);
       +                        drw_text(drw, x, 0, w, bh, ">", 0);
                        }
       -                dc->w = textw(dc, ">");
       -                dc->x = mw - dc->w;
       -                if(next)
       -                        drawtext(dc, ">", normcol);
                }
       -        mapdc(dc, win, mw, mh);
       +        drw_map(drw, win, 0, 0, mw, mh);
        }
        
        void
       t@@ -214,12 +271,12 @@ grabkeyboard(void) {
        
                /* try to grab keyboard, we may have to wait for another process to ungrab */
                for(i = 0; i < 1000; i++) {
       -                if(XGrabKeyboard(dc->dpy, DefaultRootWindow(dc->dpy), True,
       +                if(XGrabKeyboard(dpy, DefaultRootWindow(dpy), True,
                                         GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
                                return;
                        usleep(1000);
                }
       -        eprintf("cannot grab keyboard\n");
       +        die("cannot grab keyboard\n");
        }
        
        void
       t@@ -276,14 +333,15 @@ keypress(XKeyEvent *ev) {
                                        insert(NULL, nextrune(-1) - cursor);
                                break;
                        case XK_y: /* paste selection */
       -                        XConvertSelection(dc->dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
       +                        XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
                                                  utf8, utf8, win, CurrentTime);
                                return;
                        case XK_Return:
                        case XK_KP_Enter:
                                break;
                        case XK_bracketleft:
       -                        exit(EXIT_FAILURE);
       +                        cleanup();
       +                        exit(1);
                        default:
                                return;
                        }
       t@@ -330,7 +388,8 @@ keypress(XKeyEvent *ev) {
                        sel = matchend;
                        break;
                case XK_Escape:
       -                exit(EXIT_FAILURE);
       +                cleanup();
       +                exit(1);
                case XK_Home:
                        if(sel == matches) {
                                cursor = 0;
       t@@ -368,8 +427,10 @@ keypress(XKeyEvent *ev) {
                case XK_Return:
                case XK_KP_Enter:
                        puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
       -                if(!(ev->state & ControlMask))
       -                        exit(EXIT_SUCCESS);
       +                if(!(ev->state & ControlMask)) {
       +                        cleanup();
       +                        exit(0);
       +                }
                        if(sel)
                                sel->out = True;
                        break;
       t@@ -413,7 +474,7 @@ match(void) {
                /* separate input text into tokens to be matched individually */
                for(s = strtok(buf, " "); s; tokv[tokc-1] = s, s = strtok(NULL, " "))
                        if(++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
       -                        eprintf("cannot realloc %u bytes\n", tokn * sizeof *tokv);
       +                        die("cannot realloc %u bytes\n", tokn * sizeof *tokv);
                len = tokc ? strlen(tokv[0]) : 0;
        
                matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
       t@@ -470,7 +531,7 @@ paste(void) {
                Atom da;
        
                /* we have been given the current selection, now insert it into input */
       -        XGetWindowProperty(dc->dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
       +        XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
                                   utf8, &da, &di, &dl, &dl, (unsigned char **)&p);
                insert(p, (q = strchr(p, '\n')) ? q-p : (ssize_t)strlen(p));
                XFree(p);
       t@@ -486,18 +547,18 @@ readstdin(void) {
                for(i = 0; fgets(buf, sizeof buf, stdin); i++) {
                        if(i+1 >= size / sizeof *items)
                                if(!(items = realloc(items, (size += BUFSIZ))))
       -                                eprintf("cannot realloc %u bytes:", size);
       +                                die("cannot realloc %u bytes:", size);
                        if((p = strchr(buf, '\n')))
                                *p = '\0';
                        if(!(items[i].text = strdup(buf)))
       -                        eprintf("cannot strdup %u bytes:", strlen(buf)+1);
       +                        die("cannot strdup %u bytes:", strlen(buf)+1);
                        items[i].out = False;
                        if(strlen(items[i].text) > max)
                                max = strlen(maxstr = items[i].text);
                }
                if(items)
                        items[i].text = NULL;
       -        inputw = maxstr ? textw(dc, maxstr) : 0;
       +        inputw = maxstr ? TEXTW(maxstr) : 0;
                lines = MIN(lines, i);
        }
        
       t@@ -505,13 +566,13 @@ void
        run(void) {
                XEvent ev;
        
       -        while(!XNextEvent(dc->dpy, &ev)) {
       +        while(!XNextEvent(dpy, &ev)) {
                        if(XFilterEvent(&ev, win))
                                continue;
                        switch(ev.type) {
                        case Expose:
                                if(ev.xexpose.count == 0)
       -                                mapdc(dc, win, mw, mh);
       +                                drw_map(drw, win, 0, 0, mw, mh);
                                break;
                        case KeyPress:
                                keypress(&ev.xkey);
       t@@ -522,7 +583,7 @@ run(void) {
                                break;
                        case VisibilityNotify:
                                if(ev.xvisibility.state != VisibilityUnobscured)
       -                                XRaiseWindow(dc->dpy, win);
       +                                XRaiseWindow(dpy, win);
                                break;
                        }
                }
       t@@ -530,47 +591,45 @@ run(void) {
        
        void
        setup(void) {
       -        int x, y, screen = DefaultScreen(dc->dpy);
       -        Window root = RootWindow(dc->dpy, screen);
       +        int x, y;
                XSetWindowAttributes swa;
                XIM xim;
        #ifdef XINERAMA
       -        int n;
                XineramaScreenInfo *info;
       +        Window w, pw, dw, *dws;
       +        XWindowAttributes wa;
       +        int a, j, di, n, i = 0, area = 0;
       +        unsigned int du;
        #endif
        
       -        normcol[ColBG] = getcolor(dc, normbgcolor);
       -        normcol[ColFG] = getcolor(dc, normfgcolor);
       -        selcol[ColBG]  = getcolor(dc, selbgcolor);
       -        selcol[ColFG]  = getcolor(dc, selfgcolor);
       -        outcol[ColBG]  = getcolor(dc, outbgcolor);
       -        outcol[ColFG]  = getcolor(dc, outfgcolor);
       +        /* init appearance */
       +        scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor);
       +        scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor);
       +        scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor);
       +        scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor);
       +        scheme[SchemeOut].bg = drw_clr_create(drw, outbgcolor);
       +        scheme[SchemeOut].fg = drw_clr_create(drw, outfgcolor);
        
       -        clip = XInternAtom(dc->dpy, "CLIPBOARD",   False);
       -        utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False);
       +        clip = XInternAtom(dpy, "CLIPBOARD",   False);
       +        utf8 = XInternAtom(dpy, "UTF8_STRING", False);
        
                /* calculate menu geometry */
       -        bh = dc->font.height + 2;
       +        bh = drw->fonts[0]->h + 2;
                lines = MAX(lines, 0);
                mh = (lines + 1) * bh;
        #ifdef XINERAMA
       -        if((info = XineramaQueryScreens(dc->dpy, &n))) {
       -                int a, j, di, i = 0, area = 0;
       -                unsigned int du;
       -                Window w, pw, dw, *dws;
       -                XWindowAttributes wa;
       -
       -                XGetInputFocus(dc->dpy, &w, &di);
       +        if((info = XineramaQueryScreens(dpy, &n))) {
       +                XGetInputFocus(dpy, &w, &di);
                        if(mon != -1 && mon < n)
                                i = mon;
                        if(!i && w != root && w != PointerRoot && w != None) {
                                /* find top-level window containing current input focus */
                                do {
       -                                if(XQueryTree(dc->dpy, (pw = w), &dw, &w, &dws, &du) && dws)
       +                                if(XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
                                                XFree(dws);
                                } while(w != root && w != pw);
                                /* find xinerama screen with which the window intersects most */
       -                        if(XGetWindowAttributes(dc->dpy, pw, &wa))
       +                        if(XGetWindowAttributes(dpy, pw, &wa))
                                        for(j = 0; j < n; j++)
                                                if((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
                                                        area = a;
       t@@ -578,7 +637,7 @@ setup(void) {
                                                }
                        }
                        /* no focused window is on screen, so use pointer location instead */
       -                if(mon == -1 && !area && XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
       +                if(mon == -1 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
                                for(i = 0; i < n; i++)
                                        if(INTERSECT(x, y, 1, 1, info[i]))
                                                break;
       t@@ -592,29 +651,29 @@ setup(void) {
        #endif
                {
                        x = 0;
       -                y = topbar ? 0 : DisplayHeight(dc->dpy, screen) - mh;
       -                mw = DisplayWidth(dc->dpy, screen);
       +                y = topbar ? 0 : sh - mh;
       +                mw = sw;
                }
       -        promptw = (prompt && *prompt) ? textw(dc, prompt) : 0;
       +        promptw = (prompt && *prompt) ? TEXTW(prompt) : 0;
                inputw = MIN(inputw, mw/3);
                match();
        
                /* create menu window */
                swa.override_redirect = True;
       -        swa.background_pixel = normcol[ColBG];
       +        swa.background_pixel = scheme[SchemeNorm].bg->pix;
                swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
       -        win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0,
       -                            DefaultDepth(dc->dpy, screen), CopyFromParent,
       -                            DefaultVisual(dc->dpy, screen),
       +        win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
       +                            DefaultDepth(dpy, screen), CopyFromParent,
       +                            DefaultVisual(dpy, screen),
                                    CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
        
                /* open input methods */
       -        xim = XOpenIM(dc->dpy, NULL, NULL, NULL);
       +        xim = XOpenIM(dpy, NULL, NULL, NULL);
                xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
                                XNClientWindow, win, XNFocusWindow, win, NULL);
        
       -        XMapRaised(dc->dpy, win);
       -        resizedc(dc, mw, mh);
       +        XMapRaised(dpy, win);
       +        drw_resize(drw, mw, mh);
                drawmenu();
        }
        
       t@@ -622,5 +681,5 @@ void
        usage(void) {
                fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
                      "             [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr);
       -        exit(EXIT_FAILURE);
       +        exit(1);
        }
 (DIR) diff --git a/draw.c b/draw.c
       t@@ -1,177 +0,0 @@
       -/* See LICENSE file for copyright and license details. */
       -#include <locale.h>
       -#include <stdarg.h>
       -#include <stdio.h>
       -#include <stdlib.h>
       -#include <string.h>
       -#include <X11/Xlib.h>
       -#include "draw.h"
       -
       -#define MAX(a, b)  ((a) > (b) ? (a) : (b))
       -#define MIN(a, b)  ((a) < (b) ? (a) : (b))
       -#define DEFAULTFN  "fixed"
       -
       -static Bool loadfont(DC *dc, const char *fontstr);
       -
       -void
       -drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) {
       -        XSetForeground(dc->dpy, dc->gc, color);
       -        if(fill)
       -                XFillRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w, h);
       -        else
       -                XDrawRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w-1, h-1);
       -}
       -
       -void
       -drawtext(DC *dc, const char *text, unsigned long col[ColLast]) {
       -        char buf[BUFSIZ];
       -        size_t mn, n = strlen(text);
       -
       -        /* shorten text if necessary */
       -        for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) + dc->font.height/2 > dc->w; mn--)
       -                if(mn == 0)
       -                        return;
       -        memcpy(buf, text, mn);
       -        if(mn < n)
       -                for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.');
       -
       -        drawrect(dc, 0, 0, dc->w, dc->h, True, BG(dc, col));
       -        drawtextn(dc, buf, mn, col);
       -}
       -
       -void
       -drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) {
       -        int x = dc->x + dc->font.height/2;
       -        int y = dc->y + dc->font.ascent+1;
       -
       -        XSetForeground(dc->dpy, dc->gc, FG(dc, col));
       -        if(dc->font.set)
       -                XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n);
       -        else {
       -                XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid);
       -                XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n);
       -        }
       -}
       -
       -void
       -eprintf(const char *fmt, ...) {
       -        va_list ap;
       -
       -        va_start(ap, fmt);
       -        vfprintf(stderr, fmt, ap);
       -        va_end(ap);
       -
       -        if(fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
       -                fputc(' ', stderr);
       -                perror(NULL);
       -        }
       -        exit(EXIT_FAILURE);
       -}
       -
       -void
       -freedc(DC *dc) {
       -        if(dc->font.set)
       -                XFreeFontSet(dc->dpy, dc->font.set);
       -        if(dc->font.xfont)
       -                XFreeFont(dc->dpy, dc->font.xfont);
       -        if(dc->canvas)
       -                XFreePixmap(dc->dpy, dc->canvas);
       -        XFreeGC(dc->dpy, dc->gc);
       -        XCloseDisplay(dc->dpy);
       -        free(dc);
       -}
       -
       -unsigned long
       -getcolor(DC *dc, const char *colstr) {
       -        Colormap cmap = DefaultColormap(dc->dpy, DefaultScreen(dc->dpy));
       -        XColor color;
       -
       -        if(!XAllocNamedColor(dc->dpy, cmap, colstr, &color, &color))
       -                eprintf("cannot allocate color '%s'\n", colstr);
       -        return color.pixel;
       -}
       -
       -DC *
       -initdc(void) {
       -        DC *dc;
       -
       -        if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
       -                fputs("no locale support\n", stderr);
       -        if(!(dc = calloc(1, sizeof *dc)))
       -                eprintf("cannot malloc %u bytes:", sizeof *dc);
       -        if(!(dc->dpy = XOpenDisplay(NULL)))
       -                eprintf("cannot open display\n");
       -
       -        dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL);
       -        XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter);
       -        return dc;
       -}
       -
       -void
       -initfont(DC *dc, const char *fontstr) {
       -        if(!loadfont(dc, fontstr ? fontstr : DEFAULTFN)) {
       -                if(fontstr != NULL)
       -                        fprintf(stderr, "cannot load font '%s'\n", fontstr);
       -                if(fontstr == NULL || !loadfont(dc, DEFAULTFN))
       -                        eprintf("cannot load font '%s'\n", DEFAULTFN);
       -        }
       -        dc->font.height = dc->font.ascent + dc->font.descent;
       -}
       -
       -Bool
       -loadfont(DC *dc, const char *fontstr) {
       -        char *def, **missing, **names;
       -        int i, n;
       -        XFontStruct **xfonts;
       -
       -        if(!*fontstr)
       -                return False;
       -        if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) {
       -                n = XFontsOfFontSet(dc->font.set, &xfonts, &names);
       -                for(i = 0; i < n; i++) {
       -                        dc->font.ascent  = MAX(dc->font.ascent,  xfonts[i]->ascent);
       -                        dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent);
       -                        dc->font.width   = MAX(dc->font.width,   xfonts[i]->max_bounds.width);
       -                }
       -        }
       -        else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) {
       -                dc->font.ascent  = dc->font.xfont->ascent;
       -                dc->font.descent = dc->font.xfont->descent;
       -                dc->font.width   = dc->font.xfont->max_bounds.width;
       -        }
       -        if(missing)
       -                XFreeStringList(missing);
       -        return dc->font.set || dc->font.xfont;
       -}
       -
       -void
       -mapdc(DC *dc, Window win, unsigned int w, unsigned int h) {
       -        XCopyArea(dc->dpy, dc->canvas, win, dc->gc, 0, 0, w, h, 0, 0);
       -}
       -
       -void
       -resizedc(DC *dc, unsigned int w, unsigned int h) {
       -        if(dc->canvas)
       -                XFreePixmap(dc->dpy, dc->canvas);
       -
       -        dc->w = w;
       -        dc->h = h;
       -        dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h,
       -                                   DefaultDepth(dc->dpy, DefaultScreen(dc->dpy)));
       -}
       -
       -int
       -textnw(DC *dc, const char *text, size_t len) {
       -        if(dc->font.set) {
       -                XRectangle r;
       -
       -                XmbTextExtents(dc->font.set, text, len, NULL, &r);
       -                return r.width;
       -        }
       -        return XTextWidth(dc->font.xfont, text, len);
       -}
       -
       -int
       -textw(DC *dc, const char *text) {
       -        return textnw(dc, text, strlen(text)) + dc->font.height;
       -}
 (DIR) diff --git a/draw.h b/draw.h
       t@@ -1,35 +0,0 @@
       -/* See LICENSE file for copyright and license details. */
       -
       -#define FG(dc, col)  ((col)[(dc)->invert ? ColBG : ColFG])
       -#define BG(dc, col)  ((col)[(dc)->invert ? ColFG : ColBG])
       -
       -enum { ColBG, ColFG, ColBorder, ColLast };
       -
       -typedef struct {
       -        int x, y, w, h;
       -        Bool invert;
       -        Display *dpy;
       -        GC gc;
       -        Pixmap canvas;
       -        struct {
       -                int ascent;
       -                int descent;
       -                int height;
       -                int width;
       -                XFontSet set;
       -                XFontStruct *xfont;
       -        } font;
       -} DC;  /* draw context */
       -
       -void drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color);
       -void drawtext(DC *dc, const char *text, unsigned long col[ColLast]);
       -void drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]);
       -void eprintf(const char *fmt, ...);
       -void freedc(DC *dc);
       -unsigned long getcolor(DC *dc, const char *colstr);
       -DC *initdc(void);
       -void initfont(DC *dc, const char *fontstr);
       -void mapdc(DC *dc, Window win, unsigned int w, unsigned int h);
       -void resizedc(DC *dc, unsigned int w, unsigned int h);
       -int textnw(DC *dc, const char *text, size_t len);
       -int textw(DC *dc, const char *text);
 (DIR) diff --git a/drw.c b/drw.c
       t@@ -0,0 +1,413 @@
       +/* 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
       +
       +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 != 0)
       +                        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 = (Drw *)calloc(1, sizeof(Drw));
       +        if(!drw)
       +                return NULL;
       +        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);
       +        drw->fontcount = 0;
       +        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 != 0)
       +                XFreePixmap(drw->dpy, drw->drawable);
       +        drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
       +}
       +
       +void
       +drw_free(Drw *drw) {
       +        size_t i;
       +
       +        for (i = 0; i < drw->fontcount; i++) {
       +                drw_font_free(drw->fonts[i]);
       +        }
       +        XFreePixmap(drw->dpy, drw->drawable);
       +        XFreeGC(drw->dpy, drw->gc);
       +        free(drw);
       +}
       +
       +/* This function is an implementation detail. Library users should use
       + * drw_font_create instead.
       + */
       +static Fnt *
       +drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) {
       +        Fnt *font;
       +
       +        if (!(fontname || fontpattern))
       +                die("No font specified.\n");
       +
       +        if (!(font = (Fnt *)calloc(1, sizeof(Fnt))))
       +                return NULL;
       +
       +        if (fontname) {
       +                /* Using the pattern found at font->xfont->pattern does not yield same
       +                 * 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 (!(font->xfont = XftFontOpenName(drw->dpy, drw->screen, fontname)) ||
       +                    !(font->pattern = FcNameParse((FcChar8 *) fontname))) {
       +                        if (font->xfont) {
       +                                XftFontClose(drw->dpy, font->xfont);
       +                                font->xfont = NULL;
       +                        }
       +                        fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
       +                }
       +        } else if (fontpattern) {
       +                if (!(font->xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
       +                        fprintf(stderr, "error, cannot load font pattern.\n");
       +                } else {
       +                        font->pattern = NULL;
       +                }
       +        }
       +
       +        if (!font->xfont) {
       +                free(font);
       +                return NULL;
       +        }
       +
       +        font->ascent = font->xfont->ascent;
       +        font->descent = font->xfont->descent;
       +        font->h = font->ascent + font->descent;
       +        font->dpy = drw->dpy;
       +        return font;
       +}
       +
       +Fnt*
       +drw_font_create(Drw *drw, const char *fontname) {
       +        return drw_font_xcreate(drw, fontname, NULL);
       +}
       +
       +void
       +drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount) {
       +        size_t i;
       +        Fnt *font;
       +
       +        for (i = 0; i < fontcount; i++) {
       +                if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
       +                        die("Font cache exhausted.\n");
       +                } else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) {
       +                        drw->fonts[drw->fontcount++] = font;
       +                }
       +        }
       +}
       +
       +void
       +drw_font_free(Fnt *font) {
       +        if(!font)
       +                return;
       +        if(font->pattern)
       +                FcPatternDestroy(font->pattern);
       +        XftFontClose(font->dpy, font->xfont);
       +        free(font);
       +}
       +
       +Clr *
       +drw_clr_create(Drw *drw, const char *clrname) {
       +        Clr *clr;
       +        Colormap cmap;
       +        Visual *vis;
       +
       +        if(!drw)
       +                return NULL;
       +        clr = (Clr *)calloc(1, sizeof(Clr));
       +        if(!clr)
       +                return NULL;
       +        cmap = DefaultColormap(drw->dpy, drw->screen);
       +        vis = DefaultVisual(drw->dpy, drw->screen);
       +        if(!XftColorAllocName(drw->dpy, vis, cmap, clrname, &clr->rgb))
       +                die("error, cannot allocate color '%s'\n", clrname);
       +        clr->pix = clr->rgb.pixel;
       +        return clr;
       +}
       +
       +void
       +drw_clr_free(Clr *clr) {
       +        free(clr);
       +}
       +
       +void
       +drw_setscheme(Drw *drw, ClrScheme *scheme) {
       +        if(!drw)
       +                return;
       +        drw->scheme = scheme;
       +}
       +
       +void
       +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert) {
       +        if(!drw || !drw->scheme)
       +                return;
       +        XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix);
       +        if(filled)
       +                XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1);
       +        else if(empty)
       +                XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
       +}
       +
       +int
       +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) {
       +        char buf[1024];
       +        int tx, ty, th;
       +        Extnts tex;
       +        Colormap cmap;
       +        Visual *vis;
       +        XftDraw *d;
       +        Fnt *curfont, *nextfont;
       +        size_t i, len;
       +        int utf8strlen, utf8charlen, render;
       +        long utf8codepoint = 0;
       +        const char *utf8str;
       +        FcCharSet *fccharset;
       +        FcPattern *fcpattern;
       +        FcPattern *match;
       +        XftResult result;
       +        int charexists = 0;
       +
       +        if (!(render = x || y || w || h)) {
       +                w = ~w;
       +        }
       +
       +        if (!drw || !drw->scheme) {
       +                return 0;
       +        } else if (render) {
       +                XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->fg->pix : drw->scheme->bg->pix);
       +                XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
       +        }
       +
       +        if (!text || !drw->fontcount) {
       +                return 0;
       +        } else if (render) {
       +                cmap = DefaultColormap(drw->dpy, drw->screen);
       +                vis = DefaultVisual(drw->dpy, drw->screen);
       +                d = XftDrawCreate(drw->dpy, drw->drawable, vis, cmap);
       +        }
       +
       +        curfont = drw->fonts[0];
       +        while (1) {
       +                utf8strlen = 0;
       +                utf8str = text;
       +                nextfont = NULL;
       +                while (*text) {
       +                        utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
       +                        for (i = 0; i < drw->fontcount; i++) {
       +                                charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint);
       +                                if (charexists) {
       +                                        if (drw->fonts[i] == curfont) {
       +                                                utf8strlen += utf8charlen;
       +                                                text += utf8charlen;
       +                                        } else {
       +                                                nextfont = drw->fonts[i];
       +                                        }
       +                                        break;
       +                                }
       +                        }
       +
       +                        if (!charexists || (nextfont && nextfont != curfont)) {
       +                                break;
       +                        } else {
       +                                charexists = 0;
       +                        }
       +                }
       +
       +                if (utf8strlen) {
       +                        drw_font_getexts(curfont, utf8str, utf8strlen, &tex);
       +                        /* shorten text if necessary */
       +                        for(len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--)
       +                                drw_font_getexts(curfont, utf8str, len, &tex);
       +
       +                        if (len) {
       +                                memcpy(buf, utf8str, len);
       +                                buf[len] = '\0';
       +                                if(len < utf8strlen)
       +                                        for(i = len; i && i > len - 3; buf[--i] = '.');
       +
       +                                if (render) {
       +                                        th = curfont->ascent + curfont->descent;
       +                                        ty = y + (h / 2) - (th / 2) + curfont->ascent;
       +                                        tx = x + (h / 2);
       +                                        XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len);
       +                                }
       +
       +                                x += tex.w;
       +                                w -= tex.w;
       +                        }
       +                }
       +
       +                if (!*text) {
       +                        break;
       +                } else if (nextfont) {
       +                        charexists = 0;
       +                        curfont = nextfont;
       +                } else {
       +                        /* Regardless of whether or not a fallback font is found, the
       +                         * character must be drawn.
       +                         */
       +                        charexists = 1;
       +
       +                        if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
       +                                continue;
       +                        }
       +
       +                        fccharset = FcCharSetCreate();
       +                        FcCharSetAddChar(fccharset, utf8codepoint);
       +
       +                        if (!drw->fonts[0]->pattern) {
       +                                /* Refer to the comment in drw_font_xcreate for more
       +                                 * information.
       +                                 */
       +                                die("The first font in the cache must be loaded from a font string.\n");
       +                        }
       +
       +                        fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern);
       +                        FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
       +                        FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
       +
       +                        FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
       +                        FcDefaultSubstitute(fcpattern);
       +                        match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
       +
       +                        FcCharSetDestroy(fccharset);
       +                        FcPatternDestroy(fcpattern);
       +
       +                        if (match) {
       +                                curfont = drw_font_xcreate(drw, NULL, match);
       +                                if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) {
       +                                        drw->fonts[drw->fontcount++] = curfont;
       +                                } else {
       +                                        if (curfont) {
       +                                                drw_font_free(curfont);
       +                                        }
       +                                        curfont = drw->fonts[0];
       +                                }
       +                        }
       +                }
       +        }
       +
       +        if (render) {
       +                XftDrawDestroy(d);
       +        }
       +
       +        return x;
       +}
       +
       +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);
       +}
       +
       +
       +void
       +drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex) {
       +        XGlyphInfo ext;
       +
       +        if(!font || !text)
       +                return;
       +        XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
       +        tex->h = font->h;
       +        tex->w = ext.xOff;
       +}
       +
       +unsigned int
       +drw_font_getexts_width(Fnt *font, const char *text, unsigned int len) {
       +        Extnts tex;
       +
       +        if(!font)
       +                return -1;
       +        drw_font_getexts(font, text, len, &tex);
       +        return tex.w;
       +}
       +
       +Cur *
       +drw_cur_create(Drw *drw, int shape) {
       +        Cur *cur;
       +
       +        if(!drw)
       +                return NULL;
       +        cur = (Cur *)calloc(1, sizeof(Cur));
       +        if (!cur)
       +                return NULL;
       +        cur->cursor = XCreateFontCursor(drw->dpy, shape);
       +        return cur;
       +}
       +
       +void
       +drw_cur_free(Drw *drw, Cur *cursor) {
       +        if(!drw || !cursor)
       +                return;
       +        XFreeCursor(drw->dpy, cursor->cursor);
       +        free(cursor);
       +}
 (DIR) diff --git a/drw.h b/drw.h
       t@@ -0,0 +1,74 @@
       +/* See LICENSE file for copyright and license details. */
       +#define DRW_FONT_CACHE_SIZE 32
       +
       +typedef struct {
       +        unsigned long pix;
       +        XftColor rgb;
       +} Clr;
       +
       +typedef struct {
       +        Cursor cursor;
       +} Cur;
       +
       +typedef struct {
       +        Display *dpy;
       +        int ascent;
       +        int descent;
       +        unsigned int h;
       +        XftFont *xfont;
       +        FcPattern *pattern;
       +} Fnt;
       +
       +typedef struct {
       +        Clr *fg;
       +        Clr *bg;
       +        Clr *border;
       +} ClrScheme;
       +
       +typedef struct {
       +        unsigned int w, h;
       +        Display *dpy;
       +        int screen;
       +        Window root;
       +        Drawable drawable;
       +        GC gc;
       +        ClrScheme *scheme;
       +        size_t fontcount;
       +        Fnt *fonts[DRW_FONT_CACHE_SIZE];
       +} Drw;
       +
       +typedef struct {
       +        unsigned int w;
       +        unsigned int h;
       +} Extnts;
       +
       +/* 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_font_create(Drw *drw, const char *fontname);
       +void drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount);
       +void drw_font_free(Fnt *font);
       +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *extnts);
       +unsigned int drw_font_getexts_width(Fnt *font, const char *text, unsigned int len);
       +
       +/* Colour abstraction */
       +Clr *drw_clr_create(Drw *drw, const char *clrname);
       +void drw_clr_free(Clr *clr);
       +
       +/* Cursor abstraction */
       +Cur *drw_cur_create(Drw *drw, int shape);
       +void drw_cur_free(Drw *drw, Cur *cursor);
       +
       +/* Drawing context manipulation */
       +void drw_setfont(Drw *drw, Fnt *font);
       +void drw_setscheme(Drw *drw, ClrScheme *scheme);
       +
       +/* Drawing functions */
       +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert);
       +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert);
       +
       +/* Map functions */
       +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
 (DIR) diff --git a/util.c b/util.c
       t@@ -0,0 +1,23 @@
       +/* See LICENSE file for copyright and license details. */
       +#include <stdarg.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +
       +#include "util.h"
       +
       +void
       +die(const char *fmt, ...) {
       +        va_list ap;
       +
       +        va_start(ap, fmt);
       +        vfprintf(stderr, fmt, ap);
       +        va_end(ap);
       +
       +        if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
       +                fputc(' ', stderr);
       +                perror(NULL);
       +        }
       +
       +        exit(1);
       +}
 (DIR) diff --git a/util.h b/util.h
       t@@ -0,0 +1,7 @@
       +/* See LICENSE file for copyright and license details. */
       +
       +#define MAX(A, B)               ((A) > (B) ? (A) : (B))
       +#define MIN(A, B)               ((A) < (B) ? (A) : (B))
       +#define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))
       +
       +void die(const char *errstr, ...);