import new drw from libsl and minor fixes. 44c7de3d
- 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)
Markus Teich · 2016-05-21 21:51 5 file(s) · +231 −236
config.def.h +6 −6
7 7
	"monospace:size=10"
8 8
};
9 9
static const char *prompt      = NULL;      /* -p  option; prompt to the left of input field */
10 -
static const char *normbgcolor = "#222222"; /* -nb option; normal background                 */
11 -
static const char *normfgcolor = "#bbbbbb"; /* -nf option; normal foreground                 */
12 -
static const char *selbgcolor  = "#005577"; /* -sb option; selected background               */
13 -
static const char *selfgcolor  = "#eeeeee"; /* -sf option; selected foreground               */
14 -
static const char *outbgcolor  = "#00ffff";
15 -
static const char *outfgcolor  = "#000000";
10 +
static const char *colors[][2] = {
11 +
/*     fg         bg       */
12 +
	{ "#bbbbbb", "#222222" }, /* normal */
13 +
	{ "#eeeeee", "#005577" }, /* selected */
14 +
	{ "#000000", "#00ffff" }, /* out */
15 +
};
16 16
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
17 17
static unsigned int lines      = 0;
18 18
dmenu.c +68 −79
22 22
#define INTERSECT(x,y,w,h,r)  (MAX(0, MIN((x)+(w),(r).x_org+(r).width)  - MAX((x),(r).x_org)) \
23 23
                             * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
24 24
#define LENGTH(X)             (sizeof X / sizeof X[0])
25 -
#define TEXTNW(X,N)           (drw_font_getexts_width(drw->fonts[0], (X), (N)))
26 -
#define TEXTW(X)              (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
25 +
#define TEXTW(X)              (drw_fontset_getwidth(drw, (X)) + lrpad)
27 26
28 27
/* enums */
29 28
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
37 36
static char text[BUFSIZ] = "";
38 37
static int bh, mw, mh;
39 38
static int sw, sh; /* X display screen geometry width, height */
40 -
static int inputw, promptw;
39 +
static int inputw = 0, promptw;
40 +
static int lrpad; /* sum of left and right padding */
41 41
static size_t cursor;
42 42
static struct item *items = NULL;
43 43
static struct item *matches, *matchend;
49 49
static Window root, win;
50 50
static XIC xic;
51 51
52 -
static ClrScheme scheme[SchemeLast];
53 52
static Drw *drw;
53 +
static Clr *scheme[SchemeLast];
54 54
55 55
#include "config.h"
56 56
81 81
		n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
82 82
	/* calculate which items will begin the next page and previous page */
83 83
	for (i = 0, next = curr; next; next = next->right)
84 -
		if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n)
84 +
		if ((i += (lines > 0) ? bh : TEXTW(next->text)) > n)
85 85
			break;
86 86
	for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
87 -
		if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n)
87 +
		if ((i += (lines > 0) ? bh : TEXTW(prev->left->text)) > n)
88 88
			break;
89 89
}
90 90
94 94
	size_t i;
95 95
96 96
	XUngrabKey(dpy, AnyKey, AnyModifier, root);
97 -
	for (i = 0; i < SchemeLast; i++) {
98 -
		drw_clr_free(scheme[i].bg);
99 -
		drw_clr_free(scheme[i].fg);
100 -
	}
97 +
	for (i = 0; i < SchemeLast; i++)
98 +
		free(scheme[i]);
101 99
	drw_free(drw);
102 100
	XSync(dpy, False);
103 101
	XCloseDisplay(dpy);
114 112
	return NULL;
