Compare commits

..

144 Commits
4.2 ... 4.6

Author SHA1 Message Date
32f2564dbb arg.h: remove unused ARGNUM* macros 2015-11-08 23:37:47 +01:00
cc596365ac unboolify dmenu 2015-11-08 23:03:34 +01:00
c9e4e152e6 Makefile: package config.def.h on make dist 2015-11-08 16:46:34 +01:00
cc99007809 remove .hgtags file 2015-11-08 16:44:41 +01:00
34f549adb2 dmenu.1: clarify monitor numbers are starting from 0 (first mon) 2015-11-07 12:53:00 +01:00
da0b9eb6c9 fix: multimon: always spawn client on first monitor if specified with -m 0
This was always broken.

Reproduce:
	focus client on second monitor, spawn dmenu with -m 0.

Result:
	Old wrong behaviour: dmenu spawns on second monitor (focused client).
	Now: dmenu spawns on specified monitor (first).
2015-11-07 12:43:00 +01:00
44b242c763 drw: cleanup drw_text, prevent gcc warning false-positive of unused var
... we don't allow passing text is NULL anymore either, for that behaviour
just use drw_rect() (it is used in dwm).
2015-10-20 22:56:57 +02:00
e2e7fcb219 drw: simplify drw_font_xcreate and prevent a potential unneeded allocation 2015-10-20 22:55:39 +02:00
1f2226df13 drw: a valid (non-NULL) Drw and Fnt context must be passed
don't do these checks on this level. However for resource drw_*_free
we will allow it.
2015-10-20 22:53:55 +02:00
5a20b409c6 add sbase-style ecalloc(), calloc: or die
... remove intermediary variables
2015-10-20 22:51:57 +02:00
164986763a free schemes as array
this makes it slightly easier to add custom schemes aswell
2015-10-18 18:37:20 +02:00
09d0a36e03 fix input text matching
just compare the size of the input string
2015-10-04 15:04:59 +02:00
acbf35a5e3 fix incorrect ordering of match results
look for exact matches comparing the user input against the item text
2015-10-04 14:03:07 +02:00
240a7810e4 fix paste from clipboard (ctrl+shift+y)
from Joshua Lloret on the ML:

"attached is a small patch to enable pasting from clipboard, as well as primary.

It seems like there was already code in there to allow this, but since there was never any case to match the upper case 'Y', that inline if would always evaluate to false."
2015-10-04 12:32:07 +02:00
d5ce8df9d9 minor style fix 2015-09-28 00:38:17 +02:00
ba75bb30fa Remove function prototypes and reorder functions accordingly 2015-09-28 00:33:29 +02:00
10fd4f275f Untypedef struct item
Adds clarity. Typedefs for structs are definitely a discussion matter,
but there's no reason to hide a simple data-structure behind a
meaningless typedef.
2015-09-28 00:33:26 +02:00
b048eacc9d Fix the conversion from microseconds to nanoseconds 2015-09-28 00:27:09 +02:00
c42c378027 Replace deprecated usleep() with nanosleep() 2015-09-28 00:15:13 +02:00
c15a95a061 config.mk: use -std=c99 2015-09-28 00:02:28 +02:00
cf0fb79cd8 dmenu: style improvements
- move main to bottom, usage above main.
- dont use variable names with function prototypes.
- space before if, for, while, etc: 'if(' -> 'if ('.

this makes the code-style more consistent
2015-09-27 23:57:39 +02:00
03cb1ec55a drw style improvements
this makes the code-style more consistent aswell.
2015-09-27 23:56:02 +02:00
bc20c13db0 separate program-specific c99 bool and X11
True, False are X11-specific, make sure to use c99 stdbool for
program-specific things.

... also remove left-over vim mode string in config.
2015-09-27 23:02:33 +02:00
96e60c66bc config.mk: fix _XOPEN_SOURCE=700 for getline() 2015-09-27 22:55:21 +02:00
2d2175ff6f config.mk: improve feature test check
this fixes a crash on NetBSD because it requires -D_XOPEN_SOURCE (strdup, usleep).

thanks k0ga and stateless for reporting and fixing this issue!
2015-09-27 22:38:14 +02:00
bbf7b95dc9 Fixed typo introduced by shared code 2015-08-06 12:47:51 +02:00
d6742ef8a6 stest: get rid of getopt, use suckless arg.h
... also some style improvements.
2015-07-19 21:38:42 +02:00
5feb0c689d config.mk: add FREETYPELIBS and FREETYPEINC
These variables make it simpler to change the paths to this for ports.
`pkg-config` is avoided because it sucks, in particular for cross-compilation.

A commented path for *BSD is added, the Xft includes are located at:
/usr/X11R6/include there.

Also already bump the version number to 4.6, a release will come
approximately in August.
2015-07-19 20:32:08 +02:00
1c242df124 fix prompt color style how it used to be
This is the style how it was before the big Xft change. The colors were
inverted, this was not the case before the change.

