about summary refs log tree commit diff stats
diff options
context:
space:
mode:
author2019-02-08 16:25:44 -0500
committer2019-02-08 16:25:44 -0500
commit274d4f9a7b074fa99177071f74088787f922c5a0 (patch)
tree43f9170832dde1a15abc2566e191e804d9c5abb3
parente60466c0a1687c65491929adb4f4049c20e893cc (diff)
downloaddmenu-274d4f9a7b074fa99177071f74088787f922c5a0.tar.gz
-rw-r--r--LICENSE12
-rw-r--r--Makefile78
-rw-r--r--config.mk16
-rw-r--r--dmenu.168
-rw-r--r--dmenu.c87
-rw-r--r--dmenu_path12
-rw-r--r--drw.c14
7 files changed, 179 insertions, 108 deletions
diff --git a/LICENSE b/LICENSE
index 221603d..6ed8ad3 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,13 +1,15 @@
 MIT/X Consortium License
 
-© 2006-2014 Anselm R Garbe <anselm@garbe.us>
-© 2010-2012 Connor Lane Smith <cls@lubutu.com>
+© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
+© 2006-2008 Sander van Dijk <a.h.vandijk@gmail.com>
+© 2006-2007 Michał Janeczek <janeczek@gmail.com>
+© 2007 Kris Maglione <jg@suckless.org>
 © 2009 Gottox <gottox@s01.de>
 © 2009 Markus Schnalke <meillo@marmaro.de>
 © 2009 Evan Gates <evan.gates@gmail.com>
-© 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
-© 2006-2007 Michał Janeczek <janeczek at gmail dot com>
-© 2014-2015 Hiltjo Posthuma <hiltjo@codemadness.org>
+© 2010-2012 Connor Lane Smith <cls@lubutu.com>
+© 2014-2019 Hiltjo Posthuma <hiltjo@codemadness.org>
+© 2015-2018 Quentin Rameau <quinq@fifth.space>
 
 Permission is hereby granted, free of charge, to any person obtaining a
 copy of this software and associated documentation files (the "Software"),
diff --git a/Makefile b/Makefile
index a7cd04f..a03a95c 100644
--- a/Makefile
+++ b/Makefile
@@ -4,71 +4,61 @@
 include config.mk
 
 SRC = drw.c dmenu.c stest.c util.c
-OBJ = ${SRC:.c=.o}
+OBJ = $(SRC:.c=.o)
 
 all: options dmenu stest
 
 options:
 	@echo dmenu build options:
-	@echo "CFLAGS   = ${CFLAGS}"
-	@echo "LDFLAGS  = ${LDFLAGS}"
-	@echo "CC       = ${CC}"
+	@echo "CFLAGS   = $(CFLAGS)"
+	@echo "LDFLAGS  = $(LDFLAGS)"
+	@echo "CC       = $(CC)"
 
 .c.o:
-	@echo CC $<
-	@${CC} -c ${CFLAGS} $<
+	$(CC) -c $(CFLAGS) $<
 
 config.h:
-	@echo creating $@ from config.def.h
-	@cp config.def.h $@
+	cp config.def.h $@
 
-${OBJ}: arg.h config.h config.mk drw.h
+$(OBJ): arg.h config.h config.mk drw.h
 
 dmenu: dmenu.o drw.o util.o
-	@echo CC -o $@
-	@${CC} -o $@ dmenu.o drw.o util.o ${LDFLAGS}
+	$(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS)
 
 stest: stest.o
-	@echo CC -o $@
-	@${CC} -o $@ stest.o ${LDFLAGS}
+	$(CC) -o $@ stest.o $(LDFLAGS)
 
 clean:
-	@echo cleaning
-	@rm -f dmenu stest ${OBJ} dmenu-${VERSION}.tar.gz
+	rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz
 
 dist: clean
-	@echo creating dist tarball
-	@mkdir -p dmenu-${VERSION}
-	@cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1 \
-		drw.h util.h dmenu_path dmenu_run stest.1 ${SRC} \
-		dmenu-${VERSION}
-	@tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
-	@gzip dmenu-${VERSION}.tar
-	@rm -rf dmenu-${VERSION}
+	mkdir -p dmenu-$(VERSION)
+	cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\
+		drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\
+		dmenu-$(VERSION)
+	tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION)
+	gzip dmenu-$(VERSION).tar
+	rm -rf dmenu-$(VERSION)
 
 install: all
