From f2d9ddec98b17a519bea7781b755df92c1d77a53 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. --HG-- extra : convert_revision : svn%3Ac70aab31-4412-0410-b14c-859654838e24/trunk%401254 --- 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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 b*bnDa%5+bGo?*`@7ytw9bO7L=zi0bDCJPIt delta 22726 zcmZs?b95kI&;=OVwr$&2vzNx^=7m zdR<-b^-Mq5$vh~MvK%-B9LT>b#7vMq0f~rc9aliSxS3yE%>x4D1l$zl|7JM;W5|** zHva!GFhIaSU_ijl1leQnWYyKgKtMY4;6X4zFn=*+V*{`t;72$D;{WddOZIQ}+xLyp z<}0B4JLgvr^6`7!@uCq}8VXz6CS7m5(N%n1(7vg?0;ZWQq`*zkU+6gRv-c~FW>3wO zP2@JSrgq^D^BI2%iicOfm9yf@hv%nxtID}6M6aE&;@x{TORgaE@@2f}v%jiMD>eYK zti{K+fvAZlpM6u%yw|Bo7y3nYwuVquNKe)hzVMlb=;?wU_sP}A)!(nXJEbqCuDv&s z&Q>7=Wa4P;zefkdUut}p5Bv{C)&*on*xy9q9^CL=2^D2i$c%QF# zO8Wy#A2U~#Ko(*0a(8q4VfFk@HxWRTtf(|Stz_MkM2EcPRl214HGB3+<7=t+c>WRA zG(Y^Yk$ozN{^jv?XJyyX{v3C{5h$D^^z!Ff{`#7+ZzuOlU;jO|BeQ`z7baI1Tkys2 zr}XtfgQj6%j}PPXL>{~KN?2F-Zt~CI{&g$?&B*%Qay0Iv3y*@aw^@a4nmIFT>Q0t?~Oj-BFC2o_X3KPOey~7NvbJ-M~*dE8g(x zSYmKH6Arw}r~AUI@H}-cSR1%891w&r)KEE~Ip zgPFHZ+AWz*fcJ>;3==zlFV<)2z++auacZD$LJWek#6Gq6)?`R=eSaZ=iIz@QXTPF5zl@i}W_o-FB zH7>IVIX=EDXj%1aU2bJVehfq~>%iIv+OwZ|GM&hO&2 zDh|0zVJp9>A1jHOa@0Aga)rO#optG|KSp-MsNYkY=C^Y)lmvWl#t4xgXX0cHp!$YR zZ}uiL1&MR=99ao{?+>X{eee(+qz0m7D-E>0{gj{6noi89w~S`5Ag0uPab%c<1m2vT zRB5X}ewzLKas-|pruSa^md!nVy`TP`KeR}999|oQ)p9=>^lY!M3cTjF2@Anf|Gma9 zVDB2Z{M`7c-rtIO$lluGKF`cdzJ0xXT%LO!9zKtW`MdRZYar<3h5yI-N9C@Q40Gup zGBi19CLFc&BFquhM4q;@$Jf50`vrG_-t_z_*pq>=5dcVPdF*E%CGLTG>U(bZkTs*5 zIBgr#FgICs_*zGDScGxdv{eH9mi{(*Yjn+5*5iFPH#_yz*SE2L?r5Jb8EQJbKilp5 zIq&wAd@**xwGVs_IM`q(yT3JVpAq@h9<`g0UPO7z#(0)(og- z;E^Ge&RDT5%qqMWa+!vO_2q%jPC>|q+oG)B!eNnoFAs9huer>LId_NqV|%6JbNjyJ zJ%Y$>Y^-b8(mT^aArdgi?>E)=XSt>OM)olPj}X5$bJr`zrw?EEml5EB=fzTsr?el3 z2z)Gju)MFBJwsC$HyHrFn4b%0T7^c=z}&J>BhgoI%^~eS2=gW!%5wy4&$; z!&;2FE8;S$S7Y#|{A$rVlq%8hkC8u@;Kw$t-_wMNHbXPmWqi(AC5Q3W&zWM57}&fB zWQT%i9`^zY)y|&9m(@eeOm!{Iw2#YVCThvwn6@*vh_qZ7+^GrJfqinV ze89av7;rb*6n^Weo|U#zw9j#G*K3<7*)8dsoI=+jzhB6a*sV?F+6Q9}C_Yrw4u)Cw zu$KI}Saq|Y{=2NCyMRhlabwL_r<~x@wzj9bvpPCEa+Z9JcN;M~vO0#l179Z$KQnAd zOYyt5oSeSQ0tN)U6+=;dd`gH0kn%E)3vZ#tl%>_BdNd-Yb)!B!->SJ*8&u952&>*3 zd2AS=-kX%tlep5Yerj0%IJAy!V=&ys&}K2Dy#lw~e(_kjslP$DX`B*kgBdY7V}45K z6FlCo_Q#BQ!;-(2d&l0^uBK;glPAXu++l~vp*oJbapes^c|WGbA*#+2;1rf^Bbbjg zRGU!KvVOW)2^DcdYdm~^dMhPvCjO#$KkaYjv$zj65jJ+*vYhA&zv&Nc-eB={KRjJa zPPMT3jlntb-ow?X$i z9C2PlUXq7afMD*FWKNtZKx_3GsVk6+>BYUw&+YlfMWrqi-NEPl6zYaj?S_IXre5V8 zzN^$^UV#Ryh`drZL#cM|DX2pAa{voHqvCCqh@FC|O ze}fQ1d zb^5V*1nF-)5o*EHodP86k?BROH2jTMF&5v95=LVr3mWL7B+Rxv&evsC*R98C?Ku0a z=Z5>1Z|2Uc|2|X$*#j%*8<`F*D?gFrVpov446f`$w6DFs{@NRdA{-sVJ!TK}YYPLViT#dI3c3_+ zmtorVywtItP06Z~wzP2xB1&nY&}#g6SDgfZ*|F-*VNh^mvQ6U9Qc^DQpOAdS17fDd@y}#l*)yeE1u4 zJNNqdsBZSLYw7*7%ek@5snfglx7KzQh7lsYQ&}Y1H~MI@DwCLpeZQZ_ECLvx>(7*L zl=(`;bi_|eK-~05oIxf(?w#Ea-OOJO9sCuF8SDdYj%Ex&YV}d-o%N8C~%L$ueu!17-p#9W{#bg!h1$??)*n^dnLa|0G~ep3CYA0M6Sl+J|L?y0SV`Y}#1%>8qF~ zg7Q{^@_*v?f~pO($B?jXC5cvj=+|wR2TPPxv`}?i59R8f_A+Dk|K1M2&Sh=|J^XFi z7&(7)ps!u|+5AW!R{OKLWdb@R{8SdFERka#E-guk+}bfZ*VPuY6@08~8%?Un6Ny2~vT%Zj-| z37wNQ2CrgsE87^m5q;U;-WATUg_OR4DY-xq>8Q8OcXu{lB_FP@+~d;G8${JG)Ezg` zUUyPd>ce6m{_*rgzesNRW#&hNRHI1yqP-6P1E6>Aan)lAY*%G6{+3ls#;YqCz1y&H zI=*(C5sIR?PF+hD!|D_7MP>45h?(?gga0jmK}8F=JDM7PeZ2had%fHUS~=I*2-8Wv z9UktJdTHeui@y7}%4$2+y-UiH)5g%lH3M^7$>uyYzvY`7Fh*$lLqYVIjZALZDZL5fTy+gb+!aebn6^k+|~cJ5*n3==bQ?+B(>E@p|a3t*r~u z$djbW5@_D?rIp}lLN7cToc&!^dunL{lnX}!Qk1skVZMoK3_|Ra3dqmF>$DCfF9l-5 z>zM!P*<>zsu-IP;!S}#w(ogOztRYVJ)J%2$k!pxp>7k!QQYJ@PLht!tovfd9SE<3p zC%=XirCfUtuZ`QPy0|+dlrphL_pZ+MQ!g7lx#NpNzhiwPH}aJEg34Si4P3=a{jqNzh-cLOiS#SF-@ZK7|1CG87c9pDIkTc}>$fHr+Xp zHTv$TKX5($LOTW3s}UenXnFi49bb#y^J+~4$W5znKG=v-x9zRnD2=St4qF>53^}i# zAJjcvEWZAFz3fiyy*}Q|Wj=glbC2Y6fA{Nhs_SZN>QbSVH`G?6WUJ2T1n|meVZ;{7 zl#fhd)~~%5+jLc88cHEv$8Etr7IO5G*Ucbf^uGHiXPBp?KvrV4m1uD)T zp0rdL_SJZFXOO&CSD<~VE9lqh6xT{Auv=%Wp`VnTWSgliG0=y_SX=N)jnewdVscE- zMX15+LMp7tb?i(DbY5=M0IIfnf6Nl>2DkS2`=OVo;#8T!8p#|B``ttw*tRYSTC zH{uh*IhEd66qg?$V*JBp3keN`z301IxVA!O!rzz4g!gdiD2< zWu?s>>-Qb1mWUfq&0l!=PvCSCG@cxug-um*zrs1ZEf?daJq5Jg2pek8^0XI3_2-NR zLL!mi9teV$2)F>vd|Gp87Ty^6uM+=x-G|i z<#MrfRilJ?M0K;sbsg6Sy*RC`dUq!(@J`K~o5t%B@M5mgSh>;l{rN>qUYHu~t&s>z zuLF}VbO!4|A2zGO_dkB1^M~ZiSLgcs?qBbsoVGbkY6Lx4zpSn0Bb&;S^vdsR=FGw8<(9S^Dn2vRLcFJo zZ6j;Hdx|erVn7zsoSl4>G@ttRaJR$OUd6n|+OGj>h%COOdJ_kuUB4Ej~$NIzCV5MRyrnL9gnuzxe0e}-xpG6@-?o{Yk|{4aj_fExY(lH?_8&!UR_)+ zpEobqJLdyl{C?Jd@D-R~avw~tJ^E&ZeczMr2Ze!w0D&#C_m_fgoM!u=ll*!MY+mOV z76h*k^Vv9pY##3fa?zfhD1COfW3sUR7s``uDI`JOpoAddz_1DZuAMAl9*=OLM)c`l zbKqTbBzy(amN=WzvF|pEcv49BS6n5{(CE7pkm^ZNM2(GV^OBubvMr%n zlWAM(v{@$ZO~Z!*HP)`-Y#=KUW`Z&S{xtgVWYqdPjoI;g2F)RT(W>b1kgh;=C@ai! z6X;6AWL$Nl^4+90SmmhisW&6i)7(cpl+$A&)JJXT!o;Mi7p_@q^>PIm-{(_CUtoy$ zOA8O8vvW* za&E6PacAfm!RmA`ln89YUsXke@+XgC8Ux`;Cv#7CW_tcBZ(s{AaP zcQh8BF+P!H80VvG(jN#CUeG|lRg>z9lQ1{`JYw};Qu#fQ7=c+4c3ajTvYI&Zxbfyt zqhwbfu~n(T)6~+#QH!JUFajBwLGZD-O>hXV|lDHyJ#X>KYh(lF>I{$Q3O#T*e%{?>#q(OeZEOC$?ZjCf`U(MNHH$4xfmcN6IlN z2oH`XCI&yR^dGo_LK73?M~gN5M_Yg+>KE5ho!lkUZ6t3&qUx96PlWqVAQO$KU)*M1 zhLqg2kpcj)=)@%H(ZWc{BpOLf@bJXM`BjJh<4F?{^^5aIg7-?KS&FMcc)%bt(m;bE z<{MqQv5=5bOm=~cVk2jU&SP+N%u~Xa(0`TWui-0-L#n5trJ>!{GJwMyEy4l}aM8TP2iWCRI8Co@Jl@XF>&u&Pd6VbY@H+ZS*fWm;k+X zQUQlr5{L?(e6@7iFxnvMVT08)(u_anlSS_Vhr-Ip8DGUWD|7p}#VCik45ghUPUdb} z^#1p3qB3MJI;8Kz`y9BoQ5>m=xNfVPKek%96XUrT2Q+uF3OKO*f@u(?;z;COSW!lS z`2$6v`AK`>cHGSjJBRPQYv|Gn!AkNAy7Kwi-urVECq`=gst$dIwWlM- znl{sPbw0w?#-+`_F4W?*^@*6ysgDOP1ddCznOuG~cFGMSPlH>GwObA=O+)Tk+oNb` z2pg|y|450O0^Q0Y%7}wHG+{-E;|jV0>YNzb_O9xbyesz3ON0m5e4OpY^3*tSeiTjrHas{{lB2$yK{QKjOiu9w)^UPQ&a?`m&vWg9r*Ot41bc8XH9tyWvm ze??U@?k6~Ng$b7MWAYfg=?KnjLoa>8m-=!79O;q~Osz)M{0Ju?nUFAo11s}Lq)?!a zrbHM>-X}`A@41eUUk_!4ZHW(ni7+2CTjKkPWWQCR(NqIs;YnhI$;<#vk>^y032$2x zg2~JvO;MaQi(lThBnXqK|5;q5y`xDeCbNTAMQPGZu)S?bASRRjSA|bfEf%~G{+DL) z%ZuTEfsTRKB=P-3!hdlVHY+8C(9I$-fio25sjFf*T5U*osX|4BYmDfzG57Of zXyhG>Ms?v#W-!!&a=3oaq$AJbS`0xHevaiJqFNAnG`-Bjc;ci^@jP}s*okt)kq4C* zY%qmu9QaZ$4XKhSY56kDTK7wLz{WXB@0OpO|Ec0JS)FS!G>Q!0yaF^y_K6&LbgZif zs0_7NF-Rf5o^Kt$t%&;%xogQNo0x=hN(H2s#FOnqWZ=1%UsJ@CS-7rF>t((@d0c~X0-D0A$$ya4602GRq^6 zUy=itQwqqmmUAPxKn zcs{nV6HtViK`qST+g!w5U1Zi72p>{%)v?IXL=!=ypk*WpN7?v=Mn|QxnAIkA*|Pg@ zL4G~Q#*^G&3-G;CfIi>ctc)Bv2d)wA2rM~WOrBnFF@b1Q*?qrOLaL%I?fg9Ay@aus zO6sT_GX$FF=^!~78)V(jcIMv=3e)W`;60YkCJ|Q?KOg$9F=4<&CHDBP!TlJJlmZF;ev9M8Lh_P81Mc9~ zC!rgnNTUH;{&=+gw0qF+ngl{{{O{&r0=ihCy(lUkEbrHu#0`#?&!EE1T8trtRs&o4 zFc_$zPvR)%IFGt{(i!)NqkRWtvKj%z*CtM0NEe3IsKw-c$j|jWA8_u^Q%FYbxCj0O ztB7U!sfuaxh~THeQQvQlM$v_d0VCB4v_Gh^#WdaYiY8fw78maX8YagkgE4xjCQkF1 zzmt{~nxfMzP$B0O_p`odCm(nk3%wLsScl-hdUn;(@d2_6HHHWHZglp9!x&s(*umSC)C-9%|>j@bTK}!nzu18NiZbKK0eF zX|o21-SENZD7mZO@&r+Ks0h>+htj@pGJA`esVR&rDNFD-)xfN+S%PKk#!<0Q=E2Ye z((Dj%iXi$ZgF6|4Hi9r-OTnkX&Vtsu#Nfyq^Kv01n2XocK3?m-pV+c9=`rlkQcjR*{P18#qI5%b~QCg2wAe_>90hbeO{aY4GSz zKG@p2IX3%K<`QIuj_Gm1<-nSl)?c;Fa~-Ah~8L` z=uf^2<9hD{*kQ~14kH(24l10c1si8fHtwF=c}*hjj@pdWCJ?sp7)t` zu;t8gHHHi;n(!KC4mDh}4Wm86XV+m~da_QchQg8vI~88xqnvd4IKH(B-W6q5a7U&N z2S!ol82!?PbHy6_r%O|fnQ*m-`3RiRYMwG4>*$`QpftYpT?m^_3#YCXajo%&YGm81VjV- zU^h562vP7`CDGY@nimjJ^@EPBt2^$4b)u#}!=nX5heIA|>xSZ2LD5XY@*NYlV2$=L zG!dfU2aFk5378t$Rff<8jIlMNDMJS&59?af091_7{-Hb;E*voI)wo&mx`g&0DkyH1 zij?(!k3I7F*7p0hh_IF8dBGxCyqy^!A?S$gRzq`=tU&-${11zaHA!z z=dHrnLU4iM2Ve(;^okjh&a1TyNg0yY0jR4moiGd#SYYr8?LzG4VC(_uz49>dh9(p& zXgIJlF~PDSP6&{pVB!J!@kx#&@`h9_=sB6No-scEMF~hp;UfJPg$0elaJ)mnkhTs( z6OJLIDhA3Oj6WcI_jjQb5wtoOeL%9z77P@AKuoXH^$_W4V^DhUKP>>gUtJ0ZHX1@I z@Cxx;EzSRm?)U#Enu8Dft%d!6Qjh74`j>hgiq-1_q3L2BvoCcIO|o(6erKc}8fAL( zU!8{JT>=d&&awq-mIC3BONr4c^RX)PiVm`FjH-cDWBBK+8Af>sDlqhf24Na0F!F!| zz;Q(;&9aw|6Y{rupW*%zIVCuUQdglMZd_5ey~ZZbe7TbsSn0kd96oXzht!A)gSWr& z8gz&D-9XJU!HBB<^I}^7UrcG$b(YD%eebl_ousPESCoXRM_;uHUu|Z=pvFk(X}7xi zhw?``z?bS9%u3a$yyOZG$Bf4=D+aDKdIvkRXaUtv zGoh;~J0u}tDyw5hk1gc0DdE~c`&kFh94?kpX~-2N3?45PgqRucg6!U4yPm8TPZZsD zU-E+#b8@)y;?*@V%XJ;IaE766e6fY?NpaItcjyG2jK5MvDehlrW5pXNlJ=+WfM$YD z2BS_r?mF1f7go(4rJFEYN%_Xl@d= zzm^d*S|M^W)*qwRFBBL>TC{ng1LFxad8__7|5M2jcwhWg=*k=*8f-M%oA4CMx_?3~8#7hB3(24--=W4u3 zTpFmzd^LZF|ECfiMX-Gye$F}6Z>Wp_^D=A*aaLu7T+E;suqF}4AMkHWY;+ebn)&~| z$&FMqJ|k6PPg4lw=T);4gIV>5Yr+#aP9wF3ho_}a@P+aBoZnr8SL+v0Q_detcdmL7zSo&!oczw!;<`gOV~|-|JSv!OqxU5 zxEA5J&6u>I2@i?SBToS-P(FuIz6FOAtcL*5tjw@U#^BLh2P?+1lMQTCM8Dgz=~+hj zhUbxDX)zkbvc(@j!txpanyM%u#}W&S)dUOY`9ctI?gRf@H=qGwH{7HUV~f%l1u>hJ^!$NFMQJ?fu?A#4g%ET>^IdwoO|veeG&L%S4J681G#9(PGvdWAk2IA zLG51nCJZ4>QDS}>P+HTmXyw39Gcs9VwmbUKOwK&;XdYm&AD%pjGiJ8Zg{0mM7Wjpw zK3hjan*Fn6M6GL~JX8M6w|0(l(zT=d$(t;;n9N_*xY-T1(LWw&pu;)H#(@Llr8WW+ zsez3P7d}1^0~zH^K)yN7<$#9rq%I5aM;tTEiwFK-;vD22KtOp?V+8nPj`0J%hN1-s zBnLT3U=Y02{vaZsV_1gqKaIg*4ss^(U2u%Qaa)tlfp#CbU;%z_6~zi;wH;LvA-a#!q>_KqS?*FZtYxZBXA z#G^kTi~YqvK7JE{Fs}R~J%kz1VM?)`7)r2H^-=16)%`7&`-@zN!GjRq^cMzr{M*DT z8}~B99f5VT2NLXCuxC#cWftlsffla`%%fJ7_fC;sginM}O&X&;ezuQ_M`jTleF^r?C#e;nQk_Vo#MD`D zYo!TU8C?p9)5)0eQbc5;O(P_1sMMK+(Q-oAMH*t55P!b)2~DLCx^M3hmpjVSpi*Wm^5Z9`wWpQPu2NJUWp;CCH_SD zR_(o|q^I7@lIniGtck~7zJ#NVP(63?69zIL0h`8ORhcd-BkQkz7w<_r1L2Jk>UUz| zWcg_1eztO#23@DMz`)p1-teqG=`=ds$vK`N`88V8ar-r(F~f$d`UpYjHYp7Zc?>n$ zv>YUaF#bE&wCGj)QQ}WV*bjy?4}y78l~Eld=!3+^A9~+&E7+f5#0esK@qThh+c40d z9Alv-n|{qL+Ut=bwtio*VXb4W7t<%^)LHP2Q9Ktq$<(D1)}yExo+?}VU{b9(_fvKl z<~BOge=PV7{C*SnTRJ`EE{zXs=XQ$s*0P#o-klynhBD8ygb=$)t%Hv1i^SZ=7FuUKBo1JsH>TJcUxTL?d>`*G3 z2vQT-b~~8!$aPE&5en$oXf6Ev>FIXS^MWYIca>cXNbd-S5b8ZmjYb9f^ljN~&beR7 z`(WBVVg78~qFxMvh4H^+IXP*wSk{AgCQpVVM~lghjnc?^yHkYh4KuElLOp*;+ppag+F70coH8Xr5%-cyWw-=&Elz; zb87e*AWw+X`one&ZSR|$L~9XJqNaVWZ+i9meTT0VcXZ0Ci%geo*WkFt+0gICbLLZ_ zy#-&$61U6muVLkS(YEI0^}l*4hW>Y$ol|gr%9!OC|L8fBA{Tifi;4^hQrI7Ndz;#A)nqCoXqE@B2T%}REW;MxW- zRwPgE!s)6o2SDi%Qh8nkA3JYREk*cgC2o7Mu z!#vG{cb#K2gSOa>)<+b;0XK1tJnqAOF3@*FZtlgZcJ}=j1z3Ff7p78#Egv9g$G48|+88)KPo@kOA5ousHlD~q>` z8V450M$$~@>`>&%aesJg+HvDbbQ^)_U) zdACIcFoW!Wt6f>Y;LQE(vYqi}Sx3l1(vUxxA&xzZk($LapxnD%%ZY$1g7aZ8FW{jF zplH~1a+0?7IYtzp1KP1_b7 zA!5T9N{wI;63pZWP&>S>VQGzo%Bq zD>6l%rH|m0=W>ui77|uAkC)n;0^1-VFR+dN zXVxBXwY!y8EY@AC>16PjJcc1B!G6R;go;Brgp(kA3xoE%0kPQ?gdf)g_aKo4@rU%| zjo?H8`w>qeDDsb@)Z&jolnC0f%Zq7BOQqb$PiKhkh-YTSS=T4cWr&*wkXc{w_dK&u1=A~_aF703vYL^G@-$1@uK!7=86tP(X_#xh z#lt(BX~BW`_0?XS!S^z)CY#NHJp+(E4VA$l9flg_hnOVT2jgYt;ERFUxR`;@@oJRe zt!Y!gx#8Aj;RQ9d#`CYOZOW7(9?`th)gEA5AzK>gtw1{rHQe3Uy2Gf=J5wobOeep6 zp6AF6t2$NZ9R+?c>vDi%OSb7qH6Yx*+ZGIB)~~Ii@6eNaTJU zipH0;8RLx3YhJ?^aA0f%k|2gmXC0?4<=V}Es%pJ7G9`&$dEg)yNu zr~#=}O9go%)8OcoU=A_KGD~R*8u{P|E{Mf`aSuyr0k(f2*kwT6!%~4m;2(_wT|~0X zQiTZ$a1D-KE@}{w8nslQLix|6Dk@oK`JD*QCphl0=%!C<)lv?NLN_?dog$%MBF$0( ziRmA$h(%no%u)f0=Rcp>e?a;JGz|vbj%Ei0c`qMviMg9c=aVcjW$z3JR<`WBfJrQf zoZ8B@#t5PsW*?eKL^aJg7%D(oK*?#BNA{z!h(ft=?aJ-bHkL0tjM_*M3AY;Ieq@`# zW1&V~^N zucIGgy4^qbA6tDJkAP1~2eRnha)aTwhOKe@2|Ej~WwM(&d#dkdx-ahEk(a=Nu(o%_; z((V=Qdk5PKD{Yv~Q9iRHT z=Y}uSFn2`3F64@d5OisGFl*2i)>pUmjLQ2=?$LU&^fvVp6cjdn3+wQOH)V>C)vCdB zAG!@$$~}zl-Z5#bg%iBq8#P~VuzkNcM86J$FQB2 z3ikh<%q;O7Wpf0ag3IWcx})rT=o)b60ui1aS5N9L8IAW%WAawQ8&OR)gsz`ui%bpm za%fsAqRhCFLXh8FX@;H0Pqaayk1B&1Z}?T{E@gl|@4~jv#~?j!DeO(rX-5KgjJp0~ zjlhi$SxH0~e!-S-tY?P1*~jno0$U1ggbaJSdsi&uLr-&)J%O?92@n^Wr3UP1IdVhD zD~k`iYz-A6t%bHHpq7g&p#wd36<;GEU}VzfFMoSrIOm`uZ)%a9qru*U6VlMK$ofmQK)^K*RRvm&cu$6}Lp zh=&}0{kHeLR#>#)%=PRsDgZEh9#uOg3}HdGdYmesrl5ovyn-T~A`}Xq(yw~%EheTa zW9?}pO#>(S_i`3S+^B3S25XP{y|Kf93?U?+tA#T-_j=*=GAJG+QF~hgB~ok->>ez* zWOng2#qZzTjwf=WnCb-niKq&VcCe7gHAc?*&NNa=zv@QEe4h%yWBt>G7Y9zf`U({K z8)^Ar^=2LoMpHLAK1E1T3b%Stf%4+zrzHzU==$anf?vN1R-?`uU3 zY1JTNDDi-UTL&`?oG9bfIHY8L+0xYS(3Ow8Z2}YZZLeV)wna*MgCWsRGKNH)FcPZk zCGHlwBNAPZf>9bkrqbSLtlTp9z(x7Ji<_R!1(N6;O%hk1rlk6fBC-hKR80G?c0#qg zv!9uhw2Cf8V#Du9zRFqT(}m?FzIu?owk zQ9inurkjYw#5$yzMsyXL%hy+~Fj0i1E(YnMgr(*&k`o-j9^1Lw^2zb_`58+0py_hB z6#{$DX33$(`@`_&bHnq--8}wBdowJIY`LqjvFh5WnCvYflW9@ILrBtrxACYR$j|#Y{hce-;ee z`F;Tu8|Bo*ZGB&yAS%>@B^2^nZtm zaRTnQ&$=0|2ZHwADj(E(mb%YqALkXkS4J)?hw0-sGXzh|qLZRX7$K%Mq3ZV03rCm| zqFOUo6*~X@Na4XlxwN*+u#J|9p(hV@Jiz2m8sp}aslrW?JhFW+cGH_h8N0dCq zFZ?9&Bz`^2sC;};IYk-f)spmbTHwA5%%~toiaLqK zeTLfm)!)|W;3ER6;+L2jj5YGDQOmTahijU>ACB^%(@F0l$85JR#mvgGCRVf%Q?a|T zqG~<5>X!VP=^*t7G;wT5O>P|aTy9>f;$QtgLTZ<9Fu$;xu?}sbJ(y{9B*u+RNNq(0 zz%J-r88P&<1pO!NdEgTvw4<2;pQhMakLJ22k{U70b2x*jA&n|}+os&=1~Qa*%!YMI z^$WTmjKSD>a%$*m>VE>4m!ZDpW37Q@7PZMhC~a+%s}!l-YpPP3Ki&&S{=p9CfMw4& zr;1kFHVR2w%TuPezi{t0``H?87CM#kdrjuN=p{v!Dk{rtp8TBd1nNT_FcGz(4-#xT zDmMiN*o;7#d+SOwF*H}_*WoA_PJct9sB?UPLCNvOg9c;%o%Qd_*|s$_k}KqiuPe79 zv|L>fLuR-*J%+|==D>!4t>hyF3zM|N1|7&CiWf2ZCNAq=SISSNxjJ8gPH}OH4~Nyv z!3qg0$5#OsjQLkDpf6G%_^n3&HYt0HoOrEzB5s2lG#R{GDDCe02NGIm9V{rAQ!w&4 zgB2ff9n;m12wrHrT(^|J(cb0+qQ18KdoxcBmZw!nHyKmR$&cV-B9X|dA0`3`NnSWq z4XQCo5~|vL(cyCB+bBj>p%7#&)n$T%#h995tsKTXC@JDN~FkRd6w;V{j{naUe>H*D6wjYD|@=YDFJ2bx5>d1b7+BdjkP9!cax8{GlV7 zL^0~kUQC@lgjpxXNT#|gJ5G`cyY4IhzkTw)d!%2)zsT&(ncy7QKdRG~V2{%^yh-(g z1^+8F81FQQIRTxn(S!rMz!>wMm4qLdS?GJuvuZ2|?JWu7z{7CH|F`f6f&b#n0JRsT z6i+WXTps*^kr}j}$5j?%AC9FRw4BH3mEd)Pz!x-a329rvR)#=Vh}sg>pT|NIt^UkK z7c`#7hNtpQg8&!Qe?sMw$7u;5Y8R$11Wo6$pG5mvV|xW{=W(~=`dK1s2Q8mac@=P4 z{v%vP{UgXnHacKH4~aT8{{B+IUxaF!V*m@7&*PwpQD;Q?0hKIj<|@~p_n(IbE=;zl z#Vqa?n9Bc1Ey0Rg%;LIARq7J_PZnz6DmWNWA;26Bk=8_+`+>1S=Dy;)Pq0k=f|L3X zQ-rGg28K+9KGo^d#S3rA@bri!eh1m`UJ<=HY_(20yO`Tne>N&*cy^3+aAdk(4b92L zsb^QD0g4J65Yw5?S2o%7Z}1&D&f#!C#mrSkMo;Il>z_tP{8>dmp>l1l3@gK{iunN4 zqzTdWDbte;p6;zml?MbsCv`hg$vf}eoD=lct;=PY%%j$J;BO?viw+WoJRveH9Qe zP36LeA1j_26Lq&@XOGt%fA)b$rIQ8l>x(0oJ~GsDpAN*H8>^dJbFQYURvvYWt+0wt{waAOC2QAs6cW!i@qh4o5RbZgdeY2M;d>Y|L{x zl@f)63BJ*=Ua1I?J@aMT!E_=Wqk+|Qqz4+FTN9Ctwr3tfH`g3p2UIQKdi9<=eiH8T z<>*28DjrXR)-I$w^`1L!V>LZR_C1yriicUg9jm_{ zaMN?4qZ)DQOv81z7JK7$Di#jBX1+=!sG`Rx06R90k#wDewh9B2-#QFWDNqc-1tvzL zQRbFl3~vSG5;5n~xY}z`@v%IWNRo4OM23nYIpH zSxgWQL;ZOIjsY@Dxh4&aUcekO1x%6Vh*1Pqi8dg{A4L{dq^atxPX;#GWW*jCuK%C{ z_*R0}Unk|~cf}^$2lKEf%d>|;Eq~C1RQo2{#d{g6zm50xR)DID5UBWZ)V_i&PdVTx z_~PNz${m*cJZ8SYr11pi2~Scx$1Ja{y*6-%_mg+VDb>Zyo6(|qpJ!Aa#r258*a`4aEg!q*7 zCKjem*_>}m_n&9q{2W~GZV7o`ni?OyJ-+gU+Z^k-KHh%Mocc`~?l{`vjG}_wdbuO57Y=1b-;XFX^&%7^k%WF4sU%O8M(rb(# ze)~H5&>xB{&o0pmr>ER5+;7btCZ=`!f7&Lcb@FYOcpc5E0K!BjZf)g|$xHd32 z1e-vDyGxMZ?h+(Hg1fuBz2cs8-nr+#cX#!!>Z-lJZ*})yt5@y%0r+u^?~WT=Y<|)Z z#Cok{>cENzD8<>q?{)X$YbP8=-SSXibLMQnq-HIB+oQknHT3k|#7aAP6zrWCHrTxT zs(P)iCAn)6#`@v1*k)i>k*Pq?#C zKVxj;xHdunXyma-%1+P^Kqq&@K+bT%lcjSU>5e&tgzI2$y@Etd6X+`2}rPHcU5)u+f0Y$YI*XvSunZ!@w)hSuvmZ$GHL=k={Dt#Y6K$bam{ zBYYgdYqZbBwQ#8Dv6n-|u@y8}_*oZYCUwqIWUgimu;CCrr8`j(1kc?K+H*F(F$-*Ht9o}Rb zrr6>PoJ`T9t$=|#ODgRT!I&p;bqi&4BRKBt(kpIb8+fFgI2^Mv7u0zA93;CxzEd_h ze^R-dF9!Mq=GyH!3Z?_@qI(&5R`##E{tgJu(YrUSSC=bh| zKOG%52(+e#ObvYuL{o^s@8@5=c64>#FLo1o&B|U7Hq3f{EevQUk7igXAsS~!@IxMS zo2+(6n{E@hEKXfULy86f*eE|Hf|H!c%#L}8Yg})1RDE>xMxD>e3$CYLsn}KT4BZVc zNjJdoRH0EC`uUTW59`F{NH|t+M1wEvWiAV_Q7v9GZpYLgFZ+P^gy}+@(%9j~#GvjV zWiurZtEq^@J=o{X$F!Q2y!ge?yP(PJB6*HWUmF=ddh&?iU}Ml~=ax3&Hs{Jjtdvu(_W_x%E(;yA}@+ z(_A$E%p>F=d?Q|uh5UuKmCRg|O{2LTb}S@fF!klr1#j~c#?zJkZYnz3>&X=?#41xn z0P(2!{vekgW15VdDoLh1;W=$RfX9Q7zBQ@#>yKBgU(4+zKdJ3+I*)*=BqMwXA9-2E zRfg8`u*vc}>Ty4i6st6sbd%)pv{(n0X`epaI9K7%T{1a+pB-q@^CH~e^yHR8?Tg}) zK8<_j9_cY}3noS1S1Za55jSa^+|}Wr)>`za;AT&-TXcZ3u_9kHHpCaK#56Os-(l8E z92b=pCCH*yw%)k$d^58I$sa_a(vb*e(co|XrfT)v$C&ka0*J{lWd zNrRaJLN1+5D?C}PUMldQny34(EsXbUP7OT#WYpGbaX4LPWg@;ePkB^rtyM*}aqP=f zEBCedBqSf=-z02_Sz5+ljCApDeIq8&8CNc0C@Aq*7aV>OD>W1~PnMpWb^2H}_Iu;< zt8~BB{g-&95e#j2lNOI8Z*EaEfD5QU%Uu!s`{lz5(KU|h`x

F~?wvGd7D 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 T^Ii!DgaaZ$1A#=%?sfkM#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)