Compare commits

...

139 Commits
1.7 ... 3.7

Author SHA1 Message Date
8911aa1060 minor fix 2008-06-18 18:20:21 +01:00
d2305e4b13 reusing config.h's color values, note we have to use const char *, instead of const char [] here, because the pointer might change 2008-06-14 10:55:13 +01:00
fcf26a38f1 using xidx 2008-06-13 12:04:04 +01:00
4fc6cbe608 yet another cosmetic change 2008-06-13 11:53:53 +01:00
a45f106d3c cosmetic fixes 2008-06-13 11:46:50 +01:00
596033b781 s/unsigned int/uint/, s/unsigned long/ulong/ 2008-05-22 11:15:11 +01:00
357558798c foo 2008-05-20 15:07:42 +01:00
1f6af5e78f added Xinerama support to dmenu, reverted -b behavior, removed -x, -y, -w 2008-05-19 20:29:32 +01:00
f3617bd7ca no exe should be unquoted, agreed to Peter Hartlich 2008-04-10 18:12:00 +01:00
096fb3723b fixed unquoted dmenu_run $exe-use reported by Jukka 2008-04-10 16:46:32 +01:00
6423288394 though sticking to |uniq 2008-04-09 23:32:46 +01:00
7195e941c1 re-applied Peter Hartlich's and Jukkas dmenu-related patches, for odd reasons they disappeared 2008-04-09 23:31:49 +01:00
12805b69ac Added tag 3.6 for changeset 0bc2751d06e8 2008-04-03 21:56:42 +01:00
1538bb7426 fix typo 2008-03-25 19:18:16 +01:00
cbe5e88e4f using limits.h 2008-03-25 10:18:17 +00:00
2fe3ccdf58 bugfix of the -0 case in dmenu (thanks to Sander for his hint) 2008-03-25 09:43:34 +00:00
07d82c0c2a applied Jukkas hotfix 2008-03-24 15:56:41 +00:00
e7423f8fc6 more cosmetics 2008-03-23 22:06:09 +01:00
61976c292b cosmetics 2008-03-23 21:17:35 +01:00
63c9e30a76 made some whitespacing consistent. 2008-03-23 21:11:42 +01:00
015f51ac46 removed unused variable nitem. 2008-03-23 12:09:29 +01:00
542c58d8d0 several performance tweaks 2008-03-22 14:52:00 +00:00
745c46d8fa fixed match() to prefer prefix-matches to strstr-matches in the match list, extended the -y handling, next version is 3.6 2008-03-18 16:52:51 +00:00
d058e83570 Added tag 3.5 for changeset 05e5bd706b3b 2008-03-13 16:53:25 +00:00
301b4e5591 applied next patch of Sander 2008-03-13 12:02:29 +00:00
38b866ba34 applied Sanders patch 2008-03-12 21:37:43 +00:00
540a78761b removed ido-matching, changed behavior of -i meaning case insensitive matching now, commented -x, -y, -w arguments in dmenu.1 2008-03-12 15:41:19 +00:00
7804354106 removed set 2008-02-12 09:42:48 +00:00
6d8e68dcfd updated 2008-02-11 11:22:38 +00:00
d8688f7a4f added dmenu run 2007-12-22 12:20:20 +00:00
09d0926bb9 Added tag 3.4 for changeset 9ab649b3b3e5 2007-10-25 20:26:17 +02:00
adc4ec02c0 adapted c99 for pedantic checks, even if those checks are somewhat broken 2007-10-10 18:57:51 +02:00
8b2f132973 implemented strcasestr for dmenu (I call it cistrstr) for portability issues (cygwin has no strcasestr, oh dear) 2007-10-01 15:28:42 +02:00
1c488e6dac fixed font definition 2007-10-01 11:44:25 +02:00
03c546c6b3 fixed dmenu 2007-09-30 19:20:31 +02:00
fdc1dba7ce using saner defaults 2007-09-30 12:47:40 +02:00
3439470a47 applied new color nuance 2007-09-27 18:55:05 +02:00
6674bac1d3 applied my favorite color scheme 2007-09-26 19:15:47 +02:00
72a8eb412f casting char to int when using tolower (thanks to Jukkas careful cosmetic checking!) 2007-09-24 15:04:31 +02:00
975dfb4163 switching to white 2007-09-23 18:49:24 +02:00
2e898a308f yet another cosmetic fix 2007-09-23 18:32:08 +02:00
6514b07ad2 small cosmetic fix 2007-09-23 18:31:19 +02:00
724fe3cf7f applied Michał Janeczek dmenu patch, made dmenu match case-insensitive by default, added -i command line option to enable ido matching, added Michał to Copyright holders 2007-09-23 18:26:41 +02:00
70cb32b021 Added tag 3.3 for changeset 709df5a4bad7 2007-09-22 09:12:50 +02:00
56f9e26b72 applied yiyus initfont fix 2007-09-19 17:33:35 +02:00
d094ebea96 reorganized 2007-09-17 20:53:14 +02:00
ccf4d7166f next version is 3.3 2007-09-17 09:13:21 +02:00
06ae894434 micromizing dmenu step 1 2007-09-16 20:14:09 +02:00
b97783b07f fixed fallback 2007-09-15 20:28:20 +02:00
11cb2e7dcc applied ido-matching to dmenu 2007-07-24 18:19:09 +02:00
07239bbddd foooooo 2007-06-01 12:28:30 +02:00
124bfd4a08 Added tag 3.2 for changeset e4c81a78ffba 2007-05-30 12:22:38 +02:00
d27e3c1092 referred to LICENSE file 2007-05-30 12:19:06 +02:00
3a9f3a51ce I agree with the race fix of JG, but I dislike the SUSV3-breaking find, and I don't care about PATH changes, keep it simple, stupid 2007-05-24 10:34:44 +02:00
53e92b5c17 Fix the uptodate logic (uptodate if !find newer dirs than the cache). 2007-05-23 19:38:23 -04:00
d50ff5ca11 Silence the first find in dmenu_path. 2007-05-23 18:35:05 -04:00
383e40dc21 Fix grouping in dmenu_path. 2007-05-23 16:59:38 -04:00
8369e1736b Merge. 2007-05-23 16:44:15 -04:00
c04b688cc0 Changed dmenu_path (fixed race, improved speed, check that $PATH is the same as the last run). 2007-05-23 16:42:51 -04:00
4ebd7c4a21 removed some superflous strncmp's 2007-05-23 22:32:43 +02:00
dfe95cb546 made dmenu_path the way anydot proposed in response to Jukka 2007-05-23 22:13:46 +02:00
8b633bf17d applied Jukka's fix 2007-05-23 13:22:27 +02:00
64697cdd0c Added tag 3.1 for changeset 8f0f917ac988 2007-05-21 14:36:03 +02:00
5a3dfb1c40 removed strip, added -s to LDFLAGs 2007-05-15 13:44:41 +02:00
4042a11e51 applied anydot's dmenu_path caching patch, thank you! 2007-05-14 11:56:41 +02:00
aa2f73fc88 fixed a small bug in dmenu when an empty font is supplied 2007-05-02 15:25:52 +02:00
f189781bbd Added tag 3.0 for changeset 59b3024854db 2007-04-19 09:27:08 +02:00
0e19146d5f making copyright notice more compact 2007-04-13 11:36:44 +02:00
85a151c6a4 next version is 2.9 2007-03-28 08:17:57 +02:00
1fa4fa386a allowing numpad keys as inpyt 2007-03-27 16:52:50 +02:00
e1515a2fdc Added tag 2.8 for changeset dd3d02b07cac 2007-03-07 13:30:57 +01:00
f555908cef attempt to grab the keyboard only 1000 times, not forever. 2007-03-07 11:01:14 +01:00
73120414e2 reverting keyboard grab to root window - invoking several dmenu's now works again... 2007-03-07 10:54:21 +01:00
9911455f5b Wait for an Expose and draw the menu immediately after mapping the window. 2007-03-06 03:24:40 -05:00
2772c51e79 Added tag 2.7 for changeset fbd9e9d63f20 2007-03-05 11:25:18 +01:00
d57c873f10 keyboard grab works on the dmenu window now (not on the root window) - thx for Kris for this hint 2007-03-02 21:48:48 +01:00
724f72142f also, don't set the font all the time 2007-03-02 15:16:36 +01:00
5b8dce15d3 Added tag 2.6 for changeset 775f761a5647 2007-03-01 15:47:54 +01:00
063c3d26e9 useless var declaration prev_nitem 2007-02-27 14:44:21 +01:00
03c2b05bb2 changed Backspace/C-w behavior (now it only removes a single character) 2007-02-26 14:07:19 +01:00
8a066fabd9 if isatty() first read from stdin and then grab the keyboard, otherwise first grab the keyboard and then read from stdin 2007-02-26 11:44:41 +01:00
6c0e05eb0d next is 2.6, but not yet ;) 2007-02-26 10:47:52 +01:00
900d34347b Escape -s in dmenu.1 2007-02-25 16:41:05 -05:00
4d0cc17794 Added tag 2.5 for changeset 041143e9fc54 2007-02-24 15:38:26 +01:00
28b6ea0f67 I also dislike sort -u, I support that each Unix tool does one job and does it right ;) 2007-02-24 15:38:10 +01:00
598d72fba9 removed superfluous externs as well 2007-02-24 14:07:40 +01:00
007be12f2b using the old-style fashion we uses earlier 2007-02-23 15:28:25 +01:00
6b5b580aff fixed a bug when dmenu is run with -v 2007-02-23 14:39:54 +01:00
d8b48d64e1 Added tag 2.4.2 for changeset 1ca5d430524e 2007-02-23 13:51:23 +01:00
a71424ac0e ok 2.4.2 2007-02-23 13:51:06 +01:00
f1ab687c62 Added tag 2.4.1 for changeset 03e83e2788c8 2007-02-23 13:28:43 +01:00
68a24f5535 hotfix 2007-02-23 13:28:38 +01:00
47b37834c4 Added tag 2.4 for changeset 9e9036cbfb4b 2007-02-23 10:42:00 +01:00
5f436fdcb7 added dmenu_path convenience script 2007-02-23 10:16:43 +01:00
de34639883 made Fnt an anonymous struct 2007-02-22 18:16:35 +01:00
18592825d2 Added tag 2.3 for changeset b6e09682c8adcb6569656bee73c311f9ab457715 2007-02-21 11:05:19 +01:00
3bc1f99e3c applied the fix inspired by a recent wmii commit 2007-02-21 10:59:36 +01:00
8a10b35c64 renamed getcolor to initcolor 2007-02-20 13:57:05 +01:00
e4fc905317 s/setfont/initfont/ 2007-02-20 13:54:37 +01:00
a1913a6af7 readded draw.c again (except getcolor and setfont) 2007-02-20 13:54:00 +01:00
66b2e8379f removed -t, now using isatty() instead of select() to prevent execution from an interactive shell 2007-02-19 21:18:36 +01:00
665488a6ee removed draw.c, implemented C-w handling (backward word deletion) 2007-02-19 15:49:50 +01:00
1e01ca0317 Added tag 2.2 for changeset 90f0e34e7f11 2007-02-14 09:42:02 +01:00
f3c12a7bff fixed Copyright notice in Makefile 2007-02-08 14:10:17 +01:00
3ba8b71aba nah nah nah, I can't get used to the bottom bar, pushing the conditional dmenu again 2007-02-08 11:17:11 +01:00
2bac5599b3 letting dmenu appear at the bottom by default 2007-02-08 11:10:29 +01:00
dbccf6fbff got rid of LD, inspired by JGs patch to wmii 2007-02-05 11:10:41 +01:00
aea9bfec5e Added tag 2.1 for changeset 7656557298c9 2007-01-17 11:10:31 +01:00
6175a39e8e hotfix changes 2007-01-17 11:10:26 +01:00
2c7ff85f24 Added tag 2.1 for changeset d91c79020430 2007-01-17 11:10:09 +01:00
447046f7ae Added tag 2.0 for changeset 1fce5c464fcd 2007-01-16 11:42:09 +01:00
b76632b9e3 small fix 2007-01-16 11:39:26 +01:00
f8f5b27036 removed useless mx, my 2007-01-16 11:38:31 +01:00
d6bf35caad applied new default colors 2007-01-16 11:24:51 +01:00
507c030b5b small fix of Control-j in dmenu.1 2007-01-16 11:07:30 +01:00
0245394e4d Added tag 1.9 for changeset c7f5f4d54317 2007-01-12 12:43:44 +01:00
b6cd6ed266 added evil key support to dmenu 2007-01-11 15:52:37 +01:00
09813fcf2c s/Mod1-Shift-g/Mod1-G/ in fact Sander is right 2007-01-11 13:51:15 +01:00
64d68b7de9 documenting undocumented vi-alike shortcuts of dmenu 2007-01-11 11:41:16 +01:00
eaf8909378 commented recent Control-shortcut additions 2007-01-11 10:17:01 +01:00
c0fcfb7827 applied Sanders dmenu_ctrlchars.patch (thanks!) 2007-01-10 23:07:03 +01:00
a45d6983ab applied Alexis Hildebrandts patches 2007-01-10 18:06:16 +01:00
08420a935b corrections 2007-01-02 15:44:32 +01:00
deea1fb5ab corrected 2007-01-02 15:41:13 +01:00
771c0cb607 next version will contain updated copyright notice 2007-01-02 15:38:44 +01:00
43a19425af Added tag 1.8 for changeset d3e6fa22ae45b38b1bdb0d813390365e5930360b 2006-12-19 11:49:38 +01:00
bbfd5391ac fixed a typo in dmenu.1 2006-12-19 11:49:28 +01:00
0e96ae6f48 prepared dmenu-1.8, shortened command line options (-font is -fn, -selbg is -sb, -selfg is -sf, -normbg is -nb, -normfg is -nf now) 2006-12-19 11:39:07 +01:00
989649b5a5 added wmii reference to dmenu(1) 2006-12-18 13:46:44 +01:00
f8d2a29a20 agreed with Sander 2006-12-18 13:25:11 +01:00
3438af001a applied Stefan Tibus' sun patch, added -bottom option to dmenu which makes it appear in the bottom (for wmii compliance), slightly modified version than the patch proposed by Stefan Tibus 2006-12-18 12:52:58 +01:00
201b056e39 hotfix 2006-12-14 14:41:53 +01:00
3dcdbe2a57 added vi-like key-bindings for keyboards without cursor keys (they are undocumented features) 2006-12-14 14:40:58 +01:00
5336608a86 Added tag 1.7.1 for changeset 3696d77aaf02f5d15728dde3b9e35abcaf291496 2006-12-14 09:45:11 +01:00
d398a2e318 foo 2006-12-14 09:45:06 +01:00
2829009138 silent hotfix 2006-12-14 09:34:24 +01:00
cae2fbdf38 Added tag 1.7.1 for changeset 8e0b9b09bf83c429c73e60a23997f32877a645bf 2006-12-14 09:30:31 +01:00
225cd764c3 applied hotfix of sander 2006-12-14 09:30:23 +01:00
279966a16f Added tag 1.7 for changeset 58dbef4aef3d45c7a3da6945e53c9667c0f02d5b 2006-12-14 08:51:21 +01:00
13 changed files with 881 additions and 705 deletions