115 113
}
116 114
115 +
static int
116 +
drawitem(struct item *item, int x, int y, int w)
117 +
{
118 +
	if (item == sel)
119 +
		drw_setscheme(drw, scheme[SchemeSel]);
120 +
	else if (item->out)
121 +
		drw_setscheme(drw, scheme[SchemeOut]);
122 +
	else
123 +
		drw_setscheme(drw, scheme[SchemeNorm]);
124 +
125 +
	return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
126 +
}
127 +
117 128
static void
118 129
drawmenu(void)
119 130
{
120 -
	int curpos;
131 +
	unsigned int curpos;
121 132
	struct item *item;
122 -
	int x = 0, y = 0, h = bh, w;
133 +
	int x = 0, y = 0, w;
123 134
124 -
	drw_setscheme(drw, &scheme[SchemeNorm]);
125 -
	drw_rect(drw, 0, 0, mw, mh, 1, 1, 1);
135 +
	drw_setscheme(drw, scheme[SchemeNorm]);
136 +
	drw_rect(drw, 0, 0, mw, mh, 1, 1);
126 137
127 138
	if (prompt && *prompt) {
128 -
		drw_setscheme(drw, &scheme[SchemeSel]);
129 -
		drw_text(drw, x, 0, promptw, bh, prompt, 0);
130 -
		x += promptw;
139 +
		drw_setscheme(drw, scheme[SchemeSel]);
140 +
		x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
131 141
	}
132 142
	/* draw input field */
133 143
	w = (lines > 0 || !matches) ? mw - x : inputw;
134 -
	drw_setscheme(drw, &scheme[SchemeNorm]);
135 -
	drw_text(drw, x, 0, w, bh, text, 0);
144 +
	drw_setscheme(drw, scheme[SchemeNorm]);
145 +
	drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
136 146
137 -
	if ((curpos = TEXTNW(text, cursor) + bh / 2 - 2) < w) {
138 -
		drw_setscheme(drw, &scheme[SchemeNorm]);
139 -
		drw_rect(drw, x + curpos + 2, 2, 1, bh - 4, 1, 1, 0);
147 +
	drw_font_getexts(drw->fonts, text, cursor, &curpos, NULL);
148 +
	if ((curpos += lrpad / 2 - 1) < w) {
149 +
		drw_setscheme(drw, scheme[SchemeNorm]);
150 +
		drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
140 151
	}
141 152
142 153
	if (lines > 0) {
143 154
		/* draw vertical list */
144 -
		w = mw - x;
145 -
		for (item = curr; item != next; item = item->right) {
146 -
			y += h;
147 -
			if (item == sel)
148 -
				drw_setscheme(drw, &scheme[SchemeSel]);
149 -
			else if (item->out)
150 -
				drw_setscheme(drw, &scheme[SchemeOut]);
151 -
			else
152 -
				drw_setscheme(drw, &scheme[SchemeNorm]);
153 -
154 -
			drw_text(drw, x, y, w, bh, item->text, 0);
155 -
		}
155 +
		for (item = curr; item != next; item = item->right)
156 +
			drawitem(item, x, y += bh, mw - x);
156 157
	} else if (matches) {
157 158
		/* draw horizontal list */
158 159
		x += inputw;
159 160
		w = TEXTW("<");
160 161
		if (curr->left) {
161 -
			drw_setscheme(drw, &scheme[SchemeNorm]);
162 -
			drw_text(drw, x, 0, w, bh, "<", 0);
162 +
			drw_setscheme(drw, scheme[SchemeNorm]);
163 +
			drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
163 164
		}
164 -
		for (item = curr; item != next; item = item->right) {
165 -
			x += w;
166 -
			w = MIN(TEXTW(item->text), mw - x - TEXTW(">"));
167 -
168 -
			if (item == sel)
169 -
				drw_setscheme(drw, &scheme[SchemeSel]);
170 -
			else if (item->out)
171 -
				drw_setscheme(drw, &scheme[SchemeOut]);
172 -
			else
173 -
				drw_setscheme(drw, &scheme[SchemeNorm]);
174 -
			drw_text(drw, x, 0, w, bh, item->text, 0);
175 -
		}
176 -
		w = TEXTW(">");
177 -
		x = mw - w;
165 +
		x += w;
166 +
		for (item = curr; item != next; item = item->right)
167 +
			x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">")));
178 168
		if (next) {
179 -
			drw_setscheme(drw, &scheme[SchemeNorm]);
180 -
			drw_text(drw, x, 0, w, bh, ">", 0);
169 +
			w = TEXTW(">");
170 +
			drw_setscheme(drw, scheme[SchemeNorm]);
171 +
			drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0);
181 172
		}
182 173
	}
