From ba09e1d356a5f8b5c25de3cec25fbcc51fdc944b Mon Sep 17 00:00:00 2001 From: "R. Eric Wheeler" Date: Sat, 27 Feb 2021 19:10:44 -0800 Subject: [PATCH] Updated libgetch, now includes cinPeek function --- src/Console/Getch.php | 19 +++++- src/Console/Resources/getch.c | 101 +++++++++++++++++++++++++++--- src/Console/Resources/libgetch.so | Bin 16792 -> 17032 bytes tests/Getch/GetchTest.php | 10 +++ 4 files changed, 118 insertions(+), 12 deletions(-) diff --git a/src/Console/Getch.php b/src/Console/Getch.php index 5479c35..dbcc7d8 100644 --- a/src/Console/Getch.php +++ b/src/Console/Getch.php @@ -67,19 +67,32 @@ final class Getch if (null === self::$ffi) { $osFamily = PHP_OS_FAMILY; if ('Windows' === $osFamily) { - self::$ffi = FFI::cdef(self::DECLARATIONS, self::WINDOWS_LIBRARY); + $declarations = self::DECLARATIONS . ' int _kbhit();'; + self::$ffi = FFI::cdef($declarations, self::WINDOWS_LIBRARY); } elseif ('Linux' === $osFamily) { if (!file_exists($linuxLibrary)) { throw new RuntimeException(sprintf('Could not find library file %s.', $linuxLibrary)); } - - self::$ffi = FFI::cdef(self::DECLARATIONS, $linuxLibrary); + $declarations = self::DECLARATIONS . ' int cinPeek();'; + self::$ffi = FFI::cdef($declarations, $linuxLibrary); } else { throw new RuntimeException(sprintf('Sorry, %s is not supported yet.', $osFamily)); } } } + public function peek(): int + { + if(PHP_OS_FAMILY === 'Windows') { + if($ffi->_kbhit()) { + $result = $ffi->_getch(); + $ffi->_ungetch($result); + return $result; + } + return -1; + } + return $ffi->cinPeek(); + } /* public function keyCode(string $device): array { diff --git a/src/Console/Resources/getch.c b/src/Console/Resources/getch.c index ac71127..b39122a 100644 --- a/src/Console/Resources/getch.c +++ b/src/Console/Resources/getch.c @@ -20,6 +20,33 @@ static struct termios oldTermAttributes; +inline static void reverseString(char * str) +{ + if (str) + { + char * end = str + strlen(str) - 1; + + // swap the values in the two given variables + // XXX: fails when a and b refer to same memory location +# define XOR_SWAP(a,b) do\ + {\ + (a) ^= (b);\ + (b) ^= (a);\ + (a) ^= (b);\ + } while (0) + + // walk inwards from both ends of the string, + // swapping until we get to the middle + while (str < end) + { + XOR_SWAP(*str, *end); + str++; + end--; + } +# undef XOR_SWAP + } +} + static int discardRead(unsigned int length) { char buffer[length]; @@ -37,7 +64,7 @@ static int discardRead(unsigned int length) return (int)bytesRead; } -static int getEventDevice(const char * device) +static int getEventDevice(char ** device) { glob_t search; @@ -63,7 +90,8 @@ static int getEventDevice(const char * device) for(int i = 0; i z<5VS!s(8z$ZPuIap5x{;DM>jg?QVlh2`k9MG>3M*+1(t{)9z8zJft<7gjDI>l2N)s)qIG5v9^(qqbUvXkX`lw1_igD!_mt3ox{Q8n8N3O&9;Qsw?b8T_Bh;IEd!e^ds4t_=RQGI&QBd}5inJyu5lHsBr{#c4Hw z8;e2k7jR4#wW4pj5`L8eiF6eQ;T{^?Nc$BLzXT=xKCOSN#Vs*i>`RA zE!-hmLLKooFwGIOEA(VcCnt$530eD=fM@c7)?05f62Rl>Fdp6$iHo*KQ^?yD_1-DgtXsRXK5&Qkj=Rk4 zVl#J#cL^ft#7~_m=fqt{Q9H^Q-3y(2P^B=Ru< z9k1rQXGF%6m!;q-nfH6e3ZK5H>UbW|I0lDIX`iBeKWF~z{R*dhB~KNEWGpzzIAtyP zBnCmxTW~$(q%?2AXIS*7`4n>vh;EVuJQkdy$!V@nuR11E@f`7t-4_feP z7QB+JM4%FZN(3qqs6^oZBLYU^T#b>c{>1`J>HE`hXJOb#yi)VBSyW-k+YlAzyo0m* z1|M?7(^HtpWeP}henvbkNpi!Ie}i}@@oCAwPCPA@az`Y8ig;R*vjCfj#Dc~_0(dE;c>Wt{xre#7ya@y}P|*TF%xc2HBuw773rHEQ2`?^_Uw z?gfoT;@BWonzjZZXE_dX3gyA3VQw&L_!_4mL#wsE;kbDx8-5jGe|9wXV4H4;5p z5$`fm{;ZK4%3hB)8hs4~$vcLPq(48hju`igbs%~7i{kcE#2?Ts5eOBUmuM;7gg@&r z676{hsT<4v`PlOZfBZ64WHV}liX3k?QdM*2$_Dwfx2KI{#z?-L@*hv0G7|scFp`bK z$x~~SZ>*pvU_sg86+cwjLF!#2fdU-EKcxII!i`^FFm|TvjCrq+t3(=s@}E6H1N$FI zJV>YPtlt(2N&lIY|4i?hn&hbyG|nyJt5*<>RP_!#9>X? zkC62Fj~L05vUReiDDeuel^4upsJ)y z`H9P@4TU1N26fT`j*S{oDOe=;&qk9IFVUHtjXLOo1Ne@U??-BhwSIOh@7+s>UbXf4K*-kZ9G2-^HEboBkh=$rEXJfTLYymYg95^j?>7hFjV zW4e3EQRpkxm!Jyw&jK(~Q@%|kNf%k;AkYDYep8`opr?U8rO&7`WI zKrV@UoF2drPzf@BN)8lNZX;C@!a{wwaI{ws+rdSd+U)+Da} z>-JOcVXC4TezOdecIq;_y4kt!)nwBzmsQbnmCzts3OtMHixm7h4iN7aiRb2I-|; z?(c9K^5=2J=;^r_-ZhIzboW&Fyof%#2^~&G(8)lD&_s55B0p>Q-L}>xv3TIt^&l@> z*D~b4ObZEaNZyYwjwVzP8H#I&2+i<_v~<9CjbYDoxH| zr;#-$F`3u1nEPn@n3~3*=NLX=nZ_=SnZ|~Tr?H1FQSDzz`ClnHjok$?jpsSkh#L3N z#={tT!{$6zLt;#hFdsW<{pLfm^`E61b3iF(hWu%CpcuK;NHq?p{Ar`#{bH3f)tII^ zqi^EZNeEHKaJ=q5kxn`j{xt5~P&1bK&+dMm3MY5n2;z|cxO6pBxjL>~k=0Q4Bizgq z{^KwOEKPp~(@cr!bC;~7W%{fuj?iLgU@e0yu4II!NC(wlm`xSOJzyxiNE%#$!PuC* ze^JJ>L28fQlp0_nhgpl)eZ!ZiSpwJil<^4r?U!;-LkjP-ez&dIrC#M!i9jU+l?eRa z2sB5!Lcv(`Ci?e@(t_sjt_6{f&hGevroHn!gYnh{x6BXk3U|ck?`&$Oe~tXatP1an zgu?1iC;89S2e|jXk7EKR__shaI6lPj29A#dy8`M0Jy3M?gD-Uq0H=fX4_bJ}nUW64 z`Cwb3?AtNZwfu3kkLJOp|5Yd)0_{IvC=7#cyMPHB^gYlTtiwP0??T}=(2ck)uLE^p znrZ{R8T4_`deFn5mqE{fegSPe2l^1on}`*_qoB8eJ_EWAbSYNLouH>d4}wzS{}3Ge zHVMa`I>*e3*VG(@4@?1q{&zp{L7}i6yqHwCdeXE{xodaV^osjuEWc&Z>>Ghf`&FQ6 zv;`|RGm-6QakQgtNkUgls(Z{?|FLn-FQEh>Jb*rr<4wq~CI2k*s^~X3T(cTI4o3A2H?IA^$SuXKnWXV#<3VUy1&^)hcfpEq?&=^^jA4m@x_{ zxd%`h@{N$o|C_mdieYjzhVMWpjj^%c>ciP9ozOU}!dO^{F*1qdl~jp9B?6TQR3cD` zKqUf|2vj0aiNGH*0{lH3fB%N1t4#FG9VPq`r4lV%k@%aewEn)5zhUF=>@;kCGd4q+ z3I5)Wo*^jlv&)B93sDK^=c8)L2;tEg`a_t~QZ1)Hcqy&Y^{Z8^n5HTIO;E|Fh&B`a z{WCv*(I3l{_*<+)8elxD3+8&JrM3{`HRbZ~M~|%kA+3+szmn%T;&Dsz{BbwlC`;a~ zr}p>9Cj1?Hak+QueqOEVLz=c|x=Yi?G<{OjXEl9M)7Lb8Q`7e}y`frp zPEA*9`jDnAn(opx?Nc>hQ(u3#XYLxjnuta{%e;%c3+Kn~@N_PorHg=$cjkWj5(G)UivgX7gYchfT)u+=roNJ{6~ELUoyzwv!08|8HwtzE1&`(0Gf!!O8b1z$lf9Lkc+g zIihd;{7&NuiH{vu{lFb1`sEpkkL~vtfa7J@nDKT}>Q51;eIy@EKS9<(Z^?Y480CJ2 z!%4GF(e;u8uDgWP%=DkgmBMKtcucL)%uEcFQu+(Z;46WbxI{%`8GZWFs+9lFm%;lb zK6W3(fX0jWL43Q6pJQe4g=O%cl)>KyUMg-E%IH@+OSgA=8GNC{Yen%sj%8)^8_M82 z%HVs;;C&JwyAS25GWsu+!B3aL-vVALZvO-P#?j$7nv>f)yde>7Yu*x$wXeWFkVsQ^ zJlrK>c%2{X3U3C`u|rt@04RF94|a1X*ij#C4vWt2uGY39N`D8ue;5G$! z%WN#%6>ZxUHcjY%fd|5Sh2Ahi+g~Es+S1$=4YUS3nrXw#s!wlNv3_km&@~$x1AZg0 zdee&a{(!M+6CmuZ3HUdxqD?nz);+j##k#&<_;h`Z0`Rt_X^qN=4K*0OuLTkh}xWF$839w)E+0hf^}z+ zT|zsbEXZAd@FpvZ5p6;8cI|DC2b)0SF-2QBx1*!m+Zya@72f8(9Z*vg+orrEp=%`) z?XWQc$YSBPAQ|XfXIor&<$&}e>)jCri;Yq^%VF=0MVo{1pzwxU11+&&dpOYA3@6Mf z(?Bp53+`1GoJR@4_DBfDN8{ufm6Lv&umQ{)inh07#XEY@|KD>oZR78ZC~hYzaYoa{ zMKC`Pu{y0k=oyU7c-_o2s|EDDgIm4%+{e#P^MR3!?fJQnDL>~yMJDoLO|*WZXD+tq zbvRSK&CB%9dQ4Zso}S^D=k+>MpDwJp{u&k=k*UJL_PnlVn%0Ue=lF5^|4iFcJt*<| zpD91Ll08;6W8xPEMls>%WOIF^*ExE52Mv{|uGX|2jMbi>Czx(83OFyHnC>jH=jRTl z++Hq|d8Raot>yFnbEaN?;MWegT=uuG$ey2Ln9@4knu^=s3n7)y{UbbJD0$>rWEC;b zTyF*rAY--9dX$7I{r7B5#pOQLp6MV2H1^n@pWArfe3}|(k+U85BfShE6}IQ+yR!?(N{G3N zm}jHGX#rDh(BSSh||BO#bPHX$^iZ#z3 zGp92D214a)byLdr9urX+(xqgjMMFf&v)bW#ePf{abhqK)_VE3Ot`DxeG}NcFcd3lK aNXX@~EgYIH;@FvGuq{SJwCd~c-uE66$CD9oJ*h8cs^#FXvp()>@CFU!$3y+IV1r=;B1-C^0D6tq@ff$+=MNJGQ^> z<<<9Y&_V9xZ_{d%gSyR5YBf1|gJ@Y^g%N#Slty<r#$N84!{>ES~&p7&w*9V4H+{c;;%rE&~Pc)%gN)S=Z7p ztd%o<>fg3`;~Na{2K~jlik^vCx|x~qV^WHmS+9M|2v+#m?(n+fy~2@1cBKfy=6q zQbsGahS-h-`mxR7%6LRO`H0p*@7dP#7O#1?H8xI>1;~oip9EV ztUBD7l(php%U16QRIJ%us|39nqN;@c=}%C?-cQhou2WStx(VWxb%+X5=g?QFE&LSK zq|K@M=z*%jiR@?5B^(>B9ZJQg;D;vI2hOU_veqISnm)p@- z2@Z0&-SY&SSS4I5_ybDlobY6xFxS0`7t@NRk1h!Mx_Q`#(J?HXD?5?&RwLY1A^~jy z!~fM6poCwVd`)>S;pqkF=eI^37~{;zT7+L|yRjAN`kE59NF^MaEE-fI?PLEDK|LMa zhbRUoRk3XEo*4+$4l%!6>QnGSw>&Ngm}>!E5`aypy$eGGKNV8zUN5?Pr@H?`bcsZ| zp$|*jPZT-GIn25ro-1Nnho%DM7mE?ojQkd2ie3l$bo&7eB6OoW!lE8fMNC5H6+S3p zJ~WKbN0n!5u+pTXtT8yrn^#H0C)<5@%5JuQ?*F$Nbp@pZJou^b!<6*S+Mi?1(%Hu& z-+gZ?^1&O)p_cnd>l^t9L^wI@yRU>UV5I}F>Cm?)Vn4T#F&SQ&!O<`7(L6_1;C-+f zu@BZ5BmzPS+usoo65C(f4|b=OG_SP)<<{gs$+iQGQ8`;7L#&7iP%fxD9yQc(Tcm`h}{pi z+|NvE7p`l$5=A~VhJ53V!Ip9Ew+Ud)@%@=F80n%eXBMAL1J3y`wc{0OjJO?j>?__c zr+pJ9Hku*O%tjLhTG(ieK)lm}EfdT?Bk8Db0#W@J=0|lL)jv>?%jrCZGb-Pr(Q~3^ z!Ir_l=pkxwWz1`wKYdS1CdIxbWmSnFr{EYBO^ocF>>J2jVd?9Q%Qo-LULIhh6RLV0 z#db$kF95Hge1q}@N+P(sKnC3XSm{FhN#ziDQ4ITikF*Wmz}R1Ma+~s>wLv564y21I)Xvsx`nW;8x%r zUq#BRF^v|W*F7K-lAWk zDy}V`-=gOd3{`#SU9jHlkX-Aj${y73){g?uJ;#J5I!M1MSDBgE6~q-BM5IS0g- z3Xk}xi$`9(fbp`0J)ogNH}9pJg;^GmHIaj#y_nATcz_h?c($zdrDXQKv&Gw6ij^H z#j_DbICNR(-EZqxc?d!)FTDkII xsiC?OuaJY)mG<)getch()); self::assertEquals(71, $g->getch()); } + + public function setPeek() + { + $g = new Getch(); + $g->ungetch('q'); + $peek = $g->peek(); + $getch = $g->getch(); + self::assertEquals($peek, $getch); + self::assertEquals(113, $peek); + } }