Reported by "zvz" on #suckless IRC, thanks!
2015-07-19 20:29:26 +02:00
4b1fecd44e Use libdraw: add Xft and fallback-fonts support to graphics lib
- libdraw, util: add drw.{c,h}, util.{c,h} and update code.
- libdraw: fix drw_rect(): use w and h parameter.
- libdraw: print errstr if last character in string was ":" (sbase).
- libdraw: drw_clr_free() allow valid free(NULL).
- config.def.h: set default font to monospace.
- cleanup() on exit.
- LICENSE: update license string for dmenu -v to 2015.
- LICENSE: add myself to LICENSE
2015-06-27 21:47:10 +02:00
13a529ce63 applied Hiltjo's patch as suggested on the ml to fix ControlMask for C-j and C-m 2014-09-17 13:40:11 +02:00
aa69426670 fix crash with ctrl-enter as input
reproduce: ./dmenu; send EOF; press ctrl+enter.
2014-07-30 20:07:47 +01:00
4c50e43df4 updated copyright notices in LICENSE and dmenu.c file 2014-05-29 18:03:53 +02:00
5ed5e90bfb applied Martin Kühl's inverse matching flag to stest 2013-08-13 19:15:04 +02:00
13f787306f applied Martti Kühne's dmenu monitor patch
f6581ca966/monarg.patch becomes upstream now
2013-08-02 22:30:20 +02:00
597d4b4337 accepted vi'is exit approach ^[ (suggested by Arkaduisz) 2013-06-28 22:06:02 +02:00
55c9be06ca forgot to add config.def.h, thanks William 2013-04-17 21:16:51 +02:00
14f72a2763 adopted Alex Sedov's config.h revival patch to tip 2013-04-17 21:04:05 +02:00
ec64f273fb applied Alex Sedov's Tab buffer termination patch, thanks 2013-04-17 20:59:12 +02:00
0d12a47415 applied multisel patch to mainline 2013-04-17 20:56:54 +02:00
dec9a28863 dmenu_run: Split cache logic to dmenu_path again
This allows to run dmenu_path to update the cache using
a packager manager hook system
2012-07-30 17:02:12 +02:00
1299e41447 ignore prompt if it is empty in addition to NULL 2012-05-15 11:47:54 +02:00
38fccafada _POSIX_C_SOURCE=200809L 2012-04-06 16:38:01 +01:00
1f11faf7b5 listen for C-S-[jm] 2012-02-10 00:37:42 +00:00
50145384c8 add ^G escape keybinding 2012-01-19 22:52:17 +00:00
7707111c10 remove _POSIX_C_SOURCE cflag 2012-01-18 23:56:13 +00:00
7f45b3f7aa update version 4.5-hg 2012-01-08 13:13:00 +01:00
0f91dd1c74 config.mk cleanup 2012-01-04 13:30:47 +01:00
3402a8d53a align M-[hjkl] closer to vi keys 2012-01-02 19:48:11 +01:00
d3e9bd152e happy new year! 2012-01-01 20:33:56 +01:00
b95fb20269 add Mod1Mask keybindings 2012-01-01 20:32:40 +01:00
6664e4233f merge stest -> default 2011-12-19 16:52:48 +01:00
26c78cd952 comment xdg fallback behaviour 2011-12-19 16:05:55 +01:00
6b1d658d12 stest: restore stream testing 2011-12-09 11:49:44 +01:00
56a0d1fa14 fork dmenu_run to disown child shell 2011-12-09 11:36:26 +01:00
32ef0f5662 stest: cleanup 2011-11-27 23:40:48 +01:00
dcd6e771a1 stest: remove unneeded lstat 2011-11-27 23:37:01 +01:00
8cc28cb426 simplify stest 2011-11-27 23:35:09 +01:00
942199ccd0 dmenu_run: use $SHELL 2011-11-23 15:17:32 +01:00
3f9b10c86a set window background_pixel 2011-11-23 14:40:21 +01:00
5162d1b37a fix dmenu_path exec sh 2011-11-19 21:31:18 +01:00
a02a1a6623 faster dmenu_run -f 2011-11-19 21:24:07 +01:00
bb4424df07 replace lsx with stest 2011-11-19 19:54:55 +01:00
8ac44eb75a update manpage 2011-11-16 11:26:15 +01:00
ebd94a6c00 limit direction keys in vline 2011-11-15 20:32:39 +01:00
2b31952731 fix C-n / C-p directions (thanks bastien) 2011-11-14 20:02:16 +01:00
c01f36a1f3 new default colour scheme 2011-11-14 00:46:56 +01:00
168221b2f8 makefile: *.o depend on draw.h 2011-10-26 14:16:12 +01:00
dc77337ef3 add paste from clipboard 2011-10-26 13:28:15 +01:00
7b1493a631 add lots of comments 2011-10-26 13:20:14 +01:00
dd29c5d480 input focus: calculate areas of intersection 2011-10-26 12:14:50 +01:00
70b3418e3e simplify dmenu_run 2011-10-17 10:22:23 +01:00
cf641ae496 merge xim -> default 2011-10-17 10:11:48 +01:00
5e0156c072 merge lsx -> default 2011-10-17 10:08:04 +01:00
ca7ef6d2c9 use ~/.dmenu_cache if no xdg cache 2011-10-17 02:12:33 +01:00
7bbd4c56eb lsx: detect read errors 2011-10-17 01:44:07 +01:00
24565608cb xim: check for corner cases 2011-10-17 01:18:57 +01:00
05026fb5ce fix extra warning 2011-10-16 18:26:11 +01:00
c71abdc65c lsx: return failure on error 2011-10-16 18:14:51 +01:00
4126b1e323 merge default -> lsx 2011-10-16 18:13:29 +01:00
c8f0a37638 add xim support 2011-10-16 17:21:33 +01:00
1cf3864464 limit lines to input 2011-10-13 20:43:59 +01:00
8e302717cb input focus: fix for reparenting wms 2011-10-04 21:39:56 +01:00
e30ad490b2 fix monitor select when no focus 2011-09-30 21:08:37 +01:00
5020734408 merge token -> default 2011-09-20 00:11:01 +01:00
4c8d43ab73 tweak match 2011-09-20 00:09:20 +01:00
0582480801 update manpage 2011-09-20 00:06:13 +01:00
f1848a3ef0 detect active monitor using input focus 2011-09-19 18:15:03 +01:00
b26d3f54ee add token matching 2011-09-19 10:40:56 +01:00
3af80cd7d8 change version to 'hg' 2011-09-19 10:40:07 +01:00
4692f84ff0 dmenu_run: sh -c 2011-07-24 20:04:58 +01:00
c805dcbeaf fix loadfont 2011-07-22 18:16:57 +01:00
a6cbc6c9a7 Added tag 4.4 for changeset 2b105eaae831 2011-07-19 21:30:14 +01:00
bae58f61cf add lsx.1 to dist 2011-07-19 21:30:09 +01:00
1636e12210 tweaks 2011-07-17 14:06:53 +01:00
2b92c95230 efficiency tweaks 2011-07-14 20:03:08 +01:00
16a0c0d52a fix extra warning 2011-07-14 20:03:00 +01:00
5867c53390 simpler vline 2011-07-06 13:40:36 +01:00
34a816f87d rebind ^N ^P 2011-07-04 16:55:09 +01:00
0288b576ca include limits.h 2011-06-25 17:02:14 +01:00
59dafc4ad6 make clean lsx 2011-06-23 22:39:20 +01:00
49672e9d05 merge lsx -> default 2011-06-23 20:27:28 +01:00
07d452e0c8 close lsx branch 2011-06-23 20:26:57 +01:00
88d44169e9 lsx: check snprintf 2011-06-23 20:04:50 +01:00
f7cbe710a2 update draw.c 2011-06-18 22:11:19 +01:00
86e9e6f1ec simpler lsx 2011-06-18 07:50:46 +01:00
eadf090413 new dmenu_run 2011-06-13 21:50:31 +01:00
8ec1b41cac add lsx.1 2011-06-13 19:32:45 +01:00
ab3bcac0bf new lsx branch 2011-06-13 19:28:30 +01:00
723cbabc12 add ^M 2011-06-13 19:25:40 +01:00
86775cc44b Added tag 4.3.1 for changeset 34a2d77049a9 2011-05-18 17:01:59 +01:00
2103ae2891 bugfix 2011-05-18 17:01:44 +01:00
b028ec0e07 Added tag 4.3 for changeset 14c79f054bdf 2011-05-18 16:21:45 +01:00
bb3e33def3 no -m, cleanup 2011-05-18 16:20:03 +01:00
3a60b19514 fix possible overflow 2011-05-16 23:35:14 +01:00
dd2f298252 fixed extra warnings 2011-05-16 12:59:31 +01:00
11ea52d170 portability 2011-05-15 21:54:26 +01:00
4354074607 efficient incremental search 2011-05-15 16:05:32 +01:00
4776d6dc61 match first 2011-05-15 14:21:00 +01:00
d0051e7bb8 faster grab 2011-05-15 14:13:31 +01:00
fb67bd666e cleanup 2011-05-15 13:58:54 +01:00
f5bd1eed8d fast but inexact 2011-05-15 13:02:33 +01:00
1654d6cd62 cleanup 2011-05-15 02:37:49 +01:00
6cc0b0dc08 increment version 2011-05-14 23:26:41 +01:00
b2bb6ebec9 only match links 2011-05-14 23:21:38 +01:00
a662fc6e0a follow symlinks 2011-05-14 23:14:31 +01:00
d066975e78 cleanup 2011-05-14 22:43:42 +01:00
26236a4900 whoops, wrong dmenu_path version 2011-05-14 22:12:28 +01:00
15505bd711 fast dmenu_path script 2011-05-14 20:43:11 +01:00
86468aafe5 instant ^E 2011-05-14 18:39:27 +01:00
be9afce035 fix typo 2011-05-14 17:47:12 +01:00
3c067598fc use array for items 2011-05-14 17:46:20 +01:00
86019c5855 fixed xinerama corner case 2011-05-12 13:17:41 +01:00
0291c722fb fixed -m bug (thanks Rob) 2011-05-11 12:25:50 +01:00
e0e6b071c6 fast grab patch (thanks Rob) 2011-05-08 15:15:24 +01:00
cd3b4915c3 helpful errors 2011-05-06 21:13:02 +01:00
210b303941 paring 2011-05-05 15:46:48 +01:00
47e3e8be7b update license 2011-01-07 18:55:00 +00:00
d515d2758f cache option in config.mk 2011-01-07 18:54:40 +00:00
a8ea79715f gmake spews 2010-12-01 20:25:10 +00:00
3e0a141016 posix makefile 2010-12-01 20:23:16 +00:00
cbd8257914 Added tag 4.2.1 for changeset abb6579a324f 2010-11-20 09:25:08 +00:00
22cf9626c7 fixed dist target bug 2010-11-20 09:25:04 +00:00
9e13ecabe8 Added tag 4.2 for changeset 379813a051f0 2010-11-19 11:51:01 +00:00
19 changed files with 1440 additions and 789 deletions

