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);
|