This patch adds support for xft font rendering to dmenu (4.1.1). Shouts go out to lattenwald (utf8 support) and R. Kyle Murphy (fg color fix)
The patch can also be found at: http://aur.archlinux.org/packages.php?ID=28745 -- Henri
diff -up ../dmenu-4.1.1/config.def.h ../dmenu-4.1.1-xft/config.def.h --- ../dmenu-4.1.1/config.def.h 2010-05-29 14:56:51.000000000 +0300 +++ ../dmenu-4.1.1-xft/config.def.h 2010-09-01 12:05:27.510000064 +0300 @@ -7,3 +7,4 @@ static const char *normfgcolor = "#00000 static const char *selbgcolor = "#0066ff"; static const char *selfgcolor = "#ffffff"; static unsigned int spaceitem = 30; /* px between menu items */ +static const char *fontxft = "Monospace-10:normal"; /*if set xft is used */ diff -up ../dmenu-4.1.1/config.mk ../dmenu-4.1.1-xft/config.mk --- ../dmenu-4.1.1/config.mk 2010-05-29 14:56:51.000000000 +0300 +++ ../dmenu-4.1.1-xft/config.mk 2010-09-01 12:05:27.513000063 +0300 @@ -15,8 +15,8 @@ XINERAMALIBS = -L${X11LIB} -lXinerama XINERAMAFLAGS = -DXINERAMA # includes and libs -INCS = -I. -I/usr/include -I${X11INC} -LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${XINERAMALIBS} +INCS = -I. -I/usr/include -I${X11INC} -I/usr/include -I/usr/include/freetype2 +LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${XINERAMALIBS} -lXft -lX11 -lXrender -lfreetype -lz -lfontconfig -lXrender -lX11 # flags CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} diff -up ../dmenu-4.1.1/dmenu.1 ../dmenu-4.1.1-xft/dmenu.1 --- ../dmenu-4.1.1/dmenu.1 2010-05-29 14:56:51.000000000 +0300 +++ ../dmenu-4.1.1-xft/dmenu.1 2010-09-01 12:05:27.515000063 +0300 @@ -7,6 +7,7 @@ dmenu \- dynamic menu .RB [ \-b ] .RB [ \-l " <lines>"] .RB [ \-fn " <font>"] +.RB [ \-fa " <xftfont>"] .RB [ \-nb " <color>"] .RB [ \-nf " <color>"] .RB [ \-p " <prompt>"] @@ -34,6 +35,9 @@ The given number of lines will be displa .B \-fn <font> defines the font. .TP +.B \-fa <font> +defines the xft font. +.TP .B \-nb <color> defines the normal background color (#RGB, #RRGGBB, and color names are supported). .TP diff -up ../dmenu-4.1.1/dmenu.c ../dmenu-4.1.1-xft/dmenu.c --- ../dmenu-4.1.1/dmenu.c 2010-05-29 14:56:51.000000000 +0300 +++ ../dmenu-4.1.1-xft/dmenu.c 2010-09-01 12:05:27.520000063 +0300 @@ -13,6 +13,7 @@ #ifdef XINERAMA #include <X11/extensions/Xinerama.h> #endif +#include <X11/Xft/Xft.h> /* macros */ #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) @@ -29,6 +30,7 @@ typedef struct { int x, y, w, h; unsigned long norm[ColLast]; unsigned long sel[ColLast]; + Bool selected; Drawable drawable; GC gc; struct { @@ -38,6 +40,16 @@ typedef struct { int descent; int height; } font; + XftDraw *xftdraw; + XftColor xftselcolor; + XftColor xftcolor; + XGlyphInfo gi; + struct { + XftFont *xft_font; + int ascent; + int descent; + int height; + } xftfont; } DC; /* draw context */ typedef struct Item Item; @@ -135,18 +147,23 @@ calcoffsetsh(void) { void calcoffsetsv(void) { static unsigned int h; + int h2; if(!curr) return; - h = (dc.font.height + 2) * (lines + 1); + if(fontxft) + h2 = dc.xftfont.height; + else + h2 = dc.font.height; + h = (h2 + 2) * (lines + 1); for(next = curr; next; next=next->right) { - h -= dc.font.height + 2; + h -= h2 + 2; if(h <= 0) break; } - h = (dc.font.height + 2) * (lines + 1); + h = (h2 + 2) * (lines + 1); for(prev = curr; prev && prev->left; prev=prev->left) { - h -= dc.font.height + 2; + h -= h2 + 2; if(h <= 0) break; } @@ -185,9 +202,14 @@ cleanup(void) { free(allitems); allitems = itm; } + if(fontxft) { + XftColorFree (dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), &dc.xftcolor); + XftFontClose (dpy, dc.xftfont.xft_font); + XftDrawDestroy(dc.xftdraw); + } if(dc.font.set) XFreeFontSet(dpy, dc.font.set); - else + else if(!fontxft) XFreeFont(dpy, dc.font.xfont); XFreePixmap(dpy, dc.drawable); XFreeGC(dpy, dc.gc); @@ -198,8 +220,15 @@ cleanup(void) { void drawcursor(void) { XRectangle r = { dc.x, dc.y + 2, 1, dc.font.height - 2 }; + int h2; - r.x += textnw(text, cursor) + dc.font.height / 2; + if(fontxft) + h2 = dc.xftfont.height; + else + h2 = dc.font.height; + + r.height = h2 - 2; + r.x += textnw(text, cursor) + h2 / 2; XSetForeground(dpy, dc.gc, dc.norm[ColFG]); XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); @@ -213,9 +242,11 @@ drawmenu(void) { dc.h = mh; drawtext(NULL, dc.norm); /* print prompt? */ - if(prompt) { + if(promptw) { dc.w = promptw; + dc.selected = True; drawtext(prompt, dc.sel); + dc.selected = False; dc.x += dc.w; } dc.w = mw - dc.x; @@ -244,7 +275,13 @@ drawmenuh(void) { dc.x += dc.w; for(i = curr; i != next; i=i->right) { dc.w = MIN(textw(i->text), mw / 3); - drawtext(i->text, (sel == i) ? dc.sel : dc.norm); + if(sel == i) { + dc.selected = True; + drawtext(i->text, dc.sel); + dc.selected = False; + } else { + drawtext(i->text, dc.norm); + } dc.x += dc.w; } dc.w = spaceitem; @@ -255,12 +292,18 @@ drawmenuh(void) { void drawmenuv(void) { Item *i; + int h2; + + if(fontxft) + h2 = dc.xftfont.height; + else + h2 = dc.font.height; dc.w = mw - dc.x; - dc.y += dc.font.height + 2; + dc.y += h2 + 2; for(i = curr; i != next; i=i->right) { drawtext(i->text, (sel == i) ? dc.sel : dc.norm); - dc.y += dc.font.height + 2; + dc.y += h2 + 2; } drawtext(NULL, dc.norm); } @@ -268,7 +311,7 @@ drawmenuv(void) { void drawtext(const char *text, unsigned long col[ColLast]) { char buf[256]; - int i, x, y, h, len, olen; + int i, x, y, h, a, len, olen; XRectangle r = { dc.x, dc.y, dc.w, dc.h }; XSetForeground(dpy, dc.gc, col[ColBG]); @@ -276,8 +319,20 @@ drawtext(const char *text, unsigned long if(!text) return; olen = strlen(text); - h = dc.font.height; - y = dc.y + ((h+2) / 2) - (h / 2) + dc.font.ascent; + if(!fontxft) { + h = dc.font.height; + a = dc.font.ascent; + } else { + h = dc.xftfont.height; + a = dc.xftfont.ascent; + } + y = dc.y + ((h+2) / 2) - (h / 2) + a; +#if 0 + if(dc.xftfont.xft_font) { + h = dc.xftfont.ascent + dc.xftfont.descent; + y = dc.y + (dc.h / 2) - (h / 2) + dc.xftfont.ascent; + } +#endif x = dc.x + (h / 2); /* shorten text if necessary */ for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--); @@ -287,7 +342,15 @@ drawtext(const char *text, unsigned long if(len < olen) for(i = len; i && i > len - 3; buf[--i] = '.'); XSetForeground(dpy, dc.gc, col[ColFG]); - if(dc.font.set) + if(fontxft) { + if (!dc.xftdraw) + eprint("error, creating xft drawable failed"); + if(dc.selected) { + XftDrawStringUtf8(dc.xftdraw, &dc.xftselcolor, dc.xftfont.xft_font, x, y, (unsigned char*)buf, len); + } else { + XftDrawStringUtf8(dc.xftdraw, &dc.xftcolor, dc.xftfont.xft_font, x, y, (unsigned char*)buf, len); + } + } else if(dc.font.set) XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); else XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); @@ -359,6 +422,15 @@ initfont(const char *fontstr) { } void +initxft() { + if(!(dc.xftfont.xft_font = XftFontOpenName (dpy, screen, fontxft))) + eprint("error, cannot load xft font\n" ); + dc.xftfont.ascent = dc.xftfont.xft_font->ascent; + dc.xftfont.descent = dc.xftfont.xft_font->descent; + dc.xftfont.height = dc.xftfont.ascent + dc.xftfont.descent; +} + +void kpress(XKeyEvent * e) { char buf[sizeof text]; int i, num, off; @@ -697,7 +769,15 @@ setup(Bool topbar) { dc.norm[ColFG] = getcolor(normfgcolor); dc.sel[ColBG] = getcolor(selbgcolor); dc.sel[ColFG] = getcolor(selfgcolor); - initfont(font); + dc.selected = False; + if(fontxft){ + if(!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), (const char*)normfgcolor, &dc.xftcolor)) + eprint("error, cannot allocate xft font color '%s'\n", normfgcolor); + if(!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen), (const char*)selfgcolor, &dc.xftselcolor)) + eprint("error, cannot allocate xft font color '%s'\n", normfgcolor); + else + initxft(); + } else initfont(font); /* menu window */ wa.override_redirect = True; @@ -705,7 +785,10 @@ setup(Bool topbar) { wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask | VisibilityChangeMask; /* menu window geometry */ - mh = (dc.font.height + 2) * (lines + 1); + if(fontxft) + mh = (dc.xftfont.height + 2) * (lines + 1); + else + mh = (dc.font.height + 2) * (lines + 1); #if XINERAMA if(parent == RootWindow(dpy, screen) && XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) { i = 0; @@ -741,7 +824,7 @@ setup(Bool topbar) { dc.drawable = XCreatePixmap(dpy, parent, mw, mh, DefaultDepth(dpy, screen)); dc.gc = XCreateGC(dpy, parent, 0, NULL); XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); - if(!dc.font.set) + if(!dc.font.set && !fontxft) XSetFont(dpy, dc.gc, dc.font.xfont->fid); if(maxname) cmdw = MIN(textw(maxname), mw / 3); @@ -750,13 +833,20 @@ setup(Bool topbar) { text[0] = '\0'; match(text); XMapRaised(dpy, win); + if(fontxft) { + dc.xftdraw = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy,screen), DefaultColormap(dpy,screen)); + if(!dc.xftdraw) + eprint("error, cannot create xft drawable\n"); + } } int textnw(const char *text, unsigned int len) { - XRectangle r; - - if(dc.font.set) { + if(fontxft) { + XftTextExtentsUtf8(dpy, dc.xftfont.xft_font, (const FcChar8*)text, len, &dc.gi); + return dc.gi.width; + } else if(dc.font.set) { + XRectangle r; XmbTextExtents(dc.font.set, text, len, NULL, &r); return r.width; } @@ -765,6 +855,8 @@ textnw(const char *text, unsigned int le int textw(const char *text) { + if(fontxft) + return textnw(text, strlen(text)) + dc.xftfont.height; return textnw(text, strlen(text)) + dc.font.height; } @@ -791,6 +883,9 @@ main(int argc, char *argv[]) { else if(!strcmp(argv[i], "-fn")) { if(++i < argc) font = argv[i]; } + else if(!strcmp(argv[i], "-fa")) { + if(++i < argc) fontxft = argv[i]; + } else if(!strcmp(argv[i], "-nb")) { if(++i < argc) normbgcolor = argv[i]; } @@ -809,7 +904,7 @@ main(int argc, char *argv[]) { else if(!strcmp(argv[i], "-v")) eprint("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n"); else - eprint("usage: dmenu [-i] [-b] [-e <xid>] [-l <lines>] [-fn <font>] [-nb <color>]\n" + eprint("usage: dmenu [-i] [-b] [-e <xid>] [-l <lines>] [-fn <font>] [-fa <xftfont>] [-nb <color>]\n" " [-nf <color>] [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n"); if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) fprintf(stderr, "warning: no locale support\n");