183 174
	drw_map(drw, win, 0, 0, mw, mh);
191 182
192 183
	/* try to grab keyboard, we may have to wait for another process to ungrab */
193 184
	for (i = 0; i < 1000; i++) {
194 -
		if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True,
195 -
		                 GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
185 +
		if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
186 +
		                  GrabModeAsync, CurrentTime) == GrabSuccess)
196 187
			return;
197 188
		nanosleep(&ts, NULL);
198 189
	}
314 305
			insert(NULL, 0 - cursor);
315 306
			break;
316 307
		case XK_w: /* delete word */
317 -
			while (cursor > 0 && strchr(worddelimiters,
318 -
			       text[nextrune(-1)]))
308 +
			while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
319 309
				insert(NULL, nextrune(-1) - cursor);
320 -
			while (cursor > 0 && !strchr(worddelimiters,
321 -
			       text[nextrune(-1)]))
310 +
			while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
322 311
				insert(NULL, nextrune(-1) - cursor);
323 312
			break;
324 313
		case XK_y: /* paste selection */
469 458
static void
470 459
readstdin(void)
471 460
{
472 -
	char buf[sizeof text], *p, *maxstr = NULL;
473 -
	size_t i, max = 0, size = 0;
461 +
	char buf[sizeof text], *p;
462 +
	size_t i, imax = 0, size = 0;
463 +
	unsigned int tmpmax = 0;
474 464
475 465
	/* read each line from stdin and add it to the item list */
476 466
	for (i = 0; fgets(buf, sizeof buf, stdin); i++) {
482 472
		if (!(items[i].text = strdup(buf)))
483 473
			die("cannot strdup %u bytes:", strlen(buf) + 1);
484 474
		items[i].out = 0;
485 -
		if (strlen(items[i].text) > max)
486 -
			max = strlen(maxstr = items[i].text);
475 +
		drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL);
476 +
		if (tmpmax > inputw) {
477 +
			inputw = tmpmax;
478 +
			imax = i;
479 +
		}
487 480
	}
488 481
	if (items)
489 482
		items[i].text = NULL;
490 -
	inputw = maxstr ? TEXTW(maxstr) : 0;
483 +
	inputw = TEXTW(items[imax].text);
491 484
	lines = MIN(lines, i);
492 485
}
493 486
534 527
#endif
535 528
536 529
	/* init appearance */
537 -
	scheme[SchemeNorm].bg = drw_clr_create(drw, normbgcolor);
538 -
	scheme[SchemeNorm].fg = drw_clr_create(drw, normfgcolor);
539 -
	scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor);
540 -
	scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor);
541 -
	scheme[SchemeOut].bg = drw_clr_create(drw, outbgcolor);
542 -
	scheme[SchemeOut].fg = drw_clr_create(drw, outfgcolor);
530 +
	scheme[SchemeNorm] = drw_scm_create(drw, colors[SchemeNorm], 2);
531 +
	scheme[SchemeSel] = drw_scm_create(drw, colors[SchemeSel], 2);
532 +
	scheme[SchemeOut] = drw_scm_create(drw, colors[SchemeOut], 2);
543 533
544 534
	clip = XInternAtom(dpy, "CLIPBOARD",   False);
545 535
	utf8 = XInternAtom(dpy, "UTF8_STRING", False);
546 536
547 537
	/* calculate menu geometry */
548 -
	bh = drw->fonts[0]->h + 2;
538 +
	bh = drw->fonts->h + 2;
549 539
	lines = MAX(lines, 0);
550 540
	mh = (lines + 1) * bh;
551 541
#ifdef XINERAMA
584 574
		y = topbar ? 0 : sh - mh;
585 575
		mw = sw;
586 576
	}
587 -
	promptw = (prompt && *prompt) ? TEXTW(prompt) : 0;
577 +
	promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
588 578
	inputw = MIN(inputw, mw/3);
589 579
	match();
590 580
591 581
	/* create menu window */
592 582
	swa.override_redirect = True;
593 -
	swa.background_pixel = scheme[SchemeNorm].bg->pix;
583 +
	swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
594 584
	swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
595 585
	win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
596 586
	                    DefaultDepth(dpy, screen), CopyFromParent,