22
.hgtags
View File

@ -14,3 +14,25 @@ bee7fe6d1189174d0204ca3195b83cdc1bb4f82e 1.2
df3fbb050004c544d14e43c36f6a94cca6ed4a69 1.4
e071fb045bd9e8574947acff7196360bc0270e68 1.5
dcc5427f99f51a978386a0dd770467cd911ac84b 1.6
58dbef4aef3d45c7a3da6945e53c9667c0f02d5b 1.7
3696d77aaf02f5d15728dde3b9e35abcaf291496 1.7.1
d3e6fa22ae45b38b1bdb0d813390365e5930360b 1.8
c7f5f4d543170f03d70468e98a3a0ec8d2c4161b 1.9
1fce5c464fcd870b9f024aa1853d5cf3a3eb371b 2.0
7656557298c954469a6a9564e6649b1fb5db663e 2.1
90f0e34e7f118c9ad3227a1606211ee825942b1c 2.2
b6e09682c8adcb6569656bee73c311f9ab457715 2.3
9e9036cbfb4b7306c6fb366249e81dc0e65bdfde 2.4
03e83e2788c83ddd63b45a667939d7ec783c98cb 2.4.1
1ca5d430524e838c52ede912533cb90108c5cd66 2.4.2
041143e9fc544c62edc58af52cae9ac5237e5945 2.5
775f761a5647a05038e091d1c99fc35d3034cd68 2.6
fbd9e9d63f202afe6834ccfdf890904f1897ec0b 2.7
dd3d02b07cac44fbafc074a361c1002cebe7aae4 2.8
59b3024854db49739c6d237fa9077f04a2da847a 3.0
8f0f917ac988164e1b4446236e3a6ab6cfcb8c67 3.1
e4c81a78ffbad6ba4d1ad119cc654da6eca63a4c 3.2
709df5a4bad7015a346b2b44b1b3b573ea3088ff 3.3
9ab649b3b3e5bfccf1c8f352c59e5361e070a25f 3.4
05e5bd706b3b3e61399d57c4bb43df296a20112d 3.5
0bc2751d06e8b95e0138854c7815e154c5c3d990 3.6

