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