-	@echo installing executables to ${DESTDIR}${PREFIX}/bin
-	@mkdir -p ${DESTDIR}${PREFIX}/bin
-	@cp -f dmenu dmenu_path dmenu_run stest ${DESTDIR}${PREFIX}/bin
-	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
-	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path
-	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
-	@chmod 755 ${DESTDIR}${PREFIX}/bin/stest
-	@echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1
-	@mkdir -p ${DESTDIR}${MANPREFIX}/man1
-	@sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1
-	@sed "s/VERSION/${VERSION}/g" < stest.1 > ${DESTDIR}${MANPREFIX}/man1/stest.1
-	@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
-	@chmod 644 ${DESTDIR}${MANPREFIX}/man1/stest.1
+	mkdir -p $(DESTDIR)$(PREFIX)/bin
+	cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin
+	chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu
+	chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path
+	chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run
+	chmod 755 $(DESTDIR)$(PREFIX)/bin/stest
+	mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+	sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1
+	sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1
+	chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1
+	chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1
 
 uninstall:
-	@echo removing executables from ${DESTDIR}${PREFIX}/bin
-	@rm -f ${DESTDIR}${PREFIX}/bin/dmenu
-	@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_path
-	@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run
-	@rm -f ${DESTDIR}${PREFIX}/bin/stest
-	@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
-	@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
-	@rm -f ${DESTDIR}${MANPREFIX}/man1/stest.1
+	rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\
+		$(DESTDIR)$(PREFIX)/bin/dmenu_path\
+		$(DESTDIR)$(PREFIX)/bin/dmenu_run\
+		$(DESTDIR)$(PREFIX)/bin/stest\
+		$(DESTDIR)$(MANPREFIX)/man1/dmenu.1\
+		$(DESTDIR)$(MANPREFIX)/man1/stest.1
 
 .PHONY: all options clean dist install uninstall
diff --git a/config.mk b/config.mk
index de3f1d9..0929b4a 100644
--- a/config.mk
+++ b/config.mk
@@ -1,9 +1,9 @@
 # dmenu version
-VERSION = 4.7
+VERSION = 4.9
 
 # paths
 PREFIX = /usr/local
-MANPREFIX = ${PREFIX}/share/man
+MANPREFIX = $(PREFIX)/share/man
 
 X11INC = /usr/X11R6/include
 X11LIB = /usr/X11R6/lib
@@ -16,16 +16,16 @@ XINERAMAFLAGS = -DXINERAMA
 FREETYPELIBS = -lfontconfig -lXft
 FREETYPEINC = /usr/include/freetype2
 # OpenBSD (uncomment)
-#FREETYPEINC = ${X11INC}/freetype2
+#FREETYPEINC = $(X11INC)/freetype2
 
 # includes and libs
-INCS = -I${X11INC} -I${FREETYPEINC}
-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+INCS = -I$(X11INC) -I$(FREETYPEINC)
+LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
 
 # 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}
-LDFLAGS  = -s ${LIBS}
+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)
+LDFLAGS  = $(LIBS)
 
 # compiler and linker
 CC = cc
diff --git a/dmenu.1 b/dmenu.1
index 9eab758..323f93c 100644
--- a/dmenu.1
+++ b/dmenu.1
@@ -41,8 +41,8 @@ which lists programs in the user's $PATH and runs the result in their $SHELL.
 dmenu appears at the bottom of the screen.
 .TP
 .B \-f
-dmenu grabs the keyboard before reading stdin.  This is faster, but will lock up
-X until stdin reaches end\-of\-file.
+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.
@@ -100,82 +100,94 @@ Confirm input.  Prints the input text to stdout and exits, returning success.
 .B Escape
 Exit without selecting an item, returning failure.
 .TP
-C\-a
+.B Ctrl-Left
+Move cursor to the start of the current word
+.TP
+.B Ctrl-Right
+Move cursor to the end of the current word
+.TP
+.B C\-a
 Home
 .TP
-C\-b
+.B C\-b
 Left
 .TP
-C\-c
+.B C\-c
 Escape
 .TP
-C\-d
+.B C\-d
 Delete
 .TP
-C\-e
+.B C\-e
 End
 .TP
-C\-f
+.B C\-f
 Right
 .TP
-C\-g
+.B C\-g
 Escape
 .TP
-C\-h
+.B C\-h
 Backspace
 .TP
-C\-i
+.B C\-i
 Tab
 .TP
-C\-j
+.B C\-j
 Return
 .TP
-C\-J
+.B C\-J
 Shift-Return
 .TP
-C\-k
+.B C\-k
 Delete line right
 .TP
-C\-m
+.B C\-m
 Return
 .TP
-C\-M
+.B C\-M
 Shift-Return
 .TP
-C\-n
+.B C\-n
 Down
 .TP
-C\-p
+.B C\-p
 Up
 .TP
