Compare commits
425 Commits
Author | SHA1 | Date |
---|---|---|
Mark Harfouche | feef6e395b | 2 weeks ago |
Etaash Mathamsetty | 8b7dae6c42 | 2 weeks ago |
Austin Shafer | 2e7e86feef | 2 weeks ago |
Icenowy Zheng | 4b45e2612f | 3 weeks ago |
Yang Liu | d0894fff38 | 3 weeks ago |
flightlessmango | d102604291 | 3 weeks ago |
flightlessmango | a6422bc6ff | 3 weeks ago |
Jax Young | 3c58162bae | 4 weeks ago |
Etaash Mathamsetty | 1d47ed8917 | 4 weeks ago |
Etaash Mathamsetty | 1696a55951 | 4 weeks ago |
Etaash Mathamsetty | 606fa2794d | 4 weeks ago |
Radu | e8817f8ba3 | 4 weeks ago |
flightlessmango | 7fafed9cba | 1 month ago |
zmeyka3310 | b585329b6a | 1 month ago |
flightlessmango | 6c74e3c9f4 | 2 months ago |
flightlessmango | e248ee90e4 | 2 months ago |
flightlessmango | 49336958c8 | 2 months ago |
flightlessmango | 422663d0b2 | 2 months ago |
flightlessmango | 7cfca3f8c2 | 2 months ago |
flightlessmango | 1abf530785 | 3 months ago |
Joshua Ashton | c0b609f646 | 3 months ago |
flightlessmango | 8f94973cd8 | 3 months ago |
flightlessmango | bda2af47b0 | 3 months ago |
flightlessmango | edf7546d8d | 3 months ago |
flightlessmango | c0afb84e9c | 3 months ago |
Etaash Mathamsetty | 159eb13341 | 3 months ago |
flightlessmango | 6c665653ba | 3 months ago |
flightlessmango | 8a31b96766 | 3 months ago |
Voodoo Hillbilly | 2d0c0a1b3c | 3 months ago |
flightlessmango | ed6cf225f1 | 3 months ago |
Richard | 2a2cc4ada2 | 3 months ago |
flightlessmango | df61c6602f | 3 months ago |
flightlessmango | 193fdc7eef | 3 months ago |
flightlessmango | 392f18d102 | 3 months ago |
flightlessmango | 9955b22d6a | 3 months ago |
flightlessmango | 2b00432bd7 | 3 months ago |
flightlessmango | 9cb5a40d45 | 3 months ago |
J Laari | 41b8761590 | 3 months ago |
flightlessmango | 89a6f016a4 | 4 months ago |
flightlessmango | f8fb9aaa7d | 4 months ago |
flightlessmango | 41f2cf74de | 4 months ago |
flightlessmango | 2937785446 | 4 months ago |
flightlessmango | eaa96ecfba | 4 months ago |
jackun | 4a34502aeb | 4 months ago |
flightlessmango | 4307450c02 | 4 months ago |
flightlessmango | a4393e0e42 | 4 months ago |
flightlessmango | 511b7a6f2a | 4 months ago |
Leopard1907 | 391c52271c | 4 months ago |
Leopard1907 | e6b7304ec5 | 4 months ago |
João Batista | b217d75917 | 4 months ago |
flightlessmango | 7b80f733b6 | 4 months ago |
flightlessmango | 038478a96a | 4 months ago |
flightlessmango | f22d3221a3 | 4 months ago |
flightlessmango | d8ed2331c3 | 4 months ago |
flightlessmango | 6312c46663 | 4 months ago |
flightlessmango | 78a892e1af | 4 months ago |
flightlessmango | 63eaf1489a | 4 months ago |
flightlessmango | 4cbcec30b8 | 4 months ago |
flightlessmango | dc7ec94549 | 4 months ago |
flightlessmango | faa3b1c22f | 4 months ago |
flightlessmango | 0caded833e | 4 months ago |
Alex Maese | 872a564889 | 4 months ago |
Alex Maese | 5c1fe0a5e6 | 4 months ago |
Alex Maese | 6ab4624cfb | 4 months ago |
Bill Li | f0407636d3 | 4 months ago |
Alesh Slovak | 5d744d328a | 4 months ago |
Gonçalo Negrier Duarte | 4ee8a9aac4 | 4 months ago |
flightlessmango | 31f2ca5e30 | 5 months ago |
Alessandro Toia | 57cd928c63 | 5 months ago |
Arias800 | 32c6cf0ebd | 5 months ago |
flightlessmango | ec1b9c017f | 5 months ago |
flightlessmango | 55712618fa | 5 months ago |
flightlessmango | a0a31b4f64 | 5 months ago |
Etaash Mathamsetty | 4cad060334 | 5 months ago |
Błażej Szczygieł | 121cf54d9b | 5 months ago |
Alessandro Toia | 7497b1da3f | 5 months ago |
Etaash Mathamsetty | 0e92ec4790 | 5 months ago |
Milos Tijanic | 44eb25e10e | 5 months ago |
flightlessmango | 046b6cecdd | 5 months ago |
flightlessmango | b6291a51e6 | 5 months ago |
flightlessmango | ba220022d0 | 5 months ago |
flightlessmango | 8491ff5aa9 | 5 months ago |
flightlessmango | c363c96cd5 | 5 months ago |
flightlessmango | 23a2d53a3b | 5 months ago |
flightlessmango | 4f43b260cc | 5 months ago |
Etaash Mathamsetty | 6ec1f8c80e | 5 months ago |
Etaash Mathamsetty | d23b4cde8c | 5 months ago |
Sefa Eyeoglu | 4d0b1e1fb8 | 5 months ago |
Etaash Mathamsetty | 366c1a233f | 5 months ago |
Etaash Mathamsetty | 86668eeb96 | 5 months ago |
Etaash Mathamsetty | e99284aadd | 5 months ago |
Etaash Mathamsetty | 5912cce19f | 5 months ago |
flightlessmango | 1e13c385fe | 6 months ago |
flightlessmango | 291ffd263c | 6 months ago |
flightlessmango | ffff9b83b2 | 6 months ago |
flightlessmango | 14b1d87ad8 | 6 months ago |
flightlessmango | f7d291bbe9 | 6 months ago |
Etaash Mathamsetty | c39984da9e | 6 months ago |
Etaash Mathamsetty | a65d1d8a2b | 6 months ago |
Etaash Mathamsetty | 054f4cc9e3 | 6 months ago |
flightlessmango | 66b103ac55 | 6 months ago |
flightlessmango | 33b8924384 | 6 months ago |
flightlessmango | 4aa92187a7 | 6 months ago |
flightlessmango | 976ae0f75a | 6 months ago |
flightlessmango | 77d6244cea | 6 months ago |
flightlessmango | cdd8043489 | 6 months ago |
flightlessman | fb0b559d04 | 6 months ago |
flightlessmango | 7f439879e0 | 6 months ago |
flightlessmango | a41e8e0d1f | 6 months ago |
flightlessmango | 01b81d068b | 6 months ago |
flightlessmango | 6e5581ba79 | 6 months ago |
Alessandro Toia | dbf0c91f47 | 6 months ago |
flightlessmango | bcaccc8bd4 | 6 months ago |
flightlessmango | ad1d03d257 | 6 months ago |
flightlessmango | 94449aa324 | 6 months ago |
flightlessmango | 325498d1d6 | 6 months ago |
flightlessmango | 3c743a9e92 | 6 months ago |
flightlessmango | bdd2a02a10 | 6 months ago |
flightlessmango | 3f0ecf769d | 6 months ago |
flightlessmango | 8732ada5a6 | 6 months ago |
flightlessmango | 12151d2ae2 | 6 months ago |
flightlessmango | 4eb75cf4fb | 6 months ago |
flightlessmango | bbf2872486 | 6 months ago |
flightlessmango | 5b233be2f8 | 6 months ago |
flightlessmango | 6edb590b9a | 6 months ago |
flightlessmango | 1baecfc493 | 6 months ago |
flightlessmango | a4862f5461 | 6 months ago |
flightlessmango | 66d5ce43d2 | 6 months ago |
Joshua Ashton | baa06da2ad | 6 months ago |
Joshua Ashton | 7c7cb9a1dc | 6 months ago |
Alessandro Toia | a7a73afdad | 6 months ago |
flightlessmango | c368f1491e | 7 months ago |
Alessandro Toia | 83af57a9b3 | 7 months ago |
Alex Maese | 2aa604278e | 7 months ago |
Alessandro Toia | 5598fda8f4 | 7 months ago |
Joshua Ashton | 7349a1cf29 | 7 months ago |
Alessandro Toia | 54df529eba | 7 months ago |
flightlessmango | 2e0197718b | 7 months ago |
Alessandro Toia | d1d44f94c9 | 7 months ago |
Etaash Mathamsetty | c058a38168 | 7 months ago |
Etaash Mathamsetty | 732629e5a0 | 7 months ago |
FlightlessMango | 98e442ba7f | 7 months ago |
FlightlessMango | c80c2093cc | 7 months ago |
FlightlessMango | 3dfd772363 | 7 months ago |
FlightlessMango | 45671161c4 | 7 months ago |
FlightlessMango | ab3b22ec12 | 7 months ago |
FlightlessMango | 46b6fa7fed | 7 months ago |
FlightlessMango | d2b9458a46 | 7 months ago |
Alesh Slovak | 388113ff62 | 7 months ago |
FlightlessMango | 95141de657 | 7 months ago |
PedroHLC ☭ | cba217ffaf | 7 months ago |
FlightlessMango | e64e4788ef | 8 months ago |
FlightlessMango | bd37645dfd | 8 months ago |
FlightlessMango | eca65e611f | 8 months ago |
FlightlessMango | 48d84262cd | 8 months ago |
FlightlessMango | 0ab4c25343 | 8 months ago |
FlightlessMango | 2bc323b2ed | 8 months ago |
flightlessmango | fc54f48257 | 8 months ago |
FlightlessMango | 62fee600d1 | 8 months ago |
FlightlessMango | 4969eed348 | 8 months ago |
Bill Li | dc60dc71ec | 8 months ago |
Bill Li | 9ba6c8de10 | 8 months ago |
FlightlessMango | 6648143c48 | 8 months ago |
FlightlessMango | 2fa6ff7cb3 | 8 months ago |
e2dk4r | d30cf162c1 | 8 months ago |
flightlessmango | 3cee6f15de | 8 months ago |
flightlessmango | d4a66ccf7e | 8 months ago |
John Zimmermann | cfc51ec99f | 8 months ago |
John Zimmermann | a4332733c3 | 8 months ago |
Alessandro Toia | 733fbe03fa | 8 months ago |
Ryan Houdek | 267a431eef | 9 months ago |
Ryan Houdek | 511f4fc303 | 9 months ago |
flightlessmango | 38cec48f93 | 9 months ago |
flightlessmango | c33ce2b734 | 9 months ago |
flightlessmango | 86e49da174 | 9 months ago |
flightlessmango | c8c987dadb | 9 months ago |
flightlessmango | 971791d5da | 9 months ago |
flightlessmango | 53281f3f2c | 9 months ago |
flightlessmango | 1d357e17c8 | 9 months ago |
flightlessmango | adbafe1450 | 9 months ago |
flightlessmango | c38132c964 | 10 months ago |
flightlessmango | 9998593d92 | 10 months ago |
flightlessmango | 73a6c5624e | 10 months ago |
Alessandro Toia | 0394bbefd2 | 10 months ago |
Bill Li | 67193e2ad2 | 10 months ago |
flightlessmango | c5c82dbbae | 10 months ago |
flightlessmango | 56b23d7c12 | 10 months ago |
flightlessmango | 4646e2e4f6 | 10 months ago |
Joshua Ashton | d5f6f44c26 | 10 months ago |
Joshua Ashton | aeae2a00ac | 10 months ago |
Joshua Ashton | 684750158e | 10 months ago |
flightlessmango | f168483c3e | 10 months ago |
flightlessmango | dcb8741581 | 10 months ago |
flightlessmango | 60779dda9a | 10 months ago |
Alex Maese | b3fa8f5c29 | 10 months ago |
flightlessmango | ea725ed1d2 | 10 months ago |
flightlessmango | b1ae5b87c7 | 10 months ago |
flightlessmango | efebc4fe34 | 10 months ago |
flightlessmango | 669f6f96a1 | 11 months ago |
flightlessmango | 50d578973c | 11 months ago |
flightlessmango | fd4b06f876 | 11 months ago |
flightlessmango | 941c4e21a5 | 11 months ago |
flightlessmango | 94a8dfb658 | 11 months ago |
flightlessmango | c51385ab2c | 11 months ago |
flightlessmango | 9411963ad9 | 11 months ago |
flightlessmango | 0849ae42b8 | 11 months ago |
flightlessmango | 48f8db420b | 11 months ago |
flightlessmango | f351515265 | 11 months ago |
AndreFGard | 2c32046770 | 11 months ago |
Joshua Ashton | 9393066ef8 | 11 months ago |
Alessandro Toia | 1613276182 | 11 months ago |
Alessandro Toia | 672d6481cc | 11 months ago |
FlightlessMango | 84daa06581 | 11 months ago |
FlightlessMango | 1462716bf0 | 11 months ago |
Alessandro Toia | 81941faa8c | 11 months ago |
Tianhao Chai | 29058a09bb | 12 months ago |
Alessandro Toia | 2b29f2d89d | 12 months ago |
Bill Li | fbc9cb3175 | 12 months ago |
Alessandro Toia | 7d51113e16 | 12 months ago |
FlightlessMango | 50989b7307 | 12 months ago |
FlightlessMango | 4e06a50010 | 12 months ago |
FlightlessMango | 69db780e59 | 12 months ago |
FlightlessMango | 6effe1b201 | 12 months ago |
FlightlessMango | 1fc0d640ff | 12 months ago |
FlightlessMango | e5df3a183d | 12 months ago |
Alessandro Toia | b26baa21fc | 12 months ago |
Alessandro Toia | c925688954 | 12 months ago |
Alessandro Toia | 30c7755111 | 12 months ago |
FlightlessMango | 363b6530cf | 12 months ago |
Alessandro Toia | 5f05f67e22 | 12 months ago |
FlightlessMango | 544856b174 | 12 months ago |
Alessandro Toia | f40b32fc45 | 12 months ago |
Alessandro Toia | 381e593f6b | 12 months ago |
FlightlessMango | dbdc295521 | 12 months ago |
FlightlessMango | fae6035c20 | 12 months ago |
FlightlessMango | e85c580de3 | 12 months ago |
FlightlessMango | ecaba3fe36 | 12 months ago |
FlightlessMango | 8bda2a2a04 | 12 months ago |
FlightlessMango | b32e6b299c | 12 months ago |
FlightlessMango | 2bf27939e0 | 12 months ago |
FlightlessMango | d962ae5181 | 12 months ago |
Self Denial | 0341e28e05 | 12 months ago |
Self Denial | 10e5abcccc | 12 months ago |
Alessandro Toia | 10c0f608fd | 12 months ago |
Alessandro Toia | cd87e22dc3 | 12 months ago |
John Zimmermann | 23c9b50742 | 12 months ago |
FlightlessMango | 13164b01ad | 12 months ago |
Alessandro Toia | 4bc55bf966 | 12 months ago |
NoXPhasma | 14da551ae0 | 12 months ago |
FlightlessMango | fb1bf07dbb | 12 months ago |
FlightlessMango | bef35591f8 | 12 months ago |
FlightlessMango | 30661f8cea | 12 months ago |
Bill Li | efd1c07391 | 12 months ago |
Bill Li | c3bc9df842 | 12 months ago |
FlightlessMango | 9a0ecee98e | 12 months ago |
FlightlessMango | 5c1796e697 | 12 months ago |
FlightlessMango | a67849a20b | 12 months ago |
FlightlessMango | d0ee1eaa28 | 12 months ago |
FlightlessMango | 55c8be5b0b | 12 months ago |
FlightlessMango | 84caba707a | 12 months ago |
FlightlessMango | 801f02412c | 12 months ago |
Alessandro Toia | a2c421a8d3 | 1 year ago |
FlightlessMango | 8fca45ffe3 | 1 year ago |
FlightlessMango | 113912eb6b | 1 year ago |
Hannes Mann | 6342056e70 | 1 year ago |
Alessandro Toia | 41e4859547 | 1 year ago |
Clayton Craft | 22d2de4edd | 1 year ago |
Clayton Craft | 675202713d | 1 year ago |
FlightlessMango | 1a0abc65df | 1 year ago |
Łukasz Adamczak | 5c6278baf7 | 1 year ago |
Alessandro Toia | 17d4541980 | 1 year ago |
FlightlessMango | 1a2f3e4b34 | 1 year ago |
FlightlessMango | 22b885bbf9 | 1 year ago |
FlightlessMango | 53ee2275e4 | 1 year ago |
Alex Maese | d75afd6b7a | 1 year ago |
Alex Maese | 939ab8bd4a | 1 year ago |
FlightlessMango | 02c3b4f235 | 1 year ago |
Jan Solanti | ef124ae3ec | 1 year ago |
Jan Solanti | 3173cae536 | 1 year ago |
Alex Maese | 55f27d7fae | 1 year ago |
Alex Maese | cd05d1771f | 1 year ago |
Albert Sebastian | f66700296b | 1 year ago |
FlightlessMango | 08152325d4 | 1 year ago |
FlightlessMango | 09ad89f4af | 1 year ago |
FlightlessMango | d5ca00717e | 1 year ago |
FlightlessMango | cee7b8b601 | 1 year ago |
FlightlessMango | 585d6e0f46 | 1 year ago |
Alessandro Toia | 1de9551d38 | 1 year ago |
bouhaa | 1f925a8328 | 1 year ago |
Etaash Mathamsetty | 41f923b0b2 | 1 year ago |
siroccal | 3922da8a39 | 1 year ago |
FlightlessMango | 7189b75efe | 1 year ago |
FlightlessMango | 373c8e1f65 | 1 year ago |
FlightlessMango | dd57951462 | 1 year ago |
bouhaa | cd92cedeca | 1 year ago |
bouhaa | f964702aea | 1 year ago |
FlightlessMango | e60b625fd0 | 1 year ago |
Bouke Haarsma | 05d3a6db04 | 1 year ago |
FlightlessMango | f92eb8bce0 | 1 year ago |
Joshua Ashton | 6a63b7612b | 1 year ago |
Joshua Ashton | ef444a740d | 1 year ago |
FlightlessMango | 528ba6c9c0 | 1 year ago |
FlightlessMango | f38cc78996 | 1 year ago |
Bouke Haarsma | f1a6753796 | 1 year ago |
FlightlessMango | 5d93e484ce | 1 year ago |
FlightlessMango | be1e94f008 | 1 year ago |
FlightlessMango | 1f2b31008e | 1 year ago |
FlightlessMango | 41f8446cf1 | 1 year ago |
FlightlessMango | dc1761e98a | 1 year ago |
FlightlessMango | 30395ab947 | 1 year ago |
andrefsagh@gmail.com | 5054f6ceff | 1 year ago |
FlightlessMango | 602ba78ea3 | 1 year ago |
FlightlessMango | 1182bcfbec | 1 year ago |
Stephan Lachnit | a05d576b4b | 1 year ago |
andrefsagh@gmail.com | 46d81b338b | 1 year ago |
Łukasz Adamczak | 38604927d7 | 1 year ago |
NoXPhasma | 51162cb842 | 1 year ago |
NoXPhasma | d924278b4e | 1 year ago |
NoXPhasma | 557f92d37d | 1 year ago |
Adel KARA SLIMANE | e345195f56 | 1 year ago |
Adel KARA SLIMANE | 1a78c00f77 | 1 year ago |
Bill Li | bc8acf95ea | 1 year ago |
dependabot[bot] | 911a7d8c86 | 1 year ago |
FlightlessMango | 47ad73395a | 1 year ago |
FlightlessMango | 12567c68b0 | 1 year ago |
FlightlessMango | f4dfdd82c9 | 1 year ago |
FlightlessMango | 30748bdf0c | 1 year ago |
FlightlessMango | 381c519631 | 1 year ago |
FlightlessMango | b3b9672631 | 1 year ago |
FlightlessMango | 801b5ef5fc | 1 year ago |
FlightlessMango | 6e264adecb | 1 year ago |
FlightlessMango | 881b4d2e4a | 1 year ago |
FlightlessMango | 7c2a90c209 | 1 year ago |
FlightlessMango | 6abc794b42 | 1 year ago |
FlightlessMango | b48b03e131 | 1 year ago |
FlightlessMango | 5369683ff9 | 1 year ago |
FlightlessMango | f1b5ba34be | 1 year ago |
FlightlessMango | 0ae2e42fe7 | 1 year ago |
FlightlessMango | 352d365493 | 1 year ago |
FlightlessMango | f727065f34 | 1 year ago |
NoXPhasma | c53ca38916 | 1 year ago |
NoXPhasma | 561c9d8a38 | 1 year ago |
NoXPhasma | d2ec6fe277 | 1 year ago |
NoXPhasma | ad500e3251 | 1 year ago |
Adel KARA SLIMANE | 6306fed7f7 | 1 year ago |
NoXPhasma | 3efa15cf25 | 1 year ago |
FlightlessMango | 67862421a6 | 1 year ago |
FlightlessMango | 3efc2c05ee | 1 year ago |
jackun | 5fde8749af | 1 year ago |
FlightlessMango | ea3ba7e30d | 1 year ago |
FlightlessMango | 5754ca13ee | 1 year ago |
FlightlessMango | 0b3904fd38 | 1 year ago |
dependabot[bot] | af92f28da5 | 1 year ago |
FlightlessMango | 102b714c11 | 1 year ago |
FlightlessMango | 0fbd55fa77 | 1 year ago |
FlightlessMango | 744f173298 | 1 year ago |
FlightlessMango | c9e67ca573 | 1 year ago |
Alessandro Toia | 2b5bf96b20 | 1 year ago |
Alessandro Toia | 1be907ed93 | 1 year ago |
FlightlessMango | 3f8f036ee8 | 1 year ago |
Alessandro Toia | e77585d848 | 1 year ago |
Alessandro Toia | 7b5c0a43c6 | 1 year ago |
Alessandro Toia | ed85504dec | 1 year ago |
Bill Li | 9b0aec0540 | 1 year ago |
Bill Li | 2101090f00 | 1 year ago |
Alessandro Toia | 3daf7d73b3 | 1 year ago |
FlightlessMango | 2ffc579b7f | 1 year ago |
FlightlessMango | 6c46794496 | 1 year ago |
FlightlessMango | 12a86ef36e | 1 year ago |
FlightlessMango | b2c88c2dfd | 1 year ago |
FlightlessMango | cbf531e6c8 | 1 year ago |
FlightlessMango | 4161139597 | 1 year ago |
FlightlessMango | e2711b8059 | 1 year ago |
FlightlessMango | 7853af98d0 | 1 year ago |
FlightlessMango | 9bc90493be | 1 year ago |
Alessandro Toia | 98c9897430 | 1 year ago |
Alessandro Toia | c534f33156 | 1 year ago |
Alessandro Toia | a45816b8e1 | 1 year ago |
FlightlessMango | f7a712110f | 1 year ago |
FlightlessMango | 7ea1f33fa1 | 1 year ago |
FlightlessMango | ddfc32211f | 1 year ago |
FlightlessMango | 20b729cca4 | 1 year ago |
FlightlessMango | 38a1328536 | 1 year ago |
FlightlessMango | 1d8f9f6601 | 1 year ago |
jackun | 95b7b44b6f | 1 year ago |
jackun | e61321717e | 1 year ago |
jackun | 85fd1c97fd | 1 year ago |
jackun | cac664bef1 | 1 year ago |
Arvind Doobary | d41a74ca1a | 1 year ago |
FlightlessMango | 1093de8c44 | 1 year ago |
jackun | 238a621c11 | 1 year ago |
FlightlessMango | c7ab967b92 | 1 year ago |
FlightlessMango | 83d265637e | 1 year ago |
FlightlessMango | 12b16247ce | 1 year ago |
FlightlessMango | 69e4b2d877 | 1 year ago |
FlightlessMango | d99e48db5d | 1 year ago |
FlightlessMango | 11bc5111eb | 1 year ago |
FlightlessMango | 8e4857a5c6 | 1 year ago |
FlightlessMango | 7eea724f37 | 1 year ago |
FlightlessMango | 016728b450 | 1 year ago |
FlightlessMango | 2df384cdd7 | 1 year ago |
FlightlessMango | 5aec3d817b | 1 year ago |
FlightlessMango | 4b69f23fb5 | 1 year ago |
FlightlessMango | 26a9f2e663 | 1 year ago |
Alessandro Toia | 78d99a6aab | 1 year ago |
FlightlessMango | 7f945627f5 | 1 year ago |
FlightlessMango | 7225aaa628 | 1 year ago |
FlightlessMango | a67a6bc79d | 1 year ago |
Faalagorn | 98530b8f55 | 1 year ago |
Faalagorn | 047efc3eca | 1 year ago |
Faalagorn | fe8036fb16 | 1 year ago |
Faalagorn | 6b9857ac8e | 1 year ago |
Faalagorn | bc96ce3247 | 1 year ago |
Faalagorn | 8057d057b6 | 1 year ago |
Faalagorn | f3f4258bb6 | 1 year ago |
Faalagorn | a0aededcd1 | 1 year ago |
FlightlessMango | 034b378e10 | 1 year ago |
jackun | 06edee4071 | 1 year ago |
Atemu | 39080290a2 | 1 year ago |
Kira Bruneau | 2918b6ef1b | 1 year ago |
FlightlessMango | 0a2b8a73ab | 1 year ago |
FlightlessMango | 544589aa20 | 1 year ago |
FlightlessMango | 8f79e9cef3 | 1 year ago |
NoXPhasma | 191cf3775f | 1 year ago |
FlightlessMango | bcf951f3ba | 1 year ago |
@ -1,12 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: FlightlessMango
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://www.paypal.me/flightlessmango
|
@ -0,0 +1,23 @@
|
||||
name: param check
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'src/overlay_params.h'
|
||||
- 'README.md'
|
||||
- 'data/MangoHud.conf'
|
||||
|
||||
jobs:
|
||||
param-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11.2' # Replace with the version of Python you want to use
|
||||
- name: Run Python script
|
||||
run: |
|
||||
cd tests
|
||||
python params.py
|
@ -0,0 +1,70 @@
|
||||
name: Update Meson Version and Recreate Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
update-version:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install GitHub CLI
|
||||
run: sudo apt-get install -y gh
|
||||
|
||||
- name: Extract version from release
|
||||
id: extract_version
|
||||
run: echo "version=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Fetch release description
|
||||
id: fetch_description
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
TAG_NAME=${{ env.version }}
|
||||
DESCRIPTION=$(gh release view $TAG_NAME --json body -q .body)
|
||||
echo "description=$DESCRIPTION" >> $GITHUB_ENV
|
||||
|
||||
- name: Update meson.build
|
||||
run: |
|
||||
VERSION=${{ env.version }}
|
||||
sed -i "s/^\(\s*version\s*:\s*'\)[^']*'/\1${VERSION}'/" meson.build
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add meson.build
|
||||
git commit -m "Update version to ${{ env.version }}"
|
||||
git push origin HEAD:refs/heads/master
|
||||
|
||||
- name: Force-update tag
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
TAG_NAME=${{ env.version }}
|
||||
git tag -fa $TAG_NAME -m "Update tag to include version update"
|
||||
git push origin --force $TAG_NAME
|
||||
|
||||
- name: Recreate release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
TAG_NAME=${{ env.version }}
|
||||
DESCRIPTION=${{ env.description }}
|
||||
gh release delete $TAG_NAME --yes
|
||||
gh release create $TAG_NAME --target $(git rev-parse HEAD) --title "$TAG_NAME" --notes "$DESCRIPTION"
|
||||
|
||||
- name: Trigger other workflows
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
TAG_NAME=${{ env.version }}
|
||||
gh workflow run .github/workflows/build-source.yml --ref $TAG_NAME
|
||||
gh workflow run .github/workflows/build-package.yml --ref $TAG_NAME
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 56 KiB |
@ -1,23 +1,58 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
programname=`basename "$0"`
|
||||
programname=$(basename "$0")
|
||||
echo "ERROR: No program supplied"
|
||||
echo
|
||||
echo "Usage: $programname <program>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud.so"
|
||||
|
||||
# Add exe names newline separated to the string to disable LD_PRELOAD
|
||||
DISABLE_LD_PRELOAD="cs2.sh
|
||||
"
|
||||
|
||||
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_opengl.so"
|
||||
|
||||
if [ "$1" = "--dlsym" ]; then
|
||||
MANGOHUD_DLSYM=1
|
||||
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
|
||||
shift
|
||||
shift # shift will only be executed if $1 is "--dlsym"
|
||||
elif [ "$MANGOHUD_DLSYM" = "1" ]; then
|
||||
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
|
||||
fi
|
||||
|
||||
if [ "$1" = "--version" ]; then
|
||||
echo @version@
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Preload using the plain filenames of the libs, the dynamic linker will
|
||||
# figure out whether the 32 or 64 bit version should be used
|
||||
LD_PRELOAD="${LD_PRELOAD:+$LD_PRELOAD:}${MANGOHUD_LIB_NAME}"
|
||||
# grab all arguments from command_line
|
||||
command_line="$*"
|
||||
# flag for disable_preload
|
||||
disable_preload=false
|
||||
|
||||
# Check if the script name or any of the executables in DISABLE_LD_PRELOAD are in the command line
|
||||
for exe in $DISABLE_LD_PRELOAD; do
|
||||
if echo "$command_line" | grep -q "$exe"; then
|
||||
disable_preload=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
exec env MANGOHUD=1 LD_PRELOAD="${LD_PRELOAD}" "$@"
|
||||
if [ "$disable_preload" = true ]; then
|
||||
exec env MANGOHUD=1 "$@"
|
||||
else
|
||||
# Make sure we don't append mangohud lib multiple times
|
||||
# otherwise, this could cause issues with the steam runtime
|
||||
case ":${LD_PRELOAD-}:" in
|
||||
(*:$MANGOHUD_LIB_NAME:*)
|
||||
;;
|
||||
(*)
|
||||
# Preload using the plain filenames of the libs, the dynamic linker will
|
||||
# figure out whether the 32 or 64 bit version should be used
|
||||
LD_PRELOAD="${LD_PRELOAD:+$LD_PRELOAD:}${MANGOHUD_LIB_NAME}"
|
||||
esac
|
||||
|
||||
exec env MANGOHUD=1 LD_PRELOAD="${LD_PRELOAD}" "$@"
|
||||
fi
|
||||
|
@ -0,0 +1,448 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
r"""
|
||||
Script to plot all the MangoHud benchmarks contained in a given folder.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import csv
|
||||
|
||||
from typing import List, Union
|
||||
|
||||
import numpy as np
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.widgets import Cursor
|
||||
from matplotlib.colors import LinearSegmentedColormap
|
||||
from matplotlib.ticker import EngFormatter
|
||||
|
||||
plt.rcParams['font.family'] = "Lato,serif"
|
||||
plt.rcParams['font.weight'] = "600"
|
||||
|
||||
background_color = "#1A1C1D"
|
||||
legend_facecolor = "#585f63"
|
||||
legend_textcolor = "#cccbc9"
|
||||
text_color = "#e8e6e3"
|
||||
mango_color = "#BB770A"
|
||||
graphbox_linewidth = 1.5
|
||||
|
||||
mango_cmap = LinearSegmentedColormap.from_list("mango_heat", [background_color, mango_color])
|
||||
|
||||
def identity(val):
|
||||
r"""
|
||||
returns the value as-is
|
||||
"""
|
||||
return val
|
||||
|
||||
|
||||
def get_integer(val: str) -> int:
|
||||
r"""
|
||||
interprets the str 'val' as an integer and returns it
|
||||
"""
|
||||
if is_integer(val):
|
||||
return int(val)
|
||||
else:
|
||||
raise ValueError("Casting a non integer value: ", val)
|
||||
|
||||
|
||||
def is_integer(s: str) -> bool:
|
||||
r"""
|
||||
tests if 's' is an integer and returns a bool
|
||||
"""
|
||||
try:
|
||||
int(s)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def get_float(val):
|
||||
r"""
|
||||
interprets the str 'val' as a float and returns it
|
||||
"""
|
||||
if is_float(val):
|
||||
return float(val)
|
||||
else:
|
||||
return float("nan")
|
||||
|
||||
|
||||
def is_float(s: str) -> bool:
|
||||
r"""
|
||||
tests if 's' is an float and returns a bool
|
||||
"""
|
||||
try:
|
||||
float(s)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
class Database:
|
||||
r"""
|
||||
A class that contains all the csv files within
|
||||
the folder that it is instanced with
|
||||
"""
|
||||
def __init__(self,
|
||||
data_folder_path=None,
|
||||
csv_separator=" ",
|
||||
filename_var_separator="|"):
|
||||
|
||||
self.datafiles = []
|
||||
self.result_names_col = None
|
||||
self.result_values_col = None
|
||||
self.sim_settings_names_col = None
|
||||
self.sim_settings_values_col = None
|
||||
|
||||
if data_folder_path:
|
||||
self.load_from_folder(
|
||||
data_folder_path,
|
||||
csv_separator,
|
||||
filename_var_separator)
|
||||
|
||||
def load_from_folder(self,
|
||||
data_folder_path,
|
||||
csv_separator=" ",
|
||||
filename_var_separator="|"):
|
||||
r"""
|
||||
Load all CSV files form the given folder
|
||||
"""
|
||||
filepaths = list(Path(data_folder_path).rglob("*.csv"))
|
||||
|
||||
self.datafiles = []
|
||||
N = len(filepaths)
|
||||
|
||||
print(f"Loading {N} benchmark files")
|
||||
for filepath in filepaths:
|
||||
try:
|
||||
datafile = BenchmarkFile(
|
||||
str(filepath),
|
||||
csv_separator=csv_separator,
|
||||
filename_var_separator=filename_var_separator)
|
||||
self.datafiles.append(datafile)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.datafiles.sort()
|
||||
|
||||
|
||||
class BenchmarkFile:
|
||||
r"""
|
||||
A class that represents a single CSV file, can load CSV files
|
||||
with arbitrary separators. It can return separately any column
|
||||
of the file and any mathematical combinations of its columns.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
filepath="",
|
||||
filename_var_separator="|",
|
||||
csv_separator=" "):
|
||||
self.csv_separator = csv_separator
|
||||
self.filepath = Path(filepath)
|
||||
self.filename = self.filepath.name
|
||||
self.filename_var_separator = filename_var_separator
|
||||
self.variables = dict()
|
||||
|
||||
self.skip_lines = None
|
||||
|
||||
self.columns = []
|
||||
self.column_name_to_index = dict()
|
||||
|
||||
self._is_data_loaded = False
|
||||
|
||||
if not self.filepath.is_file():
|
||||
raise Exception("CSV file does not exist")
|
||||
|
||||
self._read_column_names()
|
||||
|
||||
def __lt__(self, other):
|
||||
stem = self.filename[:-4] # remove the trailing ".csv"
|
||||
other_stem = other.filename[:-4]
|
||||
if stem.startswith(other_stem):
|
||||
return False
|
||||
elif stem.startswith(other_stem):
|
||||
return True
|
||||
else:
|
||||
return stem < other_stem
|
||||
|
||||
def set_variable(self, name, value):
|
||||
r"""
|
||||
Saves a variable within the datafile instance
|
||||
Note: it will not be saved to disk, it's just a helper method to
|
||||
attach variables to a given data file.
|
||||
"""
|
||||
self.variables[name] = value
|
||||
|
||||
def get_variable(self, name):
|
||||
r"""
|
||||
Retrieves a saved variable in the instance
|
||||
"""
|
||||
return self.variables[name]
|
||||
|
||||
def _read_column_names(self):
|
||||
r"""
|
||||
Read the first few lines of the benchmark file
|
||||
to look for the row taht contains the benchmark's
|
||||
column names i.e. "fps", "frametime", "cpu_load"... etc
|
||||
and save the columns names and their index
|
||||
|
||||
Note: we decide that we found the right row by looking if it
|
||||
contains "fps"
|
||||
not the best approach, but it works TM
|
||||
"""
|
||||
|
||||
with open(self.filepath) as open_file:
|
||||
reader = csv.reader(open_file, delimiter=self.csv_separator)
|
||||
|
||||
found_fps_column = False
|
||||
for row_number, row_content in enumerate(reader):
|
||||
if row_number > 100:
|
||||
# did not find the row that starts with the
|
||||
# 'fps' column up until here. give up.
|
||||
break
|
||||
|
||||
if "fps" in row_content:
|
||||
self.skip_lines = row_number + 1
|
||||
found_fps_column = True
|
||||
|
||||
for col, col_name in enumerate(row_content):
|
||||
if col_name in self.column_name_to_index:
|
||||
raise Exception("Two columns have the same name")
|
||||
self.column_name_to_index[col_name] = col
|
||||
|
||||
if not found_fps_column:
|
||||
raise Exception("Not a benchmark file")
|
||||
|
||||
def _load_data(self):
|
||||
r"""
|
||||
Load the benchmark data into memory.
|
||||
"""
|
||||
|
||||
def extend_columns(new_column_num):
|
||||
current_row_num = 0
|
||||
if self.columns:
|
||||
current_row_num = len(self.columns[0])
|
||||
assert (all([len(column) == current_row_num for column in self.columns]))
|
||||
|
||||
current_column_num = len(self.columns)
|
||||
if new_column_num >= current_column_num:
|
||||
self.columns += [["" for j in range(current_row_num)] for i in range(new_column_num - current_column_num)]
|
||||
|
||||
# no need to load data if it's already loaded
|
||||
if self._is_data_loaded:
|
||||
return
|
||||
|
||||
with open(self.filepath) as open_file:
|
||||
reader = csv.reader(open_file, delimiter=self.csv_separator)
|
||||
self._is_data_loaded = True
|
||||
|
||||
for row_number, row_content in enumerate(reader):
|
||||
if row_number <= self.skip_lines:
|
||||
continue
|
||||
|
||||
extend_columns(len(row_content))
|
||||
for col, val in enumerate(row_content):
|
||||
self.columns[col].append(val)
|
||||
|
||||
# Delete any eventual empty column
|
||||
if all([val == "" for val in self.columns[-1]]):
|
||||
del self.columns[-1]
|
||||
|
||||
def get_column_names(self) -> List[str]:
|
||||
r"""
|
||||
Returns the list of columns names of the csv file.
|
||||
"""
|
||||
|
||||
return list(self.column_name_to_index.keys())
|
||||
|
||||
def get(self, col: str, data_type: str = "float") \
|
||||
-> Union[List[float], List[str], List[int], List[complex]]:
|
||||
r"""
|
||||
Returns the column `col`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
col : str
|
||||
The desired column name to retrieve, or its index
|
||||
|
||||
data_type : str
|
||||
"string", "integer" or "float", the type to cast
|
||||
the data to before returning it.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
||||
A list of `data_type` containing the column `col`
|
||||
|
||||
"""
|
||||
|
||||
if not self._is_data_loaded:
|
||||
self._load_data()
|
||||
|
||||
if len(self.columns) == 0:
|
||||
raise ValueError("Datafile empty, can't return any data")
|
||||
|
||||
data_caster_dict = {
|
||||
"string": identity,
|
||||
"float": get_float,
|
||||
"integer": get_integer
|
||||
}
|
||||
|
||||
if data_type not in data_caster_dict:
|
||||
raise ValueError("the given `data_type' doesn't match any "
|
||||
"known types. Which are `string', `integer', "
|
||||
"`float' or `complex'")
|
||||
|
||||
if is_integer(col):
|
||||
# the column's index is given
|
||||
return [data_caster_dict[data_type](val) for val in self.columns[col]]
|
||||
|
||||
if col in self.column_name_to_index:
|
||||
# a column name has been given
|
||||
return [data_caster_dict[data_type](val)
|
||||
for val in self.columns[self.column_name_to_index[col]]]
|
||||
|
||||
raise Exception("Column {} does not exist".format(col))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Plot all the MangoHud benchmarks contained in a given folder.')
|
||||
|
||||
parser.add_argument('folder', metavar='folder', nargs=1,
|
||||
help='path the a MangoHud benchmark folder')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
bench_folder_path = Path(args.folder[0])
|
||||
|
||||
if not bench_folder_path.is_dir():
|
||||
print(f"The path '{bench_folder_path.absolute()}' "
|
||||
"does not point to an existing folder")
|
||||
exit(1)
|
||||
|
||||
fps_subdivs = 1.0 # one division every fps_subdivs FPS
|
||||
|
||||
y_labels = [] # bench files
|
||||
x_labels = [] # FPS subidivions
|
||||
|
||||
database = Database(bench_folder_path, csv_separator=',')
|
||||
distributions = []
|
||||
|
||||
if len(database.datafiles) == 0:
|
||||
print(f"The folder \n {bench_folder_path.absolute()} \n"
|
||||
"contains no CSV file "
|
||||
"(make sure they have the .csv extension)")
|
||||
exit(1)
|
||||
|
||||
for datafile in database.datafiles:
|
||||
bar_distribution = []
|
||||
|
||||
# sort array to get percentiles
|
||||
fps_array = np.sort(datafile.get("fps"))
|
||||
|
||||
# save percentiles
|
||||
if len(fps_array) < 10000:
|
||||
print(f"'{datafile.filename}' simulation "
|
||||
"isn't long enough for precise statistics")
|
||||
datafile.set_variable("selected", False)
|
||||
continue
|
||||
|
||||
# Save label only if this file has long enough simulation
|
||||
y_labels.append(datafile.filename[:-4])
|
||||
datafile.set_variable("selected", True)
|
||||
|
||||
# Save percentiles
|
||||
datafile.set_variable("0.1%", fps_array[int(float(len(fps_array))*0.001)])
|
||||
datafile.set_variable("1%", fps_array[int(float(len(fps_array))*0.01)])
|
||||
datafile.set_variable("50%", fps_array[int(float(len(fps_array))*0.5)])
|
||||
|
||||
datafile.set_variable("average fps", np.average(fps_array))
|
||||
|
||||
for frame_num, fps in enumerate(fps_array):
|
||||
if fps > 1000:
|
||||
print("FPS value above 1000, omitting outlier.")
|
||||
continue
|
||||
index = int(fps/fps_subdivs)
|
||||
for i in range(len(bar_distribution), index+1):
|
||||
bar_distribution.append(0)
|
||||
bar_distribution[index] += 1
|
||||
distributions.append(bar_distribution)
|
||||
|
||||
if not distributions:
|
||||
print("Nothing to plot, exiting.")
|
||||
exit(1)
|
||||
|
||||
num_benchs = len(distributions)
|
||||
max_size = 0
|
||||
for distrib in distributions:
|
||||
max_size = max(max_size, len(distrib))
|
||||
for distrib in distributions:
|
||||
for i in range(len(distrib), max_size):
|
||||
distrib.append(0)
|
||||
|
||||
for i in range(max_size):
|
||||
x_labels.append(str(fps_subdivs * i))
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
# change color of the graph box to the same color as the text
|
||||
for spine in ['left', 'right', 'bottom', 'top']:
|
||||
ax.spines[spine].set_color(text_color)
|
||||
ax.spines[spine].set_linewidth(graphbox_linewidth)
|
||||
|
||||
im = ax.imshow(distributions,
|
||||
aspect="auto",
|
||||
extent=[0, max_size*fps_subdivs, 0, num_benchs],
|
||||
cmap=mango_cmap)
|
||||
|
||||
# draw thick line that separates each benchmark
|
||||
for i in range(len(y_labels)+1):
|
||||
ax.axhline(float(i), color=text_color, lw=graphbox_linewidth)
|
||||
|
||||
i = 0
|
||||
for datafile in database.datafiles:
|
||||
if datafile.get_variable("selected"):
|
||||
kwargs = dict(ymin=(num_benchs-i-1+0.15)/num_benchs,
|
||||
ymax=(num_benchs-i-0.15)/num_benchs,
|
||||
lw=3)
|
||||
|
||||
ax.axvline(datafile.get_variable("0.1%"),
|
||||
color='#35260f',
|
||||
label=("0.1%" if i == 0 else None), **kwargs)
|
||||
|
||||
ax.axvline(datafile.get_variable("1%"),
|
||||
color='#6E4503',
|
||||
label=("1%" if i == 0 else None), **kwargs)
|
||||
|
||||
ax.axvline(datafile.get_variable("50%"),
|
||||
color='#0967BA',
|
||||
label=("50%" if i == 0 else None), **kwargs)
|
||||
|
||||
ax.axvline(datafile.get_variable("average fps"),
|
||||
color='#003A6E',
|
||||
label=("Average" if i == 0 else None), **kwargs)
|
||||
i += 1
|
||||
|
||||
ax.tick_params(axis='y', colors=text_color)
|
||||
ax.tick_params(axis='x', colors=text_color)
|
||||
ax.set_yticks(np.arange(len(y_labels)-0.5, 0, -1), labels=y_labels)
|
||||
ax.grid(False)
|
||||
|
||||
fig.set_facecolor(background_color)
|
||||
|
||||
ax.ticklabel_format(axis='x', style='plain')
|
||||
|
||||
formatter0 = EngFormatter(unit='FPS')
|
||||
ax.xaxis.set_major_formatter(formatter0)
|
||||
|
||||
plt.tight_layout()
|
||||
plt.legend(facecolor=legend_facecolor, labelcolor=legend_textcolor)
|
||||
|
||||
cursor = Cursor(ax,
|
||||
horizOn=False,
|
||||
color='#6c49abff',
|
||||
linewidth=4,
|
||||
useblit=True)
|
||||
|
||||
plt.show()
|
@ -0,0 +1,7 @@
|
||||
# runtime dependencies for `mangoplot`: matplotlib and a GUI backed like PyQt5
|
||||
install_data(
|
||||
'mangoplot.py',
|
||||
install_dir: get_option('bindir'),
|
||||
rename: 'mangoplot',
|
||||
install_mode: 'rwxr-xr-x'
|
||||
)
|
@ -1,7 +1,7 @@
|
||||
DEPS_ARCH="gcc,meson,pkgconf,python-mako,glslang,libglvnd,lib32-libglvnd,libxnvctrl,libdrm"
|
||||
DEPS_FEDORA="meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel,dbus-devel"
|
||||
DEPS_DEBIAN="gcc,g++,gcc-multilib,g++-multilib,ninja-build,python3-pip,python3-setuptools,python3-wheel,pkg-config,mesa-common-dev,libx11-dev,libxnvctrl-dev,libdbus-1-dev"
|
||||
DEPS_SOLUS="mesalib-32bit-devel,glslang,libstdc++-32bit,glibc-32bit-devel,mako"
|
||||
DEPS_ARCH="gcc,meson,pkgconf,python-mako,glslang,libglvnd,lib32-libglvnd,libxnvctrl,libdrm,python-numpy,python-matplotlib,libxkbcommon,lib32-libxkbcommon"
|
||||
DEPS_FEDORA="meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel,dbus-devel,python3-numpy,python3-matplotlib,libstdc++-static,libstdc++-static.i686,libxkbcommon-devel,wayland-devel"
|
||||
DEPS_DEBIAN="gcc,g++,gcc-multilib,g++-multilib,ninja-build,python3-pip,python3-setuptools,python3-wheel,pkg-config,mesa-common-dev,libx11-dev,libxnvctrl-dev,libdbus-1-dev,python3-numpy,python3-matplotlib,libxkbcommon-dev,libxkbcommon-dev:i386,libwayland-dev,libwayland-dev:i386"
|
||||
DEPS_SOLUS="mesalib-32bit-devel,glslang,libstdc++-32bit,glibc-32bit-devel,mako,numpy,matplotlib,libxkbcommon-devel"
|
||||
|
||||
DEPS_SUSE="gcc-c++,gcc-c++-32bit,libpkgconf-devel,ninja,python3-pip,python3-Mako,libX11-devel,glslang-devel,glibc-devel,glibc-devel-32bit,libstdc++-devel,libstdc++-devel-32bit,Mesa-libGL-devel,dbus-1-devel"
|
||||
DEPS_SUSE="gcc-c++,gcc-c++-32bit,libpkgconf-devel,ninja,python3-pip,python3-Mako,libX11-devel,glslang-devel,glibc-devel,glibc-devel-32bit,libstdc++-devel,libstdc++-devel-32bit,Mesa-libGL-devel,dbus-1-devel,python-numpy,python-matplotlib,libxkbcommon-devel,libxkbcommon-devel-32bit,wayland-devel-32bit"
|
||||
DEPS_SUSE_EXTRA="libXNVCtrl-devel"
|
||||
|
@ -0,0 +1,10 @@
|
||||
[preset 1]
|
||||
no_display
|
||||
|
||||
[preset 2]
|
||||
legacy_layout=0
|
||||
cpu_stats=0
|
||||
gpu_stats=0
|
||||
fps
|
||||
fps_only=1
|
||||
frametime=0
|
@ -0,0 +1,225 @@
|
||||
#include "device.h"
|
||||
#include <filesystem.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace fs = ghc::filesystem;
|
||||
using namespace std;
|
||||
std::mutex device_lock;
|
||||
std::vector<device_batt> device_data;
|
||||
std::vector<std::string> list;
|
||||
bool device_found = false;
|
||||
bool check_gamepad = false;
|
||||
bool check_mouse = false;
|
||||
int device_count = 0;
|
||||
int xbox_count = 0;
|
||||
int ds4_count = 0;
|
||||
int ds5_count = 0;
|
||||
int switch_count = 0;
|
||||
int bitdo_count = 0;
|
||||
int logi_count = 0; //Logitech devices, mice & keyboards etc.
|
||||
int shield_count = 0;
|
||||
|
||||
std::string xbox_paths [2]{"gip","xpadneo"};
|
||||
|
||||
static bool operator<(const device_batt& a, const device_batt& b)
|
||||
{
|
||||
return a.name < b.name;
|
||||
}
|
||||
|
||||
|
||||
void device_update(const struct overlay_params& params){
|
||||
std::unique_lock<std::mutex> l(device_lock);
|
||||
fs::path path("/sys/class/power_supply");
|
||||
list.clear();
|
||||
xbox_count = 0;
|
||||
ds4_count = 0;
|
||||
ds5_count = 0;
|
||||
switch_count = 0;
|
||||
bitdo_count = 0;
|
||||
shield_count = 0;
|
||||
for (auto &p : fs::directory_iterator(path)) {
|
||||
string fileName = p.path().filename();
|
||||
//Gamepads
|
||||
if (std::find(params.device_battery.begin(), params.device_battery.end(), "gamepad") != params.device_battery.end()){
|
||||
check_gamepad = true;
|
||||
//CHECK XONE AND XPADNEO DEVICES
|
||||
for (string n : xbox_paths ) {
|
||||
if (fileName.find(n) != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
device_found = true;
|
||||
xbox_count += 1;
|
||||
}
|
||||
}
|
||||
//CHECK FOR DUAL SHOCK 4 DEVICES
|
||||
if (fileName.find("sony_controller") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
device_found = true;
|
||||
ds4_count +=1 ;
|
||||
}
|
||||
if (fileName.find("ps-controller") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
device_found = true;
|
||||
ds5_count +=1 ;
|
||||
}
|
||||
//CHECK FOR NINTENDO SWITCH DEVICES
|
||||
if (fileName.find("nintendo_switch_controller") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
device_found = true;
|
||||
switch_count += 1;
|
||||
}
|
||||
//CHECK * BITDO DEVICES
|
||||
if (fileName.find("hid-e4") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
device_found = true;
|
||||
bitdo_count += 1;
|
||||
}
|
||||
//CHECK NVIDIA SHIELD DEVICES
|
||||
if (fileName.find("thunderstrike") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
device_found = true;
|
||||
shield_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Mice and Keyboards
|
||||
//CHECK LOGITECH DEVICES
|
||||
if (std::find(params.device_battery.begin(), params.device_battery.end(), "mouse") != params.device_battery.end()) {
|
||||
check_mouse = true;
|
||||
if (fileName.find("hidpp_battery") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
device_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void device_info () {
|
||||
std::unique_lock<std::mutex> l(device_lock);
|
||||
device_count = 0;
|
||||
device_data.clear();
|
||||
//gamepad counters
|
||||
int xbox_counter = 0;
|
||||
int ds4_counter = 0;
|
||||
int ds5_counter = 0;
|
||||
int switch_counter = 0;
|
||||
int bitdo_counter = 0;
|
||||
int shield_counter = 0;
|
||||
|
||||
for (auto &path : list ) {
|
||||
//Set devices paths
|
||||
std::string capacity = path + "/capacity";
|
||||
std::string capacity_level = path + "/capacity_level";
|
||||
std::string status = path + "/status";
|
||||
std::string model = path + "/model_name";
|
||||
std::ifstream input_capacity(capacity);
|
||||
std::ifstream input_capacity_level(capacity_level);
|
||||
std::ifstream input_status(status);
|
||||
std::ifstream device_name(model);
|
||||
std::string line;
|
||||
|
||||
device_data.push_back(device_batt());
|
||||
|
||||
// GAMEPADS
|
||||
//Xone and xpadneo devices
|
||||
if (check_gamepad == true) {
|
||||
if (path.find("gip") != std::string::npos || path.find("xpadneo") != std::string::npos) {
|
||||
if (xbox_count == 1 )
|
||||
device_data[device_count].name = "XBOX PAD";
|
||||
else
|
||||
device_data[device_count].name = "XBOX PAD-" + to_string(xbox_counter + 1);
|
||||
xbox_counter++;
|
||||
}
|
||||
//DualShock 4 devices
|
||||
if (path.find("sony_controller") != std::string::npos) {
|
||||
if (ds4_count == 1)
|
||||
device_data[device_count].name = "DS4 PAD";
|
||||
else
|
||||
device_data[device_count].name = "DS4 PAD-" + to_string(ds4_counter + 1);
|
||||
ds4_counter++;
|
||||
}
|
||||
//DualSense 5 devices
|
||||
//Dual Shock 4 added to hid-playstation in Linux 6.2
|
||||
if (path.find("ps-controller") != std::string::npos) {
|
||||
if (ds5_count == 1)
|
||||
device_data[device_count].name = "DS4/5 PAD";
|
||||
else
|
||||
device_data[device_count].name = "DS4/5 PAD-" + to_string(ds5_counter + 1);
|
||||
ds5_counter++;
|
||||
}
|
||||
//Nintendo Switch devices
|
||||
if (path.find("nintendo_switch_controller") != std::string::npos) {
|
||||
if (switch_count == 1)
|
||||
device_data[device_count].name = "SWITCH PAD";
|
||||
else
|
||||
device_data[device_count].name = "SWITCH PAD-" + to_string(switch_counter + 1);
|
||||
switch_counter++;
|
||||
}
|
||||
//8bitdo devices
|
||||
if (path.find("hid-e4") != std::string::npos) {
|
||||
if (bitdo_count == 1)
|
||||
device_data[device_count].name = "8BITDO PAD";
|
||||
else
|
||||
device_data[device_count].name = "8BITDO PAD-" + to_string(bitdo_counter + 1);
|
||||
bitdo_counter++;
|
||||
}
|
||||
//Shield devices
|
||||
if (path.find("thunderstrike") != std::string::npos) {
|
||||
if (shield_count == 1)
|
||||
device_data[device_count].name = "SHIELD PAD";
|
||||
else
|
||||
device_data[device_count].name = "SHIELD PAD-" + to_string(shield_counter + 1);
|
||||
shield_counter++;
|
||||
}
|
||||
}
|
||||
|
||||
// MICE AND KEYBOARDS
|
||||
//Logitech Devices
|
||||
if (check_mouse == true) {
|
||||
if (path.find("hidpp_battery") != std::string::npos) {
|
||||
// Find a good way truncate name or retreive device type before using this
|
||||
// if (std::getline(device_name, line)) {
|
||||
// device_data[device_count].name = line;
|
||||
// }
|
||||
device_data[device_count].name = "LOGI MOUSE/KB";
|
||||
}
|
||||
}
|
||||
|
||||
//Get device charging status
|
||||
if (std::getline(input_status, line)) {
|
||||
if (line == "Charging" || line == "Full")
|
||||
device_data[device_count].is_charging = true;
|
||||
}
|
||||
//Get device Battery
|
||||
if (fs::exists(capacity)) {
|
||||
if (std::getline(input_capacity, line)) {
|
||||
device_data[device_count].battery_percent = line;
|
||||
device_data[device_count].report_percent = true;
|
||||
switch(std::stoi(line)) {
|
||||
case 0 ... 25:
|
||||
device_data[device_count].battery = "Low";
|
||||
break;
|
||||
case 26 ... 49:
|
||||
device_data[device_count].battery = "Normal";
|
||||
break;
|
||||
case 50 ... 74:
|
||||
device_data[device_count].battery = "High";
|
||||
break;
|
||||
case 75 ... 100:
|
||||
device_data[device_count].battery = "Full";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (std::getline(input_capacity_level, line)) {
|
||||
device_data[device_count].battery = line;
|
||||
}
|
||||
}
|
||||
std::sort(device_data.begin(), device_data.end());
|
||||
device_count += 1;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#ifndef MANGOHUD_DEVICE_H
|
||||
#define MANGOHUD_DEVICE_H
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "overlay_params.h"
|
||||
struct overlay_params;
|
||||
struct device_batt {
|
||||
std::string battery;
|
||||
std::string name;
|
||||
bool report_percent;
|
||||
std::string battery_percent;
|
||||
bool is_charging;
|
||||
};
|
||||
|
||||
extern std::vector<device_batt> device_data;
|
||||
extern std::mutex device_lock;
|
||||
|
||||
extern bool device_found;
|
||||
extern int device_count;
|
||||
void device_update(const overlay_params& params);
|
||||
void device_info();
|
||||
|
||||
|
||||
#endif // MANGOHUD_DEVICE_H
|
@ -0,0 +1,181 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mesa/util/os_time.h>
|
||||
#include <numeric>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
#include <stdexcept>
|
||||
#include <iomanip>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
struct metric_t {
|
||||
std::string name;
|
||||
float value;
|
||||
std::string display_name;
|
||||
};
|
||||
|
||||
class fpsMetrics {
|
||||
private:
|
||||
std::vector<std::pair<uint64_t, float>> fps_stats;
|
||||
std::thread thread;
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
bool run = false;
|
||||
bool thread_init = false;
|
||||
bool terminate = false;
|
||||
bool resetting = false;
|
||||
|
||||
void _thread() {
|
||||
thread_init = true;
|
||||
while (true){
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait(lock, [this] { return run; });
|
||||
|
||||
if (terminate)
|
||||
break;
|
||||
|
||||
calculate();
|
||||
|
||||
run = false;
|
||||
}
|
||||
}
|
||||
|
||||
void calculate(){
|
||||
std::vector<float> sorted_values;
|
||||
for (const auto& p : fps_stats)
|
||||
sorted_values.push_back(p.second);
|
||||
|
||||
std::sort(sorted_values.begin(), sorted_values.end());
|
||||
|
||||
auto it = metrics.begin();
|
||||
while (it != metrics.end()) {
|
||||
if (it->name == "AVG") {
|
||||
it->display_name = it->name;
|
||||
if (!fps_stats.empty()) {
|
||||
float sum = std::accumulate(fps_stats.begin(), fps_stats.end(), 0.0f,
|
||||
[](float acc, const std::pair<uint64_t, float>& p) {
|
||||
return acc + 1000.f / p.second;
|
||||
});
|
||||
it->value = 1000.f / (sum / fps_stats.size());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
float val = std::stof(it->name);
|
||||
if (val <= 0 || val >= 1 ) {
|
||||
SPDLOG_DEBUG("Failed to use fps metric, it's out of range {}", it->name);
|
||||
it = metrics.erase(it);
|
||||
break;
|
||||
}
|
||||
float multiplied_val = val * 100;
|
||||
std::ostringstream stream;
|
||||
if (multiplied_val == static_cast<int>(multiplied_val)) {
|
||||
stream << std::fixed << std::setprecision(0) << multiplied_val << "%";
|
||||
} else {
|
||||
stream << std::fixed << std::setprecision(1) << multiplied_val << "%";
|
||||
}
|
||||
it->display_name = stream.str();
|
||||
uint64_t idx = val * sorted_values.size() - 1;
|
||||
if (idx >= sorted_values.size())
|
||||
break;
|
||||
|
||||
it->value = sorted_values[idx];
|
||||
} catch (const std::invalid_argument& e) {
|
||||
SPDLOG_DEBUG("Failed to use fps metric value {}", it->name);
|
||||
it = metrics.erase(it);
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<metric_t> add_metrics_to_vector(std::vector<std::string> values) {
|
||||
std::vector<metric_t> _metrics;
|
||||
for (auto& val : values){
|
||||
for(char& c : val) {
|
||||
c = std::toupper(static_cast<unsigned char>(c));
|
||||
}
|
||||
_metrics.push_back({val, 0.0f});
|
||||
}
|
||||
return _metrics;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<metric_t> metrics;
|
||||
|
||||
fpsMetrics(std::vector<std::string> values){
|
||||
metrics = add_metrics_to_vector(values);
|
||||
|
||||
if (!thread_init)
|
||||
thread = std::thread(&fpsMetrics::_thread, this);
|
||||
};
|
||||
|
||||
fpsMetrics(std::vector<std::string> values, std::vector<float> only_fps) {
|
||||
metrics = add_metrics_to_vector(values);
|
||||
for (auto& fps : only_fps)
|
||||
fps_stats.push_back({0, fps});
|
||||
|
||||
calculate();
|
||||
};
|
||||
|
||||
void update(uint64_t now, double fps){
|
||||
if (resetting)
|
||||
return;
|
||||
|
||||
if (fps > 0.0001)
|
||||
fps_stats.push_back({now, fps});
|
||||
|
||||
uint64_t ten_minute_duration = 600000000000ULL; // 10 minutes in nanoseconds
|
||||
|
||||
// Check if the system's uptime is less than 10 minutes
|
||||
if (now >= ten_minute_duration) {
|
||||
uint64_t ten_minutes_ago = now - ten_minute_duration;
|
||||
|
||||
fps_stats.erase(
|
||||
std::remove_if(
|
||||
fps_stats.begin(),
|
||||
fps_stats.end(),
|
||||
[ten_minutes_ago](const std::pair<uint64_t, float>& entry) {
|
||||
return entry.first < ten_minutes_ago;
|
||||
}
|
||||
),
|
||||
fps_stats.end()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void update_thread(){
|
||||
if (resetting)
|
||||
return;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
run = true;
|
||||
}
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void reset_metrics(){
|
||||
resetting = true;
|
||||
while (run){}
|
||||
fps_stats.clear();
|
||||
resetting = false;
|
||||
}
|
||||
|
||||
~fpsMetrics(){
|
||||
terminate = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
run = true;
|
||||
}
|
||||
cv.notify_one();
|
||||
if (thread.joinable())
|
||||
thread.join();
|
||||
}
|
||||
};
|
||||
|
||||
extern std::unique_ptr<fpsMetrics> fpsmetrics;
|
@ -1,168 +0,0 @@
|
||||
#include "gamepad.h"
|
||||
#include <filesystem.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace fs = ghc::filesystem;
|
||||
using namespace std;
|
||||
std::vector<gamepad> gamepad_data;
|
||||
std::vector<std::string> list;
|
||||
bool gamepad_found = false;
|
||||
int gamepad_count = 0;
|
||||
int xbox_count = 0;
|
||||
int ds4_count = 0;
|
||||
int ds5_count = 0;
|
||||
int switch_count = 0;
|
||||
int bitdo_count = 0;
|
||||
std::string xbox_paths [2]{"gip","xpadneo"};
|
||||
|
||||
static bool operator<(const gamepad& a, const gamepad& b)
|
||||
{
|
||||
return a.name < b.name;
|
||||
}
|
||||
|
||||
|
||||
void gamepad_update(){
|
||||
fs::path path("/sys/class/power_supply");
|
||||
list.clear();
|
||||
xbox_count = 0;
|
||||
ds4_count = 0;
|
||||
ds5_count = 0;
|
||||
switch_count = 0;
|
||||
bitdo_count = 0;
|
||||
for (auto &p : fs::directory_iterator(path)) {
|
||||
string fileName = p.path().filename();
|
||||
//CHECK XONE AND XPADNEO DEVICES
|
||||
for (string n : xbox_paths ) {
|
||||
if (fileName.find(n) != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
gamepad_found = true;
|
||||
xbox_count += 1;
|
||||
}
|
||||
}
|
||||
//CHECK FOR DUAL SHOCK 4 DEVICES
|
||||
if (fileName.find("sony_controller") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
gamepad_found = true;
|
||||
ds4_count +=1 ;
|
||||
}
|
||||
if (fileName.find("ps-controller") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
gamepad_found = true;
|
||||
ds5_count +=1 ;
|
||||
}
|
||||
//CHECK FOR NINTENDO SWITCH DEVICES
|
||||
if (fileName.find("nintendo_switch_controller") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
gamepad_found = true;
|
||||
switch_count += 1;
|
||||
}
|
||||
//CHECK * BITDO DEVICES
|
||||
if (fileName.find("hid-e4") != std::string::npos) {
|
||||
list.push_back(p.path());
|
||||
gamepad_found = true;
|
||||
bitdo_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gamepad_info () {
|
||||
gamepad_count = 0;
|
||||
gamepad_data.clear();
|
||||
int xbox_counter = 0;
|
||||
int ds4_counter = 0;
|
||||
int ds5_counter = 0;
|
||||
int switch_counter = 0;
|
||||
int bitdo_counter = 0;
|
||||
|
||||
for (auto &path : list ) {
|
||||
//Set devices paths
|
||||
std::string capacity = path + "/capacity";
|
||||
std::string capacity_level = path + "/capacity_level";
|
||||
std::string status = path + "/status";
|
||||
std::ifstream input_capacity(capacity);
|
||||
std::ifstream input_capacity_level(capacity_level);
|
||||
std::ifstream input_status(status);
|
||||
std::string line;
|
||||
|
||||
gamepad_data.push_back(gamepad());
|
||||
|
||||
//Xone and xpadneo devices
|
||||
if (path.find("gip") != std::string::npos || path.find("xpadneo") != std::string::npos) {
|
||||
if (xbox_count == 1 )
|
||||
gamepad_data[gamepad_count].name = "XBOX PAD";
|
||||
else
|
||||
gamepad_data[gamepad_count].name = "XBOX PAD-" + to_string(xbox_counter + 1);
|
||||
xbox_counter++;
|
||||
}
|
||||
//DualShock 4 devices
|
||||
if (path.find("sony_controller") != std::string::npos) {
|
||||
if (ds4_count == 1)
|
||||
gamepad_data[gamepad_count].name = "DS4 PAD";
|
||||
else
|
||||
gamepad_data[gamepad_count].name = "DS4 PAD-" + to_string(ds4_counter + 1);
|
||||
ds4_counter++;
|
||||
}
|
||||
//DualSense 5 devices
|
||||
//Dual Shock 4 added to hid-playstation in Linux 6.2
|
||||
if (path.find("ps-controller") != std::string::npos) {
|
||||
if (ds5_count == 1)
|
||||
gamepad_data[gamepad_count].name = "DS4/5 PAD";
|
||||
else
|
||||
gamepad_data[gamepad_count].name = "DS4/5 PAD-" + to_string(ds5_counter + 1);
|
||||
ds5_counter++;
|
||||
}
|
||||
//Nintendo Switch devices
|
||||
if (path.find("nintendo_switch_controller") != std::string::npos) {
|
||||
if (switch_count == 1)
|
||||
gamepad_data[gamepad_count].name = "SWITCH PAD";
|
||||
else
|
||||
gamepad_data[gamepad_count].name = "SWITCH PAD-" + to_string(switch_counter + 1);
|
||||
switch_counter++;
|
||||
}
|
||||
//8bitdo devices
|
||||
if (path.find("hid-e4") != std::string::npos) {
|
||||
if (bitdo_count == 1)
|
||||
gamepad_data[gamepad_count].name = "8BITDO PAD";
|
||||
else
|
||||
gamepad_data[gamepad_count].name = "8BITDO PAD-" + to_string(bitdo_counter + 1);
|
||||
bitdo_counter++;
|
||||
}
|
||||
//Get device charging status
|
||||
if (std::getline(input_status, line)) {
|
||||
if (line == "Charging" || line == "Full")
|
||||
gamepad_data[gamepad_count].is_charging = true;
|
||||
}
|
||||
//Get device Battery
|
||||
if (fs::exists(capacity)) {
|
||||
if (std::getline(input_capacity, line)) {
|
||||
gamepad_data[gamepad_count].battery_percent = line;
|
||||
gamepad_data[gamepad_count].report_percent = true;
|
||||
switch(std::stoi(line)) {
|
||||
case 0 ... 25:
|
||||
gamepad_data[gamepad_count].battery = "Low";
|
||||
break;
|
||||
case 26 ... 49:
|
||||
gamepad_data[gamepad_count].battery = "Normal";
|
||||
break;
|
||||
case 50 ... 74:
|
||||
gamepad_data[gamepad_count].battery = "High";
|
||||
break;
|
||||
case 75 ... 100:
|
||||
gamepad_data[gamepad_count].battery = "Full";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (std::getline(input_capacity_level, line)) {
|
||||
gamepad_data[gamepad_count].battery = line;
|
||||
}
|
||||
}
|
||||
std::sort(gamepad_data.begin(), gamepad_data.end());
|
||||
gamepad_count += 1;
|
||||
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef MANGOHUD_GAMEPAD_H
|
||||
#define MANGOHUD_GAMEPAD_H
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
struct gamepad {
|
||||
std::string battery;
|
||||
std::string name;
|
||||
bool report_percent;
|
||||
std::string battery_percent;
|
||||
bool is_charging;
|
||||
};
|
||||
|
||||
extern std::vector<gamepad> gamepad_data;
|
||||
|
||||
extern bool gamepad_found;
|
||||
extern int gamepad_count;
|
||||
void gamepad_update();
|
||||
void gamepad_info();
|
||||
|
||||
|
||||
#endif // MANGOHUD_GAMEPAD_H
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,51 @@
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <filesystem.h>
|
||||
#include <inttypes.h>
|
||||
#include <mesa/util/os_time.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "gpu.h"
|
||||
#include "hud_elements.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
namespace fs = ghc::filesystem;
|
||||
|
||||
class Intel {
|
||||
private:
|
||||
bool init = false;
|
||||
bool runtime = false;
|
||||
bool stop = false;
|
||||
struct gpuInfo gpu_info_intel {};
|
||||
FILE* fdinfo;
|
||||
struct stat stat_buffer;
|
||||
std::thread thread;
|
||||
|
||||
FILE* find_fd();
|
||||
void intel_gpu_thread();
|
||||
uint64_t get_gpu_time();
|
||||
void get_fdinfo();
|
||||
|
||||
public:
|
||||
Intel() {
|
||||
if (stat("/run/pressure-vessel", &stat_buffer) == 0)
|
||||
runtime = true;
|
||||
|
||||
fdinfo = find_fd();
|
||||
// thread = std::thread(&Intel::intel_gpu_thread, this);
|
||||
}
|
||||
|
||||
void update() {
|
||||
if (fdinfo)
|
||||
get_fdinfo();
|
||||
|
||||
gpu_info = gpu_info_intel;
|
||||
}
|
||||
|
||||
// ~Intel(){
|
||||
// stop = true;
|
||||
// thread.join();
|
||||
// }
|
||||
};
|
||||
|
||||
extern std::unique_ptr<Intel> intel;
|
@ -0,0 +1,79 @@
|
||||
#include <filesystem.h>
|
||||
#include <mesa/util/os_time.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "msm.h"
|
||||
std::unique_ptr<MSM> msm;
|
||||
namespace fs = ghc::filesystem;
|
||||
|
||||
uint64_t MSM::get_gpu_time() {
|
||||
char line[256];
|
||||
uint64_t total_val = 0;
|
||||
for (auto fd : fdinfo) {
|
||||
rewind(fd);
|
||||
fflush(fd);
|
||||
uint64_t val = 0;
|
||||
while (fgets(line, sizeof(line), fd)){
|
||||
if (sscanf(line, "drm-engine-gpu: %" SCNu64 " ns", &val) == 1) {
|
||||
total_val += val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total_val;
|
||||
}
|
||||
|
||||
void MSM::find_fd() {
|
||||
DIR* dir = opendir("/proc/self/fdinfo");
|
||||
if (!dir) {
|
||||
perror("Failed to open directory");
|
||||
}
|
||||
|
||||
for (const auto& entry : fs::directory_iterator("/proc/self/fdinfo")){
|
||||
FILE* file = fopen(entry.path().string().c_str(), "r");
|
||||
|
||||
if (!file) continue;
|
||||
|
||||
char line[256];
|
||||
bool found_driver = false;
|
||||
while (fgets(line, sizeof(line), file)) {
|
||||
if (strstr(line, "msm") != NULL)
|
||||
found_driver = true;
|
||||
|
||||
if (found_driver) {
|
||||
if(strstr(line, "drm-engine-gpu")) {
|
||||
fdinfo.push_back(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_driver)
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void MSM::get_fdinfo() {
|
||||
static uint64_t previous_gpu_time, previous_time, now, gpu_time_now;
|
||||
gpu_time_now = get_gpu_time();
|
||||
now = os_time_get_nano();
|
||||
|
||||
if (previous_time && previous_gpu_time && gpu_time_now > previous_gpu_time){
|
||||
float time_since_last = now - previous_time;
|
||||
float gpu_since_last = gpu_time_now - previous_gpu_time;
|
||||
auto result = int((gpu_since_last / time_since_last) * 100);
|
||||
if (result > 100)
|
||||
result = 100;
|
||||
|
||||
gpu_info_msm.load = result;
|
||||
previous_gpu_time = gpu_time_now;
|
||||
previous_time = now;
|
||||
} else {
|
||||
previous_gpu_time = gpu_time_now;
|
||||
previous_time = now;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "gpu.h"
|
||||
|
||||
class MSM {
|
||||
private:
|
||||
struct gpuInfo gpu_info_msm {};
|
||||
std::vector<FILE*> fdinfo;
|
||||
void find_fd();
|
||||
uint64_t get_gpu_time();
|
||||
void get_fdinfo();
|
||||
|
||||
public:
|
||||
MSM() {
|
||||
find_fd();
|
||||
}
|
||||
|
||||
~MSM() {
|
||||
for (size_t i = 0; i < fdinfo.size(); i++) {
|
||||
fclose(fdinfo[i]);
|
||||
}
|
||||
fdinfo.clear();
|
||||
}
|
||||
|
||||
void update() {
|
||||
if (!fdinfo.empty())
|
||||
get_fdinfo();
|
||||
|
||||
gpu_info = gpu_info_msm;
|
||||
}
|
||||
};
|
||||
|
||||
extern std::unique_ptr<MSM> msm;
|
@ -0,0 +1,58 @@
|
||||
#include "net.h"
|
||||
#include "hud_elements.h"
|
||||
|
||||
Net::Net() {
|
||||
should_reset = false;
|
||||
fs::path net_dir(NETDIR);
|
||||
if (fs::exists(net_dir) && fs::is_directory(net_dir)) {
|
||||
for (const auto& entry : fs::directory_iterator(net_dir)) {
|
||||
if (fs::is_directory(entry.status())) {
|
||||
auto val = entry.path().filename().string();
|
||||
if (val == "lo")
|
||||
continue;
|
||||
|
||||
if (!HUDElements.params->network.empty() && HUDElements.params->network.front() == "1") {
|
||||
interfaces.push_back({entry.path().filename().string(), 0, 0});
|
||||
} else if (!HUDElements.params->network.empty()){
|
||||
auto it = std::find(HUDElements.params->network.begin(), HUDElements.params->network.end(), val);
|
||||
if (it != HUDElements.params->network.end())
|
||||
interfaces.push_back({entry.path().filename().string(), 0, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interfaces.empty())
|
||||
SPDLOG_ERROR("Network: couldn't find any interfaces");
|
||||
}
|
||||
|
||||
void Net::update() {
|
||||
if (!interfaces.empty()) {
|
||||
for (auto& iface : interfaces) {
|
||||
// path to tx_bytes and rx_bytes
|
||||
std::string txfile = (NETDIR + iface.name + TXFILE);
|
||||
std::string rxfile = (NETDIR + iface.name + RXFILE);
|
||||
|
||||
// amount of bytes at previous update
|
||||
uint64_t prevTx = iface.txBytes;
|
||||
uint64_t prevRx = iface.rxBytes;
|
||||
|
||||
// current amount of bytes
|
||||
iface.txBytes = std::stoll(read_line(txfile));
|
||||
iface.rxBytes = std::stoll(read_line(rxfile));
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
// calculate the bytes per second since last update
|
||||
iface.txBps = calculateThroughput(iface.txBytes, prevTx, iface.previousTime, now);
|
||||
iface.rxBps = calculateThroughput(iface.rxBytes, prevRx, iface.previousTime, now);
|
||||
iface.previousTime = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Net::calculateThroughput(long long currentBytes, long long previousBytes,
|
||||
std::chrono::steady_clock::time_point previousTime,
|
||||
std::chrono::steady_clock::time_point currentTime) {
|
||||
std::chrono::duration<double> elapsed = (currentTime - previousTime);
|
||||
return static_cast<long long>((currentBytes - previousBytes) / elapsed.count());
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include "filesystem.h"
|
||||
#include "file_utils.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = ghc::filesystem;
|
||||
|
||||
#ifndef NETDIR
|
||||
#define NETDIR "/sys/class/net/"
|
||||
#endif
|
||||
|
||||
#ifndef TXFILE
|
||||
#define TXFILE "/statistics/tx_bytes"
|
||||
#endif
|
||||
|
||||
#ifndef RXFILE
|
||||
#define RXFILE "/statistics/rx_bytes"
|
||||
#endif
|
||||
|
||||
class Net {
|
||||
public:
|
||||
bool should_reset = false;
|
||||
struct networkInterface {
|
||||
std::string name;
|
||||
uint64_t txBytes;
|
||||
uint64_t rxBytes;
|
||||
uint64_t txBps;
|
||||
uint64_t rxBps;
|
||||
std::chrono::steady_clock::time_point previousTime;
|
||||
};
|
||||
|
||||
Net();
|
||||
void update();
|
||||
std::vector<networkInterface> interfaces = {};
|
||||
|
||||
private:
|
||||
uint64_t calculateThroughput(long long currentBytes, long long previousBytes,
|
||||
std::chrono::steady_clock::time_point previousTime,
|
||||
std::chrono::steady_clock::time_point currentTime);
|
||||
};
|
||||
|
||||
extern std::unique_ptr<Net> net;
|
@ -0,0 +1,98 @@
|
||||
#include "shell.h"
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <sys/wait.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "string_utils.h"
|
||||
#include <array>
|
||||
|
||||
std::string Shell::readOutput() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
|
||||
std::array<char, 128> buffer;
|
||||
std::string result;
|
||||
ssize_t count;
|
||||
while ((count = ::read(from_shell[0], buffer.data(), buffer.size())) > 0) {
|
||||
result.append(buffer.data(), count);
|
||||
}
|
||||
|
||||
// Split the result into lines and return the last line
|
||||
std::istringstream stream(result);
|
||||
std::string line;
|
||||
std::string last_line;
|
||||
while (std::getline(stream, line)) {
|
||||
last_line = line;
|
||||
}
|
||||
|
||||
SPDLOG_DEBUG("Shell: recieved output: {}", last_line);
|
||||
return last_line;
|
||||
}
|
||||
|
||||
Shell::Shell() {
|
||||
if (stat("/run/pressure-vessel", &stat_buffer) == 0)
|
||||
runtime = true;
|
||||
|
||||
static bool failed;
|
||||
if (pipe(to_shell) == -1) {
|
||||
SPDLOG_ERROR("Failed to create to_shell pipe: {}", strerror(errno));
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (pipe(from_shell) == -1) {
|
||||
SPDLOG_ERROR("Failed to create from_shell pipe: {}", strerror(errno));
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// if either pipe fails, there's no point in continuing.
|
||||
if (failed){
|
||||
SPDLOG_ERROR("Shell has failed, will not be able to use exec");
|
||||
return;
|
||||
}
|
||||
|
||||
shell_pid = fork();
|
||||
|
||||
if (shell_pid == 0) { // Child process
|
||||
close(to_shell[1]);
|
||||
close(from_shell[0]);
|
||||
|
||||
dup2(to_shell[0], STDIN_FILENO);
|
||||
dup2(from_shell[1], STDOUT_FILENO);
|
||||
dup2(from_shell[1], STDERR_FILENO);
|
||||
execl("/bin/sh", "sh", "-c", "unset LD_PRELOAD; exec /bin/sh", nullptr);
|
||||
exit(1); // Exit if execl fails
|
||||
} else {
|
||||
close(to_shell[0]);
|
||||
close(from_shell[1]);
|
||||
|
||||
// Set the read end of the from_shell pipe to non-blocking
|
||||
setNonBlocking(from_shell[0]);
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
|
||||
std::string Shell::exec(std::string cmd) {
|
||||
if (!success)
|
||||
return "";
|
||||
|
||||
writeCommand(cmd);
|
||||
return readOutput();
|
||||
}
|
||||
|
||||
void Shell::writeCommand(std::string command) {
|
||||
if (write(to_shell[1], command.c_str(), command.length()) == -1)
|
||||
SPDLOG_ERROR("Failed to write to shell");
|
||||
|
||||
if (runtime)
|
||||
command = "steam-runtime-launch-client --alongside-steam --host -- " + command;
|
||||
|
||||
trim(command);
|
||||
SPDLOG_DEBUG("Shell: wrote command: {}", command);
|
||||
}
|
||||
|
||||
Shell::~Shell() {
|
||||
if (write(to_shell[1], "exit\n", 5) == -1)
|
||||
SPDLOG_ERROR("Failed exit shell");
|
||||
close(to_shell[1]);
|
||||
close(from_shell[0]);
|
||||
waitpid(shell_pid, nullptr, 0);
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#ifdef __linux__
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <sys/stat.h>
|
||||
|
||||
class Shell {
|
||||
private:
|
||||
int to_shell[2];
|
||||
int from_shell[2];
|
||||
pid_t shell_pid;
|
||||
bool success;
|
||||
struct stat stat_buffer;
|
||||
bool runtime = false;
|
||||
|
||||
#ifdef __linux__
|
||||
void setNonBlocking(int fd) {
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
#endif
|
||||
|
||||
void writeCommand(std::string command);
|
||||
std::string readOutput();
|
||||
|
||||
public:
|
||||
Shell();
|
||||
~Shell();
|
||||
std::string exec(std::string cmd);
|
||||
|
||||
};
|
||||
|
||||
extern std::unique_ptr<Shell> shell;
|
@ -0,0 +1,13 @@
|
||||
#include <wayland-client.h>
|
||||
#include <vector>
|
||||
|
||||
#ifndef KeySym
|
||||
typedef unsigned long KeySym;
|
||||
#endif
|
||||
|
||||
extern void* wl_handle;
|
||||
extern struct wl_display* wl_display_ptr;
|
||||
extern std::vector<KeySym> wl_pressed_keys;
|
||||
|
||||
void init_wayland_data();
|
||||
void update_wl_queue();
|
@ -0,0 +1,138 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <wayland-client.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <sys/mman.h>
|
||||
#include "wayland_hook.h"
|
||||
#include "timing.hpp"
|
||||
#include "keybinds.h"
|
||||
|
||||
void* wl_handle = nullptr;
|
||||
struct wl_display* wl_display_ptr = nullptr;
|
||||
struct wl_seat* seat = nullptr;
|
||||
struct wl_keyboard* keyboard = nullptr;
|
||||
struct xkb_context *context_xkb = nullptr;
|
||||
struct xkb_keymap *keymap_xkb = nullptr;
|
||||
struct xkb_state *state_xkb = nullptr;
|
||||
struct wl_event_queue* queue = nullptr;
|
||||
std::vector<KeySym> wl_pressed_keys {};
|
||||
|
||||
static void seat_handle_capabilities(void *data, wl_seat *seat, uint32_t caps);
|
||||
static void seat_handle_name(void *data, struct wl_seat *seat, const char *name) {}
|
||||
|
||||
struct wl_seat_listener seat_listener {
|
||||
.capabilities = seat_handle_capabilities,
|
||||
.name = seat_handle_name,
|
||||
};
|
||||
|
||||
static void registry_handle_global(void *data, struct wl_registry* registry, uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
if(strcmp(interface, wl_seat_interface.name) == 0)
|
||||
{
|
||||
seat = (struct wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 5);
|
||||
wl_seat_add_listener(seat, &seat_listener, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name){}
|
||||
|
||||
static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size)
|
||||
{
|
||||
char* map_shm = (char*)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
|
||||
if(!context_xkb)
|
||||
context_xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
|
||||
if(keymap_xkb && state_xkb)
|
||||
{
|
||||
xkb_keymap_unref(keymap_xkb);
|
||||
xkb_state_unref(state_xkb);
|
||||
}
|
||||
|
||||
keymap_xkb = xkb_keymap_new_from_string(
|
||||
context_xkb, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
|
||||
state_xkb = xkb_state_new(keymap_xkb);
|
||||
|
||||
munmap((void*)map_shm, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void wl_keyboard_enter(void *user_data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys){}
|
||||
|
||||
static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface)
|
||||
{
|
||||
wl_pressed_keys.clear();
|
||||
}
|
||||
|
||||
static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
|
||||
{
|
||||
xkb_keycode_t keycode = key + 8;
|
||||
xkb_keysym_t keysym = xkb_state_key_get_one_sym(state_xkb, keycode);
|
||||
|
||||
if(state)
|
||||
{
|
||||
wl_pressed_keys.push_back(keysym);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = std::find(wl_pressed_keys.begin(), wl_pressed_keys.end(), keysym);
|
||||
if(it != wl_pressed_keys.end())
|
||||
wl_pressed_keys.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group){}
|
||||
|
||||
static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay){}
|
||||
|
||||
struct wl_registry_listener registry_listener {
|
||||
.global = registry_handle_global,
|
||||
.global_remove = registry_handle_global_remove
|
||||
};
|
||||
|
||||
struct wl_keyboard_listener keyboard_listener {
|
||||
.keymap = wl_keyboard_keymap,
|
||||
.enter = wl_keyboard_enter,
|
||||
.leave = wl_keyboard_leave,
|
||||
.key = wl_keyboard_key,
|
||||
.modifiers = wl_keyboard_modifiers,
|
||||
.repeat_info = wl_keyboard_repeat_info
|
||||
};
|
||||
|
||||
static void seat_handle_capabilities(void *data, wl_seat *seat, uint32_t caps)
|
||||
{
|
||||
if(caps & WL_SEAT_CAPABILITY_KEYBOARD)
|
||||
{
|
||||
if(!keyboard)
|
||||
{
|
||||
keyboard = wl_seat_get_keyboard(seat);
|
||||
wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_wl_queue()
|
||||
{
|
||||
wl_display_dispatch_queue_pending(wl_display_ptr, queue);
|
||||
}
|
||||
|
||||
void init_wayland_data()
|
||||
{
|
||||
if (!wl_display_ptr)
|
||||
return;
|
||||
|
||||
queue = wl_display_create_queue(wl_display_ptr);
|
||||
struct wl_display *display_wrapped = (struct wl_display*)wl_proxy_create_wrapper(wl_display_ptr);
|
||||
wl_proxy_set_queue((struct wl_proxy*)display_wrapped, queue);
|
||||
wl_registry *registry = wl_display_get_registry(display_wrapped);
|
||||
wl_proxy_wrapper_destroy(display_wrapped);
|
||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||
wl_display_roundtrip_queue(wl_display_ptr, queue);
|
||||
wl_display_roundtrip_queue(wl_display_ptr, queue);
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
#include "file_utils.h"
|
||||
#include <filesystem.h>
|
||||
#include <string>
|
||||
|
||||
namespace fs = ghc::filesystem;
|
||||
|
||||
class WineSync {
|
||||
private:
|
||||
enum syncMethods {
|
||||
NONE,
|
||||
WINESERVER,
|
||||
ESYNC,
|
||||
FSYNC,
|
||||
NTSYNC
|
||||
};
|
||||
|
||||
int method = 0;
|
||||
bool inside_wine = true;
|
||||
|
||||
const char* methods[5] = {
|
||||
"NONE",
|
||||
"Wserver",
|
||||
"Esync",
|
||||
"Fsync",
|
||||
"NTsync"
|
||||
};
|
||||
|
||||
public:
|
||||
WineSync() {
|
||||
#ifdef __linux__
|
||||
// check that's were inside wine
|
||||
std::string wineProcess = get_exe_path();
|
||||
auto n = wineProcess.find_last_of('/');
|
||||
std::string preloader = wineProcess.substr(n + 1);
|
||||
if (preloader != "wine-preloader" && preloader != "wine64-preloader"){
|
||||
inside_wine = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const char* paths[2] {
|
||||
"/proc/self/map_files",
|
||||
"/proc/self/fd"
|
||||
};
|
||||
|
||||
// check which sync wine is using, if any.
|
||||
fs::path path;
|
||||
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
|
||||
path = paths[i];
|
||||
for (auto& p : fs::directory_iterator(path)) {
|
||||
auto filepath = p.path().string();
|
||||
const char* filename = filepath.c_str();
|
||||
auto sym = read_symlink(filename);
|
||||
if (sym.find("winesync") != std::string::npos)
|
||||
method = syncMethods::NTSYNC;
|
||||
else if (sym.find("fsync") != std::string::npos)
|
||||
method = syncMethods::FSYNC;
|
||||
else if (sym.find("ntsync") != std::string::npos)
|
||||
method = syncMethods::NTSYNC;
|
||||
else if (sym.find("esync") != std::string::npos)
|
||||
method = syncMethods::ESYNC;
|
||||
|
||||
if (method)
|
||||
break;
|
||||
|
||||
}
|
||||
if (method)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
bool valid() {
|
||||
return inside_wine;
|
||||
}
|
||||
|
||||
// return sync method as display name
|
||||
std::string get_method() {
|
||||
return methods[method];
|
||||
}
|
||||
};
|
||||
|
||||
extern std::unique_ptr<WineSync> winesync_ptr;
|
@ -1,11 +1,13 @@
|
||||
[wrap-file]
|
||||
directory = imgui-1.81
|
||||
source_url = https://github.com/ocornut/imgui/archive/v1.81.tar.gz
|
||||
source_filename = imgui-1.81.tar.gz
|
||||
source_hash = f7c619e03a06c0f25e8f47262dbc32d61fd033d2c91796812bf0f8c94fca78fb
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/imgui_1.81-1/get_patch
|
||||
patch_filename = imgui-1.81-1-wrap.zip
|
||||
patch_hash = 6d00b442690b6a5c5d8f898311daafbce16d370cf64f53294c3b8c5c661e435f
|
||||
directory = imgui-1.89.9
|
||||
source_url = https://github.com/ocornut/imgui/archive/refs/tags/v1.89.9.tar.gz
|
||||
source_filename = imgui-1.89.9.tar.gz
|
||||
source_hash = 1acc27a778b71d859878121a3f7b287cd81c29d720893d2b2bf74455bf9d52d6
|
||||
patch_filename = imgui_1.89.9-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/imgui_1.89.9-1/get_patch
|
||||
patch_hash = 9b21290c597d76bf8d4eeb3f9ffa024b11d9ea6c61e91d648ccc90b42843d584
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/imgui_1.89.9-1/imgui-1.89.9.tar.gz
|
||||
wrapdb_version = 1.89.9-1
|
||||
|
||||
[provide]
|
||||
imgui = imgui_dep
|
||||
|
@ -0,0 +1,13 @@
|
||||
[wrap-file]
|
||||
directory = implot-0.16
|
||||
source_url = https://github.com/epezent/implot/archive/refs/tags/v0.16.zip
|
||||
source_filename = implot-0.16.zip
|
||||
source_hash = 24f772c688f6b8a6e19d7efc10e4923a04a915f13d487b08b83553aa62ae1708
|
||||
patch_filename = implot_0.16-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/implot_0.16-1/get_patch
|
||||
patch_hash = 1c6b1462066a5452fa50c1da1dd47fed841f28232972c82d778f2962936568c7
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/implot_0.16-1/implot-0.16.zip
|
||||
wrapdb_version = 0.16-1
|
||||
|
||||
[provide]
|
||||
implot = implot_dep
|
@ -0,0 +1,134 @@
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
class Test:
|
||||
def __init__(self):
|
||||
self.options = {}
|
||||
self.error_count = 0
|
||||
self.ignore_params = ["pci_dev", "mangoapp_steam", "fsr_steam_sharpness",
|
||||
"blacklist", "media_player_format"]
|
||||
# self.files_changed()
|
||||
self.get_options()
|
||||
self.get_param_defaults()
|
||||
self.find_options_in_readme()
|
||||
self.find_options_in_conf()
|
||||
|
||||
if self.error_count > 0:
|
||||
print(f"number of errors: {self.error_count}")
|
||||
exit(1)
|
||||
|
||||
def get_options(self):
|
||||
regex = r"\((.*?)\)"
|
||||
with open('../src/overlay_params.h') as f:
|
||||
for line in f:
|
||||
if ("OVERLAY_PARAM_BOOL" in line or "OVERLAY_PARAM_CUSTOM"
|
||||
in line) and not "#" in line:
|
||||
match = re.search(regex, line)
|
||||
if match:
|
||||
key = match.group(1)
|
||||
if key in self.ignore_params:
|
||||
continue
|
||||
else:
|
||||
self.options[key] = None
|
||||
|
||||
def find_options_in_readme(self):
|
||||
with open("../README.md") as f:
|
||||
file = f.read()
|
||||
for option in self.options:
|
||||
if not option in file:
|
||||
self.error_count += 1
|
||||
print(f"Option: {option} is not found in README.md")
|
||||
|
||||
def find_options_in_conf(self):
|
||||
with open("../data/MangoHud.conf") as f:
|
||||
file = f.read()
|
||||
for option, val in self.options.items():
|
||||
|
||||
if not option in file:
|
||||
self.error_count += 1
|
||||
print(f"Option: {option} is not found in MangoHud.conf")
|
||||
|
||||
if option in file:
|
||||
option = "# " + option
|
||||
for line in file.splitlines():
|
||||
if option in line:
|
||||
line = line.strip().split("=")
|
||||
if len(line) != 2:
|
||||
continue
|
||||
|
||||
key = line[0].strip("#").strip()
|
||||
if key not in self.options:
|
||||
continue
|
||||
|
||||
value = line[1].strip()
|
||||
if "," in value:
|
||||
value = value.split(",")
|
||||
|
||||
if self.options[key] != value:
|
||||
self.error_count += 1
|
||||
print(f"Sample config: option: {key} value is not the same as default")
|
||||
print(f"default: {self.options[key]}, config: {value}")
|
||||
print("")
|
||||
|
||||
def get_param_defaults(self):
|
||||
# Open the C++ file
|
||||
with open('../src/overlay_params.cpp', 'r') as f:
|
||||
# Read the contents of the file
|
||||
contents = f.read()
|
||||
|
||||
# Define the name of the function to search for
|
||||
function_name = 'set_param_defaults'
|
||||
|
||||
# Define a regular expression to match the function definition
|
||||
function_regex = re.compile(r"void\s+" +
|
||||
function_name + r"\s*\(([^)]*)\)\s*{(.+?)\s*}\s*\n",
|
||||
re.MULTILINE | re.DOTALL)
|
||||
|
||||
# Find the match of the regular expression in the file contents
|
||||
match = function_regex.search(contents)
|
||||
|
||||
# If the function is found, extract the contents
|
||||
if match:
|
||||
# Extract the contents of the function
|
||||
function_contents = match.group(2)
|
||||
for line in function_contents.splitlines():
|
||||
|
||||
# FIXME: Some variables get stored as string in a string
|
||||
if not "enabled" in line:
|
||||
line = line.replace("params->", "")
|
||||
line = line.strip().strip(";").split("=")
|
||||
if len(line) != 2:
|
||||
continue
|
||||
|
||||
key = line[0].strip()
|
||||
value = line[1].strip()
|
||||
if key not in self.options:
|
||||
continue
|
||||
|
||||
# convert to a list if it contains curly bracket
|
||||
if "{" in value:
|
||||
value = value.replace("{", "").replace("}", "").strip().split(", ")
|
||||
# If option has color in it's name we can assume it's value is
|
||||
# one or more colors and that they are in binary.
|
||||
# We want to convert this from binary because the config
|
||||
# will not be in this format
|
||||
if "color" in key:
|
||||
value = [hex[2:] for hex in value]
|
||||
value = [string.upper() for string in value]
|
||||
|
||||
# same reasoning as above
|
||||
if "color" in key and type(value) is str:
|
||||
value = value[2:]
|
||||
value = value.upper()
|
||||
|
||||
if "fps_sampling_period" in key:
|
||||
value = re.sub(r';\s*/\*.*?\*/', '', value)
|
||||
value = str(int(int(value) / 1000000))
|
||||
|
||||
# if value is a list, make sure we don't store str in str
|
||||
if type(value) == list:
|
||||
value = [element.strip('"') for element in value]
|
||||
|
||||
self.options[key] = value
|
||||
|
||||
Test()
|
Loading…
Reference in New Issue