644 634
		else if (!strcmp(argv[i], "-fn"))  /* font or font set */
645 635
			fonts[0] = argv[++i];
646 636
		else if (!strcmp(argv[i], "-nb"))  /* normal background color */
647 -
			normbgcolor = argv[++i];
637 +
			colors[SchemeNorm][ColBg] = argv[++i];
648 638
		else if (!strcmp(argv[i], "-nf"))  /* normal foreground color */
649 -
			normfgcolor = argv[++i];
639 +
			colors[SchemeNorm][ColFg] = argv[++i];
650 640
		else if (!strcmp(argv[i], "-sb"))  /* selected background color */
651 -
			selbgcolor = argv[++i];
641 +
			colors[SchemeSel][ColBg] = argv[++i];
652 642
		else if (!strcmp(argv[i], "-sf"))  /* selected foreground color */
653 -
			selfgcolor = argv[++i];
643 +
			colors[SchemeSel][ColFg] = argv[++i];
654 644
		else
655 645
			usage();
656 646
663 653
	sw = DisplayWidth(dpy, screen);
664 654
	sh = DisplayHeight(dpy, screen);
665 655
	drw = drw_create(dpy, screen, root, sw, sh);
666 -
	drw_load_fonts(drw, fonts, LENGTH(fonts));
667 -
	if (!drw->fontcount)
656 +
	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
668 657
		die("no fonts could be loaded.\n");
669 -
	drw_setscheme(drw, &scheme[SchemeNorm]);
658 +
	lrpad = drw->fonts->h;
670 659
671 660
	if (fast) {
672 661
		grabkeyboard();
drw.c +132 −109
63 63
Drw *
64 64
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
65 65
{
66 -
	Drw *drw;
66 +
	Drw *drw = ecalloc(1, sizeof(Drw));
67 67
68 -
	drw = ecalloc(1, sizeof(Drw));
69 68
	drw->dpy = dpy;
70 69
	drw->screen = screen;
71 70
	drw->root = root;
73 72
	drw->h = h;
74 73
	drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
75 74
	drw->gc = XCreateGC(dpy, root, 0, NULL);
76 -
	drw->fontcount = 0;
77 75
	XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
78 76
79 77
	return drw;
82 80
void
83 81
drw_resize(Drw *drw, unsigned int w, unsigned int h)
84 82
{
83 +
	if (!drw)
84 +
		return;
85 +
85 86
	drw->w = w;
86 87
	drw->h = h;
87 88
	if (drw->drawable)
92 93
void
93 94
drw_free(Drw *drw)
94 95
{
95 -
	size_t i;
96 -
97 -
	for (i = 0; i < drw->fontcount; i++)
98 -
		drw_font_free(drw->fonts[i]);
99 96
	XFreePixmap(drw->dpy, drw->drawable);
100 97
	XFreeGC(drw->dpy, drw->gc);
101 98
	free(drw);
102 99
}
103 100
104 101
/* This function is an implementation detail. Library users should use
105 -
 * drw_font_create instead.
102 +
 * drw_fontset_create instead.
106 103
 */
107 104
static Fnt *
108 -
drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern)
105 +
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
109 106
{
110 107
	Fnt *font;
111 108
	XftFont *xfont = NULL;
112 109
	FcPattern *pattern = NULL;
113 110
114 111
	if (fontname) {
115 -
		/* Using the pattern found at font->xfont->pattern does not yield same
116 -
		 * the same substitution results as using the pattern returned by
112 +
		/* Using the pattern found at font->xfont->pattern does not yield the
113 +
		 * same substitution results as using the pattern returned by
117 114
		 * FcNameParse; using the latter results in the desired fallback
118 -
		 * behaviour whereas the former just results in
119 -
		 * missing-character-rectangles being drawn, at least with some fonts.
120 -
		 */
115 +
		 * behaviour whereas the former just results in missing-character
116 +
		 * rectangles being drawn, at least with some fonts. */
121 117
		if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
122 -
			fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
118 +
			fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
123 119
			return NULL;
124 120
		}
125 121
		if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
126 -
			fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
122 +
			fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
127 123
			XftFontClose(drw->dpy, xfont);
128 124
			return NULL;
129 125
		}
130 126
	} else if (fontpattern) {
131 127
		if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
132 -
			fprintf(stderr, "error, cannot load font pattern.\n");
128 +
			fprintf(stderr, "error, cannot load font from pattern.\n");
133 129
			return NULL;
134 130
		}
135 131
	} else {
139 135
	font = ecalloc(1, sizeof(Fnt));
140 136
	font->xfont = xfont;
141 137
	font->pattern = pattern;
142 -
	font->ascent = xfont->ascent;
143 -
	font->descent = xfont->descent;
144 -
	font->h = font->ascent + font->descent;
138 +
	font->h = xfont->ascent + xfont->descent;
145 139
	font->dpy = drw->dpy;
146 140
147 141
	return font;
148 142
}
149 143
150 -
Fnt*
151 -
drw_font_create(Drw *drw, const char *fontname)
144 +
static void
145 +
xfont_free(Fnt *font)
152 146
{
153 -
	return drw_font_xcreate(drw, fontname, NULL);
147 +
	if (!font)
148 +
		return;
149 +
	if (font->pattern)
150 +
		FcPatternDestroy(font->pattern);
151 +
	XftFontClose(font->dpy, font->xfont);
152 +
	free(font);
154 153
}
155 154
156 -
void
157 -
drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount)
155 +
Fnt*
156 +
drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
158 157
{
158 +
	Fnt *cur, *ret = NULL;
159 159
	size_t i;
160 -
	Fnt *font;
160 +
161 +
	if (!drw || !fonts)
162 +
		return NULL;
161 163
162 -
	for (i = 0; i < fontcount; i++) {
163 -
		if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
164 -
			die("Font cache exhausted.\n");
165 -
		} else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) {
166 -
			drw->fonts[drw->fontcount++] = font;
164 +
	for (i = 1; i <= fontcount; i++) {
165 +
		if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
166 +
			cur->next = ret;
167 +
			ret = cur;
167 168
		}
168 169
	}