23
LICENSE
View File

@ -1,22 +1,23 @@
MIT/X Consortium License
(C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
(C)opyright MMVI Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2006-2008 Anselm R. Garbe <garbeam at gmail dot com>
© 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2006-2007 Michał Janeczek <janeczek at gmail dot com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,9 +1,9 @@
# dmenu - dynamic menu
# (C)opyright MMVI Anselm R. Garbe
# See LICENSE file for copyright and license details.
include config.mk
SRC = draw.c main.c util.c
SRC = dmenu.c
OBJ = ${SRC:.c=.o}
all: options dmenu
@ -13,18 +13,16 @@ options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
@echo "LD = ${LD}"
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
${OBJ}: dmenu.h config.mk
${OBJ}: config.h config.mk
dmenu: ${OBJ}
@echo LD $@
@${LD} -o $@ ${OBJ} ${LDFLAGS}
@strip $@
@echo CC -o $@
@${CC} -o $@ ${OBJ} ${LDFLAGS}
clean:
@echo cleaning
@ -33,7 +31,7 @@ clean:
dist: clean
@echo creating dist tarball
@mkdir -p dmenu-${VERSION}
@cp -R LICENSE Makefile README config.mk dmenu.1 dmenu.h ${SRC} dmenu-${VERSION}
@cp -R LICENSE Makefile README config.mk dmenu.1 config.h dmenu_path dmenu_run ${SRC} dmenu-${VERSION}
@tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
@gzip dmenu-${VERSION}.tar
@rm -rf dmenu-${VERSION}
@ -41,16 +39,19 @@ dist: clean
install: all
@echo installing executable file to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f dmenu ${DESTDIR}${PREFIX}/bin
@cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
@echo installing manual page 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" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_run
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1

14
config.h Normal file
View File

@ -0,0 +1,14 @@
/* See LICENSE file for copyright and license details. */
/* appearance */
static const char *font = "-*-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*";
static const char *normbgcolor = "#cccccc";
static const char *normfgcolor = "#000000";
static const char *selbgcolor = "#0066ff";
static const char *selfgcolor = "#ffffff";
static uint spaceitem = 30; /* px between menu items */
#ifdef XINERAMA
static uint xidx = 0; /* Xinerama screen index to use */
#endif

View File

@ -1,5 +1,5 @@
# dmenu version
VERSION = 1.7
VERSION = 3.7
# Customize below to fit your system
@ -10,16 +10,22 @@ MANPREFIX = ${PREFIX}/share/man
X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
# Xinerama, comment if you don't want it
XINERAMALIBS = -L${X11LIB} -lXinerama
XINERAMAFLAGS = -DXINERAMA
# includes and libs
INCS = -I. -I/usr/include -I${X11INC}
LIBS = -L/usr/lib -lc -L${X11LIB} -lX11
LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${XINERAMALIBS}
# flags
CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\"
LDFLAGS = ${LIBS}
#CFLAGS = -g -Wall -O2 ${INCS} -DVERSION=\"${VERSION}\"
#LDFLAGS = -g ${LIBS}
CPPFLAGS = -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS}
# Solaris
#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
#LDFLAGS = ${LIBS}
# compiler and linker
CC = cc
LD = ${CC}

66
dmenu.1
View File

