diff --git a/config.def.h b/config.def.h index 1edb647..24cbb58 100644 --- a/config.def.h +++ b/config.def.h @@ -2,22 +2,43 @@ /* Default settings; can be overriden by command line. */ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +static int fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */ +static int centered = 0; /* -c option; centers dmenu on screen */ +static int min_width = 500; /* minimum width when centered */ /* -fn option overrides fonts[0]; default X11 font or font set */ static const char *fonts[] = { - "monospace:size=10" + "monospace:size=10", + "mononoki Nerd Font:size=12", }; static const char *prompt = NULL; /* -p option; prompt to the left of input field */ -static const char *colors[SchemeLast][2] = { +static const char *colors[SchemeLast][10] = { /* fg bg */ - [SchemeNorm] = { "#bbbbbb", "#222222" }, - [SchemeSel] = { "#eeeeee", "#005577" }, - [SchemeOut] = { "#000000", "#00ffff" }, + [SchemeNorm] = { "#bbbbbb", "#222222", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577", "#005577" }, + [SchemeNormHighlight] = { "#ffc978", "#222222", "#222222" }, + [SchemeSelHighlight] = { "#ffc978", "#005577", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff", "#00ffff" }, + [SchemeHighlight] = {"#f1fa8c", "#596377", "#3E485B"}, + [SchemeHover] = {"#ffffff", "#353D4B", "#3E485B"}, + [SchemeGreen] = {"#ffffff", "#52E067", "#41b252"}, + [SchemeRed] = {"#ffffff", "#e05252", "#c24343"}, + [SchemeYellow] = {"#ffffff", "#e0c452", "#bca33f"}, + [SchemeBlue] = {"#ffffff", "#5280e0", "#3a62b3"}, + [SchemePurple] = {"#ffffff", "#9952e0", "#7439b0"}, }; /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ static unsigned int lines = 0; +static unsigned int lineheight = 0; /* -h option; minimum height of a menu line */ +static unsigned int min_lineheight = 8; +static int sely = 0; +static int commented = 0; +static int animated = 0; /* * Characters not considered part of a word while deleting words * for example: " /?\"&[]" */ static const char worddelimiters[] = " "; + +/* Size of the window border */ +static unsigned int border_width = 0; diff --git a/config.mk b/config.mk index 05d5a3e..cbb87eb 100644 --- a/config.mk +++ b/config.mk @@ -20,11 +20,11 @@ FREETYPEINC = /usr/include/freetype2 # includes and libs INCS = -I$(X11INC) -I$(FREETYPEINC) -LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm # flags CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) -CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) +CFLAGS = -std=c99 -pedantic -Wall -O3 -march=native $(INCS) $(CPPFLAGS) LDFLAGS = $(LIBS) # compiler and linker diff --git a/dmenu.1 b/dmenu.1 index 323f93c..36926c2 100644 --- a/dmenu.1 +++ b/dmenu.1 @@ -3,9 +3,11 @@ dmenu \- dynamic menu .SH SYNOPSIS .B dmenu -.RB [ \-bfiv ] +.RB [ \-bfsrvP ] .RB [ \-l .IR lines ] +.RB [ \-h +.IR height ] .RB [ \-m .IR monitor ] .RB [ \-p @@ -20,8 +22,20 @@ dmenu \- dynamic menu .IR color ] .RB [ \-sf .IR color ] +.RB [ \-nhb +.IR color ] +.RB [ \-nhf +.IR color ] +.RB [ \-shb +.IR color ] +.RB [ \-shf +.IR color ] .RB [ \-w .IR windowid ] +.RB [ \-d +.IR separator ] +.RB [ \-D +.IR separator ] .P .BR dmenu_run " ..." .SH DESCRIPTION @@ -40,16 +54,28 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. .B \-b dmenu appears at the bottom of the screen. .TP +.B \-c +dmenu appears centered on the screen. +.TP .B \-f dmenu grabs the keyboard before reading stdin if not reading from a tty. This is faster, but will lock up X until stdin reaches end\-of\-file. .TP -.B \-i -dmenu matches menu items case insensitively. +.B \-s +dmenu matches menu items case sensitively. +.TP +.B \-P +dmenu will not directly display the keyboard input, but instead replace it with dots. All data from stdin will be ignored. +.TP +.B \-r +dmenu will reject any input which would result in no matching option left. .TP .BI \-l " lines" dmenu lists items vertically, with the given number of lines. .TP +.BI \-h " height" +dmenu uses a menu line of at least 'height' pixels tall, but no less than 8. +.TP .BI \-m " monitor" dmenu is displayed on the monitor number supplied. Monitor numbers are starting from 0. @@ -75,11 +101,31 @@ defines the selected background color. .BI \-sf " color" defines the selected foreground color. .TP +.BI \-nhb " color" +defines the normal highlight background color. +.TP +.BI \-nhf " color" +defines the normal highlight foreground color. +.TP +.BI \-shb " color" +defines the selected highlight background color. +.TP +.BI \-shf " color" +defines the selected highlight foreground color. +.TP .B \-v prints version information to stdout, then exits. .TP .BI \-w " windowid" embed into windowid. +.TP +.BI \-d " separator" +separate the input into two halves on the first occurrence of the given charcter. +Display only the first half in dmenu and print the second half to stdout upon selection. +Appending '|' to the separator reverses the display/printing order. +.TP +.BI \-D " separator" +same as \-d but separate based on the last occurrence. .SH USAGE dmenu is completely controlled by the keyboard. Items are selected using the arrow keys, page up, page down, home, and end. diff --git a/dmenu.c b/dmenu.c index 65f25ce..0f4d6b4 100644 --- a/dmenu.c +++ b/dmenu.c @@ -1,6 +1,7 @@ /* See LICENSE file for copyright and license details. */ #include #include +#include #include #include #include @@ -22,23 +23,49 @@ /* 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 INTERSECTBOOL(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, SchemeLast }; /* color schemes */ +enum { + SchemeNorm, + SchemeFade, + SchemeHighlight, + SchemeHover, + SchemeSel, + SchemeNormHighlight, + SchemeSelHighlight, + SchemeOut, + SchemeGreen, + SchemeYellow, + SchemeBlue, + SchemePurple, + SchemeRed, + SchemeLast +}; /* color schemes */ struct item { char *text; + char *text_output; struct item *left, *right; int out; + double distance; }; static char text[BUFSIZ] = ""; static char *embed; +static char separator; +static int separator_greedy; +static int separator_reverse; static int bh, mw, mh; -static int inputw = 0, promptw; +static int dmx = 0; /* put dmenu at this x offset */ +static int dmy = 0; /* put dmenu at this y offset (measured from the bottom if topbar is 0) */ +static unsigned int dmw = 0; /* make dmenu this wide */ +static int inputw = 0, promptw, passwd = 0; static int lrpad; /* sum of left and right padding */ +static int reject_no_match = 0; static size_t cursor; static struct item *items = NULL; static struct item *matches, *matchend; @@ -55,8 +82,9 @@ static Clr *scheme[SchemeLast]; #include "config.h" -static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; -static char *(*fstrstr)(const char *, const char *) = strstr; +static char * cistrstr(const char *s, const char *sub); +static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp; +static char *(*fstrstr)(const char *, const char *) = cistrstr; static void appenditem(struct item *item, struct item **list, struct item **last) @@ -89,6 +117,15 @@ calcoffsets(void) 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) { @@ -113,17 +150,160 @@ cistrstr(const char *s, const char *sub) return NULL; } -static int -drawitem(struct item *item, int x, int y, int w) +static void +drawhighlights(struct item *item, int x, int y, int maxw) { - if (item == sel) - drw_setscheme(drw, scheme[SchemeSel]); - else if (item->out) - drw_setscheme(drw, scheme[SchemeOut]); - else - drw_setscheme(drw, scheme[SchemeNorm]); + int i, indent; + char *highlight; + char c; + + if (!(strlen(item->text) && strlen(text))) + return; + + drw_setscheme(drw, scheme[item == sel + ? SchemeSelHighlight + : SchemeNormHighlight]); + + for (i = 0, highlight = item->text; *highlight && text[i];) { + if (*highlight == text[i]) { + /* get indentation */ + c = *highlight; + *highlight = '\0'; + indent = TEXTW(item->text); + *highlight = c; + + /* highlight character */ + c = highlight[1]; + highlight[1] = '\0'; + drw_text( + drw, + x + indent - (lrpad / 2), + y, + MIN(maxw - indent, TEXTW(highlight) - lrpad), + bh, 0, highlight, 0 + ); + highlight[1] = c; + i++; + } + highlight++; + } +} - return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); +static int +drawitem(struct item *item, int x, int y, int w) { + int r; + int iscomment = 0; + if (item->text[0] == '>') { + if (item->text[1] == '>') { + iscomment = 3; + switch (item->text[2]) { + case 'r': + drw_setscheme(drw, scheme[SchemeRed]); + break; + case 'g': + drw_setscheme(drw, scheme[SchemeGreen]); + break; + case 'y': + drw_setscheme(drw, scheme[SchemeYellow]); + break; + case 'b': + drw_setscheme(drw, scheme[SchemeBlue]); + break; + case 'p': + drw_setscheme(drw, scheme[SchemePurple]); + break; + case 'h': + drw_setscheme(drw, scheme[SchemeHighlight]); + break; + case 's': + drw_setscheme(drw, scheme[SchemeSel]); + break; + default: + iscomment = 1; + drw_setscheme(drw, scheme[SchemeNorm]); + break; + } + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + iscomment = 1; + } + + } else if (item->text[0] == ':') { + iscomment = 2; + if (item == sel) { + switch (item->text[1]) { + case 'r': + drw_setscheme(drw, scheme[SchemeRed]); + break; + case 'g': + drw_setscheme(drw, scheme[SchemeGreen]); + break; + case 'y': + drw_setscheme(drw, scheme[SchemeYellow]); + break; + case 'b': + drw_setscheme(drw, scheme[SchemeBlue]); + break; + case 'p': + drw_setscheme(drw, scheme[SchemePurple]); + break; + case 'h': + drw_setscheme(drw, scheme[SchemeHighlight]); + break; + case 's': + drw_setscheme(drw, scheme[SchemeSel]); + break; + default: + drw_setscheme(drw, scheme[SchemeSel]); + iscomment = 0; + break; + } + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + } + } else { + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + } + + int temppadding; + temppadding = 0; + if (iscomment == 2) { + if (item->text[2] == ' ') { + temppadding = drw->fonts->h * 3; + animated = 1; + char dest[1000]; + strcpy(dest, item->text); + dest[6] = '\0'; + drw_text(drw, x, y, temppadding, lineheight, temppadding / 2.6, dest + 3, 0); + iscomment = 6; + drw_setscheme(drw, sel == item ? scheme[SchemeHover] : scheme[SchemeNorm]); + } + } + + char *output; + if (commented) { + static char onestr[2]; + onestr[0] = item->text[0]; + onestr[1] = '\0'; + output = onestr; + } else { + output = item->text; + } + + if (item == sel) + sely = y; + r = drw_text( + drw, x + ((iscomment == 6) ? temppadding : 0), y, + commented ? bh : (w - ((iscomment == 6) ? temppadding : 0)), bh, + commented ? (bh - drw_fontset_getwidth(drw, (output))) / 2 : lrpad / 2, + output + iscomment, 0); + drawhighlights(item, x, y, w); + return r; } static void @@ -131,7 +311,8 @@ drawmenu(void) { unsigned int curpos; struct item *item; - int x = 0, y = 0, w; + int x = 0, y = 0, fh = drw->fonts->h, w; + char *censort; drw_setscheme(drw, scheme[SchemeNorm]); drw_rect(drw, 0, 0, mw, mh, 1, 1); @@ -143,12 +324,17 @@ drawmenu(void) /* 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 (passwd) { + censort = ecalloc(1, sizeof(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); 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_rect(drw, x + curpos, 2 + (bh - fh) / 2, 2, fh - 4, 1, 0); } if (lines > 0) { @@ -210,9 +396,94 @@ grabkeyboard(void) die("cannot grab keyboard"); } +int +compare_distance(const void *a, const void *b) +{ + struct item *da = *(struct item **) a; + struct item *db = *(struct item **) b; + + if (!db) + return 1; + if (!da) + return -1; + + return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1; +} + +void +fuzzymatch(void) +{ + /* bang - we have so much memory */ + struct item *it; + struct item **fuzzymatches = NULL; + char c; + int number_of_matches = 0, i, pidx, sidx, eidx; + int text_len = strlen(text), itext_len; + + matches = matchend = NULL; + + /* walk through all items */ + 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++) { + /* fuzzy match pattern */ + if (!fstrncmp(&text[pidx], &c, 1)) { + if(sidx == -1) + sidx = i; + pidx++; + if (pidx == text_len) { + eidx = i; + break; + } + } + } + /* build list of matches */ + if (eidx != -1) { + /* compute distance */ + /* add penalty if match starts late (log(sidx+2)) + * add penalty for long a match without many matching characters */ + 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++; + } + } else { + appenditem(it, &matches, &matchend); + } + } + + 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) { + 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]) { + appenditem(it, &matches, &matchend); + } + free(fuzzymatches); + } + curr = sel = matches; + calcoffsets(); +} + static void match(void) { + if (fuzzy) { + fuzzymatch(); + return; + } static char **tokv = NULL; static int tokn = 0; @@ -269,12 +540,26 @@ insert(const char *str, ssize_t n) { if (strlen(text) + n > sizeof text - 1) return; + + static char last[BUFSIZ] = ""; + if(reject_no_match) { + /* store last text value in case we need to revert it */ + memcpy(last, text, BUFSIZ); + } + /* move existing text out of the way, insert new text, and update cursor */ memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); if (n > 0) memcpy(&text[cursor], str, n); cursor += n; match(); + + if(!matches && reject_no_match) { + /* revert to last text value if theres no match */ + memcpy(text, last, BUFSIZ); + cursor -= n; + match(); + } } static size_t @@ -464,7 +749,7 @@ insert: break; case XK_Return: case XK_KP_Enter: - puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + puts((sel && !(ev->state & ShiftMask)) ? sel->text_output : text); if (!(ev->state & ControlMask)) { cleanup(); exit(0); @@ -500,6 +785,119 @@ draw: drawmenu(); } +static void +buttonpress(XEvent *e) +{ + struct item *item; + XButtonPressedEvent *ev = &e->xbutton; + int x = 0, y = 0, h = bh, w; + + if (ev->window != win) + return; + + /* right-click: exit */ + if (ev->button == Button3) + exit(1); + + if (prompt && *prompt) + x += promptw; + + /* input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + + /* left-click on input: clear input, + * NOTE: if there is no left-arrow the space for < is reserved so + * add that to the input width */ + if (ev->button == Button1 && + ((lines <= 0 && ev->x >= 0 && ev->x <= x + w + + ((!prev || !curr->left) ? TEXTW("<") : 0)) || + (lines > 0 && ev->y >= y && ev->y <= y + h))) { + insert(NULL, -cursor); + drawmenu(); + return; + } + /* middle-mouse click: paste selection */ + if (ev->button == Button2) { + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + drawmenu(); + return; + } + /* scroll up */ + if (ev->button == Button4 && prev) { + sel = curr = prev; + calcoffsets(); + drawmenu(); + return; + } + /* scroll down */ + if (ev->button == Button5 && next) { + sel = curr = next; + calcoffsets(); + drawmenu(); + return; + } + if (ev->button != Button1) + return; + if (ev->state & ~ControlMask) + return; + if (lines > 0) { + /* vertical list: (ctrl)left-click on item */ + w = mw - x; + for (item = curr; item != next; item = item->right) { + y += h; + if (ev->y >= y && ev->y <= (y + h)) { + puts(item->text); + if (!(ev->state & ControlMask)) + exit(0); + sel = item; + if (sel) { + sel->out = 1; + drawmenu(); + } + return; + } + } + } else if (matches) { + /* left-click on left arrow */ + x += inputw; + w = TEXTW("<"); + if (prev && curr->left) { + if (ev->x >= x && ev->x <= x + w) { + sel = curr = prev; + calcoffsets(); + drawmenu(); + return; + } + } + /* horizontal list: (ctrl)left-click on item */ + for (item = curr; item != next; item = item->right) { + x += w; + w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); + if (ev->x >= x && ev->x <= x + w) { + puts(item->text); + if (!(ev->state & ControlMask)) + exit(0); + sel = item; + if (sel) { + sel->out = 1; + drawmenu(); + } + return; + } + } + /* left-click on right arrow */ + w = TEXTW(">"); + x = mw - w; + if (next && ev->x >= x && ev->x <= x + w) { + sel = curr = next; + calcoffsets(); + drawmenu(); + return; + } + } +} + static void paste(void) { @@ -524,6 +922,11 @@ readstdin(void) char buf[sizeof text], *p; size_t i, imax = 0, size = 0; unsigned int tmpmax = 0; + 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++) { @@ -534,6 +937,18 @@ readstdin(void) *p = '\0'; if (!(items[i].text = strdup(buf))) die("cannot strdup %u bytes:", strlen(buf) + 1); + if (separator && (p = (separator_greedy) ? + strrchr(items[i].text, separator) : strchr(items[i].text, separator))) { + *p = '\0'; + items[i].text_output = ++p; + } else { + items[i].text_output = items[i].text; + } + if (separator_reverse) { + char *tmp = items[i].text; + items[i].text = items[i].text_output; + items[i].text_output = tmp; + } items[i].out = 0; drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); if (tmpmax > inputw) { @@ -561,6 +976,9 @@ run(void) break; cleanup(); exit(1); + case ButtonPress: + buttonpress(&ev); + break; case Expose: if (ev.xexpose.count == 0) drw_map(drw, win, 0, 0, mw, mh); @@ -609,8 +1027,10 @@ 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))) { @@ -634,12 +1054,18 @@ 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 (INTERSECTBOOL(x, y, 1, 1, info[i])) break; - x = info[i].x_org; - y = info[i].y_org + (topbar ? 0 : info[i].height - mh); - mw = info[i].width; + 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; + } XFree(info); } else #endif @@ -647,21 +1073,29 @@ setup(void) 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; + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); + x = (wa.width - mw) / 2; + y = (wa.height - mh) / 2; + } else { + x = dmx; + y = topbar ? dmy : wa.height - mh; + mw = wa.width; + } } - promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; inputw = MIN(inputw, mw/3); 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, 0, + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | + ButtonPressMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + if (border_width) + XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); XSetClassHint(dpy, win, &ch); @@ -689,8 +1123,10 @@ setup(void) static void usage(void) { - fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" - " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); + fputs("usage: dmenu [-bfsrvP] [-l lines] [-h height] [-p prompt] [-fn font] [-m monitor]\n" + " [-x xoffset] [-y yoffset] [-z width] [-nb color] [-nf color] [-sb color] [-sf color]\n" + " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n" + " [-d separator] [-D separator]\n", stderr); exit(1); } @@ -709,14 +1145,32 @@ main(int argc, char *argv[]) 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")) /* is not fuzzy search */ + fuzzy = 0; + else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ + centered = 1; + else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */ + fstrncmp = strncmp; + fstrstr = strstr; + } else if (!strcmp(argv[i], "-r")) /* reject input which results in no match */ + reject_no_match = 1; + else if (!strcmp(argv[i], "-P")) /* is the input a password */ + passwd = 1; + else if (i + 1 == argc) usage(); /* these options take one argument */ else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ lines = atoi(argv[++i]); + else if (!strcmp(argv[i], "-x")) /* window x offset */ + dmx = atoi(argv[++i]); + else if (!strcmp(argv[i], "-y")) /* window x offset */ + dmy = atoi(argv[++i]); + else if (!strcmp(argv[i], "-z")) /* window x offset */ + dmw = atoi(argv[++i]); + else if (!strcmp(argv[i], "-h")) { /* minimum height of one menu line */ + lineheight = atoi(argv[++i]); + lineheight = MAX(lineheight, min_lineheight); + } else if (!strcmp(argv[i], "-m")) mon = atoi(argv[++i]); else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ @@ -731,8 +1185,23 @@ main(int argc, char *argv[]) colors[SchemeSel][ColBg] = argv[++i]; else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ + colors[SchemeNormHighlight][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ + colors[SchemeNormHighlight][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ + colors[SchemeSelHighlight][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ + colors[SchemeSelHighlight][ColFg] = argv[++i]; else if (!strcmp(argv[i], "-w")) /* embedding window id */ embed = argv[++i]; + else if (!strcmp(argv[i], "-bw")) /* border width */ + border_width = atoi(argv[++i]); + else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "-D")) { /* field separator */ + separator_reverse = (*(argv[i+1]+1) == '|'); + separator_greedy = !strcmp(argv[i], "-D"); + separator = *argv[++i]; + } else usage(); @@ -752,6 +1221,9 @@ main(int argc, char *argv[]) die("no fonts could be loaded."); lrpad = drw->fonts->h; + if (lineheight == -1) + lineheight = drw->fonts->h * 2.5; + #ifdef __OpenBSD__ if (pledge("stdio rpath", NULL) == -1) die("pledge");