-C\-u
+.B C\-u
 Delete line left
 .TP
-C\-w
+.B C\-w
 Delete word left
 .TP
-C\-y
+.B C\-y
 Paste from primary X selection
 .TP
-C\-Y
+.B C\-Y
 Paste from X clipboard
 .TP
-M\-g
+.B M\-b
+Move cursor to the start of the current word
+.TP
+.B M\-f
+Move cursor to the end of the current word
+.TP
+.B M\-g
 Home
 .TP
-M\-G
+.B M\-G
 End
 .TP
-M\-h
+.B M\-h
 Up
 .TP
-M\-j
+.B M\-j
 Page down
 .TP
-M\-k
+.B M\-k
 Page up
 .TP
-M\-l
+.B M\-l
 Down
 .SH SEE ALSO
 .IR dwm (1),
diff --git a/dmenu.c b/dmenu.c
index d605ab4..6b8f51b 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -6,6 +6,7 @@
 #include <string.h>
 #include <strings.h>
 #include <time.h>
+#include <unistd.h>
 
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
@@ -144,7 +145,7 @@ drawmenu(void)
 	drw_setscheme(drw, scheme[SchemeNorm]);
 	drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
 
-	drw_font_getexts(drw->fonts, text, cursor, &curpos, NULL);
+	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);
@@ -288,17 +289,41 @@ nextrune(int inc)
 }
 
 static void
+movewordedge(int dir)
+{
+	if (dir < 0) { /* move cursor to the start of the word*/
+		while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
+			cursor = nextrune(-1);
+		while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
+			cursor = nextrune(-1);
+	} else { /* move cursor to the end of the word */
+		while (text[cursor] && strchr(worddelimiters, text[cursor]))
+			cursor = nextrune(+1);
+		while (text[cursor] && !strchr(worddelimiters, text[cursor]))
+			cursor = nextrune(+1);
+	}
+}
+
+static void
 keypress(XKeyEvent *ev)
 {
 	char buf[32];
 	int len;
-	KeySym ksym = NoSymbol;
+	KeySym ksym;
 	Status status;
 
 	len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
-	if (status == XBufferOverflow)
+	switch (status) {
+	default: /* XLookupNone, XBufferOverflow */
 		return;
-	if (ev->state & ControlMask)
+	case XLookupChars:
+		goto insert;
+	case XLookupKeySym:
+	case XLookupBoth:
+		break;
+	}
+
+	if (ev->state & ControlMask) {
 		switch(ksym) {
 		case XK_a: ksym = XK_Home;      break;
 		case XK_b: ksym = XK_Left;      break;
@@ -334,6 +359,12 @@ keypress(XKeyEvent *ev)
 			XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
 			                  utf8, utf8, win, CurrentTime);
 			return;
+		case XK_Left:
+			movewordedge(-1);
+			goto draw;
+		case XK_Right:
+			movewordedge(+1);
+			goto draw;
 		case XK_Return:
 		case XK_KP_Enter:
 			break;
@@ -343,8 +374,14 @@ keypress(XKeyEvent *ev)
 		default:
 			return;
 		}
-	else if (ev->state & Mod1Mask)
+	} else if (ev->state & Mod1Mask) {
 		switch(ksym) {
+		case XK_b:
+			movewordedge(-1);
+			goto draw;
+		case XK_f:
+			movewordedge(+1);
+			goto draw;
 		case XK_g: ksym = XK_Home;  break;
 		case XK_G: ksym = XK_End;   break;
 		case XK_h: ksym = XK_Up;    break;
@@ -354,8 +391,11 @@ keypress(XKeyEvent *ev)
 		default:
 			return;
 		}
+	}
+
 	switch(ksym) {
 	default:
+insert:
 		if (!iscntrl(*buf))
 			insert(buf, len);
 		break;
@@ -455,6 +495,8 @@ keypress(XKeyEvent *ev)
 		match();
 		break;
 	}
+
+draw:
 	drawmenu();
 }
 
@@ -467,10 +509,12 @@ paste(void)
 	Atom da;
 
 	/* we have been given the current selection, now insert it into input */
-	XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
-	                   utf8, &da, &di, &dl, &dl, (unsigned char **)&p);
-	insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
-	XFree(p);
+	if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
+	                   utf8, &da, &di, &dl, &dl, (unsigned char **)&p)
+	    == Success && p) {
+		insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
+		XFree(p);
+	}
 	drawmenu();
 }
 