@ -1,15 +1,16 @@
.TH DMENU 1 dmenu-VERSION
.TH DMENU 1 dmenu\-VERSION
.SH NAME
dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-font " <name>"]
.RB [ \-normbg " <color>"]
.RB [ \-normfg " <color>"]
.RB [ \-selbg " <color>"]
.RB [ \-selfg " <color>"]
.RB [ \-i ]
.RB [ \-b ]
.RB [ \-fn " <font>"]
.RB [ \-nb " <color>"]
.RB [ \-nf " <color>"]
.RB [ \-p " <prompt>"]
.RB [ \-t " <seconds>"]
.RB [ \-sb " <color>"]
.RB [ \-sf " <color>"]
.RB [ \-v ]
.SH DESCRIPTION
.SS Overview
@ -19,26 +20,29 @@ It manages huge amounts (up to 10.000 and more) of user defined menu items
efficiently.
.SS Options
.TP
.B \-font <name>
.B \-i
makes dmenu match menu entries case insensitively.
.TP
.B \-b
defines that dmenu appears at the bottom.
.TP
.B \-fn <font>
defines the font.
.TP
.B \-normbg <color>
.B \-nb <color>
defines the normal background color (#RGB, #RRGGBB, and color names are supported).
.TP
.B \-normfg <color>
.B \-nf <color>
defines the normal foreground color (#RGB, #RRGGBB, and color names are supported).
.TP
.B \-selbg <color>
defines the selected background color (#RGB, #RRGGBB, and color names are supported).
.TP
.B \-selfg <color>
defines the selected foreground color (#RGB, #RRGGBB, and color names are supported).
.TP
.B \-p <prompt>
defines a prompt to be displayed before the input area.
.TP
.B \-t <seconds>
defines the seconds to wait for standard input, before exiting (default is 3).
.B \-sb <color>
defines the selected background color (#RGB, #RRGGBB, and color names are supported).
.TP
.B \-sf <color>
defines the selected foreground color (#RGB, #RRGGBB, and color names are supported).
.TP
.B \-v
prints version information to standard output, then exits.
@ -53,38 +57,42 @@ dmenu is completely controlled by the keyboard. The following keys are recognize
Appends the character to the text in the input field. This works as a filter:
only items containing this text will be displayed.
.TP
.B Left/Right
.B Left/Right (Mod1\-h/Mod1\-l)
Select the previous/next item.
.TP
.B PageUp/PageDown
.B PageUp/PageDown (Mod1\-k/Mod1\-j)
Select the first item of the previous/next 'page' of items.
.TP
.B Home/End
.B Home/End (Mod1\-g/Mod1\-G)
Select the first/last item.
.TP
.B Tab
.B Tab (Control\-i)
Copy the selected item to the input field.
.TP
.B Return
.B Return (Control\-j)
Confirm selection and quit (print the selected item to standard output). Returns
.B 0
on termination.
.TP
.B Shift-Return
.B Shift\-Return (Control\-Shift\-j)
Confirm selection and quit (print the text in the input field to standard output).
Returns
.B 0
on termination.
.TP
.B Escape
.B Escape (Control\-bracketleft)
Quit without selecting an item. Returns
.B 1
on termination.
.TP
.B Backspace (Control-h)
Remove enough characters from the input field to change its filtering effect.
.B Backspace (Control\-h)
Remove a character from the input field.
.TP
.B Control-u
.B Control\-u
Remove all characters from the input field.
.TP
.B Control\-w
Remove all characters of current word from the input field.
.SH SEE ALSO
.BR dwm (1)
.BR dwm (1),
.BR wmii (1) .

743
dmenu.c Normal file
View File

@ -0,0 +1,743 @@
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <locale.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef XINERAMA
#include <X11/extensions/Xinerama.h>
#endif
/* macros */
#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
/* enums */
enum { ColFG, ColBG, ColLast };
/* typedefs */
typedef unsigned int uint;
typedef unsigned long ulong;
typedef struct {
int x, y, w, h;
ulong norm[ColLast];
ulong sel[ColLast];
Drawable drawable;
GC gc;
struct {
XFontStruct *xfont;
XFontSet set;
int ascent;
int descent;
int height;
} font;
} DC; /* draw context */
typedef struct Item Item;
struct Item {
char *text;
Item *next; /* traverses all items */
Item *left, *right; /* traverses items matching current search pattern */
};
/* forward declarations */
static void appenditem(Item *i, Item **list, Item **last);
static void calcoffsets(void);
static char *cistrstr(const char *s, const char *sub);
static void cleanup(void);
static void drawmenu(void);
static void drawtext(const char *text, ulong col[ColLast]);
static void *emalloc(uint size);
static void eprint(const char *errstr, ...);
static ulong getcolor(const char *colstr);
static Bool grabkeyboard(void);
static void initfont(const char *fontstr);
static void kpress(XKeyEvent * e);
static void match(char *pattern);
static void readstdin(void);
static void run(void);
static void setup(Bool topbar);
static int textnw(const char *text, uint len);
static int textw(const char *text);
#include "config.h"
/* variables */
static char *maxname = NULL;
static char *prompt = NULL;
static char text[4096];
static int cmdw = 0;
static int promptw = 0;
static int ret = 0;
static int screen;
static uint mw, mh;
static uint numlockmask = 0;
static Bool running = True;
static Display *dpy;
static DC dc = {0};
static Item *allitems = NULL; /* first of all items */
static Item *item = NULL; /* first of pattern matching items */
static Item *sel = NULL;
static Item *next = NULL;
static Item *prev = NULL;
static Item *curr = NULL;
static Window root, win;
static int (*fstrncmp)(const char *, const char *, size_t n) = strncmp;
static char *(*fstrstr)(const char *, const char *) = strstr;
void
appenditem(Item *i, Item **list, Item **last) {
if(!(*last))
*list = i;
else
(*last)->right = i;
i->left = *last;
i->right = NULL;
*last = i;
}
void
calcoffsets(void) {
int tw;
uint w;
if(!curr)
return;
w = promptw + cmdw + 2 * spaceitem;
for(next = curr; next; next=next->right) {
tw = textw(next->text);
if(tw > mw / 3)
tw = mw / 3;
w += tw;
if(w > mw)
break;
}
w = promptw + cmdw + 2 * spaceitem;
for(prev = curr; prev && prev->left; prev=prev->left) {
tw = textw(prev->left->text);
if(tw > mw / 3)
tw = mw / 3;
w += tw;
if(w > mw)
break;
}
}
char *
cistrstr(const char *s, const char *sub) {
int c, csub;
uint len;
if(!sub)
return (char *)s;
if((c = *sub++) != 0) {
c = tolower(c);
len = strlen(sub);
do {
do {
if((csub = *s++) == 0)
return (NULL);
}
while(tolower(csub) != c);
}
while(strncasecmp(s, sub, len) != 0);
s--;
}
return (char *)s;
}
void
cleanup(void) {
Item *itm;
while(allitems) {
itm = allitems->next;
free(allitems->text);
free(allitems);
allitems = itm;
}
if(dc.font.set)
XFreeFontSet(dpy, dc.font.set);
else
XFreeFont(dpy, dc.font.xfont);
XFreePixmap(dpy, dc.drawable);
XFreeGC(dpy, dc.gc);
XDestroyWindow(dpy, win);
XUngrabKeyboard(dpy, CurrentTime);
}
void
drawmenu(void) {
Item *i;
dc.x = 0;
dc.y = 0;
dc.w = mw;
dc.h = mh;
drawtext(NULL, dc.norm);
/* print prompt? */
if(promptw) {
dc.w = promptw;
drawtext(prompt, dc.sel);
}
dc.x += promptw;
dc.w = mw - promptw;
/* print command */
if(cmdw && item)
dc.w = cmdw;
drawtext(text[0] ? text : NULL, dc.norm);
dc.x += cmdw;
if(curr) {
dc.w = spaceitem;
drawtext((curr && curr->left) ? "<" : NULL, dc.norm);
dc.x += dc.w;
/* determine maximum items */
for(i = curr; i != next; i=i->right) {
dc.w = textw(i->text);
if(dc.w > mw / 3)
dc.w = mw / 3;
drawtext(i->text, (sel == i) ? dc.sel : dc.norm);
dc.x += dc.w;
}
dc.x = mw - spaceitem;
dc.w = spaceitem;
drawtext(next ? ">" : NULL, dc.norm);
}
XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, mw, mh, 0, 0);
XFlush(dpy);
}
void
drawtext(const char *text, ulong col[ColLast]) {
int x, y, w, h;
static char buf[256];
uint len, olen;
XRectangle r = { dc.x, dc.y, dc.w, dc.h };
XSetForeground(dpy, dc.gc, col[ColBG]);
XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
if(!text)
return;
w = 0;
olen = len = strlen(text);
if(len >= sizeof buf)
len = sizeof buf - 1;
memcpy(buf, text, len);
buf[len] = 0;
h = dc.font.ascent + dc.font.descent;
y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
x = dc.x + (h / 2);
/* shorten text if necessary */
while(len && (w = textnw(buf, len)) > dc.w - h)
buf[--len] = 0;
if(len < olen) {
if(len > 1)
buf[len - 1] = '.';
if(len > 2)
buf[len - 2] = '.';
if(len > 3)
buf[len - 3] = '.';
}
if(w > dc.w)
return; /* too long */
XSetForeground(dpy, dc.gc, col[ColFG]);
if(dc.font.set)
XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
else
XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
}
void *
emalloc(uint size) {
void *res = malloc(size);
if(!res)
eprint("fatal: could not malloc() %u bytes\n", size);
return res;
}
void
eprint(const char *errstr, ...) {
va_list ap;
va_start(ap, errstr);
vfprintf(stderr, errstr, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
ulong
getcolor(const char *colstr) {
Colormap cmap = DefaultColormap(dpy, screen);
XColor color;
if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
eprint("error, cannot allocate color '%s'\n", colstr);
return color.pixel;
}
Bool
grabkeyboard(void) {
uint len;
for(len = 1000; len; len--) {
if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
== GrabSuccess)
break;
usleep(1000);
}
return len > 0;
}
void
initfont(const char *fontstr) {
char *def, **missing;
int i, n;
if(!fontstr || fontstr[0] == '\0')
eprint("error, cannot load font: '%s'\n", fontstr);
missing = NULL;
if(dc.font.set)
XFreeFontSet(dpy, dc.font.set);
dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
if(missing)
XFreeStringList(missing);
if(dc.font.set) {
XFontSetExtents *font_extents;
XFontStruct **xfonts;
char **font_names;
dc.font.ascent = dc.font.descent = 0;
font_extents = XExtentsOfFontSet(dc.font.set);
n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
if(dc.font.ascent < (*xfonts)->ascent)
dc.font.ascent = (*xfonts)->ascent;
if(dc.font.descent < (*xfonts)->descent)
dc.font.descent = (*xfonts)->descent;
xfonts++;
}
}
else {
if(dc.font.xfont)
XFreeFont(dpy, dc.font.xfont);
dc.font.xfont = NULL;
if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
&& !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
eprint("error, cannot load font: '%s'\n", fontstr);
dc.font.ascent = dc.font.xfont->ascent;
dc.font.descent = dc.font.xfont->descent;
}
dc.font.height = dc.font.ascent + dc.font.descent;
}
void
kpress(XKeyEvent * e) {
char buf[32];
int i, num;
uint len;
KeySym ksym;
len = strlen(text);
buf[0] = 0;
num = XLookupString(e, buf, sizeof buf, &ksym, 0);
if(IsKeypadKey(ksym)) {
if(ksym == XK_KP_Enter)
ksym = XK_Return;
else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
ksym = (ksym - XK_KP_0) + XK_0;
}
if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
|| IsPrivateKeypadKey(ksym))
return;
/* first check if a control mask is omitted */
if(e->state & ControlMask) {
switch (ksym) {
default: /* ignore other control sequences */
return;
case XK_bracketleft:
ksym = XK_Escape;
break;
case XK_h:
case XK_H:
ksym = XK_BackSpace;
break;
case XK_i:
case XK_I:
ksym = XK_Tab;
break;
case XK_j:
case XK_J:
ksym = XK_Return;
break;
case XK_u:
case XK_U:
text[0] = 0;
match(text);
drawmenu();
return;
case XK_w:
case XK_W:
if(len) {
i = len - 1;
while(i >= 0 && text[i] == ' ')
text[i--] = 0;
while(i >= 0 && text[i] != ' ')
text[i--] = 0;
match(text);
drawmenu();
}
return;
}
}
if(CLEANMASK(e->state) & Mod1Mask) {
switch(ksym) {
default: return;
case XK_h:
ksym = XK_Left;
break;
case XK_l:
ksym = XK_Right;
break;
case XK_j:
ksym = XK_Next;
break;
case XK_k:
ksym = XK_Prior;
break;
case XK_g:
ksym = XK_Home;
break;
case XK_G:
ksym = XK_End;
break;
}
}
switch(ksym) {
default:
if(num && !iscntrl((int) buf[0])) {
buf[num] = 0;
if(len > 0)
strncat(text, buf, sizeof text);
else
strncpy(text, buf, sizeof text);
match(text);
}
break;
case XK_BackSpace:
if(len) {
text[--len] = 0;
match(text);
}
break;
case XK_End:
if(!item)
return;
while(next) {
sel = curr = next;
calcoffsets();
}
while(sel && sel->right)
sel = sel->right;
break;
case XK_Escape:
ret = 1;
running = False;
break;
case XK_Home:
if(!item)
return;
sel = curr = item;
calcoffsets();
break;
case XK_Left:
if(!(sel && sel->left))
return;
sel=sel->left;
if(sel->right == curr) {
curr = prev;
calcoffsets();
}
break;
case XK_Next:
if(!next)
return;
sel = curr = next;
calcoffsets();
break;
case XK_Prior:
if(!prev)
return;
sel = curr = prev;
calcoffsets();
break;
case XK_Return:
if((e->state & ShiftMask) && text)
fprintf(stdout, "%s", text);
else if(sel)
fprintf(stdout, "%s", sel->text);
else if(text)
fprintf(stdout, "%s", text);
fflush(stdout);
running = False;
break;
case XK_Right:
if(!(sel && sel->right))
return;
sel=sel->right;
if(sel == next) {
curr = next;
calcoffsets();
}
break;
case XK_Tab:
if(!sel)
return;
strncpy(text, sel->text, sizeof text);
match(text);
break;
}
drawmenu();
}
void
match(char *pattern) {
uint plen;
Item *i, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
if(!pattern)
return;
plen = strlen(pattern);
item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL;
for(i = allitems; i; i = i->next)
if(!fstrncmp(pattern, i->text, plen + 1))
appenditem(i, &lexact, &exactend);
else if(!fstrncmp(pattern, i->text, plen))
appenditem(i, &lprefix, &prefixend);
else if(fstrstr(i->text, pattern))
appenditem(i, &lsubstr, &substrend);
if(lexact) {
item = lexact;
itemend = exactend;
}
if(lprefix) {
if(itemend) {
itemend->right = lprefix;
lprefix->left = itemend;
}
else
item = lprefix;
itemend = prefixend;
}
if(lsubstr) {
if(itemend) {
itemend->right = lsubstr;
lsubstr->left = itemend;
}
else
item = lsubstr;
}
curr = prev = next = sel = item;
calcoffsets();
}
void
readstdin(void) {
char *p, buf[1024];
uint len = 0, max = 0;
Item *i, *new;
i = 0;
while(fgets(buf, sizeof buf, stdin)) {
len = strlen(buf);
if (buf[len - 1] == '\n')
buf[len - 1] = 0;
if(!(p = strdup(buf)))
eprint("fatal: could not strdup() %u bytes\n", strlen(buf));
if(max < len) {
maxname = p;
max = len;
}
new = emalloc(sizeof(Item));
new->next = new->left = new->right = NULL;
new->text = p;
if(!i)
allitems = new;
else
i->next = new;
i = new;
}
}
void
run(void) {
XEvent ev;
/* main event loop */
while(running && !XNextEvent(dpy, &ev))
switch (ev.type) {
default: /* ignore all crap */
break;
case KeyPress:
kpress(&ev.xkey);
break;
case Expose:
if(ev.xexpose.count == 0)
drawmenu();
break;
}
}
void
setup(Bool topbar) {
int i, j, x, y;
XModifierKeymap *modmap;
XSetWindowAttributes wa;
#if XINERAMA
XineramaScreenInfo *info = NULL;
#endif
/* init modifier map */
modmap = XGetModifierMapping(dpy);
for(i = 0; i < 8; i++)
for(j = 0; j < modmap->max_keypermod; j++) {
if(modmap->modifiermap[i * modmap->max_keypermod + j]
== XKeysymToKeycode(dpy, XK_Num_Lock))
numlockmask = (1 << i);
}
XFreeModifiermap(modmap);
/* style */
dc.norm[ColBG] = getcolor(normbgcolor);
dc.norm[ColFG] = getcolor(normfgcolor);
dc.sel[ColBG] = getcolor(selbgcolor);
dc.sel[ColFG] = getcolor(selfgcolor);
initfont(font);
/* menu window */
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask;
/* menu window geometry */
mh = dc.font.height + 2;
#if XINERAMA
if(XineramaIsActive(dpy)) {
info = XineramaQueryScreens(dpy, &i);
x = info[xidx].x_org;
y = topbar ? info[xidx].y_org : info[xidx].y_org + info[xidx].height - mh;
mw = info[xidx].width;
XFree(info);
}
else
#endif
{
x = 0;
y = topbar ? 0 : DisplayHeight(dpy, screen) - mh;
mw = DisplayWidth(dpy, screen);
}
win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
DefaultDepth(dpy, screen), CopyFromParent,
DefaultVisual(dpy, screen),
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
/* pixmap */
dc.drawable = XCreatePixmap(dpy, root, mw, mh, DefaultDepth(dpy, screen));
dc.gc = XCreateGC(dpy, root, 0, 0);
XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
if(!dc.font.set)
XSetFont(dpy, dc.gc, dc.font.xfont->fid);
if(maxname)
cmdw = textw(maxname);
if(cmdw > mw / 3)
cmdw = mw / 3;
if(prompt)
promptw = textw(prompt);
if(promptw > mw / 5)
promptw = mw / 5;
text[0] = 0;
match(text);
XMapRaised(dpy, win);
}
int
textnw(const char *text, uint len) {
XRectangle r;
if(dc.font.set) {
XmbTextExtents(dc.font.set, text, len, NULL, &r);
return r.width;
}
return XTextWidth(dc.font.xfont, text, len);
}
int
textw(const char *text) {
return textnw(text, strlen(text)) + dc.font.height;
}
int
main(int argc, char *argv[]) {
uint i;
Bool topbar = True;
/* command line args */
for(i = 1; i < argc; i++)
if(!strcmp(argv[i], "-i")) {
fstrncmp = strncasecmp;
fstrstr = cistrstr;
}
else if(!strcmp(argv[i], "-b"))
topbar = False;
else if(!strcmp(argv[i], "-fn")) {
if(++i < argc) font = argv[i];
}
else if(!strcmp(argv[i], "-nb")) {
if(++i < argc) normbgcolor = argv[i];
}
else if(!strcmp(argv[i], "-nf")) {
if(++i < argc) normfgcolor = argv[i];
}
else if(!strcmp(argv[i], "-p")) {
if(++i < argc) prompt = argv[i];
}
else if(!strcmp(argv[i], "-sb")) {
if(++i < argc) selbgcolor = argv[i];
}
else if(!strcmp(argv[i], "-sf")) {
if(++i < argc) selfgcolor = argv[i];
}
else if(!strcmp(argv[i], "-v"))
eprint("dmenu-"VERSION", © 2006-2008 dmenu engineers, see LICENSE for details\n");
else
eprint("usage: dmenu [-i] [-b] [-fn <font>] [-nb <color>] [-nf <color>]\n"
" [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n");
setlocale(LC_CTYPE, "");
if(!(dpy = XOpenDisplay(0)))
eprint("dmenu: cannot open display\n");
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
if(isatty(STDIN_FILENO)) {
readstdin();
running = grabkeyboard();
}
else { /* prevent keypress loss */
running = grabkeyboard();
readstdin();
}
setup(topbar);
drawmenu();
XSync(dpy, False);
run();
cleanup();
XCloseDisplay(dpy);
return ret;
}

51
dmenu.h
View File

@ -1,51 +0,0 @@
/* (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include <X11/Xlib.h>
#define FONT "-*-fixed-medium-r-normal-*-13-*-*-*-*-*-*-*"
#define NORMBGCOLOR "#333366"
#define NORMFGCOLOR "#cccccc"
#define SELBGCOLOR "#666699"
#define SELFGCOLOR "#eeeeee"
#define SPACE 30 /* px */
/* color */
enum { ColFG, ColBG, ColLast };
typedef struct DC DC;
typedef struct Fnt Fnt;
struct Fnt {
XFontStruct *xfont;
XFontSet set;
int ascent;
int descent;
int height;
};
struct DC {
int x, y, w, h;
unsigned long norm[ColLast];
unsigned long sel[ColLast];
Drawable drawable;
Fnt font;
GC gc;
}; /* draw context */
extern int screen;
extern Display *dpy;
extern DC dc; /* global drawing context */
/* draw.c */
extern void drawtext(const char *text,
unsigned long col[ColLast]); /* draws text with the defined color tuple */
extern unsigned long getcolor(const char *colstr); /* returns color of colstr */
extern void setfont(const char *fontstr); /* sets global font */
extern unsigned int textw(const char *text); /* returns width of text in px */
/* util.c */
extern void *emalloc(unsigned int size); /* allocates memory, exits on error */
extern void eprint(const char *errstr, ...); /* prints errstr and exits with 1 */
extern char *estrdup(const char *str); /* duplicates str, exits on allocation error */

26
dmenu_path Executable file
View File

@ -0,0 +1,26 @@
#!/bin/sh
CACHE=$HOME/.dmenu_cache
IFS=:
uptodate() {
test -f "$CACHE" &&
for dir in $PATH
do
test ! $dir -nt "$CACHE" || return 1
done
}
if ! uptodate
then
for dir in $PATH
do
cd "$dir" &&
for file in *
do
test -x "$file" && echo "$file"
done
done | sort | uniq > "$CACHE".$$ &&
mv "$CACHE".$$ "$CACHE"
fi
cat "$CACHE"

2
dmenu_run Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
exe=`dmenu_path | dmenu ${1+"$@"}` && exec $exe

121
draw.c
View File

@ -1,121 +0,0 @@
/* (C)opyright MMIV-MMVI Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include "dmenu.h"
#include <stdio.h>
#include <string.h>
/* static */
static unsigned int
textnw(const char *text, unsigned int len) {
XRectangle r;
if(dc.font.set) {
XmbTextExtents(dc.font.set, text, len, NULL, &r);
return r.width;
}
return XTextWidth(dc.font.xfont, text, len);
}
/* extern */
void
drawtext(const char *text, unsigned long col[ColLast]) {
int x, y, w, h;
static char buf[256];
unsigned int len, olen;
XGCValues gcv;
XRectangle r = { dc.x, dc.y, dc.w, dc.h };
XSetForeground(dpy, dc.gc, col[ColBG]);
XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
if(!text)
return;
w = 0;
olen = len = strlen(text);
if(len >= sizeof buf)
len = sizeof buf - 1;
memcpy(buf, text, len);
buf[len] = 0;
h = dc.font.ascent + dc.font.descent;
y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
x = dc.x + (h / 2);
/* shorten text if necessary */
while(len && (w = textnw(buf, len)) > dc.w - h)
buf[--len] = 0;
if(len < olen) {
if(len > 1)
buf[len - 1] = '.';
if(len > 2)
buf[len - 2] = '.';
if(len > 3)
buf[len - 3] = '.';
}
if(w > dc.w)
return; /* too long */
gcv.foreground = col[ColFG];
if(dc.font.set) {
XChangeGC(dpy, dc.gc, GCForeground, &gcv);
XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc,
x, y, buf, len);
}
else {
gcv.font = dc.font.xfont->fid;
XChangeGC(dpy, dc.gc, GCForeground | GCFont, &gcv);
XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
}
}
unsigned long
getcolor(const char *colstr) {
Colormap cmap = DefaultColormap(dpy, screen);
XColor color;
if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
eprint("error, cannot allocate color '%s'\n", colstr);
return color.pixel;
}
void
setfont(const char *fontstr) {
char *def, **missing;
int i, n;
missing = NULL;
if(dc.font.set)
XFreeFontSet(dpy, dc.font.set);
dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
if(missing)
XFreeStringList(missing);
if(dc.font.set) {
XFontSetExtents *font_extents;
XFontStruct **xfonts;
char **font_names;
dc.font.ascent = dc.font.descent = 0;
font_extents = XExtentsOfFontSet(dc.font.set);
n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
if(dc.font.ascent < (*xfonts)->ascent)
dc.font.ascent = (*xfonts)->ascent;
if(dc.font.descent < (*xfonts)->descent)
dc.font.descent = (*xfonts)->descent;
xfonts++;
}
}
else {
if(dc.font.xfont)
XFreeFont(dpy, dc.font.xfont);
dc.font.xfont = NULL;
if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)))
eprint("error, cannot load font: '%s'\n", fontstr);
dc.font.ascent = dc.font.xfont->ascent;
dc.font.descent = dc.font.xfont->descent;
}
dc.font.height = dc.font.ascent + dc.font.descent;
}
unsigned int
textw(const char *text) {
return textnw(text, strlen(text)) + dc.font.height;
}