44
.hgtags
View File

@ -1,44 +0,0 @@
fcc8a282cb52c6a9343b461026b386825590cd31 0.1
656be0f47df545dfdd2e1e0663663b8b1b26f031 0.2
d352e9dc112ee96aa5cad961a0ed880ae9ce7276 0.3
7acf0dde1120542917bae12e0e42293f9d2cc899 0.4
4a0ecd881c4fc15de4a0bebd79308b064be020ef 0.5
25f679fb19686140a907684ffcb423b9e9d44b53 0.6
5fc20d7158bd16b4d5f8d1c25e177680b6d54252 0.7
409667a57221f7e50ba8b5248f638915cd61b366 0.8
d046c818ea467555cc338751c9bf3024609f1f12 0.9
9e11140d4cc3eecac3b0ab752f91528fd5e04be8 1.0
e8c1e9733752db12f2dbd1fa93c46f5806242ba9 1.1
bee7fe6d1189174d0204ca3195b83cdc1bb4f82e 1.2
2eb9997be51cb1b11a8900728ccc0904f9371157 1.3
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
0508a3a6ee106f36d9b8ff07bb5b28584edfa89c 3.7
644b0798fcccd570fd519899e1601c6857496b91 3.8
21a1ed9a69b9541a355758a57103e294fb722c33 3.9
78f9f72cc9c6bdb022ff8908486b61ef5e242aad 4.0
844587572673cf6326c3f61737264a46b728fc0a 4.1
72749a826cab0baa805620e44a22e54486c97a4e 4.1.1

View File

@ -1,12 +1,13 @@
MIT/X Consortium License
© 2010 Connor Lane Smith <cls@lubutu.com>
© 2006-2010 Anselm R Garbe <anselm@garbe.us>
© 2006-2014 Anselm R Garbe <anselm@garbe.us>
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
© 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>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),

View File

