Re: [dev] [patch] dmenu - support for xft font rendering

From: Arian Kuschki <arian.kuschki_AT_googlemail.com>
Date: Wed, 01 Sep 2010 13:29:37 +0200

Excerpts from Henri Mannerberg's message of 2010-09-01 12:10:06 +0200:
>
> 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
>
> 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");

Thanks for this, Henri. The only issue I have is that the font colour
does not change when highlighted when I call dmenu with the '-l' option.

-- 
Received on Wed Sep 01 2010 - 13:29:37 CEST

This archive was generated by hypermail 2.2.0 : Wed Sep 01 2010 - 13:36:03 CEST