437
main.c
View File

@ -1,437 +0,0 @@
/* (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
* (C)opyright MMVI Sander van Dijk <a dot h dot vandijk at gmail dot com>
* See LICENSE file for license details.
*/
#include "dmenu.h"
#include <ctype.h>
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
typedef struct Item Item;
struct Item {
Item *next; /* traverses all items */
Item *left, *right; /* traverses items matching current search pattern */
char *text;
};
/* static */
static char text[4096];
static char *prompt = NULL;
static int mx, my, mw, mh;
static int ret = 0;
static int nitem = 0;
static unsigned int cmdw = 0;
static unsigned int promptw = 0;
static Bool running = True;
static Item *allitems = NULL; /* first of all items */
static Item *item = NULL; /* first of pattern matching items */
static Item *sel = NULL;
static Item *next = NULL;
static Item *prev = NULL;
static Item *curr = NULL;
static Window root;
static Window win;
static void
calcoffsets(void) {
unsigned int tw, w;
if(!curr)
return;
w = promptw + cmdw + 2 * SPACE;
for(next = curr; next; next=next->right) {
tw = textw(next->text);
if(tw > mw / 3)
tw = mw / 3;
w += tw;
if(w > mw)
break;
}
w = promptw + cmdw + 2 * SPACE;
for(prev = curr; prev && prev->left; prev=prev->left) {
tw = textw(prev->left->text);
if(tw > mw / 3)
tw = mw / 3;
w += tw;
if(w > mw)
break;
}
}
static void
drawmenu(void) {
Item *i;
dc.x = 0;
dc.y = 0;
dc.w = mw;
dc.h = mh;
drawtext(NULL, dc.norm);
/* print prompt? */
if(promptw) {
dc.w = promptw;
drawtext(prompt, dc.sel);
}
dc.x += promptw;
dc.w = mw - promptw;
/* print command */
if(cmdw && item)
dc.w = cmdw;
drawtext(text[0] ? text : NULL, dc.norm);
dc.x += cmdw;
if(curr) {
dc.w = SPACE;
drawtext((curr && curr->left) ? "<" : NULL, dc.norm);
dc.x += dc.w;
/* determine maximum items */
for(i = curr; i != next; i=i->right) {
dc.w = textw(i->text);
if(dc.w > mw / 3)
dc.w = mw / 3;
drawtext(i->text, (sel == i) ? dc.sel : dc.norm);
dc.x += dc.w;
}
dc.x = mw - SPACE;
dc.w = SPACE;
drawtext(next ? ">" : NULL, dc.norm);
}
XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, mw, mh, 0, 0);
XFlush(dpy);
}
static void
match(char *pattern) {
unsigned int plen;
Item *i, *j;
if(!pattern)
return;
plen = strlen(pattern);
item = j = NULL;
nitem = 0;
for(i = allitems; i; i=i->next)
if(!plen || !strncmp(pattern, i->text, plen)) {
if(!j)
item = i;
else
j->right = i;
i->left = j;
i->right = NULL;
j = i;
nitem++;
}
for(i = allitems; i; i=i->next)
if(plen && strncmp(pattern, i->text, plen)
&& strstr(i->text, pattern)) {
if(!j)
item = i;
else
j->right = i;
i->left = j;
i->right = NULL;
j = i;
nitem++;
}
curr = prev = next = sel = item;
calcoffsets();
}
static void
kpress(XKeyEvent * e) {
char buf[32];
int num, prev_nitem;
unsigned int i, len;
KeySym ksym;
len = strlen(text);
buf[0] = 0;
num = XLookupString(e, buf, sizeof buf, &ksym, 0);
if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
|| IsPrivateKeypadKey(ksym))
return;
/* first check if a control mask is omitted */
if(e->state & ControlMask) {
switch (ksym) {
default: /* ignore other control sequences */
return;
break;
case XK_h:
case XK_H:
ksym = XK_BackSpace;
break;
case XK_u:
case XK_U:
text[0] = 0;
match(text);
drawmenu();
return;
break;
}
}
switch(ksym) {
default:
if(num && !iscntrl((int) buf[0])) {
buf[num] = 0;
if(len > 0)
strncat(text, buf, sizeof text);
else
strncpy(text, buf, sizeof text);
match(text);
}
break;
case XK_BackSpace:
if((i = len)) {
prev_nitem = nitem;
do {
text[--i] = 0;
match(text);
} while(i && nitem && prev_nitem == nitem);
match(text);
}
break;
case XK_End:
while(next) {
sel = curr = next;
calcoffsets();
}
while(sel->right)
sel = sel->right;
break;
case XK_Escape:
ret = 1;
running = False;
break;
case XK_Home:
sel = curr = item;
calcoffsets();
break;
case XK_Left:
if(!(sel && sel->left))
return;
sel=sel->left;
if(sel->right == curr) {
curr = prev;
calcoffsets();
}
break;
case XK_Next:
if(next) {
sel = curr = next;
calcoffsets();
}
break;
case XK_Prior:
if(prev) {
sel = curr = prev;
calcoffsets();
}
break;
case XK_Return:
if((e->state & ShiftMask) && text)
fprintf(stdout, "%s", text);
else if(sel)
fprintf(stdout, "%s", sel->text);
else if(text)
fprintf(stdout, "%s", text);
fflush(stdout);
running = False;
break;
case XK_Right:
if(!(sel && sel->right))
return;
sel=sel->right;
if(sel == next) {
curr = next;
calcoffsets();
}
break;
case XK_Tab:
if(!sel)
return;
strncpy(text, sel->text, sizeof text);
match(text);
break;
}
drawmenu();
}
static char *
readstdin(void) {
static char *maxname = NULL;
char *p, buf[1024];
unsigned int len = 0, max = 0;
Item *i, *new;
i = 0;
while(fgets(buf, sizeof buf, stdin)) {
len = strlen(buf);
if (buf[len - 1] == '\n')
buf[len - 1] = 0;
p = estrdup(buf);
if(max < len) {
maxname = p;
max = len;
}
new = emalloc(sizeof(Item));
new->next = new->left = new->right = NULL;
new->text = p;
if(!i)
allitems = new;
else
i->next = new;
i = new;
}
return maxname;
}
/* extern */
int screen;
Display *dpy;
DC dc = {0};
int
main(int argc, char *argv[]) {
char *font = FONT;
char *maxname;
char *normbg = NORMBGCOLOR;
char *normfg = NORMFGCOLOR;
char *selbg = SELBGCOLOR;
char *selfg = SELFGCOLOR;
fd_set rd;
int i;
struct timeval timeout;
Item *itm;
XEvent ev;
XSetWindowAttributes wa;
timeout.tv_usec = 0;
timeout.tv_sec = 3;
/* command line args */
for(i = 1; i < argc; i++)
if(!strncmp(argv[i], "-font", 6)) {
if(++i < argc) font = argv[i];
}
else if(!strncmp(argv[i], "-normbg", 8)) {
if(++i < argc) normbg = argv[i];
}
else if(!strncmp(argv[i], "-normfg", 8)) {
if(++i < argc) normfg = argv[i];
}
else if(!strncmp(argv[i], "-selbg", 7)) {
if(++i < argc) selbg = argv[i];
}
else if(!strncmp(argv[i], "-selfg", 7)) {
if(++i < argc) selfg = argv[i];
}
else if(!strncmp(argv[i], "-p", 3)) {
if(++i < argc) prompt = argv[i];
}
else if(!strncmp(argv[i], "-t", 3)) {
if(++i < argc) timeout.tv_sec = atoi(argv[i]);
}
else if(!strncmp(argv[i], "-v", 3)) {
fputs("dmenu-"VERSION", (C)opyright MMVI Anselm R. Garbe\n", stdout);
exit(EXIT_SUCCESS);
}
else
eprint("usage: dmenu [-font <name>] [-{norm,sel}{bg,fg} <color>] [-p <prompt>] [-t <seconds>] [-v]\n", stdout);
setlocale(LC_CTYPE, "");
dpy = XOpenDisplay(0);
if(!dpy)
eprint("dmenu: cannot open display\n");
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
/* Note, the select() construction allows to grab all keypresses as
* early as possible, to not loose them. But if there is no standard
* input supplied, we will make sure to exit after MAX_WAIT_STDIN
* seconds. This is convenience behavior for rapid typers.
*/
while(XGrabKeyboard(dpy, root, True, GrabModeAsync,
GrabModeAsync, CurrentTime) != GrabSuccess)
usleep(1000);
FD_ZERO(&rd);
FD_SET(STDIN_FILENO, &rd);
if(select(ConnectionNumber(dpy) + 1, &rd, NULL, NULL, &timeout) < 1)
goto UninitializedEnd;
maxname = readstdin();
/* style */
dc.norm[ColBG] = getcolor(normbg);
dc.norm[ColFG] = getcolor(normfg);
dc.sel[ColBG] = getcolor(selbg);
dc.sel[ColFG] = getcolor(selfg);
setfont(font);
/* menu window */
wa.override_redirect = 1;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask;
mx = my = 0;
mw = DisplayWidth(dpy, screen);
mh = dc.font.height + 2;
win = XCreateWindow(dpy, root, mx, my, mw, mh, 0,
DefaultDepth(dpy, screen), CopyFromParent,
DefaultVisual(dpy, screen),
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
/* pixmap */
dc.drawable = XCreatePixmap(dpy, root, mw, mh, DefaultDepth(dpy, screen));
dc.gc = XCreateGC(dpy, root, 0, 0);
XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
if(maxname)
cmdw = textw(maxname);
if(cmdw > mw / 3)
cmdw = mw / 3;
if(prompt)
promptw = textw(prompt);
if(promptw > mw / 5)
promptw = mw / 5;
text[0] = 0;
match(text);
XMapRaised(dpy, win);
drawmenu();
XSync(dpy, False);
/* main event loop */
while(running && !XNextEvent(dpy, &ev))
switch (ev.type) {
default: /* ignore all crap */
break;
case KeyPress:
kpress(&ev.xkey);
break;
case Expose:
if(ev.xexpose.count == 0)
drawmenu();
break;
}
/* cleanup */
while(allitems) {
itm = allitems->next;
free(allitems->text);
free(allitems);
allitems = itm;
}
if(dc.font.set)
XFreeFontSet(dpy, dc.font.set);
else
XFreeFont(dpy, dc.font.xfont);
XFreePixmap(dpy, dc.drawable);
XFreeGC(dpy, dc.gc);
XDestroyWindow(dpy, win);
UninitializedEnd:
XUngrabKeyboard(dpy, CurrentTime);
XCloseDisplay(dpy);
return ret;
}

38
util.c
View File

@ -1,38 +0,0 @@
/* (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details.
*/
#include "dmenu.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
void *
emalloc(unsigned int size) {
void *res = malloc(size);
if(!res)
eprint("fatal: could not malloc() %u bytes\n", size);
return res;
}
void
eprint(const char *errstr, ...) {
va_list ap;
va_start(ap, errstr);
vfprintf(stderr, errstr, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
char *
estrdup(const char *str) {
void *res = strdup(str);
if(!res)
eprint("fatal: could not malloc() %u bytes\n", strlen(str));
return res;
}