@ -3,7 +3,10 @@
include config.mk
all: options dmenu dmenu_path
SRC = drw.c dmenu.c stest.c util.c
OBJ = ${SRC:.c=.o}
all: options dmenu stest
options:
@echo dmenu build options:
@ -11,25 +14,34 @@ options:
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
dmenu: dmenu.o draw.o
dmenu_path: dmenu_path.o
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
.c.o: config.mk
@echo CC -c $<
@${CC} -c $< ${CFLAGS}
config.h:
@echo creating $@ from config.def.h
@cp config.def.h $@
dmenu dmenu_path:
${OBJ}: arg.h config.h config.mk drw.h
dmenu: dmenu.o drw.o util.o
@echo CC -o $@
@${CC} -o $@ $+ ${LDFLAGS}
@${CC} -o $@ dmenu.o drw.o util.o ${LDFLAGS}
stest: stest.o
@echo CC -o $@
@${CC} -o $@ stest.o ${LDFLAGS}
clean:
@echo cleaning
@rm -f dmenu dmenu.o draw.o dmenu_path dmenu_path.o 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 config.mk dmenu.1 dmenu.c dmenu_path.c dmenu_run 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}
@ -37,21 +49,26 @@ dist: clean
install: all
@echo installing executables to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f dmenu dmenu_path dmenu_run ${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
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@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
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
.PHONY: all options clean dist install uninstall

49
arg.h Normal file
View File

@ -0,0 +1,49 @@
/*
* Copy me if you can.
* by 20h
*/
#ifndef ARG_H__
#define ARG_H__
extern char *argv0;
/* use main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
argv[0] && argv[0][1]\
&& argv[0][0] == '-';\
argc--, argv++) {\
char argc_;\
char **argv_;\
int brk_;\
if (argv[0][1] == '-' && argv[0][2] == '\0') {\
argv++;\
argc--;\
break;\
}\
for (brk_ = 0, argv[0]++, argv_ = argv;\
argv[0][0] && !brk_;\
argv[0]++) {\
if (argv_ != argv)\
break;\
argc_ = argv[0][0];\
switch (argc_)
#define ARGEND }\
}
#define ARGC() argc_
#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
((x), abort(), (char *)0) :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
(char *)0 :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#endif

17
config.def.h Normal file
View File

@ -0,0 +1,17 @@
/* 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[] = {
"monospace:size=10"
};
static const char *prompt = NULL; /* -p option; prompt to the elft of input field */
static const char *normbgcolor = "#222222"; /* -nb option; normal background */
static const char *normfgcolor = "#bbbbbb"; /* -nf option; normal foreground */
static const char *selbgcolor = "#005577"; /* -sb option; selected background */
static const char *selfgcolor = "#eeeeee"; /* -sf option; selected foreground */
static const char *outbgcolor = "#00ffff";
static const char *outfgcolor = "#000000";
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
static unsigned int lines = 0;

View File

@ -1,7 +1,5 @@
# dmenu version
VERSION = 4.2
# Customize below to fit your system
VERSION = 4.6
# paths
PREFIX = /usr/local
@ -14,12 +12,18 @@ X11LIB = /usr/X11R6/lib
XINERAMALIBS = -lXinerama
XINERAMAFLAGS = -DXINERAMA
# freetype
FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment)
#FREETYPEINC = ${X11INC}/freetype2
# includes and libs
INCS = -I${X11INC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS}
INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
# flags
CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
CPPFLAGS = -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}

140
dmenu.1
View File