170 +
	return (drw->fonts = ret);
169 171
}
170 172
171 173
void
172 -
drw_font_free(Fnt *font)
174 +
drw_fontset_free(Fnt *font)
173 175
{
174 -
	if (!font)
175 -
		return;
176 -
	if (font->pattern)
177 -
		FcPatternDestroy(font->pattern);
178 -
	XftFontClose(font->dpy, font->xfont);
179 -
	free(font);
176 +
	if (font) {
177 +
		drw_fontset_free(font->next);
178 +
		xfont_free(font);
179 +
	}
180 180
}
181 181
182 -
Clr *
183 -
drw_clr_create(Drw *drw, const char *clrname)
182 +
void
183 +
drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
184 184
{
185 -
	Clr *clr;
185 +
	if (!drw || !dest || !clrname)
186 +
		return;
186 187
187 -
	clr = ecalloc(1, sizeof(Clr));
188 188
	if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
189 189
	                       DefaultColormap(drw->dpy, drw->screen),
190 -
	                       clrname, &clr->rgb))
190 +
	                       clrname, dest))
191 191
		die("error, cannot allocate color '%s'\n", clrname);
192 -
	clr->pix = clr->rgb.pixel;
192 +
}
193 193
194 -
	return clr;
194 +
/* Wrapper to create color schemes. The caller has to call free(3) on the
195 +
 * returned color scheme when done using it. */
196 +
Clr *
197 +
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
198 +
{
199 +
	size_t i;
200 +
	Clr *ret;
201 +
202 +
	/* need at least two colors for a scheme */
203 +
	if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
204 +
		return NULL;
205 +
206 +
	for (i = 0; i < clrcount; i++)
207 +
		drw_clr_create(drw, &ret[i], clrnames[i]);
208 +
	return ret;
195 209
}
196 210
197 211
void
198 -
drw_clr_free(Clr *clr)
212 +
drw_setfontset(Drw *drw, Fnt *set)
199 213
{
200 -
	free(clr);
214 +
	if (drw)
215 +
		drw->fonts = set;
201 216
}
202 217
203 218
void
204 -
drw_setscheme(Drw *drw, ClrScheme *scheme)
219 +
drw_setscheme(Drw *drw, Clr *scm)
205 220
{
206 -
	drw->scheme = scheme;
221 +
	if (drw)
222 +
		drw->scheme = scm;
207 223
}
208 224
209 225
void
210 -
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert)
226 +
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
211 227
{
212 -
	if (!drw->scheme)
228 +
	if (!drw || !drw->scheme)
213 229
		return;
214 -
	XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix);
