dmenu

dynamic menu
git clone git://git.suckless.org/dmenu
Log | Files | Refs | README | LICENSE

commit 44c7de3dcf49ee568863f55610f40c7a05b4dfe7
parent b3d9451c2ddfad7c1b10e9a868afed4d92b37e41
Author: Markus Teich <markus.teich@stusta.mhn.de>
Date:   Sat, 21 May 2016 21:51:14 +0200

import new drw from libsl and minor fixes.

- extract drawitem function (code deduplication)
- fix bug where inputw was not correctly calculated from the widest item, but
  just from the one with the longest strlen() which is not the same. It's better
  now, but does not account for fallback fonts, since it would be too slow to
  calculate all the correct item widths on startup.
- minor code style fixes (indentation, useless line breaks)

Diffstat:
config.def.h | 12++++++------
dmenu.c | 147+++++++++++++++++++++++++++++++++++++------------------------------------------
drw.c | 241+++++++++++++++++++++++++++++++++++++++++++------------------------------------
drw.h | 63+++++++++++++++++++++++----------------------------------------
util.h | 4++--
5 files changed, 231 insertions(+), 236 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -7,12 +7,12 @@ static const char *fonts[] = { "monospace:size=10" }; static const char *prompt = NULL; /* -p option; prompt to the left 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 */ -static const char *selfgcolor = "#eeeeee"; /* -sf option; selected foreground */ -static const char *outbgcolor = "#00ffff"; -static const char *outfgcolor = "#000000"; +static const char *colors[][2] = { +/* fg bg */ + { "#bbbbbb", "#222222" }, /* normal */ + { "#eeeeee", "#005577" }, /* selected */ + { "#000000", "#00ffff" }, /* out */ +}; /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ static unsigned int lines = 0; diff --git a/dmenu.c b/dmenu.c @@ -22,8 +22,7 @@ #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 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) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) /* enums */ enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ @@ -37,7 +36,8 @@ struct item { static char text[BUFSIZ] = ""; static int bh, mw, mh; static int sw, sh; /* X display screen geometry width, height */ -static int inputw, promptw; +static int inputw = 0, promptw; +static int lrpad; /* sum of left and right padding */ static size_t cursor; static struct item *items = NULL; static struct item *matches, *matchend; @@ -49,8 +49,8 @@ static Display *dpy; static Window root, win; static XIC xic; -static ClrScheme scheme[SchemeLast]; static Drw *drw; +static Clr *scheme[SchemeLast]; #include "config.h" @@ -81,10 +81,10 @@ calcoffsets(void) 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(next->text), n)) > n) + if ((i += (lines > 0) ? bh : TEXTW(next->text)) > n) break; for (i = 0, prev = curr; prev && prev->left; prev = prev->left) - if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + if ((i += (lines > 0) ? bh : TEXTW(prev->left->text)) > n) break; } @@ -94,10 +94,8 @@ cleanup(void) size_t i; XUngrabKey(dpy, AnyKey, AnyModifier, root); - for (i = 0; i < SchemeLast; i++) { - drw_clr_free(scheme[i].bg); - drw_clr_free(scheme[i].fg); - } + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); drw_free(drw); XSync(dpy, False); XCloseDisplay(dpy); @@ -114,70 +112,63 @@ cistrstr(const char *s, const char *sub) return NULL; } +static int +drawitem(struct item *item, int x, int y, int w) +{ + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); +} + static void drawmenu(void) { - int curpos; + unsigned int curpos; struct item *item; - int x = 0, y = 0, h = bh, w; + int x = 0, y = 0, w; - drw_setscheme(drw, &scheme[SchemeNorm]); - drw_rect(drw, 0, 0, mw, mh, 1, 1, 1); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); if (prompt && *prompt) { - drw_setscheme(drw, &scheme[SchemeSel]); - drw_text(drw, x, 0, promptw, bh, prompt, 0); - x += promptw; + drw_setscheme(drw, scheme[SchemeSel]); + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); } /* draw input field */ w = (lines > 0 || !matches) ? mw - x : inputw; - drw_setscheme(drw, &scheme[SchemeNorm]); - drw_text(drw, x, 0, w, bh, text, 0); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, 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); + drw_font_getexts(drw->fonts, text, cursor, &curpos, NULL); + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); } if (lines > 0) { /* draw vertical list */ - w = mw - x; - for (item = curr; item != next; item = item->right) { - 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); - } + for (item = curr; item != next; item = item->right) + drawitem(item, x, y += bh, mw - x); } else if (matches) { /* draw horizontal list */ x += inputw; w = TEXTW("<"); if (curr->left) { - drw_setscheme(drw, &scheme[SchemeNorm]); - drw_text(drw, x, 0, w, bh, "<", 0); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); } - for (item = curr; item != next; item = item->right) { - 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; + x += w; + for (item = curr; item != next; item = item->right) + x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); if (next) { - drw_setscheme(drw, &scheme[SchemeNorm]); - drw_text(drw, x, 0, w, bh, ">", 0); + w = TEXTW(">"); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); } } drw_map(drw, win, 0, 0, mw, mh); @@ -191,8 +182,8 @@ grabkeyboard(void) /* try to grab keyboard, we may have to wait for another process to ungrab */ for (i = 0; i < 1000; i++) { - if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, - GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) return; nanosleep(&ts, NULL); } @@ -314,11 +305,9 @@ keypress(XKeyEvent *ev) insert(NULL, 0 - cursor); break; case XK_w: /* delete word */ - while (cursor > 0 && strchr(worddelimiters, - text[nextrune(-1)])) + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) insert(NULL, nextrune(-1) - cursor); - while (cursor > 0 && !strchr(worddelimiters, - text[nextrune(-1)])) + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) insert(NULL, nextrune(-1) - cursor); break; case XK_y: /* paste selection */ @@ -469,8 +458,9 @@ paste(void) static void readstdin(void) { - char buf[sizeof text], *p, *maxstr = NULL; - size_t i, max = 0, size = 0; + char buf[sizeof text], *p; + size_t i, imax = 0, size = 0; + unsigned int tmpmax = 0; /* read each line from stdin and add it to the item list */ for (i = 0; fgets(buf, sizeof buf, stdin); i++) { @@ -482,12 +472,15 @@ readstdin(void) if (!(items[i].text = strdup(buf))) die("cannot strdup %u bytes:", strlen(buf) + 1); items[i].out = 0; - if (strlen(items[i].text) > max) - max = strlen(maxstr = items[i].text); + drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); + if (tmpmax > inputw) { + inputw = tmpmax; + imax = i; + } } if (items) items[i].text = NULL; - inputw = maxstr ? TEXTW(maxstr) : 0; + inputw = TEXTW(items[imax].text); lines = MIN(lines, i); } @@ -534,18 +527,15 @@ setup(void) #endif /* 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); + scheme[SchemeNorm] = drw_scm_create(drw, colors[SchemeNorm], 2); + scheme[SchemeSel] = drw_scm_create(drw, colors[SchemeSel], 2); + scheme[SchemeOut] = drw_scm_create(drw, colors[SchemeOut], 2); clip = XInternAtom(dpy, "CLIPBOARD", False); utf8 = XInternAtom(dpy, "UTF8_STRING", False); /* calculate menu geometry */ - bh = drw->fonts[0]->h + 2; + bh = drw->fonts->h + 2; lines = MAX(lines, 0); mh = (lines + 1) * bh; #ifdef XINERAMA @@ -584,13 +574,13 @@ setup(void) y = topbar ? 0 : sh - mh; mw = sw; } - promptw = (prompt && *prompt) ? TEXTW(prompt) : 0; + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; inputw = MIN(inputw, mw/3); match(); /* create menu window */ swa.override_redirect = True; - swa.background_pixel = scheme[SchemeNorm].bg->pix; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; win = XCreateWindow(dpy, root, x, y, mw, mh, 0, DefaultDepth(dpy, screen), CopyFromParent, @@ -644,13 +634,13 @@ main(int argc, char *argv[]) else if (!strcmp(argv[i], "-fn")) /* font or font set */ fonts[0] = argv[++i]; else if (!strcmp(argv[i], "-nb")) /* normal background color */ - normbgcolor = argv[++i]; + colors[SchemeNorm][ColBg] = argv[++i]; else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ - normfgcolor = argv[++i]; + colors[SchemeNorm][ColFg] = argv[++i]; else if (!strcmp(argv[i], "-sb")) /* selected background color */ - selbgcolor = argv[++i]; + colors[SchemeSel][ColBg] = argv[++i]; else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ - selfgcolor = argv[++i]; + colors[SchemeSel][ColFg] = argv[++i]; else usage(); @@ -663,10 +653,9 @@ main(int argc, char *argv[]) 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) + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) die("no fonts could be loaded.\n"); - drw_setscheme(drw, &scheme[SchemeNorm]); + lrpad = drw->fonts->h; if (fast) { grabkeyboard(); diff --git a/drw.c b/drw.c @@ -63,9 +63,8 @@ utf8decode(const char *c, long *u, size_t clen) Drw * drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) { - Drw *drw; + Drw *drw = ecalloc(1, sizeof(Drw)); - drw = ecalloc(1, sizeof(Drw)); drw->dpy = dpy; drw->screen = screen; drw->root = root; @@ -73,7 +72,6 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h 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; @@ -82,6 +80,9 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h void drw_resize(Drw *drw, unsigned int w, unsigned int h) { + if (!drw) + return; + drw->w = w; drw->h = h; if (drw->drawable) @@ -92,44 +93,39 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h) 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. + * drw_fontset_create instead. */ static Fnt * -drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) { Fnt *font; XftFont *xfont = NULL; FcPattern *pattern = NULL; if (fontname) { - /* Using the pattern found at font->xfont->pattern does not yield same - * the same substitution results as using the pattern returned by + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by * FcNameParse; using the latter results in the desired fallback - * behaviour whereas the former just results in - * missing-character-rectangles being drawn, at least with some fonts. - */ + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { - fprintf(stderr, "error, cannot load font: '%s'\n", fontname); + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); return NULL; } if (!(pattern = FcNameParse((FcChar8 *) fontname))) { - fprintf(stderr, "error, cannot load font: '%s'\n", fontname); + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); XftFontClose(drw->dpy, xfont); return NULL; } } else if (fontpattern) { if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { - fprintf(stderr, "error, cannot load font pattern.\n"); + fprintf(stderr, "error, cannot load font from pattern.\n"); return NULL; } } else { @@ -139,95 +135,115 @@ drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) font = ecalloc(1, sizeof(Fnt)); font->xfont = xfont; font->pattern = pattern; - font->ascent = xfont->ascent; - font->descent = xfont->descent; - font->h = font->ascent + font->descent; + font->h = xfont->ascent + xfont->descent; font->dpy = drw->dpy; return font; } -Fnt* -drw_font_create(Drw *drw, const char *fontname) +static void +xfont_free(Fnt *font) { - return drw_font_xcreate(drw, fontname, NULL); + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); } -void -drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount) +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) { + Fnt *cur, *ret = NULL; 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; + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; } } + return (drw->fonts = ret); } void -drw_font_free(Fnt *font) +drw_fontset_free(Fnt *font) { - if (!font) - return; - if (font->pattern) - FcPatternDestroy(font->pattern); - XftFontClose(font->dpy, font->xfont); - free(font); + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } } -Clr * -drw_clr_create(Drw *drw, const char *clrname) +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) { - Clr *clr; + if (!drw || !dest || !clrname) + return; - clr = ecalloc(1, sizeof(Clr)); if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), DefaultColormap(drw->dpy, drw->screen), - clrname, &clr->rgb)) + clrname, dest)) die("error, cannot allocate color '%s'\n", clrname); - clr->pix = clr->rgb.pixel; +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; - return clr; + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; } void -drw_clr_free(Clr *clr) +drw_setfontset(Drw *drw, Fnt *set) { - free(clr); + if (drw) + drw->fonts = set; } void -drw_setscheme(Drw *drw, ClrScheme *scheme) +drw_setscheme(Drw *drw, Clr *scm) { - drw->scheme = scheme; + if (drw) + drw->scheme = scm; } void -drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert) +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) { - if (!drw->scheme) + if (!drw || !drw->scheme) return; - XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix); + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); if (filled) - XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1); - else if (empty) - XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); } int -drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) { char buf[1024]; - int tx, ty, th; - Extnts tex; + int ty; + unsigned int ew; XftDraw *d = NULL; - Fnt *curfont, *nextfont; + Fnt *usedfont, *curfont, *nextfont; size_t i, len; - int utf8strlen, utf8charlen, render; + int utf8strlen, utf8charlen, render = x || y || w || h; long utf8codepoint = 0; const char *utf8str; FcCharSet *fccharset; @@ -236,66 +252,67 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex XftResult result; int charexists = 0; - if (!drw->scheme || !drw->fontcount) + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) return 0; - if (!(render = x || y || w || h)) { + if (!render) { w = ~w; } else { - XSetForeground(drw->dpy, drw->gc, invert ? - drw->scheme->fg->pix : drw->scheme->bg->pix); + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); d = XftDrawCreate(drw->dpy, drw->drawable, DefaultVisual(drw->dpy, drw->screen), DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; } - curfont = drw->fonts[0]; + usedfont = drw->fonts; 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); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); if (charexists) { - if (drw->fonts[i] == curfont) { + if (curfont == usedfont) { utf8strlen += utf8charlen; text += utf8charlen; } else { - nextfont = drw->fonts[i]; + nextfont = curfont; } break; } } - if (!charexists || (nextfont && nextfont != curfont)) + if (!charexists || nextfont) break; else charexists = 0; } if (utf8strlen) { - drw_font_getexts(curfont, utf8str, utf8strlen, &tex); + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); /* 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); + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); if (len) { memcpy(buf, utf8str, len); buf[len] = '\0'; if (len < utf8strlen) - for (i = len; i && i > len - 3; buf[--i] = '.'); + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ 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); + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); } - x += tex.w; - w -= tex.w; + x += ew; + w -= ew; } } @@ -303,26 +320,21 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex break; } else if (nextfont) { charexists = 0; - curfont = nextfont; + usedfont = nextfont; } else { /* Regardless of whether or not a fallback font is found, the - * character must be drawn. - */ + * 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. */ + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ die("the first font in the cache must be loaded from a font string.\n"); } - fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern); + fcpattern = FcPatternDuplicate(drw->fonts->pattern); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); @@ -334,12 +346,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex FcPatternDestroy(fcpattern); if (match) { - curfont = drw_font_xcreate(drw, NULL, match); - if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) { - drw->fonts[drw->fontcount++] = curfont; + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; } else { - drw_font_free(curfont); - curfont = drw->fonts[0]; + xfont_free(usedfont); + usedfont = drw->fonts; } } } @@ -347,34 +361,40 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex if (d) XftDrawDestroy(d); - return x; + return x + (render ? w : 0); } void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) { + if (!drw) + return; + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); XSync(drw->dpy, False); } -void -drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex) +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) { - XGlyphInfo ext; - - XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); - tex->h = font->h; - tex->w = ext.xOff; + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); } -unsigned int -drw_font_getexts_width(Fnt *font, const char *text, unsigned int len) +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) { - Extnts tex; + XGlyphInfo ext; - drw_font_getexts(font, text, len, &tex); + if (!font || !text) + return; - return tex.w; + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; } Cur * @@ -382,7 +402,9 @@ drw_cur_create(Drw *drw, int shape) { Cur *cur; - cur = ecalloc(1, sizeof(Cur)); + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + cur->cursor = XCreateFontCursor(drw->dpy, shape); return cur; @@ -393,6 +415,7 @@ drw_cur_free(Drw *drw, Cur *cursor) { if (!cursor) return; + XFreeCursor(drw->dpy, cursor->cursor); free(cursor); } diff --git a/drw.h b/drw.h @@ -1,29 +1,19 @@ /* 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 { +typedef struct Fnt { Display *dpy; - int ascent; - int descent; unsigned int h; XftFont *xfont; FcPattern *pattern; + struct Fnt *next; } Fnt; -typedef struct { - Clr *fg; - Clr *bg; - Clr *border; -} ClrScheme; +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef XftColor Clr; typedef struct { unsigned int w, h; @@ -32,43 +22,36 @@ typedef struct { Window root; Drawable drawable; GC gc; - ClrScheme *scheme; - size_t fontcount; - Fnt *fonts[DRW_FONT_CACHE_SIZE]; + Clr *scheme; + Fnt *fonts; } Drw; -typedef struct { - unsigned int w; - unsigned int h; -} Extnts; - /* Drawable abstraction */ -Drw *drw_create(Display *, int, Window, unsigned int, unsigned int); -void drw_resize(Drw *, unsigned int, unsigned int); -void drw_free(Drw *); +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 *, const char *); -void drw_load_fonts(Drw *, const char *[], size_t); -void drw_font_free(Fnt *); -void drw_font_getexts(Fnt *, const char *, unsigned int, Extnts *); -unsigned int drw_font_getexts_width(Fnt *, const char *, unsigned int); +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); -/* Colour abstraction */ -Clr *drw_clr_create(Drw *, const char *); -void drw_clr_free(Clr *); +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); /* Cursor abstraction */ -Cur *drw_cur_create(Drw *, int); -void drw_cur_free(Drw *, Cur *); +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); /* Drawing context manipulation */ -void drw_setfont(Drw *, Fnt *); -void drw_setscheme(Drw *, ClrScheme *); +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); /* Drawing functions */ -void drw_rect(Drw *, int, int, unsigned int, unsigned int, int, int, int); -int drw_text(Drw *, int, int, unsigned int, unsigned int, const char *, int); +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); /* Map functions */ -void drw_map(Drw *, Window, int, int, unsigned int, unsigned int); +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/util.h b/util.h @@ -4,5 +4,5 @@ #define MIN(A, B) ((A) < (B) ? (A) : (B)) #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) -void die(const char *errstr, ...); -void *ecalloc(size_t, size_t); +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size);