mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-29 11:10:59 +01:00
Compare commits
1277 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b615093ab0 | |||
| 197d1c6e5d | |||
| bfe74b8437 | |||
| bbb92543b0 | |||
| 99785f54dd | |||
| 17bb3fd238 | |||
| 1d8bbeb89f | |||
| 939bfbb844 | |||
| fdd4a60d9d | |||
| 6eb3a8148b | |||
| 4f6a329ea3 | |||
| cb73fdcfc9 | |||
| 8a0c1d2f6c | |||
| 2aa053a904 | |||
| a9ee641bd6 | |||
| f93ffed57a | |||
| 73ebe95120 | |||
| b7bc8ca99d | |||
| 1bab113d25 | |||
| 3b042d26ab | |||
| 29567e1674 | |||
| 1e8b4eb974 | |||
| 3453de61f8 | |||
| ad1d12ed38 | |||
| ab6ec4c3f5 | |||
| 3ccaf2d7dd | |||
| 1f32e0bcb2 | |||
| d7b66c8677 | |||
| 7f9faeb720 | |||
| cc435d2b70 | |||
| e0361fdcf6 | |||
| 124f474f81 | |||
| 068d86ef13 | |||
| 10d6b3a3d7 | |||
| 62617a35de | |||
| 10379d600d | |||
| de414b7250 | |||
| be51ba9f15 | |||
| 132e69b580 | |||
| 07a7211526 | |||
| 0806145f4d | |||
| a7e19308cc | |||
| 262a8ca4d3 | |||
| 30d6fb542b | |||
| 1e0ec1004a | |||
| 35a35f066c | |||
| 9859b248ff | |||
| 66709ccc77 | |||
| 10ca4f171e | |||
| 04a5c2d9d3 | |||
| 4909ac0d52 | |||
| fb89af3d84 | |||
| 2bcd49c106 | |||
| 3a069b2331 | |||
| d5e50ee30c | |||
| 845d5983aa | |||
| 1989c6f549 | |||
| 6e2a25759d | |||
| 460a715901 | |||
| 9ac3822105 | |||
| e02e44f673 | |||
| 0f712b4eaf | |||
| 995d172d3e | |||
| 8102478f2f | |||
| 537ffac39c | |||
| 95bcc4da5b | |||
| 1b3594f600 | |||
| ae1288d087 | |||
| b201fd2db9 | |||
| 7f458b03e8 | |||
| 5f65515c3c | |||
| 3d0747b56d | |||
| 68c5dd31f5 | |||
| a57ebe992a | |||
| 54dcb1b6f9 | |||
| 39d667d1d5 | |||
| 7099964530 | |||
| ae4cbcbdba | |||
| cee27354ba | |||
| 109644ec77 | |||
| 782b7b8fd1 | |||
| a9bf53b4a1 | |||
| ab200636f1 | |||
| 4d8d6a5fe9 | |||
| ef853fe858 | |||
| 47fbfcb0e9 | |||
| c88ec3bcc3 | |||
| 2838dccbfd | |||
| f0d8c996e6 | |||
| 6ff7925f45 | |||
| ffbf4ec7ac | |||
| a3f46d9742 | |||
| 4fb57bb39b | |||
| cdbe8f54c6 | |||
| 80632920bb | |||
| 5246f149eb | |||
| f00a021e27 | |||
| 676dbcfee2 | |||
| 9801d89316 | |||
| fb52d32452 | |||
| 36dc82e926 | |||
| 7fdd1d8e93 | |||
| 0c3b852bf7 | |||
| 866c5e89c8 | |||
| a1edf7316c | |||
| c3164d15e1 | |||
| a2eda0090a | |||
| d8797ae314 | |||
| 93175aea1d | |||
| 537635cd9c | |||
| c68bedd9bf | |||
| 80a931a87f | |||
| d223b04ad8 | |||
| 0e04ebdf63 | |||
| bba45429ae | |||
| 00ccd8be12 | |||
| 42e4fe3085 | |||
| fb5622215b | |||
| ccf1dcfa6d | |||
| 0c6a9d6e98 | |||
| b6b5dabd54 | |||
| fdbaaea962 | |||
| add2339c9c | |||
| e1e2de0929 | |||
| 59f0f533e7 | |||
| 28f4f6464b | |||
| 982313063d | |||
| 1e7386788f | |||
| 3a9595e121 | |||
| f8df95d146 | |||
| 592ef68d60 | |||
| 0bf6c8df74 | |||
| be15a0942c | |||
| 4a97cc6825 | |||
| a78d2a7737 | |||
| 3930759007 | |||
| e21530551d | |||
| c26bace3c2 | |||
| fe49a9624e | |||
| 7f3209181e | |||
| 2f74d6be4d | |||
| 256c6a4257 | |||
| aaa9153f5a | |||
| f26795826c | |||
| 18b31ca875 | |||
| 17b98007b4 | |||
| 1023388d60 | |||
| 6842ae0757 | |||
| b726ee5a85 | |||
| fd08fdb9df | |||
| 203ccb31fa | |||
| 3503facd68 | |||
| b2d48a1908 | |||
| b803d1ac82 | |||
| eda38a37ba | |||
| 0b76d33f07 | |||
| 39564acde4 | |||
| 3bfc4ec674 | |||
| fea55b276e | |||
| bc51f1ba0c | |||
| 38e4ba290d | |||
| 24fb73e9b4 | |||
| f96a290f3b | |||
| 67da836a27 | |||
| 15e4bc998a | |||
| f9f368c1fb | |||
| 4a33a2c410 | |||
| 34639b61b3 | |||
| efbfda9cf8 | |||
| 6142077656 | |||
| 58692adabc | |||
| 665c472482 | |||
| 2594c01fc1 | |||
| e1d7360453 | |||
| 873a881dab | |||
| ecbf218d6e | |||
| 2fb7fabc4d | |||
| 811e634dc8 | |||
| 098b501d0e | |||
| 7baaae166f | |||
| 560a9f419d | |||
| 70d2dd37a6 | |||
| 97ac94eb44 | |||
| 6ff7f2772b | |||
| f8fd708423 | |||
| c23c98a85a | |||
| 43988a8d41 | |||
| 1b7cc1cd30 | |||
| 59e33150dd | |||
| e004c97ee0 | |||
| 9bac273165 | |||
| d1b747192f | |||
| b96d7e7bec | |||
| 7b28ad07d7 | |||
| 02dc6a4428 | |||
| 53e868667a | |||
| fbf79ac6fe | |||
| 416715a505 | |||
| ff3360e036 | |||
| 98ec0a674c | |||
| 6178256404 | |||
| 129bb7309e | |||
| 464adb3540 | |||
| 3d7ea720fb | |||
| a90ae0249d | |||
| 375d0c6b25 | |||
| e3b3b5dfc6 | |||
| 35ffdb3995 | |||
| e8350a0d6e | |||
| 98e9b6eef1 | |||
| d796945604 | |||
| cd5a2def9a | |||
| 8334035927 | |||
| 1ee8528f10 | |||
| ced49ab2a4 | |||
| ec8b9be354 | |||
| 19725cd2f7 | |||
| 20c155e5b4 | |||
| 61e3f6108c | |||
| eff8514536 | |||
| f37a5a2543 | |||
| 9a9f82b59e | |||
| 29818d2dca | |||
| 222fe7e3f9 | |||
| 7d0256fb85 | |||
| a70c37fdaf | |||
| 66e9e38f28 | |||
| 5d290cc3d5 | |||
| 2e542bea64 | |||
| c708b6f5cd | |||
| 6a275da8b1 | |||
| ecee5694fb | |||
| 4e78f1b703 | |||
| 62f78ca60c | |||
| 00af2d1df2 | |||
| 1128a75d8c | |||
| ce61783c4b | |||
| 20597bff00 | |||
| caf64f5e4d | |||
| 90a6e90b19 | |||
| e9f719f37a | |||
| 8c98bf1953 | |||
| 1c1136ebd1 | |||
| 2787d97973 | |||
| 2e06a73102 | |||
| 37c198d997 | |||
| 37afe5a79d | |||
| 0ebf09754b | |||
| 0b1bc5b527 | |||
| 3d107764fa | |||
| 4863a173e4 | |||
| 8ea4468eec | |||
| b0ba084b5e | |||
| 43d01f26ea | |||
| b5ffc32b80 | |||
| 4ff931c06f | |||
| 6eef800ea2 | |||
| b9e4d04d65 | |||
| 9c791b2c12 | |||
| 76b6768141 | |||
| 42002a6240 | |||
| ef7f5aeac3 | |||
| d5214af189 | |||
| a33d6326f1 | |||
| df99241424 | |||
| 5b1ad1008d | |||
| 39024abae9 | |||
| 4dba32aabb | |||
| 753ddec3c5 | |||
| b04587e0cb | |||
| 3abcf4195f | |||
| af8a42270c | |||
| 1c9e521775 | |||
| 7fe08bddd0 | |||
| 41da49f579 | |||
| f169165599 | |||
| 2c4caf8e0d | |||
| 51b370a105 | |||
| 14cf6bfdc1 | |||
| 4d00dc6802 | |||
| d7de7c41ca | |||
| d80fe3f004 | |||
| dcce4ccdb0 | |||
| 191c5f2ef5 | |||
| 1dc794fac0 | |||
| 5936c1cef4 | |||
| 04f26a29eb | |||
| c9ad429a1b | |||
| 2b42db58da | |||
| 32757b0e43 | |||
| c5598d2361 | |||
| 203cd7cbcf | |||
| fb309134b5 | |||
| 8b162e89de | |||
| 9670b585fb | |||
| 3b76ade662 | |||
| 1b1df2e731 | |||
| 6c19a8b4ec | |||
| 71728247ab | |||
| 4344aa2ff1 | |||
| b7d8c12015 | |||
| 63d16ca854 | |||
| f15a1e96ce | |||
| 55bfcd6d86 | |||
| 880284fa9c | |||
| 143939f19b | |||
| a979d8ae81 | |||
| b5897f784f | |||
| 83cc7a94a4 | |||
| 5edda068f2 | |||
| 6d5cb520ed | |||
| e7ecba4460 | |||
| e0c239729b | |||
| 6570a7accc | |||
| 0c4ec62cac | |||
| 5d11b65d53 | |||
| 8b40117c08 | |||
| b1f2bf3526 | |||
| d8452f71ed | |||
| 2a269bfd4c | |||
| cafe82b462 | |||
| db8ee8159d | |||
| f0765b1a13 | |||
| 93566cb73c | |||
| 6d0d093d62 | |||
| 54fe4ffece | |||
| 124bbb06f9 | |||
| f04a53337d | |||
| 514efab8eb | |||
| 232cd6659b | |||
| 7c93f9c82a | |||
| e5b6e3612a | |||
| 6cb15885c7 | |||
| e6d90c2081 | |||
| 679d868b4a | |||
| 72d240f325 | |||
| 2ca7e037ef | |||
| 5e9c31ac31 | |||
| bcb48774a0 | |||
| 9ff7969acf | |||
| 7fbd06a135 | |||
| bbb906ba10 | |||
| 2de674ec9a | |||
| b700d9ded5 | |||
| 359cb0a79a | |||
| 45f9de6cd2 | |||
| 233c481f7a | |||
| b677f1e902 | |||
| 8d82eedaef | |||
| 4dc27b365f | |||
| e5c3fa4023 | |||
| bdba78c45c | |||
| f95167e907 | |||
| 3908c39ce2 | |||
| 858d3749f4 | |||
| 51f36fa4b4 | |||
| bfe30eec79 | |||
| 4dd2f2d34b | |||
| 60431c0321 | |||
| 7b2b3405ae | |||
| 965ed4ba79 | |||
| 00603357ea | |||
| 5ea299a8d2 | |||
| 368a4c5a73 | |||
| 8df0a1c97e | |||
| 7098cd86cb | |||
| 681749ed54 | |||
| c4695b8cf1 | |||
| 1d12003555 | |||
| 16b10c162b | |||
| 51cfd4b5ed | |||
| a0452b8323 | |||
| ebf38e829d | |||
| 4c2af4071c | |||
| e3b8e94055 | |||
| 1d4b0236ac | |||
| da42108732 | |||
| 8b8efcdcce | |||
| 8bc4464b64 | |||
| 4361c5e787 | |||
| 558a90d4e0 | |||
| 881730d0bd | |||
| ac8fc63c28 | |||
| 8686820c1a | |||
| 39b3dc001a | |||
| ba60d7975c | |||
| 51a4f34bb5 | |||
| 01bb73b9e8 | |||
| 56da8f8110 | |||
| 6d6302e327 | |||
| 20277b9ee9 | |||
| b6d95a0ce5 | |||
| daf5421be7 | |||
| b739dfee0b | |||
| 80101a5803 | |||
| cf33bef128 | |||
| 7e786bee31 | |||
| cbea6c3cb1 | |||
| 48a234d742 | |||
| c271708f7d | |||
| b42f11442c | |||
| 73918956d7 | |||
| 6559c7f42d | |||
| 56950e790f | |||
| e7642ea3a2 | |||
| a88ef86ce3 | |||
| fcf4aca3b7 | |||
| 3f8d141313 | |||
| 97a9be27b9 | |||
| dc46ecbcac | |||
| 984fadf70f | |||
| 1a42cbe1cf | |||
| 9c7cfb864f | |||
| 6d795a3572 | |||
| 2af56232a4 | |||
| 3196f5efaa | |||
| 1626ced83c | |||
| 241ddb79b0 | |||
| 66ce39e3fc | |||
| 3fae9e0274 | |||
| 5eaf98ecd2 | |||
| 00b7c5b56c | |||
| 305ab14e7b | |||
| a58fc4f77d | |||
| b653ef730d | |||
| dc464d6efe | |||
| 1c3f0eb7c0 | |||
| 476cd67345 | |||
| 2c736697d2 | |||
| 738ad59f54 | |||
| 5476e01407 | |||
| 4f0b00116e | |||
| 6490e2707f | |||
| ccf3909372 | |||
| 18eef29d0a | |||
| f6c25667fe | |||
| 97fa52fef0 | |||
| c5a8950ad4 | |||
| 111c1bab75 | |||
| 47dfd8a271 | |||
| bc1d305aaa | |||
| dda39f0c67 | |||
| 0b72c9b083 | |||
| 69f3d9ae33 | |||
| db46e06f2e | |||
| 7223803a76 | |||
| 9db4ec82fd | |||
| 62180366fc | |||
| 0feec933ea | |||
| 6c03db8be9 | |||
| f0564fb9a7 | |||
| 8bf1aca7fc | |||
| 445311978e | |||
| d9f5e006b5 | |||
| 21fbf14fb8 | |||
| bc8ce39193 | |||
| ac3fc219cf | |||
| dd30c82b83 | |||
| b41cdb0ca6 | |||
| 2b03bacb28 | |||
| 7dcc7d40d5 | |||
| a0fd59d869 | |||
| 1c352bb4de | |||
| 29d626f41d | |||
| 8ca6bb084d | |||
| 0b3ef5d35c | |||
| 98c8c5b344 | |||
| eeae601924 | |||
| 773e3ea497 | |||
| 317c0cfb86 | |||
| e0c31be9d5 | |||
| 75f00af98d | |||
| 9d6bb7d717 | |||
| 2ae9c3da33 | |||
| bf1af71d03 | |||
| 61656a1a7a | |||
| ba18705d2e | |||
| 873454191f | |||
| f8908b9a36 | |||
| 8ca551cccc | |||
| 0869809857 | |||
| c31779e99d | |||
| 89ad35d8a1 | |||
| d651f3de43 | |||
| c34c3e5aae | |||
| c5c61584d9 | |||
| 48c3b4f980 | |||
| 93bca8d480 | |||
| c690fe5aa9 | |||
| 55e674e1cb | |||
| 0495c47909 | |||
| 2f79c39f62 | |||
| 44056f1c85 | |||
| 6358bd7844 | |||
| 11d77e0d09 | |||
| ec25659503 | |||
| 35dbcb2588 | |||
| 8b6a74f2ec | |||
| 2ea6c3767e | |||
| 827bc26b0a | |||
| e12f3631d4 | |||
| 52c9730542 | |||
| 225d9e7771 | |||
| d031d6c20f | |||
| 35af768d92 | |||
| 22c4620345 | |||
| 81738aa303 | |||
| 113f7d34d8 | |||
| 62513004c8 | |||
| 1b36c3cd5f | |||
| 78874ae583 | |||
| a447928853 | |||
| 9f446bdad7 | |||
| 4cf060eb27 | |||
| 08d419d75c | |||
| 3ae1bfdc24 | |||
| 0e257f25c2 | |||
| 195e148cc1 | |||
| 5ab192ee05 | |||
| 4897152af6 | |||
| 69418cb527 | |||
| 0e90f3a7d4 | |||
| 1a663e72da | |||
| e0ae1ac1a2 | |||
| e0d141ecf6 | |||
| 29eb8bb57d | |||
| bc1f240558 | |||
| 3e15a2591a | |||
| 0ec9443de5 | |||
| 4a4ca9a4a8 | |||
| acf76e565a | |||
| 778aa21192 | |||
| dfdc7284a2 | |||
| 231d7fcd4d | |||
| a3184601dc | |||
| e3dfeeb8db | |||
| ccdcd6ab6f | |||
| 64d205628e | |||
| ff13adf1b4 | |||
| c38fd9ec10 | |||
| 96793ec604 | |||
| b79a336576 | |||
| d36e3a793b | |||
| d48bb48e4f | |||
| 835001eb2f | |||
| 5647703e4d | |||
| eff0108bb5 | |||
| 31c1431ac6 | |||
| fa2c56cb9f | |||
| 104b33c1f7 | |||
| cb4502dacc | |||
| 3dc86391ab | |||
| f5a67b25f4 | |||
| d8be61411e | |||
| 61968ca95c | |||
| 9f190b46c9 | |||
| a6aac70a5e | |||
| 3aa43212ff | |||
| 7c39b64fb4 | |||
| e1963e41be | |||
| a1d4cbf67f | |||
| f5b9e09d91 | |||
| 9742dda795 | |||
| 677c213397 | |||
| 8f0a12323b | |||
| b49f0b3d9c | |||
| bc686b2c84 | |||
| 7c2f7fa4f2 | |||
| 5fd059827d | |||
| 240928138e | |||
| 508a50f803 | |||
| bd40d8756e | |||
| b732e0c6d7 | |||
| 96685b7ebe | |||
| 49a1e42efd | |||
| b0fd11fcbd | |||
| b6127ff2c9 | |||
| f3babe3187 | |||
| e9c0d886d1 | |||
| d6a4946511 | |||
| 43e0cc22ff | |||
| c42ea6eeea | |||
| fe7962d5d2 | |||
| e84195bca0 | |||
| bf0133fe60 | |||
| 942c15b7b0 | |||
| 61a0bbcc8d | |||
| 8b3673eb38 | |||
| 22f9948497 | |||
| 107f39eb15 | |||
| 4e44c0520b | |||
| 55eb3c59d4 | |||
| 878ccf1375 | |||
| ebb0cf77bd | |||
| 17914dc29e | |||
| 14dfe171fb | |||
| 265c334399 | |||
| fc303c58db | |||
| 01e7503263 | |||
| ffd0455fe3 | |||
| 7df2ed2251 | |||
| 2edc41dd9a | |||
| cada77163e | |||
| 7cecdb0e6d | |||
| 420a9e9d7b | |||
| b96d8ad00b | |||
| 52970e2d13 | |||
| 4594734b66 | |||
| eb54c89b4a | |||
| 43301afc47 | |||
| 09e28451ec | |||
| 35c2f8c924 | |||
| 3ec7e94b47 | |||
| a720e1f945 | |||
| 70061625f7 | |||
| 5cd4316f98 | |||
| e946861eb0 | |||
| f09aebecd5 | |||
| 8f266eb2a7 | |||
| 8ee08d0067 | |||
| e936ddef6b | |||
| c706201282 | |||
| e24e3b6b29 | |||
| 135547fd50 | |||
| da05bed32f | |||
| 636471c366 | |||
| cbc84fda6c | |||
| fc7a0a8484 | |||
| 4d3b6c3b1b | |||
| 70e67177fe | |||
| 9fe7d6dbfa | |||
| a139a8c4a5 | |||
| da568c986e | |||
| 727fbc0276 | |||
| 20aabdfd78 | |||
| e79aaf5ce1 | |||
| 53c2d07161 | |||
| 6b9f594620 | |||
| ba3968784f | |||
| 021ece1681 | |||
| 9787b56460 | |||
| 54792e61ff | |||
| b34e2a4055 | |||
| 3b56200ee4 | |||
| de95a62e2f | |||
| 72cb64a500 | |||
| d48bd733f0 | |||
| 0585ea7797 | |||
| 794035f8d9 | |||
| 723552f68b | |||
| 8dd38f5744 | |||
| 616a5c7aea | |||
| 5e1e94473e | |||
| 4eae8ac244 | |||
| 8aeb7c2f10 | |||
| c20b532c7a | |||
| 3c59161aca | |||
| bd454632c3 | |||
| e73adc27a0 | |||
| 15c6014c04 | |||
| 54f18982ac | |||
| 99c2f2a2c8 | |||
| 45f35b1c4d | |||
| 428c05b9de | |||
| 23ca6d94ab | |||
| 9e4e0cb838 | |||
| 95a370e5a2 | |||
| e013005f34 | |||
| 92b86f7cbb | |||
| 2e4b7baf44 | |||
| a47ff0b2dc | |||
| 1f13f6c8f5 | |||
| b0bb6fb58e | |||
| 37029dbbe3 | |||
| 35428606e7 | |||
| 2c65eee00f | |||
| a1bf014352 | |||
| 412fc94dab | |||
| b65de28805 | |||
| bc86c66cdd | |||
| 9afa01812d | |||
| 30a8a94cd2 | |||
| 3a54617196 | |||
| 3e3a03a233 | |||
| b407669b69 | |||
| e14ea926a5 | |||
| 9d8f794cb9 | |||
| 825b8b8715 | |||
| 0c94fd87db | |||
| 473a08d85c | |||
| ae5132c388 | |||
| 1addcec7a2 | |||
| e75c37c101 | |||
| b9ab823f77 | |||
| a63d854681 | |||
| 576548b15e | |||
| 3043971259 | |||
| ccf4f22f8b | |||
| f932db713c | |||
| d2e8a8fd9e | |||
| 4c1f7c114a | |||
| 90bbfe195f | |||
| 1689fc81e9 | |||
| 4190dd5a6e | |||
| 074f882d7e | |||
| 53102380b8 | |||
| 822bc158ba | |||
| 8fbc29a70c | |||
| 0ef677a98c | |||
| 10b0d0ca3e | |||
| ec4e820e92 | |||
| 6c09fe49e4 | |||
| 1ce6398391 | |||
| d1bf208a92 | |||
| 7ce1e553b8 | |||
| 9c878b6943 | |||
| 6b9a6fd103 | |||
| 896fe60461 | |||
| dadcccdf26 | |||
| 0746af7549 | |||
| 88c3b7d118 | |||
| 173d9695b6 | |||
| 4b265cd56a | |||
| 1f211cf8d1 | |||
| bac60357c6 | |||
| 3a0e188570 | |||
| 9d5b40686b | |||
| a321cd877e | |||
| 5f810de1e0 | |||
| 3f0a93d057 | |||
| cbca6a965c | |||
| fde1195c27 | |||
| 9856d97bd1 | |||
| 2751737ebe | |||
| 834292df29 | |||
| a9a29869a4 | |||
| 242fb50461 | |||
| b5c4ffcc85 | |||
| 2c3628c630 | |||
| 4cca9bf198 | |||
| 0ed27d9700 | |||
| 9bca4eb48f | |||
| 462b80fc36 | |||
| f49620a95b | |||
| 6e0949c739 | |||
| 4b18ab0de7 | |||
| 8abd75a746 | |||
| d5b215e074 | |||
| 0b22427091 | |||
| aa34910b6c | |||
| 52972ed833 | |||
| 68e75effab | |||
| bb79370d03 | |||
| 9613cdd146 | |||
| 724b816252 | |||
| 7c89a10add | |||
| fb5ec76eb3 | |||
| 7141214f95 | |||
| 7d6074968f | |||
| 3bd380802b | |||
| ec1200dc06 | |||
| 5c18c061b3 | |||
| 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 |
@@ -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
|
||||
@@ -1,5 +0,0 @@
|
||||
[codespell]
|
||||
check-filenames =
|
||||
builtin = clear,rare,usage,informal
|
||||
skip = */.git,*/cmake-build-*,*/.idea,*/completions,*/presets,*/screenshots,*/tests,*/3rdparty,*/logo/ascii,./src/detection/gpu/asahi_drm.h
|
||||
ignore-words-list = iterm,compiletime,unknwn,pengwin,siduction,master,slave,sur,doas,conexant,ags,bu
|
||||
@@ -1,2 +1,15 @@
|
||||
# Normalise text files to LF in the repository and check them out with LF, so a CRLF
|
||||
# never enters an object and never shows up as a whole-file diff on Windows.
|
||||
* text=auto eol=lf
|
||||
|
||||
# The vendored AMD Display Library SDK ships with CRLF endings of its own. Leaving it
|
||||
# alone keeps `Dep (3rdparty): upgrades display-library` from carrying a 7000-line
|
||||
# whitespace diff, and a re-vendor will not fight the rule above.
|
||||
src/3rdparty/display-library/** -text
|
||||
|
||||
# The logo fixtures store the exact bytes fastfetch prints, ANSI escapes included.
|
||||
# They are demo data rather than text, and are not read by any test.
|
||||
*.fflogo -text
|
||||
|
||||
*.h linguist-language=c
|
||||
*.c linguist-language=c
|
||||
|
||||
@@ -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,16 +6,31 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Tip: You can display a logo in fastfetch without adding it to fastfetch's official repo.
|
||||
> [!WARNING]
|
||||
> **NB:** If your logo request does not meet the basic requirements outlined in this template, the issue may be closed at any time without any further explanation.
|
||||
|
||||
**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
|
||||
You can test your logo locally using:
|
||||
`fastfetch -l /path/to/logo.txt --logo-color-1 white --logo-color-2 red`
|
||||
Please refer to the [Migration Guide](https://github.com/fastfetch-cli/fastfetch/wiki/Migrate-Neofetch-Logo-To-Fastfetch) for details on color placeholders (`$1`, `$2`, etc.).
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: OS
|
||||
description: Paste the contents of `/etc/os-release` and `/etc/lsb-release` here. If neither file exists, describe how to identify the distro.
|
||||
description: Paste the contents of `/etc/os-release` (specifically the `ID` and `PRETTY_NAME` fields). If neither file exists, describe how to identify the distro.
|
||||
placeholder: cat /etc/os-release
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Public repository URL
|
||||
description: A public repository is required to verify the request. Please provide a link to the repository where the distro is developed.
|
||||
placeholder: https://github.com/example/distro
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Distro Website
|
||||
@@ -23,25 +38,35 @@ body:
|
||||
placeholder: https://example.com
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: ASCII Art
|
||||
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
|
||||
description: |
|
||||
The ASCII logo should not take up too much space (smaller than 50x20 characters, W x H).
|
||||
**Important:** Fastfetch uses `$1`, `$2`, `$3` etc. for color placeholders. Do NOT use hardcoded ANSI escape codes (like `\033[38;2...m`) or Neofetch's `${c1}` syntax.
|
||||
Take <https://github.com/fastfetch-cli/fastfetch/tree/dev/src/logo/ascii> as examples.
|
||||
placeholder: Paste ASCII art with $1, $2 placeholders here
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
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: My distro has reached a stable state (out of beta, alpha, RC).
|
||||
required: true
|
||||
- label: The ASCII art is smaller than 50x20 characters (W x H).
|
||||
required: true
|
||||
- label: The ASCII art includes color codes, or the color codes are available in `os-release`.
|
||||
- label: The ASCII art uses `$N` color placeholders (or colors are natively available in `os-release`).
|
||||
required: true
|
||||
- label: The ASCII art has no internal padding (spaces at the start and/or end of lines).
|
||||
- label: The ASCII art has no unnecessary trailing spaces at the end of lines.
|
||||
required: true
|
||||
- label: I promise that I will send a removal request if the distro is discontinued.
|
||||
required: true
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Fastfetch · Benchmark</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--page: #f5f7fb;
|
||||
--card: #fff;
|
||||
--text: #172033;
|
||||
--muted: #64748b;
|
||||
--border: #dbe3ef;
|
||||
--accent: #2563eb;
|
||||
--accent-soft: #dbeafe;
|
||||
--chart-text: #334155;
|
||||
--chart-grid: #cbd5e1;
|
||||
}
|
||||
:root[data-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
--page: #0f172a; --card: #172033; --text: #e5edf8; --muted: #9aabc1; --border: #334155; --accent-soft: #1e3a8a;
|
||||
--chart-text: #f1f5f9;
|
||||
--chart-grid: #475569;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body { max-width: 1440px; margin: 0 auto; padding: 2.5rem 1.25rem 4rem; background: var(--page); color: var(--text); font: 16px/1.5 system-ui, -apple-system, sans-serif; }
|
||||
header { margin-bottom: 1.75rem; }
|
||||
.header-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
||||
h1 { margin: 0; font-size: clamp(2rem, 4vw, 3rem); letter-spacing: -.04em; }
|
||||
h2 { margin: 0; font-size: 1.25rem; }
|
||||
p { color: var(--muted); }
|
||||
.intro { margin: .6rem 0 1rem; }
|
||||
.links a { color: var(--accent); font-weight: 600; text-decoration: none; }
|
||||
.links a:hover { text-decoration: underline; }
|
||||
.theme-toggle, .fullscreen { border: 1px solid var(--border); border-radius: .55rem; padding: .5rem .8rem; background: var(--card); color: var(--text); cursor: pointer; font: inherit; font-weight: 600; }
|
||||
.theme-toggle:hover, .fullscreen:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.tabs { display: flex; flex-wrap: wrap; gap: .6rem; margin: 1.5rem 0; padding: .35rem; border: 1px solid var(--border); border-radius: .8rem; background: var(--card); }
|
||||
.tab { border: 0; border-radius: .55rem; padding: .65rem 1.15rem; background: transparent; color: var(--muted); cursor: pointer; font: inherit; font-weight: 700; }
|
||||
.tab:hover, .tab[aria-selected="true"] { background: var(--accent); color: #fff; }
|
||||
.tab-panel[hidden] { display: none; }
|
||||
.platform-title { display: flex; align-items: center; gap: .65rem; margin: 1.5rem 0 1rem; }
|
||||
.platform-title::before { content: ''; width: .35rem; height: 1.4rem; border-radius: 1rem; background: var(--accent); }
|
||||
.charts { display: flex; flex-direction: column; gap: 1.25rem; }
|
||||
.chart { min-width: 0; padding: 1.25rem; border: 1px solid var(--border); border-radius: 1rem; background: var(--card); box-shadow: 0 8px 28px #0f172a0c; }
|
||||
.chart:fullscreen { overflow: auto; padding: 2rem; background: var(--page); }
|
||||
.chart:fullscreen .canvas-wrap { height: calc(100vh - 13rem); }
|
||||
.chart-header { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: .75rem; }
|
||||
.chart-header span { color: var(--muted); font-size: .85rem; }
|
||||
.canvas-wrap { position: relative; height: 360px; }
|
||||
.chart-actions { display: flex; align-items: center; gap: .75rem; }
|
||||
.legend-actions { display: flex; flex-wrap: wrap; gap: .45rem; margin-top: 1rem; }
|
||||
.legend-action { border: 1px solid var(--border); border-radius: .45rem; padding: .35rem .6rem; background: var(--card); color: var(--muted); cursor: pointer; font: inherit; font-size: .8rem; }
|
||||
.legend-action:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.legend { display: flex; flex-wrap: wrap; gap: .35rem .9rem; margin-top: 1rem; }
|
||||
.legend-item { display: inline-flex; align-items: center; gap: .4rem; border: 0; padding: .15rem 0; background: transparent; color: var(--text); cursor: pointer; font: inherit; font-size: .85rem; }
|
||||
.legend-item.is-hidden { color: var(--muted); }
|
||||
.legend-swatch { width: .75rem; height: .75rem; border-radius: 50%; }
|
||||
.legend-item.is-hidden .legend-swatch { opacity: .35; }
|
||||
.chart-tooltip { position: fixed; z-index: 1000; pointer-events: none; width: min(90vw, 900px); max-height: calc(100vh - 24px); overflow: hidden; padding: .7rem .85rem; border: 1px solid var(--border); border-radius: .6rem; background: var(--card); color: var(--text); box-shadow: 0 10px 30px #0f172a33; font-size: .8rem; opacity: 0; transition: opacity .1s ease; }
|
||||
.chart-tooltip-title { margin-bottom: .35rem; font-weight: 700; white-space: normal; overflow-wrap: anywhere; }
|
||||
.chart-tooltip-message { margin-bottom: .35rem; color: var(--muted); font-weight: 400; white-space: pre-wrap; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
|
||||
.chart-tooltip-body { max-height: calc(100vh - 76px); overflow: auto; columns: 220px auto; column-gap: 1.5rem; }
|
||||
.chart-tooltip-row { display: flex; align-items: center; gap: .45rem; break-inside: avoid; white-space: nowrap; }
|
||||
.chart-tooltip-swatch { width: .55rem; height: .55rem; border-radius: 50%; flex: 0 0 auto; }
|
||||
.empty { padding: 2rem 0; color: var(--muted); }
|
||||
</style>
|
||||
<script src="data.js"></script>
|
||||
<script src="https://unpkg.com/chart.js@latest/dist/chart.umd.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="header-row">
|
||||
<h1>Fastfetch Benchmark</h1>
|
||||
<button id="theme-toggle" class="theme-toggle" type="button"></button>
|
||||
</div>
|
||||
<p class="intro">Performance history grouped by CPU family. Each chart contains all modules and the total process time for one operating system and architecture. All metric series are shown by default except total process time, module Weather, and module PublicIp — toggle any series from the legend below the chart.</p>
|
||||
<p class="links"><a href="data.js">Download raw benchmark data</a></p>
|
||||
</header>
|
||||
<nav id="tabs" class="tabs" aria-label="CPU architecture"></nav>
|
||||
<main id="panels"><p class="empty">Loading charts…</p></main>
|
||||
<script>
|
||||
(() => {
|
||||
const benchmarkData = window.BENCHMARK_DATA || {};
|
||||
const entries = Object.values(benchmarkData.entries || {}).flat();
|
||||
const groups = new Map();
|
||||
|
||||
function architectureFamily(arch) {
|
||||
const normalized = arch.toLowerCase();
|
||||
if (/^(amd64|x86_64|x86|i[3-6]86)$/.test(normalized)) return 'Intel';
|
||||
if (/^(aarch64|arm64|armv[5-8].*|arm)$/.test(normalized)) return 'ARM';
|
||||
return 'Other';
|
||||
}
|
||||
|
||||
function parseName(name) {
|
||||
let match = name.match(/^fastfetch (linux|macos|windows) ([^ ]+) (module .+|total process time)$/i);
|
||||
if (match) {
|
||||
return { platform: match[1].toLowerCase(), arch: match[2], metric: match[3] };
|
||||
}
|
||||
|
||||
// Keep older macOS records visible while new records use the platform field.
|
||||
match = name.match(/^fastfetch ([^ ]+) (module .+|total process time)$/i);
|
||||
return match ? { platform: 'macos', arch: match[1], metric: match[2] } : null;
|
||||
}
|
||||
|
||||
function entryMetadata(entry) {
|
||||
const raw = entry.commit?.timestamp ?? entry.date;
|
||||
const date = new Date(raw);
|
||||
return {
|
||||
date: Number.isNaN(date.valueOf()) ? String(entry.date ?? '') : date.toISOString(),
|
||||
sha: entry.commit?.id ?? 'unknown',
|
||||
message: entry.commit?.message?.trim() ?? ''
|
||||
};
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
const metricsByGroup = new Map();
|
||||
for (const bench of entry.benches || []) {
|
||||
const parsed = parseName(bench.name);
|
||||
if (!parsed) continue;
|
||||
const key = `${parsed.platform} ${parsed.arch}`;
|
||||
if (!metricsByGroup.has(key)) metricsByGroup.set(key, new Map());
|
||||
metricsByGroup.get(key).set(parsed.metric, bench.value);
|
||||
}
|
||||
|
||||
for (const [key, metrics] of metricsByGroup) {
|
||||
if (!groups.has(key)) groups.set(key, []);
|
||||
groups.get(key).push({ ...entryMetadata(entry), metrics });
|
||||
}
|
||||
}
|
||||
|
||||
const colors = [
|
||||
'#2563eb', '#dc2626', '#16a34a', '#9333ea', '#ea580c', '#0891b2',
|
||||
'#db2777', '#65a30d', '#4f46e5', '#ca8a04', '#0f766e', '#be123c'
|
||||
];
|
||||
// Series hidden on first render; every other metric is shown by default.
|
||||
const hiddenByDefault = new Set(['total process time', 'module Weather', 'module PublicIp']);
|
||||
const tabs = document.getElementById('tabs');
|
||||
const panels = document.getElementById('panels');
|
||||
const root = document.documentElement;
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
const savedTheme = localStorage.getItem('fastfetch-benchmark-theme');
|
||||
let theme = savedTheme || (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
const chartsByInstance = [];
|
||||
|
||||
if (!window.Chart) {
|
||||
tabs.hidden = true;
|
||||
panels.innerHTML = '<p class="empty">Failed to load Chart.js.</p>';
|
||||
return;
|
||||
}
|
||||
panels.replaceChildren();
|
||||
|
||||
function chartColors() {
|
||||
const styles = getComputedStyle(root);
|
||||
return {
|
||||
text: styles.getPropertyValue('--chart-text').trim(),
|
||||
grid: styles.getPropertyValue('--chart-grid').trim()
|
||||
};
|
||||
}
|
||||
|
||||
function externalTooltipHandler(context) {
|
||||
const { chart, tooltip } = context;
|
||||
let tooltipElement = chart.canvas.parentNode.querySelector('.chart-tooltip');
|
||||
if (!tooltipElement) {
|
||||
tooltipElement = document.createElement('div');
|
||||
tooltipElement.className = 'chart-tooltip';
|
||||
chart.canvas.parentNode.appendChild(tooltipElement);
|
||||
}
|
||||
|
||||
if (tooltip.opacity === 0) {
|
||||
tooltipElement.style.opacity = '0';
|
||||
return;
|
||||
}
|
||||
|
||||
tooltipElement.replaceChildren();
|
||||
const title = document.createElement('div');
|
||||
title.className = 'chart-tooltip-title';
|
||||
const row = chart._benchmarkRows[tooltip.dataPoints[0].dataIndex];
|
||||
title.textContent = `${new Date(row.date).toLocaleString()} · ${row.sha}`;
|
||||
tooltipElement.appendChild(title);
|
||||
if (row.message) {
|
||||
const message = document.createElement('div');
|
||||
message.className = 'chart-tooltip-message';
|
||||
message.textContent = row.message;
|
||||
tooltipElement.appendChild(message);
|
||||
}
|
||||
const bodyElement = document.createElement('div');
|
||||
bodyElement.className = 'chart-tooltip-body';
|
||||
for (const body of [...tooltip.dataPoints].sort((a, b) => b.parsed.y - a.parsed.y)) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'chart-tooltip-row';
|
||||
const swatch = document.createElement('span');
|
||||
swatch.className = 'chart-tooltip-swatch';
|
||||
swatch.style.background = body.dataset.borderColor;
|
||||
const value = document.createElement('span');
|
||||
value.textContent = `${body.dataset.label}: ${body.formattedValue} ms`;
|
||||
item.append(swatch, value);
|
||||
bodyElement.appendChild(item);
|
||||
}
|
||||
tooltipElement.appendChild(bodyElement);
|
||||
|
||||
const canvasRect = chart.canvas.getBoundingClientRect();
|
||||
const margin = 12;
|
||||
let left = canvasRect.left + tooltip.caretX + margin;
|
||||
let top = canvasRect.top + tooltip.caretY + margin;
|
||||
tooltipElement.style.left = `${left}px`;
|
||||
tooltipElement.style.top = `${top}px`;
|
||||
tooltipElement.style.opacity = '0';
|
||||
const tooltipRect = tooltipElement.getBoundingClientRect();
|
||||
if (left + tooltipRect.width > window.innerWidth - margin) left = canvasRect.left + tooltip.caretX - tooltipRect.width - margin;
|
||||
if (top + tooltipRect.height > window.innerHeight - margin) top = canvasRect.top + tooltip.caretY - tooltipRect.height - margin;
|
||||
tooltipElement.style.left = `${Math.max(margin, left)}px`;
|
||||
tooltipElement.style.top = `${Math.max(margin, top)}px`;
|
||||
tooltipElement.style.opacity = '1';
|
||||
}
|
||||
|
||||
function updateTheme() {
|
||||
root.dataset.theme = theme;
|
||||
themeToggle.textContent = theme === 'dark' ? '☀ Light mode' : '☾ Dark mode';
|
||||
themeToggle.setAttribute('aria-label', `Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`);
|
||||
const colors = chartColors();
|
||||
Chart.defaults.color = colors.text;
|
||||
Chart.defaults.borderColor = colors.grid;
|
||||
for (const chart of chartsByInstance) {
|
||||
chart.options.scales.x.title.color = colors.text;
|
||||
chart.options.scales.x.ticks.color = colors.text;
|
||||
chart.options.scales.x.grid.color = colors.grid;
|
||||
chart.options.scales.y.title.color = colors.text;
|
||||
chart.options.scales.y.ticks.color = colors.text;
|
||||
chart.options.scales.y.grid.color = colors.grid;
|
||||
chart.update();
|
||||
}
|
||||
}
|
||||
themeToggle.addEventListener('click', () => {
|
||||
theme = theme === 'dark' ? 'light' : 'dark';
|
||||
localStorage.setItem('fastfetch-benchmark-theme', theme);
|
||||
updateTheme();
|
||||
});
|
||||
updateTheme();
|
||||
const families = new Map();
|
||||
|
||||
for (const [key, rows] of groups) {
|
||||
const arch = key.slice(key.lastIndexOf(' ') + 1);
|
||||
const family = architectureFamily(arch);
|
||||
if (!families.has(family)) families.set(family, []);
|
||||
families.get(family).push({ key, rows });
|
||||
}
|
||||
|
||||
// 'Other' is intentionally not rendered: it only catches stray series (e.g. the retired
|
||||
// 'macos universal' records) that should not show up as their own tab
|
||||
const familyOrder = ['Intel', 'ARM'];
|
||||
const availableFamilies = familyOrder.filter(family => families.has(family));
|
||||
|
||||
for (const [index, family] of availableFamilies.entries()) {
|
||||
const tab = document.createElement('button');
|
||||
const panelId = `panel-${family.toLowerCase()}`;
|
||||
tab.className = 'tab';
|
||||
tab.type = 'button';
|
||||
tab.id = `tab-${family.toLowerCase()}`;
|
||||
tab.setAttribute('role', 'tab');
|
||||
tab.textContent = family;
|
||||
tab.setAttribute('aria-controls', panelId);
|
||||
tab.setAttribute('aria-selected', index === 0 ? 'true' : 'false');
|
||||
tabs.appendChild(tab);
|
||||
|
||||
const panel = document.createElement('section');
|
||||
panel.className = 'tab-panel';
|
||||
panel.id = panelId;
|
||||
panel.setAttribute('role', 'tabpanel');
|
||||
panel.hidden = index !== 0;
|
||||
panel.setAttribute('aria-labelledby', tab.id);
|
||||
const title = document.createElement('h2');
|
||||
title.className = 'platform-title';
|
||||
title.textContent = `${family} processors`;
|
||||
panel.appendChild(title);
|
||||
const chartGrid = document.createElement('div');
|
||||
chartGrid.className = 'charts';
|
||||
panel.appendChild(chartGrid);
|
||||
panels.appendChild(panel);
|
||||
|
||||
tab.addEventListener('click', () => {
|
||||
for (const otherTab of tabs.children) otherTab.setAttribute('aria-selected', otherTab === tab ? 'true' : 'false');
|
||||
for (const otherPanel of panels.children) otherPanel.hidden = otherPanel !== panel;
|
||||
});
|
||||
|
||||
for (const { key, rows } of families.get(family).sort((a, b) => a.key.localeCompare(b.key))) {
|
||||
rows.sort((a, b) => a.date.localeCompare(b.date));
|
||||
const metrics = [...new Set(rows.flatMap(row => [...row.metrics.keys()]))].sort((a, b) => {
|
||||
if (a === 'total process time') return -1;
|
||||
if (b === 'total process time') return 1;
|
||||
return a.localeCompare(b);
|
||||
});
|
||||
const section = document.createElement('article');
|
||||
section.className = 'chart';
|
||||
const chartHeader = document.createElement('div');
|
||||
chartHeader.className = 'chart-header';
|
||||
const chartTitle = document.createElement('h2');
|
||||
chartTitle.textContent = key;
|
||||
const chartActions = document.createElement('div');
|
||||
chartActions.className = 'chart-actions';
|
||||
const chartHint = document.createElement('span');
|
||||
chartHint.textContent = `${rows.length} run${rows.length === 1 ? '' : 's'}`;
|
||||
const fullscreen = document.createElement('button');
|
||||
fullscreen.className = 'fullscreen';
|
||||
fullscreen.type = 'button';
|
||||
fullscreen.textContent = '⛶ Fullscreen';
|
||||
fullscreen.addEventListener('click', () => {
|
||||
if (document.fullscreenElement) document.exitFullscreen();
|
||||
else section.requestFullscreen();
|
||||
});
|
||||
chartActions.append(chartHint, fullscreen);
|
||||
chartHeader.append(chartTitle, chartActions);
|
||||
const canvasWrap = document.createElement('div');
|
||||
canvasWrap.className = 'canvas-wrap';
|
||||
const canvas = document.createElement('canvas');
|
||||
canvasWrap.appendChild(canvas);
|
||||
section.append(chartHeader, canvasWrap);
|
||||
chartGrid.appendChild(section);
|
||||
|
||||
const colorsForChart = chartColors();
|
||||
const chart = new Chart(canvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: rows.map(row => row.sha.slice(0, 7)),
|
||||
datasets: metrics.map((metric, index) => ({
|
||||
label: metric,
|
||||
data: rows.map(row => row.metrics.has(metric) ? row.metrics.get(metric) : null),
|
||||
borderColor: colors[index % colors.length],
|
||||
backgroundColor: colors[index % colors.length],
|
||||
pointRadius: 3,
|
||||
tension: 0.2,
|
||||
spanGaps: true,
|
||||
hidden: hiddenByDefault.has(metric)
|
||||
}))
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: { mode: 'index', intersect: false },
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
external: externalTooltipHandler,
|
||||
callbacks: {
|
||||
title: tooltipItems => {
|
||||
const row = rows[tooltipItems[0].dataIndex];
|
||||
return `${row.date} · ${row.sha}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: { title: { display: true, text: 'Date', color: colorsForChart.text }, ticks: { color: colorsForChart.text }, grid: { color: colorsForChart.grid } },
|
||||
y: { beginAtZero: true, title: { display: true, text: 'Milliseconds', color: colorsForChart.text }, ticks: { color: colorsForChart.text }, grid: { color: colorsForChart.grid } }
|
||||
}
|
||||
}
|
||||
});
|
||||
chart._benchmarkRows = rows;
|
||||
chartsByInstance.push(chart);
|
||||
const legendActions = document.createElement('div');
|
||||
legendActions.className = 'legend-actions';
|
||||
const legend = document.createElement('div');
|
||||
legend.className = 'legend';
|
||||
const legendItems = [];
|
||||
|
||||
function refreshLegend() {
|
||||
legendItems.forEach((item, metricIndex) => {
|
||||
item.classList.toggle('is-hidden', !chart.isDatasetVisible(metricIndex));
|
||||
});
|
||||
}
|
||||
|
||||
function setAllLegends(visible) {
|
||||
metrics.forEach((metric, metricIndex) => chart.setDatasetVisibility(metricIndex, visible));
|
||||
refreshLegend();
|
||||
chart.update();
|
||||
}
|
||||
|
||||
function addLegendAction(label, action) {
|
||||
const button = document.createElement('button');
|
||||
button.className = 'legend-action';
|
||||
button.type = 'button';
|
||||
button.textContent = label;
|
||||
button.addEventListener('click', action);
|
||||
legendActions.appendChild(button);
|
||||
}
|
||||
|
||||
addLegendAction('Show all', () => setAllLegends(true));
|
||||
addLegendAction('Toggle all', () => {
|
||||
metrics.forEach((metric, metricIndex) => chart.setDatasetVisibility(metricIndex, !chart.isDatasetVisible(metricIndex)));
|
||||
refreshLegend();
|
||||
chart.update();
|
||||
});
|
||||
addLegendAction('Reset', () => {
|
||||
metrics.forEach((metric, metricIndex) => chart.setDatasetVisibility(metricIndex, !hiddenByDefault.has(metric)));
|
||||
refreshLegend();
|
||||
chart.update();
|
||||
});
|
||||
|
||||
metrics.forEach((metric, metricIndex) => {
|
||||
const item = document.createElement('button');
|
||||
item.className = `legend-item${chart.isDatasetVisible(metricIndex) ? '' : ' is-hidden'}`;
|
||||
item.type = 'button';
|
||||
item.innerHTML = `<span class="legend-swatch" style="background:${colors[metricIndex % colors.length]}"></span>${metric}`;
|
||||
item.addEventListener('click', () => {
|
||||
chart.setDatasetVisibility(metricIndex, !chart.isDatasetVisible(metricIndex));
|
||||
item.classList.toggle('is-hidden', !chart.isDatasetVisible(metricIndex));
|
||||
chart.update();
|
||||
});
|
||||
legendItems.push(item);
|
||||
legend.appendChild(item);
|
||||
});
|
||||
section.append(legendActions, legend);
|
||||
}
|
||||
}
|
||||
|
||||
if (!availableFamilies.length) {
|
||||
tabs.hidden = true;
|
||||
panels.innerHTML = '<p class="empty">No benchmark data available.</p>';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,18 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "sunday"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "CI"
|
||||
labels:
|
||||
- "ci"
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
@@ -21,6 +21,10 @@ Closes #
|
||||
|
||||
-
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!-- Required if this PR introduces visual changes. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have tested my changes locally.
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
name: Reusable Android
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
abi:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
# The NDK comes with the runner image, so nothing is downloaded here. The image is pinned to
|
||||
# ubuntu-24.04 rather than ubuntu-latest because which NDK a build used is part of what it is.
|
||||
#
|
||||
# The image's *latest* NDK, not its default one: the default is r27d (clang 18), while fastfetch
|
||||
# is built and shipped on Termux with clang 21. Testing three majors behind the compiler the
|
||||
# supported path uses buys nothing and rejects code clang 21 accepts.
|
||||
- name: print NDK version
|
||||
run: |
|
||||
echo "default: ${ANDROID_NDK_HOME}"
|
||||
echo "latest: ${ANDROID_NDK_LATEST_HOME}"
|
||||
test -f "${ANDROID_NDK_LATEST_HOME}/build/cmake/android.toolchain.cmake"
|
||||
cat "${ANDROID_NDK_LATEST_HOME}/source.properties"
|
||||
"${ANDROID_NDK_LATEST_HOME}"/toolchains/llvm/prebuilt/*/bin/clang --version
|
||||
|
||||
# freetype is the one library this build wants that the NDK does not ship: its headers are not
|
||||
# in the sysroot and the runner image has no libfreetype-dev, so without this step
|
||||
# ff_lib_enable(FREETYPE ...) prints "Library: missing: FREETYPE" and
|
||||
# src/detection/terminalfont/terminalfont_android.c compiles only its #else branch. That is the
|
||||
# branch the shipped Termux build never takes, so the freetype code would rot untested here.
|
||||
#
|
||||
# Handing it the host's copy is sound, for two reasons. fastfetch never links freetype --
|
||||
# BINARY_LINK_TYPE is dlopen, so ff_lib_enable skips its target_link_libraries and only the
|
||||
# include path and FF_HAVE_FREETYPE are needed. And freetype's public headers carry no host
|
||||
# assumptions: FT_SIZEOF_LONG and FT_SIZEOF_INT in freetype/config/integer-types.h are derived
|
||||
# from FT_ULONG_MAX/FT_UINT_MAX, which come from the *target's* <limits.h>, so the same copy
|
||||
# describes FT_FaceRec -- and the offset of the family_name that terminalfont_android.c reads --
|
||||
# correctly for aarch64, armv7 and x86_64 alike.
|
||||
#
|
||||
# pkg-config is the only route that can find it: the NDK toolchain leaves PKG_CONFIG_PATH,
|
||||
# PKG_CONFIG_LIBDIR and PKG_CONFIG_SYSROOT_DIR untouched, so the ordinary host search path is
|
||||
# what gets used.
|
||||
#
|
||||
# The .pc is written here rather than taken from libfreetype-dev, whose own copy carries
|
||||
# `Requires.private: zlib, bzip2, libpng, harfbuzz, libbrotlidec`. pkgconf -- which is what
|
||||
# `pkg-config` has been on Ubuntu since 22.10 -- fails outright on a private requirement it
|
||||
# cannot resolve, even for --exists, and libfreetype-dev only pulls in libpng-dev and
|
||||
# zlib1g-dev. Nothing is linked, so the include directory is the whole contract. A Version
|
||||
# field is mandatory (pkgconf treats a .pc without one as not found), hence the sed.
|
||||
- name: install freetype headers
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y --no-install-recommends libfreetype-dev
|
||||
test -f /usr/include/freetype2/ft2build.h
|
||||
VERSION=$(awk '/^#define FREETYPE_(MAJOR|MINOR|PATCH)/ {printf "%s%s", sep, $3; sep="."}' /usr/include/freetype2/freetype/freetype.h)
|
||||
test -n "${VERSION}"
|
||||
mkdir -p freetype-pc
|
||||
cat > freetype-pc/freetype2.pc <<'EOF'
|
||||
prefix=/usr
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: FreeType 2
|
||||
Description: A free, high-quality, and portable font engine.
|
||||
Version: 0
|
||||
Cflags: -I${includedir}/freetype2
|
||||
EOF
|
||||
sed -i "s/^Version: .*/Version: ${VERSION}/" freetype-pc/freetype2.pc
|
||||
PKG_CONFIG_PATH="$PWD/freetype-pc" pkg-config --cflags freetype2
|
||||
|
||||
# android-24 (Android 7.0) is Termux's own floor, and it is the floor for a second, independent
|
||||
# reason: libcamera2ndk.so only exists from API 24, fastfetch links it unconditionally, and the
|
||||
# NDK has no such thing as a weak *library* reference -- a build aimed lower would fail to load
|
||||
# on exactly the devices it was aimed at. The level is also what makes this job check anything:
|
||||
# at 24 the NDK marks AImageDecoder_* / AndroidBitmap_compress (30) and AMediaCodec_getName /
|
||||
# AMediaCodec_releaseName (28) unavailable, which is what CMakeLists.txt's
|
||||
# -D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ -Werror=unguarded-availability pair is built on.
|
||||
# Building against a newer platform would quietly stop enforcing it.
|
||||
#
|
||||
# BUILD_TESTS stays off. The test binaries are host programs, there is no device here to run
|
||||
# them on, and `ctest` therefore cannot follow -- unlike every other job in this workflow set.
|
||||
# The platform branch of the build is what this job covers.
|
||||
- name: configure project
|
||||
env:
|
||||
# Picks up the freetype2.pc written by the step above. The rest of the search path is left
|
||||
# alone, so zlib and every other host .pc file is still found exactly as before.
|
||||
PKG_CONFIG_PATH: ${{ github.workspace }}/freetype-pc
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=Off -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_LATEST_HOME}/build/cmake/android.toolchain.cmake" -DANDROID_ABI=${{ inputs.abi }} -DANDROID_PLATFORM=android-24 -DCMAKE_INSTALL_PREFIX=/data/data/com.termux/files/usr .
|
||||
|
||||
# A silent regression back to "Library: missing: FREETYPE" would look exactly like a passing
|
||||
# build -- the module would simply stop being compiled, and the only trace would be a line in
|
||||
# the configure log nobody diffs. Assert that the define actually reached the compile line.
|
||||
- name: check freetype was detected
|
||||
run: |
|
||||
grep -q -- '-DFF_HAVE_FREETYPE=1' CMakeFiles/libfastfetch.dir/flags.make || {
|
||||
echo "::error::freetype was not detected, so terminalfont_android.c compiled its #else branch only"
|
||||
grep -o -- '-DFF_HAVE_[A-Z0-9_]*' CMakeFiles/libfastfetch.dir/flags.make | sort -u
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: print ELF header
|
||||
run: readelf --wide -h fastfetch
|
||||
|
||||
- name: print dynamic dependencies
|
||||
run: readelf --wide -d fastfetch
|
||||
|
||||
# libjnigraphics.so, libmediandk.so and libcamera2ndk.so are what provide the entry points
|
||||
# CMakeLists.txt weakens. A weak reference resolves to null instead of failing the load, and
|
||||
# FF_ANDROID_API_AT_LEAST() reads the platform level rather than the symbol -- so a library
|
||||
# dropped from the link line still passes the guard on a device new enough, and the call goes
|
||||
# through a null pointer. DT_NEEDED is the only place that mistake is visible.
|
||||
- name: check linked libraries
|
||||
run: |
|
||||
for lib in libjnigraphics.so libmediandk.so libcamera2ndk.so; do
|
||||
readelf --wide -d fastfetch | grep -q "NEEDED.*\[${lib}\]" || { echo "::error::${lib} is not in DT_NEEDED"; exit 1; }
|
||||
done
|
||||
|
||||
# Every reference to an entry point the NDK marks unavailable at API 24 has to be weak: a strong
|
||||
# one turns a device below API 28/30 into a load failure instead of a graceful downgrade, which
|
||||
# is the entire reason those three files are compiled with -D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__.
|
||||
# `nm -D` prints a weak undefined symbol as `w` and a strong undefined one as `U`.
|
||||
#
|
||||
# The families are whole prefixes on purpose: every AImageDecoder* entry point is API 30 or 31,
|
||||
# but the frame-info and header-info ones spell the prefix AImageDecoderFrameInfo / HeaderInfo
|
||||
# without an underscore, and matching only `AImageDecoder_` would leave them unchecked. The
|
||||
# AMediaCodec ones are named one by one because the rest of that API -- createDecoderByType,
|
||||
# createEncoderByType, delete -- is API 21 and is correctly a strong reference.
|
||||
- name: check weak references
|
||||
run: |
|
||||
nm -D fastfetch | grep -E ' (AImageDecoder|AndroidBitmap_compress|AMediaCodec_getName$|AMediaCodec_releaseName$)' > weak.txt
|
||||
cat weak.txt
|
||||
test -s weak.txt || { echo "::error::none of these entry points is referenced any more -- if the last use is gone on purpose, this check and the -Werror=unguarded-availability pair it guards should go with it"; exit 1; }
|
||||
if grep -qv '^ *w ' weak.txt; then
|
||||
echo "::error::expected every one of these to be a weak reference:"
|
||||
grep -v '^ *w ' weak.txt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: print size
|
||||
run: |
|
||||
file fastfetch
|
||||
ls -l fastfetch
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-android-${{ inputs.abi }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Reusable Benchmark
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: benchmark
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: download benchmark data
|
||||
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
with:
|
||||
pattern: benchmark-*
|
||||
path: benchmarks
|
||||
|
||||
- name: merge benchmark data
|
||||
run: jq -s 'add' benchmarks/*/benchmark.json > benchmark.json
|
||||
|
||||
- name: save benchmark dashboard
|
||||
run: cp .github/benchmark-index.html "$RUNNER_TEMP/benchmark-index.html"
|
||||
|
||||
- name: store benchmark result
|
||||
uses: benchmark-action/github-action-benchmark@4322e5726e6334590d251fc4f92bec0efafc45dc # v1.22.2
|
||||
with:
|
||||
name: Fastfetch
|
||||
tool: customSmallerIsBetter
|
||||
output-file-path: benchmark.json
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
ref: ${{ github.sha }}
|
||||
auto-push: true
|
||||
summary-always: true
|
||||
benchmark-data-dir-path: dev/bench
|
||||
|
||||
- name: publish benchmark dashboard
|
||||
run: |
|
||||
git fetch origin gh-pages
|
||||
git switch --force-create gh-pages FETCH_HEAD
|
||||
mkdir -p dev/bench
|
||||
cp "$RUNNER_TEMP/benchmark-index.html" dev/bench/index.html
|
||||
git config user.name github-action-benchmark
|
||||
git config user.email github@users.noreply.github.com
|
||||
git add dev/bench/index.html
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "Update benchmark dashboard"
|
||||
git push origin gh-pages
|
||||
fi
|
||||
@@ -0,0 +1,66 @@
|
||||
name: Reusable DragonFly amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: cpa.sh {0}
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
||||
with:
|
||||
operating_system: dragonflybsd
|
||||
version: '6.4.2'
|
||||
shell: bash
|
||||
cpu_count: 4
|
||||
|
||||
- name: Show system info
|
||||
run: |
|
||||
uname -a
|
||||
pkg --version
|
||||
|
||||
- name: Update and install dependencies
|
||||
run: |
|
||||
sudo pkg update
|
||||
sudo 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 lua54 libva libvdpau
|
||||
|
||||
- name: CMake configuration
|
||||
run: env CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
|
||||
- name: CMake build
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: List features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: Run normal
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: Run json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: Run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: Run ldd
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-dragonfly-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,67 @@
|
||||
name: Reusable FreeBSD amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: cpa.sh {0}
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
||||
with:
|
||||
operating_system: freebsd
|
||||
version: '15.1'
|
||||
shell: bash
|
||||
cpu_count: 4
|
||||
|
||||
- name: Show system info
|
||||
run: |
|
||||
uname -a
|
||||
freebsd-version
|
||||
pkg -v
|
||||
|
||||
- name: Update and install dependencies
|
||||
run: |
|
||||
sudo pkg update
|
||||
sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3 egl opencl ocl-icd v4l_compat chafa lua54 libva libvdpau
|
||||
|
||||
- name: CMake configuration
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
|
||||
- name: CMake build
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: List features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: Run normal
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: Run json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: Run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: Run ldd
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-freebsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,59 @@
|
||||
name: Reusable Haiku amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: cpa.sh {0}
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
||||
with:
|
||||
operating_system: haiku
|
||||
version: 'r1beta6'
|
||||
shell: bash
|
||||
cpu_count: 4
|
||||
|
||||
- name: Show system info
|
||||
run: uname -a
|
||||
|
||||
- name: Install dependencies
|
||||
run: pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake llvm22_clang ninja pkgconfig python3.10 lua || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake llvm22_clang ninja pkgconfig python3.10 lua
|
||||
|
||||
- name: CMake configuration
|
||||
run: CC=clang CXX=clang++ cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} .
|
||||
|
||||
- name: CMake build
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: List features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: Run normal
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: Run json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: Run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-haiku-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,47 @@
|
||||
name: Reusable Linux armv7l
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@fa1f3e7de95534497266c7265950b5188355fbfb # v3.2.1
|
||||
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 libdrm-dev libelf-dev libefl-all-dev liblua5.4-dev libvdpau-dev libva-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-linux-armv7l
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,135 @@
|
||||
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 }}
|
||||
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
|
||||
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- 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 jq 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 libdrm-dev libelf-dev libefl-all-dev libunwind-dev liblua5.4-dev libvdpau-dev libva-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 quickjs-ng --ignore-dependencies
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: inputs.arch == 'amd64'
|
||||
uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
|
||||
with:
|
||||
languages: c
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 CMAKE_PREFIX_PATH=/home/linuxbrew/.linuxbrew 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 -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: perform CodeQL analysis
|
||||
if: inputs.arch == 'amd64'
|
||||
uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
|
||||
|
||||
- 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 -p ./fastfetch -c presets/ci.jsonc --format json > fastfetch.raw.json 2> fastfetch.error; } 2> fastfetch.time
|
||||
sed -n '/^[[:space:]]*\[/,$p' fastfetch.raw.json > fastfetch.json
|
||||
cat fastfetch.json
|
||||
cat fastfetch.error >&2
|
||||
cat fastfetch.time >&2
|
||||
|
||||
- name: prepare benchmark data
|
||||
run: |
|
||||
real_seconds=$(awk '$1 == "real" { print $2 }' fastfetch.time)
|
||||
jq -e 'type == "array"' fastfetch.json > /dev/null
|
||||
jq --arg platform "linux" --arg arch "${{ inputs.arch }}" --argjson real_seconds "$real_seconds" '
|
||||
map(select(.stat | type == "number") | {
|
||||
name: ("fastfetch " + $platform + " " + $arch + " module " + .type),
|
||||
unit: "ms",
|
||||
value: .stat
|
||||
}) + [{
|
||||
name: ("fastfetch " + $platform + " " + $arch + " total process time"),
|
||||
unit: "ms",
|
||||
value: ($real_seconds * 1000)
|
||||
}]
|
||||
' fastfetch.json > benchmark.json
|
||||
cat benchmark.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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-linux-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
- name: upload benchmark data
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: benchmark-linux-${{ inputs.arch }}
|
||||
path: benchmark.json
|
||||
@@ -0,0 +1,73 @@
|
||||
name: Reusable Linux i686
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- 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 libdrm-dev libelf-dev libefl-all-dev libunwind-dev liblua5.4-dev libvdpau-dev libva-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 quickjs-ng --ignore-dependencies
|
||||
|
||||
- name: cmake version
|
||||
run: cmake --version
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 CMAKE_PREFIX_PATH=/home/linuxbrew/.linuxbrew PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686 -fno-pie" -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_VERSION=5.10.0 -DCMAKE_SYSTEM_PROCESSOR=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 -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
|
||||
- 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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-linux-i686
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Reusable Linux loong64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: set up QEMU for loong64
|
||||
uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
|
||||
|
||||
- name: build in loong64 container
|
||||
run: |
|
||||
docker run --rm --platform linux/loong64 \
|
||||
-v ${{ github.workspace }}:/workspace \
|
||||
-w /workspace \
|
||||
lcr.loongnix.cn/debian:14 \
|
||||
bash -c '
|
||||
set -e
|
||||
uname -a
|
||||
cat /etc/os-release || true
|
||||
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
cmake make gcc 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 \
|
||||
libdrm-dev \
|
||||
libelf-dev libefl-all-dev \
|
||||
liblua5.4-dev \
|
||||
libvdpau-dev libva-dev \
|
||||
rpm
|
||||
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
cmake --build . --target package --verbose -j$(nproc)
|
||||
./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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-linux-loong64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,48 @@
|
||||
name: Reusable Linux VMs
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@fa1f3e7de95534497266c7265950b5188355fbfb # v3.2.1
|
||||
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 libdrm-dev libchafa-dev libelf-dev libefl-all-dev liblua5.4-dev libvdpau-dev libva-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-linux-${{ inputs.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,86 @@
|
||||
name: Reusable macOS Universal
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: install required packages with Homebrew
|
||||
run: |
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew update
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk chafa lua jq
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: verify universal binary
|
||||
run: lipo -archs fastfetch
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan:bluetooth:bluetoothradio --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: |
|
||||
{ time -p ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan:bluetooth:bluetoothradio --format json > fastfetch.raw.json 2> fastfetch.error; } 2> fastfetch.time
|
||||
sed -n '/^[[:space:]]*\[/,$p' fastfetch.raw.json > fastfetch.json
|
||||
cat fastfetch.json
|
||||
cat fastfetch.error >&2
|
||||
cat fastfetch.time >&2
|
||||
|
||||
- name: prepare benchmark data
|
||||
run: |
|
||||
real_seconds=$(awk '$1 == "real" { print $2 }' fastfetch.time)
|
||||
jq -e 'type == "array"' fastfetch.json > /dev/null
|
||||
jq --arg platform "macos" --arg arch "aarch64" --argjson real_seconds "$real_seconds" '
|
||||
map(select(.stat | type == "number") | {
|
||||
name: ("fastfetch " + $platform + " " + $arch + " module " + .type),
|
||||
unit: "ms",
|
||||
value: .stat
|
||||
}) + [{
|
||||
name: ("fastfetch " + $platform + " " + $arch + " total process time"),
|
||||
unit: "ms",
|
||||
value: ($real_seconds * 1000)
|
||||
}]
|
||||
' fastfetch.json > benchmark.json
|
||||
cat benchmark.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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-macos-universal
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
- name: upload benchmark data
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: benchmark-macos-universal
|
||||
path: benchmark.json
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Reusable Musl amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: setup alpine linux
|
||||
uses: jirutka/setup-alpine@ae3b3ddba35054804fc4a3507b519fa7e8152050 # v1.4.1
|
||||
with:
|
||||
branch: edge
|
||||
|
||||
- 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 elfutils-dev lua5.4-dev quickjs-ng-dev libvdpau-dev libva-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 -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" -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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-musl-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Reusable NetBSD amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: cpa.sh {0}
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
||||
with:
|
||||
operating_system: netbsd
|
||||
version: '10.1'
|
||||
shell: bash
|
||||
cpu_count: 4
|
||||
|
||||
- name: Show system info
|
||||
run: |
|
||||
uname -a
|
||||
pkgin -v
|
||||
|
||||
- name: Update and install dependencies
|
||||
run: |
|
||||
sudo pkgin -y install clang cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick libva libvdpau
|
||||
|
||||
- name: CMake configuration
|
||||
run: CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
|
||||
- name: CMake build
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: List features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: Run normal
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: Run json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: Run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: Run ldd
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-netbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,45 @@
|
||||
name: Reusable No Features Test
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" . -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_ELF=OFF -DENABLE_EET=OFF -DENABLE_THREADS=OFF -DENABLE_IMAGE_LOGO=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,70 @@
|
||||
name: Reusable OmniOS amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: cpa.sh {0}
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
||||
with:
|
||||
operating_system: omnios
|
||||
version: 'r151058'
|
||||
shell: bash
|
||||
cpu_count: 4
|
||||
|
||||
- name: Show system info
|
||||
run: |
|
||||
uname -a
|
||||
|
||||
- name: Update and install dependencies
|
||||
run: |
|
||||
sudo pkg update --accept || true
|
||||
sudo pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja
|
||||
|
||||
- name: CMake configuration
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
|
||||
- name: CMake build
|
||||
run: cmake --build . --verbose -j4
|
||||
|
||||
- name: List features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: Run normal
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: Run json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: Run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: Run ldd
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-omnios-amd64")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-omnios-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Reusable OpenBSD amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: cpa.sh {0}
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@e0b9770014ba65d5e0815f15b74031c3a635f641 # v1.6.0
|
||||
with:
|
||||
operating_system: openbsd
|
||||
version: '7.9'
|
||||
shell: bash
|
||||
cpu_count: 4
|
||||
|
||||
- name: Show system info
|
||||
run: |
|
||||
uname -a
|
||||
|
||||
- name: Update and install dependencies
|
||||
run: |
|
||||
sudo pkg_add -u
|
||||
sudo pkg_add -r llvm-21.1.8p4 cmake git wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 imagemagick chafa lua-5.4.8p0 # pkg-config is preinstalled
|
||||
|
||||
- name: CMake configuration
|
||||
run: CC=clang-21 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
|
||||
- name: CMake build
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: List features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
- name: Run normal
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: Run json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: Run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: Run ldd
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-openbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Reusable Release
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ffversion:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: get latest release version
|
||||
id: get_version_release
|
||||
uses: pozetroninc/github-action-get-latest-release@2a61c339ea7ef0a336d1daa35ef0cb1418e7676c # v0.8.0
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
|
||||
- name: download artifacts
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
|
||||
- name: create release
|
||||
if: inputs.ffversion != steps.get_version_release.outputs.release
|
||||
uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0
|
||||
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@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0
|
||||
with:
|
||||
tag: ${{ inputs.ffversion }}
|
||||
commit: ${{ github.sha }}
|
||||
bodyFile: fastfetch-release-notes.md
|
||||
allowUpdates: true
|
||||
@@ -0,0 +1,46 @@
|
||||
name: Reusable Solaris amd64
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/solaris-vm@87d3c436511cef802cd1637f86f73b1a97715c8c # v1.4.1
|
||||
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 lua
|
||||
|
||||
run: |
|
||||
export PKG_CONFIG_PATH=/usr/lib/64/pkgconfig
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja -DCMAKE_C_FLAGS="-fno-pie" -DCMAKE_EXE_LINKER_FLAGS="-no-pie" .
|
||||
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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-solaris-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -0,0 +1,147 @@
|
||||
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
|
||||
secrets:
|
||||
SIGNPATH_API_TOKEN:
|
||||
description: SignPath REST API token used to submit the signing request
|
||||
# Optional so that runs without the secret (pull requests, forks)
|
||||
# still work; the signing step is skipped there anyway.
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# The SignPath action reads job details and downloads the unsigned
|
||||
# artifact with the workflow token.
|
||||
actions: read
|
||||
|
||||
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: setup-msys2
|
||||
uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2.33.0
|
||||
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 }}-jq 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 }}-chafa mingw-w64-clang-${{ inputs.msys-arch }}-lua 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/lua55.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 -p ./fastfetch -c presets/ci.jsonc --format json > fastfetch.raw.json 2> fastfetch.error; } 2> fastfetch.time
|
||||
sed -n '/^[[:space:]]*\[/,$p' fastfetch.raw.json > fastfetch.json
|
||||
cat fastfetch.json
|
||||
cat fastfetch.error >&2
|
||||
cat fastfetch.time >&2
|
||||
|
||||
- name: prepare benchmark data
|
||||
run: |
|
||||
real_seconds=$(awk '$1 == "real" { print $2 }' fastfetch.time)
|
||||
jq -e 'type == "array"' fastfetch.json > /dev/null
|
||||
jq --arg platform "windows" --arg arch "${{ inputs.arch }}" --argjson real_seconds "$real_seconds" '
|
||||
map(select(.stat | type == "number") | {
|
||||
name: ("fastfetch " + $platform + " " + $arch + " module " + .type),
|
||||
unit: "ms",
|
||||
value: .stat
|
||||
}) + [{
|
||||
name: ("fastfetch " + $platform + " " + $arch + " total process time"),
|
||||
unit: "ms",
|
||||
value: ($real_seconds * 1000)
|
||||
}]
|
||||
' fastfetch.json > benchmark.json
|
||||
cat benchmark.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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
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
|
||||
# https://github.com/SignPath/github-action-submit-signing-request/issues/18
|
||||
uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2.3
|
||||
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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: fastfetch-windows-${{ inputs.arch }}
|
||||
path: ./fastfetch-windows-${{ inputs.arch }}.*
|
||||
overwrite: true
|
||||
|
||||
- name: upload benchmark data
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: benchmark-windows-${{ inputs.arch }}
|
||||
path: benchmark.json
|
||||
+92
-705
@@ -4,66 +4,16 @@ 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
|
||||
|
||||
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
|
||||
|
||||
linux-hosts:
|
||||
needs: no-features-test
|
||||
name: Linux-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
@@ -74,235 +24,36 @@ 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
|
||||
|
||||
- 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 g++-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 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: CC=gcc-13 CXX=g++-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: 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
|
||||
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@v4
|
||||
with:
|
||||
name: fastfetch-linux-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-hosts.yml
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
|
||||
linux-i686:
|
||||
needs: no-features-test
|
||||
name: Linux-i686
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- 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 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 -DENABLE_DIRECTX_HEADERS=Off .
|
||||
|
||||
- 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@v4
|
||||
with:
|
||||
name: fastfetch-linux-i686
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-linux-i686.yml
|
||||
|
||||
linux-armv7l:
|
||||
needs: no-features-test
|
||||
name: Linux-armv7l
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: ./.github/workflows/build-linux-armv7l.yml
|
||||
|
||||
- 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 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/ 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 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 rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -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-*.*
|
||||
|
||||
linux-armv6l:
|
||||
name: Linux-armv6l
|
||||
runs-on: ubuntu-24.04
|
||||
linux-loong64:
|
||||
needs: no-features-test
|
||||
name: Linux-loong64
|
||||
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 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 rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -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-loong64.yml
|
||||
|
||||
linux-vms:
|
||||
needs: no-features-test
|
||||
name: Linux-${{ matrix.arch }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -310,348 +61,96 @@ 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: 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 rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -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 }}
|
||||
|
||||
musl-amd64:
|
||||
needs: no-features-test
|
||||
name: Musl-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-musl-amd64.yml
|
||||
|
||||
- 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-*.*
|
||||
android:
|
||||
needs: no-features-test
|
||||
name: Android-${{ matrix.abi }}
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
abi:
|
||||
- arm64-v8a
|
||||
- armeabi-v7a
|
||||
- x86_64
|
||||
uses: ./.github/workflows/build-android.yml
|
||||
with:
|
||||
abi: ${{ matrix.abi }}
|
||||
|
||||
macos-hosts:
|
||||
name: macOS-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
needs: no-features-test
|
||||
name: macOS-universal
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
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
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
omnios-amd64:
|
||||
needs: no-features-test
|
||||
name: OmniOS-amd64
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-omnios-amd64.yml
|
||||
|
||||
- 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 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:
|
||||
needs: no-features-test
|
||||
name: Solaris-amd64
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build-solaris-amd64.yml
|
||||
|
||||
freebsd-amd64:
|
||||
needs: no-features-test
|
||||
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
|
||||
|
||||
openbsd-amd64:
|
||||
needs: no-features-test
|
||||
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.8'
|
||||
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@v4
|
||||
with:
|
||||
name: fastfetch-openbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-openbsd-amd64.yml
|
||||
|
||||
netbsd-amd64:
|
||||
needs: no-features-test
|
||||
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 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@v4
|
||||
with:
|
||||
name: fastfetch-netbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-netbsd-amd64.yml
|
||||
|
||||
dragonfly-amd64:
|
||||
needs: no-features-test
|
||||
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 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@v4
|
||||
with:
|
||||
name: fastfetch-dragonfly-amd64
|
||||
path: ./fastfetch-*.*
|
||||
uses: ./.github/workflows/build-dragonfly-amd64.yml
|
||||
|
||||
haiku-amd64:
|
||||
needs: no-features-test
|
||||
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
|
||||
|
||||
windows-hosts:
|
||||
name: Windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
needs: no-features-test
|
||||
name: Windows-${{ matrix.arch }}
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
# The SignPath action reads job details and downloads the unsigned
|
||||
# artifact with the workflow token.
|
||||
actions: read
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -660,166 +159,54 @@ jobs:
|
||||
msystem: CLANG64
|
||||
msystem-lower: clang64
|
||||
msys-arch: x86_64
|
||||
win7-compat-flag: OFF
|
||||
win7-compat-postfix: ""
|
||||
- arch: amd64
|
||||
runs-on: windows-latest
|
||||
msystem: CLANG64
|
||||
msystem-lower: clang64
|
||||
msys-arch: x86_64
|
||||
win7-compat-flag: ON
|
||||
win7-compat-postfix: "-win7"
|
||||
- arch: aarch64
|
||||
runs-on: windows-11-arm
|
||||
msystem: CLANGARM64
|
||||
msystem-lower: clangarm64
|
||||
msys-arch: aarch64
|
||||
win7-compat-flag: OFF
|
||||
win7-compat-postfix: ""
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
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:
|
||||
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
|
||||
- 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 -DENABLE_WIN7_COMPAT=${{ matrix.win7-compat-flag }} .
|
||||
|
||||
- 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
|
||||
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
id: upload-unsigned-artifact
|
||||
name: upload artifacts for signing
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
|
||||
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-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: create 7z archive
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: upload true artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
|
||||
path: ./fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.*
|
||||
overwrite: true
|
||||
benchmark:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/dev' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
name: Benchmark
|
||||
needs:
|
||||
- linux-hosts
|
||||
- macos-hosts
|
||||
- windows-hosts
|
||||
permissions:
|
||||
contents: write
|
||||
uses: ./.github/workflows/build-benchmark.yml
|
||||
|
||||
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-loong64
|
||||
- linux-vms
|
||||
- musl-amd64
|
||||
- android
|
||||
- macos-hosts
|
||||
- freebsd-amd64
|
||||
- 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 }}
|
||||
|
||||
+12
-5
@@ -1,8 +1,10 @@
|
||||
build/
|
||||
.vs/
|
||||
.vscode/
|
||||
.cache/
|
||||
.kdev4/
|
||||
/build/
|
||||
/build_*/
|
||||
/build-*/
|
||||
/.vs/
|
||||
/.vscode/
|
||||
/.cache/
|
||||
/.kdev4/
|
||||
.DS_Store
|
||||
cscope.*
|
||||
tags
|
||||
@@ -10,3 +12,8 @@ fastfetch.kdev4
|
||||
*.user
|
||||
*.user.*
|
||||
*.swp
|
||||
*.log
|
||||
/*.zh.md
|
||||
/.work*
|
||||
/doc/*.md
|
||||
/fastfetch.wiki
|
||||
|
||||
+594
-5
@@ -1,3 +1,592 @@
|
||||
# Unreleased
|
||||
|
||||
Bugfixes:
|
||||
* Improved uptime detection on SunOS via kstat. (#2674, Uptime, SunOS)
|
||||
|
||||
# 2.70.0
|
||||
|
||||
Changes:
|
||||
* LocalIP flags on Windows now describe the interface itself — its operational status, its media connection state and its type, for example `ETHERNET_CSMACD`, `IEEE80211` or `SOFTWARE_LOOPBACK` — instead of the `IP_ADAPTER_*` bits.
|
||||
* Interfaces that carry no unicast address are no longer reported, since both the addresses and the interface properties are now enumerated from the unicast address table. (LocalIP, Windows)
|
||||
* libpulse and libddcutil are no longer used on Linux. Package managers should remove them from their fastfetch package.
|
||||
* QuickJS format scripting has been removed (deprecated in v2.69.0). Migrate scripts to Lua if necessary. (General)
|
||||
* The Command module option `useStdErr` has been replaced with `useOutput`, which selects which of the command's output streams to capture: `"stdout"`, `"stderr"` or `"both"`.
|
||||
* It defaults to `"both"`, so a command that logs to stderr now has that output captured instead of discarded.
|
||||
* Set it to `"stdout"` or `"stderr"` to restore the old single-stream behavior. (Command)
|
||||
* The Shell module now reports the shell from the password database (`/etc/passwd`) instead of the `$SHELL` environment variable, which may differ when the login shell was changed after the session started. (Title)
|
||||
* The Disk custom format no longer provides `{size-available}`. `{size-free}` now reports whichever of the two the `disk.useAvailable` option selects, and the raw byte counts are available as `{bytes-used}` and `{bytes-total}`. (Disk)
|
||||
* This also means `{size-free}` now changes with `disk.useAvailable`; it used to always report the file system's free size.
|
||||
* The Disk JSON output no longer includes `bytes.available` and `bytes.used`. (Disk)
|
||||
* `bytes.free` and `bytes.total` are unchanged; the used size can be derived as `total - free`.
|
||||
|
||||
Features:
|
||||
* Added Sound detection on Android. (Sound, Android)
|
||||
* Added display detection on Android over binder, which needs neither a child process nor a permission. (Display, Android)
|
||||
* Added battery temperature, remaining time and charger type (AC / USB / wireless) detection on Android. (Battery, Android)
|
||||
* Added media detection on Android when fastfetch runs as an app UID. (Media, Android)
|
||||
* Requires `Notification access` permission
|
||||
* Added app display-name detection on Android, so the media module names the player the way the app names itself instead of by its package name, and the terminal module reports the label Termux is installed under. (Media, Terminal, Android)
|
||||
* Performance improvements & internal cleanups
|
||||
* Added Finnish language support. (General)
|
||||
* Added pkgsrc package detection on macOS. (#2627, Packages, Apple)
|
||||
* Added an ALSA fallback to Sound on Linux, so a machine that runs no sound server reports its output device and volume instead of an error. (Sound, Linux)
|
||||
|
||||
Bugfixes:
|
||||
* Fixed `--config`/`-c` bash completion offering one candidate per word instead of one candidate per file for paths containing spaces. (Completion, Bash)
|
||||
* Fixed external monitors detected via DDC/CI being randomly reported as built-in on Linux. (#2615, Brightness, Linux)
|
||||
* Fixed the Vulkan driver name being concatenated with the name of a previously enumerated device on multi-GPU systems. (#2619, Vulkan)
|
||||
* Fixed the foot terminal font size not being parsed when written as `Font Name-size`, e.g. `Berkeley Mono-12`. (#2624, TerminalFont, Linux)
|
||||
* Fixed `/boot` being hidden by default on Haiku, where it is the system volume. (Disk, Haiku)
|
||||
* Improved Snapdragon X1/X2 model name detection on Linux. (#2625, CPU, Linux)
|
||||
* Fixed the Android build failing with `GL/gl.h` not found, because the NDK ships the EGL headers that turned OpenGL detection on but not the desktop GL headers it includes. (OpenGL, Android)
|
||||
* Fixed MAC address reporting `02:00:00:00:00:00` on macOS 27. (LocalIP, Apple)
|
||||
* Fixed assertion failure when percentage exceeds 100. (Percent)
|
||||
* Fixed a non-existent command being reported as a generic child process error on Windows instead of `command not found`. (Command, Windows)
|
||||
* Fixed Android display detection on devices running Android 17. (Display, Android)
|
||||
* Fixed Windows volumes with a per-user quota (such as network shares) reporting a wrong total size and a wrapped-around used size, because the total size was quota-limited while the free size was not. (#2651, Disk, Windows)
|
||||
* On Windows 10 1809 (build 17763) and later, the whole volume's capacity and free space are now reported by default. Set `disk.useAvailable` to `true` to keep reporting the space that is available to the current user, quota included.
|
||||
|
||||
Logos:
|
||||
* Added filled Artix logo (#2631)
|
||||
* Renamed PostmarketOS to nura (#2626)
|
||||
|
||||
# 2.69.0
|
||||
|
||||
Changes:
|
||||
* ImageMagick 6 is deprecated and will be removed in a future release. It remains available for older Debian and Ubuntu releases; upgrade to ImageMagick 7 when possible.
|
||||
* The experimental QuickJS format scripting is deprecated, disabled by default, and planned for removal in the next release. Re-enable it with `cmake -DENABLE_QUICKJS=ON`, or migrate scripts to Lua. (General)
|
||||
* Replaced `--logo-recache` and `logo.recache` with `--logo-cache` and `logo.cache`. Use `true` to reuse or create valid cache entries, `false` to disable caching, or `regen` to force regeneration. (Logo)
|
||||
* Modules selected with `--structure` now honor their options from the JSON config.
|
||||
|
||||
Features:
|
||||
* Added `logo.position: "auto"` and `--logo-position auto` to place wide logos above module output when the terminal has less than 32 columns available for text. (Logo)
|
||||
* The position can also be selected in interactive `--gen-config`.
|
||||
* For now, the default position is `left` for compatibility with previous releases. This may change to `auto` in future releases.
|
||||
* Improved Bluetooth detection support:
|
||||
* Added Bluetooth Low Energy detection on Windows and macOS.
|
||||
* Report device type (LE or classic) and signal quality, if available.
|
||||
* Added Bluetooth Core 6.0–6.3 version reporting. (BluetoothRadio)
|
||||
* Improved image log support:
|
||||
* Image logos on Windows, macOS, and Android now use native image processing libraries instead of ImageMagick.
|
||||
* For package managers: ImageMagick dependencies can be removed on macOS, Windows and Android as they are no longer used for image logos.
|
||||
* Windows Terminal supports sixel logos out of the box
|
||||
* Changes to a source image now correctly invalidate its cache. (Logo)
|
||||
* Added GIF and APNG support to `--kitty` image protocol. Set `logo.animationFrame` to `0` to play animations in compatible kitty terminals; positive or negative values select a still frame. Android animation requires Android 12 (API 31); terminals without kitty graphics protocol support report an error and fall back to the built-in logo instead. (Logo)
|
||||
* Added cmake option `-DENABLE_IMAGE_LOGO=<BOOL>` (default `ON`) to disable image logos and reduce binary size. `raw` logos remain available. (Logo)
|
||||
* Improved Linux support:
|
||||
* Added RakuOS rum overlay package counting (`{rum}`) and CRUX package detection (`{crux}`). (Packages)
|
||||
* Added CPU name and frequency detection on SPARC. (CPU)
|
||||
* Improved COSMIC version detection and added Umbriel version detection. (DE / WM)
|
||||
* Improved Android support:
|
||||
* Expanded ROM detection to HarmonyOS, Flyme, OneUI, LineageOS, and many other ROMs. (DE)
|
||||
* Improved camera detection; Android 7 (API 24) or later is required. (Camera)
|
||||
* Battery level and charging state are now available to apps; detailed battery information requires root or ADB. (Battery)
|
||||
* Added display mode, physical size, rotation, and HDR information. App-based detection requires Android 13 (API 33); root or ADB can also detect displays on older versions. (Display)
|
||||
* Added Wi-Fi interface, connection, standard, and security information. Android 11 (API 30) or later is required. Termux:API is needed for the Wi-Fi permission, and location permission is needed to reveal SSID and BSSID; without location permission the two names appear as `<redacted>`. (Wifi)
|
||||
* Added Wallpaper detection, which effectively reports `/data/system/users/0/wallpaper`. (Wallpaper)
|
||||
* Added Media detection, root/ADB only. (Media / Player)
|
||||
* Improved macOS support:
|
||||
* Improved process-name detection in Top. (Top)
|
||||
* Improved wallpaper detection on macOS Sonoma and later. (#2559, Wallpaper)
|
||||
* Improved Windows support:
|
||||
* `winget` now counts packages from configured sources, excludes Microsoft Store apps, and caches results. It remains disabled by default. (Packages)
|
||||
* Player names now match the names shown by Windows (for example, Chrome is reported as Google Chrome). (Player)
|
||||
* Added battery-level detection for more controllers on Windows and macOS. (Gamepad)
|
||||
* Improved macOS controller name detection (macOS, Gamepad)
|
||||
* macOS controller names now match the system; configs that filter by name may need updating.
|
||||
* Requires macOS 11 or later.
|
||||
* Improved the built-in HTTP client used by PublicIP and Weather; it now supports custom ports and chunked responses. (PublicIP / Weather)
|
||||
* Reduced the default `waitTime` for DiskIO, NetIO, and Top from 500 ms to 250 ms. (DiskIO / NetIO / Top)
|
||||
* Added `general.preload.lua` for loading shared Lua helpers before modules run. It requires a Lua-enabled build and starts the Lua interpreter whenever configured. (General)
|
||||
* Lua format scripting is now considered stable.
|
||||
|
||||
Bugfixes:
|
||||
* Fixed Snapdragon X2 series model detection on Linux. (#2611, CPU / Linux)
|
||||
* Fixed image logo caching, padding, and positioning issues, including an iTerm display bug. (Logo)
|
||||
* Fixed several Windows issues, including Windows Terminal font detection, redirected `--gen-config` output, and gamepad battery reporting for DualShock 4 and Switch controllers. (#2573, TerminalFont; Gamepad; General)
|
||||
* Fixed formatting and display issues, including ANSI-aware truncation, date/time formatting, and bright key/title colors. (Format / DateTime / Display)
|
||||
* Fixed `--dynamic-interval` retries and stale results across multiple rounds. (PublicIP / Weather / Display / Monitor / WM / DE / Media / Player / Shell / Terminal)
|
||||
* Fixed invalid numeric and color options that could cause hangs or excessive output. (Separator / CPUUsage / NetIO / DiskIO / LoadAvg / Colors)
|
||||
* Fixed JSON output and config validation across several modules, including GPU, Btrfs, Gamepad, Packages, and Custom. (General)
|
||||
* Fixed GPU memory and device-type reporting, GNOME Classic and Trinity version detection, and default-route family names. (GPU / DE / LocalIP)
|
||||
* Fixed module output and filtering issues affecting editor failures, sound status, command selection, and ignored keyboard/gamepad devices. (Editor / Sound / Command / Keyboard / Gamepad)
|
||||
* Fixed package counting with read-only databases and corrected other package detection and JSON output issues. (Packages)
|
||||
* Fixed Base64 encoding, big-endian handling, and 64-bit value truncation on 32-bit platforms. (General)
|
||||
|
||||
Logos:
|
||||
* Added ALT Atomic
|
||||
* Removed Zerene
|
||||
|
||||
# 2.68.1
|
||||
|
||||
Changes:
|
||||
* The DE / WM / LM modules now reports the full name "Desktop Environment" / "Window Manager" / "Login Manager" instead of the abbreviations.
|
||||
|
||||
Features:
|
||||
* Improved Login Manager (LM) module
|
||||
* Added support on macOS, Windows and POSIX systems without systemd (by process list enumeration).
|
||||
* Reports pretty name on Linux.
|
||||
* Added Top module to print processes with the highest CPU, memory or disk I/O usage. (Top)
|
||||
* Added module key localization support. (General)
|
||||
* Added a new option `--key-language <?lang>` (`display.key.language: "<?lang>"` in JSON config). When left empty, it defaults to the system locale.
|
||||
* See `fastfetch -h key-language` for all supported languages.
|
||||
* Exposed in custom key format as `{module-name}`.
|
||||
* Improved Wi-Fi module
|
||||
* Added Wifi channel width detection, exposed via `{channel-width}` in custom format (reports 0 when not supported).
|
||||
* Improved Wifi channel frequency accuracy and fortified detection on Windows, macOS.
|
||||
* Added Wifi detection support on SunOS, Haiku.
|
||||
* Added Battery detection support on SunOS. (Battery, SunOS)
|
||||
* Added terminal font size detection support on Termux. (TerminalFont, Android)
|
||||
* Improved GTK theme/icons/font detection on standalone WMs such as Hyprland, sway, niri and i3. (Theme, Linux)
|
||||
* Improved fish completion scripts to print enum descriptions. (Completion)
|
||||
* Enabled Wayland support on Android. (DisplayServer, Android)
|
||||
* Added DE detection support for ASUS Zenfone. (DE, Android)
|
||||
* Added new ARM SoCs (CPU, Linux / Android)
|
||||
|
||||
Bugfixes:
|
||||
* Fixed I/O rate calculation precision in DiskIO and NetIO, and prevented division by zero. (DiskIO / NetIO)
|
||||
* Fixed the fast path of ash version detection. (Shell)
|
||||
* Fixed memory usage detection support on x86-32 FreeBSD (Memory, FreeBSD)
|
||||
* Note: Although 32-bit systems are still supported, they are deprecated and barely tested. Users are encouraged to upgrade to 64-bit systems.
|
||||
* Some internal cleanups and optimizations.
|
||||
* Attempted to fix potential issues on big-endian systems
|
||||
* Not tested due to lack of access to big-endian hardware.
|
||||
|
||||
Logos:
|
||||
* Added macOS_old
|
||||
* Added Omarchy, Omarchy2
|
||||
|
||||
# 2.67.1
|
||||
|
||||
Bugfixes:
|
||||
* Fixed a `Symbol not found` error when running on macOS 10.15. (#2499, Codec, macOS)
|
||||
* Fixed Alacritty font size never being parsed from the TOML config. (#2501, TerminalFont)
|
||||
* Fixed bright colors incorrectly being active when the terminal uses a light theme. (#2509, Display)
|
||||
* Fixed Ghostty terminal font detection (TerminalFont)
|
||||
* Now tries to load the config with the `.ghostty` extension. (#2500)
|
||||
* Fixed `font-family` fallback order. (#2510)
|
||||
* Some internal cleanups and optimizations.
|
||||
|
||||
Features:
|
||||
* Added a C23 compiler check so the build process gracefully exits during CMake configuration if the compiler is unsupported. (CMake)
|
||||
* Added compatibility workarounds for Apple Clang 16 to fix building on macOS 14. (CMake, macOS)
|
||||
* Reported real Android version codenames (e.g., "Baklava", "Vanilla Ice Cream") instead of "REL". (OS, Android)
|
||||
* Added Loadavg detection support on Haiku R1B6. (Loadavg, Haiku)
|
||||
* Enabled image logo support in the Zellij terminal multiplexer. (#2507, Logo)
|
||||
* Improved Locale detection on Windows (Locale, Windows)
|
||||
* Now respects `LC_ALL` and `LANG` environment variables if set for better compatibility with MSYS2/cygwin.
|
||||
* Appends the active system Code Page (e.g., `.UTF-8` or `.Windows-1252`) to the default system locale fallback.
|
||||
|
||||
# 2.67.0
|
||||
|
||||
Changes:
|
||||
* The minimum supported compiler version has been raised to GCC 13 (released on 2023-04-26) and Clang 16 (released on 2023-03-17) to accommodate the new C23 syntax used throughout the codebase. (General)
|
||||
* As a result, building from source in Debian 12 (which only ships GCC 12 by default) is no longer supported. Users must upgrade their compiler or use the prebuilt binaries from the GitHub releases page.
|
||||
* The `general.preRun` option has been removed due to security concerns. (General)
|
||||
* Users who relied on this option to run a command before printing output will need to move that logic to an external wrapper.
|
||||
* The `{status}` placeholder in Battery custom format is now an array type instead of a single comma-separated string. (Battery)
|
||||
* This is a breaking change for users who print `{status}` in Lua scripts. They can use `table.concat(...)` to join the array into a single string if needed.
|
||||
* The `--gen-config-*` flags (`--gen-config-full`, `--gen-config-force`, `--gen-config-full-force`) have been removed and their functions have been merged into `--gen-config`. (Global)
|
||||
* See features below.
|
||||
|
||||
Features:
|
||||
* `--gen-config` now opens an interactive configuration TUI to make generating config files easier.
|
||||
* It falls back to non-interactive generation when `$NO_COLOR` is set or when stdin/stdout is not a TTY. This behavior can also be used to disable the interactive mode.
|
||||
* Added an experimental [WebUI](https://fastfetch-cli.github.io/fastfetch-config/) ([separate project](https://github.com/fastfetch-cli/fastfetch-config)) for generating and editing configs.
|
||||
* Added `-w`/`--watch` as a seconds-based alias for `--dynamic-interval`, defaulting to 1 second when no value is provided. (#2478, Watch)
|
||||
* Added InitSystem detection on Windows, reporting the first userland process (`smss.exe`). (InitSystem, Windows)
|
||||
* Added CPU and GPU temperature detection support for Apple M5 series chips. (CPU / GPU, macOS)
|
||||
* Reported `Basic` as the Windows theme when DWM is disabled. (Theme, Windows)
|
||||
* Improved Ghostty terminal font detection performance. (#2122, TerminalFont)
|
||||
* It now parses Ghostty directly instead of running `ghostty +show-config`.
|
||||
* Added support for a wider range of TOML config syntaxes used by Alacritty. (#2456, TerminalFont)
|
||||
|
||||
Bugfixes:
|
||||
* Fixed a potential fish version detection error. (Shell, Linux)
|
||||
* Fixed incorrect resolution reporting when a monitor advertises multiple preferred modes. (#2481, Display, Linux)
|
||||
* Fixed connected monitors being missed when Wayland output events arrive late. (#2451, Display, Linux)
|
||||
* Fixed VP9 codec detection on macOS. (Codec, macOS)
|
||||
* Corrected Base64 encoding on big-endian hosts. (#2470)
|
||||
* Fixed invalid URL parsing in the PublicIP module. (PublicIP)
|
||||
* Correctly reported virtual GPUs on Windows. (#2461, GPU, Windows)
|
||||
* Added a size limit for network responses to prevent excessive memory usage and mitigate potential attacks. (PublicIP / Weather)
|
||||
* Fixed Ubuntu Studio Core detection. (OS, Linux)
|
||||
* Relaxed the HTTP response check so that both `HTTP/1.0` and `HTTP/1.1` responses are accepted when fetching data over the network. (PublicIP / Weather)
|
||||
* Various internal cleanups and optimizations:
|
||||
* Fixed multiple memory leaks (Separator, Camera, Codec, Display).
|
||||
* Added integer overflow checks to the string buffer implementation.
|
||||
* Various code cleanups and compiler warning fixes.
|
||||
|
||||
Logos:
|
||||
* Removed Hypros, MagpieOS, Furreto, EmperorOS and Magix
|
||||
|
||||
# 2.66.0
|
||||
|
||||
Changes:
|
||||
* CPU package count is no longer reported in the default output.
|
||||
* Users can still print it in custom formats via `{packages}`
|
||||
|
||||
Bugfixes:
|
||||
* Fixed compatibility with WSL 2.9.3 (#2433, WM, Linux)
|
||||
* Fixed AMD GPU names not being queried accurately in `driverSpecific` mode (GPU, Linux)
|
||||
* Fixed GPU core counting for Apple Silicon GPU on Asahi Linux (GPU, Linux)
|
||||
* Some internal cleanups and optimizations.
|
||||
|
||||
Features:
|
||||
* Added Astra Linux version detection (OS, Linux)
|
||||
* Improved performance of Windows PowerShell version detection (Shell, Windows)
|
||||
* Improved performance of Media module on macOS (Media, macOS)
|
||||
* Added proxmox backup server detection (#2443, OS, Linux)
|
||||
|
||||
Logos:
|
||||
* Added Flatcar, Azure Linux
|
||||
* Added Chimera_small, Chimera2, Parabola2_small, PostmarketOS2, Qubes_small
|
||||
* Updated linuxmint_small
|
||||
* Cleaned up distro names
|
||||
|
||||
# 2.65.2
|
||||
|
||||
Changes:
|
||||
* Memory usage calculations now exclude the ZFS ARC cache on Linux, FreeBSD, NetBSD, and SunOS. (#1995 / #2418, Memory)
|
||||
* This prevents the Memory module from reporting artificially high memory usage on systems with the ZFS kmod enabled.
|
||||
|
||||
Bugfixes:
|
||||
* The GPU module no longer pings sleeping dedicated GPUs on dual-GPU laptops. (#2419, GPU)
|
||||
* As a result, PCIe link speed detection (introduced in v2.65.0) is now only enabled in `driverSpecific` mode.
|
||||
* Fixed a crash when detecting hardware codec support with the `amdgpu` driver on Linux. (#2419, Codec, Linux)
|
||||
|
||||
Logos:
|
||||
* Updated CachyOS_small; fixed CachyOS logo colors to match the official logo better
|
||||
* Added Turkish
|
||||
|
||||
# 2.65.1
|
||||
|
||||
No changes
|
||||
|
||||
# 2.65.0
|
||||
|
||||
Changes:
|
||||
* `display.disableLinewrap` now defaults to `false`.
|
||||
* This allows long lines to wrap instead of being truncated, preventing confusion for new users.
|
||||
* Wrapped lines may overlap with the image logo. Users who prefer the old behavior can set `display.disableLinewrap: true` in their config file or use `--disable-linewrap true` on the command line.
|
||||
* Reorders package manager variables in custom format strings, moving aggregate variables (e.g., `all`, `brew-all`, `flatpak-all`) to the end. (Packages)
|
||||
* This is a breaking change for users relying on numeric placeholders (e.g., `{1}`). Numeric placeholders are a long-deprecated feature; named placeholders (e.g., `{pacman}`) are always preferred and future-proof.
|
||||
* Enables `showPeCoreCount` by default to better display hybrid-architecture core counts. (CPU)
|
||||
* No longer accepts `chafa` options in config files if `chafa` support was disabled at compile time. (Logo)
|
||||
* This may affect users who generated their config files using `fastfetch --gen-full-config`. To resolve this, simply remove the `chafa` section from the config file.
|
||||
|
||||
Features:
|
||||
* Adds detection for CPU code names (e.g., "Alder Lake", "Zen 4") and manufacturing technologies (e.g., "Intel 7", "TSMC N4") on x86 processors. (#1501, CPU, X86)
|
||||
* These can be used in custom formats via `{code-name}` and `{technology}`.
|
||||
* Adds PCIe link speed detection (current and max generation & lanes) for GPUs on Linux and Windows.
|
||||
* These can be used in custom formats via `{pcie-max-speed}` and `{pcie-curr-speed}`.
|
||||
* Adds HDR display detection via the `wp-color-management-v1` Wayland protocol. (#2356, Display, Linux)
|
||||
* Previously, HDR detection was only supported on KDE Plasma.
|
||||
* Adds the `egl-ext` detection method, which uses `EGL_EXT_device_enumeration` for GPU enumeration. (GPU)
|
||||
* Adds terminal version and font detection for `kmscon`. (#2393, Terminal / TerminalFont, Linux)
|
||||
* Adds the currently-in-beta code name "Golden Gate" for macOS 27. (OS, macOS)
|
||||
* Adds WPA version detection on FreeBSD. (Wifi, FreeBSD)
|
||||
* Adds package manager support for `porg` (#2405) and `install-release` (#2342). (Packages, Linux)
|
||||
* Adds Weston version detection. (WM, Linux)
|
||||
* Improves the core type (P-Core/E-Core) fetching mechanism on Windows by using `EfficiencyClass` instead of legacy frequency inference. (CPU, Windows)
|
||||
* Improves display detection compatibility with KDE 6.7. (Display, Linux)
|
||||
* Improves display serial number detection, now preferring alphanumeric serial numbers when available. (Display)
|
||||
* This can be used in custom formats via `{serial}`.
|
||||
* Improves the performance of `emerge` package manager detection. (#2406, Packages, Linux)
|
||||
* Improves error messages for config file parsing errors.
|
||||
* Improves reliability of the `Codec` module on Linux (Codec, Linux)
|
||||
* Adds `va-x11` backend for VA-API and VDPAU, which is used if `va-drm` backend fails to initialize.
|
||||
* Fixes an issue where the Codec module incorrectly reports no results when the `codec.showType` option is set.
|
||||
|
||||
Bugfixes:
|
||||
* Fixes physical core detection on non-x86 architectures and simplifies the frequency detection code. (CPU, Linux)
|
||||
* Fixes console mode and output code page initialization issues when running `fastfetch` in `Conhost`. (#2383, Windows)
|
||||
* Fixes public IP detection randomly failing on Linux. (#2401, PublicIP, Linux)
|
||||
* Fixes several memory leaks.
|
||||
|
||||
Logos:
|
||||
* Adds Zerene OS. (#2404)
|
||||
|
||||
# 2.64.2
|
||||
|
||||
Bugfixes:
|
||||
* Fixes image rendering being wiped quickly (#2374)
|
||||
* Regression from v2.64.0
|
||||
* Fixes ASCII logo being overwritten in `--dynamic-interval` mode
|
||||
* Regression from v2.64.0
|
||||
|
||||
Logos:
|
||||
* Updates OpenWrt and adds a small variant (#2376)
|
||||
* The old one is renamed to `openwrt_old`
|
||||
|
||||
# 2.64.1
|
||||
|
||||
Features:
|
||||
* Adds a CMake option `-DPACKAGES_REMOVE_DISABLED` to remove detection code of disabled packages (`-DPACKAGES_DISABLE_<PACKAGE_NAME>`) for slightly smaller binary size (Packages)
|
||||
|
||||
Bugfixes:
|
||||
* Fixes compatibility issues with Lua 5.3
|
||||
* Avoid possible infinite recursion in `encode_json` when encoding deeply nested tables or circular references
|
||||
* Fixes compilation issues when building with old macOS SDKs
|
||||
|
||||
# 2.64.0
|
||||
|
||||
New **optional build** dependencies:
|
||||
* [`libva`](https://github.com/intel/libva) and [`libvdpau`](https://www.freedesktop.org/wiki/Software/VDPAU) for the new `Codec` module.
|
||||
* [Lua 5.3 to 5.5](https://www.lua.org/) for Lua scripting support, or [QuickJS](https://github.com/quickjs-ng/quickjs) v0.15.0 or newer for JavaScript scripting support.
|
||||
|
||||
Features:
|
||||
* Adds `Codec` module support for Windows, macOS, Linux, and Android for hardware-accelerated video codec detection (Codec)
|
||||
* Backends used (results may vary depending on the backend due to driver differences):
|
||||
* Linux and BSD: VA-API (default) and VDPAU (fallback for NVIDIA). Fastfetch must be built with `libva` and `libvdpau` support to enable these backends.
|
||||
* Windows: D3D12VA (Windows 11) and D3D11VA+MFT (Windows 10 or older)
|
||||
* macOS: VideoToolbox
|
||||
* Android: AMediaCodec
|
||||
* Common: Vulkan Video (disabled by default; can be enabled with `"useVulkan": true`)
|
||||
* By default, both encoders and decoders are reported. If no codecs are detected for a given type, `None` is reported. This behavior can be configured using the `"showType": "encoder|decoder"` option.
|
||||
* Adds experimental Lua script support for custom formats (Global)
|
||||
* Basic usage (using `Title` as an example): `{ "type": "title", "format": "lua:return string.format('Hello %s@%s', (...).userName, (...).hostName)" }`. The `lua:` prefix indicates a Lua script. A `return` statement is required to pass the final result back to the fastfetch module; otherwise, `nil` is returned implicitly and the entire module output is skipped.
|
||||
* Parameters are passed via variable arguments `(...)`. Users can assign them to named variables for better readability. For example: `lua:local args = ...; string.format('Hello %s@%s', args.userName, args.hostName)`.
|
||||
* The Lua interpreter instance is shared across all modules. This allows users to store and manipulate data across modules. For example:
|
||||
* `{ "type": "shell", "format": "lua:shell = ..." } // Note: no "return" here`
|
||||
* `{ "type": "terminal", "format": "lua:return shell.prettyName .. ' in ' .. (...).prettyName" } // This will print the detected shell and terminal names`
|
||||
* A `json_encode(table, is_pretty)` function has been added to the Lua API for easier debugging. For example, `lua:return json_encode(...)` will print all available variables and their values in JSON format.
|
||||
* Supported Lua versions: Lua 5.3 to 5.5 (Lua 5.1 and LuaJIT are not supported). The Lua version is auto-detected at build time. Once built, users can check the configured Lua version using `fastfetch --list-features`.
|
||||
* Adds experimental QuickJS script support for custom formats as an alternative to Lua (Global)
|
||||
* Basic usage: `{ "type": "title", "format": "qjs:`Hello ${this.userName}@${this.hostName}`" }`. The `qjs:` prefix indicates a JavaScript script. No `return` statement is needed; the final result is simply the evaluated value of the script.
|
||||
* Parameters are passed via the `this` object. Usage is mostly the same as Lua, but using JavaScript syntax.
|
||||
* Requires [quickjs-ng v0.15.0](https://github.com/quickjs-ng/quickjs/releases/tag/v0.15.0) or newer.
|
||||
* Adds CMake options `-DMODULE_DISABLE_<MODULE_NAME>=ON` to disable modules at compile time for a smaller binary size
|
||||
* Use `cmake -L . | grep MODULE_DISABLE_` to list all available options.
|
||||
* These options rely on LTO for dead code elimination (`-DCMAKE_BUILD_TYPE=Release` should enable LTO by default).
|
||||
* Adds the ability to remove unneeded ASCII logos without modifying the fastfetch source code, further reducing binary size
|
||||
* Simply remove the corresponding logo file from the logo directory (`src/logo/ascii/[a-z]/*`) and re-run `cmake`.
|
||||
* Improves string manipulation specifiers in custom formats
|
||||
* They are now ANSI escape-aware and work correctly with colored output (e.g., percentages with `num-color`) (#2364). Limitations:
|
||||
* They are still not wide-character aware and treat CJK and emoji characters as raw bytes.
|
||||
* Escape sequences in the middle of strings are not supported.
|
||||
* A new `|` specifier has been added to center strings. For example, `{user-name|20}` will center the username in a 20-character-wide field.
|
||||
* Adds preliminary `Bootmgr`, `Brightness`, and `WMTheme` detection support for Haiku (#2358, Haiku)
|
||||
* Adds `Wallpaper` and `WMTheme` detection support for the COSMIC desktop environment (Linux)
|
||||
* Improves terminal name detection for Nix packages (#2352, Terminal, Linux)
|
||||
* Adds DDC/CI brightness detection support for FreeBSD (Brightness, FreeBSD)
|
||||
* DDC/CI communication can be very slow. Users can set the `ddcciSleep: null` option to skip DDC/CI detection.
|
||||
* Reworks the built-in logo printing logic. ASCII logos and modules are now printed line by line, avoiding issues like #2239
|
||||
* Note: Image logos are not affected by this change and still print the entire image first before printing any modules.
|
||||
* Adds a new `--logo-padding-bottom` option to control the padding between the bottom of the logo and the first module when `--logo-position top` is used
|
||||
* Previously, `--logo-padding-right` was used for both right and bottom padding.
|
||||
* Adds `Samsung Exynos 2600` to CPU detection (CPU, Android)
|
||||
* Adds terminal font detection support for the Muxy terminal (TerminalFont, macOS)
|
||||
* Improves the performance of BusyBox (ash) version detection and always reports `ash` as the pretty name (Shell, Linux)
|
||||
|
||||
Bugfixes:
|
||||
* Fixes SwayFX version detection (WM, Linux)
|
||||
* Fixes fallback font detection for Ghostty (TerminalFont, macOS)
|
||||
* Fixes `--stat` output to correctly align with the right border (Global)
|
||||
* Fixes boot manager on macOS 26 (mBoot) is incorrectly reported as `iBoot` (Bootmgr, macOS)
|
||||
|
||||
Logos:
|
||||
* Adds Quasar (#2338, #2323), Origami, Origami_small (#2321, #2322), BerserkArch (#2324, #2310), and NixOS2 (#2346)
|
||||
* Minor tweaks and color fixes for the NixOS_small logo (#2357)
|
||||
* Updates NurOS (#2366)
|
||||
|
||||
# 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:
|
||||
@@ -18,7 +607,7 @@ 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.
|
||||
* ~~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)
|
||||
@@ -374,7 +963,7 @@ Features:
|
||||
* Added support for interface speed detection on SunOS (LocalIP, SunOS)
|
||||
* Added detection support for Xlibre (#1888, WM, Linux)
|
||||
* Improved the accuracy of color detection (Cursor, macOS)
|
||||
* Improved the proformance of `Nix` package manager detection on macOS by porting optimizations form Linux port (#1893, Packages, macOS)
|
||||
* Improved the performance of `Nix` package manager detection on macOS by porting optimizations from the Linux port (#1893, Packages, macOS)
|
||||
|
||||
Bugfixes:
|
||||
* Fixed custom object inheriting a key from the previous custom object if the key is blank (#1477)
|
||||
@@ -885,7 +1474,7 @@ Features:
|
||||
* Change package manager name of NetBSD from `pkg` to `pkgsrc` (#1515, Packages, NetBSD)
|
||||
* Detect SOC name on RISCV (#1519, CPU, Linux)
|
||||
* Report marketing name of new QS8Es (CPU, Android)
|
||||
* Acquire acquire more os info from lsb-release if missing from os-release (#1521)
|
||||
* Acquire more OS info from lsb-release if missing from os-release (#1521)
|
||||
* CMake: add option `-DCUSTOM_LSB_RELEASE_PATH` to specify the path of `lsb-release` file
|
||||
* `-DCUSTOM_OS_RELEASE_PATH` has been supported since `v2.11.4`
|
||||
* Report more SOC names on Android (CPU, Android)
|
||||
@@ -1353,7 +1942,7 @@ Logos:
|
||||
|
||||
Bugfixes:
|
||||
* Fix bad Intel Arc GPU name detection, which was supposed to be fixed in the last version but the change was reverted accidentally (#1177, GPU, Linux)
|
||||
* Fix arm32 CPU name detection no longer work. Regression of 2.21.2 (CPU, Linux)
|
||||
* Fix arm32 CPU name detection no longer working. Regression of 2.21.2 (CPU, Linux)
|
||||
|
||||
# 2.21.2
|
||||
|
||||
@@ -2848,7 +3437,7 @@ Features:
|
||||
* Enhance `--percent-type` to allow hiding other texts (#387)
|
||||
* Add Wifi module support for Linux
|
||||
* Detect scaled resolutions (Windows, macOS)
|
||||
* Optimise font module printing (Windows)
|
||||
* Optimize font module printing (Windows)
|
||||
* Detect pacman package count inside MSYS2 environment (Windows)
|
||||
* Add Wifi / Battery module support for Android
|
||||
* Disk name support for Linux
|
||||
|
||||
+791
-269
File diff suppressed because it is too large
Load Diff
+792
@@ -0,0 +1,792 @@
|
||||
# Contributing to fastfetch
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Disclaimer
|
||||
</summary>
|
||||
|
||||
This document is generated by AI and reviewed by humans. It is primarily intended for AI-assisted development, while remaining useful to human developers.
|
||||
</details>
|
||||
|
||||
Thank you for your interest in fastfetch. This document covers building, architecture, how to add a module or a logo, code style, commit conventions, and the pull request workflow.
|
||||
|
||||
fastfetch is a system information tool written in C23, supporting Linux, macOS, Windows, the BSDs, Solaris, Haiku and Android. The project places strong emphasis on **startup time** and on keeping **dependencies optional**; many design decisions only make sense under that constraint, and this document returns to it repeatedly.
|
||||
|
||||
---
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Contributor quick reference](#contributor-quick-reference)
|
||||
- [Getting started](#getting-started)
|
||||
- [Code map](#code-map)
|
||||
- [Core architecture: modules vs. detection](#core-architecture-modules-vs-detection)
|
||||
- [Module registration](#module-registration)
|
||||
- [Walkthrough: adding a module](#walkthrough-adding-a-module)
|
||||
- [Walkthrough: adding a logo](#walkthrough-adding-a-logo)
|
||||
- [Platform conventions](#platform-conventions)
|
||||
- [Code style](#code-style)
|
||||
- [Commit messages](#commit-messages)
|
||||
- [Changelog](#changelog)
|
||||
- [Tests](#tests)
|
||||
- [Pull requests](#pull-requests)
|
||||
- [Pitfalls](#pitfalls)
|
||||
|
||||
---
|
||||
|
||||
## Contributor quick reference
|
||||
|
||||
| Task | Start here | Verify with |
|
||||
|---|---|---|
|
||||
| Build and run fastfetch | `run.sh`, `CMakeLists.txt` | `./run.sh` |
|
||||
| Add a module | `src/modules/<name>/`, `src/detection/<name>/`, `src/modules/modules.c`, `CMakeLists.txt` | `cmake -B build && cmake --build build -j` |
|
||||
| Add a platform implementation | `src/detection/<name>/<name>_<platform>.c`, the matching platform block in `CMakeLists.txt` | Build on the target platform or CI |
|
||||
| Add an ASCII logo | `src/logo/ascii/<letter>/<name>.txt`, matching `<letter>.inc` | `./build/fastfetch --logo <name>` |
|
||||
| Change formatting or JSON output | `src/modules/<name>/<name>.c` | `./build/fastfetch -s <name> --format json` |
|
||||
| Change shared formatting or containers | `src/common/format.h`, `src/common/color.h`, `src/common/FFstrbuf.h` | Build and run the matching test |
|
||||
| Run the test suite | `tests/`, `build/` | `cd build && ctest --output-on-failure` |
|
||||
|
||||
For a typical code change, the shortest useful iteration is: configure, build the `fastfetch` target, run the affected module, then run the tests. If you add a directory or a generated input, re-run `cmake -B build` so that CMake refreshes its file globs.
|
||||
|
||||
---
|
||||
|
||||
## Getting started
|
||||
|
||||
### Building
|
||||
|
||||
The quickest way is `run.sh` in the repository root: it creates `build/`, configures, compiles and runs the binary.
|
||||
|
||||
```sh
|
||||
./run.sh # build and run
|
||||
./run.sh --format json # arguments are forwarded to fastfetch
|
||||
```
|
||||
|
||||
Manual build:
|
||||
|
||||
```sh
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
cmake --build build --target fastfetch -j$(nproc)
|
||||
./build/fastfetch
|
||||
```
|
||||
|
||||
The default build type is `RelWithDebInfo`. LTO is enabled whenever `ENABLE_LTO=ON` **and** `CMAKE_BUILD_TYPE != Debug`, so the default configuration enables it and only `Debug` builds omit it. This is significant because LTO here is not merely an optimization: it is what removes the code of disabled modules. See [Pitfalls](#pitfalls).
|
||||
|
||||
### Common build options
|
||||
|
||||
| Option | Default | Notes |
|
||||
|---|---|---|
|
||||
| `CMAKE_BUILD_TYPE` | `RelWithDebInfo` | LTO is on for anything but `Debug` |
|
||||
| `BUILD_TESTS` | `OFF` | Builds the unit tests in `tests/` |
|
||||
| `BUILD_FLASHFETCH` | `ON` | Also builds the stripped-down `flashfetch` |
|
||||
| `BINARY_LINK_TYPE` | `dlopen` | `dlopen` / `dynamic` / `static` |
|
||||
| `ENABLE_ASAN` | `OFF` | Address Sanitizer |
|
||||
| `ENABLE_LTO` | `ON` | Link-time optimization |
|
||||
| `MODULE_DISABLE_<NAME>` | `OFF` | Disable a module, e.g. `MODULE_DISABLE_GPU=ON` |
|
||||
| `SET_TWEAK` | `ON` | Appends a tweak to the dev version; turned off for releases |
|
||||
|
||||
`BINARY_LINK_TYPE=dlopen` (the default) is a deliberate design choice: optional dependencies (Vulkan, Wayland, DBus, ImageMagick, chafa, …) are loaded at runtime, so a missing library never prevents startup. When adding a third-party dependency, use the dlopen helpers in `common/library.h` — do not link it directly.
|
||||
|
||||
For a minimal-dependency build, see `.github/workflows/build-no-features-test.yml`:
|
||||
|
||||
```sh
|
||||
cmake -DBUILD_TESTS=On -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_X11=OFF \
|
||||
-DENABLE_DBUS=OFF -DENABLE_ZLIB=OFF ... .
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
Required: CMake ≥ 3.21 and a C23 compiler (GCC, Clang or MSVC). Everything else is optional.
|
||||
|
||||
Optional dependencies are auto-detected: libpci, libdrm, vulkan, wayland, xcb, xrandr, dbus, sqlite3, rpm, imagemagick{6,7}, chafa, zlib, egl, glx, opencl, freetype, elf, libzfs, and more.
|
||||
|
||||
---
|
||||
|
||||
## Code map
|
||||
|
||||
```
|
||||
src/
|
||||
├── fastfetch.c entry point: CLI parsing, module dispatch, main loop (36 KB)
|
||||
├── flashfetch.c entry point for the stripped-down build
|
||||
├── options/ global (non-module) config: general / display / logo
|
||||
├── modules/ module layer — module formatting and output
|
||||
├── detection/ detection layer — platform-specific data retrieval
|
||||
├── common/ infrastructure: FFstrbuf, FFlist, formatting, printing, networking
|
||||
├── logo/ 530 ASCII logos plus the image logo backends
|
||||
└── 3rdparty/ yyjson, widecharwidth, display-library
|
||||
```
|
||||
|
||||
Flow of control:
|
||||
|
||||
```
|
||||
fastfetch.c → options/ (global configuration)
|
||||
→ modules/ (76 modules, formatting and output)
|
||||
→ detection/ (one platform implementation each, raw data)
|
||||
→ common/ (infrastructure)
|
||||
```
|
||||
|
||||
The number of module directories does not match the number of detection directories, which is expected. The following relationships are structural rather than a fixed inventory:
|
||||
|
||||
- **13 modules have no detection directory** — they are either pure layout (`break`, `separator`, `colors`, `title`, `logo`) or reuse another module's detection result (`display`, `monitor`, `kernel`, `shell`, `terminal`, `player`, `custom`, `datetime`).
|
||||
- **4 detection directories serve modules with different names** — `displayserver` (→ `display`, `monitor`), `gtk_qt` (→ `theme`, `icons`, `font`, `cursor`), `terminalshell` (→ `terminal`, `shell`) and `libc`.
|
||||
|
||||
Therefore, do not assume that `modules/<x>/` always has a matching `detection/<x>/`.
|
||||
|
||||
### Layer boundaries
|
||||
|
||||
| Layer | Owns | Must not |
|
||||
|---|---|---|
|
||||
| `options/` | **Global** config (colors, logo, threading, timeouts) | hold per-module options |
|
||||
| `modules/` | formatting, printing, JSON serialization | read `/proc`, call Win32 APIs, parse sysfs |
|
||||
| `detection/` | cross-platform data retrieval, clean result structs | print anything, read display config |
|
||||
| `common/` | general-purpose utilities, no business logic | depend on a specific module |
|
||||
|
||||
A simple check: **no file under `detection/` should contain `printf` or read `instance.config`.** Symmetrically, no file under `modules/` should contain `#ifdef __linux__`.
|
||||
|
||||
---
|
||||
|
||||
## Core architecture: modules vs. detection
|
||||
|
||||
This is the most important convention in the codebase. Every feature consists of a fixed set of files; using CPU as the example:
|
||||
|
||||
```
|
||||
modules/cpu/option.h module option struct
|
||||
modules/cpu/cpu.c formatting and output
|
||||
detection/cpu/cpu.h platform-independent interface
|
||||
detection/cpu/cpu_linux.c ┐
|
||||
detection/cpu/cpu_apple.c │
|
||||
detection/cpu/cpu_windows.c ├─ platform implementations, one linked per build
|
||||
detection/cpu/cpu_bsd.c │
|
||||
detection/cpu/cpu_nosupport.c ┘
|
||||
```
|
||||
|
||||
The interface is deliberately minimal, typically consisting of only two declarations:
|
||||
|
||||
```c
|
||||
typedef struct FFCPUResult { ... } FFCPUResult; // result struct
|
||||
const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu);
|
||||
```
|
||||
|
||||
The `const char*` return value is an **error string**; `nullptr` means success. This pattern is used throughout `detection/` — please keep it consistent.
|
||||
|
||||
Helper functions are only exposed when they are genuinely shared between platform implementations (as in `cpu.h`: `ffCPUAppleCodeToName`, `ffCPUDetectByCpuid`).
|
||||
|
||||
**The benefit of this separation:** adding a platform never touches `modules/`; fixing output formatting never touches `detection/`.
|
||||
|
||||
---
|
||||
|
||||
## Module registration
|
||||
|
||||
### The descriptor
|
||||
|
||||
Every module exports one `FFModuleBaseInfo` (defined in `common/option.h:46`) — essentially a hand-written vtable:
|
||||
|
||||
```c
|
||||
typedef struct FFModuleBaseInfo {
|
||||
const char* name; // lookup key, e.g. "cpu"
|
||||
const char* description; // help text
|
||||
FFModuleDisplayName displayName; // module name in 20 languages
|
||||
|
||||
void (*initOptions)(void* options);
|
||||
void (*destroyOptions)(void* options);
|
||||
void (*parseJsonObject)(void* options, struct yyjson_val* module);
|
||||
bool (*printModule)(void* options);
|
||||
bool (*generateJsonResult)(void* options, yyjson_mut_doc*, yyjson_mut_val*);
|
||||
void (*generateJsonConfig)(void* options, yyjson_mut_doc*, yyjson_mut_val*);
|
||||
|
||||
FFModuleFormatArgList formatArgs; // format placeholder table
|
||||
const uint8_t defaultOrder; // sort weight
|
||||
} FFModuleBaseInfo;
|
||||
```
|
||||
|
||||
The source comment acknowledges that this is undefined behavior, since `void*` is not compatible with `FF*Options*`. It is a pragmatic compromise to obtain polymorphism in C; do not attempt to "fix" it.
|
||||
|
||||
### The registry: a first-letter hash bucket
|
||||
|
||||
`src/modules/modules.c` defines 26 `static FFModuleBaseInfo*` arrays (`A[]` through `Z[]`), each terminated by `nullptr`, collected into `ffModuleInfos[26]`:
|
||||
|
||||
```c
|
||||
FFModuleBaseInfo** modules = ffModuleInfos[toupper(name[0]) - 'A']; // O(1) bucket lookup
|
||||
for (; *modules; ++modules) { // linear scan in the bucket
|
||||
if (ffStrEqualsIgnCase(name, (*modules)->name)) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
Because the registry is small, a single arithmetic bucket lookup followed by a few string comparisons is preferable to a general-purpose hash table. When adding a module, place its descriptor in the bucket matching the first letter of `.name`, and keep the existing `nullptr` terminator at the end.
|
||||
|
||||
### The calling convention: zero heap allocation
|
||||
|
||||
Every dispatch site (`jsonconfig.c:98`, `commandoption.c:189`) follows the same pattern:
|
||||
|
||||
```c
|
||||
alignas(uint64_t) uint8_t optionBuf[FF_OPTION_MAX_SIZE]; // 256 bytes, on the stack
|
||||
baseInfo->initOptions(optionBuf);
|
||||
baseInfo->parseJsonObject(optionBuf, jsonVal); // JSONC path only
|
||||
baseInfo->printModule(optionBuf); // or generateJsonResult
|
||||
baseInfo->destroyOptions(optionBuf);
|
||||
```
|
||||
|
||||
`FF_OPTION_MAX_SIZE = 1 << 8` (256 bytes). Every module's `option.h` must end with:
|
||||
|
||||
```c
|
||||
static_assert(sizeof(FFCPUOptions) <= FF_OPTION_MAX_SIZE, "FFCPUOptions size exceeds maximum allowed size");
|
||||
```
|
||||
|
||||
**In other words, no module option struct may exceed 256 bytes.** This is a hard constraint, enforced at compile time.
|
||||
|
||||
The three dispatch entry points:
|
||||
|
||||
| Entry point | Location | Used for |
|
||||
|---|---|---|
|
||||
| `parseModuleJsonObject` | `common/impl/jsonconfig.c:90` | the `modules[]` array in a JSONC config |
|
||||
| `parseStructureCommand` | `common/impl/commandoption.c:181` | the colon-separated structure string |
|
||||
|
||||
### Build-time module discovery
|
||||
|
||||
`CMakeLists.txt:134` globs `src/modules/*/*.c`, keeps only the entries whose file name matches their directory name, and generates a `MODULE_DISABLE_<UPPER>` option for each:
|
||||
|
||||
```cmake
|
||||
file(GLOB FF_MODULE_SRCS CONFIGURE_DEPENDS RELATIVE "..." ".../src/modules/*/*.c")
|
||||
set(FF_MODULE_DIRS "")
|
||||
foreach(FF_MODULE_SRC ${FF_MODULE_SRCS})
|
||||
get_filename_component(FF_MODULE_DIR "${FF_MODULE_SRC}" DIRECTORY)
|
||||
get_filename_component(FF_MODULE_NAME "${FF_MODULE_SRC}" NAME_WE)
|
||||
if("${FF_MODULE_DIR}" STREQUAL "${FF_MODULE_NAME}")
|
||||
list(APPEND FF_MODULE_DIRS "${FF_MODULE_DIR}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
|
||||
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
|
||||
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
|
||||
endforeach()
|
||||
```
|
||||
|
||||
Because the glob matches sources rather than directories, a directory counts as a module only when `src/modules/<name>/<name>.c` exists. Empty directories — which git does not track, so they are easy to leave behind — and stray files are ignored instead of breaking the configure step with `Cannot find source file`.
|
||||
|
||||
### Module sources are discovered automatically
|
||||
|
||||
`FF_MODULE_DIRS` also drives the source list (`CMakeLists.txt:516`):
|
||||
|
||||
```cmake
|
||||
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
src/modules/${FF_MODULE_DIR}/${FF_MODULE_DIR}.c
|
||||
)
|
||||
endforeach()
|
||||
```
|
||||
|
||||
Consequences:
|
||||
|
||||
- `src/modules/<name>/<name>.c` is compiled as soon as the file exists — **there is no source list to edit for the module layer**.
|
||||
- The file name must match the directory name. A directory whose `.c` file is named differently, or has none at all, is silently not a module: a typo therefore shows up as a module missing from `fastfetch --list-modules`, not as a build error.
|
||||
- The glob uses `CONFIGURE_DEPENDS`, so with the Makefile and Ninja generators the build re-evaluates it and re-runs CMake when a module source is added or removed. With other generators, or to be safe, re-run `cmake -B build` explicitly.
|
||||
|
||||
Only the module layer is automated. Sources under `src/detection/` and `src/common/impl/` are **not** globbed and must still be listed by hand — see [step 5](#5-add-the-platform-sources-to-cmakeliststxt).
|
||||
|
||||
---
|
||||
|
||||
## Walkthrough: adding a module
|
||||
|
||||
Adding a `Foo` module, step by step.
|
||||
|
||||
### 1. Define the option struct
|
||||
|
||||
`src/modules/foo/option.h`:
|
||||
|
||||
```c
|
||||
#pragma once
|
||||
|
||||
#include "common/option.h"
|
||||
|
||||
typedef struct FFFooOptions {
|
||||
FFModuleArgs moduleArgs; // must be the first field
|
||||
bool showBar;
|
||||
} FFFooOptions;
|
||||
|
||||
static_assert(sizeof(FFFooOptions) <= FF_OPTION_MAX_SIZE, "FFFooOptions size exceeds maximum allowed size");
|
||||
```
|
||||
|
||||
`FFModuleArgs` must come first. It provides `key`, `format`, `outputColor`, `keyColor`, `keyIcon` and `keyWidth`; because it is the first field, `ffJsonConfigParseModuleArgs()` handles these generically, **so no parsing code is required**.
|
||||
|
||||
### 2. Implement detection
|
||||
|
||||
`src/detection/foo/foo.h`:
|
||||
|
||||
```c
|
||||
#pragma once
|
||||
#include "fastfetch.h"
|
||||
|
||||
typedef struct FFFooResult {
|
||||
FFstrbuf name;
|
||||
uint32_t count;
|
||||
} FFFooResult;
|
||||
|
||||
const char* ffDetectFoo(const FFFooOptions* options, FFFooResult* result);
|
||||
```
|
||||
|
||||
`src/detection/foo/foo_linux.c`:
|
||||
|
||||
```c
|
||||
#include "foo.h"
|
||||
|
||||
const char* ffDetectFoo(const FFFooOptions* options, FFFooResult* result) {
|
||||
// read /sys, /proc, sysfs, dbus, ...
|
||||
return nullptr; // success; return an error string on failure
|
||||
}
|
||||
```
|
||||
|
||||
**Write one file per target platform, plus a `foo_nosupport.c` fallback for the remaining platforms.** The repository currently contains 45 `*_nosupport.c` files serving this purpose.
|
||||
|
||||
### 3. Implement the module
|
||||
|
||||
`src/modules/foo/foo.c` — implement the six functions, then define the descriptor:
|
||||
|
||||
```c
|
||||
FFModuleBaseInfo ffFooModuleInfo = {
|
||||
.name = "Foo",
|
||||
.description = "Print foo information",
|
||||
.displayName = {
|
||||
.en = "Foo", .ar = "Foo", .cs = "Foo", .de = "Foo", .es = "Foo",
|
||||
.fr = "Foo", .gl = "Foo", .he = "Foo", .id = "Foo", .it = "Foo",
|
||||
.ja = "Foo", .ko = "Foo", .pl = "Foo", .pt = "Foo", .ru = "Foo",
|
||||
.tr = "Foo", .uk = "Foo", .vi = "Foo", .zh_CN = "Foo", .zh_TW = "Foo",
|
||||
},
|
||||
.initOptions = (void*) ffInitFooOptions,
|
||||
.destroyOptions = (void*) ffDestroyFooOptions,
|
||||
.parseJsonObject = (void*) ffParseFooJsonObject,
|
||||
.printModule = (void*) ffPrintFoo,
|
||||
.generateJsonResult = (void*) ffGenerateFooJsonResult,
|
||||
.generateJsonConfig = (void*) ffGenerateFooJsonConfig,
|
||||
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
|
||||
{ "Name", "name" },
|
||||
{ "Count", "count" },
|
||||
})),
|
||||
.defaultOrder = 73,
|
||||
};
|
||||
```
|
||||
|
||||
Points to note:
|
||||
|
||||
- **`displayName`** is a literal block of all 20 language fields; there is no shortcut macro, so copy the layout from an existing module: **`en`, `ar`, `cs`, `de`, `es`, `fr`, `gl`, `he`, `id`, `it`, `ja`, `ko`, `pl`, `pt`, `ru`, `tr`, `uk`, `vi`, `zh_CN`, `zh_TW`**.
|
||||
**Fill in all 20.** The struct is read by byte offset (see [Pitfalls](#6-localization-uses-byte-offsets-not-enums)) and there is **no fallback** — a missing field means the key prints empty in that language.
|
||||
- **`formatArgs`** must be exhaustive. It drives the placeholder list printed by `fastfetch -h foo-format`; **any placeholder omitted here is neither listed nor usable by the user.**
|
||||
- The `moduleFormat` section in `doc/json_schema.json` is generated by `fastfetch -h format-json`. Its metadata comes from `FFModuleBaseInfo::formatArgs`. To keep the schema and module descriptors consistent, do not edit the `moduleFormat` section in `doc/json_schema.json` by hand; update the module metadata and regenerate it instead.
|
||||
- **`defaultOrder`**: use the current maximum plus 1. Search the existing descriptors for `.defaultOrder =` before choosing a value; do not copy a hard-coded value from this document. Leaving it out, or setting it to `0`, excludes the module from the interactive `--gen-config` picker. Only `logo`, `command` and `custom` rely on this behavior, because they require user arguments or are invoked directly by the display layer.
|
||||
|
||||
### 4. Register it
|
||||
|
||||
- Add `#include "modules/foo/foo.h"` to `src/modules/modules.h`
|
||||
- Insert into the `F[]` array in `src/modules/modules.c`:
|
||||
|
||||
```c
|
||||
#if !FF_MODULE_DISABLE_FOO
|
||||
&ffFooModuleInfo,
|
||||
#endif
|
||||
```
|
||||
|
||||
`FF_MODULE_DISABLE_FOO` is generated by CMake — you do not define it yourself.
|
||||
|
||||
### 5. Add the platform sources to `CMakeLists.txt`
|
||||
|
||||
The module layer is discovered by glob (see [Build-time module discovery](#module-sources-are-discovered-automatically)), **but `src/detection/**` and `src/common/impl/**` are not**. `CMakeLists.txt` lists them explicitly, inside one mutually exclusive chain of platform blocks:
|
||||
|
||||
| Block | Line | Covers |
|
||||
|---|---|---|
|
||||
| `if(LINUX)` | `CMakeLists.txt:522` | Linux |
|
||||
| `elseif(ANDROID)` | `CMakeLists.txt:608` | Android (Termux) |
|
||||
| `elseif(FreeBSD)` | `CMakeLists.txt:691` | FreeBSD, MidnightBSD, DragonFly |
|
||||
| `elseif(NetBSD)` | `CMakeLists.txt:788` | NetBSD |
|
||||
| `elseif(OpenBSD)` | `CMakeLists.txt:872` | OpenBSD |
|
||||
| `elseif(APPLE)` | `CMakeLists.txt:959` | macOS / iOS |
|
||||
| `elseif(WIN32)` | `CMakeLists.txt:1046` | Windows |
|
||||
| `elseif(SunOS)` | `CMakeLists.txt:1121` | Solaris / illumos |
|
||||
| `elseif(Haiku)` | `CMakeLists.txt:1204` | Haiku |
|
||||
| `elseif(GNU)` | `CMakeLists.txt:1282` | GNU/Hurd |
|
||||
|
||||
Add the platform implementation to every block whose platform it supports, and `foo_nosupport.c` to every remaining block, so that all ten platforms still link:
|
||||
|
||||
```cmake
|
||||
elseif(FreeBSD)
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
...
|
||||
src/detection/foo/foo_bsd.c
|
||||
)
|
||||
```
|
||||
|
||||
Points to note:
|
||||
|
||||
- Exactly one block is compiled per build, so a file omitted from a block does not exist for that platform, and the link fails with an undefined reference to `ffDetectFoo()` — **on that platform only**. A missing entry therefore builds fine locally and fails in CI; this is why every block must be covered.
|
||||
- A platform may reuse another platform's implementation instead of a stub. `src/common/impl/networking_linux.c`, for example, is listed in nine of the ten blocks (all but `WIN32`). Check where the closest sibling module points before adding a new file.
|
||||
- `DragonFly` is handled by the inner `if(DragonFly)` sub-block inside the FreeBSD block (`CMakeLists.txt:773`); add the variant there, as `processes`, `top` and `wifi` do.
|
||||
- New helpers under `src/common/impl/` follow the same rule: they are not globbed, and each block that needs one must list it.
|
||||
- A few files are appended outside the platform chain because they depend on an option or on a specific feature — for example the proprietary GPU backends (`CMakeLists.txt:1379`) and `src/common/impl/wcwidth.c` (`CMakeLists.txt:1408`). Those are written by hand as well.
|
||||
|
||||
### 6. Modules that need warm-up
|
||||
|
||||
If your module needs a sampling interval (CPU usage) or a network round-trip (public IP, weather), also implement `ffPrepareFoo()` and register it in the switch inside `ffPrepareCommandOption()` in `common/impl/commandoption.c`, under the matching first-letter `case`. Six modules currently do this: CPUUsage, DiskIO, NetIO, PublicIP, Top and Weather.
|
||||
|
||||
### 7. Verify
|
||||
|
||||
```sh
|
||||
cmake -B build && cmake --build build -j
|
||||
./build/fastfetch -s foo --format json # JSON output
|
||||
./build/fastfetch -h foo-format # list formatArgs placeholders
|
||||
./build/fastfetch --gen-config # confirm it appears in the picker
|
||||
```
|
||||
|
||||
Note the `-format` suffix on the help flag: `fastfetch -h foo` is not supported; only `fastfetch -h foo-format` works.
|
||||
|
||||
If you added detection sources, re-check [step 5](#5-add-the-platform-sources-to-cmakeliststxt) before pushing: a platform block you missed compiles fine locally and fails only when that platform is built.
|
||||
|
||||
---
|
||||
|
||||
## Walkthrough: adding a logo
|
||||
|
||||
A new logo **must** have a corresponding "Logo Request" issue, linked from the PR with `Closes #1234`. Logo PRs without a linked issue are not accepted.
|
||||
|
||||
### 1. Add the ASCII file
|
||||
|
||||
```
|
||||
src/logo/ascii/<first-letter>/<distro>.txt
|
||||
```
|
||||
|
||||
For example `src/logo/ascii/d/distro.txt`. Directories are already split by first letter (`a/` … `z/`, plus `_/`).
|
||||
|
||||
The file is plain ASCII art with `$1` … `$9` as color placeholders:
|
||||
|
||||
```
|
||||
$3 ,-^-___
|
||||
$3 /\\\///
|
||||
$2refined.$1 /\\\\//
|
||||
```
|
||||
|
||||
- `$1` … `$9` map to palette slots 1–9; at most 9 are supported (`FASTFETCH_LOGO_MAX_COLORS = 9`)
|
||||
- `$$` is a literal `$`
|
||||
- Characters without a placeholder inherit the current color. Of the 530 existing logos, 239 use no placeholders at all (monochrome) and 291 do; **new logos should use placeholders**, as monochrome is a legacy style
|
||||
- Tabs are expanded to 4 spaces
|
||||
- Colors can be overridden with `--logo-color-1` … `--logo-color-9`
|
||||
|
||||
### 2. Register it in the `.inc`
|
||||
|
||||
CMake turns each `.txt` file into a `FASTFETCH_DATATEXT_LOGO_<UPPERCASE>` macro (`CMakeLists.txt:418`), but **the registry itself is maintained by hand**. Edit `src/logo/ascii/<first-letter>.inc`:
|
||||
|
||||
```c
|
||||
// src/logo/ascii/d.inc
|
||||
|
||||
#ifdef FASTFETCH_DATATEXT_LOGO_DISTRO
|
||||
// Distro
|
||||
{
|
||||
.names = { "Distro" }, // ID (preferred) or NAME from /etc/os-release, do NOT add both
|
||||
.lines = FASTFETCH_DATATEXT_LOGO_DISTRO,
|
||||
.colors = {
|
||||
FF_COLOR_FG_PRIMARY, // recommended for using as WHITE replacement, light-theme terminal friendly
|
||||
FF_COLOR_FG_BLUE, // preferred
|
||||
FF_COLOR_FG_256 "34",
|
||||
FF_COLOR_FG_RGB "0;255;0", // not recommended because of bad compatibility with raw TTY
|
||||
},
|
||||
},
|
||||
#endif
|
||||
```
|
||||
|
||||
- `names` is matched case-insensitively. Do not add names that differ only by case, because they can never be distinguished. Every name is also shown by `--list-logos`, so use user-friendly spelling such as an initial capital where appropriate.
|
||||
- For a new logo, use exactly one name unless there is a specific compatibility reason to add more. That name should first be the `ID` from `/etc/os-release`. If the ID cannot distinguish this logo from another logo for the same OS, use the `NAME` value instead. `logoGetBuiltinDetected` in `src/logo/logo.c` documents the detection order: `ID`, then `NAME`, then tokens from `ID_LIKE`, then the platform name fallback. Manual `logo-source` selection uses the name directly.
|
||||
- `colors` is positional — entry 0 is `$1`, entry 1 is `$2`, and so on
|
||||
- `colors[0]` also becomes the title color and `colors[1]` the key color, unless the user overrides them
|
||||
|
||||
If the same OS has multiple logo variants, mark the variant explicitly with `.type`:
|
||||
|
||||
```c
|
||||
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
|
||||
```
|
||||
|
||||
Use `FF_LOGO_LINE_TYPE_ALTER_BIT` for an alternate logo, `FF_LOGO_LINE_TYPE_SMALL_BIT` for a small logo, or combine the flags when both apply. This is also a lookup optimization: a logo marked `FF_LOGO_LINE_TYPE_SMALL_BIT` is considered only for `type = small`, while a logo marked `FF_LOGO_LINE_TYPE_ALTER_BIT` is never selected by automatic detection. Alternate logos are available only when explicitly requested through `-l <source>`.
|
||||
|
||||
### 3. Verify
|
||||
|
||||
```sh
|
||||
./build/fastfetch -l distro
|
||||
./build/fastfetch --list-logos | grep -i distro
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Platform conventions
|
||||
|
||||
The `detection/` layer picks its implementation by **filename suffix**; the choice is made at build time from `CMAKE_SYSTEM_NAME`:
|
||||
|
||||
| Suffix | Platform |
|
||||
|---|---|
|
||||
| `_linux.c` | Linux |
|
||||
| `_apple.c` / `_apple.m` | macOS / iOS (`.m` for Objective-C) |
|
||||
| `_windows.c` / `_windows.cpp` | Windows (`.cpp` for WinRT / WMI) |
|
||||
| `_bsd.c` | FreeBSD |
|
||||
| `_nbsd.c` | NetBSD |
|
||||
| `_obsd.c` | OpenBSD |
|
||||
| `_sunos.c` | Solaris / illumos |
|
||||
| `_haiku.c` / `.cpp` | Haiku |
|
||||
| `_android.c` | Android (Termux) |
|
||||
| `_gnu.c` | GNU/Hurd |
|
||||
| `_nosupport.c` | Empty fallback |
|
||||
|
||||
Current distribution of platform files in `detection/`:
|
||||
|
||||
| Suffix | Files | Suffix | Files |
|
||||
|---|---|---|---|
|
||||
| `_linux` | 58 | `_obsd` | 16 |
|
||||
| `_windows` | 47 | `_haiku` | 13 |
|
||||
| `_apple` | 46 | `_android` | 12 |
|
||||
| `_nosupport` | 45 | `_gnu` | 3 |
|
||||
| `_bsd` | 29 | | |
|
||||
| `_sunos` | 19 | | |
|
||||
| `_nbsd` | 17 | | |
|
||||
|
||||
(These are repository inventory figures, counting `.c`, `.m`, `.cpp`, and `.h`; they may change as platforms and modules are added.)
|
||||
|
||||
**Do not accumulate `#ifdef __linux__` blocks in a single file.** When adding platform support, copy the closest existing implementation and change the suffix.
|
||||
|
||||
`common/` follows the same convention: `common/impl/` contains `io_unix.c` / `io_windows.c`, `netif_linux.c` / `netif_apple.c` / `netif_bsd.c` and similar files, with `common/apple/`, `common/windows/` and `common/haiku/` holding platform-specific helpers.
|
||||
|
||||
When several platform suffixes could apply, use the most specific implementation supported by the build system (for example, `_nbsd.c` instead of the generic `_bsd.c` on NetBSD). Keep the generic file as the fallback for platforms that share its conventions.
|
||||
|
||||
---
|
||||
|
||||
## Code style
|
||||
|
||||
### Formatting
|
||||
|
||||
The project uses clang-format with `BasedOnStyle: LLVM` plus these key overrides:
|
||||
|
||||
```yaml
|
||||
IndentWidth: 4
|
||||
UseTab: Never
|
||||
ColumnLimit: 0 # never wrap
|
||||
InsertBraces: true # braces even on single-statement ifs
|
||||
PointerAlignment: Left # char* p, not char *p
|
||||
SortIncludes: Never # include order is managed by hand
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
BinPackParameters: false # all params on one line, or one per line
|
||||
```
|
||||
|
||||
Before committing:
|
||||
|
||||
```sh
|
||||
clang-format -i src/modules/foo/*.c src/modules/foo/*.h
|
||||
```
|
||||
|
||||
`src/3rdparty/**`, `build/**` and `src/logo/builtin.c` are listed in `.clang-format-ignore` — **do not reformat them.**
|
||||
|
||||
`.editorconfig` specifies LF line endings, a 4-space indent, a final newline, and trailing whitespace trimmed (except in Markdown).
|
||||
|
||||
### Naming
|
||||
|
||||
| Kind | Convention | Example |
|
||||
|---|---|---|
|
||||
| Functions | `ff` + PascalCase | `ffDetectCPU`, `ffPrintCPU` |
|
||||
| Types | `FF` + PascalCase | `FFCPUResult`, `FFModuleBaseInfo` |
|
||||
| Variables / fields | camelCase | `coresPhysical`, `keyWidth` |
|
||||
| Macros | SCREAMING_SNAKE_CASE | `FF_OPTION_MAX_SIZE` |
|
||||
| Module options | `FF<Name>Options` | `FFCPUOptions` |
|
||||
| Detection results | `FF<Name>Result` | `FFCPUResult` |
|
||||
|
||||
### Compiler warnings
|
||||
|
||||
The build enables `-Wall -Wextra -Wconversion` plus several `-Werror`s:
|
||||
|
||||
```
|
||||
-Werror=uninitialized -Werror=return-type -Werror=vla
|
||||
-Werror=incompatible-pointer-types -Werror=implicit-function-declaration -Werror=int-conversion
|
||||
```
|
||||
|
||||
**`-Wconversion` is strict**: every implicit narrowing conversion needs an explicit cast.
|
||||
|
||||
---
|
||||
|
||||
## Commit messages
|
||||
|
||||
Format:
|
||||
|
||||
```
|
||||
<Scope>[ (Platform)]: <third-person singular verb> <object>
|
||||
```
|
||||
|
||||
Scope is the module or subsystem name; Platform is optional. Use the third-person singular present tense, capitalize the first letter, no trailing period.
|
||||
|
||||
Real examples from recent history:
|
||||
|
||||
```
|
||||
Processes (Haiku): honors `options->countKprocs`
|
||||
WM (macOS): improves reliability of WM plugin detection
|
||||
Memory (Windows): prefers `NQSI`
|
||||
Top (Linux): improves performance of `stat` parsing
|
||||
Logo (Builtin): adds omarchy
|
||||
Global: introduces global macro `FF_PATH_PKG_BASE` to replace `_PATH_LOCALBASE`
|
||||
LM (OpenBSD): adds support
|
||||
CI: disables fail on alert
|
||||
Doc: updates README
|
||||
Presets: moves `top` to the bottom of running modules [ci skip]
|
||||
```
|
||||
|
||||
Common verbs: `adds`, `removes`, `fixes`, `improves`, `updates`, `corrects`, `prefers`, `honors`, `disables`, `enables`, `introduces`, `detects`, `reports`, `skips`, `uses`.
|
||||
|
||||
Scopes in use: `Top`, `Processes`, `Memory`, `Logo (Builtin)`, `CI`, `Doc`, `Presets`, `Global`, plus individual module names.
|
||||
|
||||
Documentation-only changes use a `[ci skip]` suffix.
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
User-visible changes go into `CHANGELOG.md`, under the topmost version heading:
|
||||
|
||||
```markdown
|
||||
# Unreleased
|
||||
|
||||
Changes:
|
||||
* The DE / WM / LM modules now reports the full name ...
|
||||
|
||||
Features:
|
||||
* Added Top module to print processes with the highest CPU, memory or disk I/O usage. (Top)
|
||||
* Improved Wi-Fi module
|
||||
* Added Wifi channel width detection, exposed via `{channel-width}` in custom format.
|
||||
* Improved Wifi channel frequency accuracy on Windows, macOS.
|
||||
* Added Battery detection support on SunOS. (Battery, SunOS)
|
||||
|
||||
Bugfixes:
|
||||
* Fixed I/O rate calculation precision in DiskIO and NetIO. (DiskIO / NetIO)
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- Three sections: `Changes:` (behavior changes), `Features:`, `Bugfixes:`
|
||||
- Past tense: `Added` / `Fixed` / `Improved`
|
||||
- End each entry with its scope: `(Module, Platform)` or `(ModuleA / ModuleB)`
|
||||
- Sub-bullets are indented 4 spaces
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
Tests are standalone executables in `tests/` using a `VERIFY` macro that exits non-zero on failure:
|
||||
|
||||
```c
|
||||
#define VERIFY(expression) \
|
||||
if (!(expression)) testFailed(&strbuf, #expression, __LINE__)
|
||||
```
|
||||
|
||||
Current tests: `strbuf.c`, `list.c`, `format.c`, `color.c`, `duration.c`, `strutil.c`.
|
||||
|
||||
```sh
|
||||
cmake -B build -DBUILD_TESTS=On
|
||||
cmake --build build
|
||||
cd build && ctest --output-on-failure
|
||||
```
|
||||
|
||||
Coverage focuses on the core data structures and the formatting engine in `common/`. The `detection/` layer has no automated tests, because it depends on the state of a running system; it is instead covered by the CI matrix — 20 workflows under `.github/workflows/` spanning Linux (including musl, loong64, armv7l, i686), Android, macOS, Windows, FreeBSD, NetBSD, OpenBSD, DragonFly, Solaris, OmniOS and Haiku, plus a benchmark job. The Android job is the one that does not run what it builds: it cross-compiles for arm64-v8a, armeabi-v7a and x86_64 with the NDK at API 24 and inspects the result instead, because CI has no device or emulator. Its dynamic-symbol checks are what keep the weak references the platform branch depends on from turning into load failures on older devices.
|
||||
|
||||
**If you modify `common/FFstrbuf.h`, `common/format.h` or `common/color.h`, extend the corresponding test.**
|
||||
|
||||
---
|
||||
|
||||
## Pull requests
|
||||
|
||||
1. **Open an issue first** (feature request / bug report / logo request) to confirm that the change is wanted before investing effort. Templates are in `.github/ISSUE_TEMPLATE/`.
|
||||
2. Branch off `dev` — **`dev` is the main development branch**, not `master`.
|
||||
3. Follow the [commit message convention](#commit-messages).
|
||||
4. Update `CHANGELOG.md` for user-visible changes.
|
||||
5. Open the PR against `dev` and fill in `.github/pull_request_template.md`:
|
||||
- Summary
|
||||
- Related issue (**required for new logos**; otherwise the PR is not accepted)
|
||||
- Changes
|
||||
- Screenshots (required for visual changes)
|
||||
- Checklist: confirm you tested locally
|
||||
|
||||
### Pre-submit checklist
|
||||
|
||||
```sh
|
||||
clang-format -i <changed files> # format
|
||||
cmake -B build -DBUILD_TESTS=On && cmake --build build -j
|
||||
cd build && ctest --output-on-failure # tests
|
||||
./build/fastfetch --format json # verify JSON output is well-formed
|
||||
./build/fastfetch -c presets/all.jsonc --stat false # smoke-test every module
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pitfalls
|
||||
|
||||
The following points are easy to misinterpret when reading this codebase. Most of them follow from the startup-time objective described above.
|
||||
|
||||
### 1. Option structs must not exceed 256 bytes
|
||||
|
||||
`FF_OPTION_MAX_SIZE = 1 << 8`. Exceeding it is caught at compile time by `static_assert`. Do not attempt to increase the value: it determines the stack cost of every module invocation.
|
||||
|
||||
### 2. `FF_MODULE_DISABLE_*` controls registration, not compilation
|
||||
|
||||
```c
|
||||
// verbatim from the top of modules/modules.c:
|
||||
// FF_MODULE_DISABLE_<module> only controls if the module is registered,
|
||||
// the module code itself is still compiled.
|
||||
// We rely `LTO` to remove the unused code (only enabled in Release mode)
|
||||
```
|
||||
|
||||
**Disabling modules in a Debug build does not shrink the binary.** LTO is enabled whenever `CMAKE_BUILD_TYPE != Debug`, and the default `RelWithDebInfo` already satisfies that — so measure size with `RelWithDebInfo` or `Release`, never with `Debug`. (The "Release mode" wording in the comment above is imprecise.)
|
||||
|
||||
### 3. Only two things are discovered by glob
|
||||
|
||||
The module sources (`CMakeLists.txt:134`) and the logo `.txt` files (`CMakeLists.txt:429`). Both use `CONFIGURE_DEPENDS`, so with the Makefile and Ninja generators the build re-checks the glob and re-runs CMake when the result changes; other generators (Visual Studio and Xcode in particular) do not track it as reliably. Re-run `cmake -B build` after adding a module source or a logo file rather than relying on that behavior.
|
||||
|
||||
Everything else — `src/detection/**` and `src/common/impl/**`, and any source outside the platform chain — must be listed in `CMakeLists.txt` by hand; see [step 5](#5-add-the-platform-sources-to-cmakeliststxt).
|
||||
|
||||
### 4. CLI module options are removed
|
||||
|
||||
Per-module command-line flags such as `--cpu-temp` are **no longer supported**. The only job of `ffParseModuleOptions` now is to translate the flag into a JSON key, then `exit(477)` with a pointer to the config file:
|
||||
|
||||
```
|
||||
Error: Unsupported module option: --cpu-temp
|
||||
Support of module options has been removed. Please add the flag to the JSON config instead.
|
||||
Example (demonstration only): `{ "modules": [ { "type": "cpu", "temp": true } ] }`
|
||||
```
|
||||
|
||||
**JSONC is the first-class configuration interface; the command line is not.** When adding module options, implement `parseJsonObject` only — do not add a CLI branch.
|
||||
|
||||
### 5. `defaultOrder = 0` hides a module from `--gen-config`
|
||||
|
||||
`collectModuleInfos` (`genconfig.c:186`) skips any module whose `defaultOrder` is `0`. C zero-initializes the field, so **omitting it is the same as setting it to 0**. Only `logo`, `command` and `custom` rely on this intentionally.
|
||||
|
||||
`defaultOrder` affects only the ordering in the interactive `--gen-config` picker; it has no effect on the runtime output order.
|
||||
|
||||
### 6. Localization uses byte offsets, not enums
|
||||
|
||||
`instance.config.display.keyLanguage` does not hold a language enum — it holds a **byte offset** such as `offsetof(FFModuleDisplayName, zh_CN)`. The macro `FF_MODULE_GET_DISPLAY_NAME` (`common/option.h:123`) does plain pointer arithmetic with it:
|
||||
|
||||
```c
|
||||
#define FF_MODULE_GET_DISPLAY_NAME(moduleName) \
|
||||
(*(const char**) ((uint8_t*) &ff ## moduleName ## ModuleInfo.displayName + instance.config.display.keyLanguage))
|
||||
```
|
||||
|
||||
Consequence: **the field order of `FFModuleDisplayName` must never change** — reordering the fields silently corrupts the output for every language.
|
||||
|
||||
### 7. The `multithreading` option has limited effect
|
||||
|
||||
The global `multithreading` option currently takes effect in exactly one place: `common/impl/networking_linux.c:339`. Modules are still printed sequentially. Modules that need concurrency go through the `ffPrepare*` warm-up hooks instead, which start sampling or fire off requests before the print loop begins.
|
||||
|
||||
This may change in the future. The main blocking issue is that there are dependencies between different modules.
|
||||
|
||||
### 8. Do not print or read configuration inside `detection/`
|
||||
|
||||
The `detection/` layer must stay pure: read system state, fill a struct, return an error string. Any `printf` or any read of `instance.config` there is a design error.
|
||||
|
||||
Use `FF_DEBUG` (`src/common/debug.h`) for logging.
|
||||
|
||||
### 9. `src/logo/builtin.c` and `3rdparty/` are formatting-exempt
|
||||
|
||||
The former is a large generated and hand-maintained data table; the latter is upstream code. Both are excluded via `.clang-format-ignore`; do not reformat either.
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
- Configuration guide: <https://github.com/fastfetch-cli/fastfetch/wiki/Configuration>
|
||||
- JSON schema: `doc/json_schema.json`
|
||||
- Example presets: `presets/` (`all.jsonc`, `neofetch.jsonc`, `ci.jsonc`, …)
|
||||
- Man page source: `doc/fastfetch.1.in`
|
||||
- Code generation scripts: `scripts/` (`gen-pciids.py`, `gen-amdgpuids.py`, `gen-man.py`)
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Fastfetch
|
||||
|
||||
[](https://fastfetch-cli.github.io/fastfetch/dev/bench)
|
||||
[](https://github.com/fastfetch-cli/fastfetch/actions)
|
||||
[](https://github.com/fastfetch-cli/fastfetch/blob/dev/LICENSE)
|
||||
[](https://github.com/fastfetch-cli/fastfetch/graphs/contributors)
|
||||
@@ -13,7 +14,9 @@
|
||||
[](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 illumos (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" />
|
||||
@@ -97,7 +100,7 @@ See the Wiki: https://github.com/fastfetch-cli/fastfetch/wiki/Building
|
||||
* View all data that fastfetch detects: `fastfetch -s <module1>[:<module2>][:<module3>] --format json`
|
||||
* Display help messages: `fastfetch --help`
|
||||
* Generate a minimal config file: `fastfetch [-s <module1>[:<module2>]] --gen-config [</path/to/config.jsonc>]`
|
||||
* Use: `--gen-config-full` to generate a full config file with all optional options
|
||||
* Use [The online configuration generator](https://fastfetch-cli.github.io/fastfetch-config/) to generate a full config file with all optional options
|
||||
|
||||
## Customization
|
||||
|
||||
@@ -197,7 +200,8 @@ See [#1096](https://github.com/fastfetch-cli/fastfetch/issues/1096).
|
||||
|
||||
### Q: Fastfetch shows fewer dpkg packages than neofetch. Is it a bug?
|
||||
|
||||
Neofetch incorrectly counts `rc` packages (packages that have been removed but still have configuration files remaining). See bug: https://github.com/dylanaraps/neofetch/issues/2278
|
||||
1. Neofetch incorrectly counts `rc` packages for apt (packages that have been removed but still have configuration files remaining). See bug: https://github.com/dylanaraps/neofetch/issues/2278
|
||||
2. Neofetch incorrectly counts `gpg-pubkey` as packages for rpm. You may check the results of `dnf list --installed | wc -l` and `rpm -qa | wc -l` to see the difference.
|
||||
|
||||
### Q: I use Debian / Ubuntu / Debian-derived distro. My GPU is detected as `XXXX Device XXXX (VGA compatible)`. Is this a bug?
|
||||
|
||||
@@ -244,7 +248,20 @@ Set the key to a white space.
|
||||
|
||||
### Q: How can I display images on Windows?
|
||||
|
||||
As of April 2025:
|
||||
#### Windows Terminal
|
||||
|
||||
Windows Terminal supports the sixel image protocol only.
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"logo": {
|
||||
"type": "sixel", // DO NOT USE "auto"
|
||||
"source": "C:/path/to/image.png", // ~ can be used to refer to the Windows user home directory
|
||||
"width": <image-width-in-chars>, // Optional
|
||||
"height": <image-height-in-chars> // Optional
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### mintty and Wezterm
|
||||
|
||||
@@ -256,38 +273,7 @@ In `config.jsonc`:
|
||||
"logo": {
|
||||
"type": "iterm",
|
||||
"source": "C:/path/to/image.png",
|
||||
"width": <num-in-chars>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Windows Terminal
|
||||
|
||||
Windows Terminal supports the sixel image protocol only.
|
||||
|
||||
* If you installed fastfetch through MSYS2:
|
||||
1. Install imagemagick: `pacman -S mingw-w64-<subsystem>-x86_64-imagemagick`
|
||||
2. In `config.jsonc`:
|
||||
```jsonc
|
||||
{
|
||||
"logo": {
|
||||
"type": "sixel", // DO NOT USE "auto"
|
||||
"source": "C:/path/to/image.png", // Do NOT use `~` as fastfetch is a native Windows program and doesn't apply cygwin path conversion
|
||||
"width": <image-width-in-chars>, // Optional
|
||||
"height": <image-height-in-chars> // Optional
|
||||
}
|
||||
}
|
||||
```
|
||||
* If you installed fastfetch via scoop or downloaded the binary directly from the GitHub Releases page:
|
||||
1. Convert your image manually to sixel format using [any online image conversion service](https://www.google.com/search?q=convert+image+to+sixel)
|
||||
2. In `config.jsonc`:
|
||||
```jsonc
|
||||
{
|
||||
"logo": {
|
||||
"type": "raw", // DO NOT USE "auto"
|
||||
"source": "C:/path/to/image.sixel",
|
||||
"width": <image-width-in-chars>, // Required
|
||||
"height": <image-height-in-chars> // Required
|
||||
"width": <image-width-in-chars>
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -332,10 +318,10 @@ If you find Fastfetch useful, please consider donating.
|
||||
|
||||
Give us a star to show your support!
|
||||
|
||||
<a href="https://star-history.com/#fastfetch-cli/fastfetch&Date">
|
||||
<a href="https://star-history.dera.page/#fastfetch-cli/fastfetch&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=fastfetch-cli/fastfetch&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=fastfetch-cli/fastfetch&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=fastfetch-cli/fastfetch&type=Date" />
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://star-history.dera.page/svg?repos=fastfetch-cli/fastfetch&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://star-history.dera.page/svg?repos=fastfetch-cli/fastfetch&type=Date" />
|
||||
<img alt="Star History Chart" src="https://star-history.dera.page/svg?repos=fastfetch-cli/fastfetch&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
@@ -33,7 +33,9 @@ _fastfetch() {
|
||||
if type _filedir &>/dev/null; then
|
||||
_filedir
|
||||
elif type compgen &>/dev/null; then
|
||||
COMPREPLY+=($(compgen -f -- "$cur"))
|
||||
local -a files
|
||||
readarray -t files < <(compgen -f -- "$cur")
|
||||
COMPREPLY+=("${files[@]}")
|
||||
fi
|
||||
return
|
||||
;;
|
||||
|
||||
+56
-24
@@ -63,46 +63,78 @@ echo '
|
||||
import json, subprocess, sys
|
||||
|
||||
|
||||
def escape_fish_dq(s: str) -> str:
|
||||
"""Escape special characters for Fish double quotes to prevent unexpected expansions. One slash in output needs four slashes in script"""
|
||||
return str(s).replace("\\\\", "\\\\\\\\").replace("\\"", "\\\\\\"").replace("$", "\\\\$")
|
||||
|
||||
|
||||
def main():
|
||||
data: dict[str, list[dict]] = json.loads(subprocess.check_output(["fastfetch", "--help-raw"]))
|
||||
|
||||
enum_functions = []
|
||||
complete_commands = []
|
||||
|
||||
for key in data:
|
||||
for flag in data[key]:
|
||||
if flag["long"] == "logo-color-[1-9]":
|
||||
long_name = flag.get("long", "")
|
||||
|
||||
# Handle pseudo flag logo-color-[1-9]
|
||||
if long_name == "logo-color-[1-9]":
|
||||
safe_desc_global = escape_fish_dq(flag.get("desc", ""))
|
||||
for i in range(1, 10):
|
||||
print(f"""complete -c fastfetch -d "{flag["desc"]}" -l "logo-color-{i}" -x -a "(__fastfetch_complete_color)" """)
|
||||
complete_commands.append(f"complete -c fastfetch -d \"{safe_desc_global}\" -l \"logo-color-{i}\" -x -a \"(__fastfetch_complete_color)\"")
|
||||
continue
|
||||
|
||||
if flag.get("pseudo", False):
|
||||
continue
|
||||
|
||||
command_prefix = f"""complete -c fastfetch -d "{flag["desc"]}" -l "{flag["long"]}\""""
|
||||
safe_desc_global = escape_fish_dq(flag.get("desc", ""))
|
||||
command_prefix = f"complete -c fastfetch -d \"{safe_desc_global}\" -l \"{long_name}\""
|
||||
|
||||
if "short" in flag:
|
||||
command_prefix += f""" -o {flag["short"]}"""
|
||||
short_name = flag["short"]
|
||||
command_prefix += f" -o {short_name}"
|
||||
|
||||
if "arg" in flag:
|
||||
type: str = flag["arg"]["type"]
|
||||
if type == "bool":
|
||||
print(f"{command_prefix} -x -a \"(__fastfetch_complete_bool)\"")
|
||||
elif type == "color":
|
||||
print(f"{command_prefix} -x -a \"(__fastfetch_complete_color)\"")
|
||||
elif type == "command":
|
||||
print(f"{command_prefix} -x -a \"(__fastfetch_complete_command)\"")
|
||||
elif type == "config":
|
||||
print(f"{command_prefix} -x -a \"(__fastfetch_complete_config)\"")
|
||||
elif type == "enum":
|
||||
temp: str = " ".join(flag["arg"]["enum"])
|
||||
print(f"{command_prefix} -x -a \"{temp}\"")
|
||||
elif type == "logo":
|
||||
print(f"{command_prefix} -x -a \"(__fastfetch_complete_logo)\"")
|
||||
elif type == "structure":
|
||||
print(f"{command_prefix} -x -a \"(__fish_complete_list : __fastfetch_complete_structure)\"")
|
||||
elif type == "path":
|
||||
print(f"{command_prefix} -r -F")
|
||||
arg_type: str = flag["arg"]["type"]
|
||||
if arg_type == "bool":
|
||||
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_bool)\"")
|
||||
elif arg_type == "color":
|
||||
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_color)\"")
|
||||
elif arg_type == "command":
|
||||
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_command)\"")
|
||||
elif arg_type == "config":
|
||||
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_config)\"")
|
||||
elif arg_type == "enum":
|
||||
# Sanitize flag name to create a valid Fish function name
|
||||
func_name = long_name.replace("-", "_").replace("[", "_").replace("]", "_")
|
||||
func_name = f"__fastfetch_complete_enum_{func_name}"
|
||||
|
||||
# Generate the enum function
|
||||
func_lines = [f"function {func_name}"]
|
||||
for enum_val, enum_desc in flag["arg"]["enum"].items():
|
||||
safe_val = escape_fish_dq(enum_val)
|
||||
safe_desc = escape_fish_dq(enum_desc)
|
||||
func_lines.append(f" echo -e \"{safe_val}\\t{safe_desc}\"")
|
||||
func_lines.append("end\n")
|
||||
enum_functions.append("\n".join(func_lines))
|
||||
|
||||
# Append complete command pointing to the new function
|
||||
complete_commands.append(f"{command_prefix} -x -a \"({func_name})\"")
|
||||
elif arg_type == "logo":
|
||||
complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_logo)\"")
|
||||
elif arg_type == "structure":
|
||||
complete_commands.append(f"{command_prefix} -x -a \"(__fish_complete_list : __fastfetch_complete_structure)\"")
|
||||
elif arg_type == "path":
|
||||
complete_commands.append(f"{command_prefix} -r -F")
|
||||
else:
|
||||
print(f"{command_prefix} -x")
|
||||
complete_commands.append(f"{command_prefix} -x")
|
||||
else:
|
||||
print(f"{command_prefix} -f")
|
||||
complete_commands.append(f"{command_prefix} -f")
|
||||
|
||||
# Print generated enum functions first, then complete rules
|
||||
print("\n".join(enum_functions))
|
||||
print("\n".join(complete_commands))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Vendored
+84
@@ -1,3 +1,87 @@
|
||||
fastfetch (2.68.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.68.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 02 Sep 2026 10:47:51 +0800
|
||||
|
||||
fastfetch (2.68.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.68.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Tue, 01 Sep 2026 16:34:36 +0800
|
||||
|
||||
fastfetch (2.67.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.67.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Thu, 06 Aug 2026 16:22:22 +0800
|
||||
|
||||
fastfetch (2.66.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.66.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Fri, 10 Jul 2026 15:13:37 +0800
|
||||
|
||||
fastfetch (2.65.2~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.65.2
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 29 Jun 2026 10:05:47 +0800
|
||||
|
||||
fastfetch (2.65.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.65.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 24 Jun 2026 14:21:06 +0800
|
||||
|
||||
fastfetch (2.64.2~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.64.2
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 08 Jun 2026 15:45:11 +0800
|
||||
|
||||
fastfetch (2.63.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.63.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Fri, 15 May 2026 09:31:54 +0800
|
||||
|
||||
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
|
||||
|
||||
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, python3-setuptools
|
||||
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, libdrm-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
@@ -10,7 +10,7 @@ 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 )
|
||||
CODENAMES=( jammy noble plucky questing resolute stonking )
|
||||
DRY_RUN=0
|
||||
|
||||
if [[ -d "$OUT_DIR/build" ]]; then
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{
|
||||
"short": "h",
|
||||
"long": "help",
|
||||
"desc": "Display this help message or help for a specific command",
|
||||
"desc": "Show this help message or help for a specific command",
|
||||
"arg": {
|
||||
"type": "command",
|
||||
"optional": true
|
||||
@@ -12,11 +12,11 @@
|
||||
{
|
||||
"short": "v",
|
||||
"long": "version",
|
||||
"desc": "Show the full version of fastfetch"
|
||||
"desc": "Show the full Fastfetch version"
|
||||
},
|
||||
{
|
||||
"long": "version-raw",
|
||||
"desc": "Display the raw version string (major.minor.patch)"
|
||||
"desc": "Show the raw version string (major.minor.patch)"
|
||||
},
|
||||
{
|
||||
"long": "list-config-paths",
|
||||
@@ -36,7 +36,7 @@
|
||||
},
|
||||
{
|
||||
"long": "list-presets",
|
||||
"desc": "List presets that fastfetch knows about",
|
||||
"desc": "List available presets",
|
||||
"remark": "Presets can be loaded with \"--config <preset-name>\""
|
||||
},
|
||||
{
|
||||
@@ -46,15 +46,15 @@
|
||||
},
|
||||
{
|
||||
"long": "print-logos",
|
||||
"desc": "Display available logos"
|
||||
"desc": "Show available logos"
|
||||
},
|
||||
{
|
||||
"long": "print-structure",
|
||||
"desc": "Display the default structure"
|
||||
"desc": "Show the default structure"
|
||||
},
|
||||
{
|
||||
"long": "format",
|
||||
"desc": "Set output format",
|
||||
"desc": "Set the output format",
|
||||
"arg": {
|
||||
"type": "enum",
|
||||
"enum": {
|
||||
@@ -77,12 +77,22 @@
|
||||
},
|
||||
{
|
||||
"long": "dynamic-interval",
|
||||
"desc": "Keep fastfetch open and update the output every <num> milliseconds",
|
||||
"remark": "0 (default) to disable the behavior; don't work with --json",
|
||||
"desc": "Keep Fastfetch running and refresh the output every <num> milliseconds",
|
||||
"remark": "Set to 0 (default) to disable this behavior; does not work with --json",
|
||||
"arg": {
|
||||
"type": "num",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "watch",
|
||||
"short": "w",
|
||||
"desc": "Alias of dynamic-interval, but the value is in seconds instead of milliseconds. If no value is provided, it defaults to 1 second",
|
||||
"arg": {
|
||||
"type": "num",
|
||||
"optional": true,
|
||||
"default": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"Config": [
|
||||
@@ -90,33 +100,15 @@
|
||||
"short": "c",
|
||||
"long": "config",
|
||||
"desc": "Specify the config file or preset to load",
|
||||
"remark": "The file will be searched according to the order shown by \"fastfetch --list-config-paths\". Use \"-\" to read config from stdin or \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for more info",
|
||||
"remark": "The file is searched in the order shown by \"fastfetch --list-config-paths\". Use \"-\" to read config from stdin, or \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for details",
|
||||
"arg": {
|
||||
"type": "config"
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "gen-config",
|
||||
"desc": "Generate a minimal config file at the specified path",
|
||||
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Will print the generated config if <path> is \"-\"",
|
||||
"arg": {
|
||||
"type": "path",
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "gen-config-full",
|
||||
"desc": "Generate a full config file with all optional options at the specified path",
|
||||
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Will print the generated config if <path> is \"-\"",
|
||||
"arg": {
|
||||
"type": "path",
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "gen-config-force",
|
||||
"desc": "Generate a config file at the specified path, overwriting any existing file",
|
||||
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\"",
|
||||
"desc": "Interactively generate a config file at the specified path",
|
||||
"remark": "Opens an interactive configuration UI when run in a terminal. Falls back to non-interactive generation when env-var `$NO_COLOR` is set or stdin/stdout is not a TTY. Defaults to \"~/.config/fastfetch/config.jsonc\". Prints the generated config if <path> is \"-\"",
|
||||
"arg": {
|
||||
"type": "path",
|
||||
"optional": true
|
||||
@@ -152,7 +144,7 @@
|
||||
},
|
||||
{
|
||||
"long": "ds-force-drm",
|
||||
"desc": "Specify whether only DRM should be used to detect displays",
|
||||
"desc": "Specify whether display detection should use DRM only",
|
||||
"remark": [
|
||||
"Use this option if you encounter problems with other detection methods.",
|
||||
"Linux only"
|
||||
@@ -170,7 +162,7 @@
|
||||
},
|
||||
{
|
||||
"long": "detect-version",
|
||||
"desc": "Specify whether to detect and display versions of terminal, shell, editor, and others",
|
||||
"desc": "Specify whether to detect and display versions for terminal, shell, editor, and others",
|
||||
"remark": "Mainly for benchmarking",
|
||||
"arg": {
|
||||
"type": "bool",
|
||||
@@ -183,31 +175,32 @@
|
||||
{
|
||||
"short": "l",
|
||||
"long": "logo",
|
||||
"desc": "Set the logo source. Use \"none\" to disable the logo",
|
||||
"remark": "Should be the name of a built-in logo or a path to an image file. See also https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options",
|
||||
"desc": "Set the logo source. Use \"none\" to disable logo output",
|
||||
"remark": "Use either a built-in logo name or a path to an image file. See also https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options",
|
||||
"arg": {
|
||||
"type": "logo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "logo-type",
|
||||
"desc": "Set the type of the logo specified in \"--logo\"",
|
||||
"desc": "Set the type of logo specified by \"--logo\"",
|
||||
"remark": "See also https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options",
|
||||
"arg": {
|
||||
"type": "enum",
|
||||
"enum": {
|
||||
"auto": "If something is given, first try built-in, then file. Otherwise detect logo",
|
||||
"auto": "If a value is provided, try built-in first, then file. Otherwise auto-detect the logo",
|
||||
"builtin": "Built-in ASCII art",
|
||||
"small": "Built-in ASCII art, small version",
|
||||
"file": "Text file, printed with color code replacement",
|
||||
"file-raw": "Text file, printed as is",
|
||||
"data": "Text data, printed with color code replacement",
|
||||
"data-raw": "Text data, printed as is",
|
||||
"sixel": "Image file, printed as sixel codes",
|
||||
"kitty": "Image file, printed using kitty graphics protocol",
|
||||
"command-raw": "Command to run, whose output is printed as is",
|
||||
"sixel": "Image file, rendered as sixel",
|
||||
"kitty": "Image file, rendered using the Kitty graphics protocol",
|
||||
"kitty-direct": "Image file, tells the terminal emulator to read image data from the specified file",
|
||||
"kitty-icat": "Image file, uses `kitten icat` to display the image. Requires binary `kitten` to be installed",
|
||||
"iterm": "Image file, printed using iterm graphics protocol",
|
||||
"kitty-icat": "Image file, displayed using `kitten icat`. Requires the `kitten` binary",
|
||||
"iterm": "Image file, rendered using the iTerm graphics protocol",
|
||||
"chafa": "Image file, printed as ASCII art using libchafa",
|
||||
"raw": "Image file, printed as raw binary string",
|
||||
"none": "Disable logo printing"
|
||||
@@ -277,9 +270,17 @@
|
||||
"type": "num"
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "logo-padding-bottom",
|
||||
"desc": "Set the padding at the bottom of the logo",
|
||||
"remark": "Only used with \"--logo-position top\"",
|
||||
"arg": {
|
||||
"type": "num"
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "logo-print-remaining",
|
||||
"desc": "Specify whether to print the remaining logo if it has more lines than modules to display",
|
||||
"desc": "Specify whether to print remaining logo lines when the logo is taller than the module list",
|
||||
"arg": {
|
||||
"type": "bool",
|
||||
"optional": true,
|
||||
@@ -288,10 +289,11 @@
|
||||
},
|
||||
{
|
||||
"long": "logo-position",
|
||||
"desc": "Set the position where the logo should be displayed",
|
||||
"desc": "Set the display position of the logo",
|
||||
"arg": {
|
||||
"type": "enum",
|
||||
"enum": {
|
||||
"auto": "Automatically choose left or top based on terminal width",
|
||||
"left": "Left",
|
||||
"top": "Top",
|
||||
"right": "Right"
|
||||
@@ -299,12 +301,33 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "logo-recache",
|
||||
"desc": "If true, regenerate the image logo cache",
|
||||
"long": "logo-cache",
|
||||
"desc": "Specify how the image logo cache is used",
|
||||
"remark": "See \"--help logo-animation-frame\" for the one case where a cached rendering can outlive the image type that produced it",
|
||||
"arg": {
|
||||
"type": "bool",
|
||||
"type": "enum",
|
||||
"optional": true,
|
||||
"default": false
|
||||
"default": "true",
|
||||
"enum": {
|
||||
"true": "Reuse a cached rendering when it is valid, and write it back on a cache miss",
|
||||
"false": "Ignore the image logo cache completely: neither read nor write it",
|
||||
"regen": "Ignore any existing cached rendering and regenerate it"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "logo-animation-frame",
|
||||
"desc": "Select which frame of an animated image logo to render",
|
||||
"remark": [
|
||||
"Animated kitty graphics only, and only if the image source is animated; 1 or higher renders that frame as a still image, 0 plays the animation",
|
||||
"A selected frame is cached exactly like the first frame is, sharing its entries; the frame number is recorded next to them, so changing this option re-renders",
|
||||
"That record is per cache directory, not per image type: switching --logo-type between two types that both have a cached rendering here, and asking for a different frame, can pair one type's rendering with the other's frame number",
|
||||
"Run \"--logo-cache regen\" after such a switch"
|
||||
],
|
||||
"arg": {
|
||||
"type": "num",
|
||||
"optional": true,
|
||||
"default": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -398,7 +421,7 @@
|
||||
{
|
||||
"long": "chafa-fg-only",
|
||||
"desc": "Produce character-cell output using foreground colors only",
|
||||
"remark": "See chafa document for detail",
|
||||
"remark": "See the Chafa documentation for details",
|
||||
"arg": {
|
||||
"type": "bool",
|
||||
"optional": true,
|
||||
@@ -408,7 +431,7 @@
|
||||
{
|
||||
"long": "chafa-symbols",
|
||||
"desc": "Specify character symbols to employ in final output",
|
||||
"remark": "See chafa document for detail",
|
||||
"remark": "See the Chafa documentation for details",
|
||||
"arg": {
|
||||
"type": "str"
|
||||
}
|
||||
@@ -416,7 +439,7 @@
|
||||
{
|
||||
"long": "chafa-canvas-mode",
|
||||
"desc": "Determine how colors are used in the output",
|
||||
"remark": "This value maps the int value of enum ChafaCanvasMode. See chafa document for detail",
|
||||
"remark": "This value maps to the integer value of enum ChafaCanvasMode. See the Chafa documentation for details",
|
||||
"arg": {
|
||||
"type": "enum",
|
||||
"enum": {
|
||||
@@ -433,8 +456,8 @@
|
||||
},
|
||||
{
|
||||
"long": "chafa-color-space",
|
||||
"desc": "Set color space used for quantization",
|
||||
"remark": "This value maps the int value of enum ChafaColorSpace. See chafa document for detail",
|
||||
"desc": "Set the color space used for quantization",
|
||||
"remark": "This value maps to the integer value of enum ChafaColorSpace. See the Chafa documentation for details",
|
||||
"arg": {
|
||||
"type": "enum",
|
||||
"enum": {
|
||||
@@ -445,8 +468,8 @@
|
||||
},
|
||||
{
|
||||
"long": "chafa-dither-mode",
|
||||
"desc": "Set output dither mode (No effect with 24-bit color)",
|
||||
"remark": "This value maps the int value of enum ChafaDitherMode. See chafa document for detail",
|
||||
"desc": "Set the output dithering mode (no effect with 24-bit color)",
|
||||
"remark": "This value maps to the integer value of enum ChafaDitherMode. See the Chafa documentation for details",
|
||||
"arg": {
|
||||
"type": "enum",
|
||||
"enum": {
|
||||
@@ -462,15 +485,23 @@
|
||||
"short": "s",
|
||||
"long": "structure",
|
||||
"desc": "Set the structure of the fetch",
|
||||
"remark": "Must be a colon-separated list of keys. Use \"fastfetch --list-modules\" to see available options",
|
||||
"remark": "Must be a colon-separated list of module keys. Use \"fastfetch --list-modules\" to see available options",
|
||||
"arg": {
|
||||
"type": "structure",
|
||||
"default": "\"fastfetch --print-structure\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "structure-disabled",
|
||||
"desc": "Disable specific modules in the structure",
|
||||
"remark": "Must be a colon-separated list of keys",
|
||||
"arg": {
|
||||
"type": "structure"
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "stat",
|
||||
"desc": "Show time usage (in ms) for individual modules",
|
||||
"desc": "Show execution time (ms) for individual modules",
|
||||
"arg": {
|
||||
"type": "bool",
|
||||
"optional": true,
|
||||
@@ -533,7 +564,7 @@
|
||||
{
|
||||
"long": "duration-abbreviation",
|
||||
"desc": "Specify whether to abbreviate duration values",
|
||||
"remark": "If true, the output will be in the form of \"1h 2m\" instead of \"1 hour, 2 mins\"",
|
||||
"remark": "If true, output is shown as \"1h 2m\" instead of \"1 hour, 2 mins\"",
|
||||
"arg": {
|
||||
"type": "bool",
|
||||
"optional": true,
|
||||
@@ -574,7 +605,7 @@
|
||||
"enum": {
|
||||
"none": "Disable keys",
|
||||
"string": "Show string",
|
||||
"icon": "Show icon (requires newest nerd font)",
|
||||
"icon": "Show icon (requires a recent Nerd Font)",
|
||||
"both": "Show both icon and string (alias of `both-1`)",
|
||||
"both-0": "Show both icon and string with no spaces between them",
|
||||
"both-1": "Show both icon and string with a space between them",
|
||||
@@ -585,6 +616,39 @@
|
||||
"default": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "key-language",
|
||||
"desc": "Specify the language used for module keys",
|
||||
"remark": "Leave empty for auto-detection based on the system locale. Unsupported locales fall back to English",
|
||||
"arg": {
|
||||
"type": "enum",
|
||||
"default": "en",
|
||||
"optional": true,
|
||||
"enum": {
|
||||
"en": "English",
|
||||
"ar": "Arabic",
|
||||
"cs": "Czech",
|
||||
"de": "German",
|
||||
"es": "Spanish",
|
||||
"fi": "Finnish",
|
||||
"fr": "French",
|
||||
"gl": "Galician",
|
||||
"he": "Hebrew",
|
||||
"id": "Indonesian",
|
||||
"it": "Italian",
|
||||
"ja": "Japanese",
|
||||
"ko": "Korean",
|
||||
"pl": "Polish",
|
||||
"pt": "Portuguese",
|
||||
"ru": "Russian",
|
||||
"tr": "Turkish",
|
||||
"uk": "Ukrainian",
|
||||
"vi": "Vietnamese",
|
||||
"zh_CN": "Simplified Chinese",
|
||||
"zh_TW": "Traditional Chinese"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "bright-color",
|
||||
"desc": "Specify whether keys, title, and ASCII logo should be printed in bright color",
|
||||
@@ -631,7 +695,7 @@
|
||||
},
|
||||
{
|
||||
"long": "percent-type",
|
||||
"desc": "Set the percentage output type",
|
||||
"desc": "Set the percentage output style",
|
||||
"remark": [
|
||||
"1 for percentage number",
|
||||
"2 for multi-color bar",
|
||||
@@ -647,7 +711,7 @@
|
||||
},
|
||||
{
|
||||
"long": "percent-ndigits",
|
||||
"desc": "Set the number of digits to keep after the decimal point when formatting percentage numbers",
|
||||
"desc": "Set the number of decimal places to use when formatting percentages",
|
||||
"arg": {
|
||||
"type": "num",
|
||||
"default": 0
|
||||
@@ -694,7 +758,7 @@
|
||||
},
|
||||
{
|
||||
"long": "percent-width",
|
||||
"desc": "Specify the width of the percentage number, in number of characters",
|
||||
"desc": "Specify the width of the percentage number, in characters",
|
||||
"remark": "This option affects only percentage numbers, not bars",
|
||||
"arg": {
|
||||
"type": "num",
|
||||
@@ -703,7 +767,7 @@
|
||||
},
|
||||
{
|
||||
"long": "bar-char-elapsed",
|
||||
"desc": "Set the character to use in the elapsed part of percentage bars",
|
||||
"desc": "Set the character used for the elapsed part of percentage bars",
|
||||
"arg": {
|
||||
"type": "str",
|
||||
"default": "\u25a0"
|
||||
@@ -711,7 +775,7 @@
|
||||
},
|
||||
{
|
||||
"long": "bar-char-total",
|
||||
"desc": "Set the character to use in the total part of percentage bars",
|
||||
"desc": "Set the character used for the total part of percentage bars",
|
||||
"arg": {
|
||||
"type": "str",
|
||||
"default": "-"
|
||||
@@ -735,7 +799,7 @@
|
||||
},
|
||||
{
|
||||
"long": "bar-border-left-elapsed",
|
||||
"desc": "If both bar-border-left-elapsed and bar-border-right-elapsed are set, the border will be used as parts of bar content",
|
||||
"desc": "If both bar-border-left-elapsed and bar-border-right-elapsed are set, the border is used as part of the bar content",
|
||||
"arg": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
@@ -743,7 +807,7 @@
|
||||
},
|
||||
{
|
||||
"long": "bar-border-right-elapsed",
|
||||
"desc": "If both bar-border-left-elapsed and bar-border-right-elapsed are set, the border will be used as parts of bar content",
|
||||
"desc": "If both bar-border-left-elapsed and bar-border-right-elapsed are set, the border is used as part of the bar content",
|
||||
"arg": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
@@ -751,8 +815,8 @@
|
||||
},
|
||||
{
|
||||
"long": "bar-color-elapsed",
|
||||
"desc": "Set the color to use in the elapsed part of percentage bars",
|
||||
"remark": "By default, auto selected by percent-color-{green,yellow,red}",
|
||||
"desc": "Set the color used for the elapsed part of percentage bars",
|
||||
"remark": "By default, this is auto-selected by percent-color-{green,yellow,red}",
|
||||
"arg": {
|
||||
"type": "color",
|
||||
"default": "<auto>"
|
||||
@@ -760,7 +824,7 @@
|
||||
},
|
||||
{
|
||||
"long": "bar-color-total",
|
||||
"desc": "Set the color to use in the total part of percentage bars",
|
||||
"desc": "Set the color used for the total part of percentage bars",
|
||||
"arg": {
|
||||
"type": "color",
|
||||
"default": "light_white"
|
||||
@@ -768,7 +832,7 @@
|
||||
},
|
||||
{
|
||||
"long": "bar-color-border",
|
||||
"desc": "Set the color to use in the borders of percentage bars",
|
||||
"desc": "Set the color used for percentage bar borders",
|
||||
"arg": {
|
||||
"type": "color",
|
||||
"default": "light_white"
|
||||
@@ -784,7 +848,7 @@
|
||||
},
|
||||
{
|
||||
"long": "no-buffer",
|
||||
"desc": "Specify whether the stdout application buffer should be disabled",
|
||||
"desc": "Specify whether stdout buffering should be disabled",
|
||||
"arg": {
|
||||
"type": "bool",
|
||||
"optional": true,
|
||||
@@ -793,7 +857,7 @@
|
||||
},
|
||||
{
|
||||
"long": "size-ndigits",
|
||||
"desc": "Set the number of digits to keep after the decimal point when formatting sizes",
|
||||
"desc": "Set the number of decimal places to use when formatting sizes",
|
||||
"arg": {
|
||||
"type": "num"
|
||||
}
|
||||
@@ -844,7 +908,7 @@
|
||||
},
|
||||
{
|
||||
"long": "freq-ndigits",
|
||||
"desc": "Set the number of digits to keep after the decimal point when printing CPU/GPU frequency in GHz",
|
||||
"desc": "Set the number of decimal places to use when printing CPU/GPU frequency in GHz",
|
||||
"arg": {
|
||||
"type": "num",
|
||||
"default": 2
|
||||
@@ -864,7 +928,7 @@
|
||||
},
|
||||
{
|
||||
"long": "fraction-ndigits",
|
||||
"desc": "Set the number of digits to keep after the decimal point when printing ordinary fraction numbers",
|
||||
"desc": "Set the number of decimal places to use when printing fractional values",
|
||||
"remark": "If negative, the number of digits will be automatically determined based on the value",
|
||||
"arg": {
|
||||
"type": "num",
|
||||
@@ -877,7 +941,7 @@
|
||||
"arg": {
|
||||
"type": "enum",
|
||||
"enum": {
|
||||
"default": "Use the behavior defined internally",
|
||||
"default": "Use the built-in behavior",
|
||||
"always": "Always keep trailing zeros",
|
||||
"never": "Never keep trailing zeros"
|
||||
}
|
||||
@@ -899,7 +963,7 @@
|
||||
},
|
||||
{
|
||||
"long": "temp-ndigits",
|
||||
"desc": "Set the number of digits to keep after the decimal point when printing temperature",
|
||||
"desc": "Set the number of decimal places to use when printing temperatures",
|
||||
"arg": {
|
||||
"type": "num",
|
||||
"default": 2
|
||||
+747
-339
File diff suppressed because it is too large
Load Diff
+10
-5
@@ -50,6 +50,10 @@
|
||||
"driverSpecific": true,
|
||||
"temp": true
|
||||
},
|
||||
{
|
||||
"type": "codec",
|
||||
"splitGPU": true
|
||||
},
|
||||
"memory",
|
||||
"physicalmemory",
|
||||
{
|
||||
@@ -95,17 +99,18 @@
|
||||
"gamepad",
|
||||
"mouse",
|
||||
"keyboard",
|
||||
{
|
||||
"type": "weather",
|
||||
"timeout": 1000
|
||||
},
|
||||
"tpm",
|
||||
"netio",
|
||||
"diskio",
|
||||
{
|
||||
"type": "physicaldisk",
|
||||
"temp": true
|
||||
},
|
||||
"tpm",
|
||||
{
|
||||
"type": "weather",
|
||||
"timeout": 1000
|
||||
},
|
||||
"top",
|
||||
"version",
|
||||
"break",
|
||||
"colors"
|
||||
|
||||
+12
-7
@@ -52,6 +52,10 @@
|
||||
"driverSpecific": true,
|
||||
"temp": true
|
||||
},
|
||||
{
|
||||
"type": "codec",
|
||||
"splitGPU": true
|
||||
},
|
||||
"memory",
|
||||
"physicalmemory",
|
||||
{
|
||||
@@ -90,24 +94,25 @@
|
||||
"opengl",
|
||||
"opencl",
|
||||
"users",
|
||||
// "bluetooth", // doesn't work on macOS because it requires bluetooth permissions
|
||||
// "bluetoothradio",
|
||||
"bluetooth",
|
||||
"bluetoothradio",
|
||||
"sound",
|
||||
"camera",
|
||||
"gamepad",
|
||||
"mouse",
|
||||
"keyboard",
|
||||
{
|
||||
"type": "weather",
|
||||
"timeout": 1000
|
||||
},
|
||||
"tpm",
|
||||
"netio",
|
||||
"diskio",
|
||||
{
|
||||
"type": "physicaldisk",
|
||||
"temp": true
|
||||
},
|
||||
"tpm",
|
||||
{
|
||||
"type": "weather",
|
||||
"timeout": 1000
|
||||
},
|
||||
"top",
|
||||
"version",
|
||||
"logo",
|
||||
"break",
|
||||
|
||||
+13
-16
@@ -1,6 +1,3 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
@@ -45,12 +42,12 @@
|
||||
},
|
||||
{
|
||||
"type": "kernel",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "yellow"
|
||||
},
|
||||
{
|
||||
"type": "packages",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "yellow"
|
||||
},
|
||||
{
|
||||
@@ -65,22 +62,22 @@
|
||||
},
|
||||
{
|
||||
"type": "lm",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "wmtheme",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "icons",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "terminal",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
@@ -95,32 +92,32 @@
|
||||
},
|
||||
{
|
||||
"type": "cpu",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "gpu",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "disk",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "memory",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "swap",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "uptime",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
@@ -135,7 +132,7 @@
|
||||
},
|
||||
{
|
||||
"type": "player",
|
||||
"key": "│ ├",
|
||||
"key": "│ ├{icon}",
|
||||
"keyColor": "cyan"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Inspired by Catnap
|
||||
// Inspired by Catnap (https://github.com/iinsertNameHere/catnap)
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Inspired by Catnap
|
||||
// Inspired by Catnap (https://github.com/iinsertNameHere/catnap)
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
// draw borders first to make colors of left and right border consistant
|
||||
// draw borders first to make colors of left and right border consistent
|
||||
"key": " user",
|
||||
"type": "title",
|
||||
"format": "{user-name}",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Inspired by Catnap
|
||||
// Inspired by Catnap (https://github.com/iinsertNameHere/catnap)
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Inspired by Catnap
|
||||
// Inspired by Catnap (https://github.com/iinsertNameHere/catnap)
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
|
||||
@@ -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.
|
||||
|
||||
{
|
||||
|
||||
+60
-52
@@ -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}",
|
||||
"format": "{size-used} \/ {size-total} ({size-percentage}) - {filesystem}",
|
||||
"key": "║{#cyan}│ {icon} Disk │{$4}│{#keys}║{$2}",
|
||||
"format": "{mountpoint} {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,7 +183,7 @@
|
||||
"!system": "Windows" // Posix version
|
||||
},
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Clang │{$4}│{#keys}│{$2}",
|
||||
"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,74 @@
|
||||
"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)
|
||||
"format": "clang {~14,20}" // Prints 14~20 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} Python │{$4}│{#keys}║{$2}",
|
||||
"text": "python --version",
|
||||
"format": "python {~7}" // {~7} removes first 7 characters ("Python" with extra space)
|
||||
},
|
||||
{
|
||||
"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 +268,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 +278,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",
|
||||
|
||||
@@ -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,6 +1,3 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"display": {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
|
||||
+19
-3
@@ -16,6 +16,13 @@
|
||||
"separator",
|
||||
"os",
|
||||
"host",
|
||||
{
|
||||
"type": "board",
|
||||
"key": "Host",
|
||||
"condition": {
|
||||
"succeeded": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "kernel",
|
||||
"format": "{release}"
|
||||
@@ -31,8 +38,14 @@
|
||||
"compactType": "original",
|
||||
"key": "Resolution"
|
||||
},
|
||||
"de",
|
||||
"wm",
|
||||
{
|
||||
"type": "de",
|
||||
"key": "DE"
|
||||
},
|
||||
{
|
||||
"type": "wm",
|
||||
"key": "WM"
|
||||
},
|
||||
"wmtheme",
|
||||
"theme",
|
||||
"icons",
|
||||
@@ -41,7 +54,10 @@
|
||||
"type": "terminalfont",
|
||||
"format": "{/name}{-}{/}{name}{?size} {size}{?}"
|
||||
},
|
||||
"cpu",
|
||||
{
|
||||
"type": "cpu",
|
||||
"showPeCoreCount": false
|
||||
},
|
||||
{
|
||||
"type": "gpu",
|
||||
"key": "GPU",
|
||||
|
||||
@@ -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:
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ endBold = r"\fR" # end bold text tag
|
||||
# path to the current directory
|
||||
pathToCurrentDir = path.dirname(__file__)
|
||||
# path to the JSON option file
|
||||
pathToHelpFile = path.join(pathToCurrentDir, "../src/data/help.json")
|
||||
pathToHelpFile = path.join(pathToCurrentDir, "../doc/help.json")
|
||||
# man page section
|
||||
manSection = 1
|
||||
# title (center header)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
Copyright (c) 2014-2016 Hayaki Saito
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
|
||||
src/quant.c is derived from ppmquant, originally by Jef Poskanzer.
|
||||
|
||||
Copyright (C) 1989, 1991 by Jef Poskanzer.
|
||||
Copyright (C) 2001 by Bryan Henderson.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose and without fee is hereby granted, provided
|
||||
that the above copyright notice appear in all copies and that both that
|
||||
copyright notice and this permission notice appear in supporting
|
||||
documentation. This software is provided "as is" without express or
|
||||
implied warranty.
|
||||
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
"sixel" license (original license)
|
||||
==================================
|
||||
|
||||
src/tosixel.c and src/fromsixel.c are derived from "sixel" original version (2014-3-2)
|
||||
|
||||
Package: http://nanno.dip.jp/softlib/man/rlogin/sixel.tar.gz
|
||||
|
||||
Unofficial repo: https://github.com/saitoha/sixel
|
||||
|
||||
This work is written by kmiya@ culti. He distributes it under very permissive license.
|
||||
|
||||
The original license text(in Japanese) is:
|
||||
|
||||
```
|
||||
このプログラム及びソースコードの使用について個人・商用を問わず
|
||||
|
||||
ご自由に使用していただいで結構です。
|
||||
|
||||
また、配布・転載・紹介もご連絡の必要もありません。
|
||||
|
||||
ソースの改変による配布も自由ですが、どのバージョンの改変かを
|
||||
|
||||
明記されることを希望します。
|
||||
|
||||
バージョン情報が無い場合は、配布物の年月日を明記されることを
|
||||
|
||||
希望します。
|
||||
|
||||
2014/10/05 kmiya
|
||||
```
|
||||
|
||||
The unofficial translation:
|
||||
|
||||
```
|
||||
Anyone is free to use this program for any purpose,
|
||||
commercial or non-commercial, without any restriction.
|
||||
|
||||
Anyone is free to distribute, copy, publish, or
|
||||
advertise this software, without any contact.
|
||||
|
||||
Anyone is free to distribute with modification of the
|
||||
source code, but I "hope" that its based version or
|
||||
date will be written clearly.
|
||||
|
||||
2014/10/05 kmiya
|
||||
```
|
||||
|
||||
kmiya also said this is compatible with MIT/BSD/GPL.
|
||||
|
||||
Vendored
+218
@@ -0,0 +1,218 @@
|
||||
# libsixel (encoder subset, vendored)
|
||||
|
||||
Encoder-only subset of [libsixel](https://github.com/saitoha/libsixel) `1.8.7-r2`,
|
||||
used by `src/logo/image/` to render the `sixel` logo type **on Windows and macOS**.
|
||||
|
||||
## Why this is vendored
|
||||
|
||||
Neither MSYS2 nor vcpkg packages libsixel, so on Windows there is nothing to
|
||||
`dlopen()` or to link against. The encoder is small and has no external
|
||||
dependencies, so the necessary files are embedded instead. The intent is to upstream
|
||||
a libsixel package to MSYS2 later and switch to it; until then this directory is the
|
||||
source of truth.
|
||||
|
||||
macOS *does* have a package — Homebrew's `libsixel` is this very version, `1.8.7-r2`
|
||||
(`brew info libsixel`) — but it is deliberately not used. It drags in `jpeg-turbo` /
|
||||
`libpng` for the decoder side we never call, and linking it would mean a different
|
||||
encoder on each platform. Since Windows needs an embedded copy regardless, one shared
|
||||
copy is simpler than two.
|
||||
|
||||
## Why only Windows and macOS
|
||||
|
||||
Windows loses ImageMagick entirely — its image backend moves to WIC, which decodes
|
||||
and scales but cannot *encode* sixel — so it needs an encoder of its own.
|
||||
|
||||
macOS does not get ImageMagick either: `ENABLE_IMAGEMAGICK6` / `ENABLE_IMAGEMAGICK7`
|
||||
are gated on `LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR Haiku OR
|
||||
GNU`, with no `APPLE`, and its image backend is ImageIO. So it shares the same
|
||||
requirement.
|
||||
|
||||
On every other platform ImageMagick stays, and it already produces sixel through
|
||||
its own SIXEL coder. Adding libsixel there would mean a second encoder for a
|
||||
capability that already works: one more dependency for zero gain. So `ENABLE_SIXEL`
|
||||
is gated on `WIN32 OR APPLE`, and these sources are **not compiled at all** on
|
||||
Linux / the BSDs.
|
||||
|
||||
Consequence: sixel bytes differ between Windows / macOS (libsixel) and the rest
|
||||
(ImageMagick). That is deliberate and accepted — see
|
||||
`doc/windows-image-backend.md` decision 9.
|
||||
|
||||
## Transparency
|
||||
|
||||
The subset is used for its keycolor support. `sixel_dither_set_transparent()` marks a
|
||||
palette index as the transparent one; `output_rgb_palette_definition()` then leaves that
|
||||
entry out of the palette definition, and `sixel_encode_body()` never puts a keycolor
|
||||
pixel into the node map, so nothing is drawn there and the terminal background shows
|
||||
through. `src/logo/image/sixel.c` drives this for sources that contain fully transparent
|
||||
pixels — it maps those pixels onto a dedicated index one past the quantized palette,
|
||||
because keying a *content* colour would also drop every opaque pixel that shares it.
|
||||
|
||||
Two things have to line up for the background to actually be transparent: the caller has to
|
||||
mark a keycolor, so the keyed pixels are never painted (above), **and** the encoder has to ask
|
||||
for a transparent background, so the terminal leaves those unpainted pixels alone instead of
|
||||
filling them. The second half is the DCS `P2` parameter — see
|
||||
[Local modifications](#local-modifications) item 3.
|
||||
|
||||
Note what this is and is not: it is a **palette-index** keycolor, not alpha support. A
|
||||
partially transparent pixel has to be drawn in its unblended colour, since a sixel has
|
||||
no way to blend with whatever the terminal has behind it.
|
||||
|
||||
⚠️ Do not vendor a *newer* libsixel in the belief that this version cannot do
|
||||
transparency. It can, and it was measured: the official `1.8.7-r2` omits the keycolor
|
||||
entry and leaves the keyed pixels unpainted, with the remaining pixels correctly placed.
|
||||
When a transparent background is not showing up, check the caller first — historically
|
||||
the caller simply never called `sixel_dither_set_transparent()`, and an image logo only
|
||||
renders correctly once it does. If the caller *is* setting a keycolor and the background is
|
||||
still solid, check `P2` instead: the keyed pixels are being left unpainted correctly, but the
|
||||
terminal is filling them.
|
||||
|
||||
## What was kept
|
||||
|
||||
Starting from `sixel_dither_new` / `sixel_dither_initialize` / `sixel_output_new` /
|
||||
`sixel_encode` and following the dependency closure, only these files are reachable:
|
||||
|
||||
| File | Provides |
|
||||
|---|---|
|
||||
| `tosixel.c` | `sixel_encode`, `sixel_putc`, `sixel_node_*`, DCS envelope handling |
|
||||
| `dither.c` / `dither.h` | `sixel_dither_new`, `sixel_dither_initialize`, `sixel_dither_unref` |
|
||||
| `output.c` / `output.h` | `sixel_output_new`, `sixel_output_unref` |
|
||||
| `quant.c` / `quant.h` | Median-cut palette construction |
|
||||
| `pixelformat.c` | `sixel_helper_compute_depth`, `sixel_helper_normalize_pixelformat` |
|
||||
| `allocator.c` / `allocator.h` | `sixel_allocator_*` |
|
||||
| `status.c` / `status.h` | `sixel_helper_set_additional_message` |
|
||||
| `malloc_stub.h` | Included by `allocator.c`; compiles to nothing when `HAVE_MALLOC` |
|
||||
| `sixel.h` | Public API (upstream `include/sixel.h`) |
|
||||
|
||||
## What was removed
|
||||
|
||||
Everything else in the upstream tree, in particular:
|
||||
|
||||
- **Decoding**: `decoder.c`, `fromsixel.c`, `frompnm.c`, `fromgif.c`, `frame.c`,
|
||||
`loader.c`, `stb_image.h`, `stb_image_write.*`. Image decoding is done by the
|
||||
platform backend (WIC on Windows, ImageIO on macOS, ImageMagick elsewhere), so
|
||||
libsixel only ever sees an RGBA8 buffer here.
|
||||
- **The `sixel_encoder_*` / `sixel_decoder_*` high-level API**: `encoder.c`,
|
||||
`writer.c`, `tty.c`, `scale.c`. These pull in file I/O, terminal probing and the
|
||||
loader registry. We call the dither/output/encode trio directly.
|
||||
- **`rgblookup.h` / `rgblookup.gperf`**: a gperf-generated color-name table used
|
||||
only by `encoder.c` for `--builtin-palette` parsing.
|
||||
- **`converters/`** (`img2sixel`, `sixel2png`), **`python/`**, **`tools/`**,
|
||||
**`tests/`**, **`images/`** (216 test images), and the packaging templates
|
||||
(`package.json.in.in`, `libsixel.pc.in`).
|
||||
- **The autotools machinery**: `configure`, `Makefile.in`, `aclocal.m4`, `m4/`,
|
||||
`ltmain.sh`, `config.h.in`, … See item 2 under
|
||||
[Local modifications](#local-modifications).
|
||||
- **Six of the nine `LICENSE.*` files**: `LICENSE.images` / `.mesa` / `.pngsuite` /
|
||||
`.sdump` / `.stb` cover only material that was dropped (test images, the OpenGL
|
||||
example, the `sdump` tool, stb). The three that remain are the complete set
|
||||
required by the files above — verified against each file's own header, see
|
||||
[License](#license).
|
||||
|
||||
Result: **20 files / 256 KB**, down from 319 files / 11.3 MB. Note that 8.0 MB of
|
||||
that 11.3 MB was `images/` fixtures, so the meaningful comparison is code:
|
||||
`src/` + `include/` went from 50 files / 1.17 MB to 20 files / 256 KB.
|
||||
|
||||
## Local modifications
|
||||
|
||||
Four deliberate divergences from upstream. Re-apply all four when re-syncing.
|
||||
|
||||
1. **`sixel.h` — `SIXELAPI` is empty.** Upstream defines it as
|
||||
`__declspec(dllexport)` on Windows. This subset is compiled into `libfastfetch`
|
||||
statically rather than into a standalone DLL, so the attribute would (a) add
|
||||
every libsixel symbol to fastfetch's export table and (b) make `sixel.h`'s
|
||||
declarations disagree with the ones in `dither.h` / `output.h` / `quant.h` /
|
||||
`allocator.h`, which do not carry it (clang warns:
|
||||
`-Wdll-attribute-on-redeclaration`).
|
||||
|
||||
2. **`config.h` is hand-written.** Upstream generates it with `configure`. Only
|
||||
these vendored files read it, and they reference a small fixed set of macros,
|
||||
so the values are hard-coded. Two things to watch:
|
||||
- `HAVE_TESTS` must stay **undefined**, not `0`: `sixel.h` gates its test-only
|
||||
declarations with `#ifdef HAVE_TESTS`, so `#define HAVE_TESTS 0` would still
|
||||
pull in declarations for functions we do not compile.
|
||||
- `HAVE_MEMORY_H` / `HAVE_STRING_H` must be `1`. fastfetch builds with
|
||||
`-Werror=implicit-function-declaration`, so a missing `<memory.h>` /
|
||||
`<string.h>` include in `pixelformat.c` / `status.c` is a hard error.
|
||||
|
||||
3. **`tosixel.c` — the DCS `P2` parameter is initialised to 1.** `sixel_encode_header()`
|
||||
declares `int p[3] = {0, 0, 0}`, and the trailing-zero trimming right below it then drops
|
||||
all three, so the envelope is a bare `ESC P q`. That means `P2 = 0`, which tells the
|
||||
terminal to fill every unset pixel with **colour-table entry 0** — and that entry is
|
||||
defined by the image's own palette, so a keycolor image comes out with a solid rectangle
|
||||
behind it rather than transparency. Setting `p[1] = 1` leaves unset pixels untouched.
|
||||
|
||||
The trimming still drops the trailing aspect-ratio parameter, so the envelope is
|
||||
`ESC P 0;1 q`. `P3` defaults to 0 and the two spellings are equivalent — verified by
|
||||
decoding both with libsixel's own decoder and with ImageMagick's. ImageMagick's SIXEL
|
||||
coder and chafa both write `0;1;0` explicitly.
|
||||
|
||||
This only shows up on a terminal that implements the VT340 background rule. Windows
|
||||
Terminal does (`_backgroundFillRequired = (_conformanceLevel == 1 || !transparent)` in
|
||||
`src/terminal/adapter/SixelParser.cpp`), which is where it was reproduced; WezTerm does
|
||||
not fill, so the identical bytes look correct there. It is also why Linux never showed
|
||||
the problem: that path goes through ImageMagick, which already sends `P2 = 1`.
|
||||
|
||||
4. **`output.c` — release the allocator reference if output allocation fails.**
|
||||
`sixel_output_new()` creates an allocator or takes an additional reference before
|
||||
allocating the output context. If that allocation fails, the reference is not owned by
|
||||
an output object and must be released before returning `SIXEL_BAD_ALLOCATION`.
|
||||
Fastfetch passes a null allocator, so without this cleanup the newly created allocator
|
||||
becomes unreachable on this error path. The successful path and returned error code are
|
||||
unchanged.
|
||||
|
||||
The vendored sources are also exempted from `-Wconversion` in `CMakeLists.txt`:
|
||||
upstream has ~160 implicit int→`unsigned char` narrowing warnings. Everything else
|
||||
(`-Wall -Wextra` and fastfetch's `-Werror=` set) applies unchanged, and the subset
|
||||
compiles clean.
|
||||
|
||||
## Re-syncing with upstream
|
||||
|
||||
```sh
|
||||
VER=1.8.7-r2
|
||||
git clone --depth 1 --branch "$VER" https://github.com/saitoha/libsixel /tmp/libsixel
|
||||
|
||||
# 1. copy the closure (paths change from src/foo.c to foo.c, include/sixel.h to sixel.h)
|
||||
for f in tosixel.c dither.c dither.h output.c output.h quant.c quant.h \
|
||||
pixelformat.c allocator.c allocator.h status.c status.h malloc_stub.h; do
|
||||
cp "/tmp/libsixel/src/$f" .
|
||||
done
|
||||
cp /tmp/libsixel/include/sixel.h .
|
||||
cp /tmp/libsixel/LICENSE /tmp/libsixel/LICENSE.sixel /tmp/libsixel/LICENSE.pnmcolormap .
|
||||
|
||||
# 2. re-apply the four local modifications listed above
|
||||
# 3. verify
|
||||
```
|
||||
|
||||
Verification (must be 0 diagnostics, and the encoder must emit a DCS-wrapped stream).
|
||||
Both platforms compile this subset, so run it on both:
|
||||
|
||||
```sh
|
||||
# Windows: CC=/c/msys64/clang64/bin/cc.exe
|
||||
# macOS: CC=cc (verified with Apple clang 21: 0 diagnostics)
|
||||
CC=cc
|
||||
FLAGS="-I. -Wall -Wextra -Wconversion -Wno-conversion -Werror=uninitialized \
|
||||
-Werror=return-type -Werror=vla -Werror=incompatible-pointer-types \
|
||||
-Werror=implicit-function-declaration -Werror=int-conversion -std=gnu23"
|
||||
for f in tosixel dither output quant allocator pixelformat status; do
|
||||
$CC $FLAGS -c $f.c -o /dev/null || echo "FAILED: $f"
|
||||
done
|
||||
```
|
||||
|
||||
If upstream adds a call from the closure to a new file, the build fails at link
|
||||
time with an undefined `sixel_*` symbol — add that file and repeat.
|
||||
|
||||
## License
|
||||
|
||||
MIT-compatible, but three separate notices apply and all three files must be kept.
|
||||
Mapping verified against each source file's own copyright header:
|
||||
|
||||
| File | Applies to |
|
||||
|---|---|
|
||||
| `LICENSE` | MIT, Copyright (c) 2014-2016 Hayaki Saito — `dither.c`, `output.c`, `pixelformat.c`, `allocator.c`, `status.c` (their headers say 2014-2018/2019; same MIT terms) |
|
||||
| `LICENSE.sixel` | `tosixel.c` — derived from kmiya's original `sixel` (2014-3-2), permissive, re-licensed MIT by Hayaki Saito |
|
||||
| `LICENSE.pnmcolormap` | `quant.c` — derived from `ppmquant` by Jef Poskanzer / Bryan Henderson |
|
||||
|
||||
Upstream also ships `LICENSE.images`, `.mesa`, `.pngsuite`, `.sdump` and `.stb`;
|
||||
none of them cover a file in this subset, so they were not carried over. If a
|
||||
re-sync ever pulls in a file from the decoder side (`fromgif.c` → `.stb`,
|
||||
`fromsixel.c` → `.sixel`), re-check this table.
|
||||
Vendored
+379
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2018 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* STDC_HEADERS */
|
||||
#include <stdlib.h>
|
||||
|
||||
#if HAVE_ASSERT_H
|
||||
# include <assert.h>
|
||||
#endif /* HAVE_ASSERT_H */
|
||||
#if HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif /* HAVE_SYS_TYPES_H */
|
||||
#if HAVE_ERRNO_H
|
||||
# include <errno.h>
|
||||
#endif /* HAVE_ERRNO_H */
|
||||
#if HAVE_MEMORY_H
|
||||
# include <memory.h>
|
||||
#endif /* HAVE_MEMORY_H */
|
||||
|
||||
#include "allocator.h"
|
||||
#include "malloc_stub.h"
|
||||
|
||||
/* create allocator object */
|
||||
SIXELSTATUS
|
||||
sixel_allocator_new(
|
||||
sixel_allocator_t /* out */ **ppallocator, /* allocator object to be created */
|
||||
sixel_malloc_t /* in */ fn_malloc, /* custom malloc() function */
|
||||
sixel_calloc_t /* in */ fn_calloc, /* custom calloc() function */
|
||||
sixel_realloc_t /* in */ fn_realloc, /* custom realloc() function */
|
||||
sixel_free_t /* in */ fn_free) /* custom free() function */
|
||||
{
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
|
||||
if (ppallocator == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_allocator_new: given argument ppallocator is null.");
|
||||
status = SIXEL_BAD_ARGUMENT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (fn_malloc == NULL) {
|
||||
fn_malloc = malloc;
|
||||
}
|
||||
|
||||
if (fn_calloc == NULL) {
|
||||
fn_calloc = calloc;
|
||||
}
|
||||
|
||||
if (fn_realloc == NULL) {
|
||||
fn_realloc = realloc;
|
||||
}
|
||||
|
||||
if (fn_free == NULL) {
|
||||
fn_free = free;
|
||||
}
|
||||
|
||||
*ppallocator = fn_malloc(sizeof(sixel_allocator_t));
|
||||
if (*ppallocator == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_allocator_new: fn_malloc() failed.");
|
||||
status = SIXEL_BAD_ALLOCATION;
|
||||
goto end;
|
||||
}
|
||||
|
||||
(*ppallocator)->ref = 1;
|
||||
(*ppallocator)->fn_malloc = fn_malloc;
|
||||
(*ppallocator)->fn_calloc = fn_calloc;
|
||||
(*ppallocator)->fn_realloc = fn_realloc;
|
||||
(*ppallocator)->fn_free = fn_free;
|
||||
|
||||
status = SIXEL_OK;
|
||||
|
||||
end:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* destruct allocator object */
|
||||
static void
|
||||
sixel_allocator_destroy(
|
||||
sixel_allocator_t /* in */ *allocator) /* allocator object to
|
||||
be destroyed */
|
||||
{
|
||||
/* precondition */
|
||||
assert(allocator);
|
||||
assert(allocator->fn_free);
|
||||
|
||||
allocator->fn_free(allocator);
|
||||
}
|
||||
|
||||
|
||||
/* increase reference count of allocatort object (thread-unsafe) */
|
||||
SIXELAPI void
|
||||
sixel_allocator_ref(
|
||||
sixel_allocator_t /* in */ *allocator) /* allocator object to be
|
||||
increment reference counter */
|
||||
{
|
||||
/* precondition */
|
||||
assert(allocator);
|
||||
|
||||
/* TODO: be thread safe */
|
||||
++allocator->ref;
|
||||
}
|
||||
|
||||
|
||||
/* decrease reference count of output context object (thread-unsafe) */
|
||||
SIXELAPI void
|
||||
sixel_allocator_unref(
|
||||
sixel_allocator_t /* in */ *allocator) /* allocator object to be unreference */
|
||||
{
|
||||
/* TODO: be thread safe */
|
||||
if (allocator) {
|
||||
assert(allocator->ref > 0);
|
||||
--allocator->ref;
|
||||
if (allocator->ref == 0) {
|
||||
sixel_allocator_destroy(allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* call custom malloc() */
|
||||
SIXELAPI void *
|
||||
sixel_allocator_malloc(
|
||||
sixel_allocator_t /* in */ *allocator, /* allocator object */
|
||||
size_t /* in */ n) /* allocation size */
|
||||
{
|
||||
/* precondition */
|
||||
assert(allocator);
|
||||
assert(allocator->fn_malloc);
|
||||
|
||||
if (n == 0) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_allocator_malloc: called with n == 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (n > SIXEL_ALLOCATE_BYTES_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return allocator->fn_malloc(n);
|
||||
}
|
||||
|
||||
|
||||
/* call custom calloc() */
|
||||
SIXELAPI void *
|
||||
sixel_allocator_calloc(
|
||||
sixel_allocator_t /* in */ *allocator, /* allocator object */
|
||||
size_t /* in */ nelm, /* number of elements */
|
||||
size_t /* in */ elsize) /* size of element */
|
||||
{
|
||||
size_t n;
|
||||
|
||||
/* precondition */
|
||||
assert(allocator);
|
||||
assert(allocator->fn_calloc);
|
||||
|
||||
n = nelm * elsize;
|
||||
|
||||
if (n == 0) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_allocator_malloc: called with n == 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (n > SIXEL_ALLOCATE_BYTES_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return allocator->fn_calloc(nelm, elsize);
|
||||
}
|
||||
|
||||
|
||||
/* call custom realloc() */
|
||||
SIXELAPI void *
|
||||
sixel_allocator_realloc(
|
||||
sixel_allocator_t /* in */ *allocator, /* allocator object */
|
||||
void /* in */ *p, /* existing buffer to be re-allocated */
|
||||
size_t /* in */ n) /* re-allocation size */
|
||||
{
|
||||
/* precondition */
|
||||
assert(allocator);
|
||||
assert(allocator->fn_realloc);
|
||||
|
||||
if (n == 0) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_allocator_malloc: called with n == 0");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (n > SIXEL_ALLOCATE_BYTES_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return allocator->fn_realloc(p, n);
|
||||
}
|
||||
|
||||
|
||||
/* call custom free() */
|
||||
SIXELAPI void
|
||||
sixel_allocator_free(
|
||||
sixel_allocator_t /* in */ *allocator, /* allocator object */
|
||||
void /* in */ *p) /* existing buffer to be freed */
|
||||
{
|
||||
/* precondition */
|
||||
assert(allocator);
|
||||
assert(allocator->fn_free);
|
||||
|
||||
allocator->fn_free(p);
|
||||
}
|
||||
|
||||
|
||||
#if HAVE_TESTS
|
||||
volatile int sixel_debug_malloc_counter;
|
||||
|
||||
void *
|
||||
sixel_bad_malloc(size_t size)
|
||||
{
|
||||
return sixel_debug_malloc_counter-- == 0 ? NULL: malloc(size);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
sixel_bad_calloc(size_t count, size_t size)
|
||||
{
|
||||
(void) count;
|
||||
(void) size;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
sixel_bad_realloc(void *ptr, size_t size)
|
||||
{
|
||||
(void) ptr;
|
||||
(void) size;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif /* HAVE_TESTS */
|
||||
|
||||
#if 0
|
||||
int
|
||||
rpl_posix_memalign(void **memptr, size_t alignment, size_t size)
|
||||
{
|
||||
#if HAVE_POSIX_MEMALIGN
|
||||
return posix_memalign(memptr, alignment, size);
|
||||
#elif HAVE_ALIGNED_ALLOC
|
||||
*memptr = aligned_alloc(alignment, size);
|
||||
return *memptr ? 0: ENOMEM;
|
||||
#elif HAVE_MEMALIGN
|
||||
*memptr = memalign(alignment, size);
|
||||
return *memptr ? 0: ENOMEM;
|
||||
#elif HAVE__ALIGNED_MALLOC
|
||||
return _aligned_malloc(size, alignment);
|
||||
#else
|
||||
# error
|
||||
#endif /* _MSC_VER */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if HAVE_TESTS
|
||||
static int
|
||||
test1(void)
|
||||
{
|
||||
int nret = EXIT_FAILURE;
|
||||
SIXELSTATUS status;
|
||||
sixel_allocator_t *allocator = NULL;
|
||||
|
||||
status = sixel_allocator_new(NULL, malloc, calloc, realloc, free);
|
||||
if (status != SIXEL_BAD_ARGUMENT) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
status = sixel_allocator_new(&allocator, NULL, calloc, realloc, free);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
status = sixel_allocator_new(&allocator, malloc, NULL, realloc, free);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
status = sixel_allocator_new(&allocator, malloc, calloc, NULL, free);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
status = sixel_allocator_new(&allocator, malloc, calloc, realloc, NULL);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
nret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test2(void)
|
||||
{
|
||||
int nret = EXIT_FAILURE;
|
||||
SIXELSTATUS status;
|
||||
sixel_allocator_t *allocator = NULL;
|
||||
|
||||
sixel_debug_malloc_counter = 1;
|
||||
|
||||
status = sixel_allocator_new(&allocator, sixel_bad_malloc, calloc, realloc, free);
|
||||
if (status == SIXEL_BAD_ALLOCATION) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
nret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI int
|
||||
sixel_allocator_tests_main(void)
|
||||
{
|
||||
int nret = EXIT_FAILURE;
|
||||
size_t i;
|
||||
typedef int (* testcase)(void);
|
||||
|
||||
static testcase const testcases[] = {
|
||||
test1,
|
||||
test2
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
|
||||
nret = testcases[i]();
|
||||
if (nret != EXIT_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
nret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
return nret;
|
||||
}
|
||||
#endif /* HAVE_TESTS */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LIBSIXEL_ALLOCATOR_H
|
||||
#define LIBSIXEL_ALLOCATOR_H
|
||||
|
||||
#include <sixel.h>
|
||||
|
||||
struct sixel_allocator {
|
||||
unsigned int ref; /* reference counter */
|
||||
sixel_malloc_t fn_malloc;
|
||||
sixel_calloc_t fn_calloc;
|
||||
sixel_realloc_t fn_realloc;
|
||||
sixel_free_t fn_free;
|
||||
};
|
||||
|
||||
#if HAVE_TESTS
|
||||
int
|
||||
sixel_allocator_tests_main(void);
|
||||
#endif
|
||||
|
||||
#endif /* LIBSIXEL_ALLOCATOR_H */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Minimal hand-written replacement for the autoconf-generated config.h.
|
||||
*
|
||||
* Upstream libsixel ships config.h.in and lets `configure` produce config.h.
|
||||
* We only vendor the encoder-side subset (see README.md), and those files
|
||||
* reference a small, fixed set of macros, so the values are hard-coded here
|
||||
* instead of carrying the autotools machinery.
|
||||
*
|
||||
* The values below hold for every platform fastfetch targets:
|
||||
* Windows/MinGW-w64, Linux/glibc, Linux/musl, macOS and the BSDs.
|
||||
* Re-check this file whenever the vendored subset is re-synced with upstream.
|
||||
*/
|
||||
|
||||
#ifndef LIBSIXEL_CONFIG_H
|
||||
#define LIBSIXEL_CONFIG_H
|
||||
|
||||
/* Standard headers (upstream: AC_CHECK_HEADERS) */
|
||||
#define HAVE_ASSERT_H 1
|
||||
#define HAVE_ERRNO_H 1
|
||||
#define HAVE_INTTYPES_H 1
|
||||
#define HAVE_LIMITS_H 1
|
||||
#define HAVE_MATH_H 1
|
||||
#define HAVE_MEMORY_H 1
|
||||
#define HAVE_STRING_H 1
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
|
||||
/* libc functions (upstream: AC_CHECK_FUNCS) */
|
||||
#define HAVE_LDIV 1
|
||||
#define HAVE_MALLOC 1
|
||||
#define HAVE_REALLOC 1
|
||||
|
||||
/* Byte order of the 32bpp pixel readers in pixelformat.c; 0 = little endian */
|
||||
#define SWAP_BYTES 0
|
||||
|
||||
/*
|
||||
* Guards the diagnostic pragma push/pop in dither.c. Not needed, and the
|
||||
* pragma spelling is not portable across the compilers we support.
|
||||
*/
|
||||
#define HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS 0
|
||||
|
||||
/* Verbose quantizer tracing to stderr */
|
||||
#define HAVE_DEBUG 0
|
||||
|
||||
/*
|
||||
* HAVE_TESTS must stay *undefined* rather than 0: sixel.h gates its test-only
|
||||
* declarations with `#ifdef HAVE_TESTS`, so defining it to 0 would still pull
|
||||
* in declarations for functions we do not compile.
|
||||
*/
|
||||
#undef HAVE_TESTS
|
||||
|
||||
/* Optional image loaders and features; not vendored */
|
||||
#undef HAVE_GD
|
||||
#undef HAVE_GDK_PIXBUF2
|
||||
#undef HAVE_JPEG
|
||||
#undef HAVE_LIBCURL
|
||||
#undef HAVE_LIBPNG
|
||||
|
||||
#endif /* LIBSIXEL_CONFIG_H */
|
||||
Vendored
+918
@@ -0,0 +1,918 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2018 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if HAVE_MATH_H
|
||||
# include <math.h>
|
||||
#endif /* HAVE_MATH_H */
|
||||
#if HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif /* HAVE_STRING_H */
|
||||
#if HAVE_LIMITS_H
|
||||
# include <limits.h>
|
||||
#endif /* HAVE_LIMITS_H */
|
||||
#if HAVE_INTTYPES_H
|
||||
# include <inttypes.h>
|
||||
#endif /* HAVE_INTTYPES_H */
|
||||
|
||||
#include "dither.h"
|
||||
#include "quant.h"
|
||||
#include <sixel.h>
|
||||
|
||||
|
||||
static const unsigned char pal_mono_dark[] = {
|
||||
0x00, 0x00, 0x00, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
|
||||
static const unsigned char pal_mono_light[] = {
|
||||
0xff, 0xff, 0xff, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const unsigned char pal_gray_1bit[] = {
|
||||
0x00, 0x00, 0x00, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
|
||||
static const unsigned char pal_gray_2bit[] = {
|
||||
0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
|
||||
static const unsigned char pal_gray_4bit[] = {
|
||||
0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33,
|
||||
0x44, 0x44, 0x44, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77,
|
||||
0x88, 0x88, 0x88, 0x99, 0x99, 0x99, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb,
|
||||
0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xee, 0xee, 0xee, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
|
||||
static const unsigned char pal_gray_8bit[] = {
|
||||
0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
|
||||
0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07,
|
||||
0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b,
|
||||
0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f,
|
||||
0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13,
|
||||
0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17,
|
||||
0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b,
|
||||
0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f,
|
||||
0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23,
|
||||
0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27,
|
||||
0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b,
|
||||
0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f,
|
||||
0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33,
|
||||
0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37,
|
||||
0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b,
|
||||
0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f,
|
||||
0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43,
|
||||
0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47,
|
||||
0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b,
|
||||
0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f,
|
||||
0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53,
|
||||
0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57,
|
||||
0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b,
|
||||
0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f,
|
||||
0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63,
|
||||
0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67,
|
||||
0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b,
|
||||
0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f,
|
||||
0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73,
|
||||
0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77,
|
||||
0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b,
|
||||
0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f,
|
||||
0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83,
|
||||
0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87,
|
||||
0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b,
|
||||
0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f,
|
||||
0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93,
|
||||
0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
|
||||
0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
|
||||
0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0x9f,
|
||||
0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3,
|
||||
0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7,
|
||||
0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, 0xab,
|
||||
0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf,
|
||||
0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3,
|
||||
0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7,
|
||||
0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb,
|
||||
0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
|
||||
0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
|
||||
0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7,
|
||||
0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
|
||||
0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf,
|
||||
0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3,
|
||||
0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7,
|
||||
0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb,
|
||||
0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf,
|
||||
0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3,
|
||||
0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
|
||||
0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb,
|
||||
0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
|
||||
0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
|
||||
0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7,
|
||||
0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
|
||||
0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
|
||||
static const unsigned char pal_xterm256[] = {
|
||||
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00,
|
||||
0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0xc0,
|
||||
0x80, 0x80, 0x80, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x87, 0x00, 0x00, 0xaf,
|
||||
0x00, 0x00, 0xd7, 0x00, 0x00, 0xff, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x5f,
|
||||
0x00, 0x5f, 0x87, 0x00, 0x5f, 0xaf, 0x00, 0x5f, 0xd7, 0x00, 0x5f, 0xff,
|
||||
0x00, 0x87, 0x00, 0x00, 0x87, 0x5f, 0x00, 0x87, 0x87, 0x00, 0x87, 0xaf,
|
||||
0x00, 0x87, 0xd7, 0x00, 0x87, 0xff, 0x00, 0xaf, 0x00, 0x00, 0xaf, 0x5f,
|
||||
0x00, 0xaf, 0x87, 0x00, 0xaf, 0xaf, 0x00, 0xaf, 0xd7, 0x00, 0xaf, 0xff,
|
||||
0x00, 0xd7, 0x00, 0x00, 0xd7, 0x5f, 0x00, 0xd7, 0x87, 0x00, 0xd7, 0xaf,
|
||||
0x00, 0xd7, 0xd7, 0x00, 0xd7, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x5f,
|
||||
0x00, 0xff, 0x87, 0x00, 0xff, 0xaf, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
|
||||
0x5f, 0x00, 0x00, 0x5f, 0x00, 0x5f, 0x5f, 0x00, 0x87, 0x5f, 0x00, 0xaf,
|
||||
0x5f, 0x00, 0xd7, 0x5f, 0x00, 0xff, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x5f,
|
||||
0x5f, 0x5f, 0x87, 0x5f, 0x5f, 0xaf, 0x5f, 0x5f, 0xd7, 0x5f, 0x5f, 0xff,
|
||||
0x5f, 0x87, 0x00, 0x5f, 0x87, 0x5f, 0x5f, 0x87, 0x87, 0x5f, 0x87, 0xaf,
|
||||
0x5f, 0x87, 0xd7, 0x5f, 0x87, 0xff, 0x5f, 0xaf, 0x00, 0x5f, 0xaf, 0x5f,
|
||||
0x5f, 0xaf, 0x87, 0x5f, 0xaf, 0xaf, 0x5f, 0xaf, 0xd7, 0x5f, 0xaf, 0xff,
|
||||
0x5f, 0xd7, 0x00, 0x5f, 0xd7, 0x5f, 0x5f, 0xd7, 0x87, 0x5f, 0xd7, 0xaf,
|
||||
0x5f, 0xd7, 0xd7, 0x5f, 0xd7, 0xff, 0x5f, 0xff, 0x00, 0x5f, 0xff, 0x5f,
|
||||
0x5f, 0xff, 0x87, 0x5f, 0xff, 0xaf, 0x5f, 0xff, 0xd7, 0x5f, 0xff, 0xff,
|
||||
0x87, 0x00, 0x00, 0x87, 0x00, 0x5f, 0x87, 0x00, 0x87, 0x87, 0x00, 0xaf,
|
||||
0x87, 0x00, 0xd7, 0x87, 0x00, 0xff, 0x87, 0x5f, 0x00, 0x87, 0x5f, 0x5f,
|
||||
0x87, 0x5f, 0x87, 0x87, 0x5f, 0xaf, 0x87, 0x5f, 0xd7, 0x87, 0x5f, 0xff,
|
||||
0x87, 0x87, 0x00, 0x87, 0x87, 0x5f, 0x87, 0x87, 0x87, 0x87, 0x87, 0xaf,
|
||||
0x87, 0x87, 0xd7, 0x87, 0x87, 0xff, 0x87, 0xaf, 0x00, 0x87, 0xaf, 0x5f,
|
||||
0x87, 0xaf, 0x87, 0x87, 0xaf, 0xaf, 0x87, 0xaf, 0xd7, 0x87, 0xaf, 0xff,
|
||||
0x87, 0xd7, 0x00, 0x87, 0xd7, 0x5f, 0x87, 0xd7, 0x87, 0x87, 0xd7, 0xaf,
|
||||
0x87, 0xd7, 0xd7, 0x87, 0xd7, 0xff, 0x87, 0xff, 0x00, 0x87, 0xff, 0x5f,
|
||||
0x87, 0xff, 0x87, 0x87, 0xff, 0xaf, 0x87, 0xff, 0xd7, 0x87, 0xff, 0xff,
|
||||
0xaf, 0x00, 0x00, 0xaf, 0x00, 0x5f, 0xaf, 0x00, 0x87, 0xaf, 0x00, 0xaf,
|
||||
0xaf, 0x00, 0xd7, 0xaf, 0x00, 0xff, 0xaf, 0x5f, 0x00, 0xaf, 0x5f, 0x5f,
|
||||
0xaf, 0x5f, 0x87, 0xaf, 0x5f, 0xaf, 0xaf, 0x5f, 0xd7, 0xaf, 0x5f, 0xff,
|
||||
0xaf, 0x87, 0x00, 0xaf, 0x87, 0x5f, 0xaf, 0x87, 0x87, 0xaf, 0x87, 0xaf,
|
||||
0xaf, 0x87, 0xd7, 0xaf, 0x87, 0xff, 0xaf, 0xaf, 0x00, 0xaf, 0xaf, 0x5f,
|
||||
0xaf, 0xaf, 0x87, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xd7, 0xaf, 0xaf, 0xff,
|
||||
0xaf, 0xd7, 0x00, 0xaf, 0xd7, 0x5f, 0xaf, 0xd7, 0x87, 0xaf, 0xd7, 0xaf,
|
||||
0xaf, 0xd7, 0xd7, 0xaf, 0xd7, 0xff, 0xaf, 0xff, 0x00, 0xaf, 0xff, 0x5f,
|
||||
0xaf, 0xff, 0x87, 0xaf, 0xff, 0xaf, 0xaf, 0xff, 0xd7, 0xaf, 0xff, 0xff,
|
||||
0xd7, 0x00, 0x00, 0xd7, 0x00, 0x5f, 0xd7, 0x00, 0x87, 0xd7, 0x00, 0xaf,
|
||||
0xd7, 0x00, 0xd7, 0xd7, 0x00, 0xff, 0xd7, 0x5f, 0x00, 0xd7, 0x5f, 0x5f,
|
||||
0xd7, 0x5f, 0x87, 0xd7, 0x5f, 0xaf, 0xd7, 0x5f, 0xd7, 0xd7, 0x5f, 0xff,
|
||||
0xd7, 0x87, 0x00, 0xd7, 0x87, 0x5f, 0xd7, 0x87, 0x87, 0xd7, 0x87, 0xaf,
|
||||
0xd7, 0x87, 0xd7, 0xd7, 0x87, 0xff, 0xd7, 0xaf, 0x00, 0xd7, 0xaf, 0x5f,
|
||||
0xd7, 0xaf, 0x87, 0xd7, 0xaf, 0xaf, 0xd7, 0xaf, 0xd7, 0xd7, 0xaf, 0xff,
|
||||
0xd7, 0xd7, 0x00, 0xd7, 0xd7, 0x5f, 0xd7, 0xd7, 0x87, 0xd7, 0xd7, 0xaf,
|
||||
0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xff, 0xd7, 0xff, 0x00, 0xd7, 0xff, 0x5f,
|
||||
0xd7, 0xff, 0x87, 0xd7, 0xff, 0xaf, 0xd7, 0xff, 0xd7, 0xd7, 0xff, 0xff,
|
||||
0xff, 0x00, 0x00, 0xff, 0x00, 0x5f, 0xff, 0x00, 0x87, 0xff, 0x00, 0xaf,
|
||||
0xff, 0x00, 0xd7, 0xff, 0x00, 0xff, 0xff, 0x5f, 0x00, 0xff, 0x5f, 0x5f,
|
||||
0xff, 0x5f, 0x87, 0xff, 0x5f, 0xaf, 0xff, 0x5f, 0xd7, 0xff, 0x5f, 0xff,
|
||||
0xff, 0x87, 0x00, 0xff, 0x87, 0x5f, 0xff, 0x87, 0x87, 0xff, 0x87, 0xaf,
|
||||
0xff, 0x87, 0xd7, 0xff, 0x87, 0xff, 0xff, 0xaf, 0x00, 0xff, 0xaf, 0x5f,
|
||||
0xff, 0xaf, 0x87, 0xff, 0xaf, 0xaf, 0xff, 0xaf, 0xd7, 0xff, 0xaf, 0xff,
|
||||
0xff, 0xd7, 0x00, 0xff, 0xd7, 0x5f, 0xff, 0xd7, 0x87, 0xff, 0xd7, 0xaf,
|
||||
0xff, 0xd7, 0xd7, 0xff, 0xd7, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x5f,
|
||||
0xff, 0xff, 0x87, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff,
|
||||
0x08, 0x08, 0x08, 0x12, 0x12, 0x12, 0x1c, 0x1c, 0x1c, 0x26, 0x26, 0x26,
|
||||
0x30, 0x30, 0x30, 0x3a, 0x3a, 0x3a, 0x44, 0x44, 0x44, 0x4e, 0x4e, 0x4e,
|
||||
0x58, 0x58, 0x58, 0x62, 0x62, 0x62, 0x6c, 0x6c, 0x6c, 0x76, 0x76, 0x76,
|
||||
0x80, 0x80, 0x80, 0x8a, 0x8a, 0x8a, 0x94, 0x94, 0x94, 0x9e, 0x9e, 0x9e,
|
||||
0xa8, 0xa8, 0xa8, 0xb2, 0xb2, 0xb2, 0xbc, 0xbc, 0xbc, 0xc6, 0xc6, 0xc6,
|
||||
0xd0, 0xd0, 0xd0, 0xda, 0xda, 0xda, 0xe4, 0xe4, 0xe4, 0xee, 0xee, 0xee,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* VT340 undocumented behavior regarding the color palette reported
|
||||
* by Vertis Sidus(@vrtsds):
|
||||
* it loads the first fifteen colors as 1 through 15, and loads the
|
||||
* sixteenth color as 0.
|
||||
*/
|
||||
static const unsigned char pal_vt340_mono[] = {
|
||||
/* 1 Gray-2 */ 13 * 255 / 100, 13 * 255 / 100, 13 * 255 / 100,
|
||||
/* 2 Gray-4 */ 26 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100,
|
||||
/* 3 Gray-6 */ 40 * 255 / 100, 40 * 255 / 100, 40 * 255 / 100,
|
||||
/* 4 Gray-1 */ 6 * 255 / 100, 6 * 255 / 100, 6 * 255 / 100,
|
||||
/* 5 Gray-3 */ 20 * 255 / 100, 20 * 255 / 100, 20 * 255 / 100,
|
||||
/* 6 Gray-5 */ 33 * 255 / 100, 33 * 255 / 100, 33 * 255 / 100,
|
||||
/* 7 White 7 */ 46 * 255 / 100, 46 * 255 / 100, 46 * 255 / 100,
|
||||
/* 8 Black 0 */ 0 * 255 / 100, 0 * 255 / 100, 0 * 255 / 100,
|
||||
/* 9 Gray-2 */ 13 * 255 / 100, 13 * 255 / 100, 13 * 255 / 100,
|
||||
/* 10 Gray-4 */ 26 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100,
|
||||
/* 11 Gray-6 */ 40 * 255 / 100, 40 * 255 / 100, 40 * 255 / 100,
|
||||
/* 12 Gray-1 */ 6 * 255 / 100, 6 * 255 / 100, 6 * 255 / 100,
|
||||
/* 13 Gray-3 */ 20 * 255 / 100, 20 * 255 / 100, 20 * 255 / 100,
|
||||
/* 14 Gray-5 */ 33 * 255 / 100, 33 * 255 / 100, 33 * 255 / 100,
|
||||
/* 15 White 7 */ 46 * 255 / 100, 46 * 255 / 100, 46 * 255 / 100,
|
||||
/* 0 Black */ 0 * 255 / 100, 0 * 255 / 100, 0 * 255 / 100,
|
||||
};
|
||||
|
||||
|
||||
static const unsigned char pal_vt340_color[] = {
|
||||
/* 1 Blue */ 20 * 255 / 100, 20 * 255 / 100, 80 * 255 / 100,
|
||||
/* 2 Red */ 80 * 255 / 100, 13 * 255 / 100, 13 * 255 / 100,
|
||||
/* 3 Green */ 20 * 255 / 100, 80 * 255 / 100, 20 * 255 / 100,
|
||||
/* 4 Magenta */ 80 * 255 / 100, 20 * 255 / 100, 80 * 255 / 100,
|
||||
/* 5 Cyan */ 20 * 255 / 100, 80 * 255 / 100, 80 * 255 / 100,
|
||||
/* 6 Yellow */ 80 * 255 / 100, 80 * 255 / 100, 20 * 255 / 100,
|
||||
/* 7 Gray 50% */ 53 * 255 / 100, 53 * 255 / 100, 53 * 255 / 100,
|
||||
/* 8 Gray 25% */ 26 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100,
|
||||
/* 9 Blue* */ 33 * 255 / 100, 33 * 255 / 100, 60 * 255 / 100,
|
||||
/* 10 Red* */ 60 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100,
|
||||
/* 11 Green* */ 33 * 255 / 100, 60 * 255 / 100, 33 * 255 / 100,
|
||||
/* 12 Magenta* */ 60 * 255 / 100, 33 * 255 / 100, 60 * 255 / 100,
|
||||
/* 13 Cyan* */ 33 * 255 / 100, 60 * 255 / 100, 60 * 255 / 100,
|
||||
/* 14 Yellow* */ 60 * 255 / 100, 60 * 255 / 100, 33 * 255 / 100,
|
||||
/* 15 Gray 75% */ 80 * 255 / 100, 80 * 255 / 100, 80 * 255 / 100,
|
||||
/* 0 Black */ 0 * 255 / 100, 0 * 255 / 100, 0 * 255 / 100,
|
||||
};
|
||||
|
||||
|
||||
/* create dither context object */
|
||||
SIXELAPI SIXELSTATUS
|
||||
sixel_dither_new(
|
||||
sixel_dither_t /* out */ **ppdither, /* dither object to be created */
|
||||
int /* in */ ncolors, /* required colors */
|
||||
sixel_allocator_t /* in */ *allocator) /* allocator, null if you use
|
||||
default allocator */
|
||||
{
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
size_t headsize;
|
||||
size_t datasize;
|
||||
size_t wholesize;
|
||||
int quality_mode;
|
||||
|
||||
/* ensure given pointer is not null */
|
||||
if (ppdither == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_dither_new: ppdither is null.");
|
||||
status = SIXEL_BAD_ARGUMENT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (allocator == NULL) {
|
||||
status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
*ppdither = NULL;
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
sixel_allocator_ref(allocator);
|
||||
}
|
||||
|
||||
if (ncolors < 0) {
|
||||
ncolors = SIXEL_PALETTE_MAX;
|
||||
quality_mode = SIXEL_QUALITY_HIGHCOLOR;
|
||||
} else {
|
||||
if (ncolors > SIXEL_PALETTE_MAX) {
|
||||
status = SIXEL_BAD_INPUT;
|
||||
goto end;
|
||||
} else if (ncolors < 1) {
|
||||
status = SIXEL_BAD_INPUT;
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_dither_new: palette colors must be more than 0");
|
||||
goto end;
|
||||
}
|
||||
quality_mode = SIXEL_QUALITY_LOW;
|
||||
}
|
||||
headsize = sizeof(sixel_dither_t);
|
||||
datasize = (size_t)(ncolors * 3);
|
||||
wholesize = headsize + datasize;
|
||||
|
||||
*ppdither = (sixel_dither_t *)sixel_allocator_malloc(allocator, wholesize);
|
||||
if (*ppdither == NULL) {
|
||||
sixel_allocator_unref(allocator);
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_dither_new: sixel_allocator_malloc() failed.");
|
||||
status = SIXEL_BAD_ALLOCATION;
|
||||
goto end;
|
||||
}
|
||||
|
||||
(*ppdither)->ref = 1;
|
||||
(*ppdither)->palette = (unsigned char*)(*ppdither + 1);
|
||||
(*ppdither)->cachetable = NULL;
|
||||
(*ppdither)->reqcolors = ncolors;
|
||||
(*ppdither)->ncolors = ncolors;
|
||||
(*ppdither)->origcolors = (-1);
|
||||
(*ppdither)->keycolor = (-1);
|
||||
(*ppdither)->optimized = 0;
|
||||
(*ppdither)->optimize_palette = 0;
|
||||
(*ppdither)->complexion = 1;
|
||||
(*ppdither)->bodyonly = 0;
|
||||
(*ppdither)->method_for_largest = SIXEL_LARGE_NORM;
|
||||
(*ppdither)->method_for_rep = SIXEL_REP_CENTER_BOX;
|
||||
(*ppdither)->method_for_diffuse = SIXEL_DIFFUSE_FS;
|
||||
(*ppdither)->quality_mode = quality_mode;
|
||||
(*ppdither)->pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
(*ppdither)->allocator = allocator;
|
||||
|
||||
status = SIXEL_OK;
|
||||
|
||||
end:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* create dither context object (deprecated) */
|
||||
SIXELAPI sixel_dither_t *
|
||||
sixel_dither_create(
|
||||
int /* in */ ncolors)
|
||||
{
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
sixel_dither_t *dither = NULL;
|
||||
|
||||
status = sixel_dither_new(&dither, ncolors, NULL);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
return dither;
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI void
|
||||
sixel_dither_destroy(
|
||||
sixel_dither_t /* in */ *dither)
|
||||
{
|
||||
sixel_allocator_t *allocator;
|
||||
|
||||
if (dither) {
|
||||
allocator = dither->allocator;
|
||||
sixel_allocator_free(allocator, dither->cachetable);
|
||||
dither->cachetable = NULL;
|
||||
sixel_allocator_free(allocator, dither);
|
||||
sixel_allocator_unref(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI void
|
||||
sixel_dither_ref(
|
||||
sixel_dither_t /* in */ *dither)
|
||||
{
|
||||
/* TODO: be thread safe */
|
||||
++dither->ref;
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI void
|
||||
sixel_dither_unref(
|
||||
sixel_dither_t /* in */ *dither)
|
||||
{
|
||||
/* TODO: be thread safe */
|
||||
if (dither != NULL && --dither->ref == 0) {
|
||||
sixel_dither_destroy(dither);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI sixel_dither_t *
|
||||
sixel_dither_get(
|
||||
int /* in */ builtin_dither)
|
||||
{
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
unsigned char *palette;
|
||||
int ncolors;
|
||||
int keycolor;
|
||||
sixel_dither_t *dither = NULL;
|
||||
|
||||
switch (builtin_dither) {
|
||||
case SIXEL_BUILTIN_MONO_DARK:
|
||||
ncolors = 2;
|
||||
palette = (unsigned char *)pal_mono_dark;
|
||||
keycolor = 0;
|
||||
break;
|
||||
case SIXEL_BUILTIN_MONO_LIGHT:
|
||||
ncolors = 2;
|
||||
palette = (unsigned char *)pal_mono_light;
|
||||
keycolor = 0;
|
||||
break;
|
||||
case SIXEL_BUILTIN_XTERM16:
|
||||
ncolors = 16;
|
||||
palette = (unsigned char *)pal_xterm256;
|
||||
keycolor = (-1);
|
||||
break;
|
||||
case SIXEL_BUILTIN_XTERM256:
|
||||
ncolors = 256;
|
||||
palette = (unsigned char *)pal_xterm256;
|
||||
keycolor = (-1);
|
||||
break;
|
||||
case SIXEL_BUILTIN_VT340_MONO:
|
||||
ncolors = 16;
|
||||
palette = (unsigned char *)pal_vt340_mono;
|
||||
keycolor = (-1);
|
||||
break;
|
||||
case SIXEL_BUILTIN_VT340_COLOR:
|
||||
ncolors = 16;
|
||||
palette = (unsigned char *)pal_vt340_color;
|
||||
keycolor = (-1);
|
||||
break;
|
||||
case SIXEL_BUILTIN_G1:
|
||||
ncolors = 2;
|
||||
palette = (unsigned char *)pal_gray_1bit;
|
||||
keycolor = (-1);
|
||||
break;
|
||||
case SIXEL_BUILTIN_G2:
|
||||
ncolors = 4;
|
||||
palette = (unsigned char *)pal_gray_2bit;
|
||||
keycolor = (-1);
|
||||
break;
|
||||
case SIXEL_BUILTIN_G4:
|
||||
ncolors = 16;
|
||||
palette = (unsigned char *)pal_gray_4bit;
|
||||
keycolor = (-1);
|
||||
break;
|
||||
case SIXEL_BUILTIN_G8:
|
||||
ncolors = 256;
|
||||
palette = (unsigned char *)pal_gray_8bit;
|
||||
keycolor = (-1);
|
||||
break;
|
||||
default:
|
||||
goto end;
|
||||
}
|
||||
|
||||
status = sixel_dither_new(&dither, ncolors, NULL);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
dither = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dither->palette = palette;
|
||||
dither->keycolor = keycolor;
|
||||
dither->optimized = 1;
|
||||
dither->optimize_palette = 0;
|
||||
|
||||
end:
|
||||
return dither;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sixel_dither_set_method_for_largest(
|
||||
sixel_dither_t /* in */ *dither,
|
||||
int /* in */ method_for_largest)
|
||||
{
|
||||
if (method_for_largest == SIXEL_LARGE_AUTO) {
|
||||
method_for_largest = SIXEL_LARGE_NORM;
|
||||
}
|
||||
dither->method_for_largest = method_for_largest;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sixel_dither_set_method_for_rep(
|
||||
sixel_dither_t /* in */ *dither,
|
||||
int /* in */ method_for_rep)
|
||||
{
|
||||
if (method_for_rep == SIXEL_REP_AUTO) {
|
||||
method_for_rep = SIXEL_REP_CENTER_BOX;
|
||||
}
|
||||
dither->method_for_rep = method_for_rep;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
sixel_dither_set_quality_mode(
|
||||
sixel_dither_t /* in */ *dither,
|
||||
int /* in */ quality_mode)
|
||||
{
|
||||
if (quality_mode == SIXEL_QUALITY_AUTO) {
|
||||
if (dither->ncolors <= 8) {
|
||||
quality_mode = SIXEL_QUALITY_HIGH;
|
||||
} else {
|
||||
quality_mode = SIXEL_QUALITY_LOW;
|
||||
}
|
||||
}
|
||||
dither->quality_mode = quality_mode;
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI SIXELSTATUS
|
||||
sixel_dither_initialize(
|
||||
sixel_dither_t /* in */ *dither,
|
||||
unsigned char /* in */ *data,
|
||||
int /* in */ width,
|
||||
int /* in */ height,
|
||||
int /* in */ pixelformat,
|
||||
int /* in */ method_for_largest,
|
||||
int /* in */ method_for_rep,
|
||||
int /* in */ quality_mode)
|
||||
{
|
||||
unsigned char *buf = NULL;
|
||||
unsigned char *normalized_pixels = NULL;
|
||||
unsigned char *input_pixels;
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
|
||||
/* ensure dither object is not null */
|
||||
if (dither == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_dither_new: dither is null.");
|
||||
status = SIXEL_BAD_ARGUMENT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* increment ref count */
|
||||
sixel_dither_ref(dither);
|
||||
|
||||
sixel_dither_set_pixelformat(dither, pixelformat);
|
||||
|
||||
switch (pixelformat) {
|
||||
case SIXEL_PIXELFORMAT_RGB888:
|
||||
input_pixels = data;
|
||||
break;
|
||||
default:
|
||||
/* normalize pixelformat */
|
||||
normalized_pixels
|
||||
= (unsigned char *)sixel_allocator_malloc(dither->allocator, (size_t)(width * height * 3));
|
||||
if (normalized_pixels == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_dither_initialize: sixel_allocator_malloc() failed.");
|
||||
status = SIXEL_BAD_ALLOCATION;
|
||||
goto end;
|
||||
}
|
||||
|
||||
status = sixel_helper_normalize_pixelformat(
|
||||
normalized_pixels,
|
||||
&pixelformat,
|
||||
data,
|
||||
pixelformat,
|
||||
width,
|
||||
height);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto end;
|
||||
}
|
||||
input_pixels = normalized_pixels;
|
||||
break;
|
||||
}
|
||||
|
||||
sixel_dither_set_method_for_largest(dither, method_for_largest);
|
||||
sixel_dither_set_method_for_rep(dither, method_for_rep);
|
||||
sixel_dither_set_quality_mode(dither, quality_mode);
|
||||
|
||||
status = sixel_quant_make_palette(&buf,
|
||||
input_pixels,
|
||||
(unsigned int)(width * height * 3),
|
||||
SIXEL_PIXELFORMAT_RGB888,
|
||||
(unsigned int)dither->reqcolors,
|
||||
(unsigned int *)&dither->ncolors,
|
||||
(unsigned int *)&dither->origcolors,
|
||||
dither->method_for_largest,
|
||||
dither->method_for_rep,
|
||||
dither->quality_mode,
|
||||
dither->allocator);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto end;
|
||||
}
|
||||
memcpy(dither->palette, buf, (size_t)(dither->ncolors * 3));
|
||||
|
||||
dither->optimized = 1;
|
||||
if (dither->origcolors <= dither->ncolors) {
|
||||
dither->method_for_diffuse = SIXEL_DIFFUSE_NONE;
|
||||
}
|
||||
|
||||
sixel_quant_free_palette(buf, dither->allocator);
|
||||
status = SIXEL_OK;
|
||||
|
||||
end:
|
||||
free(normalized_pixels);
|
||||
|
||||
/* decrement ref count */
|
||||
sixel_dither_unref(dither);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* set diffusion type, choose from enum methodForDiffuse */
|
||||
SIXELAPI void
|
||||
sixel_dither_set_diffusion_type(
|
||||
sixel_dither_t /* in */ *dither,
|
||||
int /* in */ method_for_diffuse)
|
||||
{
|
||||
if (method_for_diffuse == SIXEL_DIFFUSE_AUTO) {
|
||||
if (dither->ncolors > 16) {
|
||||
method_for_diffuse = SIXEL_DIFFUSE_FS;
|
||||
} else {
|
||||
method_for_diffuse = SIXEL_DIFFUSE_ATKINSON;
|
||||
}
|
||||
}
|
||||
dither->method_for_diffuse = method_for_diffuse;
|
||||
}
|
||||
|
||||
|
||||
/* get number of palette colors */
|
||||
SIXELAPI int
|
||||
sixel_dither_get_num_of_palette_colors(
|
||||
sixel_dither_t /* in */ *dither)
|
||||
{
|
||||
return dither->ncolors;
|
||||
}
|
||||
|
||||
|
||||
/* get number of histogram colors */
|
||||
SIXELAPI int
|
||||
sixel_dither_get_num_of_histogram_colors(
|
||||
sixel_dither_t /* in */ *dither) /* dither context object */
|
||||
{
|
||||
return dither->origcolors;
|
||||
}
|
||||
|
||||
|
||||
/* typoed: remained for keeping compatibility */
|
||||
SIXELAPI int
|
||||
sixel_dither_get_num_of_histgram_colors(
|
||||
sixel_dither_t /* in */ *dither) /* dither context object */
|
||||
{
|
||||
return sixel_dither_get_num_of_histogram_colors(dither);
|
||||
}
|
||||
|
||||
|
||||
/* get palette */
|
||||
SIXELAPI unsigned char *
|
||||
sixel_dither_get_palette(
|
||||
sixel_dither_t /* in */ *dither) /* dither context object */
|
||||
{
|
||||
return dither->palette;
|
||||
}
|
||||
|
||||
|
||||
/* set palette */
|
||||
SIXELAPI void
|
||||
sixel_dither_set_palette(
|
||||
sixel_dither_t /* in */ *dither, /* dither context object */
|
||||
unsigned char /* in */ *palette)
|
||||
{
|
||||
memcpy(dither->palette, palette, (size_t)(dither->ncolors * 3));
|
||||
}
|
||||
|
||||
|
||||
/* set the factor of complexion color correcting */
|
||||
SIXELAPI void
|
||||
sixel_dither_set_complexion_score(
|
||||
sixel_dither_t /* in */ *dither, /* dither context object */
|
||||
int /* in */ score) /* complexion score (>= 1) */
|
||||
{
|
||||
dither->complexion = score;
|
||||
}
|
||||
|
||||
|
||||
/* set whether omitting palette difinition */
|
||||
SIXELAPI void
|
||||
sixel_dither_set_body_only(
|
||||
sixel_dither_t /* in */ *dither, /* dither context object */
|
||||
int /* in */ bodyonly) /* 0: output palette section
|
||||
1: do not output palette section */
|
||||
{
|
||||
dither->bodyonly = bodyonly;
|
||||
}
|
||||
|
||||
|
||||
/* set whether optimize palette size */
|
||||
SIXELAPI void
|
||||
sixel_dither_set_optimize_palette(
|
||||
sixel_dither_t /* in */ *dither, /* dither context object */
|
||||
int /* in */ do_opt) /* 0: optimize palette size
|
||||
1: don't optimize palette size */
|
||||
{
|
||||
dither->optimize_palette = do_opt;
|
||||
}
|
||||
|
||||
|
||||
/* set pixelformat */
|
||||
SIXELAPI void
|
||||
sixel_dither_set_pixelformat(
|
||||
sixel_dither_t /* in */ *dither, /* dither context object */
|
||||
int /* in */ pixelformat) /* one of enum pixelFormat */
|
||||
{
|
||||
dither->pixelformat = pixelformat;
|
||||
}
|
||||
|
||||
|
||||
/* set transparent */
|
||||
SIXELAPI void
|
||||
sixel_dither_set_transparent(
|
||||
sixel_dither_t /* in */ *dither, /* dither context object */
|
||||
int /* in */ transparent) /* transparent color index */
|
||||
{
|
||||
dither->keycolor = transparent;
|
||||
}
|
||||
|
||||
|
||||
/* set transparent */
|
||||
SIXELAPI sixel_index_t *
|
||||
sixel_dither_apply_palette(
|
||||
sixel_dither_t /* in */ *dither,
|
||||
unsigned char /* in */ *pixels,
|
||||
int /* in */ width,
|
||||
int /* in */ height)
|
||||
{
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
size_t bufsize;
|
||||
sixel_index_t *dest = NULL;
|
||||
int ncolors;
|
||||
unsigned char *normalized_pixels = NULL;
|
||||
unsigned char *input_pixels;
|
||||
|
||||
/* ensure dither object is not null */
|
||||
if (dither == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_dither_apply_palette: dither is null.");
|
||||
status = SIXEL_BAD_ARGUMENT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
sixel_dither_ref(dither);
|
||||
|
||||
bufsize = (size_t)(width * height) * sizeof(sixel_index_t);
|
||||
dest = (sixel_index_t *)sixel_allocator_malloc(dither->allocator, bufsize);
|
||||
if (dest == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_dither_new: sixel_allocator_malloc() failed.");
|
||||
status = SIXEL_BAD_ALLOCATION;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* if quality_mode is full, do not use palette caching */
|
||||
if (dither->quality_mode == SIXEL_QUALITY_FULL) {
|
||||
dither->optimized = 0;
|
||||
}
|
||||
|
||||
if (dither->cachetable == NULL && dither->optimized) {
|
||||
if (dither->palette != pal_mono_dark && dither->palette != pal_mono_light) {
|
||||
dither->cachetable = (unsigned short *)sixel_allocator_calloc(dither->allocator,
|
||||
(size_t)(1 << 3 * 5),
|
||||
sizeof(unsigned short));
|
||||
if (dither->cachetable == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_dither_new: sixel_allocator_calloc() failed.");
|
||||
status = SIXEL_BAD_ALLOCATION;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dither->pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
/* normalize pixelformat */
|
||||
normalized_pixels
|
||||
= (unsigned char *)sixel_allocator_malloc(dither->allocator, (size_t)(width * height * 3));
|
||||
if (normalized_pixels == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_dither_new: sixel_allocator_malloc() failed.");
|
||||
status = SIXEL_BAD_ALLOCATION;
|
||||
goto end;
|
||||
}
|
||||
status = sixel_helper_normalize_pixelformat(normalized_pixels,
|
||||
&dither->pixelformat,
|
||||
pixels, dither->pixelformat,
|
||||
width, height);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto end;
|
||||
}
|
||||
input_pixels = normalized_pixels;
|
||||
} else {
|
||||
input_pixels = pixels;
|
||||
}
|
||||
|
||||
status = sixel_quant_apply_palette(dest,
|
||||
input_pixels,
|
||||
width, height, 3,
|
||||
dither->palette,
|
||||
dither->ncolors,
|
||||
dither->method_for_diffuse,
|
||||
dither->optimized,
|
||||
dither->optimize_palette,
|
||||
dither->complexion,
|
||||
dither->cachetable,
|
||||
&ncolors,
|
||||
dither->allocator);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
free(dest);
|
||||
dest = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dither->ncolors = ncolors;
|
||||
|
||||
end:
|
||||
free(normalized_pixels);
|
||||
sixel_dither_unref(dither);
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
#if HAVE_TESTS
|
||||
static int
|
||||
test1(void)
|
||||
{
|
||||
sixel_dither_t *dither = NULL;
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
dither = sixel_dither_create(2);
|
||||
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
if (dither == NULL) {
|
||||
goto error;
|
||||
}
|
||||
sixel_dither_ref(dither);
|
||||
sixel_dither_unref(dither);
|
||||
nret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
sixel_dither_unref(dither);
|
||||
return nret;
|
||||
}
|
||||
|
||||
static int
|
||||
test2(void)
|
||||
{
|
||||
sixel_dither_t *dither = NULL;
|
||||
int colors;
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
dither = sixel_dither_create(INT_MAX);
|
||||
#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
if (dither == NULL) {
|
||||
goto error;
|
||||
}
|
||||
sixel_dither_set_body_only(dither, 1);
|
||||
colors = sixel_dither_get_num_of_histogram_colors(dither);
|
||||
if (colors != -1) {
|
||||
goto error;
|
||||
}
|
||||
nret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
sixel_dither_unref(dither);
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI int
|
||||
sixel_dither_tests_main(void)
|
||||
{
|
||||
int nret = EXIT_FAILURE;
|
||||
size_t i;
|
||||
typedef int (* testcase)(void);
|
||||
|
||||
static testcase const testcases[] = {
|
||||
test1,
|
||||
test2,
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
|
||||
nret = testcases[i]();
|
||||
if (nret != EXIT_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
nret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
return nret;
|
||||
}
|
||||
#endif /* HAVE_TESTS */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LIBSIXEL_DITHER_H
|
||||
#define LIBSIXEL_DITHER_H
|
||||
|
||||
#include <sixel.h>
|
||||
|
||||
/* dither context object */
|
||||
struct sixel_dither {
|
||||
unsigned int ref; /* reference counter */
|
||||
unsigned char *palette; /* palette definition */
|
||||
unsigned short *cachetable; /* cache table */
|
||||
int reqcolors; /* requested colors */
|
||||
int ncolors; /* active colors */
|
||||
int origcolors; /* original colors */
|
||||
int optimized; /* pixel is 15bpp compressable */
|
||||
int optimize_palette; /* minimize palette size */
|
||||
int complexion; /* for complexion correction */
|
||||
int bodyonly; /* do not output palette section if true */
|
||||
int method_for_largest; /* method for finding the largest dimention
|
||||
for splitting */
|
||||
int method_for_rep; /* method for choosing a color from the box */
|
||||
int method_for_diffuse; /* method for diffusing */
|
||||
int quality_mode; /* quality of histogram */
|
||||
int keycolor; /* background color */
|
||||
int pixelformat; /* pixelformat for internal processing */
|
||||
sixel_allocator_t *allocator; /* allocator */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* apply palette */
|
||||
sixel_index_t *
|
||||
sixel_dither_apply_palette(struct sixel_dither /* in */ *dither,
|
||||
unsigned char /* in */ *pixels,
|
||||
int /* in */ width,
|
||||
int /* in */ height);
|
||||
|
||||
#if HAVE_TESTS
|
||||
int
|
||||
sixel_frame_tests_main(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LIBSIXEL_DITHER_H */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MALLOC_STUB_H
|
||||
#define MALLOC_STUB_H
|
||||
|
||||
#if HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif /* HAVE_SYS_TYPES_H */
|
||||
|
||||
#if !HAVE_MALLOC
|
||||
void * rpl_malloc(size_t n);
|
||||
#endif /* !HAVE_MALLOC */
|
||||
|
||||
#if !HAVE_REALLOC
|
||||
void * rpl_realloc(void *p, size_t n);
|
||||
#endif /* !HAVE_REALLOC */
|
||||
|
||||
#if 0
|
||||
int rpl_posix_memalign(void **memptr, size_t alignment, size_t size);
|
||||
#endif
|
||||
|
||||
#endif /* MALLOC_STUB_H */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+211
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* STDC_HEADERS */
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
|
||||
#if HAVE_ASSERT_H
|
||||
# include <assert.h>
|
||||
#endif /* HAVE_ASSERT_H */
|
||||
|
||||
#include <sixel.h>
|
||||
#include "output.h"
|
||||
|
||||
|
||||
/* create new output context object */
|
||||
SIXELAPI SIXELSTATUS
|
||||
sixel_output_new(
|
||||
sixel_output_t /* out */ **output,
|
||||
sixel_write_function /* in */ fn_write,
|
||||
void /* in */ *priv,
|
||||
sixel_allocator_t /* in */ *allocator)
|
||||
{
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
size_t size;
|
||||
|
||||
if (allocator == NULL) {
|
||||
status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
sixel_allocator_ref(allocator);
|
||||
}
|
||||
size = sizeof(sixel_output_t) + SIXEL_OUTPUT_PACKET_SIZE * 2;
|
||||
|
||||
*output = (sixel_output_t *)sixel_allocator_malloc(allocator, size);
|
||||
if (*output == NULL) {
|
||||
sixel_helper_set_additional_message(
|
||||
"sixel_output_new: sixel_allocator_malloc() failed.");
|
||||
status = SIXEL_BAD_ALLOCATION;
|
||||
sixel_allocator_unref(allocator);
|
||||
goto end;
|
||||
}
|
||||
|
||||
(*output)->ref = 1;
|
||||
(*output)->has_8bit_control = 0;
|
||||
(*output)->has_sdm_glitch = 0;
|
||||
(*output)->has_gri_arg_limit = 1;
|
||||
(*output)->skip_dcs_envelope = 0;
|
||||
(*output)->palette_type = SIXEL_PALETTETYPE_AUTO;
|
||||
(*output)->fn_write = fn_write;
|
||||
(*output)->save_pixel = 0;
|
||||
(*output)->save_count = 0;
|
||||
(*output)->active_palette = (-1);
|
||||
(*output)->node_top = NULL;
|
||||
(*output)->node_free = NULL;
|
||||
(*output)->priv = priv;
|
||||
(*output)->pos = 0;
|
||||
(*output)->penetrate_multiplexer = 0;
|
||||
(*output)->encode_policy = SIXEL_ENCODEPOLICY_AUTO;
|
||||
(*output)->allocator = allocator;
|
||||
|
||||
status = SIXEL_OK;
|
||||
|
||||
end:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* deprecated: create an output object */
|
||||
SIXELAPI sixel_output_t *
|
||||
sixel_output_create(sixel_write_function fn_write, void *priv)
|
||||
{
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
sixel_output_t *output = NULL;
|
||||
|
||||
status = sixel_output_new(&output, fn_write, priv, NULL);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/* destroy output context object */
|
||||
SIXELAPI void
|
||||
sixel_output_destroy(sixel_output_t *output)
|
||||
{
|
||||
sixel_allocator_t *allocator;
|
||||
|
||||
if (output) {
|
||||
allocator = output->allocator;
|
||||
sixel_allocator_free(allocator, output);
|
||||
sixel_allocator_unref(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* increase reference count of output context object (thread-unsafe) */
|
||||
SIXELAPI void
|
||||
sixel_output_ref(sixel_output_t *output)
|
||||
{
|
||||
/* TODO: be thread-safe */
|
||||
++output->ref;
|
||||
}
|
||||
|
||||
|
||||
/* decrease reference count of output context object (thread-unsafe) */
|
||||
SIXELAPI void
|
||||
sixel_output_unref(sixel_output_t *output)
|
||||
{
|
||||
/* TODO: be thread-safe */
|
||||
if (output) {
|
||||
assert(output->ref > 0);
|
||||
output->ref--;
|
||||
if (output->ref == 0) {
|
||||
sixel_output_destroy(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* get 8bit output mode which indicates whether it uses C1 control characters */
|
||||
SIXELAPI int
|
||||
sixel_output_get_8bit_availability(sixel_output_t *output)
|
||||
{
|
||||
return output->has_8bit_control;
|
||||
}
|
||||
|
||||
|
||||
/* set 8bit output mode state */
|
||||
SIXELAPI void
|
||||
sixel_output_set_8bit_availability(sixel_output_t *output, int availability)
|
||||
{
|
||||
output->has_8bit_control = availability;
|
||||
}
|
||||
|
||||
|
||||
/* set whether limit arguments of DECGRI('!') to 255 */
|
||||
SIXELAPI void
|
||||
sixel_output_set_gri_arg_limit(
|
||||
sixel_output_t /* in */ *output, /* output context */
|
||||
int /* in */ value) /* 0: don't limit arguments of DECGRI
|
||||
1: limit arguments of DECGRI to 255 */
|
||||
{
|
||||
output->has_gri_arg_limit = value;
|
||||
}
|
||||
|
||||
|
||||
/* set GNU Screen penetration feature enable or disable */
|
||||
SIXELAPI void
|
||||
sixel_output_set_penetrate_multiplexer(sixel_output_t *output, int penetrate)
|
||||
{
|
||||
output->penetrate_multiplexer = penetrate;
|
||||
}
|
||||
|
||||
|
||||
/* set whether we skip DCS envelope */
|
||||
SIXELAPI void
|
||||
sixel_output_set_skip_dcs_envelope(sixel_output_t *output, int skip)
|
||||
{
|
||||
output->skip_dcs_envelope = skip;
|
||||
}
|
||||
|
||||
|
||||
/* set palette type: RGB or HLS */
|
||||
SIXELAPI void
|
||||
sixel_output_set_palette_type(sixel_output_t *output, int palettetype)
|
||||
{
|
||||
output->palette_type = palettetype;
|
||||
}
|
||||
|
||||
|
||||
/* set encodeing policy: auto, fast or size */
|
||||
SIXELAPI void
|
||||
sixel_output_set_encode_policy(sixel_output_t *output, int encode_policy)
|
||||
{
|
||||
output->encode_policy = encode_policy;
|
||||
}
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LIBSIXEL_OUTPUT_H
|
||||
#define LIBSIXEL_OUTPUT_H
|
||||
|
||||
typedef struct sixel_node {
|
||||
struct sixel_node *next;
|
||||
int pal;
|
||||
int sx;
|
||||
int mx;
|
||||
char *map;
|
||||
} sixel_node_t;
|
||||
|
||||
struct sixel_output {
|
||||
|
||||
int ref;
|
||||
sixel_allocator_t *allocator;
|
||||
|
||||
/* compatiblity flags */
|
||||
|
||||
/* 0: 7bit terminal,
|
||||
* 1: 8bit terminal */
|
||||
unsigned char has_8bit_control;
|
||||
|
||||
/* 0: the terminal has sixel scrolling
|
||||
* 1: the terminal does not have sixel scrolling */
|
||||
unsigned char has_sixel_scrolling;
|
||||
|
||||
/* 1: the argument of repeat introducer(DECGRI) is not limitted
|
||||
0: the argument of repeat introducer(DECGRI) is limitted 255 */
|
||||
unsigned char has_gri_arg_limit;
|
||||
|
||||
/* 0: DECSDM set (CSI ? 80 h) enables sixel scrolling
|
||||
1: DECSDM set (CSI ? 80 h) disables sixel scrolling */
|
||||
unsigned char has_sdm_glitch;
|
||||
|
||||
/* 0: do not skip DCS envelope
|
||||
* 1: skip DCS envelope */
|
||||
unsigned char skip_dcs_envelope;
|
||||
|
||||
/* PALETTETYPE_AUTO: select palette type automatically
|
||||
* PALETTETYPE_HLS : HLS color space
|
||||
* PALETTETYPE_RGB : RGB color space */
|
||||
unsigned char palette_type;
|
||||
|
||||
sixel_write_function fn_write;
|
||||
|
||||
int save_pixel;
|
||||
int save_count;
|
||||
int active_palette;
|
||||
|
||||
sixel_node_t *node_top;
|
||||
sixel_node_t *node_free;
|
||||
|
||||
int penetrate_multiplexer;
|
||||
int encode_policy;
|
||||
|
||||
void *priv;
|
||||
int pos;
|
||||
unsigned char buffer[1];
|
||||
};
|
||||
|
||||
#endif /* LIBSIXEL_OUTPUT_H */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+725
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* STDC_HEADERS */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if HAVE_MEMORY_H
|
||||
# include <memory.h>
|
||||
#endif /* HAVE_MEMORY_H */
|
||||
|
||||
#include <sixel.h>
|
||||
|
||||
static void
|
||||
get_rgb(unsigned char const *data,
|
||||
int const pixelformat,
|
||||
int depth,
|
||||
unsigned char *r,
|
||||
unsigned char *g,
|
||||
unsigned char *b)
|
||||
{
|
||||
unsigned int pixels = 0;
|
||||
#if SWAP_BYTES
|
||||
unsigned int low;
|
||||
unsigned int high;
|
||||
#endif
|
||||
int count = 0;
|
||||
|
||||
while (count < depth) {
|
||||
pixels = *(data + count) | (pixels << 8);
|
||||
count++;
|
||||
}
|
||||
|
||||
/* TODO: we should swap bytes (only necessary on LSByte first hardware?) */
|
||||
#if SWAP_BYTES
|
||||
if (depth == 2) {
|
||||
low = pixels & 0xff;
|
||||
high = (pixels >> 8) & 0xff;
|
||||
pixels = (low << 8) | high;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (pixelformat) {
|
||||
case SIXEL_PIXELFORMAT_RGB555:
|
||||
*r = ((pixels >> 10) & 0x1f) << 3;
|
||||
*g = ((pixels >> 5) & 0x1f) << 3;
|
||||
*b = ((pixels >> 0) & 0x1f) << 3;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_RGB565:
|
||||
*r = ((pixels >> 11) & 0x1f) << 3;
|
||||
*g = ((pixels >> 5) & 0x3f) << 2;
|
||||
*b = ((pixels >> 0) & 0x1f) << 3;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_RGB888:
|
||||
*r = (pixels >> 16) & 0xff;
|
||||
*g = (pixels >> 8) & 0xff;
|
||||
*b = (pixels >> 0) & 0xff;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_BGR555:
|
||||
*r = ((pixels >> 0) & 0x1f) << 3;
|
||||
*g = ((pixels >> 5) & 0x1f) << 3;
|
||||
*b = ((pixels >> 10) & 0x1f) << 3;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_BGR565:
|
||||
*r = ((pixels >> 0) & 0x1f) << 3;
|
||||
*g = ((pixels >> 5) & 0x3f) << 2;
|
||||
*b = ((pixels >> 11) & 0x1f) << 3;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_BGR888:
|
||||
*r = (pixels >> 0) & 0xff;
|
||||
*g = (pixels >> 8) & 0xff;
|
||||
*b = (pixels >> 16) & 0xff;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_RGBA8888:
|
||||
*r = (pixels >> 24) & 0xff;
|
||||
*g = (pixels >> 16) & 0xff;
|
||||
*b = (pixels >> 8) & 0xff;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_ARGB8888:
|
||||
*r = (pixels >> 16) & 0xff;
|
||||
*g = (pixels >> 8) & 0xff;
|
||||
*b = (pixels >> 0) & 0xff;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_BGRA8888:
|
||||
*r = (pixels >> 8) & 0xff;
|
||||
*g = (pixels >> 16) & 0xff;
|
||||
*b = (pixels >> 24) & 0xff;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_ABGR8888:
|
||||
*r = (pixels >> 0) & 0xff;
|
||||
*g = (pixels >> 8) & 0xff;
|
||||
*b = (pixels >> 16) & 0xff;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_GA88:
|
||||
*r = *g = *b = (pixels >> 8) & 0xff;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_G8:
|
||||
case SIXEL_PIXELFORMAT_AG88:
|
||||
*r = *g = *b = pixels & 0xff;
|
||||
break;
|
||||
default:
|
||||
*r = *g = *b = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI int
|
||||
sixel_helper_compute_depth(int pixelformat)
|
||||
{
|
||||
int depth = (-1); /* unknown */
|
||||
|
||||
switch (pixelformat) {
|
||||
case SIXEL_PIXELFORMAT_ARGB8888:
|
||||
case SIXEL_PIXELFORMAT_RGBA8888:
|
||||
case SIXEL_PIXELFORMAT_ABGR8888:
|
||||
case SIXEL_PIXELFORMAT_BGRA8888:
|
||||
depth = 4;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_RGB888:
|
||||
case SIXEL_PIXELFORMAT_BGR888:
|
||||
depth = 3;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_RGB555:
|
||||
case SIXEL_PIXELFORMAT_RGB565:
|
||||
case SIXEL_PIXELFORMAT_BGR555:
|
||||
case SIXEL_PIXELFORMAT_BGR565:
|
||||
case SIXEL_PIXELFORMAT_AG88:
|
||||
case SIXEL_PIXELFORMAT_GA88:
|
||||
depth = 2;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_G1:
|
||||
case SIXEL_PIXELFORMAT_G2:
|
||||
case SIXEL_PIXELFORMAT_G4:
|
||||
case SIXEL_PIXELFORMAT_G8:
|
||||
case SIXEL_PIXELFORMAT_PAL1:
|
||||
case SIXEL_PIXELFORMAT_PAL2:
|
||||
case SIXEL_PIXELFORMAT_PAL4:
|
||||
case SIXEL_PIXELFORMAT_PAL8:
|
||||
depth = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
expand_rgb(unsigned char *dst,
|
||||
unsigned char const *src,
|
||||
int width, int height,
|
||||
int pixelformat, int depth)
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int dst_offset;
|
||||
int src_offset;
|
||||
unsigned char r, g, b;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
for (x = 0; x < width; x++) {
|
||||
src_offset = depth * (y * width + x);
|
||||
dst_offset = 3 * (y * width + x);
|
||||
get_rgb(src + src_offset, pixelformat, depth, &r, &g, &b);
|
||||
|
||||
*(dst + dst_offset + 0) = r;
|
||||
*(dst + dst_offset + 1) = g;
|
||||
*(dst + dst_offset + 2) = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static SIXELSTATUS
|
||||
expand_palette(unsigned char *dst, unsigned char const *src,
|
||||
int width, int height, int const pixelformat)
|
||||
{
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
int x;
|
||||
int y;
|
||||
int i;
|
||||
int bpp; /* bit per plane */
|
||||
|
||||
switch (pixelformat) {
|
||||
case SIXEL_PIXELFORMAT_PAL1:
|
||||
case SIXEL_PIXELFORMAT_G1:
|
||||
bpp = 1;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_PAL2:
|
||||
case SIXEL_PIXELFORMAT_G2:
|
||||
bpp = 2;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_PAL4:
|
||||
case SIXEL_PIXELFORMAT_G4:
|
||||
bpp = 4;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_PAL8:
|
||||
case SIXEL_PIXELFORMAT_G8:
|
||||
for (i = 0; i < width * height; ++i, ++src) {
|
||||
*dst++ = *src;
|
||||
}
|
||||
status = SIXEL_OK;
|
||||
goto end;
|
||||
default:
|
||||
status = SIXEL_BAD_ARGUMENT;
|
||||
sixel_helper_set_additional_message(
|
||||
"expand_palette: invalid pixelformat.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
#if HAVE_DEBUG
|
||||
fprintf(stderr, "expanding PAL%d to PAL8...\n", bpp);
|
||||
#endif
|
||||
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width * bpp / 8; ++x) {
|
||||
for (i = 0; i < 8 / bpp; ++i) {
|
||||
*dst++ = *src >> (8 / bpp - 1 - i) * bpp & ((1 << bpp) - 1);
|
||||
}
|
||||
src++;
|
||||
}
|
||||
x = width - x * 8 / bpp;
|
||||
if (x > 0) {
|
||||
for (i = 0; i < x; ++i) {
|
||||
*dst++ = *src >> (8 - (i + 1) * bpp) & ((1 << bpp) - 1);
|
||||
}
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
status = SIXEL_OK;
|
||||
|
||||
end:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI SIXELSTATUS
|
||||
sixel_helper_normalize_pixelformat(
|
||||
unsigned char /* out */ *dst, /* destination buffer */
|
||||
int /* out */ *dst_pixelformat, /* converted pixelformat */
|
||||
unsigned char const /* in */ *src, /* source pixels */
|
||||
int /* in */ src_pixelformat, /* format of source image */
|
||||
int /* in */ width, /* width of source image */
|
||||
int /* in */ height) /* height of source image */
|
||||
{
|
||||
SIXELSTATUS status = SIXEL_FALSE;
|
||||
|
||||
switch (src_pixelformat) {
|
||||
case SIXEL_PIXELFORMAT_G8:
|
||||
expand_rgb(dst, src, width, height, src_pixelformat, 1);
|
||||
*dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_RGB565:
|
||||
case SIXEL_PIXELFORMAT_RGB555:
|
||||
case SIXEL_PIXELFORMAT_BGR565:
|
||||
case SIXEL_PIXELFORMAT_BGR555:
|
||||
case SIXEL_PIXELFORMAT_GA88:
|
||||
case SIXEL_PIXELFORMAT_AG88:
|
||||
expand_rgb(dst, src, width, height, src_pixelformat, 2);
|
||||
*dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_RGB888:
|
||||
case SIXEL_PIXELFORMAT_BGR888:
|
||||
expand_rgb(dst, src, width, height, src_pixelformat, 3);
|
||||
*dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_RGBA8888:
|
||||
case SIXEL_PIXELFORMAT_ARGB8888:
|
||||
case SIXEL_PIXELFORMAT_BGRA8888:
|
||||
case SIXEL_PIXELFORMAT_ABGR8888:
|
||||
expand_rgb(dst, src, width, height, src_pixelformat, 4);
|
||||
*dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_PAL1:
|
||||
case SIXEL_PIXELFORMAT_PAL2:
|
||||
case SIXEL_PIXELFORMAT_PAL4:
|
||||
*dst_pixelformat = SIXEL_PIXELFORMAT_PAL8;
|
||||
status = expand_palette(dst, src, width, height, src_pixelformat);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_G1:
|
||||
case SIXEL_PIXELFORMAT_G2:
|
||||
case SIXEL_PIXELFORMAT_G4:
|
||||
*dst_pixelformat = SIXEL_PIXELFORMAT_G8;
|
||||
status = expand_palette(dst, src, width, height, src_pixelformat);
|
||||
if (SIXEL_FAILED(status)) {
|
||||
goto end;
|
||||
}
|
||||
break;
|
||||
case SIXEL_PIXELFORMAT_PAL8:
|
||||
memcpy(dst, src, (size_t)(width * height));
|
||||
*dst_pixelformat = src_pixelformat;
|
||||
break;
|
||||
default:
|
||||
status = SIXEL_BAD_ARGUMENT;
|
||||
goto end;
|
||||
}
|
||||
|
||||
status = SIXEL_OK;
|
||||
|
||||
end:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
#if HAVE_TESTS
|
||||
static int
|
||||
test1(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
unsigned char src[] = { 0x46, 0xf3, 0xe5 };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if ((dst[0] << 16 | dst[1] << 8 | dst[2]) != (src[0] << 16 | src[1] << 8 | src[2])) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test1");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test2(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_RGB555;
|
||||
unsigned char src[] = { 0x47, 0x9c };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if ((dst[0] >> 3 << 10 | dst[1] >> 3 << 5 | dst[2] >> 3) != (src[0] << 8 | src[1])) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test2");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test3(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_RGB565;
|
||||
unsigned char src[] = { 0x47, 0x9c };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if ((dst[0] >> 3 << 11 | dst[1] >> 2 << 5 | dst[2] >> 3) != (src[0] << 8 | src[1])) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test3");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test4(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_BGR888;
|
||||
unsigned char src[] = { 0x46, 0xf3, 0xe5 };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if ((dst[2] << 16 | dst[1] << 8 | dst[0]) != (src[0] << 16 | src[1] << 8 | src[2])) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test4");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test5(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_BGR555;
|
||||
unsigned char src[] = { 0x23, 0xc8 };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if ((dst[2] >> 3 << 10 | dst[1] >> 3 << 5 | dst[0] >> 3) != (src[0] << 8 | src[1])) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test5");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test6(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_BGR565;
|
||||
unsigned char src[] = { 0x47, 0x88 };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if ((dst[2] >> 3 << 11 | dst[1] >> 2 << 5 | dst[0] >> 3) != (src[0] << 8 | src[1])) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test6");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test7(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_AG88;
|
||||
unsigned char src[] = { 0x47, 0x88 };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if (dst[0] != src[1]) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test7");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test8(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_GA88;
|
||||
unsigned char src[] = { 0x47, 0x88 };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if (dst[0] != src[0]) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test8");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test9(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_RGBA8888;
|
||||
unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if (dst[0] != src[0]) {
|
||||
goto error;
|
||||
}
|
||||
if (dst[1] != src[1]) {
|
||||
goto error;
|
||||
}
|
||||
if (dst[2] != src[2]) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test8");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test10(void)
|
||||
{
|
||||
unsigned char dst[3];
|
||||
int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888;
|
||||
int src_pixelformat = SIXEL_PIXELFORMAT_ARGB8888;
|
||||
unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 };
|
||||
int ret = 0;
|
||||
|
||||
int nret = EXIT_FAILURE;
|
||||
|
||||
ret = sixel_helper_normalize_pixelformat(dst,
|
||||
&dst_pixelformat,
|
||||
src,
|
||||
src_pixelformat,
|
||||
1,
|
||||
1);
|
||||
if (ret != 0) {
|
||||
goto error;
|
||||
}
|
||||
if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) {
|
||||
goto error;
|
||||
}
|
||||
if (dst[0] != src[1]) {
|
||||
goto error;
|
||||
}
|
||||
if (dst[1] != src[2]) {
|
||||
goto error;
|
||||
}
|
||||
if (dst[2] != src[3]) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
perror("test8");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI int
|
||||
sixel_pixelformat_tests_main(void)
|
||||
{
|
||||
int nret = EXIT_FAILURE;
|
||||
size_t i;
|
||||
typedef int (* testcase)(void);
|
||||
|
||||
static testcase const testcases[] = {
|
||||
test1,
|
||||
test2,
|
||||
test3,
|
||||
test4,
|
||||
test5,
|
||||
test6,
|
||||
test7,
|
||||
test8,
|
||||
test9,
|
||||
test10,
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
|
||||
nret = testcases[i]();
|
||||
if (nret != EXIT_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
nret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
return nret;
|
||||
}
|
||||
#endif /* HAVE_TESTS */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+1549
File diff suppressed because it is too large
Load Diff
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LIBSIXEL_QUANT_H
|
||||
#define LIBSIXEL_QUANT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <sixel.h>
|
||||
|
||||
/* choose colors using median-cut method */
|
||||
SIXELSTATUS
|
||||
sixel_quant_make_palette(
|
||||
unsigned char /* out */ **result,
|
||||
unsigned const char /* in */ *data, /* data for sampling */
|
||||
unsigned int /* in */ length, /* data size */
|
||||
int /* in */ pixelformat,
|
||||
unsigned int /* in */ reqcolors,
|
||||
unsigned int /* in */ *ncolors,
|
||||
unsigned int /* in */ *origcolors,
|
||||
int /* in */ methodForLargest,
|
||||
int /* in */ methodForRep,
|
||||
int /* in */ qualityMode,
|
||||
sixel_allocator_t /* in */ *allocator);
|
||||
|
||||
|
||||
/* apply color palette into specified pixel buffers */
|
||||
SIXELSTATUS
|
||||
sixel_quant_apply_palette(
|
||||
sixel_index_t /* out */ *result,
|
||||
unsigned char /* in */ *data,
|
||||
int /* in */ width,
|
||||
int /* in */ height,
|
||||
int /* in */ pixelformat,
|
||||
unsigned char /* in */ *palette,
|
||||
int /* in */ reqcolor,
|
||||
int const /* in */ methodForDiffuse,
|
||||
int /* in */ foptimize,
|
||||
int /* in */ foptimize_palette,
|
||||
int /* in */ complexion,
|
||||
unsigned short /* in */ *cachetable,
|
||||
int /* in */ *ncolor,
|
||||
sixel_allocator_t /* in */ *allocator);
|
||||
|
||||
|
||||
/* deallocate specified palette */
|
||||
void
|
||||
sixel_quant_free_palette(
|
||||
unsigned char /* in */ *data,
|
||||
sixel_allocator_t /* in */ *allocator);
|
||||
|
||||
#if HAVE_TESTS
|
||||
int
|
||||
sixel_quant_tests_main(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LIBSIXEL_QUANT_H */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"home": "https://github.com/saitoha/libsixel",
|
||||
"license": "MIT ( embed in source )",
|
||||
"version": "1.8.7-r2",
|
||||
"author": "Hayaki Saito",
|
||||
"modifications": [
|
||||
"sixel.h: SIXELAPI is empty, so the statically linked subset exports nothing",
|
||||
"config.h: hand-written, because only the vendored files read it",
|
||||
"tosixel.c: DCS P2 initialised to 1, so unset pixels are not filled with colour-table entry 0",
|
||||
"output.c: release the allocator reference if output-context allocation fails"
|
||||
]
|
||||
}
|
||||
Vendored
+1173
File diff suppressed because it is too large
Load Diff
Vendored
+355
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2018 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* STDC_HEADERS */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if HAVE_MEMORY_H
|
||||
# include <memory.h>
|
||||
#endif /* HAVE_MEMORY_H */
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif /* HAVE_STRING_H */
|
||||
#ifdef HAVE_ERRNO_H
|
||||
# include <errno.h>
|
||||
#endif /* HAVE_ERRNO_H */
|
||||
#ifdef HAVE_LIBCURL
|
||||
# include <curl/curl.h>
|
||||
#endif /* HAVE_LIBCURL */
|
||||
|
||||
#include <sixel.h>
|
||||
#include "status.h"
|
||||
|
||||
#define SIXEL_MESSAGE_OK ("succeeded")
|
||||
#define SIXEL_MESSAGE_FALSE ("unexpected error (SIXEL_FALSE)");
|
||||
#define SIXEL_MESSAGE_UNEXPECTED ("unexpected error")
|
||||
#define SIXEL_MESSAGE_INTERRUPTED ("interrupted by a signal")
|
||||
#define SIXEL_MESSAGE_BAD_ALLOCATION ("runtime error: bad allocation error")
|
||||
#define SIXEL_MESSAGE_BAD_ARGUMENT ("runtime error: bad argument detected")
|
||||
#define SIXEL_MESSAGE_BAD_INPUT ("runtime error: bad input detected")
|
||||
#define SIXEL_MESSAGE_BAD_INTEGER_OVERFLOW ("runtime error: integer overflow")
|
||||
#define SIXEL_MESSAGE_RUNTIME_ERROR ("runtime error")
|
||||
#define SIXEL_MESSAGE_LOGIC_ERROR ("logic error")
|
||||
#define SIXEL_MESSAGE_NOT_IMPLEMENTED ("feature error: not implemented")
|
||||
#define SIXEL_MESSAGE_FEATURE_ERROR ("feature error")
|
||||
#define SIXEL_MESSAGE_STBI_ERROR ("stb_image error")
|
||||
#define SIXEL_MESSAGE_STBIW_ERROR ("stb_image_write error")
|
||||
#define SIXEL_MESSAGE_JPEG_ERROR ("libjpeg error")
|
||||
#define SIXEL_MESSAGE_PNG_ERROR ("libpng error")
|
||||
#define SIXEL_MESSAGE_GDK_ERROR ("GDK error")
|
||||
#define SIXEL_MESSAGE_GD_ERROR ("GD error")
|
||||
|
||||
|
||||
static char g_buffer[1024] = { 0x0 };
|
||||
|
||||
/* set detailed error message (thread-unsafe) */
|
||||
SIXELAPI void
|
||||
sixel_helper_set_additional_message(
|
||||
const char /* in */ *message /* error message */
|
||||
)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (message == 0)
|
||||
return;
|
||||
len = strlen(message);
|
||||
memcpy(g_buffer, message, len < sizeof(g_buffer) ? len: sizeof(g_buffer) - 1);
|
||||
g_buffer[sizeof(g_buffer) - 1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* get detailed error message (thread-unsafe) */
|
||||
SIXELAPI char const *
|
||||
sixel_helper_get_additional_message(void)
|
||||
{
|
||||
return g_buffer;
|
||||
}
|
||||
|
||||
|
||||
/* convert error status code int formatted string */
|
||||
SIXELAPI char const *
|
||||
sixel_helper_format_error(
|
||||
SIXELSTATUS /* in */ status /* status code */
|
||||
)
|
||||
{
|
||||
static char buffer[1024];
|
||||
char const *error_string;
|
||||
char *p;
|
||||
size_t len;
|
||||
|
||||
switch (status & 0x1000) {
|
||||
case SIXEL_OK:
|
||||
switch (status) {
|
||||
case SIXEL_INTERRUPTED:
|
||||
error_string = SIXEL_MESSAGE_INTERRUPTED;
|
||||
break;
|
||||
case SIXEL_OK:
|
||||
default:
|
||||
error_string = SIXEL_MESSAGE_OK;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SIXEL_FALSE:
|
||||
switch (status & 0x1f00) {
|
||||
case SIXEL_RUNTIME_ERROR:
|
||||
switch (status) {
|
||||
case SIXEL_BAD_ALLOCATION:
|
||||
error_string = SIXEL_MESSAGE_BAD_ALLOCATION;
|
||||
break;
|
||||
case SIXEL_BAD_ARGUMENT:
|
||||
error_string = SIXEL_MESSAGE_BAD_ARGUMENT;
|
||||
break;
|
||||
case SIXEL_BAD_INPUT:
|
||||
error_string = SIXEL_MESSAGE_BAD_INPUT;
|
||||
break;
|
||||
case SIXEL_BAD_INTEGER_OVERFLOW:
|
||||
error_string = SIXEL_MESSAGE_BAD_INTEGER_OVERFLOW;
|
||||
break;
|
||||
default:
|
||||
error_string = SIXEL_MESSAGE_RUNTIME_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SIXEL_LOGIC_ERROR:
|
||||
error_string = SIXEL_MESSAGE_LOGIC_ERROR;
|
||||
break;
|
||||
case SIXEL_FEATURE_ERROR:
|
||||
switch (status) {
|
||||
case SIXEL_NOT_IMPLEMENTED:
|
||||
error_string = SIXEL_MESSAGE_NOT_IMPLEMENTED;
|
||||
break;
|
||||
default:
|
||||
error_string = SIXEL_MESSAGE_FEATURE_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SIXEL_LIBC_ERROR:
|
||||
p = strerror(errno);
|
||||
len = strlen(p) + 1;
|
||||
memcpy(buffer, p, len < sizeof(buffer) ? len: sizeof(buffer) - 1);
|
||||
buffer[sizeof(buffer) - 1] = 0;
|
||||
error_string = buffer;
|
||||
break;
|
||||
#ifdef HAVE_LIBCURL
|
||||
case SIXEL_CURL_ERROR:
|
||||
error_string = curl_easy_strerror(status & 0xff);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_JPEG
|
||||
case SIXEL_JPEG_ERROR:
|
||||
error_string = SIXEL_MESSAGE_JPEG_ERROR;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_LIBPNG
|
||||
case SIXEL_PNG_ERROR:
|
||||
error_string = SIXEL_MESSAGE_PNG_ERROR;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_GDK_PIXBUF2
|
||||
case SIXEL_GDK_ERROR:
|
||||
error_string = SIXEL_MESSAGE_GDK_ERROR;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_GD
|
||||
case SIXEL_GD_ERROR:
|
||||
error_string = SIXEL_MESSAGE_GD_ERROR;
|
||||
break;
|
||||
#endif
|
||||
case SIXEL_STBI_ERROR:
|
||||
error_string = SIXEL_MESSAGE_STBI_ERROR;
|
||||
break;
|
||||
case SIXEL_STBIW_ERROR:
|
||||
error_string = SIXEL_MESSAGE_STBIW_ERROR;
|
||||
break;
|
||||
case SIXEL_FALSE:
|
||||
error_string = SIXEL_MESSAGE_FALSE;
|
||||
break;
|
||||
default:
|
||||
error_string = SIXEL_MESSAGE_UNEXPECTED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error_string = SIXEL_MESSAGE_UNEXPECTED;
|
||||
break;
|
||||
}
|
||||
return error_string;
|
||||
}
|
||||
|
||||
|
||||
#if HAVE_TESTS
|
||||
static int
|
||||
test1(void)
|
||||
{
|
||||
int nret = EXIT_FAILURE;
|
||||
char const *message;
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_OK);
|
||||
if (strcmp(message, SIXEL_MESSAGE_OK) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_INTERRUPTED);
|
||||
if (strcmp(message, SIXEL_MESSAGE_INTERRUPTED) != 0) {
|
||||
goto error;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
error:
|
||||
perror("test1");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
test2(void)
|
||||
{
|
||||
int nret = EXIT_FAILURE;
|
||||
char const *message;
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_BAD_ALLOCATION);
|
||||
if (strcmp(message, SIXEL_MESSAGE_BAD_ALLOCATION) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_BAD_ARGUMENT);
|
||||
if (strcmp(message, SIXEL_MESSAGE_BAD_ARGUMENT) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_BAD_INPUT);
|
||||
if (strcmp(message, SIXEL_MESSAGE_BAD_INPUT) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_RUNTIME_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_RUNTIME_ERROR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_LOGIC_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_LOGIC_ERROR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_NOT_IMPLEMENTED);
|
||||
if (strcmp(message, SIXEL_MESSAGE_NOT_IMPLEMENTED) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_FEATURE_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_FEATURE_ERROR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_LIBC_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_UNEXPECTED) == 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCURL
|
||||
message = sixel_helper_format_error(SIXEL_CURL_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_UNEXPECTED) == 0) {
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_JPEG
|
||||
message = sixel_helper_format_error(SIXEL_JPEG_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_JPEG_ERROR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBPNG
|
||||
message = sixel_helper_format_error(SIXEL_PNG_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_PNG_ERROR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_GD
|
||||
message = sixel_helper_format_error(SIXEL_GD_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_GD_ERROR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_GDK_PIXBUF2
|
||||
message = sixel_helper_format_error(SIXEL_GDK_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_GDK_ERROR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
#endif
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_STBI_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_STBI_ERROR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
message = sixel_helper_format_error(SIXEL_STBIW_ERROR);
|
||||
if (strcmp(message, SIXEL_MESSAGE_STBIW_ERROR) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
error:
|
||||
perror("test2");
|
||||
return nret;
|
||||
}
|
||||
|
||||
|
||||
SIXELAPI int
|
||||
sixel_status_tests_main(void)
|
||||
{
|
||||
int nret = EXIT_FAILURE;
|
||||
size_t i;
|
||||
typedef int (* testcase)(void);
|
||||
|
||||
static testcase const testcases[] = {
|
||||
test1,
|
||||
test2,
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
|
||||
nret = testcases[i]();
|
||||
if (nret != EXIT_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
nret = EXIT_SUCCESS;
|
||||
|
||||
error:
|
||||
return nret;
|
||||
}
|
||||
#endif /* HAVE_TESTS */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2016 Hayaki Saito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LIBSIXEL_STATUS_H
|
||||
#define LIBSIXEL_STATUS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if HAVE_TESTS
|
||||
int
|
||||
sixel_status_tests_main(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LIBSIXEL_STATUS_H */
|
||||
|
||||
/* emacs Local Variables: */
|
||||
/* emacs mode: c */
|
||||
/* emacs tab-width: 4 */
|
||||
/* emacs indent-tabs-mode: nil */
|
||||
/* emacs c-basic-offset: 4 */
|
||||
/* emacs End: */
|
||||
/* vim: set expandtab ts=4 sts=4 sw=4 : */
|
||||
/* EOF */
|
||||
Vendored
+1635
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"home": "https://github.com/ibireme/yyjson",
|
||||
"license": "MIT ( embed in source )",
|
||||
"version": "0.12.0",
|
||||
"version": "0.13.0",
|
||||
"author": "ibireme"
|
||||
}
|
||||
|
||||
Vendored
+700
-285
File diff suppressed because it is too large
Load Diff
Vendored
+911
-674
File diff suppressed because it is too large
Load Diff
+18
-6
@@ -3,29 +3,41 @@
|
||||
#include "common/FFstrbuf.h"
|
||||
#include "common/FFlist.h"
|
||||
|
||||
typedef struct FFPlatformSysinfo
|
||||
{
|
||||
typedef struct FFPlatformSysinfo {
|
||||
FFstrbuf name;
|
||||
FFstrbuf release;
|
||||
FFstrbuf version;
|
||||
FFstrbuf architecture;
|
||||
uint32_t pageSize;
|
||||
// The base-2 logarithm of the memory page size, i.e. a page is `1 << pageSizeShift` bytes.
|
||||
// Page sizes are powers of two on every supported platform. Storing the exponent instead of
|
||||
// the byte count keeps page count arithmetic in 64 bits and makes the invariant explicit.
|
||||
uint32_t pageSizeShift;
|
||||
} 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;
|
||||
FFstrbuf userShell;
|
||||
|
||||
FFPlatformSysinfo sysinfo;
|
||||
|
||||
#if _WIN32
|
||||
uint32_t initCP; // The code page used by the console when the program started
|
||||
#endif
|
||||
} FFPlatform;
|
||||
|
||||
void ffPlatformInit(FFPlatform* platform);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
// A cache for a value that is built at most once per generation.
|
||||
//
|
||||
// The value itself is owned by the entry owner (usually a file scope `static`); the entry only
|
||||
// remembers whether that value is still valid and knows how to drop it. `--dynamic-interval`
|
||||
// starts a new generation for every round, dropping all the values that have been built so far,
|
||||
// so the modules re-detect them instead of replaying the first round's snapshot.
|
||||
//
|
||||
// An entry registers itself the first time it is used. There is therefore no central list to keep
|
||||
// in sync, and a cache cannot be forgotten in it. Do not use this type for state that is carried
|
||||
// across calls to derive a rate (cpuusage, diskio, netio, top): that is a baseline rather than a
|
||||
// snapshot, and dropping it makes the first sample of a round read as zero.
|
||||
//
|
||||
// The storage pointed to by `storage` must stay valid for the whole run; only the value behind it
|
||||
// is dropped by an invalidation.
|
||||
//
|
||||
// Not thread safe: entries are only touched from a single thread, on a frame boundary, while the
|
||||
// modules are not running.
|
||||
typedef struct FFcacheEntry {
|
||||
const char* name; // For diagnostics.
|
||||
void* storage; // Owned by the entry owner. May be nullptr.
|
||||
void (*init)(void* storage); // Optional. Runs whenever the entry is stale.
|
||||
void (*destroy)(void* storage); // Optional. Must be idempotent, and tolerate a partially initialized storage.
|
||||
bool initialized;
|
||||
bool registered;
|
||||
struct FFcacheEntry* next; // Internal: registry link.
|
||||
} FFcacheEntry;
|
||||
|
||||
// Returns `entry->storage`, running `init` on it first when the entry is stale. The entry is marked
|
||||
// initialized before `init` runs, so a call made from within `init` returns the partially built
|
||||
// value instead of recursing.
|
||||
[[gnu::nonnull(1)]] void* ffCacheGet(FFcacheEntry* entry);
|
||||
|
||||
// Staleness test for entries whose initialization needs arguments that are not part of the cache
|
||||
// identity (such as the cover file request of `ffDetectMedia`) and therefore cannot live behind
|
||||
// `init(void*)`. Returns true exactly once per generation: the caller must then build the value
|
||||
// itself.
|
||||
[[gnu::nonnull(1), nodiscard]] bool ffCacheBeginInit(FFcacheEntry* entry);
|
||||
|
||||
// Drops the cached value of one entry, so that the next request builds it again. Idempotent, and
|
||||
// safe on an entry that has never been used.
|
||||
[[gnu::nonnull(1)]] void ffCacheInvalidate(FFcacheEntry* entry);
|
||||
|
||||
// Drops every value that has been built so far. Called once per `--dynamic-interval` round.
|
||||
void ffCacheInvalidateAll(void);
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <sal.h>
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__warn_unused_result__)
|
||||
#define FF_C_NODISCARD __attribute__((__warn_unused_result__))
|
||||
#elif defined(_MSC_VER)
|
||||
#define FF_C_NODISCARD _Check_return_
|
||||
#else
|
||||
#define FF_C_NODISCARD
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__format__)
|
||||
#define FF_C_PRINTF(formatStrIndex, argsStartIndex) __attribute__((__format__ (printf, formatStrIndex, argsStartIndex)))
|
||||
#else
|
||||
#define FF_C_PRINTF(formatStrIndex, argsStartIndex)
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__format__)
|
||||
#define FF_C_SCANF(formatStrIndex, argsStartIndex) __attribute__((__format__ (scanf, formatStrIndex, argsStartIndex)))
|
||||
#else
|
||||
#define FF_C_SCANF(formatStrIndex, argsStartIndex)
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__nonnull__)
|
||||
#define FF_C_NONNULL(argIndex, ...) __attribute__((__nonnull__(argIndex, ##__VA_ARGS__)))
|
||||
#else
|
||||
#define FF_C_NONNULL(argIndex, ...)
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute) && __has_attribute(__returns_nonnull__)
|
||||
#define FF_C_RETURNS_NONNULL __attribute__((__returns_nonnull__))
|
||||
#else
|
||||
#define FF_C_RETURNS_NONNULL
|
||||
#endif
|
||||
+103
-69
@@ -1,135 +1,169 @@
|
||||
#pragma once
|
||||
|
||||
#include "FFcheckmacros.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FF_LIST_DEFAULT_ALLOC 16
|
||||
|
||||
typedef struct FFlist
|
||||
{
|
||||
typedef struct FFlist {
|
||||
uint8_t* data;
|
||||
uint32_t elementSize;
|
||||
uint32_t length;
|
||||
uint32_t capacity;
|
||||
} FFlist;
|
||||
|
||||
void* ffListAdd(FFlist* list);
|
||||
|
||||
// Removes the first element, and copy its value to `*result`
|
||||
bool ffListShift(FFlist* list, void* result);
|
||||
// `result` is only written when the list is not empty, so it may be null for an empty list
|
||||
[[gnu::nonnull(1), nodiscard]] bool ffListShift(FFlist* list, uint32_t elementSize, void* __restrict result);
|
||||
// Removes the last element, and copy its value to `*result`
|
||||
bool ffListPop(FFlist* list, void* result);
|
||||
[[gnu::nonnull(1), nodiscard]] bool ffListPop(FFlist* list, uint32_t elementSize, void* __restrict result);
|
||||
|
||||
static inline void ffListInit(FFlist* list, uint32_t elementSize)
|
||||
{
|
||||
assert(elementSize > 0);
|
||||
list->elementSize = elementSize;
|
||||
[[gnu::nonnull(1)]] static inline void ffListInit(FFlist* list) {
|
||||
list->capacity = 0;
|
||||
list->length = 0;
|
||||
list->data = NULL;
|
||||
list->data = nullptr;
|
||||
}
|
||||
|
||||
static inline void ffListInitA(FFlist* list, uint32_t elementSize, uint32_t capacity)
|
||||
{
|
||||
ffListInit(list, elementSize);
|
||||
[[gnu::nonnull(1)]] 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)list->capacity * list->elementSize);
|
||||
list->data = __builtin_expect(capacity == 0, 0) ? nullptr : (uint8_t*) malloc((size_t) capacity * elementSize);
|
||||
}
|
||||
|
||||
static inline FFlist ffListCreate(uint32_t elementSize)
|
||||
{
|
||||
[[nodiscard]] static inline FFlist ffListCreate() {
|
||||
FFlist result;
|
||||
ffListInit(&result, elementSize);
|
||||
ffListInit(&result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void* ffListGet(const FFlist* list, uint32_t index)
|
||||
{
|
||||
assert(list->capacity > index);
|
||||
return list->data + (index * list->elementSize);
|
||||
[[nodiscard]] static inline FFlist ffListCreateA(uint32_t elementSize, uint32_t capacity) {
|
||||
FFlist result;
|
||||
ffListInitA(&result, elementSize, capacity);
|
||||
return result;
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline uint32_t ffListFirstIndexComp(const FFlist* list, void* compElement, bool(*compFunc)(const void*, const void*))
|
||||
{
|
||||
for(uint32_t i = 0; i < list->length; i++)
|
||||
{
|
||||
if(compFunc(ffListGet(list, i), compElement))
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline void* ffListGet(const FFlist* list, uint32_t elementSize, uint32_t index) {
|
||||
assert(list->capacity > index);
|
||||
return list->data + (index * elementSize);
|
||||
}
|
||||
|
||||
// Not `pure`: `compFunc` is caller-supplied and may have side effects
|
||||
[[gnu::nonnull(1, 3, 4), 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;
|
||||
}
|
||||
|
||||
static inline bool ffListContains(const FFlist* list, void* compElement, bool(*compFunc)(const void*, const void*))
|
||||
{
|
||||
return ffListFirstIndexComp(list, compElement, compFunc) != list->length;
|
||||
// Not `pure`, same reason as ffListFirstIndexComp
|
||||
[[gnu::nonnull(1, 3, 4), 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, int(*compar)(const void*, const void*))
|
||||
{
|
||||
qsort(list->data, list->length, list->elementSize, compar);
|
||||
[[gnu::nonnull(1, 3)]] 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->elementSize = src->elementSize;
|
||||
[[gnu::nonnull(1)]] static inline void ffListInitMove(FFlist* list, FFlist* src) {
|
||||
if (src) {
|
||||
list->capacity = src->capacity;
|
||||
list->length = src->length;
|
||||
list->data = src->data;
|
||||
ffListInit(src, list->elementSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffListInit(list, 0);
|
||||
ffListInit(src);
|
||||
} else {
|
||||
ffListInit(list);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ffListDestroy(FFlist* list)
|
||||
{
|
||||
if (!list->data) return;
|
||||
[[gnu::nonnull(1)]] static inline void ffListDestroy(FFlist* list) {
|
||||
if (!list->data) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Avoid free-after-use. These 3 assignments are cheap so don't remove them
|
||||
// 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;
|
||||
list->data = nullptr;
|
||||
}
|
||||
|
||||
static inline void ffListClear(FFlist* list)
|
||||
{
|
||||
[[gnu::nonnull(1)]] static inline void ffListClear(FFlist* list) {
|
||||
list->length = 0;
|
||||
}
|
||||
|
||||
#define FF_LIST_FOR_EACH(itemType, itemVarName, listVar) \
|
||||
assert(sizeof(itemType) == (listVar).elementSize); \
|
||||
for(itemType* itemVarName = (itemType*)(listVar).data; \
|
||||
itemVarName - (itemType*)(listVar).data < (intptr_t)(listVar).length; \
|
||||
[[gnu::nonnull(1)]] 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;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void* ffListAdd(FFlist* list, uint32_t elementSize) {
|
||||
if (__builtin_expect(list->length == list->capacity, false)) {
|
||||
ffListReserve(list, elementSize, list->capacity == 0 ? FF_LIST_DEFAULT_ALLOC : list->capacity * 2);
|
||||
}
|
||||
|
||||
++list->length;
|
||||
return ffListGet(list, elementSize, list->length - 1);
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffListRemoveAt(FFlist* list, uint32_t elementSize, uint32_t index) {
|
||||
assert(list->length > index);
|
||||
memmove(list->data + (index * elementSize), list->data + ((index + 1) * elementSize), (size_t) (list->length - index - 1) * elementSize);
|
||||
--list->length;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1, 4)]] static inline void ffListInsertAt(FFlist* list, uint32_t elementSize, uint32_t index, const void* element) {
|
||||
assert(list->length >= index);
|
||||
ffListAdd(list, elementSize);
|
||||
memmove(list->data + ((index + 1) * elementSize), list->data + (index * elementSize), (size_t) (list->length - index - 1) * elementSize);
|
||||
memcpy(list->data + (index * elementSize), element, elementSize);
|
||||
}
|
||||
|
||||
#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 __attribute__((__cleanup__(ffListDestroy)))
|
||||
#define FF_LIST_AUTO_DESTROY [[gnu::cleanup(ffListDestroy)]] FFlist
|
||||
|
||||
#define FF_LIST_GET(itemType, listVar, index) \
|
||||
({ \
|
||||
assert(sizeof(itemType) == (listVar).elementSize); \
|
||||
({ \
|
||||
assert((listVar).capacity > (index)); \
|
||||
(itemType*)(listVar).data + (index); \
|
||||
(itemType*) (listVar).data + (index); \
|
||||
})
|
||||
|
||||
#define FF_LIST_ADD(itemType, listVar) \
|
||||
({ \
|
||||
assert(sizeof(itemType) == (listVar).elementSize); \
|
||||
(itemType*) ffListAdd(&(listVar)); \
|
||||
})
|
||||
#define FF_LIST_ADD(itemType, listVar) (itemType*) ffListAdd(&(listVar), (uint32_t) sizeof(itemType))
|
||||
|
||||
#define FF_LIST_REMOVE_AT(itemType, listVar, index) \
|
||||
ffListRemoveAt(&(listVar), (uint32_t) sizeof(itemType), (index))
|
||||
|
||||
#define FF_LIST_INSERT_AT(itemType, listVar, index, pElement) \
|
||||
ffListInsertAt(&(listVar), (uint32_t) sizeof(itemType), (index), (pElement))
|
||||
|
||||
#define FF_LIST_FIRST(itemType, listVar) FF_LIST_GET(itemType, listVar, 0)
|
||||
#define FF_LIST_LAST(itemType, listVar) \
|
||||
({ \
|
||||
assert((listVar).length > 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))
|
||||
|
||||
+339
-293
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "FFcheckmacros.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
@@ -9,6 +7,8 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "common/memrchr.h"
|
||||
#include "common/arrutil.h"
|
||||
|
||||
#ifdef FF_USE_SYSTEM_YYJSON
|
||||
#include <yyjson.h>
|
||||
@@ -17,249 +17,287 @@
|
||||
#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
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Contract attributes used throughout this header:
|
||||
//
|
||||
// `gnu::pure` marks a function that reads memory (through its arguments and through globals) but
|
||||
// writes nothing observable, so the compiler may cache and reorder calls to it. It is applied to
|
||||
// the comparison / search accessors, and deliberately NOT to:
|
||||
// * the `To*` converters -- `strtod` / `strtoull` / `strtoll` write `errno`;
|
||||
// * `ffStrbufEndsWithFn` -- the caller-supplied `fn` may have side effects;
|
||||
// * `ffStrbufWriteTo` / `ffStrbufPutTo` -- they write to a `FILE*`.
|
||||
//
|
||||
// `gnu::nonnull(N)` is added wherever the function dereferences argument N unconditionally. The
|
||||
// two cannot be combined with a null check: clang reports `-Wtautological-pointer-compare` /
|
||||
// `-Wpointer-bool-conversion` (both in `-Wall`) when a `nonnull` parameter is compared against
|
||||
// null, so a redundant `assert(p != nullptr)` is dropped in favour of the attribute. Asserts that
|
||||
// state something `nonnull` cannot express -- pointer aliasing (`value != strbuf`), index ranges
|
||||
// (`start <= strbuf->length`), or a non-pointer invariant -- are kept.
|
||||
//
|
||||
// `nodiscard` is NOT applied to the "was the buffer modified" boolean returns
|
||||
// (`ffStrbufSubstr*`, `ffStrbufRemoveSubstr`, `ffStrbufEnsureEndsWithC`, ...). Ignoring that
|
||||
// result is a normal, intended use of those functions -- `ffStrbufSubstrBefore` alone has 153 call
|
||||
// sites and ~147 of them discard the result -- so `nodiscard` would only produce noise.
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
// static string (allocated == 0), chars points to a string literal
|
||||
// dynamic string (allocated > 0), chars points to a heap allocated buffer
|
||||
typedef struct FFstrbuf
|
||||
{
|
||||
typedef struct FFstrbuf {
|
||||
uint32_t allocated;
|
||||
uint32_t length;
|
||||
char* chars;
|
||||
} 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);
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufInit(FFstrbuf* strbuf);
|
||||
[[gnu::nonnull(1)]] void ffStrbufInitA(FFstrbuf* strbuf, uint32_t allocate);
|
||||
[[gnu::nonnull(1, 2), gnu::format(printf, 2, 0)]] void ffStrbufInitVF(FFstrbuf* strbuf, const char* format, va_list arguments);
|
||||
[[gnu::nonnull(1, 3)]] void ffStrbufInitMoveNS(FFstrbuf* strbuf, uint32_t length, char* heapStr);
|
||||
[[gnu::format(printf, 2, 3), gnu::nonnull(1, 2)]] void ffStrbufInitF(FFstrbuf* strbuf, const char* format, ...);
|
||||
[[gnu::format(printf, 1, 2), gnu::nonnull(1), nodiscard]] FFstrbuf ffStrbufCreateF(const char* format, ...);
|
||||
|
||||
void ffStrbufEnsureFree(FFstrbuf* strbuf, uint32_t free);
|
||||
void ffStrbufEnsureFixedLengthFree(FFstrbuf* strbuf, uint32_t free);
|
||||
[[gnu::nonnull(1)]] void ffStrbufEnsureFixedLengthFree(FFstrbuf* strbuf, uint32_t free);
|
||||
[[gnu::nonnull(1)]] void ffStrbufEnsureFreeNoCheck(FFstrbuf* strbuf, uint32_t free);
|
||||
|
||||
void ffStrbufClear(FFstrbuf* strbuf);
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value);
|
||||
[[gnu::nonnull(1, 3)]] void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int (*transformFunc)(int));
|
||||
[[gnu::format(printf, 2, 3), gnu::nonnull(1, 2)]] void ffStrbufAppendF(FFstrbuf* strbuf, const char* format, ...);
|
||||
[[gnu::nonnull(1, 2), gnu::format(printf, 2, 0)]] void ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments);
|
||||
// Returns the pointer to the terminator, or nullptr if `value` was nullptr; callers are free to
|
||||
// ignore it, so not `nodiscard`.
|
||||
[[gnu::nonnull(1)]] const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until);
|
||||
|
||||
static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value);
|
||||
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 ffStrbufAppendVF(FFstrbuf* strbuf, const char* format, va_list arguments);
|
||||
const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char until);
|
||||
[[gnu::nonnull(1)]] void ffStrbufPrependNS(FFstrbuf* strbuf, uint32_t length, const char* value);
|
||||
[[gnu::nonnull(1)]] void ffStrbufPrependC(FFstrbuf* strbuf, char c);
|
||||
|
||||
void ffStrbufPrependNS(FFstrbuf* strbuf, uint32_t length, const char* value);
|
||||
void ffStrbufPrependC(FFstrbuf* strbuf, char c);
|
||||
|
||||
void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c);
|
||||
[[gnu::nonnull(1)]] void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c);
|
||||
|
||||
// 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_C_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...);
|
||||
[[gnu::nonnull(1, 2)]] void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value);
|
||||
[[gnu::nonnull(1, 3)]] void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value);
|
||||
[[gnu::format(printf, 2, 3), gnu::nonnull(1, 2)]] void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...);
|
||||
|
||||
void ffStrbufTrimLeft(FFstrbuf* strbuf, char c);
|
||||
void ffStrbufTrimRight(FFstrbuf* strbuf, char c);
|
||||
void ffStrbufTrimLeftSpace(FFstrbuf* strbuf);
|
||||
void ffStrbufTrimRightSpace(FFstrbuf* strbuf);
|
||||
[[gnu::nonnull(1)]] void ffStrbufTrimLeft(FFstrbuf* strbuf, char c);
|
||||
[[gnu::nonnull(1)]] void ffStrbufTrimRight(FFstrbuf* strbuf, char c);
|
||||
[[gnu::nonnull(1)]] void ffStrbufTrimLeftSpace(FFstrbuf* strbuf);
|
||||
[[gnu::nonnull(1)]] void ffStrbufTrimRightSpace(FFstrbuf* strbuf);
|
||||
|
||||
bool ffStrbufRemoveSubstr(FFstrbuf* strbuf, uint32_t startIndex, uint32_t endIndex);
|
||||
void ffStrbufRemoveS(FFstrbuf* strbuf, const char* str);
|
||||
void ffStrbufRemoveStrings(FFstrbuf* strbuf, uint32_t numStrings, const char* strings[]);
|
||||
[[gnu::nonnull(1)]] bool ffStrbufRemoveSubstr(FFstrbuf* strbuf, uint32_t startIndex, uint32_t endIndex);
|
||||
[[gnu::nonnull(1, 2)]] void ffStrbufRemoveS(FFstrbuf* strbuf, const char* str);
|
||||
// `strings` is only dereferenced when `numStrings > 0`, so it is intentionally not `nonnull(3)`
|
||||
[[gnu::nonnull(1)]] 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_C_NODISCARD uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c);
|
||||
|
||||
void ffStrbufReplaceAllC(FFstrbuf* strbuf, char find, char replace);
|
||||
[[gnu::nonnull(1)]] void ffStrbufReplaceAllC(FFstrbuf* strbuf, char find, char replace);
|
||||
|
||||
// Returns true if the strbuf is modified
|
||||
bool ffStrbufSubstrBefore(FFstrbuf* strbuf, uint32_t index);
|
||||
bool ffStrbufSubstrAfter(FFstrbuf* strbuf, uint32_t index); // Not including the index
|
||||
bool ffStrbufSubstrAfterFirstC(FFstrbuf* strbuf, char c);
|
||||
bool ffStrbufSubstrAfterFirstS(FFstrbuf* strbuf, const char* str);
|
||||
bool ffStrbufSubstrAfterLastC(FFstrbuf* strbuf, char c);
|
||||
bool ffStrbufSubstr(FFstrbuf* strbuf, uint32_t start, uint32_t end);
|
||||
[[gnu::nonnull(1)]] bool ffStrbufSubstrBefore(FFstrbuf* strbuf, uint32_t index);
|
||||
[[gnu::nonnull(1)]] bool ffStrbufSubstrAfter(FFstrbuf* strbuf, uint32_t index); // Not including the index
|
||||
[[gnu::nonnull(1)]] bool ffStrbufSubstrAfterFirstC(FFstrbuf* strbuf, char c);
|
||||
[[gnu::nonnull(1, 2)]] bool ffStrbufSubstrAfterFirstS(FFstrbuf* strbuf, const char* str);
|
||||
[[gnu::nonnull(1)]] bool ffStrbufSubstrAfterLastC(FFstrbuf* strbuf, char c);
|
||||
[[gnu::nonnull(1)]] bool ffStrbufSubstr(FFstrbuf* strbuf, uint32_t start, uint32_t end);
|
||||
|
||||
FF_C_NODISCARD uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c);
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] uint32_t ffStrbufCountC(const FFstrbuf* strbuf, char c);
|
||||
|
||||
bool ffStrbufRemoveIgnCaseEndS(FFstrbuf* strbuf, const char* end);
|
||||
[[gnu::nonnull(1, 2)]] bool ffStrbufRemoveIgnCaseEndS(FFstrbuf* strbuf, const char* end);
|
||||
|
||||
bool ffStrbufEnsureEndsWithC(FFstrbuf* strbuf, char c);
|
||||
[[gnu::nonnull(1)]] bool ffStrbufEnsureEndsWithC(FFstrbuf* strbuf, char c);
|
||||
|
||||
void ffStrbufWriteTo(const FFstrbuf* strbuf, FILE* file);
|
||||
void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file);
|
||||
[[gnu::nonnull(1)]] void ffStrbufUpperCase(FFstrbuf* strbuf);
|
||||
[[gnu::nonnull(1)]] void ffStrbufLowerCase(FFstrbuf* strbuf);
|
||||
|
||||
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);
|
||||
|
||||
void ffStrbufUpperCase(FFstrbuf* strbuf);
|
||||
void ffStrbufLowerCase(FFstrbuf* strbuf);
|
||||
|
||||
bool ffStrbufGetdelim(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
|
||||
void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, 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 `nullptr` and `n` MUST be `0` for the first call
|
||||
// Caller MUST NOT free `*lineptr`
|
||||
[[gnu::nonnull(1, 2, 4)]] bool ffStrbufGetdelim(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
|
||||
[[gnu::nonnull(1, 2, 4)]] 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.
|
||||
* Can be NULL for the first call.
|
||||
* @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 nullptr for the first call.
|
||||
* @param[in,out] n The pointer to the size of the buffer of lineptr.
|
||||
* @param[in] buffer The buffer to read from. The buffer must not be a string literal.
|
||||
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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2, 3), nodiscard]] 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.
|
||||
* @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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2, 3)]] 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);
|
||||
bool ffStrbufSeparatedContainNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
|
||||
bool ffStrbufSeparatedContainIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
|
||||
[[gnu::nonnull(1)]] bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf);
|
||||
// `comp` is only dereferenced when `compLength > 0` in the first pair, and is never dereferenced
|
||||
// when `strbuf` is empty in the second pair, so neither takes `nonnull(3)`
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] bool ffStrbufMatchSeparatedNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] bool ffStrbufMatchSeparatedIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] bool ffStrbufSeparatedContainNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] bool ffStrbufSeparatedContainIgnCaseNS(const FFstrbuf* strbuf, uint32_t compLength, const char* comp, char separator);
|
||||
|
||||
int ffStrbufAppendUtf32CodePoint(FFstrbuf* strbuf, uint32_t codepoint);
|
||||
[[gnu::nonnull(1)]] int ffStrbufAppendUtf32CodePoint(FFstrbuf* strbuf, uint32_t codepoint);
|
||||
|
||||
void ffStrbufAppendSInt(FFstrbuf* strbuf, int64_t value);
|
||||
void ffStrbufAppendUInt(FFstrbuf* strbuf, uint64_t value);
|
||||
[[gnu::nonnull(1)]] void ffStrbufAppendSInt(FFstrbuf* strbuf, int64_t value);
|
||||
[[gnu::nonnull(1)]] void ffStrbufAppendUInt(FFstrbuf* strbuf, uint64_t value);
|
||||
// Appends a double value to the string buffer with the specified precision (0~15).
|
||||
// if `precision < 0`, let yyjson decide the precision
|
||||
void ffStrbufAppendDouble(FFstrbuf* strbuf, double value, int8_t precision, bool trailingZeros);
|
||||
[[gnu::nonnull(1)]] void ffStrbufAppendDouble(FFstrbuf* strbuf, double value, int8_t precision, bool trailingZeros);
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate)
|
||||
{
|
||||
[[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
|
||||
[[gnu::nonnull(1, 2)]] static inline void ffStrbufInitCopy(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict src) {
|
||||
if (src->allocated == 0) { // static string
|
||||
*strbuf = *src;
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ffStrbufInitA(strbuf, src->allocated);
|
||||
ffStrbufAppend(strbuf, src);
|
||||
}
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateCopy(const FFstrbuf* src)
|
||||
{
|
||||
[[gnu::nonnull(1), 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)
|
||||
{
|
||||
[[gnu::nonnull(1)]] 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)
|
||||
{
|
||||
[[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)
|
||||
{
|
||||
FFstrbuf strbuf;
|
||||
ffStrbufInitVF(&strbuf, format, arguments);
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
FF_C_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, ...)
|
||||
{
|
||||
FFstrbuf strbuf;
|
||||
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
ffStrbufInitVF(&strbuf, format, arguments);
|
||||
va_end(arguments);
|
||||
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitMoveS(FFstrbuf* strbuf, char* heapStr)
|
||||
{
|
||||
[[gnu::nonnull(1, 2)]] static inline void ffStrbufInitMoveS(FFstrbuf* strbuf, char* heapStr) {
|
||||
ffStrbufInitMoveNS(strbuf, (uint32_t) strlen(heapStr), heapStr);
|
||||
}
|
||||
|
||||
static inline void ffStrbufDestroy(FFstrbuf* strbuf)
|
||||
{
|
||||
if(strbuf->allocated > 0)
|
||||
// Despite the name, this function resets strbuf to the initial/unallocated state
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufDestroy(FFstrbuf* strbuf) {
|
||||
if (strbuf->allocated > 0) {
|
||||
free(strbuf->chars);
|
||||
}
|
||||
|
||||
ffStrbufInit(strbuf);
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline uint32_t ffStrbufGetFree(const FFstrbuf* strbuf)
|
||||
{
|
||||
assert(strbuf != NULL);
|
||||
if(strbuf->allocated == 0)
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline uint32_t ffStrbufGetFree(const FFstrbuf* strbuf) {
|
||||
if (strbuf->allocated == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strbuf->allocated - strbuf->length - 1; // - 1 for the null byte
|
||||
}
|
||||
|
||||
static inline void ffStrbufRecalculateLength(FFstrbuf* strbuf)
|
||||
{
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufEnsureFree(FFstrbuf* strbuf, uint32_t free) {
|
||||
if (__builtin_expect(free == 0, false)) {
|
||||
if (__builtin_expect(!(strbuf->allocated == 0 && strbuf->length > 0), true)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (__builtin_expect(ffStrbufGetFree(strbuf) >= free, true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ffStrbufEnsureFreeNoCheck(strbuf, free);
|
||||
}
|
||||
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufClear(FFstrbuf* strbuf) {
|
||||
extern char* CHAR_NULL_PTR;
|
||||
|
||||
if (strbuf->allocated == 0) {
|
||||
strbuf->chars = CHAR_NULL_PTR;
|
||||
} else {
|
||||
strbuf->chars[0] = '\0';
|
||||
}
|
||||
|
||||
strbuf->length = 0;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufAppendC(FFstrbuf* strbuf, char c) {
|
||||
ffStrbufEnsureFree(strbuf, 1);
|
||||
strbuf->chars[strbuf->length++] = c;
|
||||
strbuf->chars[strbuf->length] = '\0';
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufAppendNC(FFstrbuf* strbuf, uint32_t num, char c) {
|
||||
if (__builtin_expect(num == 0, false)) {
|
||||
return;
|
||||
}
|
||||
ffStrbufEnsureFree(strbuf, num);
|
||||
|
||||
memset(&strbuf->chars[strbuf->length], c, num);
|
||||
strbuf->length += num;
|
||||
strbuf->chars[strbuf->length] = '\0';
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value) {
|
||||
if (__builtin_expect(value == nullptr || length == 0, false)) {
|
||||
return;
|
||||
}
|
||||
ffStrbufEnsureFree(strbuf, length);
|
||||
|
||||
memcpy(&strbuf->chars[strbuf->length], value, length);
|
||||
strbuf->length += length;
|
||||
strbuf->chars[strbuf->length] = '\0';
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value) {
|
||||
assert(value != strbuf);
|
||||
if (value == nullptr) {
|
||||
return;
|
||||
}
|
||||
ffStrbufAppendNS(strbuf, value->length, value->chars);
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufRecalculateLength(FFstrbuf* strbuf) {
|
||||
strbuf->length = (uint32_t) strlen(strbuf->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
assert(strbuf != NULL);
|
||||
|
||||
if (value == NULL)
|
||||
// `value` may be null (clears the buffer); `strbuf` may not
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value) {
|
||||
if (value == nullptr) {
|
||||
ffStrbufClear(strbuf);
|
||||
else
|
||||
} 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))
|
||||
{
|
||||
[[gnu::nonnull(1)]] static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
|
||||
if (yyjson_is_str(jsonVal)) {
|
||||
ffStrbufSetNS(strbuf, (uint32_t) unsafe_yyjson_get_len(jsonVal), unsafe_yyjson_get_str(jsonVal));
|
||||
return true;
|
||||
}
|
||||
@@ -268,355 +306,363 @@ static inline bool ffStrbufSetJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void ffStrbufAppendS(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufAppendS(FFstrbuf* strbuf, const char* value) {
|
||||
if (value == nullptr) {
|
||||
return;
|
||||
}
|
||||
ffStrbufAppendNS(strbuf, (uint32_t) strlen(value), value);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufAppendJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal)
|
||||
{
|
||||
if (yyjson_is_str(jsonVal))
|
||||
{
|
||||
// Returns whether `jsonVal` was a string. Callers routinely pre-check with `yyjson_is_str`, so the
|
||||
// result is not `nodiscard`.
|
||||
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 ffStrbufInit(FFstrbuf* strbuf)
|
||||
{
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufInit(FFstrbuf* strbuf) {
|
||||
extern char* CHAR_NULL_PTR;
|
||||
strbuf->allocated = strbuf->length = 0;
|
||||
strbuf->chars = CHAR_NULL_PTR;
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreate(void)
|
||||
{
|
||||
[[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)
|
||||
{
|
||||
[[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)
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufSetStatic(FFstrbuf* strbuf, const char* value) {
|
||||
if (strbuf->allocated > 0) {
|
||||
free(strbuf->chars);
|
||||
}
|
||||
|
||||
if(value != NULL)
|
||||
if (value != nullptr) {
|
||||
ffStrbufInitStatic(strbuf, value);
|
||||
else
|
||||
} else {
|
||||
ffStrbufInit(strbuf);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitNS(FFstrbuf* strbuf, uint32_t length, const char* str)
|
||||
{
|
||||
[[gnu::nonnull(1)]] 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)
|
||||
{
|
||||
[[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)
|
||||
{
|
||||
// Returns whether `jsonVal` was a string; not `nodiscard`, same reason as ffStrbufAppendJsonVal
|
||||
static inline bool ffStrbufInitJsonVal(FFstrbuf* strbuf, yyjson_val* jsonVal) {
|
||||
ffStrbufInit(strbuf);
|
||||
return ffStrbufAppendJsonVal(strbuf, jsonVal);
|
||||
}
|
||||
|
||||
static inline void ffStrbufInitS(FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufInitS(FFstrbuf* strbuf, const char* str) {
|
||||
ffStrbufInit(strbuf);
|
||||
ffStrbufAppendS(strbuf, str);
|
||||
}
|
||||
|
||||
FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateS(const char* str)
|
||||
{
|
||||
[[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)
|
||||
{
|
||||
assert(value != strbuf);
|
||||
if(value == NULL)
|
||||
return;
|
||||
ffStrbufAppendNS(strbuf, value->length, value->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufPrepend(FFstrbuf* strbuf, FFstrbuf* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufPrepend(FFstrbuf* strbuf, FFstrbuf* value) {
|
||||
if (value == nullptr) {
|
||||
return;
|
||||
}
|
||||
ffStrbufPrependNS(strbuf, value->length, value->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufPrependS(FFstrbuf* strbuf, const char* value)
|
||||
{
|
||||
if(value == NULL)
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufPrependS(FFstrbuf* strbuf, const char* value) {
|
||||
if (value == nullptr) {
|
||||
return;
|
||||
}
|
||||
ffStrbufPrependNS(strbuf, (uint32_t) strlen(value), value);
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD int ffStrbufComp(const FFstrbuf* strbuf, const FFstrbuf* comp)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
return memchr(strbuf->chars, c, strbuf->length) != NULL;
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline bool ffStrbufContainC(const FFstrbuf* strbuf, char c) {
|
||||
return memchr(strbuf->chars, c, strbuf->length) != nullptr;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainS(const FFstrbuf* strbuf, const char* str)
|
||||
{
|
||||
return strstr(strbuf->chars, str) != NULL;
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline bool ffStrbufContainS(const FFstrbuf* strbuf, const char* str) {
|
||||
return strstr(strbuf->chars, str) != nullptr;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContain(const FFstrbuf* strbuf, const FFstrbuf* str)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
return strcasestr(strbuf->chars, str) != NULL;
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline bool ffStrbufContainIgnCaseS(const FFstrbuf* strbuf, const char* str) {
|
||||
return strcasestr(strbuf->chars, str) != nullptr;
|
||||
}
|
||||
|
||||
static inline FF_C_NODISCARD bool ffStrbufContainIgnCase(const FFstrbuf* strbuf, const FFstrbuf* str)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline uint32_t ffStrbufNextIndexC(const FFstrbuf* strbuf, uint32_t start, char c) {
|
||||
assert(start <= strbuf->length);
|
||||
|
||||
const char* ptr = (const char*) memchr(strbuf->chars + start, c, strbuf->length - start);
|
||||
return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline bool ffStrbufContainAnyC(const FFstrbuf* strbuf, const char* cs) {
|
||||
assert(cs != nullptr);
|
||||
|
||||
return strpbrk(strbuf->chars, cs) != nullptr;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1, 3), gnu::pure, nodiscard]] static inline uint32_t ffStrbufNextIndexS(const FFstrbuf* strbuf, uint32_t start, const char* str) {
|
||||
assert(start <= strbuf->length);
|
||||
|
||||
const char* ptr = strstr(strbuf->chars + start, str);
|
||||
return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline uint32_t ffStrbufPreviousIndexC(const FFstrbuf* strbuf, uint32_t start, char c) {
|
||||
assert(start <= strbuf->length);
|
||||
|
||||
const char* ptr = (const char*) memrchr(strbuf->chars, c, start + 1);
|
||||
return ptr ? (uint32_t) (ptr - strbuf->chars) : strbuf->length;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1)]] static inline bool ffStrbufSubstrBeforeFirstC(FFstrbuf* strbuf, char c) {
|
||||
return ffStrbufSubstrBefore(strbuf, ffStrbufFirstIndexC(strbuf, c));
|
||||
}
|
||||
|
||||
static inline bool ffStrbufSubstrBeforeLastC(FFstrbuf* strbuf, char c)
|
||||
{
|
||||
[[gnu::nonnull(1)]] 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)
|
||||
{
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline 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)
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
[[gnu::nonnull(1, 3), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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;
|
||||
[[gnu::nonnull(1), gnu::pure, nodiscard]] static inline 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)
|
||||
[[gnu::nonnull(1, 3), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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]);
|
||||
// Not `pure`: the caller-supplied `fn` may have side effects
|
||||
[[gnu::nonnull(1, 2), nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
[[gnu::nonnull(1, 3), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] static inline bool ffStrbufEndsWithIgnCase(const FFstrbuf* strbuf, const FFstrbuf* end) {
|
||||
return ffStrbufEndsWithIgnCaseNS(strbuf, end->length, end->chars);
|
||||
}
|
||||
|
||||
static inline void ffStrbufTrim(FFstrbuf* strbuf, char c)
|
||||
{
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufTrim(FFstrbuf* strbuf, char c) {
|
||||
ffStrbufTrimRight(strbuf, c);
|
||||
ffStrbufTrimLeft(strbuf, c);
|
||||
}
|
||||
|
||||
static inline void ffStrbufTrimSpace(FFstrbuf* strbuf)
|
||||
{
|
||||
[[gnu::nonnull(1)]] static inline void ffStrbufTrimSpace(FFstrbuf* strbuf) {
|
||||
ffStrbufTrimRightSpace(strbuf);
|
||||
ffStrbufTrimLeftSpace(strbuf);
|
||||
}
|
||||
|
||||
static inline bool ffStrbufMatchSeparatedS(const FFstrbuf* strbuf, const char* comp, char separator)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] 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)
|
||||
{
|
||||
[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] 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)))
|
||||
[[gnu::nonnull(1, 2)]] static inline void ffStrbufWriteTo(const FFstrbuf* strbuf, FILE* file) {
|
||||
fwrite(strbuf->chars, sizeof(*strbuf->chars), strbuf->length, file);
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1, 2)]] static inline void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file) {
|
||||
ffStrbufWriteTo(strbuf, file);
|
||||
fputc('\n', file);
|
||||
}
|
||||
|
||||
// Not `pure`: `strtod` reads the LC_NUMERIC locale and writes errno
|
||||
[[gnu::nonnull(1), nodiscard]] static inline double ffStrbufToDouble(const FFstrbuf* strbuf, double defaultValue) {
|
||||
char* str_end;
|
||||
double result = strtod(strbuf->chars, &str_end);
|
||||
return str_end == strbuf->chars ? defaultValue : result;
|
||||
}
|
||||
|
||||
// Not `pure`: `strtoull` reads the LC_NUMERIC locale and writes errno
|
||||
[[gnu::nonnull(1), nodiscard]] static inline uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue) {
|
||||
char* str_end;
|
||||
unsigned long long result = strtoull(strbuf->chars, &str_end, 10);
|
||||
return str_end == strbuf->chars ? defaultValue : (uint64_t) result;
|
||||
}
|
||||
|
||||
// Not `pure`: `strtoll` reads the LC_NUMERIC locale and writes errno
|
||||
[[gnu::nonnull(1), nodiscard]] static inline int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue) {
|
||||
char* str_end;
|
||||
long long result = strtoll(strbuf->chars, &str_end, 10);
|
||||
return str_end == strbuf->chars ? defaultValue : (int64_t) result;
|
||||
}
|
||||
|
||||
// Returns true if the strbuf is modified
|
||||
[[gnu::nonnull(1)]] [[gnu::nonnull(1)]] bool ffStrbufDecodeHexEscapeSequences(FFstrbuf* strbuf);
|
||||
|
||||
#define FF_STRBUF_AUTO_DESTROY [[gnu::cleanup(ffStrbufDestroy)]] FFstrbuf
|
||||
#define FF_STRBUF_STATIC(str) { .allocated = 0, .length = (uint32_t) sizeof(str) - 1, .chars = str }
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// The NDK marks an API unavailable whenever it is newer than the API level the build targets, and
|
||||
// fastfetch keeps running on devices below the levels it uses: AImageDecoder is 30, decoding past
|
||||
// the first frame is 31, and AMediaCodec_getName is 28. CMake compiles the files that reach for
|
||||
// them with -D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ -Werror=unguarded-availability, which turns
|
||||
// those entry points into weak references and makes every unguarded use an error. Two things follow:
|
||||
//
|
||||
// * A weak reference resolves to null on an older device, so every use needs a run-time check --
|
||||
// that is FF_ANDROID_API_AT_LEAST. Unguarded, the call would jump to a null pointer.
|
||||
// * Annotating a helper with FF_ANDROID_REQUIRES_API keeps the check in one place: the compiler rejects
|
||||
// any call of that helper which is not itself inside a guard.
|
||||
//
|
||||
// https://developer.android.com/ndk/guides/using-newer-apis
|
||||
#define FF_ANDROID_REQUIRES_API(x) [[clang::availability(android, introduced = x)]]
|
||||
#define FF_ANDROID_API_AT_LEAST(x) __builtin_available(android x, *)
|
||||
|
||||
// The UIDs that hold android.permission.DUMP, which is what `dumpsys` checks before it prints
|
||||
// anything: every other UID is refused on stdout and the command still exits 0, which a caller cannot
|
||||
// tell apart from a command that printed nothing useful. Forking a `dumpsys` from anywhere else is
|
||||
// therefore pure waste, and for some fields `dumpsys` is the only route there is.
|
||||
//
|
||||
// The refusal is not always the permission, and the text differs per service and per release. On
|
||||
// an Android 16 device an app UID gets `Permission Denial: can't dump DisplayManagerService ... due to
|
||||
// missing android.permission.DUMP permission` for `display`, but `Can't find service: battery` for
|
||||
// `battery` -- that service is not in the list `dumpsys -l` shows an app at all, while `display` is.
|
||||
// The UID is what decides, so one check covers both.
|
||||
//
|
||||
// android.os.Process.ROOT_UID and SHELL_UID. The shell UID has a second use: it owns exactly one
|
||||
// package name, which the system services accept, so a process that cannot derive its own package
|
||||
// can answer with that one instead.
|
||||
typedef enum FFAndroidPrivilegedUid : uint32_t {
|
||||
FF_ANDROID_PRIVILEGED_UID_ROOT = 0,
|
||||
FF_ANDROID_PRIVILEGED_UID_SHELL = 2000,
|
||||
} FFAndroidPrivilegedUid;
|
||||
|
||||
// Whether the given UID is allowed to run `dumpsys`. The UID is passed in rather than read here, so
|
||||
// that the caller can take it from `instance.state.platform.uid` -- which is what makes the
|
||||
// privileged path reachable from a test that does not run as root or as the shell.
|
||||
static inline bool ffAndroidIsRootOrShell(uint32_t uid) {
|
||||
return uid == FF_ANDROID_PRIVILEGED_UID_ROOT || uid == FF_ANDROID_PRIVILEGED_UID_SHELL;
|
||||
}
|
||||
@@ -0,0 +1,560 @@
|
||||
#include "common/android/applabel.h"
|
||||
#include "common/android/binder.h"
|
||||
#include "common/android/dex.h"
|
||||
#include "common/android/zip.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// The package manager, and the one method that describes an app to a caller that is not the shell.
|
||||
// IPackageManager has always been part of framework.jar -- it is not one of the frameworks that
|
||||
// moved into an APEX -- so there is one path and no fallback.
|
||||
#define FF_ANDROID_LABEL_SERVICE "package"
|
||||
#define FF_ANDROID_LABEL_DESCRIPTOR "android.content.pm.IPackageManager"
|
||||
#define FF_ANDROID_LABEL_JAR "/system/framework/framework.jar"
|
||||
#define FF_ANDROID_LABEL_STUB "Landroid/content/pm/IPackageManager$Stub;"
|
||||
#define FF_ANDROID_LABEL_TRANSACTION "TRANSACTION_getApplicationInfo"
|
||||
|
||||
#define FF_ANDROID_LABEL_TABLE "resources.arsc"
|
||||
|
||||
// The longest package name AIDL accepts is 255 characters, and the request is one string16 of it, the
|
||||
// interface token, a 64-bit flags argument and a 32-bit user id.
|
||||
// clang-format off
|
||||
#define FF_ANDROID_LABEL_REQUEST_SIZE \
|
||||
(12 + ((4 + 2 * (sizeof(FF_ANDROID_LABEL_DESCRIPTOR) - 1) + 3) & ~3u) \
|
||||
+ ((4 + 2 * 255 + 3) & ~3u) + 8u + 4u)
|
||||
// clang-format on
|
||||
|
||||
// An ApplicationInfo parcel runs 2.0 to 3.2 KB on the test device, and the surplus is what a reply
|
||||
// with more split names and directories than any of those would spend.
|
||||
#define FF_ANDROID_LABEL_REPLY_SIZE (16 * 1024)
|
||||
|
||||
// Long enough for the `/data/app/~~<22 base64 chars>==/<package>-<22 chars>==/base.apk` the platform
|
||||
// installs to, with room to spare.
|
||||
#define FF_ANDROID_LABEL_PATH_SIZE 512
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// The resource table
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
//
|
||||
// `resources.arsc` is a ResTable: a header, a global string pool, and then one ResTable_package per
|
||||
// package it defines, each carrying its own pools for type names and entry keys followed by the
|
||||
// types themselves. Resolving `labelRes` is two indices deep -- the package's number names the
|
||||
// package and the type, the low half names the entry -- and every step below reads the file rather
|
||||
// than a table of what the file is expected to hold, because the layout is the platform's and the
|
||||
// numbers are per APK.
|
||||
|
||||
// ResChunk_header: type(2) headerSize(2) size(4). Every chunk in the file starts with one.
|
||||
#define FF_ARSC_TABLE_TYPE 0x0002
|
||||
#define FF_ARSC_STRING_POOL_TYPE 0x0001
|
||||
#define FF_ARSC_TABLE_PACKAGE_TYPE 0x0200
|
||||
#define FF_ARSC_TABLE_TYPE_TYPE 0x0201
|
||||
|
||||
// ResStringPool: header(8) stringCount(4) styleCount(4) flags(4) stringsStart(4) stylesStart(4)
|
||||
#define FF_ARSC_POOL_HEADER_SIZE 28
|
||||
#define FF_ARSC_OFF_POOL_CHUNK_SIZE 4
|
||||
#define FF_ARSC_OFF_POOL_COUNT 8
|
||||
#define FF_ARSC_OFF_POOL_FLAGS 16
|
||||
#define FF_ARSC_OFF_POOL_STRINGS_START 20
|
||||
|
||||
// A UTF-8 pool says so with this bit, and then holds the strings as UTF-8 rather than as UTF-16.
|
||||
#define FF_ARSC_POOL_FLAG_UTF8 0x0100
|
||||
|
||||
// ResTable_package: header(8) id(4) name(256, a UTF-16 array) typeStrings(4) lastPublicType(4) ...
|
||||
#define FF_ARSC_OFF_PACKAGE_ID 8
|
||||
#define FF_ARSC_OFF_PACKAGE_TYPE_STRINGS (8 + 4 + 256)
|
||||
|
||||
// ResTable_type: header(8) id(1) flags(1) reserved(2) entryCount(4) entriesStart(4) config(...)
|
||||
#define FF_ARSC_OFF_TYPE_ID 8
|
||||
#define FF_ARSC_OFF_TYPE_FLAGS 9
|
||||
#define FF_ARSC_OFF_TYPE_ENTRY_COUNT 12
|
||||
#define FF_ARSC_OFF_TYPE_ENTRIES_START 16
|
||||
#define FF_ARSC_OFF_TYPE_CONFIG 20
|
||||
|
||||
// The two shapes a type can take that this does not read: a sparse type stores its offsets as
|
||||
// index-offset pairs rather than as a flat table, and a 16-bit one stores them at half width. Neither
|
||||
// is in the APKs measured, and both would resolve an entry to the wrong offset rather than to none,
|
||||
// so they are refused rather than read as if they were the plain shape.
|
||||
#define FF_ARSC_TYPE_FLAG_SPARSE 0x01
|
||||
#define FF_ARSC_TYPE_FLAG_OFFSET16 0x02
|
||||
|
||||
// ResTable_entry: size(2) flags(2), where size is the header's own length and the value follows it.
|
||||
#define FF_ARSC_ENTRY_FLAG_COMPLEX 0x0001
|
||||
|
||||
// Res_value: size(2) res0(1) dataType(1) data(4). TYPE_STRING makes data an index into the global
|
||||
// string pool; every other type is either a number or a reference to another resource.
|
||||
#define FF_ARSC_OFF_VALUE_DATA_TYPE 3
|
||||
#define FF_ARSC_OFF_VALUE_DATA 4
|
||||
#define FF_ARSC_TYPE_STRING 0x03
|
||||
|
||||
// A type chunk's offset table has one word per entry, and this is what an entry with no value in that
|
||||
// configuration looks like. It is not zero: offset zero is a real entry, at the start of the block.
|
||||
#define FF_ARSC_NO_ENTRY 0xffffffffu
|
||||
|
||||
// The type a label resolves to. `string` is what `@string/...` compiles to, and it is what tells a
|
||||
// label from the icons and banners that sit beside it in the same reply -- those point at `mipmap`
|
||||
// or `drawable` entries whose value is a path inside the APK, so resolving them would hand back
|
||||
// `res/9w.png` instead of a name.
|
||||
#define FF_ANDROID_LABEL_STRING_TYPE "string"
|
||||
|
||||
static uint16_t readU16(const uint8_t* p) {
|
||||
return (uint16_t) ((uint16_t) p[0] | (uint16_t) ((uint16_t) p[1] << 8));
|
||||
}
|
||||
|
||||
static uint32_t readU32(const uint8_t* p) {
|
||||
return (uint32_t) p[0] | ((uint32_t) p[1] << 8) | ((uint32_t) p[2] << 16) | ((uint32_t) p[3] << 24);
|
||||
}
|
||||
|
||||
// One ResStringPool, of which a table has three.
|
||||
typedef struct FFAndroidLabelPool {
|
||||
const uint8_t* table;
|
||||
size_t tableSize;
|
||||
size_t offset;
|
||||
uint32_t chunkSize;
|
||||
uint32_t count;
|
||||
uint32_t headerSize;
|
||||
uint32_t stringsStart;
|
||||
bool utf8;
|
||||
} FFAndroidLabelPool;
|
||||
|
||||
[[gnu::nonnull(2), nodiscard]] static bool poolInit(FFAndroidLabelPool* pool, const uint8_t* table, size_t tableSize, size_t offset) {
|
||||
if (offset > tableSize || tableSize - offset < FF_ARSC_POOL_HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
if (readU16(table + offset) != FF_ARSC_STRING_POOL_TYPE) {
|
||||
return false;
|
||||
}
|
||||
pool->table = table;
|
||||
pool->tableSize = tableSize;
|
||||
pool->offset = offset;
|
||||
pool->chunkSize = readU32(table + offset + FF_ARSC_OFF_POOL_CHUNK_SIZE);
|
||||
pool->count = readU32(table + offset + FF_ARSC_OFF_POOL_COUNT);
|
||||
pool->headerSize = readU16(table + offset + 2);
|
||||
pool->stringsStart = readU32(table + offset + FF_ARSC_OFF_POOL_STRINGS_START);
|
||||
pool->utf8 = (readU32(table + offset + FF_ARSC_OFF_POOL_FLAGS) & FF_ARSC_POOL_FLAG_UTF8) != 0;
|
||||
return pool->headerSize >= FF_ARSC_POOL_HEADER_SIZE && pool->chunkSize > pool->headerSize;
|
||||
}
|
||||
|
||||
// The length in front of a string. A UTF-8 pool writes one or two bytes, a UTF-16 pool one or two
|
||||
// 16-bit words, and the top bit of the first says which of the two it is.
|
||||
[[gnu::nonnull(1, 3), nodiscard]] static bool readLength8(const FFAndroidLabelPool* pool, size_t* position, uint32_t* length) {
|
||||
if (*position >= pool->tableSize) {
|
||||
return false;
|
||||
}
|
||||
uint32_t value = pool->table[(*position)++];
|
||||
if (value & 0x80) {
|
||||
if (*position >= pool->tableSize) {
|
||||
return false;
|
||||
}
|
||||
value = ((value & 0x7F) << 8) | pool->table[(*position)++];
|
||||
}
|
||||
*length = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1, 3), nodiscard]] static bool readLength16(const FFAndroidLabelPool* pool, size_t* position, uint32_t* length) {
|
||||
if (*position + 2 > pool->tableSize) {
|
||||
return false;
|
||||
}
|
||||
uint32_t value = readU16(pool->table + *position);
|
||||
*position += 2;
|
||||
if (value & 0x8000) {
|
||||
if (*position + 2 > pool->tableSize) {
|
||||
return false;
|
||||
}
|
||||
value = ((value & 0x7FFF) << 16) | readU16(pool->table + *position);
|
||||
*position += 2;
|
||||
}
|
||||
*length = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static void appendCodePoint(FFstrbuf* result, uint32_t code) {
|
||||
char encoded[4] = {};
|
||||
uint32_t length = 0;
|
||||
if (code < 0x80) {
|
||||
encoded[length++] = (char) code;
|
||||
} else if (code < 0x800) {
|
||||
encoded[length++] = (char) (0xC0 | (code >> 6));
|
||||
encoded[length++] = (char) (0x80 | (code & 0x3F));
|
||||
} else if (code < 0x10000) {
|
||||
encoded[length++] = (char) (0xE0 | (code >> 12));
|
||||
encoded[length++] = (char) (0x80 | ((code >> 6) & 0x3F));
|
||||
encoded[length++] = (char) (0x80 | (code & 0x3F));
|
||||
} else {
|
||||
encoded[length++] = (char) (0xF0 | (code >> 18));
|
||||
encoded[length++] = (char) (0x80 | ((code >> 12) & 0x3F));
|
||||
encoded[length++] = (char) (0x80 | ((code >> 6) & 0x3F));
|
||||
encoded[length++] = (char) (0x80 | (code & 0x3F));
|
||||
}
|
||||
ffStrbufAppendNS(result, length, encoded);
|
||||
}
|
||||
|
||||
// One string out of a pool.
|
||||
//
|
||||
// The two lengths in front of a UTF-8 string are a *character* count and a *byte* count, and they
|
||||
// differ for everything outside ASCII. Decoding `characters` bytes is the trap: a label of a few
|
||||
// multi-byte characters comes back as its first character followed by replacement bytes. Measured on
|
||||
// the test device, where every resource table read is a UTF-8 pool, this was the difference between
|
||||
// the right label and mojibake.
|
||||
[[gnu::nonnull(1, 3), nodiscard]] static bool poolGet(const FFAndroidLabelPool* pool, uint32_t index, FFstrbuf* result) {
|
||||
if (index >= pool->count) {
|
||||
return false;
|
||||
}
|
||||
const size_t offsets = pool->offset + pool->headerSize;
|
||||
if (offsets + ((size_t) index + 1) * 4 > pool->tableSize) {
|
||||
return false;
|
||||
}
|
||||
size_t position = pool->offset + pool->stringsStart + readU32(pool->table + offsets + (size_t) index * 4);
|
||||
if (position >= pool->tableSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pool->utf8) {
|
||||
uint32_t characters = 0;
|
||||
uint32_t bytes = 0;
|
||||
if (!readLength8(pool, &position, &characters) || !readLength8(pool, &position, &bytes)) {
|
||||
return false;
|
||||
}
|
||||
// An empty string is reported as a miss rather than written: the caller sets its fallback
|
||||
// before asking, and ffStrbufSetNS() with a length of zero *clears* the buffer, which would
|
||||
// throw that fallback away instead of leaving it alone.
|
||||
if (bytes == 0 || bytes > pool->tableSize - position) {
|
||||
return false;
|
||||
}
|
||||
ffStrbufSetNS(result, bytes, (const char*) (pool->table + position));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t units = 0;
|
||||
if (!readLength16(pool, &position, &units)) {
|
||||
return false;
|
||||
}
|
||||
if (units == 0 || (size_t) units * 2 > pool->tableSize - position) {
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < units; i++) {
|
||||
uint32_t code = readU16(pool->table + position + (size_t) i * 2);
|
||||
// A code point outside the basic plane arrives as a surrogate pair, and the two halves are
|
||||
// not characters of their own: half of a pair encoded on its own is not valid UTF-8.
|
||||
if (code >= 0xD800 && code <= 0xDBFF && i + 1 < units) {
|
||||
const uint32_t low = readU16(pool->table + position + ((size_t) i + 1) * 2);
|
||||
if (low >= 0xDC00 && low <= 0xDFFF) {
|
||||
code = 0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
appendCodePoint(result, code);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// The one ResTable_package this table defines, with the two pools a lookup starts from.
|
||||
typedef struct FFAndroidLabelTable {
|
||||
const uint8_t* data;
|
||||
size_t size;
|
||||
FFAndroidLabelPool values;
|
||||
FFAndroidLabelPool types;
|
||||
size_t packageOffset;
|
||||
size_t packageHeaderSize;
|
||||
size_t packageSize;
|
||||
uint32_t packageId;
|
||||
} FFAndroidLabelTable;
|
||||
|
||||
[[gnu::nonnull(1), nodiscard]] static bool tableInit(FFAndroidLabelTable* table, const uint8_t* data, size_t size) {
|
||||
// ResTable_header: header(8) packageCount(4). The header's own size is where the global pool
|
||||
// starts, which is 12 in every table measured -- but it is the field rather than the number.
|
||||
if (size < 12 || readU16(data) != FF_ARSC_TABLE_TYPE) {
|
||||
return false;
|
||||
}
|
||||
table->data = data;
|
||||
table->size = size;
|
||||
const uint32_t headerSize = readU16(data + 2);
|
||||
if (!poolInit(&table->values, data, size, headerSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t position = (size_t) headerSize + table->values.chunkSize;
|
||||
while (position + 8 <= size) {
|
||||
const uint16_t type = readU16(data + position);
|
||||
const uint16_t chunkHeader = readU16(data + position + 2);
|
||||
const uint32_t chunkSize = readU32(data + position + 4);
|
||||
if (chunkSize < 8 || chunkSize > size - position) {
|
||||
break;
|
||||
}
|
||||
// A table can define several packages -- an app that links a shared library's resources has
|
||||
// more than one -- and the id in the resource reference is what picks the one to read.
|
||||
if (type == FF_ARSC_TABLE_PACKAGE_TYPE && chunkSize >= chunkHeader && chunkHeader >= FF_ARSC_OFF_PACKAGE_TYPE_STRINGS + 4) {
|
||||
table->packageOffset = position;
|
||||
table->packageHeaderSize = chunkHeader;
|
||||
table->packageSize = chunkSize;
|
||||
table->packageId = readU32(data + position + FF_ARSC_OFF_PACKAGE_ID);
|
||||
if (!poolInit(&table->types, data, size, position + readU32(data + position + FF_ARSC_OFF_PACKAGE_TYPE_STRINGS))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
position += chunkSize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// A type chunk whose ResTable_config is all zeroes is the one that applies when nothing about the
|
||||
// device or the locale matches a more specific chunk. An app with a translation has several chunks
|
||||
// for one type, and picking the first one that happens along would answer with whatever language it
|
||||
// was written in.
|
||||
[[gnu::nonnull(1), nodiscard]] static bool isDefaultConfig(const uint8_t* config, size_t length) {
|
||||
if (length == 0) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (config[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1, 5), nodiscard]] static bool resolveIn(const FFAndroidLabelTable* table, uint32_t typeId, uint32_t entryId, bool defaultOnly, FFstrbuf* result) {
|
||||
const size_t end = table->packageOffset + table->packageSize;
|
||||
for (size_t position = table->packageOffset + table->packageHeaderSize; position + 8 <= end;) {
|
||||
const uint16_t type = readU16(table->data + position);
|
||||
const uint16_t header = readU16(table->data + position + 2);
|
||||
const uint32_t size = readU32(table->data + position + 4);
|
||||
if (size < 8 || size > end - position) {
|
||||
return false;
|
||||
}
|
||||
if (type == FF_ARSC_TABLE_TYPE_TYPE && header >= FF_ARSC_OFF_TYPE_CONFIG) {
|
||||
const uint8_t id = table->data[position + FF_ARSC_OFF_TYPE_ID];
|
||||
const uint8_t flags = table->data[position + FF_ARSC_OFF_TYPE_FLAGS];
|
||||
if (id == typeId && (flags & (FF_ARSC_TYPE_FLAG_SPARSE | FF_ARSC_TYPE_FLAG_OFFSET16)) == 0 && size >= header) {
|
||||
const uint32_t entryCount = readU32(table->data + position + FF_ARSC_OFF_TYPE_ENTRY_COUNT);
|
||||
const uint32_t entriesStart = readU32(table->data + position + FF_ARSC_OFF_TYPE_ENTRIES_START);
|
||||
const size_t offsets = position + header;
|
||||
if (entryId < entryCount && offsets + ((size_t) entryId + 1) * 4 <= end) {
|
||||
if (defaultOnly && !isDefaultConfig(table->data + position + FF_ARSC_OFF_TYPE_CONFIG, (size_t) header - FF_ARSC_OFF_TYPE_CONFIG)) {
|
||||
position += size;
|
||||
continue;
|
||||
}
|
||||
const uint32_t offset = readU32(table->data + offsets + (size_t) entryId * 4);
|
||||
// `entriesStart` is relative to the chunk, not to the package, and both it and
|
||||
// the entry's own offset come out of the file.
|
||||
if (offset != FF_ARSC_NO_ENTRY && entriesStart <= size && offset <= size - entriesStart) {
|
||||
const size_t entry = position + entriesStart + offset;
|
||||
const size_t value = entry + readU16(table->data + entry);
|
||||
if (value >= entry && value + 8 <= end && (readU16(table->data + entry + 2) & FF_ARSC_ENTRY_FLAG_COMPLEX) == 0
|
||||
&& table->data[value + FF_ARSC_OFF_VALUE_DATA_TYPE] == FF_ARSC_TYPE_STRING) {
|
||||
return poolGet(&table->values, readU32(table->data + value + FF_ARSC_OFF_VALUE_DATA), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
position += size;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Resolves one resource id to its string, and only when the type it names is a `string`.
|
||||
//
|
||||
// The type name is the whole reason this reads the package's type pool rather than trusting the id:
|
||||
// `labelRes` and the icon, logo and banner ids that sit beside it in the same reply are all resource
|
||||
// ids of the same package, and the difference between them is the type they point at. A `mipmap` id
|
||||
// resolves perfectly well -- to `res/9w.png`.
|
||||
[[gnu::nonnull(1, 3), nodiscard]] static bool tableResolve(const FFAndroidLabelTable* table, uint32_t resourceId, FFstrbuf* result) {
|
||||
const uint32_t typeId = (resourceId >> 16) & 0xFF;
|
||||
if (((resourceId >> 24) & 0xFF) != table->packageId || typeId == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FFstrbuf typeName;
|
||||
ffStrbufInit(&typeName);
|
||||
const bool isString = poolGet(&table->types, typeId - 1, &typeName) && ffStrbufEqualS(&typeName, FF_ANDROID_LABEL_STRING_TYPE);
|
||||
ffStrbufDestroy(&typeName);
|
||||
if (!isString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The configuration that applies is the unqualified one; a chunk that only applies to a locale
|
||||
// or a density is the fallback rather than the answer, which is what the second pass is for.
|
||||
return resolveIn(table, typeId, resourceId & 0xFFFF, true, result)
|
||||
|| resolveIn(table, typeId, resourceId & 0xFFFF, false, result);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// The reply
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
// The reply's own copy of the APK path. `sourceDir` is a string in the ApplicationInfo parcel and it
|
||||
// ends in `.apk`; nothing else in the reply does, which is what makes it findable without walking
|
||||
// the parcel's field order -- that would mean recovering `ApplicationInfo.writeToParcel` out of the
|
||||
// device's dex and then framing sixty fields of unknown width, to reach one string that is already
|
||||
// recognizable.
|
||||
[[gnu::nonnull(1, 3), nodiscard]] static bool findApkPath(const uint8_t* data, size_t size, char* result, size_t capacity) {
|
||||
for (size_t i = 8; i < size; i++) {
|
||||
if (data[i] != '/') {
|
||||
continue;
|
||||
}
|
||||
size_t end = i;
|
||||
while (end < size && data[end] >= 0x20 && data[end] < 0x7F) {
|
||||
end++;
|
||||
}
|
||||
const size_t length = end - i;
|
||||
if (length >= 5 && length < capacity && memcmp(data + end - 4, ".apk", 4) == 0) {
|
||||
memcpy(result, data + i, length);
|
||||
result[length] = '\0';
|
||||
return true;
|
||||
}
|
||||
i = end;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
typedef struct FFAndroidLabelMapping {
|
||||
const uint8_t* data;
|
||||
size_t size;
|
||||
} FFAndroidLabelMapping;
|
||||
|
||||
[[gnu::nonnull(1)]] static void labelUnmap(FFAndroidLabelMapping* mapping) {
|
||||
if (mapping->data != nullptr) {
|
||||
munmap((void*) mapping->data, mapping->size);
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::nonnull(2), nodiscard]] static const char* labelMap(const char* path, FFAndroidLabelMapping* mapping) {
|
||||
const int fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
FF_DEBUG("open(%s) failed: %s", path, strerror(errno));
|
||||
return "open(apk) failed";
|
||||
}
|
||||
|
||||
struct stat status = {};
|
||||
const bool failed = fstat(fd, &status) != 0;
|
||||
if (failed || status.st_size <= 0) {
|
||||
FF_DEBUG("fstat(%s) reported %s", path, failed ? strerror(errno) : "an empty file");
|
||||
close(fd);
|
||||
return "fstat(apk) failed";
|
||||
}
|
||||
|
||||
void* mapped = mmap(nullptr, (size_t) status.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
if (mapped == MAP_FAILED) {
|
||||
FF_DEBUG("mmap(%s, %lld bytes) failed: %s", path, (long long) status.st_size, strerror(errno));
|
||||
return "mmap(apk) failed";
|
||||
}
|
||||
mapping->data = mapped;
|
||||
mapping->size = (size_t) status.st_size;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// One reply buffer for every call this file makes, and it makes one per query.
|
||||
static uint8_t s_reply[FF_ANDROID_LABEL_REPLY_SIZE];
|
||||
|
||||
const char* ffAndroidGetAppLabel(const char* packageName, FFstrbuf* result) {
|
||||
// The transaction code comes out of the jar rather than from a table here: it is a property of
|
||||
// the `.aidl` the device's ROM was built from, and a vendor that inserts a method ahead of this
|
||||
// one shifts every code behind it. See common/android/dex.h.
|
||||
int32_t transaction = FF_DEX_STATIC_INT_UNRESOLVED;
|
||||
const FFDexStaticIntRequest lookup = {
|
||||
.classDescriptor = FF_ANDROID_LABEL_STUB,
|
||||
.fieldName = FF_ANDROID_LABEL_TRANSACTION,
|
||||
.result = &transaction,
|
||||
};
|
||||
const char* error = ffDexStaticInts(FF_ANDROID_LABEL_JAR, &lookup, 1);
|
||||
if (error != nullptr) {
|
||||
return error;
|
||||
}
|
||||
if (transaction == FF_DEX_STATIC_INT_UNRESOLVED) {
|
||||
return "framework.jar does not declare IPackageManager.getApplicationInfo";
|
||||
}
|
||||
|
||||
[[gnu::cleanup(ffBinderClose)]] FFBinder binder = { .fd = -1 };
|
||||
error = ffBinderOpen(&binder);
|
||||
if (error != nullptr) {
|
||||
return error;
|
||||
}
|
||||
|
||||
[[gnu::cleanup(ffBinderServiceHandleRelease)]] FFBinderServiceHandle service = { .binder = &binder };
|
||||
error = ffBinderLookupService(&binder, FF_ANDROID_LABEL_SERVICE, FF_BINDER_SM_GET_SERVICE, &service.handle);
|
||||
if (error != nullptr) {
|
||||
FF_DEBUG("Looking up \"%s\" failed: %s", FF_ANDROID_LABEL_SERVICE, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
uint8_t request[FF_ANDROID_LABEL_REQUEST_SIZE];
|
||||
FFBinderParcel parcel = ffBinderParcelCreate(request, sizeof(request));
|
||||
ffBinderParcelPutInterfaceToken(&parcel, FF_ANDROID_LABEL_DESCRIPTOR);
|
||||
ffBinderParcelPutString16(&parcel, packageName);
|
||||
// `getApplicationInfo(String, long flags, int userId)`. The flags argument is 64-bit from Android
|
||||
// 13 on and 32-bit before it, and the wider one is what works on both: a release that reads an
|
||||
// int takes the low word and leaves the zero high word unread, because an AIDL stub stops
|
||||
// reading once its own arguments are in. The narrow one is not symmetric -- a release that reads
|
||||
// a long would take half of its own flags argument for the user id.
|
||||
ffBinderParcelPutU64(&parcel, 0);
|
||||
ffBinderParcelPutI32(&parcel, 0);
|
||||
|
||||
FFBinderReply reply = ffBinderReplyCreate(s_reply, sizeof(s_reply));
|
||||
error = ffBinderTransact(&binder, service.handle, (uint32_t) transaction, 0, &parcel, &reply);
|
||||
if (error != nullptr) {
|
||||
return error;
|
||||
}
|
||||
if (ffBinderReplyIsStatus(&reply)) {
|
||||
return "The package manager rejected the request";
|
||||
}
|
||||
// The AIDL status first, then the presence marker of a nullable Parcelable: a package the caller
|
||||
// is not allowed to see answers an empty ApplicationInfo rather than an error.
|
||||
if (ffBinderReadI32(reply.data, reply.size, 0) != 0) {
|
||||
return "The package manager refused to describe the app";
|
||||
}
|
||||
if (ffBinderReadI32(reply.data, reply.size, 4) == 0) {
|
||||
FF_DEBUG("The package manager described \"%s\" as an empty ApplicationInfo", packageName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char path[FF_ANDROID_LABEL_PATH_SIZE];
|
||||
if (!findApkPath(reply.data, reply.size, path, sizeof(path))) {
|
||||
return "The reply carries no APK path";
|
||||
}
|
||||
FF_DEBUG("\"%s\" is installed at \"%s\"", packageName, path);
|
||||
|
||||
[[gnu::cleanup(labelUnmap)]] FFAndroidLabelMapping mapping = {};
|
||||
error = labelMap(path, &mapping);
|
||||
if (error != nullptr) {
|
||||
return error;
|
||||
}
|
||||
|
||||
FFAndroidZipEntry table = {};
|
||||
error = ffAndroidZipFindEntry(mapping.data, mapping.size, FF_ANDROID_LABEL_TABLE, &table);
|
||||
if (error != nullptr) {
|
||||
return error;
|
||||
}
|
||||
if (table.method != FF_ZIP_METHOD_STORED) {
|
||||
FF_DEBUG("\"%s\" stores resources.arsc with compression method %u", packageName, (unsigned) table.method);
|
||||
return "The resource table is stored compressed";
|
||||
}
|
||||
|
||||
FFAndroidLabelTable resources = {};
|
||||
if (!tableInit(&resources, table.payload, table.payloadSize)) {
|
||||
return "The resource table could not be read";
|
||||
}
|
||||
|
||||
// Every word of the reply shaped like one of this package's resource ids, in the order they
|
||||
// appear, and the first one that resolves to a `string` is the label. See the header for what
|
||||
// that rule was audited against and why it beats recovering the parcel's field order.
|
||||
for (size_t offset = 8; offset + 4 <= reply.size; offset += 4) {
|
||||
if (tableResolve(&resources, readU32(reply.data + offset), result)) {
|
||||
FF_DEBUG("\"%s\" shows itself as \"%s\"", packageName, result->chars);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
FF_DEBUG("No resource id in the reply of \"%s\" resolves to a string", packageName);
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
// The name an app presents to the user, which is not its package name: an app installed as
|
||||
// `com.termux` presents itself as `Termux:Launcher` on the launcher, in its notifications and on
|
||||
// the settings screen.
|
||||
//
|
||||
// Every route that answers with an *already resolved* label is closed to an app UID, and each one
|
||||
// was measured on the test device rather than assumed:
|
||||
//
|
||||
// * `dumpsys package` needs android.permission.DUMP.
|
||||
// * `cmd package dump` is filtered down to 929 bytes for an app UID, against 1.2 MB for the shell.
|
||||
// * The task descriptions the activity service hands out only ever describe the caller's own task.
|
||||
// * `getApplicationInfo` answers with `labelRes` -- a resource id -- and leaves `nonLocalizedLabel`
|
||||
// null, so there is no string in the reply to read.
|
||||
//
|
||||
// What is open is the APK itself. `getApplicationInfo` carries its path (`sourceDir`), APKs are
|
||||
// world readable, and the `resources.arsc` inside one is *stored* rather than deflated -- the
|
||||
// platform expects to mmap it -- so resolving an id out of it needs no decompressor. That is the
|
||||
// route this takes: one transaction, one mapping, no fork and no child process.
|
||||
//
|
||||
// The label is the resource id in the reply whose type is `string`. That is a rule about the reply
|
||||
// rather than about the parcel's field order, and it was audited against the manifest's own
|
||||
// `android:label` over every package installed on the test device: nine in ten have exactly one
|
||||
// `string` candidate and it is the right one every time, a handful more have two and the first is
|
||||
// still the right one, and the rest have none because their label is a literal string rather than a
|
||||
// reference (`android:label="Example"`), so the reply carries no id at all. No package resolved to
|
||||
// a wrong string; the counts behind that are in doc/app-label.md.
|
||||
//
|
||||
// Writes the label into `result` when it can be resolved, and leaves `result` alone otherwise --
|
||||
// which includes the cases above where there is nothing to resolve. The caller sets its fallback
|
||||
// first, so that an unresolved label costs nothing but the query.
|
||||
//
|
||||
// Returns nullptr on success, a static message when the query itself could not be made. An error is
|
||||
// not a fallback: it says the label was not looked for, not that the app has none.
|
||||
[[gnu::nonnull(1, 2), nodiscard]] const char* ffAndroidGetAppLabel(const char* packageName, FFstrbuf* result);
|
||||
@@ -0,0 +1,334 @@
|
||||
#include "common/android/binder.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/io.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
const char* ffBinderOpen(FFBinder* binder) {
|
||||
*binder = (FFBinder) { .fd = -1, .shared = nullptr, .sharedSize = 0, .protocolVersion = 0 };
|
||||
|
||||
FF_AUTO_CLOSE_FD int fd = open(FF_BINDER_DEVICE, O_RDWR | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
FF_DEBUG("open(" FF_BINDER_DEVICE ") failed: %s", strerror(errno));
|
||||
return "Failed to open " FF_BINDER_DEVICE;
|
||||
}
|
||||
|
||||
struct binder_version version = { .protocol_version = 0 };
|
||||
const int versionIoctl = ioctl(fd, BINDER_VERSION, &version);
|
||||
if (versionIoctl != 0 || version.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION) {
|
||||
FF_DEBUG("BINDER_VERSION reported protocol %u, expected %u (%s)",
|
||||
version.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION,
|
||||
versionIoctl != 0 ? strerror(errno) : "version mismatch");
|
||||
return "Unsupported binder protocol version";
|
||||
}
|
||||
|
||||
// The region is read-only: the kernel writes reply payloads into it and hands out their
|
||||
// addresses in BR_REPLY, so it has to stay mapped for as long as the handle is used.
|
||||
void* shared = mmap(nullptr, FF_BINDER_SHARED_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0);
|
||||
if (shared == MAP_FAILED) {
|
||||
FF_DEBUG("mmap(" FF_BINDER_DEVICE ") failed: %s", strerror(errno));
|
||||
return "Failed to mmap " FF_BINDER_DEVICE;
|
||||
}
|
||||
|
||||
*binder = (FFBinder) {
|
||||
.fd = fd,
|
||||
.shared = (uint8_t*) shared,
|
||||
.sharedSize = FF_BINDER_SHARED_SIZE,
|
||||
.protocolVersion = version.protocol_version,
|
||||
};
|
||||
fd = -1; // ownership moved into *binder, which ffBinderClose() lets go
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ffBinderClose(FFBinder* binder) {
|
||||
if (binder->shared != nullptr) {
|
||||
munmap(binder->shared, binder->sharedSize);
|
||||
}
|
||||
if (binder->fd >= 0) {
|
||||
close(binder->fd);
|
||||
}
|
||||
binder->fd = -1;
|
||||
binder->shared = nullptr;
|
||||
binder->sharedSize = 0;
|
||||
}
|
||||
|
||||
const char* ffBinderTransact(FFBinder* binder, uint32_t handle, uint32_t code, uint32_t flags, const FFBinderParcel* parcel, FFBinderReply* reply) {
|
||||
reply->size = 0;
|
||||
reply->code = 0;
|
||||
reply->flags = 0;
|
||||
reply->handleCount = 0;
|
||||
reply->fdCount = 0;
|
||||
|
||||
if (parcel->truncated) {
|
||||
FF_DEBUG("The parcel was truncated at %zu bytes, the caller buffer holds %zu",
|
||||
parcel->size, parcel->capacity);
|
||||
return "Binder parcel does not fit the caller buffer";
|
||||
}
|
||||
|
||||
uint8_t writeBuffer[sizeof(uint32_t) + sizeof(struct binder_transaction_data)];
|
||||
const struct binder_transaction_data transaction = {
|
||||
.target = { .handle = handle },
|
||||
.cookie = 0,
|
||||
.code = code,
|
||||
.flags = flags,
|
||||
.sender_pid = 0,
|
||||
.sender_euid = 0,
|
||||
.data_size = parcel->size,
|
||||
.offsets_size = 0,
|
||||
.data = { .ptr = { .buffer = (binder_uintptr_t) (uintptr_t) parcel->data, .offsets = 0 } },
|
||||
};
|
||||
const uint32_t transactionCommand = BC_TRANSACTION;
|
||||
memcpy(writeBuffer, &transactionCommand, sizeof(uint32_t));
|
||||
memcpy(writeBuffer + sizeof(uint32_t), &transaction, sizeof(transaction));
|
||||
|
||||
// The read buffer only has to hold the command words and the binder_transaction_data that
|
||||
// follows BR_REPLY; the payload itself lives in the mmap region.
|
||||
uint8_t readBuffer[FF_BINDER_REPLY_BUFFER_SIZE];
|
||||
struct binder_write_read exchange = {
|
||||
.write_size = sizeof(writeBuffer),
|
||||
.write_consumed = 0,
|
||||
.write_buffer = (binder_uintptr_t) (uintptr_t) writeBuffer,
|
||||
.read_size = sizeof(readBuffer),
|
||||
.read_consumed = 0,
|
||||
.read_buffer = (binder_uintptr_t) (uintptr_t) readBuffer,
|
||||
};
|
||||
if (ioctl(binder->fd, BINDER_WRITE_READ, &exchange) != 0) {
|
||||
FF_DEBUG("BINDER_WRITE_READ(handle=%u, code=%u) failed: %s", handle, code, strerror(errno));
|
||||
return "BINDER_WRITE_READ failed";
|
||||
}
|
||||
|
||||
const uint8_t* cursor = readBuffer;
|
||||
const uint8_t* end = readBuffer + (size_t) exchange.read_consumed;
|
||||
while ((size_t) (end - cursor) >= sizeof(uint32_t)) {
|
||||
uint32_t command = 0;
|
||||
memcpy(&command, cursor, sizeof(uint32_t));
|
||||
cursor += sizeof(uint32_t);
|
||||
|
||||
switch (command) {
|
||||
case BR_NOOP:
|
||||
case BR_TRANSACTION_COMPLETE:
|
||||
case BR_SPAWN_LOOPER:
|
||||
break;
|
||||
|
||||
case BR_FAILED_REPLY:
|
||||
FF_DEBUG("BR_FAILED_REPLY for handle=%u, code=%u", handle, code);
|
||||
return "Binder transaction failed";
|
||||
case BR_DEAD_REPLY:
|
||||
FF_DEBUG("BR_DEAD_REPLY: handle=%u is dead", handle);
|
||||
return "Binder service is not running";
|
||||
|
||||
case BR_INCREFS:
|
||||
case BR_ACQUIRE:
|
||||
case BR_RELEASE:
|
||||
case BR_DECREFS:
|
||||
cursor += sizeof(struct binder_ptr_cookie);
|
||||
break;
|
||||
case BR_DEAD_BINDER:
|
||||
case BR_CLEAR_DEATH_NOTIFICATION_DONE:
|
||||
cursor += sizeof(binder_uintptr_t);
|
||||
break;
|
||||
case BR_ERROR:
|
||||
cursor += sizeof(int32_t);
|
||||
break;
|
||||
|
||||
case BR_REPLY: {
|
||||
if ((size_t) (end - cursor) < sizeof(struct binder_transaction_data)) {
|
||||
FF_DEBUG("BR_REPLY is truncated: %zu of %zu bytes left",
|
||||
(size_t) (end - cursor), sizeof(struct binder_transaction_data));
|
||||
return "Truncated binder reply";
|
||||
}
|
||||
struct binder_transaction_data data;
|
||||
memcpy(&data, cursor, sizeof(data));
|
||||
cursor += sizeof(data);
|
||||
|
||||
reply->code = data.code;
|
||||
reply->flags = data.flags;
|
||||
|
||||
const uint8_t* payload = (const uint8_t*) (uintptr_t) data.data.ptr.buffer;
|
||||
const size_t payloadSize = (size_t) data.data_size;
|
||||
const uint8_t* offsets = (const uint8_t*) (uintptr_t) data.data.ptr.offsets;
|
||||
const size_t offsetsSize = (size_t) data.offsets_size;
|
||||
|
||||
const char* error = nullptr;
|
||||
if (payloadSize > 0) {
|
||||
if (payloadSize > reply->capacity) {
|
||||
FF_DEBUG("The reply carries %zu payload bytes, the caller buffer holds %zu",
|
||||
payloadSize, reply->capacity);
|
||||
error = "Binder reply does not fit the caller buffer";
|
||||
} else {
|
||||
memcpy(reply->data, payload, payloadSize);
|
||||
reply->size = payloadSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the offset table for the flat objects the service sent us. A reply may carry
|
||||
// both kinds at once and they are not interchangeable: a handle is a reference that
|
||||
// has to be acquired (see below) to outlive the reply buffer, while a file
|
||||
// descriptor was installed in our own fd table by the kernel and is simply ours.
|
||||
for (size_t offset = 0; offset + sizeof(binder_size_t) <= offsetsSize; offset += sizeof(binder_size_t)) {
|
||||
binder_size_t objectOffset = 0;
|
||||
memcpy(&objectOffset, offsets + offset, sizeof(objectOffset));
|
||||
if (objectOffset + sizeof(struct flat_binder_object) > payloadSize) {
|
||||
continue;
|
||||
}
|
||||
struct flat_binder_object object;
|
||||
memcpy(&object, payload + objectOffset, sizeof(object));
|
||||
if (object.hdr.type == BINDER_TYPE_HANDLE) {
|
||||
if (reply->handleCount < FF_BINDER_MAX_HANDLES) {
|
||||
reply->handles[reply->handleCount++] = object.handle;
|
||||
}
|
||||
} else if (object.hdr.type == BINDER_TYPE_FD) {
|
||||
if (reply->fdCount < FF_BINDER_MAX_FDS) {
|
||||
reply->fds[reply->fdCount++] = (int32_t) object.handle;
|
||||
} else {
|
||||
// Nothing is going to hold this one and nothing is going to close it.
|
||||
close((int) object.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The kernel gave us a new reference whose only strong reference belongs to the
|
||||
// reply buffer, so it has to be acquired before that buffer is freed -- otherwise
|
||||
// the handle silently degrades to a weak reference and every later transaction on
|
||||
// it fails with BR_FAILED_REPLY. libbinder does the same in acquire_object().
|
||||
uint8_t tail[FF_BINDER_MAX_HANDLES * 4 * sizeof(uint32_t) + sizeof(uint32_t) + sizeof(binder_uintptr_t)];
|
||||
size_t tailSize = 0;
|
||||
for (uint32_t i = 0; i < reply->handleCount; i++) {
|
||||
const uint32_t acquire = BC_ACQUIRE;
|
||||
const uint32_t increfs = BC_INCREFS;
|
||||
memcpy(tail + tailSize, &acquire, sizeof(uint32_t));
|
||||
tailSize += sizeof(uint32_t);
|
||||
memcpy(tail + tailSize, &reply->handles[i], sizeof(uint32_t));
|
||||
tailSize += sizeof(uint32_t);
|
||||
memcpy(tail + tailSize, &increfs, sizeof(uint32_t));
|
||||
tailSize += sizeof(uint32_t);
|
||||
memcpy(tail + tailSize, &reply->handles[i], sizeof(uint32_t));
|
||||
tailSize += sizeof(uint32_t);
|
||||
}
|
||||
const uint32_t freeCommand = BC_FREE_BUFFER;
|
||||
const binder_uintptr_t freeBuffer = data.data.ptr.buffer;
|
||||
memcpy(tail + tailSize, &freeCommand, sizeof(uint32_t));
|
||||
tailSize += sizeof(uint32_t);
|
||||
memcpy(tail + tailSize, &freeBuffer, sizeof(freeBuffer));
|
||||
tailSize += sizeof(freeBuffer);
|
||||
|
||||
struct binder_write_read release = {
|
||||
.write_size = tailSize,
|
||||
.write_consumed = 0,
|
||||
.write_buffer = (binder_uintptr_t) (uintptr_t) tail,
|
||||
.read_size = 0,
|
||||
.read_consumed = 0,
|
||||
.read_buffer = 0,
|
||||
};
|
||||
// This one ioctl carries the whole tail, so its failure is not cosmetic: without
|
||||
// BC_ACQUIRE the handles handed to the caller are only weakly referenced and every
|
||||
// later transaction on them fails with BR_FAILED_REPLY, which says nothing about the
|
||||
// real cause. Reported, but only when nothing more specific is already at hand.
|
||||
if (ioctl(binder->fd, BINDER_WRITE_READ, &release) != 0 && error == nullptr) {
|
||||
FF_DEBUG("BC_ACQUIRE/BC_FREE_BUFFER failed: %s", strerror(errno));
|
||||
error = "Releasing the binder reply failed";
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
default:
|
||||
FF_DEBUG("Unexpected binder command 0x%08x for handle=%u, code=%u", command, handle, code);
|
||||
return "Unexpected binder command";
|
||||
}
|
||||
}
|
||||
|
||||
FF_DEBUG("The binder reply for handle=%u, code=%u held no BR_REPLY (%zu bytes read)",
|
||||
handle, code, (size_t) exchange.read_consumed);
|
||||
return "No reply from binder";
|
||||
}
|
||||
|
||||
const char* ffBinderLookupService(FFBinder* binder, const char* name, uint32_t transactionCode, uint32_t* handle) {
|
||||
uint8_t parcelBuffer[256];
|
||||
FFBinderParcel parcel = ffBinderParcelCreate(parcelBuffer, sizeof(parcelBuffer));
|
||||
ffBinderParcelPutInterfaceToken(&parcel, FF_BINDER_SM_DESCRIPTOR);
|
||||
ffBinderParcelPutString16(&parcel, name);
|
||||
|
||||
uint8_t replyBuffer[256];
|
||||
FFBinderReply reply = ffBinderReplyCreate(replyBuffer, sizeof(replyBuffer));
|
||||
const char* error = ffBinderTransact(binder, FF_BINDER_SERVICE_MANAGER_HANDLE, transactionCode, 0, &parcel, &reply);
|
||||
if (error != nullptr) {
|
||||
return error;
|
||||
}
|
||||
if (ffBinderReplyIsStatus(&reply)) {
|
||||
FF_DEBUG("The service manager returned a status parcel for %s, code=%u", name, transactionCode);
|
||||
return "Service manager rejected the request";
|
||||
}
|
||||
const int32_t exception = ffBinderReadI32(reply.data, reply.size, 0);
|
||||
if (exception != 0) {
|
||||
FF_DEBUG("The service manager raised exception %d for %s", exception, name);
|
||||
return "Service manager raised an exception";
|
||||
}
|
||||
if (reply.handleCount == 0) {
|
||||
FF_DEBUG("The service manager returned no handle for %s", name);
|
||||
return "Service is not registered";
|
||||
}
|
||||
|
||||
*handle = reply.handles[0];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ffBinderServiceHandleRelease(FFBinderServiceHandle* service) {
|
||||
if (service->binder == nullptr || service->handle == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The mirror of the acquire at the end of ffBinderTransact(): one strong and one weak reference
|
||||
// each, dropped in the same order they were taken. The kernel keeps the service node alive until
|
||||
// the last one goes, so this is what stops a short-lived fastfetch from holding one for the whole
|
||||
// of its life.
|
||||
const uint32_t commands[] = { BC_RELEASE, BC_DECREFS };
|
||||
uint8_t tail[2 * 2 * sizeof(uint32_t)];
|
||||
size_t tailSize = 0;
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
memcpy(tail + tailSize, &commands[i], sizeof(uint32_t));
|
||||
tailSize += sizeof(uint32_t);
|
||||
memcpy(tail + tailSize, &service->handle, sizeof(uint32_t));
|
||||
tailSize += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
struct binder_write_read release = {
|
||||
.write_size = tailSize,
|
||||
.write_consumed = 0,
|
||||
.write_buffer = (binder_uintptr_t) (uintptr_t) tail,
|
||||
.read_size = 0,
|
||||
.read_consumed = 0,
|
||||
.read_buffer = 0,
|
||||
};
|
||||
// Not reported: this runs from a cleanup attribute, where there is no caller left to report to,
|
||||
// and the only way for it to fail is a binder that is already dead -- which every earlier call on
|
||||
// the same binder would have said already.
|
||||
ioctl(service->binder->fd, BINDER_WRITE_READ, &release);
|
||||
service->handle = 0;
|
||||
}
|
||||
|
||||
void ffBinderAcquiredRelease(FFBinderAcquired* acquired) {
|
||||
if (acquired->replied == nullptr || *acquired->replied == false) {
|
||||
return; // nothing was acquired
|
||||
}
|
||||
|
||||
// A descriptor is not a reference: the kernel installed it in our own fd table when it delivered
|
||||
// the reply, and nobody else holds it, so closing it is the whole of giving it back.
|
||||
for (uint32_t i = 0; i < acquired->fdCount; i++) {
|
||||
close(acquired->fds[i]);
|
||||
}
|
||||
|
||||
// The mirror of the acquire the transaction appended to its tail, in the same order it took them.
|
||||
// No binder to talk through means the handles went with it when it was closed.
|
||||
if (acquired->binder == nullptr || acquired->binder->fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < acquired->handleCount; i++) {
|
||||
FFBinderServiceHandle handle = { .binder = acquired->binder, .handle = acquired->handles[i] };
|
||||
ffBinderServiceHandleRelease(&handle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
#pragma once
|
||||
|
||||
// Android exposes no public C API for the system services that carry the data fastfetch wants: the
|
||||
// NDK has no BatteryManager / WifiManager, libbinder_ndk only speaks to AIDL HALs (it refuses to
|
||||
// prepare a transaction for a binder that has no NDK class), and `/system/bin/service call` costs a
|
||||
// fork/exec worth 15-20 ms. What is left is /dev/binder itself, which is all that `service` uses
|
||||
// underneath. This header speaks that protocol directly: open() + mmap() + BINDER_WRITE_READ, no
|
||||
// extra shared library, no child process, ~0.13 ms per call once a handle is cached.
|
||||
//
|
||||
// Three details are easy to get wrong. All three were confirmed against the device's own
|
||||
// libbinder.so and with a ptrace trace of `service`, and getting any of them wrong makes the
|
||||
// service answer BAD_TYPE (0x80000001) or BR_FAILED_REPLY (0x7211):
|
||||
//
|
||||
// * A request parcel must start with three int32 values -- strict mode policy, work source and
|
||||
// 'SYST'. Parcel::markForBinder() writes nothing for kernel binder, but the receiving side's
|
||||
// Parcel::enforceInterface() still compares the third word against its own header.
|
||||
// * Parcel::writeString16() writes the length, the UTF-16 code units, a UTF-16 NUL terminator and
|
||||
// then pads to four bytes. Skipping the terminator shifts every following field by four bytes.
|
||||
// * A handle arriving in a reply is owned by that reply buffer. It has to be acquired
|
||||
// (BC_ACQUIRE + BC_INCREFS) before BC_FREE_BUFFER; otherwise only a weak reference survives and
|
||||
// every later transaction on it fails, because the kernel looks up a strong reference.
|
||||
|
||||
#include "fastfetch.h" // IWYU pragma: keep
|
||||
|
||||
// The structures and command words below are not defined here: bionic generates this header from
|
||||
// bionic/libc/kernel/uapi/linux/android/binder.h and every NDK ships it, so it is the
|
||||
// authoritative copy of the binder ABI -- struct binder_write_read, struct binder_transaction_data,
|
||||
// struct flat_binder_object, BINDER_WRITE_READ / BINDER_VERSION, the BC_* and BR_* command words,
|
||||
// TF_* and BINDER_TYPE_*. Using it beats transcribing it: a second copy can only be wrong, and if
|
||||
// the header itself is wrong that is the NDK's problem to fix, not something fastfetch could have
|
||||
// caught by keeping its own.
|
||||
#include <linux/android/binder.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#define FF_BINDER_DEVICE "/dev/binder"
|
||||
|
||||
// The kernel allocates reply payloads out of the region handed to mmap(); 1 MiB is what libbinder
|
||||
// asks for. Replies themselves are small -- `listServices` on this device is the largest at a few
|
||||
// hundred bytes -- but the region also carries the per-process buffer pool.
|
||||
#define FF_BINDER_SHARED_SIZE (1024 * 1024)
|
||||
|
||||
// A command word plus the binder_transaction_data that follows it. The payload never lands here:
|
||||
// it lives in the mmap region, and BR_REPLY only hands over a pointer to it.
|
||||
#define FF_BINDER_REPLY_BUFFER_SIZE 1024
|
||||
|
||||
// How many handles a reply may transfer to us. Replies that carry more than this still succeed, but
|
||||
// the surplus handles are left unacquired and will fail on first use.
|
||||
#define FF_BINDER_MAX_HANDLES 4
|
||||
|
||||
// How many file descriptors a reply may hand over. A descriptor is not a handle: the kernel installs
|
||||
// it in the caller's own fd table as it delivers the reply, so it is owned rather than borrowed and
|
||||
// the caller closes it. A reply carrying more than this still succeeds, and the surplus is closed
|
||||
// again by ffBinderTransact() because nothing would be left holding it.
|
||||
#define FF_BINDER_MAX_FDS 4
|
||||
|
||||
// The three int32 values Parcel::writeInterfaceToken() puts in front of the descriptor, and that
|
||||
// Parcel::enforceInterface() checks the last of. These come from libbinder rather than from the
|
||||
// kernel, which is why they are spelled out here.
|
||||
#define FF_BINDER_STRICT_MODE_PENALTY_GATHER 0x80000000u // (1 << 31), Parcel.cpp
|
||||
#define FF_BINDER_UNSET_WORK_SOURCE 0xffffffffu // IPCThreadState::kUnsetWorkSource
|
||||
#define FF_BINDER_HEADER 0x53595354u // B_PACK_CHARS('S', 'Y', 'S', 'T')
|
||||
|
||||
typedef struct FFBinder {
|
||||
int fd;
|
||||
uint8_t* shared;
|
||||
size_t sharedSize;
|
||||
int32_t protocolVersion;
|
||||
} FFBinder;
|
||||
|
||||
// Opens /dev/binder, checks the protocol version and maps the shared region. Returns nullptr on
|
||||
// success, a static message otherwise; the device is left closed on failure.
|
||||
[[gnu::nonnull(1), nodiscard]] const char* ffBinderOpen(FFBinder* binder);
|
||||
|
||||
// Usable as a cleanup attribute. Safe on a zeroed or already closed FFBinder.
|
||||
[[gnu::nonnull(1)]] void ffBinderClose(FFBinder* binder);
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Parcel writing. The caller owns the buffer; anything that does not fit is dropped and recorded
|
||||
// in `truncated`, which ffBinderTransact() then reports as an error.
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
typedef struct FFBinderParcel {
|
||||
uint8_t* data;
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
bool truncated;
|
||||
} FFBinderParcel;
|
||||
|
||||
[[nodiscard]] static inline FFBinderParcel ffBinderParcelCreate(uint8_t* data, size_t capacity) {
|
||||
return (FFBinderParcel) { .data = data, .capacity = capacity, .size = 0, .truncated = false };
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1), nodiscard]] static inline uint8_t* ffBinderParcelReserve(FFBinderParcel* parcel, size_t bytes) {
|
||||
if (bytes > parcel->capacity - parcel->size) {
|
||||
parcel->truncated = true;
|
||||
return nullptr;
|
||||
}
|
||||
uint8_t* result = parcel->data + parcel->size;
|
||||
parcel->size += bytes;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffBinderParcelPutU32(FFBinderParcel* parcel, uint32_t value) {
|
||||
uint8_t* dst = ffBinderParcelReserve(parcel, sizeof(uint32_t));
|
||||
if (dst != nullptr) {
|
||||
memcpy(dst, &value, sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffBinderParcelPutI32(FFBinderParcel* parcel, int32_t value) {
|
||||
uint8_t* dst = ffBinderParcelReserve(parcel, sizeof(int32_t));
|
||||
if (dst != nullptr) {
|
||||
memcpy(dst, &value, sizeof(int32_t));
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1)]] static inline void ffBinderParcelPutU64(FFBinderParcel* parcel, uint64_t value) {
|
||||
uint8_t* dst = ffBinderParcelReserve(parcel, sizeof(uint64_t));
|
||||
if (dst != nullptr) {
|
||||
memcpy(dst, &value, sizeof(uint64_t));
|
||||
}
|
||||
}
|
||||
|
||||
// AIDL string16. The descriptors and service names fastfetch asks for are ASCII, so one byte is one
|
||||
// UTF-16 code unit; anything else would have to be encoded properly here.
|
||||
[[gnu::nonnull(1, 2)]] static inline void ffBinderParcelPutString16(FFBinderParcel* parcel, const char* value) {
|
||||
const size_t length = strlen(value);
|
||||
const size_t payload = sizeof(uint32_t) + (length + 1) * 2; // length, code units, NUL terminator
|
||||
const size_t padded = (payload + 3) & ~(size_t) 3;
|
||||
uint8_t* dst = ffBinderParcelReserve(parcel, padded);
|
||||
if (dst == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(dst, 0, padded);
|
||||
const uint32_t length32 = (uint32_t) length;
|
||||
memcpy(dst, &length32, sizeof(uint32_t));
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
dst[sizeof(uint32_t) + i * 2] = (uint8_t) value[i];
|
||||
}
|
||||
// The two bytes after the last code unit stay zero: that is the UTF-16 NUL terminator, and
|
||||
// Parcel::readInplace() advances by (length + 1) * sizeof(char16_t) to account for it.
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1, 2)]] static inline void ffBinderParcelPutInterfaceToken(FFBinderParcel* parcel, const char* descriptor) {
|
||||
ffBinderParcelPutU32(parcel, FF_BINDER_STRICT_MODE_PENALTY_GATHER);
|
||||
ffBinderParcelPutU32(parcel, FF_BINDER_UNSET_WORK_SOURCE);
|
||||
ffBinderParcelPutU32(parcel, FF_BINDER_HEADER);
|
||||
ffBinderParcelPutString16(parcel, descriptor);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Transactions
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
typedef struct FFBinderReply {
|
||||
uint8_t* data; // caller owned; receives the payload
|
||||
size_t capacity; // ditto
|
||||
size_t size; // payload length in bytes
|
||||
uint32_t code; // transaction code the service echoed back
|
||||
uint32_t flags; // TF_* of the reply
|
||||
uint32_t handleCount;
|
||||
uint32_t handles[FF_BINDER_MAX_HANDLES];
|
||||
uint32_t fdCount;
|
||||
int32_t fds[FF_BINDER_MAX_FDS]; // owned by the caller, which closes them
|
||||
} FFBinderReply;
|
||||
|
||||
[[nodiscard]] static inline FFBinderReply ffBinderReplyCreate(uint8_t* data, size_t capacity) {
|
||||
return (FFBinderReply) { .data = data, .capacity = capacity, .size = 0, .code = 0, .flags = 0, .handleCount = 0, .handles = {}, .fdCount = 0, .fds = {} };
|
||||
}
|
||||
|
||||
// A reply carrying TF_STATUS_CODE is not an AIDL parcel at all: the first four bytes are the raw
|
||||
// status_t that onTransact() returned (-74 being UNKNOWN_TRANSACTION, i.e. no such method). Check
|
||||
// this before interpreting anything else.
|
||||
[[gnu::nonnull(1), nodiscard]] static inline bool ffBinderReplyIsStatus(const FFBinderReply* reply) {
|
||||
return (reply->flags & TF_STATUS_CODE) != 0;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1), nodiscard]] static inline uint32_t ffBinderReadU32(const uint8_t* data, size_t size, size_t offset) {
|
||||
uint32_t value = 0;
|
||||
if (offset + sizeof(uint32_t) <= size) {
|
||||
memcpy(&value, data + offset, sizeof(uint32_t));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1), nodiscard]] static inline int32_t ffBinderReadI32(const uint8_t* data, size_t size, size_t offset) {
|
||||
int32_t value = 0;
|
||||
if (offset + sizeof(int32_t) <= size) {
|
||||
memcpy(&value, data + offset, sizeof(int32_t));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[gnu::nonnull(1), nodiscard]] static inline uint64_t ffBinderReadU64(const uint8_t* data, size_t size, size_t offset) {
|
||||
uint64_t value = 0;
|
||||
if (offset + sizeof(uint64_t) <= size) {
|
||||
memcpy(&value, data + offset, sizeof(uint64_t));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Synchronous transaction. Returns nullptr on success, a static message otherwise.
|
||||
//
|
||||
// `flags` carries the TF_* values for the request, and one of them is not cosmetic: a reply that
|
||||
// carries a file descriptor is only delivered at all when the request asked with TF_ACCEPT_FDS. A
|
||||
// request that did not gets BR_FAILED_REPLY instead, which reads like a service that is not running
|
||||
// rather than like a missing flag. TF_ONE_WAY is the other one the kernel acts on, and it is what
|
||||
// makes a call fire-and-forget -- there is then no reply to fill `reply` from.
|
||||
//
|
||||
// Handles the reply carries are acquired and added to `reply->handles` before the reply buffer is
|
||||
// freed; descriptors are added to `reply->fds`, which the caller owns and closes.
|
||||
//
|
||||
// `handle` 0 is the service manager; every other handle comes from ffBinderLookupService(). A
|
||||
// service that never replies blocks this call forever -- none of the services fastfetch reads does,
|
||||
// but there is no timeout here to fall back on.
|
||||
[[gnu::nonnull(1, 5, 6), nodiscard]] const char* ffBinderTransact(FFBinder* binder, uint32_t handle, uint32_t code, uint32_t flags, const FFBinderParcel* parcel, FFBinderReply* reply);
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Service manager
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
#define FF_BINDER_SERVICE_MANAGER_HANDLE 0u
|
||||
#define FF_BINDER_SM_DESCRIPTOR "android.os.IServiceManager"
|
||||
|
||||
// Entry points of android.os.IServiceManager, as this device answers them. The numbers do not match
|
||||
// the AOSP sources: checkService is 4 here, not 3. FF_BINDER_SM_GET_SERVICE returns a nullable
|
||||
// IBinder and is the simplest reply to parse; FF_BINDER_SM_CHECK_SERVICE is what `service` itself
|
||||
// calls and answers with a parcelable instead.
|
||||
#define FF_BINDER_SM_GET_SERVICE 1u
|
||||
#define FF_BINDER_SM_GET_SERVICE2 2u
|
||||
#define FF_BINDER_SM_CHECK_SERVICE 4u
|
||||
#define FF_BINDER_SM_LIST_SERVICES 6u
|
||||
|
||||
// Resolves a service name to a handle through the service manager. Returns nullptr on success.
|
||||
[[gnu::nonnull(1, 2, 4), nodiscard]] const char* ffBinderLookupService(FFBinder* binder, const char* name, uint32_t transactionCode, uint32_t* handle);
|
||||
|
||||
// A handle resolved through the service manager, held together with the binder it came from so that a
|
||||
// single cleanup attribute can give it back on every path out of the function that took it. Zeroed --
|
||||
// or left at handle 0 by a lookup that has not run or did not succeed -- it is a no-op, which is what
|
||||
// makes it safe to declare above the lookup itself.
|
||||
typedef struct FFBinderServiceHandle {
|
||||
FFBinder* binder;
|
||||
uint32_t handle;
|
||||
} FFBinderServiceHandle;
|
||||
|
||||
// Returns the strong and weak references ffBinderLookupService() acquired on the handle. Usable as a
|
||||
// cleanup attribute; safe on a zeroed or already released FFBinderServiceHandle.
|
||||
[[gnu::nonnull(1)]] void ffBinderServiceHandleRelease(FFBinderServiceHandle* service);
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Handles a transaction acquired
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
// Whatever ffBinderTransact() collected for the caller, released together on the way out of a
|
||||
// function -- the handles it acquired, plus the descriptors it delivered, which are owned rather
|
||||
// than borrowed and would otherwise stay open for the life of the process.
|
||||
//
|
||||
// Unlike FFBinderServiceHandle this cannot be declared before the transaction runs, because which
|
||||
// handles came back is only known afterwards. `replied` is therefore a separate flag the caller
|
||||
// raises once ffBinderTransact() has returned: it acquires the handles even when it goes on to fail
|
||||
// (a reply that did not fit the caller buffer still had its BC_ACQUIRE tail sent), so releasing
|
||||
// without it would drop a reference that was never taken.
|
||||
typedef struct FFBinderAcquired {
|
||||
FFBinder* binder;
|
||||
const uint32_t* handles;
|
||||
uint32_t handleCount;
|
||||
const int32_t* fds;
|
||||
uint32_t fdCount;
|
||||
const bool* replied;
|
||||
} FFBinderAcquired;
|
||||
|
||||
// Usable as a cleanup attribute; safe on a zeroed FFBinderAcquired, and on one whose transaction did
|
||||
// not reply.
|
||||
[[gnu::nonnull(1)]] void ffBinderAcquiredRelease(FFBinderAcquired* acquired);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
|
||||
// AIDL gives every method of an interface a transaction code at build time:
|
||||
//
|
||||
// static final int TRANSACTION_<method> = IBinder.FIRST_CALL_TRANSACTION + <declaration index>;
|
||||
//
|
||||
// so the number is a property of the `.aidl` of the release that built the device's jar, not of any
|
||||
// public header. Counting the methods in an AOSP checkout only describes that checkout, and it goes
|
||||
// wrong the moment a release or a vendor fork inserts a method ahead of the one being looked for:
|
||||
// `IWifiManager.getConnectionInfo` is 29 on Android 11 and 41 on Android 16. There is no negotiation
|
||||
// and no discovery -- a wrong code reaches a different method, or none.
|
||||
//
|
||||
// Being a compile-time constant, the value lands in the `static_values` array of the `X$Stub` class
|
||||
// in the jar's dex, paired position by position with that class's static field list. Reading the two
|
||||
// together gives the mapping of the build that is actually on the device, on any release, with
|
||||
// nothing to keep in sync.
|
||||
//
|
||||
// `libdexfile.so` cannot do this for us. It is dlopen-able from an app UID through the ART apex, but
|
||||
// it is not in the NDK and its only C ABI -- the `ADexFile_*` family -- covers methods, not fields;
|
||||
// the field and class-data APIs are C++ with no ABI guarantee.
|
||||
//
|
||||
// `ffReadFileBuffer` would work but pulls the whole jar through the heap for one integer. The jar is
|
||||
// mapped instead, and only the pages actually read are faulted in.
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
// What a request is written when the jar does not answer it, on every path out of ffDexStaticInts --
|
||||
// a jar that cannot be opened and a field the class does not declare both leave it behind, so a
|
||||
// caller reads a result rather than what its own stack happened to hold there. No transaction code
|
||||
// is negative: AIDL numbers them from `IBinder.FIRST_CALL_TRANSACTION`, which is 1.
|
||||
//
|
||||
// This is what keeps the reader a lookup and nothing more. Whether a code is one the caller cannot do
|
||||
// without or one it only uses when the build declares it, whether what came back is enough to carry
|
||||
// on, and whether the module reports a value or an error are the caller's decisions, made by reading
|
||||
// the results. The reader finds numbers and says which ones it could not.
|
||||
#define FF_DEX_STATIC_INT_UNRESOLVED (-1)
|
||||
|
||||
// One `<classDescriptor>.<fieldName>` lookup, and where its value is written. `classDescriptor` is
|
||||
// the dex type descriptor, e.g. "Landroid/net/wifi/IWifiManager$Stub;".
|
||||
typedef struct FFDexStaticIntRequest {
|
||||
const char* classDescriptor;
|
||||
const char* fieldName;
|
||||
int32_t* result;
|
||||
} FFDexStaticIntRequest;
|
||||
|
||||
// Resolves the int value of every request out of the dex entries of `jarPath`, walking `classes.dex`,
|
||||
// `classes2.dex`, ... in order until one of them defines the class.
|
||||
//
|
||||
// Asking for several fields at once is not a convenience. A lookup is dominated by the type table,
|
||||
// which lists every type the dex references -- 8428 descriptors in `framework.jar`'s `classes.dex`,
|
||||
// each one read from a different part of a 51 MB mapping -- and a single pass answers every request
|
||||
// whose class is named in it. Measured over three devices' own `framework.jar`, four fields of two
|
||||
// classes cost one pass rather than four.
|
||||
//
|
||||
// Every request is written a code or FF_DEX_STATIC_INT_UNRESOLVED. A request is never allowed to
|
||||
// take the others down with it: the classes a caller asks for are looked for independently, and one
|
||||
// the jar does not declare leaves its own result at the sentinel while the rest are answered.
|
||||
//
|
||||
// Not finding a code is not a failure. A jar that is readable answers the call, whether it declares
|
||||
// every field asked for, some of them, or none -- the results say which, and what that means for the
|
||||
// module is the caller's to decide. The return value is therefore reserved for the one thing the
|
||||
// caller cannot read out of the results: the jar itself, which is reported when it cannot be opened
|
||||
// or does not hold a dex that can be read. nullptr means the lookup ran.
|
||||
[[gnu::nonnull(1, 2), nodiscard]] const char* ffDexStaticInts(const char* jarPath, const FFDexStaticIntRequest* requests, uint32_t count);
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Reading a Parcelable's wire layout
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
//
|
||||
// A Parcelable's `writeToParcel` puts its fields on the wire in the order the method reads them, and
|
||||
// that order is the only thing that says where each one is. It is not in the class's declaration: a
|
||||
// dex stores a class's instance fields sorted by field index, which is sorted by *name*, so the
|
||||
// declaration order a reader would want is not recorded anywhere but the method body.
|
||||
//
|
||||
// Recovering it therefore means walking the bytecode. The compiler emits
|
||||
//
|
||||
// iget v0, p0, LDisplayInfo;->logicalWidth:I
|
||||
// invoke-virtual {p1, v0}, Landroid/os/Parcel;->writeInt(I)V
|
||||
//
|
||||
// once per field, in write order, so the `iget` opcodes in a linear pass over the instruction grid
|
||||
// are the sequence. Two things have to be got right for that pass to stay on the grid, and both are
|
||||
// easy to get wrong in a way that still produces a plausible-looking list:
|
||||
//
|
||||
// * Instructions are 1, 2, 3, 4 or 5 code units wide, and the width is a property of the opcode.
|
||||
// The table below is ART's `Instruction::SizeInCodeUnits`.
|
||||
// * The payload pseudo-instructions a `switch` or a `fill-array-data` leaves in the stream have a
|
||||
// low byte of zero and a high byte of 1, 2 or 3. They are not instructions, they sit behind every
|
||||
// reachable instruction, and stepping into one desynchronises the grid -- so the walk stops at the
|
||||
// first of them rather than trying to step over it.
|
||||
//
|
||||
// An array field is read twice: once for its `length`, which is the count that goes on the wire, and
|
||||
// again inside the loop that writes the elements. A class cannot declare two instance fields under
|
||||
// one name, so a repeat is always that reload, and the first occurrence is where the field is
|
||||
// written. The reader de-duplicates by name for that reason.
|
||||
//
|
||||
// What is *not* recovered is how each field is framed -- `writeString` against `writeString8`,
|
||||
// `writeParcelable` against `writeTypedObject`. That is a property of the method the field is handed
|
||||
// to, not of the field, and two `String` fields of the same class can differ: `DisplayInfo.name` goes
|
||||
// through `writeString8` while `DeviceProductInfo.mName` goes through `writeString`. Telling them
|
||||
// apart would mean resolving every invoke target, so the type descriptor is reported instead and the
|
||||
// caller frames the fields its descriptor settles -- an `I`, a `J`, an `[F` -- and names the few it
|
||||
// does not. Measured over three releases' own `framework.jar`, that is four names per class.
|
||||
//
|
||||
// The point of asking the device rather than carrying a table is that the field set moves: `DisplayInfo`
|
||||
// has 34 instance fields on Android 11, 42 on 13 and 59 on 16, and the fields that arrived in between
|
||||
// (`displayGroupId`, `renderFrameRate`, `appsSupportedModes`, `committedState`, ...) are exactly the
|
||||
// ones a parser written against one release reads out of the middle of another. Reading the sequence
|
||||
// from the build that is running makes every one of them optional by construction.
|
||||
|
||||
// A field name and a type descriptor are ASCII and short -- the longest name in `DisplayInfo` is 28
|
||||
// bytes and the longest descriptor `Landroid/hardware/display/DeviceProductInfo$ManufactureDate;` is
|
||||
// 60. A longer one is a class this reader was not written for, and is reported as unresolved rather
|
||||
// than truncated: half a name would silently match nothing, which is indistinguishable from the field
|
||||
// not being there.
|
||||
#define FF_DEX_PARCEL_FIELD_NAME_MAX 40
|
||||
#define FF_DEX_PARCEL_FIELD_TYPE_MAX 72
|
||||
|
||||
// One field of the write sequence. `type` is the dex type descriptor, e.g. "I", "[F",
|
||||
// "Ljava/lang/String;" or "Landroid/view/DisplayAddress;".
|
||||
typedef struct FFDexParcelField {
|
||||
char name[FF_DEX_PARCEL_FIELD_NAME_MAX];
|
||||
char type[FF_DEX_PARCEL_FIELD_TYPE_MAX];
|
||||
} FFDexParcelField;
|
||||
|
||||
// One class, and the two things that can be asked about it: the write sequence of one of its methods,
|
||||
// and which instance fields it declares. Either half may be left out, and both are answered out of
|
||||
// the same `class_data_item` -- which is why they are one request rather than two calls.
|
||||
//
|
||||
// Asking for them together is not a convenience. Both halves are read from a walk that is dominated
|
||||
// by the type table -- 8428 descriptors in `framework.jar`'s `classes.dex`, each read from a
|
||||
// different part of a 51 MB mapping -- and the fields a class declares are listed in the very
|
||||
// `class_data_item` the sequence is read out of. A second call would map the jar again, open every
|
||||
// entry again and make that pass again to reach a list the first call already had in hand. Measured
|
||||
// on a Redmi 9A, that second call was 7.0 ms of a 23.5 ms lookup.
|
||||
typedef struct FFDexParcelRequest {
|
||||
const char* classDescriptor;
|
||||
|
||||
// The write sequence. Left out by a request that only wants the instance fields, in which case
|
||||
// `fields`, `capacity` and `count` are not read.
|
||||
const char* methodName;
|
||||
FFDexParcelField* fields;
|
||||
uint32_t capacity;
|
||||
uint32_t* count; // how many fields the sequence holds, or 0 when the jar cannot answer
|
||||
|
||||
// The instance fields. This is the question a Parcelable whose layout is not field-driven needs.
|
||||
// `DisplayCutout` is one: its `writeCutoutToParcel` reads everything through `-$$Nest$` accessors
|
||||
// the compiler synthesised, so there is no `iget` to walk and no class name on the wire either.
|
||||
// What does say how long its body is, is which fields the class carries -- Android 11 declares
|
||||
// three, Android 13 adds `mCutoutPathParserInfo` and Android 16 adds `mSideOverrides`, and each
|
||||
// one adds a known run of writes behind the three both have. A declaration is a field set rather
|
||||
// than an order, which is exactly what this answers.
|
||||
const char* const* instanceFieldNames;
|
||||
bool* instanceFieldResults;
|
||||
uint32_t instanceFieldCount;
|
||||
} FFDexParcelRequest;
|
||||
|
||||
// Resolves every request out of the dex entries of `jarPath`, walking `classes.dex`, `classes2.dex`,
|
||||
// ... in order until one of them defines the class -- the same walk, and the same one pass over the
|
||||
// type table, that ffDexStaticInts makes.
|
||||
//
|
||||
// Every request is written its answers before anything can fail: a count of 0 for the sequence, and
|
||||
// `false` for every instance field. A class the jar does not declare, a method it does not declare,
|
||||
// and a sequence longer than the caller's `capacity` all leave the count at 0, and none of them holds
|
||||
// back the others: they are independent questions about the same pass. A count of 0 is not a failure
|
||||
// but an answer -- this build does not write that class, so a caller reading it has nothing to walk.
|
||||
// The return value is reserved for the jar itself, as in ffDexStaticInts.
|
||||
[[gnu::nonnull(1, 2), nodiscard]] const char* ffDexParcelFields(const char* jarPath, const FFDexParcelRequest* requests, uint32_t count);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user