summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorFranklyn Tackitt2015-06-10 09:04:50 -0700
committerFranklyn Tackitt2015-06-10 09:04:50 -0700
commitf1a8b6b7e7c4c2f675bd1f775cf647743b53c6ff (patch)
tree6d91ecf14c14729ae1c6d5b8d64267f6940b99bf
downloadaur-f1a8b6b7e7c4c2f675bd1f775cf647743b53c6ff.tar.gz
Initial Import
-rw-r--r--.SRCINFO31
-rw-r--r--PKGBUILD43
-rw-r--r--break-fix.diff23
-rw-r--r--dmenu-4.5-fuzzy-fixed.diff94
-rw-r--r--dmenu-4.5-height-fixed.diff84
-rw-r--r--dmenu-4.5-history-fixed.diff216
-rw-r--r--dmenu-4.5-mouse-support.diff143
-rw-r--r--dmenu-4.5-xft.diff418
8 files changed, 1052 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..f11d6db9fbfb
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,31 @@
+pkgbase = dmenu-xft-mouse-height-fuzzy-history
+ pkgdesc = Dynamic X menu - with xft, mouse, height, history, and fuzzy search support
+ pkgver = 4.5
+ pkgrel = 5
+ url = http://tools.suckless.org/dmenu/
+ arch = i686
+ arch = x86_64
+ license = MIT
+ depends = sh
+ depends = libxinerama
+ depends = libxft
+ provides = dmenu
+ conflicts = dmenu
+ conflicts = dmenu2
+ source = http://dl.suckless.org/tools/dmenu-4.5.tar.gz
+ source = dmenu-4.5-xft.diff
+ source = break-fix.diff
+ source = dmenu-4.5-history-fixed.diff
+ source = dmenu-4.5-fuzzy-fixed.diff
+ source = dmenu-4.5-mouse-support.diff
+ source = dmenu-4.5-height-fixed.diff
+ md5sums = 9c46169ed703732ec52ed946c27d84b4
+ md5sums = 0c73d595eb78f159bea83f33bba15e80
+ md5sums = 6921f9d8aabb53f22adcbf5630dff6b8
+ md5sums = 8541735789d9810d7020fdba62b72296
+ md5sums = 71fc82b76c45499fcd46b3754407f59d
+ md5sums = eeec3e11ff68f27ebbc3133ad6549f56
+ md5sums = 53b286e8bd76d9225f365673fafd6083
+
+pkgname = dmenu-xft-mouse-height-fuzzy-history
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..14c3d338f494
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,43 @@
+# Maintainer: Franklyn Tackitt
+pkgname=dmenu-xft-mouse-height-fuzzy-history
+pkgver=4.5
+pkgrel=5
+pkgdesc="Dynamic X menu - with xft, mouse, height, history, and fuzzy search support"
+url="http://tools.suckless.org/dmenu/"
+arch=('i686' 'x86_64')
+license=('MIT')
+depends=('sh' 'libxinerama' 'libxft')
+conflicts=('dmenu' 'dmenu2')
+provides=('dmenu')
+patches=(dmenu-4.5-xft.diff
+ break-fix.diff
+ dmenu-4.5-history-fixed.diff
+ dmenu-4.5-fuzzy-fixed.diff
+ dmenu-4.5-mouse-support.diff
+ dmenu-4.5-height-fixed.diff)
+source=(http://dl.suckless.org/tools/dmenu-$pkgver.tar.gz "${patches[@]}")
+md5sums=('9c46169ed703732ec52ed946c27d84b4'
+ '0c73d595eb78f159bea83f33bba15e80'
+ '6921f9d8aabb53f22adcbf5630dff6b8'
+ '8541735789d9810d7020fdba62b72296'
+ '71fc82b76c45499fcd46b3754407f59d'
+ 'eeec3e11ff68f27ebbc3133ad6549f56'
+ '53b286e8bd76d9225f365673fafd6083')
+prepare() {
+ cd $srcdir/dmenu-$pkgver
+ for patch in "${patches[@]}"; do
+ echo "Patching $patch"
+ patch -p1 < "../${patch}"
+ done
+}
+build() {
+ cd $srcdir/dmenu-$pkgver
+ make
+}
+package()
+{
+ cd "$srcdir/dmenu-$pkgver"
+ make DESTDIR=$pkgdir PREFIX=/usr install
+ install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
+
+}
diff --git a/break-fix.diff b/break-fix.diff
new file mode 100644
index 000000000000..989ee6707961
--- /dev/null
+++ b/break-fix.diff
@@ -0,0 +1,23 @@
+diff -upr dmenu-4.5-xft/dmenu.c dmenu-4.5-break/dmenu.c
+--- dmenu-4.5-xft/dmenu.c 2014-05-04 17:32:10.896300286 -0600
++++ dmenu-4.5-break/dmenu.c 2014-05-04 17:31:41.406300227 -0600
+@@ -337,8 +337,9 @@ keypress(XKeyEvent *ev) {
+ sel = matchend;
+ break;
+ case XK_Escape:
+- ret = EXIT_FAILURE;
+- running = False;
++ ret = EXIT_FAILURE;
++ running = False;
++ break;
+ case XK_Home:
+ if(sel == matches) {
+ cursor = 0;
+@@ -378,6 +379,7 @@ keypress(XKeyEvent *ev) {
+ puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
+ ret = EXIT_SUCCESS;
+ running = False;
++ break;
+ case XK_Right:
+ if(text[cursor] != '\0') {
+ cursor = nextrune(+1);
diff --git a/dmenu-4.5-fuzzy-fixed.diff b/dmenu-4.5-fuzzy-fixed.diff
new file mode 100644
index 000000000000..2db2cf2b026d
--- /dev/null
+++ b/dmenu-4.5-fuzzy-fixed.diff
@@ -0,0 +1,94 @@
+diff -rupN orig/dmenu.c new/dmenu.c
+--- orig/dmenu.c 2015-02-03 11:21:10.802786099 -0700
++++ new/dmenu.c 2015-02-03 11:22:45.321029644 -0700
+@@ -34,6 +34,9 @@ static void grabkeyboard(void);
+ static void insert(const char *str, ssize_t n);
+ static void keypress(XKeyEvent *ev);
+ static void match(void);
++static void match_fuzzy(void);
++static void match_tokens(void);
++static char *strchri(const char *s, int c);
+ static size_t nextrune(int inc);
+ static void paste(void);
+ static void readitems(void);
+@@ -64,12 +67,14 @@ static Item *matches, *matchend;
+ static Item *prev, *curr, *next, *sel;
+ static Window win;
+ static XIC xic;
++static Bool fuzzy;
+
+ static char *histfile = NULL;
+ static Item *histitems, *histend;
+
+ static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
+ static char *(*fstrstr)(const char *, const char *) = strstr;
++static char *(*fstrchr)(const char *, const int) = strchr;
+
+ int
+ main(int argc, char *argv[]) {
+@@ -86,9 +91,12 @@ main(int argc, char *argv[]) {
+ topbar = False;
+ else if(!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
+ fast = True;
++ else if(!strcmp(argv[i], "-z")) /* enable fuzzy matching */
++ fuzzy = True;
+ else if(!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
+ fstrncmp = strncasecmp;
+ fstrstr = cistrstr;
++ fstrchr = strchri;
+ }
+ else if(i+1 == argc)
+ usage();
+@@ -464,8 +472,52 @@ keypress(XKeyEvent *ev) {
+ drawmenu();
+ }
+
++char *
++strchri(const char *s, int c) {
++ char *u, *l;
++ if(!isalpha(c)) return strchr(s, c);
++ if(isupper(c)) {
++ u = strchr(s, c);
++ l = strchr(s, tolower(c));
++ }
++ else {
++ l = strchr(s, c);
++ u = strchr(s, toupper(c));
++ }
++
++ if(u && l) return u < l ? u : l;
++ return u == NULL ? l : u;
++}
++
+ void
+ match(void) {
++ if(fuzzy) match_fuzzy();
++ else match_tokens();
++}
++
++void
++match_fuzzy(void) {
++ int i;
++ size_t len;
++ Item *item;
++
++ char *pos;
++
++ len = strlen(text);
++
++ matches = matchend = NULL;
++ for(item = items; item && item->text; item++) {
++ i = 0;
++ for(pos = fstrchr(item->text, text[i]); pos && text[i]; i++, pos = fstrchr(pos+1, text[i]));
++ if(i == len) appenditem(item, &matches, &matchend);
++ }
++
++ curr = sel = matches;
++ calcoffsets();
++}
++
++void
++match_tokens(void) {
+ static char **tokv = NULL;
+ static int tokn = 0;
+
diff --git a/dmenu-4.5-height-fixed.diff b/dmenu-4.5-height-fixed.diff
new file mode 100644
index 000000000000..8186a90f3668
--- /dev/null
+++ b/dmenu-4.5-height-fixed.diff
@@ -0,0 +1,84 @@
+diff -rupN orig/dmenu.1 new/dmenu.1
+--- orig/dmenu.1 2015-02-03 11:24:25.218280503 -0700
++++ new/dmenu.1 2015-02-03 11:24:34.434303318 -0700
+@@ -8,6 +8,8 @@ dmenu \- dynamic menu
+ .RB [ \-i ]
+ .RB [ \-l
+ .IR lines ]
++.RB [ \-h
++.IR height ]
+ .RB [ \-p
+ .IR prompt ]
+ .RB [ \-fn
+@@ -51,6 +53,9 @@ dmenu matches menu items case insensitiv
+ .BI \-l " lines"
+ dmenu lists items vertically, with the given number of lines.
+ .TP
++.BI \-h " height"
++defines the height of the bar in pixels.
++.TP
+ .BI \-p " prompt"
+ defines the prompt to be displayed to the left of the input field.
+ .TP
+diff -rupN orig/dmenu.c new/dmenu.c
+--- orig/dmenu.c 2015-02-03 11:24:25.218280503 -0700
++++ new/dmenu.c 2015-02-03 11:25:32.430445657 -0700
+@@ -55,7 +55,7 @@ static const char *normbgcolor = "#22222
+ static const char *normfgcolor = "#bbbbbb";
+ static const char *selbgcolor = "#005577";
+ static const char *selfgcolor = "#eeeeee";
+-static unsigned int lines = 0;
++static unsigned int lines, line_height = 0;
+ static ColorSet *normcol;
+ static ColorSet *selcol;
+ static Atom clip, utf8;
+@@ -104,6 +104,8 @@ main(int argc, char *argv[]) {
+ /* 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], "-h")) /* minimum height of single line */
++ line_height = atoi(argv[++i]);
+ else if(!strcmp(argv[i], "-p")) /* adds prompt to left of input field */
+ prompt = argv[++i];
+ else if(!strcmp(argv[i], "-fn")) /* font or font set */
+@@ -260,8 +262,8 @@ drawmenu(void) {
+ /* draw input field */
+ dc->w = (lines > 0 || !matches) ? mw - dc->x : inputw;
+ drawtext(dc, text, normcol);
+- if((curpos = textnw(dc, text, cursor) + dc->h/2 - 2) < dc->w)
+- drawrect(dc, curpos, 2, 1, dc->h - 4, True, normcol->FG);
++ if((curpos = textnw(dc, text, cursor) + dc->font.height/2) < dc->w)
++ drawrect(dc, curpos, (dc->h - dc->font.height)/2 + 1, 1, dc->font.height -1, True, normcol->FG);
+
+ if(lines > 0) {
+ /* draw vertical list */
+@@ -800,7 +802,7 @@ setup(void) {
+ utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False);
+
+ /* calculate menu geometry */
+- bh = dc->font.height + 2;
++ bh = (line_height > dc->font.height + 2) ? line_height : dc->font.height + 2;
+ lines = MAX(lines, 0);
+ mh = (lines + 1) * bh;
+ #ifdef XINERAMA
+@@ -869,7 +871,7 @@ setup(void) {
+
+ void
+ usage(void) {
+- fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font]\n"
++ fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-h height] [-p prompt] [-fn font]\n"
+ " [-nb color] [-nf color] [-sb color] [-sf color] [-hist histfile] [-v]\n", stderr);
+ exit(EXIT_FAILURE);
+ }
+diff -rupN orig/draw.c new/draw.c
+--- orig/draw.c 2015-02-03 11:24:25.219280505 -0700
++++ new/draw.c 2015-02-03 11:24:34.435303320 -0700
+@@ -39,7 +39,7 @@ drawtext(DC *dc, const char *text, Color
+ void
+ drawtextn(DC *dc, const char *text, size_t n, ColorSet *col) {
+ int x = dc->x + dc->font.height/2;
+- int y = dc->y + dc->font.ascent+1;
++ int y = dc->y + dc->font.ascent + (dc->h - dc->font.height)/2;
+
+ XSetForeground(dc->dpy, dc->gc, col->FG);
+ if(dc->font.xft_font) {
diff --git a/dmenu-4.5-history-fixed.diff b/dmenu-4.5-history-fixed.diff
new file mode 100644
index 000000000000..4cc660b1334d
--- /dev/null
+++ b/dmenu-4.5-history-fixed.diff
@@ -0,0 +1,216 @@
+diff -rupN without/dmenu.1 with/dmenu.1
+--- without/dmenu.1 2015-02-03 11:15:59.416937469 -0700
++++ with/dmenu.1 2015-02-03 11:16:13.160976522 -0700
+@@ -20,6 +20,8 @@ dmenu \- dynamic menu
+ .IR color ]
+ .RB [ \-sf
+ .IR color ]
++.RB [ \-hist
++.IR "<filename>" ]
+ .RB [ \-v ]
+ .P
+ .BR dmenu_run " ..."
+@@ -70,6 +72,9 @@ defines the selected background color.
+ .BI \-sf " color"
+ defines the selected foreground color.
+ .TP
++.BI \-hist " <histfile>"
++the file to use for history
++.TP
+ .B \-v
+ prints version information to stdout, then exits.
+ .SH USAGE
+diff -rupN without/dmenu.c with/dmenu.c
+--- without/dmenu.c 2015-02-03 11:15:59.416937469 -0700
++++ with/dmenu.c 2015-02-03 11:19:01.935443821 -0700
+@@ -36,7 +36,7 @@ static void keypress(XKeyEvent *ev);
+ static void match(void);
+ static size_t nextrune(int inc);
+ static void paste(void);
+-static void readstdin(void);
++static void readitems(void);
+ static void run(void);
+ static void setup(void);
+ static void usage(void);
+@@ -65,6 +65,9 @@ static Item *prev, *curr, *next, *sel;
+ static Window win;
+ static XIC xic;
+
++static char *histfile = NULL;
++static Item *histitems, *histend;
++
+ static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
+ static char *(*fstrstr)(const char *, const char *) = strstr;
+
+@@ -104,6 +107,8 @@ main(int argc, char *argv[]) {
+ selbgcolor = argv[++i];
+ else if(!strcmp(argv[i], "-sf")) /* selected foreground color */
+ selfgcolor = argv[++i];
++ else if(!strcmp(argv[i], "-hist"))
++ histfile = argv[++i];
+ else
+ usage();
+
+@@ -114,10 +119,10 @@ main(int argc, char *argv[]) {
+
+ if(fast) {
+ grabkeyboard();
+- readstdin();
++ readitems();
+ }
+ else {
+- readstdin();
++ readitems();
+ grabkeyboard();
+ }
+ setup();
+@@ -127,6 +132,59 @@ main(int argc, char *argv[]) {
+ return ret;
+ }
+
++static int
++writehistory(char *command) {
++ FILE *f;
++ Item *histitem;
++ char *histline;
++ char *histcmd;
++ int currcnt = 1;
++ int histcnt;
++
++ if(!histfile || strlen(command) <= 0)
++ return 0;
++
++ if((f = fopen(histfile, "w"))) {
++ /* get the current count of previous runs for this command */
++ for(histitem = histitems; histitem && histitem->text; histitem=histitem->right) {
++ histline = strdup(histitem->text);
++ histcmd = strsep(&histline, "\t");
++ if(strcmp(command, histcmd) == 0) {
++ currcnt = atoi(strsep(&histline, "\t")) + 1;
++ }
++ }
++
++ /* loop through history printing those with more runs */
++ for(histitem = histitems; histitem && histitem->text; histitem=histitem->right) {
++ histline = strdup(histitem->text);
++ histcmd = strsep(&histline, "\t");
++ histcnt = atoi(strsep(&histline, "\t"));
++ if(histcnt > currcnt) {
++ fprintf(f, "%s", histitem->text);
++ } else {
++ break;
++ }
++ }
++
++ /* print this command now so it's the first in line with this run count */
++ /* reducing the count by 1 here to keep the next comparison loop simple */
++ fprintf(f, "%s\t%d\n", command, currcnt--);
++
++ /* print all the rest except this command's old line */
++ for(; histitem && histitem->text; histitem=histitem->right) {
++ histline = strdup(histitem->text);
++ histcmd = strsep(&histline, "\t");
++ histcnt = atoi(strsep(&histline, "\t"));
++ if(histcnt < currcnt || strcmp(command, histcmd) != 0)
++ fprintf(f, "%s", histitem->text);
++ }
++ fclose(f);
++ return 1;
++ }
++
++ return 0;
++}
++
+ void
+ appenditem(Item *item, Item **list, Item **last) {
+ if(*last)
+@@ -378,6 +436,7 @@ keypress(XKeyEvent *ev) {
+ case XK_KP_Enter:
+ puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
+ ret = EXIT_SUCCESS;
++ writehistory( (sel == NULL) ? text : sel->text);
+ running = False;
+ break;
+ case XK_Right:
+@@ -484,26 +543,60 @@ paste(void) {
+ }
+
+ void
+-readstdin(void) {
+- char buf[sizeof text], *p, *maxstr = NULL;
+- size_t i, max = 0, size = 0;
++readitems(void) {
++ char buf[sizeof text], *p, *maxstr = NULL, *histline, *histcmd;
++ size_t i = 0, j = 0, k = 0, max = 0, size = 0;
++ FILE *f;
++ Bool listed;
++ Item *histitem;
++ histitems = histend = NULL;
++
++ if(histfile && (f = fopen(histfile, "r"))) {
++ for(; fgets(buf, sizeof buf, f); i++) {
++ histitem = malloc(sizeof *histitem);
++ histitem->text = strdup(buf);
++ appenditem(histitem, &histitems, &histend);
++ if(i+1 >= size / sizeof *items)
++ if(!(items = realloc(items, (size += BUFSIZ))))
++ eprintf("cannot realloc %u bytes:", size);
++ if((p = strchr(buf, '\n')))
++ *p = '\0';
++ histline = strdup(buf);
++ histcmd = strsep(&histline, "\t");
++ if(!(items[i].text = strdup(histcmd)))
++ eprintf("cannot strdup %u bytes:", strlen(histcmd)+1);
++ if(strlen(items[i].text) > max)
++ max = strlen(maxstr = items[i].text);
++ }
++ fclose(f);
++ }
+
+ /* 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))))
+- eprintf("cannot realloc %u bytes:", size);
++ for(j = i; fgets(buf, sizeof buf, stdin); j++) {
+ if((p = strchr(buf, '\n')))
+ *p = '\0';
+- if(!(items[i].text = strdup(buf)))
+- eprintf("cannot strdup %u bytes:", strlen(buf)+1);
+- if(strlen(items[i].text) > max)
+- max = strlen(maxstr = items[i].text);
++ listed = False;
++ for(k = 0; k < i; k++) {
++ if(strcmp(buf, items[k].text) == 0) {
++ listed = True;
++ j--;
++ break;
++ }
++ }
++ if(!listed) {
++ if(j+1 >= size / sizeof *items)
++ if(!(items = realloc(items, (size += BUFSIZ))))
++ eprintf("cannot realloc %u bytes:", size);
++ if(!(items[j].text = strdup(buf)))
++ eprintf("cannot strdup %u bytes:", strlen(buf)+1);
++ if(strlen(items[j].text) > max)
++ max = strlen(maxstr = items[j].text);
++ }
+ }
+ if(items)
+- items[i].text = NULL;
++ items[j].text = NULL;
+ inputw = maxstr ? textw(dc, maxstr) : 0;
+- lines = MIN(lines, i);
++ lines = MIN(lines, j);
+ }
+
+ void
+@@ -617,6 +710,6 @@ setup(void) {
+ void
+ usage(void) {
+ fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font]\n"
+- " [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr);
++ " [-nb color] [-nf color] [-sb color] [-sf color] [-hist histfile] [-v]\n", stderr);
+ exit(EXIT_FAILURE);
+ }
diff --git a/dmenu-4.5-mouse-support.diff b/dmenu-4.5-mouse-support.diff
new file mode 100644
index 000000000000..d5bb4de2fc8e
--- /dev/null
+++ b/dmenu-4.5-mouse-support.diff
@@ -0,0 +1,143 @@
+diff --git a/dmenu.c b/dmenu.c
+index 3962801..a75bf80 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -25,6 +25,7 @@ struct Item {
+ };
+
+ static void appenditem(Item *item, Item **list, Item **last);
++static void buttonpress(XEvent *e);
+ static void calcoffsets(void);
+ static char *cistrstr(const char *s, const char *sub);
+ static void drawmenu(void);
+@@ -388,6 +390,109 @@ keypress(XKeyEvent *ev) {
+ }
+
+ void
++buttonpress(XEvent *e) {
++ int curpos;
++ Item *item;
++ XButtonPressedEvent *ev = &e->xbutton;
++
++ if(ev->window != win)
++ return;
++
++ /* right-click: exit */
++ if(ev->button == Button3)
++ exit(EXIT_FAILURE);
++
++ dc->x = 0;
++ dc->y = 0;
++ dc->h = bh;
++
++ if(prompt && *prompt) {
++ dc->w = promptw;
++ dc->x = dc->w;
++ }
++ /* input field */
++ dc->w = (lines > 0 || !matches) ? mw - dc->x : inputw;
++ if((curpos = textnw(dc, text, cursor) + dc->h/2 - 2) < dc->w);
++
++ /* 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 <= dc->x + dc->w +
++ ((!prev || !curr->left) ? textw(dc, "<") : 0)) ||
++ (lines > 0 && ev->y >= dc->y && ev->y <= dc->y + dc->h))) {
++ insert(NULL, 0 - cursor);
++ drawmenu();
++ return;
++ }
++ /* middle-mouse click: paste selection */
++ if(ev->button == Button2) {
++ XConvertSelection(dc->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(lines > 0) {
++ /* vertical list: left-click on item */
++ dc->w = mw - dc->x;
++ for(item = curr; item != next; item = item->right) {
++ dc->y += dc->h;
++ if(ev->y >= dc->y && ev->y <= (dc->y + dc->h)) {
++ puts(item->text);
++ exit(EXIT_SUCCESS);
++ }
++ }
++ }
++ else if(matches) {
++ /* left-click on left arrow */
++ dc->x += inputw;
++ dc->w = textw(dc, "<");
++ if(prev && curr->left) {
++ if(ev->x >= dc->x && ev->x <= dc->x + dc->w) {
++ sel = curr = prev;
++ calcoffsets();
++ drawmenu();
++ return;
++ }
++ }
++ /* horizontal list: left-click on item */
++ for(item = curr; item != next; item = item->right) {
++ dc->x += dc->w;
++ dc->w = MIN(textw(dc, item->text), mw - dc->x - textw(dc, ">"));
++ if(ev->x >= dc->x && ev->x <= (dc->x + dc->w)) {
++ puts(item->text);
++ exit(EXIT_SUCCESS);
++ }
++ }
++ /* left-click on right arrow */
++ dc->w = textw(dc, ">");
++ dc->x = mw - dc->w;
++ if(next && ev->x >= dc->x && ev->x <= dc->x + dc->w) {
++ sel = curr = next;
++ calcoffsets();
++ drawmenu();
++ return;
++ }
++ }
++}
++
++void
+ match(void) {
+ static char **tokv = NULL;
+ static int tokn = 0;
+@@ -496,6 +601,9 @@ run(void) {
+ if(XFilterEvent(&ev, win))
+ continue;
+ switch(ev.type) {
++ case ButtonPress:
++ buttonpress(&ev);
++ break;
+ case Expose:
+ if(ev.xexpose.count == 0)
+ mapdc(dc, win, mw, mh);
+@@ -585,8 +693,9 @@ setup(void) {
+ /* create menu window */
+ swa.override_redirect = True;
+ swa.background_pixel = normcol->BG;
+- swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
++ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask |
++ ButtonPressMask;
+ win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0,
+ DefaultDepth(dc->dpy, screen), CopyFromParent,
+ DefaultVisual(dc->dpy, screen),
+ CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
diff --git a/dmenu-4.5-xft.diff b/dmenu-4.5-xft.diff
new file mode 100644
index 000000000000..ff93337ca276
--- /dev/null
+++ b/dmenu-4.5-xft.diff
@@ -0,0 +1,418 @@
+diff -upr a/config.mk b/config.mk
+--- a/config.mk 2012-01-10 19:03:22.000000000 +0200
++++ b/config.mk 2012-01-10 19:03:38.000000000 +0200
+@@ -12,9 +12,13 @@ X11LIB = /usr/X11R6/lib
+ XINERAMALIBS = -lXinerama
+ XINERAMAFLAGS = -DXINERAMA
+
++# Xft, comment if you don't want it
++XFTINC = -I/usr/include/freetype2
++XFTLIBS = -lXft -lXrender -lfreetype -lz -lfontconfig
++
+ # includes and libs
+-INCS = -I${X11INC}
+-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS}
++INCS = -I${X11INC} ${XFTINC}
++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${XFTLIBS}
+
+ # flags
+ CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
+diff -upr a/dmenu.1 b/dmenu.1
+--- a/dmenu.1 2012-01-10 19:14:19.000000000 +0200
++++ b/dmenu.1 2012-01-10 19:14:23.000000000 +0200
+@@ -53,7 +53,7 @@ dmenu lists items vertically, with the g
+ defines the prompt to be displayed to the left of the input field.
+ .TP
+ .BI \-fn " font"
+-defines the font or font set used.
++defines the font or font set used. eg. "fixed" or "Monospace-12:normal" (an xft font)
+ .TP
+ .BI \-nb " color"
+ defines the normal background color.
+diff -upr a/dmenu.c b/dmenu.c
+--- a/dmenu.c 2012-01-10 19:14:19.000000000 +0200
++++ b/dmenu.c 2012-01-10 19:24:39.000000000 +0200
+@@ -17,6 +17,7 @@
+ * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
+ #define MIN(a,b) ((a) < (b) ? (a) : (b))
+ #define MAX(a,b) ((a) > (b) ? (a) : (b))
++#define DEFFONT "fixed" /* xft example: "Monospace-11" */
+
+ typedef struct Item Item;
+ struct Item {
+@@ -26,6 +27,7 @@ struct Item {
+
+ static void appenditem(Item *item, Item **list, Item **last);
+ static void calcoffsets(void);
++static void cleanup(void);
+ static char *cistrstr(const char *s, const char *sub);
+ static void drawmenu(void);
+ static void grabkeyboard(void);
+@@ -50,10 +52,12 @@ static const char *normfgcolor = "#bbbbb
+ static const char *selbgcolor = "#005577";
+ static const char *selfgcolor = "#eeeeee";
+ static unsigned int lines = 0;
+-static unsigned long normcol[ColLast];
+-static unsigned long selcol[ColLast];
++static ColorSet *normcol;
++static ColorSet *selcol;
+ static Atom clip, utf8;
+ static Bool topbar = True;
++static Bool running = True;
++static int ret = 0;
+ static DC *dc;
+ static Item *items = NULL;
+ static Item *matches, *matchend;
+@@ -104,7 +108,9 @@ main(int argc, char *argv[]) {
+ usage();
+
+ dc = initdc();
+- initfont(dc, font);
++ initfont(dc, font ? font : DEFFONT);
++ normcol = initcolor(dc, normfgcolor, normbgcolor);
++ selcol = initcolor(dc, selfgcolor, selbgcolor);
+
+ if(fast) {
+ grabkeyboard();
+@@ -117,7 +123,8 @@ main(int argc, char *argv[]) {
+ setup();
+ run();
+
+- return 1; /* unreachable */
++ cleanup();
++ return ret;
+ }
+
+ void
+@@ -160,6 +167,15 @@ cistrstr(const char *s, const char *sub)
+ }
+
+ void
++cleanup(void) {
++ freecol(dc, normcol);
++ freecol(dc, selcol);
++ XDestroyWindow(dc->dpy, win);
++ XUngrabKeyboard(dc->dpy, CurrentTime);
++ freedc(dc);
++}
++
++void
+ drawmenu(void) {
+ int curpos;
+ Item *item;
+@@ -167,7 +183,7 @@ drawmenu(void) {
+ dc->x = 0;
+ dc->y = 0;
+ dc->h = bh;
+- drawrect(dc, 0, 0, mw, mh, True, BG(dc, normcol));
++ drawrect(dc, 0, 0, mw, mh, True, normcol->BG);
+
+ if(prompt) {
+ dc->w = promptw;
+@@ -178,7 +194,7 @@ drawmenu(void) {
+ dc->w = (lines > 0 || !matches) ? mw - dc->x : inputw;
+ drawtext(dc, text, normcol);
+ if((curpos = textnw(dc, text, cursor) + dc->h/2 - 2) < dc->w)
+- drawrect(dc, curpos, 2, 1, dc->h - 4, True, FG(dc, normcol));
++ drawrect(dc, curpos, 2, 1, dc->h - 4, True, normcol->FG);
+
+ if(lines > 0) {
+ /* draw vertical list */
+@@ -321,7 +337,8 @@ keypress(XKeyEvent *ev) {
+ sel = matchend;
+ break;
+ case XK_Escape:
+- exit(EXIT_FAILURE);
++ ret = EXIT_FAILURE;
++ running = False;
+ case XK_Home:
+ if(sel == matches) {
+ cursor = 0;
+@@ -359,7 +376,8 @@ keypress(XKeyEvent *ev) {
+ case XK_Return:
+ case XK_KP_Enter:
+ puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
+- exit(EXIT_SUCCESS);
++ ret = EXIT_SUCCESS;
++ running = False;
+ case XK_Right:
+ if(text[cursor] != '\0') {
+ cursor = nextrune(+1);
+@@ -490,7 +508,7 @@ void
+ run(void) {
+ XEvent ev;
+
+- while(!XNextEvent(dc->dpy, &ev)) {
++ while(running && !XNextEvent(dc->dpy, &ev)) {
+ if(XFilterEvent(&ev, win))
+ continue;
+ switch(ev.type) {
+@@ -524,11 +542,6 @@ setup(void) {
+ XineramaScreenInfo *info;
+ #endif
+
+- normcol[ColBG] = getcolor(dc, normbgcolor);
+- normcol[ColFG] = getcolor(dc, normfgcolor);
+- selcol[ColBG] = getcolor(dc, selbgcolor);
+- selcol[ColFG] = getcolor(dc, selfgcolor);
+-
+ clip = XInternAtom(dc->dpy, "CLIPBOARD", False);
+ utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False);
+
+@@ -582,7 +595,7 @@ setup(void) {
+
+ /* create menu window */
+ swa.override_redirect = True;
+- swa.background_pixel = normcol[ColBG];
++ swa.background_pixel = normcol->BG;
+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+ win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0,
+ DefaultDepth(dc->dpy, screen), CopyFromParent,
+diff -upr a/draw.c b/draw.c
+--- a/draw.c 2012-01-10 19:14:19.000000000 +0200
++++ b/draw.c 2012-01-10 19:14:23.000000000 +0200
+@@ -9,9 +9,6 @@
+
+ #define MAX(a, b) ((a) > (b) ? (a) : (b))
+ #define MIN(a, b) ((a) < (b) ? (a) : (b))
+-#define DEFAULTFN "fixed"
+-
+-static Bool loadfont(DC *dc, const char *fontstr);
+
+ void
+ drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) {
+@@ -23,7 +20,7 @@ drawrect(DC *dc, int x, int y, unsigned
+ }
+
+ void
+-drawtext(DC *dc, const char *text, unsigned long col[ColLast]) {
++drawtext(DC *dc, const char *text, ColorSet *col) {
+ char buf[BUFSIZ];
+ size_t mn, n = strlen(text);
+
+@@ -35,19 +32,24 @@ drawtext(DC *dc, const char *text, unsig
+ if(mn < n)
+ for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.');
+
+- drawrect(dc, 0, 0, dc->w, dc->h, True, BG(dc, col));
++ drawrect(dc, 0, 0, dc->w, dc->h, True, col->BG);
+ drawtextn(dc, buf, mn, col);
+ }
+
+ void
+-drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) {
++drawtextn(DC *dc, const char *text, size_t n, ColorSet *col) {
+ int x = dc->x + dc->font.height/2;
+ int y = dc->y + dc->font.ascent+1;
+
+- XSetForeground(dc->dpy, dc->gc, FG(dc, col));
+- if(dc->font.set)
++ XSetForeground(dc->dpy, dc->gc, col->FG);
++ if(dc->font.xft_font) {
++ if (!dc->xftdraw)
++ eprintf("error, xft drawable does not exist");
++ XftDrawStringUtf8(dc->xftdraw, &col->FG_xft,
++ dc->font.xft_font, x, y, (unsigned char*)text, n);
++ } else if(dc->font.set) {
+ XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n);
+- else {
++ } else {
+ XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid);
+ XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n);
+ }
+@@ -69,16 +71,33 @@ eprintf(const char *fmt, ...) {
+ }
+
+ void
++freecol(DC *dc, ColorSet *col) {
++ if(col) {
++ if(&col->FG_xft)
++ XftColorFree(dc->dpy, DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)),
++ DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)), &col->FG_xft);
++ free(col);
++ }
++}
++
++void
+ freedc(DC *dc) {
++ if(dc->font.xft_font) {
++ XftFontClose(dc->dpy, dc->font.xft_font);
++ XftDrawDestroy(dc->xftdraw);
++ }
+ if(dc->font.set)
+ XFreeFontSet(dc->dpy, dc->font.set);
+- if(dc->font.xfont)
++ if(dc->font.xfont)
+ XFreeFont(dc->dpy, dc->font.xfont);
+- if(dc->canvas)
++ if(dc->canvas)
+ XFreePixmap(dc->dpy, dc->canvas);
+- XFreeGC(dc->dpy, dc->gc);
+- XCloseDisplay(dc->dpy);
+- free(dc);
++ if(dc->gc)
++ XFreeGC(dc->dpy, dc->gc);
++ if(dc->dpy)
++ XCloseDisplay(dc->dpy);
++ if(dc)
++ free(dc);
+ }
+
+ unsigned long
+@@ -91,6 +110,20 @@ getcolor(DC *dc, const char *colstr) {
+ return color.pixel;
+ }
+
++ColorSet *
++initcolor(DC *dc, const char * foreground, const char * background) {
++ ColorSet * col = (ColorSet *)malloc(sizeof(ColorSet));
++ if(!col)
++ eprintf("error, cannot allocate memory for color set");
++ col->BG = getcolor(dc, background);
++ col->FG = getcolor(dc, foreground);
++ if(dc->font.xft_font)
++ if(!XftColorAllocName(dc->dpy, DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)),
++ DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)), foreground, &col->FG_xft))
++ eprintf("error, cannot allocate xft font color '%s'\n", foreground);
++ return col;
++}
++
+ DC *
+ initdc(void) {
+ DC *dc;
+@@ -109,39 +142,33 @@ initdc(void) {
+
+ void
+ initfont(DC *dc, const char *fontstr) {
+- if(!loadfont(dc, fontstr ? fontstr : DEFAULTFN)) {
+- if(fontstr != NULL)
+- fprintf(stderr, "cannot load font '%s'\n", fontstr);
+- if(fontstr == NULL || !loadfont(dc, DEFAULTFN))
+- eprintf("cannot load font '%s'\n", DEFAULTFN);
+- }
+- dc->font.height = dc->font.ascent + dc->font.descent;
+-}
+-
+-Bool
+-loadfont(DC *dc, const char *fontstr) {
+ char *def, **missing, **names;
+ int i, n;
+ XFontStruct **xfonts;
+
+- if(!*fontstr)
+- return False;
+- if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) {
++ missing = NULL;
++ if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) {
++ dc->font.ascent = dc->font.xfont->ascent;
++ dc->font.descent = dc->font.xfont->descent;
++ dc->font.width = dc->font.xfont->max_bounds.width;
++ } else if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) {
+ n = XFontsOfFontSet(dc->font.set, &xfonts, &names);
+ for(i = 0; i < n; i++) {
+ dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent);
+ dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent);
+ dc->font.width = MAX(dc->font.width, xfonts[i]->max_bounds.width);
+ }
+- }
+- else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) {
+- dc->font.ascent = dc->font.xfont->ascent;
+- dc->font.descent = dc->font.xfont->descent;
+- dc->font.width = dc->font.xfont->max_bounds.width;
++ } else if((dc->font.xft_font = XftFontOpenName(dc->dpy, DefaultScreen(dc->dpy), fontstr))) {
++ dc->font.ascent = dc->font.xft_font->ascent;
++ dc->font.descent = dc->font.xft_font->descent;
++ dc->font.width = dc->font.xft_font->max_advance_width;
++ } else {
++ eprintf("cannot load font '%s'\n", fontstr);
+ }
+ if(missing)
+ XFreeStringList(missing);
+- return dc->font.set || dc->font.xfont;
++ dc->font.height = dc->font.ascent + dc->font.descent;
++ return;
+ }
+
+ void
+@@ -151,20 +178,29 @@ mapdc(DC *dc, Window win, unsigned int w
+
+ void
+ resizedc(DC *dc, unsigned int w, unsigned int h) {
++ int screen = DefaultScreen(dc->dpy);
+ if(dc->canvas)
+ XFreePixmap(dc->dpy, dc->canvas);
+
+ dc->w = w;
+ dc->h = h;
+ dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h,
+- DefaultDepth(dc->dpy, DefaultScreen(dc->dpy)));
++ DefaultDepth(dc->dpy, screen));
++ if(dc->font.xft_font && !(dc->xftdraw)) {
++ dc->xftdraw = XftDrawCreate(dc->dpy, dc->canvas, DefaultVisual(dc->dpy,screen), DefaultColormap(dc->dpy,screen));
++ if(!(dc->xftdraw))
++ eprintf("error, cannot create xft drawable\n");
++ }
+ }
+
+ int
+ textnw(DC *dc, const char *text, size_t len) {
+- if(dc->font.set) {
++ if(dc->font.xft_font) {
++ XGlyphInfo gi;
++ XftTextExtentsUtf8(dc->dpy, dc->font.xft_font, (const FcChar8*)text, len, &gi);
++ return gi.width;
++ } else if(dc->font.set) {
+ XRectangle r;
+-
+ XmbTextExtents(dc->font.set, text, len, NULL, &r);
+ return r.width;
+ }
+diff -upr a/draw.h b/draw.h
+--- a/draw.h 2012-01-10 19:14:19.000000000 +0200
++++ b/draw.h 2012-01-10 19:14:23.000000000 +0200
+@@ -1,9 +1,6 @@
+ /* See LICENSE file for copyright and license details. */
+
+-#define FG(dc, col) ((col)[(dc)->invert ? ColBG : ColFG])
+-#define BG(dc, col) ((col)[(dc)->invert ? ColFG : ColBG])
+-
+-enum { ColBG, ColFG, ColBorder, ColLast };
++#include <X11/Xft/Xft.h>
+
+ typedef struct {
+ int x, y, w, h;
+@@ -11,6 +8,7 @@ typedef struct {
+ Display *dpy;
+ GC gc;
+ Pixmap canvas;
++ XftDraw *xftdraw;
+ struct {
+ int ascent;
+ int descent;
+@@ -18,15 +16,24 @@ typedef struct {
+ int width;
+ XFontSet set;
+ XFontStruct *xfont;
++ XftFont *xft_font;
+ } font;
+ } DC; /* draw context */
+
++typedef struct {
++ unsigned long FG;
++ XftColor FG_xft;
++ unsigned long BG;
++} ColorSet;
++
+ void drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color);
+-void drawtext(DC *dc, const char *text, unsigned long col[ColLast]);
+-void drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]);
++void drawtext(DC *dc, const char *text, ColorSet *col);
++void drawtextn(DC *dc, const char *text, size_t n, ColorSet *col);
++void freecol(DC *dc, ColorSet *col);
+ void eprintf(const char *fmt, ...);
+ void freedc(DC *dc);
+ unsigned long getcolor(DC *dc, const char *colstr);
++ColorSet *initcolor(DC *dc, const char *foreground, const char *background);
+ DC *initdc(void);
+ void initfont(DC *dc, const char *fontstr);
+ void mapdc(DC *dc, Window win, unsigned int w, unsigned int h);