From 515cc3a442e97a685d881426c7c3b9c6bbbd72a5 Mon Sep 17 00:00:00 2001 From: Derek Taylor Date: Tue, 29 Jan 2019 18:23:21 -0600 Subject: [PATCH] Pushing dmenu and related scripts. --- dmenu/LICENSE | 30 ++ dmenu/Makefile | 64 ++++ dmenu/README | 24 ++ dmenu/arg.h | 49 +++ dmenu/config.def.h | 23 ++ dmenu/config.h | 23 ++ dmenu/config.mk | 31 ++ dmenu/dmenu | Bin 0 -> 39736 bytes dmenu/dmenu.1 | 194 ++++++++++++ dmenu/dmenu.c | 766 +++++++++++++++++++++++++++++++++++++++++++++ dmenu/dmenu.o | Bin 0 -> 30840 bytes dmenu/dmenu_path | 13 + dmenu/dmenu_run | 2 + dmenu/drw.c | 421 +++++++++++++++++++++++++ dmenu/drw.h | 57 ++++ dmenu/drw.o | Bin 0 -> 10168 bytes dmenu/stest | Bin 0 -> 17696 bytes dmenu/stest.1 | 90 ++++++ dmenu/stest.c | 109 +++++++ dmenu/stest.o | Bin 0 -> 5288 bytes dmenu/util.c | 35 +++ dmenu/util.h | 8 + dmenu/util.o | Bin 0 -> 2216 bytes 23 files changed, 1939 insertions(+) create mode 100644 dmenu/LICENSE create mode 100644 dmenu/Makefile create mode 100644 dmenu/README create mode 100644 dmenu/arg.h create mode 100644 dmenu/config.def.h create mode 100644 dmenu/config.h create mode 100644 dmenu/config.mk create mode 100755 dmenu/dmenu create mode 100644 dmenu/dmenu.1 create mode 100644 dmenu/dmenu.c create mode 100644 dmenu/dmenu.o create mode 100644 dmenu/dmenu_path create mode 100755 dmenu/dmenu_run create mode 100644 dmenu/drw.c create mode 100644 dmenu/drw.h create mode 100644 dmenu/drw.o create mode 100755 dmenu/stest create mode 100644 dmenu/stest.1 create mode 100644 dmenu/stest.c create mode 100644 dmenu/stest.o create mode 100644 dmenu/util.c create mode 100644 dmenu/util.h create mode 100644 dmenu/util.o diff --git a/dmenu/LICENSE b/dmenu/LICENSE new file mode 100644 index 0000000..d0c6d64 --- /dev/null +++ b/dmenu/LICENSE @@ -0,0 +1,30 @@ +MIT/X Consortium License + +© 2006-2014 Anselm R Garbe +© 2006-2008 Sander van Dijk +© 2006-2007 Michał Janeczek +© 2007 Kris Maglione +© 2009 Gottox +© 2009 Markus Schnalke +© 2009 Evan Gates +© 2010-2012 Connor Lane Smith +© 2014-2018 Hiltjo Posthuma +© 2015-2018 Quentin Rameau + +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 +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 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. diff --git a/dmenu/Makefile b/dmenu/Makefile new file mode 100644 index 0000000..a03a95c --- /dev/null +++ b/dmenu/Makefile @@ -0,0 +1,64 @@ +# dmenu - dynamic menu +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dmenu.c stest.c util.c +OBJ = $(SRC:.c=.o) + +all: options dmenu stest + +options: + @echo dmenu build options: + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" + +.c.o: + $(CC) -c $(CFLAGS) $< + +config.h: + cp config.def.h $@ + +$(OBJ): arg.h config.h config.mk drw.h + +dmenu: dmenu.o drw.o util.o + $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) + +stest: stest.o + $(CC) -o $@ stest.o $(LDFLAGS) + +clean: + rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz + +dist: clean + mkdir -p dmenu-$(VERSION) + cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ + drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ + dmenu-$(VERSION) + tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) + gzip dmenu-$(VERSION).tar + rm -rf dmenu-$(VERSION) + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run + chmod 755 $(DESTDIR)$(PREFIX)/bin/stest + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ + $(DESTDIR)$(PREFIX)/bin/dmenu_path\ + $(DESTDIR)$(PREFIX)/bin/dmenu_run\ + $(DESTDIR)$(PREFIX)/bin/stest\ + $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ + $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +.PHONY: all options clean dist install uninstall diff --git a/dmenu/README b/dmenu/README new file mode 100644 index 0000000..a8fcdfe --- /dev/null +++ b/dmenu/README @@ -0,0 +1,24 @@ +dmenu - dynamic menu +==================== +dmenu is an efficient dynamic menu for X. + + +Requirements +------------ +In order to build dmenu you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dmenu is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dmenu +(if necessary as root): + + make clean install + + +Running dmenu +------------- +See the man page for details. diff --git a/dmenu/arg.h b/dmenu/arg.h new file mode 100644 index 0000000..e94e02b --- /dev/null +++ b/dmenu/arg.h @@ -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][0] == '-'\ + && argv[0][1];\ + 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 diff --git a/dmenu/config.def.h b/dmenu/config.def.h new file mode 100644 index 0000000..1edb647 --- /dev/null +++ b/dmenu/config.def.h @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace:size=10" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/dmenu/config.h b/dmenu/config.h new file mode 100644 index 0000000..1edb647 --- /dev/null +++ b/dmenu/config.h @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace:size=10" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/dmenu/config.mk b/dmenu/config.mk new file mode 100644 index 0000000..ae34ad8 --- /dev/null +++ b/dmenu/config.mk @@ -0,0 +1,31 @@ +# dmenu version +VERSION = 4.8 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = $(X11INC)/freetype2 + +# includes and libs +INCS = -I$(X11INC) -I$(FREETYPEINC) +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) +CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) +LDFLAGS = $(LIBS) + +# compiler and linker +CC = cc diff --git a/dmenu/dmenu b/dmenu/dmenu new file mode 100755 index 0000000000000000000000000000000000000000..720fb5edf019ffcf797a09e84f3a2f4742d804be GIT binary patch literal 39736 zcmeHwdw5jU+3(I}AVHW33K~>o)PW{KVg?B&;?zts343sWKmtTAhRI|?0?AC8%s^