@ -4,11 +4,12 @@ dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-b ]
.RB [ \-f ]
.RB [ \-i ]
.RB [ \-l
.IR lines ]
.RB [ \-m
.IR monitor ]
.IR lines ]
.RB [ \-p
.IR prompt ]
.RB [ \-fn
@ -24,29 +25,26 @@ dmenu \- dynamic menu
.RB [ \-v ]
.P
.BR dmenu_run " ..."
.P
.B dmenu_path
.SH DESCRIPTION
.B dmenu
is a dynamic menu for X, originally designed for
.BR dwm (1).
It manages huge numbers of user-defined menu items efficiently.
.P
dmenu reads a list of newline-separated items from standard input and creates a
menu. When the user selects an item or enters any text and presses Return,
their choice is printed to standard output and dmenu terminates.
is a dynamic menu for X, which reads a list of newline\-separated items from
stdin. When the user selects an item and presses Return, their choice is printed
to stdout and dmenu terminates. Entering text will narrow the items to those
matching the tokens in the input.
.P
.B dmenu_run
is a dmenu script used by dwm which lists programs in the user's PATH and
executes the selected item.
.P
.B dmenu_path
is a program used by dmenu_run to find and cache a list of executables.
is a script used by
.IR dwm (1)
which lists programs in the user's $PATH and runs the result in their $SHELL.
.SH OPTIONS
.TP
.B \-b
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.
.TP
.B \-i
dmenu matches menu items case insensitively.
.TP
@ -54,7 +52,8 @@ dmenu matches menu items case insensitively.
dmenu lists items vertically, with the given number of lines.
.TP
.BI \-m " monitor"
dmenu appears on the given Xinerama screen.
dmenu is displayed on the monitor number supplied. Monitor numbers are starting
from 0.
.TP
.BI \-p " prompt"
defines the prompt to be displayed to the left of the input field.
@ -66,7 +65,7 @@ defines the font or font set used.
defines the normal background color.
.IR #RGB ,
.IR #RRGGBB ,
and color names are supported.
and X color names are supported.
.TP
.BI \-nf " color"
defines the normal foreground color.
@ -78,27 +77,104 @@ defines the selected background color.
defines the selected foreground color.
.TP
.B \-v
prints version information to standard output, then exits.
prints version information to stdout, then exits.
.SH USAGE
dmenu is completely controlled by the keyboard. Besides standard Unix line
editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the
following keys are recognized:
dmenu is completely controlled by the keyboard. Items are selected using the
arrow keys, page up, page down, home, and end.
.TP
.B Tab (Control\-i)
.B Tab
Copy the selected item to the input field.
.TP
.B Return (Control\-j)
Confirm selection. Prints the selected item to standard output and exits,
returning success.
.TP
.B Shift\-Return (Control\-Shift\-j)
Confirm input. Prints the input text to standard output and exits, returning
.B Return
Confirm selection. Prints the selected item to stdout and exits, returning
success.
.TP
.B Escape (Control\-c)
.B Ctrl-Return
Confirm selection. Prints the selected item to stdout and continues.
.TP
.B Shift\-Return
Confirm input. Prints the input text to stdout and exits, returning success.
.TP
.B Escape
Exit without selecting an item, returning failure.
.TP
.B Control\-y
Paste the current X selection into the input field.
C\-a
Home
.TP
C\-b
Left
.TP
C\-c
Escape
.TP
C\-d
Delete
.TP
C\-e
End
.TP
C\-f
Right
.TP
C\-g
Escape
.TP
C\-h
Backspace
.TP
C\-i
Tab
.TP
C\-j
Return
.TP
C\-J
Shift-Return
.TP
C\-k
Delete line right
.TP
C\-m
Return
.TP
C\-M
Shift-Return
.TP
C\-n
Down
.TP
C\-p
Up
.TP
C\-u
Delete line left
.TP
C\-w
Delete word left
.TP
C\-y
Paste from primary X selection
.TP
C\-Y
Paste from X clipboard
.TP
M\-g
Home
.TP
M\-G
End
.TP
M\-h
Up
.TP
M\-j
Page down
.TP
M\-k
Page up
.TP
M\-l
Down
.SH SEE ALSO
.BR dwm (1)
.IR dwm (1),
.IR stest (1)

858
dmenu.c

File diff suppressed because it is too large Load Diff

13
dmenu_path Normal file
View File

@ -0,0 +1,13 @@
#!/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
IFS=:
if stest -dqr -n "$cache" $PATH; then
stest -flx $PATH | sort -u | tee "$cache"
else
cat "$cache"
fi

View File

@ -1,101 +0,0 @@
/* See LICENSE file for copyright and license details. */
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#define CACHE ".dmenu_cache"
static void die(const char *s);
static int qstrcmp(const void *a, const void *b);
static void scan(void);
static int uptodate(void);
static char **items = NULL;
static const char *home, *path;
int
main(void) {
if(!(home = getenv("HOME")))
die("no $HOME");
if(!(path = getenv("PATH")))
die("no $PATH");
if(chdir(home) < 0)
die("chdir failed");
if(uptodate()) {
execlp("cat", "cat", CACHE, NULL);
die("exec failed");
}
scan();
return EXIT_SUCCESS;
}
void
die(const char *s) {
fprintf(stderr, "dmenu_path: %s\n", s);
exit(EXIT_FAILURE);
}
int
qstrcmp(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
void
scan(void) {
char buf[PATH_MAX];
char *dir, *p;
size_t i, count;
struct dirent *ent;
DIR *dp;
FILE *cache;
count = 0;
if(!(p = strdup(path)))
die("strdup failed");
for(dir = strtok(p, ":"); dir; dir = strtok(NULL, ":")) {
if(!(dp = opendir(dir)))
continue;
while((ent = readdir(dp))) {
snprintf(buf, sizeof buf, "%s/%s", dir, ent->d_name);
if(ent->d_name[0] == '.' || access(buf, X_OK) < 0)
continue;
if(!(items = realloc(items, ++count * sizeof *items)))
die("malloc failed");
if(!(items[count-1] = strdup(ent->d_name)))
die("strdup failed");
}
closedir(dp);
}
qsort(items, count, sizeof *items, qstrcmp);
if(!(cache = fopen(CACHE, "w")))
die("open failed");
for(i = 0; i < count; i++) {
if(i > 0 && !strcmp(items[i], items[i-1]))
continue;
fprintf(cache, "%s\n", items[i]);
fprintf(stdout, "%s\n", items[i]);
}
fclose(cache);
free(p);
}
int
uptodate(void) {
char *dir, *p;
time_t mtime;
struct stat st;
if(stat(CACHE, &st) < 0)
return 0;
mtime = st.st_mtime;
if(!(p = strdup(path)))
die("strdup failed");
for(dir = strtok(p, ":"); dir; dir = strtok(NULL, ":"))
if(!stat(dir, &st) && st.st_mtime > mtime)
return 0;
free(p);
return 1;
}

View File

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

194
draw.c
View File

@ -1,194 +0,0 @@
/* See LICENSE file for copyright and license details. */
#include <locale.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include "draw.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define DEFFONT "fixed"
static Bool loadfont(DC *dc, const char *fontstr);
void
drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) {
XRectangle r = { dc->x + x, dc->y + y, w, h };
if(!fill) {
r.width -= 1;
r.height -= 1;
}
XSetForeground(dc->dpy, dc->gc, color);
(fill ? XFillRectangles : XDrawRectangles)(dc->dpy, dc->canvas, dc->gc, &r, 1);
}
void
drawtext(DC *dc, const char *text, unsigned long col[ColLast]) {
char buf[256];
size_t n, mn;
/* shorten text if necessary */
n = strlen(text);
for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) > dc->w - dc->font.height/2; mn--)
if(mn == 0)
return;
memcpy(buf, text, mn);
if(mn < n)
for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.');
drawrect(dc, 0, 0, dc->w, dc->h, True, BG(dc, col));
drawtextn(dc, buf, mn, col);
}
void
drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) {
int x, y;
x = dc->x + dc->font.height/2;
y = dc->y + dc->font.ascent+1;
XSetForeground(dc->dpy, dc->gc, FG(dc, col));
if(dc->font.set)
XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n);
else {
XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid);
XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n);
}
}
void
eprintf(const char *fmt, ...) {
va_list ap;
fprintf(stderr, "%s: ", progname);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
void
freedc(DC *dc) {
if(dc->font.set)
XFreeFontSet(dc->dpy, dc->font.set);
if(dc->font.xfont)
XFreeFont(dc->dpy, dc->font.xfont);
if(dc->canvas)
XFreePixmap(dc->dpy, dc->canvas);
XFreeGC(dc->dpy, dc->gc);
XCloseDisplay(dc->dpy);
free(dc);
}
unsigned long
getcolor(DC *dc, const char *colstr) {
Colormap cmap = DefaultColormap(dc->dpy, DefaultScreen(dc->dpy));
XColor color;
if(!XAllocNamedColor(dc->dpy, cmap, colstr, &color, &color))
eprintf("cannot allocate color '%s'\n", colstr);
return color.pixel;
}
DC *
initdc(void) {
DC *dc;
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
weprintf("no locale support\n");
if(!(dc = malloc(sizeof *dc)))
eprintf("cannot malloc %u bytes\n", sizeof *dc);
if(!(dc->dpy = XOpenDisplay(NULL)))
eprintf("cannot open display\n");
dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL);
XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter);
dc->font.xfont = NULL;
dc->font.set = NULL;
dc->canvas = None;
return dc;
}
void
initfont(DC *dc, const char *fontstr) {
if(!loadfont(dc, fontstr ? fontstr : DEFFONT)) {
if(fontstr != NULL)
weprintf("cannot load font '%s'\n", fontstr);
if(fontstr == NULL || !loadfont(dc, DEFFONT))
eprintf("cannot load font '%s'\n", DEFFONT);
}
dc->font.height = dc->font.ascent + dc->font.descent;
}
Bool
loadfont(DC *dc, const char *fontstr) {
char *def, **missing;
int i, n;
if(!*fontstr)
return False;
if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) {
char **names;
XFontStruct **xfonts;
n = XFontsOfFontSet(dc->font.set, &xfonts, &names);
for(i = dc->font.ascent = dc->font.descent = 0; i < n; i++) {
dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent);
dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent);
}
}
else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) {
dc->font.ascent = dc->font.xfont->ascent;
dc->font.descent = dc->font.xfont->descent;
}
if(missing)
XFreeStringList(missing);
return (dc->font.set || dc->font.xfont);
}
void
mapdc(DC *dc, Window win, unsigned int w, unsigned int h) {
XCopyArea(dc->dpy, dc->canvas, win, dc->gc, 0, 0, w, h, 0, 0);
}
void
resizedc(DC *dc, unsigned int w, unsigned int h) {
if(dc->canvas)
XFreePixmap(dc->dpy, dc->canvas);
dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h,
DefaultDepth(dc->dpy, DefaultScreen(dc->dpy)));
dc->x = dc->y = 0;
dc->w = w;
dc->h = h;
dc->invert = False;
}
int
textnw(DC *dc, const char *text, size_t len) {
if(dc->font.set) {
XRectangle r;
XmbTextExtents(dc->font.set, text, len, NULL, &r);
return r.width;
}
return XTextWidth(dc->font.xfont, text, len);
}
int
textw(DC *dc, const char *text) {
return textnw(dc, text, strlen(text)) + dc->font.height;
}
void
weprintf(const char *fmt, ...) {
va_list ap;
fprintf(stderr, "%s: warning: ", progname);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}

