From 35846befd8b14afd8d347f706329d27995c8a409 Mon Sep 17 00:00:00 2001 From: audy Date: Wed, 5 Jul 2023 10:01:53 -0600 Subject: [PATCH] added st, and patched it --- README | 8 +- dmenu/dmenu | Bin 0 -> 41392 bytes dmenu/dmenu.o | Bin 0 -> 33264 bytes dmenu/drw.o | Bin 0 -> 10640 bytes dmenu/stest | Bin 0 -> 15904 bytes dmenu/stest.o | Bin 0 -> 5328 bytes dmenu/util.o | Bin 0 -> 2312 bytes dwm/drw.o | Bin 10440 -> 10640 bytes dwm/dwm | Bin 66736 -> 64136 bytes dwm/dwm.o | Bin 56728 -> 58424 bytes dwm/util.o | Bin 2272 -> 2312 bytes st/FAQ | 253 +++ st/LEGACY | 17 + st/LICENSE | 34 + st/Makefile | 57 + st/README | 34 + st/TODO | 28 + st/arg.h | 50 + st/config.def.h | 476 ++++ st/config.h | 476 ++++ st/config.mk | 36 + st/st | Bin 0 -> 107688 bytes st/st-scrollback-ringbuffer-0.8.5.diff | 730 +++++++ st/st.1 | 177 ++ st/st.c | 2789 ++++++++++++++++++++++++ st/st.c.orig | 2670 +++++++++++++++++++++++ st/st.h | 127 ++ st/st.info | 239 ++ st/st.o | Bin 0 -> 81376 bytes st/win.h | 41 + st/x.c | 2101 ++++++++++++++++++ st/x.o | Bin 0 -> 75256 bytes 32 files changed, 10341 insertions(+), 2 deletions(-) create mode 100755 dmenu/dmenu create mode 100644 dmenu/dmenu.o create mode 100644 dmenu/drw.o create mode 100755 dmenu/stest create mode 100644 dmenu/stest.o create mode 100644 dmenu/util.o create mode 100644 st/FAQ create mode 100644 st/LEGACY create mode 100644 st/LICENSE create mode 100644 st/Makefile create mode 100644 st/README create mode 100644 st/TODO create mode 100644 st/arg.h create mode 100644 st/config.def.h create mode 100644 st/config.h create mode 100644 st/config.mk create mode 100755 st/st create mode 100644 st/st-scrollback-ringbuffer-0.8.5.diff create mode 100644 st/st.1 create mode 100644 st/st.c create mode 100644 st/st.c.orig create mode 100644 st/st.h create mode 100644 st/st.info create mode 100644 st/st.o create mode 100644 st/win.h create mode 100644 st/x.c create mode 100644 st/x.o diff --git a/README b/README index bc48604..8c89a35 100644 --- a/README +++ b/README @@ -1,2 +1,6 @@ -change the colors, add keybinds, etc (there is no etc, those are the only two things i did) -i left the copies built for my machine in there - which might work on yours - but you should really just learn how to build it properly youself \ No newline at end of file +dwm: + change the colors, add keybinds, etc (there is no etc, those are the only two things i did) + i left the copies built for my machine in there - which might work on yours - but you should really just learn how to build it properly youself +st: + tried applying the patches for mousewheel-controlled scrollback, but they will require a little coersion + for now i will settle with the ringbuffer scrollback patch diff --git a/dmenu/dmenu b/dmenu/dmenu new file mode 100755 index 0000000000000000000000000000000000000000..ca43ac95b88a2363f9ea986816a9ad4bcc803b51 GIT binary patch literal 41392 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%0A$y|2Psy`%y;ML;bb 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 literal 0 HcmV?d00001 diff --git a/dmenu/dmenu.o b/dmenu/dmenu.o new file mode 100644 index 0000000000000000000000000000000000000000..764d21265ab639e08c89cc2dbf94379f3675b007 GIT binary patch literal 33264 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 literal 0 HcmV?d00001 diff --git a/dmenu/drw.o b/dmenu/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..52dbaf5c13e8bdbe2a8c41bdb3a4c1a20b0382c4 GIT binary patch literal 10640 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 BfxG|! literal 0 HcmV?d00001 diff --git a/dmenu/stest b/dmenu/stest new file mode 100755 index 0000000000000000000000000000000000000000..86cc93a1bfa4df473e81d80be97def62287b4e97 GIT binary patch literal 15904 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 literal 0 HcmV?d00001 diff --git a/dmenu/stest.o b/dmenu/stest.o new file mode 100644 index 0000000000000000000000000000000000000000..bf698b8c3a52d33db409cf92cecb42eeecaffb9f GIT binary patch literal 5328 zcmbW4U5r~t6@bU~{z{yvPyV%X*`>n?TTdcaw~ZgSLciDka-puYLDN z@!GC^o!wR~-O6^rah+BWXn}{M>Pstxv?@Q11PZQ_v=O2x{5Vd*;l&$WKE4IV^@65_ONIUGapmg_~X3t zAnN11ycg?Wm6tz(jlbg0FWl6c;H7^a;-&xa=RX{R3F5rm(HDSDUMlzl0AO6j^n1mq zV%6*pR@dGEoEJ`6u50C4rkj70s_83_`L#ZFgR#AAnz7t(T6Jj9ajsRzFm#+D&pl+Z zv$iF8lb8NPOZdy$a=l)+O|-(8T8smu5j{7@{P%UJh4CiKHQep11$pJi{#O`2BRN%z ze)L)JuL73qKZ|bf23)(y%bma(-#zA^R!{x=Sn~Wc{voEGDmO=&;=#OXVX4V-ee$W#VS1ST=do%!Q%_gbpH^WG z-0&tgf4Q~`eC!sFW!_JL>kigMl;0KH@^oDg4za;>i1W`{`C)ci{P~NRHb*8efPb6h zve7wXkJgvfj`T-oVsT@3Za$HIGLy|6Us|37nABz$5(c(X)Iy-^`s4=oKO*qT`hZfv8ons!SYvz`qkofXq+5(6@IH0t|GhLpWC#u>}I(hp1VHmoWJN@xt-({v9?I*`oY*ud6c=j?}jji)rxH$ zEp@z=>lPbd3xKKUtX=0;9IX9;GkX?X1goR(cD#i<8m@Qv*TRt2FjwWM-@A4P5+P!} zEMPH<@-@LL&p@Q$==&@`WmJpRuieWlW8L+Yp$$3DI}indOCMV^U$m;HMHMbx0(*0s z)&)v#M?(>TG{*O(t*eYJ{V-Dc zGm+X^7|Uq^uLrBrBLs+-Py2EEA!WCUS6VY=1AvcAhK`0NLr;XJF0ccmqkFW#0V8Rq zQW}qD;zrVlYr#Q%mmZu-4-O3O8hCKXVNR)zqFlmM84`H12hQM}Soiwd8wgZ;hUq6=a}Prts`BcipQ z-36-KW-p!{7~}(pI4hz>KNvr>Cp!@jVD~_Af&Xba^}HOgLvl^(FUk6T$*T_j8Obk8 zKe)dn9@a(8M*VCpkqEc1>bO+Z1JJcj{tV@!My znI0J(U`W_(B09&C#u7^<;%qT$#^$jzh(CicC~jmjESi}+KEP(vnT4b|%Z%j(lSN}O zBb#N3tQj>~I%j6t)D*O1$EITQ$EIeZ3kh(xkYs5XpUQxvOl%&jIIuaxOe`b~Hk&aF zmP#AR_(FzdjA$I|SR$1*u#rv5`QV9~CktN@c*^eJhXdqp>;yrsKq0;jAqa9jo5Z&x z1i^EH`9-`BAqetKP>4T@5Cr*VD8w_!+6c=HR6NL~>^wt~l&}AoZ&b{v)Yhb8vBGffc%Ofgn!* z_q5>w$?3UPrQZPu|GwmVY<}!{IP9>mO8bb-9{*O+zQk=D&jRuLaPc76jd^=i`k_2W zZa*)7038K67V>iqAqe@Qe7$Yq2B$lMN6v$Bv!5f)#(SZU{8PT6|BW`T+wAchO7`>| zXpip%;&h&nf6~T}+x*}=f$Z)1hGXcx@T$!opNZ^WxA9NexVQ|1j?atVbh5u>v;VY> z|4wq86ZhroQom!f*KGC*8iHVDU+4BVywAqb{{`tcBss3ro+ldUDA+Acg#HEb+vIew zq5BKk_DCC1>Di|i&FBo%;S*rh^VG^1iKuQG%O=y~Q8UW)8Tg{;nG`B$9@OD8)Gz`P z&6v3~(_^W{MYtj}J()5M9d5b(;>#oTxnxdHXW+AvF`tsdjrpnBOmxw(uj~J8YcLK& z=e$`B14GH!g9W+_v*^Pg%r%VlFciCdV}qq}+#C?hVH`{MDm)t~)_t#epSLJuZ#w2B z?wlX*f9O+ALuRZh$N$PPKxU&_Qjo!TJq3-p;V?v`ql(hT`0= zRXHB-y>_AX6DvRieLBZeLavgbtexZW9@{E@x2!)fjLUHtKRm}Yp7O8d++epVH?&vo sBi_ZZILF@w%$a+D*k$Yh`I%KUex32dZ(^(Q!+4nkIb~IiU+4J$0u^<0_y7O^ literal 0 HcmV?d00001 diff --git a/dmenu/util.o b/dmenu/util.o new file mode 100644 index 0000000000000000000000000000000000000000..e39c878d6cfb484e2136ca6aa056d5cd61ca7573 GIT binary patch literal 2312 zcmbuAO=uHQ5P&Cb)TnLKiihGKD?FtEU6P1e>JPz2`;;J6E25VgldTrhByL`${(y=K zhEnh%c<|ss@Z!Nk4;}=A9(wU0c<~@wJcytNMNd_zGrMo>W7&e}z{|{h^RsW?&*sYB z+`g7b1Xv=l8|FQx0Jj#++ZoTyKnJwL_xQTmBeL;RUil6 zCWn^1(Nx>xiL4lmCvxIsJTWAy@x(oG6>H*&2jaG;ABp>(o)V8e{Zuqj7v)5BLk}7f zyLG3Ua$*-c=zWllcXINEm_ctkDq>2lq!x)t0{bpcDMp>t+<#LbU6JhT7P7M9ae(4L!;(N33MCyoVNSd9T% zzYHg6{X~CoEM2f1Ihp>w^}ipKx>0lfJ`p216nv16jhpR*O{l2nzt*R)mL8SHVG2K= z=gTpk7V8WK$WC&C*2i@MpBoNy9qauVV4HJD-wA^uxyO;wy)@4%x^P$550Ya@@O{o< zj8cR3pZ>DGgtcM&|Art6uM6!z-FtpMeRoO1o=v=0PpDVVgYGo74AxW6;NByGs)l>W YF+{ifL+F1YtUjjyS}y&`^o?NsZwk91ivR!s literal 0 HcmV?d00001 diff --git a/dwm/drw.o b/dwm/drw.o index 2343487d57374e1bada6a6d56fed0a0a3bbf1ad5..52dbaf5c13e8bdbe2a8c41bdb3a4c1a20b0382c4 100644 GIT binary patch literal 10640 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 BfxG|! literal 10440 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 Q{-mF?nas0x>+AmeA6vCHX#fBK diff --git a/dwm/dwm b/dwm/dwm index 7e814d0a7d836fd9990323cff83ea7ee51a49e64..67654edf8f0323ce69ed102a9dac04abec21b781 100755 GIT binary patch literal 64136 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 literal 66736 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 KH~6*n|9=4S(W1Wq literal 56728 zcmeIbdwf*Yxj(#f5hLPEtZ1oX9c!$KB4!XZQLGs$t8HRL5nDv9=J$QpdY;K*$v)@ze%?Rc z_m8tanb~`P*R#IQvz~QdduPuz6^TjNSy_&REa!X9(3PN$GwQXW^D=c><{af5>A3!y zRgibPs%l-otkx~vmpsz#>`vypUF%yZ!e2#)fxkgeU3=L*uKy#I#-G^acAej3rdive z)6hYsQP*CUTYAO()EJjifc!YuUrP-B>39A0D)sqge$DsY6V_AeUdY~ zH=}RHvOab@w1(PSt<{@6-kUxl zdAz^8rs&0(*NuzLy{TLnkQetRkM7)?%+BhCP|v4tc(X>sp~_abb4T)c_^6ih{SCKt z<66qt;YPk^v<|!x?e4)}mD_bLWh&a;M~C#b!P!0G2d70=eU=kmody!Cndy4}j8*xQ zA`8yVJ2`ywQwz>LDf5 zre7F*gbG=)b#`B~s}?>{#feOO>H4!;QOQzAx}NgWz&Bmr1=61!?t0tctkg#Xr@CHM ztLvva*g(bfwQKLAi~Gqy*Bg0aD~{nqtDabo#H<=TRZ& z%o*G{r^iiy(1hld2PF?XPPP->ki_Yi4EvOL!5tr-QtUo(Q> zAF1y(qZLAmy&Ki{nutP1;ydoGp@X-kKtW^;mxNbSFyfKppc}@bS4kA03B(IV;xmff z@loIBUkwgQ&DtwA7GK=65j0Nc_Uf3oBRp+eMQ3+BJgr;jHLJS$$JP0WA=?!G45;E=BcjnAxSZTIB0uczp@^`%V_*(t9OmCaxs7|zXY~~Q84=_C*%RVffAHRjm%c=?M2@I8K;1PmnuSi{rSh(k z`LXcSPqKPrrJE|kQ(x4z843!C%D+P&ZX82)l_Fb-?$S-+#5NRc86daxspPPD(d!jO z+dc{BuWfCi2fmHym%^v@E;|y@J&5i`v}$2{;^8sfP?%zc{vE}j?|d_P zu($2DProR7(Z6H71`dgP-9TdKBG;4vif6rEdLx+|o~jNNS=-{JYvL3Wo~n+h;s+sL z)3^C+CJ;w=@?e!)^tGzOrH+o5s>+o*yi}E5>JTuHKa)_lV^05tUk(n=QFS%Cy?Tc0 zPgZ^72{Hn-VFdUsj{xbxp&+7J5#YLN9McsYNVT0>G+6t+Gr)$yM$7aPYjLnSI zpi7(?-Bpd|JIeL1sqH&|h&A8!H&TMZr~AeO9T@1`6<&QbfS8ZEU)t9`x+1y@+K;Bv zLxaZQqIg06CyDTwzJY@({3Qh?XtE=y$yP)ME24Yd&NsuWU&p2Ilesa!reGv-O^Eqd z6^xJhXz*Fv&p}gLHa%XnyY&4EZy-GRkFlaZAWJc?H@YK!3ix85?v8bh{2p>zv@Pzv zj*+SFwto!{`Zp;Tq{;DJxmEG3-LZ2(9S=|3Jx~=d+K0yC^$9_RKewQyqG$j_=f$%= zB>tj5n2eJT#JEow_pvMcM$@FulZKIV?W z?W!rrt8#rc?6}^GE_%fg7zW|Csw(L-N~B-QcT3ekg-J(O5=y$RD#+`*7i!@2Z^2t5 z;I_PnkzYp-JkNZJQaY0d9jX^2+|s6wWC12ctpgvqs64Y^uj@UP5!sPE*!8bl1qxW6 zk6M8mF_SBUQmt(2><+K~h-CD*sf>y4Qc;!O(;2evBs$)**1@L-K5^@>Dj4wu6@Bzg zC71Btj}BIPn@fSOoZ{AFX0*xmo_7bgyO>BE>n{Fy1bVF8MAqNprI+NU-o-^8&hk=k z3>@h$?jGU#!v}j3Svy@X&p$uUbMpqby6LAz4E#FbeS})5ba-N1oFjNrg+N6r_fiRu`rYDlH$AJUQ-Fk3v0)Jq~WiYcKZN zBi@wp-jo8by2Pt4^vWlA~C9bDz zXULez2CT)TK;~W0c#q1j92roqTftC-=Es z+GLtJ~9_bE3(pPpk;ZBupwzXcRjr-*#{*=+~rkw}7IcvyA zes%H8rmW#K0;;kaY%SvkeVOBzl~i5E6BMRNZ!UJdEv{FNSsgntU%A@#+W`%=GUM9y z{=y=2TKj9MU%7TO;BqF~DDT=oA{T_e6)HMf8g(n#TtEMRS_ zC9$G}pWB=8wj?t1t{mmj?~f>;!t~3_UV4uu{hG9U{QGJ+Rl}$It#0?z-t6>A1WQzQZu|7J*zxbf!!r`-Mj~-c{zk{DIM;rnU{uW8q$n=Bq;pBZ zh-3xa!yYA?k*TAQ_4$YkERKez^0qq3NXS{rPb+Qc3D8GZpHwv`Vg9x*&DtF2j6pFWb!ts&7LE@<08O zMnjzme@aPgcFfxnn-eeUFWOY9>U7L|F5LBBP>FUiA0^4;%00=&(H)#!EXd%`Uc;ZH zvVX9BK5K>k+gP)yll&8_ja?+m;>mG;Z4JCcZkYwI5X(Dp@AH9(>+1}~)G&t`fVcO8 z40r!!m@O%r#D{27)L^6AAt}obC4dg3aA;sw>!)hzIWSEOq0jES3u8X@$^*OR^i;cO zQ`9+ZBPaN2>OcLeGC#iFJt0j6n!LilDUU#())lV*z*=<}vL=%#?)s~HS1Dh1%PV&_BqpCAX75F~&SN=`JM0Lq~+kb$#xVA|3(Qkv(S#XG)IImRSZ~6wPDceAnM#XWlQTT=!@K zD?*$*csw;Q@N&j=roZ3X-^Z@k|HypUph_MIlm*J32UwNsy`d(}N&@TaK6Ij#Z|X#A zV%|TLFT97SC*t*@S(&K$p6mKpC_n9by{q2+s-pAVE}qNfxGm z+S{~!pp;w?z`s!TOlT5?_0sJ5sT0GG?96LF4*KluzE4_JUIq?xH|@(y@67VjWPG%H zZ-tYUJS5tkzB)2db=>Aht-63dYXiY1ax zDBK&-kr);qD3M4yp+LOQQBzPzI<}K!O+l6G-Bc!$bVA|YRmpK!IwlIr@EtFRV{N!e zT_-*XAD$*-74B;M@HDMAm5~%h!B3M?6!W+J^aOo25*3N~-BJ|?UxpuqJE?0z2Qq&S zDvs-Yg(e!?Nd0kidu94r%=tekmV(s1k#Zra&=Y(5E>x3z{f+zpajRx1pM$sxK7W?#bZ<&_moic$Nb7M{_n}eiCrVJvbL1Ivhq}7DP$Hk?rWCM zye-%#h<*B*x2fpkc-AJbi;}_EoMfnM)!#>?@@Vn&tzG2sPV$lr1)Wv6NTz%x@83eo zUH|uMaftg+N(Ys1vyT{`rclrGLoov0^H{t>6otV(ao{nb5(py9`-7Y0uo#?tpOjFe^1|PXEa;8{N3t`cf?g4TZAf zp%y4o*YBco$2>)4NqJpoDKIaXw48Ps%3md{h;~Q!UgpFerge|$i_08Y7^XKzTwQ}I zy}u>8?xR|sJ|{QzJrtu~o$I&f>C~VqKXl~JFLNpfH#0lCjJPh#(vAdMW%^&asi$={ z8c6+aKM!!L=p0=6jf6k36&nbt(-OK&2EHqewX`=C!e#>P-k@Hl{xL8@t`PH&gxB4Z zzNW-U9gT_1HIcqcs2{}sKh@yb{fj>y9IWlZ@Pt-#th&*0O;g|F{~(c%`X2kcI$qqL zR(k=e8o8ajl4t@~H}(C9r2M6=10T44CJIyWkNQ?qI*Qq)XDAbhMril=L&Q=A{nU`+BL2spQRU4`R zfhK^;l+-QsTO)o|LEJAJ(|0wjyol*9;rZGeHTR<-`A101-PUC^tyd;dJy8Z8$Tm_> zlmDC4i2A@Nvc`|#TU)EGp$uSQpvF#Rny1zco?0<@%4|=qC*q!7J-qri^5vtekgj(2 zoc?Om^f^5jVm6@)v*KOEq9m77iTbfZ_Z+p>OJVM-7k#C%GIUhu#9Z1fh?S^r$xZLe zN*;+elHQk{It;hIo+NKNbi?hU{seWX%z{pVSvuw?N2Z^gOJb=*70K-W6v{tylg#xy zRL{GR$x@f3pQOCfis?z3fjP-Dx2_?C&Z($azm;S#k0~k?UheXnXQ_?-lo)b)T+63f!;5%p7r}NXA?JDuDxs zruSrdDotvqpSeS}n(vV5)eWXr^HG{xsg1mim1S*z>_a$)ZU_%1ahE2e*uPnk>V?)s z+#12eHUBWE!R?W4%MSh|Tst{`Aor8-+)aDJ`Ks>V&d0U|;nQARJ`&$ba`|vfdMD@i zFM}f7*TpcV=wrTg{z6S3H{J~UQ`}Pw(HziVW$~0TQxfLLeuqxZJrP*T*blWAMhM2nnbp`(C})SfYB(ezd3 zIdaqMBt-Q^FlM8`ju8LrhXypM&P{Fqd+!epqC{2QQ-jcxG>TIe|AOuasgb3}8G1so z(!6ig)o9^Z<9@`{dbxbDx^Kh%G&&mYs=e{pGtT%g<7w~3%djyDzgKCCu+gNJiF;(F zn1?(>w_|Ka2}Q(-G%*HltPpVnh-gNuZ|oxLIX15jF&%}wsA}aU{LIQQqF07kl z<(i0lj;cVZZ(wV%@TAnCuy7P=;zYPJePu~jc=huj@B8pi)YmBL{3nuPr}1DJSrVcdY`9z5ITWd+A=?NP~TmBgy>G8 zw7gI+`tG37Ol6T;Huelv51jlK?zWIFG;!a>Hq~19gf+AlOclER>?Z7E(epui*N~&t zt|2`ir1tR!u$`q>8YjA?Z>K<~gxSL66uvt$FcHhUU(!AuCYg8!NZ0TG%ML=hY@jjy zs2X*X!!ce{V^*`bxBI?$gL131>Pp~OHDRBnLuIPAsqZy4In&XtDQfm|7U27;z+*=qxz2IDgJfjp72BA(rw9aN4H%V9rP5^_j7WvRuHT0|2^i3 zSiP`X3$@@j%tei!9UVktY@vx2x>L2$ zxm)iDV3#W4|3bBoXJ12;`vPBh7$w}a`C;(YMf^GrF*mPOaOp$xsQ0Xhwj6v&dNrWmRu_OJX*uKFwT z=5*J;7;b$tS>|@Bz2MA#KuqoP$gaS{$0N62pL^G$S(3YF4?L=t9rx4y&?4G)$L^f! z19`$+*f;WZps1CR^oo2m|E;u^Q4hIA_g>WBgRwrkcXt13)V10kS}qm#{S}U;1X$Q? z<%w^jil&KI!k-*T4NG5Blbt#w!QE5cOx*shzM$`ylu-4d^xWXBRCEJ*eLLSKG}A^( zUrXXivX1$Rx$hcv$5d4|>~+E~hr8!2o}WB)Hfk5@#o6ddRK1WERtoJGR{P5nLr)R- z*SLMxzPi7To@62A0K>a#nA~4<%b&T#=$i>WA*l4ne`HoldH|(N-!04P ziIx5-eBDFT1_!ZF{xU!HV*ew!>YId|K2D@49I~dU@2H`A`Zz5j`?d`&CZao@pshz; z`ScP#fhRNacoAQEO6ok+PPE-ks%5EJIFmy~K4c z+Txe{P++>5*7hH^+w7-RA0J-*3M@=NqI$J(=XSa{&slkz>pwzG3!`Uv^*Vt3fSS)w zzW#{Dg;c%kKdJh=*D(37MXBTAF)SXx{)DX?uE6R>-!XmzoGC2YzWyYwA%#*+y+t#p zbE^8z9?HV({@t|l<%cJZOycqRXrn#j%TAk$S)&6;X|DjfMnTXP!fi|o0Fec6-8pv0`2mrKAfCFtsW7835)X(?rP-8pS61wr7 znaQJ}tBLv8mv!sJ#6){{6%|vZryiQ_pNw!id1@feuVZu3Wbhm zO0lC&C$XXBI8KR`f~ueizX9!-S20q|yRi?KTvZ0C6-Qmt8+7Zmf?3|yYAuqzJ6t+=N-U-yjjix+RAs#~;!i8=8hH#Jz0S}2tRibutaSUz z;c-0K=V^gy*pHet2I5o`VmUve;*Mo)i)Fo7QMzg6a6If-p=(L3^hsjGQ=R(gslz$b zMyDeBmKJV;?DH=y&e~Vnd(~k%7nYRvUOh~20mMo*JD!syEqW^8eTXt7!|{h2p=uP2 zB7ub0k0LLm2wes;sngZY`_NqeQt>_V5|$=7o{Hu}$MgTHj}K#(#ne!GDSB$X?2PUu zsVL@}G1)0Pl4~Q2E4>3_Xi=N*sQwe1Ro#$Q+euhEz}>Xx#^sN~d6El`<4gGX>IA*|laZkjt|4!VqOjuIJr9T~ur?3XV#wLYMl` zjeBK(rC!hJ@x30Kt+;!k8o))BrK%!f38wp<>#*2?G*zdBcg_61MR3gl9D=TVoSxaZ z#sH(1cywn`KW=Jz5Y3=K9tAtb^{*`K%kNdaL+UUdW9IaK8#4>+eeh6Mg&T}F^D__h z_`njZn`$wae>3y+LkjcYj@rJro(EBB57s}3Ln8HnYTp(@F~xym+%$T-Z!}%=0tueS zDn)tTtF7&8Qfm*jxjCo5qhIy3ZmDWg$$E6P|5SdX%vJSWs(S3V`<{CabJ~C5%daSN zz5B>t(Lq1n>3V97=K2*-k=BJLkkv-<>}dgXMRm zcTxkz$SMpJS72G0@Ls?aY)gL~`iacM2+Q?rKX`4Z;kbSae1vCgH(`>&tAlXo?{Lu< z|1+#rw_hJlAOStyqV8-;lT*_-eOH9KMKuEv)+gT=0#;w$BgO`Yx@VE_ zZ=vdX0bTC$Z=njw>^#59dTQ{Q89AN#s(S4h+?ILJjqYdYrl06)D9C>zu__-Gke^_~ z4aNNQz7ffzuNj`+H!OKve=*Xh_l-nN*!RtF=XVs_-(!O#xAq^URMKrh@3llxAGQec z!`IWl1!+U^;8GQ39Mh^(a91>|8@mKlc14@Q?lTodyZx2Lc+#1(j%VSYigzu_@~evD zSs%p)yGvgUuUik-w8jt$DlO~Tom{EPEpReE!tbuo8A|><_J7$eu}!cEzru-ql0l2 z(1^Z+QFkaSct}+>W?NnI2V%+EO7Bn9fgKd?{2`VN+85Eiy4bkTW<;2!LQVDk(+rE9TJ8=9-{pq6&+ zW~$pp7b6P-Rqv^9<64uc_dhV}{c5WBi8qnse<#IM^7l{yU?r!tYv=1aPj^+$0HLE= z#~@bAYLKz&{5UYuv&*B^v!Z?TcF@LPc=a)WaqBUmZ*pd8GL{%zlRwwv7ix0EsL7H3 zHLzx=Ca=I12Bg?v&C^c*61uNW6)-cZ{Z zd=_ovlDN0~6xDlqn_y;iThTUZA4hq;r>F-;OmtV1U<5hiTENTfy>B=@Vsq_gDu1IMJH@z0Ml+#d`O>o^ zbzeBp;&M@Yq;5%DV_n0FNL#A8c~SF1M~N?}OV%xkG`6+1 zw4EJkYrH(w*q)5kw=^`Kdt$>Fjmzb5r@pTFq-11Kb34*3iBwlkE_W00NK;*N!;;3f zvz<97He5`snz^x~v3^-=9!lEwytPd#(&IOo)5PO2SNogD!ZnRDv0iydcq zT^kv7cBHu_vZSTHZb@ULJ=NOU(v}>q>7W}qi?p;hHb)v3wYM&*Tj4-E+WC)pQZBNL18R%$rp?Ek1n~ z0o66JnhH9piA|}PH)(qL%xY)ew2GQ}vGSVA3p0`ms-9U@HGM`+MO;fD(X`mqip+(k zG9_WbJV=$h73EX*7n?U}W+G8tKBJ;yS|&mpHLvEPsv%ATn-{N)C8kf-;^m3TifJ|T z5|!08PJ4Y@OVT;%#8bw$HZMFWa_Z%gXmN4m)b_}!S42*2jGVM|S)^b=WaVj)Yiw+8 zZ)!=lpLWvM<^Mb3_N3FWw6Qrg5B0}6b*Xde0vwtz#b<$YYCDeY3s5`Hb-p*@d(NDx z7dw*|Ep2rsEoy6A(9(gb8g<4wr#s`FGn_M>vz!vA)QLG0opLAcR5+8Ir7g`Z?X7k7 zjc2znx}x#iXtBf3M$p`3Cu(?lFqz{j*6jaER(xG0FQ2rli!Ec+;B_;GbPo7p`&hTzBo*yrJDUJr32c*-5E~9?^IG zR()@2|IT1PPR+yJgczVfmkIGym{wpiSLOTeDD=jo`5!s;KWxN#OBiMb62B}@OQyTC%_MB-j{SUUfplw!R7-wEct71%n*WWHg{i7(cyx_7Fuv~m zC*tJW1K=G2_;?F{A`ifmm}ne`<&hl1MLC{JP?gRs&;2EG_;!Mp@bF)_dMi?0b28Q1 z=y^O$msIxlpDyk9FcPMcYE_%rGRK5YuX(X-Q;GL8N$<~G1}oH3S9aFHlJMRUZ5#8D zCv6KRn}}m+(xGQ+$3tL$En1xEvEyyis|RQ@3L+T=T2OkJq16aIX&#~d*-^Bqrtcu~ z{LA%Tf5dUAy(nWOo46s5J&UHKGaKc!x?t^)ikgC^{biN0>Pz|#+!xQE>g~o(*#43U zb;Dft!Bi5Oc76r&=q}o58gH@$fC&zzOiee`^51nzhm+tJ<0o3Obs*gLR}{^^4@AaIxcO5D30On%72 z!T>g@)O|&!Qunp?UpbNZ!HRzXw<0(vczohd(igk#vl0 zX;wszQ<4G^=Vd`&U0Ry*|Ho^G--dyJbq-BXe0f5|q0+u;T6XO;-GK&n6xxNcfk0 zJzg~tiT|JSe@K9T#DDb`zg!A`QUL2H@e3?mE+u}c0M=3BBNi@~VqZ|aq#q**_-Olg ztQ9ZiBm6<>B|a#<=%;nD8H9fXgF*4a9~7^;U~mM*i~gvo9Ou$si2k5>(H|5q`h(&{ z|2RuO^=lMFe^9*W4~iH4LGhyhbgeG)Ex70piWmJs@uEK{Ui8qc*9`f5A^L;jMSoDd z=nsk){b~p`42W3zgW^ShP`v05iWmK78oKGUjISmCLGhwLC|>jj#f$#4Sf3nLS^9(G zMSoDd=nsk){UxGUj@$uA{)6I0e^9*W4~iH4rJ`4k+yRLGpm@%^3l8vh%zj-$vm-&s(6vhvzxYs`H%5<>hBb3geBJE~;zBX7AW>W22{@ zS;S|NamC}J#ZhddQW&Yev97sdhLhEipLNWLgY(v-uF<$Sn&V_lBTT1q9GOQFMmtXw zWbQ#&twJ2`3!#Igbn96L8Bu|-yZOABkA!XH zb6L+&KAvSvC z!gUA5L&et*D-V^V4~~bbawcbmN@AhnSf~&ZkVFc&5J@BW%x5k&F2+NV>QG@-sCW{k zD63>&(rr`e4EbBeiqfjr3zO_ST21z&cRgH%0KX6I_Lb*`t0m|LoUG08OIsJvT6+u zAFRucQ#*p!)4AEzXi0HZ@)gvMK&2`Hwb=$v=i%JYf!dJWfm9HMF_H?W5s2N&X(q8< zY1H@DoC#S<7V*UU7|UFVb1IW{*%yY&E)KbqLRFJOwUa{g;n7&AwK7z8NhrTU=XW=! z`7YH9m0#o`okR8b=xnhCQN%x5?c+JlA8@Yuk+JI`i~OD++MSjC+fW3lC9A59RdE`% z525&5LZjSJUE)R{|E^q&ol*c4FI zhg^6J-)P0viVKwh!bpTtr+Qb*T;C)v_#vG=J<}V_1=PXu0~CKCT`(~{Y!Y;phumqQ zs+v$Oni7h#IpkJ{swT?i*R|_$O#BFB(+7+lPrzi14;hPe{D^}o0#De}mnyr9 zmBccX+5|vjIDTfPPA;N4AIZL)ia6g@!WV=_oe$`0U6(Q%B1pf4)B89l{_KlXGz|b$ z`}1hrSkE#YB(oxvzkvJ5dl~0(AwPS8q|*7_#_Bt}pv7$t(H#W=?Y|)id;|7|RQ?U@_H?4ID@H(U~90zmV~5 zjH~?*OsHluzLRmaE}t99uK`YFwvX|5h-U_snezK2l-VONtURd9DDC*+l*n<;C;L?X z)i|K~MJl)tL$%XF^XG+XU23XwYWSSZe;gVR7i?|Vol0v+;hx2vg0p~qxjHmG; ziuo0$F?LTHJCw?)mbuitaaJh*VlIzX#s^4I_J&aYR6X9UWqcyxn4_k%7Z2~@ej(PQ z=hOGI%pJLqf#1JopX%o;X%3PIt;))ID<`xzD;8Rxl|3CNE+Ay+02ZtE&l?aGq+hk~ zt217Y04n78>nR>S$2_9JG_VkGf@PMH%=uIIT+JCW<1;h{2CDkG&c|xHYzyTC z-2KW4xT};SlS_k$vYT!2sD1!gh;g}Iz-f+V{KtGPYs>=1|G+pm4W|g#S+Iwi2n9J( zI@0@!LeUTL@f;kpAhnUNdAPviM;h<8a2hv>N6kxdF6-19oD^u@3(Ie0dFo>*P#;J<&oZvoxWKN%hwNF)xa6yg<>yho zMo|3|jkD`?#4OEOVQ6(v5xa!IA%HA$>Ln{`yk8D=8T@q z@|zf6&A6K10Hbmx{?8b{hvge#0O3Dn2dnii;$Fjt?7WlnEB=3z@!Ku@&%lZ29-b4b zc@MC^F+QFuDuP-A<2Zm1$=}8fpmC7GUVKRJ26Z*$pD%Dt@-MQyTH6B4g&l;S&*QIJ z(*h4OzKbieG{6&49>jmQWk0>+n(&=SOM%8V3db|g2i%XU^(ycpmfxnNhU`2X`6nLo z9|bnop}7a)-8ibi$I}%)T-6K0SE)$7A42l7e?yNYx2XH`I17`A&tDn8=v!KTlRl`uBhq^` zb;t;u4d-)~k1|f}gu++Av&0Bo&&M@|VW`icA&&l^hwy{HqXo}r`R9OV;qLH6zCM_bFSGmwEMLa>`;7m9aoMo` zlJP;t>sUU7ERo*%Cul(0^$%HoVS&cgS_r4dvi!~Lc(o1!egfmYjH`Ji@RJzN;RbZ3 zrgO$IUds48j8A0z=#w;{);Ktw!gxcW#>cb#EXHqToQv6MWc(q>R=Pd<#EX;UhBhDjwdP z0>&e(Q0y7YxabqSgmJM?@EGHAEk}~eG3IrWZVJnb-TTY)Bt(SynB(x`=W95v0=^z{ z)Na;+aGnepQ8QtWbAb8tY{pMBWa)Gs zF9Yxg1MtlO`1Sz&g#i4O0Q^q@_`3mke*pe@06rWOgaeiDF#-4)0eD3Ko(RC}0`MgP z_=*60O#psF0REEz{O1AqqXGD~0Q`>u_|5?QBjDdK)(dLC9^>Xe0_494zz+%-zYYz+ zj|spF1Mt%W@JRvqi~zhY0B;SzR|nv?2jIU5z<(QnKN*12{muim3;KufvGqxlRw%~C)r@e6r9q?&)QmqYj$;M0T+FD!M zolBb>{KtHbr3O|X zu{X4?aPWWDAg3$*qk2m!+3wV}wbeB*R8W0O^MXYSQ*Dh-TO<9yGb?Rh?zA_le}x8_ zL-}wPHYVFzT9U2FHiwSdMq<^*FKldCI+Txgr#{uz-qPkYwAC#~zHkI%5Os3ivk_)tk5|E@fAiy$RI&`*lL0x^)S=3zL3=5KtZ7fWFT)b$>5-FqBy5%Tg z`bYTsrFf+Qov89?)_H_q$l&^Q}L6>6dOZaEb8qJ$rY`Q4n;P$C0DSEbQV$GZ2z)Fjmw8hhlNteBfS^Gp_5hy z8}RCfwie-m*BaY6W+}YiLa$$-GC*!&nJxugd@AR#8x2sMl0Vu~OBz+_NqtyeH#X{A z(SqWqnn5D4QAw&Mu(TzGGUQiDK*usP6hhSd9bo^GmbP}(wfd&U`o(Cd4vbq$1)yGh z5Y>lqx;Q8arI?3Gv?7{0J6)aCuc&LzK;tuKN$f=F%qUXy-ynX{M5e#k3_2|oLt5ISVsx8-+OV=ey zt5-BbvYEBiUXW^RTT#^lm1se=i<%qT>Xz13w#Vv|i;9~EYF+D$x<&1HhX`y*mM^JmZ+D@BI=FdgIqj$xOB>adsxOVnd1yJy z7d0d?JU~QsfpkIxfkKdXr*wF=!GmbEjVdk zvDB$W`l&4qixwme02jtt<$3tHM1 zqjRQ?n+gy$Qok?8L0<++Tw(ks4cciFTiQ^YsiH1vOd7s{D{9c)4moS-rRH@_3tc4^ z;oT*wmQ^Rw7#6B)RBlzbi2(z1I}H<)kQb>|dSrHLV`vJ?RXspSL3pV0fdPMHVwdgR>Au88rn#U~6Ju(+Yebb(jn5A{-@5o%dPX)e0%f#Dp(5`7x zQCqEFlGC2hXzg^oA1BVQ$Dtap=OOzw4TfqfRSzGd5m`y5+Dz&8`lWKoO|u#=bXk&V zFuIn0J%g%g*Qgdf4ZhbcNcd6PxS8rEPvtMxI?i*vn}yp#*xzy-yvs|4w+x)N(0<3) zf46Yir<}q2WyC}Ba$j1WlMr0wA2Z}V$ctY2hjikgJyXF?CjtaI+>ejo6Ab+O1|DOa zcz$Kz7Z~ysNB5^GEH&imSqtG`VaWRizRHj{?Yz;ze{IOqeM1U#Aid&;4FNda2c#hK z&+1F(b;gNf13qHsy8-h3hWu{~`4Lpu2*mRMKEiVZmL)It%w?SPK7xobx8zO(LB`@~eVaS{HFo*98l3tP( zo}(ET`-SIe3zvL7AAtYW!i8t@p-_fEdVh{f4~h zw~s7&$=6;B7rlcPF3%wj<6EKPXYpr<@m$NFBQ1H+8?kWl+esEK_LLg@raz}y^1@SZ z;bP}y7B2iL3zu@amT@~@Yb<%m*AFdR?77RrMenZ+eluTh8@O3N_gFl_|CNOce-3Zz zlKq59{Tao$l%wz*XW_zgwuQ^Mu+GAz|NH^tq<4#<_ZWV}MD&Wh7l7ZuIPq*XczzKe z|9~NH&JTA7$bV_boAb|a@Pk0MoyRdw_G~lknGzshWyo(gH9m*z-Gm>D+UaJ`r5_f5|xUKZlR-^asfAG2|aKh%n z3WAK>kuU!H1pBeHm81@{(51@(t@_f+Y z7Jfg{P>5K#*mDZwl;2)_#Q)h8aU|?yyGc9?sr{2Ij}L`g zEL`;7Vd296YsQJ=kN8NrJYvZU&y$9{Y5$)BD%Xm5= z0RLqG{&WETg@upidK(_Szda)uKiw{YQqDu91F|6hUl zO?YZ8T=I2`g-d_+QwtaWZwkOi@&6}?UXdRYfS((H|A2Aw8*xbcdN4r#Wdpy_kpIBI z*Bkg(2L1yBKawAqx9u+pz%Q|InFlsnxRlprj8neM@$_;_Ui`K)0Kd_~8@L|cVd0|p zZpKOP8%BON82Fn8{!jqV6Nda-hWyi(yx9M&g^N9J89aY7c>Wq7|GpvrkRiXz_fUt{3!8u&eo6Az_% z86OG{MARYUPw~T(jFaqp_=rEBVO)49ZxmjPsKeKI{=_)(nDef8EqUq31}t3o_gJ{t zdD`(HMi6@hKbvvVOZRld{`m%O_E(Dnc&;?$`wX7D4BX`TPXq5a=#>IZg z?;kDvu`DD)c;CQp#`>AH(??XW=r= zPOtiee8m1!3UGlSc8Z^;S-9BQY~fN~&jsMG1mFV}F81%S zaIya@#&cn-*?tc?QJJ5yU)t{o3m5()EL`|UF;4t{!$qiROHUfOY3G&zofuKgE`I)p!T&dd{|ifA%Kck~5JHgh61^u`xacjlaMAlc z#;JVGapW0GUU)vWaA}W+7b$Zx_6UB4g$vId3l~2xv2fu@2jDkaxbSSYaN*e*fPWBx zXP-Qjuj_DnyS{V|GjQ_VX2!q4IAKrWBla9?$*<#jQe)v_XQPEnd0lSs?=tv140&_^ z;VuIwo33KLdo5h<*Bp8Zgb<`&i9O%2aLMntEc`L%KY?-E4@H)|_#tNDqIYQkeq{h& zJm$dmFR*ZFN7q=m=>1^;zJqbf*8})S`Ielzzusr{rBlH;$qyLv9hSW4^$eWq+XUuW zZ{Wlw{F@E_4-NiaLw+@InP+}%;KU<-ICv~B5D5DSAIaCT0r=?w_zK3U{*e9a@S$*{ zAx}Dm|EB?X-DxUWrXI?;_I(Q%KitJQ*>8?7_geBVYg*^m7A`zb7(DM8cD@%NzsHc@ zZOD%-){3MZ3IDekCp)(q@-r>@`*m`s$-;%_1`C(||1N{S*WmvzOa69lr!QN$@V{>G zJZJFivE-%Ro*2~z+xb12amw$<2G4X$ezZ>RTxj89&m4nikHOPu$K_X>mO9|ljSAx}Dl=SPP8KMnctcw8XZ{&_M0KkSVC<%R!5#!07X&zJ!DvkdvY zNF#puUV!`*Lw=hf-)_nOPMhbf3&0-@z(2O|5141znfgThb3fxFEnNI?4C8kBx`w=2 zj?)d?EU!9)=bz9i{`oIUUh=#1to`j2JilarT>KA{?a8b`qk4p*>}-(ukZ_KhLj97jeUabSGrw}$YS4W1jgLm+$1emEyGByZ0D zKR0l5em>Ya9V&DFL9pO142gx`vf;Ee<#5YOlM2)=|s1j4_-NATqY zA`t#B`tWn(D+xp(eu|U&_I&~o2)_*JkO9a~t#jRl{Gilj;%$+s=4C=$zc<6n16og-%x0HP{!KqGA7-z}L{GrssWj{pT_d$O95+CuK zyw8dFSKzZ`IKB{SES}98b}nL^c)r3%c;vl+#PeeVzs!&~>&fK-{3|V7)+1{yT;`t= zPkQM?|KCgDCQDxGgZPPLsZNUhcdi^@pwdjoiWW8k+N@(%~_bQ|)dL+sgZ;Qwy$ z^cpzH&*kes^3k^QElXbb-!t%^8vGv^IB_-b^~Zd)`S)4!!kM>GG60sOC7 z^1}a)f&aqb?=x`XAH)2+0{H)7$qPTdCy)Xi?!`ySH#dh+jT8TP=0AjS!pwdw-;&=< zi4jIy_yp!T-r#Y7iC+2L53+}J2+w$n=Pe>fC=K8#v*d**Vd3v;8s~xlo=YwH4_JPo zA^!j{vA@}p7rj>)IE{l7cpO}9;JLtQ&Pu^E__GZE(EA1{*#5cQz=^+x`G0QUCjWf_ z{1004cPnj<^QeK7?*2n{M7M=+IbP%RUO@_C=l%N9dC9`19=^{w*_dtQchHiTcA8Iu zB#RH73ePt!{B9CL7|l2xXf7w^b%G&(gMrI$uL!^NZ{rPlI=veo3iKX73L-D|Bp9cI zsaL-rZ%AJ9Rd4Z#-oXiylKxwL*BG!hJ}kg z3k;sepj-T~)Q~5e#LvqN{5s$=j$di;nCr4@4SDLic=?EfR<#Bq>OPx1~{$ujlxDBh4b+`uW#{aVZ!W#Ph4?>(bH zvVX)!{CSEcFLu)V&?rz|>2nKxA<+BKD9|NwNxM#1_+}D9s9~IBhZ*^z_oPt}9vM#; z8S>_SR9gVP!s5AG(>kjxT>Q{!@cag{;)ivXyx9K(L!REjEb{aoHVR_D@ZVv`oAvGI z0rC$S@-Ki-_@A)kW!>?#g-f|VYvE$giw6JS4F1;w!zaRWReTg4dH->%g%9Th9R^PGC3#QqdIP62koOsHuyA>=d9Q`b`;vEC zxV$$ypA1A0JLNsu#TMRsqL!<&aCr}RtA%IsW#E*rZr-n0Z{UB)t2?OjaOOn@}BtC0DQfL%lqax z1mL|EF7;}+g=fl_=c{6;yobKn!lj;<82ELRaSl}mPO3z{*1$_^pc*cGU z7ye!g7yjKAF8uj<`}mQ*Zgnc z!e3?Kf6&>_j1R;^xA?xG6NDy1o^aXcmGQ@xUu(%nsdGZO)50%dyxYQM-;>^BNkRA* zvwW|CbC;&%UNdmAvzg_08u)79#Y)<7`V5@p+gN_Lfs=fM<>hw)#hzs>FR%V5oaCjS z&*Oa^k-viF^9`KjC5$p~vS%gBM+}_gxf(cS7A|^S3y-jZDhnUQc&&vC|9lH)ai=K& zZ#8hT|55P|Clo)>@3>G9J%ke^zbhsA5c%uH50nT&@b5ETsNu{wCBIu$X5eJc4J_Ye z;WsgUr-lE7@p~=&-x=Rv;dd~;(ZcUyyxYR%_pP>B_&qG&YvI3O{51=gee<0bejm&C zS@^FQ-)-T)W_+K8|AukpV8n_4A7Gr;2owaDeffL~e~9HrS@^?@7aKT1@;gpt7B0W{ zQf1-tyD_yEF26I=Wa0ArQL8LmerHMU_fa(2Bfn#_!IGEXz3H}a`5l;E3zy&3G3VRF z{~qtdC}p@7z4H4%h3qH6<#(KlEnI$wDgq7)6wM;)dc67`!sXhvaJqL(VXi)8cx1e2 zweVN?`eqB4-w~5`B=&T&Uu3^QaQWRossDm+WO=F2f@uO~K{9)Lc#{ zxO#aWY)@MFY#wB8ws5&0aKDAi@A8RV!Y{wex7(7J{%RBnB8a^7hYc1kzq7a2!sYk$ z?zM3FJ-v4=Tz*e)4Cjxc=_9|7x6Q)k_vqfWaQQvD++4DL$bR`fx?&5L-=k}?aQQvD z+bvvvkFMLo<@f0H?}iNNm39>2`L^Uses`|M!sU18uCj3HA7uV7Jkl?1v*hLX;_`W( zEb{VuadRzP+A011g6TK;y|}k5Tz(f$=HbFG{Z&5CZv~g%g)6pj`CYhH3zz=nW(${f z<^2{e>rivwiFC_4eWxWa>-~RNxa=p4$lKo!vK}8};j$m%T6kAPr(bR1@;h($Te$qr z+hz-w-+6o2!Y}xa=FjE%q4@a+jF(ur{GMBbg-d^atA#Ht(EPu%aGAG1YvHnvc-O*Z zUGOgpmvN_r=V@ZU{GMCV!ev~$)xu>Q+-Tu4&i=!~Wn9ibROe6h$~ZpP!ezaav~by9 zT4&+1zp=r><#*dgjM!hVjLTyzT=t8qEL?tv?J5hGeUh6kT=o@5AGW_<*`GAux0cc( z`g<&1Q=F{3)ESGn*6L%EoV7JBsT-@_$2=Bq->-AVUW)g>k8NwA3lfjwE#YG`LO}4= z{ZuPMN(hl-)jM!)u&pJ7E4mDAeR~ow&}JRDz+1NKTbAO@?MY`WUTWSrcJj2Dr|S2Y zYt@ZS^A_N})s1GJ{$HF&kd}r@?gleVWqj6Bl7Z9-533pQ7@X0NQI9JEa-4{q@Ua@l1L^PEqy?lOBmJb?*1r_kf%H4mbow&Oe-RC5zMF7% zApMbNwTSH3l77mHt$!6T(r>A8%Blb&RO&Azv&>p8vnS)xV19O>ES z1Es&0)7$k`uI2bMPG2kv_;^33FSKydML~}D8Rsg3zh36_CPwFyPVjEVZ2!H+>GyH} zD)N$VxqcKU2P(f?=_VV(2A+pd`O*G>q!+)EJ+^K@PVXi3jo;N~L0Y!`5+6G~4Zn8X j2jtwloW33B6&|1U)z|Y+HXSIv`>Iayj3nfv?Z5v6HPu5; diff --git a/dwm/util.o b/dwm/util.o index 53a67f47339e520b38ba4cdde751ff98c1a6258b..e39c878d6cfb484e2136ca6aa056d5cd61ca7573 100644 GIT binary patch literal 2312 zcmbuAO=uHQ5P&Cb)TnLKiihGKD?FtEU6P1e>JPz2`;;J6E25VgldTrhByL`${(y=K zhEnh%c<|ss@Z!Nk4;}=A9(wU0c<~@wJcytNMNd_zGrMo>W7&e}z{|{h^RsW?&*sYB z+`g7b1Xv=l8|FQx0Jj#++ZoTyKnJwL_xQTmBeL;RUil6 zCWn^1(Nx>xiL4lmCvxIsJTWAy@x(oG6>H*&2jaG;ABp>(o)V8e{Zuqj7v)5BLk}7f zyLG3Ua$*-c=zWllcXINEm_ctkDq>2lq!x)t0{bpcDMp>t+<#LbU6JhT7P7M9ae(4L!;(N33MCyoVNSd9T% zzYHg6{X~CoEM2f1Ihp>w^}ipKx>0lfJ`p216nv16jhpR*O{l2nzt*R)mL8SHVG2K= z=gTpk7V8WK$WC&C*2i@MpBoNy9qauVV4HJD-wA^uxyO;wy)@4%x^P$550Ya@@O{o< zj8cR3pZ>DGgtcM&|Art6uM6!z-FtpMeRoO1o=v=0PpDVVgYGo74AxW6;NByGs)l>W YF+{ifL+F1YtUjjyS}y&`^o?NsZwk91ivR!s literal 2272 zcmbW1Pe>GT6vyAXm7A$+*}>4m9)5%iI_wCQY$dC%I@yq#M%JNiTt{5mf6R_y5oBbC zMHW1C2|5UM=+GgeW1!F>Z*>pKOHkOsP8|}q_hx=S{V~m6e9P>6^LgL*`@Q-7<~I+A z^x?Wd0E7jg5BBc_1z0=0A19nJ0Zq^dlUn(&_UP9ZBCTw*UadUE5?WbjnqE$_aUG-S z@`Zv`%Z`&-3>Q&nn^?4;`;aQ`X<7Fz=CjrE~boU9Fb2haT^-{0KwxNfw0hv#~E z?i9~mZw_(d@2LMn@Vn}l4f^ghKoEa^7Q~VKEC|xeAqj#|FWv2jLHNICm(&{P$bK=8 zn$Vw+`ghg}*>SvDxJGugu6|sp!Ozv;eKmNp2ESZ`k0Yo4Mc>YTBAck8hXp&6Rnnm7 zgE5%Rm{783P0NDRh(0#ZucyX_hbM=oQd9i{`cMk)&K0dp-kw91l{ND)S6r~uP{e|Q z1?FPL2Gf0QA&2vk)!}svuidI^&j_N?j?-ET{(>NySO3<V!uEs-OJg=40_vg z{k(`QGixZexoCqjYuE-TGo=zJR)KCvt|}@h=|V1N=24O_*rqaaiHqv)cN7d$h zYR)oprfB29K7y6>4e*|z1TwNZN=oLrg?7x*S|ucn6B0xV@d8>)3dPB$^G0?WFXG-W z%7Aww5~F!>7cv_2p!F&rvj@FwVt&!D$cg!dPhf)Lm=JJ3$_1SsF6xB8gn1FmQp*qe zXm4>Qzmqi1BCn_Kh-}{Hxq(T){R{C=t#~flKdKe=V*h9w@7#!Blh41!J$*aG7?c, so it is mandatory that +applications which want to test against keypad keys send these +sequences. + +But buggy applications (like bash and irssi, for example) don't do this. A fast +solution for them is to use the following command: + + $ printf '\033[?1h\033=' >/dev/tty + +or + $ tput smkx + +In the case of bash, readline is used. Readline has a different note in its +manpage about this issue: + + enable-keypad (Off) + When set to On, readline will try to enable the + application keypad when it is called. Some systems + need this to enable arrow keys. + +Adding this option to your .inputrc will fix the keypad problem for all +applications using readline. + +If you are using zsh, then read the zsh FAQ +: + + It should be noted that the O / [ confusion can occur with other keys + such as Home and End. Some systems let you query the key sequences + sent by these keys from the system's terminal database, terminfo. + Unfortunately, the key sequences given there typically apply to the + mode that is not the one zsh uses by default (it's the "application" + mode rather than the "raw" mode). Explaining the use of terminfo is + outside of the scope of this FAQ, but if you wish to use the key + sequences given there you can tell the line editor to turn on + "application" mode when it starts and turn it off when it stops: + + function zle-line-init () { echoti smkx } + function zle-line-finish () { echoti rmkx } + zle -N zle-line-init + zle -N zle-line-finish + +Putting these lines into your .zshrc will fix the problems. + + +## How can I use meta in 8bit mode? + +St supports meta in 8bit mode, but the default terminfo entry doesn't +use this capability. If you want it, you have to use the 'st-meta' value +in TERM. + + +## I cannot compile st in OpenBSD + +OpenBSD lacks librt, despite it being mandatory in POSIX +. +If you want to compile st for OpenBSD you have to remove -lrt from config.mk, and +st will compile without any loss of functionality, because all the functions are +included in libc on this platform. + + +## The Backspace Case + +St is emulating the Linux way of handling backspace being delete and delete being +backspace. + +This is an issue that was discussed in suckless mailing list +. Here is why some old grumpy +terminal users wants its backspace to be how he feels it: + + Well, I am going to comment why I want to change the behaviour + of this key. When ASCII was defined in 1968, communication + with computers was done using punched cards, or hardcopy + terminals (basically a typewriter machine connected with the + computer using a serial port). ASCII defines DELETE as 7F, + because, in punched-card terms, it means all the holes of the + card punched; it is thus a kind of 'physical delete'. In the + same way, the BACKSPACE key was a non-destructive backspace, + as on a typewriter. So, if you wanted to delete a character, + you had to BACKSPACE and then DELETE. Another use of BACKSPACE + was to type accented characters, for example 'a BACKSPACE `'. + The VT100 had no BACKSPACE key; it was generated using the + CONTROL key as another control character (CONTROL key sets to + 0 b7 b6 b5, so it converts H (code 0x48) into BACKSPACE (code + 0x08)), but it had a DELETE key in a similar position where + the BACKSPACE key is located today on common PC keyboards. + All the terminal emulators emulated the difference between + these keys correctly: the backspace key generated a BACKSPACE + (^H) and delete key generated a DELETE (^?). + + But a problem arose when Linus Torvalds wrote Linux. Unlike + earlier terminals, the Linux virtual terminal (the terminal + emulator integrated in the kernel) returned a DELETE when + backspace was pressed, due to the VT100 having a DELETE key in + the same position. This created a lot of problems (see [1] + and [2]). Since Linux has become the king, a lot of terminal + emulators today generate a DELETE when the backspace key is + pressed in order to avoid problems with Linux. The result is + that the only way of generating a BACKSPACE on these systems + is by using CONTROL + H. (I also think that emacs had an + important point here because the CONTROL + H prefix is used + in emacs in some commands (help commands).) + + From point of view of the kernel, you can change the key + for deleting a previous character with stty erase. When you + connect a real terminal into a machine you describe the type + of terminal, so getty configures the correct value of stty + erase for this terminal. In the case of terminal emulators, + however, you don't have any getty that can set the correct + value of stty erase, so you always get the default value. + For this reason, it is necessary to add 'stty erase ^H' to your + profile if you have changed the value of the backspace key. + Of course, another solution is for st itself to modify the + value of stty erase. I usually have the inverse problem: + when I connect to non-Unix machines, I have to press CONTROL + + h to get a BACKSPACE. The inverse problem occurs when a user + connects to my Unix machines from a different system with a + correct backspace key. + + [1] http://www.ibb.net/~anne/keyboard.html + [2] http://www.tldp.org/HOWTO/Keyboard-and-Console-HOWTO-5.html + + +## But I really want the old grumpy behaviour of my terminal + +Apply [1]. + +[1] https://st.suckless.org/patches/delkey + + +## Why do images not work in st using the w3m image hack? + +w3mimg uses a hack that draws an image on top of the terminal emulator Drawable +window. The hack relies on the terminal to use a single buffer to draw its +contents directly. + +st uses double-buffered drawing so the image is quickly replaced and may show a +short flicker effect. + +Below is a patch example to change st double-buffering to a single Drawable +buffer. + +diff --git a/x.c b/x.c +--- a/x.c ++++ b/x.c +@@ -732,10 +732,6 @@ xresize(int col, int row) + win.tw = col * win.cw; + win.th = row * win.ch; + +- XFreePixmap(xw.dpy, xw.buf); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); +- XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ +@@ -1148,8 +1144,7 @@ xinit(int cols, int rows) + gcvalues.graphics_exposures = False; + dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, + &gcvalues); +- xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, +- DefaultDepth(xw.dpy, xw.scr)); ++ xw.buf = xw.win; + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + +@@ -1632,8 +1627,6 @@ xdrawline(Line line, int x1, int y1, int x2) + void + xfinishdraw(void) + { +- XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, +- win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); + + +## BadLength X error in Xft when trying to render emoji + +Xft makes st crash when rendering color emojis with the following error: + +"X Error of failed request: BadLength (poly request too large or internal Xlib length error)" + Major opcode of failed request: 139 (RENDER) + Minor opcode of failed request: 20 (RenderAddGlyphs) + Serial number of failed request: 1595 + Current serial number in output stream: 1818" + +This is a known bug in Xft (not st) which happens on some platforms and +combination of particular fonts and fontconfig settings. + +See also: +https://gitlab.freedesktop.org/xorg/lib/libxft/issues/6 +https://bugs.freedesktop.org/show_bug.cgi?id=107534 +https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + +The solution is to remove color emoji fonts or disable this in the fontconfig +XML configuration. As an ugly workaround (which may work only on newer +fontconfig versions (FC_COLOR)), the following code can be used to mask color +fonts: + + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + +Please don't bother reporting this bug to st, but notify the upstream Xft +developers about fixing this bug. + +As of 2022-09-05 this now seems to be finally fixed in libXft 2.3.5: +https://gitlab.freedesktop.org/xorg/lib/libxft/-/blob/libXft-2.3.5/NEWS diff --git a/st/LEGACY b/st/LEGACY new file mode 100644 index 0000000..bf28b1e --- /dev/null +++ b/st/LEGACY @@ -0,0 +1,17 @@ +A STATEMENT ON LEGACY SUPPORT + +In the terminal world there is much cruft that comes from old and unsup‐ +ported terminals that inherit incompatible modes and escape sequences +which noone is able to know, except when he/she comes from that time and +developed a graphical vt100 emulator at that time. + +One goal of st is to only support what is really needed. When you en‐ +counter a sequence which you really need, implement it. But while you +are at it, do not add the other cruft you might encounter while sneek‐ +ing at other terminal emulators. History has bloated them and there is +no real evidence that most of the sequences are used today. + + +Christoph Lohmann <20h@r-36.net> +2012-09-13T07:00:36.081271045+02:00 + diff --git a/st/LICENSE b/st/LICENSE new file mode 100644 index 0000000..3cbf420 --- /dev/null +++ b/st/LICENSE @@ -0,0 +1,34 @@ +MIT/X Consortium License + +© 2014-2022 Hiltjo Posthuma +© 2018 Devin J. Pohly +© 2014-2017 Quentin Rameau +© 2009-2012 Aurélien APTEL +© 2008-2017 Anselm R Garbe +© 2012-2017 Roberto E. Vargas Caballero +© 2012-2016 Christoph Lohmann <20h at r-36 dot net> +© 2013 Eon S. Jeon +© 2013 Alexander Sedov +© 2013 Mark Edgar +© 2013-2014 Eric Pruitt +© 2013 Michael Forney +© 2013-2014 Markus Teich +© 2014-2015 Laslo Hunhold + +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/st/Makefile b/st/Makefile new file mode 100644 index 0000000..470ac86 --- /dev/null +++ b/st/Makefile @@ -0,0 +1,57 @@ +# st - simple terminal +# See LICENSE file for copyright and license details. +.POSIX: + +include config.mk + +SRC = st.c x.c +OBJ = $(SRC:.c=.o) + +all: options st + +options: + @echo st build options: + @echo "CFLAGS = $(STCFLAGS)" + @echo "LDFLAGS = $(STLDFLAGS)" + @echo "CC = $(CC)" + +config.h: + cp config.def.h config.h + +.c.o: + $(CC) $(STCFLAGS) -c $< + +st.o: config.h st.h win.h +x.o: arg.h config.h st.h win.h + +$(OBJ): config.h config.mk + +st: $(OBJ) + $(CC) -o $@ $(OBJ) $(STLDFLAGS) + +clean: + rm -f st $(OBJ) st-$(VERSION).tar.gz + +dist: clean + mkdir -p st-$(VERSION) + cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ + config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ + st-$(VERSION) + tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz + rm -rf st-$(VERSION) + +install: st + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f st $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/st + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 + tic -sx st.info + @echo Please see the README file regarding the terminfo entry of st. + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/st + rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 + +.PHONY: all options clean dist install uninstall diff --git a/st/README b/st/README new file mode 100644 index 0000000..6a846ed --- /dev/null +++ b/st/README @@ -0,0 +1,34 @@ +st - simple terminal +-------------------- +st is a simple terminal emulator for X which sucks less. + + +Requirements +------------ +In order to build st you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (st is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install st (if +necessary as root): + + make clean install + + +Running st +---------- +If you did not install st with make clean install, you must compile +the st terminfo entry with the following command: + + tic -sx st.info + +See the man page for additional details. + +Credits +------- +Based on Aurélien APTEL bt source code. + diff --git a/st/TODO b/st/TODO new file mode 100644 index 0000000..5f74cd5 --- /dev/null +++ b/st/TODO @@ -0,0 +1,28 @@ +vt emulation +------------ + +* double-height support + +code & interface +---------------- + +* add a simple way to do multiplexing + +drawing +------- +* add diacritics support to xdraws() + * switch to a suckless font drawing library +* make the font cache simpler +* add better support for brightening of the upper colors + +bugs +---- + +* fix shift up/down (shift selection in emacs) +* remove DEC test sequence when appropriate + +misc +---- + + $ grep -nE 'XXX|TODO' st.c + diff --git a/st/arg.h b/st/arg.h new file mode 100644 index 0000000..a22e019 --- /dev/null +++ b/st/arg.h @@ -0,0 +1,50 @@ +/* + * 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;\ + }\ + int i_;\ + for (i_ = 1, brk_ = 0, argv_ = argv;\ + argv[0][i_] && !brk_;\ + i_++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][i_];\ + switch (argc_) + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][i_+1] != '\0')?\ + (&argv[0][i_+1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][i_+1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][i_+1] != '\0')?\ + (&argv[0][i_+1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/st/config.def.h b/st/config.def.h new file mode 100644 index 0000000..e3b469b --- /dev/null +++ b/st/config.def.h @@ -0,0 +1,476 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", + "gray90", /* default foreground colour */ + "black", /* default background colour */ +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; +unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, +}; + +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask|ShiftMask) + +static Shortcut shortcuts[] = { + /* mask keysym function argument */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, +}; + +/* + * Special keys (change & recompile st.info accordingly) + * + * Mask value: + * * Use XK_ANY_MOD to match the key no matter modifiers state + * * Use XK_NO_MOD to match the key alone (no modifiers) + * appkey value: + * * 0: no value + * * > 0: keypad application mode enabled + * * = 2: term.numlock = 1 + * * < 0: keypad application mode disabled + * appcursor value: + * * 0: no value + * * > 0: cursor application mode enabled + * * < 0: cursor application mode disabled + * + * Be careful with the order of the definitions because st searches in + * this table sequentially, so any XK_ANY_MOD must be in the last + * position for a key. + */ + +/* + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) + * to be mapped below, add them to this array. + */ +static KeySym mappedkeys[] = { -1 }; + +/* + * State bits to ignore when matching key or button events. By default, + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. + */ +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, +}; + +/* + * Selection types' masks. + * Use the same masks as usual. + * Button1Mask is always unset, to make masks match between ButtonPress. + * ButtonRelease and MotionNotify. + * If no match is found, regular selection is used. + */ +static uint selmasks[] = { + [SEL_RECTANGULAR] = Mod1Mask, +}; + +/* + * Printable characters in ASCII, used to estimate the advance width + * of single wide characters. + */ +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/st/config.h b/st/config.h new file mode 100644 index 0000000..e3b469b --- /dev/null +++ b/st/config.h @@ -0,0 +1,476 @@ +/* See LICENSE file for copyright and license details. */ + +/* + * appearance + * + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html + */ +static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +static int borderpx = 2; + +/* + * What program is execed by st depends of these precedence rules: + * 1: program passed with -e + * 2: scroll and/or utmp + * 3: SHELL environment variable + * 4: value of shell in /etc/passwd + * 5: value of shell in config.h + */ +static char *shell = "/bin/sh"; +char *utmp = NULL; +/* scroll program: to enable use a string like "scroll" */ +char *scroll = NULL; +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; + +/* identification sequence returned in DA and DECID */ +char *vtiden = "\033[?6c"; + +/* Kerning / character bounding-box multipliers */ +static float cwscale = 1.0; +static float chscale = 1.0; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +wchar_t *worddelimiters = L" "; + +/* selection timeouts (in milliseconds) */ +static unsigned int doubleclicktimeout = 300; +static unsigned int tripleclicktimeout = 600; + +/* alt screens */ +int allowaltscreen = 1; + +/* allow certain non-interactive (insecure) window operations such as: + setting the clipboard text */ +int allowwindowops = 0; + +/* + * draw latency range in ms - from new content/keypress/etc until drawing. + * within this range, st draws when content stops arriving (idle). mostly it's + * near minlatency, but it waits longer for slow updates to avoid partial draw. + * low minlatency will tear/flicker more, as it can "detect" idle too early. + */ +static double minlatency = 8; +static double maxlatency = 33; + +/* + * blinking timeout (set to 0 to disable blinking) for the terminal blinking + * attribute. + */ +static unsigned int blinktimeout = 800; + +/* + * thickness of underline and bar cursors + */ +static unsigned int cursorthickness = 2; + +/* + * bell volume. It must be a value between -100 and 100. Use 0 for disabling + * it + */ +static int bellvolume = 0; + +/* default TERM value */ +char *termname = "st-256color"; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +unsigned int tabspaces = 8; + +/* Terminal colors (16 first used in escape sequence) */ +static const char *colorname[] = { + /* 8 normal colors */ + "black", + "red3", + "green3", + "yellow3", + "blue2", + "magenta3", + "cyan3", + "gray90", + + /* 8 bright colors */ + "gray50", + "red", + "green", + "yellow", + "#5c5cff", + "magenta", + "cyan", + "white", + + [255] = 0, + + /* more colors can be added after 255 to use with DefaultXX */ + "#cccccc", + "#555555", + "gray90", /* default foreground colour */ + "black", /* default background colour */ +}; + + +/* + * Default colors (colorname index) + * foreground, background, cursor, reverse cursor + */ +unsigned int defaultfg = 258; +unsigned int defaultbg = 259; +unsigned int defaultcs = 256; +static unsigned int defaultrcs = 257; + +/* + * Default shape of cursor + * 2: Block ("█") + * 4: Underline ("_") + * 6: Bar ("|") + * 7: Snowman ("☃") + */ +static unsigned int cursorshape = 2; + +/* + * Default columns and rows numbers + */ + +static unsigned int cols = 80; +static unsigned int rows = 24; + +/* + * Default colour and shape of the mouse cursor + */ +static unsigned int mouseshape = XC_xterm; +static unsigned int mousefg = 7; +static unsigned int mousebg = 0; + +/* + * Color used to display font attributes when fontconfig selected a font which + * doesn't match the ones requested. + */ +static unsigned int defaultattr = 11; + +/* + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set). + * Note that if you want to use ShiftMask with selmasks, set this to an other + * modifier, set to 0 to not use it. + */ +static uint forcemousemod = ShiftMask; + +/* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. + */ +static MouseShortcut mshortcuts[] = { + /* mask button function argument release */ + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 }, + { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} }, + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} }, + { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} }, + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} }, +}; + +/* Internal keyboard shortcuts. */ +#define MODKEY Mod1Mask +#define TERMMOD (ControlMask|ShiftMask) + +static Shortcut shortcuts[] = { + /* mask keysym function argument */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, + { ControlMask, XK_Print, toggleprinter, {.i = 0} }, + { ShiftMask, XK_Print, printscreen, {.i = 0} }, + { XK_ANY_MOD, XK_Print, printsel, {.i = 0} }, + { TERMMOD, XK_Prior, zoom, {.f = +1} }, + { TERMMOD, XK_Next, zoom, {.f = -1} }, + { TERMMOD, XK_Home, zoomreset, {.f = 0} }, + { TERMMOD, XK_C, clipcopy, {.i = 0} }, + { TERMMOD, XK_V, clippaste, {.i = 0} }, + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, +}; + +/* + * Special keys (change & recompile st.info accordingly) + * + * Mask value: + * * Use XK_ANY_MOD to match the key no matter modifiers state + * * Use XK_NO_MOD to match the key alone (no modifiers) + * appkey value: + * * 0: no value + * * > 0: keypad application mode enabled + * * = 2: term.numlock = 1 + * * < 0: keypad application mode disabled + * appcursor value: + * * 0: no value + * * > 0: cursor application mode enabled + * * < 0: cursor application mode disabled + * + * Be careful with the order of the definitions because st searches in + * this table sequentially, so any XK_ANY_MOD must be in the last + * position for a key. + */ + +/* + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) + * to be mapped below, add them to this array. + */ +static KeySym mappedkeys[] = { -1 }; + +/* + * State bits to ignore when matching key or button events. By default, + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. + */ +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; + +/* + * This is the huge key array which defines all compatibility to the Linux + * world. Please decide about changes wisely. + */ +static Key key[] = { + /* keysym mask string appkey appcursor */ + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, + { XK_KP_End, ControlMask, "\033[J", -1, 0}, + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, + { XK_Return, Mod1Mask, "\033\r", 0, 0}, + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, + { XK_Insert, ControlMask, "\033[L", -1, 0}, + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, + { XK_Delete, ControlMask, "\033[M", -1, 0}, + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, + { XK_Home, ShiftMask, "\033[2J", 0, -1}, + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, + { XK_End, ControlMask, "\033[J", -1, 0}, + { XK_End, ControlMask, "\033[1;5F", +1, 0}, + { XK_End, ShiftMask, "\033[K", -1, 0}, + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, +}; + +/* + * Selection types' masks. + * Use the same masks as usual. + * Button1Mask is always unset, to make masks match between ButtonPress. + * ButtonRelease and MotionNotify. + * If no match is found, regular selection is used. + */ +static uint selmasks[] = { + [SEL_RECTANGULAR] = Mod1Mask, +}; + +/* + * Printable characters in ASCII, used to estimate the advance width + * of single wide characters. + */ +static char ascii_printable[] = + " !\"#$%&'()*+,-./0123456789:;<=>?" + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + "`abcdefghijklmnopqrstuvwxyz{|}~"; diff --git a/st/config.mk b/st/config.mk new file mode 100644 index 0000000..1e306f8 --- /dev/null +++ b/st/config.mk @@ -0,0 +1,36 @@ +# st version +VERSION = 0.9 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +PKG_CONFIG = pkg-config + +# includes and libs +INCS = -I$(X11INC) \ + `$(PKG_CONFIG) --cflags fontconfig` \ + `$(PKG_CONFIG) --cflags freetype2` +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ + `$(PKG_CONFIG) --libs fontconfig` \ + `$(PKG_CONFIG) --libs freetype2` + +# flags +STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 +STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) +STLDFLAGS = $(LIBS) $(LDFLAGS) + +# OpenBSD: +#CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE +#LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ +# `$(PKG_CONFIG) --libs fontconfig` \ +# `$(PKG_CONFIG) --libs freetype2` +#MANPREFIX = ${PREFIX}/man + +# compiler and linker +# CC = c99 diff --git a/st/st b/st/st new file mode 100755 index 0000000000000000000000000000000000000000..1286350e515e2dbf79177e7b6189ceedd39d729d GIT binary patch literal 107688 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 z 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 G|Gxm-e}(t} literal 0 HcmV?d00001 diff --git a/st/st-scrollback-ringbuffer-0.8.5.diff b/st/st-scrollback-ringbuffer-0.8.5.diff new file mode 100644 index 0000000..7c060c5 --- /dev/null +++ b/st/st-scrollback-ringbuffer-0.8.5.diff @@ -0,0 +1,730 @@ +commit 0663bdf11a409961da5b1120741a69814da8ce65 +Author: Timo Röhling +Date: Tue Nov 23 19:45:33 2021 +0100 + + Terminal scrollback with ring buffer + + This patch adds a ring buffer for scrollback to the terminal. The + advantage of using a ring buffer is that the common case, scrolling with + no static screen content, can be achieved very efficiently by + incrementing and decrementing the starting line (modulo buffer size). + + The scrollback buffer is limited to HISTSIZE lines in order to bound + memory usage. As the lines are allocated on demand, it is possible to + implement unlimited scrollback with few changes. If the terminal is + reset, the scroll back buffer is reset, too. + +diff --git a/config.def.h b/config.def.h +index 91ab8ca..e3b469b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -201,6 +201,8 @@ static Shortcut shortcuts[] = { + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, ++ { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, ++ { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, + }; + + /* +diff --git a/st.c b/st.c +index 51049ba..f9e24ba 100644 +--- a/st.c ++++ b/st.c +@@ -43,6 +43,10 @@ + #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) + #define ISDELIM(u) (u && wcschr(worddelimiters, u)) + ++#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)] ++#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size) ++#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)]) ++ + enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, +@@ -109,12 +113,21 @@ typedef struct { + int alt; + } Selection; + ++/* Screen lines */ ++typedef struct { ++ Line* buffer; /* ring buffer */ ++ int size; /* size of buffer */ ++ int cur; /* start of active screen */ ++ int off; /* scrollback line offset */ ++ TCursor sc; /* saved cursor */ ++} LineBuffer; ++ + /* Internal representation of the screen */ + typedef struct { + int row; /* nb row */ + int col; /* nb col */ +- Line *line; /* screen */ +- Line *alt; /* alternate screen */ ++ LineBuffer screen[2]; /* screen and alternate screen */ ++ int linelen; /* allocated line length */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ +@@ -202,6 +215,8 @@ static void tdeftran(char); + static void tstrsequence(uchar); + + static void drawregion(int, int, int, int); ++static void clearline(Line, Glyph, int, int); ++static Line ensureline(Line); + + static void selnormalize(void); + static void selscroll(int, int); +@@ -415,11 +430,12 @@ int + tlinelen(int y) + { + int i = term.col; ++ Line line = TLINE(y); + +- if (term.line[y][i - 1].mode & ATTR_WRAP) ++ if (line[i - 1].mode & ATTR_WRAP) + return i; + +- while (i > 0 && term.line[y][i - 1].u == ' ') ++ while (i > 0 && line[i - 1].u == ' ') + --i; + + return i; +@@ -528,7 +544,7 @@ selsnap(int *x, int *y, int direction) + * Snap around if the word wraps around at the end or + * beginning of a line. + */ +- prevgp = &term.line[*y][*x]; ++ prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; +@@ -543,14 +559,14 @@ selsnap(int *x, int *y, int direction) + yt = *y, xt = *x; + else + yt = newy, xt = newx; +- if (!(term.line[yt][xt].mode & ATTR_WRAP)) ++ if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + +- gp = &term.line[newy][newx]; ++ gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) +@@ -571,14 +587,14 @@ selsnap(int *x, int *y, int direction) + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { +- if (!(term.line[*y-1][term.col-1].mode ++ if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { +- if (!(term.line[*y][term.col-1].mode ++ if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } +@@ -609,13 +625,13 @@ getsel(void) + } + + if (sel.type == SEL_RECTANGULAR) { +- gp = &term.line[y][sel.nb.x]; ++ gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { +- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; ++ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } +- last = &term.line[y][MIN(lastx, linelen-1)]; ++ last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + +@@ -956,12 +972,15 @@ int + tattrset(int attr) + { + int i, j; ++ int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { ++ Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { +- if (term.line[i][j].mode & attr) ++ if (line[j].mode & attr) + return 1; + } ++ y = (y+1) % TSCREEN.size; + } + + return 0; +@@ -983,14 +1002,17 @@ void + tsetdirtattr(int attr) + { + int i, j; ++ int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { ++ Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { +- if (term.line[i][j].mode & attr) { ++ if (line[j].mode & attr) { + tsetdirt(i, i); + break; + } + } ++ y = (y+1) % TSCREEN.size; + } + } + +@@ -1003,27 +1025,19 @@ tfulldirt(void) + void + tcursor(int mode) + { +- static TCursor c[2]; +- int alt = IS_SET(MODE_ALTSCREEN); +- + if (mode == CURSOR_SAVE) { +- c[alt] = term.c; ++ TSCREEN.sc = term.c; + } else if (mode == CURSOR_LOAD) { +- term.c = c[alt]; +- tmoveto(c[alt].x, c[alt].y); ++ term.c = TSCREEN.sc; ++ tmoveto(term.c.x, term.c.y); + } + } + + void + treset(void) + { +- uint i; +- +- term.c = (TCursor){{ +- .mode = ATTR_NULL, +- .fg = defaultfg, +- .bg = defaultbg +- }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; ++ int i, j; ++ Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) +@@ -1035,17 +1049,37 @@ treset(void) + term.charset = 0; + + for (i = 0; i < 2; i++) { +- tmoveto(0, 0); +- tcursor(CURSOR_SAVE); +- tclearregion(0, 0, term.col-1, term.row-1); +- tswapscreen(); ++ term.screen[i].sc = (TCursor){{ ++ .fg = defaultfg, ++ .bg = defaultbg ++ }}; ++ term.screen[i].cur = 0; ++ term.screen[i].off = 0; ++ for (j = 0; j < term.row; ++j) { ++ if (term.col != term.linelen) ++ term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph)); ++ clearline(term.screen[i].buffer[j], g, 0, term.col); ++ } ++ for (j = term.row; j < term.screen[i].size; ++j) { ++ free(term.screen[i].buffer[j]); ++ term.screen[i].buffer[j] = NULL; ++ } + } ++ tcursor(CURSOR_LOAD); ++ term.linelen = term.col; ++ tfulldirt(); + } + + void + tnew(int col, int row) + { +- term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; ++ int i; ++ term = (Term){}; ++ term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line)); ++ term.screen[0].size = HISTSIZE; ++ term.screen[1].buffer = NULL; ++ for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL; ++ + tresize(col, row); + treset(); + } +@@ -1053,14 +1087,42 @@ tnew(int col, int row) + void + tswapscreen(void) + { +- Line *tmp = term.line; +- +- term.line = term.alt; +- term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); + } + ++void ++kscrollup(const Arg *a) ++{ ++ int n = a->i; ++ ++ if (IS_SET(MODE_ALTSCREEN)) ++ return; ++ ++ if (n < 0) n = (-n) * term.row; ++ if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off; ++ while (!TLINE(-n)) --n; ++ TSCREEN.off += n; ++ selscroll(0, n); ++ tfulldirt(); ++} ++ ++void ++kscrolldown(const Arg *a) ++{ ++ ++ int n = a->i; ++ ++ if (IS_SET(MODE_ALTSCREEN)) ++ return; ++ ++ if (n < 0) n = (-n) * term.row; ++ if (n > TSCREEN.off) n = TSCREEN.off; ++ TSCREEN.off -= n; ++ selscroll(0, -n); ++ tfulldirt(); ++} ++ + void + tscrolldown(int orig, int n) + { +@@ -1069,15 +1131,29 @@ tscrolldown(int orig, int n) + + LIMIT(n, 0, term.bot-orig+1); + +- tsetdirt(orig, term.bot-n); +- tclearregion(0, term.bot-n+1, term.col-1, term.bot); ++ /* Ensure that lines are allocated */ ++ for (i = -n; i < 0; i++) { ++ TLINE(i) = ensureline(TLINE(i)); ++ } + +- for (i = term.bot; i >= orig+n; i--) { +- temp = term.line[i]; +- term.line[i] = term.line[i-n]; +- term.line[i-n] = temp; ++ /* Shift non-scrolling areas in ring buffer */ ++ for (i = term.bot+1; i < term.row; i++) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i-n); ++ TLINE(i-n) = temp; ++ } ++ for (i = 0; i < orig; i++) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i-n); ++ TLINE(i-n) = temp; + } + ++ /* Scroll buffer */ ++ TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size; ++ /* Clear lines that have entered the view */ ++ tclearregion(0, orig, term.linelen-1, orig+n-1); ++ /* Redraw portion of the screen that has scrolled */ ++ tsetdirt(orig+n-1, term.bot); + selscroll(orig, n); + } + +@@ -1089,15 +1165,29 @@ tscrollup(int orig, int n) + + LIMIT(n, 0, term.bot-orig+1); + +- tclearregion(0, orig, term.col-1, orig+n-1); +- tsetdirt(orig+n, term.bot); ++ /* Ensure that lines are allocated */ ++ for (i = term.row; i < term.row + n; i++) { ++ TLINE(i) = ensureline(TLINE(i)); ++ } + +- for (i = orig; i <= term.bot-n; i++) { +- temp = term.line[i]; +- term.line[i] = term.line[i+n]; +- term.line[i+n] = temp; ++ /* Shift non-scrolling areas in ring buffer */ ++ for (i = orig-1; i >= 0; i--) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i+n); ++ TLINE(i+n) = temp; ++ } ++ for (i = term.row-1; i >term.bot; i--) { ++ temp = TLINE(i); ++ TLINE(i) = TLINE(i+n); ++ TLINE(i+n) = temp; + } + ++ /* Scroll buffer */ ++ TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size; ++ /* Clear lines that have entered the view */ ++ tclearregion(0, term.bot-n+1, term.linelen-1, term.bot); ++ /* Redraw portion of the screen that has scrolled */ ++ tsetdirt(orig, term.bot-n+1); + selscroll(orig, -n); + } + +@@ -1201,6 +1291,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; ++ Line line = TLINE(y); + + /* + * The table is proudly stolen from rxvt. +@@ -1209,25 +1300,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + +- if (term.line[y][x].mode & ATTR_WIDE) { ++ if (line[x].mode & ATTR_WIDE) { + if (x+1 < term.col) { +- term.line[y][x+1].u = ' '; +- term.line[y][x+1].mode &= ~ATTR_WDUMMY; ++ line[x+1].u = ' '; ++ line[x+1].mode &= ~ATTR_WDUMMY; + } +- } else if (term.line[y][x].mode & ATTR_WDUMMY) { +- term.line[y][x-1].u = ' '; +- term.line[y][x-1].mode &= ~ATTR_WIDE; ++ } else if (line[x].mode & ATTR_WDUMMY) { ++ line[x-1].u = ' '; ++ line[x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; +- term.line[y][x] = *attr; +- term.line[y][x].u = u; ++ line[x] = *attr; ++ line[x].u = u; + } + + void + tclearregion(int x1, int y1, int x2, int y2) + { +- int x, y, temp; ++ int x, y, L, S, temp; + Glyph *gp; + + if (x1 > x2) +@@ -1235,15 +1326,16 @@ tclearregion(int x1, int y1, int x2, int y2) + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + +- LIMIT(x1, 0, term.col-1); +- LIMIT(x2, 0, term.col-1); ++ LIMIT(x1, 0, term.linelen-1); ++ LIMIT(x2, 0, term.linelen-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + ++ L = TLINEOFFSET(y1); + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { +- gp = &term.line[y][x]; ++ gp = &TSCREEN.buffer[L][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; +@@ -1251,6 +1343,7 @@ tclearregion(int x1, int y1, int x2, int y2) + gp->mode = 0; + gp->u = ' '; + } ++ L = (L + 1) % TSCREEN.size; + } + } + +@@ -1265,7 +1358,7 @@ tdeletechar(int n) + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; +- line = term.line[term.c.y]; ++ line = TLINE(term.c.y); + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +@@ -1282,7 +1375,7 @@ tinsertblank(int n) + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; +- line = term.line[term.c.y]; ++ line = TLINE(term.c.y); + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +@@ -2103,7 +2196,7 @@ tdumpline(int n) + char buf[UTF_SIZ]; + const Glyph *bp, *end; + +- bp = &term.line[n][0]; ++ bp = &TLINE(n)[0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) +@@ -2486,11 +2579,11 @@ check_control_code: + if (selected(term.c.x, term.c.y)) + selclear(); + +- gp = &term.line[term.c.y][term.c.x]; ++ gp = &TLINE(term.c.y)[term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); +- gp = &term.line[term.c.y][term.c.x]; ++ gp = &TLINE(term.c.y)[term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) +@@ -2498,7 +2591,7 @@ check_control_code: + + if (term.c.x+width > term.col) { + tnewline(1); +- gp = &term.line[term.c.y][term.c.x]; ++ gp = &TLINE(term.c.y)[term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); +@@ -2529,6 +2622,11 @@ twrite(const char *buf, int buflen, int show_ctrl) + Rune u; + int n; + ++ if (TSCREEN.off) { ++ TSCREEN.off = 0; ++ tfulldirt(); ++ } ++ + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ +@@ -2555,56 +2653,85 @@ twrite(const char *buf, int buflen, int show_ctrl) + } + + void +-tresize(int col, int row) ++clearline(Line line, Glyph g, int x, int xend) + { + int i; ++ g.mode = 0; ++ g.u = ' '; ++ for (i = x; i < xend; ++i) { ++ line[i] = g; ++ } ++} ++ ++Line ++ensureline(Line line) ++{ ++ if (!line) { ++ line = xmalloc(term.linelen * sizeof(Glyph)); ++ } ++ return line; ++} ++ ++void ++tresize(int col, int row) ++{ ++ int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); ++ int linelen = MAX(col, term.linelen); + int *bp; +- TCursor c; + +- if (col < 1 || row < 1) { ++ if (col < 1 || row < 1 || row > HISTSIZE) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + +- /* +- * slide screen to keep cursor where we expect it - +- * tscrollup would work here, but we can optimize to +- * memmove because we're freeing the earlier lines +- */ +- for (i = 0; i <= term.c.y - row; i++) { +- free(term.line[i]); +- free(term.alt[i]); ++ /* Shift buffer to keep the cursor where we expect it */ ++ if (row <= term.c.y) { ++ term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size; ++ } ++ ++ /* Resize and clear line buffers as needed */ ++ if (linelen > term.linelen) { ++ for (i = 0; i < term.screen[0].size; ++i) { ++ if (term.screen[0].buffer[i]) { ++ term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph)); ++ clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen); ++ } ++ } ++ for (i = 0; i < minrow; ++i) { ++ term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph)); ++ clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen); ++ } + } +- /* ensure that both src and dst are not NULL */ +- if (i > 0) { +- memmove(term.line, term.line + i, row * sizeof(Line)); +- memmove(term.alt, term.alt + i, row * sizeof(Line)); ++ /* Allocate all visible lines for regular line buffer */ ++ for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size) ++ { ++ if (!term.screen[0].buffer[j]) { ++ term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph)); ++ } ++ if (i >= term.row) { ++ clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen); ++ } + } +- for (i += row; i < term.row; i++) { +- free(term.line[i]); +- free(term.alt[i]); ++ /* Resize alt screen */ ++ term.screen[1].cur = 0; ++ term.screen[1].size = row; ++ for (i = row; i < term.row; ++i) { ++ free(term.screen[1].buffer[i]); ++ } ++ term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line)); ++ for (i = term.row; i < row; ++i) { ++ term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph)); ++ clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen); + } + + /* resize to new height */ +- term.line = xrealloc(term.line, row * sizeof(Line)); +- term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + +- /* resize each row to new width, zero-pad if needed */ +- for (i = 0; i < minrow; i++) { +- term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); +- term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); +- } +- +- /* allocate any new rows */ +- for (/* i = minrow */; i < row; i++) { +- term.line[i] = xmalloc(col * sizeof(Glyph)); +- term.alt[i] = xmalloc(col * sizeof(Glyph)); +- } ++ /* fix tabstops */ + if (col > term.col) { + bp = term.tabs + term.col; + +@@ -2614,26 +2741,16 @@ tresize(int col, int row) + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } ++ + /* update terminal size */ + term.col = col; + term.row = row; ++ term.linelen = linelen; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); +- /* Clearing both screens (it makes dirty all lines) */ +- c = term.c; +- for (i = 0; i < 2; i++) { +- if (mincol < col && 0 < minrow) { +- tclearregion(mincol, 0, col - 1, minrow - 1); +- } +- if (0 < col && minrow < row) { +- tclearregion(0, minrow, col - 1, row - 1); +- } +- tswapscreen(); +- tcursor(CURSOR_LOAD); +- } +- term.c = c; ++ tfulldirt(); + } + + void +@@ -2645,14 +2762,15 @@ resettitle(void) + void + drawregion(int x1, int y1, int x2, int y2) + { +- int y; ++ int y, L; + ++ L = TLINEOFFSET(y1); + for (y = y1; y < y2; y++) { +- if (!term.dirty[y]) +- continue; +- +- term.dirty[y] = 0; +- xdrawline(term.line[y], x1, y, x2); ++ if (term.dirty[y]) { ++ term.dirty[y] = 0; ++ xdrawline(TSCREEN.buffer[L], x1, y, x2); ++ } ++ L = (L + 1) % TSCREEN.size; + } + } + +@@ -2667,14 +2785,15 @@ draw(void) + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); +- if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) ++ if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY) + term.ocx--; +- if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) ++ if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ if (TSCREEN.off == 0) ++ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], ++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); +diff --git a/st.h b/st.h +index 519b9bd..b48e810 100644 +--- a/st.h ++++ b/st.h +@@ -19,6 +19,7 @@ + + #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) + #define IS_TRUECOL(x) (1 << 24 & (x)) ++#define HISTSIZE 2000 + + enum glyph_attribute { + ATTR_NULL = 0, +diff --git a/x.c b/x.c +index 8a16faa..1bb5853 100644 +--- a/x.c ++++ b/x.c +@@ -59,6 +59,8 @@ static void zoom(const Arg *); + static void zoomabs(const Arg *); + static void zoomreset(const Arg *); + static void ttysend(const Arg *); ++void kscrollup(const Arg *); ++void kscrolldown(const Arg *); + + /* config.h for applying patches and the configuration. */ + #include "config.h" diff --git a/st/st.1 b/st/st.1 new file mode 100644 index 0000000..39120b4 --- /dev/null +++ b/st/st.1 @@ -0,0 +1,177 @@ +.TH ST 1 st\-VERSION +.SH NAME +st \- simple terminal +.SH SYNOPSIS +.B st +.RB [ \-aiv ] +.RB [ \-c +.IR class ] +.RB [ \-f +.IR font ] +.RB [ \-g +.IR geometry ] +.RB [ \-n +.IR name ] +.RB [ \-o +.IR iofile ] +.RB [ \-T +.IR title ] +.RB [ \-t +.IR title ] +.RB [ \-l +.IR line ] +.RB [ \-w +.IR windowid ] +.RB [[ \-e ] +.IR command +.RI [ arguments ...]] +.PP +.B st +.RB [ \-aiv ] +.RB [ \-c +.IR class ] +.RB [ \-f +.IR font ] +.RB [ \-g +.IR geometry ] +.RB [ \-n +.IR name ] +.RB [ \-o +.IR iofile ] +.RB [ \-T +.IR title ] +.RB [ \-t +.IR title ] +.RB [ \-w +.IR windowid ] +.RB \-l +.IR line +.RI [ stty_args ...] +.SH DESCRIPTION +.B st +is a simple terminal emulator. +.SH OPTIONS +.TP +.B \-a +disable alternate screens in terminal +.TP +.BI \-c " class" +defines the window class (default $TERM). +.TP +.BI \-f " font" +defines the +.I font +to use when st is run. +.TP +.BI \-g " geometry" +defines the X11 geometry string. +The form is [=][{xX}][{+-}{+-}]. See +.BR XParseGeometry (3) +for further details. +.TP +.B \-i +will fixate the position given with the -g option. +.TP +.BI \-n " name" +defines the window instance name (default $TERM). +.TP +.BI \-o " iofile" +writes all the I/O to +.I iofile. +This feature is useful when recording st sessions. A value of "-" means +standard output. +.TP +.BI \-T " title" +defines the window title (default 'st'). +.TP +.BI \-t " title" +defines the window title (default 'st'). +.TP +.BI \-w " windowid" +embeds st within the window identified by +.I windowid +.TP +.BI \-l " line" +use a tty +.I line +instead of a pseudo terminal. +.I line +should be a (pseudo-)serial device (e.g. /dev/ttyS0 on Linux for serial port +0). +When this flag is given +remaining arguments are used as flags for +.BR stty(1). +By default st initializes the serial line to 8 bits, no parity, 1 stop bit +and a 38400 baud rate. The speed is set by appending it as last argument +(e.g. 'st -l /dev/ttyS0 115200'). Arguments before the last one are +.BR stty(1) +flags. If you want to set odd parity on 115200 baud use for example 'st -l +/dev/ttyS0 parenb parodd 115200'. Set the number of bits by using for +example 'st -l /dev/ttyS0 cs7 115200'. See +.BR stty(1) +for more arguments and cases. +.TP +.B \-v +prints version information to stderr, then exits. +.TP +.BI \-e " command " [ " arguments " "... ]" +st executes +.I command +instead of the shell. If this is used it +.B must be the last option +on the command line, as in xterm / rxvt. +This option is only intended for compatibility, +and all the remaining arguments are used as a command +even without it. +.SH SHORTCUTS +.TP +.B Break +Send a break in the serial line. +Break key is obtained in PC keyboards +pressing at the same time control and pause. +.TP +.B Ctrl-Print Screen +Toggle if st should print to the +.I iofile. +.TP +.B Shift-Print Screen +Print the full screen to the +.I iofile. +.TP +.B Print Screen +Print the selection to the +.I iofile. +.TP +.B Ctrl-Shift-Page Up +Increase font size. +.TP +.B Ctrl-Shift-Page Down +Decrease font size. +.TP +.B Ctrl-Shift-Home +Reset to default font size. +.TP +.B Ctrl-Shift-y +Paste from primary selection (middle mouse button). +.TP +.B Ctrl-Shift-c +Copy the selected text to the clipboard selection. +.TP +.B Ctrl-Shift-v +Paste from the clipboard selection. +.SH CUSTOMIZATION +.B st +can be customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.SH AUTHORS +See the LICENSE file for the authors. +.SH LICENSE +See the LICENSE file for the terms of redistribution. +.SH SEE ALSO +.BR tabbed (1), +.BR utmp (1), +.BR stty (1), +.BR scroll (1) +.SH BUGS +See the TODO file in the distribution. + diff --git a/st/st.c b/st/st.c new file mode 100644 index 0000000..15836e3 --- /dev/null +++ b/st/st.c @@ -0,0 +1,2789 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) + +#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)] +#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size) +#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)]) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, +}; + +enum cursor_movement { + CURSOR_SAVE, + CURSOR_LOAD +}; + +enum cursor_state { + CURSOR_DEFAULT = 0, + CURSOR_WRAPNEXT = 1, + CURSOR_ORIGIN = 2 +}; + +enum charset { + CS_GRAPHIC0, + CS_GRAPHIC1, + CS_UK, + CS_USA, + CS_MULTI, + CS_GER, + CS_FIN +}; + +enum escape_state { + ESC_START = 1, + ESC_CSI = 2, + ESC_STR = 4, /* DCS, OSC, PM, APC */ + ESC_ALTCHARSET = 8, + ESC_STR_END = 16, /* a final string was encountered */ + ESC_TEST = 32, /* Enter in test mode */ + ESC_UTF8 = 64, +}; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + +/* Screen lines */ +typedef struct { + Line* buffer; /* ring buffer */ + int size; /* size of buffer */ + int cur; /* start of active screen */ + int off; /* scrollback line offset */ + TCursor sc; /* saved cursor */ +} LineBuffer; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + LineBuffer screen[2]; /* screen and alternate screen */ + int linelen; /* allocated line length */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + Rune lastc; /* last printed char outside of sequence, 0 if control */ +} Term; + +/* CSI Escape sequence structs */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + size_t len; /* raw string length */ + char priv; + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; +} CSIEscape; + +/* STR Escape sequence structs */ +/* ESC type [[ [] [;]] ] ESC '\' */ +typedef struct { + char type; /* ESC type ... */ + char *buf; /* allocated raw string */ + size_t siz; /* allocation size */ + size_t len; /* raw string length */ + char *args[STR_ARG_SIZ]; + int narg; /* nb of args */ +} STREscape; + +static void execsh(char *, char **); +static void stty(char **); +static void sigchld(int); +static void ttywriteraw(const char *, size_t); + +static void csidump(void); +static void csihandle(void); +static void csiparse(void); +static void csireset(void); +static void osc_color_response(int, int, int); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int); +static void tscrolldown(int, int); +static void tsetattr(const int *, int); +static void tsetchar(Rune, const Glyph *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, const int *, int); +static int twrite(const char *, int, int); +static void tfulldirt(void); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32_t tdefcolor(const int *, int *, int); +static void tdeftran(char); +static void tstrsequence(uchar); + +static void drawregion(int, int, int, int); +static void clearline(Line, Glyph, int, int); +static Line ensureline(Line); + +static void selnormalize(void); +static void selscroll(int, int); +static void selsnap(int *, int *, int); + +static size_t utf8decode(const char *, Rune *, size_t); +static Rune utf8decodebyte(char, size_t *); +static char utf8encodebyte(Rune, size_t); +static size_t utf8validate(Rune *, size_t); + +static char *base64dec(const char *); +static char base64dec_getc(const char **); + +static ssize_t xwrite(int, const char *, size_t); + +/* Globals */ +static Term term; +static Selection sel; +static CSIEscape csiescseq; +static STREscape strescseq; +static int iofd = 1; +static int cmdfd; +static pid_t pid; + +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +ssize_t +xwrite(int fd, const char *s, size_t len) +{ + size_t aux = len; + ssize_t r; + + while (len > 0) { + r = write(fd, s, len); + if (r < 0) + return r; + len -= r; + s += r; + } + + return aux; +} + +void * +xmalloc(size_t len) +{ + void *p; + + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); + + return p; +} + +void * +xrealloc(void *p, size_t len) +{ + if ((p = realloc(p, len)) == NULL) + die("realloc: %s\n", strerror(errno)); + + return p; +} + +char * +xstrdup(const char *s) +{ + char *p; + + if ((p = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return p; +} + +size_t +utf8decode(const char *c, Rune *u, size_t clen) +{ + size_t i, j, len, type; + Rune 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 != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Rune +utf8decodebyte(char c, size_t *i) +{ + for (*i = 0; *i < LEN(utfmask); ++(*i)) + if (((uchar)c & utfmask[*i]) == utfbyte[*i]) + return (uchar)c & ~utfmask[*i]; + + return 0; +} + +size_t +utf8encode(Rune u, char *c) +{ + size_t len, i; + + len = utf8validate(&u, 0); + if (len > UTF_SIZ) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = utf8encodebyte(u, 0); + u >>= 6; + } + c[0] = utf8encodebyte(u, len); + + return len; +} + +char +utf8encodebyte(Rune u, size_t i) +{ + return utfbyte[i] | (u & ~utfmask[i]); +} + +size_t +utf8validate(Rune *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; +} + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint((unsigned char)**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + static const char base64_digits[256] = { + [43] = 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, + 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SEL_IDLE; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + Line line = TLINE(y); + + if (line[i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && line[i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + const Glyph *gp, *prevgp; + + switch (sel.snap) { + case SNAP_WORD: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + const Glyph *gp, *last; + + if (sel.ob.x == -1) + return NULL; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for (y = sel.nb.y; y <= sel.ne.y; y++) { + if ((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if (sel.type == SEL_RECTANGULAR) { + gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + *ptr++ = '\n'; + } + *ptr = 0; + return str; +} + +void +selclear(void) +{ + if (sel.ob.x == -1) + return; + sel.mode = SEL_IDLE; + sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +execsh(char *cmd, char **args) +{ + char *sh, *prog, *arg; + const struct passwd *pw; + + errno = 0; + if ((pw = getpwuid(getuid())) == NULL) { + if (errno) + die("getpwuid: %s\n", strerror(errno)); + else + die("who are you?\n"); + } + + if ((sh = getenv("SHELL")) == NULL) + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; + + if (args) { + prog = args[0]; + arg = NULL; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = NULL; + } else { + prog = sh; + arg = NULL; + } + DEFAULT(args, ((char *[]) {prog, arg, NULL})); + + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + setenv("LOGNAME", pw->pw_name, 1); + setenv("USER", pw->pw_name, 1); + setenv("SHELL", sh, 1); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + execvp(prog, args); + _exit(1); +} + +void +sigchld(int a) +{ + int stat; + pid_t p; + + if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + + if (pid != p) + return; + + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + die("child exited with status %d\n", WEXITSTATUS(stat)); + else if (WIFSIGNALED(stat)) + die("child terminated due to signal %d\n", WTERMSIG(stat)); + _exit(0); +} + +void +stty(char **args) +{ + char cmd[_POSIX_ARG_MAX], **p, *q, *s; + size_t n, siz; + + if ((n = strlen(stty_args)) > sizeof(cmd)-1) + die("incorrect stty parameters\n"); + memcpy(cmd, stty_args, n); + q = cmd + n; + siz = sizeof(cmd) - n; + for (p = args; p && (s = *p); ++p) { + if ((n = strlen(s)) > siz-1) + die("stty parameter length too long\n"); + *q++ = ' '; + memcpy(q, s, n); + q += n; + siz -= n + 1; + } + *q = '\0'; + if (system(cmd) != 0) + perror("Couldn't call stty"); +} + +int +ttynew(const char *line, char *cmd, const char *out, char **args) +{ + int m, s; + + if (out) { + term.mode |= MODE_PRINT; + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); + if (iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + out, strerror(errno)); + } + } + + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) + die("open line '%s' failed: %s\n", + line, strerror(errno)); + dup2(cmdfd, 0); + stty(args); + return cmdfd; + } + + /* seems to work fine on linux, openbsd and freebsd */ + if (openpty(&m, &s, NULL, NULL, NULL) < 0) + die("openpty failed: %s\n", strerror(errno)); + + switch (pid = fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + close(iofd); + close(m); + setsid(); /* create a new process group */ + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + if (ioctl(s, TIOCSCTTY, NULL) < 0) + die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); + if (s > 2) + close(s); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif + execsh(cmd, args); + break; + default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif + close(s); + cmdfd = m; + signal(SIGCHLD, sigchld); + break; + } + return cmdfd; +} + +size_t +ttyread(void) +{ + static char buf[BUFSIZ]; + static int buflen = 0; + int ret, written; + + /* append read bytes to unprocessed bytes */ + ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + + switch (ret) { + case 0: + exit(0); + case -1: + die("couldn't read from shell: %s\n", strerror(errno)); + default: + buflen += ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + return ret; + } +} + +void +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + die("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup(void) +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { + Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { + if (line[j].mode & attr) + return 1; + } + y = (y+1) % TSCREEN.size; + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + int y = TLINEOFFSET(0); + + for (i = 0; i < term.row-1; i++) { + Line line = TSCREEN.buffer[y]; + for (j = 0; j < term.col-1; j++) { + if (line[j].mode & attr) { + tsetdirt(i, i); + break; + } + } + y = (y+1) % TSCREEN.size; + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + if (mode == CURSOR_SAVE) { + TSCREEN.sc = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = TSCREEN.sc; + tmoveto(term.c.x, term.c.y); + } +} + +void +treset(void) +{ + int i, j; + Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = MODE_WRAP|MODE_UTF8; + memset(term.trantbl, CS_USA, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + term.screen[i].sc = (TCursor){{ + .fg = defaultfg, + .bg = defaultbg + }}; + term.screen[i].cur = 0; + term.screen[i].off = 0; + for (j = 0; j < term.row; ++j) { + if (term.col != term.linelen) + term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph)); + clearline(term.screen[i].buffer[j], g, 0, term.col); + } + for (j = term.row; j < term.screen[i].size; ++j) { + free(term.screen[i].buffer[j]); + term.screen[i].buffer[j] = NULL; + } + } + tcursor(CURSOR_LOAD); + term.linelen = term.col; + tfulldirt(); +} + +void +tnew(int col, int row) +{ + int i; + term = (Term){}; + term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line)); + term.screen[0].size = HISTSIZE; + term.screen[1].buffer = NULL; + for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL; + + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +kscrollup(const Arg *a) +{ + int n = a->i; + + if (IS_SET(MODE_ALTSCREEN)) + return; + + if (n < 0) n = (-n) * term.row; + if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off; + while (!TLINE(-n)) --n; + TSCREEN.off += n; + selscroll(0, n); + tfulldirt(); +} + +void +kscrolldown(const Arg *a) +{ + + int n = a->i; + + if (IS_SET(MODE_ALTSCREEN)) + return; + + if (n < 0) n = (-n) * term.row; + if (n > TSCREEN.off) n = TSCREEN.off; + TSCREEN.off -= n; + selscroll(0, -n); + tfulldirt(); +} + +void +tscrolldown(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + /* Ensure that lines are allocated */ + for (i = -n; i < 0; i++) { + TLINE(i) = ensureline(TLINE(i)); + } + + /* Shift non-scrolling areas in ring buffer */ + for (i = term.bot+1; i < term.row; i++) { + temp = TLINE(i); + TLINE(i) = TLINE(i-n); + TLINE(i-n) = temp; + } + for (i = 0; i < orig; i++) { + temp = TLINE(i); + TLINE(i) = TLINE(i-n); + TLINE(i-n) = temp; + } + + /* Scroll buffer */ + TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size; + /* Clear lines that have entered the view */ + tclearregion(0, orig, term.linelen-1, orig+n-1); + /* Redraw portion of the screen that has scrolled */ + tsetdirt(orig+n-1, term.bot); + selscroll(orig, n); +} + +void +tscrollup(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + /* Ensure that lines are allocated */ + for (i = term.row; i < term.row + n; i++) { + TLINE(i) = ensureline(TLINE(i)); + } + + /* Shift non-scrolling areas in ring buffer */ + for (i = orig-1; i >= 0; i--) { + temp = TLINE(i); + TLINE(i) = TLINE(i+n); + TLINE(i+n) = temp; + } + for (i = term.row-1; i >term.bot; i--) { + temp = TLINE(i); + TLINE(i) = TLINE(i+n); + TLINE(i+n) = temp; + } + + /* Scroll buffer */ + TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size; + /* Clear lines that have entered the view */ + tclearregion(0, term.bot-n+1, term.linelen-1, term.bot); + /* Redraw portion of the screen that has scrolled */ + tsetdirt(orig, term.bot-n+1); + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { + selclear(); + } else if (BETWEEN(sel.nb.y, orig, term.bot)) { + sel.ob.y += n; + sel.oe.y += n; + if (sel.ob.y < term.top || sel.ob.y > term.bot || + sel.oe.y < term.top || sel.oe.y > term.bot) { + selclear(); + } else { + selnormalize(); + } + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + + csiescseq.narg = 0; + if (*p == '?') { + csiescseq.priv = 1; + p++; + } + + csiescseq.buf[csiescseq.len] = '\0'; + while (p < csiescseq.buf+csiescseq.len) { + np = NULL; + v = strtol(p, &np, 10); + if (np == p) + v = 0; + if (v == LONG_MAX || v == LONG_MIN) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; + } + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; +} + +/* for absolute user moves, when decom is set */ +void +tmoveato(int x, int y) +{ + tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); +} + +void +tmoveto(int x, int y) +{ + int miny, maxy; + + if (term.c.state & CURSOR_ORIGIN) { + miny = term.top; + maxy = term.bot; + } else { + miny = 0; + maxy = term.row - 1; + } + term.c.state &= ~CURSOR_WRAPNEXT; + term.c.x = LIMIT(x, 0, term.col-1); + term.c.y = LIMIT(y, miny, maxy); +} + +void +tsetchar(Rune u, const Glyph *attr, int x, int y) +{ + static const char *vt100_0[62] = { /* 0x41 - 0x7e */ + "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + Line line = TLINE(y); + + /* + * The table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CS_GRAPHIC0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + + if (line[x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + line[x+1].u = ' '; + line[x+1].mode &= ~ATTR_WDUMMY; + } + } else if (line[x].mode & ATTR_WDUMMY) { + line[x-1].u = ' '; + line[x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + line[x] = *attr; + line[x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, L, S, temp; + Glyph *gp; + + if (x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.linelen-1); + LIMIT(x2, 0, term.linelen-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + L = TLINEOFFSET(y1); + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &TSCREEN.buffer[L][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + L = (L + 1) % TSCREEN.size; + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = TLINE(term.c.y); + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = TLINE(term.c.y); + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n); +} + +int32_t +tdefcolor(const int *attr, int *npar, int l) +{ + int32_t idx = -1; + uint r, g, b; + + switch (attr[*npar + 1]) { + case 2: /* direct color in RGB space */ + if (*npar + 4 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + r = attr[*npar + 2]; + g = attr[*npar + 3]; + b = attr[*npar + 4]; + *npar += 4; + if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) + fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", + r, g, b); + else + idx = TRUECOLOR(r, g, b); + break; + case 5: /* indexed color */ + if (*npar + 2 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + *npar += 2; + if (!BETWEEN(attr[*npar], 0, 255)) + fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); + else + idx = attr[*npar]; + break; + case 0: /* implemented defined (only foreground) */ + case 1: /* transparent */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ + default: + fprintf(stderr, + "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; + } + + return idx; +} + +void +tsetattr(const int *attr, int l) +{ + int i; + int32_t idx; + + for (i = 0; i < l; i++) { + switch (attr[i]) { + case 0: + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; + break; + case 1: + term.c.attr.mode |= ATTR_BOLD; + break; + case 2: + term.c.attr.mode |= ATTR_FAINT; + break; + case 3: + term.c.attr.mode |= ATTR_ITALIC; + break; + case 4: + term.c.attr.mode |= ATTR_UNDERLINE; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ + case 6: /* rapid blink */ + term.c.attr.mode |= ATTR_BLINK; + break; + case 7: + term.c.attr.mode |= ATTR_REVERSE; + break; + case 8: + term.c.attr.mode |= ATTR_INVISIBLE; + break; + case 9: + term.c.attr.mode |= ATTR_STRUCK; + break; + case 22: + term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); + break; + case 23: + term.c.attr.mode &= ~ATTR_ITALIC; + break; + case 24: + term.c.attr.mode &= ~ATTR_UNDERLINE; + break; + case 25: + term.c.attr.mode &= ~ATTR_BLINK; + break; + case 27: + term.c.attr.mode &= ~ATTR_REVERSE; + break; + case 28: + term.c.attr.mode &= ~ATTR_INVISIBLE; + break; + case 29: + term.c.attr.mode &= ~ATTR_STRUCK; + break; + case 38: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.fg = idx; + break; + case 39: + term.c.attr.fg = defaultfg; + break; + case 48: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.bg = idx; + break; + case 49: + term.c.attr.bg = defaultbg; + break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; + } else if (BETWEEN(attr[i], 40, 47)) { + term.c.attr.bg = attr[i] - 40; + } else if (BETWEEN(attr[i], 90, 97)) { + term.c.attr.fg = attr[i] - 90 + 8; + } else if (BETWEEN(attr[i], 100, 107)) { + term.c.attr.bg = attr[i] - 100 + 8; + } else { + fprintf(stderr, + "erresc(default): gfx attr %d unknown\n", + attr[i]); + csidump(); + } + break; + } + } +} + +void +tsetscroll(int t, int b) +{ + int temp; + + LIMIT(t, 0, term.row-1); + LIMIT(b, 0, term.row-1); + if (t > b) { + temp = t; + t = b; + b = temp; + } + term.top = t; + term.bot = b; +} + +void +tsetmode(int priv, int set, const int *args, int narg) +{ + int alt; const int *lim; + + for (lim = args + narg; args < lim; ++args) { + if (priv) { + switch (*args) { + case 1: /* DECCKM -- Cursor key */ + xsetmode(set, MODE_APPCURSOR); + break; + case 5: /* DECSCNM -- Reverse video */ + xsetmode(set, MODE_REVERSE); + break; + case 6: /* DECOM -- Origin */ + MODBIT(term.c.state, set, CURSOR_ORIGIN); + tmoveato(0, 0); + break; + case 7: /* DECAWM -- Auto wrap */ + MODBIT(term.mode, set, MODE_WRAP); + break; + case 0: /* Error (IGNORED) */ + case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ + case 3: /* DECCOLM -- Column (IGNORED) */ + case 4: /* DECSCLM -- Scroll (IGNORED) */ + case 8: /* DECARM -- Auto repeat (IGNORED) */ + case 18: /* DECPFF -- Printer feed (IGNORED) */ + case 19: /* DECPEX -- Printer extent (IGNORED) */ + case 42: /* DECNRCM -- National characters (IGNORED) */ + case 12: /* att610 -- Start blinking cursor (IGNORED) */ + break; + case 25: /* DECTCEM -- Text Cursor Enable Mode */ + xsetmode(!set, MODE_HIDE); + break; + case 9: /* X10 mouse compatibility mode */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEX10); + break; + case 1000: /* 1000: report button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEBTN); + break; + case 1002: /* 1002: report motion on button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMOTION); + break; + case 1003: /* 1003: enable all mouse motions */ + xsetpointermotion(set); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMANY); + break; + case 1004: /* 1004: send focus events to tty */ + xsetmode(set, MODE_FOCUS); + break; + case 1006: /* 1006: extended reporting mode */ + xsetmode(set, MODE_MOUSESGR); + break; + case 1034: + xsetmode(set, MODE_8BIT); + break; + case 1049: /* swap screen & set/restore cursor as xterm */ + if (!allowaltscreen) + break; + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + /* FALLTHROUGH */ + case 47: /* swap screen */ + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(MODE_ALTSCREEN); + if (alt) { + tclearregion(0, 0, term.col-1, + term.row-1); + } + if (set ^ alt) /* set is always 1 or 0 */ + tswapscreen(); + if (*args != 1049) + break; + /* FALLTHROUGH */ + case 1048: + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + break; + case 2004: /* 2004: bracketed paste mode */ + xsetmode(set, MODE_BRCKTPASTE); + break; + /* Not implemented mouse modes. See comments there. */ + case 1001: /* mouse highlight mode; can hang the + terminal by design when implemented. */ + case 1005: /* UTF-8 mouse mode; will confuse + applications not supporting UTF-8 + and luit. */ + case 1015: /* urxvt mangled mouse mode; incompatible + and can be mistaken for other control + codes. */ + break; + default: + fprintf(stderr, + "erresc: unknown private set/reset mode %d\n", + *args); + break; + } + } else { + switch (*args) { + case 0: /* Error (IGNORED) */ + break; + case 2: + xsetmode(set, MODE_KBDLOCK); + break; + case 4: /* IRM -- Insertion-replacement */ + MODBIT(term.mode, set, MODE_INSERT); + break; + case 12: /* SRM -- Send/Receive */ + MODBIT(term.mode, !set, MODE_ECHO); + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, MODE_CRLF); + break; + default: + fprintf(stderr, + "erresc: unknown set/reset mode %d\n", + *args); + break; + } + } + } +} + +void +csihandle(void) +{ + char buf[40]; + int len; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case '@': /* ICH -- Insert blank char */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblank(csiescseq.arg[0]); + break; + case 'A': /* CUU -- Cursor Up */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); + break; + case 'B': /* CUD -- Cursor Down */ + case 'e': /* VPR --Cursor Down */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); + break; + case 'i': /* MC -- Media Copy */ + switch (csiescseq.arg[0]) { + case 0: + tdump(); + break; + case 1: + tdumpline(term.c.y); + break; + case 2: + tdumpsel(); + break; + case 4: + term.mode &= ~MODE_PRINT; + break; + case 5: + term.mode |= MODE_PRINT; + break; + } + break; + case 'c': /* DA -- Device Attributes */ + if (csiescseq.arg[0] == 0) + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; + case 'C': /* CUF -- Cursor Forward */ + case 'a': /* HPR -- Cursor Forward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x+csiescseq.arg[0], term.c.y); + break; + case 'D': /* CUB -- Cursor Backward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x-csiescseq.arg[0], term.c.y); + break; + case 'E': /* CNL -- Cursor Down and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y+csiescseq.arg[0]); + break; + case 'F': /* CPL -- Cursor Up and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y-csiescseq.arg[0]); + break; + case 'g': /* TBC -- Tabulation clear */ + switch (csiescseq.arg[0]) { + case 0: /* clear current tab stop */ + term.tabs[term.c.x] = 0; + break; + case 3: /* clear all the tabs */ + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + break; + default: + goto unknown; + } + break; + case 'G': /* CHA -- Move to */ + case '`': /* HPA */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(csiescseq.arg[0]-1, term.c.y); + break; + case 'H': /* CUP -- Move to */ + case 'f': /* HVP */ + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], 1); + tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); + break; + case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(csiescseq.arg[0]); + break; + case 'J': /* ED -- Clear screen */ + switch (csiescseq.arg[0]) { + case 0: /* below */ + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + if (term.c.y < term.row-1) { + tclearregion(0, term.c.y+1, term.col-1, + term.row-1); + } + break; + case 1: /* above */ + if (term.c.y > 1) + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, 0, term.col-1, term.row-1); + break; + default: + goto unknown; + } + break; + case 'K': /* EL -- Clear line */ + switch (csiescseq.arg[0]) { + case 0: /* right */ + tclearregion(term.c.x, term.c.y, term.col-1, + term.c.y); + break; + case 1: /* left */ + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, term.c.y, term.col-1, term.c.y); + break; + } + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0]); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0]); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR -- Device Status Report */ + switch (csiescseq.arg[0]) { + case 5: /* Status Report "OK" `0n` */ + ttywrite("\033[0n", sizeof("\033[0n") - 1, 0); + break; + case 6: /* Report Cursor Position (CPR) ";R" */ + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + break; + default: + goto unknown; + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC["); + for (i = 0; i < csiescseq.len; i++) { + c = csiescseq.buf[i] & 0xff; + if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + putc('\n', stderr); +} + +void +csireset(void) +{ + memset(&csiescseq, 0, sizeof(csiescseq)); +} + +void +osc_color_response(int num, int index, int is_osc4) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch %s color %d\n", + is_osc4 ? "osc4" : "osc", + is_osc4 ? num : index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + is_osc4 ? "4;" : "", num, r, r, g, g, b, b); + if (n < 0 || n >= sizeof(buf)) { + fprintf(stderr, "error: %s while printing %s response\n", + n < 0 ? "snprintf failed" : "truncation occurred", + is_osc4 ? "osc4" : "osc"); + } else { + ttywrite(buf, n, 1); + } +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + const struct { int idx; char *str; } osc_table[] = { + { defaultfg, "foreground" }, + { defaultbg, "background" }, + { defaultcs, "cursor" } + }; + + term.esc &= ~(ESC_STR_END|ESC_STR); + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; + + switch (strescseq.type) { + case ']': /* OSC -- Operating System Command */ + switch (par) { + case 0: + if (narg > 1) { + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); + } + return; + case 1: + if (narg > 1) + xseticontitle(strescseq.args[1]); + return; + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 10: + case 11: + case 12: + if (narg < 2) + break; + p = strescseq.args[1]; + if ((j = par - 10) < 0 || j >= LEN(osc_table)) + break; /* shouldn't be possible */ + + if (!strcmp(p, "?")) { + osc_color_response(par, osc_table[j].idx, 0); + } else if (xsetcolorname(osc_table[j].idx, p)) { + fprintf(stderr, "erresc: invalid %s color: %s\n", + osc_table[j].str, p); + } else { + tfulldirt(); + } + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + + if (p && !strcmp(p, "?")) { + osc_color_response(j, 0, 1); + } else if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) { + xloadcols(); + return; /* color reset without parameter */ + } + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); + } else { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + tfulldirt(); + } + return; + } + break; + case 'k': /* old title set compatibility */ + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; + } + + fprintf(stderr, "erresc: unknown str "); + strdump(); +} + +void +strparse(void) +{ + int c; + char *p = strescseq.buf; + + strescseq.narg = 0; + strescseq.buf[strescseq.len] = '\0'; + + if (*p == '\0') + return; + + while (strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while ((c = *p) != ';' && c != '\0') + ++p; + if (c == '\0') + return; + *p++ = '\0'; + } +} + +void +strdump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC%c", strescseq.type); + for (i = 0; i < strescseq.len; i++) { + c = strescseq.buf[i] & 0xff; + if (c == '\0') { + putc('\n', stderr); + return; + } else if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + fprintf(stderr, "ESC\\\n"); +} + +void +strreset(void) +{ + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; +} + +void +sendbreak(const Arg *arg) +{ + if (tcsendbreak(cmdfd, 0)) + perror("Error sending break"); +} + +void +tprinter(char *s, size_t len) +{ + if (iofd != -1 && xwrite(iofd, s, len) < 0) { + perror("Error writing to output file"); + close(iofd); + iofd = -1; + } +} + +void +toggleprinter(const Arg *arg) +{ + term.mode ^= MODE_PRINT; +} + +void +printscreen(const Arg *arg) +{ + tdump(); +} + +void +printsel(const Arg *arg) +{ + tdumpsel(); +} + +void +tdumpsel(void) +{ + char *ptr; + + if ((ptr = getsel())) { + tprinter(ptr, strlen(ptr)); + free(ptr); + } +} + +void +tdumpline(int n) +{ + char buf[UTF_SIZ]; + const Glyph *bp, *end; + + bp = &TLINE(n)[0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) + tprinter(buf, utf8encode(bp->u, buf)); + } + tprinter("\n", 1); +} + +void +tdump(void) +{ + int i; + + for (i = 0; i < term.row; ++i) + tdumpline(i); +} + +void +tputtab(int n) +{ + uint x = term.c.x; + + if (n > 0) { + while (x < term.col && n--) + for (++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if (n < 0) { + while (x > 0 && n++) + for (--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; + } + term.c.x = LIMIT(x, 0, term.col-1); +} + +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= MODE_UTF8; + else if (ascii == '@') + term.mode &= ~MODE_UTF8; +} + +void +tdeftran(char ascii) +{ + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; + + if ((p = strchr(cs, ascii)) == NULL) { + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) +{ + int x, y; + + if (c == '8') { /* DEC screen alignment test. */ + for (x = 0; x < term.col; ++x) { + for (y = 0; y < term.row; ++y) + tsetchar('E', &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) +{ + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; +} + +void +tcontrolcode(uchar ascii) +{ + switch (ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + return; + case '\a': /* BEL */ + if (term.esc & ESC_STR_END) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + xbell(); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return; + case '\016': /* SO (LS1 -- Locking shift 1) */ + case '\017': /* SI (LS0 -- Locking shift 0) */ + term.charset = 1 - (ascii - '\016'); + return; + case '\032': /* SUB */ + tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= ESC_ALTCHARSET; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (u < 127 || !IS_SET(MODE_UTF8)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) + width = 1; + } + + if (IS_SET(MODE_PRINT)) + tprinter(c, len); + + /* + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. + */ + if (term.esc & ESC_STR) { + if (u == '\a' || u == 030 || u == 032 || u == 033 || + ISCONTROLC1(u)) { + term.esc &= ~(ESC_START|ESC_STR); + term.esc |= ESC_STR_END; + goto check_control_code; + } + + if (strescseq.len+len >= strescseq.siz) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); + } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } + +check_control_code: + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if (control) { + /* in UTF-8 mode ignore handling C1 control characters */ + if (IS_SET(MODE_UTF8) && ISCONTROLC1(u)) + return; + tcontrolcode(u); + /* + * control codes are not shown ever + */ + if (!term.esc) + term.lastc = 0; + return; + } else if (term.esc & ESC_START) { + if (term.esc & ESC_CSI) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + term.esc = 0; + csiparse(); + csihandle(); + } + return; + } else if (term.esc & ESC_UTF8) { + tdefutf8(u); + } else if (term.esc & ESC_ALTCHARSET) { + tdeftran(u); + } else if (term.esc & ESC_TEST) { + tdectest(u); + } else { + if (!eschandle(u)) + return; + /* sequence already finished */ + } + term.esc = 0; + /* + * All characters which form part of a sequence are not + * printed + */ + return; + } + if (selected(term.c.x, term.c.y)) + selclear(); + + gp = &TLINE(term.c.y)[term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); + gp = &TLINE(term.c.y)[term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { + memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + gp->mode &= ~ATTR_WIDE; + } + + if (term.c.x+width > term.col) { + tnewline(1); + gp = &TLINE(term.c.y)[term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; + + if (width == 2) { + gp->mode |= ATTR_WIDE; + if (term.c.x+1 < term.col) { + if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { + gp[2].u = ' '; + gp[2].mode &= ~ATTR_WDUMMY; + } + gp[1].u = '\0'; + gp[1].mode = ATTR_WDUMMY; + } + } + if (term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); + } else { + term.c.state |= CURSOR_WRAPNEXT; + } +} + +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + if (TSCREEN.off) { + TSCREEN.off = 0; + tfulldirt(); + } + + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +clearline(Line line, Glyph g, int x, int xend) +{ + int i; + g.mode = 0; + g.u = ' '; + for (i = x; i < xend; ++i) { + line[i] = g; + } +} + +Line +ensureline(Line line) +{ + if (!line) { + line = xmalloc(term.linelen * sizeof(Glyph)); + } + return line; +} + +void +tresize(int col, int row) +{ + int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int linelen = MAX(col, term.linelen); + int *bp; + + if (col < 1 || row < 1 || row > HISTSIZE) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* Shift buffer to keep the cursor where we expect it */ + if (row <= term.c.y) { + term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size; + } + + /* Resize and clear line buffers as needed */ + if (linelen > term.linelen) { + for (i = 0; i < term.screen[0].size; ++i) { + if (term.screen[0].buffer[i]) { + term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph)); + clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen); + } + } + for (i = 0; i < minrow; ++i) { + term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph)); + clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen); + } + } + /* Allocate all visible lines for regular line buffer */ + for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size) + { + if (!term.screen[0].buffer[j]) { + term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph)); + } + if (i >= term.row) { + clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen); + } + } + /* Resize alt screen */ + term.screen[1].cur = 0; + term.screen[1].size = row; + for (i = row; i < term.row; ++i) { + free(term.screen[1].buffer[i]); + } + term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line)); + for (i = term.row; i < row; ++i) { + term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph)); + clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen); + } + + /* resize to new height */ + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + /* fix tabstops */ + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + + /* update terminal size */ + term.col = col; + term.row = row; + term.linelen = linelen; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + tfulldirt(); +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y, L; + + L = TLINEOFFSET(y1); + for (y = y1; y < y2; y++) { + if (term.dirty[y]) { + term.dirty[y] = 0; + xdrawline(TSCREEN.buffer[L], x1, y, x2); + } + L = (L + 1) % TSCREEN.size; + } +} + +void +draw(void) +{ + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + if (TSCREEN.off == 0) + xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], + term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} diff --git a/st/st.c.orig b/st/st.c.orig new file mode 100644 index 0000000..623376e --- /dev/null +++ b/st/st.c.orig @@ -0,0 +1,2670 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st.h" +#include "win.h" + +#if defined(__linux) + #include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) + #include +#elif defined(__FreeBSD__) || defined(__DragonFly__) + #include +#endif + +/* Arbitrary sizes */ +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 +#define ESC_BUF_SIZ (128*UTF_SIZ) +#define ESC_ARG_SIZ 16 +#define STR_BUF_SIZ ESC_BUF_SIZ +#define STR_ARG_SIZ ESC_ARG_SIZ + +/* macros */ +#define IS_SET(flag) ((term.mode & (flag)) != 0) +#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f) +#define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) +#define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) +#define ISDELIM(u) (u && wcschr(worddelimiters, u)) + +enum term_mode { + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, +}; + +enum cursor_movement { + CURSOR_SAVE, + CURSOR_LOAD +}; + +enum cursor_state { + CURSOR_DEFAULT = 0, + CURSOR_WRAPNEXT = 1, + CURSOR_ORIGIN = 2 +}; + +enum charset { + CS_GRAPHIC0, + CS_GRAPHIC1, + CS_UK, + CS_USA, + CS_MULTI, + CS_GER, + CS_FIN +}; + +enum escape_state { + ESC_START = 1, + ESC_CSI = 2, + ESC_STR = 4, /* DCS, OSC, PM, APC */ + ESC_ALTCHARSET = 8, + ESC_STR_END = 16, /* a final string was encountered */ + ESC_TEST = 32, /* Enter in test mode */ + ESC_UTF8 = 64, +}; + +typedef struct { + Glyph attr; /* current char attributes */ + int x; + int y; + char state; +} TCursor; + +typedef struct { + int mode; + int type; + int snap; + /* + * Selection variables: + * nb – normalized coordinates of the beginning of the selection + * ne – normalized coordinates of the end of the selection + * ob – original coordinates of the beginning of the selection + * oe – original coordinates of the end of the selection + */ + struct { + int x, y; + } nb, ne, ob, oe; + + int alt; +} Selection; + +/* Internal representation of the screen */ +typedef struct { + int row; /* nb row */ + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ + int ocy; /* old cursor row */ + int top; /* top scroll limit */ + int bot; /* bottom scroll limit */ + int mode; /* terminal mode flags */ + int esc; /* escape state flags */ + char trantbl[4]; /* charset table translation */ + int charset; /* current charset */ + int icharset; /* selected charset for sequence */ + int *tabs; + Rune lastc; /* last printed char outside of sequence, 0 if control */ +} Term; + +/* CSI Escape sequence structs */ +/* ESC '[' [[ [] [;]] []] */ +typedef struct { + char buf[ESC_BUF_SIZ]; /* raw string */ + size_t len; /* raw string length */ + char priv; + int arg[ESC_ARG_SIZ]; + int narg; /* nb of args */ + char mode[2]; +} CSIEscape; + +/* STR Escape sequence structs */ +/* ESC type [[ [] [;]] ] ESC '\' */ +typedef struct { + char type; /* ESC type ... */ + char *buf; /* allocated raw string */ + size_t siz; /* allocation size */ + size_t len; /* raw string length */ + char *args[STR_ARG_SIZ]; + int narg; /* nb of args */ +} STREscape; + +static void execsh(char *, char **); +static void stty(char **); +static void sigchld(int); +static void ttywriteraw(const char *, size_t); + +static void csidump(void); +static void csihandle(void); +static void csiparse(void); +static void csireset(void); +static void osc_color_response(int, int, int); +static int eschandle(uchar); +static void strdump(void); +static void strhandle(void); +static void strparse(void); +static void strreset(void); + +static void tprinter(char *, size_t); +static void tdumpsel(void); +static void tdumpline(int); +static void tdump(void); +static void tclearregion(int, int, int, int); +static void tcursor(int); +static void tdeletechar(int); +static void tdeleteline(int); +static void tinsertblank(int); +static void tinsertblankline(int); +static int tlinelen(int); +static void tmoveto(int, int); +static void tmoveato(int, int); +static void tnewline(int); +static void tputtab(int); +static void tputc(Rune); +static void treset(void); +static void tscrollup(int, int); +static void tscrolldown(int, int); +static void tsetattr(const int *, int); +static void tsetchar(Rune, const Glyph *, int, int); +static void tsetdirt(int, int); +static void tsetscroll(int, int); +static void tswapscreen(void); +static void tsetmode(int, int, const int *, int); +static int twrite(const char *, int, int); +static void tfulldirt(void); +static void tcontrolcode(uchar ); +static void tdectest(char ); +static void tdefutf8(char); +static int32_t tdefcolor(const int *, int *, int); +static void tdeftran(char); +static void tstrsequence(uchar); + +static void drawregion(int, int, int, int); + +static void selnormalize(void); +static void selscroll(int, int); +static void selsnap(int *, int *, int); + +static size_t utf8decode(const char *, Rune *, size_t); +static Rune utf8decodebyte(char, size_t *); +static char utf8encodebyte(Rune, size_t); +static size_t utf8validate(Rune *, size_t); + +static char *base64dec(const char *); +static char base64dec_getc(const char **); + +static ssize_t xwrite(int, const char *, size_t); + +/* Globals */ +static Term term; +static Selection sel; +static CSIEscape csiescseq; +static STREscape strescseq; +static int iofd = 1; +static int cmdfd; +static pid_t pid; + +static const uchar utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +ssize_t +xwrite(int fd, const char *s, size_t len) +{ + size_t aux = len; + ssize_t r; + + while (len > 0) { + r = write(fd, s, len); + if (r < 0) + return r; + len -= r; + s += r; + } + + return aux; +} + +void * +xmalloc(size_t len) +{ + void *p; + + if (!(p = malloc(len))) + die("malloc: %s\n", strerror(errno)); + + return p; +} + +void * +xrealloc(void *p, size_t len) +{ + if ((p = realloc(p, len)) == NULL) + die("realloc: %s\n", strerror(errno)); + + return p; +} + +char * +xstrdup(const char *s) +{ + char *p; + + if ((p = strdup(s)) == NULL) + die("strdup: %s\n", strerror(errno)); + + return p; +} + +size_t +utf8decode(const char *c, Rune *u, size_t clen) +{ + size_t i, j, len, type; + Rune 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 != 0) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Rune +utf8decodebyte(char c, size_t *i) +{ + for (*i = 0; *i < LEN(utfmask); ++(*i)) + if (((uchar)c & utfmask[*i]) == utfbyte[*i]) + return (uchar)c & ~utfmask[*i]; + + return 0; +} + +size_t +utf8encode(Rune u, char *c) +{ + size_t len, i; + + len = utf8validate(&u, 0); + if (len > UTF_SIZ) + return 0; + + for (i = len - 1; i != 0; --i) { + c[i] = utf8encodebyte(u, 0); + u >>= 6; + } + c[0] = utf8encodebyte(u, len); + + return len; +} + +char +utf8encodebyte(Rune u, size_t i) +{ + return utfbyte[i] | (u & ~utfmask[i]); +} + +size_t +utf8validate(Rune *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; +} + +char +base64dec_getc(const char **src) +{ + while (**src && !isprint((unsigned char)**src)) + (*src)++; + return **src ? *((*src)++) : '='; /* emulate padding if string ends */ +} + +char * +base64dec(const char *src) +{ + size_t in_len = strlen(src); + char *result, *dst; + static const char base64_digits[256] = { + [43] = 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 0, 0, 0, -1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, + 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + if (in_len % 4) + in_len += 4 - (in_len % 4); + result = dst = xmalloc(in_len / 4 * 3 + 1); + while (*src) { + int a = base64_digits[(unsigned char) base64dec_getc(&src)]; + int b = base64_digits[(unsigned char) base64dec_getc(&src)]; + int c = base64_digits[(unsigned char) base64dec_getc(&src)]; + int d = base64_digits[(unsigned char) base64dec_getc(&src)]; + + /* invalid input. 'a' can be -1, e.g. if src is "\n" (c-str) */ + if (a == -1 || b == -1) + break; + + *dst++ = (a << 2) | ((b & 0x30) >> 4); + if (c == -1) + break; + *dst++ = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + if (d == -1) + break; + *dst++ = ((c & 0x03) << 6) | d; + } + *dst = '\0'; + return result; +} + +void +selinit(void) +{ + sel.mode = SEL_IDLE; + sel.snap = 0; + sel.ob.x = -1; +} + +int +tlinelen(int y) +{ + int i = term.col; + + if (term.line[y][i - 1].mode & ATTR_WRAP) + return i; + + while (i > 0 && term.line[y][i - 1].u == ' ') + --i; + + return i; +} + +void +selstart(int col, int row, int snap) +{ + selclear(); + sel.mode = SEL_EMPTY; + sel.type = SEL_REGULAR; + sel.alt = IS_SET(MODE_ALTSCREEN); + sel.snap = snap; + sel.oe.x = sel.ob.x = col; + sel.oe.y = sel.ob.y = row; + selnormalize(); + + if (sel.snap != 0) + sel.mode = SEL_READY; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selextend(int col, int row, int type, int done) +{ + int oldey, oldex, oldsby, oldsey, oldtype; + + if (sel.mode == SEL_IDLE) + return; + if (done && sel.mode == SEL_EMPTY) { + selclear(); + return; + } + + oldey = sel.oe.y; + oldex = sel.oe.x; + oldsby = sel.nb.y; + oldsey = sel.ne.y; + oldtype = sel.type; + + sel.oe.x = col; + sel.oe.y = row; + selnormalize(); + sel.type = type; + + if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) + tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); + + sel.mode = done ? SEL_IDLE : SEL_READY; +} + +void +selnormalize(void) +{ + int i; + + if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { + sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; + sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; + } else { + sel.nb.x = MIN(sel.ob.x, sel.oe.x); + sel.ne.x = MAX(sel.ob.x, sel.oe.x); + } + sel.nb.y = MIN(sel.ob.y, sel.oe.y); + sel.ne.y = MAX(sel.ob.y, sel.oe.y); + + selsnap(&sel.nb.x, &sel.nb.y, -1); + selsnap(&sel.ne.x, &sel.ne.y, +1); + + /* expand selection over line breaks */ + if (sel.type == SEL_RECTANGULAR) + return; + i = tlinelen(sel.nb.y); + if (i < sel.nb.x) + sel.nb.x = i; + if (tlinelen(sel.ne.y) <= sel.ne.x) + sel.ne.x = term.col - 1; +} + +int +selected(int x, int y) +{ + if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || + sel.alt != IS_SET(MODE_ALTSCREEN)) + return 0; + + if (sel.type == SEL_RECTANGULAR) + return BETWEEN(y, sel.nb.y, sel.ne.y) + && BETWEEN(x, sel.nb.x, sel.ne.x); + + return BETWEEN(y, sel.nb.y, sel.ne.y) + && (y != sel.nb.y || x >= sel.nb.x) + && (y != sel.ne.y || x <= sel.ne.x); +} + +void +selsnap(int *x, int *y, int direction) +{ + int newx, newy, xt, yt; + int delim, prevdelim; + const Glyph *gp, *prevgp; + + switch (sel.snap) { + case SNAP_WORD: + /* + * Snap around if the word wraps around at the end or + * beginning of a line. + */ + prevgp = &term.line[*y][*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; + newy = *y; + if (!BETWEEN(newx, 0, term.col - 1)) { + newy += direction; + newx = (newx + term.col) % term.col; + if (!BETWEEN(newy, 0, term.row - 1)) + break; + + if (direction > 0) + yt = *y, xt = *x; + else + yt = newy, xt = newx; + if (!(term.line[yt][xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + + gp = &term.line[newy][newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) + break; + + *x = newx; + *y = newy; + prevgp = gp; + prevdelim = delim; + } + break; + case SNAP_LINE: + /* + * Snap around if the the previous line or the current one + * has set ATTR_WRAP at its end. Then the whole next or + * previous line will be selected. + */ + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { + if (!(term.line[*y-1][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { + if (!(term.line[*y][term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } + break; + } +} + +char * +getsel(void) +{ + char *str, *ptr; + int y, bufsize, lastx, linelen; + const Glyph *gp, *last; + + if (sel.ob.x == -1) + return NULL; + + bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; + ptr = str = xmalloc(bufsize); + + /* append every set & selected glyph to the selection */ + for (y = sel.nb.y; y <= sel.ne.y; y++) { + if ((linelen = tlinelen(y)) == 0) { + *ptr++ = '\n'; + continue; + } + + if (sel.type == SEL_RECTANGULAR) { + gp = &term.line[y][sel.nb.x]; + lastx = sel.ne.x; + } else { + gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } + last = &term.line[y][MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + + for ( ; gp <= last; ++gp) { + if (gp->mode & ATTR_WDUMMY) + continue; + + ptr += utf8encode(gp->u, ptr); + } + + /* + * Copy and pasting of line endings is inconsistent + * in the inconsistent terminal and GUI world. + * The best solution seems like to produce '\n' when + * something is copied from st and convert '\n' to + * '\r', when something to be pasted is received by + * st. + * FIXME: Fix the computer world. + */ + if ((y < sel.ne.y || lastx >= linelen) && + (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) + *ptr++ = '\n'; + } + *ptr = 0; + return str; +} + +void +selclear(void) +{ + if (sel.ob.x == -1) + return; + sel.mode = SEL_IDLE; + sel.ob.x = -1; + tsetdirt(sel.nb.y, sel.ne.y); +} + +void +die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void +execsh(char *cmd, char **args) +{ + char *sh, *prog, *arg; + const struct passwd *pw; + + errno = 0; + if ((pw = getpwuid(getuid())) == NULL) { + if (errno) + die("getpwuid: %s\n", strerror(errno)); + else + die("who are you?\n"); + } + + if ((sh = getenv("SHELL")) == NULL) + sh = (pw->pw_shell[0]) ? pw->pw_shell : cmd; + + if (args) { + prog = args[0]; + arg = NULL; + } else if (scroll) { + prog = scroll; + arg = utmp ? utmp : sh; + } else if (utmp) { + prog = utmp; + arg = NULL; + } else { + prog = sh; + arg = NULL; + } + DEFAULT(args, ((char *[]) {prog, arg, NULL})); + + unsetenv("COLUMNS"); + unsetenv("LINES"); + unsetenv("TERMCAP"); + setenv("LOGNAME", pw->pw_name, 1); + setenv("USER", pw->pw_name, 1); + setenv("SHELL", sh, 1); + setenv("HOME", pw->pw_dir, 1); + setenv("TERM", termname, 1); + + signal(SIGCHLD, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGALRM, SIG_DFL); + + execvp(prog, args); + _exit(1); +} + +void +sigchld(int a) +{ + int stat; + pid_t p; + + if ((p = waitpid(pid, &stat, WNOHANG)) < 0) + die("waiting for pid %hd failed: %s\n", pid, strerror(errno)); + + if (pid != p) + return; + + if (WIFEXITED(stat) && WEXITSTATUS(stat)) + die("child exited with status %d\n", WEXITSTATUS(stat)); + else if (WIFSIGNALED(stat)) + die("child terminated due to signal %d\n", WTERMSIG(stat)); + _exit(0); +} + +void +stty(char **args) +{ + char cmd[_POSIX_ARG_MAX], **p, *q, *s; + size_t n, siz; + + if ((n = strlen(stty_args)) > sizeof(cmd)-1) + die("incorrect stty parameters\n"); + memcpy(cmd, stty_args, n); + q = cmd + n; + siz = sizeof(cmd) - n; + for (p = args; p && (s = *p); ++p) { + if ((n = strlen(s)) > siz-1) + die("stty parameter length too long\n"); + *q++ = ' '; + memcpy(q, s, n); + q += n; + siz -= n + 1; + } + *q = '\0'; + if (system(cmd) != 0) + perror("Couldn't call stty"); +} + +int +ttynew(const char *line, char *cmd, const char *out, char **args) +{ + int m, s; + + if (out) { + term.mode |= MODE_PRINT; + iofd = (!strcmp(out, "-")) ? + 1 : open(out, O_WRONLY | O_CREAT, 0666); + if (iofd < 0) { + fprintf(stderr, "Error opening %s:%s\n", + out, strerror(errno)); + } + } + + if (line) { + if ((cmdfd = open(line, O_RDWR)) < 0) + die("open line '%s' failed: %s\n", + line, strerror(errno)); + dup2(cmdfd, 0); + stty(args); + return cmdfd; + } + + /* seems to work fine on linux, openbsd and freebsd */ + if (openpty(&m, &s, NULL, NULL, NULL) < 0) + die("openpty failed: %s\n", strerror(errno)); + + switch (pid = fork()) { + case -1: + die("fork failed: %s\n", strerror(errno)); + break; + case 0: + close(iofd); + close(m); + setsid(); /* create a new process group */ + dup2(s, 0); + dup2(s, 1); + dup2(s, 2); + if (ioctl(s, TIOCSCTTY, NULL) < 0) + die("ioctl TIOCSCTTY failed: %s\n", strerror(errno)); + if (s > 2) + close(s); +#ifdef __OpenBSD__ + if (pledge("stdio getpw proc exec", NULL) == -1) + die("pledge\n"); +#endif + execsh(cmd, args); + break; + default: +#ifdef __OpenBSD__ + if (pledge("stdio rpath tty proc", NULL) == -1) + die("pledge\n"); +#endif + close(s); + cmdfd = m; + signal(SIGCHLD, sigchld); + break; + } + return cmdfd; +} + +size_t +ttyread(void) +{ + static char buf[BUFSIZ]; + static int buflen = 0; + int ret, written; + + /* append read bytes to unprocessed bytes */ + ret = read(cmdfd, buf+buflen, LEN(buf)-buflen); + + switch (ret) { + case 0: + exit(0); + case -1: + die("couldn't read from shell: %s\n", strerror(errno)); + default: + buflen += ret; + written = twrite(buf, buflen, 0); + buflen -= written; + /* keep any incomplete UTF-8 byte sequence for the next call */ + if (buflen > 0) + memmove(buf, buf + written, buflen); + return ret; + } +} + +void +ttywrite(const char *s, size_t n, int may_echo) +{ + const char *next; + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); + + if (!IS_SET(MODE_CRLF)) { + ttywriteraw(s, n); + return; + } + + /* This is similar to how the kernel handles ONLCR for ttys */ + while (n > 0) { + if (*s == '\r') { + next = s + 1; + ttywriteraw("\r\n", 2); + } else { + next = memchr(s, '\r', n); + DEFAULT(next, s + n); + ttywriteraw(s, next - s); + } + n -= next - s; + s = next; + } +} + +void +ttywriteraw(const char *s, size_t n) +{ + fd_set wfd, rfd; + ssize_t r; + size_t lim = 256; + + /* + * Remember that we are using a pty, which might be a modem line. + * Writing too much will clog the line. That's why we are doing this + * dance. + * FIXME: Migrate the world to Plan 9. + */ + while (n > 0) { + FD_ZERO(&wfd); + FD_ZERO(&rfd); + FD_SET(cmdfd, &wfd); + FD_SET(cmdfd, &rfd); + + /* Check if we can write. */ + if (pselect(cmdfd+1, &rfd, &wfd, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + if (FD_ISSET(cmdfd, &wfd)) { + /* + * Only write the bytes written by ttywrite() or the + * default of 256. This seems to be a reasonable value + * for a serial line. Bigger values might clog the I/O. + */ + if ((r = write(cmdfd, s, (n < lim)? n : lim)) < 0) + goto write_error; + if (r < n) { + /* + * We weren't able to write out everything. + * This means the buffer is getting full + * again. Empty it. + */ + if (n < lim) + lim = ttyread(); + n -= r; + s += r; + } else { + /* All bytes have been written. */ + break; + } + } + if (FD_ISSET(cmdfd, &rfd)) + lim = ttyread(); + } + return; + +write_error: + die("write error on tty: %s\n", strerror(errno)); +} + +void +ttyresize(int tw, int th) +{ + struct winsize w; + + w.ws_row = term.row; + w.ws_col = term.col; + w.ws_xpixel = tw; + w.ws_ypixel = th; + if (ioctl(cmdfd, TIOCSWINSZ, &w) < 0) + fprintf(stderr, "Couldn't set window size: %s\n", strerror(errno)); +} + +void +ttyhangup(void) +{ + /* Send SIGHUP to shell */ + kill(pid, SIGHUP); +} + +int +tattrset(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) + return 1; + } + } + + return 0; +} + +void +tsetdirt(int top, int bot) +{ + int i; + + LIMIT(top, 0, term.row-1); + LIMIT(bot, 0, term.row-1); + + for (i = top; i <= bot; i++) + term.dirty[i] = 1; +} + +void +tsetdirtattr(int attr) +{ + int i, j; + + for (i = 0; i < term.row-1; i++) { + for (j = 0; j < term.col-1; j++) { + if (term.line[i][j].mode & attr) { + tsetdirt(i, i); + break; + } + } + } +} + +void +tfulldirt(void) +{ + tsetdirt(0, term.row-1); +} + +void +tcursor(int mode) +{ + static TCursor c[2]; + int alt = IS_SET(MODE_ALTSCREEN); + + if (mode == CURSOR_SAVE) { + c[alt] = term.c; + } else if (mode == CURSOR_LOAD) { + term.c = c[alt]; + tmoveto(c[alt].x, c[alt].y); + } +} + +void +treset(void) +{ + uint i; + + term.c = (TCursor){{ + .mode = ATTR_NULL, + .fg = defaultfg, + .bg = defaultbg + }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; + + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + for (i = tabspaces; i < term.col; i += tabspaces) + term.tabs[i] = 1; + term.top = 0; + term.bot = term.row - 1; + term.mode = MODE_WRAP|MODE_UTF8; + memset(term.trantbl, CS_USA, sizeof(term.trantbl)); + term.charset = 0; + + for (i = 0; i < 2; i++) { + tmoveto(0, 0); + tcursor(CURSOR_SAVE); + tclearregion(0, 0, term.col-1, term.row-1); + tswapscreen(); + } +} + +void +tnew(int col, int row) +{ + term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; + tresize(col, row); + treset(); +} + +void +tswapscreen(void) +{ + Line *tmp = term.line; + + term.line = term.alt; + term.alt = tmp; + term.mode ^= MODE_ALTSCREEN; + tfulldirt(); +} + +void +tscrolldown(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); + + for (i = term.bot; i >= orig+n; i--) { + temp = term.line[i]; + term.line[i] = term.line[i-n]; + term.line[i-n] = temp; + } + + selscroll(orig, n); +} + +void +tscrollup(int orig, int n) +{ + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + + for (i = orig; i <= term.bot-n; i++) { + temp = term.line[i]; + term.line[i] = term.line[i+n]; + term.line[i+n] = temp; + } + + selscroll(orig, -n); +} + +void +selscroll(int orig, int n) +{ + if (sel.ob.x == -1) + return; + + if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { + selclear(); + } else if (BETWEEN(sel.nb.y, orig, term.bot)) { + sel.ob.y += n; + sel.oe.y += n; + if (sel.ob.y < term.top || sel.ob.y > term.bot || + sel.oe.y < term.top || sel.oe.y > term.bot) { + selclear(); + } else { + selnormalize(); + } + } +} + +void +tnewline(int first_col) +{ + int y = term.c.y; + + if (y == term.bot) { + tscrollup(term.top, 1); + } else { + y++; + } + tmoveto(first_col ? 0 : term.c.x, y); +} + +void +csiparse(void) +{ + char *p = csiescseq.buf, *np; + long int v; + + csiescseq.narg = 0; + if (*p == '?') { + csiescseq.priv = 1; + p++; + } + + csiescseq.buf[csiescseq.len] = '\0'; + while (p < csiescseq.buf+csiescseq.len) { + np = NULL; + v = strtol(p, &np, 10); + if (np == p) + v = 0; + if (v == LONG_MAX || v == LONG_MIN) + v = -1; + csiescseq.arg[csiescseq.narg++] = v; + p = np; + if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) + break; + p++; + } + csiescseq.mode[0] = *p++; + csiescseq.mode[1] = (p < csiescseq.buf+csiescseq.len) ? *p : '\0'; +} + +/* for absolute user moves, when decom is set */ +void +tmoveato(int x, int y) +{ + tmoveto(x, y + ((term.c.state & CURSOR_ORIGIN) ? term.top: 0)); +} + +void +tmoveto(int x, int y) +{ + int miny, maxy; + + if (term.c.state & CURSOR_ORIGIN) { + miny = term.top; + maxy = term.bot; + } else { + miny = 0; + maxy = term.row - 1; + } + term.c.state &= ~CURSOR_WRAPNEXT; + term.c.x = LIMIT(x, 0, term.col-1); + term.c.y = LIMIT(y, miny, maxy); +} + +void +tsetchar(Rune u, const Glyph *attr, int x, int y) +{ + static const char *vt100_0[62] = { /* 0x41 - 0x7e */ + "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ + 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ + 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ + 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ + "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ + "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ + "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ + "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ + }; + + /* + * The table is proudly stolen from rxvt. + */ + if (term.trantbl[term.charset] == CS_GRAPHIC0 && + BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) + utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + + if (term.line[y][x].mode & ATTR_WIDE) { + if (x+1 < term.col) { + term.line[y][x+1].u = ' '; + term.line[y][x+1].mode &= ~ATTR_WDUMMY; + } + } else if (term.line[y][x].mode & ATTR_WDUMMY) { + term.line[y][x-1].u = ' '; + term.line[y][x-1].mode &= ~ATTR_WIDE; + } + + term.dirty[y] = 1; + term.line[y][x] = *attr; + term.line[y][x].u = u; +} + +void +tclearregion(int x1, int y1, int x2, int y2) +{ + int x, y, temp; + Glyph *gp; + + if (x1 > x2) + temp = x1, x1 = x2, x2 = temp; + if (y1 > y2) + temp = y1, y1 = y2, y2 = temp; + + LIMIT(x1, 0, term.col-1); + LIMIT(x2, 0, term.col-1); + LIMIT(y1, 0, term.row-1); + LIMIT(y2, 0, term.row-1); + + for (y = y1; y <= y2; y++) { + term.dirty[y] = 1; + for (x = x1; x <= x2; x++) { + gp = &term.line[y][x]; + if (selected(x, y)) + selclear(); + gp->fg = term.c.attr.fg; + gp->bg = term.c.attr.bg; + gp->mode = 0; + gp->u = ' '; + } + } +} + +void +tdeletechar(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x; + src = term.c.x + n; + size = term.col - src; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +} + +void +tinsertblank(int n) +{ + int dst, src, size; + Glyph *line; + + LIMIT(n, 0, term.col - term.c.x); + + dst = term.c.x + n; + src = term.c.x; + size = term.col - dst; + line = term.line[term.c.y]; + + memmove(&line[dst], &line[src], size * sizeof(Glyph)); + tclearregion(src, term.c.y, dst - 1, term.c.y); +} + +void +tinsertblankline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrolldown(term.c.y, n); +} + +void +tdeleteline(int n) +{ + if (BETWEEN(term.c.y, term.top, term.bot)) + tscrollup(term.c.y, n); +} + +int32_t +tdefcolor(const int *attr, int *npar, int l) +{ + int32_t idx = -1; + uint r, g, b; + + switch (attr[*npar + 1]) { + case 2: /* direct color in RGB space */ + if (*npar + 4 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + r = attr[*npar + 2]; + g = attr[*npar + 3]; + b = attr[*npar + 4]; + *npar += 4; + if (!BETWEEN(r, 0, 255) || !BETWEEN(g, 0, 255) || !BETWEEN(b, 0, 255)) + fprintf(stderr, "erresc: bad rgb color (%u,%u,%u)\n", + r, g, b); + else + idx = TRUECOLOR(r, g, b); + break; + case 5: /* indexed color */ + if (*npar + 2 >= l) { + fprintf(stderr, + "erresc(38): Incorrect number of parameters (%d)\n", + *npar); + break; + } + *npar += 2; + if (!BETWEEN(attr[*npar], 0, 255)) + fprintf(stderr, "erresc: bad fgcolor %d\n", attr[*npar]); + else + idx = attr[*npar]; + break; + case 0: /* implemented defined (only foreground) */ + case 1: /* transparent */ + case 3: /* direct color in CMY space */ + case 4: /* direct color in CMYK space */ + default: + fprintf(stderr, + "erresc(38): gfx attr %d unknown\n", attr[*npar]); + break; + } + + return idx; +} + +void +tsetattr(const int *attr, int l) +{ + int i; + int32_t idx; + + for (i = 0; i < l; i++) { + switch (attr[i]) { + case 0: + term.c.attr.mode &= ~( + ATTR_BOLD | + ATTR_FAINT | + ATTR_ITALIC | + ATTR_UNDERLINE | + ATTR_BLINK | + ATTR_REVERSE | + ATTR_INVISIBLE | + ATTR_STRUCK ); + term.c.attr.fg = defaultfg; + term.c.attr.bg = defaultbg; + break; + case 1: + term.c.attr.mode |= ATTR_BOLD; + break; + case 2: + term.c.attr.mode |= ATTR_FAINT; + break; + case 3: + term.c.attr.mode |= ATTR_ITALIC; + break; + case 4: + term.c.attr.mode |= ATTR_UNDERLINE; + break; + case 5: /* slow blink */ + /* FALLTHROUGH */ + case 6: /* rapid blink */ + term.c.attr.mode |= ATTR_BLINK; + break; + case 7: + term.c.attr.mode |= ATTR_REVERSE; + break; + case 8: + term.c.attr.mode |= ATTR_INVISIBLE; + break; + case 9: + term.c.attr.mode |= ATTR_STRUCK; + break; + case 22: + term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT); + break; + case 23: + term.c.attr.mode &= ~ATTR_ITALIC; + break; + case 24: + term.c.attr.mode &= ~ATTR_UNDERLINE; + break; + case 25: + term.c.attr.mode &= ~ATTR_BLINK; + break; + case 27: + term.c.attr.mode &= ~ATTR_REVERSE; + break; + case 28: + term.c.attr.mode &= ~ATTR_INVISIBLE; + break; + case 29: + term.c.attr.mode &= ~ATTR_STRUCK; + break; + case 38: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.fg = idx; + break; + case 39: + term.c.attr.fg = defaultfg; + break; + case 48: + if ((idx = tdefcolor(attr, &i, l)) >= 0) + term.c.attr.bg = idx; + break; + case 49: + term.c.attr.bg = defaultbg; + break; + default: + if (BETWEEN(attr[i], 30, 37)) { + term.c.attr.fg = attr[i] - 30; + } else if (BETWEEN(attr[i], 40, 47)) { + term.c.attr.bg = attr[i] - 40; + } else if (BETWEEN(attr[i], 90, 97)) { + term.c.attr.fg = attr[i] - 90 + 8; + } else if (BETWEEN(attr[i], 100, 107)) { + term.c.attr.bg = attr[i] - 100 + 8; + } else { + fprintf(stderr, + "erresc(default): gfx attr %d unknown\n", + attr[i]); + csidump(); + } + break; + } + } +} + +void +tsetscroll(int t, int b) +{ + int temp; + + LIMIT(t, 0, term.row-1); + LIMIT(b, 0, term.row-1); + if (t > b) { + temp = t; + t = b; + b = temp; + } + term.top = t; + term.bot = b; +} + +void +tsetmode(int priv, int set, const int *args, int narg) +{ + int alt; const int *lim; + + for (lim = args + narg; args < lim; ++args) { + if (priv) { + switch (*args) { + case 1: /* DECCKM -- Cursor key */ + xsetmode(set, MODE_APPCURSOR); + break; + case 5: /* DECSCNM -- Reverse video */ + xsetmode(set, MODE_REVERSE); + break; + case 6: /* DECOM -- Origin */ + MODBIT(term.c.state, set, CURSOR_ORIGIN); + tmoveato(0, 0); + break; + case 7: /* DECAWM -- Auto wrap */ + MODBIT(term.mode, set, MODE_WRAP); + break; + case 0: /* Error (IGNORED) */ + case 2: /* DECANM -- ANSI/VT52 (IGNORED) */ + case 3: /* DECCOLM -- Column (IGNORED) */ + case 4: /* DECSCLM -- Scroll (IGNORED) */ + case 8: /* DECARM -- Auto repeat (IGNORED) */ + case 18: /* DECPFF -- Printer feed (IGNORED) */ + case 19: /* DECPEX -- Printer extent (IGNORED) */ + case 42: /* DECNRCM -- National characters (IGNORED) */ + case 12: /* att610 -- Start blinking cursor (IGNORED) */ + break; + case 25: /* DECTCEM -- Text Cursor Enable Mode */ + xsetmode(!set, MODE_HIDE); + break; + case 9: /* X10 mouse compatibility mode */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEX10); + break; + case 1000: /* 1000: report button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEBTN); + break; + case 1002: /* 1002: report motion on button press */ + xsetpointermotion(0); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMOTION); + break; + case 1003: /* 1003: enable all mouse motions */ + xsetpointermotion(set); + xsetmode(0, MODE_MOUSE); + xsetmode(set, MODE_MOUSEMANY); + break; + case 1004: /* 1004: send focus events to tty */ + xsetmode(set, MODE_FOCUS); + break; + case 1006: /* 1006: extended reporting mode */ + xsetmode(set, MODE_MOUSESGR); + break; + case 1034: + xsetmode(set, MODE_8BIT); + break; + case 1049: /* swap screen & set/restore cursor as xterm */ + if (!allowaltscreen) + break; + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + /* FALLTHROUGH */ + case 47: /* swap screen */ + case 1047: + if (!allowaltscreen) + break; + alt = IS_SET(MODE_ALTSCREEN); + if (alt) { + tclearregion(0, 0, term.col-1, + term.row-1); + } + if (set ^ alt) /* set is always 1 or 0 */ + tswapscreen(); + if (*args != 1049) + break; + /* FALLTHROUGH */ + case 1048: + tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); + break; + case 2004: /* 2004: bracketed paste mode */ + xsetmode(set, MODE_BRCKTPASTE); + break; + /* Not implemented mouse modes. See comments there. */ + case 1001: /* mouse highlight mode; can hang the + terminal by design when implemented. */ + case 1005: /* UTF-8 mouse mode; will confuse + applications not supporting UTF-8 + and luit. */ + case 1015: /* urxvt mangled mouse mode; incompatible + and can be mistaken for other control + codes. */ + break; + default: + fprintf(stderr, + "erresc: unknown private set/reset mode %d\n", + *args); + break; + } + } else { + switch (*args) { + case 0: /* Error (IGNORED) */ + break; + case 2: + xsetmode(set, MODE_KBDLOCK); + break; + case 4: /* IRM -- Insertion-replacement */ + MODBIT(term.mode, set, MODE_INSERT); + break; + case 12: /* SRM -- Send/Receive */ + MODBIT(term.mode, !set, MODE_ECHO); + break; + case 20: /* LNM -- Linefeed/new line */ + MODBIT(term.mode, set, MODE_CRLF); + break; + default: + fprintf(stderr, + "erresc: unknown set/reset mode %d\n", + *args); + break; + } + } + } +} + +void +csihandle(void) +{ + char buf[40]; + int len; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + case '@': /* ICH -- Insert blank char */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblank(csiescseq.arg[0]); + break; + case 'A': /* CUU -- Cursor Up */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); + break; + case 'B': /* CUD -- Cursor Down */ + case 'e': /* VPR --Cursor Down */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); + break; + case 'i': /* MC -- Media Copy */ + switch (csiescseq.arg[0]) { + case 0: + tdump(); + break; + case 1: + tdumpline(term.c.y); + break; + case 2: + tdumpsel(); + break; + case 4: + term.mode &= ~MODE_PRINT; + break; + case 5: + term.mode |= MODE_PRINT; + break; + } + break; + case 'c': /* DA -- Device Attributes */ + if (csiescseq.arg[0] == 0) + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'b': /* REP -- if last char is printable print it more times */ + DEFAULT(csiescseq.arg[0], 1); + if (term.lastc) + while (csiescseq.arg[0]-- > 0) + tputc(term.lastc); + break; + case 'C': /* CUF -- Cursor Forward */ + case 'a': /* HPR -- Cursor Forward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x+csiescseq.arg[0], term.c.y); + break; + case 'D': /* CUB -- Cursor Backward */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(term.c.x-csiescseq.arg[0], term.c.y); + break; + case 'E': /* CNL -- Cursor Down and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y+csiescseq.arg[0]); + break; + case 'F': /* CPL -- Cursor Up and first col */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(0, term.c.y-csiescseq.arg[0]); + break; + case 'g': /* TBC -- Tabulation clear */ + switch (csiescseq.arg[0]) { + case 0: /* clear current tab stop */ + term.tabs[term.c.x] = 0; + break; + case 3: /* clear all the tabs */ + memset(term.tabs, 0, term.col * sizeof(*term.tabs)); + break; + default: + goto unknown; + } + break; + case 'G': /* CHA -- Move to */ + case '`': /* HPA */ + DEFAULT(csiescseq.arg[0], 1); + tmoveto(csiescseq.arg[0]-1, term.c.y); + break; + case 'H': /* CUP -- Move to */ + case 'f': /* HVP */ + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], 1); + tmoveato(csiescseq.arg[1]-1, csiescseq.arg[0]-1); + break; + case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(csiescseq.arg[0]); + break; + case 'J': /* ED -- Clear screen */ + switch (csiescseq.arg[0]) { + case 0: /* below */ + tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); + if (term.c.y < term.row-1) { + tclearregion(0, term.c.y+1, term.col-1, + term.row-1); + } + break; + case 1: /* above */ + if (term.c.y > 1) + tclearregion(0, 0, term.col-1, term.c.y-1); + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, 0, term.col-1, term.row-1); + break; + default: + goto unknown; + } + break; + case 'K': /* EL -- Clear line */ + switch (csiescseq.arg[0]) { + case 0: /* right */ + tclearregion(term.c.x, term.c.y, term.col-1, + term.c.y); + break; + case 1: /* left */ + tclearregion(0, term.c.y, term.c.x, term.c.y); + break; + case 2: /* all */ + tclearregion(0, term.c.y, term.col-1, term.c.y); + break; + } + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); + tscrollup(term.top, csiescseq.arg[0]); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); + tscrolldown(term.top, csiescseq.arg[0]); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); + tinsertblankline(csiescseq.arg[0]); + break; + case 'l': /* RM -- Reset Mode */ + tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); + break; + case 'M': /* DL -- Delete lines */ + DEFAULT(csiescseq.arg[0], 1); + tdeleteline(csiescseq.arg[0]); + break; + case 'X': /* ECH -- Erase char */ + DEFAULT(csiescseq.arg[0], 1); + tclearregion(term.c.x, term.c.y, + term.c.x + csiescseq.arg[0] - 1, term.c.y); + break; + case 'P': /* DCH -- Delete char */ + DEFAULT(csiescseq.arg[0], 1); + tdeletechar(csiescseq.arg[0]); + break; + case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ + DEFAULT(csiescseq.arg[0], 1); + tputtab(-csiescseq.arg[0]); + break; + case 'd': /* VPA -- Move to */ + DEFAULT(csiescseq.arg[0], 1); + tmoveato(term.c.x, csiescseq.arg[0]-1); + break; + case 'h': /* SM -- Set terminal mode */ + tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); + break; + case 'm': /* SGR -- Terminal attribute (color) */ + tsetattr(csiescseq.arg, csiescseq.narg); + break; + case 'n': /* DSR -- Device Status Report */ + switch (csiescseq.arg[0]) { + case 5: /* Status Report "OK" `0n` */ + ttywrite("\033[0n", sizeof("\033[0n") - 1, 0); + break; + case 6: /* Report Cursor Position (CPR) ";R" */ + len = snprintf(buf, sizeof(buf), "\033[%i;%iR", + term.c.y+1, term.c.x+1); + ttywrite(buf, len, 0); + break; + default: + goto unknown; + } + break; + case 'r': /* DECSTBM -- Set Scrolling Region */ + if (csiescseq.priv) { + goto unknown; + } else { + DEFAULT(csiescseq.arg[0], 1); + DEFAULT(csiescseq.arg[1], term.row); + tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); + tmoveato(0, 0); + } + break; + case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ + tcursor(CURSOR_SAVE); + break; + case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ + tcursor(CURSOR_LOAD); + break; + case ' ': + switch (csiescseq.mode[1]) { + case 'q': /* DECSCUSR -- Set Cursor Style */ + if (xsetcursor(csiescseq.arg[0])) + goto unknown; + break; + default: + goto unknown; + } + break; + } +} + +void +csidump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC["); + for (i = 0; i < csiescseq.len; i++) { + c = csiescseq.buf[i] & 0xff; + if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + putc('\n', stderr); +} + +void +csireset(void) +{ + memset(&csiescseq, 0, sizeof(csiescseq)); +} + +void +osc_color_response(int num, int index, int is_osc4) +{ + int n; + char buf[32]; + unsigned char r, g, b; + + if (xgetcolor(is_osc4 ? num : index, &r, &g, &b)) { + fprintf(stderr, "erresc: failed to fetch %s color %d\n", + is_osc4 ? "osc4" : "osc", + is_osc4 ? num : index); + return; + } + + n = snprintf(buf, sizeof buf, "\033]%s%d;rgb:%02x%02x/%02x%02x/%02x%02x\007", + is_osc4 ? "4;" : "", num, r, r, g, g, b, b); + if (n < 0 || n >= sizeof(buf)) { + fprintf(stderr, "error: %s while printing %s response\n", + n < 0 ? "snprintf failed" : "truncation occurred", + is_osc4 ? "osc4" : "osc"); + } else { + ttywrite(buf, n, 1); + } +} + +void +strhandle(void) +{ + char *p = NULL, *dec; + int j, narg, par; + const struct { int idx; char *str; } osc_table[] = { + { defaultfg, "foreground" }, + { defaultbg, "background" }, + { defaultcs, "cursor" } + }; + + term.esc &= ~(ESC_STR_END|ESC_STR); + strparse(); + par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0; + + switch (strescseq.type) { + case ']': /* OSC -- Operating System Command */ + switch (par) { + case 0: + if (narg > 1) { + xsettitle(strescseq.args[1]); + xseticontitle(strescseq.args[1]); + } + return; + case 1: + if (narg > 1) + xseticontitle(strescseq.args[1]); + return; + case 2: + if (narg > 1) + xsettitle(strescseq.args[1]); + return; + case 52: + if (narg > 2 && allowwindowops) { + dec = base64dec(strescseq.args[2]); + if (dec) { + xsetsel(dec); + xclipcopy(); + } else { + fprintf(stderr, "erresc: invalid base64\n"); + } + } + return; + case 10: + case 11: + case 12: + if (narg < 2) + break; + p = strescseq.args[1]; + if ((j = par - 10) < 0 || j >= LEN(osc_table)) + break; /* shouldn't be possible */ + + if (!strcmp(p, "?")) { + osc_color_response(par, osc_table[j].idx, 0); + } else if (xsetcolorname(osc_table[j].idx, p)) { + fprintf(stderr, "erresc: invalid %s color: %s\n", + osc_table[j].str, p); + } else { + tfulldirt(); + } + return; + case 4: /* color set */ + if (narg < 3) + break; + p = strescseq.args[2]; + /* FALLTHROUGH */ + case 104: /* color reset */ + j = (narg > 1) ? atoi(strescseq.args[1]) : -1; + + if (p && !strcmp(p, "?")) { + osc_color_response(j, 0, 1); + } else if (xsetcolorname(j, p)) { + if (par == 104 && narg <= 1) { + xloadcols(); + return; /* color reset without parameter */ + } + fprintf(stderr, "erresc: invalid color j=%d, p=%s\n", + j, p ? p : "(null)"); + } else { + /* + * TODO if defaultbg color is changed, borders + * are dirty + */ + tfulldirt(); + } + return; + } + break; + case 'k': /* old title set compatibility */ + xsettitle(strescseq.args[0]); + return; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + return; + } + + fprintf(stderr, "erresc: unknown str "); + strdump(); +} + +void +strparse(void) +{ + int c; + char *p = strescseq.buf; + + strescseq.narg = 0; + strescseq.buf[strescseq.len] = '\0'; + + if (*p == '\0') + return; + + while (strescseq.narg < STR_ARG_SIZ) { + strescseq.args[strescseq.narg++] = p; + while ((c = *p) != ';' && c != '\0') + ++p; + if (c == '\0') + return; + *p++ = '\0'; + } +} + +void +strdump(void) +{ + size_t i; + uint c; + + fprintf(stderr, "ESC%c", strescseq.type); + for (i = 0; i < strescseq.len; i++) { + c = strescseq.buf[i] & 0xff; + if (c == '\0') { + putc('\n', stderr); + return; + } else if (isprint(c)) { + putc(c, stderr); + } else if (c == '\n') { + fprintf(stderr, "(\\n)"); + } else if (c == '\r') { + fprintf(stderr, "(\\r)"); + } else if (c == 0x1b) { + fprintf(stderr, "(\\e)"); + } else { + fprintf(stderr, "(%02x)", c); + } + } + fprintf(stderr, "ESC\\\n"); +} + +void +strreset(void) +{ + strescseq = (STREscape){ + .buf = xrealloc(strescseq.buf, STR_BUF_SIZ), + .siz = STR_BUF_SIZ, + }; +} + +void +sendbreak(const Arg *arg) +{ + if (tcsendbreak(cmdfd, 0)) + perror("Error sending break"); +} + +void +tprinter(char *s, size_t len) +{ + if (iofd != -1 && xwrite(iofd, s, len) < 0) { + perror("Error writing to output file"); + close(iofd); + iofd = -1; + } +} + +void +toggleprinter(const Arg *arg) +{ + term.mode ^= MODE_PRINT; +} + +void +printscreen(const Arg *arg) +{ + tdump(); +} + +void +printsel(const Arg *arg) +{ + tdumpsel(); +} + +void +tdumpsel(void) +{ + char *ptr; + + if ((ptr = getsel())) { + tprinter(ptr, strlen(ptr)); + free(ptr); + } +} + +void +tdumpline(int n) +{ + char buf[UTF_SIZ]; + const Glyph *bp, *end; + + bp = &term.line[n][0]; + end = &bp[MIN(tlinelen(n), term.col) - 1]; + if (bp != end || bp->u != ' ') { + for ( ; bp <= end; ++bp) + tprinter(buf, utf8encode(bp->u, buf)); + } + tprinter("\n", 1); +} + +void +tdump(void) +{ + int i; + + for (i = 0; i < term.row; ++i) + tdumpline(i); +} + +void +tputtab(int n) +{ + uint x = term.c.x; + + if (n > 0) { + while (x < term.col && n--) + for (++x; x < term.col && !term.tabs[x]; ++x) + /* nothing */ ; + } else if (n < 0) { + while (x > 0 && n++) + for (--x; x > 0 && !term.tabs[x]; --x) + /* nothing */ ; + } + term.c.x = LIMIT(x, 0, term.col-1); +} + +void +tdefutf8(char ascii) +{ + if (ascii == 'G') + term.mode |= MODE_UTF8; + else if (ascii == '@') + term.mode &= ~MODE_UTF8; +} + +void +tdeftran(char ascii) +{ + static char cs[] = "0B"; + static int vcs[] = {CS_GRAPHIC0, CS_USA}; + char *p; + + if ((p = strchr(cs, ascii)) == NULL) { + fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); + } else { + term.trantbl[term.icharset] = vcs[p - cs]; + } +} + +void +tdectest(char c) +{ + int x, y; + + if (c == '8') { /* DEC screen alignment test. */ + for (x = 0; x < term.col; ++x) { + for (y = 0; y < term.row; ++y) + tsetchar('E', &term.c.attr, x, y); + } + } +} + +void +tstrsequence(uchar c) +{ + switch (c) { + case 0x90: /* DCS -- Device Control String */ + c = 'P'; + break; + case 0x9f: /* APC -- Application Program Command */ + c = '_'; + break; + case 0x9e: /* PM -- Privacy Message */ + c = '^'; + break; + case 0x9d: /* OSC -- Operating System Command */ + c = ']'; + break; + } + strreset(); + strescseq.type = c; + term.esc |= ESC_STR; +} + +void +tcontrolcode(uchar ascii) +{ + switch (ascii) { + case '\t': /* HT */ + tputtab(1); + return; + case '\b': /* BS */ + tmoveto(term.c.x-1, term.c.y); + return; + case '\r': /* CR */ + tmoveto(0, term.c.y); + return; + case '\f': /* LF */ + case '\v': /* VT */ + case '\n': /* LF */ + /* go to first col if the mode is set */ + tnewline(IS_SET(MODE_CRLF)); + return; + case '\a': /* BEL */ + if (term.esc & ESC_STR_END) { + /* backwards compatibility to xterm */ + strhandle(); + } else { + xbell(); + } + break; + case '\033': /* ESC */ + csireset(); + term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); + term.esc |= ESC_START; + return; + case '\016': /* SO (LS1 -- Locking shift 1) */ + case '\017': /* SI (LS0 -- Locking shift 0) */ + term.charset = 1 - (ascii - '\016'); + return; + case '\032': /* SUB */ + tsetchar('?', &term.c.attr, term.c.x, term.c.y); + /* FALLTHROUGH */ + case '\030': /* CAN */ + csireset(); + break; + case '\005': /* ENQ (IGNORED) */ + case '\000': /* NUL (IGNORED) */ + case '\021': /* XON (IGNORED) */ + case '\023': /* XOFF (IGNORED) */ + case 0177: /* DEL (IGNORED) */ + return; + case 0x80: /* TODO: PAD */ + case 0x81: /* TODO: HOP */ + case 0x82: /* TODO: BPH */ + case 0x83: /* TODO: NBH */ + case 0x84: /* TODO: IND */ + break; + case 0x85: /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 0x86: /* TODO: SSA */ + case 0x87: /* TODO: ESA */ + break; + case 0x88: /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 0x89: /* TODO: HTJ */ + case 0x8a: /* TODO: VTS */ + case 0x8b: /* TODO: PLD */ + case 0x8c: /* TODO: PLU */ + case 0x8d: /* TODO: RI */ + case 0x8e: /* TODO: SS2 */ + case 0x8f: /* TODO: SS3 */ + case 0x91: /* TODO: PU1 */ + case 0x92: /* TODO: PU2 */ + case 0x93: /* TODO: STS */ + case 0x94: /* TODO: CCH */ + case 0x95: /* TODO: MW */ + case 0x96: /* TODO: SPA */ + case 0x97: /* TODO: EPA */ + case 0x98: /* TODO: SOS */ + case 0x99: /* TODO: SGCI */ + break; + case 0x9a: /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 0x9b: /* TODO: CSI */ + case 0x9c: /* TODO: ST */ + break; + case 0x90: /* DCS -- Device Control String */ + case 0x9d: /* OSC -- Operating System Command */ + case 0x9e: /* PM -- Privacy Message */ + case 0x9f: /* APC -- Application Program Command */ + tstrsequence(ascii); + return; + } + /* only CAN, SUB, \a and C1 chars interrupt a sequence */ + term.esc &= ~(ESC_STR_END|ESC_STR); +} + +/* + * returns 1 when the sequence is finished and it hasn't to read + * more characters for this sequence, otherwise 0 + */ +int +eschandle(uchar ascii) +{ + switch (ascii) { + case '[': + term.esc |= ESC_CSI; + return 0; + case '#': + term.esc |= ESC_TEST; + return 0; + case '%': + term.esc |= ESC_UTF8; + return 0; + case 'P': /* DCS -- Device Control String */ + case '_': /* APC -- Application Program Command */ + case '^': /* PM -- Privacy Message */ + case ']': /* OSC -- Operating System Command */ + case 'k': /* old title set compatibility */ + tstrsequence(ascii); + return 0; + case 'n': /* LS2 -- Locking shift 2 */ + case 'o': /* LS3 -- Locking shift 3 */ + term.charset = 2 + (ascii - 'n'); + break; + case '(': /* GZD4 -- set primary charset G0 */ + case ')': /* G1D4 -- set secondary charset G1 */ + case '*': /* G2D4 -- set tertiary charset G2 */ + case '+': /* G3D4 -- set quaternary charset G3 */ + term.icharset = ascii - '('; + term.esc |= ESC_ALTCHARSET; + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { + tscrollup(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } + break; + case 'E': /* NEL -- Next line */ + tnewline(1); /* always go to first col */ + break; + case 'H': /* HTS -- Horizontal tab stop */ + term.tabs[term.c.x] = 1; + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { + tscrolldown(term.top, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } + break; + case 'Z': /* DECID -- Identify Terminal */ + ttywrite(vtiden, strlen(vtiden), 0); + break; + case 'c': /* RIS -- Reset to initial state */ + treset(); + resettitle(); + xloadcols(); + break; + case '=': /* DECPAM -- Application keypad */ + xsetmode(1, MODE_APPKEYPAD); + break; + case '>': /* DECPNM -- Normal keypad */ + xsetmode(0, MODE_APPKEYPAD); + break; + case '7': /* DECSC -- Save Cursor */ + tcursor(CURSOR_SAVE); + break; + case '8': /* DECRC -- Restore Cursor */ + tcursor(CURSOR_LOAD); + break; + case '\\': /* ST -- String Terminator */ + if (term.esc & ESC_STR_END) + strhandle(); + break; + default: + fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", + (uchar) ascii, isprint(ascii)? ascii:'.'); + break; + } + return 1; +} + +void +tputc(Rune u) +{ + char c[UTF_SIZ]; + int control; + int width, len; + Glyph *gp; + + control = ISCONTROL(u); + if (u < 127 || !IS_SET(MODE_UTF8)) { + c[0] = u; + width = len = 1; + } else { + len = utf8encode(u, c); + if (!control && (width = wcwidth(u)) == -1) + width = 1; + } + + if (IS_SET(MODE_PRINT)) + tprinter(c, len); + + /* + * STR sequence must be checked before anything else + * because it uses all following characters until it + * receives a ESC, a SUB, a ST or any other C1 control + * character. + */ + if (term.esc & ESC_STR) { + if (u == '\a' || u == 030 || u == 032 || u == 033 || + ISCONTROLC1(u)) { + term.esc &= ~(ESC_START|ESC_STR); + term.esc |= ESC_STR_END; + goto check_control_code; + } + + if (strescseq.len+len >= strescseq.siz) { + /* + * Here is a bug in terminals. If the user never sends + * some code to stop the str or esc command, then st + * will stop responding. But this is better than + * silently failing with unknown characters. At least + * then users will report back. + * + * In the case users ever get fixed, here is the code: + */ + /* + * term.esc = 0; + * strhandle(); + */ + if (strescseq.siz > (SIZE_MAX - UTF_SIZ) / 2) + return; + strescseq.siz *= 2; + strescseq.buf = xrealloc(strescseq.buf, strescseq.siz); + } + + memmove(&strescseq.buf[strescseq.len], c, len); + strescseq.len += len; + return; + } + +check_control_code: + /* + * Actions of control codes must be performed as soon they arrive + * because they can be embedded inside a control sequence, and + * they must not cause conflicts with sequences. + */ + if (control) { + /* in UTF-8 mode ignore handling C1 control characters */ + if (IS_SET(MODE_UTF8) && ISCONTROLC1(u)) + return; + tcontrolcode(u); + /* + * control codes are not shown ever + */ + if (!term.esc) + term.lastc = 0; + return; + } else if (term.esc & ESC_START) { + if (term.esc & ESC_CSI) { + csiescseq.buf[csiescseq.len++] = u; + if (BETWEEN(u, 0x40, 0x7E) + || csiescseq.len >= \ + sizeof(csiescseq.buf)-1) { + term.esc = 0; + csiparse(); + csihandle(); + } + return; + } else if (term.esc & ESC_UTF8) { + tdefutf8(u); + } else if (term.esc & ESC_ALTCHARSET) { + tdeftran(u); + } else if (term.esc & ESC_TEST) { + tdectest(u); + } else { + if (!eschandle(u)) + return; + /* sequence already finished */ + } + term.esc = 0; + /* + * All characters which form part of a sequence are not + * printed + */ + return; + } + if (selected(term.c.x, term.c.y)) + selclear(); + + gp = &term.line[term.c.y][term.c.x]; + if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) { + gp->mode |= ATTR_WRAP; + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) { + memmove(gp+width, gp, (term.col - term.c.x - width) * sizeof(Glyph)); + gp->mode &= ~ATTR_WIDE; + } + + if (term.c.x+width > term.col) { + tnewline(1); + gp = &term.line[term.c.y][term.c.x]; + } + + tsetchar(u, &term.c.attr, term.c.x, term.c.y); + term.lastc = u; + + if (width == 2) { + gp->mode |= ATTR_WIDE; + if (term.c.x+1 < term.col) { + if (gp[1].mode == ATTR_WIDE && term.c.x+2 < term.col) { + gp[2].u = ' '; + gp[2].mode &= ~ATTR_WDUMMY; + } + gp[1].u = '\0'; + gp[1].mode = ATTR_WDUMMY; + } + } + if (term.c.x+width < term.col) { + tmoveto(term.c.x+width, term.c.y); + } else { + term.c.state |= CURSOR_WRAPNEXT; + } +} + +int +twrite(const char *buf, int buflen, int show_ctrl) +{ + int charsize; + Rune u; + int n; + + for (n = 0; n < buflen; n += charsize) { + if (IS_SET(MODE_UTF8)) { + /* process a complete utf8 char */ + charsize = utf8decode(buf + n, &u, buflen - n); + if (charsize == 0) + break; + } else { + u = buf[n] & 0xFF; + charsize = 1; + } + if (show_ctrl && ISCONTROL(u)) { + if (u & 0x80) { + u &= 0x7f; + tputc('^'); + tputc('['); + } else if (u != '\n' && u != '\r' && u != '\t') { + u ^= 0x40; + tputc('^'); + } + } + tputc(u); + } + return n; +} + +void +tresize(int col, int row) +{ + int i; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; + TCursor c; + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to + * memmove because we're freeing the earlier lines + */ + for (i = 0; i <= term.c.y - row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + /* ensure that both src and dst are not NULL */ + if (i > 0) { + memmove(term.line, term.line + i, row * sizeof(Line)); + memmove(term.alt, term.alt + i, row * sizeof(Line)); + } + for (i += row; i < term.row; i++) { + free(term.line[i]); + free(term.alt[i]); + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + if (col > term.col) { + bp = term.tabs + term.col; + + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + /* update terminal size */ + term.col = col; + term.row = row; + /* reset scrolling region */ + tsetscroll(0, row-1); + /* make use of the LIMIT in tmoveto */ + tmoveto(term.c.x, term.c.y); + /* Clearing both screens (it makes dirty all lines) */ + c = term.c; + for (i = 0; i < 2; i++) { + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + term.c = c; +} + +void +resettitle(void) +{ + xsettitle(NULL); +} + +void +drawregion(int x1, int y1, int x2, int y2) +{ + int y; + + for (y = y1; y < y2; y++) { + if (!term.dirty[y]) + continue; + + term.dirty[y] = 0; + xdrawline(term.line[y], x1, y, x2); + } +} + +void +draw(void) +{ + int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + + if (!xstartdraw()) + return; + + /* adjust cursor position */ + LIMIT(term.ocx, 0, term.col-1); + LIMIT(term.ocy, 0, term.row-1); + if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY) + term.ocx--; + if (term.line[term.c.y][cx].mode & ATTR_WDUMMY) + cx--; + + drawregion(0, 0, term.col, term.row); + xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], + term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); + if (ocx != term.ocx || ocy != term.ocy) + xximspot(term.ocx, term.ocy); +} + +void +redraw(void) +{ + tfulldirt(); + draw(); +} diff --git a/st/st.h b/st/st.h new file mode 100644 index 0000000..3cea73b --- /dev/null +++ b/st/st.h @@ -0,0 +1,127 @@ +/* See LICENSE for license details. */ + +#include +#include + +/* macros */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define LEN(a) (sizeof(a) / sizeof(a)[0]) +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) +#define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) +#define DEFAULT(a, b) (a) = (a) ? (a) : (b) +#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) +#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ + (a).bg != (b).bg) +#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ + (t1.tv_nsec-t2.tv_nsec)/1E6) +#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) + +#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) +#define IS_TRUECOL(x) (1 << 24 & (x)) +#define HISTSIZE 2000 + +enum glyph_attribute { + ATTR_NULL = 0, + ATTR_BOLD = 1 << 0, + ATTR_FAINT = 1 << 1, + ATTR_ITALIC = 1 << 2, + ATTR_UNDERLINE = 1 << 3, + ATTR_BLINK = 1 << 4, + ATTR_REVERSE = 1 << 5, + ATTR_INVISIBLE = 1 << 6, + ATTR_STRUCK = 1 << 7, + ATTR_WRAP = 1 << 8, + ATTR_WIDE = 1 << 9, + ATTR_WDUMMY = 1 << 10, + ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, +}; + +enum selection_mode { + SEL_IDLE = 0, + SEL_EMPTY = 1, + SEL_READY = 2 +}; + +enum selection_type { + SEL_REGULAR = 1, + SEL_RECTANGULAR = 2 +}; + +enum selection_snap { + SNAP_WORD = 1, + SNAP_LINE = 2 +}; + +typedef unsigned char uchar; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned short ushort; + +typedef uint_least32_t Rune; + +#define Glyph Glyph_ +typedef struct { + Rune u; /* character code */ + ushort mode; /* attribute flags */ + uint32_t fg; /* foreground */ + uint32_t bg; /* background */ +} Glyph; + +typedef Glyph *Line; + +typedef union { + int i; + uint ui; + float f; + const void *v; + const char *s; +} Arg; + +void die(const char *, ...); +void redraw(void); +void draw(void); + +void printscreen(const Arg *); +void printsel(const Arg *); +void sendbreak(const Arg *); +void toggleprinter(const Arg *); + +int tattrset(int); +void tnew(int, int); +void tresize(int, int); +void tsetdirtattr(int); +void ttyhangup(void); +int ttynew(const char *, char *, const char *, char **); +size_t ttyread(void); +void ttyresize(int, int); +void ttywrite(const char *, size_t, int); + +void resettitle(void); + +void selclear(void); +void selinit(void); +void selstart(int, int, int); +void selextend(int, int, int, int); +int selected(int, int); +char *getsel(void); + +size_t utf8encode(Rune, char *); + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); + +/* config.h globals */ +extern char *utmp; +extern char *scroll; +extern char *stty_args; +extern char *vtiden; +extern wchar_t *worddelimiters; +extern int allowaltscreen; +extern int allowwindowops; +extern char *termname; +extern unsigned int tabspaces; +extern unsigned int defaultfg; +extern unsigned int defaultbg; +extern unsigned int defaultcs; diff --git a/st/st.info b/st/st.info new file mode 100644 index 0000000..8201ad6 --- /dev/null +++ b/st/st.info @@ -0,0 +1,239 @@ +st-mono| simpleterm monocolor, + acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + am, + bce, + bel=^G, + blink=\E[5m, + bold=\E[1m, + cbt=\E[Z, + cvvis=\E[?25h, + civis=\E[?25l, + clear=\E[H\E[2J, + cnorm=\E[?12l\E[?25h, + colors#2, + cols#80, + cr=^M, + csr=\E[%i%p1%d;%p2%dr, + cub=\E[%p1%dD, + cub1=^H, + cud1=^J, + cud=\E[%p1%dB, + cuf1=\E[C, + cuf=\E[%p1%dC, + cup=\E[%i%p1%d;%p2%dH, + cuu1=\E[A, + cuu=\E[%p1%dA, + dch=\E[%p1%dP, + dch1=\E[P, + dim=\E[2m, + dl=\E[%p1%dM, + dl1=\E[M, + ech=\E[%p1%dX, + ed=\E[J, + el=\E[K, + el1=\E[1K, + enacs=\E)0, + flash=\E[?5h$<80/>\E[?5l, + fsl=^G, + home=\E[H, + hpa=\E[%i%p1%dG, + hs, + ht=^I, + hts=\EH, + ich=\E[%p1%d@, + il1=\E[L, + il=\E[%p1%dL, + ind=^J, + indn=\E[%p1%dS, + invis=\E[8m, + is2=\E[4l\E>\E[?1034l, + it#8, + kel=\E[1;2F, + ked=\E[1;5F, + ka1=\E[1~, + ka3=\E[5~, + kc1=\E[4~, + kc3=\E[6~, + kbs=\177, + kcbt=\E[Z, + kb2=\EOu, + kcub1=\EOD, + kcud1=\EOB, + kcuf1=\EOC, + kcuu1=\EOA, + kDC=\E[3;2~, + kent=\EOM, + kEND=\E[1;2F, + kIC=\E[2;2~, + kNXT=\E[6;2~, + kPRV=\E[5;2~, + kHOM=\E[1;2H, + kLFT=\E[1;2D, + kRIT=\E[1;2C, + kind=\E[1;2B, + kri=\E[1;2A, + kclr=\E[3;5~, + kdl1=\E[3;2~, + kdch1=\E[3~, + kich1=\E[2~, + kend=\E[4~, + kf1=\EOP, + kf2=\EOQ, + kf3=\EOR, + kf4=\EOS, + kf5=\E[15~, + kf6=\E[17~, + kf7=\E[18~, + kf8=\E[19~, + kf9=\E[20~, + kf10=\E[21~, + kf11=\E[23~, + kf12=\E[24~, + kf13=\E[1;2P, + kf14=\E[1;2Q, + kf15=\E[1;2R, + kf16=\E[1;2S, + kf17=\E[15;2~, + kf18=\E[17;2~, + kf19=\E[18;2~, + kf20=\E[19;2~, + kf21=\E[20;2~, + kf22=\E[21;2~, + kf23=\E[23;2~, + kf24=\E[24;2~, + kf25=\E[1;5P, + kf26=\E[1;5Q, + kf27=\E[1;5R, + kf28=\E[1;5S, + kf29=\E[15;5~, + kf30=\E[17;5~, + kf31=\E[18;5~, + kf32=\E[19;5~, + kf33=\E[20;5~, + kf34=\E[21;5~, + kf35=\E[23;5~, + kf36=\E[24;5~, + kf37=\E[1;6P, + kf38=\E[1;6Q, + kf39=\E[1;6R, + kf40=\E[1;6S, + kf41=\E[15;6~, + kf42=\E[17;6~, + kf43=\E[18;6~, + kf44=\E[19;6~, + kf45=\E[20;6~, + kf46=\E[21;6~, + kf47=\E[23;6~, + kf48=\E[24;6~, + kf49=\E[1;3P, + kf50=\E[1;3Q, + kf51=\E[1;3R, + kf52=\E[1;3S, + kf53=\E[15;3~, + kf54=\E[17;3~, + kf55=\E[18;3~, + kf56=\E[19;3~, + kf57=\E[20;3~, + kf58=\E[21;3~, + kf59=\E[23;3~, + kf60=\E[24;3~, + kf61=\E[1;4P, + kf62=\E[1;4Q, + kf63=\E[1;4R, + khome=\E[1~, + kil1=\E[2;5~, + krmir=\E[2;2~, + knp=\E[6~, + kmous=\E[M, + kpp=\E[5~, + lines#24, + mir, + msgr, + npc, + op=\E[39;49m, + pairs#64, + mc0=\E[i, + mc4=\E[4i, + mc5=\E[5i, + rc=\E8, + rev=\E[7m, + ri=\EM, + rin=\E[%p1%dT, + ritm=\E[23m, + rmacs=\E(B, + rmcup=\E[?1049l, + rmir=\E[4l, + rmkx=\E[?1l\E>, + rmso=\E[27m, + rmul=\E[24m, + rs1=\Ec, + rs2=\E[4l\E>\E[?1034l, + sc=\E7, + sitm=\E[3m, + sgr0=\E[0m, + smacs=\E(0, + smcup=\E[?1049h, + smir=\E[4h, + smkx=\E[?1h\E=, + smso=\E[7m, + smul=\E[4m, + tbc=\E[3g, + tsl=\E]0;, + xenl, + vpa=\E[%i%p1%dd, +# XTerm extensions + rmxx=\E[29m, + smxx=\E[9m, +# disabled rep for now: causes some issues with older ncurses versions. +# rep=%p1%c\E[%p2%{1}%-%db, +# tmux extensions, see TERMINFO EXTENSIONS in tmux(1) + Tc, + Ms=\E]52;%p1%s;%p2%s\007, + Se=\E[2 q, + Ss=\E[%p1%d q, + +st| simpleterm, + use=st-mono, + colors#8, + setab=\E[4%p1%dm, + setaf=\E[3%p1%dm, + setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, + setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m, + sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, + +st-256color| simpleterm with 256 colors, + use=st, + ccc, + colors#256, + oc=\E]104\007, + pairs#32767, +# Nicked from xterm-256color + initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, + setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, + setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, + +st-meta| simpleterm with meta key, + use=st, + km, + rmm=\E[?1034l, + smm=\E[?1034h, + rs2=\E[4l\E>\E[?1034h, + is2=\E[4l\E>\E[?1034h, + +st-meta-256color| simpleterm with meta key and 256 colors, + use=st-256color, + km, + rmm=\E[?1034l, + smm=\E[?1034h, + rs2=\E[4l\E>\E[?1034h, + is2=\E[4l\E>\E[?1034h, + +st-bs| simpleterm with backspace as backspace, + use=st, + kbs=\010, + kdch1=\177, + +st-bs-256color| simpleterm with backspace as backspace and 256colors, + use=st-256color, + kbs=\010, + kdch1=\177, diff --git a/st/st.o b/st/st.o new file mode 100644 index 0000000000000000000000000000000000000000..502bfee793d7e1d8e9f76c552715d6867f86b3b1 GIT binary patch literal 81376 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 literal 0 HcmV?d00001 diff --git a/st/win.h b/st/win.h new file mode 100644 index 0000000..6de960d --- /dev/null +++ b/st/win.h @@ -0,0 +1,41 @@ +/* See LICENSE for license details. */ + +enum win_mode { + MODE_VISIBLE = 1 << 0, + MODE_FOCUSED = 1 << 1, + MODE_APPKEYPAD = 1 << 2, + MODE_MOUSEBTN = 1 << 3, + MODE_MOUSEMOTION = 1 << 4, + MODE_REVERSE = 1 << 5, + MODE_KBDLOCK = 1 << 6, + MODE_HIDE = 1 << 7, + MODE_APPCURSOR = 1 << 8, + MODE_MOUSESGR = 1 << 9, + MODE_8BIT = 1 << 10, + MODE_BLINK = 1 << 11, + MODE_FBLINK = 1 << 12, + MODE_FOCUS = 1 << 13, + MODE_MOUSEX10 = 1 << 14, + MODE_MOUSEMANY = 1 << 15, + MODE_BRCKTPASTE = 1 << 16, + MODE_NUMLOCK = 1 << 17, + MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ + |MODE_MOUSEMANY, +}; + +void xbell(void); +void xclipcopy(void); +void xdrawcursor(int, int, Glyph, int, int, Glyph); +void xdrawline(Line, int, int, int); +void xfinishdraw(void); +void xloadcols(void); +int xsetcolorname(int, const char *); +int xgetcolor(int, unsigned char *, unsigned char *, unsigned char *); +void xseticontitle(char *); +void xsettitle(char *); +int xsetcursor(int); +void xsetmode(int, unsigned int); +void xsetpointermotion(int); +void xsetsel(char *); +int xstartdraw(void); +void xximspot(int, int); diff --git a/st/x.c b/st/x.c new file mode 100644 index 0000000..0a3e969 --- /dev/null +++ b/st/x.c @@ -0,0 +1,2101 @@ +/* See LICENSE for license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *argv0; +#include "arg.h" +#include "st.h" +#include "win.h" + +/* types used in config.h */ +typedef struct { + uint mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Shortcut; + +typedef struct { + uint mod; + uint button; + void (*func)(const Arg *); + const Arg arg; + uint release; +} MouseShortcut; + +typedef struct { + KeySym k; + uint mask; + char *s; + /* three-valued logic variables: 0 indifferent, 1 on, -1 off */ + signed char appkey; /* application keypad */ + signed char appcursor; /* application cursor */ +} Key; + +/* X modifiers */ +#define XK_ANY_MOD UINT_MAX +#define XK_NO_MOD 0 +#define XK_SWITCH_MOD (1<<13|1<<14) + +/* function definitions used in config.h */ +static void clipcopy(const Arg *); +static void clippaste(const Arg *); +static void numlock(const Arg *); +static void selpaste(const Arg *); +static void zoom(const Arg *); +static void zoomabs(const Arg *); +static void zoomreset(const Arg *); +static void ttysend(const Arg *); +void kscrollup(const Arg *); +void kscrolldown(const Arg *); + +/* config.h for applying patches and the configuration. */ +#include "config.h" + +/* XEMBED messages */ +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 + +/* macros */ +#define IS_SET(flag) ((win.mode & (flag)) != 0) +#define TRUERED(x) (((x) & 0xff0000) >> 8) +#define TRUEGREEN(x) (((x) & 0xff00)) +#define TRUEBLUE(x) (((x) & 0xff) << 8) + +typedef XftDraw *Draw; +typedef XftColor Color; +typedef XftGlyphFontSpec GlyphFontSpec; + +/* Purely graphic info */ +typedef struct { + int tw, th; /* tty width and height */ + int w, h; /* window width and height */ + int ch; /* char height */ + int cw; /* char width */ + int mode; /* window state/mode flags */ + int cursor; /* cursor style */ +} TermWindow; + +typedef struct { + Display *dpy; + Colormap cmap; + Window win; + Drawable buf; + GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ + Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; + struct { + XIM xim; + XIC xic; + XPoint spot; + XVaNestedList spotlist; + } ime; + Draw draw; + Visual *vis; + XSetWindowAttributes attrs; + int scr; + int isfixed; /* is fixed geometry? */ + int l, t; /* left and top offset */ + int gm; /* geometry mask */ +} XWindow; + +typedef struct { + Atom xtarget; + char *primary, *clipboard; + struct timespec tclick1; + struct timespec tclick2; +} XSelection; + +/* Font structure */ +#define Font Font_ +typedef struct { + int height; + int width; + int ascent; + int descent; + int badslant; + int badweight; + short lbearing; + short rbearing; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; +} Font; + +/* Drawing Context */ +typedef struct { + Color *col; + size_t collen; + Font font, bfont, ifont, ibfont; + GC gc; +} DC; + +static inline ushort sixd_to_16bit(int); +static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); +static void xdrawglyph(Glyph, int, int); +static void xclear(int, int, int, int); +static int xgeommasktogravity(int); +static int ximopen(Display *); +static void ximinstantiate(Display *, XPointer, XPointer); +static void ximdestroy(XIM, XPointer, XPointer); +static int xicdestroy(XIC, XPointer, XPointer); +static void xinit(int, int); +static void cresize(int, int); +static void xresize(int, int); +static void xhints(void); +static int xloadcolor(int, const char *, Color *); +static int xloadfont(Font *, FcPattern *); +static void xloadfonts(const char *, double); +static void xunloadfont(Font *); +static void xunloadfonts(void); +static void xsetenv(void); +static void xseturgency(int); +static int evcol(XEvent *); +static int evrow(XEvent *); + +static void expose(XEvent *); +static void visibility(XEvent *); +static void unmap(XEvent *); +static void kpress(XEvent *); +static void cmessage(XEvent *); +static void resize(XEvent *); +static void focus(XEvent *); +static uint buttonmask(uint); +static int mouseaction(XEvent *, uint); +static void brelease(XEvent *); +static void bpress(XEvent *); +static void bmotion(XEvent *); +static void propnotify(XEvent *); +static void selnotify(XEvent *); +static void selclear_(XEvent *); +static void selrequest(XEvent *); +static void setsel(char *, Time); +static void mousesel(XEvent *, int); +static void mousereport(XEvent *); +static char *kmap(KeySym, uint); +static int match(uint, uint); + +static void run(void); +static void usage(void); + +static void (*handler[LASTEvent])(XEvent *) = { + [KeyPress] = kpress, + [ClientMessage] = cmessage, + [ConfigureNotify] = resize, + [VisibilityNotify] = visibility, + [UnmapNotify] = unmap, + [Expose] = expose, + [FocusIn] = focus, + [FocusOut] = focus, + [MotionNotify] = bmotion, + [ButtonPress] = bpress, + [ButtonRelease] = brelease, +/* + * Uncomment if you want the selection to disappear when you select something + * different in another window. + */ +/* [SelectionClear] = selclear_, */ + [SelectionNotify] = selnotify, +/* + * PropertyNotify is only turned on when there is some INCR transfer happening + * for the selection retrieval. + */ + [PropertyNotify] = propnotify, + [SelectionRequest] = selrequest, +}; + +/* Globals */ +static DC dc; +static XWindow xw; +static XSelection xsel; +static TermWindow win; + +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + int flags; + Rune unicodep; +} Fontcache; + +/* Fontcache is an array now. A new font will be appended to the array. */ +static Fontcache *frc = NULL; +static int frclen = 0; +static int frccap = 0; +static char *usedfont = NULL; +static double usedfontsize = 0; +static double defaultfontsize = 0; + +static char *opt_class = NULL; +static char **opt_cmd = NULL; +static char *opt_embed = NULL; +static char *opt_font = NULL; +static char *opt_io = NULL; +static char *opt_line = NULL; +static char *opt_name = NULL; +static char *opt_title = NULL; + +static uint buttons; /* bit field of pressed buttons */ + +void +clipcopy(const Arg *dummy) +{ + Atom clipboard; + + free(xsel.clipboard); + xsel.clipboard = NULL; + + if (xsel.primary != NULL) { + xsel.clipboard = xstrdup(xsel.primary); + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); + } +} + +void +clippaste(const Arg *dummy) +{ + Atom clipboard; + + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + XConvertSelection(xw.dpy, clipboard, xsel.xtarget, clipboard, + xw.win, CurrentTime); +} + +void +selpaste(const Arg *dummy) +{ + XConvertSelection(xw.dpy, XA_PRIMARY, xsel.xtarget, XA_PRIMARY, + xw.win, CurrentTime); +} + +void +numlock(const Arg *dummy) +{ + win.mode ^= MODE_NUMLOCK; +} + +void +zoom(const Arg *arg) +{ + Arg larg; + + larg.f = usedfontsize + arg->f; + zoomabs(&larg); +} + +void +zoomabs(const Arg *arg) +{ + xunloadfonts(); + xloadfonts(usedfont, arg->f); + cresize(0, 0); + redraw(); + xhints(); +} + +void +zoomreset(const Arg *arg) +{ + Arg larg; + + if (defaultfontsize > 0) { + larg.f = defaultfontsize; + zoomabs(&larg); + } +} + +void +ttysend(const Arg *arg) +{ + ttywrite(arg->s, strlen(arg->s), 1); +} + +int +evcol(XEvent *e) +{ + int x = e->xbutton.x - borderpx; + LIMIT(x, 0, win.tw - 1); + return x / win.cw; +} + +int +evrow(XEvent *e) +{ + int y = e->xbutton.y - borderpx; + LIMIT(y, 0, win.th - 1); + return y / win.ch; +} + +void +mousesel(XEvent *e, int done) +{ + int type, seltype = SEL_REGULAR; + uint state = e->xbutton.state & ~(Button1Mask | forcemousemod); + + for (type = 1; type < LEN(selmasks); ++type) { + if (match(selmasks[type], state)) { + seltype = type; + break; + } + } + selextend(evcol(e), evrow(e), seltype, done); + if (done) + setsel(getsel(), e->xbutton.time); +} + +void +mousereport(XEvent *e) +{ + int len, btn, code; + int x = evcol(e), y = evrow(e); + int state = e->xbutton.state; + char buf[40]; + static int ox, oy; + + if (e->type == MotionNotify) { + if (x == ox && y == oy) + return; + if (!IS_SET(MODE_MOUSEMOTION) && !IS_SET(MODE_MOUSEMANY)) + return; + /* MODE_MOUSEMOTION: no reporting if no button is pressed */ + if (IS_SET(MODE_MOUSEMOTION) && buttons == 0) + return; + /* Set btn to lowest-numbered pressed button, or 12 if no + * buttons are pressed. */ + for (btn = 1; btn <= 11 && !(buttons & (1<<(btn-1))); btn++) + ; + code = 32; + } else { + btn = e->xbutton.button; + /* Only buttons 1 through 11 can be encoded */ + if (btn < 1 || btn > 11) + return; + if (e->type == ButtonRelease) { + /* MODE_MOUSEX10: no button release reporting */ + if (IS_SET(MODE_MOUSEX10)) + return; + /* Don't send release events for the scroll wheel */ + if (btn == 4 || btn == 5) + return; + } + code = 0; + } + + ox = x; + oy = y; + + /* Encode btn into code. If no button is pressed for a motion event in + * MODE_MOUSEMANY, then encode it as a release. */ + if ((!IS_SET(MODE_MOUSESGR) && e->type == ButtonRelease) || btn == 12) + code += 3; + else if (btn >= 8) + code += 128 + btn - 8; + else if (btn >= 4) + code += 64 + btn - 4; + else + code += btn - 1; + + if (!IS_SET(MODE_MOUSEX10)) { + code += ((state & ShiftMask ) ? 4 : 0) + + ((state & Mod1Mask ) ? 8 : 0) /* meta key: alt */ + + ((state & ControlMask) ? 16 : 0); + } + + if (IS_SET(MODE_MOUSESGR)) { + len = snprintf(buf, sizeof(buf), "\033[<%d;%d;%d%c", + code, x+1, y+1, + e->type == ButtonRelease ? 'm' : 'M'); + } else if (x < 223 && y < 223) { + len = snprintf(buf, sizeof(buf), "\033[M%c%c%c", + 32+code, 32+x+1, 32+y+1); + } else { + return; + } + + ttywrite(buf, len, 0); +} + +uint +buttonmask(uint button) +{ + return button == Button1 ? Button1Mask + : button == Button2 ? Button2Mask + : button == Button3 ? Button3Mask + : button == Button4 ? Button4Mask + : button == Button5 ? Button5Mask + : 0; +} + +int +mouseaction(XEvent *e, uint release) +{ + MouseShortcut *ms; + + /* ignore Buttonmask for Button - it's set on release */ + uint state = e->xbutton.state & ~buttonmask(e->xbutton.button); + + for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) { + if (ms->release == release && + ms->button == e->xbutton.button && + (match(ms->mod, state) || /* exact or forced */ + match(ms->mod, state & ~forcemousemod))) { + ms->func(&(ms->arg)); + return 1; + } + } + + return 0; +} + +void +bpress(XEvent *e) +{ + int btn = e->xbutton.button; + struct timespec now; + int snap; + + if (1 <= btn && btn <= 11) + buttons |= 1 << (btn-1); + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 0)) + return; + + if (btn == Button1) { + /* + * If the user clicks below predefined timeouts specific + * snapping behaviour is exposed. + */ + clock_gettime(CLOCK_MONOTONIC, &now); + if (TIMEDIFF(now, xsel.tclick2) <= tripleclicktimeout) { + snap = SNAP_LINE; + } else if (TIMEDIFF(now, xsel.tclick1) <= doubleclicktimeout) { + snap = SNAP_WORD; + } else { + snap = 0; + } + xsel.tclick2 = xsel.tclick1; + xsel.tclick1 = now; + + selstart(evcol(e), evrow(e), snap); + } +} + +void +propnotify(XEvent *e) +{ + XPropertyEvent *xpev; + Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + + xpev = &e->xproperty; + if (xpev->state == PropertyNewValue && + (xpev->atom == XA_PRIMARY || + xpev->atom == clipboard)) { + selnotify(e); + } +} + +void +selnotify(XEvent *e) +{ + ulong nitems, ofs, rem; + int format; + uchar *data, *last, *repl; + Atom type, incratom, property = None; + + incratom = XInternAtom(xw.dpy, "INCR", 0); + + ofs = 0; + if (e->type == SelectionNotify) + property = e->xselection.property; + else if (e->type == PropertyNotify) + property = e->xproperty.atom; + + if (property == None) + return; + + do { + if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, + BUFSIZ/4, False, AnyPropertyType, + &type, &format, &nitems, &rem, + &data)) { + fprintf(stderr, "Clipboard allocation failed\n"); + return; + } + + if (e->type == PropertyNotify && nitems == 0 && rem == 0) { + /* + * If there is some PropertyNotify with no data, then + * this is the signal of the selection owner that all + * data has been transferred. We won't need to receive + * PropertyNotify events anymore. + */ + MODBIT(xw.attrs.event_mask, 0, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + } + + if (type == incratom) { + /* + * Activate the PropertyNotify events so we receive + * when the selection owner does send us the next + * chunk of data. + */ + MODBIT(xw.attrs.event_mask, 1, PropertyChangeMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, + &xw.attrs); + + /* + * Deleting the property is the transfer start signal. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); + continue; + } + + /* + * As seen in getsel: + * Line endings are inconsistent in the terminal and GUI world + * copy and pasting. When receiving some selection data, + * replace all '\n' with '\r'. + * FIXME: Fix the computer world. + */ + repl = data; + last = data + nitems * format / 8; + while ((repl = memchr(repl, '\n', last - repl))) { + *repl++ = '\r'; + } + + if (IS_SET(MODE_BRCKTPASTE) && ofs == 0) + ttywrite("\033[200~", 6, 0); + ttywrite((char *)data, nitems * format / 8, 1); + if (IS_SET(MODE_BRCKTPASTE) && rem == 0) + ttywrite("\033[201~", 6, 0); + XFree(data); + /* number of 32-bit chunks returned */ + ofs += nitems * format / 32; + } while (rem > 0); + + /* + * Deleting the property again tells the selection owner to send the + * next data chunk in the property. + */ + XDeleteProperty(xw.dpy, xw.win, (int)property); +} + +void +xclipcopy(void) +{ + clipcopy(NULL); +} + +void +selclear_(XEvent *e) +{ + selclear(); +} + +void +selrequest(XEvent *e) +{ + XSelectionRequestEvent *xsre; + XSelectionEvent xev; + Atom xa_targets, string, clipboard; + char *seltext; + + xsre = (XSelectionRequestEvent *) e; + xev.type = SelectionNotify; + xev.requestor = xsre->requestor; + xev.selection = xsre->selection; + xev.target = xsre->target; + xev.time = xsre->time; + if (xsre->property == None) + xsre->property = xsre->target; + + /* reject */ + xev.property = None; + + xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); + if (xsre->target == xa_targets) { + /* respond with the supported type */ + string = xsel.xtarget; + XChangeProperty(xsre->display, xsre->requestor, xsre->property, + XA_ATOM, 32, PropModeReplace, + (uchar *) &string, 1); + xev.property = xsre->property; + } else if (xsre->target == xsel.xtarget || xsre->target == XA_STRING) { + /* + * xith XA_STRING non ascii characters may be incorrect in the + * requestor. It is not our problem, use utf8. + */ + clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); + if (xsre->selection == XA_PRIMARY) { + seltext = xsel.primary; + } else if (xsre->selection == clipboard) { + seltext = xsel.clipboard; + } else { + fprintf(stderr, + "Unhandled clipboard selection 0x%lx\n", + xsre->selection); + return; + } + if (seltext != NULL) { + XChangeProperty(xsre->display, xsre->requestor, + xsre->property, xsre->target, + 8, PropModeReplace, + (uchar *)seltext, strlen(seltext)); + xev.property = xsre->property; + } + } + + /* all done, send a notification to the listener */ + if (!XSendEvent(xsre->display, xsre->requestor, 1, 0, (XEvent *) &xev)) + fprintf(stderr, "Error sending SelectionNotify event\n"); +} + +void +setsel(char *str, Time t) +{ + if (!str) + return; + + free(xsel.primary); + xsel.primary = str; + + XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); + if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) + selclear(); +} + +void +xsetsel(char *str) +{ + setsel(str, CurrentTime); +} + +void +brelease(XEvent *e) +{ + int btn = e->xbutton.button; + + if (1 <= btn && btn <= 11) + buttons &= ~(1 << (btn-1)); + + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + if (mouseaction(e, 1)) + return; + if (btn == Button1) + mousesel(e, 1); +} + +void +bmotion(XEvent *e) +{ + if (IS_SET(MODE_MOUSE) && !(e->xbutton.state & forcemousemod)) { + mousereport(e); + return; + } + + mousesel(e, 0); +} + +void +cresize(int width, int height) +{ + int col, row; + + if (width != 0) + win.w = width; + if (height != 0) + win.h = height; + + col = (win.w - 2 * borderpx) / win.cw; + row = (win.h - 2 * borderpx) / win.ch; + col = MAX(1, col); + row = MAX(1, row); + + tresize(col, row); + xresize(col, row); + ttyresize(win.tw, win.th); +} + +void +xresize(int col, int row) +{ + win.tw = col * win.cw; + win.th = row * win.ch; + + XFreePixmap(xw.dpy, xw.buf); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + DefaultDepth(xw.dpy, xw.scr)); + XftDrawChange(xw.draw, xw.buf); + xclear(0, 0, win.w, win.h); + + /* resize to new width */ + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); +} + +ushort +sixd_to_16bit(int x) +{ + return x == 0 ? 0 : 0x3737 + 0x2828 * x; +} + +int +xloadcolor(int i, const char *name, Color *ncolor) +{ + XRenderColor color = { .alpha = 0xffff }; + + if (!name) { + if (BETWEEN(i, 16, 255)) { /* 256 color */ + if (i < 6*6*6+16) { /* same colors as xterm */ + color.red = sixd_to_16bit( ((i-16)/36)%6 ); + color.green = sixd_to_16bit( ((i-16)/6) %6 ); + color.blue = sixd_to_16bit( ((i-16)/1) %6 ); + } else { /* greyscale */ + color.red = 0x0808 + 0x0a0a * (i - (6*6*6+16)); + color.green = color.blue = color.red; + } + return XftColorAllocValue(xw.dpy, xw.vis, + xw.cmap, &color, ncolor); + } else + name = colorname[i]; + } + + return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); +} + +void +xloadcols(void) +{ + int i; + static int loaded; + Color *cp; + + if (loaded) { + for (cp = dc.col; cp < &dc.col[dc.collen]; ++cp) + XftColorFree(xw.dpy, xw.vis, xw.cmap, cp); + } else { + dc.collen = MAX(LEN(colorname), 256); + dc.col = xmalloc(dc.collen * sizeof(Color)); + } + + for (i = 0; i < dc.collen; i++) + if (!xloadcolor(i, NULL, &dc.col[i])) { + if (colorname[i]) + die("could not allocate color '%s'\n", colorname[i]); + else + die("could not allocate color %d\n", i); + } + loaded = 1; +} + +int +xgetcolor(int x, unsigned char *r, unsigned char *g, unsigned char *b) +{ + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + *r = dc.col[x].color.red >> 8; + *g = dc.col[x].color.green >> 8; + *b = dc.col[x].color.blue >> 8; + + return 0; +} + +int +xsetcolorname(int x, const char *name) +{ + Color ncolor; + + if (!BETWEEN(x, 0, dc.collen)) + return 1; + + if (!xloadcolor(x, name, &ncolor)) + return 1; + + XftColorFree(xw.dpy, xw.vis, xw.cmap, &dc.col[x]); + dc.col[x] = ncolor; + + return 0; +} + +/* + * Absolute coordinates. + */ +void +xclear(int x1, int y1, int x2, int y2) +{ + XftDrawRect(xw.draw, + &dc.col[IS_SET(MODE_REVERSE)? defaultfg : defaultbg], + x1, y1, x2-x1, y2-y1); +} + +void +xhints(void) +{ + XClassHint class = {opt_name ? opt_name : termname, + opt_class ? opt_class : termname}; + XWMHints wm = {.flags = InputHint, .input = 1}; + XSizeHints *sizeh; + + sizeh = XAllocSizeHints(); + + sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; + sizeh->height = win.h; + sizeh->width = win.w; + sizeh->height_inc = win.ch; + sizeh->width_inc = win.cw; + sizeh->base_height = 2 * borderpx; + sizeh->base_width = 2 * borderpx; + sizeh->min_height = win.ch + 2 * borderpx; + sizeh->min_width = win.cw + 2 * borderpx; + if (xw.isfixed) { + sizeh->flags |= PMaxSize; + sizeh->min_width = sizeh->max_width = win.w; + sizeh->min_height = sizeh->max_height = win.h; + } + if (xw.gm & (XValue|YValue)) { + sizeh->flags |= USPosition | PWinGravity; + sizeh->x = xw.l; + sizeh->y = xw.t; + sizeh->win_gravity = xgeommasktogravity(xw.gm); + } + + XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, + &class); + XFree(sizeh); +} + +int +xgeommasktogravity(int mask) +{ + switch (mask & (XNegative|YNegative)) { + case 0: + return NorthWestGravity; + case XNegative: + return NorthEastGravity; + case YNegative: + return SouthWestGravity; + } + + return SouthEastGravity; +} + +int +xloadfont(Font *f, FcPattern *pattern) +{ + FcPattern *configured; + FcPattern *match; + FcResult result; + XGlyphInfo extents; + int wantattr, haveattr; + + /* + * Manually configure instead of calling XftMatchFont + * so that we can use the configured pattern for + * "missing glyph" lookups. + */ + configured = FcPatternDuplicate(pattern); + if (!configured) + return 1; + + FcConfigSubstitute(NULL, configured, FcMatchPattern); + XftDefaultSubstitute(xw.dpy, xw.scr, configured); + + match = FcFontMatch(NULL, configured, &result); + if (!match) { + FcPatternDestroy(configured); + return 1; + } + + if (!(f->match = XftFontOpenPattern(xw.dpy, match))) { + FcPatternDestroy(configured); + FcPatternDestroy(match); + return 1; + } + + if ((XftPatternGetInteger(pattern, "slant", 0, &wantattr) == + XftResultMatch)) { + /* + * Check if xft was unable to find a font with the appropriate + * slant but gave us one anyway. Try to mitigate. + */ + if ((XftPatternGetInteger(f->match->pattern, "slant", 0, + &haveattr) != XftResultMatch) || haveattr < wantattr) { + f->badslant = 1; + fputs("font slant does not match\n", stderr); + } + } + + if ((XftPatternGetInteger(pattern, "weight", 0, &wantattr) == + XftResultMatch)) { + if ((XftPatternGetInteger(f->match->pattern, "weight", 0, + &haveattr) != XftResultMatch) || haveattr != wantattr) { + f->badweight = 1; + fputs("font weight does not match\n", stderr); + } + } + + XftTextExtentsUtf8(xw.dpy, f->match, + (const FcChar8 *) ascii_printable, + strlen(ascii_printable), &extents); + + f->set = NULL; + f->pattern = configured; + + f->ascent = f->match->ascent; + f->descent = f->match->descent; + f->lbearing = 0; + f->rbearing = f->match->max_advance_width; + + f->height = f->ascent + f->descent; + f->width = DIVCEIL(extents.xOff, strlen(ascii_printable)); + + return 0; +} + +void +xloadfonts(const char *fontstr, double fontsize) +{ + FcPattern *pattern; + double fontval; + + if (fontstr[0] == '-') + pattern = XftXlfdParse(fontstr, False, False); + else + pattern = FcNameParse((const FcChar8 *)fontstr); + + if (!pattern) + die("can't open font %s\n", fontstr); + + if (fontsize > 1) { + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternDel(pattern, FC_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, (double)fontsize); + usedfontsize = fontsize; + } else { + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = fontval; + } else if (FcPatternGetDouble(pattern, FC_SIZE, 0, &fontval) == + FcResultMatch) { + usedfontsize = -1; + } else { + /* + * Default font size is 12, if none given. This is to + * have a known usedfontsize value. + */ + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12); + usedfontsize = 12; + } + defaultfontsize = usedfontsize; + } + + if (xloadfont(&dc.font, pattern)) + die("can't open font %s\n", fontstr); + + if (usedfontsize < 0) { + FcPatternGetDouble(dc.font.match->pattern, + FC_PIXEL_SIZE, 0, &fontval); + usedfontsize = fontval; + if (fontsize == 0) + defaultfontsize = fontval; + } + + /* Setting character width and height. */ + win.cw = ceilf(dc.font.width * cwscale); + win.ch = ceilf(dc.font.height * chscale); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC); + if (xloadfont(&dc.ifont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_WEIGHT); + FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD); + if (xloadfont(&dc.ibfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); + if (xloadfont(&dc.bfont, pattern)) + die("can't open font %s\n", fontstr); + + FcPatternDestroy(pattern); +} + +void +xunloadfont(Font *f) +{ + XftFontClose(xw.dpy, f->match); + FcPatternDestroy(f->pattern); + if (f->set) + FcFontSetDestroy(f->set); +} + +void +xunloadfonts(void) +{ + /* Free the loaded fonts in the font cache. */ + while (frclen > 0) + XftFontClose(xw.dpy, frc[--frclen].font); + + xunloadfont(&dc.font); + xunloadfont(&dc.bfont); + xunloadfont(&dc.ifont); + xunloadfont(&dc.ibfont); +} + +int +ximopen(Display *dpy) +{ + XIMCallback imdestroy = { .client_data = NULL, .callback = ximdestroy }; + XICCallback icdestroy = { .client_data = NULL, .callback = xicdestroy }; + + xw.ime.xim = XOpenIM(xw.dpy, NULL, NULL, NULL); + if (xw.ime.xim == NULL) + return 0; + + if (XSetIMValues(xw.ime.xim, XNDestroyCallback, &imdestroy, NULL)) + fprintf(stderr, "XSetIMValues: " + "Could not set XNDestroyCallback.\n"); + + xw.ime.spotlist = XVaCreateNestedList(0, XNSpotLocation, &xw.ime.spot, + NULL); + + if (xw.ime.xic == NULL) { + xw.ime.xic = XCreateIC(xw.ime.xim, XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, xw.win, + XNDestroyCallback, &icdestroy, + NULL); + } + if (xw.ime.xic == NULL) + fprintf(stderr, "XCreateIC: Could not create input context.\n"); + + return 1; +} + +void +ximinstantiate(Display *dpy, XPointer client, XPointer call) +{ + if (ximopen(dpy)) + XUnregisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); +} + +void +ximdestroy(XIM xim, XPointer client, XPointer call) +{ + xw.ime.xim = NULL; + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + XFree(xw.ime.spotlist); +} + +int +xicdestroy(XIC xim, XPointer client, XPointer call) +{ + xw.ime.xic = NULL; + return 1; +} + +void +xinit(int cols, int rows) +{ + XGCValues gcvalues; + Cursor cursor; + Window parent; + pid_t thispid = getpid(); + XColor xmousefg, xmousebg; + + if (!(xw.dpy = XOpenDisplay(NULL))) + die("can't open display\n"); + xw.scr = XDefaultScreen(xw.dpy); + xw.vis = XDefaultVisual(xw.dpy, xw.scr); + + /* font */ + if (!FcInit()) + die("could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; + xloadfonts(usedfont, 0); + + /* colors */ + xw.cmap = XDefaultColormap(xw.dpy, xw.scr); + xloadcols(); + + /* adjust fixed window geometry */ + win.w = 2 * borderpx + cols * win.cw; + win.h = 2 * borderpx + rows * win.ch; + if (xw.gm & XNegative) + xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2; + if (xw.gm & YNegative) + xw.t += DisplayHeight(xw.dpy, xw.scr) - win.h - 2; + + /* Events */ + xw.attrs.background_pixel = dc.col[defaultbg].pixel; + xw.attrs.border_pixel = dc.col[defaultbg].pixel; + xw.attrs.bit_gravity = NorthWestGravity; + xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask + | ExposureMask | VisibilityChangeMask | StructureNotifyMask + | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask; + xw.attrs.colormap = xw.cmap; + + if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0)))) + parent = XRootWindow(xw.dpy, xw.scr); + xw.win = XCreateWindow(xw.dpy, parent, xw.l, xw.t, + win.w, win.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput, + xw.vis, CWBackPixel | CWBorderPixel | CWBitGravity + | CWEventMask | CWColormap, &xw.attrs); + + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.graphics_exposures = False; + dc.gc = XCreateGC(xw.dpy, parent, GCGraphicsExposures, + &gcvalues); + xw.buf = XCreatePixmap(xw.dpy, xw.win, win.w, win.h, + DefaultDepth(xw.dpy, xw.scr)); + XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); + XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); + + /* font spec buffer */ + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); + + /* Xft rendering context */ + xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); + + /* input methods */ + if (!ximopen(xw.dpy)) { + XRegisterIMInstantiateCallback(xw.dpy, NULL, NULL, NULL, + ximinstantiate, NULL); + } + + /* white cursor, black outline */ + cursor = XCreateFontCursor(xw.dpy, mouseshape); + XDefineCursor(xw.dpy, xw.win, cursor); + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { + xmousefg.red = 0xffff; + xmousefg.green = 0xffff; + xmousefg.blue = 0xffff; + } + + if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { + xmousebg.red = 0x0000; + xmousebg.green = 0x0000; + xmousebg.blue = 0x0000; + } + + XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); + + xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); + xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); + xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); + xw.netwmiconname = XInternAtom(xw.dpy, "_NET_WM_ICON_NAME", False); + XSetWMProtocols(xw.dpy, xw.win, &xw.wmdeletewin, 1); + + xw.netwmpid = XInternAtom(xw.dpy, "_NET_WM_PID", False); + XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, + PropModeReplace, (uchar *)&thispid, 1); + + win.mode = MODE_NUMLOCK; + resettitle(); + xhints(); + XMapWindow(xw.dpy, xw.win); + XSync(xw.dpy, False); + + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick1); + clock_gettime(CLOCK_MONOTONIC, &xsel.tclick2); + xsel.primary = NULL; + xsel.clipboard = NULL; + xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); + if (xsel.xtarget == None) + xsel.xtarget = XA_STRING; +} + +int +xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) +{ + float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; + ushort mode, prevmode = USHRT_MAX; + Font *font = &dc.font; + int frcflags = FRC_NORMAL; + float runewidth = win.cw; + Rune rune; + FT_UInt glyphidx; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; + int i, f, numspecs = 0; + + for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { + /* Fetch rune and mode for current glyph. */ + rune = glyphs[i].u; + mode = glyphs[i].mode; + + /* Skip dummy wide-character spacing. */ + if (mode == ATTR_WDUMMY) + continue; + + /* Determine font for glyph if different from previous glyph. */ + if (prevmode != mode) { + prevmode = mode; + font = &dc.font; + frcflags = FRC_NORMAL; + runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { + font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } else if (mode & ATTR_ITALIC) { + font = &dc.ifont; + frcflags = FRC_ITALIC; + } else if (mode & ATTR_BOLD) { + font = &dc.bfont; + frcflags = FRC_BOLD; + } + yp = winy + font->ascent; + } + + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (glyphidx) { + specs[numspecs].font = font->match; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + continue; + } + + /* Fallback on font cache, search the font cache for match. */ + for (f = 0; f < frclen; f++) { + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); + /* Everything correct. */ + if (glyphidx && frc[f].flags == frcflags) + break; + /* We got a default font for a not found glyph. */ + if (!glyphidx && frc[f].flags == frcflags + && frc[f].unicodep == rune) { + break; + } + } + + /* Nothing was found. Use fontconfig to find matching font. */ + if (f >= frclen) { + if (!font->set) + font->set = FcFontSort(0, font->pattern, + 1, 0, &fcres); + fcsets[0] = font->set; + + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + * + * Xft and fontconfig are design failures. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, rune); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, 1, + fcpattern, &fcres); + + /* Allocate memory for the new cache entry. */ + if (frclen >= frccap) { + frccap += 16; + frc = xrealloc(frc, frccap * sizeof(Fontcache)); + } + + frc[frclen].font = XftFontOpenPattern(xw.dpy, + fontpattern); + if (!frc[frclen].font) + die("XftFontOpenPattern failed seeking fallback font: %s\n", + strerror(errno)); + frc[frclen].flags = frcflags; + frc[frclen].unicodep = rune; + + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); + + f = frclen; + frclen++; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + } + + specs[numspecs].font = frc[f].font; + specs[numspecs].glyph = glyphidx; + specs[numspecs].x = (short)xp; + specs[numspecs].y = (short)yp; + xp += runewidth; + numspecs++; + } + + return numspecs; +} + +void +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) +{ + int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); + int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, + width = charlen * win.cw; + Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; + XRenderColor colfg, colbg; + XRectangle r; + + /* Fallback on color display for attributes not supported by the font */ + if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) { + if (dc.ibfont.badslant || dc.ibfont.badweight) + base.fg = defaultattr; + } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) || + (base.mode & ATTR_BOLD && dc.bfont.badweight)) { + base.fg = defaultattr; + } + + if (IS_TRUECOL(base.fg)) { + colfg.alpha = 0xffff; + colfg.red = TRUERED(base.fg); + colfg.green = TRUEGREEN(base.fg); + colfg.blue = TRUEBLUE(base.fg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &truefg); + fg = &truefg; + } else { + fg = &dc.col[base.fg]; + } + + if (IS_TRUECOL(base.bg)) { + colbg.alpha = 0xffff; + colbg.green = TRUEGREEN(base.bg); + colbg.red = TRUERED(base.bg); + colbg.blue = TRUEBLUE(base.bg); + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, &truebg); + bg = &truebg; + } else { + bg = &dc.col[base.bg]; + } + + /* Change basic system colors [0-7] to bright system colors [8-15] */ + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7)) + fg = &dc.col[base.fg + 8]; + + if (IS_SET(MODE_REVERSE)) { + if (fg == &dc.col[defaultfg]) { + fg = &dc.col[defaultbg]; + } else { + colfg.red = ~fg->color.red; + colfg.green = ~fg->color.green; + colfg.blue = ~fg->color.blue; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, + &revfg); + fg = &revfg; + } + + if (bg == &dc.col[defaultbg]) { + bg = &dc.col[defaultfg]; + } else { + colbg.red = ~bg->color.red; + colbg.green = ~bg->color.green; + colbg.blue = ~bg->color.blue; + colbg.alpha = bg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colbg, + &revbg); + bg = &revbg; + } + } + + if ((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) { + colfg.red = fg->color.red / 2; + colfg.green = fg->color.green / 2; + colfg.blue = fg->color.blue / 2; + colfg.alpha = fg->color.alpha; + XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg); + fg = &revfg; + } + + if (base.mode & ATTR_REVERSE) { + temp = fg; + fg = bg; + bg = temp; + } + + if (base.mode & ATTR_BLINK && win.mode & MODE_BLINK) + fg = bg; + + if (base.mode & ATTR_INVISIBLE) + fg = bg; + + /* Intelligent cleaning up of the borders. */ + if (x == 0) { + xclear(0, (y == 0)? 0 : winy, borderpx, + winy + win.ch + + ((winy + win.ch >= borderpx + win.th)? win.h : 0)); + } + if (winx + width >= borderpx + win.tw) { + xclear(winx + width, (y == 0)? 0 : winy, win.w, + ((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch))); + } + if (y == 0) + xclear(winx, 0, winx + width, borderpx); + if (winy + win.ch >= borderpx + win.th) + xclear(winx, winy + win.ch, winx + width, win.h); + + /* Clean up the region we want to draw to. */ + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); + + /* Set the clip region because Xft is sometimes dirty. */ + r.x = 0; + r.y = 0; + r.height = win.ch; + r.width = width; + XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); + + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + + /* Render underline and strikethrough. */ + if (base.mode & ATTR_UNDERLINE) { + XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, + width, 1); + } + + if (base.mode & ATTR_STRUCK) { + XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3, + width, 1); + } + + /* Reset clip to none. */ + XftDrawSetClip(xw.draw, 0); +} + +void +xdrawglyph(Glyph g, int x, int y) +{ + int numspecs; + XftGlyphFontSpec spec; + + numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); + xdrawglyphfontspecs(&spec, g, numspecs, x, y); +} + +void +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) +{ + Color drawcol; + + /* remove the old cursor */ + if (selected(ox, oy)) + og.mode ^= ATTR_REVERSE; + xdrawglyph(og, ox, oy); + + if (IS_SET(MODE_HIDE)) + return; + + /* + * Select the right color for the right mode. + */ + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + + if (IS_SET(MODE_REVERSE)) { + g.mode |= ATTR_REVERSE; + g.bg = defaultfg; + if (selected(cx, cy)) { + drawcol = dc.col[defaultcs]; + g.fg = defaultrcs; + } else { + drawcol = dc.col[defaultrcs]; + g.fg = defaultcs; + } + } else { + if (selected(cx, cy)) { + g.fg = defaultfg; + g.bg = defaultrcs; + } else { + g.fg = defaultbg; + g.bg = defaultcs; + } + drawcol = dc.col[g.bg]; + } + + /* draw the new one */ + if (IS_SET(MODE_FOCUSED)) { + switch (win.cursor) { + case 7: /* st extension */ + g.u = 0x2603; /* snowman (U+2603) */ + /* FALLTHROUGH */ + case 0: /* Blinking Block */ + case 1: /* Blinking Block (Default) */ + case 2: /* Steady Block */ + xdrawglyph(g, cx, cy); + break; + case 3: /* Blinking Underline */ + case 4: /* Steady Underline */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - \ + cursorthickness, + win.cw, cursorthickness); + break; + case 5: /* Blinking bar */ + case 6: /* Steady bar */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + cursorthickness, win.ch); + break; + } + } else { + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + win.cw - 1, 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + (cx + 1) * win.cw - 1, + borderpx + cy * win.ch, + 1, win.ch - 1); + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - 1, + win.cw, 1); + } +} + +void +xsetenv(void) +{ + char buf[sizeof(long) * 8 + 1]; + + snprintf(buf, sizeof(buf), "%lu", xw.win); + setenv("WINDOWID", buf, 1); +} + +void +xseticontitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMIconName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmiconname); + XFree(prop.value); +} + +void +xsettitle(char *p) +{ + XTextProperty prop; + DEFAULT(p, opt_title); + + if (Xutf8TextListToTextProperty(xw.dpy, &p, 1, XUTF8StringStyle, + &prop) != Success) + return; + XSetWMName(xw.dpy, xw.win, &prop); + XSetTextProperty(xw.dpy, xw.win, &prop, xw.netwmname); + XFree(prop.value); +} + +int +xstartdraw(void) +{ + return IS_SET(MODE_VISIBLE); +} + +void +xdrawline(Line line, int x1, int y1, int x2) +{ + int i, x, ox, numspecs; + Glyph base, new; + XftGlyphFontSpec *specs = xw.specbuf; + + numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); + i = ox = 0; + for (x = x1; x < x2 && i < numspecs; x++) { + new = line[x]; + if (new.mode == ATTR_WDUMMY) + continue; + if (selected(x, y1)) + new.mode ^= ATTR_REVERSE; + if (i > 0 && ATTRCMP(base, new)) { + xdrawglyphfontspecs(specs, base, i, ox, y1); + specs += i; + numspecs -= i; + i = 0; + } + if (i == 0) { + ox = x; + base = new; + } + i++; + } + if (i > 0) + xdrawglyphfontspecs(specs, base, i, ox, y1); +} + +void +xfinishdraw(void) +{ + XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, + win.h, 0, 0); + XSetForeground(xw.dpy, dc.gc, + dc.col[IS_SET(MODE_REVERSE)? + defaultfg : defaultbg].pixel); +} + +void +xximspot(int x, int y) +{ + if (xw.ime.xic == NULL) + return; + + xw.ime.spot.x = borderpx + x * win.cw; + xw.ime.spot.y = borderpx + (y + 1) * win.ch; + + XSetICValues(xw.ime.xic, XNPreeditAttributes, xw.ime.spotlist, NULL); +} + +void +expose(XEvent *ev) +{ + redraw(); +} + +void +visibility(XEvent *ev) +{ + XVisibilityEvent *e = &ev->xvisibility; + + MODBIT(win.mode, e->state != VisibilityFullyObscured, MODE_VISIBLE); +} + +void +unmap(XEvent *ev) +{ + win.mode &= ~MODE_VISIBLE; +} + +void +xsetpointermotion(int set) +{ + MODBIT(xw.attrs.event_mask, set, PointerMotionMask); + XChangeWindowAttributes(xw.dpy, xw.win, CWEventMask, &xw.attrs); +} + +void +xsetmode(int set, unsigned int flags) +{ + int mode = win.mode; + MODBIT(win.mode, set, flags); + if ((win.mode & MODE_REVERSE) != (mode & MODE_REVERSE)) + redraw(); +} + +int +xsetcursor(int cursor) +{ + if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ + return 1; + win.cursor = cursor; + return 0; +} + +void +xseturgency(int add) +{ + XWMHints *h = XGetWMHints(xw.dpy, xw.win); + + MODBIT(h->flags, add, XUrgencyHint); + XSetWMHints(xw.dpy, xw.win, h); + XFree(h); +} + +void +xbell(void) +{ + if (!(IS_SET(MODE_FOCUSED))) + xseturgency(1); + if (bellvolume) + XkbBell(xw.dpy, xw.win, bellvolume, (Atom)NULL); +} + +void +focus(XEvent *ev) +{ + XFocusChangeEvent *e = &ev->xfocus; + + if (e->mode == NotifyGrab) + return; + + if (ev->type == FocusIn) { + if (xw.ime.xic) + XSetICFocus(xw.ime.xic); + win.mode |= MODE_FOCUSED; + xseturgency(0); + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[I", 3, 0); + } else { + if (xw.ime.xic) + XUnsetICFocus(xw.ime.xic); + win.mode &= ~MODE_FOCUSED; + if (IS_SET(MODE_FOCUS)) + ttywrite("\033[O", 3, 0); + } +} + +int +match(uint mask, uint state) +{ + return mask == XK_ANY_MOD || mask == (state & ~ignoremod); +} + +char* +kmap(KeySym k, uint state) +{ + Key *kp; + int i; + + /* Check for mapped keys out of X11 function keys. */ + for (i = 0; i < LEN(mappedkeys); i++) { + if (mappedkeys[i] == k) + break; + } + if (i == LEN(mappedkeys)) { + if ((k & 0xFFFF) < 0xFD00) + return NULL; + } + + for (kp = key; kp < key + LEN(key); kp++) { + if (kp->k != k) + continue; + + if (!match(kp->mask, state)) + continue; + + if (IS_SET(MODE_APPKEYPAD) ? kp->appkey < 0 : kp->appkey > 0) + continue; + if (IS_SET(MODE_NUMLOCK) && kp->appkey == 2) + continue; + + if (IS_SET(MODE_APPCURSOR) ? kp->appcursor < 0 : kp->appcursor > 0) + continue; + + return kp->s; + } + + return NULL; +} + +void +kpress(XEvent *ev) +{ + XKeyEvent *e = &ev->xkey; + KeySym ksym = NoSymbol; + char buf[64], *customkey; + int len; + Rune c; + Status status; + Shortcut *bp; + + if (IS_SET(MODE_KBDLOCK)) + return; + + if (xw.ime.xic) { + len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status); + if (status == XBufferOverflow) + return; + } else { + len = XLookupString(e, buf, sizeof buf, &ksym, NULL); + } + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { + bp->func(&(bp->arg)); + return; + } + } + + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); + return; + } + + /* 3. composed string from input method */ + if (len == 0) + return; + if (len == 1 && e->state & Mod1Mask) { + if (IS_SET(MODE_8BIT)) { + if (*buf < 0177) { + c = *buf | 0x80; + len = utf8encode(c, buf); + } + } else { + buf[1] = buf[0]; + buf[0] = '\033'; + len = 2; + } + } + ttywrite(buf, len, 1); +} + +void +cmessage(XEvent *e) +{ + /* + * See xembed specs + * http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html + */ + if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) { + if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) { + win.mode |= MODE_FOCUSED; + xseturgency(0); + } else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) { + win.mode &= ~MODE_FOCUSED; + } + } else if (e->xclient.data.l[0] == xw.wmdeletewin) { + ttyhangup(); + exit(0); + } +} + +void +resize(XEvent *e) +{ + if (e->xconfigure.width == win.w && e->xconfigure.height == win.h) + return; + + cresize(e->xconfigure.width, e->xconfigure.height); +} + +void +run(void) +{ + XEvent ev; + int w = win.w, h = win.h; + fd_set rfd; + int xfd = XConnectionNumber(xw.dpy), ttyfd, xev, drawing; + struct timespec seltv, *tv, now, lastblink, trigger; + double timeout; + + /* Waiting for window mapping */ + do { + XNextEvent(xw.dpy, &ev); + /* + * This XFilterEvent call is required because of XOpenIM. It + * does filter out the key event and some client message for + * the input method too. + */ + if (XFilterEvent(&ev, None)) + continue; + if (ev.type == ConfigureNotify) { + w = ev.xconfigure.width; + h = ev.xconfigure.height; + } + } while (ev.type != MapNotify); + + ttyfd = ttynew(opt_line, shell, opt_io, opt_cmd); + cresize(w, h); + + for (timeout = -1, drawing = 0, lastblink = (struct timespec){0};;) { + FD_ZERO(&rfd); + FD_SET(ttyfd, &rfd); + FD_SET(xfd, &rfd); + + if (XPending(xw.dpy)) + timeout = 0; /* existing events might not set xfd */ + + seltv.tv_sec = timeout / 1E3; + seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec); + tv = timeout >= 0 ? &seltv : NULL; + + if (pselect(MAX(xfd, ttyfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) { + if (errno == EINTR) + continue; + die("select failed: %s\n", strerror(errno)); + } + clock_gettime(CLOCK_MONOTONIC, &now); + + if (FD_ISSET(ttyfd, &rfd)) + ttyread(); + + xev = 0; + while (XPending(xw.dpy)) { + xev = 1; + XNextEvent(xw.dpy, &ev); + if (XFilterEvent(&ev, None)) + continue; + if (handler[ev.type]) + (handler[ev.type])(&ev); + } + + /* + * To reduce flicker and tearing, when new content or event + * triggers drawing, we first wait a bit to ensure we got + * everything, and if nothing new arrives - we draw. + * We start with trying to wait minlatency ms. If more content + * arrives sooner, we retry with shorter and shorter periods, + * and eventually draw even without idle after maxlatency ms. + * Typically this results in low latency while interacting, + * maximum latency intervals during `cat huge.txt`, and perfect + * sync with periodic updates from animations/key-repeats/etc. + */ + if (FD_ISSET(ttyfd, &rfd) || xev) { + if (!drawing) { + trigger = now; + drawing = 1; + } + timeout = (maxlatency - TIMEDIFF(now, trigger)) \ + / maxlatency * minlatency; + if (timeout > 0) + continue; /* we have time, try to find idle */ + } + + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; + if (blinktimeout && tattrset(ATTR_BLINK)) { + timeout = blinktimeout - TIMEDIFF(now, lastblink); + if (timeout <= 0) { + if (-timeout > blinktimeout) /* start visible */ + win.mode |= MODE_BLINK; + win.mode ^= MODE_BLINK; + tsetdirtattr(ATTR_BLINK); + lastblink = now; + timeout = blinktimeout; + } + } + + draw(); + XFlush(xw.dpy); + drawing = 0; + } +} + +void +usage(void) +{ + die("usage: %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid]" + " [[-e] command [args ...]]\n" + " %s [-aiv] [-c class] [-f font] [-g geometry]" + " [-n name] [-o file]\n" + " [-T title] [-t title] [-w windowid] -l line" + " [stty_args ...]\n", argv0, argv0); +} + +int +main(int argc, char *argv[]) +{ + xw.l = xw.t = 0; + xw.isfixed = False; + xsetcursor(cursorshape); + + ARGBEGIN { + case 'a': + allowaltscreen = 0; + break; + case 'c': + opt_class = EARGF(usage()); + break; + case 'e': + if (argc > 0) + --argc, ++argv; + goto run; + case 'f': + opt_font = EARGF(usage()); + break; + case 'g': + xw.gm = XParseGeometry(EARGF(usage()), + &xw.l, &xw.t, &cols, &rows); + break; + case 'i': + xw.isfixed = 1; + break; + case 'o': + opt_io = EARGF(usage()); + break; + case 'l': + opt_line = EARGF(usage()); + break; + case 'n': + opt_name = EARGF(usage()); + break; + case 't': + case 'T': + opt_title = EARGF(usage()); + break; + case 'w': + opt_embed = EARGF(usage()); + break; + case 'v': + die("%s " VERSION "\n", argv0); + break; + default: + usage(); + } ARGEND; + +run: + if (argc > 0) /* eat all remaining arguments */ + opt_cmd = argv; + + if (!opt_title) + opt_title = (opt_line || !opt_cmd) ? "st" : opt_cmd[0]; + + setlocale(LC_CTYPE, ""); + XSetLocaleModifiers(""); + cols = MAX(cols, 1); + rows = MAX(rows, 1); + tnew(cols, rows); + xinit(cols, rows); + xsetenv(); + selinit(); + run(); + + return 0; +} diff --git a/st/x.o b/st/x.o new file mode 100644 index 0000000000000000000000000000000000000000..576b9f0d7bd630bdb0f9ad1c7028c1eca599ca4d GIT binary patch literal 75256 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 W{XwR;`1Sb5s<8cRzPrD4{Qn1$_MQF! literal 0 HcmV?d00001