From 86a3ff7f6e85b25e7cc6c455c0eb4083f63989fe Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 19 Jan 2006 08:43:00 +0000 Subject: [PATCH] Updated Windows CE/PocketPC support...adds GAPI driver, landscape mode, updated project files, VS2005 support, VGA mode, more device support, etc, etc, etc. Fixes Bugzilla #47 and #28. --ryan. --- README.WinCE | 21 +- VisualCE.zip | Bin 26984 -> 41295 bytes configure.in | 1 + src/thread/win32/SDL_systhread.c | 8 + src/thread/win32/win_ce_semaphore.c | 6 +- src/video/Makefile.am | 2 +- src/video/SDL_surface.c | 1 + src/video/SDL_sysvideo.h | 3 + src/video/SDL_video.c | 3 + src/video/gapi/.cvsignore | 6 + src/video/gapi/Makefile.am | 10 + src/video/gapi/SDL_gapivideo.c | 1127 +++++++++++++++++++++++++++ src/video/gapi/SDL_gapivideo.h | 164 ++++ src/video/wincommon/SDL_lowvideo.h | 3 +- src/video/wincommon/SDL_sysevents.c | 41 + src/video/wincommon/SDL_sysmouse.c | 4 + src/video/windib/SDL_dibevents.c | 44 ++ src/video/windib/SDL_dibvideo.c | 10 +- src/video/windib/SDL_dibvideo.h | 13 + 19 files changed, 1457 insertions(+), 10 deletions(-) create mode 100644 src/video/gapi/.cvsignore create mode 100644 src/video/gapi/Makefile.am create mode 100644 src/video/gapi/SDL_gapivideo.c create mode 100644 src/video/gapi/SDL_gapivideo.h diff --git a/README.WinCE b/README.WinCE index 7ca50fe69..6f8799ef8 100644 --- a/README.WinCE +++ b/README.WinCE @@ -1,5 +1,24 @@ -Project files for embedded Visual C++ 4.0 can be found in VisualCE.zip +Project files for embedded Visual C++ 3.0, 4.0 and +Visual Studio 2005 can be found in VisualCE.zip + +SDL supports GAPI and WinDib output for Windows CE. + +GAPI driver supports: + +- all possible WinCE devices (Pocket PC, Smartphones, HPC) + with different orientations of video memory and resolutions. +- 4, 8 and 16 bpp devices +- special handling of 8bpp on 8bpp devices +- VGA mode, you can even switch between VGA and GAPI in runtime + (between 240x320 and 480x640 for example). On VGA devices you can + use either GAPI or VGA. +- Landscape mode and automatic rotation of buttons and stylus coordinates. + To enable landscape mode make width of video screen bigger than height. + For example: + SDL_SetVideoMode(320,240,16,SDL_FULLSCREEN) +- WM2005 +- SDL_ListModes NOTE: There are several SDL features not available in the WinCE port of SDL. diff --git a/VisualCE.zip b/VisualCE.zip index 4350a8702af16bd7c097bd8a39d4c705c3b8cad5..57853427c250430cbdb7f2b708a16cf8b4ff8538 100644 GIT binary patch delta 36992 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�K>WXZ_0 zAcKZ|2~+Y4iH_KynEp8_mn^|6C-1TY($Xf%pNqP#VZA?GHR|Ht4BMgmGE00v$iAYi z!V-$x=!R6%FSzurG`FubmM$NAqa{~)yi9p8+jiG0omV`zu^cevWzy}G(OrHo2UIx; z%wQ=0+aInr3U>{SwWnV#bwx7j<`=|@^kv@9&XwQeS=J!0gv2}>ctxbgW~s|`3^>7G zGI7+Bq=`=!WHBP3lEWys^rO1d0c#Rc)7A%9!7~h{8U2!VgaBXqMOdw=+9hXrjop-8 zHUYYzmR3ZqBn41EsTac%JePTFN=#oF4bvsrVsste^paG+IJ_#9|CI2q?;h?mJ#VOC zjg?mt-(7%kTo?jg2uYk5gHf^@RC-^t+$y!HT%1JemoO}3P{O2&Mim=ZBVpYLH!i7F zUZuK3b%WtH4as)8&YCls3v@e`;}y3?8H#hvbGn)9AOKX06@el&yw+{$>bk7laQ>QD z&yMAFr_npuvHX(WY#X~E3H`qIx^?9`4}Fl~+LHX9hBmLABw&7=)4wiYKXdO1;rlq| z(QC_gw0{%6aikmrq%(_VAwASs0*Iv(peY7lb2k6`ttiwUy)Y?YIjR@DwUpfkj5b%a zX^KPx7(g94MRXM;HKA?kNJeD<1;|tEY9$Ju1#?9pT8#9mYVU0;zj!Dv z#1o(Mu;5wZD>4ADBt5r-F#wVY{9F2CJf*u5nV58R3T9P0W>qU1O=5%zar&{Gd~lZy zC-TUV)EXkjDNy#JuIt)H0Q!<%=g)jfx7#7|fGieU|30`@$pz^&5OQw==>%YSdhU45{(^kjD7GA3b)wj=a5gH;9EW+&3DgiA|9yZF0&PD zcCdzCV&fElP0ajGd7ZhD=h~`f)|jaEjf580hZFMB#;=g$N0B1>P@Y8R=mqu2>P(x^%B0E!Rks9JP z<$l&3uNC zU|g$sK{3HqvJ9MQS*Y2vKi{C_+-mTFMQnjHPb@j9j6>9JO7k(1g?*#D-n#ZC7#L~b zU}rp~&2a!h0D&SazmRxAjDUQmXDV`xFWZ;3ej8xAUhb=Ao{gP#eH|nga}SEx=zkS2 zM3?D6>@p6ZW3|B;aBzUm+#JGBu!dFSkbbK5@*71a^3FVTt{r5zQR_*=2*wl>a+1el zww+9_Y~DXhnj}@6(iN?ZSZ3MPFX{Ve|22uCdud1T zlbS$5|12W9fFX2UaI>b+6u6Y}$i+9mxOv35HP_<=5K)pQ)1>hFMT4%q7H=l`Qa8CQBcoUmm62M4vA}7dbo`+ZD+Kb zk5&Z&dKl0EIPuq`rVs4Lnp8Q1E_iPZ|+d z#()_hVwHd34zQDqwR6S>LY*R+;cQ2Ew*9yU@R!U0$o)`w0~DK#f#TW%uxgkeNL{0N zs+UMvUwPc|$(47sEPS0-;P6v;*|+!Dbt3*qSk4@ZLU`QKZld;Z!JZ)2o|x zl)(3*HikjWf@rO|90C0};3z(VT^JqHSbt)ywrLE0*_bX|ft#H1bn1g^ zbULRGV0AkM=ksl!CgFbAi^Wn*eIkL~Do~z^j=~^@7s}WsW!Tp+|w+vfk+p zc_b$(#f%_|Ggis!CU}ffG2x=c5@JFNb5JNDhQI~tS|3oV5q=>5S2w5opFUW4NyEXy z%{zqRU!Z0x!-A#|q(-IdG0-y9%t-;trfRBL$)Y((6{Ry-;N&E27=hnI*q%_yfZS6Y zBPw-9;onC>ni54CZ_s&(NlZetPwPAuMOroxMT=I!xqqB2Aq$l%#rzeuRfRi1|rUQAyE zWql(16p+@H6uh+lAxszY1fZZ2$ODm_$BUzAU8c(Asp*1?Sy-UTC}iOj0W-NPq#{$f znZ$|=-1!EV=eKLh=;5^-M@Z3(Z7`4?NA zHQO$iZN`opT;F-!o)=Fe9asDupz=OAv@wLBg+~6tZ%Q1G-8_7I`HvmRC)Kb%$Z z!g*gV_<@IBtAb&0K1p8*py}1dU*2nBA#;1a?x&U<2Mm>B!y^ItM^+8h16ai0LeLSH zK?YcOIB|_HK~={F>U6Ga_!lcM5n*G&C>m*?2o6l}PlX(dz78A=r}!>z-zH~ZYc1ZH z2v>q!Ly1p+F$ff;B{7=~E7&KNKP6(55yreoTs>AZNT`LBfCVfdcpshG0MXhOTxc#~ zcR`OCo^R~7OequrAC%F^XmEKroKZZjU_6aPQkmhWd}so~)A0{0VjN^BWJ&~NaY3JM z3%grbYS)>=>udDfo4D5d=4V?EDN0mF=rx8h_!lggbKozM16t1)S?YW5m!_QcIt^CMAXNysUrD{BiYu=( zPuLSJE@z}a|9oy|mCHjZuba<*d7s!^5oPKlF^`aRQp z$Wd223D#`O%negru5wiFV!RTmq9eje)d7W!7jf`t{4*RWF_WW_rp5>*+bTt)f|dHb zg{5NHcB~Iz8KK|qq%?IbyWvTzC$>$|E5LGA$A~KJu^g>%;^);x>D&2YfA~XNp1!@V zw>gWU$Ugu-kNMSO9}WrAg!6whmS3~io<6lLRRmoqDQzMgOvLD8>>z|~yLO7?TvjxZ zL<157yn7aB$G>YAvLA17B^Rp5GA8-j-N>g;=^g?9-)UT{pMDKM7S#0K4M8!1loH6s zUsu7PptjGI(=nzQV(Cb*i6)@(cdp!OpJ=O2xJ|$#(Ax*0z9%jGE;ZOIgg-ml_OCr{ zeM$xqrLpz+Qi)}{v3QfGM29i;9;s}TtN)63TQFlTK(ulJCbZX1wZQ!oI_`Gwdl#oN zyJiE!p;c*y;poq}Dyh&*Qs9_`I3^gFYtL4f>6xx)PY~2!DIwlX$x{IeT92gdG!YSA z=#wyO#x16kdtoATJ_R3(T8j2an9nfYsvO>AwD+Jt(K)xn3>iAW zEj=%08e$bd_8>C5f~)J~GB#(L97|^!gBXsHPpBAADWQ#8IPFGKsGsPk*$>xVy9S#E z`Cz))!9MpmSU4VGO%9~L*Wz6;*u^}hUVyw(C}^HxyFM^&^iyahJqjRZiMuZloS#mtz8 z@%2*JG9Uj&5noBqzx^$<9gk zmF%7PVO&;mOlv@<{J3jzsmD>y=rT_A;&lGaju8ZpYqan%jj`kf_7N&O`8&Q_fJcR2~Xi4kK}mqoBDqeow`w=8_2VUn;T@Jx#ft++*H+^IpDe)YypE5+>|H9)7K&$D)^5}b zSQdIe$~rXrIc}!5`Jh=H(daYpZ*m{8O zr|$Wcdk-{wWfngoA8^TF{+=X)Y z98S;7DtJ}8Lx>7`Xko-$L;LLdX>f zFMv#oObl1~uN&GaYog8k`vbCZZXS9F=LSru6}H+OC%^08#Y+QuI|)@Yamp>&qVyTh zQXsb(Pwya=8Be7n=9$~zK#DiIF#8uQJ9_z5sWnjfm8sn?`_?Tphxk@4J0kdgg?0Fp zrQVzSG+?n{`P8H;4GS*7QHBthvnnS|v4n&0m83~qVLOiJmB5r4%fW-mH)53W0Tj+z zP-B?=_uNCuegb2q8ONr=C^Lzv1(dWFIJr&8IVda&Yt|j_Jd{J{<~S63I+U_9eb)Y#lVCW95un(Y=O;}ZATJ@Fbo3LsnJbhs4d~A2 z_y$8vWyH!)y01hcR+!?Ez?SQX29OUsAr`(5^Gh2zLyqOkS1|r&s342xr&8AXQ%-^= z74p~e%N)L{Qh*QS()rg`gb$Q7lW783`Mi9goqUdnZj4nplv zc{I{y!I8qc`P5c;@LG)Ul_ZMYKOKi|pC{?!R}X(2Pt5*Y)e#wb8@hE#e^-)VrzSUZ zH)U#IV_n3W!NGoq+a8w+4!R(P5EFDlVJgaPtGy_X*_bLYhqAFvVF3)Vv0h@&;NXP9 zAHz$J2VIduS_pbM)HmmLmR_&PZAhhSA6ZvC(iX5WL*n+~;GiLVJ%$o|WegD|=z_vD zlG_$qyMLk-EwRN}PvlP}HjvRq=C49T6YFcSQXvPGOpA!Z!8RTLRJG}_=2n!?h1-^Y zXgTb$?l207maKOY0d#tspiacXdw090B%-biI!vNF78}rUVm&&I|Fh|Ohvg&kpnIS) zH0NvOtv~#IvkH>1^^-^G`a^zb9J*~_#V7A(;{`A>&=rELVQb}rzSQBva z9(fP*;VTBj3~`v^G~*vJ7<=Gu@)y@3En7-EO6Vh{bBt16Zo2Ig3wh)0i8qerXw#m) zP6=zgJSUAz_CxO%e~uonQl5|0?&wbS&-9oaMNobYX!FQ4BPVKv_3p26qsZ)yLJRc) zuto3@MV9d`uT5w$^r;=VpLdsx85ChiF$+z^^Wmr|EUQ1ka8O681cM~3B3o+o+}e<7 zG@<(~W7rh^&|2&Bm>KmP3!=$YJ8@$cjCBlYEMkf>3dGkEz*+aq51>uehhPi(I_xP$ zp@PRtjSqc8z;nbDoMmjMGgu<&GJ~i95HuDjCbQTgTV~is{xtz8Bt|h5OYnc@(CilV zhqabv>zx#;-}%d6@#lB!QB_re&|#>E<;DsX$JB2QUjs7m9KQMzWdAdh?As0FoYmGbQu#*)i1RBX*%|-OwDN0KDYekP zDe%MF0k3*(qNVvQ_mN!_05scO1e`V~Wf=E7SwwmU!*m8f!UTixm6xp*(*9yGVU}TZ zGk*0L^g-fNN?6GtB95IsBzej%ay=&`Vlaw&>7n32^4BH;T_MOGGM1_V`af;CcmR|8j@h$b`?OLe+Ddd~81vyL`Huhc1!Y9X-!PEqjzvHSF!iDHVbqVRpI@w~;zusNmvMVe}Wh3pFj*M zE@)vK*Dpra#*EkNNYmHwRWt5fTxq)@VOBv#l7nXavjG(Y{vSv4?g`5bo(3ZATLg%S zGUAu7Ni|>^HjWzC>>JOBGLvv1$lBcQ+J)^J(v#>W<3I~{O)w2Ueth36=7s^KA)1h3 z{2@q(T>*MMgQ(`~YzAq~*P76efv;Jn@)|>X+j22x#BhxG!sLabHm_(B zN_+*PN6tFaxJHi7%V5gVte8f%Lj=IhzRzx|*TQsf>aO=o#5SWz@JE71xdzUjM!o~a zfaLmM=sACC6J%c%Lrt3{7r%TmjHCWa7ML8Z7dU5wq(&s%zJf}TK#P)7$XC(xSiNbQ z{w$W7WxL~q_6Nc;8HMmdd4tK&P#f;RA*dQJ65DhyYD=& zs+G=5u#hYiX`^Ib+!Cm$3E&u_DyZpyyJ704wy!=cAl^3y(bwWBJP zznq)Pt;gtF%YgC*rX`&7HC$6y+T$_}Ua<@3>DuSoq=aD>Ueoz{xQWZNMc=GjU)qc8 z;BSpPQa3X(?LnAjJ!?0r`E4sT$?0uNaf|Uv1IwLWw`ukg%4}G|E?W+N_bWo%K$5-YL_FQ`4AcG6Nw$!KbPR@fX(|Iw%t;jTw=;~eHhQbQ8v zxn|&rZi{B1yG~RS`^t>miX@mHl#m)BQi+68Ap&Zm?l5dr1Yk!@H6wgva8KRZz7b=m zDYzf9aSE?? zf!o>|?lj0s{7(W+?7r&0t&JczhB48eorB_~D|o$qvTudMugr_G*S&#v-YR z=R){*mxb=eTfoKr#hJR@b?n8Lmh{U>)oh0)qciC`j-4ZMwXW8d@N5BtQYjZoN(m{c z915QR2P-%}B?OfCKuv+GDqH$VAZ?tTY6N!EZYd_JHH!|4Kz|M(c?P}` z++>ijQ9lr#UR_j=6^Go=vZ7E!aT1`sI0Wk8@Ek%t5CD2gN#60+kq;2Q)kugB5l{p5 z?pq2`-$3fxR{qLUOz~=cZOP71-iQneUrAz-u0WZUK)}7APyWpZcv66(iUsH3H&jA$ zrF~laBTYx^3z|cq?>T?ke|`_b6Vy51aQa6*RBL0B*!bbndC|;cqQ=2vH|o3AI)eAl z*D2l=fDTCY(}cU^>8%IQEExU$LoHfa>-yT92U`S4l@X>bjiL=*)$;F0=rK9I1d6P~ zcj>&%juA5~W)8ozE?gU%s82gJ8n=y2{KNDhwN2))^d^yC=`j~0H&zNNQs>6`?5-+@!wAxim1tb2<6`Pg1z!$D^zKqBTe8c@A8c*DvfoBpv#@(wnt`)HtxL{ zVa}!H7~%yp2YvsSl87~;%VU>W*WSy7%K+MHC6N}l8gAK1>%z{QGT=|6NM>%;u)R&@ z^gSQWm9-Zs9JiJEiOy^z?)p`|*Z*i+HWhnteqX$E^_7B>M&9Oe=j0w~mHhPVb$Wm0 z13LH@%G)2kC~1E91F1>)8K>S+5kv4mS2ofmnu_{);*eO+ij_yf{fi$xid`z)SEozWaj$A3jUY0>a;fi(U`6rVY;|BnLilVx^(|tTVRuv51YcZN zq{5vyEj)d^(RCW9#ws^X2nP$sZK#UrUv8%8bmZO2oMM z2tYu^<-kA~|CgmoX9HV%8}omRRUYY02>zunW0in^%wnErdf2QsVGn&y_;^9~FZrNJ z8jzXQJDX3$EXf?!vqPV0y88e@&`h&hh|h_QhPq5VUj(D>TUg6fI;u=6GKW@GyShE^ zzRDrrZ#S>@(7~0)L$t}@(Ct;P@kP+*r*;|<2<{29G-s}Z2@?Gt_Mq!8tgPH z0nQk_NuNwk$}YQC#gZpPzpc})2*M<0187gQk_~1rS^pT-ZwG+A(PZ(ot)n=|Tl#CD}C!ZbM0H{%|jJ^l|amL8d?Wd4y+$_ zkZG&i|7CiGaBH@GuF^{1I9v7+n^rox^emV5r#IMP;+@E=PK#>1TE%tAs8bcGFr0n- zdaWt7LCK8l;{C98%qC-qGE6xUT8l(8;vxL!8?#JA?e)n^aP~n38P0j>y`20vIY9I` z;qKo(YtE-)Tn4jVlqq>x&PgFKEavwV$}^bV6?1~6A(y9;U2PQw8%l**$gdLGIzl}B z-0sA#wra~%Hty_t}z+) zW82x-!^(NTMX?pMd^D*YCeY3sN-!CdPxRZr^PH}Ubh>kY$#nW_PMY+82UTW zuY=Bw&R<5uc*+>2=F4Ck`uxQf?S~hO5<+G{)=dZHi5o(bX7I2@gNf&$(*RsM86kMe zjkdqR5J9!#;{7)15#kpTrFR*le$A{cDR`4LUUSWN5lZdWk!Dro8^oh{JxRq{%~_Y? zq*LJz6=hyx0K{D~L`GXw?C(SWSmC;AE2s=q2Uq>@h;QmIq3x=yjQf~8%aU#<4dl|I zgijTH9&0;+%?#(lk>F8mw*)*$=|l0>U%0m=j%Af}>WozY4v0!qxKfsdBH8{7&Lub& zZ{Of=ZF_leFSORTiV{8~kdZ{~nW}PFy_cU8FWHyfFZ6EPRce*8%HqT~0+tV zR~ltaipOskmWrBGDt{D}F4V{=s!DY#5Q&)lZq(jU`%GJS&(s>a&)RN{UH#z^o4`DE zE{@BjC8~Fs(q5WPV`O=j`yF)Bgjhcgv5El)6X-j#crpADhe%Zjl=Nt$?DOm3dSPnJ zo%#KgCNYD10<{!1w;zBW*}TX@n$nG0)4iGEr{_uk;aG#o>;}ptO=zFugxByb>*e*r zcmcF!5pU}<9^Dwnb&)kjx;X5zl)9Mve%J+qK`u(S)`nM|p(A6o8oO#+H6!FtH7Lm8 z`)IQ6E{g6=sszKeSZq?}r}@1~7Htn@aux*2?nPPi?vn`Lj9`zoL{g42u<;)_=VK42Hfuo3exkeHG;Mbzf$9D)lJzTlaT+uUWfp7*$5cVYLN34&vi6nm$71Y7d@t0a zW95CH(Tvwnp*uD1JpA}!KK{DN zU5}|xV!csMp1ORU%lwP_Pf3R80Jadq|LJ5tgeGpSY55=dzFbB5g&nDVuNo4US-f-$Wu%Xj*2b{#pu;pSb@;HZ@1 zm{$C&^2et)^jrb&JZakFYIswxY1HOQ z(=#V%w_OQ128u@T@_LblqfT3zo1>z}xL2G4`gu0>UW$KsIdcjD#wkz@^fV#b%9dlY zzRk1MZZv*W_vcwCcAD)MkuBF>P)?Xgak4S6XuQgT&B+E9$M@q!%M3LZ!@kL|X2o|8 zTi<(uE_)pK@K-r>%sCWp3L$i>6~Fh{RW(l3uh%k{RDQ5@SfecB0$GBt_ORB#TUIkQ z#mCA@TGfM@g0A=gSaaY_5202rV?ynM2>X9nRc;C5N>H-uy6RW%VD=*S&L8i5JlSCxYyM zBVv4_PrGrf8ZplgY<1kvPcrOTzW)D_t>Fg)U8rTaH% z(c88_67|~GVhg(kqRRKwv%`6oh@TJ0iVtTBq5=5)KG->0=uF++dzE^t^yd`L9YiPp zrDjCTT}d867csQ9|Kb*lYv)48$C#)BgG-E$M@OB{1zmSDM@~KhS)ZVePZy9E@CyLs z4*V3a{WZfC*?{IN0sL1wj%OIp9%~BiCEy0&zoku}G_3Y+v;+T!xA$MeXZxK=X3vJ5 z4P^aqZCzyewtJ}oxA$7t;G99UJKW@X_84@KA^ux)naLP*MBnXRQxah2AR76t$~eg3xl-}ZNw$)PDR1n32T{ZBQJ5fP8?V%uqjQGox`<`B(`X{%aZ0q{*Wt^syd z^f+S>^}S~q11k#9Wox`&4D}CRWujOP=SgZt{4bZWG-=6RZLNg+>l$wJ#Ep>tTR}Ec z9+}6LF9JF{ZK8sdDO|t+c0`6?s{d&~1PEzs)skw=D_b~QPaHfb$Ua|Nd~VTf!xS7r zx2;t;U3N@_ApdP$SG+!3DLoKB|1ErWsH|jP+sLc+Oz18HVVnPHeBaBCZ4CwP01#Ku z4Ij3xOfJ_|tN*acqxx++xDs*xmyWzmy&0hKa`Y#Ck+**iUM2`9xFgIF>J8EyjyX{h zAjnOE@YNN*dPHXXqvxGE zqK*U5zl=CB!ahQd2eSM>?B2@n%msDDOZsoMIY-q1XlztGP(3fe3Etb!XPOFtr^f|# z_i(?1aDq8P9U*-*ArPo3DJ`M3XW$wrnpY#3#V&llSw~y5NDE17i}kgm>(@=LVr~PQ_@aP zGh^lj7#W|=ws%t7n=5UM238wHOGPwki(D8 zt9Rz)SyWdVJRv4e%bZxpVDi&7K9T}2Qg!LiZ&lR#nm_fF0Ld)NH$l;Byi;Q1`%So& z!M%mE2sJMFmon(C$vUqI#(0Um6qGAS3vyF_RmtMOQ+dhw7# zGWm|lHZ~7(t;O-PReemZ$yc}9rY~Puy=}KU8nujLP0eyI9FQ*R%3?`P+=M?W=?}eP ztS1#sy*E@xfMYC!&PI_n9-K%WR`>Ld&!vJ42vkGMiXwLfPW?ts)evDA)EkBxE%?c~m09aquFa5<)LRnk zkPGFF$tiK;0#9lgWPC*NP|~YS3cso(tavX`&zaLDvxo#H@-mB~{W) zB-=n1aYH~Y5Lwv_w;G05L6<-<_U4Wx`!<}!e4s%O(=FgV151{d**o<|j+e~pWxkx*U*zzyV ziD)jwCQ;m(N6Y00$6qSSIhjdWaX=YP@`ZndN@?<;z!cW@K2g5CzHoHesv93|qRrH8(a!<-}- zJu^N`%Qv>~zn8J>3093Nkn)!($<%X46tn5c$L<40Ox+r^>3&Y3CAy|rLZDBUt_Gd4 z7m<0OPs4?SsdU@Sm;5R`CO=Uw-R@=qf4m)3$NPGd$Q=|xmha(+rYO~7N266kfUBAE z0+XepGPh5c>&@Xa#t66e=iWS^@b&1LJ<7JMLOnaikp8!@OaEK-0>|kRsEgS@U&hjb zaR7k)R%ZM`l@jsci|6G)?FQbv>$2A?R;k-#c*&!S4-Z})_$v|#(lg%pi6xTFMw0w; zF{*>9Po;;a7VJ9vXb^iD18if*Pb)m@8s32q4X>iX7yOu2@W<5Z%t)i!MyXLFN8Q9L zo3R(h(k9FH;3Ub#{WC6!sw%1qD!44R@Erg&ogx!+*Wth!7q9iC{fhiJC@f;&Q$tps zn+Dn-h{^}$IXR>0=C_2CW1)^9DUEm%!ntiLcY~&^&1t)o@>bKZ78Nr`!^H8TzDo|` z6KAVICKn|P#Fx6rLlOvl+U19WjwnqoQUvshxKko*1Q0}Bb|~hMZ9ZG}AbewkK}3{lTK(S=%e%Ifp1T=}w&xD{;fi_T4cUQk z@Uc4Avgj-l zzr5()tXMoKbOSDXs9Mwr1@mw*=Dxhu{nO~u?3=CQ6GCos-*+uv8UuM?A6=)`c@@eS z4G2Ss{zEnl2z2S@1*qUmlaqe%4dLx4`vHJ{@dA=0qpL0iN-&tkH@xr5d^GZu~+4_;?NAh$0WCGt-1H@#(?D&Ggdr_B zB8EXQ(xE^2%PumYcJjtSV^n$F8K8X-)a(=70ooK0<0&uN7Qj+vR?<423B6H7BmMy< z7&n_9TESxW5O7nznrD3u@bSPQ;7-8n&K}JeHr2b1zZ^Nxs^%2a^pk5aW=DDAK8O=< z@#xhB3Y*dE=Owk$AqnEDDKahA{cy_5nGKZ9?)%amcL@_HTS6iJrS@cqpOeIiX^P5Tc$TkJRgj2p@65eTFIHVj@|eI ziH^s25DN+`If1>~U7LuS&BE=X{(jB*e$`>1!%kzdcFsIot}igEEI_L$Q^-KMXa3W5 z#h>c*!iwVX0xNqe)1)hm5Q_BZf;N&E0T4#(qG)qLOdsAagm#P4$3_=oe7CBm9 zjf<;Sh%2>bOLk3kJ?}((eQoz3NNJSheDms_F0v!VYm39}G=~VZMtcq(d^_YhDc}!Y zwoH>&b#B9I={U#HM05?T8IwJmxJ$@&7b`85A#$sR2HK-8pNS>LI-MJFHZ}%A0B<+! z49|G@^HJFXNi7Smac3?f@FwQ)pL;BHdMK+a!A^Q#OdZy(0tjPH#6?!~LHNZpKY=VD zMTh`QBz?XRV1obkY$5^z&RiUZwvhcL;b@=nS_}h)Sigvfrg@lV^|Bzt!t_?! zx+DQk;TbYEjGqu96qC3P5}9Gr3J!^ZZvf~UsL;QjSO7dAB*lacA|2E62A`GUGE7=M zVykBG4IR~GErM~y)ZD6WOW!BefdGQQr`bKlp68IaK+r+apXK2)0?;*`pO4Cp#csG2 z<0}Y(@NQ~*6i)JfBKxBV-=}psoL?t{Zu6T2f_(cuMot{(Z!%(aGHC_67CqC}Bo7Y@ z=*1gdbLen@N0;!m4W4%msgU&+FbTme@K7@CR^0E_lT)^*&;XBo5ck{AItjJHD|QZS zGI<$;jIf!yi~KGgZEf{1by+_eBBxa!^*EmO$NCg~%!}08XfN4y?N{irRg{L$Vw#QP z<#SoRZQ1k&oo9{P;3hSj0)G*gchR7Lr1z=c~$U3g(+1fgXfO ztUyhBS$TO``D-aHTz4L=JoPN=<@bPgqKYzAVV%(PQEcqABIEonBk$ob{}rMB5+C5hx=~s(`THH3 z1}g)+qjR6V($Zx&(FgW@Rj}9=mtf1gtx$S!Wcr`&<}RNUF8u_KyXz{FoFVB)OqL?W zNb)*R0nCAkvSLe$%Kk-(f^bclW5y>1Kva~x#PQVm9pp5grUYU|d)irwic&4p%4xrR zxtChDEXWwYr7T|b+I4WgPFtVuR14~qt$-yMAm|t$i)R#DK9=_9mdDjQ^cXC*o8q=y z)3N7_RywA^+$rk^5^tbTD=P$VM8NV~1MXa=B!5`xXEGhC=Ub455Bjj)-a0s}vgI zoRwd|j)iIygFkQ7mULlJb#%in03PCs#>3$H)~HqFIb|?i&9Q7{6-JxRnJV{my{1&v z22paJrS4B9#>u5asY1TS_O*iQ?DyKxVSKkJu7+HI!@4NEG*kA=lDXTd->{-($&wrt zHHLF9vm9!ua{+lErg7=}sFGnN9alVkE<;BgNWkvVRkz~`6hpUH0E^ccAjh5O_T|Ru zF@O~Z6nK3_^`uFIU>|)vE4YA)vChy!4G_J*;#5>cbUh#-MUN2CNA2JQ2dTlS0^M`O zxuNo@a&i^&3`j+h;YgyvptMvYSE9t#F@;Gq_r;K~Y+vD|N|b!lp~`&4M`a~mPnNUF zU~6$sq{&b&&xS2T)}d7bP-V#JE-NNgDpAd=)Ynq#k!u)mvmFJZI?1)!EVPm2i#9n8 zmA}>O_@(ogTT_s^22qImA9{)MGf|oY-J(6X%lE%R*e_a(3=2_?M>8Q)g43%xjNdL{O7qu={r!E%2;D;EoCFl%zMM0xG zH$Tqse`KZ9?r%rNj0bSF^_REF%pIWuZx1%q;J^2}LG)nSQ7>75Y(K!LXCGPPLM>I< zI80GG+{act3=fe(WGJ6H^meL#{LW?)8^1HPuJI{#*I;&`PF zo3^FGA#!uOl(og-qz6AZ4I7kMOReFD0eiBFfZIm9l$Xij~nCJtEqF{3872--6T;g@g%il6KItl5a53vb!M{K+a`(&=E8T( zs;(v2BiTnZ^os=vx^dH1ks6`y-Nx3|@W%mkhpeu0U^OE&^{v zeg+9N%?5s&AYg;mWQp#3eG%Lz36OqSFrDvRglijtkWF}3c}RK&%ieUt*q7c}*Z@B# zF@heH^6|tAKz96xcfmDI;Ww)J+Vupy4*t5i@5q=KF%%7H0{IFDurwbI^>U?I_JI%p zcl9kQ+{ae8(QLa!W4tf~d9L~=eGRs~!Zjq`s^T8IF>yRkmcsxlv+0M%n}hkF%;O^y zgu24ZoQ*BS@ou=UE7RFgmVdc0pMxjL#q2U^-!T|4Y_==_$v{R%`1Vb3S`&?lgW>DA zsFePlXifuBpcrvMJ~bkPT|eJMX{Ih9L;~`<1CUlG3!H>aqrx5>LdjHoUxr~!9f_eQ z7H~iENsr+tT)sCve9yO3cgeL?B&~S4IKo|6N_UGbc1bOK^lJy*gfF!^9ZzQ?TLA0Vxn!^_zxTvFW;Fhk>!IN`MG9N+smWKu({FppIOxr?NlGGAxb=w97 zZ~@VL45jA7m1P6oB+)8*5t53qmq3V~{!Gj(Ovm}QQ_!LTU# zHFM&u{=_4-c2cV#NV9>g)V;$PHO7N`%kC-t@#Ep7`AMf>@(t}N;yZ|(tWh-gUREGi z`JOa8lX;DjvDm)mge6F`j(QPSpug`iKpp16``a+WA;CEPlqoULj}ybbsRg#Di@gbm@aG`m9kJW`EcU#rBvihM=M*YOVI~3$fMiiO zISz?f-A;^XhJcH*K54w@Bmyf9n#Krqn0cx@BbG)|>I}|L0R=s>Sv}F-Ap^$ucIabJ zdAv9dysO*a2{bH;{oXPR{7gpkGIntYBXThFj^_!`Q(BOdaU-7&q483USz~5lx43!5 zvG{02 zb`&Cl@)080m>9PDnh~MXHORk+gd@O$F+(BkSilB;SA@rgq7yk#4M`QZF#1m+uZIlG zcOEv%`rsPBWKP_TnuI~l{oy+KE@!pNQlp-;c}I^<$qg0M<|%TY-a%xyXCCkd{s*bz zRQ5LtJCv*i3Oj-99X7+;VNcxPhA!9cbUL}Qv|Z(jSoVUF6&$23g0_s6P(&I_lZAo3 z&MWWteprvsJkZ(noUxrv8t;oMoBvL=khQ3pFCueqDj|S?RAs(o@AzCz)Ob!a*#DKj z1A>MG|I+_Xy)rbuq;FK8?9zr$;9Op2N@%1s=E{DRff!U8VkrCNV6o{X-X7+M2o@$8 zJ-Sb?$)`RNXy827FLMRph91UIlU}?~H`~c}TZfl?Vxwx6a?2DLH$9pc@~jnTU5W?S zm-g*-?Y}4hp3m>iY{AJ&A_JG@zwXZEWn1r=N)f)Cp4Knm4=Ywx*0oUvgAw zPhpFRd&FT11daojcjCiU@>QPN^)8hketK~)@v_%MH~qu~l-VqnKN*wr*sV$zEzi0PW91+`&Ad%o89n{ z8FWQ_0LT@&{1r7Y>|%UEnPHwNehCPoA}Lrd>H9aIIQ6H(b$x|zo5LE`S}SHUEV_Jz zIwPmyw&Wp@!+wy-58iHvGC|}5r22Hk8StFL&Xsa9v~!cI-$zaPK0(YL37us?O%+9v z7+PZtLtx-T=m?Zuguv{k4|q#Ti|nA{at1|90hf*Xwy5%(78}5d?(C^L>TNwy1v~s5 z#+k`8?^tjc`Y*7zKMEnb9&4Om!;k zR?N|~GBawK8gNvca|iq(^3I-G-Gma~(@sV8$mN7|^nygTzv-1s^+h-k2IZukMZrvf z0PgnYt~oO4%_@+;FN$|4tnfG|To%}OfgFTSktBtvVHGzuK@?B6(k$Zo;~cl+3b(CT z1V$hCsFgUn4`R>3v>(r--%|MRhP1x8JK!QGHSH^3bhesjs<_xxof*TQTWGU-Xncw5 z#xF^M^H%FytC47ntxmq*j@QQ~yV@ADdE5ifob!D4bJw` zBJ??_->h0dyQP=U-}hA=O(WBv#w0s5}m_*+T3{Zvq5ha`1@dw=1+g^%Pki+jz$nP}DI-a9SPJp6J zwk{#6`PpO+J))j-VW>HUimzF3XVQKCgmwd)QHg6k|IppJBX@ONkr!vhT~$&>T{7Jn zd@{9(b!S7Lc6R~NYwg4$@V&G1q=QWQQql%zOm^Q0{w=jg6UqYEnIOiWUBtQKlWl2KHlT# zfj~p_kv92+!m@Al>X}$r?1nDOL7zs%oA9$crmzz#YYS->s+vYCtUB9 z7lOGbG^oGe;Sd5r61GY$8Zeywq^oDUw$;*W=f4Xv3?GGq%f@c~Pr!9{_-?wFkqy*3 zQX9eDqUce5kZ*e454Ty6@W->vOpCYtkq|xi{=1-KfLqZg0j`bf`c>QZ{jc8BTDV&S z^!@|7-v?|#2QE3M~?NyAG=GQU@~Zu~`{HsCuQzcdUhG+Ki}|Hy#3TEIijfPP7mZen8~xdzwvx$M|VwN&h|;2 z-U52KOA!i?89}u~{)W5Qv9!)`FTNk}3e)hYrr`bFmUpQgc57BjI z6+g|YzV|)6KM{Ki@U85`d$$+xkm=U_u$Uw|>&s>7d*Ftl9(`*y?hr5z zT8|blq-|akQc=^#F3z;PhLszU3IgMpbOty!+vp~$MBKkx>fMa(IhPs zHwn*;5Lf#X@$abj@4?})e=&$P1#_1&I`u za+vC-Z`PjWHI?ZpiFk}>=TWoYMdw(pwCtxbEV;_ea&A%52*$kM(XJOu06YR9n^?Wa*Wb+WSZu09bRa?U zE|l^|a}ZrJeA3uDk)f@A6HrB;PIIWUOR+i0<1 z2!E_H5fTvw^RhVt+@rNsW=8TY<;YG)y3_FDNI*HzljdJdkhEAMJ)CxyyVRy;QE?0z z4G7E(ub!|d@Y6O$!4^nVQ=!I!b&1WONEVbpBY-54wo?lF@IutOIE)F<6_ zIiom9RWj1bBECB!&QPCHE~oEFWWRjj5cc$eyb;@y1Q22X9f!ioBB%zBd*}n1w7eed zUmJdIc}MN_GA12FtBwYilk8ARp&-KsYUo4WzoqtYV9N>%jbsRoU}cqvgotP8TUzmQ zii%QYK`kA+vago z?cH+&P^987!j*8>6SxQAA5<|Y1v`E;N6+G*pEui1$+^$=XLy+GfGGYdT?v9vSrbXv zaWC0%PbXnZkmM<3LkA#Q{7a_OzPHB(CsVt>8J991z~i28E%+<(iNr4oV?Hn94)PHT z-(YMiryBZuvzDnB;WWEp&Fa!ZOgUp@Em8a%fSojY6UScbj+)Jx>wVJ)ydV*=_L@IC z+kLdEGSdguR^LDvHY#+w*BqC*w^UPL=5`=;$L&%82c#ewl_%e<4de&*0Hj#l58C5Y zzz4&VNb!>G`Jp68Gmkh+OZxOwqUB;ec^~YI!N{!UmZ!Lu%d_Dy5|%S zKo2v?e>XDIWO%S`lLKxtr+9IX*X5xG zyF8_x!{!Tkz(3jB!+9$Ws&IoTx+w%`9asvyK`sZV8luQ+DvQ;4Rf`WsD|!D}(=YUC z$_@+oDV}6M0MQ2?1*0ixx=`ksy8E+nwDAQ?(fY>3#HeeCK3m=7jYH95KNtPmB2zex zGmD^h8Pc?tCx}S(@|J()L4qwY?Mm02b|+LV8@ayw!y+r5w!Du$6BUevlcsQ#f z>96ey@ms5x9YV6D?5MX>JAfoghp!1-+~vkF-+eHTN=>KcE8mlU(7oc*D#!vX^=EVK z+j#FIj_6tEuLGuqeU!4B=l-)u<>$M!x0kTD*Z#AQTt8<(b;&#SAmHRziQHtyIpsw; zy;qF{s?!78FLi+b{N2$h((AHRJXYOV(c&!4_6)cx*X8PQN*We*ZTYv2x1cmtbKWpq zys0GjPD5bs)3W`uw(_joeIieXj%r?NtFX*_Uuz*jA0i9l>hxoy?@dC-zuN;}fz49z zVMVG*H2BF#ACWrCi~w!lomqO*A&<5X)m-H=ql5B6`;LWxp*6W?*MCGJrrZ)Zd<42h zF}&g@Qs%t&=piPFZN#qr@&$6qhFpv4s{SFglSAVA?j4SC$b<-e{nLo=jN|{cbsbPm zWL-D}5JWIk=@29mkp@?)rL7F0p z0wPVMNizZIZXnAlzyIXSIWOnE``vHu%$zgl-tW$=cvJhT&Y#Y-yD@HvWLvb<<#oCu zGeCvK80CAkfA~==3eAw~EUsh~4WEc1B+165*ag8|eA1)G8~ckMVqi$F0M9UwmL%a3 zQD;NjgT?31gHxG7j9h~CwHubP8*KG?uZe*woDv3clFq80@H$cPO=4^Z{Bt$bDz>by zP2;98G&54$weA#Q_4SnRpuN-4&2n8OsZ?&a3*~-jyAu-P?fM5uaY5}!=Ls?pP|=S<<~m=hlYJM`BFqd4>1Kz$Fy7bqW+y^owI zr(|{WsxysWOU|Egt!aHaF=_{Yb4v4Fr#|*nI_kxNk%Hh}8^)KwuGS?*gKNjR3)$d! z4n{Ahv}1R)xtL>*E{NULPTbF{NXU(0m{-{cIUSz&lH{n-m#)%$!?oCyI%fUKg!&FS zZ@k>%3NoftDajix7edSosoBW$XTHTF;|-{nn{95DcKzYu2K9JC`@l_afZY4#=|~N| zQ4|EXZuC*f%2I@E@a?b6&yO^9%#m@2(YymS8>hot9~m^7r_Nh4n{1hseDKsNePs(2 zH>f`ice|hZ+|=VlKmoact45(TeU>BO_@_`SH&TjLrs&L}LJUuDs^H3#ueQpzA)}9Z zmU1?6Cc}}NgvF~b&jgMstJ-gPuMPct*4h>TaQnZ2^RoZ|-R1y*BuU;%^1lScZ{>f7 zneFob597Rban5NX(jmw5}`SVxQFpEJ0~3SzTffwh}?Ht`CmMo0_=B6 z`GR6g#Nz{Qrf0XDe>`4mR_SbB#~>l?QQo$eVI$2*d1BU=i&`~Ie^uZ1Zt40gVX5u@ z5Fvz&*LO3HsEZA2i)a1;%vqZ?k*-!3)H5%<2XUJitY(wT9xoaywoT1Zg-CEqC&V&! zCs<1jfJ_IjqsQmC8fnM~+_pUNf7^(?pQQ(z~_4$yK#8WdWI;g9K`Y*o2`k ziED9=jI=WfX<@K9>kwrbFNGjzwKh)@kq~ly|HUBoMB*Z$3Fj`pX z=qu^PxkkZU+m@}Lb=y>qzdeRhFQI)@&r%<;#&2++pUfkqOMDs;)BO;_$U7O!#C`lC z5y=VipNkYS$`H{B#t>fGxQECJymb*;=@dR^D1V>&4yvMBVy$Vs+=1BgsO$m9iU1gu z!2Fc2YAltY(JR5>?$x5QWt9UsKPf+f&hRTs+J&wsIc_A0aCm^Ri^bsNcDiZ0MHN zvRE*-QIZc-8KVlmc8NMzn%vz%n#P1BgtlGC`yv}yvXv8DG**l1>pXPhaGoeE@iwZj z)qMb#%Z^s0P(vy3!!V;R!N$6IsMHai%pjew@rY|cbdb+1@CWAU@ficTR|CXM;j{@; z@)bBXM>(XO0C&>H{p}`vA|mYS54l;0%7^=bv9;~kwVtL5-?y0l=&@Ou%Y2^&t|#y& zJ@jp|wItaJwb(y=$*QjNcK{0xJZT;fM$p)I%yTRcwcJBVR|tB1FCI5SiLY_Sc}hzl zOOQmv=sSK-(z1IUU=-f~|HEkQF z)%(SeoSd-o$a2X;Pq%C{Gs#qhTKTK4><`L|#xh^={VYWB$|8`)c)>MCwIsG@@#v~c zCuu@{LiKvr#IiF*vd!sILlFa)g7`(pBnbswU9+p_WOH$SW55k?t<>GiBD(N|;h?6O zgM1w4(Z#lm!}My+d7BeXf? zQ-@`Hn9@dgr{tz$+O%40^di@S#mz@~A@(9Xd?-}3vZpe>LoBKWfNh~L!Z^W}DbOVX zhC5=vc|8a$H2fk`CxapX5bdTO>mZKb!WyzCtC)Y%4q_bV*gIHFJ21{)ZFBB*t1+7oj*_;WyIYBhG|HdHCw)XU^Jf3 z@&Qf^blhMN>>v4-!&2qf!BSex+s>X-`)X*7r47{#^E-o_0>y@p0AI$AKL+k9f6sq| zU!e}OF?}IzaGd)7Vk~VFl7@+$QV~3Iramj--tj!h&9pnM%ii zOX)uN#5 zw@XFG*cDe#FLM??Qz(-@knh>Ro)csexhQZJR$IY;K-epPmW8oMRd~gs!YEyK-y5;l zGV4Z44Is#moRwbd#zBgr2+=(s+B9+ig^31~Af_RCj67J)<48PNZl*yDm?I5T?~8I0646 z5_TYG&h3SiLm|%ESktiRlH|Quzsr$-W#{JtTUyXcypT#*OCWzm4Z#fbBCotD_qSASqh$s_hV`=i_$G>BRSD z;{X8g9{(|Mq;L#WnVy^5YxvuvMi7~;%-pu#!{<~Yd_tdRA-Tm~_ zeQ(u$Pru#oo$d!ao&!ZvmIH@?1Nrv~F%x7@Kq4Yq!xa!OZsr$P^MC+31~&!yzY&iA z5VB;9jsHIg3=l977!dGJMC^^Mx|$dWNM{~A2nGn|52kEv02T!N5Jy1#-~0cf{geIj zb$z7y66pTQ`4NPC^jdecU<8(i!q&Dy*Bfti8DAH)XKJs2X=V#4a2@mqI?ns_^-`nR zQ!`})xy`JpUAV)1+Mj~r?%8kowD{ug@nO!Ya`qC@YdfrX=a$WqE6BWj2`~Eek80Dh z4S+0b@wR0kYNE+!-xM_Gbz;(meqNoeAygI8leLI1e5xUOGOx#deED|y=i}x^>4T|j z_l2ahRR{r@I9mJ9;r`Hv8sEho|D91e)-80keoN!c+0fFX`Nvak@mT5Hu+rk3%0Us{ z`}2*`-oWD9^kpTGMVP$Q-Q0dqJ-6LW1P~=FDosr(S@$H-A#Zw>E^2rp9rFVczoPg*>$u(#+|JP3g-wt{eG0cx?=3x&i&BWe@*SkY@p7C$<@Uc zeDeD)eYM}9X&Bh!!}vI!$8NnG*44d}{5`mT4NE{XvVN!BEFupwZZq-qq(o-I0WiDt zA@ev{R&|GY_YkYj1^5K~dax^8%Vy!rFm+>V{N7Kt72~F+AGV2;E0(K8XSyen0t#Cqj$&$G`at2nN>9_OBJ$UY0wu`4h6c2C>Kj zT{azyh+)Lq4XTa?mm7^i+usbleOe;aFvaUqe0qLv8P2p5!2fAqwuT6r10Jq03&5)$ zu)CtK%5#Ij6{1mrTFY2z+_z!8jZ7$8Ym z`UX#~cPBChiF5KCSqXh_52#ap@DLrO2BKsu4Ya-elpoWYj?Jhyjb<((Ce?j$WSE5n zUYwm&X{+D9n|=Rq1Rn0DcAxu}%sqX*AO4)(wMcgyTp5Jbaz7aKY^|*bJmbQTlyXYw@>G_ke#{;9o0Fcyj-_JZk+ynK{_t@|z zYeqMI(l)ALZnEO=v4-TZ0OPP>s|5Hh{%P{o=$fmn$9r#XcIv6GZ)1Jm);?J@)O2`# zwA=G@-svg%VC;fxA9x>du)$7te`(x0CGxAuK|XuiJ*Ik%x~kdZ4>jn#Q}M>x`e>;) zC-6O2E0OLu0$$m=uaBLtSa6`Sw}&jIbNyb5Z%;fT5L zT1Kbx7$3_|2_f!0$=~i+-3{oMF4_*i^9WqC-Nzdd(@R4eY=vDeJ+b<&l~ zHK3Y?M}|;3WyP{EtMH!BWf~IJmj~WE1tA;$7G?bu4vXY_x|4f+&Sg%_xjEPy-7OuP z-SZ{y5kzidV_n6T-kuT)k$^#dy{^7J%`M$CvX23Hg!r|YyPh%LefYXRi~tWjFP2(7 zrM);r;BEem<#pNY5t_QV$pG-he4jt%S}Qc?+3X^~yTy0u>29~<4BC3_+jSEz<90YfmLMp+tCQh;=*cJ?g3s2*fys%vScy#M#xerSQ$jR=l&3LVcndA2EUhlp!(lP4YxSYIR?XGgpmOFw zSoPk>Bf|*w-lUYC#N}@F6T|xZ!8L3fgP|^lHj6>+Ww@pG^ZUvT{dKwx4b$@8{I*YIS z!O3cJs)fZ*9QM{NbpyX^?*mDQ7(05#AIS}pJ40_EQ7J;HAIFZ4$wK>Deb^_|_pN}t9vvCnvI-HJ_C!@4@tSDE6(O;f@Ac8nN_zGLB{K{(<^`pKW{Dpf0fU> zE1ux(gA$OXp@j5NKg1W0e=38L99Pk-P#O#T0<}*;h*qmElgCn8kK)qYu zf=)jck0AZECqgZFx>JCJJuYDW!tsQ5d z_3TjJ()H|F^`EY(3MVW%)aDT|UEG-C)-tB+Fm{B9Akxe(=`1axCH zNSXaPdo%ogV@GrFFgy>*ywoaDvw|SRriE=i?3Mx$qI1iZ8026Bl?Hy~=QDq_+h?R;w<@R!#Yua&CnDO=C(fU1X}-Lh>;ce(juSus~A zp)<0^;1z6cWgBBRq7VDOH-*z|A*D}XN-j`DI_hn6-JQ*s$p>r8x43ll22nK(bw^FK zSDh4<`mopszdb$C&y!nznEBBl)hNt@#;!O?=-BR zjIADJgrX>}QP+~iu=>P%QJMT6WF|da=YPqcSJ47)4kw46?=Rl_o-fvemd|w7!*r7W z4h{85J+<4Z>fful^*&@BxQ1xCG?(m*2(%wHBG!|t_t0K%`_%7Je-#j1ATwE$Phb?oN%x7+9ex{!Z7 zXdSkwE9(1gZLt1qc46&W9X!uAC}LbOUEi3s%$L#p3TNBo*wFWo&Ttp8BAPl#HdG#4 zzwzPt_kGV{FHQj(%;5;S=ntGZA%NT5R)~j{;%XK^*ryOcUWS6g^<9N2Hm_+a$EG_6 zvPRz>^&76IUudVGdNl%s3N4Snq~mkZYhJBs0J&-P^&1;e>XyB=8>Nwz+Cgh$g(2tF zxM@$UX