37
draw.h
View File

@ -1,37 +0,0 @@
/* See LICENSE file for copyright and license details. */
#define FG(dc, col) ((col)[(dc)->invert ? ColBG : ColFG])
#define BG(dc, col) ((col)[(dc)->invert ? ColFG : ColBG])
enum { ColBG, ColFG, ColBorder, ColLast };
typedef struct {
int x, y, w, h;
Bool invert;
Display *dpy;
GC gc;
Pixmap canvas;
struct {
int ascent;
int descent;
int height;
XFontSet set;
XFontStruct *xfont;
} font;
} DC; /* draw context */
unsigned long getcolor(DC *dc, const char *colstr);
void drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color);
void drawtext(DC *dc, const char *text, unsigned long col[ColLast]);
void drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]);
void initfont(DC *dc, const char *fontstr);
void freedc(DC *dc);
DC *initdc(void);
void mapdc(DC *dc, Window win, unsigned int w, unsigned int h);
void resizedc(DC *dc, unsigned int w, unsigned int h);
int textnw(DC *dc, const char *text, size_t len);
int textw(DC *dc, const char *text);
void eprintf(const char *fmt, ...);
void weprintf(const char *fmt, ...);
const char *progname;

398
drw.c Normal file
View File

@ -0,0 +1,398 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#include "drw.h"
#include "util.h"
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static long
utf8decodebyte(const char c, size_t *i)
{
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
return (unsigned char)c & ~utfmask[*i];
return 0;
}
static size_t
utf8validate(long *u, size_t i)
{
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
return i;
}
static size_t
utf8decode(const char *c, long *u, size_t clen)
{
size_t i, j, len, type;
long udecoded;
*u = UTF_INVALID;
if (!clen)
return 0;
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
if (type)
return j;
}
if (j < len)
return 0;
*u = udecoded;
utf8validate(u, len);
return len;
}
Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
{
Drw *drw;
drw = ecalloc(1, sizeof(Drw));
drw->dpy = dpy;
drw->screen = screen;
drw->root = root;
drw->w = w;
drw->h = h;
drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
drw->gc = XCreateGC(dpy, root, 0, NULL);
drw->fontcount = 0;
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw;
}
void
drw_resize(Drw *drw, unsigned int w, unsigned int h)
{
drw->w = w;
drw->h = h;
if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
}
void
drw_free(Drw *drw)
{
size_t i;
for (i = 0; i < drw->fontcount; i++)
drw_font_free(drw->fonts[i]);
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
free(drw);
}
/* This function is an implementation detail. Library users should use
* drw_font_create instead.
*/
static Fnt *
drw_font_xcreate(Drw *drw, const char *fontname, FcPattern *fontpattern)
{
Fnt *font;
XftFont *xfont = NULL;
FcPattern *pattern = NULL;
if (fontname) {
/* Using the pattern found at font->xfont->pattern does not yield same
* the same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in
* missing-character-rectangles being drawn, at least with some fonts.
*/
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
return NULL;
}
if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
fprintf(stderr, "error, cannot load font: '%s'\n", fontname);
XftFontClose(drw->dpy, xfont);
return NULL;
}
} else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font pattern.\n");
return NULL;
}
} else {
die("no font specified.\n");
}
font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont;
font->pattern = pattern;
font->ascent = xfont->ascent;
font->descent = xfont->descent;
font->h = font->ascent + font->descent;
font->dpy = drw->dpy;
return font;
}
Fnt*
drw_font_create(Drw *drw, const char *fontname)
{
return drw_font_xcreate(drw, fontname, NULL);
}
void
drw_load_fonts(Drw* drw, const char *fonts[], size_t fontcount)
{
size_t i;
Fnt *font;
for (i = 0; i < fontcount; i++) {
if (drw->fontcount >= DRW_FONT_CACHE_SIZE) {
die("Font cache exhausted.\n");
} else if ((font = drw_font_xcreate(drw, fonts[i], NULL))) {
drw->fonts[drw->fontcount++] = font;
}
}
}
void
drw_font_free(Fnt *font)
{
if (!font)
return;
if (font->pattern)
FcPatternDestroy(font->pattern);
XftFontClose(font->dpy, font->xfont);
free(font);
}
Clr *
drw_clr_create(Drw *drw, const char *clrname)
{
Clr *clr;
clr = ecalloc(1, sizeof(Clr));
if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen),
clrname, &clr->rgb))
die("error, cannot allocate color '%s'\n", clrname);
clr->pix = clr->rgb.pixel;
return clr;
}
void
drw_clr_free(Clr *clr)
{
free(clr);
}
void
drw_setscheme(Drw *drw, ClrScheme *scheme)
{
drw->scheme = scheme;
}
void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int empty, int invert)
{
if (!drw->scheme)
return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->bg->pix : drw->scheme->fg->pix);
if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w + 1, h + 1);
else if (empty)
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
}
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert)
{
char buf[1024];
int tx, ty, th;
Extnts tex;
XftDraw *d = NULL;
Fnt *curfont, *nextfont;
size_t i, len;
int utf8strlen, utf8charlen, render;
long utf8codepoint = 0;
const char *utf8str;
FcCharSet *fccharset;
FcPattern *fcpattern;
FcPattern *match;
XftResult result;
int charexists = 0;
if (!drw->scheme || !drw->fontcount)
return 0;
if (!(render = x || y || w || h)) {
w = ~w;
} else {
XSetForeground(drw->dpy, drw->gc, invert ?
drw->scheme->fg->pix : drw->scheme->bg->pix);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
}
curfont = drw->fonts[0];
while (1) {
utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
for (i = 0; i < drw->fontcount; i++) {
charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint);
if (charexists) {
if (drw->fonts[i] == curfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
} else {
nextfont = drw->fonts[i];
}
break;
}
}
if (!charexists || (nextfont && nextfont != curfont))
break;
else
charexists = 0;
}
if (utf8strlen) {
drw_font_getexts(curfont, utf8str, utf8strlen, &tex);
/* shorten text if necessary */
for (len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--)
drw_font_getexts(curfont, utf8str, len, &tex);
if (len) {
memcpy(buf, utf8str, len);
buf[len] = '\0';
if (len < utf8strlen)
for (i = len; i && i > len - 3; buf[--i] = '.');
if (render) {
th = curfont->ascent + curfont->descent;
ty = y + (h / 2) - (th / 2) + curfont->ascent;
tx = x + (h / 2);
XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len);
}
x += tex.w;
w -= tex.w;
}
}
if (!*text) {
break;
} else if (nextfont) {
charexists = 0;
curfont = nextfont;
} else {
/* Regardless of whether or not a fallback font is found, the
* character must be drawn.
*/
charexists = 1;
if (drw->fontcount >= DRW_FONT_CACHE_SIZE)
continue;
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
if (!drw->fonts[0]->pattern) {
/* Refer to the comment in drw_font_xcreate for more
* information. */
die("the first font in the cache must be loaded from a font string.\n");
}
fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern);
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
FcDefaultSubstitute(fcpattern);
match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
FcCharSetDestroy(fccharset);
FcPatternDestroy(fcpattern);
if (match) {
curfont = drw_font_xcreate(drw, NULL, match);
if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) {
drw->fonts[drw->fontcount++] = curfont;
} else {
drw_font_free(curfont);
curfont = drw->fonts[0];
}
}
}
}
if (d)
XftDrawDestroy(d);
return x;
}
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False);
}
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, Extnts *tex)
{
XGlyphInfo ext;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
tex->h = font->h;
tex->w = ext.xOff;
}
unsigned int
drw_font_getexts_width(Fnt *font, const char *text, unsigned int len)
{
Extnts tex;
drw_font_getexts(font, text, len, &tex);
return tex.w;
}
Cur *
drw_cur_create(Drw *drw, int shape)
{
Cur *cur;
cur = ecalloc(1, sizeof(Cur));
cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur;
}
void
drw_cur_free(Drw *drw, Cur *cursor)
{
if (!cursor)
return;
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}

