diff options
author | Joe <rbo@gmx.us> | 2024-12-26 16:04:49 +0100 |
---|---|---|
committer | Joe <rbo@gmx.us> | 2024-12-26 16:04:49 +0100 |
commit | 9c8bc4e708647be16b6c69a28603c3542db9c100 (patch) | |
tree | 18ef2626c9db480d5122b99d34c2b40fa1afcd01 /dmenu.c | |
parent | up (diff) | |
download | dmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.tar.gz dmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.tar.bz2 dmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.tar.xz dmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.tar.zst dmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.zip |
5.3
Diffstat (limited to 'dmenu.c')
-rw-r--r-- | dmenu.c | 302 |
1 files changed, 129 insertions, 173 deletions
@@ -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 */ |