diff --git a/config.mk b/config.mk index c0d466b..04e2dce 100644 --- a/config.mk +++ b/config.mk @@ -12,9 +12,13 @@ X11LIB = /usr/X11R6/lib XINERAMALIBS = -lXinerama XINERAMAFLAGS = -DXINERAMA +# Xft, comment if you don't want it +XFTINC = -I/usr/include/freetype2 +XFTLIBS = -lXft -lXrender -lfreetype -lz -lfontconfig + # includes and libs -INCS = -I${X11INC} -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} +INCS = -I${X11INC} ${XFTINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${XFTLIBS} # flags CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} diff --git a/dmenu.1 b/dmenu.1 index 2897ab1..d06b484 100644 --- a/dmenu.1 +++ b/dmenu.1 @@ -58,7 +58,7 @@ dmenu is displayed on the monitor supplied. defines the prompt to be displayed to the left of the input field. .TP .BI \-fn " font" -defines the font or font set used. +defines the font or font set used. eg. "fixed" or "Monospace-12:normal" (a xft font) .TP .BI \-nb " color" defines the normal background color. diff --git a/dmenu.c b/dmenu.c index 94c70de..cc6dedf 100644 --- a/dmenu.c +++ b/dmenu.c @@ -17,6 +17,7 @@ * 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 DEFFONT "fixed" /* xft example: "Monospace-11" */ typedef struct Item Item; struct Item { @@ -27,6 +28,7 @@ struct Item { static void appenditem(Item *item, Item **list, Item **last); static void calcoffsets(void); +static void cleanup(void); static char *cistrstr(const char *s, const char *sub); static void drawmenu(void); static void grabkeyboard(void); @@ -44,9 +46,9 @@ 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 ColorSet *normcol; +static ColorSet *selcol; +static ColorSet *outcol; static Atom clip, utf8; static DC *dc; static Item *items = NULL; @@ -55,6 +57,8 @@ static Item *prev, *curr, *next, *sel; static Window win; static XIC xic; static int mon = -1; +static Bool running = True; +static int ret = EXIT_SUCCESS; #include "config.h" @@ -103,7 +107,10 @@ main(int argc, char *argv[]) { usage(); dc = initdc(); - initfont(dc, font); + initfont(dc, font ? font : DEFFONT); + normcol = initcolor(dc, normfgcolor, normbgcolor); + selcol = initcolor(dc, selfgcolor, selbgcolor); + outcol = initcolor(dc, outfgcolor, outbgcolor); if(fast) { grabkeyboard(); @@ -116,7 +123,8 @@ main(int argc, char *argv[]) { setup(); run(); - return 1; /* unreachable */ + cleanup(); + return ret; } void @@ -159,6 +167,15 @@ cistrstr(const char *s, const char *sub) { } void +cleanup(void) { + freecol(dc, normcol); + freecol(dc, selcol); + XDestroyWindow(dc->dpy, win); + XUngrabKeyboard(dc->dpy, CurrentTime); + freedc(dc); +} + +void drawmenu(void) { int curpos; Item *item; @@ -166,7 +183,7 @@ drawmenu(void) { dc->x = 0; dc->y = 0; dc->h = bh; - drawrect(dc, 0, 0, mw, mh, True, BG(dc, normcol)); + drawrect(dc, 0, 0, mw, mh, True, normcol->BG); if(prompt && *prompt) { dc->w = promptw; @@ -177,7 +194,7 @@ drawmenu(void) { 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)); + drawrect(dc, curpos, 2, 1, dc->h - 4, True, normcol->FG); if(lines > 0) { /* draw vertical list */ @@ -281,9 +298,12 @@ keypress(XKeyEvent *ev) { return; case XK_Return: case XK_KP_Enter: + ret = EXIT_SUCCESS; + running = False; break; case XK_bracketleft: - exit(EXIT_FAILURE); + ret = EXIT_FAILURE; + running = False; default: return; } @@ -330,7 +350,8 @@ keypress(XKeyEvent *ev) { sel = matchend; break; case XK_Escape: - exit(EXIT_FAILURE); + ret = EXIT_FAILURE; + running = False; case XK_Home: if(sel == matches) { cursor = 0; @@ -368,8 +389,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)) { + ret = EXIT_SUCCESS; + running = False; + } if(sel) sel->out = True; break; @@ -505,7 +528,7 @@ void run(void) { XEvent ev; - while(!XNextEvent(dc->dpy, &ev)) { + while(running && !XNextEvent(dc->dpy, &ev)) { if(XFilterEvent(&ev, win)) continue; switch(ev.type) { @@ -539,13 +562,6 @@ setup(void) { XineramaScreenInfo *info; #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); - clip = XInternAtom(dc->dpy, "CLIPBOARD", False); utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); @@ -601,7 +617,7 @@ setup(void) { /* create menu window */ swa.override_redirect = True; - swa.background_pixel = normcol[ColBG]; + swa.background_pixel = normcol->BG; swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0, DefaultDepth(dc->dpy, screen), CopyFromParent, diff --git a/draw.c b/draw.c index 76f0c54..09e66ea 100644 --- a/draw.c +++ b/draw.c @@ -9,9 +9,6 @@ #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) { @@ -23,7 +20,7 @@ drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsign } void -drawtext(DC *dc, const char *text, unsigned long col[ColLast]) { +drawtext(DC *dc, const char *text, ColorSet *col) { char buf[BUFSIZ]; size_t mn, n = strlen(text); @@ -35,19 +32,24 @@ drawtext(DC *dc, const char *text, unsigned long col[ColLast]) { 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)); + drawrect(dc, 0, 0, dc->w, dc->h, True, col->BG); drawtextn(dc, buf, mn, col); } void -drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) { +drawtextn(DC *dc, const char *text, size_t n, ColorSet *col) { 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) + XSetForeground(dc->dpy, dc->gc, col->FG); + if(dc->font.xft_font) { + if(!dc->xftdraw) + eprintf("error, xft drawable does not exist"); + XftDrawStringUtf8(dc->xftdraw, &col->FG_xft, + dc->font.xft_font, x, y, (unsigned char*)text, n); + } else if(dc->font.set) { XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n); - else { + } else { XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid); XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n); } @@ -69,16 +71,33 @@ eprintf(const char *fmt, ...) { } void +freecol(DC *dc, ColorSet *col) { + if(col) { + if(&col->FG_xft) + XftColorFree(dc->dpy, DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)), + DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)), &col->FG_xft); + free(col); + } +} + +void freedc(DC *dc) { + if(dc->font.xft_font) { + XftFontClose(dc->dpy, dc->font.xft_font); + XftDrawDestroy(dc->xftdraw); + } 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); + if(dc->gc) + XFreeGC(dc->dpy, dc->gc); + if(dc->dpy) + XCloseDisplay(dc->dpy); + if(dc) + free(dc); } unsigned long @@ -91,6 +110,20 @@ getcolor(DC *dc, const char *colstr) { return color.pixel; } +ColorSet * +initcolor(DC *dc, const char * foreground, const char * background) { + ColorSet * col = (ColorSet *)malloc(sizeof(ColorSet)); + if(!col) + eprintf("error, cannot allocate memory for color set"); + col->BG = getcolor(dc, background); + col->FG = getcolor(dc, foreground); + if(dc->font.xft_font) + if(!XftColorAllocName(dc->dpy, DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)), + DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)), foreground, &col->FG_xft)) + eprintf("error, cannot allocate xft font color '%s'\n", foreground); + return col; +} + DC * initdc(void) { DC *dc; @@ -109,39 +142,33 @@ initdc(void) { 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))) { + missing = NULL; + 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; + } else 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.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); + 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; + } else if((dc->font.xft_font = XftFontOpenName(dc->dpy, DefaultScreen(dc->dpy), fontstr))) { + dc->font.ascent = dc->font.xft_font->ascent; + dc->font.descent = dc->font.xft_font->descent; + dc->font.width = dc->font.xft_font->max_advance_width; + } else { + eprintf("cannot load font '%s'\n", fontstr); } if(missing) XFreeStringList(missing); - return dc->font.set || dc->font.xfont; + dc->font.height = dc->font.ascent + dc->font.descent; + return; } void @@ -151,20 +178,29 @@ mapdc(DC *dc, Window win, unsigned int w, unsigned int h) { void resizedc(DC *dc, unsigned int w, unsigned int h) { + int screen = DefaultScreen(dc->dpy); 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))); + DefaultDepth(dc->dpy, screen)); + if(dc->font.xft_font && !(dc->xftdraw)) { + dc->xftdraw = XftDrawCreate(dc->dpy, dc->canvas, DefaultVisual(dc->dpy,screen), DefaultColormap(dc->dpy,screen)); + if(!(dc->xftdraw)) + eprintf("error, cannot create xft drawable\n"); + } } int textnw(DC *dc, const char *text, size_t len) { - if(dc->font.set) { + if(dc->font.xft_font) { + XGlyphInfo gi; + XftTextExtentsUtf8(dc->dpy, dc->font.xft_font, (const FcChar8*)text, len, &gi); + return gi.width; + } else if(dc->font.set) { XRectangle r; - XmbTextExtents(dc->font.set, text, len, NULL, &r); return r.width; } diff --git a/draw.h b/draw.h index 43a57bf..1b4f21a 100644 --- a/draw.h +++ b/draw.h @@ -1,9 +1,6 @@ /* 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 }; +#include typedef struct { int x, y, w, h; @@ -11,6 +8,7 @@ typedef struct { Display *dpy; GC gc; Pixmap canvas; + XftDraw *xftdraw; struct { int ascent; int descent; @@ -18,15 +16,24 @@ typedef struct { int width; XFontSet set; XFontStruct *xfont; + XftFont *xft_font; } font; } DC; /* draw context */ +typedef struct { + unsigned long FG; + XftColor FG_xft; + unsigned long BG; +} ColorSet; + 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 drawtext(DC *dc, const char *text, ColorSet *col); +void drawtextn(DC *dc, const char *text, size_t n, ColorSet *col); +void freecol(DC *dc, ColorSet *col); void eprintf(const char *fmt, ...); void freedc(DC *dc); unsigned long getcolor(DC *dc, const char *colstr); +ColorSet *initcolor(DC *dc, const char *foreground, const char *background); DC *initdc(void); void initfont(DC *dc, const char *fontstr); void mapdc(DC *dc, Window win, unsigned int w, unsigned int h);