74
drw.h Normal file
View File

@ -0,0 +1,74 @@
/* See LICENSE file for copyright and license details. */
#define DRW_FONT_CACHE_SIZE 32
typedef struct {
unsigned long pix;
XftColor rgb;
} Clr;
typedef struct {
Cursor cursor;
} Cur;
typedef struct {
Display *dpy;
int ascent;
int descent;
unsigned int h;
XftFont *xfont;
FcPattern *pattern;
} Fnt;
typedef struct {
Clr *fg;
Clr *bg;
Clr *border;
} ClrScheme;
typedef struct {
unsigned int w, h;
Display *dpy;
int screen;
Window root;
Drawable drawable;
GC gc;
ClrScheme *scheme;
size_t fontcount;
Fnt *fonts[DRW_FONT_CACHE_SIZE];
} Drw;
typedef struct {
unsigned int w;
unsigned int h;
} Extnts;
/* Drawable abstraction */
Drw *drw_create(Display *, int, Window, unsigned int, unsigned int);
void drw_resize(Drw *, unsigned int, unsigned int);
void drw_free(Drw *);
/* Fnt abstraction */
Fnt *drw_font_create(Drw *, const char *);
void drw_load_fonts(Drw *, const char *[], size_t);
void drw_font_free(Fnt *);
void drw_font_getexts(Fnt *, const char *, unsigned int, Extnts *);
unsigned int drw_font_getexts_width(Fnt *, const char *, unsigned int);
/* Colour abstraction */
Clr *drw_clr_create(Drw *, const char *);
void drw_clr_free(Clr *);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *, int);
void drw_cur_free(Drw *, Cur *);
/* Drawing context manipulation */
void drw_setfont(Drw *, Fnt *);
void drw_setscheme(Drw *, ClrScheme *);
/* Drawing functions */
void drw_rect(Drw *, int, int, unsigned int, unsigned int, int, int, int);
int drw_text(Drw *, int, int, unsigned int, unsigned int, const char *, int);
/* Map functions */
void drw_map(Drw *, Window, int, int, unsigned int, unsigned int);

