diff --git a/LICENSE b/LICENSE index 221603d..9762166 100644 --- a/LICENSE +++ b/LICENSE @@ -1,13 +1,15 @@ MIT/X Consortium License -© 2006-2014 Anselm R Garbe -© 2010-2012 Connor Lane Smith +© 2006-2019 Anselm R Garbe +© 2006-2008 Sander van Dijk +© 2006-2007 Michał Janeczek +© 2007 Kris Maglione © 2009 Gottox © 2009 Markus Schnalke © 2009 Evan Gates -© 2006-2008 Sander van Dijk -© 2006-2007 Michał Janeczek -© 2014-2015 Hiltjo Posthuma +© 2010-2012 Connor Lane Smith +© 2014-2019 Hiltjo Posthuma +© 2015-2019 Quentin Rameau 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/README b/README new file mode 100644 index 0000000..a8fcdfe --- /dev/null +++ b/README @@ -0,0 +1,24 @@ +dmenu - dynamic menu +==================== +dmenu is an efficient dynamic menu for X. + + +Requirements +------------ +In order to build dmenu you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dmenu is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dmenu +(if necessary as root): + + make clean install + + +Running dmenu +------------- +See the man page for details. diff --git a/README.md b/README.md deleted file mode 100644 index 202f04b..0000000 --- a/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Ronald's dmenu - -This is just my build of dmenu. Nothing special, don't bother starring ;-) - -Only changes are minor config ones: bigger font and different colors. - -This is very similar to Luke Smith's Config, but with a few minor changes, a different font and different size. - -## Installation - -After making any config changes that you want, but `make`, `sudo make install` it. diff --git a/config.def.h b/config.def.h new file mode 100644 index 0000000..c022db1 --- /dev/null +++ b/config.def.h @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "Inconsolata:size=14" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#fbf1c7", "#1d2021" }, + [SchemeSel] = { "#fbf1c7", "#fb4934" }, + [SchemeOut] = { "#fbf1c7", "#8ec07c" }, +}; +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/config.h b/config.h index e837018..4bbea21 100644 --- a/config.h +++ b/config.h @@ -4,15 +4,12 @@ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ /* -fn option overrides fonts[0]; default X11 font or font set */ static const char *fonts[] = { - "DejaVu Sans Mono:size=10" + "xos4 Terminus:size=13" + }; static const char *prompt = NULL; /* -p option; prompt to the left of input field */ -static const char *colors[SchemeLast][2] = { - /* fg bg */ - [SchemeNorm] = { "#268bd2", "#000000" }, - [SchemeSel] = { "#000000", "#268bd2" }, - [SchemeOut] = { "#000000", "#00ffff" }, -}; +#include "/home/regan/.cache/wal/colors-wal-dmenu.h" + /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ static unsigned int lines = 0; 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 b/dmenu deleted file mode 100755 index d8e28f6..0000000 Binary files a/dmenu and /dev/null differ 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..65f25ce 100644 --- a/dmenu.c +++ b/dmenu.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -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); @@ -287,18 +288,42 @@ nextrune(int inc) return n; } +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(); } @@ -512,6 +556,11 @@ run(void) if (XFilterEvent(&ev, win)) continue; switch(ev.type) { + case DestroyNotify: + if (ev.xdestroywindow.window != win) + break; + cleanup(); + exit(1); case Expose: if (ev.xexpose.count == 0) drw_map(drw, win, 0, 0, mw, mh); @@ -539,22 +588,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 +612,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,15 +662,19 @@ setup(void) win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + XSetClassHint(dpy, win, &ch); + + + /* input methods */ + if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed: could not open input device"); - /* open input methods */ - xim = XOpenIM(dpy, NULL, NULL, NULL); xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, XNFocusWindow, win, NULL); XMapRaised(dpy, win); if (embed) { - XSelectInput(dpy, parentwin, FocusChangeMask); + XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { for (i = 0; i < du && dws[i] != win; ++i) XSelectInput(dpy, dws[i], FocusChangeMask); @@ -699,7 +752,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.o b/dmenu.o deleted file mode 100644 index ed69458..0000000 Binary files a/dmenu.o and /dev/null differ diff --git a/dmenu_path b/dmenu_path old mode 100644 new mode 100755 index 338bac4..3a7cda7 --- 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); diff --git a/drw.o b/drw.o deleted file mode 100644 index f26e71b..0000000 Binary files a/drw.o and /dev/null differ diff --git a/stest b/stest deleted file mode 100755 index a3ba4b3..0000000 Binary files a/stest and /dev/null differ diff --git a/stest.o b/stest.o deleted file mode 100644 index 1aeb634..0000000 Binary files a/stest.o and /dev/null differ diff --git a/util.o b/util.o deleted file mode 100644 index f3ada11..0000000 Binary files a/util.o and /dev/null differ