230 +
	XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
215 231
	if (filled)
216 -
		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1);
217 -
	else if (empty)
218 -
		XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
232 +
		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
233 +
	else
234 +
		XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
219 235
}
220 236
221 237
int
222 -
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert)
238 +
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
223 239
{
224 240
	char buf[1024];
225 -
	int tx, ty, th;
226 -
	Extnts tex;
241 +
	int ty;
242 +
	unsigned int ew;
227 243
	XftDraw *d = NULL;
228 -
	Fnt *curfont, *nextfont;
244 +
	Fnt *usedfont, *curfont, *nextfont;
229 245
	size_t i, len;
230 -
	int utf8strlen, utf8charlen, render;
246 +
	int utf8strlen, utf8charlen, render = x || y || w || h;
231 247
	long utf8codepoint = 0;
232 248
	const char *utf8str;
233 249
	FcCharSet *fccharset;
236 252
	XftResult result;
237 253
	int charexists = 0;
238 254
239 -
	if (!drw->scheme || !drw->fontcount)
255 +
	if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
240 256
		return 0;
241 257
242 -
	if (!(render = x || y || w || h)) {
258 +
	if (!render) {
243 259
		w = ~w;
244 260
	} else {
245 -
		XSetForeground(drw->dpy, drw->gc, invert ?
246 -
		               drw->scheme->fg->pix : drw->scheme->bg->pix);
261 +
		XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
247 262
		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
248 263
		d = XftDrawCreate(drw->dpy, drw->drawable,
249 264
		                  DefaultVisual(drw->dpy, drw->screen),
250 265
		                  DefaultColormap(drw->dpy, drw->screen));
266 +
		x += lpad;
267 +
		w -= lpad;
251 268
	}
252 269
253 -
	curfont = drw->fonts[0];
270 +
	usedfont = drw->fonts;
254 271
	while (1) {
255 272
		utf8strlen = 0;
256 273
		utf8str = text;
257 274
		nextfont = NULL;
258 275
		while (*text) {
259 276
			utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
260 -
			for (i = 0; i < drw->fontcount; i++) {
261 -
				charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint);
277 +
			for (curfont = drw->fonts; curfont; curfont = curfont->next) {
278 +
				charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
262 279
				if (charexists) {
263 -
					if (drw->fonts[i] == curfont) {
280 +
					if (curfont == usedfont) {
264 281
						utf8strlen += utf8charlen;
265 282
						text += utf8charlen;
266 283
					} else {
267 -
						nextfont = drw->fonts[i];
284 +
						nextfont = curfont;
268 285
					}
269 286
					break;
270 287
				}
271 288
			}
272 289
273 -
			if (!charexists || (nextfont && nextfont != curfont))
290 +
			if (!charexists || nextfont)
274 291
				break;
275 292
			else
276 293
				charexists = 0;
277 294
		}
278 295
279 296
		if (utf8strlen) {
280 -
			drw_font_getexts(curfont, utf8str, utf8strlen, &tex);
297 +
			drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
281 298
			/* shorten text if necessary */
282 -
			for (len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--)
283 -
				drw_font_getexts(curfont, utf8str, len, &tex);
299 +
			for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
300 +
				drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
284 301
285 302
			if (len) {
286 303
				memcpy(buf, utf8str, len);
287 304
				buf[len] = '\0';
288 305
				if (len < utf8strlen)
289 -
					for (i = len; i && i > len - 3; buf[--i] = '.');
306 +
					for (i = len; i && i > len - 3; buf[--i] = '.')
307 +
						; /* NOP */
290 308
291 309
				if (render) {
292 -
					th = curfont->ascent + curfont->descent;
293 -
					ty = y + (h / 2) - (th / 2) + curfont->ascent;
294 -
					tx = x + (h / 2);
295 -
					XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len);
310 +
					ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
311 +
					XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
312 +
					                  usedfont->xfont, x, ty, (XftChar8 *)buf, len);
296 313
				}
297 -
				x += tex.w;
298 -
				w -= tex.w;
314 +
				x += ew;
315 +
				w -= ew;
299 316
			}
300 317
		}
301 318
303 320
			break;
304 321
		} else if (nextfont) {
305 322
			charexists = 0;
306 -
			curfont = nextfont;
323 +
			usedfont = nextfont;
307 324
		} else {
308 325
			/* Regardless of whether or not a fallback font is found, the
309 -
			 * character must be drawn.
310 -
			 */
326 +
			 * character must be drawn. */
311 327
			charexists = 1;
312 -
313 -
			if (drw->fontcount >= DRW_FONT_CACHE_SIZE)
314 -
				continue;
315 328
316 329
			fccharset = FcCharSetCreate();
317 330
			FcCharSetAddChar(fccharset, utf8codepoint);
318 331
319 -
			if (!drw->fonts[0]->pattern) {
320 -
				/* Refer to the comment in drw_font_xcreate for more
321 -
				 * information. */
332 +
			if (!drw->fonts->pattern) {
333 +
				/* Refer to the comment in xfont_create for more information. */
322 334
				die("the first font in the cache must be loaded from a font string.\n");
323 335
			}
324 336
325 -
			fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern);
337 +
			fcpattern = FcPatternDuplicate(drw->fonts->pattern);
326 338
			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
327 339
			FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
328 340
334 346
			FcPatternDestroy(fcpattern);
335 347
336 348
			if (match) {
337 -
				curfont = drw_font_xcreate(drw, NULL, match);
338 -
				if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) {
339 -
					drw->fonts[drw->fontcount++] = curfont;
349 +
				usedfont = xfont_create(drw, NULL, match);
350 +
				if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
351 +
					for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
352 +
						; /* NOP */
353 +
					curfont->next = usedfont;
340 354
				} else {
341 -
					drw_font_free(curfont);
342 -
					curfont = drw->fonts[0];
355 +
					xfont_free(usedfont);
356 +
					usedfont = drw->fonts;
343 357
				}
344 358
			}
345 359
		}
