aboutsummaryrefslogtreecommitdiffstats
path: root/dmenu.c
diff options
context:
space:
mode:
Diffstat (limited to 'dmenu.c')
-rw-r--r--dmenu.c302
1 files changed, 129 insertions, 173 deletions
diff --git a/dmenu.c b/dmenu.c
index c1d2836..bade890 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -23,11 +23,11 @@
/* macros */
#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \
* MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
-#define LENGTH(X) (sizeof X / sizeof X[0])
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
/* enums */
-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeNormHighlight, SchemeSelHighlight, SchemeLast }; /* color schemes */
+enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight,
+ SchemeOut, SchemeLast }; /* color schemes */
struct item {
char *text;
@@ -60,6 +60,13 @@ static Clr *scheme[SchemeLast];
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
static char *(*fstrstr)(const char *, const char *) = strstr;
+static unsigned int
+textw_clamp(const char *str, unsigned int n)
+{
+ unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
+ return MIN(w, n);
+}
+
static void
appenditem(struct item *item, struct item **list, struct item **last)
{
@@ -84,22 +91,13 @@ calcoffsets(void)
n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
/* calculate which items will begin the next page and previous page */
for (i = 0, next = curr; next; next = next->right)
- if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n)
+ if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n)
break;
for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
- if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n)
+ if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n)
break;
}
-static int
-max_textw(void)
-{
- int len = 0;
- for (struct item *item = items; item && item->text; item++)
- len = MAX(TEXTW(item->text), len);
- return len;
-}
-
static void
cleanup(void)
{
@@ -108,19 +106,29 @@ cleanup(void)
XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (i = 0; i < SchemeLast; i++)
free(scheme[i]);
+ for (i = 0; items && items[i].text; ++i)
+ free(items[i].text);
+ free(items);
drw_free(drw);
XSync(dpy, False);
XCloseDisplay(dpy);
}
static char *
-cistrstr(const char *s, const char *sub)
+cistrstr(const char *h, const char *n)
{
- size_t len;
+ size_t i;
+
+ if (!n[0])
+ return (char *)h;
- for (len = strlen(sub); *s; s++)
- if (!strncasecmp(s, sub, len))
- return (char *)s;
+ for (; *h; ++h) {
+ for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
+ tolower((unsigned char)h[i]); ++i)
+ ;
+ if (n[i] == '\0')
+ return (char *)h;
+ }
return NULL;
}
@@ -128,17 +136,16 @@ static void
drawhighlights(struct item *item, int x, int y, int maxw)
{
int i, indent;
- char *highlight;
- char c;
+ char c, *highlight;
if (!(strlen(item->text) && strlen(text)))
return;
drw_setscheme(drw, scheme[item == sel
- ? SchemeSelHighlight
- : SchemeNormHighlight]);
+ ? SchemeSelHighlight
+ : SchemeNormHighlight]);
for (i = 0, highlight = item->text; *highlight && text[i];) {
- if (!fstrncmp(&(*highlight), &text[i], 1)) {
+ if (!fstrncmp(highlight, &text[i], 1)) {
/* get indentation */
c = *highlight;
*highlight = '\0';
@@ -149,59 +156,24 @@ drawhighlights(struct item *item, int x, int y, int maxw)
c = highlight[1];
highlight[1] = '\0';
drw_text(
- drw,
- x + indent - (lrpad / 2),
- y,
- MIN(maxw - indent, TEXTW(highlight) - lrpad),
- bh, 0, highlight, 0
- );
+ drw,
+ x + indent - (lrpad / 2.),
+ y,
+ MIN(maxw - indent, TEXTW(highlight) - lrpad),
+ bh, 0, highlight, 0
+ );
highlight[1] = c;
- i++;
+ ++i;
}
- highlight++;
+ ++highlight;
}
}
-/* static void */
-/* drawhighlights(struct item *item, int x, int y, int maxw) */
-/* { */
-/* char restorechar, tokens[sizeof text], *highlight, *token; */
-/* int indentx, highlightlen; */
-/* */
-/* drw_setscheme(drw, scheme[item == sel ? SchemeSelHighlight : SchemeNormHighlight]); */
-/* strcpy(tokens, text); */
-/* for (token = strtok(tokens, " "); token; token = strtok(NULL, " ")) { */
-/* highlight = fstrstr(item->text, token); */
-/* while (highlight) { */
-/* // Move item str end, calc width for highlight indent, & restore */
-/* highlightlen = highlight - item->text; */
-/* restorechar = *highlight; */
-/* item->text[highlightlen] = '\0'; */
-/* indentx = TEXTW(item->text); */
-/* item->text[highlightlen] = restorechar; */
-/* */
-/* // Move highlight str end, draw highlight, & restore */
-/* restorechar = highlight[strlen(token)]; */
-/* highlight[strlen(token)] = '\0'; */
-/* if (indentx - (lrpad / 2) - 1 < maxw) */
-/* drw_text( */
-/* drw, */
-/* x + indentx - (lrpad / 2) - 1, */
-/* y, */
-/* MIN(maxw - indentx, TEXTW(highlight) - lrpad), */
-/* bh, 0, highlight, 0 */
-/* ); */
-/* highlight[strlen(token)] = restorechar; */
- /* */
- /* if (strlen(highlight) - strlen(token) < strlen(token)) break; */
- /* highlight = fstrstr(highlight + strlen(token), token); */
- /* } */
- /* } */
-/* } */
-
static int
drawitem(struct item *item, int x, int y, int w)
{
+ int r;
+
if (item == sel)
drw_setscheme(drw, scheme[SchemeSel]);
else if (item->out)
@@ -209,7 +181,7 @@ drawitem(struct item *item, int x, int y, int w)
else
drw_setscheme(drw, scheme[SchemeNorm]);
- int r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
+ r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
drawhighlights(item, x, y, w);
return r;
}
@@ -219,7 +191,7 @@ drawmenu(void)
{
unsigned int curpos;
struct item *item;
- int x = 0, y = 0, fh = drw->fonts->h, w;
+ int x = 0, y = 0, w;
char *censort;
drw_setscheme(drw, scheme[SchemeNorm]);
@@ -234,7 +206,7 @@ drawmenu(void)
drw_setscheme(drw, scheme[SchemeNorm]);
if (passwd) {
censort = ecalloc(1, sizeof(text));
- memset(censort, '*', strlen(text));
+ memset(censort, '.', strlen(text));
drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0);
free(censort);
} else drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
@@ -242,7 +214,7 @@ drawmenu(void)
curpos = TEXTW(text) - TEXTW(&text[cursor]);
if ((curpos += lrpad / 2 - 1) < w) {
drw_setscheme(drw, scheme[SchemeNorm]);
- drw_rect(drw, x + curpos, 2 + (bh-fh)/2, 2, fh - 4, 1, 0);
+ drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
}
if (lines > 0) {
@@ -259,7 +231,7 @@ drawmenu(void)
}
x += w;
for (item = curr; item != next; item = item->right)
- x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">")));
+ x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">")));
if (next) {
w = TEXTW(">");
drw_setscheme(drw, scheme[SchemeNorm]);
@@ -286,6 +258,24 @@ grabfocus(void)
die("cannot grab focus");
}
+static void
+grabkeyboard(void)
+{
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
+ int i;
+
+ if (embed)
+ return;
+ /* try to grab keyboard, we may have to wait for another process to ungrab */
+ for (i = 0; i < 1000; i++) {
+ if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
+ GrabModeAsync, CurrentTime) == GrabSuccess)
+ return;
+ nanosleep(&ts, NULL);
+ }
+ die("cannot grab keyboard");
+}
+
int
compare_distance(const void *a, const void *b)
{
@@ -313,18 +303,18 @@ fuzzymatch(void)
matches = matchend = NULL;
/* walk through all items */
- for (it = items; it && it->text; it++) {
+ for (it = items; it && it->text; ++it) {
if (text_len) {
itext_len = strlen(it->text);
pidx = 0; /* pointer */
sidx = eidx = -1; /* start of match, end of match */
/* walk through item text */
- for (i = 0; i < itext_len && (c = it->text[i]); i++) {
+ for (i = 0; i < itext_len && (c = it->text[i]); ++i) {
/* fuzzy match pattern */
if (!fstrncmp(&text[pidx], &c, 1)) {
if(sidx == -1)
sidx = i;
- pidx++;
+ ++pidx;
if (pidx == text_len) {
eidx = i;
break;
@@ -339,7 +329,7 @@ fuzzymatch(void)
it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len);
/* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */
appenditem(it, &matches, &matchend);
- number_of_matches++;
+ ++number_of_matches;
}
} else {
appenditem(it, &matches, &matchend);
@@ -348,19 +338,18 @@ fuzzymatch(void)
if (number_of_matches) {
/* initialize array with matches */
- if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*))))
- die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*));
- for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) {
+ if (!(fuzzymatches = realloc(fuzzymatches,
+ number_of_matches * sizeof(struct item *))))
+ die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item *));
+ for (i = 0, it = matches; it && i < number_of_matches; ++i, it = it->right)
fuzzymatches[i] = it;
- }
/* sort matches according to distance */
qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance);
/* rebuild list of matches */
matches = matchend = NULL;
- for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \
- it->text; i++, it = fuzzymatches[i]) {
+ for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it &&
+ it->text; ++i, it = fuzzymatches[i])
appenditem(it, &matches, &matchend);
- }
free(fuzzymatches);
}
curr = sel = matches;
@@ -368,24 +357,6 @@ fuzzymatch(void)
}
static void
-grabkeyboard(void)
-{
- struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
- int i;
-
- if (embed)
- return;
- /* try to grab keyboard, we may have to wait for another process to ungrab */
- for (i = 0; i < 1000; i++) {
- if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
- GrabModeAsync, CurrentTime) == GrabSuccess)
- return;
- nanosleep(&ts, NULL);
- }
- die("cannot grab keyboard");
-}
-
-static void
match(void)
{
if (fuzzy) {
@@ -404,7 +375,7 @@ match(void)
/* separate input text into tokens to be matched individually */
for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
- die("cannot realloc %u bytes:", tokn * sizeof *tokv);
+ die("cannot realloc %zu bytes:", tokn * sizeof *tokv);
len = tokc ? strlen(tokv[0]) : 0;
matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
@@ -486,19 +457,19 @@ movewordedge(int dir)
static void
keypress(XKeyEvent *ev)
{
- char buf[32];
+ char buf[64];
int len;
- KeySym ksym;
+ KeySym ksym = NoSymbol;
Status status;
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
switch (status) {
default: /* XLookupNone, XBufferOverflow */
return;
- case XLookupChars:
+ case XLookupChars: /* composed string from input method */
goto insert;
case XLookupKeySym:
- case XLookupBoth:
+ case XLookupBoth: /* a KeySym and a string are returned: use keysym */
break;
}
@@ -511,10 +482,11 @@ keypress(XKeyEvent *ev)
case XK_e: ksym = XK_End; break;
case XK_f: ksym = XK_Right; break;
case XK_g: ksym = XK_Escape; break;
- case XK_h: ksym = XK_BackSpace; break;
case XK_i: ksym = XK_Tab; break;
- case XK_j: ksym = XK_Down; break;
- case XK_k: ksym = XK_Up; break;
+ case XK_j: ksym = XK_Down; break;
+ case XK_k: ksym = XK_Up; break;
+ case XK_l: ksym = XK_Down; break;
+ case XK_h: ksym = XK_Up; break;
case XK_J: /* fallthrough */
case XK_m: /* fallthrough */
case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break;
@@ -536,9 +508,11 @@ keypress(XKeyEvent *ev)
utf8, utf8, win, CurrentTime);
return;
case XK_Left:
+ case XK_KP_Left:
movewordedge(-1);
goto draw;
case XK_Right:
+ case XK_KP_Right:
movewordedge(+1);
goto draw;
case XK_Return:
@@ -572,10 +546,11 @@ keypress(XKeyEvent *ev)
switch(ksym) {
default:
insert:
- if (!iscntrl(*buf))
+ if (!iscntrl((unsigned char)*buf))
insert(buf, len);
break;
case XK_Delete:
+ case XK_KP_Delete:
if (text[cursor] == '\0')
return;
cursor = nextrune(+1);
@@ -586,6 +561,7 @@ insert:
insert(NULL, nextrune(-1) - cursor);
break;
case XK_End:
+ case XK_KP_End:
if (text[cursor] != '\0') {
cursor = strlen(text);
break;
@@ -605,6 +581,7 @@ insert:
cleanup();
exit(1);
case XK_Home:
+ case XK_KP_Home:
if (sel == matches) {
cursor = 0;
break;
@@ -613,6 +590,7 @@ insert:
calcoffsets();
break;
case XK_Left:
+ case XK_KP_Left:
if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
cursor = nextrune(-1);
break;
@@ -621,18 +599,21 @@ insert:
return;
/* fallthrough */
case XK_Up:
+ case XK_KP_Up:
if (sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev;
calcoffsets();
}
break;
case XK_Next:
+ case XK_KP_Next:
if (!next)
return;
sel = curr = next;
calcoffsets();
break;
case XK_Prior:
+ case XK_KP_Prior:
if (!prev)
return;
sel = curr = prev;
@@ -649,6 +630,7 @@ insert:
sel->out = 1;
break;
case XK_Right:
+ case XK_KP_Right:
if (text[cursor] != '\0') {
cursor = nextrune(+1);
break;
@@ -657,6 +639,7 @@ insert:
return;
/* fallthrough */
case XK_Down:
+ case XK_KP_Down:
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
calcoffsets();
@@ -665,9 +648,9 @@ insert:
case XK_Tab:
if (!sel)
return;
- strncpy(text, sel->text, sizeof text - 1);
- text[sizeof text - 1] = '\0';
- cursor = strlen(text);
+ cursor = strnlen(sel->text, sizeof text - 1);
+ memcpy(text, sel->text, cursor);
+ text[cursor] = '\0';
match();
break;
}
@@ -697,34 +680,32 @@ paste(void)
static void
readstdin(void)
{
- char buf[sizeof text], *p;
- size_t i, imax = 0, size = 0;
- unsigned int tmpmax = 0;
- if(passwd){
+ char *line = NULL;
+ size_t i, itemsiz = 0, linesiz = 0;
+ ssize_t len;
+
+ if (passwd){
inputw = lines = 0;
return;
}
-
/* read each line from stdin and add it to the item list */
- for (i = 0; fgets(buf, sizeof buf, stdin); i++) {
- if (i + 1 >= size / sizeof *items)
- if (!(items = realloc(items, (size += BUFSIZ))))
- die("cannot realloc %u bytes:", size);
- if ((p = strchr(buf, '\n')))
- *p = '\0';
- if (!(items[i].text = strdup(buf)))
- die("cannot strdup %u bytes:", strlen(buf) + 1);
- items[i].out = 0;
- drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL);
- if (tmpmax > inputw) {
- inputw = tmpmax;
- imax = i;
+ for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) {
+ if (i + 1 >= itemsiz) {
+ itemsiz += 256;
+ if (!(items = realloc(items, itemsiz * sizeof(*items))))
+ die("cannot realloc %zu bytes:", itemsiz * sizeof(*items));
}
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ if (!(items[i].text = strdup(line)))
+ die("strdup:");
+
+ items[i].out = 0;
}
+ free(line);
if (items)
items[i].text = NULL;
- inputw = items ? TEXTW(items[imax].text) : 0;
lines = MIN(lines, i);
}
@@ -790,10 +771,8 @@ setup(void)
/* calculate menu geometry */
bh = drw->fonts->h + 2;
- bh = MAX(bh,lineheight); /* make a menu line AT LEAST 'lineheight' tall */
lines = MAX(lines, 0);
mh = (lines + 1) * bh;
- promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
#ifdef XINERAMA
i = 0;
if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
@@ -817,19 +796,12 @@ setup(void)
/* no focused window is on screen, so use pointer location instead */
if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
for (i = 0; i < n; i++)
- if (INTERSECT(x, y, 1, 1, info[i]))
+ if (INTERSECT(x, y, 1, 1, info[i]) != 0)
break;
- if (centered) {
- mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width);
- x = info[i].x_org + ((info[i].width - mw) / 2);
- y = info[i].y_org + ((info[i].height - mh) / 2);
- } else {
- x = info[i].x_org;
- y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
- mw = info[i].width;
- }
-
+ x = info[i].x_org;
+ y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
+ mw = info[i].width;
XFree(info);
} else
#endif
@@ -837,32 +809,23 @@ setup(void)
if (!XGetWindowAttributes(dpy, parentwin, &wa))
die("could not get embedding window attributes: 0x%lx",
parentwin);
-
- if (centered) {
- mw = MIN(MAX(max_textw() + promptw, min_width), wa.width);
- x = (wa.width - mw) / 2;
- y = (wa.height - mh) / 2;
- } else {
- x = 0;
- y = topbar ? 0 : wa.height - mh;
- mw = wa.width;
- }
+ x = 0;
+ y = topbar ? 0 : wa.height - mh;
+ mw = wa.width;
}
promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
- inputw = MIN(inputw, mw/3);
+ inputw = mw / 3; /* input width: ~33% of monitor width */
match();
/* create menu window */
swa.override_redirect = True;
swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
- win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width,
+ win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
- XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel);
XSetClassHint(dpy, win, &ch);
-
/* input methods */
if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
die("XOpenIM failed: could not open input device");
@@ -872,6 +835,7 @@ setup(void)
XMapRaised(dpy, win);
if (embed) {
+ XReparentWindow(dpy, win, parentwin, x, y);
XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
for (i = 0; i < du && dws[i] != win; ++i)
@@ -887,11 +851,9 @@ setup(void)
static void
usage(void)
{
- fputs("usage: dmenu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
- " [-h height]\n"
- " [-nb color] [-nf color] [-sb color] [-sf color]\n"
- " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n", stderr);
- exit(1);
+ die("usage: dmenu [-bFfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
+ " [-nb color] [-nf color] [-sb color] [-sf color]\n"
+ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]");
}
int
@@ -907,12 +869,10 @@ main(int argc, char *argv[])
exit(0);
} else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
topbar = 0;
+ else if (!strcmp(argv[i], "-F")) /* disables fuzzy matching */
+ fuzzy = 0;
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
fast = 1;
- else if (!strcmp(argv[i], "-F")) /* grabs keyboard before reading stdin */
- fuzzy = 0;
- else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */
- centered = 1;
else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
fstrncmp = strncasecmp;
fstrstr = cistrstr;
@@ -929,10 +889,6 @@ main(int argc, char *argv[])
prompt = argv[++i];
else if (!strcmp(argv[i], "-fn")) /* font or font set */
fonts[0] = argv[++i];
- else if(!strcmp(argv[i], "-h")) { /* minimum height of one menu line */
- lineheight = atoi(argv[++i]);
- lineheight = MAX(lineheight,8); /* reasonable default in case of value too small/negative */
- }
else if (!strcmp(argv[i], "-nb")) /* normal background color */
colors[SchemeNorm][ColBg] = argv[++i];
else if (!strcmp(argv[i], "-nf")) /* normal foreground color */