I$?Ebp`!8o#I+41$OI6PZd8IYj|J0h%Jp{u2w zQk(NWFrXI$z)FZDypCv*J5#RXu#96gBw$G^LyM1=O^Fa{abTIZJNOx%w>JNIU#>-srdsh{{fs%g2t1>v#_a3?ngL>x8*|Il&65U8(~B3X`c4HsQ#?c zKu9DK+#NyiA^{hmnNMpD4MR`IP^WBiEfz!iOS9s-!y|zI0ItzD{X8nUB84s)n{LxF zU%6Z?UDYUI4pH4Ka!tqePA^U?tKQv-3cOP@=eqH#1iYB5G*)h8ZEtP?lNY8&dviF# z(rf>7JI`SK%ZJVC*X!?zGTpbvX!17oy=J%vfe&Aulh7Z)8ODXWdZ&(-TPNq;^%BMf zuB%l3=Gk5H#j|t$ZTF8?QBK<&CN+W{tRL3a@{vttNqXhCHM8d6b8?GY4HfShY9ZcJ z#kP^PUp>VaD={DoX-<#dN}5l6d$`+SYcFG-WAa=#tf7Ner^}@2Jy3ICvR~)4_YM#B z_Vo7N-}QQcYbK72%jZk4$@>mRYv1p_H_IL4&yI&%?A(Oge_!WQr}H(g&T4_sk6@&c z++Df%wX_xaLB+cpsJ}2(o#+6Uaq-wxjgf-Hyn@`kyF|x1^8+d4m#ygagCI^}DvSgn2x|g&NVP ze$0Y*&64mHOj+V=NXNd~EZ|8Y-ClZe&pu?eR?m_nbBo?)te21D2yzYW9gL*uW5+ly z`SC6Q7a<09>|q?(sD~wA(;$CFv%>#?tY;&`x`)%oEn3_NBdiKkE71%o4*V*`t2U(C zSHbX~P@;E-Hw0&FCc$uy*~lhSSv1L12H6y?3ZDsCkAOzsoq$wNk|JtsRGXLVw47}T z)tXG(Qm4%_erp;&7^ty&1!n_Ui7*|M3Gk=UhbN=f*J;d*-7;tn>WfxIhlg|pszX^} zo|!;b8Ybha8lteNDX{mY(81+@_ow4WT}4Ll-6{RXumjQmdCM!1y|sGV%mN zyjNPdADx|RVtwut-CR#^OrY3qoHA2?gX%s!EmcM&Ot|ZN?kWLmY*C1Z%1@{k2RxpH z*ZfkPrdNCo498+Lfu7g+HDqj3AHz_;10_;{AQMs@s9w>SQNHi8U?+8^Rfg4;7U{io z$FBL;7;uR7WLL_3_n6h}?S2y^-9ytfrl$N2G1Bx6B78p(hR&Ff zIn)T*@M89^^3zNqUMou zObEh*qlt;Z&nf*UTta;c{AjU;|6ucQpD-QOi5)WCM)DRUs(uOnM7aM5GSNOQHghth z^pfh87YomY4!35~7lL|Q0 zl0a1OajlGL*KHIGMX?(fePs ziOP_@=#ah+?Q!7RMscJf;<~MDeA{f{PK@VX7|`6oD&WBK3#LJoiX)MCVMQ4M=JpkZ z<|gcg+i^EC>>R%GuA)mT1S`qU>&oY6e`$`Ja+s+kuD2CtA0Mvqt2*!*(w>SKZQ4lF z)p-k78W^(z_*eN%JJOyqs+HN_dGzGb9ZI7a%A#A*= z{VgSK5_B_*C?gK)z=Rbcjw|RAsB>ay+r6w)@~+rDD-j-G^KrHp*MIUT-ZoqnbUPqt zJYt)379yxQ7~EYO8?L;+OTI4D5HL@6sFf$qf-iN+im~>bkb>LR(-Pu_jy5yUOBdon zcCGa6)6x4RG0^=axf?mWnb7@4jV*5EUPaa2MD>(A&)DT|WBHf=%n(pXaoRIqIQ7G} zWrEr?uVh*wAP_*fK%0vy6+dykz|Qs}D&BcjdwD5a#|dYGH43*=l&WpD+Jycis+w^- z&Y3Gru!tX%$Jk9raB3TR;S;{tmlNPfmyBR)HKOK6I1b5#gb^HAnMWdp0(Cek!a(vm zUdnyTb%^|YAS-N3ybp|r`Iy-f-;O8ytq6^z8W0Ol5F<=v255>rraFv!+maAWWCm%9 z;-p#p_!JN(Qvb8KNP9<sKE#|!t{ugHP!;9g61swyg ziO)C*|HYZ#sFV~!H;cpsPEnXAFN@)DrDvmaV?`=;pm;@Zu++XN@(3!$KSH85KNhc; z)8QDpvbI-R-c2PTzx*{JUP6AyJ<5Ih&4rCGHNNh%GEZ_T1~+u|N&?A&WWZPVmvJ)i zF@=^s@OCcsmxz1$I4?kXc1wRF`X&|BvtlW)JLwUl%#W^95dWLy%FToSVXwWh#nhrI~RsV-mzd*7tUk` zLmeoG>-R`H{3x!)5JchUSPmkp1(8S7%RGc9PU;lTW5LyLbbvpP}?_`pNm9C?1j3xfVmC$nec6K$B!2%YjG7x_W@hP$QX|Z`mX<`N`~5+7rYQ%E#Zd|gOq+?r5h0Qa}M79@6)`!)4$iH7W<;!oxg{!MQs7Tm{qY zOL!+o*n~jL`AxKi)bo=h3Hi&0aVV; zR1?U8Q)8HyFp9JZi*Mm#ZE>Ev#yV;oG(w;vT5+Ke3a~RQ8$kLEx z<0k5$BNx5uSM|Kln6BqY36R8@dzPZZ&xSfA4FUiewm%$7GrY{w@I6?<9cZtJsb>-k z2qtL~PSSZCHx&7LP@?F_#faHey-+7^I73_j6v-KQalpu5OKXq@{(U?j+t_g^!pxu+ z=I|{p;;t?->kNc9DY@!cWN4!Cpb^kAl7z!-{6eF{Qd!Jule%o#y_X=ro+IN4Zm@ay zUMWDIZ+1pTj+_J6h;|s3oGvC$FSwXMG^*^jUn?P1(Ux{@4)IpP*h?jKM2;B(&GY0J zIT_=h9TABV-14yA#{NT;R4D}f{yBIW@lPf_89C+!xKEHEDPs%?#U~SLa9PrTiz1`F zCsA^9bpr1YnWGtRF z9*d_Fh%1Tjcl}qGFkqq*yL?yRehf%TfrNfP#c^UGc}buFcW~?D&~;Iyk$_EqJlcNR zUFcU$0wFm5SMx9dU98Yv6crDa*Q-q821m=y*MDboK(M zHO~xG$XUg`tgqS0`<})^Pem5iA^6Xpopl3R?rDr7+cQ4cR4kNvFf@TQ+eDlqh(5~TPDY@O zAdFX1@M*9!ptWv!6Y~C8d5{wj#~`^woK)Z#ppqzRyrPK8;KU0e`C0H4{&bJUa1*dJ zfy0pi3FAx=uvo1k5q0moj-j3}Y`dT8gAN zsxm|_M!*vY=hY^C1sn0dJjx1)t^M&H^Pqmug;;`W@5ErSpyz@6@k@B|8Rp>>Hy##= zseDPtW76W7J1dBXrDCWoh}V=!58?R6@dJ2@ews*yG|1vh0--cy5<$#T14qBpr!MyT z=(Gtb>d7L(dH=56y==IZDFZm8 zX*T0nwsW1;{jOJmH+Ck_dtCeDY7AZhw4NQMa6l1^fgS;(Hx}g6>)g2Bdmnblvc5wK z57C{_pmAC2g@G;!O?KsR?F~p_k77T)yabdU_Kc*99_NJi3f?Gwj7dc%7o8$} za*=7I3lpVQ@|Rz+S#dk1iEcofSkvR>$K(3w@Bp#_-RDv^y#}_FIi|*tVMP;O!_1+E zYqoB*OZezI#7j@sN!3tT5@DypD}0!fE+5CYI?lVS%nI(v)ZxG=svM(VI)A2EWB+hr zsxcj|b~m@~^t?ruksCe_|Ak_L0~!HOd@pbY#{?lt&yY0*2MIfQ;5U#YNrMm)C2C~L z0>Q)-Fur0)qw4`@5=81)|MJ~9)TkbwVF+ztx4>DGl$3yIU=QpX#|9w^ezPPxn@{r` zBC7tEW9!Pc`>#4t)9>NYf}ukp_q26`@hhNcCSm!G37fD+yBM0E4g?(t3 z1IE~z(UhSBk_UCIX#grlX#Zdy3l|O;_Db9gd0j&LHx(4ON=3?gzk45qvlUF%ogX!6 zW&!pGl`z7t@FtRJg*sgHeXTNM#*;ZYM+UEfrXfS_xHTt(`+fT=;Rhlc#5lUHP$nU} zb;$%%6)c;#zR^p?{F+5{qK`XX8skQj^2to=H#tqs6`PG0L4f=3anrI`c8#!y9OduV z_`hm;&dT%&-rX0m^a(2M%e?N+QT7gZcLs$ddh9(p&XgILbF~PDSP6&{pVB!J! z@kx%u@`h9_=sB6No-scEMF~hp;UfJPg$0elaI8bXkhTs(6OJLIDhA3Oj6WcI=Vzf5 z5wtoOeL%9zCJYpQKuoXH)gb9fW6)=qPc8txUtJ0ZHX1@I@DlM$EzSRu?)Cpinu8Df zrG@=}Qjh74`j>hgiq-QSq3J>$voCcIO|o(6UT35p8fAL(ADxEe9Rdw2&a!!HmIC3B z3yF~`^U*5viVm`FjH-cDWBA9cX-0VnDlqhf24Na0F!F!|z;Rh7&9aw|6Y{5epW)sj zIVCuUQdglMZd_5ey~YO5T)C4MSm~Z796oXzht#kNgSWr&Ds+eT%|Oi(!LX|S<3d{i zUrcG$RhG%XZSR!Vjijo}N0fxBM_;uHUu|Z=FOA{QlWukOH|6n)PZsbVJ-os*ymC%4 zaHH>ZyG{*&FVz>A<*E^R$z>jnX^$ON3|wjS4t8eI0;=z3LYI?vNJ7F?R!5E=o5-h= z!nJ|+GY*_NTr8#1kjqLKJYFgYG1J}!*}cJbJy|WDD7x*w%GR|!A#y2U)a`$pn zok%-};1qje!nWeoG->M-_vcU43}{gb85U^16*M;q+aJq_X{``B8SA$Z>n93~A}!jy z&;n+|B0nBzHaT?S-?r)k7L7C#sa!m($En5)OepMp0-CVpvA$mc_IdajqJfrB8N21?;ozbw2p9tvgNEHA zjLPtL{`MH)gI8LtY0S#-3u5%F;F}`c0?UZzh#cUPGxBv}{4+?GO+o4Z)QxDAvxu$( zE}Mo+BJ48=o52O1TGC!WWJg|#Q&3tjw1Loi<~p4 zpHLYA=4IFr;;hODxtKvuU`-;7-{4;s+33z&H1q#^k{hXJyhp0Uo}>`Si72D9o9 z)r2Q-oJ4934NXZO;|t^OI={LGuhh?@rkvfE$Q#L%ACaE|g{ps??r%Gd2bsey)7MKj zl+9#I6iO80QmdV)XW?(fD*G8eL${XVmQ=!QZwJS*YD+$@=QRBO_4r_?G|~o@R0aC@)VE)l^Hh4C_I|$uZq#^WCI%&(XX~_dX^Es;d!K3T8u`qZ1MY$pPWA?D+rl=QWavC!{fgMZ^5D%wlLqR{+ z!->dC@WfHRbO}|pC`fS;PZz%aO)|BOizxYCItX+aCJ-7w$|E2^b=kbfLx}GaV{-RbWqa4@|^zf#@-u(j)8>(V_soTnqg zEO;>W+a^y04t+K#cg0R=?_rWD)p z!2~;1AEoYR-JfE)Kgfj`JP6@Ue_(*ezl^W2aW6645Lh>RAi=%_d-g<8W}#jXXz`lB z+-p^NZx`uB_(W(l1$zE=vpzG#s6JgAX$$PKtv^?)f3=Y-vN0AkD^a>}(QoeC{|#8y zq%qp#XZxslWEQc}mtb$dlUe~P)rmw(Or7<%R+^BN(WQVmos1bzE}^U_?N5AU7C5Q& z4g5f*tI_j&!stL1s_&Y+=ss-v`(Ub~bOUPqc^FA!c?htLf$BqN6(XOWoyrJBf!{*u zw?$}ak|k-0Js@i*mZ<{O>c(8_DS04EC)DEtRanM6RKfqjZWQwXj3hSwI)s08m@A)j zY?&3%g@#F^wz7{9x$;z<@8p#j(pusVlrPoZn@W1>%`B<8=-zBCQg=*M($@TcWKadS`7@09pMen>XS~R)18>* z36fu>H662G1sXGKxT+5kg#IR_fgz8gMw^y{gb>Dm<(d+`Y(Gr=&ItR)aOy!YN2)TS zV+6gQc>hiBYi23C2`+tpaZl5RH5HXVNt?0UDP5IgBw-i7;QUj!SeFq8c9$?*ev54~c z5%|0=u=mYwH{@n!T`oIY@hUFpFDyHh%Ep7#M7G@aXFYNqQ$vITI@VhY|Gay;o%cK; z3i4fM7X#AUf+2)@50fKNfj)hkb{n(qm-0TCb`O}}8#k#JLSSM1FIbL`+bov!;GM~n z;mFZqvSXt(a$OUnalhn?I#G&#sqUrdX4YVs5wc_bAs(#Y^zahz`Ad3Y*409Jn#9Qg zZZ2`vdz`|bF+n_u50TQ2O1$0hvGIE0M9eug{1lKU#A*FzyNb5^MNXo%2q{t1KG!$B zdhNEu*NQtjWyM9N%eL#+n8oSf&&D(66QSLCU&s=-i?7dN<$BS!=H>N2dMO6~_cA-9 z;Cz=c%Q61ntHC%Y!^)iP&xuik=5i^+T5YGRf>rc$kiLP>_t|^t9oU& zN?P{-eyA6x`mZ4hRKMi{W)RbiWETmpZ2)6M^283Dt_pJil#XGP^&gH_Fpb8@8c`gW zLX~R7b`k7JHV0*>G0VlUAQlv<$~uDJ02VyVlRS9WSw=Hxi=AkFL;)Od6W7S2KJ3Q= zeK+LhUaV?o-+xhng{OaEDn&^C`6NT;!@H_5e}VZIhEe=8%(o~Kzp*MfV~hq5*Z~^9 z`Wsdj6OCW-3hl;N=9_#Gs9?t_D$&Z~EhEN(1+tMeQ#m=*ny-N{>$X3^Lo1k#A?pLj z%#`=4r;)=xOI-@9z5y;;@}vXC_7 z_osfKI1ca=mOO2CeP_8P)nl8nOV5m+}S zyb{grSm>W}y~l8`ZsOnOSF3Jyzxs<(0N4`BsQ(5LpL2+`FK~FCc>(5{fs(kBb?}HB z139#vq0XAnFwaJi1b;iZNJbGqpVM$6rnovFiRhoR4au*3l^rR@fhj8{jWa0!9`jZ8 z8U?SYAfvD-w*y2rYLdcL;GfeBFeIZ1Vaxphs0^d1eAWIyb^Vmazlk9OXU)KGHcX?~ z!^53G6RB!2@XZk-q=A63H^Z4{AYfGaV0!ibuXeZ6vc;NfHJuC|lgAL`1lYHDh){6| zhj0>vFJaJr*B~}Kg79OS;2tEhApVekyb+uTpAZT`k$(`S7JmeyM9_{MUQAP3D&;|i6;LCM2wGmvny94++v;GI;73{0BR7`JymkC{OQLG@S3hwp%)%I^maWkTR8z`WIk zTgGInRK9ejc9-YYwWyZ5| z;N8tqOyS!|G^Elyl;N$-Qjie-3A{u9g!EQ~f3Ve=f3Qqr!~sLi6wmc5ct5jr1f+i! z+{1sCtY)OaJk64}tAEsKhR9uc8s=Ir@$k-OT5uqKeYNMO@V!hc$!4=)j{sy(LuD{X zhoOe~0VWCd{#cng_(GsIE@t3kyc%VAYue;bZn!mBctK6AvHUA*n=)mHdo=HKwL92W z$d(3rE6`6*cQ>}~FlzJ8R7xAui7)SGIWj}4PStsbfp5&Z9H7{e?Y{~3YPEd9zjP~6 zY@_@{V@>*dx+XJDl&^gc0W221a-1<5Rnw%f@B$?`4!Q`i)PoP$yT0)I)QgcvQA=?9 zQB=RAv3A^|1`Lc|Q@nq)kz|cj_fbiP{{h5h1+v)j8QUAly9V3{4I9zePRqUTlo`iMDSB=td!tEB>H#2NFu!0vkr=R+gX& zI2v67Nn-3KNMyxdgka`OSH+90YDS0~ICSQaJTn+}LOB#t=*2S*BV9=428zp2%4YlJ z!siXsNrSDb%8&{;a0C50CM9`DWqe81t15zuN3i3pz!O&4368rj zy6*ermcydZ4UTfBNa&YHvs6H0`Ufjw5&wiKK=J(NGxJZ7egjQ|LARsX20`AHrE?m8I zd$*0{%MPP9QbfY7Mz|f`BJh|mmC9)W=@d5a&Fpei+e{4MG>;TulHPSjpm%}{0gD@D zO&UWg_~t%)vAEIb%{g4BI^swF+?&-emyz)G+=$l_I{R+L=EC#5VdOw8Zj5&mP3kL_ zx8{YF+M+@eP{9|P%yU}QYvQa>xFBWfOfA4Cl{{%2A%JM-!^dN|`=0T0&ke!|y#sRK zU)}QViRl{TS9$$frQc%Z-eYLA9CiZFz~TGTmr&Ksal~7^?;zj>uGPU(?w)~wsL_EZ z)%RE1dGTG1r*)T2cYbxl&F|-tH!&wrptfX;n>=x zSi0=H77K45bm3^|A%0=0#7t@TjP|vI?TM8(%;vBl^+BJB-Hr*@R)L|~{Zeu7bW|Dp zsGtkb^;teasKIi%uncv*8>(}|muZ+iq+l0v#Y70Yu-l(8=nCtrTYN<2eI)m2Jzsp8 zd$#D@dY8gKt(g|CyanhkpBlH`%?7zY9A-I1T z@(cNeY0H1k#M5y-zT2F+o%M@A%<%y>_APWv@tJDHe&i2Jo0dwzvrs!@;=U8Y{~$+p zn|~PD5Ft$Aa;zBdjfE_JIZU}j0@j;iZp+cm!(AGHAQc)#zpvR8lb0h?eOxoPVPY(>| z98}~DEwVE-*z1rSp-cFa-mIi_%5dn5q8K}A0Y_>z*XoCc0M4~*Utqc%yinksiF9m& z!FedKDt>ovmM?rpWF_oKY@!bFfWxof_O{mwix!-@o;^kd0A|jjYDa}3EXY=lQsvVW zln{fLQKVCZLcvq|RnNS|#8hRhJ#D0E;3WTC%)p2nl}*NA?NYxsb{LQ$gyeIza0cgI z%|BlR#bYFDZ%Lp;ip_%Ef(4h%EIg<9{nKrGA}5Nej^iJTs?cZ$3wc~&hXJa+$tkSh7%-T%|&zn~LLJTDyaB%BjhJh1hyc~m+%r9G<{299ZmbXPh!aLab+yRdLU%}_3sNvb1ISd``;3-b=I*;Fzjkrcv$;SLy`o9t z>eG}|zfeRLA)JV5|ItpUc6atObCOokrATb}8OawK_>#!U{rark_x2n-autY_zw%5H zMSnMGgBr&2Oc7IL*(O$D*)+mO7t?edk(gMAG~I}FzgO47EaF@7gRn)Of!cUcawTi;3Xsd5TWTNpnlrC(Hakddw4 zz;RZk^$bbEXch3}>HA0;(e9(sXr= z0b;DN0_CxSK?sJ21(^>L%304OVx%I2XsC(a29Dtl2@?>e;3Si0q|=HXzJjR+KoYq_ zI(+I_jq{B3Xz=1Sv96&=)FGEZ!z8c0{f$0*bDv}}FnI2ef`MTQ^~rfgDlpWaWVCP? zOAtlsf^afTk&;8d^;M|6`)&IAOK9{zn z`cJ6&2OMjigN(}i2bC696g}tpcH}kiYrFpM*{==98D(kfUHmbX z)Xti(kH>?%4jOi-Ry|XW_y4nF=2fjYk9v>_5^4Cl)pNFqAYqFfw*mDgT8Z8D{Tmza z`G=S$j18_4D^PdUr0Fx*-mm_$N(UbiP!+$()L^WUZ;e`}JvCI*?0t8b2c1rO6FF+T zbs=U}mNmYtg_w%njTKev(N(wT*GvbgKcIZIB_C@QEVHOh2103bi(I8h?N(Ej z()|8bK=LVS!;4SkSc(_y(OFu-O6%G_I5nu(#gGPedt!Eo{u5=EWk4Gc<-FCH`)^UsWb zU(S}Tp^;o6Pkddu4WZ@AycjaW`N43n~wrW zKIi75`Z98YNbp)#A+n@QF_u)p#h8x4pQu2T6t5MePpT?W)rvl5>X2x^2=FqL*9HP; zgu#kl`2$BZiDK02-IzLg2(wO%;Y@W`cAO*?cHL+Ge{=G_TcjVvKgjIOncy7Q->TCV zVUN-^yh-(g1^+8F7;iL)IRTxnk%WD`z!>wM<%DmTS?IfuGiodd?JWu7z}-;B|F`f6 zf&bvl0JRsT6i+WXSo-x1BQt0%kE<-kJ{(IsXep1=E5Yj+fiGyv64JJStqgVa+$ zKaYhbTK$oWE@&)|4Nv8j1_3Up|Cq`pkJA!B)Gkb&51PtjKaTdZ#`X%@%HwXw^|M6$ zBs-?^D&YLQD@?tN`UjAYY;?eY9u#$I{Q04RzW~)V%K#QIm&ZX9qt1x(4JujG%vG-c zlk?L<0~aP+)M5sA6HMiQs1{+xKSfumN?n5g$wCcW1qTBv1em2E(i$&w-#1ps+*5q@ z36`m!cTyi@icpnb$B?Per#g8!f8s6q+`d@icaRP37SWr-R_mm*i@9y~XQNVvXUA9v zN2cr5&>Wwicy>h^ps26`F`emrWfM*RF1`cDSsV_inAyt6=+BR!e-a(>djAGE1i`RR`!yjR?D~69Fqb4W(l=qlnDDQ_Lqji~t*_RU&vkw=X9Y}B%&@FV3w+A#or#hTt zZ@^zD&6?q6a<|Cn0GZ`%tFArl|k@#C_#%v2T^*F!SX(cI{eSy z`#{}41acwjK)Uzh?!9e3LTHiy$NH}QLHGQBx?`7u26RIo7e004`3asYn3^&sGdJn_ z0=RamRadzRuz*eSka9aNAT{(>D`$>V+b8|78MLi?|67v`xlrc^ZWM5SFp@cZt&4E+ z>)?FA#ypo(DN#6>;0q1wrHT;QBVV>1OefM28dyz7dZ6K%H4({3d*%UjbIsvZK-E01 zSMQnQJK+vrjvjQc;?Wdn?R>h^r)mp-r#)v8s^9*oDxh{8*D4Pt>mVWnP7Gg~q=rpi-5-OR}Tgb6++lRwg>`gZK;8kh&MJ5KElct=)Pa*qHKI z&!Go~njQ-s)reE48?L&w*c-1>v2fru^Hm~26+K1(*wHbJq^l&f6&RTO)**OGf#T0G zwHS>?sXr#sPcS)9Y7T3oUiezifF^&;v1D$;km0Ic)n3?aK4O#s2V+C#ybxc9=&ciw5IR2MUEMvLay-h}2--DvbQJExX>nZX8RNGr)i*|zgMZp4 z#HXY;K0js3=6qec_c-(7=iqwtmyq|Vsqx<1<0DVF&9RQ_?d9im?v&WIPi@@VK-ASQ z7J(l3w~Ob)!_B+5w~n)^4)vC^qV5fqf7Ms=P1C1`=z@3ri4z3W(H*W4D5>j6(A6Rgj@q|-+ zx=Pn2F07(y{?9Y|9N#=yT z$0mz-j#Q}|@hvkSe}^!_wzDTQ?`|x1lla#(`3J(u|D%qxj*44d*Elf9;4@HM3N2m= z6fIsDv_OI4TAZRS#Wld-P;3eug`)yGjT!# z`FOaS7!{ZoW*8aFEuPWIxfuC!)}GR=f2sYwM3vKIdRR7`kv;2NyJ&v)tLW7apmFVt z#NKI@zq>04>VfmBSDa*5pi{m=ma}1caC(dE#hE`RBBifkiaV^gyCE4=(l>1*OU}q9 zFdg1x8m7?V3>;60{B0Hd7EkP8{>5B7%D z9XA6J~J%XMo+0|LrzpU5AV6ecwkZl!@ubf1R&) zSHd{y|L`Fu64II~A~p005PesCKmO{qqpS07v75kaR^}YPVb=3YVL&@+H0?qO!8ik) z7xJLnWVJimbQ{lQaq1!(QZxXFj`E_z*+~h^>==eP#`Q)=)ka6J)p(q|V0vnm3SITi z(B1HobOSU`Wh%v?A3wNxFph103&!e=sPlxq%w+^Ns>N!??dbdCWgc)J)1Qlx8#`Q^ z7}Oo4Y^DTaG!+rK2m5G!NUK@Ni(d@A4Vug@l4HB@wUOq*b#RjBk{Evo(Cm^Q7Y7Ed z3Bam_g=qTQR{K}>byB29k}Oq8j~2pLlz(Tt`^Cp*<&`b_La=@gPcrKbZ0=`~Z#`1u zti=IDG!~6NaS1pGUW?UZ-2LLRmCR6+O{K9Nb|fHVF!lN4Id?NF-O0*+H^oEhtH~7% z_$qxw0O6?E{vd}QU7ED43Q?vU{uy;WfWrlsx-qHt>yKBcU(4+zJ+AFTy1h5UDhmaFby3v{(n0sh>XDI8)}$T{1a&mmO%*^CH~e^!SEM z^|Qi~K9zgr9`O-(3p!cf7c24&AvY_o4;AT&-TXcYuu>wysCd3!4NIx^Q z-(l8E7#Ecl#m}f(w%)k$d^54)&!a_a(vb&5j6o|Xp}9KP)S&ka0* zJ}Mh-34@sed=8yVD;yckUJCG_s;B$UEwp#cP7PeVB$U>waadiaWkSB!Pq|cVtyP4z zvFyuKD)%*c#3df#UMFmcSX#!Pk96^FeI>-x8CNQyEhzC==O2C%D>)Q3Pm-RSb@D_d z_FLoft8~BB{g*hU5wvZ$lNOI9u5Xalfpe%o`A6JeI;jopm7|pV>%Urzs`{ly68J9J z2)>R)J)QVYe8xJKY~@(vnG=~wGQe9o-8rnb%89^ z0#1+MY(h@}_N@ZOxW8sD$%X|Ey5_{mVvHko*tG= zj<2+A>o<>XP@s|OBJCF#+@i;y^mGK#pU!u7h7ju#=D_c=8eg4ru?e4b1cn=4BYXLd#B)X<#! z);2D>DZJ3*9AJrJ8s(}{Da}4jw#QH?7OpG^ z;F;%Bq--TO-1?SQFJdzX=jOQy8QOdHZsAM9f2Mlcq5TPZ^@t4GdhAn?Xc}&Tt8X zV}{FSC`HfU%lz&#hKX>7!OAnUiq;s-U6lgWq9o2N24^?z#eAIKd5*nF&qmduc+`td zs|OvGwoJWIux}=VNXvef)E+!beoyRXPqHpH8Sw;%pLJ8w>CCmn!8o|9NaJ;IlGGZ_ z=eJ+GzIu7cw$VRn3pl%cieYzvIC`))i;hO}D7L7I^|^}LenDP}$S*gB$e0?YX66|$ z`TE8vd}xF~X_*eqK>l1-f{Al07Cxd9$r-*6I#Dt__^DRLC<)qKX9apmkd{x8mJnN; zPov(!JQUhweLrIB9*s3CCztz3^s#$u?X95-Vsy3lp7p=D1#F9^gh1o`H)|5NINNIj zufA6}B|P*E(+5?55vLgi9ZV2(Fd3D z$Awcga>O) z*UBD;VpoEyKofl!o5=IMn|)W6_g){{t~H+jOcAI7(#D-DPJgG(0r(N_f*8x|33zqP zNj+d!%Qcfm`sV_H*I(B=?ECsClgEEy?ycCLsL;3nlq~x4*~SGfSyrq)5SnkY7)OQ^ z^}Thp=Q8V7U%i)$gc(Y*BU_YMI1w;U>8kUdZyX1-4EU@{R1zr}VMX*6s288MRkFE& z_IgaEZ|8q=*XQ=RwT)6g9h67Iym^k;dFX`iXP@@omevCRy4bwY_C&SzTv@PaUmsad z^7*7XM1Ug08*Lw2*NMsPpIo8uHLUFt_+H23l`>BKK3MzQNcPTFI98G37YDnk=mNLy zWVU%rBVgiI8FnrDc}c=PW5Of!1=7CMCu=Ro8aBloN1bAr%g9b`-do3U$xXs7cTc(+ z=ma!PVtM}J1pTCJ{(w3?qvQ<6c=+Us*^heL*%RtyV%Loy&>!&LWgnY}%%)k7N|9;k zg5|5y?)z##lBch`;)NOM!-Bp`lKtAY9wDkNi~}NTvW7`EJ2BJg>NXz_q`HS#W+S8r z*?4WxrZ!m1j1t@g$fhdeeo~0GI<0078|Jih8Vg+4VN!f%5zVw0nqP}zo_NU+_&d?i z7jMcMee@_12V*NaG-qSENqAHtXHqdcW`R;;wA>8cfWrL93uh=A2VJdY6fM6@pK4b9LB*iN2CR}V`XkU_(!D6Om4)JrD2N#q+YHx)F zN&1zLql#P4`j4w(mrf87FsN$A!QKYH<7wU8Jv?L-m|eT*A2{BO@E4S4)(98jqhfyD zco0yQIDw(yMGGx>B4Ui$YU8Q*%;&HrUr{l&_{+NEC^$Jqmc$*Nl!z9}lHfQ|zV&z} z|H&k2FEP4^t!E~=qx<$q*QV!n9ZvxM$!#dUyltxyPj$j=R`o6DZ$w()A4ZOWarVC& zuzN-t1HpU}-JJnrM9jJ|0sjY$rfc(VQ3`T1edoV)s_woKK{A#J_j`?Ljew!~qSQ>Q zF=}HH;SA2#`!%0jf+(nh9xxEY>uj^3q;$3y4}Kk1|)-K);no%9n` ztkT_qiX zIb4ga(uzNvm*$2-E8dsN0YAQd=hKdwKoP|m`j(~>2YeQHUV%RQYs9xcbP#|V+fa74 zQKp-572T1b?T|h>g{9B#^dxO-rX)`jz?U0|nOymWjeAlfk$0s2CD@;50=ne_j?Sru zfoBr7*zz}Ok4HLq_r78yN0jCnsr)pkDJho{mW+~-_I({>Xmk{t)$OL?B&{q}G#4%I zx758a%0~fy*3<~+NitNbhzHy}(P}Tnw@`{Qhtbt5kx7One9?EC*QKJIk^@()NU|_6{IOAZc1=QX-HD-#5$7 zV8W?u_0b=yNt-xfH82`=L0`?L29%kP7^h^sv&db<%Cjh)HUM_6qKO7Jn)TS{J*NV! zB5>qd>u!ids>9g!1AffPA1nuh+@phM#>mKlp*Sb{;?duX&UFvJz7va~u+S1!i03h~ z6;!y45WGe&9VO&bplEe7*vd>R z5wU=vw~fexW=^~j0pdnO5H6l9>DlOjk^Q{ro-<|pz&(pAXL;I8l1#`@K{f=iK(fqd zfBq?f;0>SOw#`o$sE>v@H1O@qUI`=2$VOgkX(jhMA;74~VVdcq=5wm4{-1DBnRxp^ zB3$!G=0nF)brviw{WGP(*j{tpN`}NFQ-6Xk&)gKuHQhWfa3J2dBnywyB8n4w`!jt< zUNk>>0)pOJUT$_W&0dqI{?*`F#RNjiL@j9-1DdH$yaAkM9Tg$?6AP(JRc#e7MjGS7 zdIoyO1mI}ZrfO!eev`lDHtmva5Iw!Z5#<#da~()A*ZzL3-~HJ<;4qCo3=;&j}Tt>_BjJC61hX;$x8-3Jw!ElQ79hMO~8GdP5fW60B zzk4qax6bbp^y1PJ`7#OS`7%|}+jZfpP{|hHKHCmNnF7;nE~dAb_E5X28~dZX+u6}@ zG*K^qhF#set)SZ!kutd(jxcT1xgC|-{^Lm`zhV({zVVJzTrWI{OW|Hc{bcI~I%Lt> z#7l3SNy=wRyx739l{rQ?;rEm3ME=M$nl!MecBvF93|{%s?{2;hRw zWo~pfV-P(YoEU)-bBY}Pc#w5F+cE%oNVQ)sp(AqLcV!&*HJ+qMcQ4RJ@BNXQ{oWE? zJ<}`qJ8Kj(7>q^m-bu=$F(F1N2>mg50o>~02hYzr9SW;zbyM8>0okOHz^nKa9#;a@ zmVSsnjjM}*4$U7QI$^(6d+ySl4fM$`1luINKZ;Zk;@Od8I|Ubfwjx&j5y0!Dk2b@Mt!lm{`jaxibBbkaZyF|dvX~JycgV`_pTtv zB1!#_;i-xxM9^HunrzTcyz0$FEMEXY%b_*erKru41Xt^?c}(7!FdI6hitWH?oOyCT)MtsPCI;ofc+onxsU#_8fzYFaQJu1qvq8AggWRY~*? z{?L$!{~3WFH}6B^%y-0vSPFn^5cJ{x@8)P5$EbCoE({(6yH3p5wf3i+LL`btpPGdG zKSH+|ttiKaN5jzIhCt^5Zv^skyG4A+AnRN>;?IE_txs+f1sV>Iv@P z#$d1F{LvR8?7#xsX*{$M@*_F9{Ru)O?03^uCtRa1+{JFlC?uf2S+)rOL{`#!n5~)% z(jCP1@00f!TSQMH$K!jB?cXQ=UxF=yGKrP=4{YsUP5z4Z5G6^JD5NmNKoW`3AGzLN z6@O=X{{rj&xq%|#{<#N1+7SzpKtfWtuy-`HhDoxkNXRP~TG+CysiGl)?xVl|ysEp< zZ#$V1Wk3Q^l1$D2ud+YFzkf>J$AAAWN%#-Rf!7HARC4b7Xz)M#|1%UsLZbO|BJy3g zOrHlTh{9Ax?mr^Je=6Sl-G5j7=S>FaA`DYVx$nL3e=7dN1#2K7QT{`*_kJQFqB#XZ X{rAI1L%&zTx?6}qNJzqF_ecK&#jq;W diff --git a/configure.in b/configure.in index b30ce486d..53a549a44 100644 --- a/configure.in +++ b/configure.in @@ -3140,6 +3140,7 @@ src/video/vgl/Makefile src/video/wincommon/Makefile src/video/windib/Makefile src/video/windx5/Makefile +src/video/gapi/Makefile src/video/x11/Makefile src/video/xbios/Makefile src/video/XFree86/Makefile diff --git a/src/thread/win32/SDL_systhread.c b/src/thread/win32/SDL_systhread.c index b705344c4..d685ae85b 100644 --- a/src/thread/win32/SDL_systhread.c +++ b/src/thread/win32/SDL_systhread.c @@ -30,7 +30,10 @@ static char rcsid = #include #include #include + +#ifndef _WIN32_WCE #include +#endif #include "SDL_error.h" #include "SDL_thread.h" @@ -53,9 +56,14 @@ int SDL_SYS_CreateThread(SDL_Thread *thread, void *args) * have to use _beginthreadex if we want the returned handle * to be accessible after the thread exits * threads created with _beginthread auto-close the handle + * Windows CE still use CreateThread. */ +#ifdef _WIN32_WCE + thread->handle = CreateThread(NULL, 0, RunThread, args, 0, &threadid); +#else thread->handle = (SYS_ThreadHandle) _beginthreadex(NULL, 0, RunThread, args, 0, &threadid); +#endif if (thread->handle == NULL) { SDL_SetError("Not enough resources to create thread"); return(-1); diff --git a/src/thread/win32/win_ce_semaphore.c b/src/thread/win32/win_ce_semaphore.c index cde391118..9ed37852f 100644 --- a/src/thread/win32/win_ce_semaphore.c +++ b/src/thread/win32/win_ce_semaphore.c @@ -201,9 +201,9 @@ static SYNCHHANDLE CleanUp (SYNCHHANDLE hSynch, DWORD Flags) BOOL ok = TRUE; if (hSynch == NULL) return NULL; - if (Flags & 4 == 1 && hSynch->hEvent == NULL) ok = FALSE; - if (Flags & 2 == 1 && hSynch->hMutex == NULL) ok = FALSE; - if (Flags & 1 == 1 && hSynch->hEvent == NULL) ok = FALSE; + if ((Flags & 4) == 1 && (hSynch->hEvent == NULL)) ok = FALSE; + if ((Flags & 2) == 1 && (hSynch->hMutex == NULL)) ok = FALSE; + if ((Flags & 1) == 1 && (hSynch->hEvent == NULL)) ok = FALSE; if (!ok) { CloseSynchHandle (hSynch); diff --git a/src/video/Makefile.am b/src/video/Makefile.am index 84949ebd1..f2c0e756e 100644 --- a/src/video/Makefile.am +++ b/src/video/Makefile.am @@ -9,7 +9,7 @@ DIST_SUBDIRS = dummy x11 dga nanox fbcon directfb vgl svga ggi aalib \ wincommon windib windx5 \ maccommon macdsp macrom riscos quartz \ bwindow ps2gs photon cybergfx epoc picogui \ - ataricommon xbios gem dc qtopia XFree86 wscons \ + ataricommon xbios gem dc qtopia XFree86 wscons gapi \ ipod os2fslib DRIVERS = @VIDEO_DRIVERS@ diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index 50382cb3a..1d25919f5 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -39,6 +39,7 @@ static char rcsid = #include "SDL_memops.h" #include "SDL_leaks.h" + /* Public routines */ /* * Create an empty RGB surface of the appropriate depth diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 730bf0730..29aa54395 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -365,6 +365,9 @@ extern VideoBootStrap SVGALIB_bootstrap; #ifdef ENABLE_AALIB extern VideoBootStrap AALIB_bootstrap; #endif +#ifdef ENABLE_GAPI +extern VideoBootStrap GAPI_bootstrap; +#endif #ifdef ENABLE_WINDIB extern VideoBootStrap WINDIB_bootstrap; #endif diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index ee76a0c18..fcb185d42 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -87,6 +87,9 @@ static VideoBootStrap *bootstrap[] = { #ifdef ENABLE_AALIB &AALIB_bootstrap, #endif +#ifdef ENABLE_GAPI + &GAPI_bootstrap, +#endif #ifdef ENABLE_WINDIB &WINDIB_bootstrap, #endif diff --git a/src/video/gapi/.cvsignore b/src/video/gapi/.cvsignore new file mode 100644 index 000000000..899d53557 --- /dev/null +++ b/src/video/gapi/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +.libs +*.o +*.lo +*.la diff --git a/src/video/gapi/Makefile.am b/src/video/gapi/Makefile.am new file mode 100644 index 000000000..005119d1a --- /dev/null +++ b/src/video/gapi/Makefile.am @@ -0,0 +1,10 @@ + +## Makefile.am for SDL using the PocketPC GAPI video driver + +noinst_LTLIBRARIES = libvideo_gapi.la +libvideo_gapi_la_SOURCES = $(GAPI_SRCS) + +# The SDL GAPI driver sources +GAPI_SRCS = \ + SDL_gapivideo.c \ + SDL_gapivideo.h diff --git a/src/video/gapi/SDL_gapivideo.c b/src/video/gapi/SDL_gapivideo.c new file mode 100644 index 000000000..be596e7c4 --- /dev/null +++ b/src/video/gapi/SDL_gapivideo.c @@ -0,0 +1,1127 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +/* Pocket PC GAPI SDL video driver implementation; +Implemented by Dmitry Yakimov - support@activekitten.com +Inspired by http://arisme.free.fr/ports/SDL.php +*/ + +// TODO: copy surface on window when lost focus +// TODO: test buttons rotation +// TODO: test on be300 and HPC ( check WinDib fullscreen keys catching ) +// TODO: test on smartphones +// TODO: windib on SH3 PPC2000 landscape test +// TODO: optimize 8bpp landscape mode + +#include +#include +#include + +#include "SDL.h" +#include "SDL_error.h" +#include "SDL_video.h" +#include "SDL_mouse.h" +#include "SDL_sysvideo.h" +#include "SDL_pixels_c.h" +#include "SDL_events_c.h" + +#include "SDL_syswm_c.h" +#include "SDL_sysmouse_c.h" +#include "SDL_dibevents_c.h" + +#include "SDL_gapivideo.h" + +#define GAPIVID_DRIVER_NAME "gapi" + +#if defined(DEBUG) || defined (_DEBUG) || defined(NDEBUG) +#define REPORT_VIDEO_INFO 1 +#else +#define REPORT_VIDEO_INFO 0 +#endif + +// for testing with GapiEmu +#define USE_GAPI_EMU 0 + +#if USE_GAPI_EMU && !REPORT_VIDEO_INFO +#pragma message("Warning: Using GapiEmu in release build. I assume you'd like to set USE_GAPI_EMU to zero.") +#endif + +// defined and used in SDL_sysevents.c +extern HINSTANCE aygshell; +extern void SDL_UnregisterApp(); +extern int DIB_AddMode(_THIS, int bpp, int w, int h); + +/* Initialization/Query functions */ +static int GAPI_VideoInit(_THIS, SDL_PixelFormat *vformat); +static SDL_Rect **GAPI_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags); +static SDL_Surface *GAPI_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags); +static int GAPI_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors); +static void GAPI_VideoQuit(_THIS); + +/* Hardware surface functions */ +static int GAPI_AllocHWSurface(_THIS, SDL_Surface *surface); +static int GAPI_LockHWSurface(_THIS, SDL_Surface *surface); +static void GAPI_UnlockHWSurface(_THIS, SDL_Surface *surface); +static void GAPI_FreeHWSurface(_THIS, SDL_Surface *surface); + +/* Windows message handling functions, will not be processed */ +static void GAPI_RealizePalette(_THIS); +static void GAPI_PaletteChanged(_THIS, HWND window); +static void GAPI_WinPAINT(_THIS, HDC hdc); + +/* etc. */ +static void GAPI_UpdateRects(_THIS, int numrects, SDL_Rect *rects); + +static HMODULE g_hGapiLib = 0; +#define LINK(type,name,import) \ + if( g_hGapiLib ) \ + name = (PFN##type)GetProcAddress( g_hGapiLib, _T(import) ); + +static char g_bRawBufferAvailable = 0; + +/* GAPI driver bootstrap functions */ + +/* hi res definitions */ +typedef struct _RawFrameBufferInfo +{ + WORD wFormat; + WORD wBPP; + VOID *pFramePointer; + int cxStride; + int cyStride; + int cxPixels; + int cyPixels; +} RawFrameBufferInfo; + +static struct _RawFrameBufferInfo g_RawFrameBufferInfo = {0}; + +#define GETRAWFRAMEBUFFER 0x00020001 + +#define FORMAT_565 1 +#define FORMAT_555 2 +#define FORMAT_OTHER 3 + +static int GAPI_Available(void) +{ + // try to use VGA display, even on emulator + HDC hdc = GetDC(NULL); + int result = ExtEscape(hdc, GETRAWFRAMEBUFFER, 0, NULL, sizeof(RawFrameBufferInfo), (char *)&g_RawFrameBufferInfo); + ReleaseDC(NULL, hdc); + g_bRawBufferAvailable = result > 0; + +#if USE_GAPI_EMU + g_hGapiLib = LoadLibrary(_T("GAPI_Emu.dll")); + if( !g_hGapiLib ) + { + SDL_SetError("Gapi Emu not found!"); + } + return g_hGapiLib != 0; +#endif + + // try to find gx.dll + g_hGapiLib = LoadLibrary(_T("\\Windows\\gx.dll")); + if( !g_hGapiLib ) + { + g_hGapiLib = LoadLibrary(_T("gx.dll")); + if( !g_hGapiLib ) return g_bRawBufferAvailable; + } + + return(1); +} + +static int cmpmodes(const void *va, const void *vb) +{ + SDL_Rect *a = *(SDL_Rect **)va; + SDL_Rect *b = *(SDL_Rect **)vb; + if ( a->w == b->w ) + return b->h - a->h; + else + return b->w - a->w; +} + +static int GAPI_AddMode(_THIS, int bpp, int w, int h) +{ + SDL_Rect *mode; + int i, index; + int next_mode; + + /* Check to see if we already have this mode */ + if ( bpp < 8 ) { /* Not supported */ + return(0); + } + index = ((bpp+7)/8)-1; + for ( i=0; iSDL_nummodes[index]; ++i ) { + mode = gapi->SDL_modelist[index][i]; + if ( (mode->w == w) && (mode->h == h) ) { + return(0); + } + } + + /* Set up the new video mode rectangle */ + mode = (SDL_Rect *)malloc(sizeof *mode); + if ( mode == NULL ) { + SDL_OutOfMemory(); + return(-1); + } + mode->x = 0; + mode->y = 0; + mode->w = w; + mode->h = h; + + /* Allocate the new list of modes, and fill in the new mode */ + next_mode = gapi->SDL_nummodes[index]; + gapi->SDL_modelist[index] = (SDL_Rect **) + realloc(gapi->SDL_modelist[index], (1+next_mode+1)*sizeof(SDL_Rect *)); + if ( gapi->SDL_modelist[index] == NULL ) { + SDL_OutOfMemory(); + gapi->SDL_nummodes[index] = 0; + free(mode); + return(-1); + } + gapi->SDL_modelist[index][next_mode] = mode; + gapi->SDL_modelist[index][next_mode+1] = NULL; + gapi->SDL_nummodes[index]++; + + return(0); +} + +static void GAPI_DeleteDevice(SDL_VideoDevice *device) +{ + if( g_hGapiLib ) + { + FreeLibrary(g_hGapiLib); + g_hGapiLib = 0; + } + free(device->hidden); + free(device); +} + +static SDL_VideoDevice *GAPI_CreateDevice(int devindex) +{ + SDL_VideoDevice *device; + + if( !g_hGapiLib && !g_bRawBufferAvailable) + { + if( !GAPI_Available() ) + { + SDL_SetError("GAPI dll is not found and VGA mode is not available!"); + return 0; + } + } + + /* Initialize all variables that we clean on shutdown */ + device = (SDL_VideoDevice *)malloc(sizeof(SDL_VideoDevice)); + if ( device ) { + memset(device, 0, (sizeof *device)); + device->hidden = (struct SDL_PrivateVideoData *) + malloc((sizeof *device->hidden)); + } + if ( (device == NULL) || (device->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( device ) { + free(device); + } + return(0); + } + memset(device->hidden, 0, (sizeof *device->hidden)); + + /* Set the function pointers */ + device->VideoInit = GAPI_VideoInit; + device->ListModes = GAPI_ListModes; + device->SetVideoMode = GAPI_SetVideoMode; + device->UpdateMouse = WIN_UpdateMouse; + device->CreateYUVOverlay = NULL; + device->SetColors = GAPI_SetColors; + device->UpdateRects = GAPI_UpdateRects; + device->VideoQuit = GAPI_VideoQuit; + device->AllocHWSurface = GAPI_AllocHWSurface; + device->CheckHWBlit = NULL; + device->FillHWRect = NULL; + device->SetHWColorKey = NULL; + device->SetHWAlpha = NULL; + device->LockHWSurface = GAPI_LockHWSurface; + device->UnlockHWSurface = GAPI_UnlockHWSurface; + device->FlipHWSurface = NULL; + device->FreeHWSurface = GAPI_FreeHWSurface; + device->SetCaption = WIN_SetWMCaption; + device->SetIcon = WIN_SetWMIcon; + device->IconifyWindow = WIN_IconifyWindow; + device->GrabInput = WIN_GrabInput; + device->GetWMInfo = WIN_GetWMInfo; + device->FreeWMCursor = WIN_FreeWMCursor; + device->CreateWMCursor = WIN_CreateWMCursor; + device->ShowWMCursor = WIN_ShowWMCursor; + device->WarpWMCursor = WIN_WarpWMCursor; + device->CheckMouseMode = WIN_CheckMouseMode; + device->InitOSKeymap = DIB_InitOSKeymap; + device->PumpEvents = DIB_PumpEvents; + + /* Set up the windows message handling functions */ + WIN_RealizePalette = GAPI_RealizePalette; + WIN_PaletteChanged = GAPI_PaletteChanged; + WIN_WinPAINT = GAPI_WinPAINT; + HandleMessage = DIB_HandleMessage; + + device->free = GAPI_DeleteDevice; + + /* Load gapi library */ +#define gx device->hidden->gxFunc + + LINK( GXOpenDisplay, gx.GXOpenDisplay, "?GXOpenDisplay@@YAHPAUHWND__@@K@Z" ) + LINK( GXCloseDisplay, gx.GXCloseDisplay, "?GXCloseDisplay@@YAHXZ" ) + LINK( GXBeginDraw, gx.GXBeginDraw, "?GXBeginDraw@@YAPAXXZ" ) + LINK( GXEndDraw, gx.GXEndDraw, "?GXEndDraw@@YAHXZ" ) + LINK( GXOpenInput, gx.GXOpenInput, "?GXOpenInput@@YAHXZ" ) + LINK( GXCloseInput, gx.GXCloseInput, "?GXCloseInput@@YAHXZ" ) + LINK( GXGetDisplayProperties, gx.GXGetDisplayProperties,"?GXGetDisplayProperties@@YA?AUGXDisplayProperties@@XZ" ) + LINK( GXGetDefaultKeys, gx.GXGetDefaultKeys, "?GXGetDefaultKeys@@YA?AUGXKeyList@@H@Z" ) + LINK( GXSuspend, gx.GXSuspend, "?GXSuspend@@YAHXZ" ) + LINK( GXResume, gx.GXResume, "?GXResume@@YAHXZ" ) + LINK( GXSetViewport, gx.GXSetViewport, "?GXSetViewport@@YAHKKKK@Z" ) + LINK( GXIsDisplayDRAMBuffer, gx.GXIsDisplayDRAMBuffer, "?GXIsDisplayDRAMBuffer@@YAHXZ" ) + + /* wrong gapi.dll */ + if( !gx.GXOpenDisplay ) + { + if( g_hGapiLib ) + { + FreeLibrary(g_hGapiLib); + g_hGapiLib = 0; + } + } + + if( !gx.GXOpenDisplay && !g_bRawBufferAvailable) + { + SDL_SetError("Error: damaged or unknown gapi.dll!\n"); + GAPI_DeleteDevice(device); + return 0; + } + + return device; +} + +VideoBootStrap GAPI_bootstrap = { + GAPIVID_DRIVER_NAME, "WinCE GAPI video driver", + GAPI_Available, GAPI_CreateDevice +}; + +static void FillStructs(_THIS, BOOL useVga) +{ +#ifdef _ARM_ + WCHAR oemstr[100]; +#endif + /* fill a device properties */ + + if( !useVga ) + { + this->hidden->gxProperties = this->hidden->gxFunc.GXGetDisplayProperties(); + this->hidden->needUpdate = 1; + this->hidden->hiresFix = 0; + this->hidden->useVga = 0; +#ifdef _ARM_ + /* check some devices and extract addition info */ + SystemParametersInfo( SPI_GETOEMINFO, sizeof( oemstr ), oemstr, 0 ); + + // buggy iPaq38xx + if ((oemstr[12] == 'H') && (oemstr[13] == '3') && (oemstr[14] == '8') && (this->hidden->gxProperties.cbxPitch > 0)) + { + this->hidden->videoMem = (PIXEL*)0xac0755a0; + this->hidden->gxProperties.cbxPitch = -640; + this->hidden->gxProperties.cbyPitch = 2; + this->hidden->needUpdate = 0; + } +#endif + } else + { + this->hidden->needUpdate = 0; + this->hidden->hiresFix = 0; + this->hidden->gxProperties.cBPP = g_RawFrameBufferInfo.wBPP; + this->hidden->gxProperties.cbxPitch = g_RawFrameBufferInfo.cxStride; + this->hidden->gxProperties.cbyPitch = g_RawFrameBufferInfo.cyStride; + this->hidden->gxProperties.cxWidth = g_RawFrameBufferInfo.cxPixels; + this->hidden->gxProperties.cyHeight = g_RawFrameBufferInfo.cyPixels; + this->hidden->videoMem = g_RawFrameBufferInfo.pFramePointer; + this->hidden->useVga = 1; + + switch( g_RawFrameBufferInfo.wFormat ) + { + case FORMAT_565: + this->hidden->gxProperties.ffFormat = kfDirect565; + break; + case FORMAT_555: + this->hidden->gxProperties.ffFormat = kfDirect555; + break; + default: + /* unknown pixel format, try define by BPP! */ + switch( g_RawFrameBufferInfo.wBPP ) + { + case 4: + case 8: + this->hidden->gxProperties.ffFormat = kfDirect; + case 16: + this->hidden->gxProperties.ffFormat = kfDirect565; + default: + this->hidden->gxProperties.ffFormat = kfDirect; + break; + } + } + } + + if( this->hidden->gxProperties.cBPP != 16 ) + { + this->hidden->gapiOrientation = SDL_ORIENTATION_UP; + } else + if( (this->hidden->gxProperties.cbxPitch > 0) && (this->hidden->gxProperties.cbyPitch > 0 )) + { + this->hidden->gapiOrientation = SDL_ORIENTATION_UP; + } else + if( (this->hidden->gxProperties.cbxPitch > 0) && (this->hidden->gxProperties.cbyPitch < 0 )) + { + this->hidden->gapiOrientation = SDL_ORIENTATION_RIGHT; // ipaq 3660 + } else + if( (this->hidden->gxProperties.cbxPitch < 0) && (this->hidden->gxProperties.cbyPitch > 0 )) + { + this->hidden->gapiOrientation = SDL_ORIENTATION_LEFT; // ipaq 3800 + } +} + +static void GAPI_CreatePalette(int ncolors, SDL_Color *colors) +{ + // Setup a custom color palette + BYTE buffer[ sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY) ]; + int i; + LOGPALETTE* pLogical = (LOGPALETTE*)buffer; + PALETTEENTRY* entries = pLogical->palPalEntry; + HPALETTE hPalette; + HDC hdc; + + for (i = 0; i < ncolors; ++i) + { + // Find intensity by replicating the bit patterns over a byte + entries[i].peRed = colors[i].r; + entries[i].peGreen = colors[i].g; + entries[i].peBlue = colors[i].b; + entries[i].peFlags = 0; + } + + // Create the GDI palette object + pLogical->palVersion = 0x0300; + pLogical->palNumEntries = ncolors; + + hPalette = CreatePalette( pLogical ); + ASSERT(hPalette); + + + // Realize the palette + hdc = GetDC(0); + + SelectPalette( hdc, hPalette, FALSE ); + RealizePalette( hdc ); + + ReleaseDC( 0, hdc ); + DeleteObject( hPalette ); +} + +int GAPI_VideoInit(_THIS, SDL_PixelFormat *vformat) +{ + int i,bpp; + + /* Create the window */ + if ( DIB_CreateWindow(this) < 0 ) { + return(-1); + } + + if( g_hGapiLib ) + { + FillStructs(this, 0); + + // SDL does not supports 2/4bpp mode, so use 16 bpp + bpp = gapi->gxProperties.cBPP < 8 ? 16 : gapi->gxProperties.cBPP; + + /* set up normal and landscape mode */ + GAPI_AddMode(this, bpp, gapi->gxProperties.cyHeight, gapi->gxProperties.cxWidth); + GAPI_AddMode(this, bpp, gapi->gxProperties.cxWidth, gapi->gxProperties.cyHeight); + } + + /* add hi-res mode */ + if( g_bRawBufferAvailable && + !((gapi->gxProperties.cxWidth == (unsigned)g_RawFrameBufferInfo.cxPixels) && (gapi->gxProperties.cyHeight == (unsigned)g_RawFrameBufferInfo.cyPixels))) + { + FillStructs(this, 1); + + // SDL does not supports 2/4bpp mode, so use 16 bpp + bpp = gapi->gxProperties.cBPP < 8 ? 16 : gapi->gxProperties.cBPP; + + /* set up normal and landscape mode */ + GAPI_AddMode(this, bpp, gapi->gxProperties.cyHeight, gapi->gxProperties.cxWidth); + GAPI_AddMode(this, bpp, gapi->gxProperties.cxWidth, gapi->gxProperties.cyHeight); + } + + /* Sort the mode lists */ + for ( i=0; iSDL_nummodes[i] > 0 ) { + qsort(gapi->SDL_modelist[i], gapi->SDL_nummodes[i], sizeof *gapi->SDL_modelist[i], cmpmodes); + } + } + + vformat->BitsPerPixel = this->hidden->gxProperties.cBPP < 8 ? 16 : (unsigned char)this->hidden->gxProperties.cBPP; + + // Get color mask + if (this->hidden->gxProperties.ffFormat & kfDirect565) { + vformat->BitsPerPixel = 16; + vformat->Rmask = 0x0000f800; + vformat->Gmask = 0x000007e0; + vformat->Bmask = 0x0000001f; + this->hidden->videoMode = GAPI_DIRECT_565; + } + else + if (this->hidden->gxProperties.ffFormat & kfDirect555) { + vformat->BitsPerPixel = 16; + vformat->Rmask = 0x00007c00; + vformat->Gmask = 0x000003e0; + vformat->Bmask = 0x0000001f; + this->hidden->videoMode = GAPI_DIRECT_555; + } + else + if ((this->hidden->gxProperties.ffFormat & kfDirect) && (this->hidden->gxProperties.cBPP < 8)) { + // We'll perform the conversion + vformat->BitsPerPixel = 16; + vformat->Rmask = 0x0000f800; // 16 bit 565 + vformat->Gmask = 0x000007e0; + vformat->Bmask = 0x0000001f; + if (this->hidden->gxProperties.ffFormat & kfDirectInverted) + this->hidden->invert = (1 << this->hidden->gxProperties.cBPP) - 1; + this->hidden->colorscale = this->hidden->gxProperties.cBPP < 8 ? 8 - this->hidden->gxProperties.cBPP : 0; + this->hidden->videoMode = GAPI_MONO; + } + else + if (this->hidden->gxProperties.ffFormat & kfPalette) { + this->hidden->videoMode = GAPI_PALETTE; + } + + /* We're done! */ + return(0); +} + +SDL_Rect **GAPI_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags) +{ + return(this->hidden->SDL_modelist[((format->BitsPerPixel+7)/8)-1]); +// return (SDL_Rect **) -1; +} + +SDL_Surface *GAPI_SetVideoMode(_THIS, SDL_Surface *current, + int width, int height, int bpp, Uint32 flags) +{ + SDL_Surface *video; + Uint32 Rmask, Gmask, Bmask; + DWORD style; + SDL_Rect allScreen; + + if( bpp < 4 ) + { + SDL_SetError("1 bpp and 2 bpp modes is not implemented yet!"); + return 0; + } + + /* Recalculate bitmasks if necessary */ + if (bpp == current->format->BitsPerPixel) { + video = current; + } + else { + switch(bpp) { + case 8: + Rmask = 0; + Gmask = 0; + Bmask = 0; + break; + case 15: + case 16: + /* Default is 565 unless the display is specifically 555 */ + if (this->hidden->gxProperties.ffFormat & kfDirect555) { + Rmask = 0x00007c00; + Gmask = 0x000003e0; + Bmask = 0x0000001f; + } + else { + Rmask = 0x0000f800; + Gmask = 0x000007e0; + Bmask = 0x0000001f; + } + break; + case 24: + case 32: + Rmask = 0x00ff0000; + Gmask = 0x0000ff00; + Bmask = 0x000000ff; + break; + default: + SDL_SetError("Unsupported Bits Per Pixel format requested"); + return NULL; + } + video = SDL_CreateRGBSurface(SDL_SWSURFACE, + 0, 0, bpp, Rmask, Gmask, Bmask, 0); + if ( video == NULL ) { + SDL_OutOfMemory(); + return(NULL); + } + } + + gapi->userOrientation = SDL_ORIENTATION_UP; + video->flags = SDL_FULLSCREEN; /* Clear flags, GAPI supports fullscreen only */ + + /* GAPI or VGA? */ + if( g_hGapiLib ) + { + FillStructs(this, 0); + if( (((unsigned)width != gapi->gxProperties.cxWidth) || ((unsigned)height != gapi->gxProperties.cyHeight)) + && (((unsigned)width != gapi->gxProperties.cyHeight) || ((unsigned)height != gapi->gxProperties.cxWidth))) + FillStructs(this, 1); // gapi is found but we use VGA resolution + } else + FillStructs(this, 1); + + if ( !this->hidden->needUpdate && !this->hidden->videoMem) { + SDL_SetError("Couldn't get address of video memory, may be unsupported device or bug"); + return(NULL); + } + + /* detect landscape mode */ + if( (width > height) && (GetSystemMetrics(SM_CXSCREEN) < GetSystemMetrics(SM_CYSCREEN))) + gapi->userOrientation = SDL_ORIENTATION_RIGHT; + + /* shall we apply hires fix? for example when we do not use hires resource */ + gapi->hiresFix = 0; + if( gapi->userOrientation == SDL_ORIENTATION_RIGHT ) + { + if( (width > GetSystemMetrics(SM_CYSCREEN)) || (height > GetSystemMetrics(SM_CXSCREEN))) + gapi->hiresFix = 1; + } else + if( (width > GetSystemMetrics(SM_CXSCREEN)) || (height > GetSystemMetrics(SM_CYSCREEN))) + gapi->hiresFix = 1; + + switch( gapi->userOrientation ) + { + case SDL_ORIENTATION_UP: + gapi->startOffset = 0; + gapi->dstLineStep = gapi->gxProperties.cbyPitch; + gapi->dstPixelStep = gapi->gxProperties.cbxPitch; + break; + case SDL_ORIENTATION_RIGHT: + switch( gapi->gapiOrientation ) + { + case SDL_ORIENTATION_UP: + case SDL_ORIENTATION_RIGHT: + case SDL_ORIENTATION_LEFT: + if( (this->hidden->videoMode == GAPI_MONO) ) + gapi->startOffset = -gapi->gxProperties.cbxPitch + 1; // monochrome mode + else + gapi->startOffset = gapi->gxProperties.cbyPitch * (gapi->gxProperties.cyHeight - 1); + + gapi->dstLineStep = gapi->gxProperties.cbxPitch; + gapi->dstPixelStep = -gapi->gxProperties.cbyPitch; + break; + } + } + + video->w = this->hidden->w = width; + video->h = this->hidden->h = height; + video->pitch = SDL_CalculatePitch(video); + + /* Small fix for WinCE/Win32 - when activating window + SDL_VideoSurface is equal to zero, so activating code + is not called properly for fullscreen windows because + macros WINDIB_FULLSCREEN uses SDL_VideoSurface + */ + SDL_VideoSurface = video; + + /* GAPI is always fullscreen, title bar is useless */ + style = 0; + + if (!SDL_windowid) + SetWindowLong(SDL_Window, GWL_STYLE, style); + + /* Allocate bitmap */ + if(gapiBuffer) + { + free(gapiBuffer); + gapiBuffer = NULL; + } + gapiBuffer = malloc(video->h * video->pitch); + video->pixels = gapiBuffer; + + if ( ! this->hidden->buffer ) { + SDL_SetError("Couldn't allocate buffer for requested mode"); + return(NULL); + } + + memset(gapiBuffer, 255, video->h * video->pitch); + MoveWindow(SDL_Window, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), FALSE); + ShowWindow(SDL_Window, SW_SHOW); + SetForegroundWindow(SDL_Window); + +#if REPORT_VIDEO_INFO + printf("Video properties:\n"); + printf("display bpp: %d\n", gapi->gxProperties.cBPP); + printf("display width: %d\n", gapi->gxProperties.cxWidth); + printf("display height: %d\n", gapi->gxProperties.cyHeight); + printf("x pitch: %d\n", gapi->gxProperties.cbxPitch); + printf("y pitch: %d\n", gapi->gxProperties.cbyPitch); + printf("gapi flags: 0x%x\n", gapi->gxProperties.ffFormat); + printf("video memory: 0x%x\n", gapi->videoMem); + printf("need update: %d\n", gapi->needUpdate); + printf("hi-res fix: %d\n", gapi->hiresFix); + printf("VGA is available on the device: %d\n", g_bRawBufferAvailable); + printf("use VGA resolution: %d\n", gapi->useVga); + printf("video surface bpp: %d\n", video->format->BitsPerPixel); + printf("video surface width: %d\n", video->w); + printf("video surface height: %d\n", video->h); +#endif + + /* Open GAPI display */ + if( !gapi->useVga ) + if( !gapi->gxFunc.GXOpenDisplay(SDL_Window, GX_FULLSCREEN) ) + { + SDL_SetError("Couldn't initialize GAPI"); + return(NULL); + } + + /* Blank screen */ + allScreen.x = allScreen.y = 0; + allScreen.w = video->w - 1; + allScreen.h = video->h - 1; + GAPI_UpdateRects(this, 1, &allScreen); + + /* We're done */ + return(video); +} + +/* We don't actually allow hardware surfaces other than the main one */ +static int GAPI_AllocHWSurface(_THIS, SDL_Surface *surface) +{ + return(-1); +} +static void GAPI_FreeHWSurface(_THIS, SDL_Surface *surface) +{ + return; +} + +/* We need to wait for vertical retrace on page flipped displays */ +static int GAPI_LockHWSurface(_THIS, SDL_Surface *surface) +{ + return(0); +} + +static void GAPI_UnlockHWSurface(_THIS, SDL_Surface *surface) +{ + return; +} + +static int updateLine8to8(_THIS, unsigned char *srcPointer, unsigned char *destPointer, int width, int height, int lines) +{ + if( gapi->dstPixelStep == 1) /* optimized blitting on most devices */ + { + memcpy(destPointer, srcPointer, width); + return 1; + } else + { + // TODO: read 4 pixels, write DWORD + int step = gapi->dstPixelStep; + while(width--) + { + *destPointer = *srcPointer++; + destPointer += step; + } + } + return 1; +} + +/* Video memory is very slow so lets optimize as much as possible */ +static int updateLine16to16(_THIS, PIXEL *srcPointer, PIXEL *destPointer, int width, int height, int lines) +{ + PIXEL *line1, *line2; + int step = gapi->dstPixelStep / 2; + + if( step == 1 ) /* optimized blitting on most devices */ + { + memcpy(destPointer, srcPointer, width * sizeof(PIXEL)); + return 1; + } + else + { + if( (gapi->gapiOrientation != SDL_ORIENTATION_UP) && + (gapi->userOrientation == SDL_ORIENTATION_UP )) // iPaq 3660/3800 and user orientation up + { + // to prevent data misalignment copy only one line + if( ((((unsigned)destPointer & 3) != 0) && (gapi->gapiOrientation == SDL_ORIENTATION_LEFT)) + || ((((unsigned)destPointer & 3) == 0) && (gapi->gapiOrientation != SDL_ORIENTATION_LEFT)) + || (lines == 1) ) + { + while(width--) + { + *destPointer = *srcPointer++; + destPointer += step; + } + return 1; + } + + /* read two lines at the same time, write DWORD */ + line1 = srcPointer; + line2 = srcPointer + SDL_VideoSurface->pitch / 2; + + if( gapi->gapiOrientation == SDL_ORIENTATION_LEFT ) + while(width--) // iPaq 3800 + { + *(DWORD*)destPointer =(*line2++ << 16) | *line1++; + destPointer += step; + } + else + { + destPointer += gapi->gxProperties.cbyPitch / 2; + while(width--) // iPaq 3660 + { + *(DWORD*)destPointer =(*line1++ << 16) | *line2++; + destPointer += step; + } + } + return 2; + } else + { + // iPaq 3800 and user orientation landscape + if( gapi->gapiOrientation == SDL_ORIENTATION_LEFT ) + { + int w1; + + // to prevent data misalignment copy only one pixel + if( (((unsigned)destPointer & 3) == 0) && (width > 0)) + { + *destPointer-- = *srcPointer++; + width--; + } + + destPointer--; + + w1 = width / 2; + + while(w1--) + { + DWORD p = *(DWORD*)srcPointer; + *((DWORD*)destPointer) = (p << 16) | (p >> 16); + destPointer -= 2; + srcPointer += 2; + } + + if( width & 1 ) // copy the last pixel + { + destPointer++; + *destPointer = *srcPointer; + } + + return 1; + } + + // modern iPaqs and user orientation landscape + // read two pixels, write DWORD + + line1 = srcPointer; + line2 = srcPointer + SDL_VideoSurface->pitch / 2; + + if( (((unsigned)destPointer & 3) != 0) || (lines == 1) ) + { + while(width--) + { + *destPointer = *srcPointer++; + destPointer += step; + } + return 1; + } + + while(width--) + { + *(DWORD*)destPointer =(*line2++ << 16) | *line1++; + destPointer -= gapi->gxProperties.cbyPitch / 2; + } + return 2; + } + } +} + +// Color component masks for 565 +#define REDMASK (31<<11) +#define GREENMASK (63<<5) +#define BLUEMASK (31) + + +static int updateLine16to4(_THIS, PIXEL *srcPointer, unsigned char *destPointer, int width, int height, int lines, int yNibble, int xNibble) +{ + PIXEL *line1, *line2; + int step = gapi->dstPixelStep; + + if( gapi->userOrientation == SDL_ORIENTATION_UP ) + { + if( yNibble ) // copy bottom half of a line + { + while(width--) + { + PIXEL c1 = *srcPointer++; + c1 = ((c1 & REDMASK) >> 11) + ((c1 & GREENMASK) >> 5) + (c1 & BLUEMASK); + *destPointer = (*destPointer & 0x0F) | ((~(c1 >> 3) << 4)); + destPointer += step; + } + return 1; + } + + // either 1 pixel picture or tail, anyway this is the last line + if( lines == 1 ) + { + while(width--) + { + PIXEL c1 = *srcPointer++; + c1 = ((c1 & REDMASK) >> 11) + ((c1 & GREENMASK) >> 5) + (c1 & BLUEMASK); + *destPointer = (*destPointer & 0xF0) | ((~(c1 >> 3) & 0xF)); + destPointer += step; + } + return 1; + } + + line1 = srcPointer; + line2 = srcPointer + SDL_VideoSurface->pitch / 2; + + while(width--) + { + PIXEL c1 = *line1++; + PIXEL c2 = *line2++; + c1 = ((c1 & REDMASK) >> 11) + ((c1 & GREENMASK) >> 5) + (c1 & BLUEMASK); + c2 = ((c2 & REDMASK) >> 11) + ((c2 & GREENMASK) >> 5) + (c2 & BLUEMASK); + *destPointer = ~((c1 >> 3) + ((c2 >> 3) << 4)); + destPointer += step; + } + return 2; + } else + { + int w1; + w1 = width / 2; + + if( xNibble ) + { + // copy one pixel + PIXEL c1 = *srcPointer++; + c1 = ((c1 & REDMASK) >> 11) + ((c1 & GREENMASK) >> 5) + (c1 & BLUEMASK); + *destPointer = (*destPointer & 0xF0) | ((~(c1 >> 3) & 0xF)); + destPointer++; + } + + while(w1--) + { + PIXEL c1 = *srcPointer; + PIXEL c2 = *(srcPointer + 1); + c1 = ((c1 & REDMASK) >> 11) + ((c1 & GREENMASK) >> 5) + (c1 & BLUEMASK); + c2 = ((c2 & REDMASK) >> 11) + ((c2 & GREENMASK) >> 5) + (c2 & BLUEMASK); + *destPointer++ = ~((c2 >> 3) + ((c1 >> 3) << 4)); + srcPointer += 2; + } + + // copy tail + if( (width & 1) && !xNibble ) + { + PIXEL c1 = *srcPointer; + c1 = ((c1 & REDMASK) >> 11) + ((c1 & GREENMASK) >> 5) + (c1 & BLUEMASK); + *destPointer = (*destPointer & 0x0F) | ((~(c1 >> 3) << 4)); + } + + return 1; + } +} + +static void GAPI_UpdateRectsMono(_THIS, int numrects, SDL_Rect *rects) +{ + int i, height; + int linesProcessed; + int xNibble, yNibble; + + for (i=0; iuserOrientation == SDL_ORIENTATION_UP ) + destPointer = (unsigned char*) gapi->videoMem + gapi->startOffset - rects[i].y * gapi->gxProperties.cBPP / 8 + rects[i].x * gapi->dstPixelStep; + else + destPointer = (unsigned char*) gapi->videoMem + gapi->startOffset + rects[i].x * gapi->gxProperties.cBPP / 8 + rects[i].y * gapi->dstLineStep; + + srcPointer = ((unsigned char*) SDL_VideoSurface->pixels) + rects[i].y * SDL_VideoSurface->pitch + rects[i].x * 2; + yNibble = rects[i].y & 1; // TODO: only for 4 bpp + xNibble = rects[i].x & 1; + height = rects[i].h; + while (height > 0) + { + switch(gapi->gxProperties.cBPP) + { + case 2: // TODO + case 4: + linesProcessed = updateLine16to4(this, (PIXEL*) srcPointer, destPointer, rects[i].w, rects[i].h, height, yNibble, xNibble); + yNibble = 0; + } + height -= linesProcessed; + if( gapi->userOrientation == SDL_ORIENTATION_UP ) + destPointer--; // always fill 1 byte + else destPointer += gapi->dstLineStep; + srcPointer += SDL_VideoSurface->pitch * linesProcessed; // pitch in bytes + } + } +} + +static void GAPI_UpdateRectsColor(_THIS, int numrects, SDL_Rect *rects) +{ + int i, height; + int bytesPerPixel = (gapi->gxProperties.cBPP + 1) / 8; + int linesProcessed; + for (i=0; ivideoMem + gapi->startOffset + rects[i].y * gapi->dstLineStep + rects[i].x * gapi->dstPixelStep; + unsigned char *srcPointer = ((unsigned char*) SDL_VideoSurface->pixels) + rects[i].y * SDL_VideoSurface->pitch + rects[i].x * bytesPerPixel; + height = rects[i].h; + +// fprintf(stderr, "Starting rect %dx%d, dst=0x%x, w = %d, h = %d\n", rects[i].w, rects[i].h,destPointer,rects[i].w,rects[i].h); +// fflush(stderr); + linesProcessed = height; + + while (height > 0) { + switch(bytesPerPixel) + { + case 1: + linesProcessed = updateLine8to8(this, srcPointer, (unsigned char *) destPointer, rects[i].w, rects[i].h, height); + break; + case 2: +#pragma warning(disable: 4133) + linesProcessed = updateLine16to16(this, (PIXEL*) srcPointer, destPointer, rects[i].w, rects[i].h, height); + break; + } + height -= linesProcessed; + destPointer += gapi->dstLineStep * linesProcessed; + srcPointer += SDL_VideoSurface->pitch * linesProcessed; // pitch in bytes + } +// fprintf(stderr, "End of rect\n"); +// fflush(stderr); + } +} + + +static void GAPI_UpdateRects(_THIS, int numrects, SDL_Rect *rects) +{ + if( gapi->needUpdate ) + gapi->videoMem = gapi->gxFunc.GXBeginDraw(); + + if( gapi->gxProperties.cBPP < 8 ) + GAPI_UpdateRectsMono(this, numrects, rects); + else + GAPI_UpdateRectsColor(this, numrects, rects); + + if( gapi->needUpdate ) + gapi->gxFunc.GXEndDraw(); +} + +static void FlushMessageQueue() +{ + MSG msg; + while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) { + if ( msg.message == WM_QUIT ) break; + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } +} + + +/* Note: If we are terminated, this could be called in the middle of + another SDL video routine -- notably UpdateRects. +*/ +void GAPI_VideoQuit(_THIS) +{ + int i, j; + /* Destroy the window and everything associated with it */ + if ( SDL_Window ) + { + if ((g_hGapiLib != 0) && this && this->hidden && this->hidden->gxFunc.GXCloseDisplay && !this->hidden->useVga) + this->hidden->gxFunc.GXCloseDisplay(); + + if (this->screen->pixels != NULL) + { + free(this->screen->pixels); + this->screen->pixels = NULL; + } + if ( screen_icn ) { + DestroyIcon(screen_icn); + screen_icn = NULL; + } + + DIB_DestroyWindow(this); + SDL_UnregisterApp(); + FlushMessageQueue(); + + SDL_Window = NULL; +#if defined(_WIN32_WCE) + +// Unload wince aygshell library to prevent leak + if( aygshell ) + { + FreeLibrary(aygshell); + aygshell = NULL; + } +#endif + + /* Free video mode lists */ + for ( i=0; iSDL_modelist[i] != NULL ) { + for ( j=0; gapi->SDL_modelist[i][j]; ++j ) + free(gapi->SDL_modelist[i][j]); + free(gapi->SDL_modelist[i]); + gapi->SDL_modelist[i] = NULL; + } + } + + } + +} + +static void GAPI_RealizePalette(_THIS) +{ + OutputDebugString(TEXT("GAPI_RealizePalette NOT IMPLEMENTED !\r\n")); +} + +static void GAPI_PaletteChanged(_THIS, HWND window) +{ + OutputDebugString(TEXT("GAPI_PaletteChanged NOT IMPLEMENTED !\r\n")); +} + +/* Exported for the windows message loop only */ +static void GAPI_WinPAINT(_THIS, HDC hdc) +{ + OutputDebugString(TEXT("GAPI_WinPAINT NOT IMPLEMENTED !\r\n")); +} + +int GAPI_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) +{ + GAPI_CreatePalette(ncolors, colors); + return 1; +} \ No newline at end of file diff --git a/src/video/gapi/SDL_gapivideo.h b/src/video/gapi/SDL_gapivideo.h new file mode 100644 index 000000000..8d9b202ac --- /dev/null +++ b/src/video/gapi/SDL_gapivideo.h @@ -0,0 +1,164 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2004 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id$"; +#endif + +#ifndef _SDL_gapivideo_h +#define _SDL_gapivideo_h + +#include "SDL_mouse.h" +#include "SDL_sysvideo.h" +#include "SDL_mutex.h" + +/* From gx.h, since it's not really C compliant */ + +struct GXDisplayProperties { + DWORD cxWidth; + DWORD cyHeight; // notice lack of 'th' in the word height. + long cbxPitch; // number of bytes to move right one x pixel - can be negative. + long cbyPitch; // number of bytes to move down one y pixel - can be negative. + long cBPP; // # of bits in each pixel + DWORD ffFormat; // format flags. +}; + +struct GXKeyList { + short vkUp; // key for up + POINT ptUp; // x,y position of key/button. Not on screen but in screen coordinates. + short vkDown; + POINT ptDown; + short vkLeft; + POINT ptLeft; + short vkRight; + POINT ptRight; + short vkA; + POINT ptA; + short vkB; + POINT ptB; + short vkC; + POINT ptC; + short vkStart; + POINT ptStart; +}; + +typedef int (*PFNGXOpenDisplay)(HWND hWnd, DWORD dwFlags); +typedef int (*PFNGXCloseDisplay)(); +typedef void* (*PFNGXBeginDraw)(); +typedef int (*PFNGXEndDraw)(); +typedef int (*PFNGXOpenInput)(); +typedef int (*PFNGXCloseInput)(); +typedef struct GXDisplayProperties (*PFNGXGetDisplayProperties)(); +typedef struct GXKeyList (*PFNGXGetDefaultKeys)(int iOptions); +typedef int (*PFNGXSuspend)(); +typedef int (*PFNGXResume)(); +typedef int (*PFNGXSetViewport)( DWORD dwTop, DWORD dwHeight, DWORD dwReserved1, DWORD dwReserved2 ); +typedef BOOL (*PFNGXIsDisplayDRAMBuffer)(); + +struct GapiFunc +{ + PFNGXOpenDisplay GXOpenDisplay; + PFNGXCloseDisplay GXCloseDisplay; + PFNGXBeginDraw GXBeginDraw; + PFNGXEndDraw GXEndDraw; + PFNGXOpenInput GXOpenInput; + PFNGXCloseInput GXCloseInput; + PFNGXGetDisplayProperties GXGetDisplayProperties; + PFNGXGetDefaultKeys GXGetDefaultKeys; + PFNGXSuspend GXSuspend; + PFNGXResume GXResume; + PFNGXSetViewport GXSetViewport; + PFNGXIsDisplayDRAMBuffer GXIsDisplayDRAMBuffer; +}; + +#define kfLandscape 0x8 // Screen is rotated 270 degrees +#define kfPalette 0x10 // Pixel values are indexes into a palette +#define kfDirect 0x20 // Pixel values contain actual level information +#define kfDirect555 0x40 // 5 bits each for red, green and blue values in a pixel. +#define kfDirect565 0x80 // 5 red bits, 6 green bits and 5 blue bits per pixel +#define kfDirect888 0x100 // 8 bits each for red, green and blue values in a pixel. +#define kfDirect444 0x200 // 4 red, 4 green, 4 blue +#define kfDirectInverted 0x400 + +#define GX_FULLSCREEN 0x01 // for OpenDisplay() +#define GX_NORMALKEYS 0x02 +#define GX_LANDSCAPEKEYS 0x03 + +typedef enum +{ + SDL_ORIENTATION_UP, + SDL_ORIENTATION_DOWN, + SDL_ORIENTATION_LEFT, + SDL_ORIENTATION_RIGHT +} SDL_ScreenOrientation; + +/* GAPI video mode */ +typedef enum { + GAPI_NONE = 0, + GAPI_DIRECT_565, + GAPI_DIRECT_555, + GAPI_MONO, + GAPI_PALETTE +} GAPIVideoMode; + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_VideoDevice *this + +typedef unsigned short PIXEL; + +/* Private display data + begin with DIB private structure to allow DIB events code sharing +*/ +struct SDL_PrivateVideoData { + HBITMAP screen_bmp; + HPALETTE screen_pal; + +#define NUM_MODELISTS 4 /* 8, 16, 24, and 32 bits-per-pixel */ + int SDL_nummodes[NUM_MODELISTS]; + SDL_Rect **SDL_modelist[NUM_MODELISTS]; + enum SDL_ScreenOrientation userOrientation; + int invert; + char hiresFix; // using hires mode without defining hires resource +// -------------- + int w, h; + enum SDL_ScreenOrientation gapiOrientation; + + void *buffer; // may be 8, 16, 24, 32 bpp + PIXEL *videoMem; + BOOL needUpdate; + struct GXKeyList keyList; + struct GapiFunc gxFunc; + struct GXDisplayProperties gxProperties; + enum GAPIVideoMode videoMode; + int colorscale; + int dstLineStep; // in bytes + int dstPixelStep; // in bytes + int startOffset; // in bytes + int useVga; +}; + + +#define gapiBuffer this->hidden->buffer +#define gapi this->hidden + +#endif /* _SDL_gapivideo_h */ diff --git a/src/video/wincommon/SDL_lowvideo.h b/src/video/wincommon/SDL_lowvideo.h index adcbc4155..b60385d36 100644 --- a/src/video/wincommon/SDL_lowvideo.h +++ b/src/video/wincommon/SDL_lowvideo.h @@ -40,7 +40,8 @@ static char rcsid = SDL_VideoSurface && \ ((SDL_VideoSurface->flags & SDL_FULLSCREEN) == SDL_FULLSCREEN) && \ (((SDL_VideoSurface->flags & SDL_OPENGL ) == SDL_OPENGL ) || \ - (strcmp(this->name, "windib") == 0)) \ + ((strcmp(this->name, "windib") == 0) || \ + (strcmp(this->name, "gapi") == 0))) \ ) #define DDRAW_FULLSCREEN() \ ( \ diff --git a/src/video/wincommon/SDL_sysevents.c b/src/video/wincommon/SDL_sysevents.c index 16dcfb21a..ad962566d 100644 --- a/src/video/wincommon/SDL_sysevents.c +++ b/src/video/wincommon/SDL_sysevents.c @@ -47,6 +47,7 @@ static char rcsid = #endif #ifdef _WIN32_WCE +#include "SDL_gapivideo.h" #define NO_GETKEYBOARDSTATE #define NO_CHANGEDISPLAYSETTINGS #endif @@ -101,6 +102,38 @@ static void LoadAygshell(void) } } +/* for gapi landscape mode */ +static void GapiTransform(SDL_ScreenOrientation rotate, char hires, Sint16 *x, Sint16 *y) { + Sint16 rotatedX; + Sint16 rotatedY; + + if (hires) { + *x = *x * 2; + *y = *y * 2; + } + + switch(rotate) { + case SDL_ORIENTATION_UP: + break; + case SDL_ORIENTATION_RIGHT: + if (!SDL_VideoSurface) + break; + rotatedX = SDL_VideoSurface->w - *y; + rotatedY = *x; + *x = rotatedX; + *y = rotatedY; + break; + case SDL_ORIENTATION_LEFT: + if (!SDL_VideoSurface) + break; + rotatedX = *y; + rotatedY = SDL_VideoSurface->h - *x; + *x = rotatedX; + *y = rotatedY; + break; + } +} + #endif static void SDL_RestoreGameMode(void) @@ -319,6 +352,10 @@ LONG CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) posted = SDL_PrivateMouseMotion(0, 1, x, y); } } else { +#ifdef _WIN32_WCE + if (SDL_VideoSurface) + GapiTransform(this->hidden->userOrientation, this->hidden->hiresFix, &x, &y); +#endif posted = SDL_PrivateMouseMotion(0, 0, x, y); } } @@ -407,6 +444,10 @@ LONG CALLBACK WinMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } else { x = (Sint16)LOWORD(lParam); y = (Sint16)HIWORD(lParam); +#ifdef _WIN32_WCE + if (SDL_VideoSurface) + GapiTransform(this->hidden->userOrientation, this->hidden->hiresFix, &x, &y); +#endif } posted = SDL_PrivateMouseButton( state, button, x, y); diff --git a/src/video/wincommon/SDL_sysmouse.c b/src/video/wincommon/SDL_sysmouse.c index ddb8def94..2aa4b1b6d 100644 --- a/src/video/wincommon/SDL_sysmouse.c +++ b/src/video/wincommon/SDL_sysmouse.c @@ -251,6 +251,7 @@ void WIN_UpdateMouse(_THIS) /* Check to see if we need to enter or leave mouse relative mode */ void WIN_CheckMouseMode(_THIS) { +#ifndef _WIN32_WCE /* If the mouse is hidden and input is grabbed, we use relative mode */ if ( !(SDL_cursorstate & CURSOR_VISIBLE) && (this->input_grab != SDL_GRAB_OFF) ) { @@ -258,4 +259,7 @@ void WIN_CheckMouseMode(_THIS) } else { mouse_relative = 0; } +#else + mouse_relative = 0; +#endif } diff --git a/src/video/windib/SDL_dibevents.c b/src/video/windib/SDL_dibevents.c index a7c3f3622..0f58c18fc 100644 --- a/src/video/windib/SDL_dibevents.c +++ b/src/video/windib/SDL_dibevents.c @@ -59,6 +59,31 @@ static BOOL prev_shiftstates[2]; and give him a chance to handle some messages. */ static WNDPROC userWindowProc = NULL; + +#ifdef _WIN32_WCE + +WPARAM rotateKey(WPARAM key,SDL_ScreenOrientation direction) +{ + if (direction != SDL_ORIENTATION_LEFT) + return key; + + switch (key) { + case 0x26: /* up */ + return 0x27; + case 0x27: /* right */ + return 0x28; + case 0x28: /* down */ + return 0x25; + case 0x25: /* left */ + return 0x26; + } + + return key; +} + +#endif + + /* The main Win32 event handler */ LONG DIB_HandleMessage(_THIS, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -70,6 +95,15 @@ LONG case WM_KEYDOWN: { SDL_keysym keysym; +#ifdef _WIN32_WCE + // Drop GAPI artefacts + if (wParam == 0x84 || wParam == 0x5B) + return 0; + + // Rotate key if necessary + if (this->hidden->orientation != SDL_ORIENTATION_UP) + wParam = rotateKey(wParam, this->hidden->orientation); +#endif /* Ignore repeated keys */ if ( lParam&REPEATED_KEYMASK ) { return(0); @@ -127,6 +161,16 @@ LONG case WM_KEYUP: { SDL_keysym keysym; +#ifdef _WIN32_WCE + // Drop GAPI artefacts + if (wParam == 0x84 || wParam == 0x5B) + return 0; + + // Rotate key if necessary + if (this->hidden->orientation != SDL_ORIENTATION_UP) + wParam = rotateKey(wParam, this->hidden->orientation); +#endif + switch (wParam) { case VK_CONTROL: if ( lParam&EXTENDED_KEYMASK ) diff --git a/src/video/windib/SDL_dibvideo.c b/src/video/windib/SDL_dibvideo.c index 0945f667c..8322f93e2 100644 --- a/src/video/windib/SDL_dibvideo.c +++ b/src/video/windib/SDL_dibvideo.c @@ -768,14 +768,15 @@ static void DIB_NormalUpdate(_THIS, int numrects, SDL_Rect *rects) ReleaseDC(SDL_Window, hdc); } + int DIB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) { RGBQUAD *pal; int i; -#ifndef _WIN32_WCE - HDC hdc, mdc; -#else +#if (_WIN32_WCE < 400 ) HDC hdc; +#else + HDC hdc, mdc; #endif /* Update the display palette */ @@ -805,7 +806,7 @@ int DIB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) } /* Set the DIB palette and update the display */ -#ifndef _WIN32_WCE +#if ( _WIN32_WCE >= 400 ) mdc = CreateCompatibleDC(hdc); SelectObject(mdc, screen_bmp); SetDIBColorTable(mdc, firstcolor, ncolors, pal); @@ -817,6 +818,7 @@ int DIB_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors) return(1); } + static void DIB_CheckGamma(_THIS) { #ifndef NO_GAMMA_SUPPORT diff --git a/src/video/windib/SDL_dibvideo.h b/src/video/windib/SDL_dibvideo.h index f12142461..1c085241b 100644 --- a/src/video/windib/SDL_dibvideo.h +++ b/src/video/windib/SDL_dibvideo.h @@ -30,6 +30,15 @@ static char rcsid = #include +/* for PDA */ +typedef enum +{ + SDL_ORIENTATION_UP, + SDL_ORIENTATION_DOWN, + SDL_ORIENTATION_LEFT, + SDL_ORIENTATION_RIGHT +} SDL_ScreenOrientation; + /* Private display data */ struct SDL_PrivateVideoData { HBITMAP screen_bmp; @@ -38,6 +47,10 @@ struct SDL_PrivateVideoData { #define NUM_MODELISTS 4 /* 8, 16, 24, and 32 bits-per-pixel */ int SDL_nummodes[NUM_MODELISTS]; SDL_Rect **SDL_modelist[NUM_MODELISTS]; + + SDL_ScreenOrientation orientation; + int invert; + char hiresFix; // using hires mode without defining hires resource }; /* Old variable names */ #define screen_bmp (this->hidden->screen_bmp)