mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 10:22:12 +02:00
Compare commits
797 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| acf610416c | |||
| 90d3b3d49f | |||
| bbc5b4de98 | |||
| 7f9b6187a6 | |||
| 8067e43631 | |||
| 61dba1ea05 | |||
| a94df76bfa | |||
| b2b748b07c | |||
| 77ce6b0e4c | |||
| d28d307b63 | |||
| 3492f9bfa1 | |||
| 79c67ec0d7 | |||
| 6527f03a9b | |||
| a8b629f58c | |||
| 5df00a3394 | |||
| 8a7c83cac6 | |||
| 27bffb3464 | |||
| 875afb7cde | |||
| 3e99a7035b | |||
| 63873763de | |||
| 27c83e88f2 | |||
| f48904805f | |||
| 531cd0909a | |||
| eafcdfec9e | |||
| d6bd855733 | |||
| af7e2d47d1 | |||
| f71c0b0866 | |||
| 1400291ae9 | |||
| 7799dbe481 | |||
| 8426833c36 | |||
| 235a2018fc | |||
| 470e3b4e57 | |||
| 1942dce980 | |||
| efca4f3597 | |||
| 43ddbdca85 | |||
| 5d29e26509 | |||
| 8daec2568c | |||
| 320e1bf4e0 | |||
| 38e98d3d51 | |||
| 50964fcddf | |||
| 6c7a8a8c41 | |||
| 92082eed54 | |||
| 26b3a84b8a | |||
| 0747fc0887 | |||
| 60fbe1fd0b | |||
| f8667d9faa | |||
| 1c89bbc34e | |||
| b37f020270 | |||
| fbc9569e13 | |||
| a923f61115 | |||
| bc0a515998 | |||
| 87d89130ad | |||
| f46aa316cb | |||
| fdb5f30cb8 | |||
| 91f1c4db10 | |||
| 44e2948b1d | |||
| b86acf6c36 | |||
| 9a15666248 | |||
| f5dfec6dcb | |||
| 8c55c06fa7 | |||
| 8159f473cc | |||
| dca3b154ef | |||
| 79b61f1391 | |||
| ba54763dde | |||
| 451bc394ee | |||
| 93f9ed1bf5 | |||
| d9e4389ed4 | |||
| b465052ad2 | |||
| 2e6b9cd503 | |||
| 0557f16c16 | |||
| 08e252a44c | |||
| 90e2d8f8d9 | |||
| 0dd8d80e02 | |||
| 3cddf31987 | |||
| fe1c5c8f6f | |||
| 994bd53d30 | |||
| 383a4190ca | |||
| 42f2e5a745 | |||
| 14b8bb2bb5 | |||
| b68ad3626c | |||
| cd25803e32 | |||
| 3d6bffe7fe | |||
| e694c8ac84 | |||
| dfff57dd85 | |||
| c5141855a2 | |||
| 6cb186f229 | |||
| e91f62aa70 | |||
| ab5cf94099 | |||
| d9eccd70ed | |||
| 025febc78d | |||
| 69cc19d6f5 | |||
| 2e4dd1d262 | |||
| 24ec0e6035 | |||
| 6e50aaf3ff | |||
| e7cd5f618d | |||
| 55e4896524 | |||
| 594c73461e | |||
| 94ceeb4482 | |||
| 4ee7dd72d0 | |||
| 414f7ce067 | |||
| e1dffe794b | |||
| 181b775f73 | |||
| c4c934d23e | |||
| 31c73543e8 | |||
| d79e1d7fbc | |||
| 3883047dce | |||
| 4eebc1b6ee | |||
| 08b79ff4ae | |||
| ffc38eb0f1 | |||
| 37ee193c40 | |||
| c10027f1d7 | |||
| 039fa1ba77 | |||
| 0db820a6c1 | |||
| 8b611899d1 | |||
| ba7e1eb18b | |||
| 6979b1efc5 | |||
| 4a61cdb1c9 | |||
| 75c28ec671 | |||
| 639252ece0 | |||
| a31d8ce87b | |||
| 41143c23f4 | |||
| 3c2e21b7b1 | |||
| 3eaebd3359 | |||
| 768bc154d4 | |||
| 00cc19e28b | |||
| bf0564f9e5 | |||
| dbb060eb62 | |||
| 1314c18a52 | |||
| 05f5e25c66 | |||
| 4b4c37ed88 | |||
| 8970774e5f | |||
| 69c86c95b7 | |||
| 910ba413da | |||
| dbed1b32b4 | |||
| 278a3834ce | |||
| 5ce3220f50 | |||
| c1c6ccce9a | |||
| e2526521a2 | |||
| 1956c66a5e | |||
| c601b68bc6 | |||
| 528f9f152c | |||
| 64d2c4410e | |||
| 6e4de3e9f1 | |||
| ce51dbf7ea | |||
| abc6c0593c | |||
| b63bd5b8c9 | |||
| 4236d830cc | |||
| c851c39837 | |||
| 1cd196b726 | |||
| f8a8864be5 | |||
| 683e90cec6 | |||
| 08de5eabed | |||
| c7ecc33b64 | |||
| ee7f574349 | |||
| 10f58e405e | |||
| d81a7b6278 | |||
| 48daf4ac1e | |||
| 6d01c0b438 | |||
| 3cc0e40e5a | |||
| 679def253d | |||
| 4dc5033c80 | |||
| b3603391eb | |||
| f6a363f7ad | |||
| 3e69361196 | |||
| 04bd5045b9 | |||
| 886bde47d6 | |||
| 254fb82520 | |||
| af884bb638 | |||
| e8e43c10fc | |||
| 9090dbd276 | |||
| f683586623 | |||
| d71d6a679d | |||
| 17eb22dfdb | |||
| c09a788ce3 | |||
| ab32056c3f | |||
| e15049856f | |||
| 16423487fb | |||
| 6b95eb2c74 | |||
| 2b82e9ab02 | |||
| 5fe2cc6350 | |||
| 50739ee890 | |||
| 97ce079488 | |||
| 7045dea284 | |||
| 7887067301 | |||
| 39cfc7aac8 | |||
| 44af8b85ad | |||
| 85594d2056 | |||
| 14cd933969 | |||
| 6550b77c2f | |||
| 7147f23504 | |||
| 94164b873c | |||
| 9cfe7a74bc | |||
| 604aacbae2 | |||
| 04c486d3db | |||
| 152f3cdee1 | |||
| b7890e1e2a | |||
| 8913620aa5 | |||
| 2a103f620d | |||
| 8d0ceff7fa | |||
| ce9f4daec4 | |||
| e639700387 | |||
| 3da344db1f | |||
| 31a35cf845 | |||
| 77c916f747 | |||
| c9dbf990d8 | |||
| ed4d903e69 | |||
| 299451ca62 | |||
| 6319b68c2c | |||
| c37ac4da2b | |||
| 643db92cd1 | |||
| 99795154b5 | |||
| 6cf403da7d | |||
| 4f3e82330b | |||
| 0c401a0112 | |||
| d4b1ae9751 | |||
| 831659966f | |||
| 5f261702a9 | |||
| c2da9b0cb1 | |||
| 239233f4e0 | |||
| b00593ae05 | |||
| b4e6688dfe | |||
| b022babd7e | |||
| 52c7f9f973 | |||
| e0eba8ce21 | |||
| cdd4cee968 | |||
| 1b23edc5c1 | |||
| e5d6006006 | |||
| 18f3809681 | |||
| 1cd87dc994 | |||
| 1e58e04024 | |||
| 408d939664 | |||
| 9b1061719c | |||
| 179539ccba | |||
| 6d73ecb944 | |||
| b981e4fd8b | |||
| 8e660136e0 | |||
| 90ca39b464 | |||
| 63f7e15554 | |||
| ab37216f7d | |||
| 57440a7a45 | |||
| f50059dc7b | |||
| 92fb5cb0c1 | |||
| 5f84630276 | |||
| 22696fe3de | |||
| cd00820546 | |||
| 918153f974 | |||
| 58e67dcc7a | |||
| 940d15c2e6 | |||
| 9ffa46652d | |||
| d45dc53da0 | |||
| 00d1812e8d | |||
| 50dc260fd6 | |||
| bf99aeda92 | |||
| 397822d0db | |||
| 12746a9681 | |||
| 355e21c099 | |||
| 9036b891a8 | |||
| b7e4e91664 | |||
| aabab6dff5 | |||
| 62c2eb4d96 | |||
| c69a6b7640 | |||
| 0521513609 | |||
| a1c5fe2bf2 | |||
| 2d4a77b6f0 | |||
| 9027f72353 | |||
| fe0f292cec | |||
| 06ecd5a1ff | |||
| 8fdaa987e3 | |||
| 15804091b0 | |||
| d8b44a5eae | |||
| 6357dcc6b4 | |||
| 947cddd926 | |||
| 246ff0bc88 | |||
| 339de56407 | |||
| 795bbde459 | |||
| f32bf9e94f | |||
| 8facb7c6e9 | |||
| 92c65aad10 | |||
| 76d6e86fe5 | |||
| e3d7eab4df | |||
| ff8d4b46ef | |||
| 4fb7860a8e | |||
| f188e9518d | |||
| a39419b12a | |||
| 941502d718 | |||
| de146c6370 | |||
| 09efb0d897 | |||
| 8defffabff | |||
| 1b543b1eb8 | |||
| ab17fb08ef | |||
| a4bc3c812d | |||
| c8fcabf4cc | |||
| a961a964ff | |||
| 412de625b2 | |||
| b739736527 | |||
| 2b19f8078e | |||
| 56eabbe2cb | |||
| b0215aae37 | |||
| 86cfa4eded | |||
| bab1e4ff82 | |||
| 6ec59f8654 | |||
| d3459af71f | |||
| 00b5f17f0d | |||
| b8ad833b4b | |||
| fb412576e1 | |||
| d551651d63 | |||
| 4e234c52a3 | |||
| edc718d1d1 | |||
| 42e01e7b8f | |||
| 8edfda979e | |||
| cc3595c3a4 | |||
| 8c72792e31 | |||
| f7ebeb58e2 | |||
| ea4a4f0691 | |||
| 81984e6e97 | |||
| f46e97fc62 | |||
| 7821552761 | |||
| cd8a0fc5f1 | |||
| 30f32f1fb5 | |||
| 9af918b1ef | |||
| 37b8bb4fc0 | |||
| 66edea702e | |||
| 930fb06ca7 | |||
| ca9c67353f | |||
| ea6ad4ac86 | |||
| 6ed8aac6bb | |||
| cfabee4ba7 | |||
| 3a35f9735b | |||
| 05fa8b0994 | |||
| 07f6f93415 | |||
| d2de3a01bb | |||
| c9b7c65ba5 | |||
| 6ad8e68d26 | |||
| b128260eb2 | |||
| ecfaf4d54d | |||
| 72bdee7fbf | |||
| bdb9593c0d | |||
| 1e234754c2 | |||
| 4a4afdadb8 | |||
| 7cadece9a0 | |||
| 724ab6ba69 | |||
| 6370485dcb | |||
| 60d343ca14 | |||
| bdc844f857 | |||
| d49c018f86 | |||
| be2201b5fc | |||
| 9ba1cd1291 | |||
| 22fa9b51e8 | |||
| f781f8d9ea | |||
| 06ecc9c480 | |||
| 50288d91e4 | |||
| 499b4ff445 | |||
| dd0021cd5e | |||
| f5b70dd8b7 | |||
| 8f8ca11203 | |||
| 121d399641 | |||
| 26ae4eb21b | |||
| 52a81dbdc4 | |||
| f924a8bf06 | |||
| c7363f904a | |||
| 53ffe8459f | |||
| 1b702e123e | |||
| 14dccce393 | |||
| d040cc810f | |||
| 224bea2559 | |||
| 777d3e599d | |||
| 7c47bc2656 | |||
| e4d31381d8 | |||
| abf8d75454 | |||
| 2ce152beaf | |||
| 766ed07999 | |||
| ddc2131343 | |||
| b64d8a013d | |||
| e6db59fdde | |||
| 7ea0bbfb21 | |||
| ab041e0ad2 | |||
| 207f742624 | |||
| 68f0b11b6f | |||
| b86c0880b9 | |||
| 75f4a3d92d | |||
| 70fa1341a8 | |||
| 0a55314d37 | |||
| 2198e6123e | |||
| dd37ee0e50 | |||
| 7006ae0420 | |||
| 86458ab32a | |||
| 67ca907e38 | |||
| e15d06bbad | |||
| eb53797174 | |||
| c8c32a78b0 | |||
| 1c032624ac | |||
| 5087ea77b4 | |||
| b6b42e69c3 | |||
| 000a688bb6 | |||
| 3e25b213b1 | |||
| a69cf456ea | |||
| 072b76cb12 | |||
| ca8f54d331 | |||
| 6423feaa94 | |||
| 92ada88ea7 | |||
| 1789ef85d7 | |||
| 42509f115c | |||
| 340a58bda1 | |||
| 08dfd1321c | |||
| 45d324a8c6 | |||
| afa986efd4 | |||
| bc6e8c7813 | |||
| 627fee23d1 | |||
| 27b474f205 | |||
| 418c7eecc7 | |||
| 350250d640 | |||
| 3cc21905af | |||
| c698eb8eb5 | |||
| 463c24beb8 | |||
| a5d1b96ecc | |||
| 63106ebdc1 | |||
| fd36783adb | |||
| 3b5186da53 | |||
| 0140a46090 | |||
| 9c38e1255f | |||
| 663f9d69ff | |||
| d12e5ab801 | |||
| 1d770d85f6 | |||
| 5b00afe45d | |||
| 5b276567ac | |||
| 56566916e3 | |||
| 2c0b4ca3a2 | |||
| 54ffdd32dc | |||
| 64f24bdee9 | |||
| 4706f97bf0 | |||
| 0bdb198d52 | |||
| fc7710821e | |||
| e22a9990ec | |||
| fc7f3a1650 | |||
| 915af9546d | |||
| 54a68640ef | |||
| ec1af7c92b | |||
| a1e20270ad | |||
| 4996986ce6 | |||
| e6de2d4fd9 | |||
| 5347e74a1b | |||
| 0ea5639a22 | |||
| aa3a916d5a | |||
| 6e64b758bf | |||
| 49ef7fc51f | |||
| 0e9a5acdbd | |||
| 4f6e80e940 | |||
| df87cdfa6e | |||
| dee4b36a46 | |||
| d04a26337a | |||
| 2afe8d11d1 | |||
| f31d3b8b21 | |||
| 8a20af9917 | |||
| fc5c2f6213 | |||
| 6c314e148a | |||
| a86d0017a9 | |||
| 4038ed2656 | |||
| 0f4bb47c2f | |||
| 19f7c67ede | |||
| ff7e81ed81 | |||
| 681a102a2b | |||
| ba27f4f2ef | |||
| cbe33a4860 | |||
| 5847e3f269 | |||
| b248dfa01c | |||
| 357aa77e21 | |||
| a44bde6f94 | |||
| 5825bce57d | |||
| 996cdd3155 | |||
| 61cdfcabf8 | |||
| 359a77097c | |||
| 1323d007d3 | |||
| d2215f26ac | |||
| 958e7dbd8f | |||
| 5b10cde59f | |||
| e8f2b9a1c7 | |||
| 0686d1db4a | |||
| 7c284f9ee8 | |||
| dc04273bd7 | |||
| d698be6239 | |||
| e44df27c96 | |||
| b365d4cca1 | |||
| 19d3c948ad | |||
| 4a581039d5 | |||
| 79849fb2b5 | |||
| c116a5abdf | |||
| 8f082730a8 | |||
| b81c4a04db | |||
| e26a05e3ac | |||
| f08a1fb0b4 | |||
| 2a82009ffe | |||
| e851f3a7ed | |||
| 3a865df09e | |||
| bae80e2932 | |||
| 4445e1e78d | |||
| f0e3d3d8e1 | |||
| a313fd1572 | |||
| 927d44d919 | |||
| 3b9a91e8af | |||
| 70b4582bb6 | |||
| 461552b8d0 | |||
| 0f3db37199 | |||
| 67f16b269a | |||
| 25671fa4b4 | |||
| 4eaa04d4a4 | |||
| 7592885b9f | |||
| 508a3fde36 | |||
| f0273224e0 | |||
| 31e833c02e | |||
| a20d915412 | |||
| d3453de73c | |||
| 79a11c4728 | |||
| 24591417ce | |||
| de6d066fc9 | |||
| 6362c896f8 | |||
| 0418e9ca5e | |||
| ba0eeab18c | |||
| 5ef630cbc8 | |||
| 1699d14984 | |||
| 501746a270 | |||
| e6bd1c340f | |||
| 49fc95689d | |||
| 3d84ad19be | |||
| 57aa0cf499 | |||
| 6496dcd7d0 | |||
| 1c066bc69a | |||
| 2f1e37bb7c | |||
| fd3c87abe7 | |||
| af075b8ef8 | |||
| 71d461266e | |||
| 163781557f | |||
| 16e6decebf | |||
| e10c9cc164 | |||
| 7ef994d0cf | |||
| 019c9f0f77 | |||
| 5fec607ebe | |||
| b065b3da51 | |||
| 1337525eb5 | |||
| 30ea52938e | |||
| d43ed33661 | |||
| 03aab33507 | |||
| 420d1cf804 | |||
| 5d901da81b | |||
| 5c783e64c0 | |||
| 2efa5a1869 | |||
| 5bfabe7a52 | |||
| c9c9a58f59 | |||
| 546908fc29 | |||
| 0a17da2da0 | |||
| bc6fd025cb | |||
| 9e49451340 | |||
| 4d185e64ac | |||
| 1fa9a61b3f | |||
| 234552fbf2 | |||
| 8423efb686 | |||
| cd8c15d01e | |||
| 3163cfb1b6 | |||
| 7ae007a1d0 | |||
| 0b52d75415 | |||
| 3a7f4f5848 | |||
| 072f67988f | |||
| c640ddbff7 | |||
| 981ae6bdd1 | |||
| 977e3f317e | |||
| e2ee24598c | |||
| 1be90084ab | |||
| 6ded0f9bb0 | |||
| d60b25e424 | |||
| 874f3918b7 | |||
| 61690a1804 | |||
| 1e43e35857 | |||
| 8c86003220 | |||
| 18c3cd2e66 | |||
| b21f35e914 | |||
| 32e3ed1fda | |||
| 6f87026b0c | |||
| e23edae917 | |||
| bdc867031a | |||
| e96b0f7a73 | |||
| 37fe446a58 | |||
| f03a0bc202 | |||
| bcbc5a4bff | |||
| 0b1e2faba1 | |||
| 4708a5e59c | |||
| 7925469e2f | |||
| 53116d65f1 | |||
| 01917b781b | |||
| d01fc56ea2 | |||
| 9c870a57f0 | |||
| 7e557a06b3 | |||
| fa63038862 | |||
| 8439204cef | |||
| 710df0b87b | |||
| 302f0fc85f | |||
| 241aa3a02c | |||
| a17f3dc3f8 | |||
| 34c0bf58cb | |||
| b64b93fc4a | |||
| a9f43ff466 | |||
| 924d78d3c4 | |||
| fa92bb84ae | |||
| 288d25e17c | |||
| 610fc2ea36 | |||
| 716c0e2b5c | |||
| 945aed7160 | |||
| 1d4d68f6e0 | |||
| 3e1167260d | |||
| 0130f610e9 | |||
| 94941daf4f | |||
| 2bbd723321 | |||
| 608382109c | |||
| bdc5174f68 | |||
| bc869f1be9 | |||
| dc69e44829 | |||
| d236850119 | |||
| 60ec8c9097 | |||
| 42e7a7d2f7 | |||
| e766809e3a | |||
| cb1f0300b9 | |||
| f2a29309f3 | |||
| 6e7b4bab75 | |||
| b4f39e251c | |||
| af6196fe6e | |||
| bca56b2049 | |||
| f4401f172e | |||
| c11b88fee5 | |||
| 88b1b558d7 | |||
| ba6cdc77c9 | |||
| b952772902 | |||
| 7a322154fa | |||
| 0b6ca158ea | |||
| 100cd9a72b | |||
| 98fb810f1f | |||
| c613e558fd | |||
| c324f83826 | |||
| e50421a6ce | |||
| 417b944e1c | |||
| cb8b4b335d | |||
| c0160537b0 | |||
| 4cb7a6424a | |||
| 7d6c79d73e | |||
| 8652737f64 | |||
| d6ac89ea8d | |||
| 2b620cf487 | |||
| f644df481c | |||
| e99ecacb52 | |||
| 3e22dd4b0e | |||
| 19f30ec687 | |||
| 76bee78200 | |||
| ce7455d8f0 | |||
| 5e27ee0f57 | |||
| 84a5c6ebe8 | |||
| ad7e23f64c | |||
| 2738f20eea | |||
| 09362b236c | |||
| 3cfef81877 | |||
| 3a04ae2542 | |||
| dbaf8a319d | |||
| 2fe2a21bfe | |||
| 37639c3582 | |||
| 9a74e6d7bc | |||
| 5d13389827 | |||
| 1c2c6644d5 | |||
| ffcddf30e3 | |||
| 22c92c7b05 | |||
| b569048010 | |||
| f703d9ac89 | |||
| ed6abfd626 | |||
| 50388efc78 | |||
| 7e8fb0fa14 | |||
| 9d70469665 | |||
| a94f0efb5b | |||
| da3a9be7c6 | |||
| cd8ab8e55a | |||
| 1b5b32afad | |||
| 917f784438 | |||
| c2d5d5cca1 | |||
| b435738c34 | |||
| 934905e63a | |||
| d15bb8a232 | |||
| c22bca1f87 | |||
| e04cc9c9d5 | |||
| 7a2bf4e0ac | |||
| 662f5127fa | |||
| fb9117553b | |||
| 9f24e4363f | |||
| 084f9294f2 | |||
| a9f1f9a647 | |||
| 0aed89036b | |||
| df15bbc5f2 | |||
| e6b0bd3817 | |||
| 11cd7153a2 | |||
| f8bc1cb605 | |||
| 8b8eb6f290 | |||
| 056fe21ee2 | |||
| cb0a88df22 | |||
| 298c0431c3 | |||
| 448de9afa5 | |||
| 9401a4b9ad | |||
| ea92ad3785 | |||
| 7aaad5e03e | |||
| 645bf5c839 | |||
| 7638e0e4a1 | |||
| 302b8a7b1b | |||
| dbb9d8a660 | |||
| c51a164042 | |||
| 6b8a62da6e | |||
| 578ca23e75 | |||
| 2e73a90835 | |||
| cc6273fecc | |||
| e63b777916 | |||
| 907567f511 | |||
| e0fd7308be | |||
| b761034aae | |||
| 04d6b1453d | |||
| 11d1754686 | |||
| 25e84decff | |||
| 7d665b203f | |||
| ac62442798 | |||
| 758f96de60 | |||
| db623b9c86 | |||
| c353bf3c96 | |||
| 338b552499 | |||
| 5d73cc1232 | |||
| faddef7a3b | |||
| 2d9bbc8049 | |||
| 64afaa07e7 | |||
| 5cf3ab23b0 | |||
| 3fe926d3a0 | |||
| f71bb682de | |||
| 904f860f3c | |||
| 8f15c9604c | |||
| ff28f5807b | |||
| 6a317366d7 | |||
| dc3e4a2d18 | |||
| 46c4a71ba3 | |||
| f0759acd11 | |||
| 96c7c2966d | |||
| 554e7bce4a | |||
| 70377a8afd | |||
| 51bc6d421a | |||
| be0756f8ba | |||
| 71a2cb4e1b | |||
| 2eba761bed | |||
| 4b5e1a816b | |||
| a92e4b2d67 | |||
| a32404ba98 | |||
| 0a792e4935 | |||
| 05108977e8 | |||
| 25353261d0 | |||
| 4b517263d0 | |||
| 98a65966e7 | |||
| 32dcf9487f | |||
| e3a698f7b1 | |||
| 1631f74700 | |||
| 78c6d71049 | |||
| 0b9ecdeade | |||
| 185a9211f1 | |||
| 24459b2fbf | |||
| 88c8dcb3e8 | |||
| f3a8fedf0a | |||
| 6b8b7c1a62 | |||
| af26828f91 | |||
| f4c43abbf7 | |||
| e84739bff0 | |||
| 66aad0e4f1 | |||
| 77c43c0214 | |||
| 31ae7a642a | |||
| 1bbb21486c | |||
| 316d8ddf9b | |||
| 5d8c21a5ca | |||
| 240ba5c00c | |||
| 554184ec72 | |||
| 7a055a2254 | |||
| 0a807ac1a3 | |||
| 4d831c8c73 | |||
| e6e78a7aae | |||
| 09bef5f41d | |||
| e394efdd85 | |||
| c10b652a2e | |||
| 1594a69d6c | |||
| 9b1e318bd1 | |||
| e292bf1dee | |||
| 55a0c6e97d | |||
| 8b8e4a40a4 | |||
| 8be070998f | |||
| 093f56a03d | |||
| bd9744c27c | |||
| adc25f4420 | |||
| 229fe7c7bf | |||
| 5d571752ae | |||
| 512ddfae6b | |||
| 7b689f94df | |||
| 9eed149b09 | |||
| 0f5d321df8 | |||
| 4e84094fd8 | |||
| 7b9fce0189 |
@@ -0,0 +1,89 @@
|
||||
# find . -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.hpp" -o -name "*.m" -o -name "*.mm" \) -print0 | xargs -0 clang-format -i
|
||||
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
UseTab: Never
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
|
||||
ColumnLimit: 0
|
||||
ReflowComments: true
|
||||
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeCloseBracketBracedList: true
|
||||
BraceWrapping:
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeElse: false
|
||||
BeforeCatch: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
IndentExternBlock: NoIndent
|
||||
|
||||
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
InsertBraces: true
|
||||
|
||||
PointerAlignment: Left
|
||||
DerivePointerAlignment: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpacesInParentheses: false
|
||||
SpaceAfterCStyleCast: true
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: 1
|
||||
SpacesInContainerLiterals: true
|
||||
|
||||
SortIncludes: Never
|
||||
IndentPPDirectives: BeforeHash
|
||||
PPIndentWidth: 4
|
||||
|
||||
IndentCaseLabels: true
|
||||
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignOperands: DontAlign
|
||||
|
||||
BinPackParameters: false
|
||||
BinPackArguments: false
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
BracedInitializerIndentWidth: 4
|
||||
Cpp11BracedListStyle: Block
|
||||
|
||||
ForEachMacros:
|
||||
- FF_LIST_FOR_EACH
|
||||
- yyjson_obj_foreach
|
||||
- yyjson_arr_foreach
|
||||
- yyjson_mut_obj_foreach
|
||||
- yyjson_mut_arr_foreach
|
||||
|
||||
AttributeMacros:
|
||||
- FF_A_FALLTHROUGH
|
||||
- FF_A_DEPRECATED
|
||||
- FF_A_CLEANUP
|
||||
- FF_A_NODISCARD
|
||||
- FF_A_PRINTF
|
||||
- FF_A_SCANF
|
||||
- FF_A_NONNULL
|
||||
- FF_A_RETURNS_NONNULL
|
||||
- FF_A_UNUSED
|
||||
- FF_A_PACKED
|
||||
- FF_A_WEAK_IMPORT
|
||||
|
||||
MaxEmptyLinesToKeep: 1
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
|
||||
---
|
||||
Language: ObjC
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
@@ -0,0 +1,3 @@
|
||||
src/3rdparty/**
|
||||
build/**
|
||||
src/logo/builtin.c
|
||||
@@ -0,0 +1,3 @@
|
||||
github: LinusDierheimer
|
||||
ko_fi: carterli
|
||||
custom: https://paypal.me/zhangsongcui
|
||||
@@ -16,6 +16,15 @@ body:
|
||||
options:
|
||||
- label: I have checked the FAQ but the issue is not covered
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Not a known issue
|
||||
description: Please check if the issue is already known and being worked on.
|
||||
options:
|
||||
- label: I have checked the existing issues but the issue is not covered
|
||||
required: true
|
||||
- label: My issue is not about crashing on Fedora and KDE 6.6
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "### General description of the bug"
|
||||
|
||||
@@ -6,33 +6,42 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Tip: A logo can be displayed by fastfetch without getting into fastfetch's official repo.
|
||||
For highly customized logo for personal use, it's recommended to keep it locally.
|
||||
Tip: You can display a logo in fastfetch without adding it to fastfetch's official repo.
|
||||
For highly customized, personal logos, we recommend keeping them locally.
|
||||
Please refer to https://github.com/fastfetch-cli/fastfetch/wiki/Migrate-Neofetch-Logo-To-Fastfetch
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: OS
|
||||
description: Paste content of `/etc/os-release` and `/etc/lsb-release` here. If none of these files exist, describe a way to identify the distro
|
||||
description: Paste the contents of `/etc/os-release` and `/etc/lsb-release` here. If neither file exists, describe how to identify the distro.
|
||||
placeholder: cat /etc/os-release
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Distro Website
|
||||
description: To help prevent spam and verify the request, a distro website is required, and a downloadable ISO must be available on that site.
|
||||
placeholder: https://example.com
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: ASCII Art
|
||||
description: An ASCII logo should not take up too much space (smaller than 50x20 characters). Please also include the color codes if not available in `os-release`
|
||||
description: The ASCII logo should not take up too much space (smaller than 50x20 characters, W x H). Please also include the color codes if they are not available in `os-release`.
|
||||
placeholder: Paste ASCII art here
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Original Image
|
||||
description: If the ASCII art is based on an image, please provide a link to the original image
|
||||
placeholder: Image URL or distro website URL
|
||||
label: Original Image URL
|
||||
description: If the ASCII art is based on an image, please provide a link to the original image file.
|
||||
placeholder: Image URL from the distro website mentioned above
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
options:
|
||||
- label: The ASCII art is smaller than 50x20 characters (width x height)
|
||||
- label: The ASCII art is smaller than 50x20 characters (W x H).
|
||||
required: true
|
||||
- label: The ASCII art contains color codes, or the color codes are available in `os-release`
|
||||
- label: The ASCII art includes color codes, or the color codes are available in `os-release`.
|
||||
required: true
|
||||
- label: The ASCII art has no internal padding (spaces at the start and/or end of lines).
|
||||
required: true
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
## Summary
|
||||
|
||||
<!-- Briefly describe what this PR does. -->
|
||||
|
||||
## Related issue (required for new logos for new distros)
|
||||
|
||||
<!--
|
||||
If this PR adds a new logo, it MUST be linked to an existing "Logo Request" issue and has the requests fulfilled.
|
||||
|
||||
Use one of the following formats so GitHub links it properly:
|
||||
- Closes #1234
|
||||
- Fixes #1234
|
||||
- Resolves #1234
|
||||
|
||||
PRs that add logos without an associated Logo Request issue will not be accepted.
|
||||
-->
|
||||
|
||||
Closes #
|
||||
|
||||
## Changes
|
||||
|
||||
-
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!-- Required if this PR introduces visual changes. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have tested my changes locally.
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Reusable DragonFly amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/dragonflybsd-vm@v1
|
||||
with:
|
||||
usesh: yes
|
||||
envs: 'CMAKE_BUILD_TYPE'
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg update
|
||||
pkg install -y llvm cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa libelf
|
||||
|
||||
run: |
|
||||
env CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-dragonfly-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Reusable FreeBSD amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: freebsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '15.0'
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg update
|
||||
sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-freebsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Reusable Haiku amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: haiku
|
||||
version: 'r1beta5'
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10 || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-haiku-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Reusable Linux armv6l
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: armv6
|
||||
distro: bookworm
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y wget
|
||||
apt-get install -y cmake make gcc libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libefl-all-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-armv6l
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Reusable Linux armv7l
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: armv7
|
||||
distro: ubuntu22.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y software-properties-common ca-certificates gpg curl
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
curl -L https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ jammy main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
|
||||
echo -e 'Acquire::https::Verify-Peer "false";\nAcquire::https::Verify-Host "false";' >> /etc/apt/apt.conf.d/99ignore-certificates
|
||||
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libefl-all-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-armv7l
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,103 @@
|
||||
name: Reusable Linux Hosts
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
runs-on:
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
ffversion:
|
||||
description: fastfetch version from linux host build
|
||||
value: ${{ jobs.build.outputs.ffversion }}
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
outputs:
|
||||
ffversion: ${{ steps.ffversion.outputs.ffversion }}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: cat /etc/os-release
|
||||
run: cat /etc/os-release
|
||||
|
||||
- name: cat /proc/cpuinfo
|
||||
run: cat /proc/cpuinfo
|
||||
|
||||
- name: add gcc-13 repo
|
||||
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libefl-all-dev libunwind-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
/home/linuxbrew/.linuxbrew/bin/brew install imagemagick chafa --ignore-dependencies
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: inputs.arch == 'amd64'
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: c
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: perform CodeQL analysis
|
||||
if: inputs.arch == 'amd64'
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: get fastfetch version
|
||||
id: ffversion
|
||||
run: echo "ffversion=$(./fastfetch --version-raw)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: polyfill glibc
|
||||
run: |
|
||||
wget https://github.com/CarterLi/polyfill-glibc/releases/download/v0.0.1/polyfill-glibc-${{ inputs.arch }} -O polyfill-glibc
|
||||
chmod +x polyfill-glibc
|
||||
strip fastfetch && ./polyfill-glibc fastfetch --target-glibc=2.17
|
||||
strip flashfetch && ./polyfill-glibc flashfetch --target-glibc=2.17
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-polyfilled")' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_PACKAGE_RELOCATABLE OFF)' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.17)")' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_RPM_SPEC_MORE_DEFINE "%global __os_install_post %{nil}")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Reusable Linux i686
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: cat /etc/os-release
|
||||
run: cat /etc/os-release
|
||||
|
||||
- name: cat /proc/cpuinfo
|
||||
run: cat /proc/cpuinfo
|
||||
|
||||
- name: add gcc-13 repo
|
||||
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 gcc-13-multilib libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libefl-all-dev libunwind-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
/home/linuxbrew/.linuxbrew/bin/brew install imagemagick chafa --ignore-dependencies
|
||||
|
||||
- name: cmake version
|
||||
run: cmake --version
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: check deb package
|
||||
run: dpkg -I fastfetch-*.deb
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-i686
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Reusable Linux VMs
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: ${{ inputs.arch }}
|
||||
distro: ubuntu22.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y software-properties-common
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev libefl-all-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-linux-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Reusable macOS Hosts
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
runs-on:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: install required packages
|
||||
run: |
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: otool -L fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-macos-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Reusable Musl amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: setup alpine linux
|
||||
uses: jirutka/setup-alpine@master
|
||||
|
||||
- name: install dependencies
|
||||
run: |
|
||||
cat /etc/alpine-release
|
||||
uname -a
|
||||
apk add cmake samurai vulkan-loader-dev libxcb-dev libxrandr-dev rpm-dev wayland-dev libdrm-dev dconf-dev imagemagick-dev chafa-dev zlib-dev dbus-dev mesa-dev opencl-dev sqlite-dev networkmanager-dev pulseaudio-dev ddcutil-dev elfutils-dev gcc g++
|
||||
shell: alpine.sh --root {0}
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DIS_MUSL=ON -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -GNinja .
|
||||
cmake --build . --target package --verbose -j4
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: run
|
||||
run: |
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-musl-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Reusable NetBSD amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: netbsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '10.1'
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkgin -y install clang cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick
|
||||
CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-netbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Reusable No Features Test
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=OFF -DENABLE_EET=OFF -DENABLE_THREADS=OFF
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Reusable OmniOS amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/omnios-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
envs: 'CMAKE_BUILD_TYPE'
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg update --accept || true
|
||||
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja
|
||||
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
|
||||
cmake --build . --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-omnios-amd64")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-omnios-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Reusable OpenBSD amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: openbsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '7.8'
|
||||
environment_variables: 'CMAKE_BUILD_TYPE'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg_add -u
|
||||
sudo pkg_add -r llvm-21.1.2p0 cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 imagemagick chafa
|
||||
CC=clang-21 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-openbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Reusable Release
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ffversion:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: get latest release version
|
||||
id: get_version_release
|
||||
uses: pozetroninc/github-action-get-latest-release@master
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
|
||||
- name: download artifacts
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: actions/download-artifact@v8
|
||||
|
||||
- name: create release
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ inputs.ffversion }}
|
||||
commit: ${{ github.sha }}
|
||||
artifactErrorsFailBuild: true
|
||||
artifacts: fastfetch-*/fastfetch-*
|
||||
body: "Please refer to [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ inputs.ffversion }}/CHANGELOG.md) for details."
|
||||
|
||||
- name: download source tarballs
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
run: |
|
||||
for i in 1 2 3 4 5; do curl -L --remote-name-all --output-dir fastfetch-source --create-dirs https://github.com/${{ github.repository }}/archive/refs/tags/${{ inputs.ffversion }}.{tar.gz,zip} && break || sleep 5; done
|
||||
ls fastfetch-*/*
|
||||
|
||||
- name: generate release notes
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
run: |
|
||||
echo "Please refer to [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ inputs.ffversion }}/CHANGELOG.md) for details." > fastfetch-release-notes.md
|
||||
echo -e "\n---\n\n<details><summary>SHA256SUMs</summary><br>\n\n\`\`\`" >> fastfetch-release-notes.md
|
||||
sha256sum fastfetch-*/* >> fastfetch-release-notes.md
|
||||
echo -e "\`\`\`\n</details>" >> fastfetch-release-notes.md
|
||||
echo -e "\n<details><summary>SHA512SUMs</summary><br>\n\n\`\`\`" >> fastfetch-release-notes.md
|
||||
sha512sum fastfetch-*/* >> fastfetch-release-notes.md
|
||||
echo -e "\`\`\`\n</details>" >> fastfetch-release-notes.md
|
||||
|
||||
- name: update release body
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ inputs.ffversion }}
|
||||
commit: ${{ github.sha }}
|
||||
bodyFile: fastfetch-release-notes.md
|
||||
allowUpdates: true
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Reusable Solaris amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/solaris-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
envs: 'CMAKE_BUILD_TYPE'
|
||||
release: "11.4-gcc-14"
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg install cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja dconf mesa
|
||||
|
||||
run: |
|
||||
export PKG_CONFIG_PATH=/usr/lib/64/pkgconfig
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
|
||||
cmake --build . --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-solaris-amd64")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-solaris-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Reusable Spellcheck
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
spellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install codespell
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y codespell
|
||||
|
||||
- name: Run Spellchecker
|
||||
run: codespell
|
||||
@@ -0,0 +1,106 @@
|
||||
name: Reusable Windows Hosts
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
runs-on:
|
||||
required: true
|
||||
type: string
|
||||
msystem:
|
||||
required: true
|
||||
type: string
|
||||
msystem-lower:
|
||||
required: true
|
||||
type: string
|
||||
msys-arch:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: setup-msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ inputs.msystem }}
|
||||
update: true
|
||||
install: git mingw-w64-clang-${{ inputs.msys-arch }}-7zip mingw-w64-clang-${{ inputs.msys-arch }}-cmake mingw-w64-clang-${{ inputs.msys-arch }}-clang mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-loader mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-headers mingw-w64-clang-${{ inputs.msys-arch }}-opencl-icd mingw-w64-clang-${{ inputs.msys-arch }}-opencl-headers mingw-w64-clang-${{ inputs.msys-arch }}-cppwinrt mingw-w64-clang-${{ inputs.msys-arch }}-imagemagick mingw-w64-clang-${{ inputs.msys-arch }}-chafa mingw-w64-clang-${{ inputs.msys-arch }}-directx-headers
|
||||
|
||||
- name: print msys version
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: env PKG_CONFIG_PATH=/${{ inputs.msystem-lower }}/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --verbose -j4
|
||||
|
||||
- name: copy necessary dlls
|
||||
run: cp /${{ inputs.msystem-lower }}/bin/{OpenCL,vulkan-1}.dll .
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
id: upload-unsigned-artifact
|
||||
name: upload artifacts for signing
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-windows-${{ inputs.arch }}
|
||||
path: |
|
||||
*.dll
|
||||
fastfetch.exe
|
||||
flashfetch.exe
|
||||
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
name: submit signing request
|
||||
uses: signpath/github-action-submit-signing-request@v1
|
||||
with:
|
||||
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||
organization-id: '${{ vars.SIGNPATH_ORG_ID }}'
|
||||
project-slug: 'fastfetch'
|
||||
signing-policy-slug: ${{ github.ref == 'refs/heads/master' && 'release-signing' || 'test-signing' }}
|
||||
github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}'
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: '.'
|
||||
|
||||
- name: create zip archive
|
||||
run: 7z a -tzip -mx9 -bd -y fastfetch-windows-${{ inputs.arch }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: create 7z archive
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ inputs.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: upload true artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fastfetch-windows-${{ inputs.arch }}
|
||||
path: ./fastfetch-windows-${{ inputs.arch }}.*
|
||||
overwrite: true
|
||||
+63
-576
@@ -4,66 +4,20 @@ on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
spellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install codespell
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y codespell
|
||||
|
||||
- name: Run Spellchecker
|
||||
run: codespell
|
||||
uses: ./.github/workflows/build-spellcheck.yml
|
||||
|
||||
no-features-test:
|
||||
name: No-features-test
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_DRM_AMDGPU=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OPENCL=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_ELF=OFF -DENABLE_DIRECTX_HEADERS=OFF -DENABLE_THREADS=OFF
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
uses: ./.github/workflows/build-no-features-test.yml
|
||||
secrets: inherit
|
||||
|
||||
linux-hosts:
|
||||
name: Linux-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
@@ -74,160 +28,38 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
- arch: aarch64
|
||||
runs-on: ubuntu-22.04-arm
|
||||
outputs:
|
||||
ffversion: ${{ steps.ffversion.outputs.ffversion }}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: ./.github/workflows/build-linux-hosts.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
secrets: inherit
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: cat /etc/os-release
|
||||
run: cat /etc/os-release
|
||||
|
||||
- name: cat /proc/cpuinfo
|
||||
run: cat /proc/cpuinfo
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libchafa-dev directx-headers-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
/home/linuxbrew/.linuxbrew/bin/brew install imagemagick --ignore-dependencies
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: matrix.arch == 'amd64'
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: c
|
||||
|
||||
- name: configure project
|
||||
run: PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: perform CodeQL analysis
|
||||
if: matrix.arch == 'amd64'
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: get fastfetch version
|
||||
id: ffversion
|
||||
run: echo "ffversion=$(./fastfetch --version-raw)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: polyfill glibc
|
||||
run: |
|
||||
wget https://github.com/CarterLi/polyfill-glibc/releases/download/v0.0.1/polyfill-glibc-${{ matrix.arch }} -O polyfill-glibc
|
||||
chmod +x polyfill-glibc
|
||||
strip fastfetch && ./polyfill-glibc fastfetch --target-glibc=2.17
|
||||
strip flashfetch && ./polyfill-glibc flashfetch --target-glibc=2.17
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-polyfilled")' >> CPackConfig.cmake
|
||||
cpack
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-linux-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
linux-i686:
|
||||
name: Linux-i686
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-linux-i686.yml
|
||||
secrets: inherit
|
||||
|
||||
linux-armv7l:
|
||||
name: Linux-armv7l
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: armv7
|
||||
distro: ubuntu20.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y ca-certificates gpg curl
|
||||
curl -L https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ focal main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
|
||||
echo -e 'Acquire::https::Verify-Peer "false";\nAcquire::https::Verify-Host "false";' >> /etc/apt/apt.conf.d/99ignore-certificates
|
||||
apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-linux-armv7l
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-armv7l.yml
|
||||
secrets: inherit
|
||||
|
||||
linux-armv6l:
|
||||
name: Linux-armv6l
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: armv6
|
||||
distro: bookworm
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y wget
|
||||
apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-linux-armv6l
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-armv6l.yml
|
||||
secrets: inherit
|
||||
|
||||
linux-vms:
|
||||
name: Linux-${{ matrix.arch }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
@@ -237,76 +69,18 @@ jobs:
|
||||
- arch: riscv64
|
||||
- arch: ppc64le
|
||||
- arch: s390x
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
id: runcmd
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
distro: ubuntu20.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-linux-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-vms.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
secrets: inherit
|
||||
|
||||
musl-amd64:
|
||||
name: Musl-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: setup alpine linux
|
||||
uses: jirutka/setup-alpine@master
|
||||
|
||||
- name: install dependencies
|
||||
run: |
|
||||
cat /etc/alpine-release
|
||||
uname -a
|
||||
apk add cmake samurai vulkan-loader-dev libxcb-dev libxrandr-dev rpm-dev wayland-dev libdrm-dev dconf-dev imagemagick-dev chafa-dev zlib-dev dbus-dev mesa-dev opencl-dev sqlite-dev networkmanager-dev pulseaudio-dev ddcutil-dev elfutils-dev gcc g++
|
||||
shell: alpine.sh --root {0}
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DIS_MUSL=ON -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -GNinja .
|
||||
cmake --build . --target package --verbose -j4
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: run
|
||||
run: |
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-musl-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-musl-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
macos-hosts:
|
||||
name: macOS-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
@@ -317,263 +91,64 @@ jobs:
|
||||
runs-on: macos-15-intel
|
||||
- arch: aarch64
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: ./.github/workflows/build-macos-hosts.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
secrets: inherit
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
omnios-amd64:
|
||||
name: OmniOS-amd64
|
||||
uses: ./.github/workflows/build-omnios-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
- name: install required packages
|
||||
run: |
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: otool -L fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-macos-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
sunos-amd64:
|
||||
runs-on: ubuntu-latest
|
||||
name: SunOS-amd64
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/omnios-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg install pkg:/package/pkg
|
||||
pkg update --accept
|
||||
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick
|
||||
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
cpack
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-sunos-amd64
|
||||
path: ./fastfetch-*.*
|
||||
solaris-amd64:
|
||||
name: Solaris-amd64
|
||||
uses: ./.github/workflows/build-solaris-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
freebsd-amd64:
|
||||
name: FreeBSD-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: freebsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '14.3'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg update
|
||||
sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-freebsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-freebsd-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
openbsd-amd64:
|
||||
name: OpenBSD-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: openbsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '7.7'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg_add -r cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 imagemagick chafa
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-openbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-openbsd-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
netbsd-amd64:
|
||||
name: NetBSD-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: netbsd
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '10.1'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkgin -y install cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-netbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-netbsd-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
dragonfly-amd64:
|
||||
name: DragonFly-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/dragonflybsd-vm@v1
|
||||
with:
|
||||
usesh: yes
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg update
|
||||
pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa libelf
|
||||
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-dragonfly-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-dragonfly-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
haiku-amd64:
|
||||
name: Haiku-amd64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: haiku
|
||||
version: 'r1beta5'
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
run: |
|
||||
uname -a
|
||||
pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10 || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-haiku-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-haiku-amd64.yml
|
||||
secrets: inherit
|
||||
|
||||
windows-hosts:
|
||||
name: Windows-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
@@ -590,68 +165,21 @@ jobs:
|
||||
msystem: CLANGARM64
|
||||
msystem-lower: clangarm64
|
||||
msys-arch: aarch64
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: setup-msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.msystem }}
|
||||
update: true
|
||||
install: git mingw-w64-clang-${{ matrix.msys-arch }}-7zip mingw-w64-clang-${{ matrix.msys-arch }}-cmake mingw-w64-clang-${{ matrix.msys-arch }}-clang mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-loader mingw-w64-clang-${{ matrix.msys-arch }}-vulkan-headers mingw-w64-clang-${{ matrix.msys-arch }}-opencl-icd mingw-w64-clang-${{ matrix.msys-arch }}-opencl-headers mingw-w64-clang-${{ matrix.msys-arch }}-cppwinrt mingw-w64-clang-${{ matrix.msys-arch }}-imagemagick mingw-w64-clang-${{ matrix.msys-arch }}-chafa
|
||||
|
||||
- name: print msys version
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: env PKG_CONFIG_PATH=/${{ matrix.msystem-lower }}/lib/pkgconfig/:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --verbose -j4
|
||||
|
||||
- name: copy necessary dlls
|
||||
run: cp /${{ matrix.msystem-lower }}/bin/{OpenCL,vulkan-1}.dll .
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: create zip archive
|
||||
run: 7z a -tzip -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: create 7z archive
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}
|
||||
path: ./fastfetch-windows-${{ matrix.arch }}.*
|
||||
uses: ./.github/workflows/build-windows-hosts.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
msystem: ${{ matrix.msystem }}
|
||||
msystem-lower: ${{ matrix.msystem-lower }}
|
||||
msys-arch: ${{ matrix.msys-arch }}
|
||||
secrets: inherit
|
||||
|
||||
release:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- linux-hosts
|
||||
- linux-i686
|
||||
- linux-armv7l
|
||||
- linux-armv6l
|
||||
- linux-vms
|
||||
@@ -661,54 +189,13 @@ jobs:
|
||||
- openbsd-amd64
|
||||
- netbsd-amd64
|
||||
- dragonfly-amd64
|
||||
- sunos-amd64
|
||||
- solaris-amd64
|
||||
- omnios-amd64
|
||||
- haiku-amd64
|
||||
- windows-hosts
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: get latest release version
|
||||
id: get_version_release
|
||||
uses: pozetroninc/github-action-get-latest-release@master
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
|
||||
- name: download artifacts
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: actions/download-artifact@v5
|
||||
|
||||
- name: create release
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ needs.linux-hosts.outputs.ffversion }}
|
||||
commit: ${{ github.sha }}
|
||||
artifactErrorsFailBuild: true
|
||||
artifacts: fastfetch-*/fastfetch-*
|
||||
body: "Please refer to [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ needs.linux-hosts.outputs.ffversion }}/CHANGELOG.md) for details."
|
||||
|
||||
- name: download source tarballs
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
run: |
|
||||
for i in 1 2 3 4 5; do curl -L --remote-name-all --output-dir fastfetch-source --create-dirs https://github.com/${{ github.repository }}/archive/refs/tags/${{ needs.linux-hosts.outputs.ffversion }}.{tar.gz,zip} && break || sleep 5; done
|
||||
ls fastfetch-*/*
|
||||
|
||||
- name: generate release notes
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
run: |
|
||||
echo "Please refer to [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/${{ needs.linux-hosts.outputs.ffversion }}/CHANGELOG.md) for details." > fastfetch-release-notes.md
|
||||
echo -e "\n---\n\n<details><summary>SHA256SUMs</summary><br>\n\n\`\`\`" >> fastfetch-release-notes.md
|
||||
sha256sum fastfetch-*/* >> fastfetch-release-notes.md
|
||||
echo -e "\`\`\`\n</details>" >> fastfetch-release-notes.md
|
||||
echo -e "\n<details><summary>SHA512SUMs</summary><br>\n\n\`\`\`" >> fastfetch-release-notes.md
|
||||
sha512sum fastfetch-*/* >> fastfetch-release-notes.md
|
||||
echo -e "\`\`\`\n</details>" >> fastfetch-release-notes.md
|
||||
|
||||
- name: update release body
|
||||
if: needs.linux-hosts.outputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ needs.linux-hosts.outputs.ffversion }}
|
||||
commit: ${{ github.sha }}
|
||||
bodyFile: fastfetch-release-notes.md
|
||||
allowUpdates: true
|
||||
uses: ./.github/workflows/build-release.yml
|
||||
with:
|
||||
ffversion: ${{ needs.linux-hosts.outputs.ffversion }}
|
||||
secrets: inherit
|
||||
|
||||
+404
@@ -1,3 +1,407 @@
|
||||
# 2.63.1
|
||||
|
||||
Bugfixes:
|
||||
* Fixes media length detection for Chrome on Linux (Media, Linux)
|
||||
* Fixes segmentation fault when specifying unsupported modules on command line
|
||||
* Disables usage of Netlink for Wi-Fi detection on s390x architectures (Wifi, Linux)
|
||||
|
||||
# 2.63.0
|
||||
|
||||
Changes:
|
||||
* Introduces a new **build-only** dependency, `libefl`, for querying the Enlightenment window manager configuration:
|
||||
* `libefl-all-dev` on Debian/Ubuntu
|
||||
* `libefl-devel` on Fedora
|
||||
* `efl` on Arch Linux
|
||||
* The Windows-specific options `battery.useSetupApi` and `global.wmiTimeout` have been removed. (Windows)
|
||||
|
||||
Features:
|
||||
* Adds wallpaper detection support on Haiku (#2314, Wallpaper, Haiku)
|
||||
* Adds Wi-Fi 6 GHz channel detection and improves protocol reporting (Wifi, Linux)
|
||||
* Improves Deepin version detection using `/etc/os-version` (#2300, OS, Linux)
|
||||
* Adds Ubuntu Kylin and Ubuntu Unity flavor detection (OS, Linux)
|
||||
* Adds NebiDE support (WMTheme, Linux)
|
||||
* Adds package counting for `cards` on NuTyX (#2287, Packages, Linux)
|
||||
* Adds `{am-pm}` to custom format for 12-hour time with am/pm (DateTime)
|
||||
* Adds support for the Enlightenment desktop environment (#2165, WM, Linux)
|
||||
* Adds support for playback progress detection (Media)
|
||||
* The module now prints the current playback position and total media duration when supported by the player. If you prefer the previous behavior, you can set `media.percent.type: ["hide-others"]` to hide the new fields.
|
||||
* Adds support for `global.playerName` for Windows (Media, Windows)
|
||||
* This allows detecting a specified media player while ignoring others.
|
||||
* Multi-battery detection is enabled on Windows by default (Battery, Windows)
|
||||
|
||||
Bugfixes:
|
||||
* Improves DisplayServer compatibility on Linux by handling newer `kde-output-device-v2` protocol updates (DisplayServer, Linux)
|
||||
* This fixes a long-standing issue where display detection fails with an `interface 'kde_output_device_mode_v2' has no event X` error.
|
||||
* Improves Wi-Fi reliability on Linux by switching to a netlink implementation (Wifi, Linux)
|
||||
* Improves network interface reliability and default route selection on macOS and Windows (Netif, macOS / Windows)
|
||||
* Validates temperature color thresholds as integers when parsing JSON configs (Temps)
|
||||
* Fixes TDE Konsole version detection (#2319, Terminal, Linux)
|
||||
* Various internal cleanups and optimizations
|
||||
|
||||
Logos:
|
||||
* Adds KibaOS, NebiOS, XJ380, openRuyi, Ximper
|
||||
* Fixes GNOME OS built-in logo detection (#2296)
|
||||
|
||||
# 2.62.1
|
||||
|
||||
Bugfixes:
|
||||
* Fixes the Host module not working on some devices (#2279, Host, Linux)
|
||||
* Regression from v2.61.0
|
||||
|
||||
Logos:
|
||||
* Adds EN-OS, LimeOS, Redrose and Uzbek
|
||||
|
||||
# 2.62.0
|
||||
|
||||
Changes:
|
||||
* Sort package managers alphabetically in output and format arguments (Packages)
|
||||
* The positional order of format arguments has changed — this is a breaking change for users relying on numeric placeholders. Named arguments are always preferred; e.g., use `{pacman}` (refer to `fastfetch -h packages-format`) instead of `{1}` to keep your config future-proof.
|
||||
|
||||
Features:
|
||||
* Reports `Unused` instead of `Disabled` for zero-size swap on macOS (#2248, Swap, macOS)
|
||||
* Improves the robustness of default route detection on Linux (#2252, LocalIP, Linux)
|
||||
* PhysicalDisk module improvements
|
||||
* Detects virtual disks and adds support for hiding them via `hideVirtual` option (defaults to `false`)
|
||||
* Detects unused/disconnected disks and adds support for hiding them via `hideUnused` option (defaults to `true`)
|
||||
* Adds support for OpenBSD and NetBSD (root privileges are required to access disk information)
|
||||
* Improves interconnect type detection on SunOS
|
||||
* Adds floppy drive support on Windows
|
||||
* Rewrites Windows GPU detection with D3DKMT APIs, unifying it with WSL GPU detection (GPU, Windows)
|
||||
* Verifies that the battery and power adapter are present before detection (Battery / PowerAdapter, Linux)
|
||||
* Improves PhysicalMemory reliability on OpenBSD and FreeBSD, including support for x86 UEFI systems on OpenBSD and non-UEFI systems on FreeBSD (PhysicalMemory, OpenBSD / FreeBSD)
|
||||
* Adds `display.common.(ndigits|spaceBeforeUnit)` options to configure common formatting for duration, percent, size, frequency and temperature values (#2234)
|
||||
* Slightly improves performance by skipping probes for ignored device types (Sound)
|
||||
* Adds support for AppImage package detection (#2179, Packages, Linux)
|
||||
|
||||
Bugfixes:
|
||||
* Fixes a potential crash on certain Intel GPU models when using `--gpu-driver-specific` (#2259, GPU, Linux)
|
||||
* Doesn't report partitions as physical disks (DiskIO, NetBSD)
|
||||
* Fixes a typo (#2260, Wifi, macOS)
|
||||
* Corrects documentation for zpool format placeholders (#2261, Zpool)
|
||||
* Restores Windows 8.1 support (Cursor, Windows)
|
||||
* Fixes an out-of-bounds memory access when swaps are disabled (Swap, Windows)
|
||||
* Fixes battery status being incorrectly reported as `Charging` on systems with multiple batteries (#2263, Battery, Linux)
|
||||
* Fixes ash shell version detection (#2271, Shell, Linux)
|
||||
* Uses the `iterm` image protocol automatically on iTerm terminals for auto logo type (Logo)
|
||||
* Adds missing options when generating full config file
|
||||
|
||||
Logos:
|
||||
* Updates AerynOS (#2272)
|
||||
|
||||
# 2.61.0
|
||||
|
||||
Changes:
|
||||
* Support for Windows 7 and 8 has been removed.
|
||||
* Windows 8.1 is now the oldest version supported by fastfetch.
|
||||
* The GPU module on WSL no longer relies on `DXCore`.
|
||||
* The `directx-headers` dependency is no longer required.
|
||||
* Fastfetch on Linux is now pure C; a C++ compiler is no longer required.
|
||||
* GPU type detection is now slightly less accurate, but detection speed should be slightly faster.
|
||||
* The GPU module on Windows now uses `DXCore` for more accurate GPU type detection (requires Windows 10 or later).
|
||||
* This feature is built only when `DXCore` headers are available, which requires installing `mingw-w64-<msystem>-x86_64-directx-headers` on MSYS2.
|
||||
|
||||
Features:
|
||||
* Adds a `brightness` option for color display configuration (#2238, Colors)
|
||||
* Adds support for detecting Bluetooth keyboards on Linux (#2220, Keyboard)
|
||||
* Adds support for detecting GlazeWM (WM, macOS)
|
||||
* Adds a `showEmptySlots` option to display empty memory slots on Linux (#2222, PhysicalMemory)
|
||||
* Adds marketing product name detection on Asahi Linux (Host, Linux)
|
||||
* Adds support for new M5 Mac models (Host, macOS)
|
||||
* Improves SMBIOS robustness by validating malformed data and improving error handling
|
||||
* Improves reliability when terminating child processes (Processing, Windows)
|
||||
* Improves Intel Mac support by querying SMBIOS data directly (Global, macOS)
|
||||
* Includes numerous internal cleanups and optimizations
|
||||
|
||||
Bugfixes:
|
||||
* Fixes missing memory devices on some machines (PhysicalMemory)
|
||||
* Fixes CPUCache deduplication for shared caches (#2228, CPUCache, Linux)
|
||||
* Fixes WM version reporting for niri (#2218, WM, Linux)
|
||||
* Fixes SSID decoding issues from `iw` output (Wifi, Linux)
|
||||
* Fixes the CMD code page being changed after running fastfetch on Windows (#2245, Windows)
|
||||
|
||||
# 2.60.0
|
||||
|
||||
Changes:
|
||||
* The CMake option `ENABLE_WIN7_COMPAT:BOOLEAN` now defaults to `OFF` and will be removed in v2.61.0, effectively dropping support for Windows 7 in the next release.
|
||||
* This follows the Windows 7 deprecation notice introduced in v2.57.0.
|
||||
* `wm.detectPlugin` now defaults to `true` (WM)
|
||||
|
||||
Features:
|
||||
* Adds `{cwd}` for custom title formatting, which displays the current working directory (Title)
|
||||
* Adds support for detecting the Zed version (#2200, Editor)
|
||||
* Adds support for detecting `moss` packages (Packages, Linux)
|
||||
* Adds support for detecting komorebi, FancyWM, and GlazeWM (WM, Windows)
|
||||
* Adds support for WM plugin version detection on macOS (WM, macOS)
|
||||
* Adds support for retrieving the executable path on OpenBSD (#2195, OpenBSD)
|
||||
|
||||
Bugfixes:
|
||||
* Fixes a potential segmentation fault caused by dereferencing a negative index (#2198)
|
||||
* Fixes `tempSensor` parsing so that it accepts only string values (#2202, CPU)
|
||||
* Fixes an issue that unexpectedly caused fewer devices to be reported (Keyboard, Linux)
|
||||
* Improves WM detection on LXQt by querying WM settings only when no WM has already been detected (#2199, WM, Linux)
|
||||
* Fixes memory leaks in DBus connection handling and in the OpenGL EGL context lifecycle
|
||||
* Fixes niri version detection on Fedora (WM, Linux)
|
||||
* Includes various internal cleanups and optimizations
|
||||
|
||||
Logos:
|
||||
* Adds `RengeOS` (#2170)
|
||||
* Adds `Emmabuntüs` (#2207)
|
||||
* Updates Artix Linux (#2157)
|
||||
* Updates Linux Mint (#2186)
|
||||
* Renames `Refracted Devuan` to `Refracta`
|
||||
* Renames `ExodiaPredator` to `ExodiaOS`
|
||||
|
||||
# 2.59.0
|
||||
|
||||
Changes:
|
||||
* Fastfetch no longer relies on the unreliable environment variables `$USER` or `%USERPROFILE%` to determine the current username (Title)
|
||||
* People who set `$USER` to customize the Fastfetch title should use `{ "type": "title", "format": "your-custom-user-name" }` to achieve the same result.
|
||||
* Fastfetch no longer tries to probe inaccessible remote disk drives on Windows (Disk, Windows)
|
||||
* People who have remote drives may use `{ "type": "disk", "hideFolders": "X:\\" }` to ignore problematic ones.
|
||||
* This change removes some ugly hacks from the codebase and matches the behavior on `*nix`.
|
||||
|
||||
Features:
|
||||
* Adds Oracle Solaris support (#2176, SunOS)
|
||||
* Adds UID / SID detection (Title)
|
||||
* In custom format: `{user-id}`
|
||||
* Switches to native GPU detection on GNU/Hurd and removes the `libpciaccess` dependency (GPU, Hurd)
|
||||
* Improves memory size detection on macOS (Memory, macOS)
|
||||
* Avoids relying on `hw.memsize_usable` by default, which may not be available on older macOS versions
|
||||
* Improves Windows disk detection accuracy and performance (Disk, Windows)
|
||||
* Adds more ARM CPU parts and removes duplicated cases (CPU, ARM)
|
||||
|
||||
Logos:
|
||||
* Adds 6-color support to the NixOS logo (including the small variant) (#2180)
|
||||
|
||||
# 2.58.0
|
||||
|
||||
An early release to fix compatibility issues with KDE Plasma 6.6.
|
||||
|
||||
Breaking changes:
|
||||
* The `de.slowVersionDetection` option has been removed. Slow version detection is now always enabled, as required on non-FHS-compliant distros (e.g., NixOS). (#2149, DE, Linux)
|
||||
|
||||
Features:
|
||||
* Adds the `--structure-disabled <modules...>` command-line flag to temporarily disable module structure printing.
|
||||
* For example: `fastfetch --structure-disabled colors` removes the color blocks from the default output.
|
||||
* Supports chassis type detection on Linux ARM devices when reported via the device tree (Chassis, Linux)
|
||||
* Supports Bedrock Linux version detection (#2155, OS, Linux)
|
||||
* Honors the `DBPath` and `RootDir` settings in `pacman.conf` when detecting Pacman packages (#2154, Packages, Linux)
|
||||
|
||||
Bugfixes:
|
||||
* Fixes a crash issue on KDE Plasma 6.6 (Display, Linux)
|
||||
* Fixes the Command module not working with `--dynamic-interval` (#2152, Command)
|
||||
* Fixes Quartz Compositor version detection. It now correctly reports the version of `WindowServer` (`SkyLight`) instead of `WindowManager`. (WM, macOS)
|
||||
|
||||
Logos:
|
||||
* Adds Kiss2
|
||||
|
||||
# 2.57.1
|
||||
|
||||
Features:
|
||||
* Tiny performance improvements (Windows)
|
||||
* Improves the reliability of hostname retrieval (Title, Windows)
|
||||
|
||||
Bugfixes:
|
||||
* Fixes potential compilation issues on Linux (#2142, Linux)
|
||||
* Fixes compilation errors on macOS when building with older SDKs (#2140, macOS)
|
||||
* Fixes compilation issues when building with `-DENABLE_SYSTEM_YYJSON=ON` (#2143)
|
||||
|
||||
Logos:
|
||||
* Updates PrismLinux and adds a small variant
|
||||
|
||||
# 2.57.0
|
||||
|
||||
Deprecation notice:
|
||||
* Support for Windows 7 (and 8.x) is deprecated and will be removed in a future release. Extended support for Windows 7 (and 8.1) ended on January 10, 2023. These versions do not officially support ANSI escape codes (running fastfetch on them requires a third-party terminal such as ConEmu). In addition, Windows 7 lacks some APIs used by fastfetch. Fastfetch currently loads these APIs dynamically at runtime to maintain compatibility, but this adds complexity to the codebase and increases the maintenance burden.
|
||||
* A CMake flag `ENABLE_WIN7_COMPAT:BOOLEAN` has been introduced (defaults to `ON` for now). If set to `OFF`, Windows 7 compatibility code is excluded, and the resulting binaries will support only Windows 10 (version 1607 and later) and Windows 11.
|
||||
* The main prebuilt Windows binaries on the Release page (`fastfetch-windows-amd64.*`) are built with `ENABLE_WIN7_COMPAT=OFF`. These are the binaries used by `scoop` and `winget`. Users who need Windows 7 (or 8.x) support can download the `-win7` variant instead.
|
||||
* ~~The `ENABLE_WIN7_COMPAT` CMake option and the `-win7` variant binaries are planned to be removed in 2.60.0~~.
|
||||
|
||||
Features:
|
||||
* Supports COSMIC DE version detection (DE, Linux)
|
||||
* Supports niri version detection (#2121, WM, Linux)
|
||||
* Supports cosmic-term version and terminal font detection (Terminal / TerminalFont, Linux)
|
||||
* Supports urxvt font detection (TerminalFont, Linux) (#2105)
|
||||
* Improves xterm font detection by checking `xterm.vt100.faceName` (TerminalFont, Linux)
|
||||
* Supports Secure Boot detection (Bootmgr, macOS)
|
||||
* Supports DPI scale factor detection on Windows 7 (Display, Windows)
|
||||
* Supports xterm 256-color codes in color configuration
|
||||
* In `display.color`: "`@<color-index>`" (e.g., "`@34`" for color index `34`)
|
||||
* In `*.format` strings: "`#@<color-index>`" (e.g., "`#@34`" for color index `34`)
|
||||
* Improves uptime accuracy on Windows 10+ (Uptime, Windows)
|
||||
* Adds a new module `Logo` to query built-in logo raw data in JSON output (Logo)
|
||||
* Usage: `fastfetch -s logo -l <logo-name> -j # Supported in JSON format only`
|
||||
* Supports shell version detection even if the binary has been deleted (#2136, Shell, Linux)
|
||||
* Overall code refinements and optimizations
|
||||
|
||||
Bugfixes:
|
||||
* Skips local / loopback routes when detecting network interfaces (LocalIP, Linux) (#2127)
|
||||
* Fixes CPU speed detection on s390x (CPU, Linux) (#2129)
|
||||
* Fixes GPU detection error handling and supports case-insensitive PCI ID parsing (GPU, Windows)
|
||||
* Fixes some networking issues and memory leaks (Networking)
|
||||
* Fixes `exePath` reporting relative paths on macOS (Shell, macOS)
|
||||
|
||||
Logos:
|
||||
* Adds openSUSE Tumbleweed braille logo
|
||||
* Adds Xinux
|
||||
* Renames HydraPWK to NetHydra
|
||||
* Fixes colors of deepin and UOS
|
||||
* Fixes colors of macOS and variants
|
||||
|
||||
# 2.56.1
|
||||
|
||||
Features:
|
||||
* Improves compatibility with KDE Plasma 6.5 (#2093, Display)
|
||||
* Adds a `tempSensor` option to specify the sensor name used for CPU temperature detection (CPU)
|
||||
* Example: `{ "type": "cpu", "tempSensor": "hwmon0" /* Use /sys/class/hwmon/hwmon0 for temperature detection */ }`
|
||||
* Refines Memory usage detection on macOS to match Activity Monitor more closely (Memory, macOS)
|
||||
* Minor optimizations
|
||||
|
||||
Bugfixes:
|
||||
* Fixes cache line size detection (CPU, macOS)
|
||||
|
||||
Logos:
|
||||
* Removes Opak
|
||||
* Updates GXDE
|
||||
|
||||
# 2.56.0
|
||||
|
||||
Features:
|
||||
* Enhances config file loading. `--config` and `-c` with relative path now also searches paths defined in `fastfetch --list-config-paths` (typically `~/.config/fastfetch/`)
|
||||
* This allows users to use `fastfetch -c my-config` without needing to specify the full path.
|
||||
* Adds NUMA node count detection (CPU)
|
||||
* Exposed via `{numa-nodes}` in custom format
|
||||
* Supported on Linux, FreeBSD and Windows
|
||||
* Supports the newest Alacritty config format (#2070, TerminalFont)
|
||||
* Detects driver specific info for Zhaoxin GPUs (GPU, Linux)
|
||||
* Detects Android OEM UI for certain OSes (DE, Android)
|
||||
* Improves users detection on Linux (#2064, Users, Linux)
|
||||
* Adds systemd fallback when utmp is unavailable
|
||||
* Fixes resource leaks
|
||||
* Always reports the newest session info
|
||||
* Adds kiss package manager support (#2072, Packages, Linux)
|
||||
* Reports `sshd` if `$SSH_TTY` is not available (Terminal)
|
||||
* Zpool module rewrite (#2051, Zpool)
|
||||
* Adds new Zpool properties: allocated, guid, readOnly
|
||||
* Zpool module now uses runtime lookup for properties to ensure portability
|
||||
* Adds NetBSD (requires `sudo`) and macOS support
|
||||
* Adds `splitLines` option for Command module, which splits the output into sub modules, each containing one line of the output (Command)
|
||||
```
|
||||
* Command output:
|
||||
Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
|
||||
* Old behavior:
|
||||
Command: Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
|
||||
* With `"splitLines": true`:
|
||||
Command 1: Line 1
|
||||
Command 2: Line 2
|
||||
Command 3: Line 3
|
||||
```
|
||||
|
||||
Bugfixes:
|
||||
* Fixes {m,o}ksh version detection on Linux (Shell)
|
||||
* Fixes Alacritty config parsing for TOML format (#2070, TerminalFont)
|
||||
* Improves builtin logo printing for piping and buffering (#2065, Logo)
|
||||
* Uses absolute path when detecting shell and terminal version if available (#2067, TerminalShell)
|
||||
|
||||
Logos:
|
||||
* Updates Codex Linux logo (#2071)
|
||||
* Adds OS/2 Warp logo (#2062)
|
||||
* Adds Amiga logo (#2061)
|
||||
|
||||
# 2.55.1
|
||||
|
||||
Bugfixes:
|
||||
* Fix parallel command execution breaks randomly (#2056 / #2058, Command)
|
||||
* Regression from v2.55.0
|
||||
* Fix `dylib` searching path on macOS (macOS)
|
||||
* Regression from v2.55.0
|
||||
* Fix an uninitialized field (#2057, Display)
|
||||
|
||||
# 2.55.0
|
||||
|
||||
Changes:
|
||||
* Commands are now executed in parallel by default to improve performance (#2045, Command)
|
||||
* This behavior can be disabled in the config file with `"parallel": false` if it causes problems with certain scripts
|
||||
* Folder/filesystem hiding is moved to the detection stage; hidden entries are no longer probed, improving performance (#2043, Disk)
|
||||
|
||||
Features:
|
||||
* Adds `command.parallel` and `command.useStdErr` config options (Command)
|
||||
* `parallel`: set to `false` to disable parallel execution (see Changes above)
|
||||
* `useStdErr`: set to `true` to use stderr output instead of stdout
|
||||
* Adds the command-line flag `--dynamic-interval <interval-in-ms>` to enable dynamic output auto-refresh (#2041)
|
||||
* Due to internal limitations, some modules do not support dynamic updates (notably Display and Media)
|
||||
* Adds support for using the current playing media's cover art as a logo source (Media / Logo)
|
||||
* Usage: `"logo": { "type": "<image-protocol>", "source": "media-cover" }` in JSON config; or `--<image-protocol> media-cover` in command line
|
||||
* Supports local sources only
|
||||
* Adds native GPU detection support on OpenBSD and NetBSD (instead of depending on `libpciaccess`) (GPU)
|
||||
* No functional changes
|
||||
* Root privileges are required to access PCI config space on OpenBSD (as always)
|
||||
* Adds GPU detection support on GNU/Hurd (GPU)
|
||||
* Requires building with `libpciaccess`
|
||||
* Shows Debian point release on Raspberry Pi OS (#2032, OS, Linux)
|
||||
* Adds `Brush` shell version detection (Shell)
|
||||
* Improves Mac family detection via prefix matching (Host)
|
||||
|
||||
Bugfixes:
|
||||
* Ignores `run-parts` during terminal/shell detection (#2048, Terminal / Shell, Linux)
|
||||
* Fixes fish version detection when `LC_ALL` is set (#2014, Shell, Linux)
|
||||
* Hides the module when no desktop icons are found (#2023, Icons, Windows)
|
||||
* Skips auxiliary display controllers to prevent the module from reporting duplicate entries (#2034, GPU, Linux)
|
||||
* Refines Apple rpath handling; fixes building for the Homebrew version on macOS (#1998, CMake)
|
||||
|
||||
Logos:
|
||||
* Adds Vincent OS and MacaroniOS
|
||||
|
||||
# 2.54.0
|
||||
|
||||
Windows binaries in Release page are now signed by SignPath.
|
||||
|
||||
Changes:
|
||||
* Moves macOS and Windows design language detection from the DE module to the Theme module
|
||||
|
||||
Features:
|
||||
* Adds `--json` and `-j` command line flags as a shortcut for `--format json`
|
||||
* Various improvements to the OS module (OS)
|
||||
* Displays point releases for Debian
|
||||
* Displays code names for Ubuntu
|
||||
* Displays build ID for macOS
|
||||
* Displays code names for Windows (previously shown in the Kernel module)
|
||||
* Adds basic support for Wine (Windows)
|
||||
* Adds basic support for hppa and sh architectures (CPU, Linux)
|
||||
* Improves T-Head SoC name detection from the device tree (#1997, CPU, Linux)
|
||||
* Supports glob patterns in `Disk.hideFolders` (Disk)
|
||||
* For example, `/boot/*` will match both `/boot/efi` and `/boot/firmware`
|
||||
* Adds brightness-level detection for external monitor support on Intel macOS (Brightness, macOS)
|
||||
* Adds configurable spacing between icon and text in keys
|
||||
* `display.key.type: "both-N"` where N is `0-4`
|
||||
* Useful for non-monospaced Nerd Fonts
|
||||
* Adds detection support for modern Samsung Exynos SoCs (CPU, Android)
|
||||
* Adds a new CMake option `-DENABLE_WORDEXP=<ON|OFF>` to enable or disable using `wordexp(3)` for acquiring logo file paths (`logo.source`)
|
||||
* Enabled by default for compatibility
|
||||
* Disabling this option reverts to using `glob(3)`, which is less functional but more secure
|
||||
|
||||
Bugfixes:
|
||||
* Avoids integer overflow when calculating swap size (#1988, Swap, Windows)
|
||||
* Trims whitespace from full user name (Title, macOS)
|
||||
* Fixes default font size for Ghostty (#1986, TerminalFont, Linux)
|
||||
* Works around an issue that could report impossibly high memory usage in rare cases (#1988, Memory, Linux)
|
||||
* Fixes incorrect glibc dependency in polyfilled DEB packages (#1983, Linux)
|
||||
* Fixes corrupted binaries in polyfilled RPM packages (#1990, Linux)
|
||||
* Fixes crashes on ancient Android kernels (#1993, Disk, Android)
|
||||
* Fixes incorrect usage of `glob(3)` (OpenBSD)
|
||||
* Prefers resolutions reported by RandR mode info, fixing incorrect resolutions on XFCE when DPI scaling is enabled (Display, Linux)
|
||||
* Various code cleanups and minor fixes
|
||||
|
||||
Logos:
|
||||
* Adds secureblue, PrismLinux, EmperorOS and Zraxyl
|
||||
* Updates T2
|
||||
|
||||
# 2.53.0
|
||||
|
||||
Changes:
|
||||
|
||||
+311
-255
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-2023 Linus Dierheimer
|
||||
Copyright (c) 2022-2025 Carter Li
|
||||
Copyright (c) 2022-2026 Carter Li
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Fastfetch
|
||||
|
||||
Fastfetch 是一款类似 neofetch 的系统信息展示工具,主要用 C 编写,强调性能和可定制性。支持 Linux、macOS、Windows 7+、Android、FreeBSD、OpenBSD、NetBSD、DragonFly、Haiku、SunOS。
|
||||
|
||||
示例配置见 presets/examples,更多截图与平台说明见 Wiki。
|
||||
|
||||
## 安装
|
||||
|
||||
Linux(部分):
|
||||
- Debian 13+ / Ubuntu: apt install fastfetch
|
||||
- Arch: pacman -S fastfetch
|
||||
- Fedora: dnf install fastfetch
|
||||
- openSUSE: zypper install fastfetch
|
||||
- Linuxbrew:brew install fastfetch
|
||||
- 各发行版打包状态:https://repology.org/project/fastfetch/versions
|
||||
|
||||
macOS:
|
||||
- Homebrew:brew install fastfetch
|
||||
- MacPorts:sudo port install fastfetch
|
||||
|
||||
Windows:
|
||||
- scoop install fastfetch
|
||||
- choco install fastfetch
|
||||
- winget install fastfetch
|
||||
- MSYS2:pacman -S mingw-w64-<subsystem>-<arch>-fastfetch
|
||||
|
||||
BSD:
|
||||
- FreeBSD:pkg install fastfetch
|
||||
- NetBSD:pkgin in fastfetch
|
||||
- OpenBSD:pkg_add fastfetch
|
||||
|
||||
Android(Termux):
|
||||
- pkg install fastfetch
|
||||
|
||||
Nightly 构建:
|
||||
- https://nightly.link/fastfetch-cli/fastfetch/workflows/ci/dev?preview
|
||||
|
||||
## 源码构建
|
||||
|
||||
基本上是 `cmake . && make`。详见 Wiki:https://github.com/fastfetch-cli/fastfetch/wiki/Building
|
||||
|
||||
## 使用
|
||||
|
||||
- 默认运行:`fastfetch`
|
||||
- 查看所有可用模块示例:`fastfetch -c all.jsonc`
|
||||
- 以 JSON 输出指定模块:`fastfetch -s <module1>[:<module2>] --format json`
|
||||
- 完整命令行帮助:`fastfetch --help`
|
||||
- 生成最小配置:`fastfetch --gen-config [</path/to/config.jsonc>]`
|
||||
- 生成完整配置:`fastfetch --gen-config-full`
|
||||
- 请使用支持 JSON schema 的编辑器(如 VSCode)编辑配置文件!
|
||||
- 如果你连接 Github 有网络困难(智能提示不生效),可将配置文件中的 `$schema` 的值替换为 `https://gitee.com/carterl/fastfetch/raw/dev/doc/json_schema.json`
|
||||
|
||||
## 定制
|
||||
|
||||
- 配置使用 JSONC,语法与选项见 Wiki:https://github.com/fastfetch-cli/fastfetch/wiki/Configuration
|
||||
- 预设示例位于 presets,可用 `-c <filename>` 加载
|
||||
- Logo 选项与图像显示见文档:https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options
|
||||
- 模块格式化(示例,仅显示 GPU 名称):
|
||||
```jsonc
|
||||
{
|
||||
"modules": [
|
||||
{ "type": "gpu", "format": "{name}" }
|
||||
]
|
||||
}
|
||||
```
|
||||
详见:https://github.com/fastfetch-cli/fastfetch/wiki/Format-String-Guide
|
||||
|
||||
## 反馈与支持
|
||||
|
||||
- 使用问题:Discussions https://github.com/fastfetch-cli/fastfetch/discussions
|
||||
- 疑似缺陷:Issues https://github.com/fastfetch-cli/fastfetch/issues(请填写模版)
|
||||
|
||||
## 赞助
|
||||
|
||||
<img src="https://github.com/user-attachments/assets/a36a6501-e8b0-4a10-9061-b9206d12ffba" width="220">
|
||||
@@ -11,8 +11,11 @@
|
||||
[](https://repology.org/project/fastfetch/versions)
|
||||
[](https://repology.org/project/fastfetch/versions)
|
||||
[](https://deepwiki.com/fastfetch-cli/fastfetch)
|
||||
[](README-cn.md)
|
||||
|
||||
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and SunOS.
|
||||
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 8.1+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku and SunOS (illumos, Solaris).
|
||||
|
||||
> Note: Fastfetch is only actively tested on x86-64 and aarch64 platforms. It may work on other platforms but is not guaranteed to do so.
|
||||
|
||||
<img src="screenshots/example1.png" width="49%" align="left" />
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/2/24/Transparent_Square_Tiles_Texture.png" width="49%" height="16px" align="left" />
|
||||
@@ -36,7 +39,8 @@ Some distributions package outdated versions of fastfetch. Older versions receiv
|
||||
<img src="https://repology.org/badge/vertical-allrepos/fastfetch.svg?columns=2" alt="Packaging status" align="right">
|
||||
</a>
|
||||
|
||||
* Debian: `apt install fastfetch` (for Debian 13 or newer)
|
||||
* Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (Ubuntu 22.04 or newer; latest version)
|
||||
* Debian / Ubuntu: `apt install fastfetch` (Debian 13 or newer; Ubuntu 25.04 or newer)
|
||||
* Debian / Ubuntu: Download `fastfetch-linux-<proper architecture>.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and double-click it (for Ubuntu 20.04 or newer and Debian 11 or newer).
|
||||
* Arch Linux: `pacman -S fastfetch`
|
||||
* Fedora: `dnf install fastfetch`
|
||||
@@ -103,6 +107,10 @@ Fastfetch uses JSONC (JSON with comments) for configuration. [See the Wiki for d
|
||||
|
||||
Logos can also be heavily customized; see the [logo documentation](https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options) for more information.
|
||||
|
||||
### WARNING
|
||||
|
||||
Fastfetch supports a `Command` module that can run arbitrary shell commands. If you copy-paste a config file from an untrusted source, it may contain malicious commands that can harm your system or compromise your privacy. Please always review the config file before using it.
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q: Neofetch is good enough. Why do I need fastfetch?
|
||||
@@ -317,6 +325,11 @@ If you find Fastfetch useful, please consider donating.
|
||||
* Current maintainer: [@CarterLi](https://paypal.me/zhangsongcui)
|
||||
* Original author: [@LinusDierheimer](https://github.com/sponsors/LinusDierheimer)
|
||||
|
||||
## Code signing
|
||||
|
||||
* Free code signing provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
|
||||
* This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it
|
||||
|
||||
## Star History
|
||||
|
||||
Give us a star to show your support!
|
||||
|
||||
+79
-1
@@ -1,3 +1,82 @@
|
||||
fastfetch (2.63.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.63.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 13 May 2026 16:02:28 +0800
|
||||
|
||||
fastfetch (2.62.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.62.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Fri, 24 Apr 2026 15:45:15 +0800
|
||||
|
||||
fastfetch (2.62.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.62.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 22 Apr 2026 09:49:55 +0800
|
||||
|
||||
fastfetch (2.61.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.61.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 30 Mar 2026 09:51:18 +0800
|
||||
|
||||
fastfetch (2.58.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.58.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Thu, 22 Jan 2026 15:30:58 +0800
|
||||
|
||||
fastfetch (2.57.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.57.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 14 Jan 2026 14:00:17 +0800
|
||||
|
||||
fastfetch (2.57.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.57.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 12 Jan 2026 10:11:21 +0800
|
||||
|
||||
fastfetch (2.56.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.56.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Thu, 18 Dec 2025 14:57:36 +0800
|
||||
|
||||
fastfetch (2.56.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.56.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 08 Dec 2025 09:21:58 +0800
|
||||
|
||||
fastfetch (2.55.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.55.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 17 Nov 2025 10:15:44 +0800
|
||||
|
||||
fastfetch (2.55.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.55.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 12 Nov 2025 09:15:24 +0800
|
||||
|
||||
fastfetch (2.54.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Fix building on plucky
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 20 Oct 2025 14:27:02 +0800
|
||||
|
||||
fastfetch (2.54.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.54.0
|
||||
* Test independent changelog entries for different Ubuntu releases
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 20 Oct 2025 10:47:02 +0800
|
||||
|
||||
fastfetch (2.53.0) jammy; urgency=medium
|
||||
|
||||
* Update to 2.53.0
|
||||
@@ -435,4 +514,3 @@ fastfetch (2.7.1) jammy; urgency=medium
|
||||
* Initial release.
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Tue, 06 Feb 2024 15:01:11 +0800
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@ Source: fastfetch
|
||||
Section: universe/utils
|
||||
Priority: optional
|
||||
Maintainer: Carter Li <zhangsongcui@live.cn>
|
||||
Build-Depends: libelf-dev, libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, ocl-icd-opencl-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build
|
||||
Build-Depends: libelf-dev, libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, ocl-icd-opencl-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, libefl-all-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build, python3-setuptools
|
||||
Standards-Version: 4.0.0
|
||||
Homepage: https://github.com/fastfetch-cli/fastfetch
|
||||
|
||||
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
fastfetch_2.53.0_source.buildinfo universe/utils optional
|
||||
Vendored
+74
-2
@@ -1,4 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# WARNING: DO NOT RUN THIS SCRIPT UNLESS YOU KNOW WHAT YOU ARE DOING.
|
||||
|
||||
debuild -S -i -I
|
||||
dput ppa:zhangsongcui3371/fastfetch ~/fastfetch_*.changes
|
||||
set -euo pipefail
|
||||
|
||||
command -v debuild >/dev/null 2>&1 || { echo "debuild not found. Install devscripts." >&2; exit 1; }
|
||||
command -v dput >/dev/null 2>&1 || { echo "dput not found." >&2; exit 1; }
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
||||
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." >/dev/null 2>&1 && pwd)"
|
||||
OUT_DIR="$(dirname "$ROOT_DIR")"
|
||||
PPA="ppa:zhangsongcui3371/fastfetch"
|
||||
CODENAMES=( jammy noble plucky questing resolute )
|
||||
DRY_RUN=0
|
||||
|
||||
if [[ -d "$OUT_DIR/build" ]]; then
|
||||
echo "Output directory exists: $OUT_DIR/build" >&2
|
||||
echo "Please move or delete it before proceeding." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TEMPLATE="$ROOT_DIR/debian/changelog.tpl"
|
||||
CHANGELOG_DEBIAN="$ROOT_DIR/debian/changelog"
|
||||
|
||||
if [[ ! -f "$TEMPLATE" ]]; then
|
||||
echo "Template not found: $TEMPLATE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q '#UBUNTU_CODENAME#' "$TEMPLATE"; then
|
||||
echo "Template missing placeholder: #UBUNTU_CODENAME#" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "IMPORTANT: Before proceeding, please ensure that 'debian/changelog.tpl' is up-to-date"
|
||||
echo " with the correct version number, release notes, and maintainer information."
|
||||
echo
|
||||
echo " The template must contain a placeholder like '#UBUNTU_CODENAME#' for the codename."
|
||||
echo
|
||||
read -p "Have you reviewed and updated 'debian/changelog.tpl'? (y/N): " -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
rm -f "$CHANGELOG_DEBIAN"
|
||||
rm -f "$ROOT_DIR/debian/files"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
for codename in "${CODENAMES[@]}"; do
|
||||
echo "==> Publishing distro: $codename"
|
||||
sed "s/#UBUNTU_CODENAME#/${codename}/g" "$TEMPLATE" > "$CHANGELOG_DEBIAN"
|
||||
|
||||
( cd "$ROOT_DIR" && debuild -S -i -I )
|
||||
|
||||
changes=( "$OUT_DIR"/fastfetch_*~${codename}_source.changes )
|
||||
if [[ ${#changes[@]} -ne 1 ]]; then
|
||||
echo "Unable to uniquely identify .changes for '$codename' in: $OUT_DIR" >&2
|
||||
printf 'Found:\n'; printf ' %s\n' "${changes[@]}" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
echo "[DRY-RUN] dput \"$PPA\" \"${changes[0]}\""
|
||||
else
|
||||
dput "$PPA" "${changes[0]}"
|
||||
fi
|
||||
|
||||
rm -f "$OUT_DIR"/fastfetch_*~${codename}_source.{changes,dsc,tar.*}
|
||||
|
||||
echo "<== Done: $codename"
|
||||
done
|
||||
|
||||
+399
-298
File diff suppressed because it is too large
Load Diff
@@ -109,6 +109,7 @@
|
||||
},
|
||||
"tpm",
|
||||
"version",
|
||||
"logo",
|
||||
"break",
|
||||
"colors"
|
||||
]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Load with --config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Load with --config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
|
||||
+52
-51
@@ -9,25 +9,26 @@
|
||||
// Constants are reusable strings referenced by {$1}, {$2}, etc.
|
||||
// These contain ANSI escape codes for cursor positioning
|
||||
"constants": [
|
||||
"──────────────────────────────────────────────", // {$1} - horizontal line for borders
|
||||
"──────────────────────────────────────────────", // {$1} - horizontal line for inner borders
|
||||
"\u001b[47D", // {$2} - move cursor left 47 columns
|
||||
"\u001b[47C", // {$3} - move cursor right 47 columns
|
||||
"\u001b[46C" // {$4} - move cursor right 46 columns
|
||||
"\u001b[46C", // {$4} - move cursor right 46 columns
|
||||
"══════════════════════════════════════════════" // {$5} - horizontal line for outer borders
|
||||
],
|
||||
"brightColor": false
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"type": "version",
|
||||
"key": "┌───────────────┬─{$1}┐\u001b[41D",
|
||||
"key": "╔═══════════════╦═{$5}╗\u001b[41D",
|
||||
"format": "\u001b[1m{#keys} {1} - {2} "
|
||||
},
|
||||
{
|
||||
"type": "os",
|
||||
// Key format breakdown for OS module:
|
||||
// "│ {icon} \u001b[s{sysname}\u001b[u\u001b[10C│{$3}│{$2}"
|
||||
// "║ {icon} \u001b[s{sysname}\u001b[u\u001b[10C│{$3}║{$2}"
|
||||
//
|
||||
// │ - Left border of key block
|
||||
// ║ - Left border of key block
|
||||
// {icon} - OS icon (defined internally by fastfetch)
|
||||
// \u001b[s - ANSI escape: save cursor position (ESC[s)
|
||||
// {sysname} - Format variable: system name (e.g., "Linux", "Darwin")
|
||||
@@ -36,28 +37,28 @@
|
||||
// \u001b[10C - ANSI escape: move cursor right 10 columns (ESC[10C)
|
||||
// │ - Right border of key block (always 10 columns from left border)
|
||||
// {$3} - Reference to constants[2]: move cursor right 47 columns
|
||||
// │ - Right border of value block
|
||||
// ║ - Right border of value block
|
||||
// {$2} - Reference to constants[1]: move cursor left 47 columns
|
||||
//
|
||||
// This creates a fixed-width layout where the key block is exactly 10 columns wide,
|
||||
// regardless of the actual content length. The cursor manipulation ensures proper
|
||||
// alignment for the table-like structure.
|
||||
"key": "│ {icon} \u001b[s{sysname}\u001b[u\u001b[10C│{$3}│{$2}"
|
||||
"key": "║ {icon} \u001b[s{sysname}\u001b[u\u001b[10C║{$3}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "datetime",
|
||||
"key": "│ {icon} Fetched │{$3}│{$2}",
|
||||
"key": "║ {icon} Fetched ║{$3}║{$2}",
|
||||
"format": "{year}-{month-pretty}-{day-pretty} {hour-pretty}:{minute-pretty}:{second-pretty} {timezone-name}"
|
||||
},
|
||||
{
|
||||
"type": "locale",
|
||||
"key": "│ {icon} Locale │{$3}│{$2}"
|
||||
"key": "║ {icon} Locale ║{$3}║{$2}"
|
||||
},
|
||||
|
||||
// Hardware section with cyan color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#cyan}┌──────────────┬{$1}┐{#keys}│\u001b[37D",
|
||||
"key": "║{#cyan}┌──────────────┬{$1}┐{#keys}║\u001b[37D",
|
||||
"format": "{#bright_cyan} Hardware "
|
||||
},
|
||||
{
|
||||
@@ -66,113 +67,113 @@
|
||||
// │{#cyan}│ - Left border with cyan color
|
||||
// {icon} - Chassis icon
|
||||
// Chassis - Fixed label text
|
||||
// │{$4}│{#keys}│{$2} - Positioning and borders for value area
|
||||
"key": "│{#cyan}│ {icon} Chassis │{$4}│{#keys}│{$2}"
|
||||
// │{$4}│{#keys}║{$2} - Positioning and borders for value area
|
||||
"key": "║{#cyan}│ {icon} Chassis │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "memory",
|
||||
"key": "│{#cyan}│ {icon} RAM │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#cyan}│ {icon} RAM │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "swap",
|
||||
"key": "│{#cyan}│ {icon} SWAP │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#cyan}│ {icon} SWAP │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "cpu",
|
||||
"key": "│{#cyan}│ {icon} CPU │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#cyan}│ {icon} CPU │{$4}│{#keys}║{$2}",
|
||||
"showPeCoreCount": true
|
||||
},
|
||||
{
|
||||
"type": "gpu",
|
||||
"key": "│{#cyan}│ {icon} GPU │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#cyan}│ {icon} GPU │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "disk",
|
||||
"key": "│{#cyan}│ {icon} Disk │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#cyan}│ {icon} Disk │{$4}│{#keys}║{$2}",
|
||||
"format": "{size-used} \/ {size-total} ({size-percentage}) - {filesystem}",
|
||||
},
|
||||
{
|
||||
"type": "battery",
|
||||
"key": "│{#cyan}│ {icon} Battery │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#cyan}│ {icon} Battery │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#cyan}└──────────────┴{$1}┘{#keys}│",
|
||||
"key": "║{#cyan}└──────────────┴{$1}┘{#keys}║",
|
||||
"format": ""
|
||||
},
|
||||
|
||||
// Desktop section with green color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#green}┌──────────────┬{$1}┐{#keys}│\u001b[37D",
|
||||
"key": "║{#green}┌──────────────┬{$1}┐{#keys}║\u001b[37D",
|
||||
"format": "{#bright_green} Desktop "
|
||||
},
|
||||
{
|
||||
"type": "de",
|
||||
"key": "│{#green}│ {icon} Desktop │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#green}│ {icon} Desktop │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "wm",
|
||||
"key": "│{#green}│ {icon} Session │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#green}│ {icon} Session │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "display",
|
||||
"key": "│{#green}│ {icon} Display │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#green}│ {icon} Display │{$4}│{#keys}║{$2}",
|
||||
"compactType": "original-with-refresh-rate"
|
||||
},
|
||||
{
|
||||
"type": "gpu",
|
||||
"key": "│{#green}│ {icon} G-Driver │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#green}│ {icon} G-Driver │{$4}│{#keys}║{$2}",
|
||||
"format": "{driver}"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#green}└──────────────┴{$1}┘{#keys}│",
|
||||
"key": "║{#green}└──────────────┴{$1}┘{#keys}║",
|
||||
"format": ""
|
||||
},
|
||||
|
||||
// Terminal section with yellow color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#yellow}┌──────────────┬{$1}┐{#keys}│\u001b[37D",
|
||||
"key": "║{#yellow}┌──────────────┬{$1}┐{#keys}║\u001b[37D",
|
||||
"format": "{#bright_yellow} Terminal "
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"key": "│{#yellow}│ {icon} Shell │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#yellow}│ {icon} Shell │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "terminal",
|
||||
"key": "│{#yellow}│ {icon} Terminal │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#yellow}│ {icon} Terminal │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "terminalfont",
|
||||
"key": "│{#yellow}│ {icon} Term Font │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#yellow}│ {icon} Term Font │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "terminaltheme",
|
||||
"key": "│{#yellow}│ {icon} Colors │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#yellow}│ {icon} Colors │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "packages",
|
||||
"key": "│{#yellow}│ {icon} Packages │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#yellow}│ {icon} Packages │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#yellow}└──────────────┴{$1}┘{#keys}│",
|
||||
"key": "║{#yellow}└──────────────┴{$1}┘{#keys}║",
|
||||
"format": ""
|
||||
},
|
||||
|
||||
// Development section with red color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#red}┌──────────────┬{$1}┐{#keys}│\u001b[39D",
|
||||
"key": "║{#red}┌──────────────┬{$1}┐{#keys}║\u001b[39D",
|
||||
"format": "{#bright_red} Development "
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"keyIcon": "", // Custom icon override
|
||||
"key": "│{#red}│ {icon} Rust │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#red}│ {icon} Rust │{$4}│{#keys}║{$2}",
|
||||
"text": "rustc --version",
|
||||
"format": "rustc {~6,13}" // Print 6th to 13th characters (version number)
|
||||
},
|
||||
@@ -182,8 +183,8 @@
|
||||
"!system": "Windows" // Posix version
|
||||
},
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Clang │{$4}│{#keys}│{$2}",
|
||||
"text": "clang --version | head -1 | awk '{print $NF}'",
|
||||
"key": "║{#red}│ {icon} Clang │{$4}│{#keys}║{$2}",
|
||||
"text": "clang --version | sed -n 's/.*version \\([0-9][0-9.]*\\).*/\\1/p'",
|
||||
"format": "clang {}"
|
||||
},
|
||||
{
|
||||
@@ -192,67 +193,67 @@
|
||||
"system": "Windows" // Windows version
|
||||
},
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Clang │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#red}│ {icon} Clang │{$4}│{#keys}║{$2}",
|
||||
"text": "clang --version | findstr version", // Finds the line with "version"
|
||||
"format": "clang {~-6}" // Prints the last 6 characters (version number)
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} NodeJS │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#red}│ {icon} NodeJS │{$4}│{#keys}║{$2}",
|
||||
"text": "node --version",
|
||||
"format": "node {~1}" // {~1} removes first character (v)
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Go │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#red}│ {icon} Go │{$4}│{#keys}║{$2}",
|
||||
"text": "go version | cut -d' ' -f3",
|
||||
"format": "go {~2}" // {~2} removes first 2 characters (go)
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Zig │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#red}│ {icon} Zig │{$4}│{#keys}║{$2}",
|
||||
"text": "zig version",
|
||||
"format": "zig {}"
|
||||
},
|
||||
{
|
||||
"type": "editor",
|
||||
"key": "│{#red}│ {icon} Editor │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#red}│ {icon} Editor │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Git │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#red}│ {icon} Git │{$4}│{#keys}║{$2}",
|
||||
"text": "git version",
|
||||
"format": "git {~12}"
|
||||
},
|
||||
{
|
||||
"type": "font",
|
||||
"key": "│{#red}│ {icon} Interface │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#red}│ {icon} Interface │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#red}└──────────────┴{$1}┘{#keys}│",
|
||||
"key": "║{#red}└──────────────┴{$1}┘{#keys}║",
|
||||
"format": ""
|
||||
},
|
||||
|
||||
// Uptime section with magenta color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#magenta}┌──────────────┬{$1}┐{#keys}│\u001b[36D",
|
||||
"key": "║{#magenta}┌──────────────┬{$1}┐{#keys}║\u001b[36D",
|
||||
"format": "{#bright_magenta} Uptime "
|
||||
},
|
||||
{
|
||||
"type": "uptime",
|
||||
"key": "│{#magenta}│ {icon} Uptime │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#magenta}│ {icon} Uptime │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"type": "users",
|
||||
"myselfOnly": true, // Only show current user
|
||||
"keyIcon": "",
|
||||
"key": "│{#magenta}│ {icon} Login │{$4}│{#keys}│{$2}"
|
||||
"key": "║{#magenta}│ {icon} Login │{$4}│{#keys}║{$2}"
|
||||
},
|
||||
{
|
||||
"condition": { // Conditional module: only show on non-macOS
|
||||
@@ -260,7 +261,7 @@
|
||||
},
|
||||
"type": "disk",
|
||||
"keyIcon": "",
|
||||
"key": "│{#magenta}│ {icon} OS Age │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#magenta}│ {icon} OS Age │{$4}│{#keys}║{$2}",
|
||||
"folders": "/", // Check root filesystem
|
||||
"format": "{create-time:10} [{days} days]" // Show creation time and age in days
|
||||
},
|
||||
@@ -270,18 +271,18 @@
|
||||
},
|
||||
"type": "disk",
|
||||
"keyIcon": "",
|
||||
"key": "│{#magenta}│ {icon} OS Age │{$4}│{#keys}│{$2}",
|
||||
"key": "║{#magenta}│ {icon} OS Age │{$4}│{#keys}║{$2}",
|
||||
"folders": "/System/Volumes/VM", // Work around for APFS on macOS
|
||||
"format": "{create-time:10} [{days} days]"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#magenta}└──────────────┴{$1}┘{#keys}│",
|
||||
"key": "║{#magenta}└──────────────┴{$1}┘{#keys}║",
|
||||
"format": ""
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "└─────────────────{$1}┘", // Bottom border of the entire layout
|
||||
"key": "╚═════════════════{$5}╝", // Bottom border of the entire layout
|
||||
"format": ""
|
||||
},
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/3.jsonc
|
||||
// Load with --config examples/3.jsonc
|
||||
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,57 @@
|
||||
// Inspired by microfetch
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
"type": "small"
|
||||
},
|
||||
"general": {
|
||||
"detectVersion": false
|
||||
},
|
||||
"display": {
|
||||
"separator": " ",
|
||||
"brightColor": false,
|
||||
"key": {
|
||||
"type": "both-2"
|
||||
}
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"type": "title",
|
||||
"format": "{user-name-colored}{#light_red}@{host-name-colored} {#}{cwd}"
|
||||
},
|
||||
{
|
||||
"type": "os",
|
||||
"key": "System "
|
||||
},
|
||||
{
|
||||
"type": "kernel",
|
||||
"key": "Kernel "
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"key": "Shell "
|
||||
},
|
||||
{
|
||||
"type": "uptime",
|
||||
"key": "Uptime "
|
||||
},
|
||||
{
|
||||
"type": "wm",
|
||||
"key": "Desktop "
|
||||
},
|
||||
{
|
||||
"type": "memory",
|
||||
"key": "Memory "
|
||||
},
|
||||
{
|
||||
"type": "disk",
|
||||
"key": "Storage (/) ",
|
||||
"folders": "/"
|
||||
},
|
||||
{
|
||||
"type": "colors",
|
||||
"key": "Colors ",
|
||||
"symbol": "circle"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/4.jsonc
|
||||
// Load with --config examples/4.jsonc
|
||||
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/5.jsonc
|
||||
// Load with --config examples/5.jsonc
|
||||
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Load with --config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Load with --config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
|
||||
@@ -6,6 +6,9 @@ def main(amdgpu_ids_path: str):
|
||||
with open(amdgpu_ids_path, 'r') as f:
|
||||
full_text = f.read()
|
||||
|
||||
if full_text == '':
|
||||
sys.exit(f'Error: {amdgpu_ids_path} file is empty')
|
||||
|
||||
products = []
|
||||
for line in full_text.split('\n'):
|
||||
if not line or line[0] == '#' or not ',\t' in line:
|
||||
|
||||
@@ -18,6 +18,9 @@ def main(keep_vendor_list: set, pci_ids_path: str):
|
||||
with open(pci_ids_path, 'r') as f:
|
||||
full_text = f.read()
|
||||
|
||||
if full_text == '':
|
||||
sys.exit(f'Error: {pci_ids_path} file is empty')
|
||||
|
||||
dev_list_text = full_text[:full_text.rfind('\n\n\n')] # remove known classes
|
||||
for line in dev_list_text.split('\n'):
|
||||
if not line or line[0] == '#':
|
||||
|
||||
+141
-88
@@ -21,7 +21,7 @@
|
||||
// SOFTWARE.
|
||||
|
||||
/// \file adl_defines.h
|
||||
/// \brief Contains all definitions exposed by ADL for \ALL platforms.\n <b>Included in ADL SDK</b>
|
||||
/// \brief Contains all definitions exposed by ADL for \WIN platforms.\n <b>Included in ADL SDK</b>
|
||||
///
|
||||
/// This file contains all definitions used by ADL.
|
||||
/// The ADL definitions include the following:
|
||||
@@ -61,6 +61,8 @@
|
||||
#define ADL_ADAPTER_INDEX_ALL -1
|
||||
/// Defines APIs with iOption none
|
||||
#define ADL_MAIN_API_OPTION_NONE 0
|
||||
/// Defines the maximum of RPC endpoint
|
||||
#define ADL_MAX_RPC_ENDPOINT 32
|
||||
/// @}
|
||||
|
||||
/// \name Definitions for iOption parameter used by
|
||||
@@ -141,6 +143,12 @@
|
||||
#define ADL_ERR_FEATURESYNC_NOT_STARTED -24
|
||||
/// Adapter is in an invalid power state
|
||||
#define ADL_ERR_INVALID_POWER_STATE -25
|
||||
/// The RPC server is in busy state
|
||||
#define ADL_ERR_SERVER_BUSY -26
|
||||
/// The GPU is occupied by application which cannot be powered off
|
||||
#define ADL_ERR_GPU_IN_USE -27
|
||||
/// The general RPC connection error
|
||||
#define ADL_ERR_RPC -28
|
||||
|
||||
/// @}
|
||||
/// </A>
|
||||
@@ -380,6 +388,7 @@
|
||||
#define ADL_BUSTYPE_PCIE_GEN2 3 /* PCI Express 2nd generation bus */
|
||||
#define ADL_BUSTYPE_PCIE_GEN3 4 /* PCI Express 3rd generation bus */
|
||||
#define ADL_BUSTYPE_PCIE_GEN4 5 /* PCI Express 4th generation bus */
|
||||
#define ADL_BUSTYPE_PCIE_GEN5 6 /* PCI Express 5th generation bus */
|
||||
/// @}
|
||||
|
||||
/// \defgroup define_ws_caps Workstation Capabilities
|
||||
@@ -872,7 +881,7 @@
|
||||
typedef enum ADLThreadingModel
|
||||
{
|
||||
ADL_THREADING_UNLOCKED = 0, /*!< Default behavior. ADL will not enforce serialization of ADL API executions by multiple threads. Multiple threads will be allowed to enter to ADL at the same time. Note that ADL library is not guaranteed to be thread-safe. Client that calls ADL_Main_Control_Create have to provide its own mechanism for ADL calls serialization. */
|
||||
ADL_THREADING_LOCKED /*!< ADL will enforce serialization of ADL API when called by multiple threads. Only single thread will be allowed to enter ADL API at the time. This option makes ADL calls thread-safe. You shouldn't use this option if ADL calls will be executed on Linux on x-server rendering thread. It can cause the application to hung. */
|
||||
ADL_THREADING_LOCKED /*!< ADL will enforce serialization of ADL API when called by multiple threads. Only single thread will be allowed to enter ADL API at the time. This option makes ADL calls thread-safe. */
|
||||
}ADLThreadingModel;
|
||||
|
||||
/// @}
|
||||
@@ -1844,12 +1853,12 @@ enum ADLOD8FeatureControl
|
||||
ADL_OD8_TEMPERATURE_FAN = 1 << 6, //FanTargetTemperature
|
||||
ADL_OD8_TEMPERATURE_SYSTEM = 1 << 7, //MaxOpTemp
|
||||
ADL_OD8_MEMORY_TIMING_TUNE = 1 << 8,
|
||||
ADL_OD8_FAN_ZERO_RPM_CONTROL = 1 << 9 ,
|
||||
ADL_OD8_AUTO_UV_ENGINE = 1 << 10, //Auto under voltage
|
||||
ADL_OD8_AUTO_OC_ENGINE = 1 << 11, //Auto overclock engine
|
||||
ADL_OD8_AUTO_OC_MEMORY = 1 << 12, //Auto overclock memory
|
||||
ADL_OD8_FAN_CURVE = 1 << 13, //Fan curve
|
||||
ADL_OD8_WS_AUTO_FAN_ACOUSTIC_LIMIT = 1 << 14, //Workstation Manual Fan controller
|
||||
ADL_OD8_FAN_ZERO_RPM_CONTROL = 1 << 9,
|
||||
ADL_OD8_AUTO_UV_ENGINE = 1 << 10, //Auto under voltage
|
||||
ADL_OD8_AUTO_OC_ENGINE = 1 << 11, //Auto overclock engine
|
||||
ADL_OD8_AUTO_OC_MEMORY = 1 << 12, //Auto overclock memory
|
||||
ADL_OD8_FAN_CURVE = 1 << 13, //Fan curve
|
||||
ADL_OD8_WS_AUTO_FAN_ACOUSTIC_LIMIT = 1 << 14, //Workstation Manual Fan controller
|
||||
ADL_OD8_GFXCLK_QUADRATIC_CURVE = 1 << 15,
|
||||
ADL_OD8_OPTIMIZED_GPU_POWER_MODE = 1 << 16,
|
||||
ADL_OD8_ODVOLTAGE_LIMIT = 1 << 17,
|
||||
@@ -1860,43 +1869,45 @@ enum ADLOD8FeatureControl
|
||||
ADL_OD8_TDC_LIMIT = 1 << 22, //TDC slider
|
||||
ADL_OD8_FULL_CONTROL_MODE = 1 << 23, //Full control
|
||||
ADL_OD8_POWER_SAVING_FEATURE_CONTROL = 1 << 24, //Power saving feature control
|
||||
ADL_OD8_POWER_GAUGE = 1 << 25 //Power Gauge
|
||||
ADL_OD8_ACTIMING_PARAMETERS_TUNE = 1 << 25, // AC Timing Parameters Tuning
|
||||
ADL_OD8_OVERDRIVE_INTERFACE = 1 << 26, // Version info feature ID for RSX, do not expose in ADL.
|
||||
ADL_OD8_AUTO_UV_ENGINE_V2 = 1 << 27, // Auto UV 2.0 with actual stress testing.
|
||||
ADL_OD8_POWER_GAUGE = 1 << 28 //Power Gauge
|
||||
};
|
||||
|
||||
|
||||
typedef enum ADLOD8SettingId
|
||||
{
|
||||
OD8_GFXCLK_FMAX = 0,
|
||||
OD8_GFXCLK_FMIN,
|
||||
OD8_GFXCLK_FREQ1,
|
||||
OD8_GFXCLK_VOLTAGE1,
|
||||
OD8_GFXCLK_FREQ2,
|
||||
OD8_GFXCLK_VOLTAGE2,
|
||||
OD8_GFXCLK_FREQ3,
|
||||
OD8_GFXCLK_VOLTAGE3,
|
||||
OD8_UCLK_FMAX,
|
||||
OD8_POWER_PERCENTAGE,
|
||||
OD8_FAN_MIN_SPEED,
|
||||
OD8_FAN_ACOUSTIC_LIMIT,
|
||||
OD8_FAN_TARGET_TEMP,
|
||||
OD8_OPERATING_TEMP_MAX,
|
||||
OD8_AC_TIMING,
|
||||
OD8_FAN_ZERORPM_CONTROL,
|
||||
OD8_AUTO_UV_ENGINE_CONTROL,
|
||||
OD8_AUTO_OC_ENGINE_CONTROL,
|
||||
OD8_AUTO_OC_MEMORY_CONTROL,
|
||||
OD8_FAN_CURVE_TEMPERATURE_1,
|
||||
OD8_FAN_CURVE_SPEED_1,
|
||||
OD8_FAN_CURVE_TEMPERATURE_2,
|
||||
OD8_FAN_CURVE_SPEED_2,
|
||||
OD8_FAN_CURVE_TEMPERATURE_3,
|
||||
OD8_FAN_CURVE_SPEED_3,
|
||||
OD8_FAN_CURVE_TEMPERATURE_4,
|
||||
OD8_FAN_CURVE_SPEED_4,
|
||||
OD8_FAN_CURVE_TEMPERATURE_5,
|
||||
OD8_FAN_CURVE_SPEED_5,
|
||||
OD8_WS_FAN_AUTO_FAN_ACOUSTIC_LIMIT,
|
||||
OD8_GFXCLK_CURVE_COEFFICIENT_A, // As part of the agreement with UI team, the min/max voltage limits for the
|
||||
OD8_GFXCLK_FMAX = 0,
|
||||
OD8_GFXCLK_FMIN,
|
||||
OD8_GFXCLK_FREQ1,
|
||||
OD8_GFXCLK_VOLTAGE1,
|
||||
OD8_GFXCLK_FREQ2,
|
||||
OD8_GFXCLK_VOLTAGE2,
|
||||
OD8_GFXCLK_FREQ3,
|
||||
OD8_GFXCLK_VOLTAGE3,
|
||||
OD8_UCLK_FMAX,
|
||||
OD8_POWER_PERCENTAGE,
|
||||
OD8_FAN_MIN_SPEED, //10
|
||||
OD8_FAN_ACOUSTIC_LIMIT,
|
||||
OD8_FAN_TARGET_TEMP,
|
||||
OD8_OPERATING_TEMP_MAX,
|
||||
OD8_AC_TIMING,
|
||||
OD8_FAN_ZERORPM_CONTROL,
|
||||
OD8_AUTO_UV_ENGINE_CONTROL,
|
||||
OD8_AUTO_OC_ENGINE_CONTROL,
|
||||
OD8_AUTO_OC_MEMORY_CONTROL,
|
||||
OD8_FAN_CURVE_TEMPERATURE_1,
|
||||
OD8_FAN_CURVE_SPEED_1, //20
|
||||
OD8_FAN_CURVE_TEMPERATURE_2,
|
||||
OD8_FAN_CURVE_SPEED_2,
|
||||
OD8_FAN_CURVE_TEMPERATURE_3,
|
||||
OD8_FAN_CURVE_SPEED_3,
|
||||
OD8_FAN_CURVE_TEMPERATURE_4,
|
||||
OD8_FAN_CURVE_SPEED_4,
|
||||
OD8_FAN_CURVE_TEMPERATURE_5,
|
||||
OD8_FAN_CURVE_SPEED_5,
|
||||
OD8_WS_FAN_AUTO_FAN_ACOUSTIC_LIMIT,
|
||||
OD8_GFXCLK_CURVE_COEFFICIENT_A, // 30 As part of the agreement with UI team, the min/max voltage limits for the
|
||||
OD8_GFXCLK_CURVE_COEFFICIENT_B, // quadratic curve graph will be stored in the min and max limits of
|
||||
OD8_GFXCLK_CURVE_COEFFICIENT_C, // coefficient a, b and c. A, b and c themselves do not have limits.
|
||||
OD8_GFXCLK_CURVE_VFT_FMIN,
|
||||
@@ -1906,7 +1917,7 @@ typedef enum ADLOD8SettingId
|
||||
OD8_OD_VOLTAGE,// RSX - voltage offset feature
|
||||
OD8_ADV_OC_LIMITS_SETTING,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_1,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_2,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_2, //40
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_3,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_4,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_POINT_5,
|
||||
@@ -1915,8 +1926,33 @@ typedef enum ADLOD8SettingId
|
||||
OD8_GFX_VOLTAGE_LIMIT_SETTING,
|
||||
OD8_TDC_PERCENTAGE,
|
||||
OD8_FULL_CONTROL_MODE_SETTING,
|
||||
OD8_FULL_CONTROL_MODE_GFXCLK,
|
||||
OD8_FULL_CONTROL_MODE_UCLK, // 50,
|
||||
OD8_IDLE_POWER_SAVING_FEATURE_CONTROL,
|
||||
OD8_RUNTIME_POWER_SAVING_FEATURE_CONTROL,
|
||||
OD8_FULL_CONTROL_MODE_FEATURE_CONTROL,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_1,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_2,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_3,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_4,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_5,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_FREQ_ANCHOR_6,
|
||||
OD8_PER_ZONE_GFX_VOLTAGE_OFFSET_VOLTAGE_LIMIT, //60
|
||||
OD8_ACTIMING_PARAMETER_TRRDS,
|
||||
OD8_ACTIMING_PARAMETER_TCL,
|
||||
OD8_ACTIMING_PARAMETER_TCWL,
|
||||
OD8_ACTIMING_PARAMETER_TRCDRD,
|
||||
OD8_ACTIMING_PARAMETER_TRCDWR,
|
||||
OD8_ACTIMING_PARAMETER_TRAS,
|
||||
OD8_ACTIMING_PARAMETER_TRPAB,
|
||||
OD8_ACTIMING_PARAMETER_TRFC,
|
||||
OD8_ACTIMING_PARAMETER_TRFCPB,
|
||||
OD8_ACTIMING_PARAMETER_TRREFD, //70
|
||||
OD8_ACTIMING_PARAMETER_TREF,
|
||||
OD8_ACTIMING_PARAMETER_TWR,
|
||||
OD8_ACTIMING_PARAMETER_TWTRS,
|
||||
OD8_OVERDRIVE_INTERFACE_ID, //74
|
||||
OD8_AUTO_UV_ENGINE_V2_ID, //75
|
||||
OD8_POWER_GAUGE,
|
||||
OD8_COUNT
|
||||
} ADLOD8SettingId;
|
||||
@@ -1980,28 +2016,28 @@ typedef enum ADLSensorType
|
||||
PMLOG_SSDGPU_POWERLIMIT = 49, // DGPU Power limit
|
||||
PMLOG_TEMPERATURE_HOTSPOT_GCD = 50,
|
||||
PMLOG_TEMPERATURE_HOTSPOT_MCD = 51,
|
||||
PMLOG_THROTTLER_TEMP_EDGE_PERCENTAGE = 52,
|
||||
PMLOG_THROTTLER_TEMP_HOTSPOT_PERCENTAGE = 53,
|
||||
PMLOG_THROTTLER_TEMP_HOTSPOT_GCD_PERCENTAGE = 54,
|
||||
PMLOG_THROTTLER_TEMP_HOTSPOT_MCD_PERCENTAGE = 55,
|
||||
PMLOG_THROTTLER_TEMP_MEM_PERCENTAGE = 56,
|
||||
PMLOG_THROTTLER_TEMP_VR_GFX_PERCENTAGE = 57,
|
||||
PMLOG_THROTTLER_TEMP_VR_MEM0_PERCENTAGE = 58,
|
||||
PMLOG_THROTTLER_TEMP_VR_MEM1_PERCENTAGE = 59,
|
||||
PMLOG_THROTTLER_TEMP_VR_SOC_PERCENTAGE = 60,
|
||||
PMLOG_THROTTLER_TEMP_LIQUID0_PERCENTAGE = 61,
|
||||
PMLOG_THROTTLER_TEMP_LIQUID1_PERCENTAGE = 62,
|
||||
PMLOG_THROTTLER_TEMP_PLX_PERCENTAGE = 63,
|
||||
PMLOG_THROTTLER_TDC_GFX_PERCENTAGE = 64,
|
||||
PMLOG_THROTTLER_TDC_SOC_PERCENTAGE = 65,
|
||||
PMLOG_THROTTLER_TDC_USR_PERCENTAGE = 66,
|
||||
PMLOG_THROTTLER_PPT0_PERCENTAGE = 67,
|
||||
PMLOG_THROTTLER_PPT1_PERCENTAGE = 68,
|
||||
PMLOG_THROTTLER_PPT2_PERCENTAGE = 69,
|
||||
PMLOG_THROTTLER_PPT3_PERCENTAGE = 70,
|
||||
PMLOG_THROTTLER_FIT_PERCENTAGE = 71,
|
||||
PMLOG_THROTTLER_GFX_APCC_PLUS_PERCENTAGE = 72,
|
||||
PMLOG_BOARD_POWER = 73,
|
||||
PMLOG_THROTTLE_PERCENTAGE_TEMP_GFX = 52,
|
||||
PMLOG_THROTTLE_PERCENTAGE_TEMP_MEM = 53,
|
||||
PMLOG_THROTTLE_PERCENTAGE_TEMP_VR = 54,
|
||||
PMLOG_THROTTLE_PERCENTAGE_POWER = 55,
|
||||
PMLOG_THROTTLE_PERCENTAGE_TDC = 56,
|
||||
PMLOG_THROTTLE_PERCENTAGE_VMAX = 57,
|
||||
PMLOG_BUS_CURR_MAX_SPEED = 58,
|
||||
PMLOG_RESERVED_1 = 59, //Currently Unused
|
||||
PMLOG_RESERVED_2 = 60, //Currently Unused
|
||||
PMLOG_RESERVED_3 = 61, //Currently Unused
|
||||
PMLOG_RESERVED_4 = 62, //Currently Unused
|
||||
PMLOG_RESERVED_5 = 63, //Currently Unused
|
||||
PMLOG_RESERVED_6 = 64, //Currently Unused
|
||||
PMLOG_RESERVED_7 = 65, //Currently Unused
|
||||
PMLOG_RESERVED_8 = 66, //Currently Unused
|
||||
PMLOG_RESERVED_9 = 67, //Currently Unused
|
||||
PMLOG_RESERVED_10 = 68, //Currently Unused
|
||||
PMLOG_RESERVED_11 = 69, //Currently Unused
|
||||
PMLOG_RESERVED_12 = 70, //Currently Unused
|
||||
PMLOG_RESERVED_13 = 71, //Currently Unused
|
||||
PMLOG_RESERVED_14 = 72,
|
||||
PMLOG_BOARD_POWER = 73,
|
||||
PMLOG_MAX_SENSORS_REAL
|
||||
} ADLSensorType;
|
||||
|
||||
@@ -2068,28 +2104,29 @@ typedef enum ADL_PMLOG_SENSORS
|
||||
ADL_PMLOG_SSDGPU_POWERLIMIT = 49, // DGPU Power limit
|
||||
ADL_PMLOG_TEMPERATURE_HOTSPOT_GCD = 50,
|
||||
ADL_PMLOG_TEMPERATURE_HOTSPOT_MCD = 51,
|
||||
ADL_PMLOG_THROTTLER_TEMP_EDGE_PERCENTAGE = 52,
|
||||
ADL_PMLOG_THROTTLER_TEMP_HOTSPOT_PERCENTAGE = 53,
|
||||
ADL_PMLOG_THROTTLER_TEMP_HOTSPOT_GCD_PERCENTAGE = 54,
|
||||
ADL_PMLOG_THROTTLER_TEMP_HOTSPOT_MCD_PERCENTAGE = 55,
|
||||
ADL_PMLOG_THROTTLER_TEMP_MEM_PERCENTAGE = 56,
|
||||
ADL_PMLOG_THROTTLER_TEMP_VR_GFX_PERCENTAGE = 57,
|
||||
ADL_PMLOG_THROTTLER_TEMP_VR_MEM0_PERCENTAGE = 58,
|
||||
ADL_PMLOG_THROTTLER_TEMP_VR_MEM1_PERCENTAGE = 59,
|
||||
ADL_PMLOG_THROTTLER_TEMP_VR_SOC_PERCENTAGE = 60,
|
||||
ADL_PMLOG_THROTTLER_TEMP_LIQUID0_PERCENTAGE = 61,
|
||||
ADL_PMLOG_THROTTLER_TEMP_LIQUID1_PERCENTAGE = 62,
|
||||
ADL_PMLOG_THROTTLER_TEMP_PLX_PERCENTAGE = 63,
|
||||
ADL_PMLOG_THROTTLER_TDC_GFX_PERCENTAGE = 64,
|
||||
ADL_PMLOG_THROTTLER_TDC_SOC_PERCENTAGE = 65,
|
||||
ADL_PMLOG_THROTTLER_TDC_USR_PERCENTAGE = 66,
|
||||
ADL_PMLOG_THROTTLER_PPT0_PERCENTAGE = 67,
|
||||
ADL_PMLOG_THROTTLER_PPT1_PERCENTAGE = 68,
|
||||
ADL_PMLOG_THROTTLER_PPT2_PERCENTAGE = 69,
|
||||
ADL_PMLOG_THROTTLER_PPT3_PERCENTAGE = 70,
|
||||
ADL_PMLOG_THROTTLER_FIT_PERCENTAGE = 71,
|
||||
ADL_PMLOG_THROTTLER_GFX_APCC_PLUS_PERCENTAGE = 72,
|
||||
ADL_PMLOG_BOARD_POWER = 73,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_TEMP_GFX = 52,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_TEMP_MEM = 53,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_TEMP_VR = 54,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_POWER = 55,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_TDC = 56,
|
||||
ADL_PMLOG_THROTTLE_PERCENTAGE_VMAX = 57,
|
||||
ADL_PMLOG_BUS_CURR_MAX_SPEED = 58,
|
||||
ADL_PMLOG_RESERVED_1 = 59, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_2 = 60, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_3 = 61, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_4 = 62, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_5 = 63, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_6 = 64, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_7 = 65, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_8 = 66, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_9 = 67, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_10 = 68, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_11 = 69, //Currently Unused
|
||||
ADL_PMLOG_RESERVED_12 = 70, //Currently Unused
|
||||
ADL_PMLOG_CLK_NPUCLK = 71, //NPU frequency
|
||||
ADL_PMLOG_NPU_BUSY_AVG = 72, //NPU busy
|
||||
ADL_PMLOG_BOARD_POWER = 73,
|
||||
ADL_PMLOG_TEMPERATURE_INTAKE = 74,
|
||||
ADL_PMLOG_MAX_SENSORS_REAL
|
||||
} ADL_PMLOG_SENSORS;
|
||||
|
||||
@@ -2393,6 +2430,9 @@ typedef enum ADL_PMLOG_SENSORS
|
||||
/// Indicates there is crossGPU clone
|
||||
#define ADL_CROSSGPUDISPLAYCLONE 0x2
|
||||
|
||||
|
||||
/// @}
|
||||
|
||||
/// @}
|
||||
|
||||
/// \defgroup define_D3DKMT_HANDLE
|
||||
@@ -2493,7 +2533,11 @@ typedef enum ADL_UIFEATURES_GROUP
|
||||
ADL_UIFEATURES_GROUP_PROVSR = 12,
|
||||
ADL_UIFEATURES_GROUP_SMA = 13,
|
||||
ADL_UIFEATURES_GROUP_CAMERA = 14,
|
||||
ADL_UIFEATURES_GROUP_FRTCPRO = 15
|
||||
ADL_UIFEATURES_GROUP_FRTCPRO = 15,
|
||||
ADL_UIFEATURES_GROUP_DELAGNEXT = 16,
|
||||
ADL_UIFEATURES_GROUP_RTBOOST = 17,
|
||||
ADL_UIFEATURES_GROUP_UAI = 18
|
||||
|
||||
} ADL_UIFEATURES_GROUP;
|
||||
|
||||
|
||||
@@ -2587,9 +2631,18 @@ typedef enum ADL_USER_SETTINGS
|
||||
ADL_USER_SETTINGS_USU_PROFILE = 1 << 4, //notify USU settings change
|
||||
ADL_USER_SETTINGS_CVDC_PROFILE = 1 << 5, //notify Color Vision Deficiency Corretion settings change
|
||||
ADL_USER_SETTINGS_SCE_PROFILE = 1 << 6,
|
||||
ADL_USER_SETTINGS_PROVSR = 1 << 7
|
||||
ADL_USER_SETTINGS_PROVSR = 1 << 7,
|
||||
ADL_USER_SETTINGS_RTBOOST_PROFILE=1 << 8,
|
||||
ADL_USER_SETTINGS_USU2_PROFILE = 1 << 9, //notify USU2 settings change
|
||||
} ADL_USER_SETTINGS;
|
||||
|
||||
//FRAME_TIMESTAMPS_SHARED_MEMORY type
|
||||
typedef enum FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE
|
||||
{
|
||||
FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE_UNKNOWN = 0,
|
||||
FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE_LEGACY,
|
||||
FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE_INDEXED
|
||||
}FRAME_TIMESTAMPS_SHARED_MEMORY_TYPE;
|
||||
#define ADL_REG_DEVICE_FUNCTION_1 0x00000001
|
||||
#endif /* ADL_DEFINES_H_ */
|
||||
|
||||
|
||||
+2
-6
@@ -33,14 +33,10 @@
|
||||
|
||||
#include "adl_structures.h"
|
||||
|
||||
#if defined (LINUX)
|
||||
#define __stdcall
|
||||
#endif /* (LINUX) */
|
||||
|
||||
/// Memory Allocation Call back
|
||||
typedef void* ( __stdcall *ADL_MAIN_MALLOC_CALLBACK )( int );
|
||||
|
||||
#define ADL_SDK_MAJOR_VERSION 17
|
||||
#define ADL_SDK_MINOR_VERSION 1
|
||||
#define ADL_SDK_MAJOR_VERSION 18
|
||||
#define ADL_SDK_MINOR_VERSION 0
|
||||
|
||||
#endif /* ADL_SDK_H_ */
|
||||
|
||||
+29
-36
@@ -21,7 +21,7 @@
|
||||
// SOFTWARE.
|
||||
|
||||
/// \file adl_structures.h
|
||||
///\brief This file contains the structure declarations that are used by the public ADL interfaces for \ALL platforms.\n <b>Included in ADL SDK</b>
|
||||
///\brief This file contains the structure declarations that are used by the public ADL interfaces for \WIN platforms.\n <b>Included in ADL SDK</b>
|
||||
///
|
||||
/// All data structures used in AMD Display Library (ADL) public interfaces should be defined in this header file.
|
||||
///
|
||||
@@ -41,7 +41,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct AdapterInfo
|
||||
{
|
||||
/// \ALL_STRUCT_MEM
|
||||
/// \WIN_STRUCT_MEM
|
||||
|
||||
/// Size of the structure.
|
||||
int iSize;
|
||||
@@ -59,7 +59,7 @@ typedef struct AdapterInfo
|
||||
int iVendorID;
|
||||
/// Adapter name.
|
||||
char strAdapterName[ADL_MAX_PATH];
|
||||
/// Display name. For example, "\\\\Display0" for Windows or ":0:0" for Linux.
|
||||
/// Display name. For example, "\\\\Display0" for Windows.
|
||||
char strDisplayName[ADL_MAX_PATH];
|
||||
/// Present or not; 1 if present and 0 if not present.It the logical adapter is present, the display name such as \\\\.\\Display1 can be found from OS
|
||||
int iPresent;
|
||||
@@ -80,38 +80,8 @@ typedef struct AdapterInfo
|
||||
|
||||
#endif /* (_WIN32) || (_WIN64) */
|
||||
|
||||
#if defined (LINUX)
|
||||
/// \LNX_STRUCT_MEM
|
||||
|
||||
/// Internal X screen number from GPUMapInfo (DEPRICATED use XScreenInfo)
|
||||
int iXScreenNum;
|
||||
/// Internal driver index from GPUMapInfo
|
||||
int iDrvIndex;
|
||||
/// \deprecated Internal x config file screen identifier name. Use XScreenInfo instead.
|
||||
char strXScreenConfigName[ADL_MAX_PATH];
|
||||
|
||||
#endif /* (LINUX) */
|
||||
} AdapterInfo, *LPAdapterInfo;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///\brief Structure containing information about the Linux X screen information.
|
||||
///
|
||||
/// This structure is used to store the current screen number and xorg.conf ID name assoicated with an adapter index.
|
||||
/// This structure is updated during ADL_Main_Control_Refresh or ADL_ScreenInfo_Update.
|
||||
/// Note: This structure should be used in place of iXScreenNum and strXScreenConfigName in AdapterInfo as they will be
|
||||
/// deprecated.
|
||||
/// \nosubgrouping
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#if defined (LINUX)
|
||||
typedef struct XScreenInfo
|
||||
{
|
||||
/// Internal X screen number from GPUMapInfo.
|
||||
int iXScreenNum;
|
||||
/// Internal x config file screen identifier name.
|
||||
char strXScreenConfigName[ADL_MAX_PATH];
|
||||
} XScreenInfo, *LPXScreenInfo;
|
||||
#endif /* (LINUX) */
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///\brief Structure containing information about an controller mode
|
||||
///
|
||||
@@ -453,8 +423,12 @@ typedef struct ADLDDCInfo2
|
||||
int ulMaxBacklightMinLuminanceData;
|
||||
int ulMinBacklightMinLuminanceData;
|
||||
|
||||
// Display screen width/height
|
||||
int ulScreenWidth;
|
||||
int ulScreenHeight;
|
||||
|
||||
// Reserved for future use
|
||||
int iReserved[4];
|
||||
int iReserved[2];
|
||||
} ADLDDCInfo2, *LPADLDDCInfo2;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -3614,6 +3588,23 @@ typedef struct ADL_BOOST_SETTINGS
|
||||
int GlobalMinRes_Step; //Gloabl Min Resolution step value
|
||||
}ADL_BOOST_SETTINGS;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///\brief Structure containing information about BOOST Settings
|
||||
///
|
||||
/// Elements of BOOST settings.
|
||||
/// \nosubgrouping
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct ADL_BOOST_SETTINGSX2
|
||||
{
|
||||
int Hotkey; // Hotkey value
|
||||
int GlobalEnable; //Global enable value
|
||||
int GlobalMinRes; //Gloabl Min Resolution value
|
||||
int GlobalMinRes_MinLimit; //Gloabl Min Resolution slider min limit value
|
||||
int GlobalMinRes_MaxLimit; //Gloabl Min Resolution slider max limit value
|
||||
int GlobalMinRes_Step; //Gloabl Min Resolution step value
|
||||
int VsrSupported; //Allows for interop with Upscaling/RSR
|
||||
}ADL_BOOST_SETTINGSX2;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///\brief Structure containing information about ProVSR Settings change reason
|
||||
@@ -3817,7 +3808,7 @@ typedef struct ADL_RADEON_LED_PATTERN_CONFIG
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
typedef struct AdapterInfoX2
|
||||
{
|
||||
/// \ALL_STRUCT_MEM
|
||||
/// \WIN_STRUCT_MEM
|
||||
|
||||
/// Size of the structure.
|
||||
int iSize;
|
||||
@@ -4164,6 +4155,7 @@ typedef struct ADLHDCPSettings
|
||||
///
|
||||
/// This structure is used to store the Mantle Driver information
|
||||
/// \nosubgrouping
|
||||
/// \deprecated This structure has been deprecated.
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct ADLMantleAppInfo
|
||||
@@ -4286,4 +4278,5 @@ typedef struct ADLSmartShiftSettings
|
||||
int iCurrentValue;
|
||||
int iFlags; //refer to define_smartshift_bits
|
||||
}ADLSmartShiftSettings, *LPADLSmartShiftSettings;
|
||||
#endif /* ADL_STRUCTURES_H_ */
|
||||
|
||||
#endif /* ADL_STRUCTURES_H_ */
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"home": "https://github.com/GPUOpen-LibrariesAndSDKs/display-library",
|
||||
"license": "MIT (embeded in source)",
|
||||
"version": "ADL SDK 17.1",
|
||||
"version": "ADL SDK 18.1",
|
||||
"author": "Advanced Micro Devices, Inc"
|
||||
}
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "util/FFlist.h"
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/FFlist.h"
|
||||
|
||||
typedef struct FFPlatformSysinfo
|
||||
{
|
||||
typedef struct FFPlatformSysinfo {
|
||||
FFstrbuf name;
|
||||
FFstrbuf release;
|
||||
FFstrbuf version;
|
||||
FFstrbuf architecture;
|
||||
FFstrbuf displayVersion;
|
||||
uint32_t pageSize;
|
||||
} FFPlatformSysinfo;
|
||||
|
||||
typedef struct FFPlatform
|
||||
{
|
||||
typedef struct FFPlatform {
|
||||
FFstrbuf homeDir; // Trailing slash included
|
||||
FFstrbuf cacheDir; // Trailing slash included
|
||||
FFlist configDirs; // List of FFstrbuf, trailing slash included
|
||||
FFlist dataDirs; // List of FFstrbuf, trailing slash included
|
||||
FFstrbuf exePath; // The real path of current exe
|
||||
FFstrbuf exePath; // The real path of current exe (empty if unavailable)
|
||||
FFstrbuf cwd; // Trailing slash included
|
||||
|
||||
uint32_t pid;
|
||||
#ifndef _WIN32
|
||||
uint32_t uid;
|
||||
#else
|
||||
FFstrbuf sid;
|
||||
#endif
|
||||
FFstrbuf userName;
|
||||
FFstrbuf fullUserName;
|
||||
FFstrbuf hostName;
|
||||
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/attributes.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define FF_LIST_DEFAULT_ALLOC 16
|
||||
|
||||
typedef struct FFlist {
|
||||
uint8_t* data;
|
||||
uint32_t length;
|
||||
uint32_t capacity;
|
||||
} FFlist;
|
||||
|
||||
void* ffListAdd(FFlist* list, uint32_t elementSize);
|
||||
|
||||
// Removes the first element, and copy its value to `*result`
|
||||
bool ffListShift(FFlist* list, uint32_t elementSize, void* __restrict result);
|
||||
// Removes the last element, and copy its value to `*result`
|
||||
bool ffListPop(FFlist* list, uint32_t elementSize, void* __restrict result);
|
||||
|
||||
static inline void ffListInit(FFlist* list) {
|
||||
list->capacity = 0;
|
||||
list->length = 0;
|
||||
list->data = NULL;
|
||||
}
|
||||
|
||||
static inline void ffListInitA(FFlist* list, uint32_t elementSize, uint32_t capacity) {
|
||||
ffListInit(list);
|
||||
list->capacity = capacity;
|
||||
list->data = __builtin_expect(capacity == 0, 0) ? NULL : (uint8_t*) malloc((size_t) capacity * elementSize);
|
||||
}
|
||||
|
||||
FF_A_NODISCARD static inline FFlist ffListCreate() {
|
||||
FFlist result;
|
||||
ffListInit(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
FF_A_NODISCARD static inline FFlist ffListCreateA(uint32_t elementSize, uint32_t capacity) {
|
||||
FFlist result;
|
||||
ffListInitA(&result, elementSize, capacity);
|
||||
return result;
|
||||
}
|
||||
|
||||
FF_A_NODISCARD static inline void* ffListGet(const FFlist* list, uint32_t elementSize, uint32_t index) {
|
||||
assert(list->capacity > index);
|
||||
return list->data + (index * elementSize);
|
||||
}
|
||||
|
||||
FF_A_NODISCARD static inline uint32_t ffListFirstIndexComp(const FFlist* list, uint32_t elementSize, void* compElement, bool (*compFunc)(const void*, const void*)) {
|
||||
for (uint32_t i = 0; i < list->length; i++) {
|
||||
if (compFunc(ffListGet(list, elementSize, i), compElement)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return list->length;
|
||||
}
|
||||
|
||||
FF_A_NODISCARD static inline bool ffListContains(const FFlist* list, uint32_t elementSize, void* compElement, bool (*compFunc)(const void*, const void*)) {
|
||||
return ffListFirstIndexComp(list, elementSize, compElement, compFunc) != list->length;
|
||||
}
|
||||
|
||||
static inline void ffListSort(FFlist* list, uint32_t elementSize, int (*compar)(const void*, const void*)) {
|
||||
qsort(list->data, list->length, elementSize, compar);
|
||||
}
|
||||
|
||||
// Move the contents of `src` into `list`, and left `src` empty
|
||||
static inline void ffListInitMove(FFlist* list, FFlist* src) {
|
||||
if (src) {
|
||||
list->capacity = src->capacity;
|
||||
list->length = src->length;
|
||||
list->data = src->data;
|
||||
ffListInit(src);
|
||||
} else {
|
||||
ffListInit(list);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ffListDestroy(FFlist* list) {
|
||||
if (!list->data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid free-after-use. These 3 assignments are cheap so don't remove them
|
||||
list->capacity = list->length = 0;
|
||||
free(list->data);
|
||||
list->data = NULL;
|
||||
}
|
||||
|
||||
static inline void ffListClear(FFlist* list) {
|
||||
list->length = 0;
|
||||
}
|
||||
|
||||
static inline void ffListReserve(FFlist* list, uint32_t elementSize, uint32_t newCapacity) {
|
||||
if (__builtin_expect(newCapacity <= list->capacity, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
list->data = (uint8_t*) realloc(list->data, (size_t) newCapacity * elementSize);
|
||||
list->capacity = newCapacity;
|
||||
}
|
||||
|
||||
#define FF_LIST_FOR_EACH(itemType, itemVarName, listVar) \
|
||||
for (itemType* itemVarName = (itemType*) (listVar).data; \
|
||||
itemVarName - (itemType*) (listVar).data < (intptr_t) (listVar).length; \
|
||||
++itemVarName)
|
||||
|
||||
#define FF_LIST_AUTO_DESTROY FFlist FF_A_CLEANUP(ffListDestroy)
|
||||
|
||||
#define FF_LIST_GET(itemType, listVar, index) \
|
||||
({ \
|
||||
assert((listVar).capacity > (index)); \
|
||||
(itemType*) (listVar).data + (index); \
|
||||
})
|
||||
|
||||
#define FF_LIST_ADD(itemType, listVar) (itemType*) ffListAdd(&(listVar), (uint32_t) sizeof(itemType))
|
||||
|
||||
#define FF_LIST_FIRST(itemType, listVar) FF_LIST_GET(itemType, listVar, 0)
|
||||
#define FF_LIST_LAST(itemType, listVar) \
|
||||
({ \
|
||||
assert((listVar).length > 0); \
|
||||
FF_LIST_GET(itemType, listVar, ((listVar).length - 1)); \
|
||||
})
|
||||
|
||||
#define FF_LIST_CONTAINS(listVar, pCompElement, compFunc) \
|
||||
({ \
|
||||
typedef __typeof__(*(pCompElement)) compElementType; \
|
||||
typedef bool compFuncType(const compElementType*, const compElementType*); \
|
||||
static_assert(__builtin_types_compatible_p(__typeof__(compFunc), compFuncType), "Incompatible callback function"); \
|
||||
ffListContains(&(listVar), (uint32_t) sizeof(*(pCompElement)), (pCompElement), (bool (*)(const void*, const void*)) compFunc); \
|
||||
})
|
||||
|
||||
#define FF_LIST_SHIFT(listVar, pResult) \
|
||||
ffListShift(&(listVar), (uint32_t) sizeof(*(pResult)), (pResult))
|
||||
#define FF_LIST_POP(listVar, pResult) \
|
||||
ffListPop(&(listVar), (uint32_t) sizeof(*(pResult)), (pResult))
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "FFcheckmacros.h"
|
||||
#include "common/attributes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
@@ -17,15 +17,16 @@
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// #include <shlwapi.h>
|
||||
__stdcall char* StrStrIA(const char* lpFirst, const char* lpSrch);
|
||||
// #include <shlwapi.h>
|
||||
__stdcall char* StrStrIA(const char* lpFirst, const char* lpSrch);
|
||||
#define strcasestr StrStrIA
|
||||
#endif
|
||||
|
||||
#define FASTFETCH_STRBUF_DEFAULT_ALLOC 32
|
||||
|
||||
typedef struct FFstrbuf
|
||||
{
|
||||
// static string (allocated == 0), chars points to a string literal
|
||||
// dynamic string (allocated > 0), chars points to a heap allocated buffer
|
||||
typedef struct FFstrbuf {
|
||||
uint32_t allocated;
|
||||
uint32_t length;
|
||||
char* chars;
|
||||
@@ -34,6 +35,7 @@ typedef struct FFstrbuf
|
||||
static inline void ffStrbufInit(FFstrbuf* strbuf);
|
||||
void ffStrbufInitA(FFstrbuf* strbuf, uint32_t allocate);
|
||||
void ffStrbufInitVF(FFstrbuf* strbuf, const char* format, va_list arguments);
|
||||
void ffStrbufInitMoveNS(FFstrbuf* strbuf, uint32_t length, char* heapStr);
|
||||
|
||||
void ffStrbufEnsureFree(FFstrbuf* strbuf, uint32_t free);
|
||||
void ffStrbufEnsureFixedLengthFree(FFstrbuf* strbuf, uint32_t free);
|
||||
@@ -44,8 +46,8 @@ static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* _
|
||||
void ffStrbufAppendC(FFstrbuf* strbuf, char c);
|
||||
void ffStrbufAppendNC(FFstrbuf* strbuf, uint32_t num, char c);
|
||||
void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value);
|
||||
void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int(*transformFunc)(int));
|
||||
FF_C_PRINTF(2, 3) void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...);
|
||||
void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int (*transformFunc)(int));
|
||||
FF_A_PRINTF(2, 3) void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...);
|
||||
void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments);
|
||||
const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until);
|
||||
|
||||
@@ -54,7 +56,11 @@ void ffStrbufPrependC(FFstrbuf* strbuf, char c);
|
||||
|
||||
void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c);
|
||||
|
||||
FF_C_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...);
|
||||
// Clear the content of strbuf and set new value
|
||||
// NOTE: Unlike ffStrbufAppend*, ffStrbufSet* functions may NOT reserve extra space
|
||||
void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value);
|
||||
void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value);
|
||||
FF_A_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...);
|
||||
|
||||
void ffStrbufTrimLeft(FFstrbuf* strbuf, char c);
|
||||
void ffStrbufTrimRight(FFstrbuf* strbuf, char c);
|
||||
@@ -65,10 +71,10 @@ bool ffStrbufRemoveSubstr(FFstrbuf* strbuf, uint32_t startIndex, uint32_t endInd
|
||||
void ffStrbufRemoveS(FFstrbuf* strbuf, const char* str);
|
||||
void ffStrbufRemoveStrings(FFstrbuf* strbuf, uint32_t numStrings, const char* strings[]);
|
||||
|
||||
FF_C_NODISCARD uint32_t ffStrbufNextIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
|
||||
FF_C_NODISCARD uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t start, const char* str);
|
||||
FF_A_NODISCARD uint32_t ffStrbufNextIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
|
||||
FF_A_NODISCARD uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t start, const char* str);
|
||||
|
||||
FF_C_NODISCARD uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
|
||||
FF_A_NODISCARD uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
|
||||
|
||||
void ffStrbufReplaceAllC(FFstrbuf* strbuf, char find, char replace);
|
||||
|
||||
@@ -80,7 +86,7 @@ bool ffStrbufSubstrAfterFirstS(FFstrbuf* strbuf, const char* str);
|
||||
bool ffStrbufSubstrAfterLastC(FFstrbuf* strbuf, char c);
|
||||
bool ffStrbufSubstr(FFstrbuf* strbuf, uint32_t start, uint32_t end);
|
||||
|
||||
FF_C_NODISCARD uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c);
|
||||
FF_A_NODISCARD uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c);
|
||||
|
||||
bool ffStrbufRemoveIgnCaseEndS(FFstrbuf* strbuf, const char* end);
|
||||
|
||||
@@ -89,15 +95,46 @@ bool ffStrbufEnsureEndsWithC(FFstrbuf* strbuf, char c);
|
||||
void ffStrbufWriteTo(const FFstrbuf* strbuf, FILE* file);
|
||||
void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file);
|
||||
|
||||
FF_C_NODISCARD double ffStrbufToDouble(const FFstrbuf* strbuf, double defaultValue);
|
||||
FF_C_NODISCARD int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue);
|
||||
FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue);
|
||||
FF_A_NODISCARD double ffStrbufToDouble(const FFstrbuf* strbuf, double defaultValue);
|
||||
FF_A_NODISCARD int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue);
|
||||
FF_A_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue);
|
||||
|
||||
void ffStrbufUpperCase(FFstrbuf* strbuf);
|
||||
void ffStrbufLowerCase(FFstrbuf* strbuf);
|
||||
|
||||
bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer);
|
||||
void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer);
|
||||
// Function alters the buffer to extract lines or delimited segments (replaces the delimiter with '\0')
|
||||
// so that buffer MUST be heap allocated (NOT a static string)
|
||||
// `lineptr` must be `NULL` and `n` MUST be `0` for the first call
|
||||
// Caller MUST NOT free `*lineptr`
|
||||
bool ffStrbufGetdelim(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
|
||||
void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
|
||||
|
||||
/**
|
||||
* @brief Read a line from a FFstrbuf.
|
||||
*
|
||||
* @details Behaves like getline(3) but reads from a FFstrbuf.
|
||||
*
|
||||
* @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line
|
||||
(points to buffer's internal memory address to avoid memory allocation and copy).
|
||||
MUST NOT be freed by the caller, unlike `getline(3)`.
|
||||
* MUST be NULL for the first call.
|
||||
* @param[in,out] n The pointer to the size of the buffer of lineptr.
|
||||
MUST be 0 for the first call.
|
||||
* @param[in] buffer The buffer to read from.
|
||||
MUST be heap allocated (NOT a static string).
|
||||
*
|
||||
* @return true if a line has been read, false if the end of the buffer has been reached.
|
||||
*/
|
||||
static inline bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer) {
|
||||
return ffStrbufGetdelim(lineptr, n, '\n', buffer);
|
||||
}
|
||||
/**
|
||||
* @brief Restore the end of a line that was modified by ffStrbufGetline.
|
||||
* @warning This function should be called before breaking an ffStrbufGetline loop if `buffer` will be used later.
|
||||
*/
|
||||
static inline void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer) {
|
||||
ffStrbufGetdelimRestore(lineptr, n, '\n', buffer);
|
||||
}
|
||||
bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf);
|
||||
bool ffStrbufMatchSeparatedNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
|
||||
bool ffStrbufMatchSeparatedIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
|
||||
@@ -112,71 +149,58 @@ void ffStrbufAppendUInt(FFstrbuf* strbuf, uint64_t value);
|
||||
// if `precision < 0`, let yyjson decide the precision
|
||||
void ffStrbufAppendDouble(FFstrbuf* strbuf, double value, int8_t precision, bool trailingZeros);
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitA(&strbuf, allocate);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitCopy(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict src)
|
||||
{
|
||||
if (src->allocated == 0) // static string
|
||||
memcpy(strbuf, src, sizeof(FFstrbuf));
|
||||
else
|
||||
{
|
||||
static inline void ffStrbufInitCopy(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict src) {
|
||||
if (src->allocated == 0) { // static string
|
||||
*strbuf = *src;
|
||||
} else {
|
||||
ffStrbufInitA(strbuf, src->allocated);
|
||||
ffStrbufAppend(strbuf, src);
|
||||
}
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateCopy(const FFstrbuf* src)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateCopy(const FFstrbuf* src) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitCopy(&strbuf, src);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
// Move the content of `src` into `strbuf`, and left `src` empty
|
||||
static inline void ffStrbufInitMove(FFstrbuf* strbuf, FFstrbuf* src)
|
||||
{
|
||||
if (src)
|
||||
{
|
||||
strbuf->allocated = src->allocated;
|
||||
strbuf->chars = src->chars;
|
||||
strbuf->length = src->length;
|
||||
static inline void ffStrbufInitMove(FFstrbuf* strbuf, FFstrbuf* src) {
|
||||
if (src) {
|
||||
*strbuf = *src;
|
||||
ffStrbufInit(src);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
ffStrbufInit(strbuf);
|
||||
}
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateMove(FFstrbuf* src)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateMove(FFstrbuf* src) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitMove(&strbuf, src);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateVF(const char* format, va_list arguments)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateVF(const char* format, va_list arguments) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitVF(&strbuf, format, arguments);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
FF_C_PRINTF(2, 3)
|
||||
static inline void ffStrbufInitF(FFstrbuf* strbuf, const char* format, ...)
|
||||
{
|
||||
FF_A_PRINTF(2, 3) static inline void ffStrbufInitF(FFstrbuf* strbuf, const char* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
ffStrbufInitVF(strbuf, format, arguments);
|
||||
va_end(arguments);
|
||||
}
|
||||
|
||||
FF_C_PRINTF(1, 2)
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateF(const char* format, ...)
|
||||
{
|
||||
FF_A_PRINTF(1, 2) FF_A_NODISCARD static inline FFstrbuf
|
||||
ffStrbufCreateF(const char* format, ...) {
|
||||
FFstrbuf strbuf;
|
||||
|
||||
va_list arguments;
|
||||
@@ -187,416 +211,357 @@ FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateF(const char* format, ...)
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufDestroy(FFstrbuf* strbuf)
|
||||
{
|
||||
extern char* CHAR_NULL_PTR;
|
||||
|
||||
if(strbuf->allocated == 0)
|
||||
{
|
||||
strbuf->length = 0;
|
||||
strbuf->chars = CHAR_NULL_PTR;
|
||||
return;
|
||||
}
|
||||
|
||||
//Avoid free-after-use. These 3 assignments are cheap so don't remove them
|
||||
strbuf->allocated = strbuf->length = 0;
|
||||
free(strbuf->chars);
|
||||
strbuf->chars = CHAR_NULL_PTR;
|
||||
static inline void ffStrbufInitMoveS(FFstrbuf* strbuf, char* heapStr) {
|
||||
ffStrbufInitMoveNS(strbuf, (uint32_t) strlen(heapStr), heapStr);
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline uint32_t ffStrbufGetFree(const FFstrbuf* strbuf)
|
||||
{
|
||||
// Despite the name, this function resets strbuf to the initial/unallocated state
|
||||
static inline void ffStrbufDestroy(FFstrbuf* strbuf) {
|
||||
if (strbuf->allocated > 0) {
|
||||
free(strbuf->chars);
|
||||
}
|
||||
|
||||
ffStrbufInit(strbuf);
|
||||
}
|
||||
|
||||
FF_A_NODISCARD static inline uint32_t ffStrbufGetFree(const FFstrbuf* strbuf) {
|
||||
assert(strbuf != NULL);
|
||||
if(strbuf->allocated == 0)
|
||||
if (strbuf->allocated == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strbuf->allocated - strbuf->length - 1; // - 1 for the null byte
|
||||
}
|
||||
|
||||
static inline void ffStrbufRecalculateLength(FFstrbuf* strbuf)
|
||||
{
|
||||
static inline void ffStrbufRecalculateLength(FFstrbuf* strbuf) {
|
||||
strbuf->length = (uint32_t) strlen(strbuf->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufAppendS(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value) {
|
||||
assert(strbuf != NULL);
|
||||
|
||||
if (value == NULL) {
|
||||
ffStrbufClear(strbuf);
|
||||
} else {
|
||||
ffStrbufSetNS(strbuf, (uint32_t) strlen(value), value);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
|
||||
assert(strbuf != NULL);
|
||||
|
||||
if (yyjson_is_str(jsonVal)) {
|
||||
ffStrbufSetNS(strbuf, (uint32_t) unsafe_yyjson_get_len(jsonVal), unsafe_yyjson_get_str(jsonVal));
|
||||
return true;
|
||||
}
|
||||
|
||||
ffStrbufClear(strbuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void ffStrbufAppendS(FFstrbuf* strbuf, const char* value) {
|
||||
if (value == NULL) {
|
||||
return;
|
||||
}
|
||||
ffStrbufAppendNS(strbuf, (uint32_t) strlen(value), value);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufAppendJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal)
|
||||
{
|
||||
if (yyjson_is_str(jsonVal))
|
||||
{
|
||||
static inline bool ffStrbufAppendJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
|
||||
if (yyjson_is_str(jsonVal)) {
|
||||
ffStrbufAppendNS(strbuf, (uint32_t) unsafe_yyjson_get_len(jsonVal), unsafe_yyjson_get_str(jsonVal));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
ffStrbufClear(strbuf);
|
||||
|
||||
if(value != NULL)
|
||||
ffStrbufAppendNS(strbuf, (uint32_t) strlen(value), value);
|
||||
}
|
||||
|
||||
static inline void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value)
|
||||
{
|
||||
ffStrbufClear(strbuf);
|
||||
ffStrbufAppendNS(strbuf, length, value);
|
||||
}
|
||||
|
||||
static inline void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value)
|
||||
{
|
||||
assert(value && value != strbuf);
|
||||
if (strbuf->allocated == 0 && value->allocated == 0)
|
||||
{
|
||||
memcpy(strbuf, value, sizeof(FFstrbuf));
|
||||
return;
|
||||
}
|
||||
ffStrbufSetNS(strbuf, value->length, value->chars);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal)
|
||||
{
|
||||
ffStrbufClear(strbuf);
|
||||
return ffStrbufAppendJsonVal(strbuf, jsonVal);
|
||||
}
|
||||
|
||||
static inline void ffStrbufInit(FFstrbuf* strbuf)
|
||||
{
|
||||
static inline void ffStrbufInit(FFstrbuf* strbuf) {
|
||||
extern char* CHAR_NULL_PTR;
|
||||
strbuf->allocated = 0;
|
||||
strbuf->length = 0;
|
||||
strbuf->allocated = strbuf->length = 0;
|
||||
strbuf->chars = CHAR_NULL_PTR;
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreate(void)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreate(void) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInit(&strbuf);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitStatic(FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline void ffStrbufInitStatic(FFstrbuf* strbuf, const char* str) {
|
||||
ffStrbufInit(strbuf);
|
||||
if (!str) return;
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
strbuf->allocated = 0;
|
||||
strbuf->length = (uint32_t) strlen(str);
|
||||
strbuf->chars = (char*) str;
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateStatic(const char* str)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateStatic(const char* str) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitStatic(&strbuf, str);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufSetStatic(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
if(strbuf->allocated > 0)
|
||||
ffStrbufDestroy(strbuf);
|
||||
static inline void ffStrbufSetStatic(FFstrbuf* strbuf, const char* value) {
|
||||
if (strbuf->allocated > 0) {
|
||||
free(strbuf->chars);
|
||||
}
|
||||
|
||||
if(value != NULL)
|
||||
if (value != NULL) {
|
||||
ffStrbufInitStatic(strbuf, value);
|
||||
} else {
|
||||
ffStrbufInit(strbuf);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitNS(FFstrbuf* strbuf, uint32_t length, const char* str)
|
||||
{
|
||||
static inline void ffStrbufInitNS(FFstrbuf* strbuf, uint32_t length, const char* str) {
|
||||
ffStrbufInit(strbuf);
|
||||
ffStrbufAppendNS(strbuf, length, str);
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateNS(uint32_t length, const char* str)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateNS(uint32_t length, const char* str) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitNS(&strbuf, length, str);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline bool ffStrbufInitJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal)
|
||||
{
|
||||
static inline bool ffStrbufInitJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
|
||||
ffStrbufInit(strbuf);
|
||||
return ffStrbufAppendJsonVal(strbuf, jsonVal);
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitS(FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline void ffStrbufInitS(FFstrbuf* strbuf, const char* str) {
|
||||
ffStrbufInit(strbuf);
|
||||
ffStrbufAppendS(strbuf, str);
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateS(const char* str)
|
||||
{
|
||||
FF_A_NODISCARD static inline FFstrbuf ffStrbufCreateS(const char* str) {
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitS(&strbuf, str);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value)
|
||||
{
|
||||
static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value) {
|
||||
assert(value != strbuf);
|
||||
if(value == NULL)
|
||||
if (value == NULL) {
|
||||
return;
|
||||
}
|
||||
ffStrbufAppendNS(strbuf, value->length, value->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufPrepend(FFstrbuf* strbuf, FFstrbuf* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
static inline void ffStrbufPrepend(FFstrbuf* strbuf, FFstrbuf* value) {
|
||||
if (value == NULL) {
|
||||
return;
|
||||
}
|
||||
ffStrbufPrependNS(strbuf, value->length, value->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufPrependS(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
static inline void ffStrbufPrependS(FFstrbuf* strbuf, const char* value) {
|
||||
if (value == NULL) {
|
||||
return;
|
||||
}
|
||||
ffStrbufPrependNS(strbuf, (uint32_t) strlen(value), value);
|
||||
}
|
||||
|
||||
static inline int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp) {
|
||||
uint32_t length = strbuf->length > comp->length ? comp->length : strbuf->length;
|
||||
return memcmp(strbuf->chars, comp->chars, length + 1);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEqual(const FFstrbuf* strbuf, const FFstrbuf* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) {
|
||||
return ffStrbufComp(strbuf, comp) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD int ffStrbufCompS(const FFstrbuf* strbuf, const char* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD int ffStrbufCompS(const FFstrbuf* strbuf, const char* comp) {
|
||||
return strcmp(strbuf->chars, comp);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEqualS(const FFstrbuf* strbuf, const char* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEqualS(const FFstrbuf* strbuf, const char* comp) {
|
||||
return ffStrbufCompS(strbuf, comp) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD int ffStrbufIgnCaseCompS(const FFstrbuf* strbuf, const char* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD int ffStrbufIgnCaseCompS(const FFstrbuf* strbuf, const char* comp) {
|
||||
return strcasecmp(strbuf->chars, comp);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufIgnCaseEqualS(const FFstrbuf* strbuf, const char* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufIgnCaseEqualS(const FFstrbuf* strbuf, const char* comp) {
|
||||
return ffStrbufIgnCaseCompS(strbuf, comp) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD int ffStrbufIgnCaseComp(const FFstrbuf* strbuf, const FFstrbuf* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD int ffStrbufIgnCaseComp(const FFstrbuf* strbuf, const FFstrbuf* comp) {
|
||||
return ffStrbufIgnCaseCompS(strbuf, comp->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufIgnCaseEqual(const FFstrbuf* strbuf, const FFstrbuf* comp)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufIgnCaseEqual(const FFstrbuf* strbuf, const FFstrbuf* comp) {
|
||||
return ffStrbufIgnCaseComp(strbuf, comp) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContainC(const FFstrbuf* strbuf, char c) {
|
||||
return memchr(strbuf->chars, c, strbuf->length) != NULL;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainS(const FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContainS(const FFstrbuf* strbuf, const char* str) {
|
||||
return strstr(strbuf->chars, str) != NULL;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContain(const FFstrbuf* strbuf, const FFstrbuf* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContain(const FFstrbuf* strbuf, const FFstrbuf* str) {
|
||||
return ffStrbufContainS(strbuf, str->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainIgnCaseS(const FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContainIgnCaseS(const FFstrbuf* strbuf, const char* str) {
|
||||
return strcasestr(strbuf->chars, str) != NULL;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* str) {
|
||||
return ffStrbufContainIgnCaseS(strbuf, str->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD uint32_t ffStrbufFirstIndexC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline FF_A_NODISCARD uint32_t ffStrbufFirstIndexC(const FFstrbuf* strbuf, char c) {
|
||||
return ffStrbufNextIndexC(strbuf, 0, c);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD uint32_t ffStrbufFirstIndex(const FFstrbuf* strbuf, const FFstrbuf* searched)
|
||||
{
|
||||
static inline FF_A_NODISCARD uint32_t ffStrbufFirstIndex(const FFstrbuf* strbuf, const FFstrbuf* searched) {
|
||||
return ffStrbufNextIndexS(strbuf, 0, searched->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD uint32_t ffStrbufFirstIndexS(const FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
static inline FF_A_NODISCARD uint32_t ffStrbufFirstIndexS(const FFstrbuf* strbuf, const char* str) {
|
||||
return ffStrbufNextIndexS(strbuf, 0, str);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD uint32_t ffStrbufLastIndexC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
if(strbuf->length == 0)
|
||||
static inline FF_A_NODISCARD uint32_t ffStrbufLastIndexC(const FFstrbuf* strbuf, char c) {
|
||||
if (strbuf->length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ffStrbufPreviousIndexC(strbuf, strbuf->length - 1, c);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSubstrBeforeFirstC(FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline bool ffStrbufSubstrBeforeFirstC(FFstrbuf* strbuf, char c) {
|
||||
return ffStrbufSubstrBefore(strbuf, ffStrbufFirstIndexC(strbuf, c));
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSubstrBeforeLastC(FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline bool ffStrbufSubstrBeforeLastC(FFstrbuf* strbuf, char c) {
|
||||
return ffStrbufSubstrBefore(strbuf, ffStrbufLastIndexC(strbuf, c));
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithC(const FFstrbuf* strbuf, char c) {
|
||||
return strbuf->chars[0] == c;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithSN(const FFstrbuf* strbuf, const char* start, uint32_t length)
|
||||
{
|
||||
if (length > strbuf->length)
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithSN(const FFstrbuf* strbuf, const char* start, uint32_t length) {
|
||||
if (length > strbuf->length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return memcmp(strbuf->chars, start, length) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithS(const FFstrbuf* strbuf, const char* start)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithS(const FFstrbuf* strbuf, const char* start) {
|
||||
return ffStrbufStartsWithSN(strbuf, start, (uint32_t) strlen(start));
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWith(const FFstrbuf* strbuf, const FFstrbuf* start)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWith(const FFstrbuf* strbuf, const FFstrbuf* start) {
|
||||
return ffStrbufStartsWithSN(strbuf, start->chars, start->length);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t length, const char* start)
|
||||
{
|
||||
if(length > strbuf->length)
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t length, const char* start) {
|
||||
if (length > strbuf->length) {
|
||||
return false;
|
||||
}
|
||||
return strncasecmp(strbuf->chars, start, length) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithIgnCaseS(const FFstrbuf* strbuf, const char* start)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithIgnCaseS(const FFstrbuf* strbuf, const char* start) {
|
||||
return ffStrbufStartsWithIgnCaseNS(strbuf, (uint32_t) strlen(start), start);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufStartsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* start)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufStartsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* start) {
|
||||
return ffStrbufStartsWithIgnCaseNS(strbuf, start->length, start->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithC(const FFstrbuf* strbuf, char c)
|
||||
{
|
||||
return strbuf->length == 0 ? false :
|
||||
strbuf->chars[strbuf->length - 1] == c;
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithC(const FFstrbuf* strbuf, char c) {
|
||||
return strbuf->length == 0 ? false : strbuf->chars[strbuf->length - 1] == c;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end)
|
||||
{
|
||||
if(endLength > strbuf->length)
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end) {
|
||||
if (endLength > strbuf->length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return memcmp(strbuf->chars + strbuf->length - endLength, end, endLength) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithS(const FFstrbuf* strbuf, const char* end)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithS(const FFstrbuf* strbuf, const char* end) {
|
||||
return ffStrbufEndsWithNS(strbuf, (uint32_t) strlen(end), end);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithFn(const FFstrbuf* strbuf, int (*const fn)(int))
|
||||
{
|
||||
return strbuf->length == 0 ? false :
|
||||
fn(strbuf->chars[strbuf->length - 1]);
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithFn(const FFstrbuf* strbuf, int (*const fn)(int)) {
|
||||
return strbuf->length == 0 ? false : fn(strbuf->chars[strbuf->length - 1]);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWith(const FFstrbuf* strbuf, const FFstrbuf* end)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWith(const FFstrbuf* strbuf, const FFstrbuf* end) {
|
||||
return ffStrbufEndsWithNS(strbuf, end->length, end->chars);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end)
|
||||
{
|
||||
if(endLength > strbuf->length)
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithIgnCaseNS(const FFstrbuf* strbuf, uint32_t endLength, const char* end) {
|
||||
if (endLength > strbuf->length) {
|
||||
return false;
|
||||
}
|
||||
return strcasecmp(strbuf->chars + strbuf->length - endLength, end) == 0;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithIgnCaseS(const FFstrbuf* strbuf, const char* end)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithIgnCaseS(const FFstrbuf* strbuf, const char* end) {
|
||||
return ffStrbufEndsWithIgnCaseNS(strbuf, (uint32_t) strlen(end), end);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufEndsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* end)
|
||||
{
|
||||
static inline FF_A_NODISCARD bool ffStrbufEndsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* end) {
|
||||
return ffStrbufEndsWithIgnCaseNS(strbuf, end->length, end->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufTrim(FFstrbuf* strbuf, char c)
|
||||
{
|
||||
static inline void ffStrbufTrim(FFstrbuf* strbuf, char c) {
|
||||
ffStrbufTrimRight(strbuf, c);
|
||||
ffStrbufTrimLeft(strbuf, c);
|
||||
}
|
||||
|
||||
static inline void ffStrbufTrimSpace(FFstrbuf* strbuf)
|
||||
{
|
||||
static inline void ffStrbufTrimSpace(FFstrbuf* strbuf) {
|
||||
ffStrbufTrimRightSpace(strbuf);
|
||||
ffStrbufTrimLeftSpace(strbuf);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufMatchSeparatedS(const FFstrbuf* strbuf, const char* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufMatchSeparatedS(const FFstrbuf* strbuf, const char* comp, char separator) {
|
||||
return ffStrbufMatchSeparatedNS(strbuf, (uint32_t) strlen(comp), comp, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufMatchSeparated(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufMatchSeparated(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator) {
|
||||
return ffStrbufMatchSeparatedNS(strbuf, comp->length, comp->chars, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufMatchSeparatedIgnCaseS(const FFstrbuf* strbuf, const char* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufMatchSeparatedIgnCaseS(const FFstrbuf* strbuf, const char* comp, char separator) {
|
||||
return ffStrbufMatchSeparatedIgnCaseNS(strbuf, (uint32_t) strlen(comp), comp, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufMatchSeparatedIgnCase(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufMatchSeparatedIgnCase(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator) {
|
||||
return ffStrbufMatchSeparatedIgnCaseNS(strbuf, comp->length, comp->chars, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSeparatedContainS(const FFstrbuf* strbuf, const char* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufSeparatedContainS(const FFstrbuf* strbuf, const char* comp, char separator) {
|
||||
return ffStrbufSeparatedContainNS(strbuf, (uint32_t) strlen(comp), comp, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSeparatedContain(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufSeparatedContain(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator) {
|
||||
return ffStrbufSeparatedContainNS(strbuf, comp->length, comp->chars, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSeparatedContainIgnCaseS(const FFstrbuf* strbuf, const char* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufSeparatedContainIgnCaseS(const FFstrbuf* strbuf, const char* comp, char separator) {
|
||||
return ffStrbufSeparatedContainIgnCaseNS(strbuf, (uint32_t) strlen(comp), comp, separator);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSeparatedContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator)
|
||||
{
|
||||
static inline bool ffStrbufSeparatedContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* comp, char separator) {
|
||||
return ffStrbufSeparatedContainIgnCaseNS(strbuf, comp->length, comp->chars, separator);
|
||||
}
|
||||
|
||||
#define FF_STRBUF_AUTO_DESTROY FFstrbuf __attribute__((__cleanup__(ffStrbufDestroy)))
|
||||
// Returns true if the strbuf is modified
|
||||
bool ffStrbufDecodeHexEscapeSequences(FFstrbuf* strbuf);
|
||||
|
||||
#define FF_STRBUF_AUTO_DESTROY FFstrbuf FF_A_CLEANUP(ffStrbufDestroy)
|
||||
@@ -0,0 +1,225 @@
|
||||
#include "cf_helpers.h"
|
||||
|
||||
const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result) {
|
||||
if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
|
||||
if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt64Type, result)) {
|
||||
return "Number type is not SInt64";
|
||||
}
|
||||
return NULL;
|
||||
} else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
|
||||
if (CFDataGetLength((CFDataRef) cf) != sizeof(int64_t)) {
|
||||
return "Data length is not sizeof(int64_t)";
|
||||
}
|
||||
CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(int64_t)), (uint8_t*) result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return "TypeID is neither 'CFNumber' nor 'CFData'";
|
||||
}
|
||||
|
||||
const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result) {
|
||||
if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
|
||||
if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt32Type, result)) {
|
||||
return "Number type is not SInt32";
|
||||
}
|
||||
return NULL;
|
||||
} else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
|
||||
if (CFDataGetLength((CFDataRef) cf) != sizeof(*result)) {
|
||||
return "Data length is not sizeof(int32_t)";
|
||||
}
|
||||
CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(*result)), (uint8_t*) result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return "TypeID is neither 'CFNumber' nor 'CFData'";
|
||||
}
|
||||
|
||||
const char* ffCfNumGetDouble(CFTypeRef cf, double* result) {
|
||||
if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
|
||||
if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberDoubleType, result) &&
|
||||
!CFNumberGetValue((CFNumberRef) cf, kCFNumberFloatType, result)) {
|
||||
return "Number type is not Double or Float";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return "TypeID is neither 'CFNumber'";
|
||||
}
|
||||
|
||||
const char* ffCfDateGetEpoch(CFTypeRef cf, uint64_t* result) {
|
||||
if (CFGetTypeID(cf) != CFDateGetTypeID()) {
|
||||
return "TypeID is not 'CFDate'";
|
||||
}
|
||||
|
||||
CFAbsoluteTime absTime = CFDateGetAbsoluteTime((CFDateRef) cf);
|
||||
// Convert from seconds to milliseconds and add the difference between 1970 and 2001 in milliseconds
|
||||
*result = (uint64_t) ((absTime + 978307200 /*kCFAbsoluteTimeIntervalSince1970*/) * 1000);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
|
||||
ffStrbufClear(result);
|
||||
if (!cf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (CFGetTypeID(cf) == CFStringGetTypeID()) {
|
||||
CFStringRef cfStr = (CFStringRef) cf;
|
||||
|
||||
const char* cstr = CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8);
|
||||
if (cstr) {
|
||||
ffStrbufSetS(result, cstr);
|
||||
} else {
|
||||
uint32_t length = (uint32_t) CFStringGetLength(cfStr);
|
||||
if (length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
ffStrbufEnsureFixedLengthFree(result, (uint32_t) CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8));
|
||||
if (!CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8)) {
|
||||
return "CFStringGetCString() failed";
|
||||
}
|
||||
// CFStringGetCString ensures the buffer is NUL terminated
|
||||
// https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring
|
||||
result->length = (uint32_t) strnlen(result->chars, (uint32_t) result->allocated);
|
||||
}
|
||||
} else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
|
||||
CFDataRef cfData = (CFDataRef) cf;
|
||||
uint32_t length = (uint32_t) CFDataGetLength(cfData);
|
||||
if (length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
ffStrbufEnsureFixedLengthFree(result, length + 1);
|
||||
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
|
||||
result->length = (uint32_t) strnlen(result->chars, length);
|
||||
result->chars[result->length] = '\0';
|
||||
} else {
|
||||
return "TypeID is neither 'CFString' nor 'CFData'";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result) {
|
||||
ffStrbufClear(result);
|
||||
if (!cf) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (CFGetTypeID(cf) == CFDataGetTypeID()) {
|
||||
CFDataRef cfData = (CFDataRef) cf;
|
||||
uint32_t length = (uint32_t) CFDataGetLength(cfData);
|
||||
if (length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
ffStrbufEnsureFixedLengthFree(result, length + 1);
|
||||
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
|
||||
result->length = length;
|
||||
result->chars[result->length] = '\0';
|
||||
} else {
|
||||
return "TypeID is not 'CFData'";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfStrGetString(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfDataGetDataAsString(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) {
|
||||
CFBooleanRef cf = (CFBooleanRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
if (CFGetTypeID(cf) != CFBooleanGetTypeID()) {
|
||||
return "TypeID is not 'CFBoolean'";
|
||||
}
|
||||
|
||||
*result = CFBooleanGetValue(cf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfNumGetInt(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfNumGetInt64(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfNumGetDouble(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
if (CFGetTypeID(cf) != CFDataGetTypeID()) {
|
||||
return "TypeID is not 'CFData'";
|
||||
}
|
||||
|
||||
CFIndex trueLength = CFDataGetLength((CFDataRef) cf);
|
||||
|
||||
if (trueLength < offset + size) {
|
||||
return "Data length is less than offset + size";
|
||||
}
|
||||
|
||||
if (length) {
|
||||
*length = (uint32_t) trueLength;
|
||||
}
|
||||
|
||||
CFDataGetBytes((CFDataRef) cf, CFRangeMake(offset, size), result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result) {
|
||||
CFDictionaryRef cf = (CFDictionaryRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL || CFGetTypeID(cf) != CFDictionaryGetTypeID()) {
|
||||
return "TypeID is not 'CFDictionary'";
|
||||
}
|
||||
|
||||
*result = cf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDictGetDateAsEpoch(CFDictionaryRef dict, CFStringRef key, uint64_t* result) {
|
||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||
if (cf == NULL) {
|
||||
return "CFDictionaryGetValue() failed";
|
||||
}
|
||||
|
||||
return ffCfDateGetEpoch(cf, result);
|
||||
}
|
||||
@@ -4,34 +4,40 @@
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
|
||||
//Return error info if failed, NULL otherwise
|
||||
// Return error info if failed, NULL otherwise
|
||||
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result);
|
||||
const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result);
|
||||
const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result);
|
||||
const char* ffCfNumGetDouble(CFTypeRef cf, double* result);
|
||||
const char* ffCfDateGetEpoch(CFTypeRef cf, uint64_t* result);
|
||||
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result);
|
||||
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
||||
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result);
|
||||
const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result);
|
||||
const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result);
|
||||
const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* result);
|
||||
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length);
|
||||
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
||||
const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result);
|
||||
const char* ffCfDictGetDateAsEpoch(CFDictionaryRef dict, CFStringRef key, uint64_t* result);
|
||||
|
||||
static inline CFNumberRef ffCfCreateInt(int value)
|
||||
{
|
||||
static inline CFNumberRef ffCfCreateInt(int value) {
|
||||
return CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value);
|
||||
}
|
||||
|
||||
static inline void cfReleaseWrapper(void* type)
|
||||
{
|
||||
if (*(CFTypeRef*) type)
|
||||
static inline void cfReleaseWrapper(void* type) {
|
||||
assert(type);
|
||||
if (*(CFTypeRef*) type) {
|
||||
CFRelease(*(CFTypeRef*) type);
|
||||
}
|
||||
}
|
||||
|
||||
#define FF_CFTYPE_AUTO_RELEASE __attribute__((__cleanup__(cfReleaseWrapper)))
|
||||
#define FF_CFTYPE_AUTO_RELEASE FF_A_CLEANUP(cfReleaseWrapper)
|
||||
|
||||
static inline void wrapIoObjectRelease(io_object_t* service)
|
||||
{
|
||||
static inline void wrapIoObjectRelease(io_object_t* service) {
|
||||
assert(service);
|
||||
if (*service)
|
||||
if (*service) {
|
||||
IOObjectRelease(*service);
|
||||
}
|
||||
}
|
||||
#define FF_IOOBJECT_AUTO_RELEASE __attribute__((__cleanup__(wrapIoObjectRelease)))
|
||||
#define FF_IOOBJECT_AUTO_RELEASE FF_A_CLEANUP(wrapIoObjectRelease)
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "osascript.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
bool ffOsascript(const char* input, FFstrbuf* result)
|
||||
{
|
||||
@@ -0,0 +1,465 @@
|
||||
#include "smc_temps.h"
|
||||
#include "common/apple/cf_helpers.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
|
||||
static const char kSmcCmdReadBytes = 5;
|
||||
static const char kSmcCmdReadKeyInfo = 9;
|
||||
static const uint32_t kKernelIndexSmc = 2;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char major;
|
||||
char minor;
|
||||
char build;
|
||||
char reserved[1];
|
||||
uint16_t release;
|
||||
} SmcKeyData_vers_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t version;
|
||||
uint16_t length;
|
||||
uint32_t cpuPLimit;
|
||||
uint32_t gpuPLimit;
|
||||
uint32_t memPLimit;
|
||||
} SmcKeyData_pLimitData_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t dataSize;
|
||||
uint32_t dataType;
|
||||
char dataAttributes;
|
||||
} SmcKeyData_keyInfo_t;
|
||||
|
||||
typedef unsigned char SmcBytes_t[32];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t key;
|
||||
SmcKeyData_vers_t vers;
|
||||
SmcKeyData_pLimitData_t pLimitData;
|
||||
SmcKeyData_keyInfo_t keyInfo;
|
||||
char result;
|
||||
char status;
|
||||
char data8;
|
||||
uint32_t data32;
|
||||
SmcBytes_t bytes;
|
||||
} SmcKeyData_t;
|
||||
|
||||
typedef char UInt32Char_t[5];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UInt32Char_t key;
|
||||
uint32_t dataSize;
|
||||
UInt32Char_t dataType;
|
||||
SmcBytes_t bytes;
|
||||
} SmcVal_t;
|
||||
|
||||
static uint32_t smcStrtoul(const char* str, int size, int base) {
|
||||
uint32_t total = 0;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (base == 16) {
|
||||
total += (uint32_t) (str[i] << (size - 1 - i) * 8);
|
||||
} else {
|
||||
total += (uint32_t) ((unsigned char) (str[i]) << (size - 1 - i) * 8);
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static void smcUltostr(char* str, uint32_t val) {
|
||||
str[0] = (char) (val >> 24);
|
||||
str[1] = (char) (val >> 16);
|
||||
str[2] = (char) (val >> 8);
|
||||
str[3] = (char) val;
|
||||
str[4] = '\0';
|
||||
}
|
||||
|
||||
static const char* smcCall(io_connect_t conn, uint32_t selector, SmcKeyData_t* inputStructure, SmcKeyData_t* outputStructure) {
|
||||
size_t size = sizeof(SmcKeyData_t);
|
||||
|
||||
if (IOConnectCallStructMethod(conn, selector, inputStructure, size, outputStructure, &size) != kIOReturnSuccess) {
|
||||
return "IOConnectCallStructMethod(conn) failed";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Provides key info, using a cache to dramatically improve the energy impact of smcFanControl
|
||||
static const char* smcGetKeyInfo(io_connect_t conn, const uint32_t key, SmcKeyData_keyInfo_t* key_info) {
|
||||
SmcKeyData_t inputStructure = { 0 };
|
||||
SmcKeyData_t outputStructure = { 0 };
|
||||
|
||||
inputStructure.key = key;
|
||||
inputStructure.data8 = kSmcCmdReadKeyInfo;
|
||||
|
||||
const char* error = smcCall(conn, kKernelIndexSmc, &inputStructure, &outputStructure);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
*key_info = outputStructure.keyInfo;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* smcReadSmcVal(io_connect_t conn, const UInt32Char_t key, SmcVal_t* val) {
|
||||
SmcKeyData_t inputStructure = { 0 };
|
||||
SmcKeyData_t outputStructure = { 0 };
|
||||
|
||||
inputStructure.key = smcStrtoul(key, 4, 16);
|
||||
strcpy(val->key, key);
|
||||
|
||||
const char* error = smcGetKeyInfo(conn, inputStructure.key, &outputStructure.keyInfo);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
val->dataSize = outputStructure.keyInfo.dataSize;
|
||||
smcUltostr(val->dataType, outputStructure.keyInfo.dataType);
|
||||
inputStructure.keyInfo.dataSize = val->dataSize;
|
||||
inputStructure.data8 = kSmcCmdReadBytes;
|
||||
|
||||
error = smcCall(conn, kKernelIndexSmc, &inputStructure, &outputStructure);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* smcOpen(io_connect_t* conn) {
|
||||
FF_IOOBJECT_AUTO_RELEASE io_object_t device = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("AppleSMC"));
|
||||
if (!device) {
|
||||
return "No SMC device found";
|
||||
}
|
||||
|
||||
if (IOServiceOpen(device, mach_task_self(), 0, conn) != kIOReturnSuccess) {
|
||||
return "IOServiceOpen() failed";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* smcReadValue(io_connect_t conn, const UInt32Char_t key, double* value) {
|
||||
SmcVal_t val = { 0 };
|
||||
const char* error = smcReadSmcVal(conn, key, &val);
|
||||
if (error != NULL) {
|
||||
return error;
|
||||
}
|
||||
if (val.dataSize == 0) {
|
||||
return "Empty SMC result";
|
||||
}
|
||||
|
||||
switch (val.dataType[0]) {
|
||||
case 'u': // unsigned integer types
|
||||
if (val.dataType[1] == 'i') {
|
||||
switch (val.dataSize) {
|
||||
case 1:
|
||||
*value = *(uint8_t*) (val.bytes);
|
||||
break;
|
||||
case 2:
|
||||
*value = ntohs(*(uint16_t*) (val.bytes));
|
||||
break;
|
||||
case 4:
|
||||
*value = ntohl(*(uint32_t*) (val.bytes));
|
||||
break;
|
||||
case 8:
|
||||
*value = (double) ntohll(*(uint64_t*) (val.bytes));
|
||||
break;
|
||||
default:
|
||||
return "Unsupported SMC unsigned integer data size";
|
||||
}
|
||||
} else {
|
||||
return "Unsupported SMC unsigned data type";
|
||||
}
|
||||
break;
|
||||
|
||||
case 'f': // floating point types
|
||||
if (ffStrEquals(val.dataType, "flt ") && val.dataSize == 4) {
|
||||
*value = *(float*) (val.bytes);
|
||||
} else if (val.dataType[1] == 'p' && val.dataSize == 2) // fixed point types
|
||||
{
|
||||
if (ffStrEquals(val.dataType, "fp1f")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 32768.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp4c")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 4096.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp5b")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 2048.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp6a")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 1024.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp79")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 512.0;
|
||||
} else if (ffStrEquals(val.dataType, "fp88")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 256.0;
|
||||
} else if (ffStrEquals(val.dataType, "fpa6")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 64.0;
|
||||
} else if (ffStrEquals(val.dataType, "fpc4")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 16.0;
|
||||
} else if (ffStrEquals(val.dataType, "fpe2")) {
|
||||
*value = ntohs(*(uint16_t*) (val.bytes)) / 4.0;
|
||||
} else {
|
||||
return "Unsupported SMC floating point data type";
|
||||
}
|
||||
} else {
|
||||
return "Unsupported SMC floating point data type";
|
||||
}
|
||||
break;
|
||||
|
||||
case 's': // signed integer types
|
||||
if (val.dataType[1] == 'i') {
|
||||
switch (val.dataSize) {
|
||||
case 1:
|
||||
*value = *(int8_t*) (val.bytes);
|
||||
break;
|
||||
case 2:
|
||||
*value = ntohs(*(int16_t*) (val.bytes));
|
||||
break;
|
||||
case 4:
|
||||
*value = ntohl(*(int32_t*) (val.bytes));
|
||||
break;
|
||||
case 8:
|
||||
*value = (double) ntohll(*(int64_t*) (val.bytes));
|
||||
break;
|
||||
default:
|
||||
return "Unsupported SMC signed integer data size";
|
||||
}
|
||||
} else if (val.dataType[1] == 'p' && val.dataSize == 2) // signed fixed point types
|
||||
{
|
||||
if (ffStrEquals(val.dataType, "sp1e")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 16384.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp3c")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 4096.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp4b")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 2048.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp5a")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 1024.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp69")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 512.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp78")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 256.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp87")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 128.0;
|
||||
} else if (ffStrEquals(val.dataType, "sp96")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 64.0;
|
||||
} else if (ffStrEquals(val.dataType, "spb4")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 16.0;
|
||||
} else if (ffStrEquals(val.dataType, "spf0")) {
|
||||
*value = (int16_t) ntohs(*(int16_t*) (val.bytes)) / 1.0;
|
||||
} else {
|
||||
return "Unsupported SMC signed integer data type";
|
||||
}
|
||||
} else {
|
||||
return "Unsupported SMC signed data type";
|
||||
}
|
||||
break;
|
||||
|
||||
case '{': // special types like pwm
|
||||
if (ffStrEquals(val.dataType, "{pwm") && val.dataSize == 2) {
|
||||
*value = (double) ntohs(*(uint16_t*) (val.bytes)) * 100 / 65536.0;
|
||||
} else {
|
||||
return "Unsupported SMC special data type";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return "Unsupported SMC data type";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool detectTemp(io_connect_t conn, const char* sensor, double* sum) {
|
||||
double temp = 0;
|
||||
const char* error = smcReadValue(conn, sensor, &temp);
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
// https://github.com/exelban/stats/blob/14e29c4d60229c363cca9c9d25c30c87b7870830/Modules/Sensors/readers.swift#L124
|
||||
if (temp < 10 || temp > 120) {
|
||||
return false;
|
||||
}
|
||||
*sum += temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
static io_connect_t conn;
|
||||
|
||||
const char* ffDetectSmcSpecificTemp(const char* sensor, double* result) {
|
||||
if (!conn) {
|
||||
if (smcOpen(&conn) != NULL) {
|
||||
conn = (io_connect_t) -1;
|
||||
}
|
||||
}
|
||||
if (conn == (io_connect_t) -1) {
|
||||
return "Could not open SMC connection";
|
||||
}
|
||||
|
||||
if (!detectTemp(conn, sensor, result)) {
|
||||
return "Could not read SMC temperature";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
|
||||
if (!conn) {
|
||||
if (smcOpen(&conn) != NULL) {
|
||||
conn = (io_connect_t) -1;
|
||||
}
|
||||
}
|
||||
if (conn == (io_connect_t) -1) {
|
||||
return "Could not open SMC connection";
|
||||
}
|
||||
|
||||
uint32_t count = 0;
|
||||
*result = 0;
|
||||
|
||||
// https://github.com/exelban/stats/blob/master/Modules/Sensors/values.swift
|
||||
switch (type) {
|
||||
case FF_TEMP_CPU_X64:
|
||||
count += detectTemp(conn, "TC0D", result); // CPU diode
|
||||
count += detectTemp(conn, "TC0E", result); // CPU diode virtual
|
||||
count += detectTemp(conn, "TC0F", result); // CPU diode filtered
|
||||
count += detectTemp(conn, "TC0P", result); // CPU proximity
|
||||
break;
|
||||
|
||||
case FF_TEMP_CPU_M1X:
|
||||
count += detectTemp(conn, "Tp09", result); // CPU efficient core 1
|
||||
count += detectTemp(conn, "Tp0T", result); // CPU efficient core 2
|
||||
|
||||
count += detectTemp(conn, "Tp01", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tp05", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tp0D", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tp0H", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tp0L", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tp0P", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tp0X", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tp0b", result); // CPU performance core 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_CPU_M2X:
|
||||
count += detectTemp(conn, "Tp1h", result); // CPU efficiency core 1
|
||||
count += detectTemp(conn, "Tp1t", result); // CPU efficiency core 2
|
||||
count += detectTemp(conn, "Tp1p", result); // CPU efficiency core 3
|
||||
count += detectTemp(conn, "Tp1l", result); // CPU efficiency core 4
|
||||
|
||||
count += detectTemp(conn, "Tp01", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tp05", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tp09", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tp0D", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tp0X", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tp0b", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tp0f", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tp0j", result); // CPU performance core 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_CPU_M3X:
|
||||
count += detectTemp(conn, "Te05", result); // CPU efficiency core 1
|
||||
count += detectTemp(conn, "Te0L", result); // CPU efficiency core 2
|
||||
count += detectTemp(conn, "Te0P", result); // CPU efficiency core 3
|
||||
count += detectTemp(conn, "Te0S", result); // CPU efficiency core 4
|
||||
count += detectTemp(conn, "Tf04", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tf09", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tf0A", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tf0B", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tf0D", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tf0E", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tf44", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tf49", result); // CPU performance core 8
|
||||
count += detectTemp(conn, "Tf4A", result); // CPU performance core 9
|
||||
count += detectTemp(conn, "Tf4B", result); // CPU performance core 10
|
||||
count += detectTemp(conn, "Tf4D", result); // CPU performance core 11
|
||||
count += detectTemp(conn, "Tf4E", result); // CPU performance core 12
|
||||
break;
|
||||
|
||||
case FF_TEMP_CPU_M4X:
|
||||
count += detectTemp(conn, "Te05", result); // CPU efficiency core 1
|
||||
count += detectTemp(conn, "Te0S", result); // CPU efficiency core 2
|
||||
count += detectTemp(conn, "Te09", result); // CPU efficiency core 3
|
||||
count += detectTemp(conn, "Te0H", result); // CPU efficiency core 4
|
||||
count += detectTemp(conn, "Tp01", result); // CPU performance core 1
|
||||
count += detectTemp(conn, "Tp05", result); // CPU performance core 2
|
||||
count += detectTemp(conn, "Tp09", result); // CPU performance core 3
|
||||
count += detectTemp(conn, "Tp0D", result); // CPU performance core 4
|
||||
count += detectTemp(conn, "Tp0V", result); // CPU performance core 5
|
||||
count += detectTemp(conn, "Tp0Y", result); // CPU performance core 6
|
||||
count += detectTemp(conn, "Tp0b", result); // CPU performance core 7
|
||||
count += detectTemp(conn, "Tp0e", result); // CPU performance core 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_INTEL:
|
||||
count += detectTemp(conn, "TCGC", result); // GPU Intel Graphics
|
||||
goto gpu_unknown;
|
||||
|
||||
case FF_TEMP_GPU_AMD:
|
||||
count += detectTemp(conn, "TGDD", result); // GPU AMD Radeon
|
||||
goto gpu_unknown;
|
||||
|
||||
case FF_TEMP_GPU_UNKNOWN: // Nvidia?
|
||||
gpu_unknown:
|
||||
count += detectTemp(conn, "TG0D", result); // GPU diode
|
||||
count += detectTemp(conn, "TG0P", result); // GPU proximity
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_M1X:
|
||||
count += detectTemp(conn, "Tg05", result); // GPU 1
|
||||
count += detectTemp(conn, "Tg0D", result); // GPU 2
|
||||
count += detectTemp(conn, "Tg0L", result); // GPU 3
|
||||
count += detectTemp(conn, "Tg0T", result); // GPU 4
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_M2X:
|
||||
count += detectTemp(conn, "Tg0f", result); // GPU 1
|
||||
count += detectTemp(conn, "Tg0j", result); // GPU 2
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_M3X:
|
||||
count += detectTemp(conn, "Tf14", result); // GPU 1
|
||||
count += detectTemp(conn, "Tf18", result); // GPU 2
|
||||
count += detectTemp(conn, "Tf19", result); // GPU 3
|
||||
count += detectTemp(conn, "Tf1A", result); // GPU 4
|
||||
count += detectTemp(conn, "Tf24", result); // GPU 5
|
||||
count += detectTemp(conn, "Tf28", result); // GPU 6
|
||||
count += detectTemp(conn, "Tf29", result); // GPU 7
|
||||
count += detectTemp(conn, "Tf2A", result); // GPU 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_GPU_M4X:
|
||||
count += detectTemp(conn, "Tg0G", result); // GPU 1 (Basic)
|
||||
count += detectTemp(conn, "Tg0H", result); // GPU 2 (Basic)
|
||||
count += detectTemp(conn, "Tg1U", result); // GPU 1 (Pro / Max)
|
||||
count += detectTemp(conn, "Tg1k", result); // GPU 2 (Pro / Max)
|
||||
count += detectTemp(conn, "Tg0K", result); // GPU 3
|
||||
count += detectTemp(conn, "Tg0L", result); // GPU 4
|
||||
count += detectTemp(conn, "Tg0d", result); // GPU 5
|
||||
count += detectTemp(conn, "Tg0e", result); // GPU 6
|
||||
count += detectTemp(conn, "Tg0j", result); // GPU 7
|
||||
count += detectTemp(conn, "Tg0k", result); // GPU 8
|
||||
break;
|
||||
|
||||
case FF_TEMP_BATTERY:
|
||||
count += detectTemp(conn, "TB1T", result); // Battery
|
||||
count += detectTemp(conn, "TB2T", result); // Battery
|
||||
break;
|
||||
|
||||
case FF_TEMP_MEMORY:
|
||||
count += detectTemp(conn, "Tm02", result); // Memory 1
|
||||
count += detectTemp(conn, "Tm06", result); // Memory 2
|
||||
count += detectTemp(conn, "Tm08", result); // Memory 3
|
||||
count += detectTemp(conn, "Tm09", result); // Memory 4
|
||||
break;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
return "No temperatures detected";
|
||||
}
|
||||
|
||||
*result /= count;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -2,15 +2,13 @@
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFTempValue
|
||||
{
|
||||
typedef struct FFTempValue {
|
||||
FFstrbuf name;
|
||||
FFstrbuf deviceClass;
|
||||
double value;
|
||||
} FFTempValue;
|
||||
|
||||
enum FFTempType
|
||||
{
|
||||
enum FFTempType {
|
||||
FF_TEMP_CPU_X64,
|
||||
FF_TEMP_CPU_M1X,
|
||||
FF_TEMP_CPU_M2X,
|
||||
@@ -30,4 +28,5 @@ enum FFTempType
|
||||
FF_TEMP_MEMORY,
|
||||
};
|
||||
|
||||
const char* ffDetectSmcSpecificTemp(const char* sensor, double* result);
|
||||
const char* ffDetectSmcTemps(enum FFTempType type, double* result);
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/FFstrbuf.h"
|
||||
|
||||
// For ffRegReadValue(s)
|
||||
// If length is 0, data will be allocated with malloc() and must be freed by the caller
|
||||
// Otherwise, data must point to a buffer of at least length bytes, and the actual length of the data will be written to length.
|
||||
typedef struct FFArgBuffer {
|
||||
void* data;
|
||||
uint32_t length;
|
||||
} FFArgBuffer;
|
||||
|
||||
typedef enum FF_A_PACKED FFArgType {
|
||||
FF_ARG_TYPE_NULL = 0,
|
||||
FF_ARG_TYPE_UINT,
|
||||
FF_ARG_TYPE_UINT64,
|
||||
FF_ARG_TYPE_UINT16,
|
||||
FF_ARG_TYPE_UINT8,
|
||||
FF_ARG_TYPE_INT,
|
||||
FF_ARG_TYPE_STRING,
|
||||
FF_ARG_TYPE_STRBUF,
|
||||
FF_ARG_TYPE_FLOAT,
|
||||
FF_ARG_TYPE_DOUBLE,
|
||||
FF_ARG_TYPE_LIST,
|
||||
FF_ARG_TYPE_BOOL,
|
||||
FF_ARG_TYPE_BUFFER,
|
||||
} FFArgType;
|
||||
|
||||
#define FF_ARG(variable, var_name) { _Generic((variable), \
|
||||
uint32_t: FF_ARG_TYPE_UINT, \
|
||||
uint64_t: FF_ARG_TYPE_UINT64, \
|
||||
uint16_t: FF_ARG_TYPE_UINT16, \
|
||||
uint8_t: FF_ARG_TYPE_UINT8, \
|
||||
int32_t: FF_ARG_TYPE_INT, \
|
||||
char*: FF_ARG_TYPE_STRING, \
|
||||
const char*: FF_ARG_TYPE_STRING, \
|
||||
FFstrbuf: FF_ARG_TYPE_STRBUF, \
|
||||
float: FF_ARG_TYPE_FLOAT, \
|
||||
double: FF_ARG_TYPE_DOUBLE, \
|
||||
FFlist: FF_ARG_TYPE_LIST, \
|
||||
bool: FF_ARG_TYPE_BOOL, \
|
||||
FFArgBuffer: FF_ARG_TYPE_BUFFER), \
|
||||
_Generic((variable), char*: (variable), const char*: (variable), default: &(variable)), \
|
||||
(var_name) }
|
||||
@@ -3,7 +3,9 @@
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef __has_builtin
|
||||
#if __has_builtin(__is_array)
|
||||
#if !__cplusplus && FF_SUPPORTS_COUNT_OF
|
||||
#define ARRAY_SIZE(x) _Countof(x)
|
||||
#elif __has_builtin(__is_array)
|
||||
#define ARRAY_SIZE(x) ({ static_assert(__is_array(__typeof__(x)), "Must be an array"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
|
||||
#elif __has_builtin(__builtin_types_compatible_p)
|
||||
#define ARRAY_SIZE(x) ({ static_assert(!__builtin_types_compatible_p(__typeof__(x), __typeof__(&*(x))), "Must not be a pointer"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __has_attribute
|
||||
#error Unsupported compiler
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#define FF_A_FALLTHROUGH __attribute__((__fallthrough__))
|
||||
#define FF_A_DEPRECATED __attribute__((__deprecated__))
|
||||
#define FF_A_CLEANUP(func) __attribute__((__cleanup__(func)))
|
||||
#define FF_A_NODISCARD __attribute__((__warn_unused_result__))
|
||||
#define FF_A_PRINTF(formatStrIndex, argsStartIndex) __attribute__((__format__(printf, formatStrIndex, argsStartIndex)))
|
||||
#define FF_A_SCANF(formatStrIndex, argsStartIndex) __attribute__((__format__(scanf, formatStrIndex, argsStartIndex)))
|
||||
#define FF_A_NONNULL(argIndex, ...) __attribute__((__nonnull__(argIndex, ##__VA_ARGS__)))
|
||||
#define FF_A_RETURNS_NONNULL __attribute__((__returns_nonnull__))
|
||||
#define FF_A_UNUSED __attribute__((__unused__))
|
||||
#define FF_A_PACKED __attribute__((__packed__))
|
||||
#define FF_A_WEAK_IMPORT __attribute__((__weak_import__))
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
void ffBase64EncodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output);
|
||||
static inline FFstrbuf ffBase64EncodeStrbuf(const FFstrbuf* in)
|
||||
{
|
||||
void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output);
|
||||
static inline FFstrbuf ffBase64EncodeStrbuf(const FFstrbuf* in) {
|
||||
FFstrbuf out = ffStrbufCreateA(10 + in->length * 4 / 3);
|
||||
ffBase64EncodeRaw(in->length, in->chars, &out.length, out.chars);
|
||||
assert(out.length < out.allocated);
|
||||
@@ -12,9 +11,8 @@ static inline FFstrbuf ffBase64EncodeStrbuf(const FFstrbuf* in)
|
||||
return out;
|
||||
}
|
||||
|
||||
bool ffBase64DecodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output);
|
||||
static inline FFstrbuf ffBase64DecodeStrbuf(const FFstrbuf* in)
|
||||
{
|
||||
bool ffBase64DecodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output);
|
||||
static inline FFstrbuf ffBase64DecodeStrbuf(const FFstrbuf* in) {
|
||||
FFstrbuf out = ffStrbufCreateA(10 + in->length * 3 / 4);
|
||||
ffBase64DecodeRaw(in->length, in->chars, &out.length, out.chars);
|
||||
assert(out.length < out.allocated);
|
||||
@@ -1,232 +0,0 @@
|
||||
#include "commandoption.h"
|
||||
#include "common/color.h"
|
||||
#include "common/printing.h"
|
||||
#include "common/time.h"
|
||||
#include "common/jsonconfig.h"
|
||||
#include "fastfetch_datatext.h"
|
||||
#include "modules/modules.h"
|
||||
#include "util/stringUtils.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
bool ffParseModuleOptions(const char* key, const char* value)
|
||||
{
|
||||
if (!ffStrStartsWith(key, "--") || !ffCharIsEnglishAlphabet(key[2])) return false;
|
||||
if (value && !*value) value = NULL;
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(key[2]) - 'A']; *modules; ++modules)
|
||||
{
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
const char* subKey = ffOptionTestPrefix(key, baseInfo->name);
|
||||
if (subKey != NULL)
|
||||
{
|
||||
if (subKey[0] == '\0' || subKey[0] == '-') // Key is exactly the module name or has a leading '-'
|
||||
{
|
||||
fprintf(stderr, "Error: unknown module key %s\n", key);
|
||||
exit(477);
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY moduleName = ffStrbufCreateS(baseInfo->name);
|
||||
ffStrbufLowerCase(&moduleName);
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY jsonKey = ffStrbufCreate();
|
||||
bool flag = false;
|
||||
for (const char* p = subKey; *p; ++p)
|
||||
{
|
||||
if (*p == '-')
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
fprintf(stderr, "Error: invalid double `-` in module key %s\n", key);
|
||||
exit(477);
|
||||
}
|
||||
flag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isalpha((unsigned char)*p) && !isdigit((unsigned char)*p))
|
||||
{
|
||||
fprintf(stderr, "Error: invalid character `%c` in module key %s\n", *p, key);
|
||||
exit(477);
|
||||
}
|
||||
|
||||
if (flag)
|
||||
{
|
||||
flag = false;
|
||||
ffStrbufAppendC(&jsonKey, (char) toupper((unsigned char) *p));
|
||||
}
|
||||
else
|
||||
ffStrbufAppendC(&jsonKey, *p);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: Unsupported module option: %s\n", key);
|
||||
fputs(" Support of module options has been removed. Please add the flag to the JSON config instead.\n", stderr);
|
||||
fprintf(stderr, " Example (demonstration only): `{ \"modules\": [ { \"type\": \"%s\", \"%s\": %s%s%s } ] }`\n", moduleName.chars, jsonKey.chars, value ? "\"" : "", value ? value : "true", value ? "\"" : "");
|
||||
fputs(" See <https://github.com/fastfetch-cli/fastfetch/wiki/Configuration> for more information.\n", stderr);
|
||||
exit(477);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffPrepareCommandOption(FFdata* data)
|
||||
{
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_CPUUSAGE_MODULE_NAME, ':'))
|
||||
ffPrepareCPUUsage();
|
||||
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_DISKIO_MODULE_NAME, ':'))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
|
||||
ffInitDiskIOOptions(&options);
|
||||
ffPrepareDiskIO(&options);
|
||||
}
|
||||
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_NETIO_MODULE_NAME, ':'))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
|
||||
ffInitNetIOOptions(&options);
|
||||
ffPrepareNetIO(&options);
|
||||
}
|
||||
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_PUBLICIP_MODULE_NAME, ':'))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
|
||||
ffInitPublicIpOptions(&options);
|
||||
ffPreparePublicIp(&options);
|
||||
}
|
||||
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_WEATHER_MODULE_NAME, ':'))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
|
||||
ffInitWeatherOptions(&options);
|
||||
ffPrepareWeather(&options);
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonConfig(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
|
||||
{
|
||||
yyjson_mut_val* modules = yyjson_mut_obj_get(doc->root, "modules");
|
||||
if (!modules)
|
||||
modules = yyjson_mut_obj_add_arr(doc, doc->root, "modules");
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY type = ffStrbufCreateS(baseInfo->name);
|
||||
ffStrbufLowerCase(&type);
|
||||
|
||||
if (instance.state.fullConfig)
|
||||
{
|
||||
yyjson_mut_val* module = yyjson_mut_obj(doc);
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "type", &type);
|
||||
|
||||
if (baseInfo->generateJsonConfig)
|
||||
baseInfo->generateJsonConfig(options, doc, module);
|
||||
|
||||
if (yyjson_mut_obj_size(module) > 1)
|
||||
yyjson_mut_arr_add_val(modules, module);
|
||||
else
|
||||
yyjson_mut_arr_add_strbuf(doc, modules, &type);
|
||||
}
|
||||
else
|
||||
{
|
||||
yyjson_mut_arr_add_strbuf(doc, modules, &type);
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonResult(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
|
||||
{
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
|
||||
yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name);
|
||||
if (baseInfo->generateJsonResult)
|
||||
baseInfo->generateJsonResult(options, doc, module);
|
||||
else
|
||||
yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format");
|
||||
}
|
||||
|
||||
static void parseStructureCommand(
|
||||
const char* line,
|
||||
void (*fn)(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* jsonDoc),
|
||||
yyjson_mut_doc* jsonDoc
|
||||
)
|
||||
{
|
||||
if(ffCharIsEnglishAlphabet(line[0]))
|
||||
{
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(line[0]) - 'A']; *modules; ++modules)
|
||||
{
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
if (ffStrEqualsIgnCase(line, baseInfo->name))
|
||||
{
|
||||
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
|
||||
baseInfo->initOptions(optionBuf);
|
||||
if (__builtin_expect(jsonDoc != NULL, false))
|
||||
fn(baseInfo, optionBuf, jsonDoc);
|
||||
else
|
||||
baseInfo->printModule(optionBuf);
|
||||
baseInfo->destroyOptions(optionBuf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffPrintError(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "<no implementation provided>");
|
||||
}
|
||||
|
||||
void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc)
|
||||
{
|
||||
//Parse the structure and call the modules
|
||||
int32_t thres = instance.config.display.stat;
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < data->structure.length)
|
||||
{
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(&data->structure, startIndex, ':');
|
||||
data->structure.chars[colonIndex] = '\0';
|
||||
|
||||
double ms = 0;
|
||||
if(thres >= 0)
|
||||
ms = ffTimeGetTick();
|
||||
|
||||
parseStructureCommand(data->structure.chars + startIndex, genJsonResult, jsonDoc);
|
||||
|
||||
if(thres >= 0)
|
||||
{
|
||||
ms = ffTimeGetTick() - ms;
|
||||
|
||||
if (jsonDoc)
|
||||
{
|
||||
yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(jsonDoc->root);
|
||||
yyjson_mut_obj_add_real(jsonDoc, moduleJson, "stat", ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
char str[64];
|
||||
int len = snprintf(str, sizeof str, "%.3fms", ms);
|
||||
if (thres > 0)
|
||||
snprintf(str, sizeof str, "\e[%sm%.3fms\e[m", (ms <= thres ? FF_COLOR_FG_GREEN : ms <= 2 * thres ? FF_COLOR_FG_YELLOW : FF_COLOR_FG_RED), ms);
|
||||
printf("\e7\e[1A\e[9999999C\e[%dD%s\e8", len, str); // Save; Up 1; Right 9999999; Left <len>; Print <str>; Load
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (!jsonDoc && !instance.config.display.noBuffer) fflush(stdout);
|
||||
#endif
|
||||
|
||||
startIndex = colonIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data, yyjson_mut_doc* jsonDoc)
|
||||
{
|
||||
//If we don't have a custom structure, use the default one
|
||||
if(data->structure.length == 0)
|
||||
ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
|
||||
|
||||
//Parse the structure and call the modules
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < data->structure.length)
|
||||
{
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(&data->structure, startIndex, ':');
|
||||
data->structure.chars[colonIndex] = '\0';
|
||||
|
||||
parseStructureCommand(data->structure.chars + startIndex, genJsonConfig, jsonDoc);
|
||||
|
||||
startIndex = colonIndex + 1;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
// Things only needed by fastfetch
|
||||
typedef struct FFdata
|
||||
{
|
||||
FFstrbuf structure;
|
||||
bool configLoaded;
|
||||
} FFdata;
|
||||
#include "common/ffdata.h"
|
||||
|
||||
void ffPrepareCommandOption(FFdata* data);
|
||||
void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc);
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data, yyjson_mut_doc* jsonDoc);
|
||||
void ffPrintCommandOption(FFdata* data);
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data);
|
||||
bool ffParseModuleOptions(const char* key, const char* value);
|
||||
|
||||
+14
-13
@@ -1,13 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef FF_HAVE_DBUS
|
||||
#include <dbus/dbus.h>
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "common/library.h"
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/library.h"
|
||||
|
||||
typedef struct FFDBusLibrary
|
||||
{
|
||||
typedef struct FFDBusLibrary {
|
||||
FF_LIBRARY_SYMBOL(dbus_bus_get)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_new_method_call)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_append_args)
|
||||
@@ -19,27 +18,29 @@ typedef struct FFDBusLibrary
|
||||
FF_LIBRARY_SYMBOL(dbus_message_iter_next)
|
||||
FF_LIBRARY_SYMBOL(dbus_message_unref)
|
||||
FF_LIBRARY_SYMBOL(dbus_connection_send_with_reply_and_block)
|
||||
FF_LIBRARY_SYMBOL(dbus_connection_unref)
|
||||
} FFDBusLibrary;
|
||||
|
||||
typedef struct FFDBusData
|
||||
{
|
||||
typedef struct FFDBusData {
|
||||
const FFDBusLibrary* lib;
|
||||
DBusConnection* connection;
|
||||
} FFDBusData;
|
||||
|
||||
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data); //Returns an error message or NULL on success
|
||||
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data); // Returns an error message or NULL on success
|
||||
bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result);
|
||||
bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result);
|
||||
bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint32_t* result);
|
||||
bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint64_t* result);
|
||||
DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method, const char* arg1, const char* arg2);
|
||||
DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property);
|
||||
bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result);
|
||||
bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int32_t* result);
|
||||
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint32_t* result);
|
||||
bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int64_t* result);
|
||||
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint64_t* result);
|
||||
void ffDBusDestroyData(FFDBusData* data);
|
||||
|
||||
static inline DBusMessage* ffDBusGetAllProperties(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface)
|
||||
{
|
||||
static inline DBusMessage* ffDBusGetAllProperties(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface) {
|
||||
return ffDBusGetMethodReply(dbus, busName, objectPath, "org.freedesktop.DBus.Properties", "GetAll", interface, NULL);
|
||||
}
|
||||
|
||||
#define FF_DBUS_AUTO_DESTROY_DATA FF_A_CLEANUP(ffDBusDestroyData)
|
||||
|
||||
#endif // FF_HAVE_DBUS
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "common/time.h"
|
||||
|
||||
static inline const char* ffFindFileName(const char* file) {
|
||||
const char* lastSlash = __builtin_strrchr(file, '/');
|
||||
#ifdef _WIN32
|
||||
if (lastSlash == NULL) {
|
||||
lastSlash = __builtin_strrchr(file, '\\');
|
||||
}
|
||||
#endif
|
||||
if (lastSlash != NULL) {
|
||||
return lastSlash + 1;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FF_DEBUG_PRINT(file_, line_, format_, ...) \
|
||||
do { \
|
||||
if (instance.config.display.debugMode) \
|
||||
fprintf(stderr, "[%s%4d, %s] " format_ "\n", ffFindFileName(file_), line_, ffTimeToTimeStr(ffTimeGetNow()), ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define FF_DEBUG_PRINT(file_, line_, format_, ...) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define FF_DEBUG(format, ...) FF_DEBUG_PRINT(__FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
|
||||
#if _WIN32
|
||||
const char* ffDebugWin32Error(DWORD errorCode);
|
||||
const char* ffDebugNtStatus(NTSTATUS status);
|
||||
const char* ffDebugHResult(HRESULT hr);
|
||||
#endif
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "common/FFstrbuf.h"
|
||||
|
||||
void ffEdidGetVendorAndModel(const uint8_t edid[128], FFstrbuf* result);
|
||||
bool ffEdidGetName(const uint8_t edid[128], FFstrbuf* name);
|
||||
@@ -10,3 +10,4 @@ void ffEdidGetPhysicalResolution(const uint8_t edid[128], uint32_t* width, uint3
|
||||
void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height); // in mm
|
||||
void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week);
|
||||
bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length);
|
||||
bool ffEdidIsValid(const uint8_t edid[128], uint32_t length);
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/FFstrbuf.h"
|
||||
|
||||
typedef enum FF_A_PACKED FFDataResultDocType {
|
||||
FF_RESULT_DOC_TYPE_DEFAULT = 0,
|
||||
FF_RESULT_DOC_TYPE_JSON,
|
||||
FF_RESULT_DOC_TYPE_CONFIG,
|
||||
FF_RESULT_DOC_TYPE_CONFIG_FULL,
|
||||
} FFDataResultDocType;
|
||||
|
||||
// FFdata aggregates configuration, generation parameters, and output state used by fastfetch.
|
||||
// It holds the parsed configuration document, a mutable JSON document for results, and related metadata.
|
||||
typedef struct FFdata {
|
||||
yyjson_doc* configDoc; // Parsed JSON configuration document
|
||||
yyjson_mut_doc* resultDoc; // Mutable JSON document for storing results
|
||||
FFstrbuf structure; // Custom output structure from command line
|
||||
FFstrbuf structureDisabled; // Disabled modules in the output structure from command line
|
||||
FFstrbuf genConfigPath; // Path to generate configuration file
|
||||
FFDataResultDocType docType; // Type of result document
|
||||
bool configLoaded;
|
||||
} FFdata;
|
||||
@@ -1,221 +0,0 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/font.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
void ffFontInit(FFfont* font)
|
||||
{
|
||||
// Ensure no memory allocates
|
||||
ffStrbufInit(&font->pretty);
|
||||
ffStrbufInit(&font->name);
|
||||
ffStrbufInit(&font->size);
|
||||
ffListInit(&font->styles, sizeof(FFstrbuf));
|
||||
}
|
||||
|
||||
static void strbufAppendNSExcludingC(FFstrbuf* strbuf, uint32_t length, const char* value, char exclude)
|
||||
{
|
||||
if(value == NULL || length == 0)
|
||||
return;
|
||||
|
||||
ffStrbufEnsureFree(strbuf, length);
|
||||
|
||||
for(uint32_t i = 0; i < length; i++)
|
||||
{
|
||||
if(value[i] != exclude)
|
||||
strbuf->chars[strbuf->length++] = value[i];
|
||||
}
|
||||
|
||||
strbuf->chars[strbuf->length] = '\0';
|
||||
}
|
||||
|
||||
static void fontInitPretty(FFfont* font)
|
||||
{
|
||||
ffStrbufAppend(&font->pretty, &font->name);
|
||||
|
||||
if(font->size.length == 0 && font->styles.length == 0)
|
||||
return;
|
||||
else if(font->pretty.length == 0)
|
||||
ffStrbufAppendS(&font->pretty, "default");
|
||||
|
||||
ffStrbufAppendS(&font->pretty, " (");
|
||||
|
||||
if(font->size.length > 0)
|
||||
{
|
||||
ffStrbufAppend(&font->pretty, &font->size);
|
||||
ffStrbufAppendS(&font->pretty, "pt");
|
||||
|
||||
if(font->styles.length > 0)
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < font->styles.length; i++)
|
||||
{
|
||||
ffStrbufAppend(&font->pretty, FF_LIST_GET(FFstrbuf, font->styles, i));
|
||||
|
||||
if(i < font->styles.length - 1)
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
|
||||
ffStrbufAppendC(&font->pretty, ')');
|
||||
}
|
||||
|
||||
void ffFontInitQt(FFfont* font, const char* data)
|
||||
{
|
||||
ffFontInit(font);
|
||||
|
||||
//See https://doc.qt.io/qt-5/qfont.html#toString
|
||||
|
||||
//Family
|
||||
data = ffStrbufAppendSUntilC(&font->name, data, ',');
|
||||
ffStrbufTrim(&font->name, ' ');
|
||||
if (!data) goto exit;
|
||||
data++;
|
||||
|
||||
//Size
|
||||
data = ffStrbufAppendSUntilC(&font->size, data, ',');
|
||||
ffStrbufTrim(&font->size, ' ');
|
||||
if (!data) goto exit;
|
||||
data++;
|
||||
|
||||
//Style
|
||||
data = strrchr(data, ',');
|
||||
if (!data) goto exit;
|
||||
data++;
|
||||
if (isalpha(*data))
|
||||
{
|
||||
do
|
||||
{
|
||||
FFstrbuf* style = (FFstrbuf*) ffListAdd(&font->styles);
|
||||
ffStrbufInit(style);
|
||||
data = ffStrbufAppendSUntilC(style, data, ' ');
|
||||
if (data) data++;
|
||||
} while (data);
|
||||
}
|
||||
|
||||
exit:
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* alternativeBuffer)
|
||||
{
|
||||
while(**data == ' ' || **data == '\t' || **data == ',')
|
||||
++(*data);
|
||||
|
||||
const char* wordStart = *data;
|
||||
|
||||
while(**data != ' ' && **data != '\t' && **data != ',' && **data != '\0' && **data != '`' && **data != '\\')
|
||||
++(*data);
|
||||
|
||||
uint32_t wordLength = (uint32_t) (*data - wordStart);
|
||||
if(wordLength == 0)
|
||||
return;
|
||||
|
||||
if(**data == '\0' || **data == '`' || **data == '\\')
|
||||
{
|
||||
ffStrbufAppendNS(&font->size, wordLength, wordStart);
|
||||
if(ffStrbufEndsWithS(&font->size, "px"))
|
||||
ffStrbufSubstrBefore(&font->size, font->size.length - 2);
|
||||
|
||||
double dummy;
|
||||
if(sscanf(font->size.chars, "%lf", &dummy) == 1)
|
||||
return;
|
||||
|
||||
ffStrbufClear(&font->size);
|
||||
}
|
||||
|
||||
if(
|
||||
strncasecmp(wordStart, "Ultra", 5) == 0 ||
|
||||
strncasecmp(wordStart, "Extra", 5) == 0 ||
|
||||
strncasecmp(wordStart, "Semi", 4) == 0 ||
|
||||
strncasecmp(wordStart, "Demi", 4) == 0 ||
|
||||
strncasecmp(wordStart, "Normal", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Roman", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Oblique", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Italic", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Thin", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Light", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Bold", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Black", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Condensed", wordLength) == 0 ||
|
||||
strncasecmp(wordStart, "Expanded", wordLength) == 0
|
||||
) {
|
||||
if(alternativeBuffer == NULL)
|
||||
{
|
||||
alternativeBuffer = (FFstrbuf*) ffListAdd(&font->styles);
|
||||
ffStrbufInit(alternativeBuffer);
|
||||
}
|
||||
|
||||
strbufAppendNSExcludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
|
||||
if(
|
||||
strncasecmp(wordStart, "Ultra ", 6) == 0 ||
|
||||
strncasecmp(wordStart, "Extra ", 6) == 0 ||
|
||||
strncasecmp(wordStart, "Semi ", 5) == 0 ||
|
||||
strncasecmp(wordStart, "Demi ", 5) == 0
|
||||
) {
|
||||
fontPangoParseWord(data, font, alternativeBuffer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(alternativeBuffer != NULL)
|
||||
{
|
||||
strbufAppendNSExcludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
return;
|
||||
}
|
||||
|
||||
if(font->name.length > 0)
|
||||
ffStrbufAppendC(&font->name, ' ');
|
||||
ffStrbufAppendNS(&font->name, wordLength, wordStart);
|
||||
}
|
||||
|
||||
void ffFontInitPango(FFfont* font, const char* data)
|
||||
{
|
||||
ffFontInit(font);
|
||||
|
||||
while(*data != '\0' && *data != '`' && *data != '\\')
|
||||
fontPangoParseWord(&data, font, NULL);
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitValues(FFfont* font, const char* name, const char* size)
|
||||
{
|
||||
ffFontInit(font);
|
||||
|
||||
ffStrbufAppendS(&font->name, name);
|
||||
ffStrbufTrim(&font->name, '"');
|
||||
ffStrbufAppendS(&font->size, size);
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitWithSpace(FFfont* font, const char* rawName)
|
||||
{
|
||||
const char* pspace = strrchr(rawName, ' ');
|
||||
if(pspace == NULL)
|
||||
{
|
||||
ffFontInitCopy(font, rawName);
|
||||
return;
|
||||
}
|
||||
|
||||
ffFontInit(font);
|
||||
|
||||
ffStrbufAppendNS(&font->name, (uint32_t)(pspace - rawName), rawName);
|
||||
ffStrbufAppendS(&font->size, pspace + 1);
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontDestroy(FFfont* font)
|
||||
{
|
||||
ffStrbufDestroy(&font->pretty);
|
||||
ffStrbufDestroy(&font->name);
|
||||
ffStrbufDestroy(&font->size);
|
||||
|
||||
FF_LIST_FOR_EACH(FFstrbuf, str, font->styles)
|
||||
ffStrbufDestroy(str);
|
||||
ffListDestroy(&font->styles);
|
||||
}
|
||||
+7
-6
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "util/FFlist.h"
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/FFlist.h"
|
||||
|
||||
typedef struct FFfont
|
||||
{
|
||||
typedef struct FFfont {
|
||||
FFstrbuf pretty;
|
||||
FFstrbuf name;
|
||||
FFstrbuf size;
|
||||
@@ -15,10 +14,12 @@ void ffFontInit(FFfont* font);
|
||||
void ffFontInitQt(FFfont* font, const char* data);
|
||||
void ffFontInitPango(FFfont* font, const char* data);
|
||||
void ffFontInitValues(FFfont* font, const char* name, const char* size);
|
||||
void ffFontInitXlfd(FFfont* font, const char* xlfd);
|
||||
void ffFontInitXft(FFfont* font, const char* xft);
|
||||
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style);
|
||||
void ffFontInitWithSpace(FFfont* font, const char* rawName);
|
||||
void ffFontDestroy(FFfont* font);
|
||||
|
||||
static inline void ffFontInitCopy(FFfont* font, const char* name)
|
||||
{
|
||||
static inline void ffFontInitCopy(FFfont* font, const char* name) {
|
||||
ffFontInitValues(font, name, NULL);
|
||||
}
|
||||
|
||||
+3
-35
@@ -1,41 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "common/argType.h"
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFformatArgType
|
||||
{
|
||||
FF_FORMAT_ARG_TYPE_NULL = 0,
|
||||
FF_FORMAT_ARG_TYPE_UINT,
|
||||
FF_FORMAT_ARG_TYPE_UINT64,
|
||||
FF_FORMAT_ARG_TYPE_UINT16,
|
||||
FF_FORMAT_ARG_TYPE_UINT8,
|
||||
FF_FORMAT_ARG_TYPE_INT,
|
||||
FF_FORMAT_ARG_TYPE_STRING,
|
||||
FF_FORMAT_ARG_TYPE_STRBUF,
|
||||
FF_FORMAT_ARG_TYPE_FLOAT,
|
||||
FF_FORMAT_ARG_TYPE_DOUBLE,
|
||||
FF_FORMAT_ARG_TYPE_LIST,
|
||||
FF_FORMAT_ARG_TYPE_BOOL
|
||||
} FFformatArgType;
|
||||
|
||||
#define FF_FORMAT_ARG(variable, var_name) { _Generic((variable), \
|
||||
uint32_t: FF_FORMAT_ARG_TYPE_UINT, \
|
||||
uint64_t: FF_FORMAT_ARG_TYPE_UINT64, \
|
||||
uint16_t: FF_FORMAT_ARG_TYPE_UINT16, \
|
||||
uint8_t: FF_FORMAT_ARG_TYPE_UINT8, \
|
||||
int32_t: FF_FORMAT_ARG_TYPE_INT, \
|
||||
char*: FF_FORMAT_ARG_TYPE_STRING, \
|
||||
const char*: FF_FORMAT_ARG_TYPE_STRING, \
|
||||
FFstrbuf: FF_FORMAT_ARG_TYPE_STRBUF, \
|
||||
float: FF_FORMAT_ARG_TYPE_FLOAT, \
|
||||
double: FF_FORMAT_ARG_TYPE_DOUBLE, \
|
||||
FFlist: FF_FORMAT_ARG_TYPE_LIST, \
|
||||
bool: FF_FORMAT_ARG_TYPE_BOOL \
|
||||
), _Generic((variable), char*: (variable), const char*: (variable), default: &(variable) ), (var_name) }
|
||||
|
||||
typedef struct FFformatarg
|
||||
{
|
||||
FFformatArgType type;
|
||||
typedef struct FFformatarg {
|
||||
FFArgType type;
|
||||
const void* value;
|
||||
const char* name; // argument name, must start with an alphabet
|
||||
} FFformatarg;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result);
|
||||
|
||||
@@ -5,20 +5,22 @@ extern "C" {
|
||||
#include <File.h>
|
||||
#include <AppFileInfo.h>
|
||||
|
||||
bool ffGetFileVersion(const char* filePath, FFstrbuf* version)
|
||||
{
|
||||
bool ffGetFileVersion(const char* filePath, FFstrbuf* version) {
|
||||
BFile f(filePath, B_READ_ONLY);
|
||||
if (f.InitCheck() != B_OK)
|
||||
if (f.InitCheck() != B_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BAppFileInfo fileInfo(&f);
|
||||
if (f.InitCheck() != B_OK)
|
||||
if (f.InitCheck() != B_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
version_info info;
|
||||
if (fileInfo.GetVersionInfo(&info, B_SYSTEM_VERSION_KIND) != B_OK)
|
||||
if (fileInfo.GetVersionInfo(&info, B_SYSTEM_VERSION_KIND) != B_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ffStrbufSetF(version, "%d.%d.%d", (int)info.major, (int)info.middle, (int)info.minor);
|
||||
ffStrbufSetF(version, "%d.%d.%d", (int) info.major, (int) info.middle, (int) info.minor);
|
||||
return true;
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "common/FFstrbuf.h"
|
||||
|
||||
bool ffGetFileVersion(const char* filePath, FFstrbuf* version);
|
||||
@@ -1,81 +1,94 @@
|
||||
#include "FFPlatform_private.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "common/io/io.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/io.h"
|
||||
#include "detection/version/version.h"
|
||||
|
||||
void ffPlatformInit(FFPlatform* platform)
|
||||
{
|
||||
void ffPlatformInit(FFPlatform* platform) {
|
||||
ffStrbufInit(&platform->homeDir);
|
||||
ffStrbufInit(&platform->cacheDir);
|
||||
ffListInit(&platform->configDirs, sizeof(FFstrbuf));
|
||||
ffListInit(&platform->dataDirs, sizeof(FFstrbuf));
|
||||
ffListInit(&platform->configDirs);
|
||||
ffListInit(&platform->dataDirs);
|
||||
ffStrbufInit(&platform->exePath);
|
||||
ffStrbufInit(&platform->cwd);
|
||||
|
||||
ffStrbufInit(&platform->userName);
|
||||
ffStrbufInit(&platform->fullUserName);
|
||||
ffStrbufInit(&platform->hostName);
|
||||
ffStrbufInit(&platform->userShell);
|
||||
|
||||
#ifdef _WIN32
|
||||
ffStrbufInit(&platform->sid);
|
||||
#endif
|
||||
|
||||
FFPlatformSysinfo* info = &platform->sysinfo;
|
||||
|
||||
ffStrbufInit(&info->name);
|
||||
ffStrbufInit(&info->release);
|
||||
ffStrbufInit(&info->version);
|
||||
ffStrbufInit(&info->architecture);
|
||||
info->pageSize = 0;
|
||||
|
||||
ffPlatformInitImpl(platform);
|
||||
|
||||
if(info->name.length == 0)
|
||||
if (info->name.length == 0) {
|
||||
ffStrbufSetStatic(&info->name, ffVersionResult.sysName);
|
||||
}
|
||||
|
||||
if(info->architecture.length == 0)
|
||||
if (info->architecture.length == 0) {
|
||||
ffStrbufSetStatic(&info->architecture, ffVersionResult.architecture);
|
||||
}
|
||||
}
|
||||
|
||||
void ffPlatformDestroy(FFPlatform* platform)
|
||||
{
|
||||
void ffPlatformDestroy(FFPlatform* platform) {
|
||||
ffStrbufDestroy(&platform->homeDir);
|
||||
ffStrbufDestroy(&platform->cacheDir);
|
||||
|
||||
FF_LIST_FOR_EACH(FFstrbuf, dir, platform->configDirs)
|
||||
FF_LIST_FOR_EACH (FFstrbuf, dir, platform->configDirs) {
|
||||
ffStrbufDestroy(dir);
|
||||
}
|
||||
ffListDestroy(&platform->configDirs);
|
||||
|
||||
FF_LIST_FOR_EACH(FFstrbuf, dir, platform->dataDirs)
|
||||
FF_LIST_FOR_EACH (FFstrbuf, dir, platform->dataDirs) {
|
||||
ffStrbufDestroy(dir);
|
||||
}
|
||||
ffListDestroy(&platform->dataDirs);
|
||||
ffStrbufDestroy(&platform->exePath);
|
||||
ffStrbufDestroy(&platform->cwd);
|
||||
|
||||
ffStrbufDestroy(&platform->userName);
|
||||
ffStrbufDestroy(&platform->hostName);
|
||||
ffStrbufDestroy(&platform->userShell);
|
||||
ffStrbufDestroy(&platform->fullUserName);
|
||||
|
||||
#ifdef _WIN32
|
||||
ffStrbufDestroy(&platform->sid);
|
||||
#endif
|
||||
|
||||
FFPlatformSysinfo* info = &platform->sysinfo;
|
||||
ffStrbufDestroy(&info->architecture);
|
||||
ffStrbufDestroy(&info->name);
|
||||
ffStrbufDestroy(&info->release);
|
||||
ffStrbufDestroy(&info->version);
|
||||
ffStrbufDestroy(&info->displayVersion);
|
||||
}
|
||||
|
||||
void ffPlatformPathAddAbsolute(FFlist* dirs, const char* path)
|
||||
{
|
||||
if (!ffPathExists(path, FF_PATHTYPE_DIRECTORY))
|
||||
void ffPlatformPathAddAbsolute(FFlist* dirs, const char* path) {
|
||||
if (!ffPathExists(path, FF_PATHTYPE_DIRECTORY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateS(path);
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
if (!ffListContains(dirs, &buffer, (void*) ffStrbufEqual))
|
||||
ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer);
|
||||
if (!FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
|
||||
ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void ffPlatformPathAddHome(FFlist* dirs, const FFPlatform* platform, const char* suffix)
|
||||
{
|
||||
void ffPlatformPathAddHome(FFlist* dirs, const FFPlatform* platform, const char* suffix) {
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateA(64);
|
||||
ffStrbufAppend(&buffer, &platform->homeDir);
|
||||
ffStrbufAppendS(&buffer, suffix);
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
if (ffPathExists(buffer.chars, FF_PATHTYPE_DIRECTORY) && !ffListContains(dirs, &buffer, (void*) ffStrbufEqual))
|
||||
ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer);
|
||||
if (ffPathExists(buffer.chars, FF_PATHTYPE_DIRECTORY) && !FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
|
||||
ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "FFPlatform.h"
|
||||
#include "common/FFPlatform.h"
|
||||
|
||||
void ffPlatformInitImpl(FFPlatform* platform);
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
#include "FFPlatform_private.h"
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/arrayUtils.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/io.h"
|
||||
#include "fastfetch_config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <limits.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <paths.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach-o/dyld.h>
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <kvm.h>
|
||||
#include "common/path.h"
|
||||
#elif defined(__HAIKU__)
|
||||
#include <image.h>
|
||||
#include <OS.h>
|
||||
#endif
|
||||
|
||||
static void getExePath(FFPlatform* platform) {
|
||||
char exePath[PATH_MAX];
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
ssize_t exePathLen = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1);
|
||||
if (exePathLen >= 0) {
|
||||
exePath[exePathLen] = '\0';
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
uint32_t exePathLen = sizeof(exePath);
|
||||
if (_NSGetExecutablePath(exePath, &exePathLen) == 0) {
|
||||
exePathLen = (uint32_t) strlen(exePath);
|
||||
} else {
|
||||
exePathLen = 0;
|
||||
}
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
size_t exePathLen = sizeof(exePath);
|
||||
if (sysctl(
|
||||
(int[]) { CTL_KERN,
|
||||
#ifdef __FreeBSD__
|
||||
KERN_PROC,
|
||||
KERN_PROC_PATHNAME,
|
||||
(pid_t) platform->pid
|
||||
#else
|
||||
KERN_PROC_ARGS,
|
||||
(pid_t) platform->pid,
|
||||
KERN_PROC_PATHNAME
|
||||
#endif
|
||||
},
|
||||
4,
|
||||
exePath,
|
||||
&exePathLen,
|
||||
NULL,
|
||||
0) < 0)
|
||||
exePathLen = 0;
|
||||
else {
|
||||
exePathLen--; // remove terminating NUL
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
// OpenBSD doesn't have a reliable way to get the executable path.
|
||||
// Current implementation uses argv[0], which can be easily spoofed.
|
||||
// See #2195
|
||||
size_t exePathLen = 0;
|
||||
kvm_t* kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
|
||||
if (kd) {
|
||||
int kpCount;
|
||||
struct kinfo_proc* kp = kvm_getprocs(kd, KERN_PROC_PID, (pid_t) platform->pid, sizeof(*kp), &kpCount);
|
||||
if (kp && kpCount == 1) {
|
||||
char** argv = kvm_getargv(kd, kp, 0);
|
||||
if (argv && argv[0]) {
|
||||
char* arg0 = argv[0];
|
||||
if (arg0[0]) {
|
||||
if (strchr(arg0, '/') != NULL) // likely a path (absolute or relative)
|
||||
{
|
||||
exePathLen = strlen(arg0);
|
||||
if (exePathLen < ARRAY_SIZE(exePath)) {
|
||||
memcpy(exePath, arg0, exePathLen);
|
||||
exePath[exePathLen] = '\0';
|
||||
} else {
|
||||
exePathLen = 0;
|
||||
}
|
||||
} else {
|
||||
FF_STRBUF_AUTO_DESTROY tmpPath = ffStrbufCreate();
|
||||
if (ffFindExecutableInPath(arg0, &tmpPath) == NULL && tmpPath.length < ARRAY_SIZE(exePath)) {
|
||||
memcpy(exePath, tmpPath.chars, tmpPath.length + 1);
|
||||
exePathLen = tmpPath.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (exePathLen > 0) {
|
||||
struct stat st;
|
||||
if (stat(exePath, &st) == 0 && S_ISREG(st.st_mode)) {
|
||||
int cntp;
|
||||
struct kinfo_file* kf = kvm_getfiles(kd, KERN_FILE_BYPID, (pid_t) platform->pid, sizeof(*kf), &cntp);
|
||||
if (kf) {
|
||||
int i;
|
||||
for (i = 0; i < cntp; i++) {
|
||||
if (kf[i].fd_fd == KERN_FILE_TEXT) {
|
||||
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
|
||||
if (st.st_dev != (dev_t) kf[i].va_fsid || st.st_ino != (ino_t) kf[i].va_fileid) {
|
||||
i = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < 0) {
|
||||
exePathLen = 0;
|
||||
}
|
||||
} else {
|
||||
// If we can't get the list of open files, we can't verify that the file is actually the executable
|
||||
// Assume it is
|
||||
}
|
||||
} else {
|
||||
exePathLen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
kvm_close(kd);
|
||||
}
|
||||
#elif defined(__sun)
|
||||
ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1);
|
||||
if (exePathLen >= 0) {
|
||||
exePath[exePathLen] = '\0';
|
||||
}
|
||||
#elif defined(__HAIKU__)
|
||||
size_t exePathLen = 0;
|
||||
image_info info;
|
||||
int32 cookie = 0;
|
||||
|
||||
while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
|
||||
if (info.type == B_APP_IMAGE) {
|
||||
exePathLen = strlcpy(exePath, info.name, sizeof(exePath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (exePathLen > 0) {
|
||||
ffStrbufEnsureFree(&platform->exePath, PATH_MAX);
|
||||
if (realpath(exePath, platform->exePath.chars)) {
|
||||
ffStrbufRecalculateLength(&platform->exePath);
|
||||
} else {
|
||||
ffStrbufSetNS(&platform->exePath, (uint32_t) exePathLen, exePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void platformPathAddEnv(FFlist* dirs, const char* env) {
|
||||
const char* envValue = getenv(env);
|
||||
if (!ffStrSet(envValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY value = ffStrbufCreateA(64);
|
||||
ffStrbufAppendS(&value, envValue);
|
||||
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < value.length) {
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(&value, startIndex, ':');
|
||||
value.chars[colonIndex] = '\0';
|
||||
|
||||
if (!ffStrSet(value.chars + startIndex)) {
|
||||
startIndex = colonIndex + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ffPlatformPathAddAbsolute(dirs, value.chars + startIndex);
|
||||
|
||||
startIndex = colonIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void getHomeDir(FFPlatform* platform, const struct passwd* pwd) {
|
||||
const char* home = pwd ? pwd->pw_dir : getenv("HOME");
|
||||
ffStrbufAppendS(&platform->homeDir, home);
|
||||
ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
|
||||
}
|
||||
|
||||
static void getCacheDir(FFPlatform* platform) {
|
||||
const char* cache = getenv("XDG_CACHE_HOME");
|
||||
if (ffStrSet(cache)) {
|
||||
ffStrbufAppendS(&platform->cacheDir, cache);
|
||||
ffStrbufEnsureEndsWithC(&platform->cacheDir, '/');
|
||||
} else {
|
||||
ffStrbufAppend(&platform->cacheDir, &platform->homeDir);
|
||||
ffStrbufAppendS(&platform->cacheDir, ".cache/");
|
||||
}
|
||||
}
|
||||
|
||||
static void getConfigDirs(FFPlatform* platform) {
|
||||
// Always make sure `${XDG_CONFIG_HOME:-$HOME/.config}` is the first entry
|
||||
platformPathAddEnv(&platform->configDirs, "XDG_CONFIG_HOME");
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, ".config/");
|
||||
|
||||
#if defined(__APPLE__)
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "Library/Preferences/");
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "Library/Application Support/");
|
||||
#endif
|
||||
#if defined(__HAIKU__)
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "config/settings/");
|
||||
#endif
|
||||
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "");
|
||||
platformPathAddEnv(&platform->configDirs, "XDG_CONFIG_DIRS");
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC "/xdg/");
|
||||
#endif
|
||||
|
||||
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_ETC "/");
|
||||
ffPlatformPathAddAbsolute(&platform->configDirs, FASTFETCH_TARGET_DIR_INSTALL_SYSCONF "/");
|
||||
}
|
||||
|
||||
static void getDataDirs(FFPlatform* platform) {
|
||||
platformPathAddEnv(&platform->dataDirs, "XDG_DATA_HOME");
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, ".local/share/");
|
||||
|
||||
// Add ${currentExePath}/../share
|
||||
if (platform->exePath.length > 0) {
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(&platform->exePath);
|
||||
ffStrbufSubstrBeforeLastC(&path, '/');
|
||||
ffStrbufSubstrBeforeLastC(&path, '/');
|
||||
ffStrbufAppendS(&path, "/share");
|
||||
ffPlatformPathAddAbsolute(&platform->dataDirs, path.chars);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, "Library/Application Support/");
|
||||
#endif
|
||||
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, "");
|
||||
platformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS");
|
||||
#ifdef _PATH_LOCALBASE
|
||||
ffPlatformPathAddAbsolute(&platform->dataDirs, _PATH_LOCALBASE "/share/");
|
||||
#endif
|
||||
ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/local/share/");
|
||||
ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/share/");
|
||||
}
|
||||
|
||||
static void getUserName(FFPlatform* platform, const struct passwd* pwd) {
|
||||
if (pwd) {
|
||||
ffStrbufSetS(&platform->userName, pwd->pw_name);
|
||||
ffStrbufSetS(&platform->fullUserName, pwd->pw_gecos);
|
||||
ffStrbufTrimSpace(&platform->fullUserName);
|
||||
} else {
|
||||
ffStrbufSetS(&platform->userName, getenv("USER"));
|
||||
}
|
||||
}
|
||||
|
||||
static void getHostName(FFPlatform* platform, const struct utsname* uts) {
|
||||
ffStrbufAppendS(&platform->hostName, uts->nodename);
|
||||
}
|
||||
|
||||
static void getUserShell(FFPlatform* platform, const struct passwd* pwd) {
|
||||
const char* shell = getenv("SHELL");
|
||||
if (!ffStrSet(shell) && pwd) {
|
||||
shell = pwd->pw_shell;
|
||||
}
|
||||
|
||||
ffStrbufAppendS(&platform->userShell, shell);
|
||||
}
|
||||
|
||||
static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts) {
|
||||
ffStrbufAppendS(&info->name, uts->sysname);
|
||||
ffStrbufAppendS(&info->release, uts->release);
|
||||
ffStrbufAppendS(&info->version, uts->version);
|
||||
#ifdef __HAIKU__
|
||||
/* historical reason */
|
||||
if (ffStrEquals(uts->machine, "BePC")) {
|
||||
ffStrbufSetStatic(&info->architecture, "i386");
|
||||
} else
|
||||
#endif
|
||||
ffStrbufAppendS(&info->architecture, uts->machine);
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
size_t length = sizeof(info->pageSize);
|
||||
sysctl((int[]) { CTL_HW, HW_PAGESIZE }, 2, &info->pageSize, &length, NULL, 0);
|
||||
#else
|
||||
info->pageSize = (uint32_t) sysconf(_SC_PAGESIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void getCwd(FFPlatform* platform) {
|
||||
char cwd[PATH_MAX];
|
||||
if (getcwd(cwd, sizeof(cwd)) != NULL) {
|
||||
ffStrbufSetS(&platform->cwd, cwd);
|
||||
ffStrbufEnsureEndsWithC(&platform->cwd, '/');
|
||||
}
|
||||
}
|
||||
|
||||
void ffPlatformInitImpl(FFPlatform* platform) {
|
||||
platform->pid = (uint32_t) getpid();
|
||||
platform->uid = getuid();
|
||||
struct passwd* pwd = getpwuid(platform->uid);
|
||||
|
||||
struct utsname uts;
|
||||
if (uname(&uts) < 0) {
|
||||
memset(&uts, 0, sizeof(uts));
|
||||
}
|
||||
|
||||
getExePath(platform);
|
||||
getCwd(platform);
|
||||
getHomeDir(platform, pwd);
|
||||
getCacheDir(platform);
|
||||
getConfigDirs(platform);
|
||||
getDataDirs(platform);
|
||||
|
||||
getUserName(platform, pwd);
|
||||
getHostName(platform, &uts);
|
||||
getUserShell(platform, pwd);
|
||||
|
||||
getSysinfo(&platform->sysinfo, &uts);
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
#include "FFPlatform_private.h"
|
||||
#include "common/io.h"
|
||||
#include "common/library.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/windows/registry.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <sddl.h>
|
||||
|
||||
#define SECURITY_WIN32 1 // For secext.h
|
||||
#include <secext.h>
|
||||
|
||||
static void getExePath(FFPlatform* platform) {
|
||||
wchar_t exePathW[MAX_PATH];
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hPath = CreateFileW(
|
||||
ffGetPeb()->ProcessParameters->ImagePathName.Buffer,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
if (hPath != INVALID_HANDLE_VALUE) {
|
||||
DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_NORMALIZED);
|
||||
if (len > 0 && len < MAX_PATH) {
|
||||
ffStrbufSetNWS(&platform->exePath, len, exePathW);
|
||||
if (ffStrbufStartsWithS(&platform->exePath, "\\\\?\\")) {
|
||||
ffStrbufSubstrAfter(&platform->exePath, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (platform->exePath.length == 0) {
|
||||
PCUNICODE_STRING imagePathName = &ffGetPeb()->ProcessParameters->ImagePathName;
|
||||
ffStrbufSetNWS(&platform->exePath, imagePathName->Length / sizeof(wchar_t), imagePathName->Buffer);
|
||||
}
|
||||
|
||||
ffStrbufReplaceAllC(&platform->exePath, '\\', '/');
|
||||
}
|
||||
|
||||
static void getHomeDir(FFPlatform* platform) {
|
||||
PWSTR pPath = NULL;
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pPath))) {
|
||||
ffStrbufSetWS(&platform->homeDir, pPath);
|
||||
ffStrbufReplaceAllC(&platform->homeDir, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
|
||||
} else {
|
||||
ffStrbufSetS(&platform->homeDir, getenv("USERPROFILE"));
|
||||
ffStrbufReplaceAllC(&platform->homeDir, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
|
||||
}
|
||||
CoTaskMemFree(pPath);
|
||||
}
|
||||
|
||||
static void getCacheDir(FFPlatform* platform) {
|
||||
PWSTR pPath = NULL;
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_LocalAppData, KF_FLAG_DEFAULT, NULL, &pPath))) {
|
||||
ffStrbufSetWS(&platform->cacheDir, pPath);
|
||||
ffStrbufReplaceAllC(&platform->cacheDir, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&platform->cacheDir, '/');
|
||||
} else {
|
||||
ffStrbufAppend(&platform->cacheDir, &platform->homeDir);
|
||||
ffStrbufAppendS(&platform->cacheDir, "AppData/Local/");
|
||||
}
|
||||
CoTaskMemFree(pPath);
|
||||
}
|
||||
|
||||
static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId) {
|
||||
PWSTR pPath = NULL;
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT, NULL, &pPath))) {
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateWS(pPath);
|
||||
CoTaskMemFree(pPath);
|
||||
ffStrbufReplaceAllC(&buffer, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
if (!FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
|
||||
ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void platformPathAddEnvSuffix(FFlist* dirs, const char* env, const char* suffix) {
|
||||
const char* value = getenv(env);
|
||||
if (!ffStrSet(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateA(64);
|
||||
ffStrbufAppendS(&buffer, value);
|
||||
ffStrbufReplaceAllC(&buffer, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
if (suffix) {
|
||||
ffStrbufAppendS(&buffer, suffix);
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
}
|
||||
|
||||
if (ffPathExists(buffer.chars, FF_PATHTYPE_DIRECTORY) && !FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
|
||||
ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void getConfigDirs(FFPlatform* platform) {
|
||||
if (getenv("MSYSTEM")) {
|
||||
// We are in MSYS2 / Git Bash
|
||||
platformPathAddEnvSuffix(&platform->configDirs, "HOME", ".config/");
|
||||
platformPathAddEnvSuffix(&platform->configDirs, "HOME", NULL);
|
||||
platformPathAddEnvSuffix(&platform->configDirs, "MINGW_PREFIX", "etc");
|
||||
}
|
||||
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, ".config/");
|
||||
platformPathAddKnownFolder(&platform->configDirs, &FOLDERID_ProgramData);
|
||||
platformPathAddKnownFolder(&platform->configDirs, &FOLDERID_RoamingAppData);
|
||||
platformPathAddKnownFolder(&platform->configDirs, &FOLDERID_LocalAppData);
|
||||
ffPlatformPathAddHome(&platform->configDirs, platform, "");
|
||||
}
|
||||
|
||||
static void getDataDirs(FFPlatform* platform) {
|
||||
if (getenv("MSYSTEM") && getenv("HOME")) {
|
||||
// We are in MSYS2 / Git Bash
|
||||
platformPathAddEnvSuffix(&platform->dataDirs, "HOME", ".local/share/");
|
||||
platformPathAddEnvSuffix(&platform->dataDirs, "HOME", NULL);
|
||||
platformPathAddEnvSuffix(&platform->dataDirs, "MINGW_PREFIX", "share");
|
||||
}
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, ".local/share/");
|
||||
platformPathAddKnownFolder(&platform->dataDirs, &FOLDERID_ProgramData);
|
||||
platformPathAddKnownFolder(&platform->dataDirs, &FOLDERID_RoamingAppData);
|
||||
platformPathAddKnownFolder(&platform->dataDirs, &FOLDERID_LocalAppData);
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, "");
|
||||
}
|
||||
|
||||
static void getUserName(FFPlatform* platform) {
|
||||
wchar_t buffer[256];
|
||||
DWORD size = ARRAY_SIZE(buffer);
|
||||
if (GetUserNameExW(NameDisplay, buffer, &size)) {
|
||||
ffStrbufSetWS(&platform->fullUserName, buffer);
|
||||
}
|
||||
|
||||
NTSYSAPI NTSTATUS NTAPI LsaGetUserName(
|
||||
_Outptr_ PLSA_UNICODE_STRING * UserName,
|
||||
_Outptr_opt_ PLSA_UNICODE_STRING * DomainName);
|
||||
PLSA_UNICODE_STRING userName = NULL;
|
||||
if (NT_SUCCESS(LsaGetUserName(&userName, NULL))) {
|
||||
ffStrbufSetNWS(&platform->userName, userName->Length / sizeof(wchar_t), userName->Buffer);
|
||||
RtlFreeUnicodeString(userName); // Required. userName.Buffer is allocated separately
|
||||
LsaFreeMemory(userName);
|
||||
} else {
|
||||
ffStrbufSetS(&platform->userName, getenv("USERNAME"));
|
||||
}
|
||||
|
||||
alignas(TOKEN_USER) char buf[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_USER)];
|
||||
if (NT_SUCCESS(NtQueryInformationToken(NtCurrentProcessToken(), TokenUser, buf, sizeof(buf), &size))) {
|
||||
TOKEN_USER* tokenUser = (TOKEN_USER*) buf;
|
||||
UNICODE_STRING sidString = { .Buffer = buffer, .Length = 0, .MaximumLength = sizeof(buffer) };
|
||||
if (NT_SUCCESS(RtlConvertSidToUnicodeString(&sidString, tokenUser->User.Sid, FALSE))) {
|
||||
ffStrbufSetNWS(&platform->sid, sidString.Length / sizeof(wchar_t), sidString.Buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getHostName(FFPlatform* platform) {
|
||||
wchar_t buffer[256];
|
||||
DWORD len = ARRAY_SIZE(buffer);
|
||||
if (GetComputerNameExW(ComputerNameDnsHostname, buffer, &len) && len > 0) {
|
||||
ffStrbufSetNWS(&platform->hostName, len, buffer);
|
||||
} else {
|
||||
len = ARRAY_SIZE(buffer);
|
||||
if (GetComputerNameExW(ComputerNameNetBIOS, buffer, &len) && len > 0) {
|
||||
ffStrbufSetNWS(&platform->hostName, len, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getUserShell(FFPlatform* platform) {
|
||||
// Works in MSYS2
|
||||
const char* userShell = getenv("SHELL");
|
||||
if (userShell) {
|
||||
ffStrbufAppendS(&platform->userShell, userShell);
|
||||
ffStrbufReplaceAllC(&platform->userShell, '\\', '/');
|
||||
}
|
||||
}
|
||||
|
||||
static const char* detectWine(void) {
|
||||
const char* __cdecl wine_get_version(void);
|
||||
void* hntdll = ffLibraryGetModule(L"ntdll.dll");
|
||||
if (!hntdll) {
|
||||
return NULL;
|
||||
}
|
||||
FF_LIBRARY_LOAD_SYMBOL_LAZY(hntdll, wine_get_version);
|
||||
if (!ffwine_get_version) {
|
||||
return NULL;
|
||||
}
|
||||
return ffwine_get_version();
|
||||
}
|
||||
|
||||
static void getSystemReleaseAndVersion(FFPlatformSysinfo* info) {
|
||||
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
|
||||
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t ubr = 0;
|
||||
ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
|
||||
FF_ARG(ubr, L"UBR"),
|
||||
FF_ARG(info->version, L"BuildLabEx"),
|
||||
},
|
||||
NULL);
|
||||
|
||||
PPEB_FULL peb = ffGetPeb();
|
||||
|
||||
ffStrbufSetF(&info->release,
|
||||
"%u.%u.%u.%u",
|
||||
(unsigned) peb->OSMajorVersion,
|
||||
(unsigned) peb->OSMinorVersion,
|
||||
(unsigned) peb->OSBuildNumber,
|
||||
(unsigned) ubr);
|
||||
|
||||
const char* wineVersion = detectWine();
|
||||
if (wineVersion) {
|
||||
ffStrbufSetF(&info->name, "Wine_%s", wineVersion);
|
||||
} else {
|
||||
ffStrbufSetStatic(&info->name, "WIN32_NT");
|
||||
}
|
||||
}
|
||||
|
||||
static void getSystemPageSize(FFPlatformSysinfo* info) {
|
||||
SYSTEM_BASIC_INFORMATION sbi;
|
||||
if (NT_SUCCESS(NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL))) {
|
||||
info->pageSize = sbi.PhysicalPageSize;
|
||||
} else {
|
||||
info->pageSize = 4096;
|
||||
}
|
||||
}
|
||||
|
||||
static void getSystemArchitecture(FFPlatformSysinfo* info) {
|
||||
SYSTEM_PROCESSOR_INFORMATION spi;
|
||||
if (NT_SUCCESS(NtQuerySystemInformation(SystemProcessorInformation, &spi, sizeof(spi), NULL))) {
|
||||
switch (spi.ProcessorArchitecture) {
|
||||
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||
ffStrbufSetStatic(&info->architecture, "x86_64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_IA64:
|
||||
ffStrbufSetStatic(&info->architecture, "ia64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||
switch (spi.ProcessorLevel) {
|
||||
case 4:
|
||||
ffStrbufSetStatic(&info->architecture, "i486");
|
||||
break;
|
||||
case 5:
|
||||
ffStrbufSetStatic(&info->architecture, "i586");
|
||||
break;
|
||||
case 6:
|
||||
ffStrbufSetStatic(&info->architecture, "i686");
|
||||
break;
|
||||
default:
|
||||
ffStrbufSetStatic(&info->architecture, "i386");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ARM64:
|
||||
ffStrbufSetStatic(&info->architecture, "aarch64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ARM:
|
||||
ffStrbufSetStatic(&info->architecture, "arm");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_PPC:
|
||||
ffStrbufSetStatic(&info->architecture, "ppc");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_MIPS:
|
||||
ffStrbufSetStatic(&info->architecture, "mips");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ALPHA:
|
||||
ffStrbufSetStatic(&info->architecture, "alpha");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_ALPHA64:
|
||||
ffStrbufSetStatic(&info->architecture, "alpha64");
|
||||
break;
|
||||
case PROCESSOR_ARCHITECTURE_UNKNOWN:
|
||||
default:
|
||||
ffStrbufSetStatic(&info->architecture, "unknown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getCwd(FFPlatform* platform) {
|
||||
PCURDIR cwd = &ffGetPeb()->ProcessParameters->CurrentDirectory;
|
||||
ffStrbufSetNWS(&platform->cwd, cwd->DosPath.Length / sizeof(WCHAR), cwd->DosPath.Buffer);
|
||||
ffStrbufReplaceAllC(&platform->cwd, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&platform->cwd, '/');
|
||||
}
|
||||
|
||||
void ffPlatformInitImpl(FFPlatform* platform) {
|
||||
platform->pid = (uint32_t) (uintptr_t) ffGetTeb()->ClientId.UniqueProcess;
|
||||
getExePath(platform);
|
||||
getCwd(platform);
|
||||
getHomeDir(platform);
|
||||
getCacheDir(platform);
|
||||
getConfigDirs(platform);
|
||||
getDataDirs(platform);
|
||||
|
||||
getUserName(platform);
|
||||
getHostName(platform);
|
||||
getUserShell(platform);
|
||||
|
||||
getSystemReleaseAndVersion(&platform->sysinfo);
|
||||
getSystemArchitecture(&platform->sysinfo);
|
||||
getSystemPageSize(&platform->sysinfo);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "common/FFlist.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void* ffListAdd(FFlist* list, uint32_t elementSize) {
|
||||
if (list->length == list->capacity) {
|
||||
ffListReserve(list, elementSize, list->capacity == 0 ? FF_LIST_DEFAULT_ALLOC : list->capacity * 2);
|
||||
}
|
||||
|
||||
++list->length;
|
||||
return ffListGet(list, elementSize, list->length - 1);
|
||||
}
|
||||
|
||||
bool ffListShift(FFlist* list, uint32_t elementSize, void* __restrict result) {
|
||||
if (list->length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(result, list->data, elementSize);
|
||||
memmove(list->data, list->data + elementSize, (size_t) elementSize * (list->length - 1));
|
||||
--list->length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ffListPop(FFlist* list, uint32_t elementSize, void* __restrict result) {
|
||||
if (list->length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(result, ffListGet(list, elementSize, list->length - 1), elementSize);
|
||||
--list->length;
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,12 @@
|
||||
#include "base64.h"
|
||||
#include "common/base64.h"
|
||||
|
||||
// https://github.com/kostya/benchmarks/blob/master/base64/test-nolib.c#L145
|
||||
void ffBase64EncodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output)
|
||||
{
|
||||
void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output) {
|
||||
static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
char *out = output;
|
||||
const char *ends = str + (size - size % 3);
|
||||
while (str != ends)
|
||||
{
|
||||
uint32_t n = __builtin_bswap32(*(uint32_t *)str);
|
||||
char* out = output;
|
||||
const char* ends = str + (size - size % 3);
|
||||
while (str != ends) {
|
||||
uint32_t n = __builtin_bswap32(*(uint32_t*) str);
|
||||
*out++ = chars[(n >> 26) & 63];
|
||||
*out++ = chars[(n >> 20) & 63];
|
||||
*out++ = chars[(n >> 14) & 63];
|
||||
@@ -16,94 +14,94 @@ void ffBase64EncodeRaw(uint32_t size, const char *str, uint32_t *out_size, char
|
||||
str += 3;
|
||||
}
|
||||
|
||||
if (size % 3 == 1)
|
||||
{
|
||||
uint64_t n = (uint64_t)*str << 16;
|
||||
if (size % 3 == 1) {
|
||||
uint64_t n = (uint64_t) *str << 16;
|
||||
*out++ = chars[(n >> 18) & 63];
|
||||
*out++ = chars[(n >> 12) & 63];
|
||||
*out++ = '=';
|
||||
*out++ = '=';
|
||||
}
|
||||
else if (size % 3 == 2)
|
||||
{
|
||||
uint64_t n = (uint64_t)*str++ << 16;
|
||||
n |= (uint64_t)*str << 8;
|
||||
} else if (size % 3 == 2) {
|
||||
uint64_t n = (uint64_t) *str++ << 16;
|
||||
n |= (uint64_t) *str << 8;
|
||||
*out++ = chars[(n >> 18) & 63];
|
||||
*out++ = chars[(n >> 12) & 63];
|
||||
*out++ = chars[(n >> 6) & 63];
|
||||
*out++ = '=';
|
||||
}
|
||||
*out = '\0';
|
||||
*out_size = (uint32_t)(out - output);
|
||||
*out_size = (uint32_t) (out - output);
|
||||
}
|
||||
|
||||
static uint8_t decode_table[256];
|
||||
|
||||
void init_decode_table()
|
||||
{
|
||||
void init_decode_table() {
|
||||
uint8_t ch = 0;
|
||||
do
|
||||
{
|
||||
do {
|
||||
int32_t code = -1;
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
code = ch - 0x41;
|
||||
if (ch >= 'a' && ch <= 'z')
|
||||
}
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
code = ch - 0x47;
|
||||
if (ch >= '0' && ch <= '9')
|
||||
}
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
code = ch + 0x04;
|
||||
if (ch == '+' || ch == '-')
|
||||
}
|
||||
if (ch == '+' || ch == '-') {
|
||||
code = 0x3E;
|
||||
if (ch == '/' || ch == '_')
|
||||
}
|
||||
if (ch == '/' || ch == '_') {
|
||||
code = 0x3F;
|
||||
}
|
||||
decode_table[ch] = (uint8_t) code;
|
||||
} while (ch++ < 0xFF);
|
||||
}
|
||||
|
||||
#define next_char(x) uint8_t x = decode_table[(uint8_t) *str++];
|
||||
|
||||
bool ffBase64DecodeRaw(uint32_t size, const char *str, uint32_t *out_size, char *output)
|
||||
{
|
||||
if (*(uint64_t*) decode_table == 0)
|
||||
bool ffBase64DecodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output) {
|
||||
if (*(uint64_t*) decode_table == 0) {
|
||||
init_decode_table();
|
||||
}
|
||||
|
||||
char *out = output;
|
||||
while (size > 0 && (str[size - 1] == '\n' || str[size - 1] == '\r' || str[size - 1] == '='))
|
||||
char* out = output;
|
||||
while (size > 0 && (str[size - 1] == '\n' || str[size - 1] == '\r' || str[size - 1] == '=')) {
|
||||
size--;
|
||||
}
|
||||
|
||||
const char *ends = str + size - 4;
|
||||
while (true)
|
||||
{
|
||||
if (str > ends)
|
||||
const char* ends = str + size - 4;
|
||||
while (true) {
|
||||
if (str > ends) {
|
||||
break;
|
||||
while (*str == '\n' || *str == '\r')
|
||||
}
|
||||
while (*str == '\n' || *str == '\r') {
|
||||
str++;
|
||||
}
|
||||
|
||||
if (str > ends)
|
||||
if (str > ends) {
|
||||
break;
|
||||
}
|
||||
next_char(a);
|
||||
next_char(b);
|
||||
next_char(c);
|
||||
next_char(d);
|
||||
|
||||
*out++ = (char)(a << 2 | b >> 4);
|
||||
*out++ = (char)(b << 4 | c >> 2);
|
||||
*out++ = (char)(c << 6 | d >> 0);
|
||||
*out++ = (char) (a << 2 | b >> 4);
|
||||
*out++ = (char) (b << 4 | c >> 2);
|
||||
*out++ = (char) (c << 6 | d >> 0);
|
||||
}
|
||||
|
||||
uint8_t mod = (uint8_t) (ends - str + 4) % 4;
|
||||
if (mod == 2)
|
||||
{
|
||||
if (mod == 2) {
|
||||
next_char(a);
|
||||
next_char(b);
|
||||
*out++ = (char)(a << 2 | b >> 4);
|
||||
}
|
||||
else if (mod == 3)
|
||||
{
|
||||
*out++ = (char) (a << 2 | b >> 4);
|
||||
} else if (mod == 3) {
|
||||
next_char(a);
|
||||
next_char(b);
|
||||
next_char(c);
|
||||
*out++ = (char)(a << 2 | b >> 4);
|
||||
*out++ = (char)(b << 4 | c >> 2);
|
||||
*out++ = (char) (a << 2 | b >> 4);
|
||||
*out++ = (char) (b << 4 | c >> 2);
|
||||
}
|
||||
|
||||
*out = '\0';
|
||||
@@ -0,0 +1,303 @@
|
||||
#include "common/binary.h"
|
||||
#include "common/io.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/swap.h>
|
||||
#include <mach-o/fat.h>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" // swap_fat_arch
|
||||
|
||||
// Ref: https://github.com/AlexDenisov/segment_dumper/blob/master/main.c
|
||||
|
||||
typedef struct {
|
||||
const uint8_t* data;
|
||||
size_t length;
|
||||
} FFMemoryMapping;
|
||||
|
||||
static inline void wrapMunmap(FFMemoryMapping* mapping) {
|
||||
assert(mapping);
|
||||
if (mapping->data == NULL || mapping->data == MAP_FAILED) {
|
||||
return;
|
||||
}
|
||||
munmap((void*) mapping->data, mapping->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to access data from a memory-mapped file at a specific offset
|
||||
*/
|
||||
static inline const void* readData(const FFMemoryMapping* mapping, size_t size, off_t offset) {
|
||||
if (offset < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t start = (size_t) offset;
|
||||
if (start > mapping->length || size > mapping->length - start) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mapping->data + start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a Mach-O section by extracting strings from the __cstring section
|
||||
*
|
||||
* @param mapping Memory mapping of the Mach-O object file
|
||||
* @param name Section name to check
|
||||
* @param offset Offset of the section in the file
|
||||
* @param size Size of the section
|
||||
* @param cb Callback function to process strings
|
||||
* @param userdata User data for the callback
|
||||
* @param minLength Minimum string length to extract
|
||||
*
|
||||
* @return true to continue processing, false to stop
|
||||
*/
|
||||
static bool handleMachSection(const FFMemoryMapping* mapping, const char* name, off_t offset, size_t size, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
if (!ffStrEquals(name, "__cstring")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* data = readData(mapping, size, offset);
|
||||
if (!data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (size_t off = 0; off < size; ++off) {
|
||||
const char* p = data + off;
|
||||
if (*p == '\0') {
|
||||
continue;
|
||||
}
|
||||
uint32_t len = (uint32_t) strnlen(p, size - off);
|
||||
if (len < minLength) {
|
||||
off += len; // Skip short strings
|
||||
continue;
|
||||
}
|
||||
if (*p >= ' ' && *p <= '~') { // Ignore control characters
|
||||
if (!cb(p, len, userdata)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
off += len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a Mach-O header (32-bit or 64-bit)
|
||||
*
|
||||
* This function parses the load commands in a Mach-O header, looking for
|
||||
* LC_SEGMENT or LC_SEGMENT_64 commands that contain the __TEXT segment.
|
||||
* It then processes the sections within that segment to extract strings.
|
||||
*
|
||||
* @param mapping Memory mapping of the Mach-O object file
|
||||
* @param offset Offset of the Mach header in the file
|
||||
* @param is_64 Whether this is a 64-bit Mach-O header
|
||||
* @param cb Callback function to process strings
|
||||
* @param userdata User data for the callback
|
||||
* @param minLength Minimum string length to extract
|
||||
*
|
||||
* @return NULL on success, error message on failure
|
||||
*/
|
||||
static const char* dumpMachHeader(const FFMemoryMapping* mapping, off_t offset, bool is_64, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
uint32_t ncmds;
|
||||
off_t loadCommandsOffset = offset;
|
||||
|
||||
if (is_64) {
|
||||
const struct mach_header_64* header = readData(mapping, sizeof(struct mach_header_64), offset);
|
||||
if (!header) {
|
||||
return "read mach header failed";
|
||||
}
|
||||
|
||||
ncmds = header->ncmds;
|
||||
loadCommandsOffset += sizeof(*header);
|
||||
} else {
|
||||
const struct mach_header* header = readData(mapping, sizeof(struct mach_header), offset);
|
||||
if (!header) {
|
||||
return "read mach header failed";
|
||||
}
|
||||
|
||||
ncmds = header->ncmds;
|
||||
loadCommandsOffset += sizeof(*header);
|
||||
}
|
||||
|
||||
off_t commandOffset = loadCommandsOffset;
|
||||
const struct load_command* cmd = NULL;
|
||||
for (uint32_t i = 0U; i < ncmds; i++, commandOffset += cmd->cmdsize) {
|
||||
cmd = readData(mapping, sizeof(*cmd), commandOffset);
|
||||
if (!cmd) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd->cmdsize < sizeof(*cmd)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd->cmd == LC_SEGMENT_64) {
|
||||
const struct segment_command_64* segment = readData(mapping, sizeof(struct segment_command_64), commandOffset);
|
||||
if (!segment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ffStrEquals(segment->segname, "__TEXT")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t j = 0U; j < segment->nsects; j++) {
|
||||
off_t sectionOffset = commandOffset + (off_t) sizeof(*segment) + (off_t) (j * sizeof(struct section_64));
|
||||
const struct section_64* section = readData(mapping, sizeof(struct section_64), sectionOffset);
|
||||
if (!section) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!handleMachSection(mapping, section->sectname, (off_t) section->offset, (size_t) section->size, cb, userdata, minLength)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
} else if (cmd->cmd == LC_SEGMENT) {
|
||||
const struct segment_command* segment = readData(mapping, sizeof(struct segment_command), commandOffset);
|
||||
if (!segment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ffStrEquals(segment->segname, "__TEXT")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < segment->nsects; j++) {
|
||||
off_t sectionOffset = commandOffset + (off_t) sizeof(*segment) + (off_t) (j * sizeof(struct section));
|
||||
const struct section* section = readData(mapping, sizeof(struct section), sectionOffset);
|
||||
if (!section) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!handleMachSection(mapping, section->sectname, (off_t) section->offset, (size_t) section->size, cb, userdata, minLength)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a Fat binary header (Universal binary)
|
||||
*
|
||||
* This function handles the fat header of a universal binary, which can contain
|
||||
* multiple Mach-O binaries for different architectures. It extracts and processes
|
||||
* each embedded Mach-O file.
|
||||
*
|
||||
* @param mapping Memory mapping of the universal binary
|
||||
* @param cb Callback function to process strings
|
||||
* @param userdata User data for the callback
|
||||
* @param minLength Minimum string length to extract
|
||||
*
|
||||
* @return NULL on success, error message on failure
|
||||
*/
|
||||
static const char* dumpFatHeader(const FFMemoryMapping* mapping, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
const struct fat_header* headerRaw = readData(mapping, sizeof(struct fat_header), 0);
|
||||
if (!headerRaw) {
|
||||
return "read fat header failed";
|
||||
}
|
||||
|
||||
struct fat_header header = *headerRaw;
|
||||
|
||||
bool needSwap = header.magic == FAT_CIGAM || header.magic == FAT_CIGAM_64;
|
||||
|
||||
if (needSwap) {
|
||||
swap_fat_header(&header, NX_UnknownByteOrder);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0U; i < header.nfat_arch; i++) {
|
||||
off_t machHeaderOffset = 0;
|
||||
if (header.magic == FAT_MAGIC) {
|
||||
off_t archOffset = (off_t) sizeof(struct fat_header) + (off_t) (i * sizeof(struct fat_arch));
|
||||
const struct fat_arch* archRaw = readData(mapping, sizeof(struct fat_arch), archOffset);
|
||||
if (!archRaw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct fat_arch arch = *archRaw;
|
||||
|
||||
if (needSwap) {
|
||||
swap_fat_arch(&arch, 1, NX_UnknownByteOrder);
|
||||
}
|
||||
machHeaderOffset = (off_t) arch.offset;
|
||||
} else {
|
||||
off_t archOffset = (off_t) sizeof(struct fat_header) + (off_t) (i * sizeof(struct fat_arch_64));
|
||||
const struct fat_arch_64* archRaw = readData(mapping, sizeof(struct fat_arch_64), archOffset);
|
||||
if (!archRaw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct fat_arch_64 arch = *archRaw;
|
||||
|
||||
if (needSwap) {
|
||||
swap_fat_arch_64(&arch, 1, NX_UnknownByteOrder);
|
||||
}
|
||||
|
||||
machHeaderOffset = (off_t) arch.offset;
|
||||
}
|
||||
|
||||
const uint32_t* magic = readData(mapping, sizeof(uint32_t), machHeaderOffset);
|
||||
if (!magic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*magic == MH_MAGIC_64 || *magic == MH_MAGIC) {
|
||||
return dumpMachHeader(mapping, machHeaderOffset, *magic == MH_MAGIC_64, cb, userdata, minLength);
|
||||
}
|
||||
}
|
||||
return "Unsupported fat header";
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts string literals from a Mach-O (Apple) binary file
|
||||
*
|
||||
* This function supports both single-architecture Mach-O files and
|
||||
* universal binaries (fat binaries) containing multiple architectures.
|
||||
* It locates the __cstring section in the __TEXT segment which contains
|
||||
* the string literals used in the program.
|
||||
*/
|
||||
const char* ffBinaryExtractStrings(const char* machoFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
FF_AUTO_CLOSE_FD int fd = open(machoFile, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
return "File could not be opened";
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0 || st.st_size <= 0) {
|
||||
return "Failed to stat file";
|
||||
}
|
||||
|
||||
FF_A_CLEANUP(wrapMunmap) FFMemoryMapping mapping = {
|
||||
.data = mmap(NULL, (size_t) st.st_size, PROT_READ, MAP_PRIVATE, fd, 0),
|
||||
.length = (size_t) st.st_size,
|
||||
};
|
||||
if (mapping.data == MAP_FAILED) {
|
||||
return "mmap failed";
|
||||
}
|
||||
|
||||
// Read the magic number to determine the type of binary
|
||||
const uint32_t* magic = readData(&mapping, sizeof(uint32_t), 0);
|
||||
if (!magic) {
|
||||
return "read magic number failed";
|
||||
}
|
||||
|
||||
// Check for supported formats
|
||||
// MH_CIGAM and MH_CIGAM_64 seem to be no longer used, as `swap_mach_header` is marked as deprecated.
|
||||
// However FAT_CIGAM and FAT_CIGAM_64 are still used (/usr/bin/vim).
|
||||
if (*magic != MH_MAGIC && *magic != MH_MAGIC_64 && *magic != FAT_CIGAM && *magic != FAT_CIGAM_64 && *magic != FAT_MAGIC && *magic != FAT_MAGIC_64) {
|
||||
return "Unsupported format or big endian mach-o file";
|
||||
}
|
||||
|
||||
// Process either a fat binary or a regular Mach-O binary
|
||||
if (*magic == FAT_MAGIC || *magic == FAT_MAGIC_64 || *magic == FAT_CIGAM || *magic == FAT_CIGAM_64) {
|
||||
return dumpFatHeader(&mapping, cb, userdata, minLength);
|
||||
} else {
|
||||
return dumpMachHeader(&mapping, 0, *magic == MH_MAGIC_64, cb, userdata, minLength);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "binary.h"
|
||||
#include "common/binary.h"
|
||||
|
||||
#if defined(FF_HAVE_ELF) || defined(__sun) || (defined(__FreeBSD__) && !defined(__DragonFly__)) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
|
||||
#include "common/io/io.h"
|
||||
#include "common/library.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "common/io.h"
|
||||
#include "common/library.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <libelf.h> // #1254
|
||||
#include <fcntl.h>
|
||||
#include <libelf.h> // #1254
|
||||
#include <fcntl.h>
|
||||
|
||||
/**
|
||||
* Structure to hold dynamically loaded libelf function pointers
|
||||
@@ -36,15 +36,15 @@ struct FFElfData {
|
||||
*
|
||||
* The function supports both 32-bit and 64-bit ELF formats.
|
||||
*/
|
||||
const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength)
|
||||
{
|
||||
const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
// Initialize libelf if not already done
|
||||
if (!elfData.inited)
|
||||
{
|
||||
if (!elfData.inited) {
|
||||
elfData.inited = true;
|
||||
FF_LIBRARY_LOAD(libelf, "dlopen libelf" FF_LIBRARY_EXTENSION " failed", "libelf" FF_LIBRARY_EXTENSION, 1);
|
||||
FF_LIBRARY_LOAD_MESSAGE(libelf, "libelf" FF_LIBRARY_EXTENSION, 1);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_version)
|
||||
if (elfData.ffelf_version(EV_CURRENT) == EV_NONE) return "elf_version() failed";
|
||||
if (elfData.ffelf_version(EV_CURRENT) == EV_NONE) {
|
||||
return "elf_version() failed";
|
||||
}
|
||||
|
||||
// Load all required libelf functions
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libelf, elfData, elf_begin)
|
||||
@@ -59,56 +59,70 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s
|
||||
libelf = NULL;
|
||||
}
|
||||
|
||||
if (elfData.ffelf_end == NULL)
|
||||
if (elfData.ffelf_end == NULL) {
|
||||
return "load libelf failed";
|
||||
}
|
||||
|
||||
// Open the ELF file
|
||||
FF_AUTO_CLOSE_FD int fd = open(elfFile, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) return "open() failed";
|
||||
if (fd < 0) {
|
||||
return "open() failed";
|
||||
}
|
||||
|
||||
Elf* elf = elfData.ffelf_begin(fd, ELF_C_READ, NULL);
|
||||
if (elf == NULL) return "elf_begin() failed";
|
||||
if (elf == NULL) {
|
||||
return "elf_begin() failed";
|
||||
}
|
||||
|
||||
// Get the section header string table index
|
||||
size_t shstrndx = 0;
|
||||
if (elfData.ffelf_getshdrstrndx(elf, &shstrndx) < 0)
|
||||
{
|
||||
if (elfData.ffelf_getshdrstrndx(elf, &shstrndx) < 0) {
|
||||
elfData.ffelf_end(elf);
|
||||
return "elf_getshdrstrndx() failed";
|
||||
}
|
||||
|
||||
// Iterate through all sections, looking for .rodata which contains string literals
|
||||
Elf_Scn* scn = NULL;
|
||||
while ((scn = elfData.ffelf_nextscn(elf, scn)) != NULL)
|
||||
{
|
||||
while ((scn = elfData.ffelf_nextscn(elf, scn)) != NULL) {
|
||||
// Try 64-bit section header first, then 32-bit if that fails
|
||||
Elf64_Shdr* shdr64 = elfData.ffelf64_getshdr(scn);
|
||||
Elf32_Shdr* shdr32 = NULL;
|
||||
if (shdr64 == NULL)
|
||||
{
|
||||
if (shdr64 == NULL) {
|
||||
shdr32 = elfData.ffelf32_getshdr(scn);
|
||||
if (shdr32 == NULL) continue;
|
||||
if (shdr32 == NULL) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the section name and check if it's .rodata
|
||||
const char* name = elfData.ffelf_strptr(elf, shstrndx, shdr64 ? shdr64->sh_name : shdr32->sh_name);
|
||||
if (name == NULL || !ffStrEquals(name, ".rodata")) continue;
|
||||
if (name == NULL || !ffStrEquals(name, ".rodata")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the section data
|
||||
Elf_Data* data = elfData.ffelf_getdata(scn, NULL);
|
||||
if (data == NULL) continue;
|
||||
if (data == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scan the section for string literals
|
||||
for (size_t off = 0; off < data->d_size; ++off)
|
||||
{
|
||||
for (size_t off = 0; off < data->d_size; ++off) {
|
||||
const char* p = (const char*) data->d_buf + off;
|
||||
if (*p == '\0') continue;
|
||||
uint32_t len = (uint32_t) strlen(p);
|
||||
if (len < minLength) continue;
|
||||
if (*p == '\0') {
|
||||
continue;
|
||||
}
|
||||
uint32_t len = (uint32_t) strnlen(p, data->d_size - off);
|
||||
if (len < minLength) {
|
||||
off += len;
|
||||
continue;
|
||||
}
|
||||
// Only process printable ASCII characters
|
||||
if (*p >= ' ' && *p <= '~') // Ignore control characters
|
||||
{
|
||||
if (!cb(p, len, userdata)) break;
|
||||
if (!cb(p, len, userdata)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
off += len;
|
||||
}
|
||||
@@ -125,8 +139,7 @@ const char* ffBinaryExtractStrings(const char* elfFile, bool (*cb)(const char* s
|
||||
/**
|
||||
* Fallback implementation when libelf is not available
|
||||
*/
|
||||
const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength)
|
||||
{
|
||||
const char* ffBinaryExtractStrings(const char* file, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
FF_UNUSED(file, cb, userdata, minLength);
|
||||
return "Fastfetch was built without libelf support";
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#include "common/binary.h"
|
||||
#include "common/io.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Extracts string literals from a PE (Windows) executable
|
||||
*
|
||||
* This function maps the PE file into memory, locates the .rdata section
|
||||
* (which typically contains string literals), and scans it for valid strings.
|
||||
* Each string found is passed to the callback function for processing.
|
||||
*/
|
||||
const char* ffBinaryExtractStrings(const char* peFile, bool (*cb)(const char* str, uint32_t len, void* userdata), void* userdata, uint32_t minLength) {
|
||||
FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileA(peFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
return "CreateFileA() failed";
|
||||
}
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hSection = NULL;
|
||||
if (!NT_SUCCESS(NtCreateSection(&hSection, SECTION_MAP_READ, NULL, NULL, PAGE_READONLY, SEC_COMMIT, hFile))) {
|
||||
return "NtCreateSection() failed";
|
||||
}
|
||||
|
||||
PVOID base = NULL;
|
||||
SIZE_T viewSize = 0;
|
||||
if (!NT_SUCCESS(NtMapViewOfSection(hSection, NtCurrentProcess(), &base, 0, 0, NULL, &viewSize, ViewUnmap, 0, PAGE_READONLY))) {
|
||||
return "NtMapViewOfSection() failed";
|
||||
}
|
||||
|
||||
PIMAGE_NT_HEADERS ntHeaders = RtlImageNtHeader(base);
|
||||
if (!ntHeaders) {
|
||||
NtUnmapViewOfSection(NtCurrentProcess(), base);
|
||||
return "RtlImageNtHeader() failed";
|
||||
}
|
||||
|
||||
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeaders);
|
||||
for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; ++i, ++section) {
|
||||
// Look for initialized data sections with the name ".rdata" which typically contains string literals
|
||||
if ((section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) && ffStrEquals((const char*) section->Name, ".rdata")) {
|
||||
uint8_t* data = (uint8_t*) base + section->PointerToRawData;
|
||||
|
||||
// Scan the section for string literals
|
||||
for (size_t off = 0; off < section->SizeOfRawData; ++off) {
|
||||
const char* p = (const char*) data + off;
|
||||
if (*p == '\0') {
|
||||
continue;
|
||||
}
|
||||
uint32_t len = (uint32_t) strnlen(p, section->SizeOfRawData - off);
|
||||
if (len < minLength) {
|
||||
off += len;
|
||||
continue;
|
||||
}
|
||||
// Only process printable ASCII characters
|
||||
if (*p >= ' ' && *p <= '~') // Ignore control characters
|
||||
{
|
||||
if (!cb(p, len, userdata)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
off += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NtUnmapViewOfSection(NtCurrentProcess(), base);
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
#include "common/commandoption.h"
|
||||
#include "common/color.h"
|
||||
#include "common/printing.h"
|
||||
#include "common/time.h"
|
||||
#include "common/jsonconfig.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "fastfetch_datatext.h"
|
||||
#include "modules/modules.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
bool ffParseModuleOptions(const char* key, const char* value) {
|
||||
if (!ffStrStartsWith(key, "--") || !ffCharIsEnglishAlphabet(key[2])) {
|
||||
return false;
|
||||
}
|
||||
if (value && !*value) {
|
||||
value = NULL;
|
||||
}
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(key[2]) - 'A']; *modules; ++modules) {
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
const char* subKey = ffOptionTestPrefix(key, baseInfo->name);
|
||||
if (subKey != NULL) {
|
||||
if (subKey[0] == '\0' || subKey[0] == '-') // Key is exactly the module name or has a leading '-'
|
||||
{
|
||||
fprintf(stderr, "Error: unknown module key %s\n", key);
|
||||
exit(477);
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY moduleName = ffStrbufCreateS(baseInfo->name);
|
||||
ffStrbufLowerCase(&moduleName);
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY jsonKey = ffStrbufCreate();
|
||||
bool flag = false;
|
||||
for (const char* p = subKey; *p; ++p) {
|
||||
if (*p == '-') {
|
||||
if (flag) {
|
||||
fprintf(stderr, "Error: invalid double `-` in module key %s\n", key);
|
||||
exit(477);
|
||||
}
|
||||
flag = true;
|
||||
} else {
|
||||
if (!isalpha((unsigned char) *p) && !isdigit((unsigned char) *p)) {
|
||||
fprintf(stderr, "Error: invalid character `%c` in module key %s\n", *p, key);
|
||||
exit(477);
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
flag = false;
|
||||
ffStrbufAppendC(&jsonKey, (char) toupper((unsigned char) *p));
|
||||
} else {
|
||||
ffStrbufAppendC(&jsonKey, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: Unsupported module option: %s\n", key);
|
||||
fputs(" Support of module options has been removed. Please add the flag to the JSON config instead.\n", stderr);
|
||||
fprintf(stderr, " Example (demonstration only): `{ \"modules\": [ { \"type\": \"%s\", \"%s\": %s%s%s } ] }`\n", moduleName.chars, jsonKey.chars, value ? "\"" : "", value ?: "true", value ? "\"" : "");
|
||||
fputs(" See <https://github.com/fastfetch-cli/fastfetch/wiki/Configuration> for more information.\n", stderr);
|
||||
exit(477);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffPrepareCommandOption(FFdata* data) {
|
||||
char* moduleType = NULL;
|
||||
size_t moduleLen = 0;
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
|
||||
#define FF_IF_MODULE_MATCH(moduleNameConstant) if (moduleLen == strlen(moduleNameConstant) && ffStrEqualsIgnCase(moduleType, moduleNameConstant) && !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':'))
|
||||
|
||||
switch (moduleType[0]) {
|
||||
case 'C':
|
||||
case 'c':
|
||||
FF_IF_MODULE_MATCH(FF_CPUUSAGE_MODULE_NAME)
|
||||
ffPrepareCPUUsage();
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
case 'd':
|
||||
FF_IF_MODULE_MATCH(FF_DISKIO_MODULE_NAME) {
|
||||
FF_A_CLEANUP(ffDestroyDiskIOOptions) FFDiskIOOptions options;
|
||||
ffInitDiskIOOptions(&options);
|
||||
ffPrepareDiskIO(&options);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
case 'n':
|
||||
FF_IF_MODULE_MATCH(FF_NETIO_MODULE_NAME) {
|
||||
FF_A_CLEANUP(ffDestroyNetIOOptions) FFNetIOOptions options;
|
||||
ffInitNetIOOptions(&options);
|
||||
ffPrepareNetIO(&options);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
case 'p':
|
||||
FF_IF_MODULE_MATCH(FF_PUBLICIP_MODULE_NAME) {
|
||||
FF_A_CLEANUP(ffDestroyPublicIpOptions) FFPublicIPOptions options;
|
||||
ffInitPublicIpOptions(&options);
|
||||
ffPreparePublicIp(&options);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
case 'w':
|
||||
FF_IF_MODULE_MATCH(FF_WEATHER_MODULE_NAME) {
|
||||
FF_A_CLEANUP(ffDestroyWeatherOptions) FFWeatherOptions options;
|
||||
ffInitWeatherOptions(&options);
|
||||
ffPrepareWeather(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#undef FF_IF_MODULE_MATCH
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonConfig(FFdata* data, FFModuleBaseInfo* baseInfo, void* options) {
|
||||
yyjson_mut_doc* doc = data->resultDoc;
|
||||
|
||||
yyjson_mut_val* modules = yyjson_mut_obj_get(doc->root, "modules");
|
||||
if (!modules) {
|
||||
modules = yyjson_mut_obj_add_arr(doc, doc->root, "modules");
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY type = ffStrbufCreateS(baseInfo->name);
|
||||
ffStrbufLowerCase(&type);
|
||||
|
||||
if (data->docType == FF_RESULT_DOC_TYPE_CONFIG_FULL) {
|
||||
yyjson_mut_val* module = yyjson_mut_obj(doc);
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "type", &type);
|
||||
|
||||
if (baseInfo->generateJsonConfig) {
|
||||
baseInfo->generateJsonConfig(options, doc, module);
|
||||
}
|
||||
|
||||
if (yyjson_mut_obj_size(module) > 1) {
|
||||
yyjson_mut_arr_add_val(modules, module);
|
||||
} else {
|
||||
yyjson_mut_arr_add_strbuf(doc, modules, &type);
|
||||
}
|
||||
} else {
|
||||
yyjson_mut_arr_add_strbuf(doc, modules, &type);
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonResult(FFdata* data, FFModuleBaseInfo* baseInfo, void* options) {
|
||||
yyjson_mut_doc* doc = data->resultDoc;
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
|
||||
yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name);
|
||||
if (baseInfo->generateJsonResult) {
|
||||
baseInfo->generateJsonResult(options, doc, module);
|
||||
} else {
|
||||
yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format");
|
||||
}
|
||||
}
|
||||
|
||||
static bool parseStructureCommand(
|
||||
FFdata* data,
|
||||
const char* line,
|
||||
void (*fn)(FFdata*, FFModuleBaseInfo* baseInfo, void* options)) {
|
||||
if (ffCharIsEnglishAlphabet(line[0])) {
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(line[0]) - 'A']; *modules; ++modules) {
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
if (ffStrEqualsIgnCase(line, baseInfo->name)) {
|
||||
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
|
||||
baseInfo->initOptions(optionBuf);
|
||||
if (data->resultDoc != NULL) {
|
||||
fn(data, baseInfo, optionBuf);
|
||||
} else {
|
||||
baseInfo->printModule(optionBuf);
|
||||
}
|
||||
baseInfo->destroyOptions(optionBuf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data->resultDoc) {
|
||||
yyjson_mut_doc* doc = data->resultDoc;
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
|
||||
yyjson_mut_obj_add_str(doc, module, "type", line);
|
||||
yyjson_mut_obj_add_str(doc, module, "error", "Unknown module type");
|
||||
} else {
|
||||
ffPrintError(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "<no implementation provided>");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffPrintCommandOption(FFdata* data) {
|
||||
// Parse the structure and call the modules
|
||||
int32_t thres = instance.config.display.stat;
|
||||
|
||||
char* moduleType = NULL;
|
||||
size_t moduleLen = 0;
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double ms = 0;
|
||||
if (thres >= 0) {
|
||||
ms = ffTimeGetTick();
|
||||
}
|
||||
|
||||
parseStructureCommand(data, moduleType, genJsonResult);
|
||||
|
||||
if (thres >= 0) {
|
||||
ms = ffTimeGetTick() - ms;
|
||||
|
||||
if (data->resultDoc) {
|
||||
yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(data->resultDoc->root);
|
||||
yyjson_mut_obj_add_real(data->resultDoc, moduleJson, "stat", ms);
|
||||
} else {
|
||||
char str[64];
|
||||
int len = snprintf(str, sizeof str, "%.3fms", ms);
|
||||
if (thres > 0) {
|
||||
snprintf(str, sizeof str, "\e[%sm%.3fms\e[m", (ms <= thres ? FF_COLOR_FG_GREEN : ms <= 2 * thres ? FF_COLOR_FG_YELLOW
|
||||
: FF_COLOR_FG_RED),
|
||||
ms);
|
||||
}
|
||||
printf("\e7\e[1A\e[9999999C\e[%dD%s\e8", len, str); // Save; Up 1; Right 9999999; Left <len>; Print <str>; Load
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (!data->resultDoc && !instance.config.display.noBuffer) {
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data) {
|
||||
// If we don't have a custom structure, use the default one
|
||||
if (data->structure.length == 0) {
|
||||
ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
|
||||
}
|
||||
|
||||
char* moduleType = NULL;
|
||||
size_t moduleLen = 0;
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parseStructureCommand(data, moduleType, genJsonConfig);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
#include "dbus.h"
|
||||
#include "common/dbus.h"
|
||||
|
||||
#ifdef FF_HAVE_DBUS
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
static bool loadLibSymbols(FFDBusLibrary* lib)
|
||||
{
|
||||
static bool loadLibSymbols(FFDBusLibrary* lib) {
|
||||
FF_LIBRARY_LOAD(dbus, false, "libdbus-1" FF_LIBRARY_EXTENSION, 4);
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_bus_get, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_new_method_call, false)
|
||||
@@ -19,18 +18,17 @@ static bool loadLibSymbols(FFDBusLibrary* lib)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_next, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_unref, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_send_with_reply_and_block, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_unref, false)
|
||||
dbus = NULL; // don't auto dlclose
|
||||
return true;
|
||||
}
|
||||
|
||||
static const FFDBusLibrary* loadLib(void)
|
||||
{
|
||||
static const FFDBusLibrary* loadLib(void) {
|
||||
static FFDBusLibrary lib;
|
||||
static bool loaded = false;
|
||||
static bool loadSuccess = false;
|
||||
|
||||
if(!loaded)
|
||||
{
|
||||
if (!loaded) {
|
||||
loaded = true;
|
||||
loadSuccess = loadLibSymbols(&lib);
|
||||
}
|
||||
@@ -38,190 +36,234 @@ static const FFDBusLibrary* loadLib(void)
|
||||
return loadSuccess ? &lib : NULL;
|
||||
}
|
||||
|
||||
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data)
|
||||
{
|
||||
const char* ffDBusLoadData(DBusBusType busType, FFDBusData* data) {
|
||||
data->lib = loadLib();
|
||||
if(data->lib == NULL)
|
||||
if (data->lib == NULL) {
|
||||
return "Failed to load DBus library";
|
||||
}
|
||||
|
||||
data->connection = data->lib->ffdbus_bus_get(busType, NULL);
|
||||
if(data->connection == NULL)
|
||||
if (data->connection == NULL) {
|
||||
return "Failed to connect to DBus";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result)
|
||||
{
|
||||
void ffDBusDestroyData(FFDBusData* data) {
|
||||
if (data->connection != NULL) {
|
||||
data->lib->ffdbus_connection_unref(data->connection);
|
||||
data->connection = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool ffDBusGetString(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result) {
|
||||
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
|
||||
|
||||
if(argType == DBUS_TYPE_STRING || argType == DBUS_TYPE_OBJECT_PATH)
|
||||
{
|
||||
if (argType == DBUS_TYPE_STRING || argType == DBUS_TYPE_OBJECT_PATH) {
|
||||
const char* value = NULL;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
|
||||
if(!ffStrSet(value))
|
||||
if (!ffStrSet(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ffStrbufAppendS(result, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType == DBUS_TYPE_BYTE)
|
||||
{
|
||||
if (argType == DBUS_TYPE_BYTE) {
|
||||
uint8_t value;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
ffStrbufAppendC(result, (char) value);
|
||||
return false; // Don't append a comma
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType != DBUS_TYPE_VARIANT && argType != DBUS_TYPE_ARRAY)
|
||||
if (argType != DBUS_TYPE_VARIANT && argType != DBUS_TYPE_ARRAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter subIter;
|
||||
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
|
||||
|
||||
if(argType == DBUS_TYPE_VARIANT)
|
||||
if (argType == DBUS_TYPE_VARIANT) {
|
||||
return ffDBusGetString(dbus, &subIter, result);
|
||||
}
|
||||
|
||||
//At this point we have an array
|
||||
// At this point we have an array
|
||||
|
||||
int subArgType = dbus->lib->ffdbus_message_iter_get_arg_type(&subIter);
|
||||
if (subArgType == DBUS_TYPE_INVALID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (subArgType == DBUS_TYPE_BYTE) {
|
||||
while (true) {
|
||||
uint8_t value;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(&subIter, &value);
|
||||
ffStrbufAppendC(result, (char) value);
|
||||
|
||||
if (!dbus->lib->ffdbus_message_iter_next(&subIter)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool foundAValue = false;
|
||||
|
||||
while(true)
|
||||
{
|
||||
if(ffDBusGetString(dbus, &subIter, result))
|
||||
{
|
||||
while (true) {
|
||||
if (ffDBusGetString(dbus, &subIter, result)) {
|
||||
foundAValue = true;
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
|
||||
if(!dbus->lib->ffdbus_message_iter_next(&subIter))
|
||||
if (!dbus->lib->ffdbus_message_iter_next(&subIter)) {
|
||||
break;
|
||||
else
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(foundAValue)
|
||||
if (foundAValue) {
|
||||
ffStrbufSubstrBefore(result, result->length - 2);
|
||||
}
|
||||
|
||||
return foundAValue;
|
||||
}
|
||||
|
||||
bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result)
|
||||
{
|
||||
bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result) {
|
||||
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
|
||||
|
||||
if(argType == DBUS_TYPE_BOOLEAN)
|
||||
{
|
||||
if (argType == DBUS_TYPE_BOOLEAN) {
|
||||
dbus_bool_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType != DBUS_TYPE_VARIANT)
|
||||
if (argType != DBUS_TYPE_VARIANT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter subIter;
|
||||
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
|
||||
return ffDBusGetBool(dbus, &subIter, result);
|
||||
}
|
||||
|
||||
bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint32_t* result)
|
||||
{
|
||||
bool ffDBusGetUint(FFDBusData* dbus, DBusMessageIter* iter, uint64_t* result) {
|
||||
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
|
||||
|
||||
if(argType == DBUS_TYPE_BYTE)
|
||||
{
|
||||
if (argType == DBUS_TYPE_BYTE) {
|
||||
uint8_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_UINT16)
|
||||
{
|
||||
if (argType == DBUS_TYPE_UINT16) {
|
||||
uint16_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_UINT32)
|
||||
{
|
||||
if (argType == DBUS_TYPE_UINT32) {
|
||||
uint32_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType == DBUS_TYPE_UINT64) {
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType != DBUS_TYPE_VARIANT)
|
||||
if (argType != DBUS_TYPE_VARIANT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter subIter;
|
||||
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
|
||||
return ffDBusGetUint(dbus, &subIter, result);
|
||||
}
|
||||
|
||||
bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int32_t* result)
|
||||
{
|
||||
bool ffDBusGetInt(FFDBusData* dbus, DBusMessageIter* iter, int64_t* result) {
|
||||
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
|
||||
|
||||
if(argType == DBUS_TYPE_INT16)
|
||||
{
|
||||
if (argType == DBUS_TYPE_INT16) {
|
||||
int16_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_INT32)
|
||||
{
|
||||
if (argType == DBUS_TYPE_INT32) {
|
||||
int32_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType == DBUS_TYPE_INT64) {
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_BYTE)
|
||||
{
|
||||
if (argType == DBUS_TYPE_BYTE) {
|
||||
uint8_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_UINT16)
|
||||
{
|
||||
if (argType == DBUS_TYPE_UINT16) {
|
||||
uint16_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = (int16_t) value;
|
||||
*result = (int32_t) value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType == DBUS_TYPE_UINT32)
|
||||
{
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, result);
|
||||
if (argType == DBUS_TYPE_UINT32) {
|
||||
uint32_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
*result = (int32_t) value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(argType != DBUS_TYPE_VARIANT)
|
||||
if (argType == DBUS_TYPE_UINT64) {
|
||||
uint64_t value = 0;
|
||||
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
|
||||
if (value > INT64_MAX) {
|
||||
return false;
|
||||
}
|
||||
*result = (int64_t) value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (argType != DBUS_TYPE_VARIANT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter subIter;
|
||||
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
|
||||
return ffDBusGetInt(dbus, &subIter, result);
|
||||
}
|
||||
|
||||
DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method, const char* arg1, const char* arg2)
|
||||
{
|
||||
DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method, const char* arg1, const char* arg2) {
|
||||
DBusMessage* message = dbus->lib->ffdbus_message_new_method_call(busName, objectPath, interface, method);
|
||||
if(message == NULL)
|
||||
if (message == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (arg1)
|
||||
{
|
||||
if (arg2)
|
||||
if (arg1) {
|
||||
if (arg2) {
|
||||
dbus->lib->ffdbus_message_append_args(message, DBUS_TYPE_STRING, &arg1, DBUS_TYPE_STRING, &arg2, DBUS_TYPE_INVALID);
|
||||
else
|
||||
} else {
|
||||
dbus->lib->ffdbus_message_append_args(message, DBUS_TYPE_STRING, &arg1, DBUS_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, NULL);
|
||||
@@ -231,15 +273,17 @@ DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const c
|
||||
return reply;
|
||||
}
|
||||
|
||||
DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property)
|
||||
{
|
||||
DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property) {
|
||||
DBusMessage* message = dbus->lib->ffdbus_message_new_method_call(busName, objectPath, "org.freedesktop.DBus.Properties", "Get");
|
||||
if(message == NULL)
|
||||
if (message == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dbus->lib->ffdbus_message_append_args(message,
|
||||
DBUS_TYPE_STRING, &interface,
|
||||
DBUS_TYPE_STRING, &property,
|
||||
DBUS_TYPE_STRING,
|
||||
&interface,
|
||||
DBUS_TYPE_STRING,
|
||||
&property,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, NULL);
|
||||
@@ -249,15 +293,14 @@ DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char
|
||||
return reply;
|
||||
}
|
||||
|
||||
bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result)
|
||||
{
|
||||
bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result) {
|
||||
DBusMessage* reply = ffDBusGetProperty(dbus, busName, objectPath, interface, property);
|
||||
if(reply == NULL)
|
||||
if (reply == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter rootIterator;
|
||||
if(!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator))
|
||||
{
|
||||
if (!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator)) {
|
||||
dbus->lib->ffdbus_message_unref(reply);
|
||||
return false;
|
||||
}
|
||||
@@ -269,15 +312,14 @@ bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char*
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint32_t* result)
|
||||
{
|
||||
bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint64_t* result) {
|
||||
DBusMessage* reply = ffDBusGetProperty(dbus, busName, objectPath, interface, property);
|
||||
if(reply == NULL)
|
||||
if (reply == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBusMessageIter rootIterator;
|
||||
if(!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator))
|
||||
{
|
||||
if (!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator)) {
|
||||
dbus->lib->ffdbus_message_unref(reply);
|
||||
return false;
|
||||
}
|
||||
@@ -289,4 +331,4 @@ bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* ob
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif //FF_HAVE_DBUS
|
||||
#endif // FF_HAVE_DBUS
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
const char* ffDebugWin32Error(DWORD errorCode) {
|
||||
static char buffer[512];
|
||||
|
||||
wchar_t bufferW[256];
|
||||
ULONG len = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
(DWORD) errorCode,
|
||||
0,
|
||||
bufferW,
|
||||
ARRAY_SIZE(bufferW),
|
||||
NULL);
|
||||
|
||||
if (len == 0) {
|
||||
snprintf(buffer, sizeof(buffer), "Unknown error code (%lu)", errorCode);
|
||||
} else {
|
||||
// Remove trailing newline
|
||||
while (len > 0 && (bufferW[len - 1] == '\r' || bufferW[len - 1] == '\n')) {
|
||||
--len;
|
||||
}
|
||||
|
||||
if (NT_SUCCESS(RtlUnicodeToUTF8N(buffer, sizeof(buffer), &len, bufferW, len * sizeof(wchar_t)))) {
|
||||
snprintf(buffer + len, sizeof(buffer) - len, " (%lu)", errorCode);
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "Unknown error (%lu)", errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char* ffDebugNtStatus(NTSTATUS status) {
|
||||
return ffDebugWin32Error(RtlNtStatusToDosError(status));
|
||||
}
|
||||
|
||||
static inline DWORD HRESULTToWin32Error(HRESULT hr) {
|
||||
if (SUCCEEDED(hr)) {
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
|
||||
return HRESULT_CODE(hr);
|
||||
}
|
||||
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
const char* ffDebugHResult(HRESULT hr) {
|
||||
return ffDebugWin32Error(HRESULTToWin32Error(hr));
|
||||
}
|
||||
@@ -1,25 +1,27 @@
|
||||
#include "duration.h"
|
||||
#include "common/duration.h"
|
||||
|
||||
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result)
|
||||
{
|
||||
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result) {
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
|
||||
bool spaceBeforeUnit = options->durationSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
|
||||
if (totalSeconds < 60)
|
||||
{
|
||||
if (totalSeconds < 60) {
|
||||
ffStrbufAppendUInt(result, totalSeconds);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
ffStrbufAppendS(result, options->durationAbbreviation ? "sec" : "second");
|
||||
if (totalSeconds != 1)
|
||||
if (totalSeconds != 1) {
|
||||
ffStrbufAppendC(result, 's');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t seconds = (uint32_t) (totalSeconds % 60);
|
||||
totalSeconds /= 60;
|
||||
if (seconds >= 30)
|
||||
if (seconds >= 30) {
|
||||
totalSeconds++;
|
||||
}
|
||||
|
||||
uint32_t minutes = (uint32_t) (totalSeconds % 60);
|
||||
totalSeconds /= 60;
|
||||
@@ -27,56 +29,59 @@ void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result)
|
||||
totalSeconds /= 24;
|
||||
uint32_t days = (uint32_t) totalSeconds;
|
||||
|
||||
if (days > 0)
|
||||
{
|
||||
if (days > 0) {
|
||||
ffStrbufAppendUInt(result, days);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (options->durationAbbreviation)
|
||||
{
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
if (options->durationAbbreviation) {
|
||||
ffStrbufAppendC(result, 'd');
|
||||
|
||||
if (hours > 0 || minutes > 0)
|
||||
if (hours > 0 || minutes > 0) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
ffStrbufAppendS(result, days == 1 ? "day" : "days");
|
||||
|
||||
if (days >= 100)
|
||||
if (days >= 100) {
|
||||
ffStrbufAppendS(result, "(!)");
|
||||
}
|
||||
|
||||
if (hours > 0 || minutes > 0)
|
||||
if (hours > 0 || minutes > 0) {
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hours > 0)
|
||||
{
|
||||
if (hours > 0) {
|
||||
ffStrbufAppendUInt(result, hours);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (options->durationAbbreviation)
|
||||
{
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
if (options->durationAbbreviation) {
|
||||
ffStrbufAppendC(result, 'h');
|
||||
|
||||
if (minutes > 0)
|
||||
if (minutes > 0) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
ffStrbufAppendS(result, hours == 1 ? "hour" : "hours");
|
||||
|
||||
if (minutes > 0)
|
||||
if (minutes > 0) {
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minutes > 0)
|
||||
{
|
||||
if (minutes > 0) {
|
||||
ffStrbufAppendUInt(result, minutes);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (options->durationAbbreviation)
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
if (options->durationAbbreviation) {
|
||||
ffStrbufAppendC(result, 'm');
|
||||
else
|
||||
} else {
|
||||
ffStrbufAppendS(result, minutes == 1 ? "min" : "mins");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
#include "common/edidHelper.h"
|
||||
|
||||
void ffEdidGetPhysicalResolution(const uint8_t edid[128], uint32_t* width, uint32_t* height) {
|
||||
const int dtd = 54;
|
||||
*width = (((uint32_t) edid[dtd + 4] >> 4) << 8) | edid[dtd + 2];
|
||||
*height = (((uint32_t) edid[dtd + 7] >> 4) << 8) | edid[dtd + 5];
|
||||
}
|
||||
|
||||
void ffEdidGetPreferredResolutionAndRefreshRate(const uint8_t edid[128], uint32_t* width, uint32_t* height, double* refreshRate) {
|
||||
for (uint32_t i = 0x36; i < 0x7E; i += 0x12) { // read through descriptor blocks...
|
||||
if (edid[i] != 0x00 && edid[i + 1] != 0x00) { // a dtd
|
||||
uint32_t hactive = edid[i + 2] + (uint32_t) ((edid[i + 4] & 0xf0) << 4);
|
||||
uint32_t hblank = edid[i + 3] + (uint32_t) ((edid[i + 4] & 0x0f) << 8);
|
||||
uint32_t vactive = edid[i + 5] + (uint32_t) ((edid[i + 7] & 0xf0) << 4);
|
||||
uint32_t vblank = edid[i + 6] + (uint32_t) ((edid[i + 7] & 0x0f) << 8);
|
||||
uint32_t pixclk = ((uint32_t) edid[i + 1] << 8) | (edid[i]);
|
||||
*width = hactive;
|
||||
*height = vactive;
|
||||
*refreshRate = (double) pixclk * 10000 / (double) (hactive + hblank) / (double) (vactive + vblank);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ffEdidGetVendorAndModel(const uint8_t edid[128], FFstrbuf* result) {
|
||||
// https://github.com/jinksong/read_edid/blob/master/parse-edid/parse-edid.c
|
||||
ffStrbufAppendF(result, "%c%c%c%04X", (char) (((uint32_t) edid[8] >> 2 & 0x1f) + 'A' - 1), (char) (((((uint32_t) edid[8] & 0x3) << 3) | (((uint32_t) edid[9] & 0xe0) >> 5)) + 'A' - 1), (char) (((uint32_t) edid[9] & 0x1f) + 'A' - 1), (uint32_t) (edid[10] + (uint32_t) (edid[11] << 8)));
|
||||
}
|
||||
|
||||
bool ffEdidGetName(const uint8_t edid[128], FFstrbuf* name) {
|
||||
// https://github.com/jinksong/read_edid/blob/master/parse-edid/parse-edid.c
|
||||
for (uint32_t i = 0x36; i < 0x7E; i += 0x12) { // read through descriptor blocks...
|
||||
if (edid[i] == 0x00) { // not a timing descriptor
|
||||
if (edid[i + 3] == 0xfc) { // Model Name tag
|
||||
for (uint32_t j = 0; j < 13; j++) {
|
||||
if (edid[i + 5 + j] == 0x0a) {
|
||||
ffStrbufAppendNS(name, j, (const char*) &edid[i + 5]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use manufacturer + model number as monitor name
|
||||
ffEdidGetVendorAndModel(edid, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height) {
|
||||
// Detailed Timing Descriptors
|
||||
uint32_t dw = (((uint32_t) edid[68] & 0xF0) << 4) + edid[66];
|
||||
uint32_t dh = (((uint32_t) edid[68] & 0x0F) << 8) + edid[67];
|
||||
|
||||
// Basic Display Parameters
|
||||
uint32_t bw = edid[21] * 10;
|
||||
uint32_t bh = edid[22] * 10;
|
||||
|
||||
// Some monitors report invalid data in DTD. See #1406
|
||||
if (abs((int) dw - (int) bw) < 10 && abs((int) dh - (int) bh) < 10) {
|
||||
*width = dw;
|
||||
*height = dh;
|
||||
} else {
|
||||
*width = bw;
|
||||
*height = bh;
|
||||
}
|
||||
}
|
||||
|
||||
void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week) {
|
||||
if (edid[17] > 0 && edid[17] < 0xFF) {
|
||||
*year = (uint16_t) (edid[17] + 1990);
|
||||
*week = (uint16_t) edid[16];
|
||||
if (*week == 0xFF) {
|
||||
*week = 0;
|
||||
}
|
||||
} else {
|
||||
*year = *week = 0;
|
||||
}
|
||||
|
||||
*serial = *(uint32_t*) &edid[12];
|
||||
}
|
||||
|
||||
bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length) {
|
||||
if (length <= 128) {
|
||||
return false;
|
||||
}
|
||||
for (const uint8_t* cta = &edid[128]; cta < &edid[length]; cta += 128) {
|
||||
// https://en.wikipedia.org/wiki/Extended_Display_Identification_Data#CTA_EDID_Timing_Extension_Block
|
||||
if (cta[0] != 0x02 /* CTA EDID */) {
|
||||
continue;
|
||||
}
|
||||
if (cta[1] < 0x03 /* Version 3 */) {
|
||||
continue;
|
||||
}
|
||||
const uint8_t offset = cta[2];
|
||||
if (offset <= 4) {
|
||||
continue;
|
||||
}
|
||||
for (uint8_t i = 4; i < offset;) {
|
||||
uint8_t blkLen = cta[i] & 0x1f;
|
||||
if (blkLen > 0) {
|
||||
uint8_t blkTag = (cta[i] & 0xe0) >> 5;
|
||||
if (blkTag == 0x07 /* Extended Block Type Tag */) {
|
||||
uint8_t extendedTag = cta[i + 1];
|
||||
if (extendedTag == 6 /* HDR SMDB */ || extendedTag == 7 /* HDR DMDB */) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += (uint8_t) (blkLen + 1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffEdidIsValid(const uint8_t edid[128], uint32_t length) {
|
||||
if (length < 128 || length % 128 != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const uint8_t edidHeader[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
|
||||
if (memcmp(edid, edidHeader, sizeof(edidHeader)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t sum = 0;
|
||||
for (uint32_t i = 0; i < 128; i++) {
|
||||
sum += edid[i];
|
||||
}
|
||||
|
||||
return sum == 0;
|
||||
}
|
||||
@@ -0,0 +1,513 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/FFlist.h"
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/font.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
void ffFontInit(FFfont* font) {
|
||||
// Ensure no memory allocates
|
||||
ffStrbufInit(&font->pretty);
|
||||
ffStrbufInit(&font->name);
|
||||
ffStrbufInit(&font->size);
|
||||
ffListInit(&font->styles);
|
||||
}
|
||||
|
||||
static void strbufAppendNSExcludingC(FFstrbuf* strbuf, uint32_t length, const char* value, char exclude) {
|
||||
if (value == NULL || length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufEnsureFree(strbuf, length);
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (value[i] != exclude) {
|
||||
strbuf->chars[strbuf->length++] = value[i];
|
||||
}
|
||||
}
|
||||
|
||||
strbuf->chars[strbuf->length] = '\0';
|
||||
}
|
||||
|
||||
static void fontInitPretty(FFfont* font) {
|
||||
ffStrbufAppend(&font->pretty, &font->name);
|
||||
|
||||
if (font->size.length == 0 && font->styles.length == 0) {
|
||||
return;
|
||||
} else if (font->pretty.length == 0) {
|
||||
ffStrbufAppendS(&font->pretty, "default");
|
||||
}
|
||||
|
||||
ffStrbufAppendS(&font->pretty, " (");
|
||||
|
||||
if (font->size.length > 0) {
|
||||
ffStrbufAppend(&font->pretty, &font->size);
|
||||
if (!ffStrbufEndsWithS(&font->size, "pt") && !ffStrbufEndsWithS(&font->size, "px")) {
|
||||
ffStrbufAppendS(&font->pretty, "pt");
|
||||
}
|
||||
|
||||
if (font->styles.length > 0) {
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < font->styles.length; i++) {
|
||||
ffStrbufAppend(&font->pretty, FF_LIST_GET(FFstrbuf, font->styles, i));
|
||||
|
||||
if (i < font->styles.length - 1) {
|
||||
ffStrbufAppendS(&font->pretty, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
ffStrbufAppendC(&font->pretty, ')');
|
||||
}
|
||||
|
||||
void ffFontInitQt(FFfont* font, const char* data) {
|
||||
ffFontInit(font);
|
||||
|
||||
// See https://doc.qt.io/qt-5/qfont.html#toString
|
||||
|
||||
// Family
|
||||
data = ffStrbufAppendSUntilC(&font->name, data, ',');
|
||||
ffStrbufTrim(&font->name, ' ');
|
||||
if (!data) {
|
||||
goto exit;
|
||||
}
|
||||
data++;
|
||||
|
||||
// Size
|
||||
data = ffStrbufAppendSUntilC(&font->size, data, ',');
|
||||
ffStrbufTrim(&font->size, ' ');
|
||||
if (!data) {
|
||||
goto exit;
|
||||
}
|
||||
data++;
|
||||
|
||||
// Style
|
||||
data = strrchr(data, ',');
|
||||
if (!data) {
|
||||
goto exit;
|
||||
}
|
||||
data++;
|
||||
if (isalpha(*data)) {
|
||||
do {
|
||||
FFstrbuf* style = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInit(style);
|
||||
data = ffStrbufAppendSUntilC(style, data, ' ');
|
||||
if (data) {
|
||||
data++;
|
||||
}
|
||||
} while (data);
|
||||
}
|
||||
|
||||
exit:
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* alternativeBuffer) {
|
||||
while (**data == ' ' || **data == '\t' || **data == ',') {
|
||||
++(*data);
|
||||
}
|
||||
|
||||
const char* wordStart = *data;
|
||||
|
||||
while (**data != ' ' && **data != '\t' && **data != ',' && **data != '\0' && **data != '`' && **data != '\\') {
|
||||
++(*data);
|
||||
}
|
||||
|
||||
uint32_t wordLength = (uint32_t) (*data - wordStart);
|
||||
if (wordLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (**data == '\0' || **data == '`' || **data == '\\') {
|
||||
ffStrbufAppendNS(&font->size, wordLength, wordStart);
|
||||
if (ffStrbufEndsWithS(&font->size, "px")) {
|
||||
ffStrbufSubstrBefore(&font->size, font->size.length - 2);
|
||||
}
|
||||
|
||||
double dummy;
|
||||
if (sscanf(font->size.chars, "%lf", &dummy) == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufClear(&font->size);
|
||||
}
|
||||
|
||||
if (
|
||||
ffStrStartsWithIgnCase(wordStart, "Ultra") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Extra") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Semi") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Demi") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Normal") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Roman") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Oblique") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Italic") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Thin") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Light") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Bold") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Black") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Condensed") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Expanded")) {
|
||||
if (alternativeBuffer == NULL) {
|
||||
alternativeBuffer = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInit(alternativeBuffer);
|
||||
}
|
||||
|
||||
strbufAppendNSExcludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
|
||||
if (
|
||||
ffStrStartsWithIgnCase(wordStart, "Ultra ") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Extra ") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Semi ") ||
|
||||
ffStrStartsWithIgnCase(wordStart, "Demi ")) {
|
||||
fontPangoParseWord(data, font, alternativeBuffer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (alternativeBuffer != NULL) {
|
||||
strbufAppendNSExcludingC(alternativeBuffer, wordLength, wordStart, '-');
|
||||
return;
|
||||
}
|
||||
|
||||
if (font->name.length > 0) {
|
||||
ffStrbufAppendC(&font->name, ' ');
|
||||
}
|
||||
ffStrbufAppendNS(&font->name, wordLength, wordStart);
|
||||
}
|
||||
|
||||
void ffFontInitPango(FFfont* font, const char* data) {
|
||||
ffFontInit(font);
|
||||
|
||||
while (*data != '\0' && *data != '`' && *data != '\\') {
|
||||
fontPangoParseWord(&data, font, NULL);
|
||||
}
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitValues(FFfont* font, const char* name, const char* size) {
|
||||
ffFontInit(font);
|
||||
|
||||
ffStrbufAppendS(&font->name, name);
|
||||
ffStrbufTrim(&font->name, '"');
|
||||
ffStrbufAppendS(&font->size, size);
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitXlfd(FFfont* font, const char* xlfd) {
|
||||
assert(xlfd && *xlfd);
|
||||
|
||||
// https://en.wikipedia.org/wiki/X_logical_font_description
|
||||
ffFontInit(font);
|
||||
|
||||
// XLFD: -foundry-family-weight-slant-setwidth-addstyle-pixelsize-pointsize-xres-yres-spacing-averagewidth-charsetregistry-charsetencoding
|
||||
// It often starts with '-', which would create an empty first field. Skip it to align indexes.
|
||||
if (*xlfd == '-') {
|
||||
xlfd++;
|
||||
}
|
||||
|
||||
const char* pstart = xlfd;
|
||||
|
||||
for (int field = 0; field < 14; field++) {
|
||||
const char* pend = strchr(pstart, '-');
|
||||
uint32_t length = pend ? (uint32_t) (pend - pstart) : (uint32_t) strlen(pstart);
|
||||
|
||||
if (length > 0) {
|
||||
if (field == 1) // family
|
||||
{
|
||||
ffStrbufAppendNS(&font->name, length, pstart);
|
||||
} else if (field == 7) // pointsize (decipoints, preferred)
|
||||
{
|
||||
// parse positive integer from substring
|
||||
long deciPt = 0;
|
||||
bool ok = true;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
char c = pstart[i];
|
||||
if (c < '0' || c > '9') {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
deciPt = deciPt * 10 + (c - '0');
|
||||
}
|
||||
|
||||
if (ok && deciPt > 0) {
|
||||
ffStrbufClear(&font->size);
|
||||
|
||||
char tmp[32];
|
||||
if (deciPt % 10 == 0) {
|
||||
snprintf(tmp, sizeof(tmp), "%ldpt", deciPt / 10);
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "%ld.%ldpt", deciPt / 10, deciPt % 10);
|
||||
}
|
||||
|
||||
ffStrbufAppendS(&font->size, tmp);
|
||||
}
|
||||
} else if (field == 6) // pixelsize (fallback if pointsize missing/invalid)
|
||||
{
|
||||
if (font->size.length == 0) {
|
||||
long px = 0;
|
||||
bool ok = true;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
char c = pstart[i];
|
||||
if (c < '0' || c > '9') {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
px = px * 10 + (c - '0');
|
||||
}
|
||||
|
||||
if (ok && px > 0) {
|
||||
ffStrbufAppendNS(&font->size, length, pstart);
|
||||
ffStrbufAppendS(&font->size, "px");
|
||||
}
|
||||
}
|
||||
} else if (field >= 2 && field <= 5) // weight/slant/setwidth/addstyle
|
||||
{
|
||||
// ignore "normal" (case-insensitive)
|
||||
if (!(length == 6 && ffStrStartsWithIgnCase(pstart, "normal"))) {
|
||||
FFstrbuf* style = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInitNS(style, length, pstart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!pend) {
|
||||
break;
|
||||
}
|
||||
|
||||
pstart = pend + 1;
|
||||
}
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitXft(FFfont* font, const char* xft) {
|
||||
assert(xft);
|
||||
|
||||
// https://en.wikipedia.org/wiki/Xft
|
||||
// Xft/Fontconfig pattern examples:
|
||||
// "DejaVu Sans Mono-10"
|
||||
// "monospace:size=10:weight=bold:slant=italic"
|
||||
// "Fira Code-12:style=Regular"
|
||||
// Goal: extract family(name), size, and some common styles.
|
||||
|
||||
ffFontInit(font);
|
||||
|
||||
// 1) Parse "head" part before first ':' => usually "family[-size]" (may include commas)
|
||||
const char* p = xft;
|
||||
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
++p;
|
||||
}
|
||||
|
||||
const char* headStart = p;
|
||||
while (*p != '\0' && *p != ':') {
|
||||
++p;
|
||||
}
|
||||
const char* headEnd = p;
|
||||
|
||||
// trim tail spaces
|
||||
while (headEnd > headStart && (headEnd[-1] == ' ' || headEnd[-1] == '\t')) {
|
||||
--headEnd;
|
||||
}
|
||||
|
||||
// If multiple families are listed, take the first one (up to comma)
|
||||
for (const char* q = headStart; q < headEnd; ++q) {
|
||||
if (*q == ',') {
|
||||
headEnd = q;
|
||||
while (headEnd > headStart && (headEnd[-1] == ' ' || headEnd[-1] == '\t')) {
|
||||
--headEnd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Try parse trailing "-<number>" as size, otherwise entire head is name
|
||||
const char* dashPos = NULL;
|
||||
const char* sizeStart = NULL;
|
||||
|
||||
for (const char* q = headEnd; q > headStart;) {
|
||||
--q;
|
||||
if (*q == '-' && (q + 1) < headEnd && ffCharIsDigit(q[1])) {
|
||||
dashPos = q;
|
||||
sizeStart = q + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dashPos) {
|
||||
bool ok = true;
|
||||
bool seenDigit = false;
|
||||
for (const char* q = sizeStart; q < headEnd; ++q) {
|
||||
if (ffCharIsDigit(*q)) {
|
||||
seenDigit = true;
|
||||
} else if (*q == '.') {
|
||||
continue;
|
||||
} else {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok && seenDigit) {
|
||||
const char* nameEnd = dashPos;
|
||||
while (nameEnd > headStart && (nameEnd[-1] == ' ' || nameEnd[-1] == '\t')) {
|
||||
--nameEnd;
|
||||
}
|
||||
|
||||
if (nameEnd > headStart) {
|
||||
ffStrbufAppendNS(&font->name, (uint32_t) (nameEnd - headStart), headStart);
|
||||
}
|
||||
|
||||
if (headEnd > sizeStart) {
|
||||
ffStrbufAppendNS(&font->size, (uint32_t) (headEnd - sizeStart), sizeStart);
|
||||
}
|
||||
} else {
|
||||
if (headEnd > headStart) {
|
||||
ffStrbufAppendNS(&font->name, (uint32_t) (headEnd - headStart), headStart);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (headEnd > headStart) {
|
||||
ffStrbufAppendNS(&font->name, (uint32_t) (headEnd - headStart), headStart);
|
||||
}
|
||||
}
|
||||
|
||||
ffStrbufTrim(&font->name, ' ');
|
||||
ffStrbufTrim(&font->name, '"');
|
||||
|
||||
// 2) Parse key=value fields after ':' (Fontconfig-like). Fields separated by ':'.
|
||||
// Common keys: size, pixelsize, pointsize, style, weight, slant, width
|
||||
while (*p == ':') {
|
||||
++p;
|
||||
|
||||
// key
|
||||
const char* keyStart = p;
|
||||
while (*p != '\0' && *p != '=' && *p != ':') {
|
||||
++p;
|
||||
}
|
||||
const char* keyEnd = p;
|
||||
|
||||
if (*p != '=') {
|
||||
continue; // skip tokens without '='
|
||||
}
|
||||
|
||||
++p; // skip '='
|
||||
|
||||
// value (until next ':', allow backslash-escaping)
|
||||
FF_STRBUF_AUTO_DESTROY value = ffStrbufCreate();
|
||||
|
||||
while (*p != '\0' && *p != ':') {
|
||||
if (*p == '\\' && p[1] != '\0') {
|
||||
++p;
|
||||
ffStrbufAppendC(&value, *p);
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
|
||||
ffStrbufAppendC(&value, *p);
|
||||
++p;
|
||||
}
|
||||
|
||||
ffStrbufTrim(&value, ' ');
|
||||
ffStrbufTrim(&value, '"');
|
||||
|
||||
uint32_t keyLen = (uint32_t) (keyEnd - keyStart);
|
||||
|
||||
// helper: set numeric size if not set yet
|
||||
const bool sizeEmpty = (font->size.length == 0);
|
||||
if (value.length > 0) {
|
||||
if (
|
||||
(keyLen == 4 && ffStrStartsWithIgnCase(keyStart, "size")) ||
|
||||
(keyLen == 9 && (ffStrStartsWithIgnCase(keyStart, "pixelsize") || ffStrStartsWithIgnCase(keyStart, "pointsize")))) {
|
||||
if (sizeEmpty && ffCharIsDigit(value.chars[0])) {
|
||||
ffStrbufAppend(&font->size, &value);
|
||||
ffStrbufAppendS(&font->size,
|
||||
(keyLen == 9 && ffStrStartsWithIgnCase(keyStart, "pixelsize")) ? "px" : "pt");
|
||||
}
|
||||
} else if (keyLen == 5 && ffStrStartsWithIgnCase(keyStart, "style")) {
|
||||
// style may contain multiple words: "Bold Italic"
|
||||
const char* s = value.chars;
|
||||
while (*s != '\0') {
|
||||
while (*s == ' ' || *s == '\t' || *s == ',') {
|
||||
++s;
|
||||
}
|
||||
|
||||
const char* w = s;
|
||||
while (*s != '\0' && *s != ' ' && *s != '\t' && *s != ',') {
|
||||
++s;
|
||||
}
|
||||
|
||||
if (s > w) {
|
||||
FFstrbuf* style = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInitNS(style, (uint32_t) (s - w), w);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
(keyLen == 6 && ffStrStartsWithIgnCase(keyStart, "weight")) ||
|
||||
(keyLen == 5 && ffStrStartsWithIgnCase(keyStart, "slant")) ||
|
||||
(keyLen == 5 && ffStrStartsWithIgnCase(keyStart, "width"))) {
|
||||
// normalize: remove '-' to align with other parsers ("Semi-Bold" -> "SemiBold")
|
||||
FFstrbuf* style = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInit(style);
|
||||
strbufAppendNSExcludingC(style, value.length, value.chars, '-');
|
||||
ffStrbufTrim(style, ' ');
|
||||
if (style->length == 0) {
|
||||
ffStrbufDestroy(style);
|
||||
--font->styles.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style) {
|
||||
ffFontInit(font);
|
||||
|
||||
if (name) {
|
||||
ffStrbufInitMove(&font->name, name);
|
||||
}
|
||||
if (size) {
|
||||
ffStrbufInitMove(&font->size, size);
|
||||
}
|
||||
if (style) {
|
||||
FFstrbuf* styleBuf = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInitMove(styleBuf, style);
|
||||
}
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitWithSpace(FFfont* font, const char* rawName) {
|
||||
const char* pspace = strrchr(rawName, ' ');
|
||||
if (pspace == NULL) {
|
||||
ffFontInitCopy(font, rawName);
|
||||
return;
|
||||
}
|
||||
|
||||
ffFontInit(font);
|
||||
|
||||
ffStrbufAppendNS(&font->name, (uint32_t) (pspace - rawName), rawName);
|
||||
ffStrbufAppendS(&font->size, pspace + 1);
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontDestroy(FFfont* font) {
|
||||
ffStrbufDestroy(&font->pretty);
|
||||
ffStrbufDestroy(&font->name);
|
||||
ffStrbufDestroy(&font->size);
|
||||
|
||||
FF_LIST_FOR_EACH (FFstrbuf, str, font->styles) {
|
||||
ffStrbufDestroy(str);
|
||||
}
|
||||
ffListDestroy(&font->styles);
|
||||
}
|
||||
@@ -1,59 +1,63 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/format.h"
|
||||
#include "common/parsing.h"
|
||||
#include "util/textModifier.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "common/textModifier.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
|
||||
{
|
||||
switch(formatarg->type)
|
||||
{
|
||||
case FF_FORMAT_ARG_TYPE_INT:
|
||||
ffStrbufAppendSInt(buffer, *(int32_t*)formatarg->value);
|
||||
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) {
|
||||
switch (formatarg->type) {
|
||||
case FF_ARG_TYPE_INT:
|
||||
ffStrbufAppendSInt(buffer, *(int32_t*) formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_UINT:
|
||||
ffStrbufAppendUInt(buffer, *(uint32_t*)formatarg->value);
|
||||
case FF_ARG_TYPE_UINT:
|
||||
ffStrbufAppendUInt(buffer, *(uint32_t*) formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_UINT64:
|
||||
ffStrbufAppendUInt(buffer, *(uint64_t*)formatarg->value);
|
||||
case FF_ARG_TYPE_UINT64:
|
||||
ffStrbufAppendUInt(buffer, *(uint64_t*) formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_UINT16:
|
||||
ffStrbufAppendUInt(buffer, *(uint16_t*)formatarg->value);
|
||||
case FF_ARG_TYPE_UINT16:
|
||||
ffStrbufAppendUInt(buffer, *(uint16_t*) formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_UINT8:
|
||||
ffStrbufAppendUInt(buffer, *(uint8_t*)formatarg->value);
|
||||
case FF_ARG_TYPE_UINT8:
|
||||
ffStrbufAppendUInt(buffer, *(uint8_t*) formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_STRING:
|
||||
ffStrbufAppendS(buffer, (const char*)formatarg->value);
|
||||
case FF_ARG_TYPE_STRING:
|
||||
ffStrbufAppendS(buffer, (const char*) formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_STRBUF:
|
||||
ffStrbufAppend(buffer, (const FFstrbuf*)formatarg->value);
|
||||
case FF_ARG_TYPE_STRBUF:
|
||||
ffStrbufAppend(buffer, (const FFstrbuf*) formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_FLOAT:
|
||||
ffStrbufAppendDouble(buffer, *(float*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
case FF_ARG_TYPE_FLOAT:
|
||||
ffStrbufAppendDouble(buffer, *(float*) formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_DOUBLE:
|
||||
ffStrbufAppendDouble(buffer, *(double*)formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
case FF_ARG_TYPE_DOUBLE:
|
||||
ffStrbufAppendDouble(buffer, *(double*) formatarg->value, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros != FF_FRACTION_TRAILING_ZEROS_TYPE_NEVER);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_BOOL:
|
||||
ffStrbufAppendS(buffer, *(bool*)formatarg->value ? "true" : "false");
|
||||
case FF_ARG_TYPE_BOOL:
|
||||
ffStrbufAppendS(buffer, *(bool*) formatarg->value ? "true" : "false");
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_LIST:
|
||||
{
|
||||
case FF_ARG_TYPE_LIST: {
|
||||
const FFlist* list = (const FFlist*) formatarg->value;
|
||||
for(uint32_t i = 0; i < list->length; i++)
|
||||
{
|
||||
for (uint32_t i = 0; i < list->length; i++) {
|
||||
ffStrbufAppend(buffer, FF_LIST_GET(FFstrbuf, *list, i));
|
||||
if(i < list->length - 1)
|
||||
if (i < list->length - 1) {
|
||||
ffStrbufAppendS(buffer, ", ");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FF_ARG_TYPE_BUFFER: {
|
||||
// Placeholder for binary data, just print the size for now
|
||||
const FFArgBuffer* argBuffer = (const FFArgBuffer*) formatarg->value;
|
||||
ffStrbufAppendF(buffer, "buffer(%u bytes)", argBuffer->length);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if(formatarg->type != FF_FORMAT_ARG_TYPE_NULL)
|
||||
if (formatarg->type != FF_ARG_TYPE_NULL) {
|
||||
fprintf(stderr, "Error: format string \"%s\": argument is not implemented: %i\n", buffer->chars, formatarg->type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -66,62 +70,48 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
|
||||
* @param placeholderValue the string to parse
|
||||
* @return uint32_t the parsed value
|
||||
*/
|
||||
static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
static uint32_t getArgumentIndex(const char* placeholderValue, uint32_t numArgs, const FFformatarg* arguments) {
|
||||
char firstChar = placeholderValue[0];
|
||||
if (firstChar == '\0')
|
||||
if (firstChar == '\0') {
|
||||
return 0; // use arg counter
|
||||
}
|
||||
|
||||
if (firstChar >= '0' && firstChar <= '9')
|
||||
{
|
||||
if (firstChar >= '0' && firstChar <= '9') {
|
||||
char* pEnd = NULL;
|
||||
uint32_t result = (uint32_t) strtoul(placeholderValue, &pEnd, 10);
|
||||
if (result > numArgs)
|
||||
if (result > numArgs) {
|
||||
return UINT32_MAX;
|
||||
if (*pEnd != '\0')
|
||||
}
|
||||
if (*pEnd != '\0') {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (ffCharIsEnglishAlphabet(firstChar))
|
||||
{
|
||||
for (uint32_t i = 0; i < numArgs; ++i)
|
||||
{
|
||||
} else if (ffCharIsEnglishAlphabet(firstChar)) {
|
||||
for (uint32_t i = 0; i < numArgs; ++i) {
|
||||
const FFformatarg* arg = &arguments[i];
|
||||
if (arg->name && strcasecmp(placeholderValue, arg->name) == 0)
|
||||
if (arg->name && ffStrEqualsIgnCase(placeholderValue, arg->name)) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start, const FFstrbuf* placeholderValue, uint32_t index, uint32_t formatStringLength)
|
||||
{
|
||||
static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start, const FFstrbuf* placeholderValue, uint32_t index, uint32_t formatStringLength) {
|
||||
ffStrbufAppendS(buffer, start);
|
||||
ffStrbufAppend(buffer, placeholderValue);
|
||||
|
||||
if(index < formatStringLength)
|
||||
if (index < formatStringLength) {
|
||||
ffStrbufAppendC(buffer, '}');
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool formatArgSet(const FFformatarg* arg)
|
||||
{
|
||||
return arg->value != NULL && (
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_INT && *(int*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_UINT16 && *(uint16_t*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_BOOL && *(bool*)arg->value) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_LIST && ((FFlist*)arg->value)->length > 0)
|
||||
);
|
||||
static inline bool formatArgSet(const FFformatarg* arg) {
|
||||
return arg->value != NULL && ((arg->type == FF_ARG_TYPE_DOUBLE && *(double*) arg->value > 0.0) || (arg->type == FF_ARG_TYPE_FLOAT && *(float*) arg->value > 0.0) || (arg->type == FF_ARG_TYPE_INT && *(int32_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*) arg->value)->length > 0) || (arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*) arg->value)) || (arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT && *(uint32_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT64 && *(uint64_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_BOOL && *(bool*) arg->value) || (arg->type == FF_ARG_TYPE_LIST && ((FFlist*) arg->value)->length > 0));
|
||||
}
|
||||
|
||||
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments)
|
||||
{
|
||||
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t numArgs, const FFformatarg* arguments) {
|
||||
uint32_t argCounter = 0;
|
||||
|
||||
uint32_t numOpenIfs = 0;
|
||||
@@ -129,11 +119,9 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY placeholderValue = ffStrbufCreate();
|
||||
|
||||
for(uint32_t i = 0; i < formatstr->length; ++i)
|
||||
{
|
||||
for (uint32_t i = 0; i < formatstr->length; ++i) {
|
||||
// if we don't have a placeholder start just copy the chars over to output buffer
|
||||
if(formatstr->chars[i] != '{')
|
||||
{
|
||||
if (formatstr->chars[i] != '{') {
|
||||
ffStrbufAppendC(buffer, formatstr->chars[i]);
|
||||
continue;
|
||||
}
|
||||
@@ -142,8 +130,7 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
++i;
|
||||
|
||||
// double {{ elvaluates to a single { and doesn't count as start
|
||||
if(formatstr->chars[i] == '{')
|
||||
{
|
||||
if (formatstr->chars[i] == '{') {
|
||||
ffStrbufAppendC(buffer, '{');
|
||||
continue;
|
||||
}
|
||||
@@ -158,61 +145,58 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
|
||||
char firstChar = placeholderValue.chars[0];
|
||||
|
||||
if (placeholderValue.length == 1)
|
||||
{
|
||||
if (placeholderValue.length == 1) {
|
||||
// test if for stop, if so break the loop
|
||||
if (firstChar == '-')
|
||||
if (firstChar == '-') {
|
||||
break;
|
||||
}
|
||||
|
||||
// test for end of an if, if so do nothing
|
||||
if (firstChar == '?')
|
||||
{
|
||||
if(numOpenIfs == 0)
|
||||
if (firstChar == '?') {
|
||||
if (numOpenIfs == 0) {
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
} else {
|
||||
--numOpenIfs;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// test for end of a not if, if so do nothing
|
||||
if (firstChar == '/')
|
||||
{
|
||||
if(numOpenNotIfs == 0)
|
||||
if (firstChar == '/') {
|
||||
if (numOpenNotIfs == 0) {
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
} else {
|
||||
--numOpenNotIfs;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// test for end of a color, if so do nothing
|
||||
if (firstChar == '#')
|
||||
{
|
||||
if (!instance.config.display.pipe)
|
||||
if (firstChar == '#') {
|
||||
if (!instance.config.display.pipe) {
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// test for if, if so evaluate it
|
||||
if (firstChar == '?')
|
||||
{
|
||||
if (firstChar == '?') {
|
||||
ffStrbufSubstrAfter(&placeholderValue, 0);
|
||||
|
||||
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
||||
|
||||
// testing for an invalid index
|
||||
if (index > numArgs)
|
||||
{
|
||||
if (index > numArgs || index < 1) {
|
||||
appendInvalidPlaceholder(buffer, "{?", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
|
||||
// continue normally if an format arg is set and the value is > 0
|
||||
if (formatArgSet(&arguments[index - 1]))
|
||||
{
|
||||
if (formatArgSet(&arguments[index - 1])) {
|
||||
++numOpenIfs;
|
||||
continue;
|
||||
}
|
||||
@@ -223,22 +207,19 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
}
|
||||
|
||||
// test for not if, if so evaluate it
|
||||
if (firstChar == '/')
|
||||
{
|
||||
if (firstChar == '/') {
|
||||
ffStrbufSubstrAfter(&placeholderValue, 0);
|
||||
|
||||
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
||||
|
||||
// testing for an invalid index
|
||||
if (index > numArgs)
|
||||
{
|
||||
if (index > numArgs || index < 1) {
|
||||
appendInvalidPlaceholder(buffer, "{/", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
|
||||
//continue normally if an format arg is not set or the value is 0
|
||||
if (!formatArgSet(&arguments[index - 1]))
|
||||
{
|
||||
// continue normally if an format arg is not set or the value is 0
|
||||
if (!formatArgSet(&arguments[index - 1])) {
|
||||
++numOpenNotIfs;
|
||||
continue;
|
||||
}
|
||||
@@ -248,11 +229,9 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
continue;
|
||||
}
|
||||
|
||||
//test for color, if so evaluate it
|
||||
if (firstChar == '#')
|
||||
{
|
||||
if (!instance.config.display.pipe)
|
||||
{
|
||||
// test for color, if so evaluate it
|
||||
if (firstChar == '#') {
|
||||
if (!instance.config.display.pipe) {
|
||||
ffStrbufAppendS(buffer, "\e[");
|
||||
ffOptionParseColorNoClear(placeholderValue.chars + 1, buffer);
|
||||
ffStrbufAppendC(buffer, 'm');
|
||||
@@ -260,29 +239,25 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
continue;
|
||||
}
|
||||
|
||||
//test for constant or env var, if so evaluate it
|
||||
if (firstChar == '$')
|
||||
{
|
||||
// test for constant or env var, if so evaluate it
|
||||
if (firstChar == '$') {
|
||||
char* pend = NULL;
|
||||
int32_t indexSigned = (int32_t) strtol(placeholderValue.chars + 1, &pend, 10);
|
||||
if (pend == placeholderValue.chars + 1)
|
||||
{
|
||||
if (pend == placeholderValue.chars + 1) {
|
||||
// treat placeholder as an environment variable
|
||||
char* envValue = getenv(placeholderValue.chars + 1);
|
||||
if (envValue)
|
||||
if (envValue) {
|
||||
ffStrbufAppendS(buffer, envValue);
|
||||
else
|
||||
} else {
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
// treat placeholder as a constant
|
||||
uint32_t index = (uint32_t) (indexSigned < 0 ? (int32_t) instance.config.display.constants.length + indexSigned : indexSigned - 1);
|
||||
|
||||
if (*pend != '\0' || instance.config.display.constants.length <= index)
|
||||
if (*pend != '\0' || instance.config.display.constants.length <= index) {
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
else
|
||||
{
|
||||
} else {
|
||||
FFstrbuf* item = FF_LIST_GET(FFstrbuf, instance.config.display.constants, index);
|
||||
ffStrbufAppend(buffer, item);
|
||||
}
|
||||
@@ -292,115 +267,104 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
|
||||
char* pSep = placeholderValue.chars;
|
||||
char cSep = '\0';
|
||||
while (*pSep && *pSep != ':' && *pSep != '<' && *pSep != '>' && *pSep != '~')
|
||||
while (*pSep && *pSep != ':' && *pSep != '<' && *pSep != '>' && *pSep != '~') {
|
||||
++pSep;
|
||||
if (*pSep)
|
||||
{
|
||||
}
|
||||
if (*pSep) {
|
||||
cSep = *pSep;
|
||||
*pSep = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
pSep = NULL;
|
||||
}
|
||||
|
||||
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
|
||||
|
||||
// test for invalid index
|
||||
if (index == 0)
|
||||
if (index == 0) {
|
||||
index = ++argCounter;
|
||||
}
|
||||
|
||||
if (index > numArgs)
|
||||
{
|
||||
if (pSep) *pSep = cSep;
|
||||
if (index > numArgs) {
|
||||
if (pSep) {
|
||||
*pSep = cSep;
|
||||
}
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cSep)
|
||||
if (!cSep) {
|
||||
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
|
||||
else if (cSep == '~')
|
||||
{
|
||||
} else if (cSep == '~') {
|
||||
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
||||
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
|
||||
|
||||
char* pEnd = NULL;
|
||||
int32_t start = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
||||
if (start < 0)
|
||||
if (start < 0) {
|
||||
start = (int32_t) tempString.length + start;
|
||||
if (start >= 0 && (uint32_t) start < tempString.length)
|
||||
{
|
||||
if (*pEnd == '\0')
|
||||
}
|
||||
if (start >= 0 && (uint32_t) start < tempString.length) {
|
||||
if (*pEnd == '\0') {
|
||||
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
|
||||
else if (*pEnd == ',')
|
||||
{
|
||||
} else if (*pEnd == ',') {
|
||||
int32_t end = (int32_t) strtol(pEnd + 1, &pEnd, 10);
|
||||
if (!*pEnd)
|
||||
{
|
||||
if (end < 0)
|
||||
if (!*pEnd) {
|
||||
if (end < 0) {
|
||||
end = (int32_t) tempString.length + end;
|
||||
if ((uint32_t) end > tempString.length)
|
||||
}
|
||||
if ((uint32_t) end > tempString.length) {
|
||||
end = (int32_t) tempString.length;
|
||||
if (end > start)
|
||||
}
|
||||
if (end > start) {
|
||||
ffStrbufAppendNS(buffer, (uint32_t) (end - start), &tempString.chars[start]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*pEnd)
|
||||
{
|
||||
if (*pEnd) {
|
||||
*pSep = cSep;
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
char* pEnd = NULL;
|
||||
int32_t truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
|
||||
if (*pEnd != '\0')
|
||||
{
|
||||
if (*pEnd != '\0') {
|
||||
*pSep = cSep;
|
||||
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ellipsis = false;
|
||||
if (truncLength < 0)
|
||||
{
|
||||
if (truncLength < 0) {
|
||||
ellipsis = true;
|
||||
truncLength = -truncLength;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
|
||||
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
|
||||
if (tempString.length == (uint32_t) truncLength)
|
||||
if (tempString.length == (uint32_t) truncLength) {
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
else if (tempString.length > (uint32_t) truncLength)
|
||||
{
|
||||
if (cSep == ':')
|
||||
{
|
||||
} else if (tempString.length > (uint32_t) truncLength) {
|
||||
if (cSep == ':') {
|
||||
ffStrbufSubstrBefore(&tempString, (uint32_t) truncLength);
|
||||
ffStrbufTrimRightSpace(&tempString);
|
||||
} else {
|
||||
ffStrbufSubstrBefore(&tempString, (uint32_t) (!ellipsis ? truncLength : truncLength - 1));
|
||||
}
|
||||
else
|
||||
ffStrbufSubstrBefore(&tempString, (uint32_t) (!ellipsis? truncLength : truncLength - 1));
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
|
||||
if (ellipsis)
|
||||
if (ellipsis) {
|
||||
ffStrbufAppendS(buffer, "…");
|
||||
}
|
||||
else if (cSep == ':')
|
||||
}
|
||||
} else if (cSep == ':') {
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
else
|
||||
{
|
||||
if (cSep == '<')
|
||||
{
|
||||
} else {
|
||||
if (cSep == '<') {
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
|
||||
ffStrbufAppend(buffer, &tempString);
|
||||
}
|
||||
@@ -408,6 +372,7 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
|
||||
}
|
||||
}
|
||||
|
||||
if (!instance.config.display.pipe)
|
||||
if (!instance.config.display.pipe) {
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,25 @@
|
||||
#include "frequency.h"
|
||||
#include "common/frequency.h"
|
||||
|
||||
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result)
|
||||
{
|
||||
if (mhz == 0)
|
||||
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result) {
|
||||
if (mhz == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
bool spaceBeforeUnit = options->freqSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
int8_t ndigits = options->freqNdigits;
|
||||
|
||||
if (ndigits >= 0)
|
||||
{
|
||||
if (ndigits >= 0) {
|
||||
ffStrbufAppendDouble(result, mhz / 1000., ndigits, true);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
ffStrbufAppendS(result, "GHz");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufAppendUInt(result, mhz);
|
||||
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
|
||||
if (spaceBeforeUnit) {
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
ffStrbufAppendS(result, "MHz");
|
||||
}
|
||||
return true;
|
||||
@@ -0,0 +1,274 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/init.h"
|
||||
#include "common/parsing.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/textModifier.h"
|
||||
#include "detection/displayserver/displayserver.h"
|
||||
#include "detection/terminaltheme/terminaltheme.h"
|
||||
#include "logo/logo.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <locale.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include "common/windows/unicode.h"
|
||||
#else
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
FFinstance instance; // Global singleton
|
||||
|
||||
static void initState(FFstate* state) {
|
||||
state->logoWidth = 0;
|
||||
state->logoHeight = 0;
|
||||
state->keysHeight = 0;
|
||||
state->terminalLightTheme = false;
|
||||
state->titleFqdn = false;
|
||||
|
||||
ffPlatformInit(&state->platform);
|
||||
state->dynamicInterval = 0;
|
||||
|
||||
{
|
||||
// don't enable bright color if the terminal is in light mode
|
||||
FFTerminalThemeResult result;
|
||||
if (ffDetectTerminalTheme(&result, true /* forceEnv for performance */) && !result.bg.dark) {
|
||||
state->terminalLightTheme = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void defaultConfig(void) {
|
||||
ffOptionsInitLogo(&instance.config.logo);
|
||||
ffOptionsInitGeneral(&instance.config.general);
|
||||
ffOptionsInitDisplay(&instance.config.display);
|
||||
}
|
||||
|
||||
void ffInitInstance(void) {
|
||||
#ifdef _WIN32
|
||||
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendat>
|
||||
setlocale(LC_ALL, ".UTF8");
|
||||
#else
|
||||
// Never use `setlocale(LC_ALL, "")`
|
||||
setlocale(LC_TIME, "");
|
||||
#endif
|
||||
|
||||
defaultConfig();
|
||||
initState(&instance.state);
|
||||
}
|
||||
|
||||
static volatile bool ffDisableLinewrap = false;
|
||||
static volatile bool ffHideCursor = false;
|
||||
#ifdef _WIN32
|
||||
static volatile UINT oldCp = CP_UTF8;
|
||||
#endif
|
||||
|
||||
static void resetConsole(void) {
|
||||
if (ffDisableLinewrap) {
|
||||
fputs("\033[?7h", stdout);
|
||||
}
|
||||
|
||||
if (ffHideCursor) {
|
||||
fputs("\033[?25h", stdout);
|
||||
}
|
||||
|
||||
if (instance.state.dynamicInterval > 0) {
|
||||
fputs("\033[?1049l", stdout); // Disable alternate buffer
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
fflush(stdout);
|
||||
|
||||
if (oldCp != CP_UTF8) {
|
||||
SetConsoleOutputCP(oldCp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
BOOL WINAPI consoleHandler(FF_A_UNUSED DWORD signal) {
|
||||
resetConsole();
|
||||
exit(0);
|
||||
}
|
||||
#else
|
||||
static void exitSignalHandler(FF_A_UNUSED int signal) {
|
||||
resetConsole();
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ffStart(void) {
|
||||
ffDisableLinewrap = instance.config.display.disableLinewrap && !instance.config.display.pipe;
|
||||
ffHideCursor = instance.config.display.hideCursor && !instance.config.display.pipe;
|
||||
|
||||
#ifdef _WIN32
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
if (instance.config.display.noBuffer) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
} else {
|
||||
setvbuf(stdout, NULL, _IOFBF, 4096);
|
||||
}
|
||||
SetConsoleCtrlHandler(consoleHandler, TRUE);
|
||||
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD mode = 0;
|
||||
if (GetConsoleMode(hStdout, &mode)) {
|
||||
SetConsoleMode(hStdout, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
oldCp = GetConsoleOutputCP();
|
||||
if (oldCp != CP_UTF8) {
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (instance.config.display.noBuffer) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
}
|
||||
struct sigaction action;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
action.sa_handler = exitSignalHandler;
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
sigaction(SIGQUIT, &action, NULL);
|
||||
sigset_t newmask;
|
||||
sigemptyset(&newmask);
|
||||
sigaddset(&newmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &newmask, NULL);
|
||||
#endif
|
||||
|
||||
// reset everything to default before we start printing
|
||||
if (!instance.config.display.pipe) {
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
}
|
||||
|
||||
if (ffHideCursor) {
|
||||
fputs("\033[?25l", stdout);
|
||||
}
|
||||
|
||||
if (ffDisableLinewrap) {
|
||||
fputs("\033[?7l", stdout);
|
||||
}
|
||||
|
||||
if (instance.state.dynamicInterval > 0) {
|
||||
fputs("\033[?1049h\033[H", stdout); // Enable alternate buffer
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void ffFinish(void) {
|
||||
resetConsole();
|
||||
}
|
||||
|
||||
static void destroyConfig(void) {
|
||||
ffOptionsDestroyLogo(&instance.config.logo);
|
||||
ffOptionsDestroyGeneral(&instance.config.general);
|
||||
ffOptionsDestroyDisplay(&instance.config.display);
|
||||
}
|
||||
|
||||
static void destroyState(void) {
|
||||
ffPlatformDestroy(&instance.state.platform);
|
||||
}
|
||||
|
||||
void ffDestroyInstance(void) {
|
||||
destroyConfig();
|
||||
destroyState();
|
||||
}
|
||||
|
||||
// Must be in a file compiled with the libfastfetch target, because the FF_HAVE* macros are not defined for the executable targets
|
||||
void ffListFeatures(void) {
|
||||
fputs(
|
||||
#if FF_HAVE_THREADS
|
||||
"threads\n"
|
||||
#endif
|
||||
#if FF_HAVE_VULKAN
|
||||
"vulkan\n"
|
||||
#endif
|
||||
#if FF_HAVE_WAYLAND
|
||||
"wayland\n"
|
||||
#endif
|
||||
#if FF_HAVE_XCB_RANDR
|
||||
"xcb-randr\n"
|
||||
#endif
|
||||
#if FF_HAVE_XRANDR
|
||||
"xrandr\n"
|
||||
#endif
|
||||
#if FF_HAVE_DRM
|
||||
"drm\n"
|
||||
#endif
|
||||
#if FF_HAVE_DRM_AMDGPU
|
||||
"drm_amdgpu\n"
|
||||
#endif
|
||||
#if FF_HAVE_GIO
|
||||
"gio\n"
|
||||
#endif
|
||||
#if FF_HAVE_DCONF
|
||||
"dconf\n"
|
||||
#endif
|
||||
#if FF_HAVE_EET
|
||||
"eet\n"
|
||||
#endif
|
||||
#if FF_HAVE_DBUS
|
||||
"dbus\n"
|
||||
#endif
|
||||
#if FF_HAVE_IMAGEMAGICK7
|
||||
"imagemagick7\n"
|
||||
#endif
|
||||
#if FF_HAVE_IMAGEMAGICK6
|
||||
"imagemagick6\n"
|
||||
#endif
|
||||
#if FF_HAVE_CHAFA
|
||||
"chafa\n"
|
||||
#endif
|
||||
#if FF_HAVE_ZLIB
|
||||
"zlib\n"
|
||||
#endif
|
||||
#if FF_HAVE_SQLITE3
|
||||
"sqlite3\n"
|
||||
#endif
|
||||
#if FF_HAVE_RPM
|
||||
"rpm\n"
|
||||
#endif
|
||||
#if FF_HAVE_EGL
|
||||
"egl\n"
|
||||
#endif
|
||||
#if FF_HAVE_GLX
|
||||
"glx\n"
|
||||
#endif
|
||||
#if FF_HAVE_OPENCL
|
||||
"opencl\n"
|
||||
#endif
|
||||
#if FF_HAVE_FREETYPE
|
||||
"freetype\n"
|
||||
#endif
|
||||
#if FF_HAVE_PULSE
|
||||
"libpulse\n"
|
||||
#endif
|
||||
#if FF_HAVE_DDCUTIL
|
||||
"libddcutil\n"
|
||||
#endif
|
||||
#if FF_HAVE_ELF || __sun || (__FreeBSD__ && !__DragonFly__) || __OpenBSD__ || __NetBSD__
|
||||
"libelf\n"
|
||||
#endif
|
||||
#if FF_HAVE_LIBZFS
|
||||
"libzfs\n"
|
||||
#endif
|
||||
#if FF_USE_SYSTEM_YYJSON
|
||||
"System yyjson\n"
|
||||
#endif
|
||||
#if FF_HAVE_LINUX_VIDEODEV2
|
||||
"linux/videodev2\n"
|
||||
#endif
|
||||
#if FF_HAVE_EMBEDDED_PCIIDS
|
||||
"Embedded pciids\n"
|
||||
#endif
|
||||
#if FF_HAVE_WINRT
|
||||
"WinRT headers\n"
|
||||
#endif
|
||||
#if FF_WIN81_COMPAT
|
||||
"Windows 8.1 Compatibility\n"
|
||||
#endif
|
||||
#if FF_APPLE_MEMSIZE_USABLE
|
||||
"Apple memsize_usable\n"
|
||||
#endif
|
||||
"",
|
||||
stdout);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "io.h"
|
||||
#include "common/io.h"
|
||||
#include "fastfetch.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/time.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
@@ -8,213 +8,177 @@
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#ifndef __APPLE__
|
||||
#include <poll.h>
|
||||
#include <poll.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#if FF_HAVE_WORDEXP
|
||||
#include <wordexp.h>
|
||||
#elif FF_HAVE_GLOB
|
||||
#warning "<wordexp.h> is not available, use <glob.h> instead"
|
||||
#include <glob.h>
|
||||
#else
|
||||
#warning "Neither <wordexp.h> nor <glob.h> is available"
|
||||
#include <glob.h>
|
||||
#endif
|
||||
|
||||
static void createSubfolders(const char* fileName)
|
||||
{
|
||||
static void createSubfolders(const char* fileName) {
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
|
||||
|
||||
char *token = NULL;
|
||||
while((token = strchr(fileName, '/')) != NULL)
|
||||
{
|
||||
ffStrbufAppendNS(&path, (uint32_t)(token - fileName + 1), fileName);
|
||||
const char* token = NULL;
|
||||
while ((token = strchr(fileName, '/')) != NULL) {
|
||||
ffStrbufAppendNS(&path, (uint32_t) (token - fileName + 1), fileName);
|
||||
mkdir(path.chars, S_IRWXU | S_IRGRP | S_IROTH);
|
||||
fileName = token + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data)
|
||||
{
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data) {
|
||||
int openFlagsModes = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC;
|
||||
mode_t openFlagsRights = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
int FF_AUTO_CLOSE_FD fd = open(fileName, openFlagsModes, openFlagsRights);
|
||||
if(fd == -1)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT) {
|
||||
createSubfolders(fileName);
|
||||
fd = open(fileName, openFlagsModes, openFlagsRights);
|
||||
if(fd == -1)
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return write(fd, data, dataSize) > 0;
|
||||
}
|
||||
|
||||
static inline void readWithLength(int fd, FFstrbuf* buffer, uint32_t length)
|
||||
{
|
||||
static inline void readWithLength(int fd, FFstrbuf* buffer, uint32_t length) {
|
||||
ffStrbufEnsureFixedLengthFree(buffer, length);
|
||||
ssize_t bytesRead = 0;
|
||||
while(
|
||||
length > 0 && (bytesRead = read(fd, buffer->chars + buffer->length, length)) > 0
|
||||
) {
|
||||
while (
|
||||
length > 0 && (bytesRead = read(fd, buffer->chars + buffer->length, length)) > 0) {
|
||||
buffer->length += (uint32_t) bytesRead;
|
||||
length -= (uint32_t) bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void readUntilEOF(int fd, FFstrbuf* buffer)
|
||||
{
|
||||
static inline void readUntilEOF(int fd, FFstrbuf* buffer) {
|
||||
ffStrbufEnsureFree(buffer, 31);
|
||||
uint32_t available = ffStrbufGetFree(buffer);
|
||||
ssize_t bytesRead = 0;
|
||||
while(
|
||||
(bytesRead = read(fd, buffer->chars + buffer->length, available)) > 0
|
||||
) {
|
||||
while (
|
||||
(bytesRead = read(fd, buffer->chars + buffer->length, available)) > 0) {
|
||||
buffer->length += (uint32_t) bytesRead;
|
||||
if((uint32_t) bytesRead == available)
|
||||
if ((uint32_t) bytesRead == available) {
|
||||
ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte.
|
||||
}
|
||||
available = ffStrbufGetFree(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
bool ffAppendFDBuffer(int fd, FFstrbuf* buffer)
|
||||
{
|
||||
bool ffAppendFDBuffer(int fd, FFstrbuf* buffer) {
|
||||
struct stat fileInfo;
|
||||
if(fstat(fd, &fileInfo) != 0)
|
||||
if (fstat(fd, &fileInfo) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fileInfo.st_size > 0)
|
||||
readWithLength(fd, buffer, (uint32_t)fileInfo.st_size);
|
||||
else
|
||||
if (fileInfo.st_size > 0) {
|
||||
readWithLength(fd, buffer, (uint32_t) fileInfo.st_size);
|
||||
} else {
|
||||
readUntilEOF(fd, buffer);
|
||||
}
|
||||
|
||||
buffer->chars[buffer->length] = '\0';
|
||||
|
||||
return buffer->length > 0;
|
||||
}
|
||||
|
||||
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data)
|
||||
{
|
||||
int FF_AUTO_CLOSE_FD fd = open(fileName, O_RDONLY | O_CLOEXEC);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
return ffReadFDData(fd, dataSize, data);
|
||||
}
|
||||
|
||||
ssize_t ffReadFileDataRelative(int dfd, const char* fileName, size_t dataSize, void* data)
|
||||
{
|
||||
int FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
|
||||
if(fd == -1)
|
||||
return -1;
|
||||
|
||||
return ffReadFDData(fd, dataSize, data);
|
||||
}
|
||||
|
||||
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
int FF_AUTO_CLOSE_FD fd = open(fileName, O_RDONLY | O_CLOEXEC);
|
||||
if(fd == -1)
|
||||
return false;
|
||||
|
||||
return ffAppendFDBuffer(fd, buffer);
|
||||
}
|
||||
|
||||
bool ffAppendFileBufferRelative(int dfd, const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
int FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, O_RDONLY | O_CLOEXEC);
|
||||
if(fd == -1)
|
||||
return false;
|
||||
|
||||
return ffAppendFDBuffer(fd, buffer);
|
||||
}
|
||||
|
||||
bool ffPathExpandEnv(FF_MAYBE_UNUSED const char* in, FF_MAYBE_UNUSED FFstrbuf* out)
|
||||
{
|
||||
bool ffPathExpandEnv(const char* in, FFstrbuf* out) {
|
||||
bool result = false;
|
||||
|
||||
#if FF_HAVE_WORDEXP // https://github.com/termux/termux-packages/pull/7056
|
||||
#if FF_HAVE_WORDEXP
|
||||
|
||||
wordexp_t exp;
|
||||
if (wordexp(in, &exp, 0) != 0)
|
||||
if (wordexp(in, &exp, 0) != 0) { // WARN: 0 = no safety flags; command substitution allowed
|
||||
return false;
|
||||
}
|
||||
|
||||
if (exp.we_wordc >= 1)
|
||||
{
|
||||
if (exp.we_wordc >= 1) {
|
||||
result = true;
|
||||
ffStrbufSetS(out, exp.we_wordv[exp.we_wordc > 1 ? ffTimeGetNow() % exp.we_wordc : 0]);
|
||||
}
|
||||
|
||||
wordfree(&exp);
|
||||
|
||||
#elif FF_HAVE_GLOB
|
||||
#else
|
||||
|
||||
glob_t gb;
|
||||
if (glob(in, GLOB_NOSORT | GLOB_TILDE, NULL, &gb) != 0)
|
||||
if (glob(in, GLOB_NOSORT
|
||||
#ifdef GLOB_TILDE
|
||||
| GLOB_TILDE
|
||||
#endif
|
||||
#ifdef GLOB_BRACE
|
||||
| GLOB_BRACE
|
||||
#endif
|
||||
,
|
||||
NULL,
|
||||
&gb) != 0)
|
||||
return false;
|
||||
|
||||
if (gb.gl_matchc >= 1)
|
||||
{
|
||||
if (gb.gl_pathc >= 1) {
|
||||
result = true;
|
||||
ffStrbufSetS(out, gb.gl_pathv[gb.gl_matchc > 1 ? ffTimeGetNow() % gb.gl_matchc : 0]);
|
||||
ffStrbufSetS(out, gb.gl_pathv[gb.gl_pathc > 1 ? ffTimeGetNow() % (unsigned) gb.gl_pathc : 0]);
|
||||
}
|
||||
|
||||
globfree(&gb);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int ftty = -1;
|
||||
static struct termios oldTerm;
|
||||
void restoreTerm(void)
|
||||
{
|
||||
void restoreTerm(void) {
|
||||
tcsetattr(ftty, TCSAFLUSH, &oldTerm);
|
||||
}
|
||||
|
||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...)
|
||||
{
|
||||
if (ftty < 0)
|
||||
{
|
||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...) {
|
||||
if (ftty < 0) {
|
||||
ftty = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||
if (ftty < 0)
|
||||
if (ftty < 0) {
|
||||
return "open(\"/dev/tty\", O_RDWR | O_NOCTTY | O_CLOEXEC) failed";
|
||||
}
|
||||
|
||||
if(tcgetattr(ftty, &oldTerm) == -1)
|
||||
if (tcgetattr(ftty, &oldTerm) == -1) {
|
||||
return "tcgetattr(STDIN_FILENO, &oldTerm) failed";
|
||||
}
|
||||
|
||||
struct termios newTerm = oldTerm;
|
||||
newTerm.c_lflag &= (tcflag_t) ~(ICANON | ECHO);
|
||||
if(tcsetattr(ftty, TCSAFLUSH, &newTerm) == -1)
|
||||
if (tcsetattr(ftty, TCSAFLUSH, &newTerm) == -1) {
|
||||
return "tcsetattr(STDIN_FILENO, TCSAFLUSH, &newTerm)";
|
||||
}
|
||||
atexit(restoreTerm);
|
||||
}
|
||||
|
||||
ffWriteFDData(ftty, strlen(request), request);
|
||||
|
||||
//Give the terminal some time to respond
|
||||
#ifndef __APPLE__
|
||||
if(poll(&(struct pollfd) { .fd = ftty, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS) <= 0)
|
||||
// Give the terminal some time to respond
|
||||
#ifndef __APPLE__
|
||||
if (poll(&(struct pollfd) { .fd = ftty, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS) <= 0) {
|
||||
return "poll(/dev/tty) timeout or failed";
|
||||
#else
|
||||
}
|
||||
#else
|
||||
{
|
||||
// On macOS, poll(/dev/tty) always returns immediately
|
||||
// See also https://nathancraddock.com/blog/macos-dev-tty-polling/
|
||||
fd_set rd;
|
||||
FD_ZERO(&rd);
|
||||
FD_SET(ftty, &rd);
|
||||
if(select(ftty + 1, &rd, NULL, NULL, &(struct timeval) { .tv_sec = FF_IO_TERM_RESP_WAIT_MS / 1000, .tv_usec = (FF_IO_TERM_RESP_WAIT_MS % 1000) * 1000 }) <= 0)
|
||||
if (select(ftty + 1, &rd, NULL, NULL, &(struct timeval) { .tv_sec = FF_IO_TERM_RESP_WAIT_MS / 1000, .tv_usec = (FF_IO_TERM_RESP_WAIT_MS % 1000) * 1000 }) <= 0) {
|
||||
return "select(/dev/tty) timeout or failed";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
char buffer[1024];
|
||||
size_t bytesRead = 0;
|
||||
@@ -222,12 +186,10 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (true) {
|
||||
ssize_t nRead = read(ftty, buffer + bytesRead, sizeof(buffer) - bytesRead - 1);
|
||||
|
||||
if (nRead <= 0)
|
||||
{
|
||||
if (nRead <= 0) {
|
||||
va_end(args);
|
||||
return "read(STDIN_FILENO, buffer, sizeof(buffer) - 1) failed";
|
||||
}
|
||||
@@ -240,13 +202,13 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
int ret = vsscanf(buffer, format, cargs);
|
||||
va_end(cargs);
|
||||
|
||||
if (ret <= 0)
|
||||
{
|
||||
if (ret <= 0) {
|
||||
va_end(args);
|
||||
return "vsscanf(buffer, format, args) failed";
|
||||
}
|
||||
if (ret >= nParams)
|
||||
if (ret >= nParams) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
@@ -254,22 +216,22 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ffSuppressIO(bool suppress)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (instance.config.display.debugMode)
|
||||
bool ffSuppressIO(bool suppress) {
|
||||
#ifndef NDEBUG
|
||||
if (instance.config.display.debugMode) {
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool init = false;
|
||||
static int origOut = -1;
|
||||
static int origErr = -1;
|
||||
static int nullFile = -1;
|
||||
|
||||
if(!init)
|
||||
{
|
||||
if(!suppress)
|
||||
if (!init) {
|
||||
if (!suppress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
origOut = dup(STDOUT_FILENO);
|
||||
origErr = dup(STDERR_FILENO);
|
||||
@@ -277,8 +239,9 @@ bool ffSuppressIO(bool suppress)
|
||||
init = true;
|
||||
}
|
||||
|
||||
if(nullFile == -1)
|
||||
if (nullFile == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
@@ -288,47 +251,49 @@ bool ffSuppressIO(bool suppress)
|
||||
return true;
|
||||
}
|
||||
|
||||
void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indentation, const char* folderName, bool pretty)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD int dfd = open(folder->chars, O_RDONLY | O_CLOEXEC);
|
||||
if (dfd < 0)
|
||||
void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indentation, const char* folderName, bool pretty) {
|
||||
int dfd = open(folder->chars, O_RDONLY | O_CLOEXEC | O_DIRECTORY); // Ownership of dfd will be transformed to dir
|
||||
if (dfd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DIR* dir = fdopendir(dfd);
|
||||
if(dir == NULL)
|
||||
return;
|
||||
FF_AUTO_CLOSE_DIR DIR* dir = fdopendir(dfd);
|
||||
if (dir == NULL) {
|
||||
close(dfd);
|
||||
return; // Should not happen
|
||||
}
|
||||
|
||||
uint32_t folderLength = folder->length;
|
||||
|
||||
if(pretty && folderName != NULL)
|
||||
{
|
||||
for(uint8_t i = 0; i < indentation - 1; i++)
|
||||
if (pretty && folderName != NULL) {
|
||||
for (uint8_t i = 0; i < indentation - 1; i++) {
|
||||
fputs(" | ", stdout);
|
||||
}
|
||||
printf("%s/\n", folderName);
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
|
||||
while((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
if(entry->d_name[0] == '.') // skip hidden files
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_name[0] == '.') { // skip hidden files
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isDir = false;
|
||||
#if !defined(__sun) && !defined(__HAIKU__)
|
||||
if(entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK)
|
||||
if (entry->d_type != DT_UNKNOWN && entry->d_type != DT_LNK) {
|
||||
isDir = entry->d_type == DT_DIR;
|
||||
else
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
struct stat stbuf;
|
||||
if (fstatat(dfd, entry->d_name, &stbuf, 0) < 0)
|
||||
if (fstatat(dfd, entry->d_name, &stbuf, 0) < 0) {
|
||||
isDir = false;
|
||||
else
|
||||
} else {
|
||||
isDir = S_ISDIR(stbuf.st_mode);
|
||||
}
|
||||
}
|
||||
if (isDir)
|
||||
{
|
||||
if (isDir) {
|
||||
ffStrbufAppendS(folder, entry->d_name);
|
||||
ffStrbufAppendC(folder, '/');
|
||||
listFilesRecursively(baseLength, folder, (uint8_t) (indentation + 1), entry->d_name, pretty);
|
||||
@@ -336,34 +301,33 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pretty)
|
||||
{
|
||||
for(uint8_t i = 0; i < indentation; i++)
|
||||
if (pretty) {
|
||||
for (uint8_t i = 0; i < indentation; i++) {
|
||||
fputs(" | ", stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
fputs(folder->chars + baseLength, stdout);
|
||||
}
|
||||
|
||||
puts(entry->d_name);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void ffListFilesRecursively(const char* path, bool pretty)
|
||||
{
|
||||
void ffListFilesRecursively(const char* path, bool pretty) {
|
||||
FF_STRBUF_AUTO_DESTROY folder = ffStrbufCreateS(path);
|
||||
ffStrbufEnsureEndsWithC(&folder, '/');
|
||||
listFilesRecursively(folder.length, &folder, 0, NULL, pretty);
|
||||
}
|
||||
|
||||
FFNativeFD ffGetNullFD(void)
|
||||
{
|
||||
FFNativeFD ffGetNullFD(void) {
|
||||
static FFNativeFD hNullFile = -1;
|
||||
if (hNullFile != -1)
|
||||
if (hNullFile != -1) {
|
||||
return hNullFile;
|
||||
}
|
||||
hNullFile = open("/dev/null", O_WRONLY | O_CLOEXEC);
|
||||
return hNullFile;
|
||||
}
|
||||
|
||||
bool ffRemoveFile(const char* fileName) {
|
||||
return unlink(fileName) == 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user