aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dmenu.c587
1 files changed, 299 insertions, 288 deletions
diff --git a/dmenu.c b/dmenu.c
index b684175..34df9ed 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -40,134 +40,55 @@ struct Item {
40}; 40};
41 41
42/* forward declarations */ 42/* forward declarations */
43static void *emalloc(unsigned int size); 43void calcoffsets(void);
44static void eprint(const char *errstr, ...); 44void cleanup(void);
45static char *estrdup(const char *str); 45void drawmenu(void);
46static void drawtext(const char *text, unsigned long col[ColLast]); 46void drawtext(const char *text, unsigned long col[ColLast]);
47static unsigned int textw(const char *text); 47void *emalloc(unsigned int size);
48static unsigned int textnw(const char *text, unsigned int len); 48void eprint(const char *errstr, ...);
49static void calcoffsets(void); 49char *estrdup(const char *str);
50static void drawmenu(void); 50unsigned long getcolor(const char *colstr);
51static Bool grabkeyboard(void); 51Bool grabkeyboard(void);
52static unsigned long getcolor(const char *colstr); 52void initfont(const char *fontstr);
53static void initfont(const char *fontstr); 53void kpress(XKeyEvent * e);
54static int strido(const char *text, const char *pattern); 54void match(char *pattern);
55static void match(char *pattern); 55void readstdin(void);
56static void kpress(XKeyEvent * e); 56void run(void);
57static char *readstdin(void); 57void setup(Bool bottom);
58static void usage(void); 58int strido(const char *text, const char *pattern);
59 59unsigned int textnw(const char *text, unsigned int len);
60 60unsigned int textw(const char *text);
61/* variables */
62static int screen;
63static Display *dpy;
64static DC dc = {0};
65static char text[4096];
66static char *prompt = NULL;
67static int mw, mh;
68static int ret = 0;
69static int nitem = 0;
70static unsigned int cmdw = 0;
71static unsigned int promptw = 0;
72static unsigned int numlockmask = 0;
73static Bool running = True;
74static Item *allitems = NULL; /* first of all items */
75static Item *item = NULL; /* first of pattern matching items */
76static Item *sel = NULL;
77static Item *next = NULL;
78static Item *prev = NULL;
79static Item *curr = NULL;
80static Window root;
81static Window win;
82 61
83#include "config.h" 62#include "config.h"
84 63
85static void * 64/* variables */
86emalloc(unsigned int size) { 65char *font = FONT;
87 void *res = malloc(size); 66char *maxname = NULL;
88 67char *normbg = NORMBGCOLOR;
89 if(!res) 68char *normfg = NORMFGCOLOR;
90 eprint("fatal: could not malloc() %u bytes\n", size); 69char *prompt = NULL;
91 return res; 70char *selbg = SELBGCOLOR;
92} 71char *selfg = SELFGCOLOR;
93 72char text[4096];
94static void 73int screen;
95eprint(const char *errstr, ...) { 74int ret = 0;
96 va_list ap; 75unsigned int cmdw = 0;
97 76unsigned int mw, mh;
98 va_start(ap, errstr); 77unsigned int promptw = 0;
99 vfprintf(stderr, errstr, ap); 78unsigned int nitem = 0;
100 va_end(ap); 79unsigned int numlockmask = 0;
101 exit(EXIT_FAILURE); 80Bool running = True;
102} 81Display *dpy;
103 82DC dc = {0};
104static char * 83Item *allitems = NULL; /* first of all items */
105estrdup(const char *str) { 84Item *item = NULL; /* first of pattern matching items */
106 void *res = strdup(str); 85Item *sel = NULL;
107 86Item *next = NULL;
108 if(!res) 87Item *prev = NULL;
109 eprint("fatal: could not malloc() %u bytes\n", strlen(str)); 88Item *curr = NULL;
110 return res; 89Window root, win;
111} 90
112 91void
113
114static void
115drawtext(const char *text, unsigned long col[ColLast]) {
116 int x, y, w, h;
117 static char buf[256];
118 unsigned int len, olen;
119 XRectangle r = { dc.x, dc.y, dc.w, dc.h };
120
121 XSetForeground(dpy, dc.gc, col[ColBG]);
122 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
123 if(!text)
124 return;
125 w = 0;
126 olen = len = strlen(text);
127 if(len >= sizeof buf)
128 len = sizeof buf - 1;
129 memcpy(buf, text, len);
130 buf[len] = 0;
131 h = dc.font.ascent + dc.font.descent;
132 y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
133 x = dc.x + (h / 2);
134 /* shorten text if necessary */
135 while(len && (w = textnw(buf, len)) > dc.w - h)
136 buf[--len] = 0;
137 if(len < olen) {
138 if(len > 1)
139 buf[len - 1] = '.';
140 if(len > 2)
141 buf[len - 2] = '.';
142 if(len > 3)
143 buf[len - 3] = '.';
144 }
145 if(w > dc.w)
146 return; /* too long */
147 XSetForeground(dpy, dc.gc, col[ColFG]);
148 if(dc.font.set)
149 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
150 else
151 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
152}
153
154static unsigned int
155textw(const char *text) {
156 return textnw(text, strlen(text)) + dc.font.height;
157}
158
159static unsigned int
160textnw(const char *text, unsigned int len) {
161 XRectangle r;
162
163 if(dc.font.set) {
164 XmbTextExtents(dc.font.set, text, len, NULL, &r);
165 return r.width;
166 }
167 return XTextWidth(dc.font.xfont, text, len);
168}
169
170static void
171calcoffsets(void) { 92calcoffsets(void) {
172 unsigned int tw, w; 93 unsigned int tw, w;
173 94
@@ -193,7 +114,27 @@ calcoffsets(void) {
193 } 114 }
194} 115}
195 116
196static void 117void
118cleanup(void) {
119 Item *itm;
120
121 while(allitems) {
122 itm = allitems->next;
123 free(allitems->text);
124 free(allitems);
125 allitems = itm;
126 }
127 if(dc.font.set)
128 XFreeFontSet(dpy, dc.font.set);
129 else
130 XFreeFont(dpy, dc.font.xfont);
131 XFreePixmap(dpy, dc.drawable);
132 XFreeGC(dpy, dc.gc);
133 XDestroyWindow(dpy, win);
134 XUngrabKeyboard(dpy, CurrentTime);
135}
136
137void
197drawmenu(void) { 138drawmenu(void) {
198 Item *i; 139 Item *i;
199 140
@@ -234,20 +175,75 @@ drawmenu(void) {
234 XFlush(dpy); 175 XFlush(dpy);
235} 176}
236 177
237static Bool 178void
238grabkeyboard(void) { 179drawtext(const char *text, unsigned long col[ColLast]) {
239 unsigned int len; 180 int x, y, w, h;
181 static char buf[256];
182 unsigned int len, olen;
183 XRectangle r = { dc.x, dc.y, dc.w, dc.h };
240 184
241 for(len = 1000; len; len--) { 185 XSetForeground(dpy, dc.gc, col[ColBG]);
242 if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime) 186 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
243 == GrabSuccess) 187 if(!text)
244 break; 188 return;
245 usleep(1000); 189 w = 0;
190 olen = len = strlen(text);
191 if(len >= sizeof buf)
192 len = sizeof buf - 1;
193 memcpy(buf, text, len);
194 buf[len] = 0;
195 h = dc.font.ascent + dc.font.descent;
196 y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
197 x = dc.x + (h / 2);
198 /* shorten text if necessary */
199 while(len && (w = textnw(buf, len)) > dc.w - h)
200 buf[--len] = 0;
201 if(len < olen) {
202 if(len > 1)
203 buf[len - 1] = '.';
204 if(len > 2)
205 buf[len - 2] = '.';
206 if(len > 3)
207 buf[len - 3] = '.';
246 } 208 }
247 return len > 0; 209 if(w > dc.w)
210 return; /* too long */
211 XSetForeground(dpy, dc.gc, col[ColFG]);
212 if(dc.font.set)
213 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
214 else
215 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
216}
217
218void *
219emalloc(unsigned int size) {
220 void *res = malloc(size);
221
222 if(!res)
223 eprint("fatal: could not malloc() %u bytes\n", size);
224 return res;
225}
226
227void
228eprint(const char *errstr, ...) {
229 va_list ap;
230
231 va_start(ap, errstr);
232 vfprintf(stderr, errstr, ap);
233 va_end(ap);
234 exit(EXIT_FAILURE);
235}
236
237char *
238estrdup(const char *str) {
239 void *res = strdup(str);
240
241 if(!res)
242 eprint("fatal: could not malloc() %u bytes\n", strlen(str));
243 return res;
248} 244}
249 245
250static unsigned long 246unsigned long
251getcolor(const char *colstr) { 247getcolor(const char *colstr) {
252 Colormap cmap = DefaultColormap(dpy, screen); 248 Colormap cmap = DefaultColormap(dpy, screen);
253 XColor color; 249 XColor color;
@@ -257,7 +253,20 @@ getcolor(const char *colstr) {
257 return color.pixel; 253 return color.pixel;
258} 254}
259 255
260static void 256Bool
257grabkeyboard(void) {
258 unsigned int len;
259
260 for(len = 1000; len; len--) {
261 if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
262 == GrabSuccess)
263 break;
264 usleep(1000);
265 }
266 return len > 0;
267}
268
269void
261initfont(const char *fontstr) { 270initfont(const char *fontstr) {
262 char *def, **missing; 271 char *def, **missing;
263 int i, n; 272 int i, n;
@@ -299,65 +308,7 @@ initfont(const char *fontstr) {
299 dc.font.height = dc.font.ascent + dc.font.descent; 308 dc.font.height = dc.font.ascent + dc.font.descent;
300} 309}
301 310
302static int 311void
303strido(const char *text, const char *pattern) {
304 for(; *text && *pattern; text++)
305 if (*text == *pattern)
306 pattern++;
307 return !*pattern;
308}
309
310static void
311match(char *pattern) {
312 unsigned int plen;
313 Item *i, *j;
314
315 if(!pattern)
316 return;
317 plen = strlen(pattern);
318 item = j = NULL;
319 nitem = 0;
320 for(i = allitems; i; i=i->next)
321 if(!plen || !strncmp(pattern, i->text, plen)) {
322 if(!j)
323 item = i;
324 else
325 j->right = i;
326 i->left = j;
327 i->right = NULL;
328 j = i;
329 nitem++;
330 }
331 for(i = allitems; i; i=i->next)
332 if(plen && strncmp(pattern, i->text, plen)
333 && strstr(i->text, pattern)) {
334 if(!j)
335 item = i;
336 else
337 j->right = i;
338 i->left = j;
339 i->right = NULL;
340 j = i;
341 nitem++;
342 }
343 for(i = allitems; i; i=i->next)
344 if(plen && strncmp(pattern, i->text, plen)
345 && !strstr(i->text, pattern)
346 && strido(i->text,pattern)) {
347 if(!j)
348 item = i;
349 else
350 j->right = i;
351 i->left = j;
352 i->right = NULL;
353 j = i;
354 nitem++;
355 }
356 curr = prev = next = sel = item;
357 calcoffsets();
358}
359
360static void
361kpress(XKeyEvent * e) { 312kpress(XKeyEvent * e) {
362 char buf[32]; 313 char buf[32];
363 int i, num; 314 int i, num;
@@ -528,9 +479,58 @@ kpress(XKeyEvent * e) {
528 drawmenu(); 479 drawmenu();
529} 480}
530 481
531static char * 482void
483match(char *pattern) {
484 unsigned int plen;
485 Item *i, *j;
486
487 if(!pattern)
488 return;
489 plen = strlen(pattern);
490 item = j = NULL;
491 nitem = 0;
492 for(i = allitems; i; i=i->next)
493 if(!plen || !strncmp(pattern, i->text, plen)) {
494 if(!j)
495 item = i;
496 else
497 j->right = i;
498 i->left = j;
499 i->right = NULL;
500 j = i;
501 nitem++;
502 }
503 for(i = allitems; i; i=i->next)
504 if(plen && strncmp(pattern, i->text, plen)
505 && strstr(i->text, pattern)) {
506 if(!j)
507 item = i;
508 else
509 j->right = i;
510 i->left = j;
511 i->right = NULL;
512 j = i;
513 nitem++;
514 }
515 for(i = allitems; i; i=i->next)
516 if(plen && strncmp(pattern, i->text, plen)
517 && !strstr(i->text, pattern)
518 && strido(i->text,pattern)) {
519 if(!j)
520 item = i;
521 else
522 j->right = i;
523 i->left = j;
524 i->right = NULL;
525 j = i;
526 nitem++;
527 }
528 curr = prev = next = sel = item;
529 calcoffsets();
530}
531
532void
532readstdin(void) { 533readstdin(void) {
533 static char *maxname = NULL;
534 char *p, buf[1024]; 534 char *p, buf[1024];
535 unsigned int len = 0, max = 0; 535 unsigned int len = 0, max = 0;
536 Item *i, *new; 536 Item *i, *new;
@@ -554,88 +554,50 @@ readstdin(void) {
554 i->next = new; 554 i->next = new;
555 i = new; 555 i = new;
556 } 556 }
557
558 return maxname;
559} 557}
560 558
561static void 559void
562usage(void) { 560run(void) {
563 eprint("usage: dmenu [-b] [-fn <font>] [-nb <color>] [-nf <color>]\n" 561 XEvent ev;
564 " [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n"); 562
563 /* main event loop */
564 while(running && !XNextEvent(dpy, &ev))
565 switch (ev.type) {
566 default: /* ignore all crap */
567 break;
568 case KeyPress:
569 kpress(&ev.xkey);
570 break;
571 case Expose:
572 if(ev.xexpose.count == 0)
573 drawmenu();
574 break;
575 }
565} 576}
566 577
567int 578void
568main(int argc, char *argv[]) { 579setup(Bool bottom) {
569 Bool bottom = False; 580 unsigned int i, j;
570 char *font = FONT;
571 char *maxname;
572 char *normbg = NORMBGCOLOR;
573 char *normfg = NORMFGCOLOR;
574 char *selbg = SELBGCOLOR;
575 char *selfg = SELFGCOLOR;
576 int i, j;
577 Item *itm;
578 XEvent ev;
579 XModifierKeymap *modmap; 581 XModifierKeymap *modmap;
580 XSetWindowAttributes wa; 582 XSetWindowAttributes wa;
581 583
582 /* command line args */
583 for(i = 1; i < argc; i++)
584 if(!strcmp(argv[i], "-b")) {
585 bottom = True;
586 }
587 else if(!strcmp(argv[i], "-fn")) {
588 if(++i < argc) font = argv[i];
589 }
590 else if(!strcmp(argv[i], "-nb")) {
591 if(++i < argc) normbg = argv[i];
592 }
593 else if(!strcmp(argv[i], "-nf")) {
594 if(++i < argc) normfg = argv[i];
595 }
596 else if(!strcmp(argv[i], "-p")) {
597 if(++i < argc) prompt = argv[i];
598 }
599 else if(!strcmp(argv[i], "-sb")) {
600 if(++i < argc) selbg = argv[i];
601 }
602 else if(!strcmp(argv[i], "-sf")) {
603 if(++i < argc) selfg = argv[i];
604 }
605 else if(!strcmp(argv[i], "-v"))
606 eprint("dmenu-"VERSION", © 2006-2007 Anselm R. Garbe, Sander van Dijk\n");
607 else
608 usage();
609 setlocale(LC_CTYPE, "");
610 dpy = XOpenDisplay(0);
611 if(!dpy)
612 eprint("dmenu: cannot open display\n");
613 screen = DefaultScreen(dpy);
614 root = RootWindow(dpy, screen);
615 if(isatty(STDIN_FILENO)) {
616 maxname = readstdin();
617 running = grabkeyboard();
618 }
619 else { /* prevent keypress loss */
620 running = grabkeyboard();
621 maxname = readstdin();
622 }
623 /* init modifier map */ 584 /* init modifier map */
624 modmap = XGetModifierMapping(dpy); 585 modmap = XGetModifierMapping(dpy);
625 for (i = 0; i < 8; i++) { 586 for(i = 0; i < 8; i++)
626 for (j = 0; j < modmap->max_keypermod; j++) { 587 for(j = 0; j < modmap->max_keypermod; j++) {
627 if(modmap->modifiermap[i * modmap->max_keypermod + j] 588 if(modmap->modifiermap[i * modmap->max_keypermod + j]
628 == XKeysymToKeycode(dpy, XK_Num_Lock)) 589 == XKeysymToKeycode(dpy, XK_Num_Lock))
629 numlockmask = (1 << i); 590 numlockmask = (1 << i);
630 } 591 }
631 }
632 XFreeModifiermap(modmap); 592 XFreeModifiermap(modmap);
593
633 /* style */ 594 /* style */
634 dc.norm[ColBG] = getcolor(normbg); 595 dc.norm[ColBG] = getcolor(normbg);
635 dc.norm[ColFG] = getcolor(normfg); 596 dc.norm[ColFG] = getcolor(normfg);
636 dc.sel[ColBG] = getcolor(selbg); 597 dc.sel[ColBG] = getcolor(selbg);
637 dc.sel[ColFG] = getcolor(selfg); 598 dc.sel[ColFG] = getcolor(selfg);
638 initfont(font); 599 initfont(font);
600
639 /* menu window */ 601 /* menu window */
640 wa.override_redirect = 1; 602 wa.override_redirect = 1;
641 wa.background_pixmap = ParentRelative; 603 wa.background_pixmap = ParentRelative;
@@ -647,6 +609,7 @@ main(int argc, char *argv[]) {
647 DefaultDepth(dpy, screen), CopyFromParent, 609 DefaultDepth(dpy, screen), CopyFromParent,
648 DefaultVisual(dpy, screen), 610 DefaultVisual(dpy, screen),
649 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); 611 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
612
650 /* pixmap */ 613 /* pixmap */
651 dc.drawable = XCreatePixmap(dpy, root, mw, mh, DefaultDepth(dpy, screen)); 614 dc.drawable = XCreatePixmap(dpy, root, mw, mh, DefaultDepth(dpy, screen));
652 dc.gc = XCreateGC(dpy, root, 0, 0); 615 dc.gc = XCreateGC(dpy, root, 0, 0);
@@ -664,38 +627,86 @@ main(int argc, char *argv[]) {
664 text[0] = 0; 627 text[0] = 0;
665 match(text); 628 match(text);
666 XMapRaised(dpy, win); 629 XMapRaised(dpy, win);
667 drawmenu(); 630}
668 XSync(dpy, False);
669 631
670 /* main event loop */ 632int
671 while(running && !XNextEvent(dpy, &ev)) 633strido(const char *text, const char *pattern) {
672 switch (ev.type) { 634 for(; *text && *pattern; text++)
673 default: /* ignore all crap */ 635 if (*text == *pattern)
674 break; 636 pattern++;
675 case KeyPress: 637 return !*pattern;
676 kpress(&ev.xkey); 638}
677 break; 639
678 case Expose: 640unsigned int
679 if(ev.xexpose.count == 0) 641textnw(const char *text, unsigned int len) {
680 drawmenu(); 642 XRectangle r;
681 break; 643
644 if(dc.font.set) {
645 XmbTextExtents(dc.font.set, text, len, NULL, &r);
646 return r.width;
647 }
648 return XTextWidth(dc.font.xfont, text, len);
649}
650
651unsigned int
652textw(const char *text) {
653 return textnw(text, strlen(text)) + dc.font.height;
654}
655
656int
657main(int argc, char *argv[]) {
658 Bool bottom = False;
659 unsigned int i;
660
661 /* command line args */
662 for(i = 1; i < argc; i++)
663 if(!strcmp(argv[i], "-b")) {
664 bottom = True;
665 }
666 else if(!strcmp(argv[i], "-fn")) {
667 if(++i < argc) font = argv[i];
668 }
669 else if(!strcmp(argv[i], "-nb")) {
670 if(++i < argc) normbg = argv[i];
671 }
672 else if(!strcmp(argv[i], "-nf")) {
673 if(++i < argc) normfg = argv[i];
674 }
675 else if(!strcmp(argv[i], "-p")) {
676 if(++i < argc) prompt = argv[i];
677 }
678 else if(!strcmp(argv[i], "-sb")) {
679 if(++i < argc) selbg = argv[i];
680 }
681 else if(!strcmp(argv[i], "-sf")) {
682 if(++i < argc) selfg = argv[i];
682 } 683 }
684 else if(!strcmp(argv[i], "-v"))
685 eprint("dmenu-"VERSION", © 2006-2007 Anselm R. Garbe, Sander van Dijk\n");
686 else
687 eprint("usage: dmenu [-b] [-fn <font>] [-nb <color>] [-nf <color>]\n"
688 " [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n");
689 setlocale(LC_CTYPE, "");
690 dpy = XOpenDisplay(0);
691 if(!dpy)
692 eprint("dmenu: cannot open display\n");
693 screen = DefaultScreen(dpy);
694 root = RootWindow(dpy, screen);
683 695
684 /* cleanup */ 696 if(isatty(STDIN_FILENO)) {
685 while(allitems) { 697 readstdin();
686 itm = allitems->next; 698 running = grabkeyboard();
687 free(allitems->text);
688 free(allitems);
689 allitems = itm;
690 } 699 }
691 if(dc.font.set) 700 else { /* prevent keypress loss */
692 XFreeFontSet(dpy, dc.font.set); 701 running = grabkeyboard();
693 else 702 readstdin();
694 XFreeFont(dpy, dc.font.xfont); 703 }
695 XFreePixmap(dpy, dc.drawable); 704
696 XFreeGC(dpy, dc.gc); 705 setup(bottom);
697 XDestroyWindow(dpy, win); 706 drawmenu();
698 XUngrabKeyboard(dpy, CurrentTime); 707 XSync(dpy, False);
708 run();
709 cleanup();
699 XCloseDisplay(dpy); 710 XCloseDisplay(dpy);
700 return ret; 711 return ret;
701} 712}