From 5b8efd68ba2d8e2c707d5811a5ce7185b2bead76 Mon Sep 17 00:00:00 2001 From: VitoTringolo Date: Sun, 30 Jun 2024 09:50:12 +0200 Subject: [PATCH 01/40] [rmodels] Return true if no need to interpolate to avoid log flooding (#4118) --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index cc6ac823c..5f061e2dd 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5649,7 +5649,7 @@ static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_ } // Constant animation, no need to interpolate - if (FloatEquals(tend, tstart)) return false; + if (FloatEquals(tend, tstart)) return true; float duration = fmaxf((tend - tstart), EPSILON); float t = (time - tstart)/duration; From 6e2661f92d38aa669047bcf63db8c27a1c1f6dec Mon Sep 17 00:00:00 2001 From: Bruno Cabral Date: Sun, 30 Jun 2024 01:33:32 -0700 Subject: [PATCH 02/40] [rtextures] Created `ImageFromChannel()` (#4105) * created ImageFromChannel Adds the possibility to extract a specific channel from an image * naming convention * example window height * removed threshold * removed alpha channel * channel example organization * updated channel example image --- examples/textures/textures_image_channel.c | 106 ++++++++++ examples/textures/textures_image_channel.png | Bin 0 -> 234960 bytes src/raylib.h | 1 + src/rtextures.c | 202 +++++++++++++++++++ 4 files changed, 309 insertions(+) create mode 100644 examples/textures/textures_image_channel.c create mode 100644 examples/textures/textures_image_channel.png diff --git a/examples/textures/textures_image_channel.c b/examples/textures/textures_image_channel.c new file mode 100644 index 000000000..39618c5f4 --- /dev/null +++ b/examples/textures/textures_image_channel.c @@ -0,0 +1,106 @@ +/******************************************************************************************* +* +* raylib [textures] example - Retrive image channel (mask) +* +* NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) +* +* Example originally created with raylib 5.1-dev, last time updated with raylib 5.1-dev +* +* Example contributed by Bruno Cabral (github.com/brccabral) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2024-2024 Bruno Cabral (github.com/brccabral) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - extract channel from image"); + + Image fudesumiImage = LoadImage("resources/fudesumi.png"); + + Image imageAlpha = ImageFromChannel(fudesumiImage, 3); + ImageAlphaMask(&imageAlpha, imageAlpha); + + Image imageRed = ImageFromChannel(fudesumiImage, 0); + ImageAlphaMask(&imageRed, imageAlpha); + + Image imageGreen = ImageFromChannel(fudesumiImage, 1); + ImageAlphaMask(&imageGreen, imageAlpha); + + Image imageBlue = ImageFromChannel(fudesumiImage, 2); + ImageAlphaMask(&imageBlue, imageAlpha); + + Image backgroundImage = GenImageChecked(screenWidth, screenHeight, screenWidth/20, screenHeight/20, ORANGE, YELLOW); + + Texture2D fudesumiTexture = LoadTextureFromImage(fudesumiImage); + Texture2D textureAlpha = LoadTextureFromImage(imageAlpha); + Texture2D textureRed = LoadTextureFromImage(imageRed); + Texture2D textureGreen = LoadTextureFromImage(imageGreen); + Texture2D textureBlue = LoadTextureFromImage(imageBlue); + Texture2D backgroundTexture = LoadTextureFromImage(backgroundImage); + + UnloadImage(fudesumiImage); + UnloadImage(imageAlpha); + UnloadImage(imageRed); + UnloadImage(imageGreen); + UnloadImage(imageBlue); + UnloadImage(backgroundImage); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + + Rectangle fudesumiRec = {0, 0, fudesumiImage.width, fudesumiImage.height}; + + Rectangle fudesumiPos = {50, 10, fudesumiImage.width*0.8f, fudesumiImage.height*0.8f}; + Rectangle redPos = { 410, 10, fudesumiPos.width / 2, fudesumiPos.height / 2 }; + Rectangle greenPos = { 600, 10, fudesumiPos.width / 2, fudesumiPos.height / 2 }; + Rectangle bluePos = { 410, 230, fudesumiPos.width / 2, fudesumiPos.height / 2 }; + Rectangle alphaPos = { 600, 230, fudesumiPos.width / 2, fudesumiPos.height / 2 }; + + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + DrawTexture(backgroundTexture, 0, 0, WHITE); + DrawTexturePro(fudesumiTexture, fudesumiRec, fudesumiPos, (Vector2) {0, 0}, 0, WHITE); + + DrawTexturePro(textureRed, fudesumiRec, redPos, (Vector2) {0, 0}, 0, RED); + DrawTexturePro(textureGreen, fudesumiRec, greenPos, (Vector2) {0, 0}, 0, GREEN); + DrawTexturePro(textureBlue, fudesumiRec, bluePos, (Vector2) {0, 0}, 0, BLUE); + DrawTexturePro(textureAlpha, fudesumiRec, alphaPos, (Vector2) {0, 0}, 0, WHITE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(backgroundTexture); + UnloadTexture(fudesumiTexture); + UnloadTexture(textureRed); + UnloadTexture(textureGreen); + UnloadTexture(textureBlue); + UnloadTexture(textureAlpha); + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/textures/textures_image_channel.png b/examples/textures/textures_image_channel.png new file mode 100644 index 0000000000000000000000000000000000000000..55e1d2eaa8dd2ad1c4d67b7e8f3a4e301317137b GIT binary patch literal 234960 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
$`lHz5Q~nCM#Ju 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

Lg56RW@h#hnzaPQOEDbjq|?c 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

{~ zqealO4L-rM*_8^JRo8h~_(WQ{YPw!5)pVO{rp(%GjRt+_V-6FpiwTo1P#{xxa3 zR@;9J*7e4q7!-wda&!epq00=e3(sEgGCV86~1tr@vXc z`s(sa(OYg!6}|X2D%GA_wK=ku`812Z{jO9O%`e{<=T^!7TFA80SHb7sBv$JYD5Ebkhbz0KI`_wr@g8rJlKf5ij8iq}N*Tr{!XsS;}Ni#NA4%lYP)sop!+l!Bg#9j-QVib7jAGR3*6 z_Ltx*-?zGZ-!$CEk{A1sem5MIu z=}qbL*1q<;wpi}B%MH%EVplC(XaB!xSn=rkuC=f3y+3qpcjD!}yJ}DIyfj-Iu{*D^ zYul@*Z#Cxe^Yl;StNpQ3LTPVgbAwBILX-u|#jrO?^6cCA-hIlmvfDK&P~zI}%2!Jb zzUzLtGhbn6-1_n*(q{L#FZVsXk^eG%*Z0YCYqzQXbzrhsQ4^vim@K-!>6=F2VNS;k zp`RK@jF!o+Hn8EFzvNi)l4~Ltxx#t*UVog`xA6h2L<#xU1m(sEtsRTE||wg z*Q;d9agz%IY;_7-cjw+#Xm0l0w3BIOj@=~~{{);eWUj@ohMe%s?;UcU92 zIc3RDHUGt!s(5?V2fs42=ZZ((+4EHW3Ko4YblQaDrdrT1iDiwi zt0I=ks{DL%LFgM-WHDbHD0^JENj-g=&VZrIc1W@(q7@M%h`7=^{MZQ zHp`ItY7=T==E5A;-zb#8>i2D0YSK)$WvK}gO$8cG#l^-L`-*Ohr7 zIlg>#TFa%ySwa?a95)Ycj`B2E`*vx~=|ne%jm!;)G8~d!5A+!pSMBBcQ`Nz~{_pj` z|Nf`9U9@!7y~qE4p3E|V-P2c{i~rzQYdLE}R%*?BoA}xDcQ5C2c(wiPyx#i1dk^0K z_e*|@$?>f`*>*>R{qLP}=8rqRZQj%w%bH*F1#0CU-d4o1Q2yV(_0erD0v;;!zC_;N zJ9X=8vx#2mg|XtGQT)v*#jadOg_ch04(iI;cB0q({&~yy))O;>qcd~8FWit&mExZ4 zm%nVIqPxkYLk5$!Y@PDs>r;7UG2@+I-}6uMdv3t9*lE_asSJ&RAr?N)J&#H|jDGHU z z%qtWB1_e#jxA-wn?eKK3zIEq9wB&!qNVM5apF4MR$8p6J=i=ZQ^XC2f8`NL*hWq2i z?fV&Ixl3+eSoYh*{czta$^RMB;jz<>zQ5nH|C!voA6NHo&{Zh=?HDA_FzM-L`DeH8 z_szRlfB$!_%OYh1{yA$x7tfjVEa%NSrYkM1zjn8AzW&E&JpJ&j{Rc&lzxr$cZMBrk z+I^1Oqt4c=yVv@k_mj6eo4WAhggT~Q>)67lvR5*AXtjJY&{o?iBDo(-xjhb>!BW$hIfkN9!Cr!?;zj8r%cFb$t_tebuZ`Kh z!k3emqm}h$PN#=T%){WUyk!S8Eu|79Q$IMRPRoCM%C0N+L}jHz1c6=4s)P}^R2 zSo`J8KWF(1e&^o%rrp1F@tmf-gI;CXb)Fh>A55hx7g+CkZ9Qv7#9F(E#JOkk>l~QX z4mb4ujGFiTz~+Y+=fD1X`~4cVuKQCqJe}CE|52twknu_*yUg(1{{edMn-gD#zvbB~ zdwFe~>Ef_bH_gzAT5i84$UoV!c+W4^A6iF+O27Kq+rR35tzEA_%c80J=#QxzUH{IP z{{DAvsNTGH**8`nFS~H?|N2kg@61epTl~E4;gN|}3@h(9Rhv!IwN}X3y-`B)X4`Yi z8OuT|JG}3&u9V?Euzcy#$?Wo;7cX18C6nd#Yrp|>-pF~#Vxa{s!AFVoG0xMqreP-x-UVSbeB zM^_6=vvZ`+WsU>xqEke>{0i)PimpdJEtilwSMd03f7btr3%n-lTW~n{F!poZ=E|O# zvT(P=pQ`7Mwy!q&w>(~unp-V*Ss^i_H!qdx1pkJTPV217+t-qUHy-?L;N`im?B3=d zOFybd^w}FNjXlMv)ElL#ZO*W>&ZF#ey!qQbObVhr&zfEp9#-MCapJW2Q7iSs#J92h z|NM0sJtfY!ZFoKUelgknwe~16nzX)B=x>-~Rd&EZO;#D6&{E^|6JBRCdits^KEvMl z;Jt&_bspBxi3@He&ggkm$Km{YfdnJxK7%!^yI*}=)%yDF9^GFvch{Nz+GXCjn&syi zg-hReyjE9#bK&gomC^b)U+(HI)~~qux~}5K)1@m`hLql4vYG#*Vk*Xc)luaUEuXBY{s&qi~V^D-j;q(nx!Uf{%z99C#&nEr>wqV z8ui7oD2L^APftoj)sH$8^W%m*lYP=U{_V?ioAftgT6=19E_Y5agV1?x0p8tPOxyz< zAL;Nk3i3###;@9RaQ-uop3e;*LJv$2xXNjAz`&IK(3Ud{78-hc4%B!)FZnh_Y{?Wy zR;K_htBA0#o2CXCnoK$LU3FPzP|=o&NgJO`&=&bvd-S{BKjqq5O(#WZP174ica`?N zU$BHlrA_n3t;vTz+c<qr2eFM z&p5PTrD0p{?1Mj68~AK)y6>ghU$CR@JSdwxnW)MvQ(Mn!EK^Z>gsD9L?zAr#lpm|} z%v4dizUHyLo=@NJpUc*omD}A|a(khaTIQjuEupI&A_YB4Hsw6)+x)?_mg#eFtk0e& zzB2U_d86gp-tB&{QoGdpit+V>J;EiTPo8~#*eL%}%Q8BQfX1LaSAd6m9bVe(1AUbhZcWf3mdW&ZP9g7~)I`N2DD_G2ENoJ8mE!B2`c zm*|&ti*Tu`?pv=R(o&YVeR0j>=&)Jq ze)@UGy7gy;Y;z}Qq+Z>zVWVJO?aqU@&wq*k^5?C_wq?q5zOPpLzD{t*yaOK>ZZz~e zv5i??r#1KXmaV3n(%z~`NhP@`3#L5qUKEgX+w6>{y8NN@|9=Zl>6)}}-&US4>)8{N zZnH&iPuuyRd%Iz6m3W_WpqG-s7haynyW~16rsS*(m?(EGH*Z(?>1{fnek#56Sr=p~ z=p?|`o{`u0`RDn$Cy(2IORI@5y1)0j!qNx9^M9+m%O@9I6fP<aIY?LA$}B6iih zg4Mqd8`+tA@7_I~^J&@+HjgP!OR{AyYd17Y1sJidnWC+IG$r8e1O=Akx)H}^{|9`1 zogcra{`}34?>D`?X+87XkGxr8^EP+y|J*m}qv8GfPo3Tt6%W-7%P(&+nlE#KwtHbYn6BhecyRGhTuW;$N>vBB)PC8<0QX41EJ1p$~t@xs9fAVkFX=hs}tId`v zK4ufP=H8j&+1m;3ZC8J z+_KH=!S6{651tj_mOQ|*gNMz@ljpR8Ow&Zc_9q?Z|MKT1+~s!mnQ1h6t?l0yXT>hp zyZ1uMn5TPHPv>wh_7~y0Kbf&!sep(7%C1wL<;9y5f~FsRa&pq*X{H}0@~6IdRaI%b zZ?bgvpRe%??U~Q#Hy7(Q>mI)OmLcf1L>v&%FsOi+swM{>r;!8!sNfpR=V4M zTi79$$#pGdf}iP5F$P6GTN@^kvi#QGU`2Zgk8{6F`5rIwdc~K!p5x1*hJ>>AOUGJI z=B=x{9(8W#uBp4{n}4`^xZr@b!ewS<>4#6`4@_0hu%9rI*YpqfQqfI3+14Kd@3o%S zIsa*Ly~vh31ve8m_!#Ija~baXD)uBpAc#@bRCJP2f#&v%r))c3uHSn4N^*RcQ`wA+ zq?+_kYqkF$+7g~&Z>E~NY-`AE+4sAv#oT3gzf*1Gm$=XE!m;D`x%!7Y9{>N+C3E@C z+4-#jf2CG4PuMH7Q6T50&56hT>mT2~zq8d{{`QHh^5&MAjSFlSGRQ|9&D{Lu({{V$ zkN4uMcC1?zpz*LWZmF&J^Uqz9Z@a#+IXYiiqM2r}`|=XQHy4H4cZWs)xqrBJ&udxl z4Y_ZH=6Uinc`@@J*ut}D+tF?Rbo^~h51ieer!>8|Am(J#&&?ITUY1tGbr$S!v6_4E z`@zChMqLv-oH#T@zU~m~)mjwj_?#u<{f5pKR$ktwpX@Vy=D+*UXS{Ih)uY=)C)`PX ze$8aTYR`*TmsEUs-L&6EQ9`T5kf;0FF^*~02M%SjwV(ZTaw<<_XNN$F5#RRvJLl~zwtn&c`rEK|&nJ7P|G4r~Y+iiA zU90BKxpvq822MCH&Xm_9JK^ltpFv&|&x>nX>Zu-o_|sJH{~p~V%>1f9ewqI}v#>k; z;zgYsTU32d2I|b1zwW-t*A>U@gzo<@Y&x_=+0=jG3?32nFFAG1caCfg-`6tr+uAFl zZOc|#?cCnfvtrU5?w*|YW%1VUxtee9f8d-a5S9MsMU@t^!k6H`qGg_rH2pJ8orZ?I`v7mp+9F+xpnDkThUA3w>@Uy zdHX$f8E?(LPybWh%P!yFKk3{#*WLT;c0TSrpWL@g?j=Xd+xOd!ynlD3HoR`e;@1xr z^Et$IMwrAJJdWSj`7+fqHL+!W&EZEIB(3*x`q^H1ysCfKnmZ9G8rl<-depZrytn71 zS@&}7X+HWwn^O&5o1{&M%KLQSbJx1gvyI;77I}gOTb8#n?%Z=aKCh~A|NVWP4^tX< zZod8EMaSgx)j2nAoNno3vfp{-(b?ruJv~!C)Vi)r*-$vyH-7%XK=-K|bm!OHJ5tIk z#M$rRa`6H~r_0(qOU%>Ua^4?(l6Lf``ifaMPxzcYBcpO?()s<8=jw%Per5HuU$4yZ zK6q`OPFv!Ko9F*dI$x8;$<2Q$H^2V>^D47^&x`gG4u3xNHhEI>vIG6cx6GgS+wti9 zb>=(XGfppk9pk0yc2Z>f_Jd77n|^*~b=MP>*?;HZiKUhQK27KN|MBeokEj3J%@XzJ zH(1EbXSV~|kjc4|^m(fHx8}AEHm*Y~3z@>-bMV{W3HUtc=GPxjSl|9R-@n-^bN$)k zQ@Y+i9mV`VAFtfz-Lrb%*-0zopBf%^t4WjE^SJ(N``zt_mH#vI#!5YyQYo|P&7`=d z#!!O~G7Bc8eK|ULtMA{d|5fkWYtiVn zr?k2i0?IhM8(ETsm?G6T&ndaI`5xXKAU-FPVuuppU&k!`?K$JUYJwG zk^g=5|BoInpF5-Hq_}|L^N2lG-bdZDQ~ak+Z(4b}f5+ZOXZI~B&HVXVZ(gnJ^n3r} z-S4Kq-t=GL|AgmGuj3C?9(nXyHGi`GJ2s#F&fnkNe_LaT*hgYtCLdy+2;Fs)xMbU*Rk4+#O~#fC~Jk{%pz*6*5D zoo&73hiu&LuK|uJn``BHet!+RwE5D_?d#`hxBuMutisSTMn~=!tJy*ZH4&N5*>4xP zpFF7;rq@36yycxw-RCuK#hyL&RI=ptDmekyOx`cjbNO%nReaOMbC}_>zNJW6&+GNP zo6=HG$Cp1|<#poB7h=1$L{!PB*j=!5l&*|U1d#~xXUO#r=Li7Xs z^=+^11^7L_b0^%JYw$)S~Wk{dew|?w^enb_H?Iu%Wxe#-DEkz$N16W$A${l zXR7&syqGKXY~e|>;ySs!u$nnJay1G*=i{#ac_}IH+`i&Rz?3lOtnhGw9o(CqoN@8I z`NZd1?&(KA#iQ$X#%#Hv8(1CSs##tbx7mG`&@ZMtmd`(=+?h8wM{W8Y{_-^w{odGD z_#IlU^fgHBN7v0sH{$m5Yi;*lR28c^WsQJg(u|%{H;$}b+@ve@`CIw}^T*o^HLK^& zIo-Fz{-{NgeE5_^cUO<2Qg^hX*8Rx%(&2J&_L;Nh)g^UX1JB7;>UN)M-}Yk%xBC6^ z8YRV|zlrBd&u@tM`*5gX8v-vA@TO&ADFIwXI{QH-k2i~?f z&fY#zX}xT2!>8U)F+sbccbOfSvF`qc9~&i>P5coz%jWX=`@8KF`4_F5apD#K?`=*G z+l9EU{;w9Q3f=4dtJ}r-%SQ8!+0Qy}zu$g&a>dK3DXVIJ`c}1>eBN6ba9^3(rjBW< z_KMY)eqO&7vAs~ux!u?HA9GZc)wg5QbhXWE?7BaDzgyy|Z4_2sH>rEwjBAhn=boP@ z`lLj*^pIK9mK3GKdUbRC=AO&?xN+extx5M^ulf8wVt)M9gxuNLZHIqdojvpWMXzl+ zx~ac<^d)Myot^d4&h7i!U}o9Ykvr zTBvk>(b4dTe5V?VKAkhZ^EoDFG~Zb>qs7cp%zHKa!=1`=>#H;$uKM@nV292-wb`YBW0wT7Wb z-hBQShxloAh56n6xx9}S>n>ng(mVP3;bosvXRlT|fAPafVV8RbKc6Uezj5&BU98bn z#F2fi@?%QrE%w_McI0|=u)$wG z@z^^?>kqY^PoKNZFz(aio5L2}fN0=YKKmmHH@D zb|m3*+m*++_GeAgTOHa^Vh~g#HvMVqhnZ2iE`cITEkB(zm$0*w4cdM3(cLd6n=HlJ z-l&;YzKK}0Wlf?(uIBlDGp>CS{>3D4M}MctgJ;1ON+p+eovt)E@|o#T?Z)Iyw$}ZD zmut^N-E5wD{q`A|PvU~Ey@w7@ysjVX`*ZQ>8=s!M)D$wxDUevbYSQgxYXl#L?G%;L zTA0MW@2~U>qY~3F39Iy1TUTri*z?`^oOI0pxqV;P{e7A#|9(Mxc4SPM-1NS`4XvN` ztsd2hE|vTBZC+8-F%tv*4q3y9Js;W*Z*x8D7`FF+{)cIfX5n_r3p1(&TL08pcWag1lWC1&7U6=r`9*?-bZ@=us?3*onH*r;`@s zxh_n!zrT;;?&bdah52`8zFqfd($aMIptcGJrpUalt4amBcU)bj<8fLr@gc8@%Ouff zoAylo^Y^d;gX?bJU-8AZOG=)u)Q*2Nkv-Y!|Ci6z61Tsr8a_BUS(r0$+jZybZyt+J zS@!>lj`sDMFYIlhc3cjgQ>Vvl&$_;7=3!~gvD(O-=CKO;cS)R9y6vs?l3=+?4%&gw2h-NqWKN~mmSv^#)nUq zJug#jX!bRBeg{{+f}mFF4%OVlrjK5pusZ(sX`<)WH6C+0rbaYRS?~LvQ_g<=`qPIe zZ;{jHsr`N9PW8+E#(8s2nubUIO4IJW(#B&Z|Kgx(|GU%!o;7#h+3J?Pk8{cEZ%Tf* zyW(Z2$eblv+YT?YVX|S$mwb|TEaJ;4Q6KA(oXhAb56TXII>r9nR#HBd0f=BZK@%sIT`t^ zPaoXDxyHP=c~z+Fsbcxt6CM@+&Q$51t}Hg``j_R6$Jxt-zR2-@6%7dHnPIf$fg zIq&Ju`0($f{m$r6?PNDG=VHDWv+w&|kP!fdzwf5aC!V}h|Fp2U{zbGx=)z6xUQ<_A z+`JvpEL|4L*{G+)CKE07>*(*OBO8@E@9mudt>Ts^T9o&A@Oc1_(bUwC`d)4zT%cedN@n!o68-CTyZEMFTmx)y2VPAWe3#Ivt0xA8iE z=G`ON0{OqfxA_U}+I#ZSmi+&G7P}q9{(qb&78dbstL}E@UF=z9o=H{?OVkuqI?S~S zBKH42kr-iqZj-_T@r#LOJI_?zIU0O5Z-t)M9P{r&AC`GUM7(II@Up4nJZ$76sIqZF zg*$_d=M#ROcMH*d~ zmd&WKtl0JBMDyMF9o%0we7W*r=ToC(N00NO>)qeReJq(%z4JED1$DXJUqzN`*A$#s z7Oe7k_c)Hb;5|Rz_D{3jPwxM~CNz)#u*k;=cOqH*zUG~fWU91pY79TLA=)LvVjG9R z<=0_{KAo;z;>H8hxddjmjsnCd<&_3SsFhj(c`l1VT~)xn*e_mo7ls>#C*Cm6 zOVNnbTNu%(qcKN)VOW^P#b9Nz3Zc;K)^eU1beb4kE6BQ^L+?=T|LwuwBXI)hU%Jn%5Ni zcRw>1l%J}kW8?YgXK?9mOz#ax zrIdj4&wl5JT$$M^pwk~-WW8vi%VFn!{z=v+&uPZG_{@k~dF5&OY+0#gvnMAxmCfVe z@7Gt%Fe+NOYsT((n|{3Vo-6%!rt1A?hU-LopU+`Dt1iddthqdwXP#kw+uQ$ZCNJ(? zECa~ z9k%QEu39Upe3f^P7ajJfkj|T5@x(jbBI|_3+K!gW$V5SzFSkE<=K<+yEX z%F8C{_WI}CiZ?HIy5E`hB;TTzJAD7N1!0VfK&8&|8NSlGCtk2Vo$V^{CRUgG&za3B ziSj2V6|DJjNY;4|cbxmL`UehY<+kr!aCymfcORDam&~(NW*(n>A~|upiBags~#?ii@0={BDL$cFX>`cK^0C{*@=)@7X;$#C+xI)Q6v0J-kj? zEKSyo`E*M&d&i0u41pr9fg%iT{N^8Y>*lc5uTHm=#3y@^y+^y%ZheSwdgL~i1Lp8x%DgS^V52ie{`6DAzJwf;#5D|h0l zsey;`7Os7h)BJLO|A)`fhblJ7HQt$crfOT5y1R8+LYeNxf(dK&za#{FZ@&Nf>C&A? zHXUz%sT%N4@Mq-IC2A*YcwHA(J(Air-}Xz#`q({{f_@1?eYpW!to!fnY;BR+vt*gu z%e&T9H~zT`%NGXyv1Qo6pBXH~bLiIB?M7vH-lR=_!M*(-Kl8bLVh#0cUZ43XE@Ij> z<8L+hO_T3l$G-m9wUMQ8|0l@_%RVn&n;eli=bDYf=eyT(9eyugTV0c7zjgjC|F_pC zzAgXw-e!&551X9-5~r>eCN3A6X8-QN*M|2z;uDSXyqhxwv{Saf*s=WIi|6t4J&vtf zdGY7+`S;UqUV5MV>G(hIq>1|Dy+x zwOIZ4E2vp@5LC8&J9X!FE=QQdLz_9`T|bY%mu32AtZ&D#=GHE;ZvKW$epkPpKLT!< zd9Kd$wW&D$;Lf4f96zFt@7b<=%$x^~=4E zFN~kgu{y0p{m(AF8&j`!K5GD_?YFZuo1L;)mL4{=Ssv57U`x~&bwyWj>TwMW0juZ& z 0) + { + TRACELOG(LOG_WARNING, "This image has only 1 channel. Setting channel to it."); + selectedChannel = 0; + } + } + else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) + { + if (selectedChannel > 1) + { + TRACELOG(LOG_WARNING, "This image has only 2 channels. Setting channel to alpha."); + selectedChannel = 1; + } + } + else if (image.format == PIXELFORMAT_UNCOMPRESSED_R5G6B5 + || image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8 + || image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32 + || image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16 + ) + { + if (selectedChannel > 2) + { + TRACELOG(LOG_WARNING, "This image has only 3 channels. Setting channel to red."); + selectedChannel = 0; + } + } + + // formats rgba + if (selectedChannel > 3) + { + TRACELOG(LOG_WARNING, "ImageFromChannel supports channels 0 to 3 (rgba). Setting channel to alpha."); + selectedChannel = 3; + } + + result.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + result.height = image.height; + result.width = image.width; + result.mipmaps = 1; + + unsigned char *pixels = (unsigned char *)RL_CALLOC(image.width * image.height, sizeof(unsigned char)); // values 0 to 255 + + if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats"); + else + { + for (int i = 0, k = 0; i < image.width * image.height; ++i) + { + float imageValue = -1; + switch (image.format) + { + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: + { + imageValue = (float)((unsigned char *)image.data)[i + selectedChannel]/255.0f; + + } break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + { + imageValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f; + + k += 2; + } break; + case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + { + unsigned short pixel = ((unsigned short *)image.data)[i]; + + if (selectedChannel == 0) + { + imageValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31); + } + else if (selectedChannel == 1) + { + imageValue = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31); + } + else if (selectedChannel == 2) + { + imageValue = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31); + } + else if (selectedChannel == 3) + { + imageValue = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f; + } + + } break; + case PIXELFORMAT_UNCOMPRESSED_R5G6B5: + { + unsigned short pixel = ((unsigned short *)image.data)[i]; + + if (selectedChannel == 0) + { + imageValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31); + } + else if (selectedChannel == 1) + { + imageValue = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63); + } + else if (selectedChannel == 2) + { + imageValue = (float)(pixel & 0b0000000000011111)*(1.0f/31); + } + + } break; + case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: + { + unsigned short pixel = ((unsigned short *)image.data)[i]; + + if (selectedChannel == 0) + { + imageValue = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15); + } + else if (selectedChannel == 1) + { + imageValue = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15); + } + else if (selectedChannel == 2) + { + imageValue = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15); + } + else if (selectedChannel == 3) + { + imageValue = (float)(pixel & 0b0000000000001111)*(1.0f/15); + } + + } break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: + { + imageValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f; + + k += 4; + } break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: + { + imageValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f; + + k += 3; + } break; + case PIXELFORMAT_UNCOMPRESSED_R32: + { + imageValue = ((float *)image.data)[k]; + + k += 1; + } break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32: + { + imageValue = ((float *)image.data)[k + selectedChannel]; + + k += 3; + } break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: + { + imageValue = ((float *)image.data)[k + selectedChannel]; + + k += 4; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + imageValue = HalfToFloat(((unsigned short *)image.data)[k]); + + k += 1; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + imageValue = HalfToFloat(((unsigned short *)image.data)[k+selectedChannel]); + + k += 3; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + imageValue = HalfToFloat(((unsigned short *)image.data)[k + selectedChannel]); + + k += 4; + } break; + default: break; + } + + pixels[i] = imageValue * 255; + } + } + + result.data = pixels; + + return result; +} + // Resize and image to new size using Nearest-Neighbor scaling algorithm void ImageResizeNN(Image *image,int newWidth,int newHeight) { From a805f46f552626c9d6b39baeda2d03963ab53cb4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 30 Jun 2024 08:33:45 +0000 Subject: [PATCH 03/40] Update raylib_api.* by CI --- parser/output/raylib_api.json | 15 + parser/output/raylib_api.lua | 9 + parser/output/raylib_api.txt | 562 +++++++++++++++++----------------- parser/output/raylib_api.xml | 6 +- 4 files changed, 313 insertions(+), 279 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 2baa6c3f3..5e3fc2c4a 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -7229,6 +7229,21 @@ } ] }, + { + "name": "ImageFromChannel", + "description": "Create an image from a selected channel of another image", + "returnType": "Image", + "params": [ + { + "type": "Image", + "name": "image" + }, + { + "type": "int", + "name": "selectedChannel" + } + ] + }, { "name": "ImageFormat", "description": "Convert image data to desired format", diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index a6d91cd6d..37ec004ba 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -5543,6 +5543,15 @@ return { {type = "Color", name = "tint"} } }, + { + name = "ImageFromChannel", + description = "Create an image from a selected channel of another image", + returnType = "Image", + params = { + {type = "Image", name = "image"}, + {type = "int", name = "selectedChannel"} + } + }, { name = "ImageFormat", description = "Convert image data to desired format", diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index f283ad795..5f62c2e19 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -984,7 +984,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 572 +Functions found: 573 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2789,76 +2789,82 @@ Function 295: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 296: ImageFormat() (2 input parameters) +Function 296: ImageFromChannel() (2 input parameters) + Name: ImageFromChannel + Return type: Image + Description: Create an image from a selected channel of another image + Param[1]: image (type: Image) + Param[2]: selectedChannel (type: int) +Function 297: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 297: ImageToPOT() (2 input parameters) +Function 298: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 298: ImageCrop() (2 input parameters) +Function 299: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 299: ImageAlphaCrop() (2 input parameters) +Function 300: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 300: ImageAlphaClear() (3 input parameters) +Function 301: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 301: ImageAlphaMask() (2 input parameters) +Function 302: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 302: ImageAlphaPremultiply() (1 input parameters) +Function 303: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 303: ImageBlurGaussian() (2 input parameters) +Function 304: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 304: ImageKernelConvolution() (3 input parameters) +Function 305: ImageKernelConvolution() (3 input parameters) Name: ImageKernelConvolution Return type: void Description: Apply custom square convolution kernel to image Param[1]: image (type: Image *) Param[2]: kernel (type: const float *) Param[3]: kernelSize (type: int) -Function 305: ImageResize() (3 input parameters) +Function 306: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 306: ImageResizeNN() (3 input parameters) +Function 307: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 307: ImageResizeCanvas() (6 input parameters) +Function 308: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2868,12 +2874,12 @@ Function 307: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 308: ImageMipmaps() (1 input parameters) +Function 309: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 309: ImageDither() (5 input parameters) +Function 310: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2882,109 +2888,109 @@ Function 309: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 310: ImageFlipVertical() (1 input parameters) +Function 311: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 311: ImageFlipHorizontal() (1 input parameters) +Function 312: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 312: ImageRotate() (2 input parameters) +Function 313: ImageRotate() (2 input parameters) Name: ImageRotate Return type: void Description: Rotate image by input angle in degrees (-359 to 359) Param[1]: image (type: Image *) Param[2]: degrees (type: int) -Function 313: ImageRotateCW() (1 input parameters) +Function 314: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 314: ImageRotateCCW() (1 input parameters) +Function 315: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 315: ImageColorTint() (2 input parameters) +Function 316: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 316: ImageColorInvert() (1 input parameters) +Function 317: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 317: ImageColorGrayscale() (1 input parameters) +Function 318: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 318: ImageColorContrast() (2 input parameters) +Function 319: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 319: ImageColorBrightness() (2 input parameters) +Function 320: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 320: ImageColorReplace() (3 input parameters) +Function 321: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 321: LoadImageColors() (1 input parameters) +Function 322: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 322: LoadImagePalette() (3 input parameters) +Function 323: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 323: UnloadImageColors() (1 input parameters) +Function 324: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 324: UnloadImagePalette() (1 input parameters) +Function 325: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 325: GetImageAlphaBorder() (2 input parameters) +Function 326: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 326: GetImageColor() (3 input parameters) +Function 327: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 327: ImageClearBackground() (2 input parameters) +Function 328: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 328: ImageDrawPixel() (4 input parameters) +Function 329: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2992,14 +2998,14 @@ Function 328: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 329: ImageDrawPixelV() (3 input parameters) +Function 330: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 330: ImageDrawLine() (6 input parameters) +Function 331: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -3009,7 +3015,7 @@ Function 330: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 331: ImageDrawLineV() (4 input parameters) +Function 332: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -3017,7 +3023,7 @@ Function 331: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 332: ImageDrawLineEx() (5 input parameters) +Function 333: ImageDrawLineEx() (5 input parameters) Name: ImageDrawLineEx Return type: void Description: Draw a line defining thickness within an image @@ -3026,7 +3032,7 @@ Function 332: ImageDrawLineEx() (5 input parameters) Param[3]: end (type: Vector2) Param[4]: thick (type: int) Param[5]: color (type: Color) -Function 333: ImageDrawCircle() (5 input parameters) +Function 334: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -3035,7 +3041,7 @@ Function 333: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 334: ImageDrawCircleV() (4 input parameters) +Function 335: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -3043,7 +3049,7 @@ Function 334: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 335: ImageDrawCircleLines() (5 input parameters) +Function 336: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -3052,7 +3058,7 @@ Function 335: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 336: ImageDrawCircleLinesV() (4 input parameters) +Function 337: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -3060,7 +3066,7 @@ Function 336: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 337: ImageDrawRectangle() (6 input parameters) +Function 338: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -3070,7 +3076,7 @@ Function 337: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 338: ImageDrawRectangleV() (4 input parameters) +Function 339: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -3078,14 +3084,14 @@ Function 338: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 339: ImageDrawRectangleRec() (3 input parameters) +Function 340: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 340: ImageDrawRectangleLines() (4 input parameters) +Function 341: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -3093,7 +3099,7 @@ Function 340: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 341: ImageDrawTriangle() (5 input parameters) +Function 342: ImageDrawTriangle() (5 input parameters) Name: ImageDrawTriangle Return type: void Description: Draw triangle within an image @@ -3102,7 +3108,7 @@ Function 341: ImageDrawTriangle() (5 input parameters) Param[3]: v2 (type: Vector2) Param[4]: v3 (type: Vector2) Param[5]: color (type: Color) -Function 342: ImageDrawTriangleEx() (7 input parameters) +Function 343: ImageDrawTriangleEx() (7 input parameters) Name: ImageDrawTriangleEx Return type: void Description: Draw triangle with interpolated colors within an image @@ -3113,7 +3119,7 @@ Function 342: ImageDrawTriangleEx() (7 input parameters) Param[5]: c1 (type: Color) Param[6]: c2 (type: Color) Param[7]: c3 (type: Color) -Function 343: ImageDrawTriangleLines() (5 input parameters) +Function 344: ImageDrawTriangleLines() (5 input parameters) Name: ImageDrawTriangleLines Return type: void Description: Draw triangle outline within an image @@ -3122,7 +3128,7 @@ Function 343: ImageDrawTriangleLines() (5 input parameters) Param[3]: v2 (type: Vector2) Param[4]: v3 (type: Vector2) Param[5]: color (type: Color) -Function 344: ImageDrawTriangleFan() (4 input parameters) +Function 345: ImageDrawTriangleFan() (4 input parameters) Name: ImageDrawTriangleFan Return type: void Description: Draw a triangle fan defined by points within an image (first vertex is the center) @@ -3130,7 +3136,7 @@ Function 344: ImageDrawTriangleFan() (4 input parameters) Param[2]: points (type: Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) -Function 345: ImageDrawTriangleStrip() (4 input parameters) +Function 346: ImageDrawTriangleStrip() (4 input parameters) Name: ImageDrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points within an image @@ -3138,7 +3144,7 @@ Function 345: ImageDrawTriangleStrip() (4 input parameters) Param[2]: points (type: Vector2 *) Param[3]: pointCount (type: int) Param[4]: color (type: Color) -Function 346: ImageDraw() (5 input parameters) +Function 347: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -3147,7 +3153,7 @@ Function 346: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 347: ImageDrawText() (6 input parameters) +Function 348: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -3157,7 +3163,7 @@ Function 347: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 348: ImageDrawTextEx() (7 input parameters) +Function 349: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -3168,79 +3174,79 @@ Function 348: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 349: LoadTexture() (1 input parameters) +Function 350: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 350: LoadTextureFromImage() (1 input parameters) +Function 351: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 351: LoadTextureCubemap() (2 input parameters) +Function 352: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 352: LoadRenderTexture() (2 input parameters) +Function 353: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 353: IsTextureReady() (1 input parameters) +Function 354: IsTextureReady() (1 input parameters) Name: IsTextureReady Return type: bool Description: Check if a texture is ready Param[1]: texture (type: Texture2D) -Function 354: UnloadTexture() (1 input parameters) +Function 355: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 355: IsRenderTextureReady() (1 input parameters) +Function 356: IsRenderTextureReady() (1 input parameters) Name: IsRenderTextureReady Return type: bool Description: Check if a render texture is ready Param[1]: target (type: RenderTexture2D) -Function 356: UnloadRenderTexture() (1 input parameters) +Function 357: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 357: UpdateTexture() (2 input parameters) +Function 358: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 358: UpdateTextureRec() (3 input parameters) +Function 359: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 359: GenTextureMipmaps() (1 input parameters) +Function 360: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 360: SetTextureFilter() (2 input parameters) +Function 361: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 361: SetTextureWrap() (2 input parameters) +Function 362: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 362: DrawTexture() (4 input parameters) +Function 363: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -3248,14 +3254,14 @@ Function 362: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 363: DrawTextureV() (3 input parameters) +Function 364: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 364: DrawTextureEx() (5 input parameters) +Function 365: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -3264,7 +3270,7 @@ Function 364: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 365: DrawTextureRec() (4 input parameters) +Function 366: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -3272,7 +3278,7 @@ Function 365: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 366: DrawTexturePro() (6 input parameters) +Function 367: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -3282,7 +3288,7 @@ Function 366: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 367: DrawTextureNPatch() (6 input parameters) +Function 368: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -3292,112 +3298,112 @@ Function 367: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 368: ColorIsEqual() (2 input parameters) +Function 369: ColorIsEqual() (2 input parameters) Name: ColorIsEqual Return type: bool Description: Check if two colors are equal Param[1]: col1 (type: Color) Param[2]: col2 (type: Color) -Function 369: Fade() (2 input parameters) +Function 370: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 370: ColorToInt() (1 input parameters) +Function 371: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color (0xRRGGBBAA) Param[1]: color (type: Color) -Function 371: ColorNormalize() (1 input parameters) +Function 372: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 372: ColorFromNormalized() (1 input parameters) +Function 373: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 373: ColorToHSV() (1 input parameters) +Function 374: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 374: ColorFromHSV() (3 input parameters) +Function 375: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 375: ColorTint() (2 input parameters) +Function 376: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 376: ColorBrightness() (2 input parameters) +Function 377: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 377: ColorContrast() (2 input parameters) +Function 378: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 378: ColorAlpha() (2 input parameters) +Function 379: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 379: ColorAlphaBlend() (3 input parameters) +Function 380: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 380: GetColor() (1 input parameters) +Function 381: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 381: GetPixelColor() (2 input parameters) +Function 382: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 382: SetPixelColor() (3 input parameters) +Function 383: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 383: GetPixelDataSize() (3 input parameters) +Function 384: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 384: GetFontDefault() (0 input parameters) +Function 385: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 385: LoadFont() (1 input parameters) +Function 386: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 386: LoadFontEx() (4 input parameters) +Function 387: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character setFont @@ -3405,14 +3411,14 @@ Function 386: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: codepoints (type: int *) Param[4]: codepointCount (type: int) -Function 387: LoadFontFromImage() (3 input parameters) +Function 388: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 388: LoadFontFromMemory() (6 input parameters) +Function 389: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3422,12 +3428,12 @@ Function 388: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: codepoints (type: int *) Param[6]: codepointCount (type: int) -Function 389: IsFontReady() (1 input parameters) +Function 390: IsFontReady() (1 input parameters) Name: IsFontReady Return type: bool Description: Check if a font is ready Param[1]: font (type: Font) -Function 390: LoadFontData() (6 input parameters) +Function 391: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3437,7 +3443,7 @@ Function 390: LoadFontData() (6 input parameters) Param[4]: codepoints (type: int *) Param[5]: codepointCount (type: int) Param[6]: type (type: int) -Function 391: GenImageFontAtlas() (6 input parameters) +Function 392: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3447,30 +3453,30 @@ Function 391: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 392: UnloadFontData() (2 input parameters) +Function 393: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: glyphs (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 393: UnloadFont() (1 input parameters) +Function 394: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 394: ExportFontAsCode() (2 input parameters) +Function 395: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 395: DrawFPS() (2 input parameters) +Function 396: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 396: DrawText() (5 input parameters) +Function 397: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3479,7 +3485,7 @@ Function 396: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 397: DrawTextEx() (6 input parameters) +Function 398: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3489,7 +3495,7 @@ Function 397: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 398: DrawTextPro() (8 input parameters) +Function 399: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3501,7 +3507,7 @@ Function 398: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 399: DrawTextCodepoint() (5 input parameters) +Function 400: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3510,7 +3516,7 @@ Function 399: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 400: DrawTextCodepoints() (7 input parameters) +Function 401: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3521,18 +3527,18 @@ Function 400: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 401: SetTextLineSpacing() (1 input parameters) +Function 402: SetTextLineSpacing() (1 input parameters) Name: SetTextLineSpacing Return type: void Description: Set vertical line spacing when drawing with line-breaks Param[1]: spacing (type: int) -Function 402: MeasureText() (2 input parameters) +Function 403: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 403: MeasureTextEx() (4 input parameters) +Function 404: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3540,195 +3546,195 @@ Function 403: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 404: GetGlyphIndex() (2 input parameters) +Function 405: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 405: GetGlyphInfo() (2 input parameters) +Function 406: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 406: GetGlyphAtlasRec() (2 input parameters) +Function 407: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 407: LoadUTF8() (2 input parameters) +Function 408: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 408: UnloadUTF8() (1 input parameters) +Function 409: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 409: LoadCodepoints() (2 input parameters) +Function 410: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 410: UnloadCodepoints() (1 input parameters) +Function 411: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 411: GetCodepointCount() (1 input parameters) +Function 412: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 412: GetCodepoint() (2 input parameters) +Function 413: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 413: GetCodepointNext() (2 input parameters) +Function 414: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 414: GetCodepointPrevious() (2 input parameters) +Function 415: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 415: CodepointToUTF8() (2 input parameters) +Function 416: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 416: TextCopy() (2 input parameters) +Function 417: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 417: TextIsEqual() (2 input parameters) +Function 418: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 418: TextLength() (1 input parameters) +Function 419: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 419: TextFormat() (2 input parameters) +Function 420: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 420: TextSubtext() (3 input parameters) +Function 421: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 421: TextReplace() (3 input parameters) +Function 422: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 422: TextInsert() (3 input parameters) +Function 423: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 423: TextJoin() (3 input parameters) +Function 424: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 424: TextSplit() (3 input parameters) +Function 425: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 425: TextAppend() (3 input parameters) +Function 426: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 426: TextFindIndex() (2 input parameters) +Function 427: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 427: TextToUpper() (1 input parameters) +Function 428: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 428: TextToLower() (1 input parameters) +Function 429: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 429: TextToPascal() (1 input parameters) +Function 430: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 430: TextToSnake() (1 input parameters) +Function 431: TextToSnake() (1 input parameters) Name: TextToSnake Return type: const char * Description: Get Snake case notation version of provided string Param[1]: text (type: const char *) -Function 431: TextToCamel() (1 input parameters) +Function 432: TextToCamel() (1 input parameters) Name: TextToCamel Return type: const char * Description: Get Camel case notation version of provided string Param[1]: text (type: const char *) -Function 432: TextToInteger() (1 input parameters) +Function 433: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 433: TextToFloat() (1 input parameters) +Function 434: TextToFloat() (1 input parameters) Name: TextToFloat Return type: float Description: Get float value from text (negative values not supported) Param[1]: text (type: const char *) -Function 434: DrawLine3D() (3 input parameters) +Function 435: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 435: DrawPoint3D() (2 input parameters) +Function 436: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 436: DrawCircle3D() (5 input parameters) +Function 437: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3737,7 +3743,7 @@ Function 436: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 437: DrawTriangle3D() (4 input parameters) +Function 438: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3745,14 +3751,14 @@ Function 437: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 438: DrawTriangleStrip3D() (3 input parameters) +Function 439: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: const Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 439: DrawCube() (5 input parameters) +Function 440: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3761,14 +3767,14 @@ Function 439: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 440: DrawCubeV() (3 input parameters) +Function 441: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 441: DrawCubeWires() (5 input parameters) +Function 442: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3777,21 +3783,21 @@ Function 441: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 442: DrawCubeWiresV() (3 input parameters) +Function 443: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 443: DrawSphere() (3 input parameters) +Function 444: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 444: DrawSphereEx() (5 input parameters) +Function 445: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3800,7 +3806,7 @@ Function 444: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 445: DrawSphereWires() (5 input parameters) +Function 446: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3809,7 +3815,7 @@ Function 445: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 446: DrawCylinder() (6 input parameters) +Function 447: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3819,7 +3825,7 @@ Function 446: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 447: DrawCylinderEx() (6 input parameters) +Function 448: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3829,7 +3835,7 @@ Function 447: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 448: DrawCylinderWires() (6 input parameters) +Function 449: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3839,7 +3845,7 @@ Function 448: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 449: DrawCylinderWiresEx() (6 input parameters) +Function 450: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3849,7 +3855,7 @@ Function 449: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 450: DrawCapsule() (6 input parameters) +Function 451: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3859,7 +3865,7 @@ Function 450: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 451: DrawCapsuleWires() (6 input parameters) +Function 452: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3869,51 +3875,51 @@ Function 451: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 452: DrawPlane() (3 input parameters) +Function 453: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 453: DrawRay() (2 input parameters) +Function 454: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 454: DrawGrid() (2 input parameters) +Function 455: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 455: LoadModel() (1 input parameters) +Function 456: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 456: LoadModelFromMesh() (1 input parameters) +Function 457: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 457: IsModelReady() (1 input parameters) +Function 458: IsModelReady() (1 input parameters) Name: IsModelReady Return type: bool Description: Check if a model is ready Param[1]: model (type: Model) -Function 458: UnloadModel() (1 input parameters) +Function 459: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 459: GetModelBoundingBox() (1 input parameters) +Function 460: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 460: DrawModel() (4 input parameters) +Function 461: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3921,7 +3927,7 @@ Function 460: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 461: DrawModelEx() (6 input parameters) +Function 462: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3931,7 +3937,7 @@ Function 461: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 462: DrawModelWires() (4 input parameters) +Function 463: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3939,7 +3945,7 @@ Function 462: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 463: DrawModelWiresEx() (6 input parameters) +Function 464: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3949,13 +3955,13 @@ Function 463: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 464: DrawBoundingBox() (2 input parameters) +Function 465: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 465: DrawBillboard() (5 input parameters) +Function 466: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3964,7 +3970,7 @@ Function 465: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 466: DrawBillboardRec() (6 input parameters) +Function 467: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3974,7 +3980,7 @@ Function 466: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 467: DrawBillboardPro() (9 input parameters) +Function 468: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3987,13 +3993,13 @@ Function 467: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 468: UploadMesh() (2 input parameters) +Function 469: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 469: UpdateMeshBuffer() (5 input parameters) +Function 470: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -4002,19 +4008,19 @@ Function 469: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 470: UnloadMesh() (1 input parameters) +Function 471: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 471: DrawMesh() (3 input parameters) +Function 472: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 472: DrawMeshInstanced() (4 input parameters) +Function 473: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -4022,35 +4028,35 @@ Function 472: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 473: GetMeshBoundingBox() (1 input parameters) +Function 474: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 474: GenMeshTangents() (1 input parameters) +Function 475: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 475: ExportMesh() (2 input parameters) +Function 476: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 476: ExportMeshAsCode() (2 input parameters) +Function 477: ExportMeshAsCode() (2 input parameters) Name: ExportMeshAsCode Return type: bool Description: Export mesh as code file (.h) defining multiple arrays of vertex attributes Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 477: GenMeshPoly() (2 input parameters) +Function 478: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 478: GenMeshPlane() (4 input parameters) +Function 479: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -4058,42 +4064,42 @@ Function 478: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 479: GenMeshCube() (3 input parameters) +Function 480: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 480: GenMeshSphere() (3 input parameters) +Function 481: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 481: GenMeshHemiSphere() (3 input parameters) +Function 482: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 482: GenMeshCylinder() (3 input parameters) +Function 483: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 483: GenMeshCone() (3 input parameters) +Function 484: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 484: GenMeshTorus() (4 input parameters) +Function 485: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -4101,7 +4107,7 @@ Function 484: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 485: GenMeshKnot() (4 input parameters) +Function 486: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -4109,84 +4115,84 @@ Function 485: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 486: GenMeshHeightmap() (2 input parameters) +Function 487: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 487: GenMeshCubicmap() (2 input parameters) +Function 488: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 488: LoadMaterials() (2 input parameters) +Function 489: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 489: LoadMaterialDefault() (0 input parameters) +Function 490: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 490: IsMaterialReady() (1 input parameters) +Function 491: IsMaterialReady() (1 input parameters) Name: IsMaterialReady Return type: bool Description: Check if a material is ready Param[1]: material (type: Material) -Function 491: UnloadMaterial() (1 input parameters) +Function 492: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 492: SetMaterialTexture() (3 input parameters) +Function 493: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 493: SetModelMeshMaterial() (3 input parameters) +Function 494: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 494: LoadModelAnimations() (2 input parameters) +Function 495: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: int *) -Function 495: UpdateModelAnimation() (3 input parameters) +Function 496: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 496: UnloadModelAnimation() (1 input parameters) +Function 497: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 497: UnloadModelAnimations() (2 input parameters) +Function 498: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: animCount (type: int) -Function 498: IsModelAnimationValid() (2 input parameters) +Function 499: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 499: CheckCollisionSpheres() (4 input parameters) +Function 500: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -4194,40 +4200,40 @@ Function 499: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 500: CheckCollisionBoxes() (2 input parameters) +Function 501: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 501: CheckCollisionBoxSphere() (3 input parameters) +Function 502: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 502: GetRayCollisionSphere() (3 input parameters) +Function 503: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 503: GetRayCollisionBox() (2 input parameters) +Function 504: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 504: GetRayCollisionMesh() (3 input parameters) +Function 505: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 505: GetRayCollisionTriangle() (4 input parameters) +Function 506: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -4235,7 +4241,7 @@ Function 505: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 506: GetRayCollisionQuad() (5 input parameters) +Function 507: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -4244,158 +4250,158 @@ Function 506: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 507: InitAudioDevice() (0 input parameters) +Function 508: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 508: CloseAudioDevice() (0 input parameters) +Function 509: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 509: IsAudioDeviceReady() (0 input parameters) +Function 510: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 510: SetMasterVolume() (1 input parameters) +Function 511: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 511: GetMasterVolume() (0 input parameters) +Function 512: GetMasterVolume() (0 input parameters) Name: GetMasterVolume Return type: float Description: Get master volume (listener) No input parameters -Function 512: LoadWave() (1 input parameters) +Function 513: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 513: LoadWaveFromMemory() (3 input parameters) +Function 514: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 514: IsWaveReady() (1 input parameters) +Function 515: IsWaveReady() (1 input parameters) Name: IsWaveReady Return type: bool Description: Checks if wave data is ready Param[1]: wave (type: Wave) -Function 515: LoadSound() (1 input parameters) +Function 516: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 516: LoadSoundFromWave() (1 input parameters) +Function 517: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 517: LoadSoundAlias() (1 input parameters) +Function 518: LoadSoundAlias() (1 input parameters) Name: LoadSoundAlias Return type: Sound Description: Create a new sound that shares the same sample data as the source sound, does not own the sound data Param[1]: source (type: Sound) -Function 518: IsSoundReady() (1 input parameters) +Function 519: IsSoundReady() (1 input parameters) Name: IsSoundReady Return type: bool Description: Checks if a sound is ready Param[1]: sound (type: Sound) -Function 519: UpdateSound() (3 input parameters) +Function 520: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 520: UnloadWave() (1 input parameters) +Function 521: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 521: UnloadSound() (1 input parameters) +Function 522: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 522: UnloadSoundAlias() (1 input parameters) +Function 523: UnloadSoundAlias() (1 input parameters) Name: UnloadSoundAlias Return type: void Description: Unload a sound alias (does not deallocate sample data) Param[1]: alias (type: Sound) -Function 523: ExportWave() (2 input parameters) +Function 524: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 524: ExportWaveAsCode() (2 input parameters) +Function 525: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 525: PlaySound() (1 input parameters) +Function 526: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 526: StopSound() (1 input parameters) +Function 527: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 527: PauseSound() (1 input parameters) +Function 528: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 528: ResumeSound() (1 input parameters) +Function 529: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 529: IsSoundPlaying() (1 input parameters) +Function 530: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 530: SetSoundVolume() (2 input parameters) +Function 531: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 531: SetSoundPitch() (2 input parameters) +Function 532: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 532: SetSoundPan() (2 input parameters) +Function 533: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 533: WaveCopy() (1 input parameters) +Function 534: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 534: WaveCrop() (3 input parameters) +Function 535: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined frames range Param[1]: wave (type: Wave *) Param[2]: initFrame (type: int) Param[3]: finalFrame (type: int) -Function 535: WaveFormat() (4 input parameters) +Function 536: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4403,203 +4409,203 @@ Function 535: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 536: LoadWaveSamples() (1 input parameters) +Function 537: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 537: UnloadWaveSamples() (1 input parameters) +Function 538: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 538: LoadMusicStream() (1 input parameters) +Function 539: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 539: LoadMusicStreamFromMemory() (3 input parameters) +Function 540: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 540: IsMusicReady() (1 input parameters) +Function 541: IsMusicReady() (1 input parameters) Name: IsMusicReady Return type: bool Description: Checks if a music stream is ready Param[1]: music (type: Music) -Function 541: UnloadMusicStream() (1 input parameters) +Function 542: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 542: PlayMusicStream() (1 input parameters) +Function 543: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 543: IsMusicStreamPlaying() (1 input parameters) +Function 544: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 544: UpdateMusicStream() (1 input parameters) +Function 545: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 545: StopMusicStream() (1 input parameters) +Function 546: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 546: PauseMusicStream() (1 input parameters) +Function 547: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 547: ResumeMusicStream() (1 input parameters) +Function 548: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 548: SeekMusicStream() (2 input parameters) +Function 549: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 549: SetMusicVolume() (2 input parameters) +Function 550: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 550: SetMusicPitch() (2 input parameters) +Function 551: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 551: SetMusicPan() (2 input parameters) +Function 552: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 552: GetMusicTimeLength() (1 input parameters) +Function 553: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 553: GetMusicTimePlayed() (1 input parameters) +Function 554: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 554: LoadAudioStream() (3 input parameters) +Function 555: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 555: IsAudioStreamReady() (1 input parameters) +Function 556: IsAudioStreamReady() (1 input parameters) Name: IsAudioStreamReady Return type: bool Description: Checks if an audio stream is ready Param[1]: stream (type: AudioStream) -Function 556: UnloadAudioStream() (1 input parameters) +Function 557: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 557: UpdateAudioStream() (3 input parameters) +Function 558: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 558: IsAudioStreamProcessed() (1 input parameters) +Function 559: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 559: PlayAudioStream() (1 input parameters) +Function 560: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 560: PauseAudioStream() (1 input parameters) +Function 561: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 561: ResumeAudioStream() (1 input parameters) +Function 562: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 562: IsAudioStreamPlaying() (1 input parameters) +Function 563: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 563: StopAudioStream() (1 input parameters) +Function 564: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 564: SetAudioStreamVolume() (2 input parameters) +Function 565: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 565: SetAudioStreamPitch() (2 input parameters) +Function 566: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 566: SetAudioStreamPan() (2 input parameters) +Function 567: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 567: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 568: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 568: SetAudioStreamCallback() (2 input parameters) +Function 569: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 569: AttachAudioStreamProcessor() (2 input parameters) +Function 570: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream, receives the samples as 'float' Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 570: DetachAudioStreamProcessor() (2 input parameters) +Function 571: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 571: AttachAudioMixedProcessor() (1 input parameters) +Function 572: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline, receives the samples as 'float' Param[1]: processor (type: AudioCallback) -Function 572: DetachAudioMixedProcessor() (1 input parameters) +Function 573: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index fa953b626..bafb8a658 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -670,7 +670,7 @@ - + @@ -1810,6 +1810,10 @@ + + + + From 17cbc75aa710ed629560580e8105d637b3124653 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 30 Jun 2024 11:07:38 +0200 Subject: [PATCH 04/40] REVIEWED: Formatting, follow raylib coding conventions --- examples/examples.rc | 27 ++++ examples/raylib.ico | Bin 0 -> 9752 bytes src/platforms/rcore_android.c | 2 +- src/platforms/rcore_desktop_rgfw.c | 219 ++++++++++++++--------------- src/platforms/rcore_desktop_sdl.c | 3 +- src/platforms/rcore_drm.c | 13 +- src/raudio.c | 4 +- src/raylib.h | 2 +- src/raymath.h | 29 ++-- src/rmodels.c | 10 +- src/rshapes.c | 4 +- src/rtextures.c | 136 +++++++----------- 12 files changed, 221 insertions(+), 228 deletions(-) create mode 100644 examples/examples.rc create mode 100644 examples/raylib.ico diff --git a/examples/examples.rc b/examples/examples.rc new file mode 100644 index 000000000..4767ad480 --- /dev/null +++ b/examples/examples.rc @@ -0,0 +1,27 @@ +GLFW_ICON ICON "raylib.ico" + +1 VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + //BLOCK "080904E4" // English UK + BLOCK "040904E4" // English US + BEGIN + VALUE "CompanyName", "raylib technologies" + VALUE "FileDescription", "raylib example" + VALUE "FileVersion", "1.0" + VALUE "InternalName", "raylib-example" + VALUE "LegalCopyright", "(c) 2024 raylib technologies (@raylibtech)" + //VALUE "OriginalFilename", "raylib_app.exe" + VALUE "ProductName", "raylib-example" + VALUE "ProductVersion", "1.0" + END + END + BLOCK "VarFileInfo" + BEGIN + //VALUE "Translation", 0x809, 1252 // English UK + VALUE "Translation", 0x409, 1252 // English US + END +END diff --git a/examples/raylib.ico b/examples/raylib.ico new file mode 100644 index 0000000000000000000000000000000000000000..0cedcc55cee7da0e5784256078a93f2901e5d734 GIT binary patch literal 9752 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 literal 0 HcmV?d00001 diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index d8568d929..dc8724d8f 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -660,7 +660,7 @@ void PollInputEvents(void) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; } } - + // Register previous touch states for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index e85f7a0eb..dbbbe10cc 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -104,9 +104,7 @@ __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, // Types and Structures Definition //---------------------------------------------------------------------------------- typedef struct { - // TODO: Define the platform specific variables required - - RGFW_window* window; // Native display device (physical screen connection) + RGFW_window *window; // Native display device (physical screen connection) } PlatformData; //---------------------------------------------------------------------------------- @@ -114,9 +112,9 @@ typedef struct { //---------------------------------------------------------------------------------- extern CoreData CORE; // Global CORE state context -static PlatformData platform = { NULL }; // Platform specific +static PlatformData platform = { NULL }; // Platform specific -static const unsigned short RGFWKeyToRayKey[] = { +static const unsigned short keyMappingRGFW[] = { [RGFW_KEY_NULL] = KEY_NULL, [RGFW_Quote] = KEY_APOSTROPHE, [RGFW_Comma] = KEY_COMMA, @@ -157,7 +155,7 @@ static const unsigned short RGFWKeyToRayKey[] = { [RGFW_SuperL] = KEY_LEFT_SUPER, #ifndef RGFW_MACOS [RGFW_ShiftR] = KEY_RIGHT_SHIFT, - + [RGFW_AltR] = KEY_RIGHT_ALT, #endif [RGFW_Space] = KEY_SPACE, @@ -237,7 +235,7 @@ bool InitGraphicsDevice(void); // Initialize graphics device // Check if application should close bool WindowShouldClose(void) -{ +{ if (CORE.Window.shouldClose == false) CORE.Window.shouldClose = RGFW_window_shouldClose(platform.window); if (CORE.Window.ready) return CORE.Window.shouldClose; @@ -246,7 +244,7 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) -{ +{ RGFW_window_maximize(platform.window); ToggleBorderlessWindowed(); } @@ -255,7 +253,7 @@ void ToggleFullscreen(void) void ToggleBorderlessWindowed(void) { CORE.Window.flags & FLAG_WINDOW_UNDECORATED; - + if (platform.window != NULL) TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() after window creation not available on target platform"); } @@ -430,33 +428,34 @@ void ClearWindowState(unsigned int flags) // Set icon for window void SetWindowIcon(Image image) { - i32 channels = 4; + i32 channels = 4; - switch (image.format) { + switch (image.format) + { case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: case PIXELFORMAT_UNCOMPRESSED_R16: // 16 bpp (1 channel - half float) case PIXELFORMAT_UNCOMPRESSED_R32: // 32 bpp (1 channel - float) + { channels = 1; - break; - + } break; case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: // 8*2 bpp (2 channels) case PIXELFORMAT_UNCOMPRESSED_R5G6B5: // 16 bpp case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // 24 bpp case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: // 16 bpp (1 bit alpha) case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: // 16 bpp (4 bit alpha) case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: // 32 bpp + { channels = 2; - break; - + } break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32: // 32*3 bpp (3 channels - float) case PIXELFORMAT_UNCOMPRESSED_R16G16B16: // 16*3 bpp (3 channels - half float) case PIXELFORMAT_COMPRESSED_DXT1_RGB: // 4 bpp (no alpha) case PIXELFORMAT_COMPRESSED_ETC1_RGB: // 4 bpp case PIXELFORMAT_COMPRESSED_ETC2_RGB: // 4 bpp case PIXELFORMAT_COMPRESSED_PVRT_RGB: // 4 bpp + { channels = 3; - break; - + } break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: // 32*4 bpp (4 channels - float) case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: // 16*4 bpp (4 channels - half float) case PIXELFORMAT_COMPRESSED_DXT1_RGBA: // 4 bpp (1 bit alpha) @@ -465,10 +464,10 @@ void SetWindowIcon(Image image) case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: // 8 bpp case PIXELFORMAT_COMPRESSED_PVRT_RGBA: // 4 bpp case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: // 8 bpp - case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 2 bpp + case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 2 bpp + { channels = 4; - break; - + } break; default: break; } @@ -537,65 +536,70 @@ void SetWindowFocused(void) // Get native window handle void *GetWindowHandle(void) { - #ifndef RGFW_WINDOWS - return (void*)platform.window->src.window; - #else +#ifndef RGFW_WINDOWS + return (void *)platform.window->src.window; +#else return platform.window->src.hwnd; - #endif +#endif } // Get number of monitors int GetMonitorCount(void) { - RGFW_monitor* mons = RGFW_getMonitors(); - size_t i; - for (i = 0; i < 6; i++) { + #define MAX_MONITORS_SUPPORTED 6 + + int count = MAX_MONITORS_SUPPORTED; + RGFW_monitor *mons = RGFW_getMonitors(); + + for (int i = 0; i < 6; i++) + { if (!mons[i].rect.x && !mons[i].rect.y && !mons[i].rect.w && mons[i].rect.h) - return i; + { + count = i; + break; + } } - return 6; + return count; } // Get number of monitors int GetCurrentMonitor(void) { - RGFW_monitor* mons = RGFW_getMonitors(); + int current = 0; + RGFW_monitor *mons = RGFW_getMonitors(); RGFW_monitor mon = RGFW_window_getMonitor(platform.window); - size_t i; - for (i = 0; i < 6; i++) { - if (mons[i].rect.x == mon.rect.x && - mons[i].rect.y == mon.rect.y) - return i; + for (int i = 0; i < 6; i++) + { + if ((mons[i].rect.x == mon.rect.x) && (mons[i].rect.y == mon.rect.y)) current = i; } - return 0; + return current; } // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - RGFW_monitor* mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(); - return (Vector2){mons[monitor].rect.x, mons[monitor].rect.y}; + return (Vector2){mons[monitor].rect.x, mons[monitor].rect.y}; } // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - RGFW_monitor* mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(); - return mons[monitor].rect.w; + return mons[monitor].rect.w; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - RGFW_monitor* mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(); - return mons[monitor].rect.h; - return 0; + return mons[monitor].rect.h; } // Get selected monitor physical width in millimetres @@ -603,15 +607,15 @@ int GetMonitorPhysicalWidth(int monitor) { RGFW_monitor* mons = RGFW_getMonitors(); - return mons[monitor].physW; + return mons[monitor].physW; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - RGFW_monitor* mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(); - return mons[monitor].physH; + return mons[monitor].physH; } // Get selected monitor refresh rate @@ -624,7 +628,7 @@ int GetMonitorRefreshRate(int monitor) // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - RGFW_monitor* mons = RGFW_getMonitors(); + RGFW_monitor *mons = RGFW_getMonitors(); return mons[monitor].name; } @@ -640,7 +644,7 @@ Vector2 GetWindowScaleDPI(void) { RGFW_monitor monitor = RGFW_window_getMonitor(platform.window); - return (Vector2){((u32)monitor.scaleX) * platform.window->r.w, ((u32) monitor.scaleX) * platform.window->r.h}; + return (Vector2){((u32)monitor.scaleX)*platform.window->r.w, ((u32) monitor.scaleX)*platform.window->r.h}; } // Set clipboard text content @@ -757,7 +761,7 @@ void SetMouseCursor(int cursor) static KeyboardKey ConvertScancodeToKey(u32 keycode); // Register all input events -void PollInputEvents(void) +void PollInputEvents(void) { #if defined(SUPPORT_GESTURES_SYSTEM) // NOTE: Gestures update must be called every frame to reset gestures correctly @@ -774,7 +778,7 @@ void PollInputEvents(void) CORE.Input.Mouse.currentWheelMove.y = 0; // Register previous mouse position - + // Reset last gamepad button/axis registered state for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++) @@ -808,7 +812,7 @@ void PollInputEvents(void) } // Register previous mouse states - for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; // Poll input events for current platform @@ -818,7 +822,7 @@ void PollInputEvents(void) #define RGFW_HOLD_MOUSE (1L<<2) #if defined(RGFW_X11) //|| defined(RGFW_MACOS) - if (platform.window->src.winArgs & RGFW_HOLD_MOUSE) + if (platform.window->src.winArgs & RGFW_HOLD_MOUSE) { CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; @@ -831,7 +835,8 @@ void PollInputEvents(void) while (RGFW_window_checkEvent(platform.window)) { - if (platform.window->event.type >= RGFW_jsButtonPressed && platform.window->event.type <= RGFW_jsAxisMove) { + if ((platform.window->event.type >= RGFW_jsButtonPressed) && (platform.window->event.type <= RGFW_jsAxisMove)) + { if (!CORE.Input.Gamepad.ready[platform.window->event.joystick]) { CORE.Input.Gamepad.ready[platform.window->event.joystick] = true; @@ -848,11 +853,10 @@ void PollInputEvents(void) switch (event->type) { case RGFW_quit: CORE.Window.shouldClose = true; break; - case RGFW_dnd: // Dropped file { - size_t i; - for (i = 0; i < event->droppedFilesCount; i++) { + for (int i = 0; i < event->droppedFilesCount; i++) + { if (CORE.Window.dropFileCount == 0) { // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files @@ -862,7 +866,7 @@ void PollInputEvents(void) CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]); - + CORE.Window.dropFileCount++; } else if (CORE.Window.dropFileCount < 1024) @@ -896,8 +900,9 @@ void PollInputEvents(void) case RGFW_keyPressed: { KeyboardKey key = ConvertScancodeToKey(event->keyCode); - - if (key != KEY_NULL) { + + if (key != KEY_NULL) + { // If key was up, add it to the key pressed queue if ((CORE.Input.Keyboard.currentKeyState[key] == 0) && (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE)) { @@ -923,7 +928,6 @@ void PollInputEvents(void) CORE.Input.Keyboard.charPressedQueueCount++; } } break; - case RGFW_keyReleased: { KeyboardKey key = ConvertScancodeToKey(event->keyCode); @@ -933,7 +937,8 @@ void PollInputEvents(void) // Check mouse events case RGFW_mouseButtonPressed: { - if (event->button == RGFW_mouseScrollUp || event->button == RGFW_mouseScrollDown) { + if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown)) + { CORE.Input.Mouse.currentWheelMove.y = event->scroll; break; } @@ -951,11 +956,12 @@ void PollInputEvents(void) case RGFW_mouseButtonReleased: { - if (event->button == RGFW_mouseScrollUp || event->button == RGFW_mouseScrollDown) { + if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown)) + { CORE.Input.Mouse.currentWheelMove.y = event->scroll; break; } - + int btn = event->button; if (btn == RGFW_mouseLeft) btn = 1; else if (btn == RGFW_mouseRight) btn = 2; @@ -968,20 +974,21 @@ void PollInputEvents(void) } break; case RGFW_mousePosChanged: { - if (platform.window->src.winArgs & RGFW_HOLD_MOUSE) { - + if (platform.window->src.winArgs & RGFW_HOLD_MOUSE) + { CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; - - if ((event->point.x - (platform.window->r.w / 2)) * 2) - CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.currentPosition.x; - if ((event->point.y - (platform.window->r.h / 2)) * 2) + + if ((event->point.x - (platform.window->r.w/2))*2) + CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.currentPosition.x; + if ((event->point.y - (platform.window->r.h/2))*2) CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.currentPosition.y; - CORE.Input.Mouse.currentPosition.x = (event->point.x - (platform.window->r.w / 2)) * 2; - CORE.Input.Mouse.currentPosition.y = (event->point.y - (platform.window->r.h / 2)) * 2; + CORE.Input.Mouse.currentPosition.x = (event->point.x - (platform.window->r.w/2))*2; + CORE.Input.Mouse.currentPosition.y = (event->point.y - (platform.window->r.h/2))*2; } - else { - CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + else + { + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; CORE.Input.Mouse.currentPosition.x = (float)event->point.x; CORE.Input.Mouse.currentPosition.y = (float)event->point.y; } @@ -989,7 +996,6 @@ void PollInputEvents(void) CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; touchAction = 2; } break; - case RGFW_jsButtonPressed: { int button = -1; @@ -1061,25 +1067,30 @@ void PollInputEvents(void) case RGFW_jsAxisMove: { int axis = -1; - - size_t i; - for (i = 0; i < event->axisesCount; i++) + for (int i = 0; i < event->axisesCount; i++) { - switch(i) { - case 0: - if (abs(event->axis[i].x) > abs(event->axis[i].y)) { - axis = GAMEPAD_AXIS_LEFT_X; + switch(i) + { + case 0: + { + if (abs(event->axis[i].x) > abs(event->axis[i].y)) + { + axis = GAMEPAD_AXIS_LEFT_X; break; } - + axis = GAMEPAD_AXIS_LEFT_Y; - break; - case 1: - if (abs(event->axis[i].x) > abs(event->axis[i].y)) { - axis = GAMEPAD_AXIS_RIGHT_X; break; + } break; + case 1: + { + if (abs(event->axis[i].x) > abs(event->axis[i].y)) + { + axis = GAMEPAD_AXIS_RIGHT_X; + break; } - axis = GAMEPAD_AXIS_RIGHT_Y; break; + axis = GAMEPAD_AXIS_RIGHT_Y; + } break; case 2: axis = GAMEPAD_AXIS_LEFT_TRIGGER; break; case 3: axis = GAMEPAD_AXIS_RIGHT_TRIGGER; break; default: break; @@ -1137,9 +1148,7 @@ void PollInputEvents(void) #endif } - if (RGFW_disableCursor && platform.window->event.inFocus) - RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0)); - + if (RGFW_disableCursor && platform.window->event.inFocus) RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0)); //----------------------------------------------------------------------------- } @@ -1151,15 +1160,7 @@ void PollInputEvents(void) // Initialize platform: graphics, inputs and more int InitPlatform(void) { - // TODO: Initialize graphic device: display/window - // It usually requires setting up the platform display system configuration - // and connexion with the GPU through some system graphic API - // raylib uses OpenGL so, platform should create that kind of connection - // Below example illustrates that process using EGL library - //---------------------------------------------------------------------------- // Initialize RGFW internal global state, only required systems - // Initialize graphic device: display/window and graphic context - //---------------------------------------------------------------------------- unsigned int flags = RGFW_CENTER | RGFW_ALLOW_DND; // Check window creation flags @@ -1201,14 +1202,14 @@ int InitPlatform(void) if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1); - + RGFW_window_makeCurrent(platform.window); // Check surface and context activation if (platform.window != NULL) { CORE.Window.ready = true; - + CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; @@ -1241,7 +1242,7 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - // TODO: Load OpenGL extensions + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions //---------------------------------------------------------------------------- rlLoadExtensions((void*)RGFW_getProcAddress); @@ -1255,12 +1256,12 @@ int InitPlatform(void) // ... //---------------------------------------------------------------------------- - // TODO: Initialize timing system + // Initialize timing system //---------------------------------------------------------------------------- InitTimer(); //---------------------------------------------------------------------------- - // TODO: Initialize storage system + // Initialize storage system //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- @@ -1281,14 +1282,12 @@ int InitPlatform(void) void ClosePlatform(void) { RGFW_window_close(platform.window); - // TODO: De-initialize graphics, inputs and more } +// Keycode mapping +static KeyboardKey ConvertScancodeToKey(u32 keycode) +{ + if (keycode > sizeof(keyMappingRGFW)/sizeof(unsigned short)) return 0; -static KeyboardKey ConvertScancodeToKey(u32 keycode) { - if (keycode > sizeof(RGFWKeyToRayKey) / sizeof(unsigned short)) - return 0; - - return RGFWKeyToRayKey[keycode]; + return keyMappingRGFW[keycode]; } -// EOF \ No newline at end of file diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index c22150efb..8a0fea81a 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1132,7 +1132,8 @@ void PollInputEvents(void) { KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); - if (key != KEY_NULL) { + if (key != KEY_NULL) + { // If key was up, add it to the key pressed queue if ((CORE.Input.Keyboard.currentKeyState[key] == 0) && (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE)) { diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index ba6071460..17a8e4d00 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -398,7 +398,7 @@ int GetMonitorWidth(int monitor) { width = platform.connector->modes[platform.modeIndex].hdisplay; } - + return width; } @@ -415,7 +415,7 @@ int GetMonitorHeight(int monitor) { height = platform.connector->modes[platform.modeIndex].vdisplay; } - + return height; } @@ -479,7 +479,7 @@ const char *GetMonitorName(int monitor) { name = platform.connector->modes[platform.modeIndex].name; } - + return name; } @@ -1136,7 +1136,8 @@ void ClosePlatform(void) // Close the evdev devices - if (platform.mouseFd != -1) { + if (platform.mouseFd != -1) + { close(platform.mouseFd); platform.mouseFd = -1; } @@ -1924,9 +1925,7 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt const int nearestHeightDiff = abs(platform.connector->modes[nearestIndex].vdisplay - height); const int nearestFpsDiff = abs(platform.connector->modes[nearestIndex].vrefresh - fps); - if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) { - nearestIndex = i; - } + if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) nearestIndex = i; } return nearestIndex; diff --git a/src/raudio.c b/src/raudio.c index 623984e1d..e33712978 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1464,7 +1464,7 @@ Music LoadMusicStream(const char *fileName) jar_xm_reset(ctxXm); // Make sure we start at the beginning of the song musicLoaded = true; } - else + else { jar_xm_free_context(ctxXm); } @@ -1566,7 +1566,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, music.looping = true; // Looping enabled by default musicLoaded = true; } - else + else { stb_vorbis_close(ctxOgg); } diff --git a/src/raylib.h b/src/raylib.h index 26cf03e85..3f6b61f8a 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1332,9 +1332,9 @@ RLAPI Image GenImageText(int width, int height, const char *text); // Image manipulation functions RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) RLAPI Image ImageFromImage(Image image, Rectangle rec); // Create an image from another image piece +RLAPI Image ImageFromChannel(Image image, int selectedChannel); // Create an image from a selected channel of another image (GRAYSCALE/R16/R32) RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) -RLAPI Image ImageFromChannel(Image image, int selectedChannel); // Create an image from a selected channel of another image RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format RLAPI void ImageToPOT(Image *image, Color fill); // Convert image to POT (power-of-two) RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle diff --git a/src/raymath.h b/src/raymath.h index 63947ee8d..0a0b05803 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -2221,9 +2221,9 @@ RMAPI Quaternion QuaternionCubicHermiteSpline(Quaternion q1, Quaternion outTange float t2 = t * t; float t3 = t2 * t; float h00 = 2 * t3 - 3 * t2 + 1; - float h10 = t3 - 2 * t2 + t; - float h01 = -2 * t3 + 3 * t2; - float h11 = t3 - t2; + float h10 = t3 - 2 * t2 + t; + float h01 = -2 * t3 + 3 * t2; + float h11 = t3 - t2; Quaternion p0 = QuaternionScale(q1, h00); Quaternion m0 = QuaternionScale(outTangent1, h10); @@ -2533,7 +2533,7 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio translation->y = mat.m13; translation->z = mat.m14; - // Extract upper-left for determinant computation. + // Extract upper-left for determinant computation const float a = mat.m0; const float b = mat.m4; const float c = mat.m8; @@ -2543,28 +2543,29 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio const float g = mat.m2; const float h = mat.m6; const float i = mat.m10; - const float A = e * i - f * h; - const float B = f * g - d * i; - const float C = d * h - e * g; + const float A = e*i - f*h; + const float B = f*g - d*i; + const float C = d*h - e*g; - // Extract scale. - const float det = a * A + b * B + c * C; - float scalex = Vector3Length((Vector3) {a, b, c}); - float scaley = Vector3Length((Vector3) {d, e, f}); - float scalez = Vector3Length((Vector3) {g, h, i}); - Vector3 s = {scalex, scaley, scalez}; + // Extract scale + const float det = a*A + b*B + c*C; + float scalex = Vector3Length((Vector3){ a, b, c }); + float scaley = Vector3Length((Vector3){ d, e, f }); + float scalez = Vector3Length((Vector3){ g, h, i }); + Vector3 s = { scalex, scaley, scalez }; if (det < 0) s = Vector3Negate(s); *scale = s; - // Remove scale from the matrix if it is not close to zero. + // Remove scale from the matrix if it is not close to zero Matrix clone = mat; if (!FloatEquals(det, 0)) { clone.m0 /= s.x; clone.m5 /= s.y; clone.m10 /= s.z; + // Extract rotation *rotation = QuaternionFromMatrix(clone); } diff --git a/src/rmodels.c b/src/rmodels.c index 5f061e2dd..432413cd0 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4327,7 +4327,7 @@ static Model LoadIQM(const char *fileName) model.materials[i].maps[MATERIAL_MAP_ALBEDO].texture = LoadTexture(TextFormat("%s/%s", basePath, material)); model.meshMaterial[i] = i; - + TRACELOG(LOG_DEBUG, "MODEL: [%s] mesh name (%s), material (%s)", fileName, name, material); model.meshes[i].vertexCount = imesh[i].num_vertexes; @@ -4636,7 +4636,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou animations[a].boneCount = iqmHeader->num_poses; animations[a].bones = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo)); animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); - memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32); // I don't like this 32 here + memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32); // I don't like this 32 here TraceLog(LOG_INFO, "IQM Anim %s", animations[a].name); // animations[a].framerate = anim.framerate; // TODO: Use animation framerate data? @@ -4913,7 +4913,7 @@ static Model LoadGLTF(const char *fileName) PBR specular/glossiness flow and extended texture flows not supported - Supports multiple meshes per model (every primitives is loaded as a separate mesh) - Supports basic animations - - Transforms, including parent-child relations, are applied on the mesh data, but the + - Transforms, including parent-child relations, are applied on the mesh data, but the hierarchy is not kept (as it can't be represented). - Mesh instances in the glTF file (i.e. same mesh linked from multiple nodes) are turned into separate raylib Meshes. @@ -5101,7 +5101,7 @@ static Model LoadGLTF(const char *fileName) // Each primitive within a glTF node becomes a Raylib Mesh. // The local-to-world transform of each node is used to transform the // points/normals/tangents of the created Mesh(es). - // Any glTF mesh linked from more than one Node (i.e. instancing) + // Any glTF mesh linked from more than one Node (i.e. instancing) // is turned into multiple Mesh's, as each Node will have its own // transform applied. // Note: the code below disregards the scenes defined in the file, all nodes are used. @@ -5262,7 +5262,7 @@ static Model LoadGLTF(const char *fileName) else TRACELOG(LOG_WARNING, "MODEL: [%s] Texcoords attribute data format not supported", fileName); } else TRACELOG(LOG_WARNING, "MODEL: [%s] Texcoords attribute data format not supported, use vec2 float", fileName); - + int index = mesh->primitives[p].attributes[j].index; if (index == 0) model.meshes[meshIndex].texcoords = texcoordPtr; else if (index == 1) model.meshes[meshIndex].texcoords2 = texcoordPtr; diff --git a/src/rshapes.c b/src/rshapes.c index b119f50ec..fd6ff7d23 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1834,7 +1834,7 @@ void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thic if (pointCount >= 3) { for (int i = 0; i < pointCount - 2; i += 2) DrawSplineSegmentBezierQuadratic(points[i], points[i + 1], points[i + 2], thick, color); - + // Cap circle drawing at the end of every segment //for (int i = 2; i < pointCount - 2; i += 2) DrawCircleV(points[i], thick/2.0f, color); } @@ -1846,7 +1846,7 @@ void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, C if (pointCount >= 4) { for (int i = 0; i < pointCount - 3; i += 3) DrawSplineSegmentBezierCubic(points[i], points[i + 1], points[i + 2], points[i + 3], thick, color); - + // Cap circle drawing at the end of every segment //for (int i = 3; i < pointCount - 3; i += 3) DrawCircleV(points[i], thick/2.0f, color); } diff --git a/src/rtextures.c b/src/rtextures.c index 9d547cd0b..b6d331385 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1636,19 +1636,18 @@ Image ImageFromChannel(Image image, int selectedChannel) Image result = { 0 }; // Security check to avoid program crash - if ((image.data == NULL) || (image.width == 0) || (image.height == 0)) - return result; + if ((image.data == NULL) || (image.width == 0) || (image.height == 0)) return result; - // Check selected channel + // Check selected channel is valid if (selectedChannel < 0) { TRACELOG(LOG_WARNING, "Channel cannot be negative. Setting channel to 0."); selectedChannel = 0; } - if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE - || image.format == PIXELFORMAT_UNCOMPRESSED_R32 - || image.format == PIXELFORMAT_UNCOMPRESSED_R16 - ) + + if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE || + image.format == PIXELFORMAT_UNCOMPRESSED_R32 || + image.format == PIXELFORMAT_UNCOMPRESSED_R16) { if (selectedChannel > 0) { @@ -1664,11 +1663,10 @@ Image ImageFromChannel(Image image, int selectedChannel) selectedChannel = 1; } } - else if (image.format == PIXELFORMAT_UNCOMPRESSED_R5G6B5 - || image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8 - || image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32 - || image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16 - ) + else if (image.format == PIXELFORMAT_UNCOMPRESSED_R5G6B5 || + image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8 || + image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32 || + image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16) { if (selectedChannel > 2) { @@ -1677,153 +1675,121 @@ Image ImageFromChannel(Image image, int selectedChannel) } } - // formats rgba + // Check for RGBA formats if (selectedChannel > 3) { TRACELOG(LOG_WARNING, "ImageFromChannel supports channels 0 to 3 (rgba). Setting channel to alpha."); selectedChannel = 3; } + // TODO: Consider other one-channel formats: R16, R32 result.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; result.height = image.height; result.width = image.width; result.mipmaps = 1; - unsigned char *pixels = (unsigned char *)RL_CALLOC(image.width * image.height, sizeof(unsigned char)); // values 0 to 255 + unsigned char *pixels = (unsigned char *)RL_CALLOC(image.width*image.height, sizeof(unsigned char)); // Values from 0 to 255 if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats"); else { - for (int i = 0, k = 0; i < image.width * image.height; ++i) + for (int i = 0, k = 0; i < image.width*image.height; i++) { - float imageValue = -1; + float pixelValue = -1; switch (image.format) { case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: { - imageValue = (float)((unsigned char *)image.data)[i + selectedChannel]/255.0f; + pixelValue = (float)((unsigned char *)image.data)[i + selectedChannel]/255.0f; } break; case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: { - imageValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f; - + pixelValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f; k += 2; + } break; case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: { unsigned short pixel = ((unsigned short *)image.data)[i]; - if (selectedChannel == 0) - { - imageValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31); - } - else if (selectedChannel == 1) - { - imageValue = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31); - } - else if (selectedChannel == 2) - { - imageValue = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31); - } - else if (selectedChannel == 3) - { - imageValue = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f; - } + if (selectedChannel == 0) pixelValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31); + else if (selectedChannel == 1) pixelValue = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31); + else if (selectedChannel == 2) pixelValue = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31); + else if (selectedChannel == 3) pixelValue = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f; } break; case PIXELFORMAT_UNCOMPRESSED_R5G6B5: { unsigned short pixel = ((unsigned short *)image.data)[i]; - if (selectedChannel == 0) - { - imageValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31); - } - else if (selectedChannel == 1) - { - imageValue = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63); - } - else if (selectedChannel == 2) - { - imageValue = (float)(pixel & 0b0000000000011111)*(1.0f/31); - } + if (selectedChannel == 0) pixelValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31); + else if (selectedChannel == 1) pixelValue = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63); + else if (selectedChannel == 2) pixelValue = (float)(pixel & 0b0000000000011111)*(1.0f/31); } break; case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: { unsigned short pixel = ((unsigned short *)image.data)[i]; - if (selectedChannel == 0) - { - imageValue = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15); - } - else if (selectedChannel == 1) - { - imageValue = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15); - } - else if (selectedChannel == 2) - { - imageValue = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15); - } - else if (selectedChannel == 3) - { - imageValue = (float)(pixel & 0b0000000000001111)*(1.0f/15); - } + if (selectedChannel == 0) pixelValue = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15); + else if (selectedChannel == 1) pixelValue = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15); + else if (selectedChannel == 2) pixelValue = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15); + else if (selectedChannel == 3) pixelValue = (float)(pixel & 0b0000000000001111)*(1.0f/15); } break; case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: { - imageValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f; - + pixelValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f; k += 4; + } break; case PIXELFORMAT_UNCOMPRESSED_R8G8B8: { - imageValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f; - + pixelValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f; k += 3; + } break; case PIXELFORMAT_UNCOMPRESSED_R32: { - imageValue = ((float *)image.data)[k]; - + pixelValue = ((float *)image.data)[k]; k += 1; + } break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32: { - imageValue = ((float *)image.data)[k + selectedChannel]; - + pixelValue = ((float *)image.data)[k + selectedChannel]; k += 3; + } break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: { - imageValue = ((float *)image.data)[k + selectedChannel]; - + pixelValue = ((float *)image.data)[k + selectedChannel]; k += 4; + } break; case PIXELFORMAT_UNCOMPRESSED_R16: { - imageValue = HalfToFloat(((unsigned short *)image.data)[k]); - + pixelValue = HalfToFloat(((unsigned short *)image.data)[k]); k += 1; + } break; case PIXELFORMAT_UNCOMPRESSED_R16G16B16: { - imageValue = HalfToFloat(((unsigned short *)image.data)[k+selectedChannel]); - + pixelValue = HalfToFloat(((unsigned short *)image.data)[k+selectedChannel]); k += 3; + } break; case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: { - imageValue = HalfToFloat(((unsigned short *)image.data)[k + selectedChannel]); - + pixelValue = HalfToFloat(((unsigned short *)image.data)[k + selectedChannel]); k += 4; + } break; default: break; } - pixels[i] = imageValue * 255; + pixels[i] = (unsigned char)(pixelValue*255); } } @@ -4911,12 +4877,12 @@ Color Fade(Color color, float alpha) int ColorToInt(Color color) { int result = 0; - - result = (int)(((unsigned int)color.r << 24) | - ((unsigned int)color.g << 16) | - ((unsigned int)color.b << 8) | + + result = (int)(((unsigned int)color.r << 24) | + ((unsigned int)color.g << 16) | + ((unsigned int)color.b << 8) | (unsigned int)color.a); - + return result; } From d8214c916e6b80188cb8f9b18e11f50767b165b8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 30 Jun 2024 09:07:57 +0000 Subject: [PATCH 05/40] Update raylib_api.* by CI --- parser/output/raylib_api.json | 30 +++++++++++++++--------------- parser/output/raylib_api.lua | 18 +++++++++--------- parser/output/raylib_api.txt | 16 ++++++++-------- parser/output/raylib_api.xml | 8 ++++---- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 5e3fc2c4a..a556c84b7 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -7183,6 +7183,21 @@ } ] }, + { + "name": "ImageFromChannel", + "description": "Create an image from a selected channel of another image (GRAYSCALE/R16/R32)", + "returnType": "Image", + "params": [ + { + "type": "Image", + "name": "image" + }, + { + "type": "int", + "name": "selectedChannel" + } + ] + }, { "name": "ImageText", "description": "Create an image from text (default font)", @@ -7229,21 +7244,6 @@ } ] }, - { - "name": "ImageFromChannel", - "description": "Create an image from a selected channel of another image", - "returnType": "Image", - "params": [ - { - "type": "Image", - "name": "image" - }, - { - "type": "int", - "name": "selectedChannel" - } - ] - }, { "name": "ImageFormat", "description": "Convert image data to desired format", diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 37ec004ba..cefb37e17 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -5521,6 +5521,15 @@ return { {type = "Rectangle", name = "rec"} } }, + { + name = "ImageFromChannel", + description = "Create an image from a selected channel of another image (GRAYSCALE/R16/R32)", + returnType = "Image", + params = { + {type = "Image", name = "image"}, + {type = "int", name = "selectedChannel"} + } + }, { name = "ImageText", description = "Create an image from text (default font)", @@ -5543,15 +5552,6 @@ return { {type = "Color", name = "tint"} } }, - { - name = "ImageFromChannel", - description = "Create an image from a selected channel of another image", - returnType = "Image", - params = { - {type = "Image", name = "image"}, - {type = "int", name = "selectedChannel"} - } - }, { name = "ImageFormat", description = "Convert image data to desired format", diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 5f62c2e19..59f418d6a 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -2773,14 +2773,20 @@ Function 293: ImageFromImage() (2 input parameters) Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 294: ImageText() (3 input parameters) +Function 294: ImageFromChannel() (2 input parameters) + Name: ImageFromChannel + Return type: Image + Description: Create an image from a selected channel of another image (GRAYSCALE/R16/R32) + Param[1]: image (type: Image) + Param[2]: selectedChannel (type: int) +Function 295: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 295: ImageTextEx() (5 input parameters) +Function 296: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2789,12 +2795,6 @@ Function 295: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 296: ImageFromChannel() (2 input parameters) - Name: ImageFromChannel - Return type: Image - Description: Create an image from a selected channel of another image - Param[1]: image (type: Image) - Param[2]: selectedChannel (type: int) Function 297: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index bafb8a658..27499123c 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1798,6 +1798,10 @@ + + + + @@ -1810,10 +1814,6 @@ - - - - From 953df38ac47e7e05a8f7b5eef12990aa07522af8 Mon Sep 17 00:00:00 2001 From: smalltimewizard <83366732+smalltimewizard@users.noreply.github.com> Date: Sun, 30 Jun 2024 05:09:21 -0400 Subject: [PATCH 06/40] [rmodels] `DrawSphereEx()` optimization (#4106) * Optimize DrawSphereEx() Precalculates sin/cos to eliminate unnecessary calls. * Formatting correction to previous commit * Bugfix to optimized DrawSphereEx() OBO error -- added 1 additional precalculated cos/sin value to each array to complete the 360-degree wraparound. Technically the value of these last elements will always be the same as the first element due to 360-degree wraparound, but this is the simplest solution. * Corrected missing free() * Formatting correction * New DrawSphereEx() algorithm --- src/rmodels.c | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 432413cd0..6de27021e 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -432,30 +432,36 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); - for (int i = 0; i < (rings + 2); i++) - { - for (int j = 0; j < slices; j++) - { - rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)), - sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)), - cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices))); - rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), - sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), - cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); - rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)), - sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), - cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices))); + float ringangle = DEG2RAD*(180.0f/(rings + 1)); // Angle between latitudinal parallels + float sliceangle = DEG2RAD*(360.0f/slices); // Angle between longitudinal meridians - rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)), - sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)), - cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices))); - rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), - sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i))), - cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); - rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), - sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), - cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); + float cosring = cosf(ringangle); + float sinring = sinf(ringangle); + float cosslice = cosf(sliceangle); + float sinslice = sinf(sliceangle); + + Vector3 vertices[4]; // Store face vertices + vertices[2] = (Vector3){0,1,0}; + vertices[3] = (Vector3){sinring, cosring, 0}; + + for (int i = 0; i < rings + 1; i++) { + for (int j = 0; j < slices; j++) { + vertices[0] = vertices[2]; // Rotate around y axis to set up vertices for next face + vertices[1] = vertices[3]; + vertices[2] = (Vector3){cosslice*vertices[2].x - sinslice*vertices[2].z, vertices[2].y, sinslice*vertices[2].x + cosslice*vertices[2].z}; // Rotation matrix around y axis + vertices[3] = (Vector3){cosslice*vertices[3].x - sinslice*vertices[3].z, vertices[3].y, sinslice*vertices[3].x + cosslice*vertices[3].z}; + + rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); + rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); + rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z); + + rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); + rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z); + rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); } + + vertices[2] = vertices[3]; // Rotate around z axis to set up starting vertices for next ring + vertices[3] = (Vector3){cosring*vertices[3].x + sinring*vertices[3].y, -sinring*vertices[3].x + cosring*vertices[3].y, vertices[3].z}; // Rotation matrix around z axis } rlEnd(); rlPopMatrix(); From 13e309251184343b67773d16e6e264c7cac7c280 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 30 Jun 2024 11:15:45 +0200 Subject: [PATCH 07/40] REVIEWED: `DrawSphereEx()`, added educational info --- src/rmodels.c | 60 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 6de27021e..b67c8e695 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -424,6 +424,48 @@ void DrawSphere(Vector3 centerPos, float radius, Color color) // Draw sphere with extended parameters void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color) { +#if 0 + // Basic implementation, do not use it! + // For a sphere with 16 rings and 16 slices it requires 8640 cos()/sin() function calls! + // New optimized version below only requires 4 cos()/sin() calls + + rlPushMatrix(); + // NOTE: Transformation is applied in inverse order (scale -> translate) + rlTranslatef(centerPos.x, centerPos.y, centerPos.z); + rlScalef(radius, radius, radius); + + rlBegin(RL_TRIANGLES); + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < (rings + 2); i++) + { + for (int j = 0; j < slices; j++) + { + rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)), + sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)), + cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices))); + rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), + sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), + cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); + rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)), + sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), + cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices))); + + rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)), + sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)), + cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices))); + rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), + sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i))), + cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); + rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)), + sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))), + cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices))); + } + } + rlEnd(); + rlPopMatrix(); +#endif + rlPushMatrix(); // NOTE: Transformation is applied in inverse order (scale -> translate) rlTranslatef(centerPos.x, centerPos.y, centerPos.z); @@ -440,16 +482,18 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color float cosslice = cosf(sliceangle); float sinslice = sinf(sliceangle); - Vector3 vertices[4]; // Store face vertices - vertices[2] = (Vector3){0,1,0}; - vertices[3] = (Vector3){sinring, cosring, 0}; + Vector3 vertices[4] = { 0 }; // Required to store face vertices + vertices[2] = (Vector3){ 0, 1, 0 }; + vertices[3] = (Vector3){ sinring, cosring, 0 }; - for (int i = 0; i < rings + 1; i++) { - for (int j = 0; j < slices; j++) { + for (int i = 0; i < rings + 1; i++) + { + for (int j = 0; j < slices; j++) + { vertices[0] = vertices[2]; // Rotate around y axis to set up vertices for next face vertices[1] = vertices[3]; - vertices[2] = (Vector3){cosslice*vertices[2].x - sinslice*vertices[2].z, vertices[2].y, sinslice*vertices[2].x + cosslice*vertices[2].z}; // Rotation matrix around y axis - vertices[3] = (Vector3){cosslice*vertices[3].x - sinslice*vertices[3].z, vertices[3].y, sinslice*vertices[3].x + cosslice*vertices[3].z}; + vertices[2] = (Vector3){ cosslice*vertices[2].x - sinslice*vertices[2].z, vertices[2].y, sinslice*vertices[2].x + cosslice*vertices[2].z }; // Rotation matrix around y axis + vertices[3] = (Vector3){ cosslice*vertices[3].x - sinslice*vertices[3].z, vertices[3].y, sinslice*vertices[3].x + cosslice*vertices[3].z }; rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z); rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z); @@ -461,7 +505,7 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color } vertices[2] = vertices[3]; // Rotate around z axis to set up starting vertices for next ring - vertices[3] = (Vector3){cosring*vertices[3].x + sinring*vertices[3].y, -sinring*vertices[3].x + cosring*vertices[3].y, vertices[3].z}; // Rotation matrix around z axis + vertices[3] = (Vector3){ cosring*vertices[3].x + sinring*vertices[3].y, -sinring*vertices[3].x + cosring*vertices[3].y, vertices[3].z }; // Rotation matrix around z axis } rlEnd(); rlPopMatrix(); From 1e1061d5c7582090df0851c08359d7c7847f9559 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 30 Jun 2024 11:37:58 +0200 Subject: [PATCH 08/40] REVIEWED: Formatting, follow raylib coding conventions --- src/platforms/rcore_android.c | 16 ++++---- src/platforms/rcore_desktop_rgfw.c | 6 +-- src/platforms/rcore_desktop_sdl.c | 59 ++++++++++++++---------------- src/platforms/rcore_drm.c | 8 ++-- src/platforms/rcore_web.c | 6 ++- src/raylib.h | 2 +- src/raymath.h | 22 +++++------ src/rcamera.h | 4 +- src/rlgl.h | 2 +- src/rshapes.c | 16 ++++---- src/rtextures.c | 14 +++---- 11 files changed, 77 insertions(+), 78 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index dc8724d8f..8fc51d0e1 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -81,7 +81,7 @@ static PlatformData platform = { 0 }; // Platform specific data // Local Variables Definition //---------------------------------------------------------------------------------- #define KEYCODE_MAP_SIZE 162 -static const KeyboardKey KeycodeMap[KEYCODE_MAP_SIZE] = { +static const KeyboardKey mapKeycode[KEYCODE_MAP_SIZE] = { KEY_NULL, // AKEYCODE_UNKNOWN 0, // AKEYCODE_SOFT_LEFT 0, // AKEYCODE_SOFT_RIGHT @@ -1133,9 +1133,9 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue( event, AMOTION_EVENT_AXIS_RZ, 0); CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f; + event, AMOTION_EVENT_AXIS_BRAKE, 0)*2.0f - 1.0f; CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f; + event, AMOTION_EVENT_AXIS_GAS, 0)*2.0f - 1.0f; // dpad is reported as an axis on android float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); @@ -1201,7 +1201,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) return 1; // Handled gamepad button } - KeyboardKey key = (keycode > 0 && keycode < KEYCODE_MAP_SIZE) ? KeycodeMap[keycode] : KEY_NULL; + KeyboardKey key = (keycode > 0 && keycode < KEYCODE_MAP_SIZE)? mapKeycode[keycode] : KEY_NULL; if (key != KEY_NULL) { // Save current key and its state @@ -1252,10 +1252,10 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height - float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x) / (float)CORE.Window.display.width; - float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y) / (float)CORE.Window.display.height; - CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x * widthRatio - (float)CORE.Window.renderOffset.x / 2; - CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y * heightRatio - (float)CORE.Window.renderOffset.y / 2; + float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x)/(float)CORE.Window.display.width; + float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y)/(float)CORE.Window.display.height; + CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2; + CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2; } int32_t action = AMotionEvent_getAction(event); diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index dbbbe10cc..d53237fcd 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -1097,16 +1097,16 @@ void PollInputEvents(void) } #ifdef __linux__ - float value = (event->axis[i].x + event->axis[i].y) / (float) 32767; + float value = (event->axis[i].x + event->axis[i].y)/(float)32767; #else - float value = (event->axis[i].x + -event->axis[i].y) / (float) 32767; + float value = (event->axis[i].x + -event->axis[i].y)/(float)32767; #endif CORE.Input.Gamepad.axisState[event->joystick][axis] = value; // Register button state for triggers in addition to their axes if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER)) { - int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER) ? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; int pressed = (value > 0.1f); CORE.Input.Gamepad.currentButtonState[event->joystick][button] = pressed; if (pressed) CORE.Input.Gamepad.lastButtonPressed = button; diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 8a0fea81a..d9637f705 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -80,7 +80,7 @@ static PlatformData platform = { 0 }; // Platform specific data // Local Variables Definition //---------------------------------------------------------------------------------- #define SCANCODE_MAPPED_NUM 232 -static const KeyboardKey ScancodeToKey[SCANCODE_MAPPED_NUM] = { +static const KeyboardKey mapScancodeToKey[SCANCODE_MAPPED_NUM] = { KEY_NULL, // SDL_SCANCODE_UNKNOWN 0, 0, @@ -476,9 +476,9 @@ void ClearWindowState(unsigned int flags) // Set icon for window void SetWindowIcon(Image image) { - SDL_Surface* iconSurface = NULL; + SDL_Surface *iconSurface = NULL; - Uint32 rmask, gmask, bmask, amask; + unsigned int rmask = 0, gmask = 0, bmask = 0, amask = 0; int depth = 0; // Depth in bits int pitch = 0; // Pixel spacing (pitch) in bytes @@ -492,72 +492,67 @@ void SetWindowIcon(Image image) case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: rmask = 0xFF, gmask = 0xFF00; bmask = 0, amask = 0; - depth = 16, pitch = image.width * 2; + depth = 16, pitch = image.width*2; break; case PIXELFORMAT_UNCOMPRESSED_R5G6B5: rmask = 0xF800, gmask = 0x07E0; bmask = 0x001F, amask = 0; - depth = 16, pitch = image.width * 2; + depth = 16, pitch = image.width*2; break; case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // Uses BGR for 24-bit rmask = 0x0000FF, gmask = 0x00FF00; bmask = 0xFF0000, amask = 0; - depth = 24, pitch = image.width * 3; + depth = 24, pitch = image.width*3; break; case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: rmask = 0xF800, gmask = 0x07C0; bmask = 0x003E, amask = 0x0001; - depth = 16, pitch = image.width * 2; + depth = 16, pitch = image.width*2; break; case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: rmask = 0xF000, gmask = 0x0F00; bmask = 0x00F0, amask = 0x000F; - depth = 16, pitch = image.width * 2; + depth = 16, pitch = image.width*2; break; case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: rmask = 0xFF000000, gmask = 0x00FF0000; bmask = 0x0000FF00, amask = 0x000000FF; - depth = 32, pitch = image.width * 4; + depth = 32, pitch = image.width*4; break; case PIXELFORMAT_UNCOMPRESSED_R32: rmask = 0xFFFFFFFF, gmask = 0; bmask = 0, amask = 0; - depth = 32, pitch = image.width * 4; + depth = 32, pitch = image.width*4; break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32: rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; bmask = 0xFFFFFFFF, amask = 0; - depth = 96, pitch = image.width * 12; + depth = 96, pitch = image.width*12; break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; bmask = 0xFFFFFFFF, amask = 0xFFFFFFFF; - depth = 128, pitch = image.width * 16; + depth = 128, pitch = image.width*16; break; case PIXELFORMAT_UNCOMPRESSED_R16: rmask = 0xFFFF, gmask = 0; bmask = 0, amask = 0; - depth = 16, pitch = image.width * 2; + depth = 16, pitch = image.width*2; break; case PIXELFORMAT_UNCOMPRESSED_R16G16B16: rmask = 0xFFFF, gmask = 0xFFFF; bmask = 0xFFFF, amask = 0; - depth = 48, pitch = image.width * 6; + depth = 48, pitch = image.width*6; break; case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: rmask = 0xFFFF, gmask = 0xFFFF; bmask = 0xFFFF, amask = 0xFFFF; - depth = 64, pitch = image.width * 8; + depth = 64, pitch = image.width*8; break; - default: - // Compressed formats are not supported - return; + default: return; // Compressed formats are not supported } - iconSurface = SDL_CreateRGBSurfaceFrom( - image.data, image.width, image.height, depth, pitch, - rmask, gmask, bmask, amask - ); + iconSurface = SDL_CreateRGBSurfaceFrom( image.data, image.width, image.height, depth, pitch, rmask, gmask, bmask, amask ); if (iconSurface) { @@ -599,7 +594,7 @@ void SetWindowMonitor(int monitor) // 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3, // see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba // 2. A workaround for SDL2 is leaving fullscreen, moving the window, then entering full screen again. - const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) ? true : false; + const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)? true : false; const int screenWidth = CORE.Window.screen.width; const int screenHeight = CORE.Window.screen.height; @@ -941,11 +936,11 @@ int SetGamepadMappings(const char *mappings) // Set gamepad vibration void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor) { - //Limit input values to between 0.0f and 1.0f - leftMotor = (0.0f > leftMotor) ? 0.0f : leftMotor; - rightMotor = (0.0f > rightMotor) ? 0.0f : rightMotor; - leftMotor = (1.0f < leftMotor) ? 1.0f : leftMotor; - rightMotor = (1.0f < rightMotor) ? 1.0f : rightMotor; + // Limit input values to between 0.0f and 1.0f + leftMotor = (0.0f > leftMotor)? 0.0f : leftMotor; + rightMotor = (0.0f > rightMotor)? 0.0f : rightMotor; + leftMotor = (1.0f < leftMotor)? 1.0f : leftMotor; + rightMotor = (1.0f < rightMotor)? 1.0f : rightMotor; if (IsGamepadAvailable(gamepad)) { @@ -1365,13 +1360,13 @@ void PollInputEvents(void) if (axis >= 0) { // SDL axis value range is -32768 to 32767, we normalize it to RayLib's -1.0 to 1.0f range - float value = event.jaxis.value / (float) 32767; + float value = event.jaxis.value/(float)32767; CORE.Input.Gamepad.axisState[event.jaxis.which][axis] = value; // Register button state for triggers in addition to their axes if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER)) { - int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER) ? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; int pressed = (value > 0.1f); CORE.Input.Gamepad.currentButtonState[event.jaxis.which][button] = pressed; if (pressed) CORE.Input.Gamepad.lastButtonPressed = button; @@ -1554,6 +1549,7 @@ int InitPlatform(void) for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++) { platform.gamepad[i] = SDL_JoystickOpen(i); + if (platform.gamepad[i]) { CORE.Input.Gamepad.ready[i] = true; @@ -1608,8 +1604,9 @@ static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) { if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) { - return ScancodeToKey[sdlScancode]; + return mapScancodeToKey[sdlScancode]; } + return KEY_NULL; // No equivalent key in Raylib } // EOF diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 17a8e4d00..f888d0e47 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -1028,7 +1028,7 @@ int InitPlatform(void) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false @@ -1614,7 +1614,7 @@ static void PollKeyboardEvents(void) } } - TRACELOG(LOG_DEBUG, "INPUT: KEY_%s Keycode(linux): %4i KeyCode(raylib): %4i", (event.value == 0) ? "UP " : "DOWN", event.code, keycode); + TRACELOG(LOG_DEBUG, "INPUT: KEY_%s Keycode(linux): %4i KeyCode(raylib): %4i", (event.value == 0)? "UP " : "DOWN", event.code, keycode); } } } @@ -1641,7 +1641,7 @@ static void PollGamepadEvents(void) { short keycodeRaylib = linuxToRaylibMap[event.code]; - TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: KEY_%s Keycode(linux): %4i Keycode(raylib): %4i", i, (event.value == 0) ? "UP " : "DOWN", event.code, keycodeRaylib); + TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: KEY_%s Keycode(linux): %4i Keycode(raylib): %4i", i, (event.value == 0)? "UP" : "DOWN", event.code, keycodeRaylib); if ((keycodeRaylib != 0) && (keycodeRaylib < MAX_GAMEPAD_BUTTONS)) { @@ -1666,7 +1666,7 @@ static void PollGamepadEvents(void) int range = platform.gamepadAbsAxisRange[i][event.code][1]; // NOTE: Scaling of event.value to get values between -1..1 - CORE.Input.Gamepad.axisState[i][axisRaylib] = (2 * (float)(event.value - min) / range) - 1; + CORE.Input.Gamepad.axisState[i][axisRaylib] = (2*(float)(event.value - min)/range) - 1; } } } diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index afe8adfb1..47b8d42d6 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -190,7 +190,8 @@ void ToggleFullscreen(void) if (enterFullscreen) { // NOTE: The setTimeouts handle the browser mode change delay - EM_ASM( + EM_ASM + ( setTimeout(function() { Module.requestFullscreen(false, false); @@ -298,7 +299,8 @@ void ToggleBorderlessWindowed(void) { // NOTE: 1. The setTimeouts handle the browser mode change delay // 2. The style unset handles the possibility of a width="value%" like on the default shell.html file - EM_ASM( + EM_ASM + ( setTimeout(function() { Module.requestFullscreen(false, true); diff --git a/src/raylib.h b/src/raylib.h index 3f6b61f8a..2c7a6a573 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1332,7 +1332,7 @@ RLAPI Image GenImageText(int width, int height, const char *text); // Image manipulation functions RLAPI Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) RLAPI Image ImageFromImage(Image image, Rectangle rec); // Create an image from another image piece -RLAPI Image ImageFromChannel(Image image, int selectedChannel); // Create an image from a selected channel of another image (GRAYSCALE/R16/R32) +RLAPI Image ImageFromChannel(Image image, int selectedChannel); // Create an image from a selected channel of another image (GRAYSCALE) RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format diff --git a/src/raymath.h b/src/raymath.h index 0a0b05803..0b6951938 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -175,7 +175,7 @@ typedef struct float16 { // Clamp float value RMAPI float Clamp(float value, float min, float max) { - float result = (value < min) ? min : value; + float result = (value < min)? min : value; if (result > max) result = max; @@ -962,12 +962,12 @@ RMAPI Vector3 Vector3CubicHermite(Vector3 v1, Vector3 tangent1, Vector3 v2, Vect { Vector3 result = { 0 }; - float amountPow2 = amount * amount; - float amountPow3 = amount * amount * amount; + float amountPow2 = amount*amount; + float amountPow3 = amount*amount*amount; - result.x = (2 * amountPow3 - 3 * amountPow2 + 1) * v1.x + (amountPow3 - 2 * amountPow2 + amount) * tangent1.x + (-2 * amountPow3 + 3 * amountPow2) * v2.x + (amountPow3 - amountPow2) * tangent2.x; - result.y = (2 * amountPow3 - 3 * amountPow2 + 1) * v1.y + (amountPow3 - 2 * amountPow2 + amount) * tangent1.y + (-2 * amountPow3 + 3 * amountPow2) * v2.y + (amountPow3 - amountPow2) * tangent2.y; - result.z = (2 * amountPow3 - 3 * amountPow2 + 1) * v1.z + (amountPow3 - 2 * amountPow2 + amount) * tangent1.z + (-2 * amountPow3 + 3 * amountPow2) * v2.z + (amountPow3 - amountPow2) * tangent2.z; + result.x = (2*amountPow3 - 3*amountPow2 + 1)*v1.x + (amountPow3 - 2*amountPow2 + amount)*tangent1.x + (-2*amountPow3 + 3*amountPow2)*v2.x + (amountPow3 - amountPow2)*tangent2.x; + result.y = (2*amountPow3 - 3*amountPow2 + 1)*v1.y + (amountPow3 - 2*amountPow2 + amount)*tangent1.y + (-2*amountPow3 + 3*amountPow2)*v2.y + (amountPow3 - amountPow2)*tangent2.y; + result.z = (2*amountPow3 - 3*amountPow2 + 1)*v1.z + (amountPow3 - 2*amountPow2 + amount)*tangent1.z + (-2*amountPow3 + 3*amountPow2)*v2.z + (amountPow3 - amountPow2)*tangent2.z; return result; } @@ -2218,11 +2218,11 @@ RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) // as described in the GLTF 2.0 specification: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#interpolation-cubic RMAPI Quaternion QuaternionCubicHermiteSpline(Quaternion q1, Quaternion outTangent1, Quaternion q2, Quaternion inTangent2, float t) { - float t2 = t * t; - float t3 = t2 * t; - float h00 = 2 * t3 - 3 * t2 + 1; - float h10 = t3 - 2 * t2 + t; - float h01 = -2 * t3 + 3 * t2; + float t2 = t*t; + float t3 = t2*t; + float h00 = 2*t3 - 3*t2 + 1; + float h10 = t3 - 2*t2 + t; + float h01 = -2*t3 + 3*t2; float h11 = t3 - t2; Quaternion p0 = QuaternionScale(q1, h00); diff --git a/src/rcamera.h b/src/rcamera.h index 9c19556c4..d3042d306 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -490,8 +490,8 @@ void UpdateCamera(Camera *camera, int mode) if (IsGamepadAvailable(0)) { // Gamepad controller support - CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); - CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X)*2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y)*2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); diff --git a/src/rlgl.h b/src/rlgl.h index 98d43ea57..5ab677d98 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1560,7 +1560,7 @@ void rlNormal3f(float x, float y, float z) float length = sqrtf(normalx*normalx + normaly*normaly + normalz*normalz); if (length != 0.0f) { - float ilength = 1.0f / length; + float ilength = 1.0f/length; normalx *= ilength; normaly *= ilength; normalz *= ilength; diff --git a/src/rshapes.c b/src/rshapes.c index fd6ff7d23..16e80b17e 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -2235,10 +2235,10 @@ bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, floa float dx = center2.x - center1.x; // X distance between centers float dy = center2.y - center1.y; // Y distance between centers - float distanceSquared = dx * dx + dy * dy; // Distance between centers squared + float distanceSquared = dx*dx + dy*dy; // Distance between centers squared float radiusSum = radius1 + radius2; - collision = (distanceSquared <= (radiusSum * radiusSum)); + collision = (distanceSquared <= (radiusSum*radiusSum)); return collision; } @@ -2329,17 +2329,17 @@ RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Ve return CheckCollisionCircles(p1, 0, center, radius); } - float lengthSQ = ((dx * dx) + (dy * dy)); - float dotProduct = (((center.x - p1.x) * (p2.x - p1.x)) + ((center.y - p1.y) * (p2.y - p1.y))) / (lengthSQ); + float lengthSQ = ((dx*dx) + (dy*dy)); + float dotProduct = (((center.x - p1.x)*(p2.x - p1.x)) + ((center.y - p1.y)*(p2.y - p1.y)))/(lengthSQ); if (dotProduct > 1.0f) dotProduct = 1.0f; else if (dotProduct < 0.0f) dotProduct = 0.0f; - float dx2 = (p1.x - (dotProduct * (dx))) - center.x; - float dy2 = (p1.y - (dotProduct * (dy))) - center.y; - float distanceSQ = ((dx2 * dx2) + (dy2 * dy2)); + float dx2 = (p1.x - (dotProduct*(dx))) - center.x; + float dy2 = (p1.y - (dotProduct*(dy))) - center.y; + float distanceSQ = ((dx2*dx2) + (dy2*dy2)); - return (distanceSQ <= radius * radius); + return (distanceSQ <= radius*radius); } // Get collision rectangle for two rectangles collision diff --git a/src/rtextures.c b/src/rtextures.c index b6d331385..ccfa12ecb 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3599,7 +3599,7 @@ void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int en } // Calculate fixed-point increment for shorter length - int decInc = (longLen == 0)? 0 : (shortLen<<16) / longLen; + int decInc = (longLen == 0)? 0 : (shortLen << 16)/longLen; // Draw the line pixel by pixel if (yLonger) @@ -3608,7 +3608,7 @@ void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int en for (int i = 0, j = 0; i != endVal; i += sgnInc, j += decInc) { // Calculate pixel position and draw it - ImageDrawPixel(dst, startPosX + (j>>16), startPosY + i, color); + ImageDrawPixel(dst, startPosX + (j >> 16), startPosY + i, color); } } else @@ -3617,7 +3617,7 @@ void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int en for (int i = 0, j = 0; i != endVal; i += sgnInc, j += decInc) { // Calculate pixel position and draw it - ImageDrawPixel(dst, startPosX + i, startPosY + (j>>16), color); + ImageDrawPixel(dst, startPosX + i, startPosY + (j >> 16), color); } } } @@ -3815,10 +3815,10 @@ void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color col { // Calculate the 2D bounding box of the triangle // Determine the minimum and maximum x and y coordinates of the triangle vertices - int xMin = (int)((v1.x < v2.x)? ((v1.x < v3.x) ? v1.x : v3.x) : ((v2.x < v3.x) ? v2.x : v3.x)); - int yMin = (int)((v1.y < v2.y)? ((v1.y < v3.y) ? v1.y : v3.y) : ((v2.y < v3.y) ? v2.y : v3.y)); - int xMax = (int)((v1.x > v2.x)? ((v1.x > v3.x) ? v1.x : v3.x) : ((v2.x > v3.x) ? v2.x : v3.x)); - int yMax = (int)((v1.y > v2.y)? ((v1.y > v3.y) ? v1.y : v3.y) : ((v2.y > v3.y) ? v2.y : v3.y)); + int xMin = (int)((v1.x < v2.x)? ((v1.x < v3.x)? v1.x : v3.x) : ((v2.x < v3.x)? v2.x : v3.x)); + int yMin = (int)((v1.y < v2.y)? ((v1.y < v3.y)? v1.y : v3.y) : ((v2.y < v3.y)? v2.y : v3.y)); + int xMax = (int)((v1.x > v2.x)? ((v1.x > v3.x)? v1.x : v3.x) : ((v2.x > v3.x)? v2.x : v3.x)); + int yMax = (int)((v1.y > v2.y)? ((v1.y > v3.y)? v1.y : v3.y) : ((v2.y > v3.y)? v2.y : v3.y)); // Clamp the bounding box to the image dimensions if (xMin < 0) xMin = 0; From 4c90fc6eff77fad4074047153636bf85475a1fd7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 30 Jun 2024 09:38:16 +0000 Subject: [PATCH 09/40] Update raylib_api.* by CI --- parser/output/raylib_api.json | 2 +- parser/output/raylib_api.lua | 2 +- parser/output/raylib_api.txt | 2 +- parser/output/raylib_api.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index a556c84b7..e9ec07879 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -7185,7 +7185,7 @@ }, { "name": "ImageFromChannel", - "description": "Create an image from a selected channel of another image (GRAYSCALE/R16/R32)", + "description": "Create an image from a selected channel of another image (GRAYSCALE)", "returnType": "Image", "params": [ { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index cefb37e17..1ec84bcad 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -5523,7 +5523,7 @@ return { }, { name = "ImageFromChannel", - description = "Create an image from a selected channel of another image (GRAYSCALE/R16/R32)", + description = "Create an image from a selected channel of another image (GRAYSCALE)", returnType = "Image", params = { {type = "Image", name = "image"}, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 59f418d6a..c4d5f052c 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -2776,7 +2776,7 @@ Function 293: ImageFromImage() (2 input parameters) Function 294: ImageFromChannel() (2 input parameters) Name: ImageFromChannel Return type: Image - Description: Create an image from a selected channel of another image (GRAYSCALE/R16/R32) + Description: Create an image from a selected channel of another image (GRAYSCALE) Param[1]: image (type: Image) Param[2]: selectedChannel (type: int) Function 295: ImageText() (3 input parameters) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 27499123c..daa1b5472 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1798,7 +1798,7 @@ - + From c8313d9ebc428590c7098a75d2db0eed1d877127 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jul 2024 11:44:20 +0200 Subject: [PATCH 10/40] Update Storage base path, use provided SDL base path --- src/platforms/rcore_desktop_sdl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index d9637f705..9a6d16132 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1576,13 +1576,15 @@ int InitPlatform(void) CORE.Time.previous = GetTime(); // Get time as double #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod() + SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod() #endif //---------------------------------------------------------------------------- // Initialize storage system //---------------------------------------------------------------------------- - CORE.Storage.basePath = GetWorkingDirectory(); // Define base path for storage + // Define base path for storage + CORE.Storage.basePath = SDL_GetBasePath(); // Alternative: GetWorkingDirectory(); + CHDIR(CORE.Storage.basePath); //---------------------------------------------------------------------------- TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully"); From 9e22fddf1ac54d0075f1b4a5c2bbc819116dfb83 Mon Sep 17 00:00:00 2001 From: Mike Will Date: Mon, 1 Jul 2024 06:22:12 -0400 Subject: [PATCH 11/40] [build.zig] Make emscripten build compatible with Zig 0.13.0 (#4121) --- .gitignore | 1 + src/build.zig | 12 +++--------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index eeb1ed09d..d857bf2d4 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,7 @@ GRTAGS GTAGS # Zig programming language +.zig-cache/ zig-cache/ zig-out/ build/ diff --git a/src/build.zig b/src/build.zig index 0b85e5f87..3b462de61 100644 --- a/src/build.zig +++ b/src/build.zig @@ -207,15 +207,9 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. const cache_include = std.fs.path.join(b.allocator, &.{ b.sysroot.?, "cache", "sysroot", "include" }) catch @panic("Out of memory"); defer b.allocator.free(cache_include); - if (comptime builtin.zig_version.minor > 12) { - var dir = std.fs.cwd().openDir(cache_include, std.fs.Dir.OpenDirOptions{ .access_sub_paths = true, .no_follow = true }) catch @panic("No emscripten cache. Generate it!"); - dir.close(); - raylib.addIncludePath(b.path(cache_include)); - } else { - var dir = std.fs.openDirAbsolute(cache_include, std.fs.Dir.OpenDirOptions{ .access_sub_paths = true, .no_follow = true }) catch @panic("No emscripten cache. Generate it!"); - dir.close(); - raylib.addIncludePath(.{ .path = cache_include }); - } + var dir = std.fs.openDirAbsolute(cache_include, std.fs.Dir.OpenDirOptions{ .access_sub_paths = true, .no_follow = true }) catch @panic("No emscripten cache. Generate it!"); + dir.close(); + raylib.addIncludePath(.{ .cwd_relative = cache_include }); }, else => { @panic("Unsupported OS"); From b5473d5ffde38fb0a6f9e31cfaf0fc8d50aaff74 Mon Sep 17 00:00:00 2001 From: Bruno Cabral Date: Mon, 1 Jul 2024 03:24:01 -0700 Subject: [PATCH 12/40] [rtextures] advance k in LoadImageColors (#4120) Some formats are not advancing k to get pixels values --- src/rtextures.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index ccfa12ecb..54bf7c6a0 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3111,6 +3111,7 @@ Color *LoadImageColors(Image image) pixels[i].b = 0; pixels[i].a = 255; + k += 1; } break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32: { @@ -3124,9 +3125,9 @@ Color *LoadImageColors(Image image) case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: { pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f); - pixels[i].g = (unsigned char)(((float *)image.data)[k]*255.0f); - pixels[i].b = (unsigned char)(((float *)image.data)[k]*255.0f); - pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f); + pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f); + pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f); + pixels[i].a = (unsigned char)(((float *)image.data)[k + 3]*255.0f); k += 4; } break; @@ -3137,6 +3138,7 @@ Color *LoadImageColors(Image image) pixels[i].b = 0; pixels[i].a = 255; + k += 1; } break; case PIXELFORMAT_UNCOMPRESSED_R16G16B16: { @@ -3150,9 +3152,9 @@ Color *LoadImageColors(Image image) case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: { pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); - pixels[i].g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); - pixels[i].b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); - pixels[i].a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 1])*255.0f); + pixels[i].b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 2])*255.0f); + pixels[i].a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 3])*255.0f); k += 4; } break; From 61cfd1afcb465b5913362981fe66b7db84e58915 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jul 2024 13:05:20 +0200 Subject: [PATCH 13/40] Minor tweaks --- src/platforms/rcore_desktop.c | 4 ++-- src/rshapes.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index bb3fcf4f0..dbdde9dbd 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1147,7 +1147,7 @@ void PollInputEvents(void) const unsigned char *buttons = state.buttons; - for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) + for (int k = 0; (buttons != NULL) && (k < MAX_GAMEPAD_BUTTONS); k++) { int button = -1; // GamepadButton enum values assigned @@ -1189,7 +1189,7 @@ void PollInputEvents(void) // Get current axis state const float *axes = state.axes; - for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) + for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1); k++) { CORE.Input.Gamepad.axisState[i][k] = axes[k]; } diff --git a/src/rshapes.c b/src/rshapes.c index 16e80b17e..9e7de1e2f 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -337,7 +337,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA } // NOTE: In case number of segments is odd, we add one last piece to the cake - if (((unsigned int)segments%2) == 1) + if ((((unsigned int)segments)%2) == 1) { rlColor4ub(color.r, color.g, color.b, color.a); From 2bc75b877a253f496696c1b870483ed919d4e709 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jul 2024 13:06:15 +0200 Subject: [PATCH 14/40] WARNING: TEST: Security check to address potential overflow cocerns --- src/raylib.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 2c7a6a573..ebc7fe555 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -123,11 +123,12 @@ // Allow custom memory allocators // NOTE: Require recompiling raylib sources +#define MAX_ALLOC_SIZE 1024*1024 // 1GB of maximum allocation data #ifndef RL_MALLOC - #define RL_MALLOC(sz) malloc(sz) + #define RL_MALLOC(sz) ((sz > MAX_ALLOC_SIZE)? malloc(sz) : NULL) #endif #ifndef RL_CALLOC - #define RL_CALLOC(n,sz) calloc(n,sz) + #define RL_CALLOC(n,sz) ((n*sz > MAX_ALLOC_SIZE)? calloc(n,sz) : NULL) #endif #ifndef RL_REALLOC #define RL_REALLOC(ptr,sz) realloc(ptr,sz) From 1fb0565148dbdf8f6969b3c8900e9882cb2839ef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 1 Jul 2024 11:06:39 +0000 Subject: [PATCH 15/40] Update raylib_api.* by CI --- parser/output/raylib_api.json | 10 +++- parser/output/raylib_api.lua | 10 +++- parser/output/raylib_api.txt | 105 ++++++++++++++++++---------------- parser/output/raylib_api.xml | 7 ++- 4 files changed, 75 insertions(+), 57 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index e9ec07879..a93d7c492 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -60,16 +60,22 @@ "value": "(180.0f/PI)", "description": "" }, + { + "name": "MAX_ALLOC_SIZE", + "type": "INT_MATH", + "value": "1024*1024", + "description": "1GB of maximum allocation data" + }, { "name": "RL_MALLOC(sz)", "type": "MACRO", - "value": "malloc(sz)", + "value": "((sz > MAX_ALLOC_SIZE)? malloc(sz) : NULL)", "description": "" }, { "name": "RL_CALLOC(n,sz)", "type": "MACRO", - "value": "calloc(n,sz)", + "value": "((n*sz > MAX_ALLOC_SIZE)? calloc(n,sz) : NULL)", "description": "" }, { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 1ec84bcad..97eeb8682 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -60,16 +60,22 @@ return { value = "(180.0f/PI)", description = "" }, + { + name = "MAX_ALLOC_SIZE", + type = "INT_MATH", + value = "1024*1024", + description = "1GB of maximum allocation data" + }, { name = "RL_MALLOC(sz)", type = "MACRO", - value = "malloc(sz)", + value = "((sz > MAX_ALLOC_SIZE)? malloc(sz) : NULL)", description = "" }, { name = "RL_CALLOC(n,sz)", type = "MACRO", - value = "calloc(n,sz)", + value = "((n*sz > MAX_ALLOC_SIZE)? calloc(n,sz) : NULL)", description = "" }, { diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index c4d5f052c..14b8a6548 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -1,5 +1,5 @@ -Defines found: 57 +Defines found: 58 Define 001: RAYLIB_H Name: RAYLIB_H @@ -51,237 +51,242 @@ Define 010: RAD2DEG Type: FLOAT_MATH Value: (180.0f/PI) Description: -Define 011: RL_MALLOC(sz) +Define 011: MAX_ALLOC_SIZE + Name: MAX_ALLOC_SIZE + Type: INT_MATH + Value: 1024*1024 + Description: 1GB of maximum allocation data +Define 012: RL_MALLOC(sz) Name: RL_MALLOC(sz) Type: MACRO - Value: malloc(sz) + Value: ((sz > MAX_ALLOC_SIZE)? malloc(sz) : NULL) Description: -Define 012: RL_CALLOC(n,sz) +Define 013: RL_CALLOC(n,sz) Name: RL_CALLOC(n,sz) Type: MACRO - Value: calloc(n,sz) + Value: ((n*sz > MAX_ALLOC_SIZE)? calloc(n,sz) : NULL) Description: -Define 013: RL_REALLOC(ptr,sz) +Define 014: RL_REALLOC(ptr,sz) Name: RL_REALLOC(ptr,sz) Type: MACRO Value: realloc(ptr,sz) Description: -Define 014: RL_FREE(ptr) +Define 015: RL_FREE(ptr) Name: RL_FREE(ptr) Type: MACRO Value: free(ptr) Description: -Define 015: CLITERAL(type) +Define 016: CLITERAL(type) Name: CLITERAL(type) Type: MACRO Value: type Description: -Define 016: RL_COLOR_TYPE +Define 017: RL_COLOR_TYPE Name: RL_COLOR_TYPE Type: GUARD Value: Description: -Define 017: RL_RECTANGLE_TYPE +Define 018: RL_RECTANGLE_TYPE Name: RL_RECTANGLE_TYPE Type: GUARD Value: Description: -Define 018: RL_VECTOR2_TYPE +Define 019: RL_VECTOR2_TYPE Name: RL_VECTOR2_TYPE Type: GUARD Value: Description: -Define 019: RL_VECTOR3_TYPE +Define 020: RL_VECTOR3_TYPE Name: RL_VECTOR3_TYPE Type: GUARD Value: Description: -Define 020: RL_VECTOR4_TYPE +Define 021: RL_VECTOR4_TYPE Name: RL_VECTOR4_TYPE Type: GUARD Value: Description: -Define 021: RL_QUATERNION_TYPE +Define 022: RL_QUATERNION_TYPE Name: RL_QUATERNION_TYPE Type: GUARD Value: Description: -Define 022: RL_MATRIX_TYPE +Define 023: RL_MATRIX_TYPE Name: RL_MATRIX_TYPE Type: GUARD Value: Description: -Define 023: LIGHTGRAY +Define 024: LIGHTGRAY Name: LIGHTGRAY Type: COLOR Value: CLITERAL(Color){ 200, 200, 200, 255 } Description: Light Gray -Define 024: GRAY +Define 025: GRAY Name: GRAY Type: COLOR Value: CLITERAL(Color){ 130, 130, 130, 255 } Description: Gray -Define 025: DARKGRAY +Define 026: DARKGRAY Name: DARKGRAY Type: COLOR Value: CLITERAL(Color){ 80, 80, 80, 255 } Description: Dark Gray -Define 026: YELLOW +Define 027: YELLOW Name: YELLOW Type: COLOR Value: CLITERAL(Color){ 253, 249, 0, 255 } Description: Yellow -Define 027: GOLD +Define 028: GOLD Name: GOLD Type: COLOR Value: CLITERAL(Color){ 255, 203, 0, 255 } Description: Gold -Define 028: ORANGE +Define 029: ORANGE Name: ORANGE Type: COLOR Value: CLITERAL(Color){ 255, 161, 0, 255 } Description: Orange -Define 029: PINK +Define 030: PINK Name: PINK Type: COLOR Value: CLITERAL(Color){ 255, 109, 194, 255 } Description: Pink -Define 030: RED +Define 031: RED Name: RED Type: COLOR Value: CLITERAL(Color){ 230, 41, 55, 255 } Description: Red -Define 031: MAROON +Define 032: MAROON Name: MAROON Type: COLOR Value: CLITERAL(Color){ 190, 33, 55, 255 } Description: Maroon -Define 032: GREEN +Define 033: GREEN Name: GREEN Type: COLOR Value: CLITERAL(Color){ 0, 228, 48, 255 } Description: Green -Define 033: LIME +Define 034: LIME Name: LIME Type: COLOR Value: CLITERAL(Color){ 0, 158, 47, 255 } Description: Lime -Define 034: DARKGREEN +Define 035: DARKGREEN Name: DARKGREEN Type: COLOR Value: CLITERAL(Color){ 0, 117, 44, 255 } Description: Dark Green -Define 035: SKYBLUE +Define 036: SKYBLUE Name: SKYBLUE Type: COLOR Value: CLITERAL(Color){ 102, 191, 255, 255 } Description: Sky Blue -Define 036: BLUE +Define 037: BLUE Name: BLUE Type: COLOR Value: CLITERAL(Color){ 0, 121, 241, 255 } Description: Blue -Define 037: DARKBLUE +Define 038: DARKBLUE Name: DARKBLUE Type: COLOR Value: CLITERAL(Color){ 0, 82, 172, 255 } Description: Dark Blue -Define 038: PURPLE +Define 039: PURPLE Name: PURPLE Type: COLOR Value: CLITERAL(Color){ 200, 122, 255, 255 } Description: Purple -Define 039: VIOLET +Define 040: VIOLET Name: VIOLET Type: COLOR Value: CLITERAL(Color){ 135, 60, 190, 255 } Description: Violet -Define 040: DARKPURPLE +Define 041: DARKPURPLE Name: DARKPURPLE Type: COLOR Value: CLITERAL(Color){ 112, 31, 126, 255 } Description: Dark Purple -Define 041: BEIGE +Define 042: BEIGE Name: BEIGE Type: COLOR Value: CLITERAL(Color){ 211, 176, 131, 255 } Description: Beige -Define 042: BROWN +Define 043: BROWN Name: BROWN Type: COLOR Value: CLITERAL(Color){ 127, 106, 79, 255 } Description: Brown -Define 043: DARKBROWN +Define 044: DARKBROWN Name: DARKBROWN Type: COLOR Value: CLITERAL(Color){ 76, 63, 47, 255 } Description: Dark Brown -Define 044: WHITE +Define 045: WHITE Name: WHITE Type: COLOR Value: CLITERAL(Color){ 255, 255, 255, 255 } Description: White -Define 045: BLACK +Define 046: BLACK Name: BLACK Type: COLOR Value: CLITERAL(Color){ 0, 0, 0, 255 } Description: Black -Define 046: BLANK +Define 047: BLANK Name: BLANK Type: COLOR Value: CLITERAL(Color){ 0, 0, 0, 0 } Description: Blank (Transparent) -Define 047: MAGENTA +Define 048: MAGENTA Name: MAGENTA Type: COLOR Value: CLITERAL(Color){ 255, 0, 255, 255 } Description: Magenta -Define 048: RAYWHITE +Define 049: RAYWHITE Name: RAYWHITE Type: COLOR Value: CLITERAL(Color){ 245, 245, 245, 255 } Description: My own White (raylib logo) -Define 049: RL_BOOL_TYPE +Define 050: RL_BOOL_TYPE Name: RL_BOOL_TYPE Type: GUARD Value: Description: -Define 050: MOUSE_LEFT_BUTTON +Define 051: MOUSE_LEFT_BUTTON Name: MOUSE_LEFT_BUTTON Type: UNKNOWN Value: MOUSE_BUTTON_LEFT Description: -Define 051: MOUSE_RIGHT_BUTTON +Define 052: MOUSE_RIGHT_BUTTON Name: MOUSE_RIGHT_BUTTON Type: UNKNOWN Value: MOUSE_BUTTON_RIGHT Description: -Define 052: MOUSE_MIDDLE_BUTTON +Define 053: MOUSE_MIDDLE_BUTTON Name: MOUSE_MIDDLE_BUTTON Type: UNKNOWN Value: MOUSE_BUTTON_MIDDLE Description: -Define 053: MATERIAL_MAP_DIFFUSE +Define 054: MATERIAL_MAP_DIFFUSE Name: MATERIAL_MAP_DIFFUSE Type: UNKNOWN Value: MATERIAL_MAP_ALBEDO Description: -Define 054: MATERIAL_MAP_SPECULAR +Define 055: MATERIAL_MAP_SPECULAR Name: MATERIAL_MAP_SPECULAR Type: UNKNOWN Value: MATERIAL_MAP_METALNESS Description: -Define 055: SHADER_LOC_MAP_DIFFUSE +Define 056: SHADER_LOC_MAP_DIFFUSE Name: SHADER_LOC_MAP_DIFFUSE Type: UNKNOWN Value: SHADER_LOC_MAP_ALBEDO Description: -Define 056: SHADER_LOC_MAP_SPECULAR +Define 057: SHADER_LOC_MAP_SPECULAR Name: SHADER_LOC_MAP_SPECULAR Type: UNKNOWN Value: SHADER_LOC_MAP_METALNESS Description: -Define 057: GetMouseRay +Define 058: GetMouseRay Name: GetMouseRay Type: UNKNOWN Value: GetScreenToWorldRay diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index daa1b5472..840507928 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1,6 +1,6 @@ - + @@ -11,8 +11,9 @@ - - + + + From d243094ede4386ed2ff6e07009fa5b292dda328c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jul 2024 18:28:44 +0200 Subject: [PATCH 16/40] WARNING: BREAKING: Renamed `PLATFORM_DESKTOP` to `PLATFORM_DESKTOP_GLFW` This could potentially be a breaking change, for consistency, now every possible desktop backend has the proper name assigned: GLFW, SDL, RGFW raylib build system has been reviewed to fallback to `PLATFORM_DESKTOP_GLFW` by default when `PLATFORM_DESKTOP` defined --- examples/Makefile | 72 +++++----- projects/CMake/CMakeLists.txt | 2 +- src/CMakeLists.txt | 4 +- src/Makefile | 134 +++++++++--------- .../{rcore_desktop.c => rcore_desktop_glfw.c} | 0 src/rcore.c | 14 +- 6 files changed, 123 insertions(+), 103 deletions(-) rename src/platforms/{rcore_desktop.c => rcore_desktop_glfw.c} (100%) diff --git a/examples/Makefile b/examples/Makefile index 3c39e09e5..c9f819dde 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -4,7 +4,9 @@ # # This file supports building raylib examples for the following platforms: # -# > PLATFORM_DESKTOP (GLFW backend): +# > PLATFORM_DESKTOP +# - Defaults to PLATFORM_DESKTOP_GLFW +# > PLATFORM_DESKTOP_GFLW (GLFW backend): # - Windows (Win32, Win64) # - Linux (X11/Wayland desktop mode) # - macOS/OSX (x64, arm64) @@ -52,6 +54,12 @@ # Define target platform: PLATFORM_DESKTOP, PLATFORM_DESKTOP_SDL, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP +ifeq ($(PLATFORM), PLATFORM_DESKTOP) + TARGET_PLATFORM = PLATFORM_DESKTOP_GLFW +else + TARGET_PLATFORM = $(PLATFORM) +endif + # Define required raylib variables PROJECT_NAME ?= raylib_examples RAYLIB_VERSION ?= 5.0.0 @@ -91,7 +99,7 @@ BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources # Determine PLATFORM_OS when required -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB PLATFORM_DESKTOP_RGFW)) +ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB PLATFORM_DESKTOP_RGFW)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -118,7 +126,7 @@ ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLA endif endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) UNAMEOS = $(shell uname) ifeq ($(UNAMEOS),Linux) PLATFORM_OS = LINUX @@ -127,7 +135,7 @@ endif # RAYLIB_PATH adjustment for LINUX platform # TODO: Do we really need this? -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),LINUX) RAYLIB_PREFIX ?= .. RAYLIB_PATH = $(realpath $(RAYLIB_PREFIX)) @@ -135,14 +143,14 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif # Default path for raylib on Raspberry Pi -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) RAYLIB_PATH ?= /home/pi/raylib endif # Define raylib release directory for compiled library RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) ifeq ($(PLATFORM_OS),WINDOWS) # Emscripten required variables EMSDK_PATH ?= C:/emsdk @@ -158,7 +166,7 @@ endif #------------------------------------------------------------------------------------------------ CC = gcc -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),OSX) # OSX default compiler CC = clang @@ -168,7 +176,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) CC = clang endif endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) # HTML5 emscripten compiler # WARNING: To compile to HTML5, code must be redesigned # to use emscripten.h and emscripten_set_main_loop() @@ -179,15 +187,15 @@ endif #------------------------------------------------------------------------------------------------ MAKE ?= make -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) MAKE = mingw32-make endif endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) MAKE = mingw32-make endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) MAKE = emmake make endif @@ -206,11 +214,11 @@ CFLAGS = -Wall -std=c99 -D_DEFAULT_SOURCE -Wno-missing-braces -Wunused-result ifeq ($(BUILD_MODE),DEBUG) CFLAGS += -g -D_DEBUG - ifeq ($(PLATFORM),PLATFORM_WEB) + ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) CFLAGS += -sASSERTIONS=1 --profiling endif else - ifeq ($(PLATFORM),PLATFORM_WEB) + ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) ifeq ($(BUILD_WEB_ASYNCIFY),TRUE) CFLAGS += -O3 else @@ -227,7 +235,7 @@ endif # -Wstrict-prototypes warn if a function is declared or defined without specifying the argument types # -Werror=implicit-function-declaration catch function calls without prior declaration #CFLAGS += -Wextra -Wmissing-prototypes -Wstrict-prototypes -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),LINUX) ifeq ($(RAYLIB_LIBTYPE),STATIC) CFLAGS += -D_DEFAULT_SOURCE @@ -238,7 +246,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) CFLAGS += -std=gnu99 -DEGL_NO_X11 endif @@ -248,7 +256,7 @@ endif INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external # Define additional directories containing required header files -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),BSD) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif @@ -256,10 +264,10 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) INCLUDE_PATHS += -I/usr/include/libdrm endif @@ -273,7 +281,7 @@ endif #------------------------------------------------------------------------------------------------ LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) # NOTE: The resource .rc file contains windows executable icon and properties LDFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data @@ -289,7 +297,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Lsrc -L$(RAYLIB_LIB_PATH) endif endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) ifeq ($(PLATFORM_OS),WINDOWS) # NOTE: The resource .rc file contains windows executable icon and properties LDFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data @@ -300,7 +308,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) endif LDFLAGS += -L$(SDL_LIBRARY_PATH) endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) # -Os # size optimization # -O2 # optimization level 2, if used, also set --memory-init-file 0 # -sUSE_GLFW=3 # Use glfw3 library (context/input management) @@ -347,7 +355,7 @@ endif # Define libraries required on linking: LDLIBS # NOTE: To link libraries (lib.so or lib.a), use -l #------------------------------------------------------------------------------------------------ -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) # Libraries for Windows desktop compilation # NOTE: WinMM library required to set high-res timer resolution @@ -393,7 +401,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lglfw endif endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) ifeq ($(PLATFORM_OS),WINDOWS) # Libraries for Windows desktop compilation LDLIBS = -lraylib -lSDL2 -lSDL2main -lopengl32 -lgdi32 @@ -421,7 +429,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) LDLIBS += -latomic endif endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) ifeq ($(PLATFORM_OS),WINDOWS) # Libraries for Windows desktop compilation LDLIBS = ..\src\libraylib.a -lgdi32 -lwinmm -lopengl32 @@ -446,12 +454,12 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) LDLIBS += -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) LDLIBS = -lraylib -lGLESv2 -lEGL -lpthread -lrt -lm -lgbm -ldrm -ldl -latomic endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) # Libraries for web (HTML5) compiling LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.a endif @@ -638,17 +646,17 @@ others: $(OTHERS) # Generic compilation pattern # NOTE: Examples must be ready for Android compilation! %: %.c -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) $(MAKE) -f Makefile.Android PROJECT_NAME=$@ PROJECT_SOURCE_FILES=$< -else ifeq ($(PLATFORM),PLATFORM_WEB) +else ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) $(MAKE) -f Makefile.Web $@ else - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(TARGET_PLATFORM) endif # Clean everything clean: -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) del *.o *.exe /s endif @@ -661,11 +669,11 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) rm -f *.o endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) find . -type f -executable -delete rm -fv *.o endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) ifeq ($(PLATFORM_OS),WINDOWS) del *.wasm *.html *.js *.data else diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index ee258d604..3b22964c5 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -5,7 +5,7 @@ project(example) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Dependencies -set(RAYLIB_VERSION 5.0) +set(RAYLIB_VERSION 5.5) find_package(raylib ${RAYLIB_VERSION} QUIET) # QUIET or REQUIRED if (NOT raylib_FOUND) # If there's none, fetch and build raylib include(FetchContent) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c1360ee29..8f4d0e26e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ # Setup the project and settings project(raylib C) -set(PROJECT_VERSION 5.0.0) -set(API_VERSION 500) +set(PROJECT_VERSION 5.5.0) +set(API_VERSION 550) include(GNUInstallDirs) include(JoinPaths) diff --git a/src/Makefile b/src/Makefile index 19251b956..e40a50796 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,7 +4,9 @@ # # This file supports building raylib library for the following platforms: # -# > PLATFORM_DESKTOP (GLFW backend): +# > PLATFORM_DESKTOP +# - Defaults to PLATFORM_DESKTOP_GLFW +# > PLATFORM_DESKTOP_GLFW (GLFW backend): # - Windows (Win32, Win64) # - Linux (X11/Wayland desktop mode) # - macOS/OSX (x64, arm64) @@ -55,9 +57,15 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform PLATFORM ?= PLATFORM_DESKTOP +ifeq ($(PLATFORM), PLATFORM_DESKTOP) + TARGET_PLATFORM = PLATFORM_DESKTOP_GLFW +else + TARGET_PLATFORM = $(PLATFORM) +endif + # Define required raylib variables RAYLIB_VERSION = 5.0.0 RAYLIB_API_VERSION = 500 @@ -119,7 +127,7 @@ HOST_PLATFORM_OS ?= WINDOWS PLATFORM_OS ?= WINDOWS # Determine PLATFORM_OS when required -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB PLATFORM_ANDROID PLATFORM_DESKTOP_RGFW)) +ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW PLATFORM_WEB PLATFORM_ANDROID)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -152,7 +160,7 @@ ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLA endif endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) UNAMEOS = $(shell uname) ifeq ($(UNAMEOS),Linux) PLATFORM_OS = LINUX @@ -161,7 +169,7 @@ ifeq ($(PLATFORM),PLATFORM_DRM) PLATFORM_SHELL = sh endif endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) ifeq ($(PLATFORM_OS),LINUX) ifndef PLATFORM_SHELL PLATFORM_SHELL = sh @@ -169,7 +177,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) ifeq ($(PLATFORM_OS), WINDOWS) # Emscripten required variables EMSDK_PATH ?= C:/emsdk @@ -181,7 +189,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) # Android architecture # Starting at 2019 using arm64 is mandatory for published apps, # Starting on August 2020, minimum required target API is Android 10 (API level 29) @@ -221,7 +229,7 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) endif # Define raylib graphics api depending on selected platform -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) # By default use OpenGL 3.3 on desktop platforms GRAPHICS ?= GRAPHICS_API_OPENGL_33 #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 @@ -229,28 +237,28 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) - # By default use OpenGL 3.3 on desktop platforms - GRAPHICS ?= GRAPHICS_API_OPENGL_33 - #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 - #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 - #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 - #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) -endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) # By default use OpenGL 3.3 on desktop platform with SDL backend GRAPHICS ?= GRAPHICS_API_OPENGL_33 endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) + # By default use OpenGL 3.3 on desktop platforms + GRAPHICS ?= GRAPHICS_API_OPENGL_33 + #GRAPHICS = GRAPHICS_API_OPENGL_11 # Uncomment to use OpenGL 1.1 + #GRAPHICS = GRAPHICS_API_OPENGL_21 # Uncomment to use OpenGL 2.1 + #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 + #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) +endif +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) # On DRM OpenGL ES 2.0 must be used GRAPHICS = GRAPHICS_API_OPENGL_ES2 endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) # On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0 GRAPHICS = GRAPHICS_API_OPENGL_ES2 #GRAPHICS = GRAPHICS_API_OPENGL_ES3 # Uncomment to use ES3/WebGL2 (preliminary support). endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) # By default use OpenGL ES 2.0 on Android GRAPHICS = GRAPHICS_API_OPENGL_ES2 endif @@ -260,7 +268,7 @@ endif CC = gcc AR = ar -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),OSX) # OSX default compiler CC = clang @@ -271,7 +279,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) CC = clang endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) # Define RPI cross-compiler #CC = armv6j-hardfloat-linux-gnueabi-gcc @@ -279,12 +287,12 @@ ifeq ($(PLATFORM),PLATFORM_DRM) AR = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-ar endif endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) # HTML5 emscripten compiler CC = emcc AR = emar endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) # Android toolchain (must be provided for desired architecture and compiler) ifeq ($(ANDROID_ARCH),arm) CC = $(ANDROID_TOOLCHAIN)/bin/$(ANDROID_COMPILER_ARCH)-linux-androideabi$(ANDROID_API_VERSION)-clang @@ -316,13 +324,13 @@ endif # -D_GNU_SOURCE access to lots of nonstandard GNU/Linux extension functions # -Werror=pointer-arith catch unportable code that does direct arithmetic on void pointers # -fno-strict-aliasing jar_xm.h does shady stuff (breaks strict aliasing) -CFLAGS = -Wall -D_GNU_SOURCE -D$(PLATFORM) -D$(GRAPHICS) -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing +CFLAGS = -Wall -D_GNU_SOURCE -D$(TARGET_PLATFORM) -D$(GRAPHICS) -Wno-missing-braces -Werror=pointer-arith -fno-strict-aliasing ifneq ($(RAYLIB_CONFIG_FLAGS), NONE) CFLAGS += -DEXTERNAL_CONFIG_FLAGS $(RAYLIB_CONFIG_FLAGS) endif -ifeq ($(PLATFORM), PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM), PLATFORM_WEB) # NOTE: When using multi-threading in the user code, it requires -pthread enabled CFLAGS += -std=gnu99 else @@ -338,13 +346,13 @@ ifeq ($(RAYLIB_BUILD_MODE),DEBUG) endif ifeq ($(RAYLIB_BUILD_MODE),RELEASE) - ifeq ($(PLATFORM),PLATFORM_WEB) + ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) CFLAGS += -Os endif - ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) CFLAGS += -O1 endif - ifeq ($(PLATFORM),PLATFORM_ANDROID) + ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) CFLAGS += -O2 endif endif @@ -354,10 +362,10 @@ endif # -Wmissing-prototypes warn if a global function is defined without a previous prototype declaration # -Wstrict-prototypes warn if a function is declared or defined without specifying the argument types # -Werror=implicit-function-declaration catch function calls without prior declaration -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) CFLAGS += -Werror=implicit-function-declaration endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) # -Os # size optimization # -O2 # optimization level 2, if used, also set --memory-init-file 0 # -sUSE_GLFW=3 # Use glfw3 library (context/input management) -> Only for linker! @@ -375,7 +383,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) endif #CFLAGS += -sGL_ENABLE_GET_PROC_ADDRESS endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) # Compiler flags for arquitecture ifeq ($(ANDROID_ARCH),arm) CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 @@ -411,19 +419,18 @@ ifeq ($(RAYLIB_LIBTYPE),SHARED) endif endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) # without EGL_NO_X11 eglplatform.h tears Xlib.h in which tears X.h in # which contains a conflicting type Font CFLAGS += -DEGL_NO_X11 CFLAGS += -Werror=implicit-function-declaration endif # Use Wayland display on Linux desktop -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS), LINUX) ifeq ($(GLFW_LINUX_ENABLE_X11),TRUE) CFLAGS += -D_GLFW_X11 endif - ifeq ($(GLFW_LINUX_ENABLE_WAYLAND),TRUE) CFLAGS += -D_GLFW_WAYLAND LDFLAGS += $(shell pkg-config wayland-client wayland-cursor wayland-egl xkbcommon --libs) @@ -457,26 +464,26 @@ CFLAGS += $(CUSTOM_CFLAGS) INCLUDE_PATHS = -I. # Define additional directories containing required header files -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) INCLUDE_PATHS += -Iexternal/glfw/include ifeq ($(PLATFORM_OS),BSD) INCLUDE_PATHS += -I/usr/local/include endif endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) endif -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) INCLUDE_PATHS += -Iexternal/glfw/include endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/usr/include INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include endif endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) NATIVE_APP_GLUE = $(ANDROID_NDK)/sources/android/native_app_glue # Include android_native_app_glue.h INCLUDE_PATHS += -I$(NATIVE_APP_GLUE) @@ -502,7 +509,7 @@ endif #------------------------------------------------------------------------------------------------ LDFLAGS = $(CUSTOM_LDFLAGS) -L. -L$(RAYLIB_RELEASE_PATH) -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) ifneq ($(CC), tcc) LDFLAGS += -Wl,--out-implib,$(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME)dll.a @@ -518,17 +525,17 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib endif endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) LDFLAGS += -L$(SDL_LIBRARY_PATH) endif -ifeq ($(PLATFORM),PLATFORM_DRM) +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib endif endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) LDFLAGS += -Wl,-soname,libraylib.$(RAYLIB_API_VERSION).so -Wl,--exclude-libs,libatomic.a LDFLAGS += -Wl,--build-id -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings # Force linking of library module to define symbol @@ -542,7 +549,7 @@ endif # Define libraries required on linking: LDLIBS # NOTE: This is only required for dynamic library generation #------------------------------------------------------------------------------------------------ -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(PLATFORM_OS),WINDOWS) ifeq ($(CC), tcc) LDLIBS = -lopengl32 -lgdi32 -lwinmm -lshell32 @@ -570,7 +577,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS = -lglfw endif endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_SDL) ifeq ($(PLATFORM_OS),WINDOWS) LDLIBS = -static-libgcc -lopengl32 -lgdi32 endif @@ -582,16 +589,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) endif LDLIBS += -lSDL2 -lSDL2main endif -ifeq ($(PLATFORM),PLATFORM_DRM) - LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl - ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) - LDLIBS += -latomic - endif -endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) - LDLIBS = -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lc -lm -endif -ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW) ifeq ($(PLATFORM_OS),WINDOWS) # Libraries for Windows desktop compilation LDLIBS = -lgdi32 -lwinmm -lopengl32 @@ -615,6 +613,15 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP_RGFW) LDLIBS += -lm -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo endif endif +ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) + LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl + ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) + LDLIBS += -latomic + endif +endif +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) + LDLIBS = -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lc -lm +endif # Define source code object files required #------------------------------------------------------------------------------------------------ @@ -624,8 +631,7 @@ OBJS = rcore.o \ rtext.o \ utils.o - -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW) ifeq ($(USE_EXTERNAL_GLFW),FALSE) OBJS += rglfw.o endif @@ -640,7 +646,7 @@ ifeq ($(RAYLIB_MODULE_RAYGUI),TRUE) OBJS += raygui.o endif -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) OBJS += android_native_app_glue.o endif @@ -652,14 +658,14 @@ all: raylib # Compile raylib library # NOTE: Release directory is created if not exist raylib: $(OBJS) -ifeq ($(PLATFORM),PLATFORM_WEB) +ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) # Compile raylib libray for web #$(CC) $(OBJS) -r -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(AR) rcs $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(OBJS) @echo "raylib library generated (lib$(RAYLIB_LIB_NAME).a)!" else ifeq ($(RAYLIB_LIBTYPE),SHARED) - ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW)) + ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW)) ifeq ($(PLATFORM_OS),WINDOWS) # NOTE: Linking with provided resource file $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/$(RAYLIB_LIB_NAME).dll $(OBJS) $(RAYLIB_RES_FILE) $(LDFLAGS) $(LDLIBS) @@ -688,7 +694,7 @@ else cd $(RAYLIB_RELEASE_PATH) && ln -fs lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so lib$(RAYLIB_LIB_NAME).so endif endif - ifeq ($(PLATFORM),PLATFORM_DRM) + ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) # Compile raylib shared library version $(RAYLIB_VERSION). # WARNING: you should type "make clean" before doing this target $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) $(OBJS) $(LDFLAGS) $(LDLIBS) @@ -696,7 +702,7 @@ else cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) lib$(RAYLIB_LIB_NAME).so endif - ifeq ($(PLATFORM),PLATFORM_ANDROID) + ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so $(OBJS) $(LDFLAGS) $(LDLIBS) @echo "raylib shared library generated (lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so)!" # WARNING: symbolic links creation on Windows should be done using mklink command, no ln available @@ -851,7 +857,7 @@ clean: clean_shell_$(PLATFORM_SHELL) clean_shell_sh: rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c $(RAYLIB_RELEASE_PATH)/*-protocol.h $(RAYLIB_RELEASE_PATH)/*-protocol-code.h -ifeq ($(PLATFORM),PLATFORM_ANDROID) +ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o endif diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop_glfw.c similarity index 100% rename from src/platforms/rcore_desktop.c rename to src/platforms/rcore_desktop_glfw.c diff --git a/src/rcore.c b/src/rcore.c index 2c9af11d5..e68a9a7d5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,7 +3,7 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* > PLATFORM_DESKTOP (GLFW backend): +* > PLATFORM_DESKTOP_GLFW (GLFW backend): * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) * - macOS/OSX (x64, arm64) @@ -493,9 +493,13 @@ void __stdcall Sleep(unsigned long msTimeout); // Required for: Wai const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' #endif // !SUPPORT_MODULE_RTEXT -// Include platform-specific submodules #if defined(PLATFORM_DESKTOP) - #include "platforms/rcore_desktop.c" + #define PLATFORM_DESKTOP_GLFW +#endif + +// Include platform-specific submodules +#if defined(PLATFORM_DESKTOP_GLFW) + #include "platforms/rcore_desktop_glfw.c" #elif defined(PLATFORM_DESKTOP_SDL) #include "platforms/rcore_desktop_sdl.c" #elif defined(PLATFORM_DESKTOP_RGFW) @@ -564,10 +568,12 @@ void InitWindow(int width, int height, const char *title) { TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); -#if defined(PLATFORM_DESKTOP) +#if defined(PLATFORM_DESKTOP_GLFW) TRACELOG(LOG_INFO, "Platform backend: DESKTOP (GLFW)"); #elif defined(PLATFORM_DESKTOP_SDL) TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)"); +#elif defined(PLATFORM_DESKTOP_RGFW) + TRACELOG(LOG_INFO, "Platform backend: DESKTOP (RGFW)"); #elif defined(PLATFORM_WEB) TRACELOG(LOG_INFO, "Platform backend: WEB (HTML5)"); #elif defined(PLATFORM_DRM) From fbd79cde5f67ec863832a95942ae0c2e88947bbc Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jul 2024 18:30:22 +0200 Subject: [PATCH 17/40] Reverted memory allocators additional checks --- src/raylib.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index ebc7fe555..2c7a6a573 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -123,12 +123,11 @@ // Allow custom memory allocators // NOTE: Require recompiling raylib sources -#define MAX_ALLOC_SIZE 1024*1024 // 1GB of maximum allocation data #ifndef RL_MALLOC - #define RL_MALLOC(sz) ((sz > MAX_ALLOC_SIZE)? malloc(sz) : NULL) + #define RL_MALLOC(sz) malloc(sz) #endif #ifndef RL_CALLOC - #define RL_CALLOC(n,sz) ((n*sz > MAX_ALLOC_SIZE)? calloc(n,sz) : NULL) + #define RL_CALLOC(n,sz) calloc(n,sz) #endif #ifndef RL_REALLOC #define RL_REALLOC(ptr,sz) realloc(ptr,sz) From 6d30220acc49a4645ef51ce17ad546dad718a997 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 1 Jul 2024 16:30:47 +0000 Subject: [PATCH 18/40] Update raylib_api.* by CI --- parser/output/raylib_api.json | 10 +--- parser/output/raylib_api.lua | 10 +--- parser/output/raylib_api.txt | 105 ++++++++++++++++------------------ parser/output/raylib_api.xml | 7 +-- 4 files changed, 57 insertions(+), 75 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index a93d7c492..e9ec07879 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -60,22 +60,16 @@ "value": "(180.0f/PI)", "description": "" }, - { - "name": "MAX_ALLOC_SIZE", - "type": "INT_MATH", - "value": "1024*1024", - "description": "1GB of maximum allocation data" - }, { "name": "RL_MALLOC(sz)", "type": "MACRO", - "value": "((sz > MAX_ALLOC_SIZE)? malloc(sz) : NULL)", + "value": "malloc(sz)", "description": "" }, { "name": "RL_CALLOC(n,sz)", "type": "MACRO", - "value": "((n*sz > MAX_ALLOC_SIZE)? calloc(n,sz) : NULL)", + "value": "calloc(n,sz)", "description": "" }, { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 97eeb8682..1ec84bcad 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -60,22 +60,16 @@ return { value = "(180.0f/PI)", description = "" }, - { - name = "MAX_ALLOC_SIZE", - type = "INT_MATH", - value = "1024*1024", - description = "1GB of maximum allocation data" - }, { name = "RL_MALLOC(sz)", type = "MACRO", - value = "((sz > MAX_ALLOC_SIZE)? malloc(sz) : NULL)", + value = "malloc(sz)", description = "" }, { name = "RL_CALLOC(n,sz)", type = "MACRO", - value = "((n*sz > MAX_ALLOC_SIZE)? calloc(n,sz) : NULL)", + value = "calloc(n,sz)", description = "" }, { diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 14b8a6548..c4d5f052c 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -1,5 +1,5 @@ -Defines found: 58 +Defines found: 57 Define 001: RAYLIB_H Name: RAYLIB_H @@ -51,242 +51,237 @@ Define 010: RAD2DEG Type: FLOAT_MATH Value: (180.0f/PI) Description: -Define 011: MAX_ALLOC_SIZE - Name: MAX_ALLOC_SIZE - Type: INT_MATH - Value: 1024*1024 - Description: 1GB of maximum allocation data -Define 012: RL_MALLOC(sz) +Define 011: RL_MALLOC(sz) Name: RL_MALLOC(sz) Type: MACRO - Value: ((sz > MAX_ALLOC_SIZE)? malloc(sz) : NULL) + Value: malloc(sz) Description: -Define 013: RL_CALLOC(n,sz) +Define 012: RL_CALLOC(n,sz) Name: RL_CALLOC(n,sz) Type: MACRO - Value: ((n*sz > MAX_ALLOC_SIZE)? calloc(n,sz) : NULL) + Value: calloc(n,sz) Description: -Define 014: RL_REALLOC(ptr,sz) +Define 013: RL_REALLOC(ptr,sz) Name: RL_REALLOC(ptr,sz) Type: MACRO Value: realloc(ptr,sz) Description: -Define 015: RL_FREE(ptr) +Define 014: RL_FREE(ptr) Name: RL_FREE(ptr) Type: MACRO Value: free(ptr) Description: -Define 016: CLITERAL(type) +Define 015: CLITERAL(type) Name: CLITERAL(type) Type: MACRO Value: type Description: -Define 017: RL_COLOR_TYPE +Define 016: RL_COLOR_TYPE Name: RL_COLOR_TYPE Type: GUARD Value: Description: -Define 018: RL_RECTANGLE_TYPE +Define 017: RL_RECTANGLE_TYPE Name: RL_RECTANGLE_TYPE Type: GUARD Value: Description: -Define 019: RL_VECTOR2_TYPE +Define 018: RL_VECTOR2_TYPE Name: RL_VECTOR2_TYPE Type: GUARD Value: Description: -Define 020: RL_VECTOR3_TYPE +Define 019: RL_VECTOR3_TYPE Name: RL_VECTOR3_TYPE Type: GUARD Value: Description: -Define 021: RL_VECTOR4_TYPE +Define 020: RL_VECTOR4_TYPE Name: RL_VECTOR4_TYPE Type: GUARD Value: Description: -Define 022: RL_QUATERNION_TYPE +Define 021: RL_QUATERNION_TYPE Name: RL_QUATERNION_TYPE Type: GUARD Value: Description: -Define 023: RL_MATRIX_TYPE +Define 022: RL_MATRIX_TYPE Name: RL_MATRIX_TYPE Type: GUARD Value: Description: -Define 024: LIGHTGRAY +Define 023: LIGHTGRAY Name: LIGHTGRAY Type: COLOR Value: CLITERAL(Color){ 200, 200, 200, 255 } Description: Light Gray -Define 025: GRAY +Define 024: GRAY Name: GRAY Type: COLOR Value: CLITERAL(Color){ 130, 130, 130, 255 } Description: Gray -Define 026: DARKGRAY +Define 025: DARKGRAY Name: DARKGRAY Type: COLOR Value: CLITERAL(Color){ 80, 80, 80, 255 } Description: Dark Gray -Define 027: YELLOW +Define 026: YELLOW Name: YELLOW Type: COLOR Value: CLITERAL(Color){ 253, 249, 0, 255 } Description: Yellow -Define 028: GOLD +Define 027: GOLD Name: GOLD Type: COLOR Value: CLITERAL(Color){ 255, 203, 0, 255 } Description: Gold -Define 029: ORANGE +Define 028: ORANGE Name: ORANGE Type: COLOR Value: CLITERAL(Color){ 255, 161, 0, 255 } Description: Orange -Define 030: PINK +Define 029: PINK Name: PINK Type: COLOR Value: CLITERAL(Color){ 255, 109, 194, 255 } Description: Pink -Define 031: RED +Define 030: RED Name: RED Type: COLOR Value: CLITERAL(Color){ 230, 41, 55, 255 } Description: Red -Define 032: MAROON +Define 031: MAROON Name: MAROON Type: COLOR Value: CLITERAL(Color){ 190, 33, 55, 255 } Description: Maroon -Define 033: GREEN +Define 032: GREEN Name: GREEN Type: COLOR Value: CLITERAL(Color){ 0, 228, 48, 255 } Description: Green -Define 034: LIME +Define 033: LIME Name: LIME Type: COLOR Value: CLITERAL(Color){ 0, 158, 47, 255 } Description: Lime -Define 035: DARKGREEN +Define 034: DARKGREEN Name: DARKGREEN Type: COLOR Value: CLITERAL(Color){ 0, 117, 44, 255 } Description: Dark Green -Define 036: SKYBLUE +Define 035: SKYBLUE Name: SKYBLUE Type: COLOR Value: CLITERAL(Color){ 102, 191, 255, 255 } Description: Sky Blue -Define 037: BLUE +Define 036: BLUE Name: BLUE Type: COLOR Value: CLITERAL(Color){ 0, 121, 241, 255 } Description: Blue -Define 038: DARKBLUE +Define 037: DARKBLUE Name: DARKBLUE Type: COLOR Value: CLITERAL(Color){ 0, 82, 172, 255 } Description: Dark Blue -Define 039: PURPLE +Define 038: PURPLE Name: PURPLE Type: COLOR Value: CLITERAL(Color){ 200, 122, 255, 255 } Description: Purple -Define 040: VIOLET +Define 039: VIOLET Name: VIOLET Type: COLOR Value: CLITERAL(Color){ 135, 60, 190, 255 } Description: Violet -Define 041: DARKPURPLE +Define 040: DARKPURPLE Name: DARKPURPLE Type: COLOR Value: CLITERAL(Color){ 112, 31, 126, 255 } Description: Dark Purple -Define 042: BEIGE +Define 041: BEIGE Name: BEIGE Type: COLOR Value: CLITERAL(Color){ 211, 176, 131, 255 } Description: Beige -Define 043: BROWN +Define 042: BROWN Name: BROWN Type: COLOR Value: CLITERAL(Color){ 127, 106, 79, 255 } Description: Brown -Define 044: DARKBROWN +Define 043: DARKBROWN Name: DARKBROWN Type: COLOR Value: CLITERAL(Color){ 76, 63, 47, 255 } Description: Dark Brown -Define 045: WHITE +Define 044: WHITE Name: WHITE Type: COLOR Value: CLITERAL(Color){ 255, 255, 255, 255 } Description: White -Define 046: BLACK +Define 045: BLACK Name: BLACK Type: COLOR Value: CLITERAL(Color){ 0, 0, 0, 255 } Description: Black -Define 047: BLANK +Define 046: BLANK Name: BLANK Type: COLOR Value: CLITERAL(Color){ 0, 0, 0, 0 } Description: Blank (Transparent) -Define 048: MAGENTA +Define 047: MAGENTA Name: MAGENTA Type: COLOR Value: CLITERAL(Color){ 255, 0, 255, 255 } Description: Magenta -Define 049: RAYWHITE +Define 048: RAYWHITE Name: RAYWHITE Type: COLOR Value: CLITERAL(Color){ 245, 245, 245, 255 } Description: My own White (raylib logo) -Define 050: RL_BOOL_TYPE +Define 049: RL_BOOL_TYPE Name: RL_BOOL_TYPE Type: GUARD Value: Description: -Define 051: MOUSE_LEFT_BUTTON +Define 050: MOUSE_LEFT_BUTTON Name: MOUSE_LEFT_BUTTON Type: UNKNOWN Value: MOUSE_BUTTON_LEFT Description: -Define 052: MOUSE_RIGHT_BUTTON +Define 051: MOUSE_RIGHT_BUTTON Name: MOUSE_RIGHT_BUTTON Type: UNKNOWN Value: MOUSE_BUTTON_RIGHT Description: -Define 053: MOUSE_MIDDLE_BUTTON +Define 052: MOUSE_MIDDLE_BUTTON Name: MOUSE_MIDDLE_BUTTON Type: UNKNOWN Value: MOUSE_BUTTON_MIDDLE Description: -Define 054: MATERIAL_MAP_DIFFUSE +Define 053: MATERIAL_MAP_DIFFUSE Name: MATERIAL_MAP_DIFFUSE Type: UNKNOWN Value: MATERIAL_MAP_ALBEDO Description: -Define 055: MATERIAL_MAP_SPECULAR +Define 054: MATERIAL_MAP_SPECULAR Name: MATERIAL_MAP_SPECULAR Type: UNKNOWN Value: MATERIAL_MAP_METALNESS Description: -Define 056: SHADER_LOC_MAP_DIFFUSE +Define 055: SHADER_LOC_MAP_DIFFUSE Name: SHADER_LOC_MAP_DIFFUSE Type: UNKNOWN Value: SHADER_LOC_MAP_ALBEDO Description: -Define 057: SHADER_LOC_MAP_SPECULAR +Define 056: SHADER_LOC_MAP_SPECULAR Name: SHADER_LOC_MAP_SPECULAR Type: UNKNOWN Value: SHADER_LOC_MAP_METALNESS Description: -Define 058: GetMouseRay +Define 057: GetMouseRay Name: GetMouseRay Type: UNKNOWN Value: GetScreenToWorldRay diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 840507928..daa1b5472 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -1,6 +1,6 @@ - + @@ -11,9 +11,8 @@ - - - + + From 2f49250baf68000f79d37f2cc0478d3fa310c331 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jul 2024 18:31:16 +0200 Subject: [PATCH 19/40] WARNING: UPDATE: Starting works on `raylib 5.5` release --- examples/Makefile | 2 +- src/Makefile | 4 ++-- src/raylib.dll.rc | 10 +++++----- src/raylib.dll.rc.data | Bin 11246 -> 11246 bytes src/raylib.h | 6 +++--- src/raylib.rc | 12 ++++++------ src/raylib.rc.data | Bin 11182 -> 11182 bytes 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index c9f819dde..b9a2aa886 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -62,7 +62,7 @@ endif # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 5.0.0 +RAYLIB_VERSION ?= 5.5.0 RAYLIB_PATH ?= .. # Define raylib source code path diff --git a/src/Makefile b/src/Makefile index e40a50796..c68a03797 100644 --- a/src/Makefile +++ b/src/Makefile @@ -67,8 +67,8 @@ else endif # Define required raylib variables -RAYLIB_VERSION = 5.0.0 -RAYLIB_API_VERSION = 500 +RAYLIB_VERSION = 5.5.0 +RAYLIB_API_VERSION = 550 # Define raylib source code path RAYLIB_SRC_PATH ?= ../src diff --git a/src/raylib.dll.rc b/src/raylib.dll.rc index ba2f5bbf4..b0c2f58f8 100644 --- a/src/raylib.dll.rc +++ b/src/raylib.dll.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 5,0,0,0 -PRODUCTVERSION 5,0,0,0 +FILEVERSION 5,5,0,0 +PRODUCTVERSION 5,5,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib dynamic library (www.raylib.com)" - VALUE "FileVersion", "5.0.0" + VALUE "FileVersion", "5.5.0" VALUE "InternalName", "raylib.dll" - VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" + VALUE "LegalCopyright", "(c) 2024 Ramon Santamaria (@raysan5)" VALUE "OriginalFilename", "raylib.dll" VALUE "ProductName", "raylib" - VALUE "ProductVersion", "5.0.0" + VALUE "ProductVersion", "5.5.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.dll.rc.data b/src/raylib.dll.rc.data index e93edcffbcb1e7363078ec6d16a9c83b79e50b3c..3c87c5ca34644547b4c229bfdd185df4174c5954 100644 GIT binary patch delta 48 zcmaDC{w{pO9Sv?)237_TU}a#P{82-Ek|xJwHqBd%rjs9P7BZSl4%WKLXgXO_`zQco C1rERf delta 48 zcmaDC{w{pO9Sv><237_LV4eI?Lwu4Z$7D9mTZ{&iA8Hmd8cz<^y2@xUSyTHc0AVB! As{jB1 diff --git a/src/raylib.h b/src/raylib.h index 2c7a6a573..0703131c4 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib v5.1-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* raylib v5.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * * FEATURES: * - NO external dependencies, all required libraries included with raylib @@ -82,9 +82,9 @@ #include // Required for: va_list - Only used by TraceLogCallback #define RAYLIB_VERSION_MAJOR 5 -#define RAYLIB_VERSION_MINOR 1 +#define RAYLIB_VERSION_MINOR 5 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "5.1-dev" +#define RAYLIB_VERSION "5.5" // Function specifiers in case library is build/used as a shared library // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll diff --git a/src/raylib.rc b/src/raylib.rc index 12f35aaed..5076b4a26 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 5,0,0,0 -PRODUCTVERSION 5,0,0,0 +FILEVERSION 5,5,0,0 +PRODUCTVERSION 5,5,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib application (www.raylib.com)" - VALUE "FileVersion", "5.0.0" + VALUE "FileVersion", "5.5.0" VALUE "InternalName", "raylib app" - VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" - //VALUE "OriginalFilename", "raylib_app.exe" + VALUE "LegalCopyright", "(c) 2024 Ramon Santamaria (@raysan5)" + //VALUE "OriginalFilename", "raylib_app" VALUE "ProductName", "raylib app" - VALUE "ProductVersion", "5.0.0" + VALUE "ProductVersion", "5.5.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.rc.data b/src/raylib.rc.data index 1476a1cbaeb46b6b4ddc19647cb7fe87993a5e97..5a9aa4ea0603f67448db688d502efe5173e92e11 100644 GIT binary patch delta 48 zcmZ1%zAk*j9Sv?)237_TU}a#P{82-Ek|xJwHqA?nrju`K7BZSl_STxqXgc|&)=>aT C;|{L? delta 48 zcmZ1%zAk*j9Sv><237_LV4eI?Lwu4Z$7D9mON<7SZ)z4Y8c+7tn#*V~`K8uT07(K4 Ang9R* From 899d03e77ecd8e7db4a6b76c24e93d9b945ba22c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 1 Jul 2024 16:31:58 +0000 Subject: [PATCH 20/40] Update raylib_api.* by CI --- parser/output/raylib_api.json | 4 ++-- parser/output/raylib_api.lua | 4 ++-- parser/output/raylib_api.txt | 4 ++-- parser/output/raylib_api.xml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index e9ec07879..810599d57 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -15,7 +15,7 @@ { "name": "RAYLIB_VERSION_MINOR", "type": "INT", - "value": 1, + "value": 5, "description": "" }, { @@ -27,7 +27,7 @@ { "name": "RAYLIB_VERSION", "type": "STRING", - "value": "5.1-dev", + "value": "5.5", "description": "" }, { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 1ec84bcad..3bc27ffa9 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -15,7 +15,7 @@ return { { name = "RAYLIB_VERSION_MINOR", type = "INT", - value = 1, + value = 5, description = "" }, { @@ -27,7 +27,7 @@ return { { name = "RAYLIB_VERSION", type = "STRING", - value = "5.1-dev", + value = "5.5", description = "" }, { diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index c4d5f052c..4aed95a34 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -14,7 +14,7 @@ Define 002: RAYLIB_VERSION_MAJOR Define 003: RAYLIB_VERSION_MINOR Name: RAYLIB_VERSION_MINOR Type: INT - Value: 1 + Value: 5 Description: Define 004: RAYLIB_VERSION_PATCH Name: RAYLIB_VERSION_PATCH @@ -24,7 +24,7 @@ Define 004: RAYLIB_VERSION_PATCH Define 005: RAYLIB_VERSION Name: RAYLIB_VERSION Type: STRING - Value: "5.1-dev" + Value: "5.5" Description: Define 006: __declspec(x) Name: __declspec(x) diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index daa1b5472..32a920a82 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -3,9 +3,9 @@ - + - + From 18d726be65a4ca8da760358eb4c7906de1467c04 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jul 2024 18:49:47 +0200 Subject: [PATCH 21/40] Update Makefile --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index b9a2aa886..74409155a 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -99,7 +99,7 @@ BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources # Determine PLATFORM_OS when required -ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB PLATFORM_DESKTOP_RGFW)) +ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_DESKTOP_GLFW PLATFORM_DESKTOP_SDL PLATFORM_DESKTOP_RGFW PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) From ab20b2179f6d20c8a2e53761c57324cc987898d4 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jul 2024 18:56:06 +0200 Subject: [PATCH 22/40] Update Makefile --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 74409155a..be8d87204 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -651,7 +651,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) else ifeq ($(TARGET_PLATFORM),PLATFORM_WEB) $(MAKE) -f Makefile.Web $@ else - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(TARGET_PLATFORM) + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -D$(TARGET_PLATFORM) endif # Clean everything From 9d3bd43c6ed48e6276687ccec8b9caabbf8f73d3 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Mon, 1 Jul 2024 13:03:21 -0700 Subject: [PATCH 23/40] [CORE] Fix MSVC warnings/errors and raymath.h in C++ (#4125) * Update raylib_api.* by CI * Fix MSVC warnings. Make raymath.h work in C++ in MSVC * whitespace cleanup --------- Co-authored-by: github-actions[bot] --- src/raymath.h | 10 +++++++--- src/rtextures.c | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index 0b6951938..62d52f8fd 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -2549,9 +2549,13 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio // Extract scale const float det = a*A + b*B + c*C; - float scalex = Vector3Length((Vector3){ a, b, c }); - float scaley = Vector3Length((Vector3){ d, e, f }); - float scalez = Vector3Length((Vector3){ g, h, i }); + Vector3 abc = { a, b, c }; + Vector3 def = { d, e, f }; + Vector3 ghi = { g, h, i }; + + float scalex = Vector3Length(abc); + float scaley = Vector3Length(def); + float scalez = Vector3Length(ghi); Vector3 s = { scalex, scaley, scalez }; if (det < 0) s = Vector3Negate(s); diff --git a/src/rtextures.c b/src/rtextures.c index 54bf7c6a0..1e88b64a4 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3658,7 +3658,7 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co { // Line is more horizontal // Calculate half the width of the line - int wy = (thick - 1)*sqrtf(dx*dx + dy*dy)/(2*abs(dx)); + int wy = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dx)); // Draw additional lines above and below the main line for (int i = 1; i <= wy; i++) @@ -3671,7 +3671,7 @@ void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color co { // Line is more vertical or perfectly horizontal // Calculate half the width of the line - int wx = (thick - 1)*sqrtf(dx*dx + dy*dy)/(2*abs(dy)); + int wx = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dy)); // Draw additional lines to the left and right of the main line for (int i = 1; i <= wx; i++) From 8fbb447a6d3696766443316afeef6d033629409d Mon Sep 17 00:00:00 2001 From: Frank Kartheuser Date: Thu, 4 Jul 2024 00:01:40 +0200 Subject: [PATCH 24/40] Change SDL_Joystick to SDL_GameController (#4129) With SDL_Joystick my game controller wasn't working properly. That's why I changed it to SDL_GameController. --- src/platforms/rcore_desktop_sdl.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 9a6d16132..3d6293597 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -64,7 +64,7 @@ typedef struct { SDL_Window *window; SDL_GLContext glContext; - SDL_Joystick *gamepad[MAX_GAMEPADS]; + SDL_GameController *gamepad[MAX_GAMEPADS]; SDL_Cursor *cursor; bool cursorRelative; } PlatformData; @@ -944,7 +944,7 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor) if (IsGamepadAvailable(gamepad)) { - SDL_JoystickRumble(platform.gamepad[gamepad], (Uint16)(leftMotor*65535.0f), (Uint16)(rightMotor*65535.0f), (Uint32)(MAX_GAMEPAD_VIBRATION_TIME*1000.0f)); + SDL_GameControllerRumble(platform.gamepad[gamepad], (Uint16)(leftMotor*65535.0f), (Uint16)(rightMotor*65535.0f), (Uint32)(MAX_GAMEPAD_VIBRATION_TIME*1000.0f)); } } @@ -1245,15 +1245,15 @@ void PollInputEvents(void) if (!CORE.Input.Gamepad.ready[jid] && (jid < MAX_GAMEPADS)) { - platform.gamepad[jid] = SDL_JoystickOpen(jid); + platform.gamepad[jid] = SDL_GameControllerOpen(jid); if (platform.gamepad[jid]) { CORE.Input.Gamepad.ready[jid] = true; - CORE.Input.Gamepad.axisCount[jid] = SDL_JoystickNumAxes(platform.gamepad[jid]); + CORE.Input.Gamepad.axisCount[jid] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[jid])); CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; - strncpy(CORE.Input.Gamepad.name[jid], SDL_JoystickName(platform.gamepad[jid]), 63); + strncpy(CORE.Input.Gamepad.name[jid], SDL_GameControllerNameForIndex(jid), 63); CORE.Input.Gamepad.name[jid][63] = '\0'; } else @@ -1266,15 +1266,15 @@ void PollInputEvents(void) { int jid = event.jdevice.which; - if (jid == SDL_JoystickInstanceID(platform.gamepad[jid])) + if (jid == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[jid]))) { - SDL_JoystickClose(platform.gamepad[jid]); - platform.gamepad[jid] = SDL_JoystickOpen(0); + SDL_GameControllerClose(platform.gamepad[jid]); + platform.gamepad[jid] = SDL_GameControllerOpen(0); CORE.Input.Gamepad.ready[jid] = false; memset(CORE.Input.Gamepad.name[jid], 0, 64); } } break; - case SDL_JOYBUTTONDOWN: + case SDL_CONTROLLERBUTTONDOWN: { int button = -1; @@ -1308,7 +1308,7 @@ void PollInputEvents(void) CORE.Input.Gamepad.lastButtonPressed = button; } } break; - case SDL_JOYBUTTONUP: + case SDL_CONTROLLERBUTTONUP: { int button = -1; @@ -1342,7 +1342,7 @@ void PollInputEvents(void) if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; } } break; - case SDL_JOYAXISMOTION: + case SDL_CONTROLLERAXISMOTION: { int axis = -1; @@ -1548,15 +1548,15 @@ int InitPlatform(void) // Initialize gamepads for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++) { - platform.gamepad[i] = SDL_JoystickOpen(i); + platform.gamepad[i] = SDL_GameControllerOpen(i); if (platform.gamepad[i]) { CORE.Input.Gamepad.ready[i] = true; - CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(platform.gamepad[i]); + CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i])); CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; - strncpy(CORE.Input.Gamepad.name[i], SDL_JoystickName(platform.gamepad[i]), 63); + strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), 63); CORE.Input.Gamepad.name[i][63] = '\0'; } else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError()); From c95b2e88b703e7fee5e7f4e7b0c01906c58f794c Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 Jul 2024 11:12:20 +0200 Subject: [PATCH 25/40] Example review --- examples/models/models_loading_gltf.c | 19 +++++++++---------- .../examples/models_loading_gltf.vcxproj | 3 +++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index e85b30e6c..8b8838c8d 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -30,11 +30,11 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [models] example - loading gltf"); + InitWindow(screenWidth, screenHeight, "raylib [models] example - loading gltf animations"); // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position + camera.position = (Vector3){ 6.0f, 6.0f, 6.0f }; // Camera position camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y @@ -42,17 +42,14 @@ int main(void) // Load gltf model Model model = LoadModel("resources/models/gltf/robot.glb"); - + Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position + // Load gltf model animations int animsCount = 0; unsigned int animIndex = 0; unsigned int animCurrentFrame = 0; ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/robot.glb", &animsCount); - Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - - DisableCursor(); // Limit cursor to relative movement inside the window - SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -61,7 +58,8 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera, CAMERA_THIRD_PERSON); + UpdateCamera(&camera, CAMERA_ORBITAL); + // Select current animation if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount; else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount; @@ -79,10 +77,8 @@ int main(void) ClearBackground(RAYWHITE); BeginMode3D(camera); - DrawModel(model, position, 1.0f, WHITE); // Draw animated model DrawGrid(10, 1.0f); - EndMode3D(); DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY); @@ -101,3 +97,6 @@ int main(void) return 0; } + + + diff --git a/projects/VS2022/examples/models_loading_gltf.vcxproj b/projects/VS2022/examples/models_loading_gltf.vcxproj index 141fb9423..aba2e6843 100644 --- a/projects/VS2022/examples/models_loading_gltf.vcxproj +++ b/projects/VS2022/examples/models_loading_gltf.vcxproj @@ -376,6 +376,9 @@ + + + {e89d61ac-55de-4482-afd4-df7242ebc859} From 9a280cda0be211ee751cdb44fedab8f1e698be2a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 Jul 2024 11:12:24 +0200 Subject: [PATCH 26/40] Update rlgl.h --- src/rlgl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 5ab677d98..5b921ab9c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -844,9 +844,9 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #define GL_GLEXT_PROTOTYPES #include // OpenGL ES 2.0 extensions library #elif defined(GRAPHICS_API_OPENGL_ES2) - // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, + // NOTE: OpenGL ES 2.0 can be enabled on Desktop platforms, // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 - #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) + #if defined(PLATFORM_DESKTOP_GLFW) || defined(PLATFORM_DESKTOP_SDL) #define GLAD_GLES2_IMPLEMENTATION #include "external/glad_gles2.h" #else @@ -2390,7 +2390,7 @@ void rlLoadExtensions(void *loader) #elif defined(GRAPHICS_API_OPENGL_ES2) - #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) + #if defined(PLATFORM_DESKTOP_GLFW) || defined(PLATFORM_DESKTOP_SDL) // TODO: Support GLAD loader for OpenGL ES 3.0 if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); From 1039e3c1bd61ce8694c6c191d6b34eb4807e5aae Mon Sep 17 00:00:00 2001 From: kai-z99 <147789796+kai-z99@users.noreply.github.com> Date: Sun, 7 Jul 2024 00:05:25 -0700 Subject: [PATCH 27/40] [rshapes] Give CheckCollisionPointCircle() its own implementation (#4135) * remove function call * fix --- src/rshapes.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rshapes.c b/src/rshapes.c index 9e7de1e2f..5d5ff02ca 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -2172,7 +2172,9 @@ bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius) { bool collision = false; - collision = CheckCollisionCircles(point, 0, center, radius); + float distanceSquared = (point.x - center.x) * (point.x - center.x) + (point.y - center.y) * (point.y - center.y); + + collision = distanceSquared <= radius * radius; return collision; } From a8240722c61d216d96c3f2cab27426ee7b7a3919 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 7 Jul 2024 09:09:34 +0200 Subject: [PATCH 28/40] REVIEWED: `CheckCollisionPointRec()` --- src/rshapes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 5d5ff02ca..89339708a 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -2172,9 +2172,9 @@ bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius) { bool collision = false; - float distanceSquared = (point.x - center.x) * (point.x - center.x) + (point.y - center.y) * (point.y - center.y); + float distanceSquared = (point.x - center.x)*(point.x - center.x) + (point.y - center.y)*(point.y - center.y); - collision = distanceSquared <= radius * radius; + if (distanceSquared <= radius*radius) collision = true; return collision; } From b61303244c2731b88d93fe5bc976f5e8e6ff72e3 Mon Sep 17 00:00:00 2001 From: Ninad Sachania Date: Sun, 7 Jul 2024 14:17:44 +0530 Subject: [PATCH 29/40] Fix Reddit badge (#4136) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79e883034..303bbcdd1 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html) [![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE) [![Discord Members](https://img.shields.io/discord/426912293134270465.svg?label=Discord&logo=discord)](https://discord.gg/raylib) -[![Subreddit Subscribers](https://img.shields.io/reddit/subreddit-subscribers/raylib?label=reddit%20r%2Fraylib&logo=reddit)](https://www.reddit.com/r/raylib/) +[![Reddit Static Badge](https://img.shields.io/badge/-r%2Fraylib-red?style=flat&logo=reddit&label=reddit)](https://www.reddit.com/r/raylib/) [![Youtube Subscribers](https://img.shields.io/youtube/channel/subscribers/UC8WIBkhYb5sBNqXO1mZ7WSQ?style=flat&label=Youtube&logo=youtube)](https://www.youtube.com/c/raylib) [![Twitch Status](https://img.shields.io/twitch/status/raysan5?style=flat&label=Twitch&logo=twitch)](https://www.twitch.tv/raysan5) From 6dd2a0e64554a37c9ab8c0a6204cb5bb1badc970 Mon Sep 17 00:00:00 2001 From: bohonghuang <1281299809@qq.com> Date: Mon, 8 Jul 2024 02:27:51 +0800 Subject: [PATCH 30/40] [rmodels] Consistent `DrawBillboardPro` with `DrawTexturePro` (#4132) * [rmodels] Re-implement `DrawBillboardPro` * [rmodels] Add comments to `DrawBillboardPro` * [rmodels] Make `DrawBillboardPro` consistent with `DrawTexturePro` * Update raylib_api.* by CI --------- Co-authored-by: github-actions[bot] --- examples/models/models_billboard.c | 12 ++- parser/output/raylib_api.json | 2 +- parser/output/raylib_api.lua | 2 +- parser/output/raylib_api.txt | 2 +- parser/output/raylib_api.xml | 2 +- src/raylib.h | 2 +- src/rmodels.c | 152 +++++++++++------------------ 7 files changed, 71 insertions(+), 103 deletions(-) diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 237e1b69d..7ad28513f 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -44,10 +44,12 @@ int main(void) // NOTE: Billboard locked on axis-Y Vector3 billUp = { 0.0f, 1.0f, 0.0f }; + // Set the height of the rotating billboard to 1.0 with the aspect ratio fixed + Vector2 size = { source.width / source.height, 1.0f }; + // Rotate around origin // Here we choose to rotate around the image center - // NOTE: (-1, 1) is the range where origin.x, origin.y is inside the texture - Vector2 rotateOrigin = { 0.0f }; + Vector2 origin = Vector2Scale(size, 0.5f); // Distance is needed for the correct billboard draw order // Larger distance (further away from the camera) should be drawn prior to smaller distance. @@ -84,11 +86,11 @@ int main(void) if (distanceStatic > distanceRotating) { DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE); - DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, (Vector2) {1.0f, 1.0f}, rotateOrigin, rotation, WHITE); + DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE); } else { - DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, (Vector2) {1.0f, 1.0f}, rotateOrigin, rotation, WHITE); + DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE); DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE); } @@ -108,4 +110,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 810599d57..ec09abf79 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -10400,7 +10400,7 @@ }, { "type": "float", - "name": "size" + "name": "scale" }, { "type": "Color", diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 3bc27ffa9..963b0ef6d 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -7230,7 +7230,7 @@ return { {type = "Camera", name = "camera"}, {type = "Texture2D", name = "texture"}, {type = "Vector3", name = "position"}, - {type = "float", name = "size"}, + {type = "float", name = "scale"}, {type = "Color", name = "tint"} } }, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 4aed95a34..406482226 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -3968,7 +3968,7 @@ Function 466: DrawBillboard() (5 input parameters) Param[1]: camera (type: Camera) Param[2]: texture (type: Texture2D) Param[3]: position (type: Vector3) - Param[4]: size (type: float) + Param[4]: scale (type: float) Param[5]: tint (type: Color) Function 467: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 32a920a82..9b8a73fa7 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -2645,7 +2645,7 @@ - + diff --git a/src/raylib.h b/src/raylib.h index 0703131c4..3bc44f03d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1546,7 +1546,7 @@ RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, floa RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint); // Draw a model wires (with texture if set) RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters RLAPI void DrawBoundingBox(BoundingBox box, Color color); // Draw bounding box (wires) -RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint); // Draw a billboard texture +RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a billboard texture RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation diff --git a/src/rmodels.c b/src/rmodels.c index b67c8e695..31b4c574a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3638,11 +3638,11 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float } // Draw a billboard -void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint) +void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint) { Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height }; - DrawBillboardRec(camera, texture, source, position, (Vector2){ size, size }, tint); + DrawBillboardRec(camera, texture, source, position, (Vector2) { scale*fabsf((float)source.width/source.height), scale }, tint); } // Draw a billboard (part of a texture defined by a rectangle) @@ -3651,116 +3651,82 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector // NOTE: Billboard locked on axis-Y Vector3 up = { 0.0f, 1.0f, 0.0f }; - DrawBillboardPro(camera, texture, source, position, up, size, Vector2Zero(), 0.0f, tint); + DrawBillboardPro(camera, texture, source, position, up, size, Vector2Scale(size, 0.5), 0.0f, tint); } // Draw a billboard with additional parameters -// NOTE: Size defines the destination rectangle size, stretching the source texture as required void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint) { - // NOTE: Billboard size will maintain source rectangle aspect ratio, size will represent billboard width - Vector2 sizeRatio = { size.x*fabsf((float)source.width/source.height), size.y }; - + // Compute the up vector and the right vector Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); - Vector3 right = { matView.m0, matView.m4, matView.m8 }; - //Vector3 up = { matView.m1, matView.m5, matView.m9 }; + right = Vector3Scale(right, size.x); + up = Vector3Scale(up, size.y); - Vector3 rightScaled = Vector3Scale(right, sizeRatio.x/2); - Vector3 upScaled = Vector3Scale(up, sizeRatio.y/2); - - Vector3 p1 = Vector3Add(rightScaled, upScaled); - Vector3 p2 = Vector3Subtract(rightScaled, upScaled); - - Vector3 topLeft = Vector3Scale(p2, -1); - Vector3 topRight = p1; - Vector3 bottomRight = p2; - Vector3 bottomLeft = Vector3Scale(p1, -1); - - if (rotation != 0.0f) + // Flip the content of the billboard while maintaining the counterclockwise edge rendering order + if (size.x < 0.0f) { - float sinRotation = sinf(rotation*DEG2RAD); - float cosRotation = cosf(rotation*DEG2RAD); - - // NOTE: (-1, 1) is the range where origin.x, origin.y is inside the texture - float rotateAboutX = sizeRatio.x*origin.x/2; - float rotateAboutY = sizeRatio.y*origin.y/2; - - float xtvalue, ytvalue; - float rotatedX, rotatedY; - - xtvalue = Vector3DotProduct(right, topLeft) - rotateAboutX; // Project points to x and y coordinates on the billboard plane - ytvalue = Vector3DotProduct(up, topLeft) - rotateAboutY; - rotatedX = xtvalue*cosRotation - ytvalue*sinRotation + rotateAboutX; // Rotate about the point origin - rotatedY = xtvalue*sinRotation + ytvalue*cosRotation + rotateAboutY; - topLeft = Vector3Add(Vector3Scale(up, rotatedY), Vector3Scale(right, rotatedX)); // Translate back to cartesian coordinates - - xtvalue = Vector3DotProduct(right, topRight) - rotateAboutX; - ytvalue = Vector3DotProduct(up, topRight) - rotateAboutY; - rotatedX = xtvalue*cosRotation - ytvalue*sinRotation + rotateAboutX; - rotatedY = xtvalue*sinRotation + ytvalue*cosRotation + rotateAboutY; - topRight = Vector3Add(Vector3Scale(up, rotatedY), Vector3Scale(right, rotatedX)); - - xtvalue = Vector3DotProduct(right, bottomRight) - rotateAboutX; - ytvalue = Vector3DotProduct(up, bottomRight) - rotateAboutY; - rotatedX = xtvalue*cosRotation - ytvalue*sinRotation + rotateAboutX; - rotatedY = xtvalue*sinRotation + ytvalue*cosRotation + rotateAboutY; - bottomRight = Vector3Add(Vector3Scale(up, rotatedY), Vector3Scale(right, rotatedX)); - - xtvalue = Vector3DotProduct(right, bottomLeft)-rotateAboutX; - ytvalue = Vector3DotProduct(up, bottomLeft)-rotateAboutY; - rotatedX = xtvalue*cosRotation - ytvalue*sinRotation + rotateAboutX; - rotatedY = xtvalue*sinRotation + ytvalue*cosRotation + rotateAboutY; - bottomLeft = Vector3Add(Vector3Scale(up, rotatedY), Vector3Scale(right, rotatedX)); + source.x += size.x; + source.width *= -1.0; + right = Vector3Negate(right); + origin.x *= -1.0f; + } + if (size.y < 0.0f) + { + source.y += size.y; + source.height *= -1.0; + up = Vector3Negate(up); + origin.y *= -1.0f; } - // Translate points to the draw center (position) - topLeft = Vector3Add(topLeft, position); - topRight = Vector3Add(topRight, position); - bottomRight = Vector3Add(bottomRight, position); - bottomLeft = Vector3Add(bottomLeft, position); + // Draw the texture region described by source on the following rectangle in 3D space: + // + // size.x <--. + // 3 ^---------------------------+ 2 \ rotation + // | | / + // | | + // | origin.x position | + // up |.............. | size.y + // | . | + // | . origin.y | + // | . | + // 0 +---------------------------> 1 + // right + Vector3 forward; + if (rotation != 0.0) forward = Vector3CrossProduct(right, up); + + Vector3 origin3D = Vector3Add(Vector3Scale(Vector3Normalize(right), origin.x), Vector3Scale(Vector3Normalize(up), origin.y)); + + Vector3 points[4]; + points[0] = Vector3Zero(); + points[1] = right; + points[2] = Vector3Add(up, right); + points[3] = up; + + for (int i = 0; i < 4; i++) + { + points[i] = Vector3Subtract(points[i], origin3D); + if (rotation != 0.0) points[i] = Vector3RotateByAxisAngle(points[i], forward, rotation * DEG2RAD); + points[i] = Vector3Add(points[i], position); + } + + Vector2 texcoords[4]; + texcoords[0] = (Vector2) { (float)source.x/texture.width, (float)(source.y + source.height)/texture.height }; + texcoords[1] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height }; + texcoords[2] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)source.y/texture.height }; + texcoords[3] = (Vector2) { (float)source.x/texture.width, (float)source.y/texture.height }; rlSetTexture(texture.id); - rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); - - if (sizeRatio.x*sizeRatio.y >= 0.0f) + for (int i = 0; i < 4; i++) { - // Bottom-left corner for texture and quad - rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height); - rlVertex3f(topLeft.x, topLeft.y, topLeft.z); - - // Top-left corner for texture and quad - rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height); - rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); - - // Top-right corner for texture and quad - rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height); - rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); - - // Bottom-right corner for texture and quad - rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height); - rlVertex3f(topRight.x, topRight.y, topRight.z); - } - else - { - // Reverse vertex order if the size has only one negative dimension - rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height); - rlVertex3f(topRight.x, topRight.y, topRight.z); - - rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height); - rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); - - rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height); - rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); - - rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height); - rlVertex3f(topLeft.x, topLeft.y, topLeft.z); + rlTexCoord2f(texcoords[i].x, texcoords[i].y); + rlVertex3f(points[i].x, points[i].y, points[i].z); } rlEnd(); - rlSetTexture(0); } From df4ff4e78b4e2d35f05ffc402a2d72fe4f047a23 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 7 Jul 2024 20:57:18 +0200 Subject: [PATCH 31/40] REVIEWED: Direction must be normalized #4131 --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 3bc44f03d..60ec3c23e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -421,7 +421,7 @@ typedef struct ModelAnimation { // Ray, ray for raycasting typedef struct Ray { Vector3 position; // Ray position (origin) - Vector3 direction; // Ray direction + Vector3 direction; // Ray direction (normalized) } Ray; // RayCollision, ray hit information From bc6cf61794d54158fae94b122e37121ef178b4bb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 7 Jul 2024 18:57:40 +0000 Subject: [PATCH 32/40] Update raylib_api.* by CI --- parser/output/raylib_api.json | 2 +- parser/output/raylib_api.lua | 2 +- parser/output/raylib_api.txt | 2 +- parser/output/raylib_api.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index ec09abf79..c00407dc6 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -1058,7 +1058,7 @@ { "type": "Vector3", "name": "direction", - "description": "Ray direction" + "description": "Ray direction (normalized)" } ] }, diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 963b0ef6d..9cbd1b9da 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -1058,7 +1058,7 @@ return { { type = "Vector3", name = "direction", - description = "Ray direction" + description = "Ray direction (normalized)" } } }, diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 406482226..31433073e 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -473,7 +473,7 @@ Struct 23: Ray (2 fields) Name: Ray Description: Ray, ray for raycasting Field[1]: Vector3 position // Ray position (origin) - Field[2]: Vector3 direction // Ray direction + Field[2]: Vector3 direction // Ray direction (normalized) Struct 24: RayCollision (4 fields) Name: RayCollision Description: RayCollision, ray hit information diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 9b8a73fa7..51473562c 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -220,7 +220,7 @@ - + From b8e51794310e58e477cfa630059d061fcd5fa1f4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 7 Jul 2024 21:02:20 +0200 Subject: [PATCH 33/40] Update rmodels.c --- src/rmodels.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 31b4c574a..c6acff5dc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5172,7 +5172,7 @@ static Model LoadGLTF(const char *fileName) // Transform the vertices float *vertices = model.meshes[meshIndex].vertices; - for (int k = 0; k < attribute->count; k++) + for (unsigned int k = 0; k < attribute->count; k++) { Vector3 vt = Vector3Transform((Vector3){ vertices[3*k], vertices[3*k+1], vertices[3*k+2] }, worldMatrix); vertices[3*k] = vt.x; @@ -5196,7 +5196,7 @@ static Model LoadGLTF(const char *fileName) // Transform the normals float *normals = model.meshes[meshIndex].normals; - for (int k = 0; k < attribute->count; k++) + for (unsigned int k = 0; k < attribute->count; k++) { Vector3 nt = Vector3Transform((Vector3){ normals[3*k], normals[3*k+1], normals[3*k+2] }, worldMatrixNormals); normals[3*k] = nt.x; @@ -5220,7 +5220,7 @@ static Model LoadGLTF(const char *fileName) // Transform the tangents float *tangents = model.meshes[meshIndex].tangents; - for (int k = 0; k < attribute->count; k++) + for (unsigned int k = 0; k < attribute->count; k++) { Vector3 tt = Vector3Transform((Vector3){ tangents[3*k], tangents[3*k+1], tangents[3*k+2] }, worldMatrix); tangents[3*k] = tt.x; From 9764fef26260e6fcf671ddffb230360cc1efa1f8 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 7 Jul 2024 21:02:35 +0200 Subject: [PATCH 34/40] Update models_billboard.c --- examples/models/models_billboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/models/models_billboard.c b/examples/models/models_billboard.c index 7ad28513f..cfb49e55c 100644 --- a/examples/models/models_billboard.c +++ b/examples/models/models_billboard.c @@ -45,7 +45,7 @@ int main(void) Vector3 billUp = { 0.0f, 1.0f, 0.0f }; // Set the height of the rotating billboard to 1.0 with the aspect ratio fixed - Vector2 size = { source.width / source.height, 1.0f }; + Vector2 size = { source.width/source.height, 1.0f }; // Rotate around origin // Here we choose to rotate around the image center From 598b7f52104a4dd9246ffe01cffcdc0b376cb45c Mon Sep 17 00:00:00 2001 From: NishiOwO <89888985+NishiOwO@users.noreply.github.com> Date: Tue, 9 Jul 2024 05:47:35 +0900 Subject: [PATCH 35/40] Add workaround for NetBSD (#4139) --- src/platforms/rcore_desktop_glfw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index dbdde9dbd..67cf9deff 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1617,6 +1617,10 @@ int InitPlatform(void) CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- +#if defined(__NetBSD__) + // Workaround for NetBSD + char* glfwPlatform = "X11"; +#else char* glfwPlatform = ""; switch (glfwGetPlatform()) { @@ -1626,6 +1630,7 @@ int InitPlatform(void) case GLFW_PLATFORM_X11: glfwPlatform = "X11"; break; case GLFW_PLATFORM_NULL: glfwPlatform = "Null"; break; } +#endif TRACELOG(LOG_INFO, "GLFW platform: %s", glfwPlatform); TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully"); From 3abe728712fb10e215ffcf97a1b06178895692d3 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 8 Jul 2024 22:54:19 +0200 Subject: [PATCH 36/40] Minor tweaks --- src/platforms/rcore_desktop_glfw.c | 4 ++-- src/platforms/rcore_desktop_rgfw.c | 2 +- src/raudio.c | 2 +- src/rmodels.c | 2 +- src/rtext.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 67cf9deff..42c64402f 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1619,9 +1619,9 @@ int InitPlatform(void) #if defined(__NetBSD__) // Workaround for NetBSD - char* glfwPlatform = "X11"; + char *glfwPlatform = "X11"; #else - char* glfwPlatform = ""; + char *glfwPlatform = ""; switch (glfwGetPlatform()) { case GLFW_PLATFORM_WIN32: glfwPlatform = "Win32"; break; diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index d53237fcd..7dfe1f51c 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -79,7 +79,7 @@ void CloseWindow(void); #endif #ifdef _MSC_VER -__declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char* lpMultiByteStr, int cbMultiByte, wchar_t* lpWideCharStr, int cchWideChar); +__declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar); #endif #include "../external/RGFW.h" diff --git a/src/raudio.c b/src/raudio.c index e33712978..21d1adae3 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1550,7 +1550,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) { // Open ogg audio stream - stb_vorbis* ctxOgg = stb_vorbis_open_memory((const unsigned char*)data, dataSize, NULL, NULL); + stb_vorbis* ctxOgg = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL); if (ctxOgg != NULL) { diff --git a/src/rmodels.c b/src/rmodels.c index c6acff5dc..50328fca7 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4294,7 +4294,7 @@ static Model LoadIQM(const char *fileName) // In case file can not be read, return an empty model if (fileDataPtr == NULL) return model; - const char* basePath = GetDirectoryPath(fileName); + const char *basePath = GetDirectoryPath(fileName); // Read IQM header IQMHeader *iqmHeader = (IQMHeader *)fileDataPtr; diff --git a/src/rtext.c b/src/rtext.c index 8daf3a7bc..755b15efd 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1572,7 +1572,7 @@ char *TextReplace(const char *text, const char *replace, const char *by) byLen = TextLength(by); // Count the number of replacements needed - insertPoint = (char*)text; + insertPoint = (char *)text; for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen; // Allocate returning string and point temp to it @@ -2339,7 +2339,7 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, i int readBytes = 0; // Data bytes read (line) int readVars = 0; // Variables filled by sscanf() - const char *fileText = (const char*)fileData; + const char *fileText = (const char *)fileData; const char *fileTextPtr = fileText; bool fontMalformed = false; // Is the font malformed From 98662b6a4a77994000348891820b6785d4a3b09e Mon Sep 17 00:00:00 2001 From: Colleague Riley Date: Tue, 9 Jul 2024 03:12:03 -0400 Subject: [PATCH 37/40] update RGFW to RGFW 1.0 (#4144) * update RGFW * fix bug with GetCurrentMonitor --- src/external/RGFW.h | 4182 ++++++++++++++-------------- src/platforms/rcore_desktop_rgfw.c | 92 +- 2 files changed, 2125 insertions(+), 2149 deletions(-) diff --git a/src/external/RGFW.h b/src/external/RGFW.h index 86e68a17f..646ecc9a0 100644 --- a/src/external/RGFW.h +++ b/src/external/RGFW.h @@ -37,7 +37,6 @@ This version doesn't work for desktops (I'm pretty sure) #define RGFW_OPENGL_ES2 - (optional) use OpenGL ES (version 2) #define RGFW_OPENGL_ES3 - (optional) use OpenGL ES (version 3) - #define RGFW_VULKAN - (optional) use vulkan for the rendering backend (rather than opengl) #define RGFW_DIRECTX - (optional) use directX for the rendering backend (rather than opengl) (windows only, defaults to opengl for unix) #define RGFW_NO_API - (optional) don't use any rendering API (no opengl, no vulkan, no directX) @@ -59,7 +58,7 @@ /* Credits : - EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support + EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support, siliapp.h -> referencing stb - This project is heavily inspired by the stb single header files @@ -72,6 +71,13 @@ Copyright (c) 2002-2006 Marcus Geelnard Copyright (c) 2006-2019 Camilla Löwy + + contributors : (feel free to put yourself here if you contribute) + krisvers -> code review + EimaMei (SaCode) -> code review + Code-Nycticebus -> bug fixes + Rob Rohan -> X11 bugs and missing features + AICDG (@THISISAGOODNAME) -> vulkan support (example) */ #ifndef RGFW_MALLOC @@ -90,6 +96,12 @@ #endif #endif +/* for windows 95 testing (not that it works well) */ +#ifdef RGFW_WIN95 +#define RGFW_NO_MONITOR +#define RGFW_NO_PASSTHROUGH +#endif + #ifndef RGFWDEF #ifdef __APPLE__ #define RGFWDEF static inline @@ -98,8 +110,12 @@ #endif #endif +#ifndef RGFW_ENUM +#define RGFW_ENUM(type, name) type name; enum +#endif + #ifndef RGFW_UNUSED -#define RGFW_UNUSED(x) if (x){} +#define RGFW_UNUSED(x) (void)(x); #endif #ifdef __cplusplus @@ -135,6 +151,10 @@ extern "C" { #endif #endif +#if !defined(b8) + typedef u8 b8; +#endif + #if defined(RGFW_X11) && defined(__APPLE__) #define RGFW_MACOS_X11 #undef __APPLE__ @@ -161,16 +181,18 @@ extern "C" { #undef _X86_ #else #undef _AMD64_ +#ifndef _X86_ #define _X86_ #endif +#endif -#include - +#ifndef RGFW_NO_XINPUT #ifdef __MINGW32__ #include #else #include #endif +#endif #else #if defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11) @@ -192,28 +214,10 @@ extern "C" { #undef RGFW_EGL #endif -#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined (RGFW_VULKAN) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API) +#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API) #define RGFW_OPENGL #endif -#ifdef RGFW_VULKAN -#ifndef RGFW_MAX_FRAMES_IN_FLIGHT -#define RGFW_MAX_FRAMES_IN_FLIGHT 2 -#endif - -#ifdef RGFW_X11 -#define VK_USE_PLATFORM_XLIB_KHR -#endif -#ifdef RGFW_WINDOWS -#define VK_USE_PLATFORM_WIN32_KHR -#endif -#ifdef RGFW_MACOS -#define VK_USE_PLATFORM_MACOS_MVK -#endif - -#include -#endif - #if defined(RGFW_X11) && (defined(RGFW_OPENGL)) #ifndef GLX_MESA_swap_control #define GLX_MESA_swap_control @@ -244,8 +248,12 @@ extern "C" { #endif #endif - /*! Optional arguments for making a windows */ -#define RGFW_TRANSPARENT_WINDOW (1L<<9) /*!< the window is transparent */ +#ifndef RGFW_ALPHA +#define RGFW_ALPHA 128 /* alpha value for RGFW_TRANSPARENT_WINDOW (WINAPI ONLY, macOS + linux don't need this) */ +#endif + +/*! Optional arguments for making a windows */ +#define RGFW_TRANSPARENT_WINDOW (1L<<9) /*!< the window is transparent (only properly works on X11 and MacOS, although it's although for windows) */ #define RGFW_NO_BORDER (1L<<3) /*!< the window doesn't have border */ #define RGFW_NO_RESIZE (1L<<4) /*!< the window cannot be resized by the user */ #define RGFW_ALLOW_DND (1L<<5) /*!< the window supports drag and drop*/ @@ -255,6 +263,7 @@ extern "C" { #define RGFW_OPENGL_SOFTWARE (1L<<11) /*! use OpenGL software rendering */ #define RGFW_COCOA_MOVE_TO_RESOURCE_DIR (1L << 12) /* (cocoa only), move to resource folder */ #define RGFW_SCALE_TO_MONITOR (1L << 13) /* scale the window to the screen */ +#define RGFW_NO_INIT_API (1L << 2) /* DO not init an API (mostly for bindings, you should use `#define RGFW_NO_API` in C */ #define RGFW_NO_GPU_RENDER (1L<<14) /* don't render (using the GPU based API)*/ #define RGFW_NO_CPU_RENDER (1L<<15) /* don't render (using the CPU based buffer rendering)*/ @@ -298,6 +307,11 @@ extern "C" { #define RGFW_focusIn 12 /*!< window is in focus now */ #define RGFW_focusOut 13 /*!< window is out of focus now */ +#define RGFW_mouseEnter 14 /* mouse entered the window */ +#define RGFW_mouseLeave 15 /* mouse left the window */ + +#define RGFW_windowRefresh 16 /* The window content needs to be refreshed */ + /* attribs change event note The event data is sent straight to the window structure with win->r.x, win->r.y, win->r.w and win->r.h @@ -334,23 +348,25 @@ extern "C" { #define RGFW_NUMLOCK (1L << 2) /*! joystick button codes (based on xbox/playstation), you may need to change these values per controller */ -#ifndef RGFW_JS_A +#ifndef RGFW_joystick_codes -#define RGFW_JS_A 0 /* or PS X button */ -#define RGFW_JS_B 1 /* or PS circle button */ -#define RGFW_JS_Y 2 /* or PS triangle button */ -#define RGFW_JS_X 3 /* or PS square button */ -#define RGFW_JS_START 9 /* start button */ -#define RGFW_JS_SELECT 8 /* select button */ -#define RGFW_JS_HOME 10 /* home button */ -#define RGFW_JS_UP 13 /* dpad up */ -#define RGFW_JS_DOWN 14 /* dpad down*/ -#define RGFW_JS_LEFT 15 /* dpad left */ -#define RGFW_JS_RIGHT 16 /* dpad right */ -#define RGFW_JS_L1 4 /* left bump */ -#define RGFW_JS_L2 5 /* left trigger*/ -#define RGFW_JS_R1 6 /* right bumper */ -#define RGFW_JS_R2 7 /* right trigger */ +typedef RGFW_ENUM(u8, RGFW_joystick_codes) { + RGFW_JS_A = 0, /* or PS X button */ + RGFW_JS_B = 1, /* or PS circle button */ + RGFW_JS_Y = 2, /* or PS triangle button */ + RGFW_JS_X = 3, /* or PS square button */ + RGFW_JS_START = 9, /* start button */ + RGFW_JS_SELECT = 8, /* select button */ + RGFW_JS_HOME = 10, /* home button */ + RGFW_JS_UP = 13, /* dpad up */ + RGFW_JS_DOWN = 14, /* dpad down*/ + RGFW_JS_LEFT = 15, /* dpad left */ + RGFW_JS_RIGHT = 16, /* dpad right */ + RGFW_JS_L1 = 4, /* left bump */ + RGFW_JS_L2 = 5, /* left trigger*/ + RGFW_JS_R1 = 6, /* right bumper */ + RGFW_JS_R2 = 7, /* right trigger */ +}; #endif @@ -373,6 +389,7 @@ typedef struct { i32 x, y; } RGFW_vector; #define RGFW_RECT(x, y, w, h) (RGFW_rect){x, y, w, h} #define RGFW_AREA(w, h) (RGFW_area){w, h} + #ifndef RGFW_NO_MONITOR typedef struct RGFW_monitor { char name[128]; /* monitor name */ RGFW_rect rect; /* monitor Workarea */ @@ -388,14 +405,11 @@ typedef struct { i32 x, y; } RGFW_vector; RGFWDEF RGFW_monitor* RGFW_getMonitors(void); /* get the primary monitor */ RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void); + #endif /* NOTE: some parts of the data can represent different things based on the event (read comments in RGFW_Event struct) */ typedef struct RGFW_Event { -#ifdef RGFW_WINDOWS char keyName[16]; /* key name of event*/ -#else - char* keyName; /*!< key name of event */ -#endif /*! drag and drop data */ /* 260 max paths with a max length of 260 */ @@ -408,12 +422,13 @@ typedef struct { i32 x, y; } RGFW_vector; u32 type; /*!< which event has been sent?*/ RGFW_vector point; /*!< mouse x, y of event (or drop point) */ - u32 keyCode; /*!< keycode of event !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */ - + u32 fps; /*the current fps of the window [the fps is checked when events are checked]*/ u64 frameTime, frameTime2; /* this is used for counting the fps */ + + u8 keyCode; /*!< keycode of event !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */ - u8 inFocus; /*if the window is in focus or not*/ + b8 inFocus; /*if the window is in focus or not (this is always true for MacOS windows due to the api being weird) */ u8 lockState; @@ -441,7 +456,7 @@ typedef struct { i32 x, y; } RGFW_vector; u32 display; void* displayLink; void* window; - u8 dndPassed; + b8 dndPassed; #endif #if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) @@ -455,15 +470,6 @@ typedef struct { i32 x, y; } RGFW_vector; GLXContext rSurf; /*!< source graphics context */ #endif #else -#ifdef RGFW_VULKAN - VkSurfaceKHR rSurf; /*!< source graphics context */ - - /* vulkan data */ - VkSwapchainKHR swapchain; - u32 image_count; - VkImage* swapchain_images; - VkImageView* swapchain_image_views; -#endif #ifdef RGFW_OSMESA OSMesaContext rSurf; @@ -495,6 +501,7 @@ typedef struct { i32 x, y; } RGFW_vector; #endif #ifdef RGFW_X11 XImage* bitmap; + GC gc; #endif #ifdef RGFW_MACOS void* bitmap; /* API's bitmap for storing or managing */ @@ -513,7 +520,7 @@ typedef struct { i32 x, y; } RGFW_vector; RGFW_area scale; /* window scaling */ #ifdef RGFW_MACOS - u8 cursorChanged; /* for steve jobs */ + b8 cursorChanged; /* for steve jobs */ #endif u32 winArgs; /* windows args (for RGFW to check) */ @@ -545,6 +552,9 @@ typedef struct { i32 x, y; } RGFW_vector; typedef void* RGFW_thread; /* thread type for window */ #endif + /* this has to be set before createWindow is called, else the fulscreen size is used */ + RGFWDEF void RGFW_setBufferSize(RGFW_area size); /* the buffer cannot be resized (by RGFW) */ + RGFW_window* RGFW_createWindow( const char* name, /* name of the window */ RGFW_rect rect, /* rect of window */ @@ -561,9 +571,19 @@ typedef struct { i32 x, y; } RGFW_vector; ex. while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one] + + this function is optional if you choose to use event callbacks, + although you still need some way to tell RGFW to process events eg. `RGFW_window_checkEvents` */ - RGFW_Event* RGFW_window_checkEvent(RGFW_window* win); /*!< check events (returns a pointer to win->event or NULL if there is no event)*/ + RGFW_Event* RGFW_window_checkEvent(RGFW_window* win); /*!< check current event (returns a pointer to win->event or NULL if there is no event)*/ + + /* + check all the events until there are none left, + this should only be used if you're using callbacks only + */ + RGFWDEF void RGFW_window_checkEvents(RGFW_window* win); + /*! window managment functions*/ RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */ @@ -572,9 +592,10 @@ typedef struct { i32 x, y; } RGFW_vector; RGFW_vector v/* new pos*/ ); + #ifndef RGFW_NO_MONITOR /* move to a specific monitor */ RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m); - + #endif RGFWDEF void RGFW_window_resize(RGFW_window* win, RGFW_area a/* new size*/ ); @@ -588,6 +609,14 @@ typedef struct { i32 x, y; } RGFW_vector; RGFWDEF void RGFW_window_minimize(RGFW_window* win); /* minimize the window (in taskbar (per OS))*/ RGFWDEF void RGFW_window_restore(RGFW_window* win); /* restore the window from minimized (per OS)*/ + RGFWDEF void RGFW_window_setBorder(RGFW_window* win, b8 border); /* if the window should have a border or not (borderless) based on bool value of `border` */ + + RGFWDEF void RGFW_window_setDND(RGFW_window* win, b8 allow); /* turn on / off dnd (RGFW_ALLOW_DND stil must be passed to the window)*/ + + #ifndef RGFW_NO_PASSTHROUGH + RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough); /* turn on / off mouse passthrough */ + #endif + RGFWDEF void RGFW_window_setName(RGFW_window* win, char* name ); @@ -638,16 +667,18 @@ typedef struct { i32 x, y; } RGFW_vector; RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector v); /* if the window should close (RGFW_close was sent or escape was pressed) */ - RGFWDEF u8 RGFW_window_shouldClose(RGFW_window* win); + RGFWDEF b8 RGFW_window_shouldClose(RGFW_window* win); /* if window is fullscreen'd */ - RGFWDEF u8 RGFW_window_isFullscreen(RGFW_window* win); + RGFWDEF b8 RGFW_window_isFullscreen(RGFW_window* win); /* if window is hidden */ - RGFWDEF u8 RGFW_window_isHidden(RGFW_window* win); + RGFWDEF b8 RGFW_window_isHidden(RGFW_window* win); /* if window is minimized */ - RGFWDEF u8 RGFW_window_isMinimized(RGFW_window* win); + RGFWDEF b8 RGFW_window_isMinimized(RGFW_window* win); /* if window is maximized */ - RGFWDEF u8 RGFW_window_isMaximized(RGFW_window* win); + RGFWDEF b8 RGFW_window_isMaximized(RGFW_window* win); + + #ifndef RGFW_NO_MONITOR /* scale the window to the monitor, this is run by default if the user uses the arg `RGFW_SCALE_TO_MONITOR` during window creation @@ -655,53 +686,91 @@ typedef struct { i32 x, y; } RGFW_vector; RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win); /* get the struct of the window's monitor */ RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win); + #endif /*!< make the window the current opengl drawing context */ RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win); /*error handling*/ - RGFWDEF u8 RGFW_Error(void); /* returns true if an error has occurred (doesn't print errors itself) */ + RGFWDEF b8 RGFW_Error(void); /* returns true if an error has occurred (doesn't print errors itself) */ /*!< if window == NULL, it checks if the key is pressed globally. Otherwise, it checks only if the key is pressed while the window in focus.*/ - RGFWDEF u8 RGFW_isPressedI(RGFW_window* win, u32 key); /*!< if key is pressed (key code)*/ + RGFWDEF b8 RGFW_isPressed(RGFW_window* win, u8 key); /*!< if key is pressed (key code)*/ - RGFWDEF u8 RGFW_wasPressedI(RGFW_window* win, u32 key); /*!< if key was pressed (checks prev keymap only) (key code)*/ + RGFWDEF b8 RGFW_wasPressed(RGFW_window* win, u8 key); /*!< if key was pressed (checks prev keymap only) (key code)*/ - RGFWDEF u8 RGFW_isHeldI(RGFW_window* win, u32 key); /*!< if key is held (key code)*/ - RGFWDEF u8 RGFW_isReleasedI(RGFW_window* win, u32 key); /*!< if key is released (key code)*/ + RGFWDEF b8 RGFW_isHeld(RGFW_window* win, u8 key); /*!< if key is held (key code)*/ + RGFWDEF b8 RGFW_isReleased(RGFW_window* win, u8 key); /*!< if key is released (key code)*/ - RGFWDEF u8 RGFW_isMousePressed(RGFW_window* win, u8 button); - RGFWDEF u8 RGFW_isMouseHeld(RGFW_window* win, u8 button); - RGFWDEF u8 RGFW_isMouseReleased(RGFW_window* win, u8 button); - RGFWDEF u8 RGFW_wasMousePressed(RGFW_window* win, u8 button); + RGFWDEF b8 RGFW_isClicked(RGFW_window* win, u8 key); - /* - !!Keycodes defined at the bottom of RGFW_HEADER part of this file!! - */ - /*!< converts a key code to it's key string */ - RGFWDEF char* RGFW_keyCodeTokeyStr(u64 key); - /*!< converts a string of a key to it's key code */ - RGFWDEF u32 RGFW_keyStrToKeyCode(char* key); - /*!< if key is pressed (key string) */ -#define RGFW_isPressedS(win, key) RGFW_isPressedI(win, RGFW_keyStrToKeyCode(key)) + RGFWDEF b8 RGFW_isMousePressed(RGFW_window* win, u8 button); + RGFWDEF b8 RGFW_isMouseHeld(RGFW_window* win, u8 button); + RGFWDEF b8 RGFW_isMouseReleased(RGFW_window* win, u8 button); + RGFWDEF b8 RGFW_wasMousePressed(RGFW_window* win, u8 button); /*! clipboard functions*/ RGFWDEF char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */ -#define RGFW_clipboardFree free /* the string returned from RGFW_readClipboard must be freed */ + RGFWDEF void RGFW_clipboardFree(char* str); /* the string returned from RGFW_readClipboard must be freed */ RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */ - /* - convert a keyString to a char version - */ - RGFWDEF char RGFW_keystrToChar(const char*); - /* - ex. - "parenleft" -> '(' - "A" -> 'A', - "Return" -> "\n" + /* + + + Event callbacks, + these are completely optional, you can use the normal + RGFW_checkEvent() method if you prefer that + */ + /* RGFW_windowMoved, the window and its new rect value */ + typedef void (* RGFW_windowmovefunc)(RGFW_window* win, RGFW_rect r); + /* RGFW_windowResized, the window and its new rect value */ + typedef void (* RGFW_windowresizefunc)(RGFW_window* win, RGFW_rect r); + /* RGFW_quit, the window that was closed */ + typedef void (* RGFW_windowquitfunc)(RGFW_window* win); + /* RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its inFocus */ + typedef void (* RGFW_focusfunc)(RGFW_window* win, b8 inFocus); + /* RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */ + typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, RGFW_vector point, b8 status); + /* RGFW_mousePosChanged, the window that the move happened on and the new point of the mouse */ + typedef void (* RGFW_mouseposfunc)(RGFW_window* win, RGFW_vector point); + /* RGFW_dnd_init, the window, the point of the drop on the windows */ + typedef void (* RGFW_dndInitfunc)(RGFW_window* win, RGFW_vector point); + /* RGFW_windowRefresh, the window that needs to be refreshed */ + typedef void (* RGFW_windowrefreshfunc)(RGFW_window* win); + /* RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the keycode, the string version, the state of mod keys, if it was a press (else it's a release) */ + typedef void (* RGFW_keyfunc)(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed); + /* RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */ + typedef void (* RGFW_mousebuttonfunc)(RGFW_window* win, u8 button, double scroll, b8 pressed); + /* RGFW_jsButtonPressed / RGFW_jsButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */ + typedef void (* RGFW_jsButtonfunc)(RGFW_window* win, u16 joystick, u8 button, b8 pressed); + /* RGFW_jsAxisMove, the window that got the event, the joystick in question, the axis values and the amount of axises */ + typedef void (* RGFW_jsAxisfunc)(RGFW_window* win, u16 joystick, RGFW_vector axis[2], u8 axisesCount); + + /* RGFW_dnd, the window that had the drop, the drop data and the amount files dropped */ + #ifdef RGFW_ALLOC_DROPFILES + typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount); + #else + typedef void (* RGFW_dndfunc)(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount); + #endif + + RGFWDEF void RGFW_setWindowMoveCallback(RGFW_windowmovefunc func); + RGFWDEF void RGFW_setWindowResizeCallback(RGFW_windowresizefunc func); + RGFWDEF void RGFW_setWindowQuitCallback(RGFW_windowquitfunc func); + RGFWDEF void RGFW_setMousePosCallback(RGFW_mouseposfunc func); + RGFWDEF void RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func); + RGFWDEF void RGFW_setFocusCallback(RGFW_focusfunc func); + RGFWDEF void RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func); + RGFWDEF void RGFW_setDndCallback(RGFW_dndfunc func); + RGFWDEF void RGFW_setDndInitCallback(RGFW_dndInitfunc func); + RGFWDEF void RGFW_setKeyCallback(RGFW_keyfunc func); + RGFWDEF void RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func); + RGFWDEF void RGFW_setjsButtonCallback(RGFW_jsButtonfunc func); + RGFWDEF void RGFW_setjsAxisCallback(RGFW_jsAxisfunc func); + + #ifndef RGFW_NO_THREADS /*! threading functions*/ @@ -715,7 +784,7 @@ typedef struct { i32 x, y; } RGFW_vector; #if defined(__unix__) || defined(__APPLE__) typedef void* (* RGFW_threadFunc_ptr)(void*); #else - typedef DWORD (* RGFW_threadFunc_ptr)(void*); + typedef DWORD (__stdcall *RGFW_threadFunc_ptr) (LPVOID lpThreadParameter); #endif RGFWDEF RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args); /*!< create a thread*/ @@ -746,63 +815,14 @@ typedef struct { i32 x, y; } RGFW_vector; /*! Set OpenGL version hint */ RGFWDEF void RGFW_setGLVersion(i32 major, i32 minor); RGFWDEF void* RGFW_getProcAddress(const char* procname); /* get native opengl proc address */ + RGFWDEF void RGFW_window_makeCurrent_OpenGL(RGFW_window* win); /* to be called by RGFW_window_makeCurrent */ #endif /* supports openGL, directX, OSMesa, EGL and software rendering */ RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /* swap the rendering buffer */ RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval); RGFWDEF void RGFW_window_setGPURender(RGFW_window* win, i8 set); - -#ifdef RGFW_VULKAN - typedef struct { - VkInstance instance; - VkPhysicalDevice physical_device; - VkDevice device; - - VkDebugUtilsMessengerEXT debugMessenger; - - VkQueue graphics_queue; - VkQueue present_queue; - - VkFramebuffer* framebuffers; - - VkRenderPass render_pass; - VkPipelineLayout pipeline_layout; - VkPipeline graphics_pipeline; - - VkCommandPool command_pool; - VkCommandBuffer* command_buffers; - - VkSemaphore* available_semaphores; - VkSemaphore* finished_semaphore; - VkFence* in_flight_fences; - VkFence* image_in_flight; - size_t current_frame; - } RGFW_vulkanInfo; - - /*! initializes a vulkan rendering context for the RGFW window, - this outputs the vulkan surface into wwin->src.rSurf - other vulkan data is stored in the global instance of the RGFW_vulkanInfo structure which is returned - by the initVulkan() function - RGFW_VULKAN must be defined for this function to be defined - - */ - RGFWDEF RGFW_vulkanInfo* RGFW_initVulkan(RGFW_window* win); - RGFWDEF void RGFW_freeVulkan(void); - - RGFWDEF RGFW_vulkanInfo* RGFW_getVulkanInfo(void); - - RGFWDEF int RGFW_initData(RGFW_window* win); - RGFWDEF void RGFW_createSurface(VkInstance instance, RGFW_window* win); - int RGFW_deviceInitialization(RGFW_window* win); - int RGFW_createSwapchain(RGFW_window* win); - RGFWDEF int RGFW_createRenderPass(void); - int RGFW_createCommandPool(void); - int RGFW_createCommandBuffers(RGFW_window* win); - int RGFW_createSyncObjects(RGFW_window* win); - RGFWDEF int RGFW_createFramebuffers(RGFW_window* win); -#endif - + RGFWDEF void RGFW_window_setCPURender(RGFW_window* win, i8 set); #ifdef RGFW_DIRECTX typedef struct { IDXGIFactory* pFactory; @@ -824,7 +844,7 @@ typedef struct { i32 x, y; } RGFW_vector; RGFWDEF u64 RGFW_getTimeNS(void); /* get time in nanoseconds */ RGFWDEF void RGFW_sleep(u64 microsecond); /* sleep for a set time */ - typedef enum { + typedef RGFW_ENUM(u8, RGFW_Key) { RGFW_KEY_NULL = 0, RGFW_Escape, RGFW_F1, @@ -932,10 +952,12 @@ typedef struct { i32 x, y; } RGFW_vector; RGFW_KP_9, RGFW_KP_0, RGFW_KP_Period, - RGFW_KP_Return - } RGFW_Key; + RGFW_KP_Return, - typedef enum RGFW_mouseIcons { + final_key, + }; + + typedef RGFW_ENUM(u8, RGFW_mouseIcons) { RGFW_MOUSE_NORMAL = 0, RGFW_MOUSE_ARROW, RGFW_MOUSE_IBEAM, @@ -947,7 +969,7 @@ typedef struct { i32 x, y; } RGFW_vector; RGFW_MOUSE_RESIZE_NESW, RGFW_MOUSE_RESIZE_ALL, RGFW_MOUSE_NOT_ALLOWED, - } RGFW_mouseIcons; + }; #endif /* RGFW_HEADER */ @@ -970,7 +992,7 @@ typedef struct { i32 x, y; } RGFW_vector; for (;;) { RGFW_window_checkEvent(win); // NOTE: checking events outside of a while loop may cause input lag - if (win->event.type == RGFW_quit || RGFW_isPressedI(win, RGFW_Escape)) + if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape)) break; RGFW_window_swapBuffers(win); @@ -1024,11 +1046,14 @@ typedef struct { i32 x, y; } RGFW_vector; #include /* - +RGFW_IMPLEMENTATION starts with generic RGFW defines This is the start of keycode data - +Why not use macros instead of the numbers itself? +Windows -> Not all virtual keys are macros (VK_0 - VK_1, VK_a - VK_z) +Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size +MacOS -> windows and linux already don't have keycodes as macros, so there's no point */ u8 RGFW_keycodes[] = { @@ -1146,17 +1171,13 @@ This is the start of keycode data [RGFW_OS_BASED_VALUE(110, 0x24, 116)] = RGFW_Home, }; - #ifdef RGFW_X11 - u8 RGFW_mouseIconSrc[] = {68, 68, 152, 34, 60, 108, 116, 12, 14, 52, 0}; - #elif defined(RGFW_WINDOWS) - u32 RGFW_mouseIconSrc[] = {32512, 32512, 32513, 32515, 32649, 32644, 32645, 32642, 32643, 32646, 32648}; - #elif defined(RGFW_MACOS) - char* RGFW_mouseIconSrc[] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"}; - #endif - - u8 RGFW_keyboard[128] = { 0 }; - u8 RGFW_keyboard_prev[128]; + typedef struct { + b8 current : 1; + b8 prev : 1; + } RGFW_keyState; + RGFW_keyState RGFW_keyboard[final_key] = { {0, 0} }; + RGFWDEF u32 RGFW_apiKeyCodeToRGFW(u32 keycode); u32 RGFW_apiKeyCodeToRGFW(u32 keycode) { @@ -1166,415 +1187,81 @@ This is the start of keycode data return RGFW_keycodes[keycode]; } + RGFWDEF void RGFW_resetKey(void); + void RGFW_resetKey(void) { + size_t len = final_key; + + size_t i; + for (i = 0; i < len; i++) + RGFW_keyboard[i].prev = 0; + } + /* - -this is the end of keycode data - + this is the end of keycode data */ -#ifdef RGFW_WINDOWS -#include +/* + event callback defines start here +*/ -#endif -#ifdef RGFW_MACOS /* - based on silicon.h + These exist to avoid the + if (func == NULL) check + for (allegedly) better performance */ + void RGFW_windowmovefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } + void RGFW_windowresizefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); } + void RGFW_windowquitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); } + void RGFW_focusfuncEMPTY(RGFW_window* win, b8 inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);} + void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_vector point, b8 status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);} + void RGFW_mouseposfuncEMPTY(RGFW_window* win, RGFW_vector point) {RGFW_UNUSED(win); RGFW_UNUSED(point);} + void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_vector point) {RGFW_UNUSED(win); RGFW_UNUSED(point);} + void RGFW_windowrefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); } + void RGFW_keyfuncEMPTY(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(keycode); RGFW_UNUSED(keyName); RGFW_UNUSED(lockState); RGFW_UNUSED(pressed);} + void RGFW_mousebuttonfuncEMPTY(RGFW_window* win, u8 button, double scroll, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);} + void RGFW_jsButtonfuncEMPTY(RGFW_window* win, u16 joystick, u8 button, b8 pressed){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(button); RGFW_UNUSED(pressed); } + void RGFW_jsAxisfuncEMPTY(RGFW_window* win, u16 joystick, RGFW_vector axis[2], u8 axisesCount){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); } -#ifndef GL_SILENCE_DEPRECATION -#define GL_SILENCE_DEPRECATION -#endif + #ifdef RGFW_ALLOC_DROPFILES + void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);} + #else + void RGFW_dndfuncEMPTY(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);} + #endif -#include -#include -#include -#include + RGFW_windowmovefunc RGFW_windowMoveCallback = RGFW_windowmovefuncEMPTY; + RGFW_windowresizefunc RGFW_windowResizeCallback = RGFW_windowresizefuncEMPTY; + RGFW_windowquitfunc RGFW_windowQuitCallback = RGFW_windowquitfuncEMPTY; + RGFW_mouseposfunc RGFW_mousePosCallback = RGFW_mouseposfuncEMPTY; + RGFW_windowrefreshfunc RGFW_windowRefreshCallback = RGFW_windowrefreshfuncEMPTY; + RGFW_focusfunc RGFW_focusCallback = RGFW_focusfuncEMPTY; + RGFW_mouseNotifyfunc RGFW_mouseNotifyCallBack = RGFW_mouseNotifyfuncEMPTY; + RGFW_dndfunc RGFW_dndCallback = RGFW_dndfuncEMPTY; + RGFW_dndInitfunc RGFW_dndInitCallback = RGFW_dndInitfuncEMPTY; + RGFW_keyfunc RGFW_keyCallback = RGFW_keyfuncEMPTY; + RGFW_mousebuttonfunc RGFW_mouseButtonCallback = RGFW_mousebuttonfuncEMPTY; + RGFW_jsButtonfunc RGFW_jsButtonCallback = RGFW_jsButtonfuncEMPTY; + RGFW_jsAxisfunc RGFW_jsAxisCallback = RGFW_jsAxisfuncEMPTY; - typedef CGRect NSRect; - typedef CGPoint NSPoint; - typedef CGSize NSSize; - - typedef void NSBitmapImageRep; - typedef void NSCursor; - typedef void NSDraggingInfo; - typedef void NSWindow; - typedef void NSApplication; - typedef void NSScreen; - typedef void NSEvent; - typedef void NSString; - typedef void NSOpenGLContext; - typedef void NSPasteboard; - typedef void NSColor; - typedef void NSArray; - typedef void NSImageRep; - typedef void NSImage; - typedef void NSOpenGLView; - - - typedef const char* NSPasteboardType; - typedef unsigned long NSUInteger; - typedef long NSInteger; - typedef NSInteger NSModalResponse; - -#ifdef __arm64__ - /* ARM just uses objc_msgSend */ -#define abi_objc_msgSend_stret objc_msgSend -#define abi_objc_msgSend_fpret objc_msgSend -#else /* __i386__ */ - /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */ -#define abi_objc_msgSend_stret objc_msgSend_stret -#define abi_objc_msgSend_fpret objc_msgSend_fpret -#endif - -#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) -#define objc_msgSend_bool ((BOOL (*)(id, SEL))objc_msgSend) -#define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) -#define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) -#define objc_msgSend_uint ((NSUInteger (*)(id, SEL))objc_msgSend) -#define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) -#define objc_msgSend_void_SEL ((void (*)(id, SEL, SEL))objc_msgSend) -#define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) - -#define si_declare_single(class, name, func) \ - void class##_##name(class* obj) { \ - return objc_msgSend_void(obj, sel_registerName(func)); \ - } - - -#define loadFunc(funcName) \ - static void* func = NULL;\ - if (func == NULL) \ - func = sel_registerName(funcName); - - void NSRelease(id obj) { - loadFunc("release"); - objc_msgSend_void(obj, func); - } - -#define release NSRelease - - si_declare_single(NSApplication, finishLaunching, "finishLaunching") - si_declare_single(NSOpenGLContext, flushBuffer, "flushBuffer") - - NSString* NSString_stringWithUTF8String(const char* str) { - loadFunc("stringWithUTF8String:"); - - return ((id(*)(id, SEL, const char*))objc_msgSend) - ((id)objc_getClass("NSString"), func, str); - } - - const char* NSString_to_char(NSString* str) { - return ((const char* (*)(id, SEL)) objc_msgSend) (str, sel_registerName("UTF8String")); - } - - void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) { - Class selected_class; - - if (strcmp(class_name, "NSView") == 0) { - selected_class = objc_getClass("ViewClass"); - } else if (strcmp(class_name, "NSWindow") == 0) { - selected_class = objc_getClass("WindowClass"); - } else { - selected_class = objc_getClass(class_name); - } - - class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0); - } - - /* Header for the array. */ - typedef struct siArrayHeader { - size_t count; - /* TODO(EimaMei): Add a `type_width` later on. */ - } siArrayHeader; - - /* Gets the header of the siArray. */ -#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1) - - void* si_array_init_reserve(size_t sizeof_element, size_t count) { - siArrayHeader* ptr = malloc(sizeof(siArrayHeader) + (sizeof_element * count)); - void* array = ptr + sizeof(siArrayHeader); - - siArrayHeader* header = SI_ARRAY_HEADER(array); - header->count = count; - - return array; - } - -#define si_array_len(array) (SI_ARRAY_HEADER(array)->count) -#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", function) - /* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/ -#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", function) - - NSRect NSMakeRect(double x, double y, double width, double height) { - NSRect r; - r.origin.x = x; - r.origin.y = y; - r.size.width = width; - r.size.height = height; - - return r; - } - - NSPoint NSMakePoint(double x, double y) { - NSPoint point; - point.x = x; - point.y = y; - return point; - } - - NSSize NSMakeSize(double w, double h) { - NSSize size; - size.width = w; - size.height = h; - return size; - } - - void* si_array_init(void* allocator, size_t sizeof_element, size_t count) { - void* array = si_array_init_reserve(sizeof_element, count); - memcpy(array, allocator, sizeof_element * count); - - return array; - } + void RGFW_window_checkEvents(RGFW_window* win) { while (RGFW_window_checkEvent(win) != NULL && RGFW_window_shouldClose(win) == 0) { if (win->event.type == RGFW_quit) return; }} - unsigned char* NSBitmapImageRep_bitmapData(NSBitmapImageRep* imageRep) { - return ((unsigned char* (*)(id, SEL))objc_msgSend) - (imageRep, sel_registerName("bitmapData")); - } - -#define NS_ENUM(type, name) type name; enum - - typedef NS_ENUM(NSUInteger, NSBitmapFormat) { - NSBitmapFormatAlphaFirst = 1 << 0, // 0 means is alpha last (RGBA, CMYKA, etc.) - NSBitmapFormatAlphaNonpremultiplied = 1 << 1, // 0 means is premultiplied - NSBitmapFormatFloatingPointSamples = 1 << 2, // 0 is integer - - NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8), - NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9), - NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10), - NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11) - }; - - NSBitmapImageRep* NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) { - void* func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:"); - - return (NSBitmapImageRep*) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, const char*, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend) - (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits); - } - - NSColor* NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) { - void* nsclass = objc_getClass("NSColor"); - void* func = sel_registerName("colorWithSRGBRed:green:blue:alpha:"); - return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend) - (nsclass, func, red, green, blue, alpha); - } - - NSCursor* NSCursor_initWithImage(NSImage* newImage, NSPoint aPoint) { - void* func = sel_registerName("initWithImage:hotSpot:"); - void* nsclass = objc_getClass("NSCursor"); - - return (NSCursor*) ((id(*)(id, SEL, id, NSPoint))objc_msgSend) - (NSAlloc(nsclass), func, newImage, aPoint); - } - - void NSImage_addRepresentation(NSImage* image, NSImageRep* imageRep) { - void* func = sel_registerName("addRepresentation:"); - objc_msgSend_void_id(image, func, imageRep); - } - - NSImage* NSImage_initWithSize(NSSize size) { - void* func = sel_registerName("initWithSize:"); - return ((id(*)(id, SEL, NSSize))objc_msgSend) - (NSAlloc((id)objc_getClass("NSImage")), func, size); - } -#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers)) - typedef NS_ENUM(NSInteger, NSOpenGLContextParameter) { - NSOpenGLContextParameterSwapInterval NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ - NSOpenGLContextParameterSurfaceOrder NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ - NSOpenGLContextParameterSurfaceOpacity NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ - NSOpenGLContextParameterSurfaceBackingSize NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params. Width/height of surface backing size */ - NSOpenGLContextParameterReclaimResources NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params. */ - NSOpenGLContextParameterCurrentRendererID NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param. Retrieves the current renderer ID */ - NSOpenGLContextParameterGPUVertexProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param. Currently processing vertices with GPU (get) */ - NSOpenGLContextParameterGPUFragmentProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param. Currently processing fragments with GPU (get) */ - NSOpenGLContextParameterHasDrawable NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param. Boolean returned if drawable is attached */ - NSOpenGLContextParameterMPSwapsInFlight NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param. Max number of swaps queued by the MP GL engine */ - - NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */ - NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */ - NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */ - NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */ - NSOpenGLContextParameterSurfaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */ - }; - - - void NSOpenGLContext_setValues(NSOpenGLContext* context, const int* vals, NSOpenGLContextParameter param) { - void* func = sel_registerName("setValues:forParameter:"); - ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend) - (context, func, vals, param); - } - - void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) { - void* func = sel_registerName("initWithAttributes:"); - return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend) - (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), func, attribs); - } - - NSOpenGLView* NSOpenGLView_initWithFrame(NSRect frameRect, uint32_t* format) { - void* func = sel_registerName("initWithFrame:pixelFormat:"); - return (NSOpenGLView*) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend) - (NSAlloc((id)objc_getClass("NSOpenGLView")), func, frameRect, format); - } - - void NSCursor_performSelector(NSCursor* cursor, void* selector) { - void* func = sel_registerName("performSelector:"); - objc_msgSend_void_SEL(cursor, func, selector); - } - - NSPasteboard* NSPasteboard_generalPasteboard(void) { - return (NSPasteboard*) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard")); - } - - NSString** cstrToNSStringArray(char** strs, size_t len) { - static NSString* nstrs[6]; - size_t i; - for (i = 0; i < len; i++) - nstrs[i] = NSString_stringWithUTF8String(strs[i]); - - return nstrs; - } - - const char* NSPasteboard_stringForType(NSPasteboard* pasteboard, NSPasteboardType dataType) { - void* func = sel_registerName("stringForType:"); - return (const char*) NSString_to_char(((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, func, NSString_stringWithUTF8String(dataType))); - } - - NSArray* c_array_to_NSArray(void* array, size_t len) { - SEL func = sel_registerName("initWithObjects:count:"); - void* nsclass = objc_getClass("NSArray"); - return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) - (NSAlloc(nsclass), func, array, len); - } - - void NSregisterForDraggedTypes(void* view, NSPasteboardType* newTypes, size_t len) { - NSString** ntypes = cstrToNSStringArray((char**)newTypes, len); - - NSArray* array = c_array_to_NSArray(ntypes, len); - objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array); - NSRelease(array); - } - - NSInteger NSPasteBoard_declareTypes(NSPasteboard* pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) { - NSString** ntypes = cstrToNSStringArray((char**)newTypes, len); - - void* func = sel_registerName("declareTypes:owner:"); - - NSArray* array = c_array_to_NSArray(ntypes, len); - - NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend) - (pasteboard, func, array, owner); - NSRelease(array); - - return output; - } - - bool NSPasteBoard_setString(NSPasteboard* pasteboard, const char* stringToWrite, NSPasteboardType dataType) { - void* func = sel_registerName("setString:forType:"); - return ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend) - (pasteboard, func, NSString_stringWithUTF8String(stringToWrite), NSString_stringWithUTF8String(dataType)); - } - - void NSRetain(id obj) { objc_msgSend_void(obj, sel_registerName("retain")); } - - typedef enum NSApplicationActivationPolicy { - NSApplicationActivationPolicyRegular, - NSApplicationActivationPolicyAccessory, - NSApplicationActivationPolicyProhibited - } NSApplicationActivationPolicy; - - typedef NS_ENUM(u32, NSBackingStoreType) { - NSBackingStoreRetained = 0, - NSBackingStoreNonretained = 1, - NSBackingStoreBuffered = 2 - }; - - typedef NS_ENUM(u32, NSWindowStyleMask) { - NSWindowStyleMaskBorderless = 0, - NSWindowStyleMaskTitled = 1 << 0, - NSWindowStyleMaskClosable = 1 << 1, - NSWindowStyleMaskMiniaturizable = 1 << 2, - NSWindowStyleMaskResizable = 1 << 3, - NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */ - NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12, - NSWindowStyleMaskFullScreen = 1 << 14, - NSWindowStyleMaskFullSizeContentView = 1 << 15, - NSWindowStyleMaskUtilityWindow = 1 << 4, - NSWindowStyleMaskDocModalWindow = 1 << 6, - NSWindowStyleMaskNonactivatingPanel = 1 << 7, - NSWindowStyleMaskHUDWindow = 1 << 13 - }; - - typedef const char* NSPasteboardType; - NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPboardType - - - - typedef NS_ENUM(i32, NSDragOperation) { - NSDragOperationNone = 0, - NSDragOperationCopy = 1, - NSDragOperationLink = 2, - NSDragOperationGeneric = 4, - NSDragOperationPrivate = 8, - NSDragOperationMove = 16, - NSDragOperationDelete = 32, - NSDragOperationEvery = ULONG_MAX, - - //NSDragOperationAll_Obsolete API_DEPRECATED("", macos(10.0,10.10)) = 15, // Use NSDragOperationEvery - //NSDragOperationAll API_DEPRECATED("", macos(10.0,10.10)) = NSDragOperationAll_Obsolete, // Use NSDragOperationEvery - }; - - - NSUInteger NSArray_count(NSArray* array) { - void* func = sel_registerName("count"); - return ((NSUInteger(*)(id, SEL))objc_msgSend)(array, func); - } - - void* NSArray_objectAtIndex(NSArray* array, NSUInteger index) { - void* func = sel_registerName("objectAtIndex:"); - return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index); - } - - const char** NSPasteboard_readObjectsForClasses(NSPasteboard* pasteboard, Class* classArray, size_t len, void* options) { - void* func = sel_registerName("readObjectsForClasses:options:"); - - NSArray* array = c_array_to_NSArray(classArray, len); - - NSArray* output = (NSArray*) ((id(*)(id, SEL, id, void*))objc_msgSend) - (pasteboard, func, array, options); - - NSRelease(array); - NSUInteger count = NSArray_count(output); - - const char** res = si_array_init_reserve(sizeof(const char*), count); - - void* path_func = sel_registerName("path"); - - for (NSUInteger i = 0; i < count; i++) { - void* url = NSArray_objectAtIndex(output, i); - NSString* url_str = ((id(*)(id, SEL))objc_msgSend)(url, path_func); - res[i] = NSString_to_char(url_str); - } - - return res; - } - - void* NSWindow_contentView(NSWindow* window) { - void* func = sel_registerName("contentView"); - return objc_msgSend_id(window, func); - } -#endif - + void RGFW_setWindowMoveCallback(RGFW_windowmovefunc func) { RGFW_windowMoveCallback = func; } + void RGFW_setWindowResizeCallback(RGFW_windowresizefunc func) { RGFW_windowResizeCallback = func; } + void RGFW_setWindowQuitCallback(RGFW_windowquitfunc func) { RGFW_windowQuitCallback = func; } + void RGFW_setMousePosCallback(RGFW_mouseposfunc func) { RGFW_mousePosCallback = func; } + void RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func) { RGFW_windowRefreshCallback = func; } + void RGFW_setFocusCallback(RGFW_focusfunc func) { RGFW_focusCallback = func; } + void RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func) { RGFW_mouseNotifyCallBack = func; } + void RGFW_setDndCallback(RGFW_dndfunc func) { RGFW_dndCallback = func; } + void RGFW_setDndInitCallback(RGFW_dndInitfunc func) { RGFW_dndInitCallback = func; } + void RGFW_setKeyCallback(RGFW_keyfunc func) { RGFW_keyCallback = func; } + void RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func) { RGFW_mouseButtonCallback = func; } + void RGFW_setjsButtonCallback(RGFW_jsButtonfunc func) { RGFW_jsButtonCallback = func; } + void RGFW_setjsAxisCallback(RGFW_jsAxisfunc func) { RGFW_jsAxisCallback = func; } +/* + no more event call back defines +*/ #define RGFW_ASSERT(check, str) {\ if (!(check)) { \ @@ -1583,44 +1270,26 @@ this is the end of keycode data } \ } - u8 RGFW_error = 0; - u8 RGFW_Error() { return RGFW_error; } + b8 RGFW_error = 0; + b8 RGFW_Error() { return RGFW_error; } #define SET_ATTRIB(a, v) { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ attribs[index++] = v; \ } - -#define ADD_ATTRIB(a) { \ - assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ - attribs[index++] = a; \ -} - -#if defined(RGFW_X11) || defined(RGFW_WINDOWS) - void RGFW_window_showMouse(RGFW_window* win, i8 show) { - static u8 RGFW_blk[] = { 0, 0, 0, 0 }; - if (show == 0) - RGFW_window_setMouse(win, RGFW_blk, RGFW_AREA(1, 1), 4); - else - RGFW_window_setMouseDefault(win); + + RGFW_area RGFW_bufferSize = {0, 0}; + void RGFW_setBufferSize(RGFW_area size) { + RGFW_bufferSize = size; } -#endif - #ifdef RGFW_WINDOWS - __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod); - #endif RGFWDEF RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args); - RGFWDEF void RGFW_init_buffer(RGFW_window* win); RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) { RGFW_window* win = (RGFW_window*) RGFW_MALLOC(sizeof(RGFW_window)); /* make a new RGFW struct */ - #ifdef RGFW_WINDOWS - timeBeginPeriod(1); - #endif - #ifdef RGFW_ALLOC_DROPFILES win->event.droppedFiles = (char**) RGFW_MALLOC(sizeof(char*) * RGFW_MAX_DROPS); u32 i; @@ -1628,13 +1297,6 @@ this is the end of keycode data win->event.droppedFiles[i] = (char*) RGFW_CALLOC(RGFW_MAX_PATH, sizeof(char)); #endif -#ifdef RGFW_X11 - /* open X11 display */ - /* this is done here so the screen size can be accessed */ - win->src.display = XOpenDisplay(NULL); - assert(win->src.display != NULL); -#endif - #ifndef RGFW_X11 RGFW_area screenR = RGFW_getScreenSize(); #else @@ -1657,662 +1319,236 @@ this is the end of keycode data win->event.inFocus = 1; win->event.droppedFilesCount = 0; win->src.joystickCount = 0; -#ifdef RGFW_MACOS - RGFW_window_setMouseDefault(win); -#endif -#ifdef RGFW_WINDOWS - win->src.maxSize = RGFW_AREA(0, 0); - win->src.minSize = RGFW_AREA(0, 0); -#endif win->src.winArgs = 0; + win->event.lockState = 0; return win; } + #ifndef RGFW_NO_MONITOR void RGFW_window_scaleToMonitor(RGFW_window* win) { RGFW_monitor monitor = RGFW_window_getMonitor(win); RGFW_window_resize(win, RGFW_AREA(((u32) monitor.scaleX) * win->r.w, ((u32) monitor.scaleX) * win->r.h)); } + #endif - void RGFW_init_buffer(RGFW_window* win) { -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - RGFW_area area = RGFW_getScreenSize(); -#if !(defined(RGFW_WINDOWS)) || defined(RGFW_OSMESA) - win->buffer = RGFW_MALLOC(area.w * area.h * 4); +RGFW_window* RGFW_root = NULL; + + +#define RGFW_HOLD_MOUSE (1L<<2) /*!< hold the moues still */ +#define RGFW_MOUSE_LEFT (1L<<3) /* if mouse left the window */ + + void RGFW_clipboardFree(char* str) { RGFW_FREE(str); } + + b8 RGFW_mouseButtons[5] = { 0 }; + b8 RGFW_mouseButtons_prev[5]; + + b8 RGFW_isMousePressed(RGFW_window* win, u8 button) { + assert(win != NULL); + return RGFW_mouseButtons[button] && (win != NULL) && win->event.inFocus; + } + b8 RGFW_wasMousePressed(RGFW_window* win, u8 button) { + assert(win != NULL); + return RGFW_mouseButtons_prev[button] && (win != NULL) && win->event.inFocus; + } + b8 RGFW_isMouseHeld(RGFW_window* win, u8 button) { + return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); + } + b8 RGFW_isMouseReleased(RGFW_window* win, u8 button) { + return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); + } + + b8 RGFW_isPressed(RGFW_window* win, u8 key) { + assert(win != NULL); + return RGFW_keyboard[key].current && win->event.inFocus; + } + + b8 RGFW_wasPressed(RGFW_window* win, u8 key) { + assert(win != NULL); + return RGFW_keyboard[key].prev && win->event.inFocus; + } + + b8 RGFW_isHeld(RGFW_window* win, u8 key) { + return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); + } + + b8 RGFW_isClicked(RGFW_window* win, u8 key) { + return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key)); + } + + b8 RGFW_isReleased(RGFW_window* win, u8 key) { + return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key)); + } + + void RGFW_window_makeCurrent(RGFW_window* win) { + assert(win != NULL); + +#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) + RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL); #endif -#ifdef RGFW_OSMESA - win->src.rSurf = OSMesaCreateContext(OSMESA_RGBA, NULL); - OSMesaMakeCurrent(win->src.rSurf, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); -#endif -#ifdef RGFW_X11 - win->src.bitmap = XCreateImage( - win->src.display, DefaultVisual(win->src.display, XDefaultScreen(win->src.display)), - DefaultDepth(win->src.display, XDefaultScreen(win->src.display)), - ZPixmap, 0, NULL, area.w, area.h, - 32, 0 - ); -#endif -#ifdef RGFW_WINDOWS - BITMAPV5HEADER bi = { 0 }; - ZeroMemory(&bi, sizeof(bi)); - bi.bV5Size = sizeof(bi); - bi.bV5Width = area.w; - bi.bV5Height = -((LONG) area.h); - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5BlueMask = 0x00ff0000; - bi.bV5GreenMask = 0x0000ff00; - bi.bV5RedMask = 0x000000ff; - bi.bV5AlphaMask = 0xff000000; - - win->src.bitmap = CreateDIBSection(win->src.hdc, - (BITMAPINFO*) &bi, - DIB_RGB_COLORS, - (void**) &win->buffer, - NULL, - (DWORD) 0); - - win->src.hdcMem = CreateCompatibleDC(win->src.hdc); -#endif -#else -RGFW_UNUSED(win); /* if buffer rendering is not being used */ +#ifdef RGFW_OPENGL + RGFW_window_makeCurrent_OpenGL(win); #endif } + void RGFW_window_setGPURender(RGFW_window* win, i8 set) { + if (!set && !(win->src.winArgs & RGFW_NO_GPU_RENDER)) + win->src.winArgs |= RGFW_NO_GPU_RENDER; + + else if (set && win->src.winArgs & RGFW_NO_GPU_RENDER) + win->src.winArgs ^= RGFW_NO_GPU_RENDER; + } + + void RGFW_window_setCPURender(RGFW_window* win, i8 set) { + if (!set && !(win->src.winArgs & RGFW_NO_CPU_RENDER)) + win->src.winArgs |= RGFW_NO_CPU_RENDER; + + else if (set && win->src.winArgs & RGFW_NO_CPU_RENDER) + win->src.winArgs ^= RGFW_NO_CPU_RENDER; + } + + void RGFW_window_maximize(RGFW_window* win) { + assert(win != NULL); + + RGFW_area screen = RGFW_getScreenSize(); + + RGFW_window_move(win, RGFW_VECTOR(0, 0)); + RGFW_window_resize(win, screen); + } + + b8 RGFW_window_shouldClose(RGFW_window* win) { + assert(win != NULL); + return (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape)); + } + + void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; RGFW_windowQuitCallback(win); } + + #ifndef RGFW_NO_MONITOR + void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) { + RGFW_window_move(win, RGFW_VECTOR(m.rect.x + win->r.x, m.rect.y + win->r.y)); + } + #endif + + RGFWDEF void RGFW_clipCursor(RGFW_rect); + + #if !defined(RGFW_WINDOWS) && !defined(RGFW_MACOS) + void RGFW_clipCursor(RGFW_rect r) { RGFW_UNUSED(r) } + #endif + + void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) { + if (!(win->src.winArgs & RGFW_HOLD_MOUSE)) { + RGFW_clipCursor(win->r); + win->src.winArgs |= RGFW_HOLD_MOUSE; + } + + if (!area.w && !area.h) + area = RGFW_AREA(win->r.w / 2, win->r.h / 2); + + #ifndef RGFW_MACOS + RGFW_window_moveMouse(win, RGFW_VECTOR(win->r.x + (area.w), win->r.y + (area.h))); + #endif + } + + void RGFW_window_mouseUnhold(RGFW_window* win) { + if ((win->src.winArgs & RGFW_HOLD_MOUSE)) { + win->src.winArgs ^= RGFW_HOLD_MOUSE; + + RGFW_clipCursor(RGFW_RECT(0, 0, 0, 0)); + } + } + + void RGFW_window_checkFPS(RGFW_window* win) { + u64 deltaTime = RGFW_getTimeNS() - win->event.frameTime; + + u64 fps = round(1e+9 / deltaTime); + win->event.fps = fps; + + if (win->fpsCap && fps > win->fpsCap) { + u64 frameTimeNS = 1e+9 / win->fpsCap; + u64 sleepTimeMS = (frameTimeNS - deltaTime) / 1e6; + + if (sleepTimeMS > 0) { + RGFW_sleep(sleepTimeMS); + win->event.frameTime = 0; + } + } + + win->event.frameTime = RGFW_getTimeNS(); + + if (win->fpsCap == 0) + return; + + deltaTime = RGFW_getTimeNS() - win->event.frameTime2; + win->event.fps = round(1e+9 / deltaTime); + win->event.frameTime2 = RGFW_getTimeNS(); + } + + u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { return win->src.jsPressed[c][button]; } + + #if defined(RGFW_X11) || defined(RGFW_WINDOWS) + void RGFW_window_showMouse(RGFW_window* win, i8 show) { + static u8 RGFW_blk[] = { 0, 0, 0, 0 }; + if (show == 0) + RGFW_window_setMouse(win, RGFW_blk, RGFW_AREA(1, 1), 4); + else + RGFW_window_setMouseDefault(win); + } + #endif + + RGFWDEF void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock); + void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) { + if (capital && !(win->event.lockState & RGFW_CAPSLOCK)) + win->event.lockState |= RGFW_CAPSLOCK; + else if (!capital && (win->event.lockState & RGFW_CAPSLOCK)) + win->event.lockState ^= RGFW_CAPSLOCK; + + if (numlock && !(win->event.lockState & RGFW_NUMLOCK)) + win->event.lockState |= RGFW_NUMLOCK; + else if (!numlock && (win->event.lockState & RGFW_NUMLOCK)) + win->event.lockState ^= RGFW_NUMLOCK; + } + + #if defined(RGFW_X11) || defined(RGFW_MACOS) + struct timespec; + + int nanosleep(const struct timespec* duration, struct timespec* rem); + int clock_gettime(clockid_t clk_id, struct timespec* tp); + int setenv(const char *name, const char *value, int overwrite); + + void RGFW_window_setDND(RGFW_window* win, b8 allow) { + if (allow && !(win->src.winArgs & RGFW_ALLOW_DND)) + win->src.winArgs |= RGFW_ALLOW_DND; + + else if (!allow && (win->src.winArgs & RGFW_ALLOW_DND)) + win->src.winArgs ^= RGFW_ALLOW_DND; + } + #endif + +/* + graphics API spcific code (end of generic code) + starts here +*/ + + +/* + OpenGL defines start here (Normal, EGL, OSMesa) +*/ + #if defined(RGFW_OPENGL) || defined(RGFW_EGL) || defined(RGFW_OSMESA) #ifndef __APPLE__ #include #else +#ifndef GL_SILENCE_DEPRECATION +#define GL_SILENCE_DEPRECATION +#endif #include -#endif +#include #endif -#ifdef RGFW_VULKAN - RGFW_vulkanInfo RGFW_vulkan_info; - - RGFW_vulkanInfo* RGFW_initVulkan(RGFW_window* win) { - assert(win != NULL); - - if ( - RGFW_initData(win) || - RGFW_deviceInitialization(win) || - RGFW_createSwapchain(win) - ) - return NULL; - - u32 graphics_family_index = 0; - u32 present_family_index = 0; - - vkGetDeviceQueue(RGFW_vulkan_info.device, graphics_family_index, 0, &RGFW_vulkan_info.graphics_queue); - vkGetDeviceQueue(RGFW_vulkan_info.device, present_family_index, 0, &RGFW_vulkan_info.present_queue); - - if ( - RGFW_createRenderPass() || - RGFW_createFramebuffers(win) || - RGFW_createCommandPool() || - RGFW_createCommandBuffers(win) || - RGFW_createSyncObjects(win) - ) - return NULL; - - return &RGFW_vulkan_info; - } - - int RGFW_initData(RGFW_window* win) { - assert(win != NULL); - - win->src.swapchain = VK_NULL_HANDLE; - win->src.image_count = 0; - RGFW_vulkan_info.current_frame = 0; - - return 0; - } - - void RGFW_createSurface(VkInstance instance, RGFW_window* win) { - assert(win != NULL); - assert(instance); - - win->src.rSurf = VK_NULL_HANDLE; - -#ifdef RGFW_X11 - VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) win->src.display, (Window) win->src.window }; - - vkCreateXlibSurfaceKHR(RGFW_vulkan_info.instance, &x11, NULL, &win->src.rSurf); -#endif -#ifdef RGFW_WINDOWS - VkWin32SurfaceCreateInfoKHR win32 = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 0, 0, GetModuleHandle(NULL), win->src.window }; - - vkCreateWin32SurfaceKHR(RGFW_vulkan_info.instance, &win32, NULL, &win->src.rSurf); -#endif -#if defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) - VkMacOSSurfaceCreateFlagsMVK macos = { VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, 0, 0, win->src.display, win->src.window }; - - vkCreateMacOSSurfaceMVK(RGFW_vulkan_info.instance, &macos, NULL, &win->src.rSurf); -#endif - } - - RGFW_vulkanInfo* RGFW_getVulkanInfo(void) { - return &RGFW_vulkan_info; - } - - int RGFW_deviceInitialization(RGFW_window* win) { - assert(win != NULL); - - VkApplicationInfo appInfo = { 0 }; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = "RGFW app"; - appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); - - char* extension = -#ifdef RGFW_WINDOWS - "VK_KHR_win32_surface"; -#elif defined(RGFW_X11) - VK_KHR_XLIB_SURFACE_EXTENSION_NAME; -#elif defined(RGFW_MACOS) - "VK_MVK_macos_surface"; -#else - NULL; -#endif - - VkInstanceCreateInfo instance_create_info = { 0 }; - instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instance_create_info.pApplicationInfo = &appInfo; - instance_create_info.enabledExtensionCount = extension ? 2 : 0, - instance_create_info.ppEnabledExtensionNames = (const char* [2]){ - VK_KHR_SURFACE_EXTENSION_NAME, - extension - }; - - if (vkCreateInstance(&instance_create_info, NULL, &RGFW_vulkan_info.instance) != VK_SUCCESS) { - fprintf(stderr, "failed to create instance!\n"); - return -1; - } - - - RGFW_createSurface(RGFW_vulkan_info.instance, win); - - u32 deviceCount = 0; - vkEnumeratePhysicalDevices(RGFW_vulkan_info.instance, &deviceCount, NULL); - VkPhysicalDevice* devices = (VkPhysicalDevice*) RGFW_MALLOC(sizeof(VkPhysicalDevice) * deviceCount); - vkEnumeratePhysicalDevices(RGFW_vulkan_info.instance, &deviceCount, devices); - - RGFW_vulkan_info.physical_device = devices[0]; - - u32 queue_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(RGFW_vulkan_info.physical_device, &queue_family_count, NULL); - VkQueueFamilyProperties* queueFamilies = (VkQueueFamilyProperties*) RGFW_MALLOC(sizeof(VkQueueFamilyProperties) * queue_family_count); - vkGetPhysicalDeviceQueueFamilyProperties(RGFW_vulkan_info.physical_device, &queue_family_count, queueFamilies); - - float queuePriority = 1.0f; - - VkPhysicalDeviceFeatures device_features = { 0 }; - - VkDeviceCreateInfo device_create_info = { 0 }; - device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - VkDeviceQueueCreateInfo queue_create_infos[2] = { - {0}, - {0}, - }; - queue_create_infos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_create_infos[0].queueCount = 1; - queue_create_infos[0].pQueuePriorities = &queuePriority; - queue_create_infos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_create_infos[1].queueCount = 1; - queue_create_infos[1].pQueuePriorities = &queuePriority; - device_create_info.queueCreateInfoCount = 2; - device_create_info.pQueueCreateInfos = queue_create_infos; - - device_create_info.enabledExtensionCount = 1; - - const char* device_extensions[] = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME - }; - - device_create_info.ppEnabledExtensionNames = device_extensions; - device_create_info.pEnabledFeatures = &device_features; - - if (vkCreateDevice(RGFW_vulkan_info.physical_device, &device_create_info, NULL, &RGFW_vulkan_info.device) != VK_SUCCESS) { - fprintf(stderr, "failed to create logical device!\n"); - return -1; - } - - return 0; - } - - int RGFW_createSwapchain(RGFW_window* win) { - assert(win != NULL); - - VkSurfaceFormatKHR surfaceFormat = { VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; - VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; - - VkSurfaceCapabilitiesKHR capabilities = { 0 }; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(RGFW_vulkan_info.physical_device, win->src.rSurf, &capabilities); - - win->src.image_count = capabilities.minImageCount + 1; - if (capabilities.maxImageCount > 0 && win->src.image_count > capabilities.maxImageCount) { - win->src.image_count = capabilities.maxImageCount; - } - - VkSwapchainCreateInfoKHR swapchain_create_info = { 0 }; - swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swapchain_create_info.surface = win->src.rSurf; - swapchain_create_info.minImageCount = win->src.image_count; - swapchain_create_info.imageFormat = surfaceFormat.format; - swapchain_create_info.imageColorSpace = surfaceFormat.colorSpace; - swapchain_create_info.imageExtent = (VkExtent2D){ win->r.w, win->r.h }; - swapchain_create_info.imageArrayLayers = 1; - swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swapchain_create_info.queueFamilyIndexCount = 2; - swapchain_create_info.preTransform = capabilities.currentTransform; - swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swapchain_create_info.presentMode = presentMode; - swapchain_create_info.clipped = VK_TRUE; - swapchain_create_info.oldSwapchain = VK_NULL_HANDLE; - - if (vkCreateSwapchainKHR(RGFW_vulkan_info.device, &swapchain_create_info, NULL, &win->src.swapchain) != VK_SUCCESS) { - fprintf(stderr, "failed to create swap chain!\n"); - return -1; - } - - u32 imageCount; - vkGetSwapchainImagesKHR(RGFW_vulkan_info.device, win->src.swapchain, &imageCount, NULL); - win->src.swapchain_images = (VkImage*) RGFW_MALLOC(sizeof(VkImage) * imageCount); - vkGetSwapchainImagesKHR(RGFW_vulkan_info.device, win->src.swapchain, &imageCount, win->src.swapchain_images); - - win->src.swapchain_image_views = (VkImageView*) RGFW_MALLOC(sizeof(VkImageView) * imageCount); - for (u32 i = 0; i < imageCount; i++) { - VkImageViewCreateInfo image_view_cre_infos = { 0 }; - image_view_cre_infos.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - image_view_cre_infos.image = win->src.swapchain_images[i]; - image_view_cre_infos.viewType = VK_IMAGE_VIEW_TYPE_2D; - image_view_cre_infos.format = VK_FORMAT_B8G8R8A8_SRGB; - image_view_cre_infos.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - image_view_cre_infos.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - image_view_cre_infos.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - image_view_cre_infos.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - image_view_cre_infos.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - image_view_cre_infos.subresourceRange.baseMipLevel = 0; - image_view_cre_infos.subresourceRange.levelCount = 1; - image_view_cre_infos.subresourceRange.baseArrayLayer = 0; - image_view_cre_infos.subresourceRange.layerCount = 1; - if (vkCreateImageView(RGFW_vulkan_info.device, &image_view_cre_infos, NULL, &win->src.swapchain_image_views[i]) != VK_SUCCESS) { - fprintf(stderr, "failed to create image views!"); - return -1; - } - } - - return 0; - } - - int RGFW_createRenderPass(void) { - VkAttachmentDescription color_attachment = { 0 }; - color_attachment.format = VK_FORMAT_B8G8R8A8_SRGB; - color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; - color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference color_attachment_ref = { 0 }; - color_attachment_ref.attachment = 0; - color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass = { 0 }; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_attachment_ref; - - VkSubpassDependency dependency = { 0 }; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - VkRenderPassCreateInfo render_pass_info = { 0 }; - render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - render_pass_info.attachmentCount = 1; - render_pass_info.pAttachments = &color_attachment; - render_pass_info.subpassCount = 1; - render_pass_info.pSubpasses = &subpass; - render_pass_info.dependencyCount = 1; - render_pass_info.pDependencies = &dependency; - - if (vkCreateRenderPass(RGFW_vulkan_info.device, &render_pass_info, NULL, &RGFW_vulkan_info.render_pass) != VK_SUCCESS) { - fprintf(stderr, "failed to create render pass\n"); - return -1; // failed to create render pass! - } - return 0; - } - - int RGFW_createCommandPool(void) { - VkCommandPoolCreateInfo pool_info = { 0 }; - pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - pool_info.queueFamilyIndex = 0; - - if (vkCreateCommandPool(RGFW_vulkan_info.device, &pool_info, NULL, &RGFW_vulkan_info.command_pool) != VK_SUCCESS) { - fprintf(stderr, "failed to create command pool\n"); - return -1; // failed to create command pool - } - return 0; - } - - int RGFW_createCommandBuffers(RGFW_window* win) { - assert(win != NULL); - - RGFW_vulkan_info.command_buffers = (VkCommandBuffer*) RGFW_MALLOC(sizeof(VkCommandBuffer) * win->src.image_count); - - VkCommandBufferAllocateInfo allocInfo = { 0 }; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = RGFW_vulkan_info.command_pool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = (u32) win->src.image_count; - - if (vkAllocateCommandBuffers(RGFW_vulkan_info.device, &allocInfo, RGFW_vulkan_info.command_buffers) != VK_SUCCESS) { - return -1; // failed to allocate command buffers; - } - - return 0; - } - - int RGFW_createSyncObjects(RGFW_window* win) { - assert(win != NULL); - - RGFW_vulkan_info.available_semaphores = (VkSemaphore*) RGFW_MALLOC(sizeof(VkSemaphore) * RGFW_MAX_FRAMES_IN_FLIGHT); - RGFW_vulkan_info.finished_semaphore = (VkSemaphore*) RGFW_MALLOC(sizeof(VkSemaphore) * RGFW_MAX_FRAMES_IN_FLIGHT); - RGFW_vulkan_info.in_flight_fences = (VkFence*) RGFW_MALLOC(sizeof(VkFence) * RGFW_MAX_FRAMES_IN_FLIGHT); - RGFW_vulkan_info.image_in_flight = (VkFence*) RGFW_MALLOC(sizeof(VkFence) * win->src.image_count); - - VkSemaphoreCreateInfo semaphore_info = { 0 }; - semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VkFenceCreateInfo fence_info = { 0 }; - fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - for (size_t i = 0; i < RGFW_MAX_FRAMES_IN_FLIGHT; i++) { - if (vkCreateSemaphore(RGFW_vulkan_info.device, &semaphore_info, NULL, &RGFW_vulkan_info.available_semaphores[i]) != VK_SUCCESS || - vkCreateSemaphore(RGFW_vulkan_info.device, &semaphore_info, NULL, &RGFW_vulkan_info.finished_semaphore[i]) != VK_SUCCESS || - vkCreateFence(RGFW_vulkan_info.device, &fence_info, NULL, &RGFW_vulkan_info.in_flight_fences[i]) != VK_SUCCESS) { - fprintf(stderr, "failed to create sync objects\n"); - return -1; // failed to create synchronization objects for a frame - } - } - - for (size_t i = 0; i < win->src.image_count; i++) { - RGFW_vulkan_info.image_in_flight[i] = VK_NULL_HANDLE; - } - - return 0; - } - - int RGFW_createFramebuffers(RGFW_window* win) { - assert(win != NULL); - - RGFW_vulkan_info.framebuffers = (VkFramebuffer*) RGFW_MALLOC(sizeof(VkFramebuffer) * win->src.image_count); - - for (size_t i = 0; i < win->src.image_count; i++) { - VkImageView attachments[] = { win->src.swapchain_image_views[i] }; - - VkFramebufferCreateInfo framebuffer_info = { 0 }; - framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebuffer_info.renderPass = RGFW_vulkan_info.render_pass; - framebuffer_info.attachmentCount = 1; - framebuffer_info.pAttachments = attachments; - framebuffer_info.width = win->r.w; - framebuffer_info.height = win->r.h; - framebuffer_info.layers = 1; - - if (vkCreateFramebuffer(RGFW_vulkan_info.device, &framebuffer_info, NULL, &RGFW_vulkan_info.framebuffers[i]) != VK_SUCCESS) { - return -1; // failed to create framebuffer - } - } - return 0; - } - - void RGFW_freeVulkan(void) { - vkDeviceWaitIdle(RGFW_vulkan_info.device); - - for (size_t i = 0; i < RGFW_MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(RGFW_vulkan_info.device, RGFW_vulkan_info.finished_semaphore[i], NULL); - vkDestroySemaphore(RGFW_vulkan_info.device, RGFW_vulkan_info.available_semaphores[i], NULL); - vkDestroyFence(RGFW_vulkan_info.device, RGFW_vulkan_info.in_flight_fences[i], NULL); - } - - vkDestroyCommandPool(RGFW_vulkan_info.device, RGFW_vulkan_info.command_pool, NULL); - - vkDestroyPipeline(RGFW_vulkan_info.device, RGFW_vulkan_info.graphics_pipeline, NULL); - vkDestroyPipelineLayout(RGFW_vulkan_info.device, RGFW_vulkan_info.pipeline_layout, NULL); - vkDestroyRenderPass(RGFW_vulkan_info.device, RGFW_vulkan_info.render_pass, NULL); - -#ifdef RGFW_DEBUG - PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(RGFW_vulkan_info.instance, "vkDestroyDebugUtilsMessengerEXT"); - if (func != NULL) { - func(RGFW_vulkan_info.instance, RGFW_vulkan_info.debugMessenger, NULL); - } -#endif - - vkDestroyDevice(RGFW_vulkan_info.device, NULL); - vkDestroyInstance(RGFW_vulkan_info.instance, NULL); - - RGFW_FREE(RGFW_vulkan_info.framebuffers); - RGFW_FREE(RGFW_vulkan_info.command_buffers); - RGFW_FREE(RGFW_vulkan_info.available_semaphores); - RGFW_FREE(RGFW_vulkan_info.finished_semaphore); - RGFW_FREE(RGFW_vulkan_info.in_flight_fences); - RGFW_FREE(RGFW_vulkan_info.image_in_flight); - } - -#endif /* RGFW_VULKAN */ - - RGFW_window* RGFW_root = NULL; - -#ifdef RGFW_X11 -#include -#ifndef RGFW_NO_X11_CURSOR -#include -#endif -#include - -#ifndef RGFW_NO_DPI -#include -#include -#endif -#endif - -#define RGFW_HOLD_MOUSE (1L<<2) /*!< hold the moues still */ - -#ifdef RGFW_WINDOWS -#include -#include -#include -#include -#include -#include -#endif - - u8 RGFW_mouseButtons[5] = { 0 }; - u8 RGFW_mouseButtons_prev[5]; - - u8 RGFW_isMousePressed(RGFW_window* win, u8 button) { - if (win != NULL && !win->event.inFocus) - return 0; - - return RGFW_mouseButtons[button]; - } - u8 RGFW_wasMousePressed(RGFW_window* win, u8 button) { - if (win != NULL && !win->event.inFocus) - return 0; - - return RGFW_mouseButtons_prev[button]; - } - u8 RGFW_isMouseHeld(RGFW_window* win, u8 button) { - return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); - } - u8 RGFW_isMouseReleased(RGFW_window* win, u8 button) { - return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button)); - } - - u8 RGFW_isPressedI(RGFW_window* win, u32 key) { - RGFW_UNUSED(win); - - return RGFW_keyboard[key]; - } - - u8 RGFW_wasPressedI(RGFW_window* win, u32 key) { - RGFW_UNUSED(win); - - return RGFW_keyboard_prev[key]; - } - - u8 RGFW_isHeldI(RGFW_window* win, u32 key) { - return (RGFW_isPressedI(win, key) && RGFW_wasPressedI(win, key)); - } - - u8 RGFW_isReleasedI(RGFW_window* win, u32 key) { - return (!RGFW_isPressedI(win, key) && RGFW_wasPressedI(win, key)); - } - - char* RGFW_keyCodeTokeyStr(u64 key) { - static char* keyStrs[128] = {"Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backtick", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-", "=", "BackSpace", "Tab", "CapsLock", "ShiftL", "ControlL", "AltL", "SuperL", "ShiftR", "ControlR", "AltR", "SuperR", " ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", ".", ",", "-", "[", "]", ";", "Return", "'", "\\", "Up", "Down", "Left", "Right", "Delete", "Insert", "End", "Home", "PageUp", "PageDown", "Numlock", "KP_Slash", "Multiply", "KP_Minus", "KP_1", "KP_2", "KP_3", "KP_4", "KP_5", "KP_6", "KP_7", "KP_8", "KP_9", "KP_0", "KP_Period", "KP_Return" }; - - return keyStrs[key]; - } - - u32 RGFW_keyStrToKeyCode(char* key) { - static char* keyStrs[128] = {"Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backtick", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-", "=", "BackSpace", "Tab", "CapsLock", "ShiftL", "ControlL", "AltL", "SuperL", "ShiftR", "ControlR", "AltR", "SuperR", " ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", ".", ",", "-", "[", "]", ";", "Return", "'", "\\", "Up", "Down", "Left", "Right", "Delete", "Insert", "End", "Home", "PageUp", "PageDown", "Numlock", "KP_Slash", "Multiply", "KP_Minus", "KP_1", "KP_2", "KP_3", "KP_4", "KP_5", "KP_6", "KP_7", "KP_8", "KP_9", "KP_0", "KP_Period", "KP_Return" }; - - key--; - while (key++) { - u32 i; - for (i = 0; i < 128; i++) { - if (*keyStrs[i] == '\1') - continue; - - if (*keyStrs[i] != *key) { - keyStrs[i] = "\1"; - continue; - } - - if (*keyStrs[i] == '\0' && *key == '\0') - return RGFW_apiKeyCodeToRGFW(i); - - else - keyStrs[i]++; - } - - if (*key == '\0') - break; - } - - return 0; - } - - - char RGFW_keystrToChar(const char* str) { - if (str[1] == 0) - return str[0]; - - static const char* map[] = { - "asciitilde", "`", - "grave", "~", - "exclam", "!", - "at", "@", - "numbersign", "#", - "dollar", "$", - "percent", "%%", - "asciicircum", "^", - "ampersand", "&", - "asterisk", "*", - "parenleft", "(", - "parenright", ")", - "underscore", "_", - "minus", "-", - "plus", "+", - "equal", "=", - "braceleft", "{", - "bracketleft", "[", - "bracketright", "]", - "braceright", "}", - "colon", ":", - "semicolon", ";", - "quotedbl", "\"", - "apostrophe", "'", - "bar", "|", - "backslash", "\'", - "less", "<", - "comma", ",", - "greater", ">", - "period", ".", - "question", "?", - "slash", "/", - "space", " ", - "Return", "\n", - "Enter", "\n", - "enter", "\n", - }; - - u8 i = 0; - for (i = 0; i < (sizeof(map) / sizeof(char*)); i += 2) - if (strcmp(map[i], str) == 0) - return *map[i + 1]; - - return '\0'; - } - -#ifndef M_PI -#define M_PI 3.14159265358979323846 /* pi */ -#endif - -#ifndef RGFW_WINDOWS - struct timespec; - - int nanosleep(const struct timespec* duration, struct timespec* rem); - int clock_gettime(clockid_t clk_id, struct timespec* tp); - int setenv(const char *name, const char *value, int overwrite); - - u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { return win->src.jsPressed[c][button]; } -#else - - typedef u64 (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); - PFN_XInputGetState XInputGetStateSRC = NULL; - #define XInputGetState XInputGetStateSRC - static HMODULE RGFW_XInput_dll = NULL; - - u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { - RGFW_UNUSED(win) - - XINPUT_STATE state; - if (XInputGetState == NULL || XInputGetState(c, &state) == ERROR_DEVICE_NOT_CONNECTED) - return 0; - - if (button == RGFW_JS_A) return state.Gamepad.wButtons & XINPUT_GAMEPAD_A; - else if (button == RGFW_JS_B) return state.Gamepad.wButtons & XINPUT_GAMEPAD_B; - else if (button == RGFW_JS_Y) return state.Gamepad.wButtons & XINPUT_GAMEPAD_Y; - else if (button == RGFW_JS_X) return state.Gamepad.wButtons & XINPUT_GAMEPAD_X; - else if (button == RGFW_JS_START) return state.Gamepad.wButtons & XINPUT_GAMEPAD_START; - else if (button == RGFW_JS_SELECT) return state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK; - else if (button == RGFW_JS_UP) return state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP; - else if (button == RGFW_JS_DOWN) return state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN; - else if (button == RGFW_JS_LEFT) return state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT; - else if (button == RGFW_JS_RIGHT) return state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT; - else if (button == RGFW_JS_L1) return state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER; - else if (button == RGFW_JS_R1) return state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER; - else if (button == RGFW_JS_L2 && state.Gamepad.bLeftTrigger) return 1; - else if (button == RGFW_JS_R2 && state.Gamepad.bRightTrigger) return 1; - - return 0; - } -#endif - -#if defined(RGFW_OPENGL) || defined(RGFW_EGL) +/* EGL, normal OpenGL only */ +#if !defined(RGFW_OSMESA) i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0; #ifndef RGFW_EGL @@ -2346,6 +1582,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ return version; } +/* OPENGL normal only (no EGL / OSMesa) */ #ifndef RGFW_EGL #define RGFW_GL_RENDER_TYPE RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE, 0x2003, 73) @@ -2377,6 +1614,10 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #define WGL_SAMPLE_BUFFERS_ARB 0x2041 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202B + +#define WGL_TRANSPARENT_ARB 0x200A #endif static u32* RGFW_initAttribs(u32 useSoftware) { @@ -2412,6 +1653,8 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ #endif #ifdef RGFW_WINDOWS + WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, + WGL_TRANSPARENT_ARB, TRUE, WGL_COLOR_BITS_ARB, 32, #endif @@ -2459,7 +1702,8 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ return attribs; } -#else +/* EGL only (no OSMesa nor normal OPENGL) */ +#elif defined(RGFW_EGL) #include @@ -2619,14 +1863,71 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ eglTerminate(win->src.EGL_display); } -#endif /* RGFW_EGL */ -#endif /* RGFW_GL stuff? */ + void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + assert(win != NULL); + + eglSwapInterval(win->src.EGL_display, swapInterval); - /* - This is where OS specific stuff starts - */ + win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; + + } +#endif /* RGFW_EGL */ + +/* + end of RGFW_EGL defines +*/ + +/* OPENGL Normal / EGL defines only (no OS MESA) Ends here */ + +#elif defined(RGFW_OSMESA) /* OSmesa only */ +RGFWDEF void RGFW_OSMesa_reorganize(void); + +/* reorganize buffer for osmesa */ +void RGFW_OSMesa_reorganize(void) { + u8* row = (u8*) RGFW_MALLOC(win->r.w * 3); + + i32 half_height = win->r.h / 2; + i32 stride = win->r.w * 3; + + i32 y; + for (y = 0; y < half_height; ++y) { + i32 top_offset = y * stride; + i32 bottom_offset = (win->r.h - y - 1) * stride; + memcpy(row, win->buffer + top_offset, stride); + memcpy(win->buffer + top_offset, win->buffer + bottom_offset, stride); + memcpy(win->buffer + bottom_offset, row, stride); + } + + RGFW_FREE(row); +} +#endif /* RGFW_OSMesa */ + +#endif /* RGFW_GL (OpenGL, EGL, OSMesa )*/ + +/* +This is where OS specific stuff starts +*/ + + +/* + + +Start of Linux / Unix defines + + +*/ #ifdef RGFW_X11 +#ifndef RGFW_NO_X11_CURSOR +#include +#endif +#include + +#ifndef RGFW_NO_DPI +#include +#include +#endif + #include #include #include @@ -2634,6 +1935,8 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ #include /* for converting keycode to string */ #include /* for hiding */ +#include +#include #include /* for data limits (mainly used in drag and drop functions) */ #include @@ -2642,6 +1945,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ #include #endif + u8 RGFW_mouseIconSrc[] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor}; /*atoms needed for drag and drop*/ Atom XdndAware, XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XdndActionMove, XdndActionLink, XdndActionAsk, XdndActionPrivate; @@ -2674,6 +1978,55 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); } #endif + RGFWDEF void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi); + void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi) { +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) + RGFW_bufferSize = RGFW_getScreenSize(); + + win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); + + #ifdef RGFW_OSMESA + win->src.rSurf = OSMesaCreateContext(OSMESA_RGBA, NULL); + OSMesaMakeCurrent(win->src.rSurf, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); + #endif + + win->src.bitmap = XCreateImage( + win->src.display, vi->visual, + vi->depth, + ZPixmap, 0, NULL, RGFW_bufferSize.w, RGFW_bufferSize.h, + 32, 0 + ); + + win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL); + + #else + RGFW_UNUSED(win); /* if buffer rendering is not being used */ + RGFW_UNUSED(vi) + #endif + } + + + + void RGFW_window_setBorder(RGFW_window* win, u8 border) { + static Atom _MOTIF_WM_HINTS = 0; + if (_MOTIF_WM_HINTS == 0 ) + _MOTIF_WM_HINTS = XInternAtom(win->src.display, "_MOTIF_WM_HINTS", False); + + struct __x11WindowHints { + unsigned long flags, functions, decorations, status; + long input_mode; + } hints; + hints.flags = (1L << 1); + hints.decorations = !border; + + XChangeProperty( + win->src.display, win->src.window, + _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, + 32, PropModeReplace, (u8*)&hints, 5 + ); + } + RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) if (X11Cursorhandle == NULL) { @@ -2698,7 +2051,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ RGFW_window* win = RGFW_window_basic_init(rect, args); - u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask; /* X11 events accepted*/ + u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /* X11 events accepted*/ #ifdef RGFW_OPENGL u32* visual_attribs = RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE); @@ -2740,45 +2093,60 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ XFree(fbc); - u32 valuemask = CWBorderPixel | CWColormap; + if (args & RGFW_TRANSPARENT_WINDOW) { + XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /* for RGBA backgrounds*/ + } #else - XVisualInfo* vi = (XVisualInfo*) RGFW_MALLOC(sizeof(XVisualInfo)); - vi->screen = DefaultScreen((Display*) win->src.display); - vi->visual = DefaultVisual((Display*) win->src.display, vi->screen); + XVisualInfo viNorm; - vi->depth = 0; - u32 valuemask = 0; + viNorm.visual = DefaultVisual((Display*) win->src.display, DefaultScreen((Display*) win->src.display)); + + viNorm.depth = 0; + XVisualInfo* vi = &viNorm; + + XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /* for RGBA backgrounds*/ #endif - /* make X window attrubutes*/ XSetWindowAttributes swa; Colormap cmap; swa.colormap = cmap = XCreateColormap((Display*) win->src.display, - RootWindow(win->src.display, vi->screen), + DefaultRootWindow(win->src.display), vi->visual, AllocNone); swa.background_pixmap = None; swa.border_pixel = 0; swa.event_mask = event_mask; + + swa.background_pixel = 0; /* create the window*/ - win->src.window = XCreateWindow((Display*) win->src.display, RootWindow((Display*) win->src.display, vi->screen), win->r.x, win->r.y, win->r.w, win->r.h, + win->src.window = XCreateWindow((Display*) win->src.display, DefaultRootWindow((Display*) win->src.display), win->r.x, win->r.y, win->r.w, win->r.h, 0, vi->depth, InputOutput, vi->visual, - valuemask | CWEventMask, &swa); - + CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); XFreeColors((Display*) win->src.display, cmap, NULL, 0, 0); - if (args & RGFW_TRANSPARENT_WINDOW) - XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /* for RGBA backgrounds*/ + #ifdef RGFW_OPENGL XFree(vi); + #endif + // In your .desktop app, if you set the property + // StartupWMClass=RGFW that will assoicate the launcher icon + // with your application - robrohan + XClassHint *hint = XAllocClassHint(); + assert(hint != NULL); + hint->res_class = "RGFW"; + hint->res_name = (char*)name; // just use the window name as the app name + XSetClassHint((Display*) win->src.display, win->src.window, hint); + XFree(hint); + + if ((args & RGFW_NO_INIT_API) == 0) { #ifdef RGFW_OPENGL i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - + if (RGFW_majorVersion || RGFW_minorVersion) { context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; context_attribs[3] = RGFW_majorVersion; @@ -2800,14 +2168,14 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ if (RGFW_root == NULL) RGFW_root = win; - RGFW_init_buffer(win); - -#ifdef RGFW_VULKAN - RGFW_initVulkan(win); -#endif + RGFW_init_buffer(win, vi); + } + + #ifndef RGFW_NO_MONITOR if (args & RGFW_SCALE_TO_MONITOR) RGFW_window_scaleToMonitor(win); + #endif if (args & RGFW_NO_RESIZE) { /* make it so the user can't resize the window*/ XSizeHints* sh = XAllocSizeHints(); @@ -2820,29 +2188,21 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ } if (args & RGFW_NO_BORDER) { - /* Atom vars for no-border*/ - static Atom window_type = 0; - static Atom value = 0; - - if (window_type == 0) { - window_type = XInternAtom((Display*) win->src.display, "_NET_WM_WINDOW_TYPE", False); - value = XInternAtom((Display*) win->src.display, "_NET_WM_WINDOW_TYPE_DOCK", False); - } - - XChangeProperty((Display*) win->src.display, (Drawable) win->src.window, window_type, XA_ATOM, 32, PropModeReplace, (u8*) &value, 1); /* toggle border*/ + RGFW_window_setBorder(win, 0); } XSelectInput((Display*) win->src.display, (Drawable) win->src.window, event_mask); /* tell X11 what events we want*/ /* make it so the user can't close the window until the program does*/ if (wm_delete_window == 0) - wm_delete_window = XInternAtom((Display*) win->src.display, "WM_DELETE_WINDOW", 1); + wm_delete_window = XInternAtom((Display*) win->src.display, "WM_DELETE_WINDOW", False); XSetWMProtocols((Display*) win->src.display, (Drawable) win->src.window, &wm_delete_window, 1); /* connect the context to the window*/ #ifdef RGFW_OPENGL - glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.rSurf); + if ((args & RGFW_NO_INIT_API) == 0) + glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.rSurf); #endif /* set the background*/ @@ -2880,7 +2240,8 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ } #ifdef RGFW_EGL - RGFW_createOpenGLContext(win); + if ((args & RGFW_NO_INIT_API) == 0) + RGFW_createOpenGLContext(win); #endif RGFW_window_setMouseDefault(win); @@ -2933,6 +2294,14 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { assert(win != NULL); + + if (win->event.type == 0) + RGFW_resetKey(); + + if (win->event.type == RGFW_quit) { + return NULL; + } + win->event.type = 0; #ifdef __linux__ @@ -2942,7 +2311,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ struct js_event e; - if (!win->src.joysticks[i]) + if (win->src.joysticks[i] == 0) continue; i32 flags = fcntl(win->src.joysticks[i], F_GETFL, 0); @@ -2955,6 +2324,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ win->event.type = e.value ? RGFW_jsButtonPressed : RGFW_jsButtonReleased; win->event.button = e.number; win->src.jsPressed[i][e.number] = e.value; + RGFW_jsButtonCallback(win, i, e.number, e.value); return &win->event; case JS_EVENT_AXIS: ioctl(win->src.joysticks[i], JSIOCGAXES, &win->event.axisesCount); @@ -2967,7 +2337,8 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ win->event.axis[e.number / 2].x = xAxis; win->event.axis[e.number / 2].y = yAxis; win->event.type = RGFW_jsAxisMove; - win->event.joystick = e.number / 2; + win->event.joystick = i; + RGFW_jsAxisCallback(win, i, win->event.axis, win->event.axisesCount); return &win->event; default: break; @@ -3005,54 +2376,64 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ /* set event key data */ KeySym sym = XkbKeycodeToKeysym((Display*) win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0); win->event.keyCode = RGFW_apiKeyCodeToRGFW(E.xkey.keycode); - win->event.keyName = XKeysymToString(sym); /* convert to string */ + + char* str = XKeysymToString(sym); + if (str != NULL) + strncpy(win->event.keyName, str, 16); - RGFW_keyboard_prev[win->event.keyCode] = RGFW_isPressedI(win, win->event.keyCode); + win->event.keyName[15] = '\0'; + + RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode); /* get keystate data */ win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased; - if (win->event.type == RGFW_keyReleased) { - if (sym == XK_Caps_Lock && win->event.lockState & RGFW_CAPSLOCK) - win->event.lockState ^= RGFW_CAPSLOCK; - else if (sym == XK_Caps_Lock) - win->event.lockState |= RGFW_CAPSLOCK; + XKeyboardState keystate; + XGetKeyboardControl((Display*) win->src.display, &keystate); - else if (sym == XK_Num_Lock && win->event.lockState & RGFW_NUMLOCK) - win->event.lockState ^= RGFW_NUMLOCK; - else if (sym == XK_Num_Lock) - win->event.lockState |= RGFW_NUMLOCK; - } - - RGFW_keyboard[win->event.keyCode] = (E.type == KeyPress); + RGFW_updateLockState(win, (keystate.led_mask & 1), (keystate.led_mask & 2)); + + RGFW_keyboard[win->event.keyCode].current = (E.type == KeyPress); + RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, (E.type == KeyPress)); break; case ButtonPress: case ButtonRelease: - win->event.type = (E.type == ButtonPress) ? RGFW_mouseButtonPressed : RGFW_mouseButtonReleased; - - if (win->event.button == RGFW_mouseScrollUp) { - win->event.scroll = 1; - } - else if (win->event.button == RGFW_mouseScrollDown) { - win->event.scroll = -1; + win->event.type = E.type; // the events match + + switch(win->event.button) { + case RGFW_mouseScrollUp: + win->event.scroll = 1; + break; + case RGFW_mouseScrollDown: + win->event.scroll = -1; + break; + default: break; } win->event.button = E.xbutton.button; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = (E.type == ButtonPress); + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress)); break; case MotionNotify: win->event.point.x = E.xmotion.x; win->event.point.y = E.xmotion.y; win->event.type = RGFW_mousePosChanged; + RGFW_mousePosCallback(win, win->event.point); + break; + + case Expose: + win->event.type = RGFW_windowRefresh; + RGFW_windowRefreshCallback(win); break; case ClientMessage: /* if the client closed the window*/ if (E.xclient.data.l[0] == (i64) wm_delete_window) { win->event.type = RGFW_quit; + RGFW_windowQuitCallback(win); break; } @@ -3071,10 +2452,11 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ if ((win->src.winArgs & RGFW_ALLOW_DND) == 0) break; - u8 formFree = 0; if (E.xclient.message_type == XdndEnter) { u64 count; - Atom* formats = (Atom*) 0; + Atom* formats; + Atom real_formats[6]; + Bool list = E.xclient.data.l[1] & 1; xdnd.source = E.xclient.data.l[0]; @@ -3102,17 +2484,16 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ (unsigned long*) &bytesAfter, (u8**) &formats); } else { - formats = (Atom*) RGFW_MALLOC(E.xclient.data.l[2] + E.xclient.data.l[3] + E.xclient.data.l[4]); - formFree = 1; - count = 0; if (E.xclient.data.l[2] != None) - formats[count++] = E.xclient.data.l[2]; + real_formats[count++] = E.xclient.data.l[2]; if (E.xclient.data.l[3] != None) - formats[count++] = E.xclient.data.l[3]; + real_formats[count++] = E.xclient.data.l[3]; if (E.xclient.data.l[4] != None) - formats[count++] = E.xclient.data.l[4]; + real_formats[count++] = E.xclient.data.l[4]; + + formats = real_formats; } u32 i; @@ -3140,14 +2521,8 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ } } - if (list && formats) { + if (list) { XFree(formats); - formats = (Atom*) 0; - } else if (formFree && formats != (Atom*) 0) { - RGFW_FREE(formats); - - formats = (Atom*) 0; - formFree = 1; } break; @@ -3223,6 +2598,8 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ False, NoEventMask, &reply); XFlush((Display*) win->src.display); } + + RGFW_dndInitCallback(win, win->event.point); break; case SelectionNotify: /* this is only for checking for xdnd drops */ @@ -3293,7 +2670,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ line++; } path[index] = '\0'; - strcpy(win->event.droppedFiles[win->event.droppedFilesCount - 1], path); + strncpy(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1); } if (data) @@ -3312,37 +2689,54 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ XFlush((Display*) win->src.display); } + RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); break; case FocusIn: win->event.inFocus = 1; - - XKeyboardState keystate; - XGetKeyboardControl((Display*) win->src.display, &keystate); - win->event.lockState = keystate.led_mask; - win->event.type = RGFW_focusIn; + RGFW_focusCallback(win, 1); break; break; case FocusOut: win->event.inFocus = 0; win->event.type = RGFW_focusOut; + RGFW_focusCallback(win, 0); break; + + case EnterNotify: { + win->event.type = RGFW_mouseEnter; + win->event.point.x = E.xcrossing.x; + win->event.point.y = E.xcrossing.y; + RGFW_mouseNotifyCallBack(win, win->event.point, 1); + break; + } + + case LeaveNotify: { + win->event.type = RGFW_mouseLeave; + RGFW_mouseNotifyCallBack(win, win->event.point, 0); + break; + } + case ConfigureNotify: { - // detect resize + /* detect resize */ if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) { - win->event.type = RGFW_windowResized; - win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height); - break; + win->event.type = RGFW_windowResized; + win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height); + RGFW_windowResizeCallback(win, win->r); + break; } - // detect move + /* detect move */ if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) { - win->event.type = RGFW_windowMoved; - win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h); + win->event.type = RGFW_windowMoved; + win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h); + RGFW_windowMoveCallback(win, win->r); + break; + } + break; - } } default: { break; @@ -3357,76 +2751,6 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ return NULL; } - void RGFW_window_close(RGFW_window* win) { - assert(win != NULL); - -#ifdef RGFW_VULKAN - for (u32 i = 0; i < win->src.image_count; i++) { - vkDestroyImageView(RGFW_vulkan_info.device, win->src.swapchain_image_views[i], NULL); - } - - vkDestroySwapchainKHR(RGFW_vulkan_info.device, win->src.swapchain, NULL); - vkDestroySurfaceKHR(RGFW_vulkan_info.instance, win->src.rSurf, NULL); - RGFW_FREE(win->src.swapchain_image_views); - RGFW_FREE(win->src.swapchain_images); -#endif - -#ifdef RGFW_EGL - RGFW_closeEGL(win); -#endif - -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (win->buffer != NULL) { - XDestroyImage((XImage*) win->src.bitmap); - } -#endif - - if ((Display*) win->src.display) { -#ifdef RGFW_OPENGL - glXDestroyContext((Display*) win->src.display, win->src.rSurf); -#endif - - if (win == RGFW_root) - RGFW_root = NULL; - - if ((Drawable) win->src.window) - XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /* close the window*/ - - if (win->src.display) - XCloseDisplay((Display*) win->src.display); /* kill the display*/ - } - -#ifdef RGFW_ALLOC_DROPFILES - { - u32 i; - for (i = 0; i < RGFW_MAX_DROPS; i++) - RGFW_FREE(win->event.droppedFiles[i]); - - - RGFW_FREE(win->event.droppedFiles); - } -#endif - - RGFW_windowsOpen--; -#if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) - if (X11Cursorhandle != NULL && RGFW_windowsOpen <= 0) { - dlclose(X11Cursorhandle); - - X11Cursorhandle = NULL; - } -#endif - - /* set cleared display / window to NULL for error checking */ - win->src.display = (Display*) 0; - win->src.window = (Window) 0; - - u8 i; - for (i = 0; i < win->src.joystickCount; i++) - close(win->src.joysticks[i]); - - RGFW_FREE(win); /* free collected window data */ - } - void RGFW_window_move(RGFW_window* win, RGFW_vector v) { assert(win != NULL); win->r.x = v.x; @@ -3447,13 +2771,16 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { assert(win != NULL); + if (a.w == 0 && a.h == 0) + return; + XSizeHints hints; long flags; XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags); hints.flags |= PMinSize; - + hints.min_width = a.w; hints.min_height = a.h; @@ -3463,6 +2790,9 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { assert(win != NULL); + if (a.w == 0 && a.h == 0) + return; + XSizeHints hints; long flags; @@ -3489,13 +2819,51 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ XMapWindow(win->src.display, (Window) win->src.window); XFlush(win->src.display); - } + } void RGFW_window_setName(RGFW_window* win, char* name) { assert(win != NULL); XStoreName((Display*) win->src.display, (Window) win->src.window, name); } + + void* RGFW_libxshape = NULL; + + #ifndef RGFW_NO_PASSTHROUGH + void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { + assert(win != NULL); + + #if defined(__CYGWIN__) + RGFW_libxshape = dlopen("libXext-6.so", RTLD_LAZY | RTLD_LOCAL); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_libxshape = dlopen("libXext.so", RTLD_LAZY | RTLD_LOCAL); + #else + RGFW_libxshape = dlopen("libXext.so.6", RTLD_LAZY | RTLD_LOCAL); + #endif + + typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int); + static PFN_XShapeCombineMask XShapeCombineMask; + + typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int); + static PFN_XShapeCombineRegion XShapeCombineRegion; + + if (XShapeCombineMask != NULL) + XShapeCombineMask = (PFN_XShapeCombineMask) dlsym(RGFW_libxshape, "XShapeCombineMask"); + + if (XShapeCombineRegion != NULL) + XShapeCombineRegion = (PFN_XShapeCombineRegion) dlsym(RGFW_libxshape, "XShapeCombineMask"); + + if (passthrough) { + Region region = XCreateRegion(); + XShapeCombineRegion(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); + + return; + } + + XShapeCombineMask(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet); + } + #endif /* the majority function is sourced from GLFW @@ -3587,8 +2955,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ if (event.xbutton.x == v.x && event.xbutton.y == v.y) return; - XWarpPointer(win->src.display, None, None, 0, 0, 0, 0, -event.xbutton.x, -event.xbutton.y); - XWarpPointer(win->src.display, None, None, 0, 0, 0, 0, v.x, v.y); + XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) v.x - win->r.x, (int) v.y - win->r.y); } RGFWDEF void RGFW_window_disableMouse(RGFW_window* win) { @@ -3654,7 +3021,8 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ if (target == UTF8 || target == XA_STRING) { s = (char*)RGFW_MALLOC(sizeof(char) * sizeN); - strcpy(s, data); + strncpy(s, data, sizeN); + s[sizeN] = '\0'; XFree(data); } @@ -3937,7 +3305,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ XrmValue value; char* type = NULL; - if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && strcmp(type, "String") == 0) + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && strncmp(type, "String", 7) == 0) xdpi = ydpi = atof(value.addr); XrmDestroyDatabase(db); #endif @@ -3955,7 +3323,7 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ monitor.physW = (monitor.rect.w * 25.4f / 96.f); monitor.physH = (monitor.rect.h * 25.4f / 96.f); - strcpy(monitor.name, DisplayString(display)); + strncpy(monitor.name, DisplayString(display), 128); XGetSystemContentScale(display, &monitor.scaleX, &monitor.scaleY); @@ -4024,9 +3392,187 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { return RGFW_XCreateMonitor(DefaultScreen(win->src.display)); } + + #ifdef RGFW_OPENGL + void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + assert(win != NULL); + + glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.rSurf); + } + #endif + + + void RGFW_window_swapBuffers(RGFW_window* win) { + assert(win != NULL); + + RGFW_window_makeCurrent(win); + + /* clear the window*/ + if (!(win->src.winArgs & RGFW_NO_CPU_RENDER)) { +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + #ifdef RGFW_OSMESA + RGFW_OSMesa_reorganize(); + #endif + RGFW_area area = RGFW_bufferSize; + +#ifndef RGFW_X11_DONT_CONVERT_BGR + win->src.bitmap->data = (char*) win->buffer; + u32 x, y; + for (y = 0; y < (u32)win->r.h; y++) { + for (x = 0; x < (u32)win->r.w; x++) { + u32 index = (y * 4 * area.w) + x * 4; + + u8 red = win->src.bitmap->data[index]; + win->src.bitmap->data[index] = win->buffer[index + 2]; + win->src.bitmap->data[index + 2] = red; + + } + } +#endif + XPutImage(win->src.display, (Window) win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, RGFW_bufferSize.w, RGFW_bufferSize.h); +#endif + } + + if (!(win->src.winArgs & RGFW_NO_GPU_RENDER)) { + #ifdef RGFW_EGL + eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); + #elif defined(RGFW_OPENGL) + glXSwapBuffers((Display*) win->src.display, (Window) win->src.window); + #endif + } + + RGFW_window_checkFPS(win); + } + + #if !defined(RGFW_EGL) + void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + assert(win != NULL); + + #if defined(RGFW_OPENGL) + ((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))((Display*) win->src.display, (Window) win->src.window, swapInterval); + #endif + + win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; + } + #endif + + + void RGFW_window_close(RGFW_window* win) { + assert(win != NULL); +#ifdef RGFW_EGL + RGFW_closeEGL(win); #endif +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if (win->buffer != NULL) { + XDestroyImage((XImage*) win->src.bitmap); + XFreeGC(win->src.display, win->src.gc); + } +#endif + + if ((Display*) win->src.display) { +#ifdef RGFW_OPENGL + glXDestroyContext((Display*) win->src.display, win->src.rSurf); +#endif + + if (win == RGFW_root) + RGFW_root = NULL; + + if ((Drawable) win->src.window) + XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /* close the window*/ + + XCloseDisplay((Display*) win->src.display); /* kill the display*/ + } + +#ifdef RGFW_ALLOC_DROPFILES + { + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + RGFW_FREE(win->event.droppedFiles[i]); + + + RGFW_FREE(win->event.droppedFiles); + } +#endif + + RGFW_windowsOpen--; +#if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) + if (X11Cursorhandle != NULL && RGFW_windowsOpen <= 0) { + dlclose(X11Cursorhandle); + + X11Cursorhandle = NULL; + } +#endif + + if (RGFW_libxshape != NULL && RGFW_windowsOpen <= 0) { + dlclose(RGFW_libxshape); + RGFW_libxshape = NULL; + } + + /* set cleared display / window to NULL for error checking */ + win->src.display = (Display*) 0; + win->src.window = (Window) 0; + + u8 i; + for (i = 0; i < win->src.joystickCount; i++) + close(win->src.joysticks[i]); + + RGFW_FREE(win); /* free collected window data */ + } + + u64 RGFW_getTimeNS(void) { + struct timespec ts = { 0 }; + clock_gettime(1, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + + return nanoSeconds; + } + + u64 RGFW_getTime(void) { + struct timespec ts = { 0 }; + clock_gettime(1, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + + return (double)(nanoSeconds) * 1e-9; + } +/* + End of linux / unix defines +*/ + +#endif /* RGFW_X11 */ + + +/* + + Start of Windows defines + + +*/ + #ifdef RGFW_WINDOWS + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef RGFW_NO_XINPUT + typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); + PFN_XInputGetState XInputGetStateSRC = NULL; + #define XInputGetState XInputGetStateSRC + + typedef DWORD (WINAPI * PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE); + PFN_XInputGetKeystroke XInputGetKeystrokeSRC = NULL; + #define XInputGetKeystroke XInputGetKeystrokeSRC + + static HMODULE RGFW_XInput_dll = NULL; + #endif + + u32 RGFW_mouseIconSrc[] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO}; + char* createUTF8FromWideStringWin32(const WCHAR* source); #define GL_FRONT 0x0404 @@ -4064,21 +3610,18 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ void* RGFWjoystickApi = NULL; /* these two wgl functions need to be preloaded */ - typedef long long int (WINAPI* wglCreateContextAttribsARB_type)(HDC hdc, HGLRC hShareContext, - const int* attribList); - wglCreateContextAttribsARB_type wglCreateContextAttribsARB = NULL; + typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); + PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; /* defines for creating ARB attributes */ #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define WGL_TRANSPARENT_ARB 0x200A #define WGL_DRAW_TO_WINDOW_ARB 0x2001 #define WGL_ACCELERATION_ARB 0x2003 #define WGL_NO_ACCELERATION_ARB 0x2025 #define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_DOUBLE_BUFFER_ARB 0x2011 -#define WGL_PIXEL_TYPE_ARB 0x2013 #define WGL_COLOR_BITS_ARB 0x2014 #define WGL_RED_BITS_ARB 0x2015 #define WGL_RED_SHIFT_ARB 0x2016 @@ -4099,7 +3642,6 @@ RGFW_UNUSED(win); /* if buffer rendering is not being used */ #define WGL_DEPTH_BITS_ARB 0x2022 #define WGL_STENCIL_BITS_ARB 0x2023 #define WGL_FULL_ACCELERATION_ARB 0x2027 -#define WGL_TYPE_RGBA_ARB 0x202B #define WGL_CONTEXT_FLAGS_ARB 0x2094 #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 @@ -4144,7 +3686,7 @@ static HMODULE wglinstance = NULL; return (void*) GetProcAddress(wglinstance, procname); } - typedef u64 (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); + typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; #endif @@ -4169,11 +3711,14 @@ static HMODULE wglinstance = NULL; #ifndef RGFW_NO_DPI static HMODULE RGFW_Shcore_dll = NULL; - typedef u64 (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); + typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL; #define GetDpiForMonitor GetDpiForMonitorSRC #endif + __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod); + + #ifndef RGFW_NO_XINPUT void RGFW_loadXInput(void) { u32 i; static const char* names[] = { @@ -4188,22 +3733,76 @@ static HMODULE wglinstance = NULL; RGFW_XInput_dll = LoadLibraryA(names[i]); if (RGFW_XInput_dll) { - XInputGetStateSRC = (PFN_XInputGetState)GetProcAddress(RGFW_XInput_dll, "XInputGetState"); + XInputGetStateSRC = (PFN_XInputGetState)(void*)GetProcAddress(RGFW_XInput_dll, "XInputGetState"); if (XInputGetStateSRC == NULL) printf("Failed to load XInputGetState"); } } } + #endif + + RGFWDEF void RGFW_init_buffer(RGFW_window* win); + void RGFW_init_buffer(RGFW_window* win) { +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) + RGFW_bufferSize = RGFW_getScreenSize(); + + BITMAPV5HEADER bi = { 0 }; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = RGFW_bufferSize.w; + bi.bV5Height = -((LONG) RGFW_bufferSize.h); + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5BlueMask = 0x00ff0000; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5RedMask = 0x000000ff; + bi.bV5AlphaMask = 0xff000000; + + win->src.bitmap = CreateDIBSection(win->src.hdc, + (BITMAPINFO*) &bi, + DIB_RGB_COLORS, + (void**) &win->buffer, + NULL, + (DWORD) 0); + + win->src.hdcMem = CreateCompatibleDC(win->src.hdc); + + #if defined(RGFW_OSMESA) + win->src.rSurf = OSMesaCreateContext(OSMESA_RGBA, NULL); + OSMesaMakeCurrent(win->src.rSurf, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); + #endif +#else +RGFW_UNUSED(win); /* if buffer rendering is not being used */ +#endif + } + + void RGFW_window_setDND(RGFW_window* win, b8 allow) { + DragAcceptFiles(win->src.window, allow); + } + + void RGFW_clipCursor(RGFW_rect rect) { + if (!rect.x && !rect.y && rect.w && !rect.h) { + ClipCursor(NULL); + return; + } + + RECT r = {rect.x, rect.y, rect.x + rect.w, rect.y + rect.h}; + ClipCursor(&r); + } RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) { + #ifndef RGFW_NO_XINPUT if (RGFW_XInput_dll == NULL) RGFW_loadXInput(); - + #endif + #ifndef RGFW_NO_DPI if (RGFW_Shcore_dll == NULL) { RGFW_Shcore_dll = LoadLibraryA("shcore.dll"); - GetDpiForMonitorSRC = (PFN_GetDpiForMonitor)GetProcAddress(RGFW_Shcore_dll, "GetDpiForMonitor"); + GetDpiForMonitorSRC = (PFN_GetDpiForMonitor)(void*)GetProcAddress(RGFW_Shcore_dll, "GetDpiForMonitor"); } #endif @@ -4219,6 +3818,8 @@ static HMODULE wglinstance = NULL; #endif } + timeBeginPeriod(1); + if (name[0] == 0) name = (char*) " "; RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1); @@ -4226,10 +3827,10 @@ static HMODULE wglinstance = NULL; RGFW_window* win = RGFW_window_basic_init(rect, args); - if (RGFW_root == NULL) { - RGFW_root = win; - } - + win->src.maxSize = RGFW_AREA(0, 0); + win->src.minSize = RGFW_AREA(0, 0); + + HINSTANCE inh = GetModuleHandleA(NULL); WNDCLASSA Class = { 0 }; /* Setup the Window class. */ @@ -4260,15 +3861,13 @@ static HMODULE wglinstance = NULL; win->src.hOffset = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top); win->src.window = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + win->src.hOffset, 0, 0, inh, 0); - if (args & RGFW_TRANSPARENT_WINDOW) { - SetWindowLongA(win->src.window, GWL_EXSTYLE, GetWindowLongA(win->src.window, GWL_EXSTYLE) | WS_EX_LAYERED); - } if (args & RGFW_ALLOW_DND) { win->src.winArgs |= RGFW_ALLOW_DND; - DragAcceptFiles(win->src.window, TRUE); + RGFW_window_setDND(win, 1); } win->src.hdc = GetDC(win->src.window); + if ((args & RGFW_NO_INIT_API) == 0) { #ifdef RGFW_DIRECTX assert(FAILED(CreateDXGIFactory(&__uuidof(IDXGIFactory), (void**) &RGFW_dxInfo.pFactory)) == 0); @@ -4338,10 +3937,10 @@ static HMODULE wglinstance = NULL; .nVersion = 1, .iPixelType = PFD_TYPE_RGBA, .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, - .cColorBits = 32, + .cColorBits = 24, .cAlphaBits = 8, .iLayerType = PFD_MAIN_PLANE, - .cDepthBits = 24, + .cDepthBits = 32, .cStencilBits = 8, }; @@ -4352,8 +3951,8 @@ static HMODULE wglinstance = NULL; wglMakeCurrent(dummy_dc, dummy_context); if (wglChoosePixelFormatARB == NULL) { - wglCreateContextAttribsARB = (wglCreateContextAttribsARB_type) wglGetProcAddress("wglCreateContextAttribsARB"); - wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) wglGetProcAddress("wglChoosePixelFormatARB"); + wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB"); + wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB"); } wglMakeCurrent(dummy_dc, 0); @@ -4412,8 +4011,8 @@ static HMODULE wglinstance = NULL; } wglMakeCurrent(win->src.hdc, win->src.rSurf); - wglShareLists(RGFW_root->src.rSurf, win->src.rSurf); #endif + } #ifdef RGFW_OSMESA #ifdef RGFW_LINK_OSM ESA @@ -4424,33 +4023,67 @@ static HMODULE wglinstance = NULL; #endif #ifdef RGFW_OPENGL - ReleaseDC(win->src.window, win->src.hdc); - win->src.hdc = GetDC(win->src.window); - wglMakeCurrent(win->src.hdc, win->src.rSurf); + if ((args & RGFW_NO_INIT_API) == 0) { + ReleaseDC(win->src.window, win->src.hdc); + win->src.hdc = GetDC(win->src.window); + wglMakeCurrent(win->src.hdc, win->src.rSurf); + } #endif DestroyWindow(dummyWin); RGFW_init_buffer(win); -#ifdef RGFW_VULKAN - RGFW_initVulkan(win); -#endif + #ifndef RGFW_NO_MONITOR if (args & RGFW_SCALE_TO_MONITOR) RGFW_window_scaleToMonitor(win); + #endif #ifdef RGFW_EGL - RGFW_createOpenGLContext(win); + if ((args & RGFW_NO_INIT_API) == 0) + RGFW_createOpenGLContext(win); #endif if (args & RGFW_HIDE_MOUSE) RGFW_window_showMouse(win, 0); + if (args & RGFW_TRANSPARENT_WINDOW) { + SetWindowLong(win->src.window, GWL_EXSTYLE, GetWindowLong(win->src.window, GWL_EXSTYLE) | WS_EX_LAYERED); + SetLayeredWindowAttributes(win->src.window, RGB(255, 255, 255), RGFW_ALPHA, LWA_ALPHA); + } + ShowWindow(win->src.window, SW_SHOWNORMAL); + + if (RGFW_root == NULL) + RGFW_root = win; + + #ifdef RGFW_OPENGL + else + wglShareLists(RGFW_root->src.rSurf, win->src.rSurf); + #endif return win; } + void RGFW_window_setBorder(RGFW_window* win, u8 border) { + DWORD style = GetWindowLong(win->src.window, GWL_STYLE); + + if (border == 0) { + SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); + SetWindowPos( + win->src.window, HWND_TOP, 0, 0, 0, 0, + SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE + ); + } + else { + SetWindowLong(win->src.window, GWL_STYLE, style | WS_OVERLAPPEDWINDOW); + SetWindowPos( + win->src.window, HWND_TOP, 0, 0, 0, 0, + SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE + ); + } + } + RGFW_area RGFW_getScreenSize(void) { return RGFW_AREA(GetDeviceCaps(GetDC(NULL), HORZRES), GetDeviceCaps(GetDC(NULL), VERTRES)); @@ -4494,98 +4127,55 @@ static HMODULE wglinstance = NULL; ShowWindow(win->src.window, SW_RESTORE); } - static i32 RGFW_checkXInput(RGFW_Event* e) { - static WORD buttons[4]; - static BYTE triggers[4][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; + u8 RGFW_xinput2RGFW[] = { + RGFW_JS_A, /* or PS X button */ + RGFW_JS_B, /* or PS circle button */ + RGFW_JS_X, /* or PS square button */ + RGFW_JS_Y, /* or PS triangle button */ + RGFW_JS_R1, /* right bumper */ + RGFW_JS_L1, /* left bump */ + RGFW_JS_L2, /* left trigger*/ + RGFW_JS_R2, /* right trigger */ + 0, 0, 0, 0, 0, 0, 0, 0, + RGFW_JS_UP, /* dpad up */ + RGFW_JS_DOWN, /* dpad down*/ + RGFW_JS_LEFT, /* dpad left */ + RGFW_JS_RIGHT, /* dpad right */ + RGFW_JS_START, /* start button */ + RGFW_JS_SELECT/* select button */ + }; + + static i32 RGFW_checkXInput(RGFW_window* win, RGFW_Event* e) { size_t i; for (i = 0; i < 4; i++) { + XINPUT_KEYSTROKE keystroke; + + if (XInputGetKeystroke == NULL) + return 0; + + DWORD result = XInputGetKeystroke((DWORD)i, 0, &keystroke); + + if ((keystroke.Flags & XINPUT_KEYSTROKE_REPEAT) == 0 && result != ERROR_EMPTY) { + if (result != ERROR_SUCCESS) + return 0; + + if (keystroke.VirtualKey > VK_PAD_BACK) + continue; + + // RGFW_jsButtonPressed + 1 = RGFW_jsButtonReleased + e->type = RGFW_jsButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); + e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800]; + win->src.jsPressed[i][e->button] = !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN); + + return 1; + } + XINPUT_STATE state; if (XInputGetState == NULL || XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED ) return 0; - - e->button = 0; - if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A && !(buttons[i] & XINPUT_GAMEPAD_A)) { - e->button = RGFW_JS_A; - e->type = RGFW_jsButtonPressed; - buttons[i] = state.Gamepad.wButtons; - return 1; - } else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_B && !(buttons[i] & XINPUT_GAMEPAD_B)) - e->button = RGFW_JS_B; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y && !(buttons[i] & XINPUT_GAMEPAD_Y)) - e->button = RGFW_JS_Y; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_X && !(buttons[i] & XINPUT_GAMEPAD_X)) - e->button = RGFW_JS_X; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START && !(buttons[i] & XINPUT_GAMEPAD_START)) - e->button = RGFW_JS_START; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK && !(buttons[i] & XINPUT_GAMEPAD_BACK)) - e->button = RGFW_JS_SELECT; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP && !(buttons[i] & XINPUT_GAMEPAD_DPAD_UP)) - e->button = RGFW_JS_UP; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN && !(buttons[i] & XINPUT_GAMEPAD_DPAD_DOWN)) - e->button = RGFW_JS_DOWN; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT && !(buttons[i] & XINPUT_GAMEPAD_DPAD_LEFT)) - e->button = RGFW_JS_LEFT; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT && !(buttons[i] & XINPUT_GAMEPAD_DPAD_RIGHT)) - e->button = RGFW_JS_RIGHT; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER && !(buttons[i] & XINPUT_GAMEPAD_LEFT_SHOULDER)) - e->button = RGFW_JS_L1; - else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER && !(buttons[i] & XINPUT_GAMEPAD_RIGHT_SHOULDER)) - e->button = RGFW_JS_R1; - else if (state.Gamepad.bLeftTrigger && triggers[i][0] == 0) - e->button = RGFW_JS_L2; - else if (state.Gamepad.bRightTrigger && triggers[i][1] == 0) - e->button = RGFW_JS_R2; - - triggers[i][0] = state.Gamepad.bLeftTrigger; - triggers[i][1] = state.Gamepad.bRightTrigger; - - if (e->button) { - buttons[i] = state.Gamepad.wButtons; - e->type = RGFW_jsButtonPressed; - return 1; - } - - if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_A) && (buttons[i] & XINPUT_GAMEPAD_A)) { - e->button = RGFW_JS_A; - e->type = RGFW_jsButtonReleased; - buttons[i] = state.Gamepad.wButtons; - return 1; - } else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_B) && (buttons[i] & XINPUT_GAMEPAD_B)) - e->button = RGFW_JS_B; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) && (buttons[i] & XINPUT_GAMEPAD_Y)) - e->button = RGFW_JS_Y; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_X) && (buttons[i] & XINPUT_GAMEPAD_X)) - e->button = RGFW_JS_X; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_START) && (buttons[i] & XINPUT_GAMEPAD_START)) - e->button = RGFW_JS_START; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) && (buttons[i] & XINPUT_GAMEPAD_BACK)) - e->button = RGFW_JS_SELECT; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) && (buttons[i] & XINPUT_GAMEPAD_DPAD_UP)) - e->button = RGFW_JS_UP; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) && (buttons[i] & XINPUT_GAMEPAD_DPAD_DOWN)) - e->button = RGFW_JS_DOWN; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) && (buttons[i] & XINPUT_GAMEPAD_DPAD_LEFT)) - e->button = RGFW_JS_LEFT; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) && (buttons[i] & XINPUT_GAMEPAD_DPAD_RIGHT)) - e->button = RGFW_JS_RIGHT; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) && (buttons[i] & XINPUT_GAMEPAD_LEFT_SHOULDER)) - e->button = RGFW_JS_L1; - else if (!(state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) && (buttons[i] & XINPUT_GAMEPAD_RIGHT_SHOULDER)) - e->button = RGFW_JS_R1; - else if (state.Gamepad.bLeftTrigger == 0 && triggers[i][0] != 0) - e->button = RGFW_JS_L2; - else if (state.Gamepad.bRightTrigger == 0 && triggers[i][1] != 0) - e->button = RGFW_JS_R2; - - buttons[i] = state.Gamepad.wButtons; - - if (e->button) { - e->type = RGFW_jsButtonReleased; - return 1; - } #define INPUT_DEADZONE ( 0.24f * (float)(0x7FFF) ) // Default to 24% of the +/- 32767 range. This is a reasonable default value but can be altered if needed. if ((state.Gamepad.sThumbLX < INPUT_DEADZONE && @@ -4629,6 +4219,10 @@ static HMODULE wglinstance = NULL; RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { assert(win != NULL); + if (win->event.type == RGFW_quit) { + return NULL; + } + MSG msg; if (RGFW_eventWindow.src.window == win->src.window) { @@ -4636,12 +4230,14 @@ static HMODULE wglinstance = NULL; win->r.x = RGFW_eventWindow.r.x; win->r.y = RGFW_eventWindow.r.y; win->event.type = RGFW_windowMoved; + RGFW_windowMoveCallback(win, win->r); } if (RGFW_eventWindow.r.w != -1) { win->r.w = RGFW_eventWindow.r.w; win->r.h = RGFW_eventWindow.r.h; win->event.type = RGFW_windowResized; + RGFW_windowResizeCallback(win, win->r); } RGFW_eventWindow.src.window = NULL; @@ -4650,37 +4246,82 @@ static HMODULE wglinstance = NULL; return &win->event; } + + static HDROP drop; + + if (win->event.type == RGFW_dnd_init) { + if (win->event.droppedFilesCount) { + u32 i; + for (i = 0; i < win->event.droppedFilesCount; i++) + win->event.droppedFiles[i][0] = '\0'; + } + + win->event.droppedFilesCount = 0; + win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0); + //win->event.droppedFiles = (char**)RGFW_CALLOC(win->event.droppedFilesCount, sizeof(char*)); + + u32 i; + for (i = 0; i < win->event.droppedFilesCount; i++) { + const UINT length = DragQueryFileW(drop, i, NULL, 0); + WCHAR* buffer = (WCHAR*) RGFW_CALLOC((size_t) length + 1, sizeof(WCHAR)); + + DragQueryFileW(drop, i, buffer, length + 1); + strncpy(win->event.droppedFiles[i], createUTF8FromWideStringWin32(buffer), RGFW_MAX_PATH); + win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0'; + RGFW_FREE(buffer); + } + + DragFinish(drop); + RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); + + win->event.type = RGFW_dnd; + return &win->event; + } + win->event.inFocus = (GetForegroundWindow() == win->src.window); - if (RGFW_checkXInput(&win->event)) + if (RGFW_checkXInput(win, &win->event)) return &win->event; - if (win->event.type == RGFW_quit) - return NULL; - static BYTE keyboardState[256]; if (PeekMessageA(&msg, win->src.window, 0u, 0u, PM_REMOVE)) { switch (msg.message) { case WM_CLOSE: case WM_QUIT: + RGFW_windowQuitCallback(win); win->event.type = RGFW_quit; break; case WM_ACTIVATE: win->event.inFocus = (LOWORD(msg.wParam) == WA_INACTIVE); - if (win->event.inFocus) + if (win->event.inFocus) { win->event.type = RGFW_focusIn; - else + RGFW_focusCallback(win, 1); + } + else { win->event.type = RGFW_focusOut; - - break; + RGFW_focusCallback(win, 0); + } + break; + + case WM_PAINT: + win->event.type = RGFW_windowRefresh; + RGFW_windowRefreshCallback(win); + break; + + case WM_MOUSELEAVE: + win->event.type = RGFW_mouseLeave; + win->src.winArgs |= RGFW_MOUSE_LEFT; + RGFW_mouseNotifyCallBack(win, win->event.point, 0); + break; + case WM_KEYUP: { win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam); - RGFW_keyboard_prev[win->event.keyCode] = RGFW_isPressedI(win, win->event.keyCode); + RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode); static char keyName[16]; @@ -4692,22 +4333,25 @@ static HMODULE wglinstance = NULL; CharLowerBuffA(keyName, 16); } } - + + RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001)); + strncpy(win->event.keyName, keyName, 16); - if (RGFW_isPressedI(win, RGFW_ShiftL)) { + if (RGFW_isPressed(win, RGFW_ShiftL)) { ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR), keyboardState, (LPWORD) win->event.keyName, 0); } win->event.type = RGFW_keyReleased; - RGFW_keyboard[win->event.keyCode] = 0; + RGFW_keyboard[win->event.keyCode].current = 0; + RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0); break; } case WM_KEYDOWN: { win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam); - RGFW_keyboard_prev[win->event.keyCode] = RGFW_isPressedI(win, win->event.keyCode); + RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode); static char keyName[16]; @@ -4719,24 +4363,36 @@ static HMODULE wglinstance = NULL; CharLowerBuffA(keyName, 16); } } - + + RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001)); + strncpy(win->event.keyName, keyName, 16); - if (RGFW_isPressedI(win, RGFW_ShiftL) & 0x8000) { + if (RGFW_isPressed(win, RGFW_ShiftL) & 0x8000) { ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR), keyboardState, (LPWORD) win->event.keyName, 0); } win->event.type = RGFW_keyPressed; - RGFW_keyboard[win->event.keyCode] = 1; + RGFW_keyboard[win->event.keyCode].current = 1; + RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1); break; } case WM_MOUSEMOVE: + win->event.type = RGFW_mousePosChanged; + win->event.point.x = GET_X_LPARAM(msg.lParam); win->event.point.y = GET_Y_LPARAM(msg.lParam); - win->event.type = RGFW_mousePosChanged; + RGFW_mousePosCallback(win, win->event.point); + + if (win->src.winArgs & RGFW_MOUSE_LEFT) { + win->src.winArgs ^= RGFW_MOUSE_LEFT; + win->event.type = RGFW_mouseEnter; + RGFW_mouseNotifyCallBack(win, win->event.point, 1); + } + break; case WM_LBUTTONDOWN: @@ -4744,18 +4400,21 @@ static HMODULE wglinstance = NULL; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 1; win->event.type = RGFW_mouseButtonPressed; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); break; case WM_RBUTTONDOWN: win->event.button = RGFW_mouseRight; win->event.type = RGFW_mouseButtonPressed; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 1; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); break; case WM_MBUTTONDOWN: win->event.button = RGFW_mouseMiddle; win->event.type = RGFW_mouseButtonPressed; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 1; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); break; case WM_MOUSEWHEEL: @@ -4770,6 +4429,7 @@ static HMODULE wglinstance = NULL; win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA; win->event.type = RGFW_mouseButtonPressed; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); break; case WM_LBUTTONUP: @@ -4779,6 +4439,7 @@ static HMODULE wglinstance = NULL; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 0; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); break; case WM_RBUTTONUP: win->event.button = RGFW_mouseRight; @@ -4786,6 +4447,7 @@ static HMODULE wglinstance = NULL; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 0; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); break; case WM_MBUTTONUP: win->event.button = RGFW_mouseMiddle; @@ -4793,29 +4455,17 @@ static HMODULE wglinstance = NULL; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 0; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); break; /* much of this event is source from glfw */ - case WM_DROPFILES: { + case WM_DROPFILES: { + win->event.type = RGFW_dnd_init; - if (win->event.droppedFilesCount) { - u32 i; - for (i = 0; i < win->event.droppedFilesCount; i++) - win->event.droppedFiles[i][0] = '\0'; - } - - win->event.droppedFilesCount = 0; - - win->event.type = RGFW_dnd; - - HDROP drop = (HDROP) msg.wParam; + drop = (HDROP) msg.wParam; POINT pt; - u32 i; - - win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0); - //win->event.droppedFiles = (char**)RGFW_CALLOC(win->event.droppedFilesCount, sizeof(char*)); /* Move the mouse to the position of the drop */ DragQueryPoint(drop, &pt); @@ -4823,19 +4473,9 @@ static HMODULE wglinstance = NULL; win->event.point.x = pt.x; win->event.point.y = pt.y; - for (i = 0; i < win->event.droppedFilesCount; i++) { - const UINT length = DragQueryFileW(drop, i, NULL, 0); - WCHAR* buffer = (WCHAR*) RGFW_CALLOC((size_t) length + 1, sizeof(WCHAR)); - - DragQueryFileW(drop, i, buffer, length + 1); - strcpy(win->event.droppedFiles[i], createUTF8FromWideStringWin32(buffer)); - - RGFW_FREE(buffer); - } - - DragFinish(drop); + RGFW_dndInitCallback(win, win->event.point); } - break; + break; case WM_GETMINMAXINFO: { if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0) @@ -4860,18 +4500,10 @@ static HMODULE wglinstance = NULL; else win->event.type = 0; - win->event.lockState = 0; - - if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0) - win->event.lockState |= RGFW_CAPSLOCK; - if ((GetKeyState(VK_NUMLOCK) & 0x0001) != 0) - win->event.lockState |= RGFW_NUMLOCK; - if ((GetKeyState(VK_SCROLL) & 0x0001) != 0) - win->event.lockState |= 3; - - - if (!IsWindow(win->src.window)) + if (!IsWindow(win->src.window)) { win->event.type = RGFW_quit; + RGFW_windowQuitCallback(win); + } if (win->event.type) return &win->event; @@ -4921,7 +4553,8 @@ static HMODULE wglinstance = NULL; info->iIndex++; return TRUE; } - + + #ifndef RGFW_NO_MONITOR RGFW_monitor win32CreateMonitor(HMONITOR src) { RGFW_monitor monitor; MONITORINFO monitorInfo; @@ -4943,7 +4576,7 @@ static HMODULE wglinstance = NULL; for (deviceIndex = 0; EnumDisplayDevicesA(0, (DWORD) deviceIndex, &dd, 0); deviceIndex++) { char* deviceName = dd.DeviceName; if (EnumDisplayDevicesA(deviceName, info.iIndex, &dd, 0)) { - strcpy(monitor.name, dd.DeviceString); /* copy the monitor's name */ + strncpy(monitor.name, dd.DeviceString, 128); /* copy the monitor's name */ break; } } @@ -4975,7 +4608,10 @@ static HMODULE wglinstance = NULL; return monitor; } + #endif /* RGFW_NO_MONITOR */ + + #ifndef RGFW_NO_MONITOR RGFW_monitor RGFW_monitors[6]; BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { RGFW_UNUSED(hdcMonitor) @@ -5008,6 +4644,7 @@ static HMODULE wglinstance = NULL; HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY); return win32CreateMonitor(src); } + #endif HICON RGFW_loadHandleImage(RGFW_window* win, u8* src, RGFW_area a, BOOL icon) { assert(win != NULL); @@ -5087,10 +4724,8 @@ static HMODULE wglinstance = NULL; if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u32))) return; - - mouse = RGFW_mouseIconSrc[mouse]; - char* icon = MAKEINTRESOURCEA(mouse); + char* icon = MAKEINTRESOURCEA(RGFW_mouseIconSrc[mouse]); SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon)); SetCursor(LoadCursorA(NULL, icon)); @@ -5107,21 +4742,6 @@ static HMODULE wglinstance = NULL; void RGFW_window_close(RGFW_window* win) { assert(win != NULL); -#ifdef RGFW_VULKAN - for (u32 i = 0; i < win->src.image_count; i++) { - vkDestroyFramebuffer(RGFW_vulkan_info.device, RGFW_vulkan_info.framebuffers[i], NULL); - } - - for (u32 i = 0; i < win->src.image_count; i++) { - vkDestroyImageView(RGFW_vulkan_info.device, win->src.swapchain_image_views[i], NULL); - } - - vkDestroySwapchainKHR(RGFW_vulkan_info.device, win->src.swapchain, NULL); - vkDestroySurfaceKHR(RGFW_vulkan_info.instance, win->src.rSurf, NULL); - RGFW_FREE(win->src.swapchain_image_views); - RGFW_FREE(win->src.swapchain_images); -#endif - #ifdef RGFW_EGL RGFW_closeEGL(win); #endif @@ -5213,9 +4833,45 @@ static HMODULE wglinstance = NULL; SetWindowTextA(win->src.window, name); } + /* sourced from GLFW */ + #ifndef RGFW_NO_PASSTHROUGH + void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { + assert(win != NULL); + + COLORREF key = 0; + BYTE alpha = 0; + DWORD flags = 0; + DWORD exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE); + + if (exStyle & WS_EX_LAYERED) + GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags); + + if (passthrough) + exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED); + else + { + exStyle &= ~WS_EX_TRANSPARENT; + // NOTE: Window opacity also needs the layered window style so do not + // remove it if the window is alpha blended + if (exStyle & WS_EX_LAYERED) + { + if (!(flags & LWA_ALPHA)) + exStyle &= ~WS_EX_LAYERED; + } + } + + SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle); + + if (passthrough) { + SetLayeredWindowAttributes(win->src.window, key, alpha, flags); + } + } + #endif + /* much of this function is sourced from GLFW */ void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) { assert(win != NULL); + #ifndef RGFW_WIN95 RGFW_UNUSED(channels) HICON handle = RGFW_loadHandleImage(win, src, a, TRUE); @@ -5223,6 +4879,11 @@ static HMODULE wglinstance = NULL; SetClassLongPtrA(win->src.window, GCLP_HICON, (LPARAM) handle); DestroyIcon(handle); + #else + RGFW_UNUSED(src) + RGFW_UNUSED(a) + RGFW_UNUSED(channels) + #endif } char* RGFW_readClipboard(size_t* size) { @@ -5254,6 +4915,8 @@ static HMODULE wglinstance = NULL; if (size != NULL) *size = textLen + 1; + + text[textLen] = '\0'; } /* Release the clipboard data */ @@ -5311,6 +4974,76 @@ static HMODULE wglinstance = NULL; SetCursorPos(p.x, p.y); } + #ifdef RGFW_OPENGL + void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + assert(win != NULL); + wglMakeCurrent(win->src.hdc, (HGLRC) win->src.rSurf); + } + #endif + + #ifndef RGFW_EGL + void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + assert(win != NULL); + + #if defined(RGFW_OPENGL) + typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); + static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; + static void* loadSwapFunc = (void*) 1; + + if (loadSwapFunc == NULL) { + fprintf(stderr, "wglSwapIntervalEXT not supported\n"); + win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; + return; + } + + if (wglSwapIntervalEXT == NULL) { + loadSwapFunc = (void*) wglGetProcAddress("wglSwapIntervalEXT"); + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) loadSwapFunc; + } + + if (wglSwapIntervalEXT(swapInterval) == FALSE) + fprintf(stderr, "Failed to set swap interval\n"); + #endif + + win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; + + } + #endif + + void RGFW_window_swapBuffers(RGFW_window* win) { + assert(win != NULL); + + RGFW_window_makeCurrent(win); + + /* clear the window*/ + + if (!(win->src.winArgs & RGFW_NO_CPU_RENDER)) { +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + #ifdef RGFW_OSMESA + RGFW_OSMesa_reorganize(); + #endif + + HGDIOBJ oldbmp = SelectObject(win->src.hdcMem, win->src.bitmap); + BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY); + SelectObject(win->src.hdcMem, oldbmp); +#endif + } + + if (!(win->src.winArgs & RGFW_NO_GPU_RENDER)) { + #ifdef RGFW_EGL + eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); + #elif defined(RGFW_OPENGL) + SwapBuffers(win->src.hdc); + #endif + + #if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) + win->src.swapchain->lpVtbl->Present(win->src.swapchain, 0, 0); + #endif + } + + RGFW_window_checkFPS(win); + } + char* createUTF8FromWideStringWin32(const WCHAR* source) { char* target; i32 size; @@ -5330,15 +5063,395 @@ static HMODULE wglinstance = NULL; return target; } + u64 RGFW_getTimeNS(void) { + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + + return (u64) (counter.QuadPart * 1e9 / frequency.QuadPart); + } + + u64 RGFW_getTime(void) { + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return (u64) (counter.QuadPart / (double) frequency.QuadPart); + } + + void RGFW_sleep(u64 ms) { + Sleep(ms); + } + #ifndef RGFW_NO_THREADS RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { return CreateThread(NULL, 0, ptr, args, 0, NULL); } void RGFW_cancelThread(RGFW_thread thread) { CloseHandle((HANDLE) thread); } void RGFW_joinThread(RGFW_thread thread) { WaitForSingleObject((HANDLE) thread, INFINITE); } void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { SetThreadPriority((HANDLE) thread, priority); } #endif -#endif +#endif /* RGFW_WINDOWS */ + +/* + End of Windows defines +*/ + + + +/* + + Start of MacOS defines + + +*/ #if defined(RGFW_MACOS) + /* + based on silicon.h + start of cocoa wrapper + */ + +#include +#include +#include +#include +#include + + typedef CGRect NSRect; + typedef CGPoint NSPoint; + typedef CGSize NSSize; + + typedef void NSBitmapImageRep; + typedef void NSCursor; + typedef void NSDraggingInfo; + typedef void NSWindow; + typedef void NSApplication; + typedef void NSScreen; + typedef void NSEvent; + typedef void NSString; + typedef void NSOpenGLContext; + typedef void NSPasteboard; + typedef void NSColor; + typedef void NSArray; + typedef void NSImageRep; + typedef void NSImage; + typedef void NSOpenGLView; + + + typedef const char* NSPasteboardType; + typedef unsigned long NSUInteger; + typedef long NSInteger; + typedef NSInteger NSModalResponse; + +#ifdef __arm64__ + /* ARM just uses objc_msgSend */ +#define abi_objc_msgSend_stret objc_msgSend +#define abi_objc_msgSend_fpret objc_msgSend +#else /* __i386__ */ + /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */ +#define abi_objc_msgSend_stret objc_msgSend_stret +#define abi_objc_msgSend_fpret objc_msgSend_fpret +#endif + +#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) +#define objc_msgSend_bool ((BOOL (*)(id, SEL))objc_msgSend) +#define objc_msgSend_void ((void (*)(id, SEL))objc_msgSend) +#define objc_msgSend_void_id ((void (*)(id, SEL, id))objc_msgSend) +#define objc_msgSend_uint ((NSUInteger (*)(id, SEL))objc_msgSend) +#define objc_msgSend_void_bool ((void (*)(id, SEL, BOOL))objc_msgSend) +#define objc_msgSend_void_SEL ((void (*)(id, SEL, SEL))objc_msgSend) +#define objc_msgSend_id ((id (*)(id, SEL))objc_msgSend) + + void NSRelease(id obj) { + objc_msgSend_void(obj, sel_registerName("release")); + } + + #define release NSRelease + + NSString* NSString_stringWithUTF8String(const char* str) { + return ((id(*)(id, SEL, const char*))objc_msgSend) + ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str); + } + + const char* NSString_to_char(NSString* str) { + return ((const char* (*)(id, SEL)) objc_msgSend) (str, sel_registerName("UTF8String")); + } + + void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) { + Class selected_class; + + if (strcmp(class_name, "NSView") == 0) { + selected_class = objc_getClass("ViewClass"); + } else if (strcmp(class_name, "NSWindow") == 0) { + selected_class = objc_getClass("WindowClass"); + } else { + selected_class = objc_getClass(class_name); + } + + class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0); + } + + /* Header for the array. */ + typedef struct siArrayHeader { + size_t count; + /* TODO(EimaMei): Add a `type_width` later on. */ + } siArrayHeader; + + /* Gets the header of the siArray. */ +#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1) + + void* si_array_init_reserve(size_t sizeof_element, size_t count) { + siArrayHeader* ptr = malloc(sizeof(siArrayHeader) + (sizeof_element * count)); + void* array = ptr + sizeof(siArrayHeader); + + siArrayHeader* header = SI_ARRAY_HEADER(array); + header->count = count; + + return array; + } + +#define si_array_len(array) (SI_ARRAY_HEADER(array)->count) +#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", function) + /* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/ +#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", function) + + unsigned char* NSBitmapImageRep_bitmapData(NSBitmapImageRep* imageRep) { + return ((unsigned char* (*)(id, SEL))objc_msgSend) + (imageRep, sel_registerName("bitmapData")); + } + +#define NS_ENUM(type, name) type name; enum + + typedef NS_ENUM(NSUInteger, NSBitmapFormat) { + NSBitmapFormatAlphaFirst = 1 << 0, // 0 means is alpha last (RGBA, CMYKA, etc.) + NSBitmapFormatAlphaNonpremultiplied = 1 << 1, // 0 means is premultiplied + NSBitmapFormatFloatingPointSamples = 1 << 2, // 0 is integer + + NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8), + NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9), + NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10), + NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11) + }; + + NSBitmapImageRep* NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) { + void* func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:"); + + return (NSBitmapImageRep*) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, const char*, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend) + (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits); + } + + NSColor* NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) { + void* nsclass = objc_getClass("NSColor"); + void* func = sel_registerName("colorWithSRGBRed:green:blue:alpha:"); + return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend) + (nsclass, func, red, green, blue, alpha); + } + + NSCursor* NSCursor_initWithImage(NSImage* newImage, NSPoint aPoint) { + void* func = sel_registerName("initWithImage:hotSpot:"); + void* nsclass = objc_getClass("NSCursor"); + + return (NSCursor*) ((id(*)(id, SEL, id, NSPoint))objc_msgSend) + (NSAlloc(nsclass), func, newImage, aPoint); + } + + void NSImage_addRepresentation(NSImage* image, NSImageRep* imageRep) { + void* func = sel_registerName("addRepresentation:"); + objc_msgSend_void_id(image, func, imageRep); + } + + NSImage* NSImage_initWithSize(NSSize size) { + void* func = sel_registerName("initWithSize:"); + return ((id(*)(id, SEL, NSSize))objc_msgSend) + (NSAlloc((id)objc_getClass("NSImage")), func, size); + } +#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers)) + typedef NS_ENUM(NSInteger, NSOpenGLContextParameter) { + NSOpenGLContextParameterSwapInterval NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ + NSOpenGLContextParameterSurfaceOrder NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ + NSOpenGLContextParameterSurfaceOpacity NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ + NSOpenGLContextParameterSurfaceBackingSize NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params. Width/height of surface backing size */ + NSOpenGLContextParameterReclaimResources NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params. */ + NSOpenGLContextParameterCurrentRendererID NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param. Retrieves the current renderer ID */ + NSOpenGLContextParameterGPUVertexProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param. Currently processing vertices with GPU (get) */ + NSOpenGLContextParameterGPUFragmentProcessing NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param. Currently processing fragments with GPU (get) */ + NSOpenGLContextParameterHasDrawable NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param. Boolean returned if drawable is attached */ + NSOpenGLContextParameterMPSwapsInFlight NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param. Max number of swaps queued by the MP GL engine */ + + NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */ + NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */ + NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */ + NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */ + NSOpenGLContextParameterSurfaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */ + }; + + + void NSOpenGLContext_setValues(NSOpenGLContext* context, const int* vals, NSOpenGLContextParameter param) { + void* func = sel_registerName("setValues:forParameter:"); + ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend) + (context, func, vals, param); + } + + void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) { + void* func = sel_registerName("initWithAttributes:"); + return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend) + (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), func, attribs); + } + + NSOpenGLView* NSOpenGLView_initWithFrame(NSRect frameRect, uint32_t* format) { + void* func = sel_registerName("initWithFrame:pixelFormat:"); + return (NSOpenGLView*) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend) + (NSAlloc((id)objc_getClass("NSOpenGLView")), func, frameRect, format); + } + + void NSCursor_performSelector(NSCursor* cursor, void* selector) { + void* func = sel_registerName("performSelector:"); + objc_msgSend_void_SEL(cursor, func, selector); + } + + NSPasteboard* NSPasteboard_generalPasteboard(void) { + return (NSPasteboard*) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard")); + } + + NSString** cstrToNSStringArray(char** strs, size_t len) { + static NSString* nstrs[6]; + size_t i; + for (i = 0; i < len; i++) + nstrs[i] = NSString_stringWithUTF8String(strs[i]); + + return nstrs; + } + + const char* NSPasteboard_stringForType(NSPasteboard* pasteboard, NSPasteboardType dataType) { + void* func = sel_registerName("stringForType:"); + return (const char*) NSString_to_char(((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, func, NSString_stringWithUTF8String(dataType))); + } + + NSArray* c_array_to_NSArray(void* array, size_t len) { + SEL func = sel_registerName("initWithObjects:count:"); + void* nsclass = objc_getClass("NSArray"); + return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) + (NSAlloc(nsclass), func, array, len); + } + + void NSregisterForDraggedTypes(void* view, NSPasteboardType* newTypes, size_t len) { + NSString** ntypes = cstrToNSStringArray((char**)newTypes, len); + + NSArray* array = c_array_to_NSArray(ntypes, len); + objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array); + NSRelease(array); + } + + NSInteger NSPasteBoard_declareTypes(NSPasteboard* pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) { + NSString** ntypes = cstrToNSStringArray((char**)newTypes, len); + + void* func = sel_registerName("declareTypes:owner:"); + + NSArray* array = c_array_to_NSArray(ntypes, len); + + NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend) + (pasteboard, func, array, owner); + NSRelease(array); + + return output; + } + + bool NSPasteBoard_setString(NSPasteboard* pasteboard, const char* stringToWrite, NSPasteboardType dataType) { + void* func = sel_registerName("setString:forType:"); + return ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend) + (pasteboard, func, NSString_stringWithUTF8String(stringToWrite), NSString_stringWithUTF8String(dataType)); + } + + void NSRetain(id obj) { objc_msgSend_void(obj, sel_registerName("retain")); } + + typedef enum NSApplicationActivationPolicy { + NSApplicationActivationPolicyRegular, + NSApplicationActivationPolicyAccessory, + NSApplicationActivationPolicyProhibited + } NSApplicationActivationPolicy; + + typedef NS_ENUM(u32, NSBackingStoreType) { + NSBackingStoreRetained = 0, + NSBackingStoreNonretained = 1, + NSBackingStoreBuffered = 2 + }; + + typedef NS_ENUM(u32, NSWindowStyleMask) { + NSWindowStyleMaskBorderless = 0, + NSWindowStyleMaskTitled = 1 << 0, + NSWindowStyleMaskClosable = 1 << 1, + NSWindowStyleMaskMiniaturizable = 1 << 2, + NSWindowStyleMaskResizable = 1 << 3, + NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */ + NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12, + NSWindowStyleMaskFullScreen = 1 << 14, + NSWindowStyleMaskFullSizeContentView = 1 << 15, + NSWindowStyleMaskUtilityWindow = 1 << 4, + NSWindowStyleMaskDocModalWindow = 1 << 6, + NSWindowStyleMaskNonactivatingPanel = 1 << 7, + NSWindowStyleMaskHUDWindow = 1 << 13 + }; + + typedef const char* NSPasteboardType; + NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPboardType + + + + typedef NS_ENUM(i32, NSDragOperation) { + NSDragOperationNone = 0, + NSDragOperationCopy = 1, + NSDragOperationLink = 2, + NSDragOperationGeneric = 4, + NSDragOperationPrivate = 8, + NSDragOperationMove = 16, + NSDragOperationDelete = 32, + NSDragOperationEvery = ULONG_MAX, + + //NSDragOperationAll_Obsolete API_DEPRECATED("", macos(10.0,10.10)) = 15, // Use NSDragOperationEvery + //NSDragOperationAll API_DEPRECATED("", macos(10.0,10.10)) = NSDragOperationAll_Obsolete, // Use NSDragOperationEvery + }; + + void* NSArray_objectAtIndex(NSArray* array, NSUInteger index) { + void* func = sel_registerName("objectAtIndex:"); + return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index); + } + + const char** NSPasteboard_readObjectsForClasses(NSPasteboard* pasteboard, Class* classArray, size_t len, void* options) { + void* func = sel_registerName("readObjectsForClasses:options:"); + + NSArray* array = c_array_to_NSArray(classArray, len); + + NSArray* output = (NSArray*) ((id(*)(id, SEL, id, void*))objc_msgSend) + (pasteboard, func, array, options); + + NSRelease(array); + NSUInteger count = ((NSUInteger(*)(id, SEL))objc_msgSend)(output, sel_registerName("count")); + + const char** res = si_array_init_reserve(sizeof(const char*), count); + + void* path_func = sel_registerName("path"); + + for (NSUInteger i = 0; i < count; i++) { + void* url = NSArray_objectAtIndex(output, i); + NSString* url_str = ((id(*)(id, SEL))objc_msgSend)(url, path_func); + res[i] = NSString_to_char(url_str); + } + + return res; + } + + void* NSWindow_contentView(NSWindow* window) { + void* func = sel_registerName("contentView"); + return objc_msgSend_id(window, func); + } + + /* + End of cocoa wrapper + */ + + char* RGFW_mouseIconSrc[] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"}; void* RGFWnsglFramework = NULL; @@ -5362,20 +5475,18 @@ static HMODULE wglinstance = NULL; return kCVReturnSuccess; } - RGFW_window* RGFW_windows[10]; - u32 RGFW_windows_size = 0; - id NSWindow_delegate(RGFW_window* win) { return (id) objc_msgSend_id(win->src.window, sel_registerName("delegate")); } u32 RGFW_OnClose(void* self) { - u32 i; - for (i = 0; i < RGFW_windows_size; i++) - if (RGFW_windows[i] && NSWindow_delegate(RGFW_windows[i]) == self) { - RGFW_windows[i]->event.type = RGFW_quit; - return true; - } + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void*)&win); + if (win == NULL) + return true; + + win->event.type = RGFW_quit; + RGFW_windowQuitCallback(win); return true; } @@ -5386,13 +5497,44 @@ static HMODULE wglinstance = NULL; NSDragOperation draggingEntered(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); + + printf("hi\n"); return NSDragOperationCopy; } NSDragOperation draggingUpdated(id self, SEL sel, id sender) { - RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); + RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void*)&win); + if (win == NULL) + return true; + + if (!(win->src.winArgs & RGFW_ALLOW_DND)) { + return false; + } + + win->event.type = RGFW_dnd_init; + win->src.dndPassed = 0; + + NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); + + win->event.point = RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y)); + RGFW_dndInitCallback(win, win->event.point); + return NSDragOperationCopy; } - bool prepareForDragOperation(void) { return true; } + bool prepareForDragOperation(id self) { + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void*)&win); + if (win == NULL) + return true; + + if (!(win->src.winArgs & RGFW_ALLOW_DND)) { + return false; + } + + return true; + } void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); return; } @@ -5400,16 +5542,15 @@ static HMODULE wglinstance = NULL; bool performDragOperation(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); - NSWindow* window = objc_msgSend_id(sender, sel_registerName("draggingDestinationWindow")); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void*)&win); + if (win == NULL) + return true; + + //NSWindow* window = objc_msgSend_id(sender, sel_registerName("draggingDestinationWindow")); u32 i; bool found = 0; - for (i = 0; i < RGFW_windows_size; i++) - if (RGFW_windows[i]->src.window == window) { - found = 1; - break; - } - if (!found) i = 0; @@ -5418,20 +5559,23 @@ static HMODULE wglinstance = NULL; char** droppedFiles = (char**) NSPasteboard_readObjectsForClasses(pasteBoard, array, 1, NULL); - RGFW_windows[i]->event.droppedFilesCount = si_array_len(droppedFiles); + win->event.droppedFilesCount = si_array_len(droppedFiles); u32 y; - for (y = 0; y < RGFW_windows[i]->event.droppedFilesCount; y++) - strcpy(RGFW_windows[i]->event.droppedFiles[y], droppedFiles[y]); + for (y = 0; y < win->event.droppedFilesCount; y++) { + strncpy(win->event.droppedFiles[y], droppedFiles[y], RGFW_MAX_PATH); - RGFW_windows[i]->event.type = RGFW_dnd; - RGFW_windows[i]->src.dndPassed = 0; + win->event.droppedFiles[y][RGFW_MAX_PATH - 1] = '\0'; + } + + win->event.type = RGFW_dnd; + win->src.dndPassed = 0; NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); + win->event.point = RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y)); - RGFW_windows[i]->event.point.x = (i32)p.x; - RGFW_windows[i]->event.point.x = (i32)p.y; + RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); return true; } @@ -5468,41 +5612,62 @@ static HMODULE wglinstance = NULL; NSSize RGFW__osxWindowResize(void* self, SEL sel, NSSize frameSize) { RGFW_UNUSED(sel); - u32 i; - for (i = 0; i < RGFW_windows_size; i++) { - if (RGFW_windows[i] && NSWindow_delegate(RGFW_windows[i]) == self) { - RGFW_windows[i]->r.w = frameSize.width; - RGFW_windows[i]->r.h = frameSize.height; - RGFW_windows[i]->event.type = RGFW_windowResized; - - return frameSize; - } - } - + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void*)&win); + if (win == NULL) + return frameSize; + + win->r.w = frameSize.width; + win->r.h = frameSize.height; + win->event.type = RGFW_windowResized; + RGFW_windowResizeCallback(win, win->r); return frameSize; } void RGFW__osxWindowMove(void* self, SEL sel) { RGFW_UNUSED(sel); - u32 i; - for (i = 0; i < RGFW_windows_size; i++) { - if (RGFW_windows[i] && NSWindow_delegate(RGFW_windows[i]) == self) { - NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)(RGFW_windows[i]->src.window, sel_registerName("frame")); - RGFW_windows[i]->r.x = (i32) frame.origin.x; - RGFW_windows[i]->r.y = (i32) frame.origin.y; + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void*)&win); + if (win == NULL) + return; + + NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)(win->src.window, sel_registerName("frame")); + win->r.x = (i32) frame.origin.x; + win->r.y = (i32) frame.origin.y; - RGFW_windows[i]->event.type = RGFW_windowMoved; - return; - } - } + win->event.type = RGFW_windowMoved; + RGFW_windowMoveCallback(win, win->r); } - #ifdef __cplusplus - #define APPKIT_EXTERN extern "C" - #else - #define APPKIT_EXTERN extern - #endif + void RGFW__osxUpdateLayer(void* self, SEL sel) { + RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void*)&win); + if (win == NULL) + return; + + win->event.type = RGFW_windowRefresh; + RGFW_windowRefreshCallback(win); + } + + RGFWDEF void RGFW_init_buffer(RGFW_window* win); + void RGFW_init_buffer(RGFW_window* win) { + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) + RGFW_bufferSize = RGFW_getScreenSize(); + + win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); + + #ifdef RGFW_OSMESA + win->src.rSurf = OSMesaCreateContext(OSMESA_RGBA, NULL); + OSMesaMakeCurrent(win->src.rSurf, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h); + #endif + #else + RGFW_UNUSED(win); /* if buffer rendering is not being used */ + #endif + } NSPasteboardType const NSPasteboardTypeURL = "public.url"; NSPasteboardType const NSPasteboardTypeFileURL = "public.file-url"; @@ -5527,6 +5692,8 @@ static HMODULE wglinstance = NULL; } RGFW_window* win = RGFW_window_basic_init(rect, args); + + RGFW_window_setMouseDefault(win); NSRect windowRect; windowRect.origin.x = win->r.x; @@ -5554,6 +5721,7 @@ static HMODULE wglinstance = NULL; objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str); #ifdef RGFW_OPENGL + if ((args & RGFW_NO_INIT_API) == 0) { void* attrs = RGFW_initAttribs(args & RGFW_OPENGL_SOFTWARE); void* format = NSOpenGLPixelFormat_initWithAttributes(attrs); @@ -5568,17 +5736,17 @@ static HMODULE wglinstance = NULL; printf("Switching to software rendering\n"); } - win->src.view = NSOpenGLView_initWithFrame(NSMakeRect(0, 0, win->r.w, win->r.h), format); + win->src.view = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {win->r.w, win->r.h}}, format); objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL")); win->src.rSurf = objc_msgSend_id(win->src.view, sel_registerName("openGLContext")); - -#else - NSRect contentRect = NSMakeRect(0, 0, win->r.w, win->r.h); + } else +#endif + { + NSRect contentRect = (NSRect){{0, 0}, {win->r.w, win->r.h}}; win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend) (NSAlloc((id)objc_getClass("NSView")), sel_registerName("initWithFrame:"), contentRect); -#endif - + } void* contentView = NSWindow_contentView(win->src.window); objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true); @@ -5586,21 +5754,22 @@ static HMODULE wglinstance = NULL; objc_msgSend_void_id(win->src.window, sel_registerName("setContentView:"), win->src.view); #ifdef RGFW_OPENGL - objc_msgSend_void(win->src.rSurf, sel_registerName("makeCurrentContext")); + if ((args & RGFW_NO_INIT_API) == 0) + objc_msgSend_void(win->src.rSurf, sel_registerName("makeCurrentContext")); #endif if (args & RGFW_TRANSPARENT_WINDOW) { #ifdef RGFW_OPENGL + if ((args & RGFW_NO_INIT_API) == 0) { i32 opacity = 0; - NSOpenGLContext_setValues(win->src.rSurf, &opacity, 304); + #define NSOpenGLCPSurfaceOpacity 236 + NSOpenGLContext_setValues(win->src.rSurf, &opacity, NSOpenGLCPSurfaceOpacity); + } #endif objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false); objc_msgSend_void_id(win->src.window, sel_registerName("setBackgroundColor:"), NSColor_colorWithSRGB(0, 0, 0, 0)); - - ((void (*)(id, SEL, CGFloat))objc_msgSend) - (win->src.window, sel_registerName("setAlphaValue:"), 0x00); } win->src.display = CGMainDisplayID(); @@ -5610,12 +5779,10 @@ static HMODULE wglinstance = NULL; RGFW_init_buffer(win); -#ifdef RGFW_VULKAN - RGFW_initVulkan(win); -#endif - + #ifndef RGFW_NO_MONITOR if (args & RGFW_SCALE_TO_MONITOR) RGFW_window_scaleToMonitor(win); + #endif if (args & RGFW_HIDE_MOUSE) RGFW_window_showMouse(win, 0); @@ -5624,9 +5791,15 @@ static HMODULE wglinstance = NULL; NSMoveToResourceDir(); Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0); - + + class_addIvar( + delegateClass, "RGFW_window", + sizeof(RGFW_window*), rint(log2(sizeof(RGFW_window*))), + "L" + ); class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) RGFW__osxWindowResize, "{NSSize=ff}@:{NSSize=ff}"); + class_addMethod(delegateClass, sel_registerName("updateLayer:"), (IMP) RGFW__osxUpdateLayer, ""); class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, ""); class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, ""); class_addMethod(delegateClass, sel_registerName("draggingEntered:"), (IMP)draggingEntered, "l@:@"); @@ -5661,16 +5834,7 @@ static HMODULE wglinstance = NULL; objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow")); - NSApplication_finishLaunching(NSApp); - - RGFW_windows_size++; - - size_t i; - for (i = 0; i < RGFW_windows_size; i++) - if (!RGFW_windows[i]) { - RGFW_windows[i] = win; - break; - } + objc_msgSend_void(NSApp, sel_registerName("finishLaunching")); if (RGFW_root == NULL) RGFW_root = win; @@ -5681,6 +5845,19 @@ static HMODULE wglinstance = NULL; return win; } + void RGFW_window_setBorder(RGFW_window* win, u8 border) { + NSBackingStoreType storeType = NSWindowStyleMaskBorderless; + if (!border) { + storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + } + if (!(win->src.winArgs & RGFW_NO_RESIZE)) { + storeType |= NSWindowStyleMaskResizable; + } + + ((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)(win->src.window, sel_registerName("setStyleMask:"), storeType); + + objc_msgSend_void_bool(win->src.window, sel_registerName("setHasShadow:"), border); + } RGFW_area RGFW_getScreenSize(void) { static CGDirectDisplayID display = 0; @@ -5704,7 +5881,7 @@ static HMODULE wglinstance = NULL; RGFW_vector RGFW_window_getMousePoint(RGFW_window* win) { NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(win->src.window, sel_registerName("mouseLocationOutsideOfEventStream")); - return RGFW_VECTOR((u32) p.x, (u32) (p.y)); + return RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y)); } u32 RGFW_keysPressed[10]; /*10 keys at a time*/ @@ -5798,16 +5975,17 @@ static HMODULE wglinstance = NULL; NSEventModifierFlagShift = 1 << 17, NSEventModifierFlagControl = 1 << 18, NSEventModifierFlagOption = 1 << 19, - NSEventModifierFlagCommand = 1 << 20 + NSEventModifierFlagCommand = 1 << 20, + NSEventModifierFlagNumericPad = 1 << 21 } NSEventModifierFlags; RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) { assert(win != NULL); - + if (win->event.type == RGFW_quit) - return &win->event; + return NULL; - if (win->event.type == RGFW_dnd && win->src.dndPassed == 0) { + if ((win->event.type == RGFW_dnd || win->event.type == RGFW_dnd_init) && win->src.dndPassed == 0) { win->src.dndPassed = 1; return &win->event; } @@ -5816,7 +5994,7 @@ static HMODULE wglinstance = NULL; if (eventFunc == NULL) eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"); - if ((win->event.type == RGFW_windowMoved || win->event.type == RGFW_windowResized) && win->event.keyCode != 120) { + if ((win->event.type == RGFW_windowMoved || win->event.type == RGFW_windowResized || win->event.type == RGFW_windowRefresh) && win->event.keyCode != 120) { win->event.keyCode = 120; return &win->event; } @@ -5824,7 +6002,6 @@ static HMODULE wglinstance = NULL; NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend) (NSApp, eventFunc, ULONG_MAX, NULL, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); - if (e == NULL) return NULL; @@ -5844,29 +6021,32 @@ static HMODULE wglinstance = NULL; win->event.droppedFilesCount = 0; win->event.type = 0; - bool isKey = (bool) objc_msgSend_bool(win->src.window, sel_registerName("isKeyWindow")); - - if (win->event.inFocus != isKey) { - win->event.inFocus = isKey; - - if (win->event.inFocus) - win->event.type = RGFW_focusIn; - else - win->event.type = RGFW_focusOut; - - return &win->event; - } - switch (objc_msgSend_uint(e, sel_registerName("type"))) { + case NSEventTypeMouseEntered: { + win->event.type = RGFW_mouseEnter; + NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow")); + + win->event.point = RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y)); + RGFW_mouseNotifyCallBack(win, win->event.point, 1); + break; + } + + case NSEventTypeMouseExited: + win->event.type = RGFW_mouseLeave; + RGFW_mouseNotifyCallBack(win, win->event.point, 0); + break; + case NSEventTypeKeyDown: { u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); win->event.keyCode = RGFW_apiKeyCodeToRGFW(key); - RGFW_keyboard_prev[win->event.keyCode] = RGFW_keyboard[win->event.keyCode]; + RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current; win->event.type = RGFW_keyPressed; - win->event.keyName = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); + char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); + strncpy(win->event.keyName, str, 16); + RGFW_keyboard[win->event.keyCode].current = 1; - RGFW_keyboard[win->event.keyCode] = 1; + RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1); break; } @@ -5874,86 +6054,53 @@ static HMODULE wglinstance = NULL; u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode")); win->event.keyCode = RGFW_apiKeyCodeToRGFW(key);; - RGFW_keyboard_prev[win->event.keyCode] = RGFW_keyboard[win->event.keyCode]; + RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current; win->event.type = RGFW_keyReleased; - win->event.keyName = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); + char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters"))); + strncpy(win->event.keyName, str, 16); - RGFW_keyboard[win->event.keyCode] = 0; + RGFW_keyboard[win->event.keyCode].current = 0; + RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0); break; } case NSEventTypeFlagsChanged: { u32 flags = objc_msgSend_uint(e, sel_registerName("modifierFlags")); - memcpy(RGFW_keyboard_prev + RGFW_CapsLock, RGFW_keyboard + RGFW_CapsLock, 9); + RGFW_updateLockState(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255)); + + u8 i; + for (i = 0; i < 9; i++) + RGFW_keyboard[i + RGFW_CapsLock].prev = 0; + + for (i = 0; i < 5; i++) { + u32 shift = (1 << (i + 16)); + u32 key = i + RGFW_CapsLock; - if ((flags & NSEventModifierFlagCapsLock) && !RGFW_wasPressedI(win, RGFW_CapsLock)) { - RGFW_keyboard[RGFW_CapsLock] = 1; - win->event.type = RGFW_keyPressed; - win->event.keyCode = RGFW_apiKeyCodeToRGFW(57); - break; - } if (!(flags & NSEventModifierFlagCapsLock) && RGFW_wasPressedI(win, RGFW_CapsLock)) { - RGFW_keyboard[RGFW_CapsLock] = 0; - win->event.type = RGFW_keyReleased; - win->event.keyCode = RGFW_apiKeyCodeToRGFW(57); - break; + if ((flags & shift) && !RGFW_wasPressed(win, key)) { + RGFW_keyboard[key].current = 1; + + if (key != RGFW_CapsLock) + RGFW_keyboard[key+ 4].current = 1; + + win->event.type = RGFW_keyPressed; + win->event.keyCode = key; + break; + } + + if (!(flags & shift) && RGFW_wasPressed(win, key)) { + RGFW_keyboard[key].current = 0; + + if (key != RGFW_CapsLock) + RGFW_keyboard[key + 4].current = 0; + + win->event.type = RGFW_keyReleased; + win->event.keyCode = key; + break; + } } - if ((flags & NSEventModifierFlagOption) && !RGFW_wasPressedI(win, RGFW_AltL)) { - RGFW_keyboard[RGFW_AltL] = 1; - RGFW_keyboard[RGFW_AltR] = 1; - win->event.type = RGFW_keyPressed; - win->event.keyCode = RGFW_apiKeyCodeToRGFW(58); - break; - } if (!(flags & NSEventModifierFlagOption) && RGFW_wasPressedI(win, RGFW_AltL)) { - RGFW_keyboard[RGFW_AltL] = 0; - RGFW_keyboard[RGFW_AltR] = 0; - win->event.type = RGFW_keyReleased; - win->event.keyCode = RGFW_apiKeyCodeToRGFW(58); - break; - } - - if ((flags & NSEventModifierFlagControl) && !RGFW_wasPressedI(win, RGFW_ControlL)) { - RGFW_keyboard[RGFW_ControlL] = 1; - RGFW_keyboard[RGFW_ControlR] = 1; - win->event.type = RGFW_keyPressed; - win->event.keyCode = RGFW_apiKeyCodeToRGFW(59); - break; - } if (!(flags & NSEventModifierFlagControl) && RGFW_wasPressedI(win, RGFW_ControlL)) { - RGFW_keyboard[RGFW_ControlL] = 0; - RGFW_keyboard[RGFW_ControlR] = 0; - win->event.type = RGFW_keyReleased; - win->event.keyCode = 59; - break; - } - - if ((flags & NSEventModifierFlagCommand) && !RGFW_wasPressedI(win, RGFW_SuperL)) { - RGFW_keyboard[RGFW_SuperL] = 1; - RGFW_keyboard[RGFW_SuperR] = 1; - win->event.type = RGFW_keyPressed; - win->event.keyCode = RGFW_apiKeyCodeToRGFW(55); - break; - } if (!(flags & NSEventModifierFlagCommand) && RGFW_wasPressedI(win, RGFW_SuperL)) { - RGFW_keyboard[RGFW_SuperL] = 0; - RGFW_keyboard[RGFW_SuperR] = 0; - win->event.type = RGFW_keyReleased; - win->event.keyCode = RGFW_apiKeyCodeToRGFW(55); - break; - } - - if ((flags & NSEventModifierFlagShift) && !RGFW_wasPressedI(win, RGFW_ShiftL)) { - RGFW_keyboard[RGFW_ShiftL] = 1; - RGFW_keyboard[RGFW_ShiftR] = 1; - win->event.type = RGFW_keyPressed; - win->event.keyCode = RGFW_apiKeyCodeToRGFW(56); - break; - } if (!(flags & NSEventModifierFlagShift) && RGFW_wasPressedI(win, RGFW_ShiftL)) { - RGFW_keyboard[RGFW_ShiftL] = 0; - RGFW_keyboard[RGFW_ShiftR] = 0; - win->event.type = RGFW_keyReleased; - win->event.keyCode = RGFW_apiKeyCodeToRGFW(56); - break; - } + RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, win->event.type == RGFW_keyPressed); break; } @@ -5963,8 +6110,18 @@ static HMODULE wglinstance = NULL; case NSEventTypeMouseMoved: win->event.type = RGFW_mousePosChanged; NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow")); - win->event.point = RGFW_VECTOR((u32) p.x, (u32) (win->r.h - p.y)); + + if ((win->src.winArgs & RGFW_HOLD_MOUSE)) { + p.x = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaX")); + p.y = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY")); + + p.x = ((win->r.w / 2)) + p.x; + p.y = ((win->r.h / 2)) + p.y; + win->event.point = RGFW_VECTOR((u32) p.x, (u32) (p.y)); + } + + RGFW_mousePosCallback(win, win->event.point); break; case NSEventTypeLeftMouseDown: @@ -5972,6 +6129,7 @@ static HMODULE wglinstance = NULL; win->event.type = RGFW_mouseButtonPressed; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 1; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); break; case NSEventTypeOtherMouseDown: @@ -5979,6 +6137,7 @@ static HMODULE wglinstance = NULL; win->event.type = RGFW_mouseButtonPressed; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 1; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); break; case NSEventTypeRightMouseDown: @@ -5986,6 +6145,7 @@ static HMODULE wglinstance = NULL; win->event.type = RGFW_mouseButtonPressed; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 1; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); break; case NSEventTypeLeftMouseUp: @@ -5993,6 +6153,7 @@ static HMODULE wglinstance = NULL; win->event.type = RGFW_mouseButtonReleased; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 0; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); break; case NSEventTypeOtherMouseUp: @@ -6000,6 +6161,15 @@ static HMODULE wglinstance = NULL; RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; RGFW_mouseButtons[win->event.button] = 0; win->event.type = RGFW_mouseButtonReleased; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); + break; + + case NSEventTypeRightMouseUp: + win->event.button = RGFW_mouseRight; + RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; + RGFW_mouseButtons[win->event.button] = 0; + win->event.type = RGFW_mouseButtonReleased; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0); break; case NSEventTypeScrollWheel: { @@ -6017,15 +6187,10 @@ static HMODULE wglinstance = NULL; win->event.scroll = deltaY; - win->event.type = RGFW_mouseButtonReleased; + win->event.type = RGFW_mouseButtonPressed; + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1); break; } - case NSEventTypeRightMouseUp: - win->event.button = RGFW_mouseRight; - RGFW_mouseButtons_prev[win->event.button] = RGFW_mouseButtons[win->event.button]; - RGFW_mouseButtons[win->event.button] = 0; - win->event.type = RGFW_mouseButtonReleased; - break; default: break; @@ -6043,7 +6208,7 @@ static HMODULE wglinstance = NULL; win->r.x = v.x; win->r.y = v.y; ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) - (win->src.window, sel_registerName("setFrame:display:animate:"), NSMakeRect(win->r.x, win->r.y, win->r.w, win->r.h), true, true); + (win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true); } void RGFW_window_resize(RGFW_window* win, RGFW_area a) { @@ -6052,7 +6217,7 @@ static HMODULE wglinstance = NULL; win->r.w = a.w; win->r.h = a.h; ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) - (win->src.window, sel_registerName("setFrame:display:animate:"), NSMakeRect(win->r.x, win->r.y, win->r.w, win->r.h), true, true); + (win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true); } void RGFW_window_minimize(RGFW_window* win) { @@ -6074,14 +6239,26 @@ static HMODULE wglinstance = NULL; objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str); } + #ifndef RGFW_NO_PASSTHROUGH + void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { + objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough); + } + #endif + void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { + if (a.w == 0 && a.h == 0) + return; + ((void (*)(id, SEL, NSSize))objc_msgSend) - (win->src.window, sel_registerName("setMinSize:"), NSMakeSize(a.w, a.h)); + (win->src.window, sel_registerName("setMinSize:"), (NSSize){a.w, a.h}); } void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + if (a.w == 0 && a.h == 0) + return; + ((void (*)(id, SEL, NSSize))objc_msgSend) - (win->src.window, sel_registerName("setMaxSize:"), NSMakeSize(a.w, a.h)); + (win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h}); } void RGFW_window_setIcon(RGFW_window* win, u8* data, RGFW_area area, i32 channels) { @@ -6093,7 +6270,7 @@ static HMODULE wglinstance = NULL; memcpy(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * channels); // Add ze representation. - void* dock_image = NSImage_initWithSize(NSMakeSize(area.w, area.h)); + void* dock_image = NSImage_initWithSize((NSSize){area.w, area.h}); NSImage_addRepresentation(dock_image, (void*) representation); // Finally, set the dock image to it. @@ -6123,11 +6300,11 @@ static HMODULE wglinstance = NULL; memcpy(NSBitmapImageRep_bitmapData(representation), image, a.w * a.h * channels); // Add ze representation. - void* cursor_image = NSImage_initWithSize(NSMakeSize(a.w, a.h)); + void* cursor_image = NSImage_initWithSize((NSSize){a.w, a.h}); NSImage_addRepresentation(cursor_image, representation); // Finally, set the cursor image. - void* cursor = NSCursor_initWithImage(cursor_image, NSMakePoint(0, 0)); + void* cursor = NSCursor_initWithImage(cursor_image, (NSPoint){0.0, 0.0}); objc_msgSend_void(cursor, sel_registerName("set")); @@ -6166,11 +6343,15 @@ static HMODULE wglinstance = NULL; objc_msgSend_void(mouse, sel_registerName("set")); } + void RGFW_clipCursor(RGFW_rect r) { + CGWarpMouseCursorPosition(CGPointMake(r.x + (r.w / 2), r.y + (r.h / 2))); + CGAssociateMouseAndMouseCursorPosition((!r.x && !r.y && r.w && !r.h)); + } + void RGFW_window_moveMouse(RGFW_window* win, RGFW_vector v) { RGFW_UNUSED(win); - assert(win != NULL); - CGWarpMouseCursorPosition(CGPointMake(v.x, v.y)); + CGWarpMouseCursorPosition(CGPointMake(v.x, v.y)); } @@ -6252,19 +6433,23 @@ static HMODULE wglinstance = NULL; return RGFW_NSCreateMonitor(win->src.display); } -#ifdef __cplusplus -#define APPKIT_EXTERN extern "C" -#else -#define APPKIT_EXTERN extern -#endif - char* RGFW_readClipboard(size_t* size) { char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString); - size_t clip_len = strlen(clip); + + size_t clip_len = 1; + + if (clip != NULL) { + clip_len = strlen(clip) + 1; + } char* str = (char*)RGFW_MALLOC(sizeof(char) * clip_len); - strcpy(str, clip); + + if (clip != NULL) { + strncpy(str, clip, clip_len); + } + str[clip_len] = '\0'; + if (size != NULL) *size = clip_len; return str; @@ -6295,24 +6480,73 @@ static HMODULE wglinstance = NULL; return win->src.joystickCount - 1; } - void RGFW_window_close(RGFW_window* win) { + #ifdef RGFW_OPENGL + void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + assert(win != NULL); + objc_msgSend_void(win->src.rSurf, sel_registerName("makeCurrentContext")); + } + #endif + + #if !defined(RGFW_EGL) + void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + assert(win != NULL); + #if defined(RGFW_OPENGL) + + NSOpenGLContext_setValues(win->src.rSurf, &swapInterval, 222); + #endif + + win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; + } + #endif + + void RGFW_window_swapBuffers(RGFW_window* win) { assert(win != NULL); -#ifdef RGFW_VULKAN - for (int i = 0; i < win->src.image_count; i++) { - vkDestroyFramebuffer(RGFW_vulkan_info.device, RGFW_vulkan_info.framebuffers[i], NULL); - } + RGFW_window_makeCurrent(win); - for (int i = 0; i < win->src.image_count; i++) { - vkDestroyImageView(RGFW_vulkan_info.device, win->src.swapchain_image_views[i], NULL); - } + /* clear the window*/ - vkDestroySwapchainKHR(RGFW_vulkan_info.device, win->src.swapchain, NULL); - vkDestroySurfaceKHR(RGFW_vulkan_info.instance, win->src.rSurf, NULL); - RGFW_FREE(win->src.swapchain_image_views); - RGFW_FREE(win->src.swapchain_images); + if (!(win->src.winArgs & RGFW_NO_CPU_RENDER)) { +#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + #ifdef RGFW_OSMESA + RGFW_OSMesa_reorganize(); + #endif + + RGFW_area area = RGFW_bufferSize; + void* view = NSWindow_contentView(win->src.window); + void* layer = objc_msgSend_id(view, sel_registerName("layer")); + + ((void(*)(id, SEL, NSRect))objc_msgSend)(layer, + sel_registerName("setFrame:"), + (NSRect){{0, 0}, {win->r.w, win->r.h}}); + + NSBitmapImageRep* rep = NSBitmapImageRep_initWithBitmapData( + &win->buffer, win->r.w, win->r.h, 8, 4, true, false, + "NSDeviceRGBColorSpace", 0, + area.w * 4, 32 + ); + id image = NSAlloc((id)objc_getClass("NSImage")); + NSImage_addRepresentation(image, rep); + objc_msgSend_void_id(layer, sel_registerName("setContents:"), (id) image); + + release(image); + release(rep); #endif + } + if (!(win->src.winArgs & RGFW_NO_GPU_RENDER)) { + #ifdef RGFW_EGL + eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); + #elif defined(RGFW_OPENGL) + objc_msgSend_void(win->src.rSurf, sel_registerName("flushBuffer")); + #endif + } + + RGFW_window_checkFPS(win); + } + + void RGFW_window_close(RGFW_window* win) { + assert(win != NULL); release(win->src.view); #ifdef RGFW_ALLOC_DROPFILES @@ -6326,16 +6560,7 @@ static HMODULE wglinstance = NULL; } #endif - u32 i; - for (i = 0; i < RGFW_windows_size; i++) - if (RGFW_windows[i]->src.window == win->src.window) { - RGFW_windows[i] = NULL; - break; - } - - if (!i) { - RGFW_windows_size = 0; - + if (RGFW_root == win) { objc_msgSend_void_id(NSApp, sel_registerName("terminate:"), (id) win->src.window); NSApp = NULL; } @@ -6350,10 +6575,31 @@ static HMODULE wglinstance = NULL; RGFW_FREE(win); } -#endif + u64 RGFW_getTimeNS(void) { + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + mach_timebase_info(&timebase_info); + } + return mach_absolute_time() * timebase_info.numer / timebase_info.denom; + } + + u64 RGFW_getTime(void) { + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + mach_timebase_info(&timebase_info); + } + return (double) mach_absolute_time() * (double) timebase_info.numer / ((double) timebase_info.denom * 1e9); + } +#endif /* RGFW_MACOS */ + +/* + End of MaOS defines +*/ + +/* unix (macOS, linux) only stuff */ #if defined(RGFW_X11) || defined(RGFW_MACOS) - +/* unix threading */ #ifndef RGFW_NO_THREADS #include @@ -6370,340 +6616,16 @@ static HMODULE wglinstance = NULL; void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio(thread, priority); } #endif #endif -#endif - - void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - assert(win != NULL); - -#ifdef RGFW_OPENGL -#ifdef RGFW_X11 - glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.rSurf); -#endif -#ifdef RGFW_WINDOWS - wglMakeCurrent(win->src.hdc, (HGLRC) win->src.rSurf); -#endif -#if defined(RGFW_MACOS) - objc_msgSend_void(win->src.rSurf, sel_registerName("makeCurrentContext")); -#endif -#else -#ifdef RGFW_EGL - eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context); -#endif -#endif - - } - - void RGFW_window_makeCurrent(RGFW_window* win) { - assert(win != NULL); - -#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) - RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL); -#endif - -#ifdef RGFW_OPENGL - RGFW_window_makeCurrent_OpenGL(win); -#endif - } - - void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { - assert(win != NULL); - -#ifdef RGFW_OPENGL -#ifdef RGFW_X11 - ((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))((Display*) win->src.display, (Window) win->src.window, swapInterval); -#endif -#ifdef RGFW_WINDOWS - - typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); - static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; - static void* loadSwapFunc = (void*) 1; - - if (loadSwapFunc == NULL) { - fprintf(stderr, "wglSwapIntervalEXT not supported\n"); - win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; - return; - } - - if (wglSwapIntervalEXT == NULL) { - loadSwapFunc = (void*) wglGetProcAddress("wglSwapIntervalEXT"); - wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) loadSwapFunc; - } - - if (wglSwapIntervalEXT(swapInterval) == FALSE) - fprintf(stderr, "Failed to set swap interval\n"); - -#endif -#if defined(RGFW_MACOS) - NSOpenGLContext_setValues(win->src.rSurf, &swapInterval, 222); -#endif -#endif - -#ifdef RGFW_EGL - eglSwapInterval(win->src.EGL_display, swapInterval); -#endif - - win->fpsCap = (swapInterval == 1) ? 0 : swapInterval; - - } - - void RGFW_window_setGPURender(RGFW_window* win, i8 set) { - if (!set && !(win->src.winArgs & RGFW_NO_GPU_RENDER)) - win->src.winArgs |= RGFW_NO_GPU_RENDER; - - else if (set && win->src.winArgs & RGFW_NO_GPU_RENDER) - win->src.winArgs ^= RGFW_NO_GPU_RENDER; - } - - void RGFW_window_setCPURender(RGFW_window* win, i8 set) { - if (!set && !(win->src.winArgs & RGFW_NO_CPU_RENDER)) - win->src.winArgs |= RGFW_NO_CPU_RENDER; - - else if (set && win->src.winArgs & RGFW_NO_CPU_RENDER) - win->src.winArgs ^= RGFW_NO_CPU_RENDER; - } - - - void RGFW_window_swapBuffers(RGFW_window* win) { - assert(win != NULL); - - RGFW_window_makeCurrent(win); - - /* clear the window*/ - - if (!(win->src.winArgs & RGFW_NO_CPU_RENDER)) { -#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) -#ifdef RGFW_OSMESA - u8* row = (u8*) RGFW_MALLOC(win->r.w * 3); - - i32 half_height = win->r.h / 2; - i32 stride = win->r.w * 3; - - i32 y; - for (y = 0; y < half_height; ++y) { - i32 top_offset = y * stride; - i32 bottom_offset = (win->r.h - y - 1) * stride; - memcpy(row, win->buffer + top_offset, stride); - memcpy(win->buffer + top_offset, win->buffer + bottom_offset, stride); - memcpy(win->buffer + bottom_offset, row, stride); - } - - RGFW_FREE(row); -#endif - -#ifdef RGFW_X11 - RGFW_area area = RGFW_getScreenSize(); - -#ifndef RGFW_X11_DONT_CONVERT_BGR - win->src.bitmap->data = (char*) win->buffer; - u32 x, y; - for (y = 0; y < (u32)win->r.h; y++) { - for (x = 0; x < (u32)win->r.w; x++) { - u32 index = (y * 4 * area.w) + x * 4; - - u8 red = win->src.bitmap->data[index]; - win->src.bitmap->data[index] = win->buffer[index + 2]; - win->src.bitmap->data[index + 2] = red; - } - } -#endif - - XPutImage(win->src.display, (Window) win->src.window, XDefaultGC(win->src.display, XDefaultScreen(win->src.display)), win->src.bitmap, 0, 0, 0, 0, win->r.w, win->r.h); -#endif -#ifdef RGFW_WINDOWS - HGDIOBJ oldbmp = SelectObject(win->src.hdcMem, win->src.bitmap); - BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY); - SelectObject(win->src.hdcMem, oldbmp); -#endif -#if defined(RGFW_MACOS) - RGFW_area area = RGFW_getScreenSize(); - void* view = NSWindow_contentView(win->src.window); - void* layer = objc_msgSend_id(view, sel_registerName("layer")); - - ((void(*)(id, SEL, NSRect))objc_msgSend)(layer, - sel_registerName("setFrame:"), - NSMakeRect(0, 0, win->r.w, win->r.h)); - - NSBitmapImageRep* rep = NSBitmapImageRep_initWithBitmapData( - &win->buffer, win->r.w, win->r.h, 8, 4, true, false, - "NSDeviceRGBColorSpace", 0, - area.w * 4, 32 - ); - id image = NSAlloc((id)objc_getClass("NSImage")); - NSImage_addRepresentation(image, rep); - objc_msgSend_void_id(layer, sel_registerName("setContents:"), (id) image); - - release(image); - release(rep); -#endif -#endif - -#ifdef RGFW_VULKAN -#ifdef RGFW_PRINT_ERRORS - fprintf(stderr, "RGFW_window_swapBuffers %s\n", "RGFW_window_swapBuffers is not yet supported for Vulkan"); - RGFW_error = 1; -#endif -#endif - } - - if (!(win->src.winArgs & RGFW_NO_GPU_RENDER)) { - #ifdef RGFW_EGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #elif defined(RGFW_OPENGL) - #if defined(RGFW_X11) && defined(RGFW_OPENGL) - glXSwapBuffers((Display*) win->src.display, (Window) win->src.window); - #elif defined(RGFW_WINDOWS) - SwapBuffers(win->src.hdc); - #elif defined(RGFW_MACOS) - NSOpenGLContext_flushBuffer(win->src.rSurf); - #endif - #endif - - #if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX) - win->src.swapchain->lpVtbl->Present(win->src.swapchain, 0, 0); - #endif - } - - RGFW_window_checkFPS(win); - } - - void RGFW_window_maximize(RGFW_window* win) { - assert(win != NULL); - - RGFW_area screen = RGFW_getScreenSize(); - - RGFW_window_move(win, RGFW_VECTOR(0, 0)); - RGFW_window_resize(win, screen); - } - - u8 RGFW_window_shouldClose(RGFW_window* win) { - assert(win != NULL); - - /* || RGFW_isPressedI(win, RGFW_Escape) */ - return (win->event.type == RGFW_quit || RGFW_isPressedI(win, RGFW_Escape)); - } - - void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; } - - void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) { - RGFW_window_move(win, RGFW_VECTOR(m.rect.x + win->r.x, m.rect.y + win->r.y)); - } - - void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) { - if (!(win->src.winArgs & RGFW_HOLD_MOUSE)) { - #ifdef RGFW_WINDOWS - RECT rect = {win->r.x, win->r.y, win->r.x + win->r.w, win->r.y + win->r.h}; - ClipCursor(&rect); - #endif - } - - win->src.winArgs |= RGFW_HOLD_MOUSE; - - if (!area.w && !area.h) - area = RGFW_AREA(win->r.w / 2, win->r.h / 2); - - RGFW_window_moveMouse(win, RGFW_VECTOR(win->r.x + (area.w), win->r.y + (area.h))); - } - - void RGFW_window_mouseUnhold(RGFW_window* win) { - win->src.winArgs ^= RGFW_HOLD_MOUSE; - - #ifdef RGFW_WINDOWS - ClipCursor(NULL); - #endif - } - +/* unix sleep */ void RGFW_sleep(u64 ms) { -#ifndef RGFW_WINDOWS struct timespec time; time.tv_sec = 0; time.tv_nsec = ms * 1e+6; nanosleep(&time, NULL); -#else - Sleep(ms); -#endif - } - - void RGFW_window_checkFPS(RGFW_window* win) { - u64 deltaTime = RGFW_getTimeNS() - win->event.frameTime; - - u64 fps = round(1e+9 / deltaTime); - win->event.fps = fps; - - if (win->fpsCap && fps > win->fpsCap) { - u64 frameTimeNS = 1e+9 / win->fpsCap; - u64 sleepTimeMS = (frameTimeNS - deltaTime) / 1e6; - - if (sleepTimeMS > 0) { - RGFW_sleep(sleepTimeMS); - win->event.frameTime = 0; - } - } - - win->event.frameTime = RGFW_getTimeNS(); - - if (win->fpsCap) { - u64 deltaTime = RGFW_getTimeNS() - win->event.frameTime2; - - win->event.fps = round(1e+9 / deltaTime); - - win->event.frameTime2 = RGFW_getTimeNS(); - } - } - -#ifdef __APPLE__ -#include -#endif - - u64 RGFW_getTimeNS(void) { -#ifdef RGFW_WINDOWS - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - - return (u64) (counter.QuadPart * 1e9 / frequency.QuadPart); -#elif defined(__unix__) - struct timespec ts = { 0 }; - clock_gettime(1, &ts); - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - - return nanoSeconds; -#elif defined(__APPLE__) - static mach_timebase_info_data_t timebase_info; - if (timebase_info.denom == 0) { - mach_timebase_info(&timebase_info); - } - return mach_absolute_time() * timebase_info.numer / timebase_info.denom; -#endif - return 0; - } - - u64 RGFW_getTime(void) { -#ifdef RGFW_WINDOWS - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - return (u64) (counter.QuadPart / (double) frequency.QuadPart); -#elif defined(__unix__) - struct timespec ts = { 0 }; - clock_gettime(1, &ts); - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - - return (double)(nanoSeconds) * 1e-9; -#elif defined(__APPLE__) - static mach_timebase_info_data_t timebase_info; - if (timebase_info.denom == 0) { - mach_timebase_info(&timebase_info); - } - return (double) mach_absolute_time() * (double) timebase_info.numer / ((double) timebase_info.denom * 1e9); -#endif - return 0; } +#endif /* end of unix / mac stuff*/ #endif /*RGFW_IMPLEMENTATION*/ #ifdef __cplusplus diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 7dfe1f51c..5bd08217f 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -8,19 +8,17 @@ * - MacOS (Cocoa) * * LIMITATIONS: -* - Limitation 01 -* - Limitation 02 +* - TODO * * POSSIBLE IMPROVEMENTS: -* - Improvement 01 -* - Improvement 02 +* - TODO * * ADDITIONAL NOTES: * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_PLATFORM_CUSTOM_FLAG -* Custom flag for rcore on target platform -not used- +* #define RCORE_PLATFORM_RGFW +* Custom flag for rcore on target platform RGFW * * DEPENDENCIES: * - RGFW.h (main library): Windowing and inputs management @@ -244,7 +242,7 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) -{ +{ RGFW_window_maximize(platform.window); ToggleBorderlessWindowed(); } @@ -252,10 +250,9 @@ void ToggleFullscreen(void) // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - CORE.Window.flags & FLAG_WINDOW_UNDECORATED; - - if (platform.window != NULL) - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() after window creation not available on target platform"); + if (platform.window != NULL) { + RGFW_window_setBorder(platform.window, CORE.Window.flags & FLAG_WINDOW_UNDECORATED); + } } // Set window state: maximized, if resizable @@ -292,6 +289,7 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_WINDOW_RESIZABLE) { + printf("%i %i\n", platform.window->r.w, platform.window->r.h); RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h)); RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h)); } @@ -313,7 +311,7 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_WINDOW_UNFOCUSED) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_RGFW"); } if (flags & FLAG_WINDOW_TOPMOST) { @@ -325,7 +323,7 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_WINDOW_TRANSPARENT) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_RGFW"); + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT post window creation post window creation is not supported on PLATFORM_DESKTOP_RGFW"); } if (flags & FLAG_WINDOW_HIGHDPI) { @@ -333,7 +331,7 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) { - TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_RGFW"); + RGFW_window_setMousePassthrough(platform.window, flags & FLAG_WINDOW_MOUSE_PASSTHROUGH); } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { @@ -408,7 +406,7 @@ void ClearWindowState(unsigned int flags) } if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) { - //SDL_SetWindowGrab(platform.window, SDL_TRUE); + RGFW_window_setMousePassthrough(platform.window, flags & FLAG_WINDOW_MOUSE_PASSTHROUGH); TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_RGFW"); } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) @@ -566,16 +564,16 @@ int GetMonitorCount(void) // Get number of monitors int GetCurrentMonitor(void) { - int current = 0; RGFW_monitor *mons = RGFW_getMonitors(); RGFW_monitor mon = RGFW_window_getMonitor(platform.window); for (int i = 0; i < 6; i++) { - if ((mons[i].rect.x == mon.rect.x) && (mons[i].rect.y == mon.rect.y)) current = i; + if ((mons[i].rect.x == mon.rect.x) && (mons[i].rect.y == mon.rect.y)) + return i; } - return current; + return 0; } // Get selected monitor position @@ -760,6 +758,62 @@ void SetMouseCursor(int cursor) static KeyboardKey ConvertScancodeToKey(u32 keycode); +/* + TODO, try to make this better (RSGL uses this method too :I ) + sourced from RSGL obviously -> ColleagueRiley +*/ +char RSGL_keystrToChar(const char* str) { + if (str[1] == 0) + return str[0]; + + + static const char* map[] = { + "asciitilde", "`", + "grave", "~", + "exclam", "!", + "at", "@", + "numbersign", "#", + "dollar", "$", + "percent", "%%", + "asciicircum", "^", + "ampersand", "&", + "asterisk", "*", + "parenleft", "(", + "parenright", ")", + "underscore", "_", + "minus", "-", + "plus", "+", + "equal", "=", + "braceleft", "{", + "bracketleft", "[", + "bracketright", "]", + "braceright", "}", + "colon", ":", + "semicolon", ";", + "quotedbl", "\"", + "apostrophe", "'", + "bar", "|", + "backslash", "\'", + "less", "<", + "comma", ",", + "greater", ">", + "period", ".", + "question", "?", + "slash", "/", + "space", " ", + "Return", "\n", + "Enter", "\n", + "enter", "\n", + }; + + u8 i = 0; + for (i = 0; i < (sizeof(map) / sizeof(char*)); i += 2) + if (strcmp(map[i], str) == 0) + return *map[i + 1]; + + return '\0'; +} + // Register all input events void PollInputEvents(void) { @@ -924,7 +978,7 @@ void PollInputEvents(void) if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) { // Add character (codepoint) to the queue - CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = RGFW_keystrToChar(event->keyName); + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = RSGL_keystrToChar(event->keyName); CORE.Input.Keyboard.charPressedQueueCount++; } } break; From fa03246d0e6e4911cb9513065f07ed2014430b05 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 9 Jul 2024 09:21:57 +0200 Subject: [PATCH 38/40] REVIEWED: Code formatting to follow raylib conventions --- src/platforms/rcore_desktop_rgfw.c | 104 ++++++++++++++--------------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/src/platforms/rcore_desktop_rgfw.c b/src/platforms/rcore_desktop_rgfw.c index 5bd08217f..83430f511 100644 --- a/src/platforms/rcore_desktop_rgfw.c +++ b/src/platforms/rcore_desktop_rgfw.c @@ -47,56 +47,55 @@ **********************************************************************************************/ #ifdef GRAPHICS_API_OPENGL_ES2 -#define RGFW_OPENGL_ES2 + #define RGFW_OPENGL_ES2 #endif void ShowCursor(void); void CloseWindow(void); -#ifdef __linux__ -#define _INPUT_EVENT_CODES_H +#if defined(__linux__) + #define _INPUT_EVENT_CODES_H #endif #if defined(__unix__) || defined(__linux__) -#define _XTYPEDEF_FONT + #define _XTYPEDEF_FONT #endif #define RGFW_IMPLEMENTATION #if defined(__WIN32) || defined(__WIN64) -#define WIN32_LEAN_AND_MEAN -#define Rectangle rectangle_win32 -#define CloseWindow CloseWindow_win32 -#define ShowCursor __imp_ShowCursor -#define _APISETSTRING_ + #define WIN32_LEAN_AND_MEAN + #define Rectangle rectangle_win32 + #define CloseWindow CloseWindow_win32 + #define ShowCursor __imp_ShowCursor + #define _APISETSTRING_ #endif -#ifdef __APPLE__ -#define Point NSPOINT -#define Size NSSIZE +#if defined(__APPLE__) + #define Point NSPOINT + #define Size NSSIZE #endif -#ifdef _MSC_VER -__declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar); +#if defined(_MSC_VER) +__declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar); #endif #include "../external/RGFW.h" - - #if defined(__WIN32) || defined(__WIN64) -#undef DrawText -#undef ShowCursor -#undef CloseWindow -#undef Rectangle + #undef DrawText + #undef ShowCursor + #undef CloseWindow + #undef Rectangle #endif -#ifdef __APPLE__ -#undef Point -#undef Size +#if defined(__APPLE__) + #undef Point + #undef Size #endif #include +#include // Required for: strcmp() //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -110,7 +109,9 @@ typedef struct { //---------------------------------------------------------------------------------- extern CoreData CORE; // Global CORE state context -static PlatformData platform = { NULL }; // Platform specific +static PlatformData platform = { NULL }; // Platform specific + +static bool RGFW_disableCursor = false; static const unsigned short keyMappingRGFW[] = { [RGFW_KEY_NULL] = KEY_NULL, @@ -250,7 +251,8 @@ void ToggleFullscreen(void) // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - if (platform.window != NULL) { + if (platform.window != NULL) + { RGFW_window_setBorder(platform.window, CORE.Window.flags & FLAG_WINDOW_UNDECORATED); } } @@ -569,8 +571,7 @@ int GetCurrentMonitor(void) for (int i = 0; i < 6; i++) { - if ((mons[i].rect.x == mon.rect.x) && (mons[i].rect.y == mon.rect.y)) - return i; + if ((mons[i].rect.x == mon.rect.x) && (mons[i].rect.y == mon.rect.y)) return i; } return 0; @@ -672,8 +673,6 @@ void HideCursor(void) CORE.Input.Mouse.cursorHidden = true; } -bool RGFW_disableCursor = false; - // Enables cursor (unlock cursor) void EnableCursor(void) { @@ -690,6 +689,7 @@ void EnableCursor(void) void DisableCursor(void) { RGFW_disableCursor = true; + // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); @@ -727,7 +727,7 @@ void OpenURL(const char *url) if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { - // TODO: + // TODO: Open URL implementation } } @@ -758,16 +758,12 @@ void SetMouseCursor(int cursor) static KeyboardKey ConvertScancodeToKey(u32 keycode); -/* - TODO, try to make this better (RSGL uses this method too :I ) - sourced from RSGL obviously -> ColleagueRiley -*/ -char RSGL_keystrToChar(const char* str) { - if (str[1] == 0) - return str[0]; +// TODO: Review function to avoid duplicate with RSGL +char RSGL_keystrToChar(const char *str) +{ + if (str[1] == 0) return str[0]; - - static const char* map[] = { + static const char *map[] = { "asciitilde", "`", "grave", "~", "exclam", "!", @@ -806,10 +802,10 @@ char RSGL_keystrToChar(const char* str) { "enter", "\n", }; - u8 i = 0; - for (i = 0; i < (sizeof(map) / sizeof(char*)); i += 2) - if (strcmp(map[i], str) == 0) - return *map[i + 1]; + for (unsigned char i = 0; i < (sizeof(map)/sizeof(char *)); i += 2) + { + if (strcmp(map[i], str) == 0) return *map[i + 1]; + } return '\0'; } @@ -866,25 +862,25 @@ void PollInputEvents(void) } // Register previous mouse states - for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) - CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; // Poll input events for current platform //----------------------------------------------------------------------------- CORE.Window.resizedLastFrame = false; +#define RGFW_HOLD_MOUSE (1L<<2) - #define RGFW_HOLD_MOUSE (1L<<2) - #if defined(RGFW_X11) //|| defined(RGFW_MACOS) +#if defined(RGFW_X11) //|| defined(RGFW_MACOS) if (platform.window->src.winArgs & RGFW_HOLD_MOUSE) { CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; } - else { + else + { CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } - #endif +#endif while (RGFW_window_checkEvent(platform.window)) { @@ -901,7 +897,7 @@ void PollInputEvents(void) } } - RGFW_Event* event = &platform.window->event; + RGFW_Event *event = &platform.window->event; // All input events can be processed after polling switch (event->type) @@ -1163,6 +1159,7 @@ void PollInputEvents(void) int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; int pressed = (value > 0.1f); CORE.Input.Gamepad.currentButtonState[event->joystick][button] = pressed; + if (pressed) CORE.Input.Gamepad.lastButtonPressed = button; else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; } @@ -1254,8 +1251,7 @@ int InitPlatform(void) platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags); - if (CORE.Window.flags & FLAG_VSYNC_HINT) - RGFW_window_swapInterval(platform.window, 1); + if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1); RGFW_window_makeCurrent(platform.window); @@ -1320,12 +1316,12 @@ int InitPlatform(void) CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - #ifdef RGFW_X11 +#ifdef RGFW_X11 for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++) { RGFW_registerJoystick(platform.window, i); } - #endif +#endif TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully"); From 174313acbfa74d2a45fc0207f47ab00fbdb06050 Mon Sep 17 00:00:00 2001 From: SuperUserNameMan <9801802+SuperUserNameMan@users.noreply.github.com> Date: Tue, 9 Jul 2024 09:23:14 +0200 Subject: [PATCH 39/40] `WindowSizeCallback()` should not try to handle DPI since already managed by GLFW (#4143) If `FLAG_WINDOW_HIGHDPI` is set, `InitPlatform()` will aks GLFW to handle resize window content area based on the monitor content scale using : ` glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); ` So `WindowSizeCallback()` does not have to handle it a second time. --- src/platforms/rcore_desktop_glfw.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/platforms/rcore_desktop_glfw.c b/src/platforms/rcore_desktop_glfw.c index 42c64402f..56a4e2619 100644 --- a/src/platforms/rcore_desktop_glfw.c +++ b/src/platforms/rcore_desktop_glfw.c @@ -1669,23 +1669,9 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height) if (IsWindowFullscreen()) return; // Set current screen size -#if defined(__APPLE__) + CORE.Window.screen.width = width; CORE.Window.screen.height = height; -#else - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - Vector2 windowScaleDPI = GetWindowScaleDPI(); - - CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x); - CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y); - } - else - { - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - } -#endif // NOTE: Postprocessing texture is not scaled to new size } From 74680748b9abe05ceda5d412b61ff99addf72807 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Tue, 9 Jul 2024 10:45:07 -0700 Subject: [PATCH 40/40] [Shapes] Remove duplicate color calls in DrawGrid (#4148) * Update raylib_api.* by CI * No need to call the color 4 times in a row, it's batched --------- Co-authored-by: github-actions[bot] --- src/rmodels.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 50328fca7..c1d56765e 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1072,16 +1072,10 @@ void DrawGrid(int slices, float spacing) if (i == 0) { rlColor3f(0.5f, 0.5f, 0.5f); - rlColor3f(0.5f, 0.5f, 0.5f); - rlColor3f(0.5f, 0.5f, 0.5f); - rlColor3f(0.5f, 0.5f, 0.5f); } else { rlColor3f(0.75f, 0.75f, 0.75f); - rlColor3f(0.75f, 0.75f, 0.75f); - rlColor3f(0.75f, 0.75f, 0.75f); - rlColor3f(0.75f, 0.75f, 0.75f); } rlVertex3f((float)i*spacing, 0.0f, (float)-halfSlices*spacing);