VwpguIybuT%@z#h+Q7YnnkMTmiK*Zvl-@De@VQ0vG&iBW8 zp7WgZxOp<~dVlL(?|Rq$vKM>ZT<)nzO-YfoCr$dkL|j%8M@odp<3AM+kP@j#It#xS zNfV_J;L{n#{YyB3FMk?Shtdpzr-4$qt-?Qs!WlhQ5kR9-7MG-OCURQo z2alEg4${dpIImHCI2PYZA)iDCMsr-F`f!vtTUxmPmK%rCdzdkj)H8+SS~$uM3b##! z+a|(k)GG2zqgsBFjr6x$_$N~r{OHjr$boTYzw6=l}E( z#UL#d@fqdIN@2f7MfB3;%}sSPXI$P~pV!6-ySi*D-G5Lc8g{JsOMWIB$hR8A`%B=LBr5^`v4NgH8sL{1lve<{hRdHP4D{?X z$cGgM_W#^K&uRntQUm-a2KYh)`=bVSK4_rlZUg)*1N+}Hz;_$iGs8gtj}6M@bp!q5 z4fOooAia+o$iHI{?-2t%-x%04%Rqmc0Y2Rz-o*y;g$DNAXOPd28Q^0K^qURrdB7lE zr$IivVi4~?4D{C-==qU>{1gNGKQgfMKL+?#13S|U;(f^=-lYcmUouEnuYvyY26{e4 zyjju&Y2^bjSoa)ec(zm&)q!y410M_d5`k;;$#;aFWce=O!}WVN2|X*5^hlVu)!6Mc zQJX2%x~u2b*8AK1tD4$F{ z2U^3Sil8stF4a^tHA7eVT7MuU)wtTc>lXTbA#Y$6bk@vo^#@!{?XAt;^-@h)o8KGq zR|Es0vT$2_uuZCQ2M{vg3-^!+r7H_LmGrP@OcddVYUC`TBFV$2HjS&VcX$sT_ z*EvI>wx+sp$PY6H!OFH^tG_LT2&=-at--cXyC>-Lk~Q3b*=3r`4Oc~*pM08w?fyYS z5nWlcx4m6%q9lRi3g!mun;M$@ZHR24x2fIFZQv?2yB7skamzWZCA2IUSPPq~{LRRl zreJ_;Z3_7HDL_Ux>+)`%zau1awJg}W-ih3kY8Hh3ZR@L<%~k6IKGH)fS$Qy}6+skQ zTQD3zwo&f8=Snqmy{*h(KrDP^5DD`m|9OV!(?OZjQs)T<*MwWEkhy_XtOCK(HgAiU zYLQ*45ggN1<%2PSc9Lp9$@+qUhNe|quEOW?H+aL%p{j6Qd#EXda*`^1mEI65^!obJ zV6gc#;0m`kH~COO0F^a*+o%v&g#ka$+v2bEwzV@q7Q+ygLfqwV548o?pAPS=uO}~E z9D@?4tq_4(G8;mCK~vKZs;1oU2%%E9FA6mjNr0$s^E}Zb!btd&rt%K7EL4yNv<}pB zs#e651)GCy&gSMIONypUGgwGdaS6LL1q4yzHGZx)tc)Z?rKN;l> z1)B))HMWs(hqtz&Dd25x`k`NH@wc=D*E0J_vbL5IDte?AFAQ7T(26Pe}166d=_5*&cUkS?%=v8H27X23^zh3y1c<8i#Hk>s9)t=d_D+Lw_|XTG5_% zYcdp;qZ-v#!sw!z5BYLGEwn~S2R(Wn9HEtQ3UK-(=@i8MD#jpfT|cg=2}_c6=?gY) zNYXisPm@jwK74*tCg~k1_C2)y5N*7Xig`R+#eRo|Zxj1a&u-xU8m{qD(s+|uC*+dw zu>#LZ!Y2qkI|;v7;MOEOSKv8GxLx1{NqDiq`)=lT(cZ#Y+{u0q>2UqHdQ^v>uaj4G z_@z4hm=2$!!v}PDtqwo5fiswPNO?N^s16^i!?pPV6`!^rLf+bVPxwj##FHK2j*%b~ zhxS+_+(me*27=~9I74_Ece3At2xkbV`l~%f5$+;fR4E3O=y3i1l&r%kjP_JUxQj5Y zZV^%wp6o~6Sl>+ozH{yiOT z)!{ihJV%Gq9-8(P=)8Sin_);DIo%6dK_$~+j|IL9fEEk=WqaSC;G1E@059F?%P->!2 zj_%IrWr&5@ie86Z)5x} z;?s%W!uajP(-c0wnel%mo~G{c2;=`qJPk+jR>uE^c$%umS2F%y;%SN=uVnn4#M9I~ zUc&fI#M6>1UcmSb#E&N4%J?4=PgC=F7USE9rzv?{V*G02X(}E+_AP+Pb;Q#YJbsk% zONpnYQoN7x3yG(tT)c6g!^9 z_|J)_sdZdp{71x3BL3JZO8+~=TZuo)_&12ZnD{=%A0Yk`;(HkX9PyVDzm4&`h^HxZ zd<)~Z6Him;_-4lcm3Ugh#v_dXBk?qaj<+)YH^kG_Ilhwd_YzN2=6EIJ?ZzGShblJRE~PgB@<3FAi*PgB=;0pnANrzvaP%J`Ejz|&MUp2hgj ziT^(F65~H2{u<(seZ$J1_!8oeGX4$Xoy7Ms{s8f%#P=}%IpS&R8Q;eEUBuIrGrooK z+li;CW_&Z_|4KYfG2;=&|B-l_TE<%${~O|IN*Q0t_9<9>)6pM;&zS0m&OaJjK_Vy?*o*}6Ed5UWIkU8nLD`53A8K9js`Ac*5cX=nf?|S^M(*8PZD^V1UwLUS_s&Z z1nwe%sSwyA1k#v*rE3p5T`iiw!`%K6&S#K8l(F%X$U=Jpfu4lz>R50XufyZzSmDp0 z4kf-A%GCWR@%~J8*VmMfp?3h^PU0I70g0aj@dhqlM&kEE+@4U|A&y*pfqWn}8o}m} zcQd+DIX3Z8!efrfut@erkHR3u^f+NVkB*enqAKNAtT9Vg@-{FHiEt*|0l53e+Yi`} zt7V7=`j<0bGAmYCN$&2uY|m0)EmcpD&bygT(_x45vdQ#Q zsXk<7f>jXIB!0~#OgUuI9o&0{&U-WW9!uUq?(KlvegMiZMMOx=DTt_zl$9~Z#hTw( z?nk`p;n_R}ZZy(^q>-nuyD~Lqx);1MemOi7J(j=hQcP|x_Y%n!n|6R$x&lZK)bAp! zJ7k-t79FRe+k*_WbS{9Wr@P*kG1`87 z)^gi4%kAe;^NzV7Tx@E9aOf-#wA$q8K+3wQ_T%=$@vkWVVmH`Iq{hFZV=JL#U#;GO9LHSUigd3MDK~Chv3=f9A5x{mhlC!sK19ly_M2<|DBm z0RO<1`ws8}NDA6%dZM>~tL!^0uQ@zgPU*MoC`-5Os2YLFf1$l6>iEH8Bqlrx@TjS# zw~@}2K3Vw;zE(hJZU+FVr@la6q4L`1o%M|mTp4r?;Q9WP+sq`ra4B1Moz z5$r>3AX0b?N|wrNswoUviUt6ct72+ox(n}zyuC+83(t#uUkc?Qy2k*i-(WKOL^^aR zdt;_)kp0DpuVZ5_g(G*T@@&d6#dHDOPtX!ZMy0Qbna&eJ<43?z+=p7An7$!ZOV6cL zm}bMhuhX^-Az%P1+nBe3xMHRwAZU+SNKq9zA+&{rFwDZ5N`hed%0-Q-7_e81e=(1( zGFEN#s9TU(C~azWlOamY5Pi*FbPL~Ns|bxy4Uk04F%rJ^{jA&l7aEf%W_l9>s}W+% zzcCY)yT=>{*`9$!qm#7vI4jY^90R0I{G1dN{u$!XvxA6Yx(7skg*t&);azas_rveE zaIk(&jekjIIWiHcRI}95*#!#8HVPbb{D|ae?_GVEa>kDW%XV} zg!=5TAC{x&6Qs};tZyh@ZnLeuRF2JfOHTC4vkK2bs1T}#*oa@bd*Tm>pkJ#-Y7S@P=TWTc`ZJ>gnU>D2C?`+%gSJDUkl70v>e1#Q zKhf)T{UHOXKJHOIQ}06tq3*we%0;r#fzg&uIs6Xsh35X6fSvsGC%W~^u2nN2+(pgFT#xl6WgK z3HlG#!WcoL@QDssko@%sWa*rZC46*XlBM&0tnJxSH~N$<3noGP2F!WTNS=bAD`t8E zglaEp$P|h%=CFXMKRF-aY9NUQW+uG2(>6eP)>#Hn-H#=GycjOat#3iA?4Xn)lhABs zM<#P0CiAAEcF2}z?qWqG$E4%wm8q7lU&AQb7qX4^C@12NQJCn!b(YR+p=sR&xaU|p zn|Vgg`SW@ubc{Rlw5uC-QQ1GxrIGFJNwk1V5$;qcED_ z_-ndUTtL8X0Fk#dESIe@95mt(!E*mx=M8lt10|c3Lh(6f@K-z%O{Sgcbo&1QXc(B zLLEzm-RMw(mMr+)Pr0-)FNc$9DL2~X~)>T-ne#PY^L#oVq>!u$H|6!ajf z5Q2`?1>MerTDl&C6@$Si^5Cyk^5DJkd*B;+I|Y650$^-Pk4dc`?$Ah>S`PsmaGr>HyLlkL!TN1RT-r_mH$V(Ge%n)W`^-@wolzl0)-I<^uYvO#(k zNuxGq$5^&H7BkJ{0W6*WWSL%)K|OTmbHpg7 z3FH|Ya|w}RIm^=ZS0=>p_+wB51*f2ng*ibpO2zaQ@rvmPNc>)ih(12W(v?XIj95`T zg;CQ}TEG=hy00V~zfybwkKhpes4zq4@`9uR2}*a8(tL@AUK}H-!$jrp}d$A|?!XFqAt828iVfhIQ9V(_$tUE(i~)? z?86*&dk-mjl9TP0ijFMAM-tu-2V!P$*2!6vRnBD(YZzT%gT$UuqBU2FZ z9FzmE?~xB#ok7=Q?{O<%E~=81Z)D{J74viQ39RZ@CnB??(0Q`*kw^J}c0gW22_f4? zVv5`U9wxxbAu6a|=Mv{)=OQdloz>;`_ZC&b=a!FitdcZjmoOWEy$1`Taj*_0(rnAd z2(t`Xf_2w35`5L0U=gzEN-~$))|U|UC`Ucr?I|b#le>6t+hy)sc9SeDQh)ss#1&Hp z7Y|#=%5KLBO64Lbgvd!aXGI)o_aDktFM-ua$ena(=y&&usRrTbh(1F#oqgnZ0& zQYN8}A_SmS#W?xILAiS|?EO^z?<`FFkq@&>eyCWRshC!Px|KIQ$`^5!MnP;egL=yMWSo%kCV&x<-bL-s(kF3u6?%*lvk z;*(%vc@N}(V)W|~L@B{VPU_!%{r*!U$t!o_loR+E3B2J_{3O7Aql${WRp+Al(=4}rsb zGb07+0VXkuBzC2|R@{H%*^qf2o-C&4;OKV&d6@t`5Pdi zDWv0uT7>v94}of7>SRvSjYLpGcK2VyO&kqCUBC=b;AAH5K+tL?@A#yGEJZZ20(B~r z@{!c8{{O1srO=Dk7yUR#*I-<>Z0dO(3m}N$*k#KAWsaeNv@~7Mc;XIvzy? z==Ra)Lx}#9m)L6b-Fg&mz6*M*O3qkomU{35){Za44ilP32e`!Z;U`eZ7hg*CS3M6V z(iT^?zUI9BK59;Mch0gEF!srM(iQ!ve2fe4WdkcL2NR}w?ld^m zaTsRtz8TKdlJe;j7>}gH6|R`67sxN5VG`(xuU$&qrM%-*cB=EoKyh?;wkS$U7ls4~ z9tWYWks6o%u=>8AA8x+_NtWJrIX@<+m$_t5&Irboc`fG@x>imQPU%!-UW>us} z3eAJwpK-mvBC!C(Vx|kAU^N#1+{(h`@IeF^)bTXA&}IwDe>X->{DTIzC?_szwT*<|GzlIBYVEDCkZbREak97zP+D7i{$`KT@`XF1dwPf|b zjNJM${2;jzl8EnYlI$*|el~65&%mg2Fv(9dksa8Sj+yY^Xiz(7qU?t0&zG`L{{m4T z%%m*54*sZTN0`yb#T6tUb8LqMcKR^dKaClp5?{zO^IfFmy6Zi$$)CeNrQg$?w*}sw z;>mAt#ZeGT=LT5lj?J*C&(KhlHv%g8UQa!|(@Y=W7nz>>rur-RPt7|B9#PH)$=%~D z{S#2Xg{EC#4>4atlpNT z^u|}C$I?*;^gDU_LDEdulj>ge0d#FxcYMKOSf*v2`Y;J z<^e`n^ShhYrUK$P{L!168+ zX*htr??v`c=*4l7HEB9XGY}dss%<&4@=vvwly?{Y2O+TMtG)`>jdu~vw)DB!5%Ml}%$9e8D8J$`AAf9JcHr5{ndDJKi5oMZjG0&aRRF9x?naF`p?B+`RnunOqu+-|Y!*#SF-@M)ml(g_K?t#lU6i<2<9|VrSqNb1`W;-@jZT-sH0Sw{!lFJ+ zl$DySe$14r5nJpEn3xzJgBEzb-hUx#&0Z2z$};4AWoVt`p7p4!!hh| z7bV6oU?IbkWaQ5XSy_?QkFBXUWn~TKMQ>^v`Y)nJKhmu%w=w@Cnuka6Ww@1p$$O0vA0tnRsk{&lGoyMeFAzi_w z3s1s4k21NR<&x=RB8us0PWFPR*;HS#lS~y)KW)f-6ZkE=se%rf5o29>1mW=dflfc1 z@+kYN7Kqh=^1OWF6)dbT!VVLy-`7!wDG=T+L5r+>;pv_>LeAYauiJ)#IQ}@2C|%oq z16sSqy_Z%gxx3V}|AlC~4&QVZDkz3US$X}72axD8Xj#hbi2n)wsWv~PCBgUD3N3bX zHh&RW@H!=EmK^H7r5tL}$IOT|)Atm(=A1^Vfkf-1Jos#K-Z>#s~X zc5{aM8_J*9k1}LmSr+CO%120Nwr(Qp!h2)&MG}G)tmEtD*|W~d!q;ixW9>Pr2{y3o zxgGh$m#Z`-h*-`#Eyp@5 zwTOgiW$RHoF#oR1cE*;ZJJZfnD$%^F(107Q^afqYawqnti)QZGu-a@S6|>wx91o{XJgnPkgkWdJM5n-S3~vabY< z_898Bt}x|TCC25}67}Ol?EMvNJc4xp9jRItS1@WX>(vxTPo`MzCe8Jvxt1#US2WAP zAtlsu3?*o+ud$L| zuFEM`UZjjbw?0}{cDa;2PUQu)0!3Zk^$Ch1hgJDlyA^L$Gh8rfbm&|Rf+b{9x$?H? z7^Xlz@nSi)6%v}K@RYtQDb5AXO6S5# z^(O~eP+&jGupeI*e;cZLMKY~JmU*-=g<=Oa)*`p^0woV)8WgWXo4CfK?4ehsqt*E+ zOlm8cmL1sba4P9GHg?4Ddc7Rg5o<(!KYR-}owsFbqi*F}%I9+Bg+523r&`=; z+YB1J&vp+8@)7;gF63@bCQ|9L&7gA4(wW<7lObpCL39O~$1-=}O&O+cECrU%Fr4n_ zO$8W#SHp?b>NIo!k276D}VRvk8jyrZ! z2I@U(cDg&QqCk#isjFW@=MY;AueDaE5_eX@kgHv}?~`aHm2e4)x=5{JVmD>DeHB?G zI;3&PXdIO#ss;J!N~yPDPYH=Z%;TKbIhRJiNkOMnBv0s}10ra(E(n~!Aq_bSQ4|RB z1NNd3A@|^k=>&c@Nu51>3trh3eWxdKG7_5A*|Yxp#2t;C>8RJ^dGQkw+r^XYBnj%8N2MGHI1(d~8pAc@95GKZD@xV3Lmq5KpFGht# z?IURfL{tca;OZB6E#^-ABYZf%5z6VMf-F>iO$XV0s9;|_P`cN3wT#8+z}j;?%7D9j z$tk&bSNkZu&f4orm-EB_X6+|53th1-3$e{`PDMD?;_h4?9g_#1IAvh5a27dI!%8e2x+Um7PBL=Jsc%mz^D8mSMCG*a$Hv!z z#aMp7^ff|8cW3uvxMo8Z27I)zX-?%!%-o-$u8&WesMErVn}%o{{kSp1g|`TqFHsV) zd^|+4(ljG90mI}MY`zkkDf}_LsYE&PIp!@N_d%shqYf?53TQo7-|79;I3$8`l1 z)e(B%)!k`33ZZ@JtT$)1h+3i*S(^?i*1cz`HAoO%+@P9V1TZv}7A?@kUoX>}oyZO6 ze0x>+oE=sKQI|rR&eu{c8npxSL`j_ui09Xe7ZZsE>N=D#)jT?MdqoxmPN2J4idSa* zeVlWNTlpsq*K}T7IYlP`C%>1=UOna!t@7l)PydH!m74zo6zx2f7P`oOn7=ne^YSRi zUCL+9#5h_UreG!({s?86p{oMAcVhH@trs>9{=Xnd693)vT@HMg1K;JqcRBD~4t$pb z-{rt}Iq+Q$e3t|N@8KdBXt^jSe;#2GXc5=2_Tib#y zts!zZ1g!L>MRK-S@yX7nP_S*qD69TimKUhA`ska3Bo$~l?QYlM?K*s&bzKvEQM0Lj z#VCB;FAxZZtm319*2!UO-Fkdu^D51Qe;-hXUyVx(L?HV7rFD(= zTHE>OJx2}a*P?x^^R$$(1s?|uSnI_%Nqxa^b3IgstoW><)!$O*ug3=vSBcE9;){pk z`=VD_3pyq@cSwkxGQS-;N+Ij~FveT&ug~XclFB^p%F_AHg)V7PbwyEaRrNymyx9^< zy7qRAytIxz(xubcBVFoek9284L1E#QR}!U%XagPzB_C{W z_4@o*wd31~4ts%QmDu+lr&&cx$uefGwE>mK8o+lXud;q`a{Kp2B?-3Dr!~1G30Xry zYb*asrzS6wZEbJ$`{)~}Nbk_N*!Lju@lIaTSS(Vg5lWlzg-EWtDPSeU=k?*YB@B2l z_qc&xE{EEMBFL9~_?#xbX(_d%u-O-ChsKzcI$>}=t?%_uB7yZho|^?oyO{LQ`FBpa zKj;4UzY$G@Z8sBx9Tz+>64ml5DdmE+SuQ}|16KJ|BGCc&=ADVe7SJuA+pzglf*rP% z;ES;(PW!`cpf8Xd=uyzuKu2Sb>*si3Py;#v@AU2h-3$5@=qkL9dk^$IP#g9)H{fW; zgPe^KsDWpC%GH$R{7D z4Eh_;5a>?O&7hCqe{MVo+6Ve9Xvs0ek5^jsursuvDL-5&rF3MaTrhfM#%AOV;W*wP z9m3w?<)D&lBr8B0oHOv(w>^<4L0TkpR)slxj^(U%84>B4^JiUlh3yicEc|j%bw?tB zC62jkl>_Zj{l(5_I_bXr*5#}^!<$&CWzpcQ^2`e>c-JUwfoV_v4X|_hwL*{Hq zm6}PnjK62`w-d6vU^~hFIJMNAeMefU+1j06ZqB*gcl| z>>;=*9UD_25lwTMOHvl3>W$I5gxvSwv_4!ATyOHEzWOr&g z+2SHw%E%U^tHWI6HkbI$u%{D2sGdKL@V`Pml`73ycTifoDPL}F7j!NY|dFaBuiOc zpfBaTgm%OvXSh1up~C29Ak4qq2YmlgTuKzrW|H_!iT z2kXLYQ5RBIjT)Nw28rMls8lK7h$aLolY8%ipm z#0_yL#NF^sy>IxG^KV^Vm@Y{#&w9qB{`3Q#>tZf&%SAAz2$kiYm3#<;YG+PI6#zl@bXoV<}cd-uM;ZK8hN z3;FMnJUn63U?1=T{F>ociaO72I^74ba%cI=`T&=?GHnXFfHRFkPKV>n)y7!DwgTI)!)ky%1#Abft#I?Ynbc2F(_MqQiQZzcpDI6V{u#9u z2I*^S4%B61+&K$};GCp;A*HdJdO9oe5xM9#XU%5WAyFB+AZvvzOY7-243$x&6(+Fg zc}X@yJ_Pwj$d3Zml@qHwtejY1E!yYi%+y`xdon%dEt%-L?#V1QZ_Z5nM=En|$y^TV zB;*5w{+!D1J;alWG1LyXRBg^eNuYjR%jylKmHtn}Iq<)R{FCfor&U3CW#g{^vQr>C ziDX%usA3sGejfPc!tZnPV|8$f2UCAlf~JHLKnF8q;G#P60`%lS530rgdykk5Xy3u6CQMH}pO~xxlG?uN(gZj6LY?*mJ(1+IPEIzW_}0|LWUBkVG;> zd|DZ-6cJNZr$-k4TH8KH2;vMrd#J9^gF^yC9uW*3CWl3`rDv(`;Gy{%J%SzlhH;JX z(C`i#d+=y>Q~jhzOb1zAB_40-S$g1PqTdR>NQ6%aUdv}pDVcl-XV&S#i&VyI>E!7D zygP>~#-6-hYg8p96#{}Ig=s`h05cIg9BQ6o~3pz#6nSxdbx>(Q#LDvepQP8^u z{k@=%3A$I%gMxk_=y5?uTq@!hbc&!e1+5U2{^Jlm4T7!}bfchm3;KIO9}{%1pa%v0 zK+xlYj<9jP=L$MS(3yf(2)bC%20_;fx>3-(1^vCCj|sY0(1U`0An0*HM@$y+3pzzm z`p!DY`|JjerGTIfiq-X*a>g#d=Ec$@Lor~ZyJ1P z9iIH8N$G?At==z9!U5?tJ;L2-xbaZykM+K3=ry%|Ybc%}Y5m<$JX6y8(V_S#N$YQi z;^^J2dW5^vq|uUfSbU7Ma+rFMhA-&14vSkPZ5-3bl15+9U#Um9I}JA>Y4>32ebaE` zQMO-?X!7R_j*EKVG>kghcsLXvFAdkq96UXj13@hBX4G<(Y6CR`Ts>C6|0 z3U99VJRGV8aR>qKuOP`Bt5SIC;7*PJlmH67H9HV(zR3p@T77g z4KTM^x}uB&>jh4qwjezZ3Y_i5!0$YfpSOv4wRBx9@JNz9Qw45KO4kgAXG@1R@Cdc~ zbG5*aCgJ74$)5EUTz<2#(<9{jguG@?mB3$3!j}lVAjwYpEJzB?o&{bY^w%?df;5nn z-ZcX6OVSe-_^~AXhXPlV@S6qRlY~cs57)ol0i5!oq(~Sj2<`b2-kOB}R^ZyYObbQ- z)tKr}qlgzQd!A+Znd8D|LJ#ZEG$o+)^_&#x%<<*?)Zy*70w+5&XLBWNZwk(-z*8{Z zZSiqDhZAYKz*i>W#RmF~@~wpFIa~UYHZYQ&;qrMN zCU9+Cszv=@OwXC)?9VYCrP9qyR!x%A>F9r~sGrH>=>yOsmUKe`{Mi8CVSx7;;O`sY zCk*gY26$!~*=d%t#CjdcVb27H4>6rFGaEMLTBZteM z<&d`u89iBr{2xw!jX}IO8sO&;o_glIZWEI?OQ{QatZYvU&R-kodDsBoZGgXSfDahp zqtIavmtLcLXfHL8w;SM2hMT3CqP=esne8!gLa2+4GQr zJpBij;qu{m1N?{q-fw`PGQdZ(dah3qcc07J`F`?K|}WAMW1Q zcMbIS8{j7maFaoMKO6Yi)HCm)y@<)1r2&zjY!4i1_@05DSqAt#p#5%WV8W3 z!2r);xLIlw{puuPf1!cA8#vuRToe_8f-EwSZ!o}{4e%}l{C)%cVFP>*!?CV$a|5+~ z>w^N1it@#Zg*`_E9uf6Z8wWo#&`%4`;o9jq1N=e*yubjTZGbN^z}FbyQOqlbyD$1C z1Nlt`_&o;rLk9Sh2KY+`_&Wyp35J`cXj1<+0vq=<&-tq;-|IvlF^Trn6QpJMV@pNe)(`zG`}Z=mOO z1N=Rq=O;qX=Uk1X8pwYszr4A%2YgquSCmUgMWZ5=e=klQ+`xvkY(kGqR;q>)Zb`GO4%INsGR)ip{j z>m+`@aUH3}jm3xrh<~j_#|Z~d6SDJpQavs(CYYZmtjDFy6cw&NZboe0b##bQYH1V- zr53s|nPP1Adjq)CxGfkA;q2U6|GHos&h4%8=i~HSD8E1=zTV&5)BY6sBvZJDPlw4_BCn!uUU8D22hh+iOEDwLZGNovvK2 z57w?~4%T^_YwK|sw7u3F?tt!=)@FYQNA<3pUUcQ~LUi?VQ?0kH&AT2qCx_Z_y)&-) z_Se>jTUzLrXdPEe^M19|A@S7@1=uC^QmyN{dCs|Xi95f8 z8?M2t!ws+g9`^F`VXmg<_rlY!xWirCM(t}4vs}||=y5#0z>YZCxxuq_(Qcgt%{?%p++SAjGN_W`^D+f zN>o@>yY>9~?UvS|y3e>>d#E?=Bu~EYTz9uUUC!R@ZEu%x8@HDI;z;XY5r~V?$!w7a z?C$WSXy}G>mpF}E>#m+#d%B8O&qd}2{MGF6=UQ>~_t4dMC)~rZKXbOupmXw47oM^v<+9Fis)}hKkY-+rv{}^5rh?O z!7zFqN=x%Xzc1tstipL>77H~?a!^N$Zm@;!R!43n>7iOa8_kFc{Ir6adFX|C&WH=@ z`R((ywMejN&!jsQun~_k{>ET2DKp%q#85>Hw`=$?ASTg1Ue7zq*J={(8p<)%?}NZNO3RyP)u|V zltESgX6R;WS?=p1polSc@tzQB>ceNWx6{|u4|W&mrb>gsW++=9@bPx)ZTI`mY^&(; zd~0fbjcaNfyiK}XnW6PV)Q(uXC<)2o$vCa+U|qGJA6Fi%iqxT?7~4;Gpjw?Lb*<}@ za)cst&n0^?`VI99cyTPf+3!zEeN9W9Cm384ZmnXcyTx(op+Qkb)l}s=LTHWci)i?( zaR<=A0?tsd1tp3>|61*T79j4xnT9P0``gx62GJ||+w?h8$&a(MLgRhITj_&ivQm*6h=?p7EFq=)=zzn*8hr#YUX-d7kZnhJJjl==wy(; z)WSu-T*LCJn)MJ(e%ME~r;?7`V%vXV^?6P8Qa*8w-u6Z*zkYoH`Zx`>ai6uAd^KT$#KZ(s z3%@phvzG!0S8H=f%4dB*KAichFsaLD9Z^0_X&@A2S(EQ?6qA_7dMMF29+b~!Bo;z* zBL;5^=3JzaG{ZQ`;(WASDIZfR%r21){|6pA$CZLVEdSV}ojcH|wjLJVOjdIfy%c{~ z`VNNQEfi_g3P1gWWyp|+_Gk6s-xJ|AY8xU=?>vS+F0lC0V^FhSJ71ONrlIUGY;k9!m8r9M(f(_cQ;lE4@udQb_`n!BBB&S;Z8v5I$@Y*>QjcVsGHF?Tw z{qqMflzy%Jq?N)~&_1f1y2q+>lk;Kx>BDR9<1|YDhe-b<$G;;fymmfDqerQ6>mDuN zG-y8@_-H6=LxgGX@wT)Oce=iC?liCIq4)6?{AtP5&f9GLABKM$VX5wE;k9!)+eCP) z$XKm@YVm62cn`u-7HQ$N^E-X)bSKV3!%zQc;WhdZJoVwFh>&jOblYITa$XCk(Juh& z!}o09ay@OFYWtI%lO(I=5cF$M(xus766GF6?VMUK=2ZK&Y7Rla1*Q0t)2W4@kt9#D z^yq0)5}ALih`&G_^rdmA0e@P$wf=*;XU%pNQ2cDUjXTC@FFFLS#jDBEaopj;KXfOT L7?~6xiIx5rZ7IGL literal 0 HcmV?d00001 diff --git a/dmenu/dmenu.1 b/dmenu/dmenu.1 new file mode 100644 index 0000000..323f93c --- /dev/null +++ b/dmenu/dmenu.1 @@ -0,0 +1,194 @@ +.TH DMENU 1 dmenu\-VERSION +.SH NAME +dmenu \- dynamic menu +.SH SYNOPSIS +.B dmenu +.RB [ \-bfiv ] +.RB [ \-l +.IR lines ] +.RB [ \-m +.IR monitor ] +.RB [ \-p +.IR prompt ] +.RB [ \-fn +.IR font ] +.RB [ \-nb +.IR color ] +.RB [ \-nf +.IR color ] +.RB [ \-sb +.IR color ] +.RB [ \-sf +.IR color ] +.RB [ \-w +.IR windowid ] +.P +.BR dmenu_run " ..." +.SH DESCRIPTION +.B dmenu +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 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 if not reading from a tty. This +is faster, but will lock up X until stdin reaches end\-of\-file. +.TP +.B \-i +dmenu matches menu items case insensitively. +.TP +.BI \-l " lines" +dmenu lists items vertically, with the given number of lines. +.TP +.BI \-m " monitor" +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. +.TP +.BI \-fn " font" +defines the font or font set used. +.TP +.BI \-nb " color" +defines the normal background color. +.IR #RGB , +.IR #RRGGBB , +and X color names are supported. +.TP +.BI \-nf " color" +defines the normal foreground color. +.TP +.BI \-sb " color" +defines the selected background color. +.TP +.BI \-sf " color" +defines the selected foreground color. +.TP +.B \-v +prints version information to stdout, then exits. +.TP +.BI \-w " windowid" +embed into windowid. +.SH USAGE +dmenu is completely controlled by the keyboard. Items are selected using the +arrow keys, page up, page down, home, and end. +.TP +.B Tab +Copy the selected item to the input field. +.TP +.B Return +Confirm selection. Prints the selected item to stdout and exits, returning +success. +.TP +.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 Ctrl-Left +Move cursor to the start of the current word +.TP +.B Ctrl-Right +Move cursor to the end of the current word +.TP +.B C\-a +Home +.TP +.B C\-b +Left +.TP +.B C\-c +Escape +.TP +.B C\-d +Delete +.TP +.B C\-e +End +.TP +.B C\-f +Right +.TP +.B C\-g +Escape +.TP +.B C\-h +Backspace +.TP +.B C\-i +Tab +.TP +.B C\-j +Return +.TP +.B C\-J +Shift-Return +.TP +.B C\-k +Delete line right +.TP +.B C\-m +Return +.TP +.B C\-M +Shift-Return +.TP +.B C\-n +Down +.TP +.B C\-p +Up +.TP +.B C\-u +Delete line left +.TP +.B C\-w +Delete word left +.TP +.B C\-y +Paste from primary X selection +.TP +.B C\-Y +Paste from X clipboard +.TP +.B M\-b +Move cursor to the start of the current word +.TP +.B M\-f +Move cursor to the end of the current word +.TP +.B M\-g +Home +.TP +.B M\-G +End +.TP +.B M\-h +Up +.TP +.B M\-j +Page down +.TP +.B M\-k +Page up +.TP +.B M\-l +Down +.SH SEE ALSO +.IR dwm (1), +.IR stest (1) diff --git a/dmenu/dmenu.c b/dmenu/dmenu.c new file mode 100644 index 0000000..6b8f51b --- /dev/null +++ b/dmenu/dmenu.c @@ -0,0 +1,766 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef XINERAMA +#include +#endif +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ + * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ + +struct item { + char *text; + struct item *left, *right; + int out; +}; + +static char text[BUFSIZ] = ""; +static char *embed; +static int bh, mw, mh; +static int inputw = 0, promptw; +static int lrpad; /* sum of left and right padding */ +static size_t cursor; +static struct item *items = NULL; +static struct item *matches, *matchend; +static struct item *prev, *curr, *next, *sel; +static int mon = -1, screen; + +static Atom clip, utf8; +static Display *dpy; +static Window root, parentwin, win; +static XIC xic; + +static Drw *drw; +static Clr *scheme[SchemeLast]; + +#include "config.h" + +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; +static char *(*fstrstr)(const char *, const char *) = strstr; + +static void +appenditem(struct item *item, struct item **list, struct item **last) +{ + if (*last) + (*last)->right = item; + else + *list = item; + + item->left = *last; + item->right = NULL; + *last = item; +} + +static void +calcoffsets(void) +{ + int i, n; + + if (lines > 0) + n = lines * bh; + else + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); + /* calculate which items will begin the next page and previous page */ + for (i = 0, next = curr; next; next = next->right) + if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + break; +} + +static void +cleanup(void) +{ + size_t i; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +} + +static char * +cistrstr(const char *s, const char *sub) +{ + size_t len; + + for (len = strlen(sub); *s; s++) + if (!strncasecmp(s, sub, len)) + return (char *)s; + return NULL; +} + +static int +drawitem(struct item *item, int x, int y, int w) +{ + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); +} + +static void +drawmenu(void) +{ + unsigned int curpos; + struct item *item; + int x = 0, y = 0, w; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); + + if (prompt && *prompt) { + drw_setscheme(drw, scheme[SchemeSel]); + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); + } + /* draw input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); + + curpos = TEXTW(text) - TEXTW(&text[cursor]); + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); + } + + if (lines > 0) { + /* draw vertical list */ + for (item = curr; item != next; item = item->right) + drawitem(item, x, y += bh, mw - x); + } else if (matches) { + /* draw horizontal list */ + x += inputw; + w = TEXTW("<"); + if (curr->left) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); + } + x += w; + for (item = curr; item != next; item = item->right) + x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); + if (next) { + w = TEXTW(">"); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); + } + } + drw_map(drw, win, 0, 0, mw, mh); +} + +static void +grabfocus(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; + Window focuswin; + int i, revertwin; + + for (i = 0; i < 100; ++i) { + XGetInputFocus(dpy, &focuswin, &revertwin); + if (focuswin == win) + return; + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + nanosleep(&ts, NULL); + } + die("cannot grab focus"); +} + +static void +grabkeyboard(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + if (embed) + return; + /* try to grab keyboard, we may have to wait for another process to ungrab */ + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, NULL); + } + die("cannot grab keyboard"); +} + +static void +match(void) +{ + static char **tokv = NULL; + static int tokn = 0; + + char buf[sizeof text], *s; + int i, tokc = 0; + size_t len, textsize; + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; + + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) + die("cannot realloc %u bytes:", tokn * sizeof *tokv); + len = tokc ? strlen(tokv[0]) : 0; + + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; + textsize = strlen(text) + 1; + for (item = items; item && item->text; item++) { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + if (i != tokc) /* not all tokens match */ + continue; + /* exact matches go first, then prefixes, then substrings */ + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); + else if (!fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lprefix, &prefixend); + else + appenditem(item, &lsubstr, &substrend); + } + if (lprefix) { + if (matches) { + matchend->right = lprefix; + lprefix->left = matchend; + } else + matches = lprefix; + matchend = prefixend; + } + if (lsubstr) { + if (matches) { + matchend->right = lsubstr; + lsubstr->left = matchend; + } else + matches = lsubstr; + matchend = substrend; + } + curr = sel = matches; + calcoffsets(); +} + +static void +insert(const char *str, ssize_t n) +{ + if (strlen(text) + n > sizeof text - 1) + return; + /* move existing text out of the way, insert new text, and update cursor */ + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); + if (n > 0) + memcpy(&text[cursor], str, n); + cursor += n; + match(); +} + +static size_t +nextrune(int inc) +{ + ssize_t n; + + /* return location of next utf8 rune in the given direction (+1 or -1) */ + for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) + ; + return n; +} + +static void +movewordedge(int dir) +{ + if (dir < 0) { /* move cursor to the start of the word*/ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + } else { /* move cursor to the end of the word */ + while (text[cursor] && strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + while (text[cursor] && !strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + } +} + +static void +keypress(XKeyEvent *ev) +{ + char buf[32]; + int len; + KeySym ksym; + Status status; + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: + goto insert; + case XLookupKeySym: + case XLookupBoth: + break; + } + + if (ev->state & ControlMask) { + switch(ksym) { + case XK_a: ksym = XK_Home; break; + case XK_b: ksym = XK_Left; break; + case XK_c: ksym = XK_Escape; break; + case XK_d: ksym = XK_Delete; break; + case XK_e: ksym = XK_End; break; + case XK_f: ksym = XK_Right; break; + case XK_g: ksym = XK_Escape; break; + case XK_h: ksym = XK_BackSpace; break; + case XK_i: ksym = XK_Tab; break; + case XK_j: /* fallthrough */ + case XK_J: /* fallthrough */ + case XK_m: /* fallthrough */ + case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; + case XK_n: ksym = XK_Down; break; + case XK_p: ksym = XK_Up; break; + + case XK_k: /* delete right */ + text[cursor] = '\0'; + match(); + break; + case XK_u: /* delete left */ + insert(NULL, 0 - cursor); + break; + case XK_w: /* delete word */ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + break; + case XK_y: /* paste selection */ + case XK_Y: + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + return; + case XK_Left: + movewordedge(-1); + goto draw; + case XK_Right: + movewordedge(+1); + goto draw; + case XK_Return: + case XK_KP_Enter: + break; + case XK_bracketleft: + cleanup(); + exit(1); + default: + return; + } + } else if (ev->state & Mod1Mask) { + switch(ksym) { + case XK_b: + movewordedge(-1); + goto draw; + case XK_f: + movewordedge(+1); + goto draw; + case XK_g: ksym = XK_Home; break; + case XK_G: ksym = XK_End; break; + case XK_h: ksym = XK_Up; break; + case XK_j: ksym = XK_Next; break; + case XK_k: ksym = XK_Prior; break; + case XK_l: ksym = XK_Down; break; + default: + return; + } + } + + switch(ksym) { + default: +insert: + if (!iscntrl(*buf)) + insert(buf, len); + break; + case XK_Delete: + if (text[cursor] == '\0') + return; + cursor = nextrune(+1); + /* fallthrough */ + case XK_BackSpace: + if (cursor == 0) + return; + insert(NULL, nextrune(-1) - cursor); + break; + case XK_End: + if (text[cursor] != '\0') { + cursor = strlen(text); + break; + } + if (next) { + /* jump to end of list and position items in reverse */ + curr = matchend; + calcoffsets(); + curr = prev; + calcoffsets(); + while (next && (curr = curr->right)) + calcoffsets(); + } + sel = matchend; + break; + case XK_Escape: + cleanup(); + exit(1); + case XK_Home: + if (sel == matches) { + cursor = 0; + break; + } + sel = curr = matches; + calcoffsets(); + break; + case XK_Left: + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Up: + if (sel && sel->left && (sel = sel->left)->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: + case XK_KP_Enter: + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + if (!(ev->state & ControlMask)) { + cleanup(); + exit(0); + } + if (sel) + sel->out = 1; + break; + case XK_Right: + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Down: + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + break; + case XK_Tab: + if (!sel) + return; + strncpy(text, sel->text, sizeof text - 1); + text[sizeof text - 1] = '\0'; + cursor = strlen(text); + match(); + break; + } + +draw: + drawmenu(); +} + +static void +paste(void) +{ + char *p, *q; + int di; + unsigned long dl; + Atom da; + + /* we have been given the current selection, now insert it into input */ + if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, + utf8, &da, &di, &dl, &dl, (unsigned char **)&p) + == Success && p) { + insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); + XFree(p); + } + drawmenu(); +} + +static void +readstdin(void) +{ + char buf[sizeof text], *p; + size_t i, imax = 0, size = 0; + unsigned int tmpmax = 0; + + /* read each line from stdin and add it to the item list */ + for (i = 0; fgets(buf, sizeof buf, stdin); i++) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %u bytes:", size); + if ((p = strchr(buf, '\n'))) + *p = '\0'; + if (!(items[i].text = strdup(buf))) + die("cannot strdup %u bytes:", strlen(buf) + 1); + items[i].out = 0; + drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); + if (tmpmax > inputw) { + inputw = tmpmax; + imax = i; + } + } + if (items) + items[i].text = NULL; + inputw = items ? TEXTW(items[imax].text) : 0; + lines = MIN(lines, i); +} + +static void +run(void) +{ + XEvent ev; + + while (!XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, None)) + continue; + switch(ev.type) { + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); + break; + case FocusIn: + /* regrab focus from parent window */ + if (ev.xfocus.window != win) + grabfocus(); + break; + case KeyPress: + keypress(&ev.xkey); + break; + case SelectionNotify: + if (ev.xselection.property == utf8) + paste(); + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dpy, win); + break; + } + } +} + +static void +setup(void) +{ + int x, y, i, j; + unsigned int du; + XSetWindowAttributes swa; + XIM xim; + Window w, dw, *dws; + XWindowAttributes wa; + XClassHint ch = {"dmenu", "dmenu"}; +#ifdef XINERAMA + XineramaScreenInfo *info; + Window pw; + int a, di, n, area = 0; +#endif + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(drw, colors[j], 2); + + clip = XInternAtom(dpy, "CLIPBOARD", False); + utf8 = XInternAtom(dpy, "UTF8_STRING", False); + + /* calculate menu geometry */ + bh = drw->fonts->h + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; +#ifdef XINERAMA + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { + XGetInputFocus(dpy, &w, &di); + if (mon >= 0 && mon < n) + i = mon; + else if (w != root && w != PointerRoot && w != None) { + /* find top-level window containing current input focus */ + do { + if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) + XFree(dws); + } while (w != root && w != pw); + /* find xinerama screen with which the window intersects most */ + if (XGetWindowAttributes(dpy, pw, &wa)) + for (j = 0; j < n; j++) + if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { + area = a; + i = j; + } + } + /* no focused window is on screen, so use pointer location instead */ + if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) + for (i = 0; i < n; i++) + if (INTERSECT(x, y, 1, 1, info[i])) + break; + + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + XFree(info); + } else +#endif + { + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + } + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + inputw = MIN(inputw, mw/3); + match(); + + /* create menu window */ + swa.override_redirect = True; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + XSetClassHint(dpy, win, &ch); + + /* open input methods */ + xim = XOpenIM(dpy, NULL, NULL, NULL); + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, NULL); + + XMapRaised(dpy, win); + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask); + if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { + for (i = 0; i < du && dws[i] != win; ++i) + XSelectInput(dpy, dws[i], FocusChangeMask); + XFree(dws); + } + grabfocus(); + } + drw_resize(drw, mw, mh); + drawmenu(); +} + +static void +usage(void) +{ + fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + XWindowAttributes wa; + int i, fast = 0; + + for (i = 1; i < argc; i++) + /* these options take no arguments */ + if (!strcmp(argv[i], "-v")) { /* prints version information */ + puts("dmenu-"VERSION); + exit(0); + } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; + } else if (i + 1 == argc) + usage(); + /* these options take one argument */ + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + lines = atoi(argv[++i]); + else if (!strcmp(argv[i], "-m")) + mon = atoi(argv[++i]); + else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ + prompt = argv[++i]; + else if (!strcmp(argv[i], "-fn")) /* font or font set */ + fonts[0] = argv[++i]; + else if (!strcmp(argv[i], "-nb")) /* normal background color */ + colors[SchemeNorm][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ + colors[SchemeNorm][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-sb")) /* selected background color */ + colors[SchemeSel][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ + colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; + else + usage(); + + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!XSetLocaleModifiers("")) + fputs("warning: no locale modifiers support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("cannot open display"); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + if (!embed || !(parentwin = strtol(embed, NULL, 0))) + parentwin = root; + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + drw = drw_create(dpy, screen, root, wa.width, wa.height); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + +#ifdef __OpenBSD__ + if (pledge("stdio rpath", NULL) == -1) + die("pledge"); +#endif + + if (fast && !isatty(0)) { + grabkeyboard(); + readstdin(); + } else { + readstdin(); + grabkeyboard(); + } + setup(); + run(); + + return 1; /* unreachable */ +} diff --git a/dmenu/dmenu.o b/dmenu/dmenu.o new file mode 100644 index 0000000000000000000000000000000000000000..7eb584dbbdc586286facc867cfdbfe0c81c94183 GIT binary patch literal 30840 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 literal 0 HcmV?d00001 diff --git a/dmenu/dmenu_path b/dmenu/dmenu_path new file mode 100644 index 0000000..3a7cda7 --- /dev/null +++ b/dmenu/dmenu_path @@ -0,0 +1,13 @@ +#!/bin/sh + +cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}" +cache="$cachedir/dmenu_run" + +[ ! -e "$cachedir" ] && mkdir -p "$cachedir" + +IFS=: +if stest -dqr -n "$cache" $PATH; then + stest -flx $PATH | sort -u | tee "$cache" +else + cat "$cache" +fi diff --git a/dmenu/dmenu_run b/dmenu/dmenu_run new file mode 100755 index 0000000..834ede5 --- /dev/null +++ b/dmenu/dmenu_run @@ -0,0 +1,2 @@ +#!/bin/sh +dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & diff --git a/dmenu/drw.c b/dmenu/drw.c new file mode 100644 index 0000000..c638323 --- /dev/null +++ b/dmenu/drw.c @@ -0,0 +1,421 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#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 = 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); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + 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) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(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 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 from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(Clr)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + char buf[1024]; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + 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)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + while (1) { + utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->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) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + 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); +} diff --git a/dmenu/drw.h b/dmenu/drw.h new file mode 100644 index 0000000..4c67419 --- /dev/null +++ b/dmenu/drw.h @@ -0,0 +1,57 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/dmenu/drw.o b/dmenu/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..4911081430974151139a01ec452260874df9b16b GIT binary patch literal 10168 zcmbtZeQ=vadVg&xiJSyUqyfy=Ac`Og2d)(oPzWD-?N|1S6U4y|F`q_}<=9|buJj5! zTmVzs;g&b%_yao3eRP;kXA0B%gB$4enjRoxLI_9hAe7!68MqJ*NNNHB4hZ3b`g``> zwKu9Yow;sC@9zHg*=L`xeRkKmFOh1gt*J3osWGlHW_t=Xj97TK9T&~G;Wth)*hJZ0 z*3S4h%(<_gI`ai@Sf)8>@pt)uL(h8l!u|!!^8gK6PCVukK?>qMHRYOT zICSECrx1~J(mdt{m}b4%vA&&+eMGhljGqQ^v2S+b;u;yuc>*1|BK-UMMp1bu;5f+sP}8zrix`V3HqU{P&tOnbX$B zoT0ej1>-=*cck8KNZ&lA7>^yR3H&{A?k3J%6yRSxM^+F~&iHTFGybOVtSi_=$-X$c z5B!o&$J-R53^Km(fmaxY)wuD=R=>?07;5;pP`E@2yV_1gOCnf7Jsg{9HBQZjkL7fP z<06J8AOmw+8xaQfmc+zfdp>jG!D$b35K-T7I}B3~M-qGw8G?6&7c;)Y;yX?Lst`0W zaTu&**Atx2~SrBQleJbzA3J@v5-*u%Do4mXyF_{XN)u1+_iBBe4li;sdykzp- zCVvf1`;0kjBO;NwJH=dQwwc$v`2bU~d%ow3eyK8vyGugd46zj<4G~ZEFcBDENt-p6 zY*; z2{M>@Ly~_>c24kD*YiE??Ea5By=39>@WZH`ABP`91v?@y?SOS7b80qt zL=?N$0>M`u_>6%R4uL%&uaD#v-V>I8&=@ZT#$SQ9e8<><(&({Kd(C)h^HtQ_KYFZW zi`J3RWBaM~AIHnsU?LTc9XL9A>_}ju5mU`)VOrzqqtoXlqKBU@O`qkGJUu$S#C*1v zhW1bU%x68+I`Y*U=T0Rj6Xn3q_pz~~p1=dY2Q1Es_|Hr6e;YNQ+Hd~si23x<T(R_vR=0TMsU-;jc(RSB~%Ad@1#WpsT1Q1YO1VY=@JK=)SS{kH$xjA1SWb zP6Aqd-}JfBz5Mg(GsQW@znNaLoy4>F)Uwp864NM6MhD>SaAgA9 z=NwZc^5p7|iWgfo{b8Tmj~-5;Q$Kw=bBc(Bdwu*Zv5}A!f6bhM(DZW_KQ{JR4fq5e zEy3YFg!J-n%VkQ{h)*9v^*zX%nnc<_a`GjjCCYk^9S)`tf6tepui$t;h^b=*QfOV9 zDI-$6s}YchKBC4%EQL9kE+!<)zbWoJv=z)5_BWF(zh*f4G6=BG|EcLE>k4M1Rv*3V z)D%COoZN7nt=>^s$jUFjWO-Rb@l&yX>@`Tm;UHF9X?_Ruh)?YGgPz)?=dQ+e<_35@ z&YUgS*ppr#ZH^QYU8j)Xwl2(y*I41ma>yzId6f3%9SBB>eb{!1K$H9QJtg#XQWD|?1Qy+k zYf;o)OJSySmvCVUw-@MONseNVx`(o&B%ytDf$*pJ`)(i{x&(no0iZ5XN&X^HoWdM& zvC!`Y(>Kqa_+k+^a8mpO9P7B-1=S6~#5y?mk%{>|22Z#`;J`Z5Rk|&N*th2~{@V0s zbSXIWx8fe~$-#TRAXx@uhi)eTZVVarLUNLO{0qPSg0@H?fd!C)yjUpWK|Q_HJuOb6 zGQ*^PzfsY+_k;(tW`0?j_SXH|;aUA3&JHVWKd=~$}oy9nZBOBYPYzHzq*6vkbZd9q?fu{1QAI+|6NL_Dl-B0v%R`aY>((z1 z#Tr&LL_;eYqp@go#bxgBQnxZ{?!Mlr841>$xp;x^VN9j_@mX^0VyavG!Li!(lpp*p zbYpVtajKjA!5`En{Phoc68_MnH{p+Lo0s%AZk?a>$Ho?z{&?-6KW6zGO@E}xA8PW~ zV?vYPN8vq6^HDa?r_~?aR%`j|w|biWp)oHtO+jf+z4WRp(^Eu;vWz|$giIuo!SDl# zC{3igkO@mY!taG4TSD(LiZ5HR?4v((Kc=* z8AcXjFtNz=`^+iVHl31YglIbb!O7Zd{Po*Bt^UwfujP-7&9nURd;Ag8A2O9WNXb3q z^+)7f6*z=FQGs;=`=!L5K%4ZPtcAYYJm9(23sZ|VYVTa=k679&iA8IH=<6mHK_DH7 zfL%SE_b4WPt|uKkNKQBpq>V{`uvM7DARk(iR_F>jw$(zXtt7`7bdA^EDVBFV*$;YK z(l4Bk&a9tA>OEH11jO^3TG4vE?iSQ0As@h* z5Ue7?+!+3&TrY~{j1GSgocyc>Jte(bN)rcFg*A-SMh*TWudfDtML(gRT6m&aSJ^Y&-oHxt_Wp)p~S@=)5iWr;uJ@ESt&a6 zjE8g(?bQ zjy{zK@SBVN2UYN&RKfqb3cejUt*h)N@-wiVRp?)r^kJEkmZ4AOE&S$^^8j$-|6NHh zayGCps?g5^-CTSYSHYK7!9!JWlAGk2l(|BkeXAt?q{LM`T2=VmAn9x678W@XI%Oq( zx5QO$w`U@|BKe530W$f4&$aGZRqzX|;8#?^Q&sR=tKdUb@bN16qgC)H zs^Gt@g72<^zf}c4Tm}EU3QqA#_WxQ2rZ_8M|I;LX;UYIc#2Ja^1;FR>OH9)DNqTWk zf<94&-ssK`H)Mr~Vl2sOU z)tXw{WTv{-wzRY-I=VW{rc|QKXvws>uLdhyu=BaiMrV)Rf|Z*4bA>D|-Id8_@kTJR zxY6UeV01Q%ep}zjKzc~^TAPi|_N<-i8_b$Eo&p<-cD5j9=d*>ryR$}T3j~*Qh^f#+ zfS4)(tciz%T1`r^b|^b|9bPv^!S2TM$LJZtGsEtI)P%OtTf^w?bLAxa5C_3p2;QI9 z*frA|$0QX5Ln0_-1{8tlW^KtsNCaL5yn3)W4d$~MTj9q3c;n^2ena2e59yLgKUJ}ns z^i=%tXB|=0ekA-f4Zj|3DukbbpQ8UhfhdHZiJ!uML?8;`bXF>y?hRB3KN~-Ve@Y+< z;Ut5?PsMparILSC!*%+rH2hr5Q+$4`;pb`izepTu&j@SyziRkm4Oed}l85f5ivMnn z{{KcOv81*9M|~xH9obZ5DLjjcP%C7$r`Tv@v18Lk2E}@ z@%d20=`ODLe<5+I0vf&&`JD>Mqpy3DhSMEi@p)Ur=}xckjl=+js!Q-w_%j-QsfHiW z@XIv(-{+x&Lc==${TkkgF~#32pFTuSZw7_mzW^N+(wAN}S5OD#p$c5lAD1}sxg0-* zul3O^S04Ivq{2t5;I;AzMSRw1^jB&)$)fmNr{VPfp9;TS;v_$v*9zaH(d+SJYdF1; z6#cy#pOZD5YxGxZ_@f#-(Zp!}U1%p@z3=eBRS=JzmX*k_E z75}`3)7xI*W15`$x*^a|q2;9rqObO=!qq!^T#U}rt9R}*6}WoOzE**&ck5YNex^0l z`!QC5tM}tA6}WmQK30LNcj8YgaP>~4cN!I?mwG4CyGX~?`;JxMGw-zuT)n>zR^aMg z;FEcqhUur?1uFk5T;2bThQj6nJG~LLopojbJ2#t)KuVd6!^Az4Au}b-S<$P9QSmi%8KaIv*{%`zimqP7xawi=< zYCh#MDkM+k+Jrwvhc`-0-Y>|Ur{GsPpLkY?2n7FwO_xfPbrpk^`qSB7K_ce8e4eO` LA-9_OR?hz)an2#- literal 0 HcmV?d00001 diff --git a/dmenu/stest b/dmenu/stest new file mode 100755 index 0000000000000000000000000000000000000000..c0bb011ec737442a2555d6ef77223636f1bcb0ad GIT binary patch literal 17696 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