aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkus Teich <markus.teich@stusta.mhn.de>2016-05-21 21:51:14 +0200
committerHiltjo Posthuma <hiltjo@codemadness.org>2016-06-03 19:13:15 +0200
commit44c7de3dcf49ee568863f55610f40c7a05b4dfe7 (patch)
tree1a3e45f6e616ff256a0d195272d68ca69f5c153f
parentb3d9451c2ddfad7c1b10e9a868afed4d92b37e41 (diff)
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)
-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);