summaryrefslogtreecommitdiff
path: root/core/dmenu/dmenu.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/dmenu/dmenu.c')
-rw-r--r--core/dmenu/dmenu.c331
1 files changed, 146 insertions, 185 deletions
diff --git a/core/dmenu/dmenu.c b/core/dmenu/dmenu.c
index 54a14e2..ebc2801 100644
--- a/core/dmenu/dmenu.c
+++ b/core/dmenu/dmenu.c
@@ -41,7 +41,8 @@ struct item {
static char text[BUFSIZ] = "";
static char *embed;
static int bh, mw, mh;
-static int inputw = 0, promptw;
+static int ih;
+static int promptw;
static int lrpad; /* sum of left and right padding */
static size_t cursor;
static struct item *items = NULL;
@@ -60,7 +61,9 @@ static Clr *scheme[SchemeLast];
#include "../accent.h"
static ColorShm *accentshm;
static char accentcol[8] = "#005577";
+static Clr accent[2];
static const double opacity = 0.85;
+static const unsigned int border_width = 3;
static void initaccent(void);
static void updateaccent(void);
@@ -68,14 +71,18 @@ static void setopacity(Window w, double opacity);
#include "config.h"
-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)
+static int
+fuzzy(const char *text, const char *pattern)
{
- unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
- return MIN(w, n);
+ for (; *pattern; pattern++) {
+ while (*text && tolower((unsigned char)*text) !=
+ tolower((unsigned char)*pattern))
+ text++;
+ if (!*text)
+ return 0;
+ text++;
+ }
+ return 1;
}
static void
@@ -94,19 +101,15 @@ appenditem(struct item *item, struct item **list, struct item **last)
static void
calcoffsets(void)
{
- int i, n;
-
- if (lines > 0)
- n = lines * bh;
- else
- 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 : textw_clamp(next->text, n)) > n)
- break;
- for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
- if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n)
- break;
+ int i, n;
+
+ n = lines * bh;
+ for (i = 0, next = curr; next; next = next->right)
+ if ((i += bh) > n)
+ break;
+ for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
+ if ((i += bh) > n)
+ break;
}
static void
@@ -128,8 +131,13 @@ cleanup(void)
static void
updateaccent(void)
{
- if (readaccent(&accentshm, accentcol))
+ if (readaccent(&accentshm, accentcol)) {
drw_clr_create(drw, &scheme[SchemeSel][ColBg], accentcol);
+ drw_clr_create(drw, &accent[ColFg], accentcol);
+ drw_clr_create(drw, &accent[ColBg], accentcol);
+ if (win)
+ XSetWindowBorder(dpy, win, accent[ColFg].pixel);
+ }
}
static void
@@ -137,24 +145,6 @@ initaccent(void)
{
updateaccent();
}
-static char *
-cistrstr(const char *h, const char *n)
-{
- size_t i;
-
- if (!n[0])
- return (char *)h;
-
- 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;
-}
-
static int
drawitem(struct item *item, int x, int y, int w)
{
@@ -171,50 +161,41 @@ drawitem(struct item *item, int x, int y, int w)
static void
drawmenu(void)
{
- unsigned int curpos;
- struct item *item;
- int x = 0, y = 0, w;
+ unsigned int curpos;
+ struct item *item;
+ int x = 0, y = 0, w;
- drw_setscheme(drw, scheme[SchemeNorm]);
- drw_rect(drw, 0, 0, mw, mh, 1, 1);
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, 0, 0, mw, mh, 1, 1);
- if (prompt && *prompt) {
- drw_setscheme(drw, scheme[SchemeSel]);
- x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
- }
- /* draw input field */
- w = (lines > 0 || !matches) ? mw - x : inputw;
- drw_setscheme(drw, scheme[SchemeNorm]);
- drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
+ if (prompt && *prompt) {
+ drw_setscheme(drw, scheme[SchemeSel]);
+ x = drw_text(drw, x, 0, promptw, ih, lrpad / 2, prompt, 0);
+ }
+ /* draw input field */
+ w = mw - x;
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_text(drw, x, 0, w, ih, lrpad / 2, text, 0);
+
+ curpos = TEXTW(text) - TEXTW(&text[cursor]);
+ if ((curpos += lrpad / 2 - 1) < w) {
+ int cy = (ih - bh) / 2 + 2;
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_rect(drw, x + curpos, cy, 2, bh - 4, 1, 0);
+ }
- curpos = TEXTW(text) - TEXTW(&text[cursor]);
- if ((curpos += lrpad / 2 - 1) < w) {
- drw_setscheme(drw, scheme[SchemeNorm]);
- drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
- }
+ drw_setscheme(drw, accent);
+ drw_rect(drw, 0, ih - 1, mw, 1, 1, 1);
- if (lines > 0) {
- /* draw vertical list */
- for (item = curr; item != next; item = item->right)
- drawitem(item, x, y += bh, mw - x);
- } else if (matches) {
- /* draw horizontal list */
- x += inputw;
- w = TEXTW("<");
- if (curr->left) {
- drw_setscheme(drw, scheme[SchemeNorm]);
- drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
- }
- x += w;
- for (item = curr; item != next; item = item->right)
- x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">")));
- if (next) {
- w = TEXTW(">");
- drw_setscheme(drw, scheme[SchemeNorm]);
- drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0);
- }
- }
- drw_map(drw, win, 0, 0, mw, mh);
+ y = ih;
+ for (item = curr; item != next; item = item->right) {
+ drawitem(item, x, y, mw - x);
+ drw_setscheme(drw, accent);
+ drw_rect(drw, x, y + bh - 1, mw - x, 1, 1, 1);
+ y += bh;
+ }
+
+ drw_map(drw, win, 0, 0, mw, mh);
}
static void
@@ -264,55 +245,32 @@ grabkeyboard(void)
static void
match(void)
{
- static char **tokv = NULL;
- static int tokn = 0;
-
- char buf[sizeof text], *s;
- int i, tokc = 0;
- size_t len, textsize;
- struct item *item, *lprefix, *lsubstr, *prefixend, *substrend;
-
- strcpy(buf, text);
- /* 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 %zu bytes:", tokn * sizeof *tokv);
- len = tokc ? strlen(tokv[0]) : 0;
-
- matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
- textsize = strlen(text) + 1;
- for (item = items; item && item->text; item++) {
- for (i = 0; i < tokc; i++)
- if (!fstrstr(item->text, tokv[i]))
- break;
- if (i != tokc) /* not all tokens match */
- continue;
- /* exact matches go first, then prefixes, then substrings */
- if (!tokc || !fstrncmp(text, item->text, textsize))
- appenditem(item, &matches, &matchend);
- else if (!fstrncmp(tokv[0], item->text, len))
- appenditem(item, &lprefix, &prefixend);
- else
- appenditem(item, &lsubstr, &substrend);
- }
- if (lprefix) {
- if (matches) {
- matchend->right = lprefix;
- lprefix->left = matchend;
- } else
- matches = lprefix;
- matchend = prefixend;
- }
- if (lsubstr) {
- if (matches) {
- matchend->right = lsubstr;
- lsubstr->left = matchend;
- } else
- matches = lsubstr;
- matchend = substrend;
- }
- curr = sel = matches;
- calcoffsets();
+ static char **tokv = NULL;
+ static int tokn = 0;
+
+ char buf[sizeof text], *s;
+ int i, tokc = 0;
+ struct item *item;
+
+ strcpy(buf, text);
+ /* separate input text into tokens to be matched individually */
+ for (s = strtok(buf, " "); s; s = strtok(NULL, " ")) {
+ if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
+ die("cannot realloc %zu bytes:", tokn * sizeof *tokv);
+ tokv[tokc - 1] = s;
+ }
+
+ matches = matchend = NULL;
+ for (item = items; item && item->text; item++) {
+ for (i = 0; i < tokc; i++)
+ if (!fuzzy(item->text, tokv[i]))
+ break;
+ if (i != tokc)
+ continue;
+ appenditem(item, &matches, &matchend);
+ }
+ curr = sel = matches;
+ calcoffsets();
}
static void
@@ -675,67 +633,71 @@ setup(void)
int a, di, n, area = 0;
#endif
/* init appearance */
- for (j = 0; j < SchemeLast; j++)
- scheme[j] = drw_scm_create(drw, colors[j], 2);
+ for (j = 0; j < SchemeLast; j++)
+ scheme[j] = drw_scm_create(drw, colors[j], 2);
+ drw_clr_create(drw, &accent[ColFg], accentcol);
+ drw_clr_create(drw, &accent[ColBg], accentcol);
clip = XInternAtom(dpy, "CLIPBOARD", False);
utf8 = XInternAtom(dpy, "UTF8_STRING", False);
- /* calculate menu geometry */
- bh = drw->fonts->h + 2;
- lines = MAX(lines, 0);
- mh = (lines + 1) * bh;
+ /* calculate menu geometry */
+ bh = drw->fonts->h + 2;
+ ih = (bh * 3) / 2;
#ifdef XINERAMA
- i = 0;
- if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
- XGetInputFocus(dpy, &w, &di);
- if (mon >= 0 && mon < n)
- i = mon;
- else if (w != root && w != PointerRoot && w != None) {
- /* find top-level window containing current input focus */
- do {
- if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
- XFree(dws);
- } while (w != root && w != pw);
- /* find xinerama screen with which the window intersects most */
- if (XGetWindowAttributes(dpy, pw, &wa))
- for (j = 0; j < n; j++)
- if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
- area = a;
- i = j;
- }
- }
- /* 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]) != 0)
- break;
-
- x = info[i].x_org;
- y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
- mw = info[i].width;
- XFree(info);
- } else
+ i = 0;
+ if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
+ XGetInputFocus(dpy, &w, &di);
+ if (mon >= 0 && mon < n)
+ i = mon;
+ else if (w != root && w != PointerRoot && w != None) {
+ do {
+ if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
+ XFree(dws);
+ } while (w != root && w != pw);
+ if (XGetWindowAttributes(dpy, pw, &wa))
+ for (j = 0; j < n; j++)
+ if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
+ area = a;
+ i = j;
+ }
+ }
+ 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]) != 0)
+ break;
+
+ mw = info[i].width / 2;
+ int max_lines = (info[i].height - ih) / bh;
+ lines = lines ? MIN(lines, max_lines) : MIN(20, max_lines);
+ mh = ih + lines * bh;
+ x = info[i].x_org + (info[i].width - mw) / 2;
+ y = info[i].y_org + (info[i].height - mh) / 2;
+ XFree(info);
+ } else
#endif
- {
- if (!XGetWindowAttributes(dpy, parentwin, &wa))
- die("could not get embedding window attributes: 0x%lx",
- parentwin);
- x = 0;
- y = topbar ? 0 : wa.height - mh;
- mw = wa.width;
- }
- promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
- inputw = mw / 3; /* input width: ~33% of monitor width */
- match();
+ {
+ if (!XGetWindowAttributes(dpy, parentwin, &wa))
+ die("could not get embedding window attributes: 0x%lx",
+ parentwin);
+ mw = wa.width / 2;
+ int max_lines = (wa.height - ih) / bh;
+ lines = lines ? MIN(lines, max_lines) : MIN(20, max_lines);
+ mh = ih + lines * bh;
+ x = (wa.width - mw) / 2;
+ y = (wa.height - mh) / 2;
+ }
+ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
+ match();
/* create menu window */
swa.override_redirect = True;
swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
- win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
- CopyFromParent, CopyFromParent, CopyFromParent,
- CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+ swa.border_pixel = accent[ColFg].pixel;
+ win = XCreateWindow(dpy, root, x, y, mw, mh, border_width,
+ CopyFromParent, CopyFromParent, CopyFromParent,
+ CWOverrideRedirect | CWBackPixel | CWEventMask | CWBorderPixel, &swa);
XSetClassHint(dpy, win, &ch);
/* input methods */
@@ -782,12 +744,11 @@ 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")) /* grabs keyboard before reading stdin */
- fast = 1;
- else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
- fstrncmp = strncasecmp;
- fstrstr = cistrstr;
- } else if (i + 1 == argc)
+ else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
+ fast = 1;
+ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
+ /* no-op: matching is case-insensitive */
+ } else if (i + 1 == argc)
usage();
/* these options take one argument */
else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */