From a6f5b1f1b335016dc984da9344494b5bb21e5a2b Mon Sep 17 00:00:00 2001 From: Isaac Wisdom Date: Fri, 16 Jul 2021 23:33:39 -0700 Subject: [PATCH] First working build of "flexible triggers and actions" for Release 2.0 --- RemarkableLamyEraser.pro | 17 +- RemarkableLamyEraser/LamyEraser.conf | 36 +++ RemarkableLamyEraser/RemarkableLamyEraser | Bin 50648 -> 59552 bytes actions.c | 34 +-- actions.h | 85 +++---- configuration.c | 112 +++++++++ configuration.h | 64 +++++ main.c | 275 ++++++++++++---------- orientation.c | 2 - triggers.c | 11 +- triggers.h | 39 +-- 11 files changed, 447 insertions(+), 228 deletions(-) create mode 100644 RemarkableLamyEraser/LamyEraser.conf create mode 100644 configuration.c create mode 100644 configuration.h diff --git a/RemarkableLamyEraser.pro b/RemarkableLamyEraser.pro index 863a0eb..a517272 100644 --- a/RemarkableLamyEraser.pro +++ b/RemarkableLamyEraser.pro @@ -8,10 +8,12 @@ INCLUDEPATH += /opt/codex/rm11x/2.5.2/sysroots/x86_64-codexsdk-linux/usr/lib/arm HEADERS += \ actions.h \ + configuration.h \ orientation.h \ triggers.h \ SOURCES += \ + configuration.c \ orientation.c \ actions.c \ triggers.c \ @@ -19,18 +21,23 @@ SOURCES += \ OTHER_FILES += \ + RemarkableLamyEraser.conf\ RemarkableLamyEraser/LamyEraser.service \ README.md \ - triggers_struct.h \ - triggers_struct.c \ +conf.files = RemarkableLamyEraser/LamyEraser.conf +conf.path = /home/root/.config/LamyEraser service.files = RemarkableLamyEraser/LamyEraser.service - +service.path = /home/root/RemarkableLamyEraser target.path = /home/root/RemarkableLamyEraser -service.path = /home/root/RemarkableLamyEraser + INSTALLS += \ target \ - service + service \ + conf \ + +DISTFILES += \ + RemarkableLamyEraser/LamyEraser.conf diff --git a/RemarkableLamyEraser/LamyEraser.conf b/RemarkableLamyEraser/LamyEraser.conf new file mode 100644 index 0000000..0a84f59 --- /dev/null +++ b/RemarkableLamyEraser/LamyEraser.conf @@ -0,0 +1,36 @@ +# RemarkableLamyEraser Configuration File~ +# Set up your triggers and effects here +# Any trigger left blank will have no effect. +# Effects are divided into tools and actions : +# Actions are compatible with click-type actions + +# The available actions are: +# toolbar : presses the toolbar panel button +# writing : presses the writing utensil button +# undo : presses the undo button +# redo : presses the redo button + +# Tools are compatible with press&hold type triggers, or with click type triggers as toggles +# The available tools are: +# erase : changes to eraser tool. on deactivation, changes back to writing utensil +# note that on the RM2, this mode uses special features available from the marker plus. +# erase-selection : changes to erase selection tool. on deactivation, changes back to writing utensil +# select : changes to select tool. on deactivation, changes back to writing utensil + +# Example valid configuration lines: +# click erase +# press&hold select + + + +click erase +double-click undo +triple-click +quadruple-click +quintuple-click + +press&hold erase-selection +double-press&hold +triple-press&hold +quadruple-press&hold +quintuple-press&hold diff --git a/RemarkableLamyEraser/RemarkableLamyEraser b/RemarkableLamyEraser/RemarkableLamyEraser index 7bc887529832cb13cc6688cf0e799aa03995b680..1de10eb022e04dacf393d6d650890dfd558d1553 100755 GIT binary patch literal 59552 zcmeFa31C%Moj-o=yEn;Ol9!hSvXIaVhLW~fUVs8^X&aWZrZj;-sYOFbUKUC6(&UAu zLP-m>w1oneE;`txicVEjbWl6mDq7TzR%M*dSe;e_X)Mx3bWqV^`F%d;-1FW|z_!!h zbbd4ce=mGwQt_+yErDZ&;$$SxKW zDDRve7GiqXkl=+1!EzuOA>&SgCc#Dof|i?*)q<`?x`aH?1j@mdfWW~FF$ZZ8k{lj& z4qrp?NO&5IY>yC3m$8A0+-nIQ2`4}kDAzu8B2FjLCd>h!pk*~fu||;RU5ppe zekE)X7$1QsZ);s$-qu*!*4ojtzO=rpy``zFJ6bjwBB?_g)SI_>8CinmL7#ciMmB&v z%K!Bbnx_fb70F;9=pEMF7CB&_g1tTUU9p~ zuIz=Xv_-qB{B$`{qUw#I58x+)D*#9Zq>*s0Ai{J9&(KVSSq@I0d5r>$me_^=?0>v$&8TZ` zdvN1jy~UwDJ$IzlAx`D>AK$p?)eN1Tc*?iNF(H`8U%F@;`0Xw_6ZqvL=r50;Cybzv zjG%Kz(A^{Gx)F5Y2%2T7FZ#oJ(--MaY62nYMI-3H8$n+=f?hF#zHbCwGlITr1ifzr zO&|0{{5nk_B;7WG{@e(1h*^t%T3w5g&y*45CtK}6QK#za%JGtxm$M?-t3 zsHk9h}s@$?~cTz&F;<) z(G{t0B##L+HMB*$BM>!4y1GPbv?10eIu#EVXg1o^SP#9N=0wZ)OXXtWW zcdWiER@YwN+9B3;wZ5w(hHxwM$HQ+t<&7tR}qX53Q>v+}=b{v21%o9TzKkCAtcHxIz_~S18pbLM@ zh4;Jghh6w?7rxzv_qy=)F1*u)x47_CE_{UxuXf?{U3jGnpYFoLF1*-<+b%rUg@;_Y z7x<|gN`E6(^&fjCrEjR_SJxkZ`k}rdqq6^)5W5B{dv_l9?cO!;-E-$&J_I>=-vNGL zTHnwayMNFO?;0@0{U=PZ{e%eh56%}mkN*_%lcsptBbE<}>i%OtFvJgjVhI1n=Dwli zRl5e(!%lLj|5$G2&f~OqKlGDD|1k@^X{c+g00y6deyx+*te((&jc2IQoAKL+a+PVi}HsndLA41+3mwD=euJtweGRhhh8S@9dcK@+6 zp9T$I0ta^uY=-=mYX*$R4Kbl=bbct%L^8o~2H-wWIIz-fziXfFl)4)6{k z&LLNv8^NPK86!VX771e{{ce{4)72@Pq0AB)p9`G5!*8!NXPVNNk0Ne|>2XHrFI{
    nt@FzbA_yAxf;AX)40XG3KFQ2>- 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 literal 50648 zcmeIb3t(JToj-o=+?&a~l1|d3N!n1RwWR?v$)pXmmKM_YBWatYDHN!iq{-w#lT1t| zePBytA5fr$RtkbvlPV&*s8y??q6VniRdl;9yRr&v+7Rg?U`2FU1^fGa&bepiCN0qA zxB6fIKW}?-zOVB=-}61sd*`7*T&e{;dl1)t$Ga0c?V4V{qhMBWSwL1)mss^NGe&~q-P4@dNzmayV_ftin?P(Gr*EE)Iqr=E7y=D=nvY=i#oCp zNF)EL>VNfo@{tb*e*A%FZpixmm;dd<4?O=eiYECU#0)4|u50jvPr5>rM5v%Q^g&Na ztKktJ6j^1xD*2vwKYDR`(ZvbWPw3_iq09){;keq*Z<8;b8H~ zxxYWqvmw^4Mqxct%!Y>M+jfxzp`$!%(mf()59h_*-DyQ6Wbqr0<1bVchMNu$E1 zhPGID6s*Q*SC?pwHN@LQr=mdv)yA3{>mk?K)!Gqn5+I`YM@4NdZL95$*LTHh+v{69 z#HOy+cvLiXMWbR##j^PeYNr>KLaezx)?tg)vUnU1T>tHV99Q^HjZ@T<)!K?~>k^L= z!4;3VL^cFAGe#x>#N*I;gN!jyWHCnP&Si{2A)hfiX^1ffg(-~Dp$izJG71@^K}Q&4 z5HDqn!DSX>ROB4S7zD}~Bfo`=F~}@sj6tP>F)XZPjKQpiF$R&fjL|dKF~*=%#~6cG zBV!CoEsW7Q+ZdxkcQVEx;wcONhu0;3<>~#_X=^Y%Yz_7erw`se=sEN4;jBwh@bBGy zD)R?>Pi6hrW5XH_IAF^Gzwvrf<~0ZWk^_F;0YB@2pLW1cIpAXs_%R23)B!)@fFE?g zhaB)h2fWt-?{dIf9dO(Mw>jWO2fWS!*Erw`z-Ly4|D*gDD^3kY`-Th3_MH-9-|#aj zeZy98->?vO54!8_9z1^b?5~c4M*O>g2d?NFem&GbWJc~DG{rrqO|k2=2=)&}#J*EM z0e>gz$t5a>M1TLur-kqxL|Pa4O#f~0MP>g<19-2k^Xs4)qGw3-_Ma>@gb%a#&~(I& z;QNC8C+ngrZ`yJjWZl`;kQM4bDKeH0rHKBM9|o<=*7Y&Kv$Fe!X8|{YcMmepn?bXn z#|_y{qtbgoqs{=*1RQDF9cgX?{FFiQ(jDv>_E~J+RR_aL)@?RsTua-(c(bhqGtLpEhA1 zbQnjJj#&;Jube&mgRDyW@5Z6yu*HS=N#tupE;;2AHw=kL|5zhpVnkUz^*7wlB7U$)@;K3SbNHAJKk;C?nep{deY1Xg`QAw#2W*ezZHZ-Mazr zgpNAs+JX2K%5A~U)Zg5_{}*S^zPK7TJcqap@p}=bBMfH3PUxk-SSRCbpJ5#^{T=i@pWes1 zJJ|_4%*cH&sQz)!@F^+RKlC%iYmt_ING*de>^AUO&==Y|zY7@s=|v$vHv9x&?Z36~ z+2=vGK!40ZzXtS&LHEPPuR7>u^f&6{@Y%Dnjxd$TIzs=Ib#x!>)w-X74)pOCj~S|5 z4?6UW1@9&B9Hw@kfQ9tP8<5`zmLE8ly+X1E<*$#Sb-c{gjv*poW68S$3 zI>rsOss59z0HeRWScd%APf~0jZAZSPpcmS7?Yk|Y7lHnIq;EKDlJa{n18GBd%f2hb zJ;OU7V~GBtn-L4tzq=D{PN=fBl6RiIk%GQqqHoNE?&FZv`MnSFwBu3Gyr@UEgW0zJ z^@xv5GejHk8fT0`BgH zCY~)eCj9B_i)TWH*bMc1ggLY)VqzR98*)X;Fy5^`Nf{9l7>6V-(~Lb^d{W8=ZP_4r z!Fhx8GWt&*zW9TKj|Y#uWx62NEqlt4t_*1+PorP19t!q;D;Hw~>a`!VW5d8t{}Odp zDF$9W19{9RC;Nka!=)!yoH~RuVoW%Vu%mLw135SQI_UoZdMWZG%)CBJxytXq zz;;=BV&ADVurU|5F#lsx$KcTGusM#wwiBTaYqh4imU!%{OWeN|_=AGX<&9U~=wm$X;>U$J;a8*pM2Og2SKQ{c}eD;+}>hNs> zjXJfCER>sLq9_}b{%-=G)0{k+o#8fM-Wt;Dn5QXRqQ=o6GEM%4%cCA^#SBd`@0eE zNBb57$Zv4>pf}Vj=R#9_`Lu}qGy28u7jA^S6}(H@?TP$&cq!7rgj({P_^< ztOT8PQvrGf%Bp#+>pSc@ifuJ}Obqq6Yq;Olf6^s-U6$B|{+KLJF4p^H@JBwvEQC^o zg$NZ0`N$*R*3CZQqCcU(OZutz6#Kvw$Q2+2;mc0Qco658p>5Ewb;_^l_F6N9@|fb@ z)10Td{$yKWJHG;S5B%F%_N~)F#OTi_*{4jl!#l$-2YY*8Ek@tYhi(sak3o9Zo%>$q zn#|}#J|V1okzcwvc=}mc_WmKGu7Bu{Z=C%V<+wh6FLZsW>Lb~?Hk_t_^pFGC*Ajg6*)9~27b94dHW&jLpk@O49q(P+L!W>wZGAg^a$N~s z)cJP$?!EwB&td+fo|Fr)jrRiDwg}2fAFVA@<8=+%hE;a=Alt%n)WzOO2Z!BrvCcu+ z@>cFTrP|#+r?a6W5ILAAGuvX2deJV7kh(V%p`Dlm)L8cj7F!RZeys6^953H0&oJ_Q z1bL3XTID&xk>~3u7#Yft=l5TA=yWtM;_E&hqSD7 z*9Fx1pCHRMk**sHb)(zBe&pAZYlt(<6M3vfo*W<9{*6wRCu|w|o}KS}+FXh868W;- zr-qXK$pW3@um#>p$OUb`T9rGYQ>`N^k>Be%$vUwj$vPQ8ePG;u@mcWS&Ig~gZMoR? zfq4Nw_|_;NcpN@>4Q-_ndA^emcH8+H7wCh2hYvgs9~?n`uZ>Ikz;hlSJOe)0Q=+W@ zWVs>aob`>!(Cs1|}ZMA0T4;VAomY`i%_PxOSz~5tS zV~90FWsnt!Uj=`Tec!YSq6R374msR_eX{5ZBFM1@<7ZH{L+)GAcD757%X&P%8oFAvuKhcQF>k)ezC}Gj=&1>+F(eS$ zhj9R7JjyQHF>scBL^}unGU&q?|6(WVCPjMr;k@M#KTc3lSSPpLjX# z#$UYrg8%hbjGO=IWr6YJh4R;5`P77gSN6|NeMR^VygYFE>#w|U6=?oruYA&9^2#Ut zov+mSBd@4=4);sDiss2aQHJ{i+)I{ch@o$Y6PQJNHR z_Z%NMt_8%-VL2C}&Nz0kzO3NxQ_o<`uJZrl2~}_U~tE9aPW!Y zpTSSFkw-A{FT*+?+$t=mz7*OUxmS+g!O(XR?;h@r>>LV2b`NViXu~APJ!;oq0Qbzy zpXo8(kIxAG3v8<%!q{d)S4u|cyD3aN5ZP%`56eOw%xm)~9W0|Pi(NkSk@0IoY7H#J zhlVpEPYjF5j^Q%V3xDhwA&YX2kJz%mMH$7J(9jHdJ$cPX9^mzF*F2JCh`k7#5eBef zxD{b7LLows<1OyjupN)JO%?A8JzLQx*=~bnEZ^OO<$pN)Vw+7PzT^+s>l9hAMU3Y- zP1=0WdKJybxrns6Qm$|KnxKVoTH2KaT*5gN4Ov1pbgg` zZb3M+1U6&rsi?zT(u=vT6297xwRhd!gV{*Wc9l}McyMw3k&|a``@%t@;_k%WKSOo8e%^J+dKFBx!2EMeT!RS?a|_{SS(&#)D~-~ zZ!7L@sqX?M+Fswap?-Z^w0Lu@p`|t6R*Y%=fBx!%%F1O6=hk($HpZGlc3yQ?h1$A9 z;kHn5XIHGDcnTznn;JvYuMQPAMmH9B^t82wwuYL!qMacay#a7kBH@jpXyBgK z5Je5Kj(D_#+chGDYjUis6=;3DHP$hCc4%@Zeq-yZZ;ekrA0M>p*5;P@Qe+oxoEz`z ziBgY)Hli+(Ra0y8d8&~{Gzt7rx5BR9(Db4Ret+-Y(oNsCHblFNyJHQJ;`Vw}JARJC z?@J?KPXNNv8wXtw&<2ETbuOZ&L5ds~}p_*8%ZBbW!ceJZ|#q`-B z{wPawRFXom4zVy=|Blk=OB5B0yJGDjipM(Mz21^@tcb%#ssAl)aiq~D(1}Ksb#=5Y z+7M4>(JgWGd^?MC%R4&7=nUX@O>!tJDk@6SHJ`bVu{63Fb+#EJ82VTBqSdP}S-PrX zp+v=kie(GRF^=FOTdZ2CfK`hZOTx-EE9Nh%R#gravqLM_R8*i=*K};?h;2e8{6BhC zC+4<_`t~iVNry^`O6ZR8J6;$oIR~|N;B-WBbR!0ECLB%UIEFc(wL8=ii-+n%8!>G( zhSUJdjx|~yLonJ?91RYAvN}09Pi-s;fhE>-v#%{w1N3YmN{Vb{p(ai@b1~t_{))Ds zG6{9mw>#+5-$Fhd?(B+ocZ(IP7A~3{k}WJWZ{@-eTN(2WhvTv4<~B|PwoJ{cB}*z6 zDPCi&2P*_yg;=<1&HRc*A=NDBEvQ+xYUS+Enw1MzLHNA9>P2tOTfL}a(Sn*#&8k%u z%*$@Gm{gf#NE&UAAE7x@yRR+^DGxP9o9cVo;ze{uIE*o^AsVh;Q3Btt?`g)|ysCP^ zvXx6P(3ON&*IW;4m7VxWF_}kwS94E0>XklSgi|r_Ji4M!tc-=)V~x>JcW1PrwW&4Q zIB9gI=l3Gg*=9GiwPLlRhP}5@b6#gDN9ysRE7~a6CmLtWO44{w$4I^!Z*bP*muI(+ zm?T*6zg>Uxz~4OZHxK;H1Ap_tyYRq~F^1TKz`=t?#1)*`g#`m{Uw6V zPsb?3*(D1W%nlXc#QUY8nME^j7eB8I?D}Z^`c`3F2{2^=b`+wU!}Deew=Z>`$MCv* zX28g_#u`~hwvl7z8hOTeSH3aXxXdVYEtHPMy*-ku60)|`a}B)5*BAZm5|`kw;%j=L zq&Hw!)~2IPOaYB;f&OP(aHdfC>`urZg}leMfjSkx7}cp#J(g*Rr>V%k6o1Vm&EG!( z&pjlo#!mlL7^>3q{K_HhQ|L>j|Cbq>&$BHpnu(aUyTsq&sJcr349Y?1*r;+$mU8$b z(0Fn2IS_%e@Z!1gIS5AYdu2K4JIlavDG}HNU1VR6;QBE3CyeI`0Gd|nDMp5;1Oiqu zVOQ!;P%dzulN|R9a7GT^QMVFJo@MX9mLCMZrIQ6n`ymEA*Ie z5cYT{L!J31;ehww2>*$2(EBCYeU@;R?<3G@3WG?lZvcNyLn8UUTdBz0+tngAL29@7*;0fY1bf2Yb>cEohG*f8bgjLf*vdFE}-7m zC^JUYT`Ig|WnB%jC0quKx3ZbU^)ghZWHF&@0JwX}7w|XL`U?JLFy#vXGUcDoLozcP zDl;eW&m{f{^AE-3AEbL3;+}tm`Z?|zCh#r>!F}xmkQ3f(KyY8z2iWq?MTy zMZ!0-X#~8>D6@`m(7T%O-#rKoS>7pxYuWa5y)NpkTMIbf`;SapzZr1Idn;18qtrRY z`)k5YgbTbkGi@{BLhn`MO)P%g<2`ZEa?p?BwQP5 zQ{_&{za4+a9!&7EUPU=lEfP|#hmp);&7@j1&hr!<;JyPtBM9$Y8nTx*p*lfu?_+zg zl*{g-h)1N;V`l(nvnh#SLF%O$XmQ@Z2a+8t50f&*HC=x!z})&C{gk zd;fsE%zvdDLS8RreoA<$7|T&5lYd||%}upvPsVnDn=~tv4JuQj^3OIh*l*OIohtQv zWx*+sYLS_0Ednex(SDhU(sWMJyS3npBL&$?GUXr1I76=d^Ls$ZK((T<%$W34>rwob z4U@&z<#GX*MEb>HM^e3Paj8}g_#U?Hqvp@kJg5pe4LwKILQO;ePS{e@&@Tvk)HF0m zIH0DXlZ1m_ISrj6oaG&W0&|FPu2)V%&lAq~$~owlghO6A1-(FciZ_D4=6?__@bafT z^H=ODh2GDy8ebw@>Xp;N%dE3G-ow=YYnuFsw;5dXH`4$g5$W{JMF8b^4NHCmL5qY` zYcl@I@tjkR%}=$il5L<2j*z_THE){mbFMaL8*JrSAA{b!2np91QOM*y#>Q5r;3KqV zo`PElFEKs>nN*9~@?uQ2R8sQz^Q5^A8))YkW6EJbNU_g_P%Z2|kD{yyMyB z(mw_8*HAV=SkEIG|0Mk4oj}U?Pf}_krN%!&>cx^;4cxa)f?tPn-z^l#$8mq*&;2`q zDduskwgisHVJUVaAj{wk$ZHOKk;53l=zR#7CEBIL!^khtg!R^ELDZ|t4kVMyEZ1|~ zY#q7Gdb<^trB+zySzMVj>!C?5uFSo=rPAc$%G?(NCB+o?u0*xTb(Zye3aSQf{qaG7YMo`hL0GM`tTzd(b(Zxf!fKsmoh7W+S(Y$}sCAZQ zNJOo(ESE&oI?J*oqSjefibT{p%W_LZt+OnTu>}^Yb(ZBbxTI0*EGuBpV71P&f)Y{d zEGyHv8636Fvc?%#15xWNE61n+qSjefuCW{1)H=(`H|_+n6V=u%ieY3f84jUf^ zveq|`NRdR=`IZtXmPnm%6_JQU)H=(WZcIfQwa&6;NZZsp%eun&DDvqPE<9i*Qc}MP zF+V36%bsMcewJ5GGS*>&YLc-&!IG;<#(Iba_n=ajg66iUBfvUk3DVfaNSy~nQaSTU z>NJtgPCAC2)VjtXeGEIPRiRo}};|Vae z@ZLZ;jWCvsZ7{);&Lm@h574-Q^!>OgD@1G@`XU%vdr=9-jkMTpJ{>YdUp9U$6vjHK z4r1dTf&6!1pBVcg=r-mu%eD;MD8ew~&=GD48c`8e}hKm|L9Ruf%F^j?Il zr4(LV0%7yOz%5yunEz^(|NYEAw*XQHpf+n0C6_S=^T3%~@}5DaIMysGK^SjTngCSm9t@FLF#arP+jW|In1-wgg?725s?)bXco!68L6~DA zsFWI4p-V5lPd`ZSZx{L3((n+AwHe1WUPEjgN=NF8X3^{rOY~(BFWS!HOY}vecO+2W zjb%4=3S$bB-3Bbn&7@Nwgq-WZ+KGHz*SLf|!gXL>&TY(Z+ApB?n-IO&17l}_^$>>n z954PDGib~6;N=CuOGiE(AhRG_KnsC39<)6+tp}Vc$teV9DmaI1&PEiw473PnBJV*I zjsy2M66wUa0nn&F?*srUeJ;VI(l|J$!C3;%n>J^Y%x*LOR=beFggRuf8hpDp(D68; z=5q!$JS+%jfn$h`L#*?QWUKMeGyjPaPF&y9B#eM+M2~LI`Xw|OY2>=i%8S{E8`>L% z5u{_Yp?BQt&{hkfye!zY0l>ZB&@~&S1s>UmXm>vNo525!%_prCw4IQ^T z;M@hyPZJz5t`4-1fF?pw{JjyuD+q-;QMM)ouOJl4$Dd4z5}HIBgRj?^dm$=%Cs0+-qVNl_ah zt)RG>^e{pywt@uqA?KmYu7ea#nL+SAf*uU}-$4w^i$Ro0cvQ1}lLo=MmeUW+;clu6-UP|9<(CFL$8b>|ck z+z9pVJWBhx!x~J@xrGWp0wt-rKLlC$1E8no@gCIUA3(~~@q~ST_UF_IR{{=jY5_@Z z?|6P^q7wYv-tqiinZ$Lq=Z{~N#okulOmCVG%l_^Ziy78d1;l$cpsET z(xm@=Mgx%2w0dd_7&LfR8YeYRP$F~ExV_`aG|Xb zWoXf9eR8wuO}H$rpK3Cf5j@OBky%KAhorz+Qa>*R*yGZ^K!J?Curz(LYm3Z->N8`Y zjF}?A5`xoZk{To#BRgZ}XHcVKt|B$d6ZCVp$j>!VFhG*BaCj5ej1d-2mq_EHk|D#Cseu0|3=x-Z2%$QM8SgoLBV@m$1l zWNjQuLrHQxOm>uD4O&Ic!%~J?*wJ(T0LK_diDshqvgJDMEA&r_c|Y@b36*YqRb{#h zgxpd*k@DOa%xrZ4KAMIjkdV;xWVMSk-)f_Dylb1lERV>-A_jH;%qr{Xdt3B?lS)}*S|&#+ou z2kLUyA*rmwMgXjbAE^AeESs8B<=do zgk2BNuKN;pN%REJ{Kp`C0JZb7GGIhi41h-!V%!nd-#w+kRaG#Bs)~E4*oR4462knwW)D8|&yobp7 zx|E@rKL?t-RaruDGZWL<);>$LYI`hwylc@*PE$ij7n;4=QpW16R6-PaV;ybO_N1-Z;Kw(`;Of z`vEm}79=#jKcTUc8r!LHsn*zNr>=8o+&o(2jj+^+K7++&B@`HkxPDai8>PkvpizzO z68#!b)$*F``ptU{Nm;GmvvXk&3n3TX;NGfQuW(c=VO6cOpv9=$3!N3xoVyd|)X|)O zNth$i-zLnd*XBHIo3jQ9b={r-?VZfwOru9&!pfXq(>piLL3-t#i5Sq7cW$IP+^|;O zk!Uec<(+lfoVB(&&pXW7bphtcNy(l}2rF~0rFX7<1JAUSN$;>w%A9Ly&WeON66MXO zGUqyNPOfcEw>>bkvUv0H*4|;}f<10x28v+FRZtKHyd({{EnOQR8Mo5_IrYyZXCPsZ zWuyG;Qes%zl4DrW%;}E;2a(6cK`8@K;*-DeM`6J*{K(pO+(ztmL zM|tv=A}JG{vh(6TQ4MHjP>A0t8eE&#X^VLwYIpU#{Wk3)2knSWD{;_{+q6Xv+DkU= z`Xp_Fg|g9<#8A>m!vnmpR!y6O(3B=@Wyq!Mizv*+vM>QvnE2HAMr?fi5aq^(QQq@N zmyS|Ao=^!dqAU~M03#0!=A(8z^lPgrm56V_Y%g!R@w;l$QHk;K+Mk;K+Mk;K+M zk;K+Mk;K+Mk^ecBG28=C-vR!=Rby& zaz9^g;-@4x@l%qU_$kRve0OpapBo+6#P=hToA~Yta_d8GK&5)hLH2S3D%HoINraah zP^tb;0JMZhZa{fTJ1N8sD9@GL5*1!HU+yIUg_pZdzPt|r_ISDLmrIZmiin*NhjrQ*3(fEE_M@X2!0;QNoNLWikz9M{)giRz|L_emO4}y?i zn2#-R7FyoNu!KEG?zRp<#PnD{!-SI8$3!05z_DR~uqN8ms-^vAVJc*yoUd1MG1qEr4`wR+Qk2%v8DRw)m7%2sc0yUJFtw_Q`MK?wUOgf%~x7d%coud++siJuldMukuWT=Z~L(GBq!F-XN^z z1<#v=)x6;O6Ja$kc+L{e^4oi_L~{KD_=~+)BKdxM@0Cc%Z|}Vlnc}ziUWpX=?Y&nb zg?@YQm3x8_zrFW*C6H2oJquv(z51PduYTv=tN%k3v-e*8&b?RvVPt^4*E!I(#2>_8 z?7dbXTIzq5Y3#jM|7a~ul3t|uSC}Q?Y&nbb$)yAl}MxC-g{-;wfLQTuYP;) zwHoVJ#2D_Z2B{T0tE30*omB}RK|1WL5>Ayr9$R+;%xDAIdmFRQ;C8Baj|91$ z>b+fp+)nk$?bI|53O_=~6xI_=!0lAu1X9vikv!KYO~H|9ZlwDAo}&~uQvGr#FoPSZ zez}pF!Hrb^LsEbnss7Kef`$yv9Dccx8stW5V6y8fWRDX`WO5@lZHffBk(wqqQZu-b znkE@|3<;&yfVoKZaWsB#u}uG8n9!(+PaX_%3Wv21txq_%M= z1O3MG2L&$-zwa~ZgXE`R2#EI{!_NV z6C(v4RRULw{MT)PpW6aG&`@oZiu^3Ri-xLS8_kH=44z11o+`zaVqygtAzBr;C1%R3Im#1iCA6m|vz^X9M|L0W5*U8D$Ua1&NIaeB$!F zE++PZe=Yd=hXB+A;QM1JFS9Ov?wdfz}W%LZ*5Lo=G=ocmEf>}W};{4>gO7o&8AG`uLb)7urIOMG`0~mzFh1Y znHUaWq1A|_?-ezo)xzI~kAV^M|Scp!_(9--O%~is+)dR6%=f@n;mrI`9Ziz~=7-^?Q(i^&I*8ZFx63G>eZ7 zq5KCy`zJ_;Y+6$L32>eTXZC2$#6eq6{!0KjX1wnlkvFu62aTH6_l_3H&j;sKa1PoW zR?sZa-URJo2dxq`4+fMc&e5OfF=51zGV!;@<0>*7a^hV;6CVeAjGTBE(9T~Ft(QrNz6mJ!TU3s5?M$E(U{6LbRM8`x>oYO&T}RK% z1!wHT;AEluZNlFwbmuILei-6K_GU1cuNe0;C}c1&;Q)7uuc1~f(=gK~q)$!fjj=E?3_yNTf;mtyPMGPl{a5*yq1MbP!tcKtJgB@xkdk6k zP}@-LM`K83M3!eP)cXBQYXJ48B^0sM`D`?0*;t}}I~$;IeU6b#mzGS2=4BYm#!}mu zNyxq!34>!DvKiUNv^*t{nIi2QO9~TYjYalz^583`grju1;13IiUhSvV+E|=@G}F1E znpdiG5L6#0AWR#FbPh}cT!!s(v5KK8u$4f3zxg!*11WqrYNsU=hE&}<@a~x zT$CrE9hjyan64c&W^_@5&O)*587yk1)8S)_jBE0A`(UZcaz8v@`8%1I{>TREQO(GO zRyFSS9k{hJN>HF2hafn)eq&7zD=ZHpXp1GXy2sxEc~|a%`A{`MGvM}Q_N@0223(Yq zXm8AMqQd|vx!4cgnQmqjDiW;8j&xOy4m9pbBS`2KlSdHXM`t%l)Mm1G=#Iic(C#R@ zy|EJAx)xG(EqGKl<2Me~d3+-~55^DeW%ic9OxcEY*RcCgI)&JsN>Nv1bbjn2pv(HR zU7cvmnMvQP#yplaoaL-xR-E1cS&!qK9v|WFoRR*{P1f)Q)ugJ_d*|n=3cpAtmpCfV znd_)}zSxm33=AQNNy*xs?5KA%gZJp>H3i8W4RWeO3>q#OS>J3E(?*hTt2Kf!YUoU~ z30)1WcHJsCeEx6l;i`2x`u4QE5mlY#a5;RMJ*w`;IXY{O;;Bk?wu!f?QO6LA-afMZ zR6MLVaNgZ!n>&IkQX|eDVOPPYIn$hu6!&TbdOKhJ${Vtm3o*> z^vaB6kyWqEWUm~Xq~_yBJ(@EPVlkoz zR2$NMRydOLPeyV`;;8Fz z{IpxIc`M5R&&qjwZ$4V5SIw^uklCB1Ng|I0R+J)D&iXo8>5}w#rQZN zR+aoZSkY?ouzYjzk$dyU!YD2&E-5i{ib_g~r%yNEUsPI*2ZaseQ`m@gi9Y@vt+=?S zyQ{dns{t?M!W*KBoB8dr;^{@xXNJ2Xczt3+YiBf!ocV^#a0gz5w=pVH@NNpx8lW;8{gRqU5h;(FdWE6 zeEAWde7r!rWEtK$lMD7=LpSca@r63PHms;xe24IJjb3q#`H zm7U_6^K}Zmc(6Fu882?Y>&`Y8ceR(4Y(}5BqG&p6j9sC-c=N0)Yp*N~ONs8r4XX8D zP}lfxcY=4Yq(Q#qq<&^gQ@A4<>v+p9DUGvRhTYu$T$**C`7`Jj!tvHvv>LS8MEgZm z>h_C9>uA4=msKo6vqQtxO%jcDWRpY!r()R|=B)2&ZHr?7<52)4#djLnvY9sC2=xl) z_{+zu^2S|OZEh~Q^0Mjp_wC}9eHE?EeZ^*cd9#`J4RU5&dL{p&jXw0sCoVJ7mKU3w zn|@ke7%87&#+u5*0fU46q){_c5_RU87wm2-{d*a7v32z4i`rrH@6gB@y$qK zq_Cpcb9Gi@q{W2?PK;+zE|>TeN^qV|^NtPq|E=c34*ZTT`77_+_1*2^=4eN>tF_@B zi~6o=U(Q&67S%$imqT`|o~?T`IyQP=PXp#0d^UAseH(fnnmdt1w^maa`X;8Pa9i7z zrQl!=lJ6-6h&f0P2vS=k-f{^t_Qswk{z*@dfO z*RH}yOU2F>j0r_p(oqDXL33+)cu7g~l8WN+<Bm6CQiGaOK!O?CcuuCU@h?WlHGq;qq{#u(-V3bN$Yp z5+vrC+q@I>z4Nj~FIKp28L@swcQ|J}#7Jh3Pjb{tjDZ;V^caZ2ilYxt&E<{teD1a) zGh_J^=IrIW&4opMJ7u5$xcplwyK1bdITBtLDK>-4&AgAVDlRWvY1Y14UKuW)VcuLc zJt9RtA@>F|dwC=rCR)G240w(gn}Ox!6@^&(>|D`Y-n!~ibIbD975ELpF!;#7OZ?Lo5$cWWP;wFX) z^-T=mk&B>nRxnKs9dRzYASpj&4-g$WF`gwBHaxh57Yr^py+!7xA}m6%3~B!@7C2@a zMwoE%^s1G(IV$dJ#=pMio0`qaBD1*2tST}iMT8?o;TbpG^6hVW-uLsLmUBdL$@Rdb z^6*mgvK@E)%nUDIxp*;eqqtNEd}Eh6w#huQy=iAl1qPvVbMdQ4W;Wqw%3Ra*O=MX7 zQ}e3j<;8{N=5ev3d|UbL>Z~a4Y*lcgw>AC?!*mT@-y2P(Bl%8|D^^SW`+;4eXq5rRU+(fjk zN$%L4duz?9gTIfPPhvaL%q%iHl7BBRDMw|QoI^}jn_0|1AH-4=Z>5yGEtT}w1@G*1 zBzJcB)={%qDS-ui`KUS5M)4aO)bd@h_+4>pdLT`{vlOJn`%V_7XYESaZQW|#=DN#R zoW9hx(3hSy-!BcJoPOEN{Pg@?sk_~`rrc)jF>jZ$1-JOp^XL2PvGv=>_Z3L-T+9z3(0)9dZ#{UO%Wc-&wAO>G>&@sVP8?u8Dznq* zN7?45TRUNI!7kr!@2#HOQunxTPq_o9Ztrp}PA_w<^Q9Ne|M3rh@DIno&up#vPo|6c z{*bLhKaU4HcUim5TV=^ZH}LT&p=I9=2go_@IdQ+(AOsGHJ9}nvgP%|!48`irx&!1uvXWYX)2Hn6 z?)Kc8dYgMs%I(%2=AE*Ioxtt%IcY$Hn^R zleZm<`2zz7Y`kGQG;cM&6E!1(mrU2z#+sUHXWGY4#JH*Ab^ zOFq`+P@>Ydc5Jl2iUh$P7L7am@&^tUyf)UO4Yk$8Hi%k&hovKyEOxX5rKpuZ4%7m! zZE8SLc!#BsyoR1GsBi0OSH7}Mk}_@e-Erj@sCa)5oH)|o3B}#|V8)9wcaA8V0< z+@h}SZ0lhrjrH+*Nt9nTQ4PrUr1WApwaE4o8ddAM9oF{P&7`}2W7KXmwVN7R1VVim zy~*#!!K}9W=I-gDmfv+j#*TuZrtJ2I77G?6_(dH+@mQ@5Nn;X)fy1I5C>b`%rSWLG zXedmYZ1Zt6kYs`=R-=w`I$KP#uGr16m)Yg$W413^JDOB`V_8@x*i1LtzfL)Gipds$ zUkg!GStGJ8`HPk|6*j_WY_ifP*x|Fg6CDOB9Jd~kZU?#s?PjAYL-rmRAsd0L#0wc; z@N1|1{YwrgEUB;?P@)AV>jDmym67b+BRv|E{T>~$Io=}6kKQY-MoF7`&@pPUd5SOR zu@c(X2v*I(z2wXC;DdvHExzKkm+jOBz9F z08ibLhwEnokGFM)8`1yp6{orQ9XJ$j4uw}u4~5YZh$EA56XIF##R?O9$WY%DZEHpI zfOX-X4*EYVe})drjbE_aqg~C>aD7`FerQ7*nkf&#rVmnSQm(0 z7^&FyP=a_1a2msA-8Ps0PpbWc;m zC>x(WIE)xl)i9B?TeWMum!Q%(+r-x21W&#F&4)eVadwQiHsFUubS&HljG8;`A71s> z8eysYnG7AMJ=)F@m)~w%U*EMV`Pn^zfw{h2a1c~r4L{$A-qhWCGk#MR*ca1ZcC^3Z z*xFrR->|8*yD`>Ye7^6MotG%|=O{6*G<0`%aBIH0{aWm};WK+g*yy-(*3h|Sq}e1i zL4d=8E)BkK)(vaXk-*R&!CZrnV2T*N;8AZ$Y0fpbO(j zPdwVa3A2j*D^d+kZiwm?8|?;w)+|5uSb=Km>14CkTO$qfyJ69q`p)ZG<1IC@9?X<% z#kHugS~MNP>f?qeISsx!hK2+?Ia(_v)%+lt`c@n*wxubH5f`a2?loZWt+hLMPsx?q zNJsgmizX~}RQ4M=A2pD`R>P6QVNz3*om%HZW4n6Rua92g@=xML8z0s}8ZYR-H7OoX5I90ec{F+*AZGERSAz2%W%krYXD?N+&ML?#;?`ufX zJ!3kSm&)(9GEk!(8(X{J*O9fHC<{NhN2SS(l8)6hRJpUvT;FN$#y8f|bLe1nf2;|G zS6kq<@^gjqjDRv$4N=iXRQ&4b`+Kl_VSA6?Y_D&+Sp%_-#%@jzvfcr7;uJu<#@@*n z-7Iait-cdeKn+_mT2*3OtO3H&jgrjXuF~jTGnga~IjzLUeC1$()C;3+Soz7X_ne>Z;$ub$J*4frW5GE$)x7^6zLBsM5|0n_V0` zOqk@5wpQu2=hUt!=WCm$^Kyu3QX+eb|NnLsgMs+1*D~_HPt_Lc18u_LIpxh!1T+*3 zzWNklF=ldZwDH;PHxTfa97g=~iYYp`_u)2MUXNnK^8|uVc+d9TW63|nPa}9lrA;Ar z1|e101R~h-bBGW-f{+lgGZ6QK$Zwpe_w-0=FVH=T#ArZ*JRm8rWBAJ*SR`O1UtJ{D zFuEVX5_sW7A0e7fbxKAJBb{ND z%S+NJ?XkCNbvL4vNi)#u?%|{FT>2{9T6tE~8orRx|3CU=5=OC0NNwB~K_1 z;Ah8N1S&@~OM*3w$`Diq5n8Aij2=!f2pv-lM*QZLN`ZXjMeRyul~xH?Q6FD6ZHOvH z)B-A_Zz?LI7AOOiQMUxE7%>Y_&(&X@)PG%4e|1v-bxHlzN&VL)^;akLC!EU{g__c) zN=6ogY_o(?6@wAWIkM!*lvU1@Rei~nRnC-EiC(-FdG0|#jWBv%g4Z&l&XKuLwoeLF zF;cSEIc1Z2JrLk)-l3Nf->QKg$B0@+>ZR;7DNw~o$zJP}P3k?3AGFURKrbVn<*}6k zGNqDAM$|bnmt@K+XG*9}q^xqLOy;>68SPSeG8&ZNwT!59WG1o(&%la5qI0siEUF2U$^3Dz*u zvPoq%pl*-?m5kabBpW56m|`%Z&XKtcV#eS{fM5orrBZecBkCL}+XjJNCClh(39ey8 zEhA<5galt70=0JZWSZNO%xdv`3n)!@`s*MsGl+De%ex0$OKPpVlc^PNh|STB&4dt?QubLA=v})y)@Y97k}8lD{k z#-0Xq<>N+Fs`GyonI2d9Gh#l5SQ~_{LaDlv5tV?-=n)B4GNLX}wWd8tzF%o#q%_^Y z=!{fa$w;M141laA7SAQFNK^q~t&{HzQEjZGk8zo-(i%p5?~5toxAd(AuuIVy@hvZ) zZ~o<3YC%Ur`~DFRx<_C5m2w-BOlTcitW7_p^DBiLMe z6`c`VjimEk76Ss*oZ! zis_g15med}-Gvgf5ZE^vmm$D3MjTV6uL!7QiSY>(d2WL-pWT^)z{hoX+JjH&@R*0b zcq6$P!`oJbUIcv|0-ZC|!w5$Zjv@>ooIrRQ;TeQy5uQgljqnlzXB2(0S+pT^BE%6k zBWy+JMd(NU>mH-K1#69sSJ!~nQ-F5L7O5|uMK6+FMRG%#{6Ei z!uc2$F4)b>#9cox)(tOH@XQwrhQ#^3Y+k1PiZ$*q5;&gr#HGJMZR!&@oIP=wB3s5=6ZklZ?@8b}64!rBc?+yD zrpHsGj>{C|9T@o&M&VpYBrj83ltciT^8Fo2k|{fz6AvX3K&F^vw*W=9#Ba`i9H4Fw z>}ZD_DagMHu1vPSG(maoU)*;{{Ix{;OcVQ66OvlAeqBH4jN)olH6!@ipLlnGSTTyv z{Pp*q@yMTY&Q$^Srcv-9PXq4{-JQwHWH{t_h8YiFU;55$s;&`G(#?&=NeC<5;@|>Kyl)Y!Nk~s6@`7|egKiS3OIB;1W2hQ|7AExDH`rIT=KF@|Z`G*`h`42j9rsrofv^@F8960$AeBZ;#=NU1L zli%;anV#pvoP2&EMdRf2yqLzBzGcEloL}lu*BJ3Q>W6qQNCxWf1aN+Zha)-hLE!vM zkH&uuoZs=$_#43ap&yMiKYlJpUE|~iG;z*s2Ko>A{HTw*a^zRrh%0`M7>n_Y-}uq^ zB;fqSkH!mu^ZPuU>2!ZPg8bDrM$8>G9?~B?`=&0{-cC4h@_81{$={53Fl(HAo`=&o z?Vpoq?@AxvkF2g7u@w0e*Y?%`=gW|_y?jL~U$CtC{7@}lr_3B!o*lsXHf4=7zc$DC zwikST>Hg2NbLvv|9ChHdpXcT@fAn}r{d&H}D-pzppMa+*+rt0yG-igR!V=#BuE#I(`E-c9DMo}>EXw&ouQzxakomuqm=6Myf5w6Ho2L5QUna)e*MaM^ zjKovmNPX^?_!v6YzA_O{#}Z1EQF#3*ylWJ` z3%EWfOZx_ZA4UE0Vt)K02*-msaPs-O*rYw<ahtuBZ4CX5xJJtG;OeKLFR~ z-O2w~;QBl;asF%bjpYLXReqxsEfe`W{e$S}%_7SCMcSG@}ZoDIfCklDs zQ@t(%C*W|z2QM1Iy8{}FMQRDZ-J4c;M7bD;^1>ZB zkyI2DQyTvl+Se^#R|DhfasFw<;j;hHC$n%SfIn)t!VL{LG+4VSTEC&TspEh4QFS|$ z4rF4VxH|6UL8uW2X>fRG1g;K0se@3OXP=5w zi>{mzxgv2;hS)4ELm6z9PzCw=5;uH->2b)elo zH^ftw&P2A8Wj)~Va_a~`s#40MbvQxS9_vW>^xOksJfhgx9n)v?dEm*Jk5X0RI8}zj zjOy^MKKd;^HsTDRI>qT!W1pvTk{r!M9z{x;X7>SgEY_LCaT+XXSKIO&*UOd9F>FWfx#%#oEN$+L{#$?9Mh)K-YW; zt>%$iXP!#2KA8xCRTxcpL{^@xwGYRRNU$C=r<0KMug++?P{&K1QlhAPOFN#!KpgK< zaf^oJQxZ4;Drd}j^OuF=_02YhW12;3&TVZJMZ{X_yIVw28?`Te<^0^7kTECu032jWCkG5es zvuFl9r$I~}8no5Qh1muC;m#@VDtRZv{q7nd&fQk>c)bUKOD@WB-?I~p5MX`j<$VbV zT8{hU`yn?)3nA8W3jzQB?AaLgao_wX!$CFQu!ehiF%0qXojVY7-}|uwr4R4VmY3G|MPOPV_bZM;PV*?JVSdnv zzgn=4xi1kcPVzvKa+Li(0)Ne*9QQY}-a_u5!KaTY$9<1n$ep%nh@?KaN<51|`zR;( zKO)$NAZ%aEli?(Smg7FfR>+;N-d=zl^->@2ul7K$5U{>HG_X@@1U(E}RlH1>mdrn6? z&~iniM?vNu^qD_9(f%x8nX*I|Gs$Tw=??e0d$cgTG_g2qNlZs2-*7!~@t5 zS3i46Vao7&-zd4I5936ZV#&YSFO+AS*H_tR1b+QhEVtIW{h)<code == BTN_STYLUS) { - orientation = getToolbarOrientation(); - if(ev_pen->value == 1) { - printf("writing select tool...\n"); - writeOrientedTapSequence(fd_touch, &orientation, RMversion, 4, SELECT, TOOLBAR, SELECT, TOOLBAR); - state = 1; - } - else { - printf("writing writing tool...\n"); - writeOrientedTapSequence(fd_touch, &orientation, RMversion, 4, WRITING, TOOLBAR, WRITING, TOOLBAR); - state = 0; - } - } - else if (ev_pen->code == BTN_TOOL_PEN && ev_pen->value == 1 && state != -1) { - if (state == 1) { - printf("writing select tool...\n"); - writeOrientedTapSequence(fd_touch, &orientation, RMversion, 4, SELECT, TOOLBAR, SELECT, TOOLBAR); - } - else if (state == 0) - { - printf("writing writing tool...\n"); - writeOrientedTapSequence(fd_touch, &orientation, RMversion, 4, WRITING, TOOLBAR, WRITING, TOOLBAR); - } - } -} - - diff --git a/actions.h b/actions.h index 9208c4e..8355e44 100644 --- a/actions.h +++ b/actions.h @@ -5,59 +5,64 @@ #include #include "orientation.h" -//erase modes -#define PRESS_MODE 1 -#define TOGGLE_MODE 2 -#define PRESS_MODE_RM1 3 //press and hold mode for RM1 -#define TOGGLE_MODE_RM1 4 //toggle mode for RM1 - -#define PRESS_MODE_RM2 5 //press and hold mode -#define TOGGLE_MODE_RM2 6 //toggle mode - - - -#define TOOLBAR 0 -//actions: -#define WRITING 1 //writing button -#define ERASER 2 //eraser button -#define ERASER_ERASE 3 //eraser: normal eraser button -#define ERASE_SELECT 4 //eraser: erase selection button -#define SELECT 5 //select button -#define UNDO 6 //undo button -#define REDO 7 //redo button +#define NUM_EFFECTS 9 + +//effects: +#define NULL_EFFECT 0 //null effect +#define TOOLBAR 1 //action: toolbar +#define WRITING 2 //action: writing +#define ERASER_PANEL 3 // +#define ERASER_ERASE 4 //tool: eraser normal eraser button +#define ERASER_SELECT 5 //tool: erase-selection: erase selection button +#define SELECT 6 //tool: selection +#define UNDO 7 //action: undo button +#define REDO 8 //action: redo button + +#define HOLD_OFF_OFFSET 0x0ff +#define ERASER_ERASE_OFF (ERASER_ERASE + HOLD_OFF_OFFSET) +#define ERASER_SELECT_OFF (ERASER_SELECT + HOLD_OFF_OFFSET) +#define SELECT_OFF (SELECT + HOLD_OFF_OFFSET) + +#define TOGGLE_OFFSET 0xfff +#define ERASER_ERASE_TOGGLE (ERASER_ERASE + TOGGLE_OFFSET) +#define ERASER_SELECT_TOGGLE (ERASER_SELECT + TOGGLE_OFFSET) +#define SELECT_TOGGLE (SELECT + TOGGLE_OFFSET) //Touch Screen Coordinates: #define RM2_RHtoolX 60 //define one x coordinate for all panel tools #define RM2_RHtoolY 50 //define one y coordinate for panel tools #define RM2_LHtoolX 1360 //define one y coordinate for panel tools #define RM2_LHtoolY 1820 //define one y coordinate for panel tools -static const int locationLookup[2][8][4][2] = { +static const int locationLookup[2][NUM_EFFECTS][4][2] = { { /* RM1 */ //RHP //RHL //LHP //LHL - { {RM2_RHtoolX, 1820}, { 40, RM2_RHtoolY}, {RM2_LHtoolX, 1820}, { 40, RM2_LHtoolY} }, //TOOLBAR 0 - { {RM2_RHtoolX, 1680}, { 80, RM2_RHtoolY}, {RM2_LHtoolX, 1680}, { 80, RM2_LHtoolY} }, //WRITING 1 - { {RM2_RHtoolX, 1450}, { 430, RM2_RHtoolY}, {RM2_LHtoolX, 1450}, { 430, RM2_LHtoolY} }, //ERASER 2 - { { 280, 1450}, { 430, 270}, { 1140, 1450}, { 430, 1590} }, //ERASER_ERASE 3 - { { 280, 1330}, { 550, 270}, { 1140, 1340}, { 550, 1590} }, //ERASE_SELECT 4 - { {RM2_RHtoolX, 1330}, { 550, RM2_RHtoolY}, {RM2_LHtoolX, 1340}, { 550, RM2_LHtoolY} }, //SELECT 5 - { {RM2_RHtoolX, 1220}, { 670, RM2_RHtoolY}, {RM2_LHtoolX, 1220}, { 670, RM2_LHtoolY} }, //UNDO 6 - { {RM2_RHtoolX, 1090}, { 800, RM2_RHtoolY}, {RM2_LHtoolX, 1090}, { 800, RM2_LHtoolY} }, //REDO 7 + { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, //NO_EFFECT 0 + { {RM2_RHtoolX, 1820}, { 40, RM2_RHtoolY}, {RM2_LHtoolX, 1820}, { 40, RM2_LHtoolY} }, //TOOLBAR 1 + { {RM2_RHtoolX, 1680}, { 80, RM2_RHtoolY}, {RM2_LHtoolX, 1680}, { 80, RM2_LHtoolY} }, //WRITING 2 + { {RM2_RHtoolX, 1450}, { 430, RM2_RHtoolY}, {RM2_LHtoolX, 1450}, { 430, RM2_LHtoolY} }, //ERASER 3 + { { 280, 1450}, { 430, 270}, { 1140, 1450}, { 430, 1590} }, //ERASER_ERASE 4 + { { 280, 1330}, { 550, 270}, { 1140, 1340}, { 550, 1590} }, //ERASE_SELECT 5 + { {RM2_RHtoolX, 1330}, { 550, RM2_RHtoolY}, {RM2_LHtoolX, 1340}, { 550, RM2_LHtoolY} }, //SELECT 6 + { {RM2_RHtoolX, 1220}, { 670, RM2_RHtoolY}, {RM2_LHtoolX, 1220}, { 670, RM2_LHtoolY} }, //UNDO 7 + { {RM2_RHtoolX, 1090}, { 800, RM2_RHtoolY}, {RM2_LHtoolX, 1090}, { 800, RM2_LHtoolY} }, //REDO 8 }, { /* RM2 */ //RHP //RHL //LHP //LHL - { {RM2_RHtoolX, 1820}, { 40, RM2_RHtoolY}, {RM2_LHtoolX, 1820}, { 40, RM2_LHtoolY} }, //TOOLBAR 0 - { {RM2_RHtoolX, 1680}, { 80, RM2_RHtoolY}, {RM2_LHtoolX, 1680}, { 80, RM2_LHtoolY} }, //WRITING 1 - { {RM2_RHtoolX, 1450}, { 430, RM2_RHtoolY}, {RM2_LHtoolX, 1450}, { 430, RM2_LHtoolY} }, //ERASER 2 - { { 280, 1450}, { 430, 270}, { 1140, 1450}, { 430, 1590} }, //ERASER_ERASE 3 - { { 280, 1330}, { 550, 270}, { 1140, 1340}, { 550, 1590} }, //ERASE_SELECT 4 - { {RM2_RHtoolX, 1330}, { 550, RM2_RHtoolY}, {RM2_LHtoolX, 1340}, { 550, RM2_LHtoolY} }, //SELECT 5 - { {RM2_RHtoolX, 1220}, { 670, RM2_RHtoolY}, {RM2_LHtoolX, 1220}, { 670, RM2_LHtoolY} }, //UNDO 6 - { {RM2_RHtoolX, 1090}, { 800, RM2_RHtoolY}, {RM2_LHtoolX, 1090}, { 800, RM2_LHtoolY} }, //REDO 7 + { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, //NO_EFFECT 0 + { {RM2_RHtoolX, 1820}, { 40, RM2_RHtoolY}, {RM2_LHtoolX, 1820}, { 40, RM2_LHtoolY} }, //TOOLBAR 1 + { {RM2_RHtoolX, 1680}, { 80, RM2_RHtoolY}, {RM2_LHtoolX, 1680}, { 80, RM2_LHtoolY} }, //WRITING 2 + { {RM2_RHtoolX, 1450}, { 430, RM2_RHtoolY}, {RM2_LHtoolX, 1450}, { 430, RM2_LHtoolY} }, //ERASER 3 + { { 280, 1450}, { 430, 270}, { 1140, 1450}, { 430, 1590} }, //ERASER_ERASE 4 + { { 280, 1330}, { 550, 270}, { 1140, 1340}, { 550, 1590} }, //ERASE_SELECT 5 + { {RM2_RHtoolX, 1330}, { 550, RM2_RHtoolY}, {RM2_LHtoolX, 1340}, { 550, RM2_LHtoolY} }, //SELECT 6 + { {RM2_RHtoolX, 1220}, { 670, RM2_RHtoolY}, {RM2_LHtoolX, 1220}, { 670, RM2_LHtoolY} }, //UNDO 7 + { {RM2_RHtoolX, 1090}, { 800, RM2_RHtoolY}, {RM2_LHtoolX, 1090}, { 800, RM2_LHtoolY} }, //REDO 8 } }; + //static const struct input_event tool_touch_off = { .type = EV_KEY, .code =BTN_TOUCH, .value = 0}; //these might be used in the future to improve press and hold mode static const struct input_event tool_pen_on = { .type = EV_KEY, .code = BTN_TOOL_PEN, .value = 1}; //used when pen approaches the screen static const struct input_event tool_pen_off = { .type = EV_KEY, .code = BTN_TOOL_PEN, .value =0}; @@ -79,9 +84,9 @@ void activateToolEraserRM1(int fd_touch, int rmVersion); void deactivateToolEraserRM1(int fd_touch, int rmVersion); void toggleToolEraserRM1(int fd_touch, int rmVersion); -void activateToolEraseSelection(int fd_touch, int rmVersion); -void deactivateToolEraseSelection(int fd_touch, int rmVersion); -void toggleToolEraseSelection(int fd_touch, int rmVersion); +void activateToolEraseSelect(int fd_touch, int rmVersion); +void deactivateToolEraseSelect(int fd_touch, int rmVersion); +void toggleToolEraseSelect(int fd_touch, int rmVersion); void activateToolSelect(int fd_touch, int rmVersion); void deactivateToolSelect(int fd_touch, int rmVersion); diff --git a/configuration.c b/configuration.c new file mode 100644 index 0000000..89c76b2 --- /dev/null +++ b/configuration.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +#include + + + +int getTriggerConfig(const char *path, struct configuration *config) { + //returns 0 on success + //returns 1 if error in configuration file + // returns -1 if configuration file doesn't exist + FILE *fp; + char* line; + int lineNum = 0; + size_t len =0; + ssize_t read; + char trigger[24] = "--"; + char effect[24] = "--"; + int readTrigger = -1, readEffect = -1; + + fp = fopen(path, "r"); + if (fp == NULL) + return -1; + while ((read = getline(&line, &len, fp)) != -1) { + lineNum++; + if (line[0] == '#' || line[0] == '\n') { + continue; + } + + if ( (sscanf(line, "%s %s", trigger, effect) == 2) ) { + printf("Line %2d: Read trigger <%s> with effect <%s>\n", lineNum, trigger, effect); + } + else { + //printf("Line %2d: Read trigger <%s> has no effect.\n", lineNum, trigger); + strcpy(effect, "null"); + } + + for (int i = 0; i < NUM_TRIGGERS; i++){ + if (!strcmp(trigger, triggers[i])) { + readTrigger = i; + break; + } + } + for (int j = 0; j < NUM_EFFECTS; j++) { + if (!strcmp(effect, effects[j])) { + readEffect = j; + break; + } + } + if (readTrigger == -1) { + printf("Invalid trigger %s in line %d of configuration file at %s\n", trigger, lineNum, path); + return 1; + } + if (readEffect == -1) { + printf("Invalid effect %s in line %d of configuration file at %s\n", effect, lineNum, path); + return 1; + } + + switch(readTrigger) { + case TRIGGER_CLICK_1 : + config->click1Effect = readEffect; + break; + case TRIGGER_CLICK_2 : + config->click2Effect = readEffect; + break; + case TRIGGER_CLICK_3 : + config->click3Effect = readEffect; + break; + case TRIGGER_CLICK_4 : + config->click4Effect = readEffect; + break; + case TRIGGER_CLICK_5 : + config->click5Effect = readEffect; + break; + case TRIGGER_HOLD_1 : + config->hold1Effect = readEffect; + break; + case TRIGGER_HOLD_2 : + config->hold2Effect = readEffect; + break; + case TRIGGER_HOLD_3 : + config->hold3Effect = readEffect; + break; + case TRIGGER_HOLD_4 : + config->hold4Effect = readEffect; + break; + case TRIGGER_HOLD_5 : + config->hold5Effect = readEffect; + break; + } + strcpy(trigger, "--"); + strcpy(effect, "--"); + readTrigger = -1; + readEffect = -1; + } + fclose(fp); + return 0; +} +void printConfig(struct configuration *config) { + printf("click1Effect: %x\n", config->click1Effect); + printf("click2Effect: %x\n", config->click2Effect); + printf("click3Effect: %x\n", config->click3Effect); + printf("click4Effect: %x\n", config->click4Effect); + printf("click5Effect: %x\n", config->click5Effect); + printf("hold1Effect: %x\n", config->hold1Effect); + printf("hold2Effect: %x\n", config->hold2Effect); + printf("hold3Effect: %x\n", config->hold3Effect); + printf("hold4Effect: %x\n", config->hold4Effect); + printf("hold5Effect: %x\n", config->hold5Effect); +} diff --git a/configuration.h b/configuration.h new file mode 100644 index 0000000..8b1127c --- /dev/null +++ b/configuration.h @@ -0,0 +1,64 @@ +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +struct configuration { + int click1Effect; + int click2Effect; + int click3Effect; + int click4Effect; + int click5Effect; + int hold1Effect; + int hold2Effect; + int hold3Effect; + int hold4Effect; + int hold5Effect; +} configuration; + +//triggers: +#define TRIGGER_CLICK_1 1 +#define TRIGGER_CLICK_2 2 +#define TRIGGER_CLICK_3 3 +#define TRIGGER_CLICK_4 4 +#define TRIGGER_CLICK_5 5 +#define TRIGGER_HOLD_1 6 +#define TRIGGER_HOLD_2 7 +#define TRIGGER_HOLD_3 8 +#define TRIGGER_HOLD_4 9 +#define TRIGGER_HOLD_5 10 + +#define NULL_EFFECT 0 + +#define NUM_TRIGGERS 11 +#define NUM_EFFECTS 9 +//recognized words in configuration file. make sure these stay up to date with actions.h! +static const char *triggers[NUM_TRIGGERS] = { + "alignment", //for alignment, value is 0 + "click", //TRIGGER_CLICK_1 1 + "double-click", //TRIGGER_CLICK_2 2 + "triple-click", //TRIGGER_CLICK_3 3 + "quadruple-click", //TRIGGER_CLICK_4 4 + "quintuple-click", //TRIGGER_CLICK_5 5 + "press&hold", //TRIGGER_PRESS_1 6 + "double-press&hold", //TRIGGER_PRESS_2 7 + "triple-press&hold", //TRIGGER_PRESS_3 8 + "quadruple-press&hold", //TRIGGER_PRESS_4 9 + "quintuple-press&hold", //TRIGGER_PRESS_5 10 +}; + +static const char *effects[NUM_EFFECTS] = { + "null", //null effect 0 + "toolbar", //TOOLBAR 1 + "writing", //WRITING 2 + "ERASER_INTERNAL", //ERASER 3 + "erase", //ERASER_ERASE 4 + "erase-selection", //ERASE_SELECTION 5 + "select", //SELECT 6 + "undo", //UNDO 7 + "redo", //REDO 8 +}; + + +int getTriggerConfig(const char *path, struct configuration* config); +void printConfig(struct configuration* config); + +#endif diff --git a/main.c b/main.c index 4ee99ec..8b093b8 100644 --- a/main.c +++ b/main.c @@ -10,6 +10,7 @@ #include "triggers.h" #include "actions.h" #include "orientation.h" +#include "configuration.h" #define PEN_DEVICE_RM2 "/dev/input/event1" #define TOUCH_DEVICE_RM2 "/dev/input/event2" @@ -17,17 +18,34 @@ #define TOUCH_DEVICE_RM1 "/dev/input/event1" int main(int argc, char *argv[]) { - printf("RemarkableLamyEraser 1.1\n"); + printf("RemarkableLamyEraser 2.0\n"); printf("----------------------------------\n"); - - int forceRM1 = 0, mode = 0, doubleClickAction = 0; - int trigger; + struct configuration config; + char* confPath = "/home/root/.config/LamyEraser/LamyEraser.conf"; //default conf path + int trigger = NULL_TRIGGER, effect = NULL_EFFECT; struct input_event ev_pen; const size_t input_event_size = sizeof(struct input_event); int fd_pen, fd_touch; char name[256] = "Unknown"; + int forceRM1Style = 0; int rmVersion = getRmVersion(); + // check our input args + if (argc > 2) { + printf("Unknown arguments supplied. Exiting...\n"); + } + else if (argc == 2) + if ( !strcmp(argv[1], "--config-file") ) { + confPath = argv[2]; + } + + printf("Using configuration file at %s\n", confPath); + if ( getTriggerConfig(confPath, &config) == -1) + printf("Reading configuration file failed. Using default settings...\n"); + printf("----------------------------------\n"); + + + /* Open Input Devices */ if (rmVersion == 2) { fd_pen = open(PEN_DEVICE_RM2, O_RDWR); @@ -65,67 +83,25 @@ int main(int argc, char *argv[]) { printf(" device name = %s\n", name); printf("----------------------------------\n"); + int temp; + temp = config.click1Effect; + if (temp == ERASER_ERASE || temp == ERASER_SELECT || temp == SELECT) + config.click1Effect += TOGGLE_OFFSET; + temp = config.click2Effect; + if (temp == ERASER_ERASE || temp == ERASER_SELECT || temp == SELECT) + config.click2Effect += TOGGLE_OFFSET; + temp = config.click3Effect; + if (temp == ERASER_ERASE || temp == ERASER_SELECT || temp == SELECT) + config.click3Effect += TOGGLE_OFFSET; + temp = config.click4Effect; + if (temp == ERASER_ERASE || temp == ERASER_SELECT || temp == SELECT) + config.click4Effect += TOGGLE_OFFSET; + temp = config.click5Effect; + if (temp == ERASER_ERASE || temp == ERASER_SELECT || temp == SELECT) + config.click5Effect += TOGGLE_OFFSET; + + printConfig(&config); - // check our input args - for (int i = 1; i < argc; i++) { - if (!strncmp(argv[i], "--press", 7)) { - printf("MODE: PRESS AND HOLD\n"); - mode = PRESS_MODE; - } - else if (!strncmp(argv[i], "--toggle", 8)) { - printf("MODE: TOGGLE\n"); - mode = TOGGLE_MODE; - } - else if (!strncmp(argv[i], "--double-press", 14)) { - if (!strncmp(argv[i + 1], "undo", 4)) { - printf("DOUBLE CLICK ACTION: UNDO\n"); - doubleClickAction = UNDO; - i++; // manually increment i so we skip interpretting the double press action arg - } - else if (!strncmp(argv[i + 1], "redo", 4)) { - printf("DOUBLE CLICK ACTION: REDO\n"); - doubleClickAction = REDO; - i++; // manually increment i so we skip interpretting the double press action arg - } - else if (!strncmp(argv[i + 1], "select", 6)) { - printf("DOUBLE CLICK ACTION: SELECT TOOL\n"); - doubleClickAction = SELECT; - i++; // manually increment i so we skip interpretting the double press action arg - } - else { - printf("Unknown double press action <%s>. Using default.\n", argv[i + 1]); - printf("DOUBLE CLICK ACTION: UNDO\n"); - doubleClickAction = UNDO; - } - } - else if (!strncmp(argv[i], "--force-RM1", 11)) { - printf("Debug: FORCING RM1-STYLE ACTIONS\n"); - forceRM1 = 1; - } - else { - printf("Unknown argument %s\nExiting...\n", argv[i]); - exit(EXIT_FAILURE); - } - } - - //select appropraite mode based on RM version - switch (mode) { - case 0: //unset - printf("No mode specified! Using default.\nMODE: PRESS AND HOLD\n"); - /* FALL-THROUGH */ - case PRESS_MODE: - mode = (rmVersion == 2 && !forceRM1) ? PRESS_MODE_RM2 : PRESS_MODE_RM1; - break; - case TOGGLE_MODE: - mode = (rmVersion == 2 && !forceRM1) ? TOGGLE_MODE_RM2 : TOGGLE_MODE_RM1; - break; - } - - if (!doubleClickAction) { - printf("No double click action specified! Using default.\nDOUBLE CLICK ACTION: UNDO\n"); - doubleClickAction = UNDO; - } - int tripleClickAction = REDO; //main loop body for (;;) { @@ -134,29 +110,66 @@ int main(int argc, char *argv[]) { printTriggers(trigger, false); - if (trigger == TRIPLE_CLICK) { //currently hardcoded to be REDO - switch (tripleClickAction) { - case WRITING: - printf("writing write\n"); - actionWriting(fd_touch, rmVersion); - break; - case UNDO: - printf("writing undo\n"); - actionUndo(fd_touch, rmVersion); - break; - case REDO: - printf("writing redo\n"); - actionRedo(fd_touch, rmVersion); - break; - case SELECT: - printf("writing select\n"); - toggleToolSelect(fd_touch, rmVersion); - break; - } + switch(trigger) { + case CLICK_1 : + effect = config.click1Effect; + break; + case CLICK_2 : + effect = config.click2Effect; + break; + case CLICK_3 : + effect = config.click3Effect; + break; + case CLICK_4 : + effect = config.click4Effect; + break; + case CLICK_5 : + effect = config.click5Effect; + break; + + case HOLD_1_ON : + effect = config.hold1Effect; + break; + case HOLD_2_ON : + effect = config.hold2Effect; + break; + case HOLD_3_ON : + effect = config.hold3Effect; + break; + case HOLD_4_ON : + effect = config.hold4Effect; + break; + case HOLD_5_ON : + effect = config.hold5Effect; + break; + + case HOLD_1_OFF : + effect = config.hold1Effect + HOLD_OFF_OFFSET; + break; + case HOLD_2_OFF : + effect = config.hold2Effect + HOLD_OFF_OFFSET; + break; + case HOLD_3_OFF : + effect = config.hold3Effect + HOLD_OFF_OFFSET; + break; + case HOLD_4_OFF : + effect = config.hold4Effect + HOLD_OFF_OFFSET; + break; + case HOLD_5_OFF : + effect = config.hold5Effect + HOLD_OFF_OFFSET; + break; + default : + effect = NULL_EFFECT; } - if (trigger == DOUBLE_CLICK) { - switch (doubleClickAction) { + + + + switch (effect) { + case TOOLBAR: + printf("writing toolbar\n"); + actionToolbar(fd_touch, rmVersion); + break; case WRITING: printf("writing write\n"); actionWriting(fd_touch, rmVersion); @@ -169,58 +182,62 @@ int main(int argc, char *argv[]) { printf("writing redo\n"); actionRedo(fd_touch, rmVersion); break; + + + case ERASER_ERASE: + printf("writing eraser\n"); + if (rmVersion == 1 || forceRM1Style) { + activateToolEraserRM1(fd_touch, rmVersion); + } + else + activateToolEraserRM2(fd_pen); + break; + case ERASER_ERASE_OFF: + printf("writing eraser\n"); + if (rmVersion == 1 || forceRM1Style) { + activateToolEraserRM1(fd_touch, rmVersion); + } + else + deactivateToolEraserRM2(fd_pen); + break; + case ERASER_ERASE_TOGGLE: + printf("writing eraser\n"); + if (rmVersion == 1 || forceRM1Style) { + toggleToolEraserRM1(fd_touch, rmVersion); + } + else + toggleToolEraserRM2(fd_pen); + break; + case ERASER_SELECT: + printf("writing erase selection\n"); + activateToolEraseSelect(fd_touch, rmVersion); + break; + case ERASER_SELECT_OFF: + printf("writing erase selection off\n"); + deactivateToolEraseSelect(fd_touch, rmVersion); + break; + case ERASER_SELECT_TOGGLE: + printf("writing erase selection off\n"); + toggleToolEraseSelect(fd_touch, rmVersion); + break; + case SELECT: + printf("writing select\n"); + activateToolSelect(fd_touch, rmVersion); + break; + case SELECT_OFF: + printf("writing select\n"); + deactivateToolSelect(fd_touch, rmVersion); + break; + case SELECT_TOGGLE: printf("writing select\n"); toggleToolSelect(fd_touch, rmVersion); break; } - } - - switch(mode) { - case PRESS_MODE_RM2: - switch(trigger) { - case PRESS_HOLD_ON : - activateToolEraserRM2(fd_pen); - break; - case PRESS_HOLD_OFF : - deactivateToolEraserRM2(fd_pen); - break; - } - break; - case PRESS_MODE_RM1: - switch(trigger) { - case PRESS_HOLD_ON : - activateToolEraserRM1(fd_touch, rmVersion); - break; - case PRESS_HOLD_OFF : - deactivateToolEraserRM1(fd_touch, rmVersion); - break; - } - break; - case TOGGLE_MODE_RM2: - if (trigger == CLICK) - toggleToolEraserRM2(fd_pen); - break; - case TOGGLE_MODE_RM1: - if (trigger == CLICK) - toggleToolEraserRM1(fd_touch, rmVersion); - break; - } - - + if (rmVersion == 2) actionToolEraserRM2(&ev_pen, fd_pen); - // (these groupings for future use) - //RUN ONCE TYPE ACTIONS: code can run exactly once to perform action. - //MARKER, UNDO, REDO - - //LOOP TYPE ACTIONS: code must loop to perform action - //ERASE, ERASE-SELECT - - - - } return EXIT_SUCCESS; } diff --git a/orientation.c b/orientation.c index 7063ca1..9b42937 100644 --- a/orientation.c +++ b/orientation.c @@ -50,8 +50,6 @@ int checkConf(const char *path, const char *param, const char *paramTrue) { // printf("%s", line); int value = !strncmp(line, paramTrue, strlen(paramTrue)); fclose(fp); - if (line) - free(line); return value; } } diff --git a/triggers.c b/triggers.c index 45903d5..a1fbf8e 100644 --- a/triggers.c +++ b/triggers.c @@ -10,8 +10,7 @@ double getTimeDelta(struct timeval* current, struct timeval* prev) { } -#define MAX_CLICK_TIME 0.15 //seconds -#define MAX_DOUBLE_CLICK_TIME 0.4 //seconds + int getTrigger(struct input_event* ev_pen) { //triggers: //Click || Press&Hold @@ -22,7 +21,7 @@ int getTrigger(struct input_event* ev_pen) { //Bits 0-5 encodes the number ie. (click, double click, press and hold, double press and hold) static double elapsedTime; //time between presses of button - static int trigger = TRIGGER_NULL; + static int trigger = NULL_TRIGGER; static int clicks = 0; static bool clickDetected; static bool sent = 0; @@ -32,7 +31,7 @@ int getTrigger(struct input_event* ev_pen) { if(ev_pen->code == BTN_STYLUS && ev_pen->value == 1) { prevTime = ev_pen->time; //update most recent time clicks += 1 ; - trigger = TRIGGER_NULL; //set the trigger to NULL as we don't have enough info to ascertain the state yet. + trigger = NULL_TRIGGER; //set the trigger to NULL as we don't have enough info to ascertain the state yet. clickDetected = false; } @@ -60,7 +59,7 @@ if (clicks > 0) { sent = true; } else - trigger = TRIGGER_NULL; + trigger = NULL_TRIGGER; if (ev_pen->code == BTN_STYLUS && ev_pen->value == 0) { /*printf("Clicks: %d, prevTime=%ld.%ld, now=%ld.%ld\n" @@ -95,7 +94,7 @@ if (clicks > 0) { } } else - trigger = TRIGGER_NULL; + trigger = NULL_TRIGGER; return trigger; } diff --git a/triggers.h b/triggers.h index 5e33a9d..7050d63 100644 --- a/triggers.h +++ b/triggers.h @@ -11,20 +11,31 @@ //Triple Click || TripleClick&Hold //Bit 7 encodes trigger on or off //Bit 6 encodes Click or Press and hold -//Bits 0-5 encodes the number as an int ie. (click=1, double click=2, press and hold=1, double press and hold=2) -#define TRIGGER_NULL 0b00000000 - -#define CLICK 0b10000001 -#define DOUBLE_CLICK 0b10000010 -#define TRIPLE_CLICK 0b10000011 - -#define PRESS_HOLD_ON 0b11000001 -#define DOUBLE_PRESS_HOLD_ON 0b11000010 -#define TRIPLE_PRESS_HOLD_ON 0b11000011 - -#define PRESS_HOLD_OFF 0b01000001 -#define DOUBLE_PRESS_HOLD_OFF 0b01000010 -#define TRIPLE_PRESS_HOLD_OFF 0b01000011 +//Bits 4-5 unused +//Bits 0-3 encodes the number as an int ie. (click=1, double click=2, press and hold=1, double press and hold=2) +#define NULL_TRIGGER 0x00 + +#define CLICK_1 0x81 +#define CLICK_2 0x82 +#define CLICK_3 0x83 +#define CLICK_4 0x84 +#define CLICK_5 0x85 + +#define HOLD_1_ON 0xc1 +#define HOLD_2_ON 0xc2 +#define HOLD_3_ON 0xc3 +#define HOLD_4_ON 0xc4 +#define HOLD_5_ON 0xc5 + +#define HOLD_1_OFF 0x41 +#define HOLD_2_OFF 0x42 +#define HOLD_3_OFF 0x43 +#define HOLD_4_OFF 0x44 +#define HOLD_5_OFF 0x45 + + +#define MAX_CLICK_TIME 0.2 //seconds +#define MAX_DOUBLE_CLICK_TIME 0.4 //seconds int getTrigger(struct input_event* ev_pen);