@@ -509,7 +553,7 @@ run(void)
 	XEvent ev;
 
 	while (!XNextEvent(dpy, &ev)) {
-		if (XFilterEvent(&ev, win))
+		if (XFilterEvent(&ev, None))
 			continue;
 		switch(ev.type) {
 		case Expose:
@@ -539,22 +583,21 @@ run(void)
 static void
 setup(void)
 {
-	int x, y, i = 0;
+	int x, y, i, j;
 	unsigned int du;
 	XSetWindowAttributes swa;
 	XIM xim;
 	Window w, dw, *dws;
 	XWindowAttributes wa;
+	XClassHint ch = {"dmenu", "dmenu"};
 #ifdef XINERAMA
 	XineramaScreenInfo *info;
 	Window pw;
-	int a, j, di, n, area = 0;
+	int a, di, n, area = 0;
 #endif
-
 	/* init appearance */
-	scheme[SchemeNorm] = drw_scm_create(drw, colors[SchemeNorm], 2);
-	scheme[SchemeSel] = drw_scm_create(drw, colors[SchemeSel], 2);
-	scheme[SchemeOut] = drw_scm_create(drw, colors[SchemeOut], 2);
+	for (j = 0; j < SchemeLast; j++)
+		scheme[j] = drw_scm_create(drw, colors[j], 2);
 
 	clip = XInternAtom(dpy, "CLIPBOARD",   False);
 	utf8 = XInternAtom(dpy, "UTF8_STRING", False);
@@ -564,6 +607,7 @@ setup(void)
 	lines = MAX(lines, 0);
 	mh = (lines + 1) * bh;
 #ifdef XINERAMA
+	i = 0;
 	if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
 		XGetInputFocus(dpy, &w, &di);
 		if (mon >= 0 && mon < n)
@@ -613,6 +657,7 @@ setup(void)
 	win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
 	                    CopyFromParent, CopyFromParent, CopyFromParent,
 	                    CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+	XSetClassHint(dpy, win, &ch);
 
 	/* open input methods */
 	xim = XOpenIM(dpy, NULL, NULL, NULL);
@@ -620,6 +665,7 @@ setup(void)
 	                XNClientWindow, win, XNFocusWindow, win, NULL);
 
 	XMapRaised(dpy, win);
+	XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
 	if (embed) {
 		XSelectInput(dpy, parentwin, FocusChangeMask);
 		if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
@@ -685,6 +731,8 @@ main(int argc, char *argv[])
 
 	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
 		fputs("warning: no locale support\n", stderr);
+	if (!XSetLocaleModifiers(""))
+		fputs("warning: no locale modifiers support\n", stderr);
 	if (!(dpy = XOpenDisplay(NULL)))
 		die("cannot open display");
 	screen = DefaultScreen(dpy);
@@ -699,7 +747,12 @@ main(int argc, char *argv[])
 		die("no fonts could be loaded.");
 	lrpad = drw->fonts->h;
 
-	if (fast) {
+#ifdef __OpenBSD__
+	if (pledge("stdio rpath", NULL) == -1)
+		die("pledge");
+#endif
+
+	if (fast && !isatty(0)) {
 		grabkeyboard();
 		readstdin();
 	} else {
diff --git a/dmenu_path b/dmenu_path
index 338bac4..3a7cda7 100644
--- a/dmenu_path
+++ b/dmenu_path
@@ -1,10 +1,10 @@
 #!/bin/sh
-cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
-if [ -d "$cachedir" ]; then
-	cache=$cachedir/dmenu_run
-else
-	cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
-fi
+
+cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}"
+cache="$cachedir/dmenu_run"
+
+[ ! -e "$cachedir" ] && mkdir -p "$cachedir"
+
 IFS=:
 if stest -dqr -n "$cache" $PATH; then
 	stest -flx $PATH | sort -u | tee "$cache"
diff --git a/drw.c b/drw.c
index c1582e7..8fd1ca4 100644
--- a/drw.c
+++ b/drw.c
@@ -132,6 +132,19 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
 		die("no font specified.");
 	}
 
+	/* Do not allow using color fonts. This is a workaround for a BadLength
+	 * error from Xft with color glyphs. Modelled on the Xterm workaround. See
+	 * https://bugzilla.redhat.com/show_bug.cgi?id=1498269
+	 * https://lists.suckless.org/dev/1701/30932.html
+	 * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
+	 * and lots more all over the internet.
+	 */
+	FcBool iscol;
+	if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
+		XftFontClose(drw->dpy, xfont);
+		return NULL;
+	}
+
 	font = ecalloc(1, sizeof(Fnt));
 	font->xfont = xfont;
 	font->pattern = pattern;
@@ -337,6 +350,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
 			fcpattern = FcPatternDuplicate(drw->fonts->pattern);
 			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
 			FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
+			FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
 
 			FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
 			FcDefaultSubstitute(fcpattern);