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)
This commit is contained in:
Markus Teich 2016-05-21 21:51:14 +02:00 committed by Hiltjo Posthuma
parent b3d9451c2d
commit 44c7de3dcf
5 changed files with 237 additions and 242 deletions

View File

@ -7,12 +7,12 @@ static const char *fonts[] = {
"monospace:size=10" "monospace:size=10"
}; };
static const char *prompt = NULL; /* -p option; prompt to the left of input field */ 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 *colors[][2] = {
static const char *normfgcolor = "#bbbbbb"; /* -nf option; normal foreground */ /* fg bg */
static const char *selbgcolor = "#005577"; /* -sb option; selected background */ { "#bbbbbb", "#222222" }, /* normal */
static const char *selfgcolor = "#eeeeee"; /* -sf option; selected foreground */ { "#eeeeee", "#005577" }, /* selected */
static const char *outbgcolor = "#00ffff"; { "#000000", "#00ffff" }, /* out */
static const char *outfgcolor = "#000000"; };
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
static unsigned int lines = 0; static unsigned int lines = 0;

145
dmenu.c
View File

@ -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)) \ #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))) * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
#define LENGTH(X) (sizeof X / sizeof X[0]) #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_fontset_getwidth(drw, (X)) + lrpad)
#define TEXTW(X) (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
/* enums */ /* enums */
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
@ -37,7 +36,8 @@ struct item {
static char text[BUFSIZ] = ""; static char text[BUFSIZ] = "";
static int bh, mw, mh; static int bh, mw, mh;
static int sw, sh; /* X display screen geometry width, height */ 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 size_t cursor;
static struct item *items = NULL; static struct item *items = NULL;
static struct item *matches, *matchend; static struct item *matches, *matchend;
@ -49,8 +49,8 @@ static Display *dpy;
static Window root, win; static Window root, win;
static XIC xic; static XIC xic;
static ClrScheme scheme[SchemeLast];
static Drw *drw; static Drw *drw;
static Clr *scheme[SchemeLast];
#include "config.h" #include "config.h"
@ -81,10 +81,10 @@ calcoffsets(void)
n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
/* calculate which items will begin the next page and previous page */ /* calculate which items will begin the next page and previous page */
for (i = 0, next = curr; next; next = next->right) 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; break;
for (i = 0, prev = curr; prev && prev->left; prev = prev->left) 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; break;
} }
@ -94,10 +94,8 @@ cleanup(void)
size_t i; size_t i;
XUngrabKey(dpy, AnyKey, AnyModifier, root); XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (i = 0; i < SchemeLast; i++) { for (i = 0; i < SchemeLast; i++)
drw_clr_free(scheme[i].bg); free(scheme[i]);
drw_clr_free(scheme[i].fg);
}
drw_free(drw); drw_free(drw);
XSync(dpy, False); XSync(dpy, False);
XCloseDisplay(dpy); XCloseDisplay(dpy);
@ -114,70 +112,63 @@ cistrstr(const char *s, const char *sub)
return NULL; 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 static void
drawmenu(void) drawmenu(void)
{ {
int curpos; unsigned int curpos;
struct item *item; struct item *item;
int x = 0, y = 0, h = bh, w; int x = 0, y = 0, w;
drw_setscheme(drw, &scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, 0, 0, mw, mh, 1, 1, 1); drw_rect(drw, 0, 0, mw, mh, 1, 1);
if (prompt && *prompt) { if (prompt && *prompt) {
drw_setscheme(drw, &scheme[SchemeSel]); drw_setscheme(drw, scheme[SchemeSel]);
drw_text(drw, x, 0, promptw, bh, prompt, 0); x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
x += promptw;
} }
/* draw input field */ /* draw input field */
w = (lines > 0 || !matches) ? mw - x : inputw; w = (lines > 0 || !matches) ? mw - x : inputw;
drw_setscheme(drw, &scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, text, 0); drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
if ((curpos = TEXTNW(text, cursor) + bh / 2 - 2) < w) { drw_font_getexts(drw->fonts, text, cursor, &curpos, NULL);
drw_setscheme(drw, &scheme[SchemeNorm]); if ((curpos += lrpad / 2 - 1) < w) {
drw_rect(drw, x + curpos + 2, 2, 1, bh - 4, 1, 1, 0); drw_setscheme(drw, scheme[SchemeNorm]);
drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
} }
if (lines > 0) { if (lines > 0) {
/* draw vertical list */ /* draw vertical list */
w = mw - x; for (item = curr; item != next; item = item->right)
for (item = curr; item != next; item = item->right) { drawitem(item, x, y += bh, mw - x);
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) { } else if (matches) {
/* draw horizontal list */ /* draw horizontal list */
x += inputw; x += inputw;
w = TEXTW("<"); w = TEXTW("<");
if (curr->left) { if (curr->left) {
drw_setscheme(drw, &scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, "<", 0); drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
} }
for (item = curr; item != next; item = item->right) {
x += w; x += w;
w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); for (item = curr; item != next; item = item->right)
x = drawitem(item, x, 0, 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) { if (next) {
drw_setscheme(drw, &scheme[SchemeNorm]); w = TEXTW(">");
drw_text(drw, x, 0, w, bh, ">", 0); drw_setscheme(drw, scheme[SchemeNorm]);
drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0);
} }
} }
drw_map(drw, win, 0, 0, mw, mh); 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 */ /* try to grab keyboard, we may have to wait for another process to ungrab */
for (i = 0; i < 1000; i++) { for (i = 0; i < 1000; i++) {
if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) GrabModeAsync, CurrentTime) == GrabSuccess)
return; return;
nanosleep(&ts, NULL); nanosleep(&ts, NULL);
} }
@ -314,11 +305,9 @@ keypress(XKeyEvent *ev)
insert(NULL, 0 - cursor); insert(NULL, 0 - cursor);
break; break;
case XK_w: /* delete word */ case XK_w: /* delete word */
while (cursor > 0 && strchr(worddelimiters, while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
text[nextrune(-1)]))
insert(NULL, nextrune(-1) - cursor); insert(NULL, nextrune(-1) - cursor);
while (cursor > 0 && !strchr(worddelimiters, while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
text[nextrune(-1)]))
insert(NULL, nextrune(-1) - cursor); insert(NULL, nextrune(-1) - cursor);
break; break;
case XK_y: /* paste selection */ case XK_y: /* paste selection */
@ -469,8 +458,9 @@ paste(void)
static void static void
readstdin(void) readstdin(void)
{ {
char buf[sizeof text], *p, *maxstr = NULL; char buf[sizeof text], *p;
size_t i, max = 0, size = 0; size_t i, imax = 0, size = 0;
unsigned int tmpmax = 0;
/* read each line from stdin and add it to the item list */ /* read each line from stdin and add it to the item list */
for (i = 0; fgets(buf, sizeof buf, stdin); i++) { for (i = 0; fgets(buf, sizeof buf, stdin); i++) {
@ -482,12 +472,15 @@ readstdin(void)
if (!(items[i].text = strdup(buf))) if (!(items[i].text = strdup(buf)))
die("cannot strdup %u bytes:", strlen(buf) + 1); die("cannot strdup %u bytes:", strlen(buf) + 1);
items[i].out = 0; items[i].out = 0;
if (strlen(items[i].text) > max) drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL);
max = strlen(maxstr = items[i].text); if (tmpmax > inputw) {
inputw = tmpmax;
imax = i;
}
} }
if (items) if (items)
items[i].text = NULL; items[i].text = NULL;
inputw = maxstr ? TEXTW(maxstr) : 0; inputw = TEXTW(items[imax].text);
lines = MIN(lines, i); lines = MIN(lines, i);
} }
@ -534,18 +527,15 @@ setup(void)
#endif #endif
/* init appearance */ /* init appearance */
scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor); scheme[SchemeNorm] = drw_scm_create(drw, colors[SchemeNorm], 2);
scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor); scheme[SchemeSel] = drw_scm_create(drw, colors[SchemeSel], 2);
scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor); scheme[SchemeOut] = drw_scm_create(drw, colors[SchemeOut], 2);
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(dpy, "CLIPBOARD", False); clip = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False); utf8 = XInternAtom(dpy, "UTF8_STRING", False);
/* calculate menu geometry */ /* calculate menu geometry */
bh = drw->fonts[0]->h + 2; bh = drw->fonts->h + 2;
lines = MAX(lines, 0); lines = MAX(lines, 0);
mh = (lines + 1) * bh; mh = (lines + 1) * bh;
#ifdef XINERAMA #ifdef XINERAMA
@ -584,13 +574,13 @@ setup(void)
y = topbar ? 0 : sh - mh; y = topbar ? 0 : sh - mh;
mw = sw; mw = sw;
} }
promptw = (prompt && *prompt) ? TEXTW(prompt) : 0; promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
inputw = MIN(inputw, mw/3); inputw = MIN(inputw, mw/3);
match(); match();
/* create menu window */ /* create menu window */
swa.override_redirect = True; swa.override_redirect = True;
swa.background_pixel = scheme[SchemeNorm].bg->pix; swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
win = XCreateWindow(dpy, root, x, y, mw, mh, 0, win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
DefaultDepth(dpy, screen), CopyFromParent, DefaultDepth(dpy, screen), CopyFromParent,
@ -644,13 +634,13 @@ main(int argc, char *argv[])
else if (!strcmp(argv[i], "-fn")) /* font or font set */ else if (!strcmp(argv[i], "-fn")) /* font or font set */
fonts[0] = argv[++i]; fonts[0] = argv[++i];
else if (!strcmp(argv[i], "-nb")) /* normal background color */ 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 */ 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 */ 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 */ else if (!strcmp(argv[i], "-sf")) /* selected foreground color */
selfgcolor = argv[++i]; colors[SchemeSel][ColFg] = argv[++i];
else else
usage(); usage();
@ -663,10 +653,9 @@ main(int argc, char *argv[])
sw = DisplayWidth(dpy, screen); sw = DisplayWidth(dpy, screen);
sh = DisplayHeight(dpy, screen); sh = DisplayHeight(dpy, screen);
drw = drw_create(dpy, screen, root, sw, sh); drw = drw_create(dpy, screen, root, sw, sh);
drw_load_fonts(drw, fonts, LENGTH(fonts)); if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
if (!drw->fontcount)
die("no fonts could be loaded.\n"); die("no fonts could be loaded.\n");
drw_setscheme(drw, &scheme[SchemeNorm]); lrpad = drw->fonts->h;
if (fast) { if (fast) {
grabkeyboard(); grabkeyboard();

253
drw.c
View File

@ -63,9 +63,8 @@ utf8decode(const char *c, long *u, size_t clen)
Drw * Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) 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->dpy = dpy;
drw->screen = screen; drw->screen = screen;
drw->root = root; 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->h = h;
drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
drw->gc = XCreateGC(dpy, root, 0, NULL); drw->gc = XCreateGC(dpy, root, 0, NULL);
drw->fontcount = 0;
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw; return drw;
@ -82,6 +80,9 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
void void
drw_resize(Drw *drw, unsigned int w, unsigned int h) drw_resize(Drw *drw, unsigned int w, unsigned int h)
{ {
if (!drw)
return;
drw->w = w; drw->w = w;
drw->h = h; drw->h = h;
if (drw->drawable) if (drw->drawable)
@ -92,44 +93,39 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
void void
drw_free(Drw *drw) 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); XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc); XFreeGC(drw->dpy, drw->gc);
free(drw); free(drw);
} }
/* This function is an implementation detail. Library users should use /* This function is an implementation detail. Library users should use
* drw_font_create instead. * drw_fontset_create instead.
*/ */
static Fnt * static Fnt *
drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern) xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
{ {
Fnt *font; Fnt *font;
XftFont *xfont = NULL; XftFont *xfont = NULL;
FcPattern *pattern = NULL; FcPattern *pattern = NULL;
if (fontname) { if (fontname) {
/* Using the pattern found at font->xfont->pattern does not yield same /* Using the pattern found at font->xfont->pattern does not yield the
* the same substitution results as using the pattern returned by * same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback * FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in * behaviour whereas the former just results in missing-character
* missing-character-rectangles being drawn, at least with some fonts. * rectangles being drawn, at least with some fonts. */
*/
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { 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; return NULL;
} }
if (!(pattern = FcNameParse((FcChar8 *) fontname))) { 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); XftFontClose(drw->dpy, xfont);
return NULL; return NULL;
} }
} else if (fontpattern) { } else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, 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; return NULL;
} }
} else { } else {
@ -139,37 +135,14 @@ drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern)
font = ecalloc(1, sizeof(Fnt)); font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont; font->xfont = xfont;
font->pattern = pattern; font->pattern = pattern;
font->ascent = xfont->ascent; font->h = xfont->ascent + xfont->descent;
font->descent = xfont->descent;
font->h = font->ascent + font->descent;
font->dpy = drw->dpy; font->dpy = drw->dpy;
return font; return font;
} }
Fnt* static void
drw_font_create(Drw *drw, const char *fontname) xfont_free(Fnt *font)
{
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) if (!font)
return; return;
@ -179,55 +152,98 @@ drw_font_free(Fnt *font)
free(font); free(font);
} }
Clr * Fnt*
drw_clr_create(Drw *drw, const char *clrname) drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
{ {
Clr *clr; Fnt *cur, *ret = NULL;
size_t i;
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_fontset_free(Fnt *font)
{
if (font) {
drw_fontset_free(font->next);
xfont_free(font);
}
}
void
drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
{
if (!drw || !dest || !clrname)
return;
clr = ecalloc(1, sizeof(Clr));
if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen), DefaultColormap(drw->dpy, drw->screen),
clrname, &clr->rgb)) clrname, dest))
die("error, cannot allocate color '%s'\n", clrname); die("error, cannot allocate color '%s'\n", clrname);
clr->pix = clr->rgb.pixel; }
return clr; /* 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;
/* 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 void
drw_clr_free(Clr *clr) drw_setfontset(Drw *drw, Fnt *set)
{ {
free(clr); if (drw)
drw->fonts = set;
} }
void void
drw_setscheme(Drw *drw, ClrScheme *scheme) drw_setscheme(Drw *drw, Clr *scm)
{ {
drw->scheme = scheme; if (drw)
drw->scheme = scm;
} }
void 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; 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) if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
else if (empty) else
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
} }
int 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]; char buf[1024];
int tx, ty, th; int ty;
Extnts tex; unsigned int ew;
XftDraw *d = NULL; XftDraw *d = NULL;
Fnt *curfont, *nextfont; Fnt *usedfont, *curfont, *nextfont;
size_t i, len; size_t i, len;
int utf8strlen, utf8charlen, render; int utf8strlen, utf8charlen, render = x || y || w || h;
long utf8codepoint = 0; long utf8codepoint = 0;
const char *utf8str; const char *utf8str;
FcCharSet *fccharset; 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; XftResult result;
int charexists = 0; int charexists = 0;
if (!drw->scheme || !drw->fontcount) if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
return 0; return 0;
if (!(render = x || y || w || h)) { if (!render) {
w = ~w; w = ~w;
} else { } else {
XSetForeground(drw->dpy, drw->gc, invert ? XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
drw->scheme->fg->pix : drw->scheme->bg->pix);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable, d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen), DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen)); DefaultColormap(drw->dpy, drw->screen));
x += lpad;
w -= lpad;
} }
curfont = drw->fonts[0]; usedfont = drw->fonts;
while (1) { while (1) {
utf8strlen = 0; utf8strlen = 0;
utf8str = text; utf8str = text;
nextfont = NULL; nextfont = NULL;
while (*text) { while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
for (i = 0; i < drw->fontcount; i++) { for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint); charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) { if (charexists) {
if (drw->fonts[i] == curfont) { if (curfont == usedfont) {
utf8strlen += utf8charlen; utf8strlen += utf8charlen;
text += utf8charlen; text += utf8charlen;
} else { } else {
nextfont = drw->fonts[i]; nextfont = curfont;
} }
break; break;
} }
} }
if (!charexists || (nextfont && nextfont != curfont)) if (!charexists || nextfont)
break; break;
else else
charexists = 0; charexists = 0;
} }
if (utf8strlen) { if (utf8strlen) {
drw_font_getexts(curfont, utf8str, utf8strlen, &tex); drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
/* shorten text if necessary */ /* 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--) for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(curfont, utf8str, len, &tex); drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
if (len) { if (len) {
memcpy(buf, utf8str, len); memcpy(buf, utf8str, len);
buf[len] = '\0'; buf[len] = '\0';
if (len < utf8strlen) if (len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.'); for (i = len; i && i > len - 3; buf[--i] = '.')
; /* NOP */
if (render) { if (render) {
th = curfont->ascent + curfont->descent; ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
ty = y + (h / 2) - (th / 2) + curfont->ascent; XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
tx = x + (h / 2); usedfont->xfont, x, ty, (XftChar8 *)buf, len);
XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len);
} }
x += tex.w; x += ew;
w -= tex.w; w -= ew;
} }
} }
@ -303,26 +320,21 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *tex
break; break;
} else if (nextfont) { } else if (nextfont) {
charexists = 0; charexists = 0;
curfont = nextfont; usedfont = nextfont;
} else { } else {
/* Regardless of whether or not a fallback font is found, the /* Regardless of whether or not a fallback font is found, the
* character must be drawn. * character must be drawn. */
*/
charexists = 1; charexists = 1;
if (drw->fontcount >= DRW_FONT_CACHE_SIZE)
continue;
fccharset = FcCharSetCreate(); fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint); FcCharSetAddChar(fccharset, utf8codepoint);
if (!drw->fonts[0]->pattern) { if (!drw->fonts->pattern) {
/* Refer to the comment in drw_font_xcreate for more /* Refer to the comment in xfont_create for more information. */
* information. */
die("the first font in the cache must be loaded from a font string.\n"); 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); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); 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); FcPatternDestroy(fcpattern);
if (match) { if (match) {
curfont = drw_font_xcreate(drw, NULL, match); usedfont = xfont_create(drw, NULL, match);
if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) { if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
drw->fonts[drw->fontcount++] = curfont; for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
; /* NOP */
curfont->next = usedfont;
} else { } else {
drw_font_free(curfont); xfont_free(usedfont);
curfont = drw->fonts[0]; 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) if (d)
XftDrawDestroy(d); XftDrawDestroy(d);
return x; return x + (render ? w : 0);
} }
void void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) 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); XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False); XSync(drw->dpy, False);
} }
unsigned int
drw_fontset_getwidth(Drw *drw, const char *text)
{
if (!drw || !drw->fonts || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
}
void void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex) drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{ {
XGlyphInfo ext; XGlyphInfo ext;
if (!font || !text)
return;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
tex->h = font->h; if (w)
tex->w = ext.xOff; *w = ext.xOff;
} if (h)
*h = font->h;
unsigned int
drw_font_getexts_width(Fnt *font, const char *text, unsigned int len)
{
Extnts tex;
drw_font_getexts(font, text, len, &tex);
return tex.w;
} }
Cur * Cur *
@ -382,7 +402,9 @@ drw_cur_create(Drw *drw, int shape)
{ {
Cur *cur; Cur *cur;
cur = ecalloc(1, sizeof(Cur)); if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape); cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur; return cur;
@ -393,6 +415,7 @@ drw_cur_free(Drw *drw, Cur *cursor)
{ {
if (!cursor) if (!cursor)
return; return;
XFreeCursor(drw->dpy, cursor->cursor); XFreeCursor(drw->dpy, cursor->cursor);
free(cursor); free(cursor);
} }

63
drw.h
View File

@ -1,29 +1,19 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#define DRW_FONT_CACHE_SIZE 32
typedef struct {
unsigned long pix;
XftColor rgb;
} Clr;
typedef struct { typedef struct {
Cursor cursor; Cursor cursor;
} Cur; } Cur;
typedef struct { typedef struct Fnt {
Display *dpy; Display *dpy;
int ascent;
int descent;
unsigned int h; unsigned int h;
XftFont *xfont; XftFont *xfont;
FcPattern *pattern; FcPattern *pattern;
struct Fnt *next;
} Fnt; } Fnt;
typedef struct { enum { ColFg, ColBg }; /* Clr scheme index */
Clr *fg; typedef XftColor Clr;
Clr *bg;
Clr *border;
} ClrScheme;
typedef struct { typedef struct {
unsigned int w, h; unsigned int w, h;
@ -32,43 +22,36 @@ typedef struct {
Window root; Window root;
Drawable drawable; Drawable drawable;
GC gc; GC gc;
ClrScheme *scheme; Clr *scheme;
size_t fontcount; Fnt *fonts;
Fnt *fonts[DRW_FONT_CACHE_SIZE];
} Drw; } Drw;
typedef struct {
unsigned int w;
unsigned int h;
} Extnts;
/* Drawable abstraction */ /* Drawable abstraction */
Drw *drw_create(Display *, int, Window, unsigned int, unsigned int); Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
void drw_resize(Drw *, unsigned int, unsigned int); void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *); void drw_free(Drw *drw);
/* Fnt abstraction */ /* Fnt abstraction */
Fnt *drw_font_create(Drw *, const char *); Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
void drw_load_fonts(Drw *, const char *[], size_t); void drw_fontset_free(Fnt* set);
void drw_font_free(Fnt *); unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
void drw_font_getexts(Fnt *, const char *, unsigned int, Extnts *); void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
unsigned int drw_font_getexts_width(Fnt *, const char *, unsigned int);
/* Colour abstraction */ /* Colorscheme abstraction */
Clr *drw_clr_create(Drw *, const char *); void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
void drw_clr_free(Clr *); Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
/* Cursor abstraction */ /* Cursor abstraction */
Cur *drw_cur_create(Drw *, int); Cur *drw_cur_create(Drw *drw, int shape);
void drw_cur_free(Drw *, Cur *); void drw_cur_free(Drw *drw, Cur *cursor);
/* Drawing context manipulation */ /* Drawing context manipulation */
void drw_setfont(Drw *, Fnt *); void drw_setfontset(Drw *drw, Fnt *set);
void drw_setscheme(Drw *, ClrScheme *); void drw_setscheme(Drw *drw, Clr *scm);
/* Drawing functions */ /* Drawing functions */
void drw_rect(Drw *, int, int, unsigned int, unsigned int, int, int, int); void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *, int, int, unsigned int, unsigned int, const char *, int); 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 */ /* 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);

4
util.h
View File

@ -4,5 +4,5 @@
#define MIN(A, B) ((A) < (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
void die(const char *errstr, ...); void die(const char *fmt, ...);
void *ecalloc(size_t, size_t); void *ecalloc(size_t nmemb, size_t size);