90
stest.1 Normal file
View File

@ -0,0 +1,90 @@
.TH STEST 1 dmenu\-VERSION
.SH NAME
stest \- filter a list of files by properties
.SH SYNOPSIS
.B stest
.RB [ -abcdefghlpqrsuwx ]
.RB [ -n
.IR file ]
.RB [ -o
.IR file ]
.RI [ file ...]
.SH DESCRIPTION
.B stest
takes a list of files and filters by the files' properties, analogous to
.IR test (1).
Files which pass all tests are printed to stdout. If no files are given, stest
reads files from stdin.
.SH OPTIONS
.TP
.B \-a
Test hidden files.
.TP
.B \-b
Test that files are block specials.
.TP
.B \-c
Test that files are character specials.
.TP
.B \-d
Test that files are directories.
.TP
.B \-e
Test that files exist.
.TP
.B \-f
Test that files are regular files.
.TP
.B \-g
Test that files have their set-group-ID flag set.
.TP
.B \-h
Test that files are symbolic links.
.TP
.B \-l
Test the contents of a directory given as an argument.
.TP
.BI \-n " file"
Test that files are newer than
.IR file .
.TP
.BI \-o " file"
Test that files are older than
.IR file .
.TP
.B \-p
Test that files are named pipes.
.TP
.B \-q
No files are printed, only the exit status is returned.
.TP
.B \-r
Test that files are readable.
.TP
.B \-s
Test that files are not empty.
.TP
.B \-u
Test that files have their set-user-ID flag set.
.TP
.B \-v
Invert the sense of tests, only failing files pass.
.TP
.B \-w
Test that files are writable.
.TP
.B \-x
Test that files are executable.
.SH EXIT STATUS
.TP
.B 0
At least one file passed all tests.
.TP
.B 1
No files passed all tests.
.TP
.B 2
An error occurred.
.SH SEE ALSO
.IR dmenu (1),
.IR test (1)

109
stest.c Normal file
View File

@ -0,0 +1,109 @@
/* See LICENSE file for copyright and license details. */
#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "arg.h"
char *argv0;
#define FLAG(x) (flag[(x)-'a'])
static void test(const char *, const char *);
static void usage(void);
static int match = 0;
static int flag[26];
static struct stat old, new;
static void
test(const char *path, const char *name)
{
struct stat st, ln;
if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */
&& (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */
&& (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */
&& (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */
&& (!FLAG('e') || access(path, F_OK) == 0) /* exists */
&& (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */
&& (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */
&& (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */
&& (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */
&& (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */
&& (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */
&& (!FLAG('r') || access(path, R_OK) == 0) /* readable */
&& (!FLAG('s') || st.st_size > 0) /* not empty */
&& (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */
&& (!FLAG('w') || access(path, W_OK) == 0) /* writable */
&& (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */
if (FLAG('q'))
exit(0);
match = 1;
puts(name);
}
}
static void
usage(void)
{
fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] "
"[-n file] [-o file] [file...]\n", argv0);
exit(2); /* like test(1) return > 1 on error */
}
int
main(int argc, char *argv[])
{
struct dirent *d;
char path[PATH_MAX], *line = NULL, *file;
size_t linesiz = 0;
ssize_t n;
DIR *dir;
int r;
ARGBEGIN {
case 'n': /* newer than file */
case 'o': /* older than file */
file = EARGF(usage());
if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old))))
perror(file);
break;
default:
/* miscellaneous operators */
if (strchr("abcdefghlpqrsuvwx", ARGC()))
FLAG(ARGC()) = 1;
else
usage(); /* unknown flag */
} ARGEND;
if (!argc) {
/* read list from stdin */
while ((n = getline(&line, &linesiz, stdin)) > 0) {
if (n && line[n - 1] == '\n')
line[n - 1] = '\0';
test(line, line);
}
free(line);
} else {
for (; argc; argc--, argv++) {
if (FLAG('l') && (dir = opendir(*argv))) {
/* test directory contents */
while ((d = readdir(dir))) {
r = snprintf(path, sizeof path, "%s/%s",
*argv, d->d_name);
if (r >= 0 && (size_t)r < sizeof path)
test(path, d->d_name);
}
closedir(dir);
} else {
test(*argv, *argv);
}
}
}
return match ? 0 : 1;
}

33
util.c Normal file
View File

@ -0,0 +1,33 @@
/* See LICENSE file for copyright and license details. */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
void *
ecalloc(size_t nmemb, size_t size)
{
void *p;
if (!(p = calloc(nmemb, size)))
perror(NULL);
return p;
}
void
die(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
}
exit(1);
}

8
util.h Normal file
View File

@ -0,0 +1,8 @@
/* See LICENSE file for copyright and license details. */
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
void die(const char *errstr, ...);
void *ecalloc(size_t, size_t);