347 361
	if (d)
348 362
		XftDrawDestroy(d);
349 363
350 -
	return x;
364 +
	return x + (render ? w : 0);
351 365
}
352 366
353 367
void
354 368
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
355 369
{
370 +
	if (!drw)
371 +
		return;
372 +
356 373
	XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
357 374
	XSync(drw->dpy, False);
358 375
}
359 376
360 -
void
361 -
drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex)
377 +
unsigned int
378 +
drw_fontset_getwidth(Drw *drw, const char *text)
362 379
{
363 -
	XGlyphInfo ext;
364 -
365 -
	XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
366 -
	tex->h = font->h;
367 -
	tex->w = ext.xOff;
380 +
	if (!drw || !drw->fonts || !text)
381 +
		return 0;
382 +
	return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
368 383
}
369 384
370 -
unsigned int
371 -
drw_font_getexts_width(Fnt *font, const char *text, unsigned int len)
385 +
void
386 +
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
372 387
{
373 -
	Extnts tex;
388 +
	XGlyphInfo ext;
374 389
375 -
	drw_font_getexts(font, text, len, &tex);
390 +
	if (!font || !text)
391 +
		return;
376 392
377 -
	return tex.w;
393 +
	XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
394 +
	if (w)
395 +
		*w = ext.xOff;
396 +
	if (h)
397 +
		*h = font->h;
378 398
}
379 399
380 400
Cur *
382 402
{
383 403
	Cur *cur;
384 404
385 -
	cur = ecalloc(1, sizeof(Cur));
405 +
	if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
406 +
		return NULL;
407 +
386 408
	cur->cursor = XCreateFontCursor(drw->dpy, shape);
387 409
388 410
	return cur;
393 415
{
394 416
	if (!cursor)
395 417
		return;
418 +
396 419
	XFreeCursor(drw->dpy, cursor->cursor);
397 420
	free(cursor);
398 421
}
drw.h +23 −40
1 1
/* See LICENSE file for copyright and license details. */
2 -
#define DRW_FONT_CACHE_SIZE 32
3 -
4 -
typedef struct {
5 -
	unsigned long pix;
6 -
	XftColor rgb;
7 -
} Clr;
8 2
9 3
typedef struct {
10 4
	Cursor cursor;
11 5
} Cur;
12 6
13 -
typedef struct {
7 +
typedef struct Fnt {
14 8
	Display *dpy;
15 -
	int ascent;
16 -
	int descent;
17 9
	unsigned int h;
18 10
	XftFont *xfont;
19 11
	FcPattern *pattern;
12 +
	struct Fnt *next;
20 13
} Fnt;
21 14
22 -
typedef struct {
23 -
	Clr *fg;
24 -
	Clr *bg;
25 -
	Clr *border;
26 -
} ClrScheme;
15 +
enum { ColFg, ColBg }; /* Clr scheme index */
16 +
typedef XftColor Clr;
27 17
28 18
typedef struct {
29 19
	unsigned int w, h;
32 22
	Window root;
33 23
	Drawable drawable;
34 24
	GC gc;
35 -
	ClrScheme *scheme;
36 -
	size_t fontcount;
37 -
	Fnt *fonts[DRW_FONT_CACHE_SIZE];
25 +
	Clr *scheme;
26 +
	Fnt *fonts;
38 27
} Drw;
39 28
40 -
typedef struct {
41 -
	unsigned int w;
42 -
	unsigned int h;
43 -
} Extnts;
44 -
45 29
/* Drawable abstraction */
46 -
Drw *drw_create(Display *, int, Window, unsigned int, unsigned int);
47 -
void drw_resize(Drw *, unsigned int, unsigned int);
48 -
void drw_free(Drw *);
30 +
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
31 +
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
32 +
void drw_free(Drw *drw);
49 33
50 34
/* Fnt abstraction */
51 -
Fnt *drw_font_create(Drw *, const char *);
52 -
void drw_load_fonts(Drw *, const char *[], size_t);
53 -
void drw_font_free(Fnt *);
54 -
void drw_font_getexts(Fnt *, const char *, unsigned int, Extnts *);
55 -
unsigned int drw_font_getexts_width(Fnt *, const char *, unsigned int);
35 +
Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
36 +
void drw_fontset_free(Fnt* set);
37 +
unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
38 +
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
56 39
57 -
/* Colour abstraction */
58 -
Clr *drw_clr_create(Drw *, const char *);
59 -
void drw_clr_free(Clr *);
40 +
/* Colorscheme abstraction */
41 +
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
42 +
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
60 43
61 44
/* Cursor abstraction */
62 -
Cur *drw_cur_create(Drw *, int);
63 -
void drw_cur_free(Drw *, Cur *);
45 +
Cur *drw_cur_create(Drw *drw, int shape);
46 +
void drw_cur_free(Drw *drw, Cur *cursor);
64 47
65 48
/* Drawing context manipulation */
66 -
void drw_setfont(Drw *, Fnt *);
67 -
void drw_setscheme(Drw *, ClrScheme *);
49 +
void drw_setfontset(Drw *drw, Fnt *set);
50 +
void drw_setscheme(Drw *drw, Clr *scm);
68 51
69 52
/* Drawing functions */
70 -
void drw_rect(Drw *, int, int, unsigned int, unsigned int, int, int, int);
71 -
int drw_text(Drw *, int, int, unsigned int, unsigned int, const char *, int);
53 +
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
54 +
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
72 55
73 56
/* Map functions */
74 -
void drw_map(Drw *, Window, int, int, unsigned int, unsigned int);
57 +
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
util.h +2 −2
4 4
#define MIN(A, B)               ((A) < (B) ? (A) : (B))
5 5
#define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))
6 6
7 -
void die(const char *errstr, ...);
8 -
void *ecalloc(size_t, size_t);
7 +
void die(const char *fmt, ...);
8 +
void *ecalloc(size_t nmemb, size_t size);