#include <ctype.h>
#include <locale.h>
#include <stdarg.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
+#include <strings.h>
#include <unistd.h>
+#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
-#include <X11/keysym.h>
+#ifdef XINERAMA
+#include <X11/extensions/Xinerama.h>
+#endif
/* macros */
#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
enum { ColFG, ColBG, ColLast };
/* typedefs */
+typedef unsigned int uint;
+typedef unsigned long ulong;
typedef struct {
int x, y, w, h;
- unsigned long norm[ColLast];
- unsigned long sel[ColLast];
+ ulong norm[ColLast];
+ ulong sel[ColLast];
Drawable drawable;
GC gc;
struct {
typedef struct Item Item;
struct Item {
+ char *text;
Item *next; /* traverses all items */
Item *left, *right; /* traverses items matching current search pattern */
- char *text;
};
/* forward declarations */
-static void *emalloc(unsigned int size);
-static void eprint(const char *errstr, ...);
-static char *estrdup(const char *str);
-static void drawtext(const char *text, unsigned long col[ColLast]);
-static unsigned int textw(const char *text);
-static unsigned int textnw(const char *text, unsigned int len);
+static void appenditem(Item *i, Item **list, Item **last);
static void calcoffsets(void);
+static char *cistrstr(const char *s, const char *sub);
+static void cleanup(void);
static void drawmenu(void);
+static void drawtext(const char *text, ulong col[ColLast]);
+static void *emalloc(uint size);
+static void eprint(const char *errstr, ...);
+static ulong getcolor(const char *colstr);
static Bool grabkeyboard(void);
-static unsigned long getcolor(const char *colstr);
static void initfont(const char *fontstr);
-static int strido(const char *text, const char *pattern);
-static void match(char *pattern);
static void kpress(XKeyEvent * e);
-static char *readstdin(void);
-static void usage(void);
+static void match(char *pattern);
+static void readstdin(void);
+static void run(void);
+static void setup(Bool topbar);
+static int textnw(const char *text, uint len);
+static int textw(const char *text);
+#include "config.h"
/* variables */
-static int screen;
-static Display *dpy;
-static DC dc = {0};
-static char text[4096];
+static char *font = FONT;
+static char *maxname = NULL;
+static char *normbg = NORMBGCOLOR;
+static char *normfg = NORMFGCOLOR;
static char *prompt = NULL;
-static int mw, mh;
+static char *selbg = SELBGCOLOR;
+static char *selfg = SELFGCOLOR;
+static char text[4096];
+static int cmdw = 0;
+static int promptw = 0;
static int ret = 0;
-static int nitem = 0;
-static unsigned int cmdw = 0;
-static unsigned int promptw = 0;
-static unsigned int numlockmask = 0;
+static int screen;
+static uint mw, mh;
+static uint numlockmask = 0;
static Bool running = True;
+static Display *dpy;
+static DC dc = {0};
static Item *allitems = NULL; /* first of all items */
static Item *item = NULL; /* first of pattern matching items */
static Item *sel = NULL;
static Item *next = NULL;
static Item *prev = NULL;
static Item *curr = NULL;
-static Window root;
-static Window win;
-
-#include "config.h"
-
-static void *
-emalloc(unsigned int size) {
- void *res = malloc(size);
-
- if(!res)
- eprint("fatal: could not malloc() %u bytes\n", size);
- return res;
-}
-
-static void
-eprint(const char *errstr, ...) {
- va_list ap;
-
- va_start(ap, errstr);
- vfprintf(stderr, errstr, ap);
- va_end(ap);
- exit(EXIT_FAILURE);
-}
-
-static char *
-estrdup(const char *str) {
- void *res = strdup(str);
-
- if(!res)
- eprint("fatal: could not malloc() %u bytes\n", strlen(str));
- return res;
-}
-
-
-static void
-drawtext(const char *text, unsigned long col[ColLast]) {
- int x, y, w, h;
- static char buf[256];
- unsigned int len, olen;
- XRectangle r = { dc.x, dc.y, dc.w, dc.h };
-
- XSetForeground(dpy, dc.gc, col[ColBG]);
- XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
- if(!text)
- return;
- w = 0;
- olen = len = strlen(text);
- if(len >= sizeof buf)
- len = sizeof buf - 1;
- memcpy(buf, text, len);
- buf[len] = 0;
- h = dc.font.ascent + dc.font.descent;
- y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
- x = dc.x + (h / 2);
- /* shorten text if necessary */
- while(len && (w = textnw(buf, len)) > dc.w - h)
- buf[--len] = 0;
- if(len < olen) {
- if(len > 1)
- buf[len - 1] = '.';
- if(len > 2)
- buf[len - 2] = '.';
- if(len > 3)
- buf[len - 3] = '.';
- }
- if(w > dc.w)
- return; /* too long */
- XSetForeground(dpy, dc.gc, col[ColFG]);
- if(dc.font.set)
- XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
+static Window root, win;
+static int (*fstrncmp)(const char *, const char *, size_t n) = strncmp;
+static char *(*fstrstr)(const char *, const char *) = strstr;
+
+void
+appenditem(Item *i, Item **list, Item **last) {
+ if(!(*last))
+ *list = i;
else
- XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
+ (*last)->right = i;
+ i->left = *last;
+ i->right = NULL;
+ *last = i;
}
-static unsigned int
-textw(const char *text) {
- return textnw(text, strlen(text)) + dc.font.height;
-}
-
-static unsigned int
-textnw(const char *text, unsigned int len) {
- XRectangle r;
-
- if(dc.font.set) {
- XmbTextExtents(dc.font.set, text, len, NULL, &r);
- return r.width;
- }
- return XTextWidth(dc.font.xfont, text, len);
-}
-
-static void
+void
calcoffsets(void) {
- unsigned int tw, w;
+ int tw;
+ uint w;
if(!curr)
return;
- w = promptw + cmdw + 2 * SPACE;
+ w = promptw + cmdw + 2 * spaceitem;
for(next = curr; next; next=next->right) {
tw = textw(next->text);
if(tw > mw / 3)
if(w > mw)
break;
}
- w = promptw + cmdw + 2 * SPACE;
+ w = promptw + cmdw + 2 * spaceitem;
for(prev = curr; prev && prev->left; prev=prev->left) {
tw = textw(prev->left->text);
if(tw > mw / 3)
}
}
-static void
+char *
+cistrstr(const char *s, const char *sub) {
+ int c, csub;
+ uint len;
+
+ if(!sub)
+ return (char *)s;
+ if((c = *sub++) != 0) {
+ c = tolower(c);
+ len = strlen(sub);
+ do {
+ do {
+ if((csub = *s++) == 0)
+ return (NULL);
+ }
+ while(tolower(csub) != c);
+ }
+ while(strncasecmp(s, sub, len) != 0);
+ s--;
+ }
+ return (char *)s;
+}
+
+void
+cleanup(void) {
+ Item *itm;
+
+ while(allitems) {
+ itm = allitems->next;
+ free(allitems->text);
+ free(allitems);
+ allitems = itm;
+ }
+ if(dc.font.set)
+ XFreeFontSet(dpy, dc.font.set);
+ else
+ XFreeFont(dpy, dc.font.xfont);
+ XFreePixmap(dpy, dc.drawable);
+ XFreeGC(dpy, dc.gc);
+ XDestroyWindow(dpy, win);
+ XUngrabKeyboard(dpy, CurrentTime);
+}
+
+void
drawmenu(void) {
Item *i;
drawtext(text[0] ? text : NULL, dc.norm);
dc.x += cmdw;
if(curr) {
- dc.w = SPACE;
+ dc.w = spaceitem;
drawtext((curr && curr->left) ? "<" : NULL, dc.norm);
dc.x += dc.w;
/* determine maximum items */
drawtext(i->text, (sel == i) ? dc.sel : dc.norm);
dc.x += dc.w;
}
- dc.x = mw - SPACE;
- dc.w = SPACE;
+ dc.x = mw - spaceitem;
+ dc.w = spaceitem;
drawtext(next ? ">" : NULL, dc.norm);
}
XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, mw, mh, 0, 0);
XFlush(dpy);
}
-static Bool
-grabkeyboard(void) {
- unsigned int len;
+void
+drawtext(const char *text, ulong col[ColLast]) {
+ int x, y, w, h;
+ static char buf[256];
+ uint len, olen;
+ XRectangle r = { dc.x, dc.y, dc.w, dc.h };
- for(len = 1000; len; len--) {
- if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
- == GrabSuccess)
- break;
- usleep(1000);
+ XSetForeground(dpy, dc.gc, col[ColBG]);
+ XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
+ if(!text)
+ return;
+ w = 0;
+ olen = len = strlen(text);
+ if(len >= sizeof buf)
+ len = sizeof buf - 1;
+ memcpy(buf, text, len);
+ buf[len] = 0;
+ h = dc.font.ascent + dc.font.descent;
+ y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
+ x = dc.x + (h / 2);
+ /* shorten text if necessary */
+ while(len && (w = textnw(buf, len)) > dc.w - h)
+ buf[--len] = 0;
+ if(len < olen) {
+ if(len > 1)
+ buf[len - 1] = '.';
+ if(len > 2)
+ buf[len - 2] = '.';
+ if(len > 3)
+ buf[len - 3] = '.';
}
- return len > 0;
+ if(w > dc.w)
+ return; /* too long */
+ XSetForeground(dpy, dc.gc, col[ColFG]);
+ if(dc.font.set)
+ XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
+ else
+ XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
+}
+
+void *
+emalloc(uint size) {
+ void *res = malloc(size);
+
+ if(!res)
+ eprint("fatal: could not malloc() %u bytes\n", size);
+ return res;
+}
+
+void
+eprint(const char *errstr, ...) {
+ va_list ap;
+
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
}
-static unsigned long
+ulong
getcolor(const char *colstr) {
Colormap cmap = DefaultColormap(dpy, screen);
XColor color;
return color.pixel;
}
-static void
+Bool
+grabkeyboard(void) {
+ uint len;
+
+ for(len = 1000; len; len--) {
+ if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
+ == GrabSuccess)
+ break;
+ usleep(1000);
+ }
+ return len > 0;
+}
+
+void
initfont(const char *fontstr) {
char *def, **missing;
int i, n;
if(dc.font.xfont)
XFreeFont(dpy, dc.font.xfont);
dc.font.xfont = NULL;
- if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) {
- if(!(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
- eprint("error, cannot load font: '%s'\n", fontstr);
- }
+ if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
+ && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
+ eprint("error, cannot load font: '%s'\n", fontstr);
dc.font.ascent = dc.font.xfont->ascent;
dc.font.descent = dc.font.xfont->descent;
}
dc.font.height = dc.font.ascent + dc.font.descent;
}
-static int
-strido(const char *text, const char *pattern) {
- for(; *text && *pattern; text++)
- if (*text == *pattern)
- pattern++;
- return !*pattern;
-}
-
-static void
-match(char *pattern) {
- unsigned int plen;
- Item *i, *j;
-
- if(!pattern)
- return;
- plen = strlen(pattern);
- item = j = NULL;
- nitem = 0;
- for(i = allitems; i; i=i->next)
- if(!plen || !strncmp(pattern, i->text, plen)) {
- if(!j)
- item = i;
- else
- j->right = i;
- i->left = j;
- i->right = NULL;
- j = i;
- nitem++;
- }
- for(i = allitems; i; i=i->next)
- if(plen && strncmp(pattern, i->text, plen)
- && strstr(i->text, pattern)) {
- if(!j)
- item = i;
- else
- j->right = i;
- i->left = j;
- i->right = NULL;
- j = i;
- nitem++;
- }
- for(i = allitems; i; i=i->next)
- if(plen && strncmp(pattern, i->text, plen)
- && !strstr(i->text, pattern)
- && strido(i->text,pattern)) {
- if(!j)
- item = i;
- else
- j->right = i;
- i->left = j;
- i->right = NULL;
- j = i;
- nitem++;
- }
- curr = prev = next = sel = item;
- calcoffsets();
-}
-
-static void
+void
kpress(XKeyEvent * e) {
char buf[32];
int i, num;
- unsigned int len;
+ uint len;
KeySym ksym;
len = strlen(text);
buf[0] = 0;
num = XLookupString(e, buf, sizeof buf, &ksym, 0);
- if(IsKeypadKey(ksym)) {
- if(ksym == XK_KP_Enter) {
+ if(IsKeypadKey(ksym)) {
+ if(ksym == XK_KP_Enter)
ksym = XK_Return;
- } else if(ksym >= XK_KP_0 && ksym <= XK_KP_9) {
+ else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
ksym = (ksym - XK_KP_0) + XK_0;
- }
}
if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
- || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
- || IsPrivateKeypadKey(ksym))
+ || IsMiscFunctionKey(ksym) || IsPFKey(ksym)
+ || IsPrivateKeypadKey(ksym))
return;
/* first check if a control mask is omitted */
if(e->state & ControlMask) {
drawmenu();
}
-static char *
+void
+match(char *pattern) {
+ uint plen;
+ Item *i, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
+
+ if(!pattern)
+ return;
+ plen = strlen(pattern);
+ item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL;
+ for(i = allitems; i; i = i->next)
+ if(!fstrncmp(pattern, i->text, plen + 1))
+ appenditem(i, &lexact, &exactend);
+ else if(!fstrncmp(pattern, i->text, plen))
+ appenditem(i, &lprefix, &prefixend);
+ else if(fstrstr(i->text, pattern))
+ appenditem(i, &lsubstr, &substrend);
+ if(lexact) {
+ item = lexact;
+ itemend = exactend;
+ }
+ if(lprefix) {
+ if(itemend) {
+ itemend->right = lprefix;
+ lprefix->left = itemend;
+ }
+ else
+ item = lprefix;
+ itemend = prefixend;
+ }
+ if(lsubstr) {
+ if(itemend) {
+ itemend->right = lsubstr;
+ lsubstr->left = itemend;
+ }
+ else
+ item = lsubstr;
+ }
+ curr = prev = next = sel = item;
+ calcoffsets();
+}
+
+void
readstdin(void) {
- static char *maxname = NULL;
char *p, buf[1024];
- unsigned int len = 0, max = 0;
+ uint len = 0, max = 0;
Item *i, *new;
i = 0;
len = strlen(buf);
if (buf[len - 1] == '\n')
buf[len - 1] = 0;
- p = estrdup(buf);
+ if(!(p = strdup(buf)))
+ eprint("fatal: could not strdup() %u bytes\n", strlen(buf));
if(max < len) {
maxname = p;
max = len;
i->next = new;
i = new;
}
-
- return maxname;
}
-static void
-usage(void) {
- eprint("usage: dmenu [-b] [-fn <font>] [-nb <color>] [-nf <color>]\n"
- " [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n");
+void
+run(void) {
+ XEvent ev;
+
+ /* main event loop */
+ while(running && !XNextEvent(dpy, &ev))
+ switch (ev.type) {
+ default: /* ignore all crap */
+ break;
+ case KeyPress:
+ kpress(&ev.xkey);
+ break;
+ case Expose:
+ if(ev.xexpose.count == 0)
+ drawmenu();
+ break;
+ }
}
-int
-main(int argc, char *argv[]) {
- Bool bottom = False;
- char *font = FONT;
- char *maxname;
- char *normbg = NORMBGCOLOR;
- char *normfg = NORMFGCOLOR;
- char *selbg = SELBGCOLOR;
- char *selfg = SELFGCOLOR;
- int i, j;
- Item *itm;
- XEvent ev;
+void
+setup(Bool topbar) {
+ int i, j, x, y;
XModifierKeymap *modmap;
XSetWindowAttributes wa;
+#if XINERAMA
+ XineramaScreenInfo *info = NULL;
+#endif
- /* command line args */
- for(i = 1; i < argc; i++)
- if(!strcmp(argv[i], "-b")) {
- bottom = True;
- }
- else if(!strcmp(argv[i], "-fn")) {
- if(++i < argc) font = argv[i];
- }
- else if(!strcmp(argv[i], "-nb")) {
- if(++i < argc) normbg = argv[i];
- }
- else if(!strcmp(argv[i], "-nf")) {
- if(++i < argc) normfg = argv[i];
- }
- else if(!strcmp(argv[i], "-p")) {
- if(++i < argc) prompt = argv[i];
- }
- else if(!strcmp(argv[i], "-sb")) {
- if(++i < argc) selbg = argv[i];
- }
- else if(!strcmp(argv[i], "-sf")) {
- if(++i < argc) selfg = argv[i];
- }
- else if(!strcmp(argv[i], "-v"))
- eprint("dmenu-"VERSION", © 2006-2007 Anselm R. Garbe, Sander van Dijk\n");
- else
- usage();
- setlocale(LC_CTYPE, "");
- dpy = XOpenDisplay(0);
- if(!dpy)
- eprint("dmenu: cannot open display\n");
- screen = DefaultScreen(dpy);
- root = RootWindow(dpy, screen);
- if(isatty(STDIN_FILENO)) {
- maxname = readstdin();
- running = grabkeyboard();
- }
- else { /* prevent keypress loss */
- running = grabkeyboard();
- maxname = readstdin();
- }
/* init modifier map */
modmap = XGetModifierMapping(dpy);
- for (i = 0; i < 8; i++) {
- for (j = 0; j < modmap->max_keypermod; j++) {
+ for(i = 0; i < 8; i++)
+ for(j = 0; j < modmap->max_keypermod; j++) {
if(modmap->modifiermap[i * modmap->max_keypermod + j]
== XKeysymToKeycode(dpy, XK_Num_Lock))
numlockmask = (1 << i);
}
- }
XFreeModifiermap(modmap);
+
/* style */
dc.norm[ColBG] = getcolor(normbg);
dc.norm[ColFG] = getcolor(normfg);
dc.sel[ColBG] = getcolor(selbg);
dc.sel[ColFG] = getcolor(selfg);
initfont(font);
+
/* menu window */
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask;
- mw = DisplayWidth(dpy, screen);
+
+ /* menu window geometry */
mh = dc.font.height + 2;
- win = XCreateWindow(dpy, root, 0,
- bottom ? DisplayHeight(dpy, screen) - mh : 0, mw, mh, 0,
+#if XINERAMA
+ if(XineramaIsActive(dpy)) {
+ info = XineramaQueryScreens(dpy, &i);
+ x = info[xidx].x_org;
+ y = topbar ? info[xidx].y_org : info[xidx].y_org + info[xidx].height - mh;
+ mw = info[xidx].width;
+ XFree(info);
+ }
+ else
+#endif
+ {
+ x = 0;
+ y = topbar ? 0 : DisplayHeight(dpy, screen) - mh;
+ mw = DisplayWidth(dpy, screen);
+ }
+
+ win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
DefaultDepth(dpy, screen), CopyFromParent,
DefaultVisual(dpy, screen),
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
+
/* pixmap */
dc.drawable = XCreatePixmap(dpy, root, mw, mh, DefaultDepth(dpy, screen));
dc.gc = XCreateGC(dpy, root, 0, 0);
text[0] = 0;
match(text);
XMapRaised(dpy, win);
- drawmenu();
- XSync(dpy, False);
+}
- /* main event loop */
- while(running && !XNextEvent(dpy, &ev))
- switch (ev.type) {
- default: /* ignore all crap */
- break;
- case KeyPress:
- kpress(&ev.xkey);
- break;
- case Expose:
- if(ev.xexpose.count == 0)
- drawmenu();
- break;
+int
+textnw(const char *text, uint len) {
+ XRectangle r;
+
+ if(dc.font.set) {
+ XmbTextExtents(dc.font.set, text, len, NULL, &r);
+ return r.width;
+ }
+ return XTextWidth(dc.font.xfont, text, len);
+}
+
+int
+textw(const char *text) {
+ return textnw(text, strlen(text)) + dc.font.height;
+}
+
+int
+main(int argc, char *argv[]) {
+ uint i;
+ Bool topbar = True;
+
+ /* command line args */
+ for(i = 1; i < argc; i++)
+ if(!strcmp(argv[i], "-i")) {
+ fstrncmp = strncasecmp;
+ fstrstr = cistrstr;
+ }
+ else if(!strcmp(argv[i], "-b"))
+ topbar = False;
+ else if(!strcmp(argv[i], "-fn")) {
+ if(++i < argc) font = argv[i];
+ }
+ else if(!strcmp(argv[i], "-nb")) {
+ if(++i < argc) normbg = argv[i];
+ }
+ else if(!strcmp(argv[i], "-nf")) {
+ if(++i < argc) normfg = argv[i];
+ }
+ else if(!strcmp(argv[i], "-p")) {
+ if(++i < argc) prompt = argv[i];
}
+ else if(!strcmp(argv[i], "-sb")) {
+ if(++i < argc) selbg = argv[i];
+ }
+ else if(!strcmp(argv[i], "-sf")) {
+ if(++i < argc) selfg = argv[i];
+ }
+ else if(!strcmp(argv[i], "-v"))
+ eprint("dmenu-"VERSION", © 2006-2008 dmenu engineers, see LICENSE for details\n");
+ else
+ eprint("usage: dmenu [-i] [-b] [-fn <font>] [-nb <color>] [-nf <color>]\n"
+ " [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n");
+ setlocale(LC_CTYPE, "");
+ if(!(dpy = XOpenDisplay(0)))
+ eprint("dmenu: cannot open display\n");
+ screen = DefaultScreen(dpy);
+ root = RootWindow(dpy, screen);
- /* cleanup */
- while(allitems) {
- itm = allitems->next;
- free(allitems->text);
- free(allitems);
- allitems = itm;
+ if(isatty(STDIN_FILENO)) {
+ readstdin();
+ running = grabkeyboard();
}
- if(dc.font.set)
- XFreeFontSet(dpy, dc.font.set);
- else
- XFreeFont(dpy, dc.font.xfont);
- XFreePixmap(dpy, dc.drawable);
- XFreeGC(dpy, dc.gc);
- XDestroyWindow(dpy, win);
- XUngrabKeyboard(dpy, CurrentTime);
+ else { /* prevent keypress loss */
+ running = grabkeyboard();
+ readstdin();
+ }
+
+ setup(topbar);
+ drawmenu();
+ XSync(dpy, False);
+ run();
+ cleanup();
XCloseDisplay(dpy);
return ret;
}