Compare commits

..

639 Commits

Author SHA1 Message Date
Linus Dierheimer f0b9800974 Fix fastfetch recaching every time if an old cache exists 2022-03-23 11:38:42 +01:00
Linus Dierheimer be84549f4c Refactored media detection 2022-03-23 11:15:50 +01:00
Linus Dierheimer 0916258c87 Greatly improved detection of media player name 2022-03-22 18:59:04 +01:00
Linus Dierheimer ad006b466c Version output include extra info 2022-03-20 17:12:52 +01:00
Linus Dierheimer efdf38a9ea Version 1.0.0 ! 2022-03-20 16:47:05 +01:00
Linus Dierheimer 10a202fe80 First Release? v14 2022-03-20 16:44:08 +01:00
Linus Dierheimer 6e8aca0945 First Release? v13 2022-03-20 16:37:27 +01:00
Linus Dierheimer 4cd08b82d4 First Release? v12 2022-03-20 16:32:15 +01:00
Linus Dierheimer a2ebecdf6d First Release? v11 2022-03-20 16:28:57 +01:00
Linus Dierheimer aa352a4427 First Release? v10 2022-03-20 16:23:25 +01:00
Linus Dierheimer 10c97aea4b First Release? v9 2022-03-20 16:22:27 +01:00
Linus Dierheimer e08cb24cb5 First Release? v8 2022-03-20 16:08:20 +01:00
Linus Dierheimer bfbaecb533 First Release? v7 2022-03-20 16:01:31 +01:00
Linus Dierheimer d915f415b1 First Release? v6 2022-03-20 15:57:59 +01:00
Linus Dierheimer 1887e21ddc First Release? v5 2022-03-20 15:53:08 +01:00
Linus Dierheimer 0b6c974126 First Release? v4 2022-03-20 15:45:22 +01:00
Linus Dierheimer fdd8919e1c First Release? v3 2022-03-20 15:43:21 +01:00
Linus Dierheimer 0c936cab27 First Release? v2 2022-03-20 15:42:37 +01:00
Linus Dierheimer d17aae5d65 First Release? 2022-03-20 15:40:23 +01:00
Linus Dierheimer 77e05b0c95 prepare for releases 2022-03-20 13:16:04 +01:00
Linus Dierheimer 0f6e266fc3 Removed unneeded else 2022-03-17 16:52:19 +01:00
Linus Dierheimer de779cf1ea Create FUNDING.yml 2022-03-17 16:35:34 +01:00
Linus Dierheimer ecbb854e54 KDE Neon logo 2022-03-15 18:49:44 +01:00
Linus Dierheimer da26a3497e Update README.md 2022-03-15 11:13:15 +01:00
Linus Dierheimer a6770581d3 Easter egg for uptime >= 100 days
Similiar to htop
2022-03-14 18:02:03 +01:00
Linus Dierheimer 07a685bb9a 16 Colors in TTY 2022-03-14 17:31:16 +01:00
Linus Dierheimer 00fdb8a0c4 Generic Linux logo 2022-03-09 17:39:36 +01:00
shivanandvp 4e68c09f27 Adding RebornOS to the supported logos (#140)
* feat: Add logos for RebornOS

* feat: Decrease the size of the RebornOS logo and prepare a better small RebornOS logo

* fix: Align text for both the regular and small RebornOS logos
2022-03-08 18:39:23 +01:00
Linus Dierheimer b5a36b0825 Update README.md 2022-03-08 12:13:01 +01:00
Linus Dierheimer 231016264b Update CMakeLists.txt 2022-03-08 12:03:21 +01:00
Linus Dierheimer 0451852e34 Install License with cmake too 2022-03-08 11:59:06 +01:00
Linus Dierheimer b9dfe1f858 Fix compilation warnings 2022-03-08 11:07:31 +01:00
Linus Dierheimer 51bbff4741 Zorin Logo 2022-03-08 10:47:04 +01:00
Linus Dierheimer 7c9b5c2c61 Update datetime.c 2022-02-25 15:06:24 +01:00
Linus Dierheimer fd450bcccc Date Time support 2022-02-25 13:30:37 +01:00
Linus Dierheimer 0cb6e40bac Fix neofetch preset 2022-02-21 14:44:46 +01:00
Linus Dierheimer 9c20c50c3d Merge pull request #136 from CyberTailor/master
CMake improvements
2022-02-21 14:40:41 +01:00
Anna “CyberTailor” aa9dd9c155 CMakeLists.txt: make tests optional
Run test-strbuf using ctest.
2022-02-21 12:30:06 +05:00
Anna “CyberTailor” 415c097489 CMakeLists.txt: use git only when in repository
Allow building fastfetch from tarballs with -DPROJECT_VERSION and
-DPROJECT_VERSION_MAJOR options.
2022-02-21 12:30:06 +05:00
Anna “CyberTailor” 109026211c CMakeLists: add installation target 2022-02-21 12:30:05 +05:00
Linus Dierheimer ed63ef1a28 Support detecting artist if a browser is current media player 2022-02-14 13:04:57 +01:00
Linus Dierheimer f54ac1c4e6 Update tests 2022-02-14 12:04:54 +01:00
Linus Dierheimer 313084f91c Update presets 2022-02-14 11:44:10 +01:00
Linus Dierheimer 59bd9927a4 Fix concating of song artists 2022-02-14 11:08:04 +01:00
Linus Dierheimer e2be34e23d Update DEVELOPMENT.md 2022-02-14 01:12:26 +01:00
Linus Dierheimer 3ca3687993 Update README.md 2022-02-14 00:48:35 +01:00
Linus Dierheimer c066446685 Update GitHub Actions again 2022-02-14 00:43:56 +01:00
Linus Dierheimer ecb38e8d20 Update GitHub Actions 2022-02-14 00:41:11 +01:00
Linus Dierheimer c800077b73 Fix GitHub actions once again 2022-02-14 00:35:54 +01:00
Linus Dierheimer fcea3649e2 Fix GitHub actions again 2022-02-14 00:29:54 +01:00
Linus Dierheimer eaaa2705d3 Fix GitHub actions 2022-02-14 00:25:51 +01:00
Linus Dierheimer a32b1c5887 Update github actions to support song and media player update 2022-02-14 00:18:22 +01:00
Linus Dierheimer cfb5d204a1 Media player and song detection! 2022-02-13 23:51:13 +01:00
Linus Dierheimer d6ae32374e Update DEVELOPMENT.md 2022-02-13 13:26:00 +01:00
Linus Dierheimer 451848f6fd Small improvements to code quality 2022-02-12 22:30:57 +01:00
Linus Dierheimer a7728f61fb Battery: show full status 2022-02-12 18:20:00 +01:00
Linus Dierheimer fb3028cafa Update DEVELOPMENT.md 2022-02-12 12:45:00 +01:00
Linus Dierheimer ea5b663b16 Rename class to deviceClass, so cpp linters don't see a problem 2022-02-12 12:38:14 +01:00
Linus Dierheimer 9659f8c7b1 Add AUR packaging as submodul 2022-02-12 12:31:42 +01:00
Linus Dierheimer 3f64ef0876 Show emerge packages earlier in list 2022-02-12 12:13:02 +01:00
Linus Dierheimer 8c6cb9405d Use FFstrbuf in emerge package counting 2022-02-12 12:06:16 +01:00
Viorel 8a754f5b89 Add packages support for Gentoo Linux (#131)
Count packages installed by Gentoo emerge
2022-02-12 11:16:14 +01:00
Linus Dierheimer 5f965ac787 Fixed two compiler warnings 2022-02-09 15:22:57 +01:00
Linus Dierheimer bc8887fb42 Improved compile time by compiling common sources as a static lib 2022-02-09 15:20:07 +01:00
Linus Dierheimer 2ff3595251 Update DEVELOPMENT.md 2022-02-09 11:14:20 +01:00
Linus Dierheimer 47a58ad7b5 Update build-deb-package.yml 2022-02-08 19:30:55 +01:00
Linus Dierheimer ec523e0274 Update DEVELOPMENT.md 2022-02-08 19:28:05 +01:00
xzvf 87e410e0fc Add a Github Action to build and package fastfetch for Debian. (#127)
* Create a build.yml file

* add debian packaging

untested

* Copy & Paste is hard

* Fix typo, libRpm -> librpm

* Change working-dir

* Try again. Maybe now it will find the deb package

* Add more dependencies

* This should be it, hopefully

* Fix installed packages

* Change the name of the auto-created releases

* Document GH action

* Remove unnecessary packages. 

Apparently brute-forcing dependencies until the build is successful is not a good strategy.

* Move building the packaging into a seperate github action.

* Remove packaging from test Action
2022-02-08 19:15:28 +01:00
Linus Dierheimer cc43ed2351 Merge pull request #130 from ceamac/features/cmake/options
Make dependencies check optional
2022-02-08 11:51:08 +01:00
Viorel a28c4600f4 Make dependencies check optional 2022-02-08 07:10:46 +02:00
Linus Dierheimer 7ad73d3ba1 Fix arco linux logo 2022-01-29 10:32:23 +01:00
Linus Dierheimer f72ec8a99f Updated screenshot 2022-01-23 13:54:56 +01:00
Linus Dierheimer 6107241f4a Update README.md 2022-01-23 12:08:58 +01:00
Linus Dierheimer 68e8ddce6f Update README.md 2022-01-23 12:07:49 +01:00
Linus Dierheimer 1a57d82675 Update README.md 2022-01-23 12:07:04 +01:00
Linus Dierheimer c662416b33 Update README.md 2022-01-23 12:02:26 +01:00
Linus Dierheimer 79331d7fe5 Update README.md 2022-01-23 12:00:28 +01:00
Linus Dierheimer e887b56209 Fix performance test 2022-01-22 23:03:09 +01:00
Linus Dierheimer 23b71cc907 Fixed some rare compilation warnings from old compilers 2022-01-22 23:01:09 +01:00
Linus Dierheimer 4e45a2e3ba Fix building with librpm support enabled 2022-01-22 22:54:20 +01:00
Linus Dierheimer 14115eb089 Merge pull request #123 from x-zvf/master
Add packaging for Debian based distributions.
2022-01-22 13:11:41 +01:00
xzvf 8f466735a8 Add .deb file and deb-package build directory to .gitignore 2022-01-22 12:40:58 +01:00
xzvf 7a7eac9edd Copy presets to /usr/share/fastfetch/presets 2022-01-22 12:40:10 +01:00
xzvf 65dc9bb166 Remove flashfetch target, and fix 2 shellcheck warnings 2022-01-22 12:35:21 +01:00
xzvf (Péter Bohner) 57ef794864 Add packaging for Debian based distributions.
This commit adds a script (`create-deb-pkg.sh`), which creates a deb package containing the fastfetch and flashfetch executables as well as the bash-completion file. The script must be executed in an environment, where `dpkg-deb` is present (a debian based environment).
Because Deb versioning is not compatible with fastfetch version strings, it is adapted to the format: `0:1$FF_VERSION-0`.
2022-01-22 11:56:11 +01:00
Linus Dierheimer cd4739e4f7 Merge pull request #122 from TahaAbbasi-Hashemi/master
Artix_small logo had an extra space in line4
2022-01-20 15:25:02 +01:00
Taha Abbasi-Hashemi 3a667add01 Artix_small logo had an extra space in line4 2022-01-20 09:10:01 -05:00
Linus Dierheimer 6906094b8b Merge branch 'master' of github.com:LinusDierheimer/fastfetch 2022-01-20 12:19:18 +01:00
Linus Dierheimer b8a239e379 Add endeavour logo 2022-01-20 12:18:56 +01:00
Linus Dierheimer e30a64e97e Update README.md 2022-01-20 01:22:13 +01:00
Linus Dierheimer f2663a65cc More flexible OS file detection 2022-01-18 18:56:37 +01:00
Linus Dierheimer a0f5432903 Temp detection 2022-01-16 16:33:15 +01:00
Linus Dierheimer 20427a534e Respect WAYLAND_SOCKET on builds without wayland-client support 2022-01-14 13:31:39 +01:00
Linus Dierheimer 9789d1f5f1 Fallback OS detection using lsb 2022-01-14 13:21:49 +01:00
Linus Dierheimer bf2152bac8 Disable public ip timeout by default 2022-01-12 19:16:49 +01:00
Linus Dierheimer accaaa7de5 Expose GPU driver to gpu format string 2022-01-12 18:57:35 +01:00
Linus Dierheimer 322e5c5a48 FFstrbuf: don't do unneeded destroy work 2022-01-12 02:05:12 +01:00
Linus Dierheimer 6d7ad48edf Clear destroyed FFstrbuf 2022-01-12 01:59:57 +01:00
Linus Dierheimer 6a62f909d4 Destroyed FFstrbufs can now be reused 2022-01-12 01:57:49 +01:00
Linus Dierheimer 3653836490 Merge pull request #119 from CachyOS/master
Add CachyOS logo support
2022-01-12 01:52:15 +01:00
Linus Dierheimer f21013f646 Use same variable name in definition and declaration 2022-01-12 01:33:03 +01:00
Linus Dierheimer 8639e59019 Silence a false positive compiler warning 2022-01-12 01:21:36 +01:00
Linus Dierheimer 33127388f0 refactored FFstrbuf 2022-01-12 01:02:14 +01:00
Vladislav Nepogodin f54101e9f5 🔥 add cachyos 2022-01-12 01:17:23 +04:00
Vladislav Nepogodin d79b7a8944 🔥 add cachyos 2022-01-12 01:11:58 +04:00
Vladislav Nepogodin 2c7314dd8e 🔥 add cachyos logo 2022-01-12 01:10:04 +04:00
Linus Dierheimer 729d9d5c47 Add help for IP format 2022-01-11 12:36:32 +01:00
Linus Dierheimer 1c72219a88 Fix ordering of macros in settings.c 2022-01-10 20:45:58 +01:00
Linus Dierheimer 816d86aee8 Public IP module 2022-01-10 20:20:18 +01:00
Linus Dierheimer d6a80d7c85 Android support part 7 2022-01-10 15:31:36 +01:00
Linus Dierheimer ef034b7adb Android support part 6 2022-01-10 15:26:25 +01:00
Linus Dierheimer 4bbb13b44d Android support part 5 2022-01-10 15:24:10 +01:00
Linus Dierheimer 4aff8a353b Android support part 4 2022-01-10 15:22:25 +01:00
Linus Dierheimer 190974e079 Android support part 3 2022-01-10 15:20:51 +01:00
Linus Dierheimer e95e1e3530 Android support part 2 2022-01-10 14:41:34 +01:00
Linus Dierheimer 1d6119f858 Update DEVELOPMENT.md 2022-01-09 19:49:19 +01:00
Linus Dierheimer bf60429e36 Android support part 1 2022-01-09 16:51:57 +01:00
Linus Dierheimer 31d0729cdd Small syntactic changes 2022-01-08 21:58:37 +01:00
Linus Dierheimer 0879746e0f Unified method for testing if c strings contain a value 2022-01-07 18:00:38 +01:00
Linus Dierheimer 3f30fa8d4a Improved WM / DE detection logic 2022-01-07 17:37:57 +01:00
Linus Dierheimer 53f31f2401 Look for different versions of the same library 2022-01-05 16:14:27 +01:00
Linus Dierheimer 59e61f53e1 XCB: set wm protocol if connection succeded 2022-01-05 15:02:54 +01:00
Linus Dierheimer 7a3bf2f77d Rewrote X11 code 2022-01-05 14:40:57 +01:00
Linus Dierheimer bd3a5fde07 Finish rewriting of WM/DE detection 2022-01-02 19:02:21 +01:00
Linus Dierheimer ecb1853d56 Using EWMH to detect WM in X11 sessions 2022-01-02 18:42:58 +01:00
Linus Dierheimer bcf543e129 Unified WM / DE / Resolution detection 2022-01-02 16:55:26 +01:00
Linus Dierheimer 06fa8e7478 Use ffStrbufEndsWithC instead of custom impl 2022-01-01 17:28:41 +01:00
Linus Dierheimer f73137aef8 Better filtering of batteries 2021-12-31 14:12:44 +01:00
Linus Dierheimer d4f753a9f8 Moved detection files to their own folder 2021-12-20 16:34:46 +01:00
Linus Dierheimer 65bac0efc9 Color name mapping 2021-12-20 12:12:55 +01:00
Linus Dierheimer a15b3661c3 Flashfetch: don't print cpu usage and local ip by default 2021-12-19 22:52:16 +01:00
Linus Dierheimer a011b55692 Custom logo colors support 2021-12-19 22:39:22 +01:00
Linus Dierheimer 87eb2659de Create codeql-analysis.yml 2021-12-15 22:18:35 +01:00
Linus Dierheimer 1a7f04fe0c Suppress the output of drivers more efficently 2021-12-15 19:12:07 +01:00
Linus Dierheimer 7638d0b8b6 Disable stdout during vulkan operations 2021-12-15 15:42:33 +01:00
Linus Dierheimer b89e41c1d7 Updated GitHub bug report template 2021-12-13 18:19:12 +01:00
Linus Dierheimer 809f9310a5 Updated DEVELOPMENT.md 2021-12-10 15:58:34 +01:00
Linus Dierheimer 9b7f236397 Update DEVELOPMENT.md 2021-12-09 13:27:36 +01:00
Linus Dierheimer 8f865cf823 Update DEVELOPMENT.md 2021-12-09 12:04:04 +01:00
Linus Dierheimer dedbd5b529 Update DEVELOPMENT.md 2021-12-07 17:46:44 +01:00
Linus Dierheimer 5f178880c7 updated DEVELOPMENT:md 2021-12-07 17:42:57 +01:00
Linus Dierheimer 74b809f68a removed obsolete function 2021-12-07 17:21:25 +01:00
Linus Dierheimer f0732d0056 small cleanup 2021-12-07 17:20:11 +01:00
Linus Dierheimer e6851eeabc Merge branch 'master' of github.com:LinusDierheimer/fastfetch 2021-12-06 20:04:03 +01:00
Linus Dierheimer 9e2d6be31f Removed zsh completion stub 2021-12-06 20:03:53 +01:00
Linus Dierheimer 7f846dadaf Fix --logo option when using not existent file 2021-12-06 20:03:17 +01:00
Linus Dierheimer 79bec5c220 Merge pull request #112 from CarterLi/master
CPU Usage: add new module
2021-11-24 12:17:05 +01:00
Carter Li 8653a881fd CPU Usage: add new module 2021-11-24 19:14:27 +08:00
Linus Dierheimer aaa6f32f15 updated .editorconfig 2021-11-19 12:42:03 +01:00
Linus Dierheimer bbbf00e834 Separator string option 2021-11-18 18:25:24 +01:00
Linus Dierheimer 7bab4ef298 Fix disk space output 2021-11-17 23:22:42 +01:00
Linus Dierheimer 9db9a2c195 Added -Wconversion to compiler options 2021-11-17 18:38:57 +01:00
Linus Dierheimer cb16cd583b Orderd arco linux logo method alphabetically 2021-11-17 12:09:55 +01:00
Linus Dierheimer 2abaf64c12 fix a compiler warning in ffStrbufEnsureCapacity 2021-11-17 12:07:08 +01:00
Linus Dierheimer 4e818c981a Found a better way to fix the warning from pevious commit 2021-11-17 12:00:06 +01:00
Linus Dierheimer 6c4961aaff Fix a wrong compiler warning in io.c 2021-11-17 11:57:50 +01:00
Linus Dierheimer 7db781c508 Fix possible seg fault in ffStrbufAppendVF 2021-11-17 11:41:17 +01:00
Linus Dierheimer 7ced537e59 FFstrbuf is now always null terminated 2021-11-17 10:59:31 +01:00
Linus Dierheimer f0e623c3de Remove unnecesarry strbuf allocation 2021-11-16 13:05:02 +01:00
Linus Dierheimer 40d535ffba Allow 0 length FFstrbuf 2021-11-16 12:54:49 +01:00
Linus Dierheimer 18bfc6bd11 Merge pull request #110 from Rayrsn/master
Add Arco Linux logo support
2021-11-16 10:48:03 +01:00
Rayr 6823b65615 Added Arco Linux logo 2021-11-16 00:19:50 +03:30
Rayr ad347a6083 Update README.md 2021-11-16 00:19:37 +03:30
Rayr fff69ac7e2 Update README.md 2021-11-16 00:18:53 +03:30
Rayr d41d31a6a7 Added Arco Linux logo 2021-11-16 00:18:20 +03:30
Linus Dierheimer 9e6b0b03ea Tell vulkan the right project version 2021-11-13 18:43:33 +01:00
Linus Dierheimer dff3ca58ae GPU: vulkan fallback 2021-11-13 18:25:35 +01:00
Linus Dierheimer fc77e40ea9 Added dwm to list of known window managers 2021-11-13 16:57:59 +01:00
Linus Dierheimer 869aa67d1f Fix font detection on plasma 5.23+ 2021-11-13 16:46:32 +01:00
Linus Dierheimer fd447ab439 Common macros for loading shared libs 2021-11-13 14:10:30 +01:00
Linus Dierheimer 6cdf32dabc Small variants of logos 2021-11-12 01:39:21 +01:00
Linus Dierheimer 1c8eca1010 Logo printing: keep state and config separate 2021-11-11 19:10:52 +01:00
Linus Dierheimer 42b8182428 Added linux mint old logo 2021-11-10 14:03:11 +01:00
Linus Dierheimer e54398f7b3 Fix autocompletion of logos 2021-11-09 16:53:08 +01:00
Linus Dierheimer 27334f77cc Remove outdated section from DEVELOPMENT.md 2021-11-09 15:20:54 +01:00
Linus Dierheimer 6b0343a46c Merge branch 'master' of remote 2021-11-09 15:18:02 +01:00
Linus Dierheimer 214540e7ef Logo revolution 2021-11-09 15:14:25 +01:00
Linus Dierheimer 0c7a415f9b librpm: don't print error messages 2021-11-08 11:50:50 +01:00
Linus Dierheimer 0048c1ca98 Merge pull request #106 from CarterLi/master
Add CentOS logo support
2021-11-08 11:08:06 +01:00
Carter Li df2ad48d13 Logo: add CentOS logo 2021-11-08 17:37:17 +08:00
Carter Li a81f897258 CMakeList: don't require C++ compilers 2021-11-08 10:28:48 +08:00
Linus Dierheimer e9db9ac87a Merge branch 'master' of github.com:LinusDierheimer/fastfetch 2021-11-07 21:36:08 +01:00
Linus Dierheimer a198322d58 logo part 1 2021-11-07 21:35:59 +01:00
Linus Dierheimer 6e6086a8b4 Merge pull request #105 from CarterLi/master
Packages: use the official librpm instead of libsqlite3
2021-11-07 21:23:32 +01:00
李通洲 b3b31d693e Settings: simplify macros and code cleanup 2021-11-06 22:50:52 +08:00
李通洲 f6700f5272 Packages: use the official RPM public API
instead of diving into its implementation detail to count installed rpm packages, which support CentOS
2021-11-06 22:47:51 +08:00
Linus Dierheimer 58ec50fa7f Merge pull request #103 from Linus789/preset-neofetch-fix
Make the preset neofetch more like neofetch
2021-11-03 18:23:05 +01:00
Linus789 15328345da Make the preset neofetch more like neofetch
- os-format: Print architecture without square brackets
- resolution-format: Do not print the refresh rate
- de-format: Use pretty name and print version
- theme-format: Print color scheme without brackets
- memory-format: Use MiB instead of Mib
2021-11-03 17:46:29 +01:00
Linus Dierheimer 0f16bf2698 Merge branch 'master' of github.com:LinusDierheimer/fastfetch 2021-11-01 19:39:05 +01:00
Linus Dierheimer 46ecdd42b6 Development notice 2021-11-01 19:37:45 +01:00
Linus Dierheimer 7663637ea9 Development notice 2021-11-01 19:33:12 +01:00
Linus Dierheimer bf1fbd572a Fix display of long terminal names 2021-11-01 14:47:36 +01:00
Linus Dierheimer e3f0319575 Merge pull request #102 from NBonaparte/master
fix: check if `processName` is NULL/empty to prevent segfaults
2021-11-01 12:32:33 +01:00
NBonaparte f333ede1f7 fix: check if processName is NULL/empty to prevent segfaults 2021-10-31 22:13:29 -07:00
Linus Dierheimer c0584cf465 More options documented in config file 2021-10-31 12:12:51 +01:00
Linus Dierheimer 00c05f14c7 Update bug_report.md 2021-10-25 13:34:43 +02:00
Linus Dierheimer c008f66a76 Fix bug in terminal format 2021-10-25 13:31:25 +02:00
Linus Dierheimer 57377019e6 Verbose and devinfo presets 2021-10-25 13:29:39 +02:00
Linus Dierheimer 2f555df690 Update issue templates 2021-10-24 21:25:10 +02:00
Linus Dierheimer eb6b8778b3 Update issue templates 2021-10-24 21:20:58 +02:00
Linus Dierheimer 09285338c5 Fix possible unassigned variable in memory code 2021-10-24 15:11:17 +02:00
Linus Dierheimer 20dd2fa28f New fedora logo 2021-10-24 14:47:28 +02:00
Linus Dierheimer 7a21f44a3a Exit signal handler 2021-10-23 15:48:24 +02:00
Linus Dierheimer 1df591b46f More informations in default config file 2021-10-23 14:59:11 +02:00
Linus Dierheimer b3ef53ddc9 Store big strings in separate files 2021-10-22 21:53:29 +02:00
Linus Dierheimer e18199e8ec Fix odering of XDG_CONFIG_DIRS 2021-10-22 18:42:14 +02:00
Linus Dierheimer 3923f11a45 Merge pull request #100 from CarterLi/master
Fix double free memory error
2021-10-20 18:48:07 +02:00
李通洲 c223ace5eb Fix double free memory error 2021-10-21 00:41:35 +08:00
Linus Dierheimer 9b399da872 Merge pull request #99 from CarterLi/master
Add Android basic support
2021-10-20 16:31:11 +02:00
李通洲 1a5b1428dc Add Android basic support 2021-10-20 22:27:29 +08:00
Linus Dierheimer 5589293d76 Merge pull request #98 from CarterLi/cmake
CMakeList: cleanup
2021-10-20 15:19:35 +02:00
李通洲 714181175d CMakeList: cleanup
Default CMAKE_BUILD_TYPE to Release, which implies -O3 -DNDEBUG
Don't force building with -O3, which makes debugging more friendly.
Remove option BUILD_DEBUG, which is covered by -DCMAKE_BUILD_TYPE=DEBUG / RelWithDebInfo
2021-10-20 08:13:49 +00:00
Linus Dierheimer fd257ddde9 better FF_UNUSED 2021-10-19 19:34:55 +02:00
Linus Dierheimer 349e5b24fa better support for building without features 2021-10-19 18:44:59 +02:00
Linus Dierheimer 73436824f9 Merge pull request #96 from CarterLi/build
Make it possible to build without optional dependencies
2021-10-19 17:07:25 +02:00
李通洲 e5322cf165 Make it possible to build without optional dependencies 2021-10-19 22:50:17 +08:00
Linus Dierheimer 564ddfc1b6 Merge pull request #95 from CarterLi/master
Add a new module that shows the number of current running processes
2021-10-19 15:07:12 +02:00
李通洲 181dfb5cfc Processes: add new module 2021-10-19 02:25:34 +00:00
Linus Dierheimer 072a4bc412 Merge pull request #94 from yuannan/master
Multithreading make
2021-10-18 16:47:38 +02:00
yuannan 914cd9219d tests multithreading 2021-10-18 02:40:33 +01:00
yuannan dbc4a815c5 updated building instructions 2021-10-18 02:23:02 +01:00
yuannan 5d97436c7d multithreading make 2021-10-18 02:18:42 +01:00
Linus Dierheimer 5f0c5a0351 Merge pull request #91 from CarterLi/master
Fix detecting Windows Terminal on WSL2
2021-10-17 15:17:32 +02:00
李通洲 7dc70b9730 Fix detecting Windows Terminal on WSL2 and SSH env 2021-10-17 21:12:19 +08:00
Linus Dierheimer d288a698b0 Merge pull request #92 from CarterLi/localip
Add new module that shows local IP addresses which are useful with frontend development
2021-10-17 15:09:26 +02:00
李通洲 38e2f8cf3e LocalIp: add new module 2021-10-17 21:05:04 +08:00
Linus Dierheimer 0b3f96610b Merge pull request #93 from CarterLi/wsl2
Better support devices running without desktop environment ( and WSL2 ), fix #83
2021-10-17 14:33:24 +02:00
李通洲 3a649493ea Don't print error: XDG_RUNTIME_DIR not set in the environment. when $XDG_RUNTIME_DIR is not set
According to [the manpage](https://manpages.debian.org/experimental/libwayland-doc/wl_display_connect.3.en.html), $XDG_RUNTIME_DIR must be set before calling `wl_display_connect(NULL)`.
Otherwise `wl_display_connect` will always fail, and print the error message
2021-10-17 18:37:51 +08:00
李通洲 8d7d33882f Always assume TTY when both $XDG_SESSION_TYPE and $WAYLAND_DISPLAY are not set
$TERM can be set to value other than `linux` ( eg xterm, xterm-256color ), just assume TTY when no desktop env is found, which effectively bypasses `getWMDE`
Fix #83
2021-10-17 18:34:11 +08:00
Linus Dierheimer f265307dc4 Prefer process name in terminal output 2021-09-19 14:58:46 +02:00
Linus Dierheimer e02d81d866 Merge pull request #81 from halfhiddencode/patch-1
Update logo.c
2021-07-31 09:52:39 +02:00
halfhiddencode a2742ce60b Update logo.c
correction in garuda logo
2021-07-18 08:16:23 +00:00
Linus Dierheimer 588a68ac4c Better OS output 2021-07-12 19:01:31 +02:00
Linus Dierheimer 0f916bb492 Better ffParseSemver 2021-07-08 20:02:53 +02:00
Linus Dierheimer 9faf664320 Merge pull request #77 from BL4CKH47H4CK3R/patch-1
Beautify Arch Logo
2021-07-06 22:10:02 +02:00
BL4CKH47H4CK3R 7fa971bc86 Beautify Arch Logo 2021-07-06 19:49:27 +00:00
Linus Dierheimer b733bb628d Update README.md 2021-07-06 21:25:12 +02:00
Linus Dierheimer d7356a8323 Cleand up resolution code 2021-07-06 21:15:08 +02:00
Linus Dierheimer 26f4fac62b feature request #75: CelOS logo 2021-07-06 20:36:30 +02:00
Linus Dierheimer 273d4205b5 Xrandr: support for multiple output monitors 2021-07-05 20:59:58 +02:00
Linus Dierheimer 1717b51807 fix compiler warning about unused result of fscanf 2021-07-02 23:07:25 +02:00
Linus Dierheimer 7b5af1b133 Rewrote x11 resolution detection 2021-07-02 22:43:58 +02:00
Linus Dierheimer 45e5546c92 Don't show certain informations in TTY 2021-06-27 21:53:50 +02:00
Linus Dierheimer 8f1b067953 fix resolution in x11 / TTY 2021-06-27 21:43:09 +02:00
Linus Dierheimer ad220546ba small cleanup in cursor code 2021-06-27 21:18:43 +02:00
Linus Dierheimer 78af5cb8a8 better resolution detection 2021-06-27 21:09:18 +02:00
Linus Dierheimer dc2f45626f Merge pull request #72 from DarNCelsius/master
Clean up cursor.c
2021-06-26 19:13:39 +02:00
DarN 4d408f55fa Optimisation 2021-06-26 19:00:15 +02:00
Linus Dierheimer 3f936e084d Merge pull request #71 from DarNCelsius/master
LXQT cursor support
2021-06-26 18:35:02 +02:00
DarN 764d2b7fc9 Refactored config file cursor functions 2021-06-26 18:14:09 +02:00
DarN 11c07d6738 LXQT cursor support 2021-06-26 17:31:50 +02:00
Linus Dierheimer 262a3eb59f remove trailing - in cursor output 2021-06-26 17:08:15 +02:00
Linus Dierheimer 0d09b89016 Merge pull request #70 from DarNCelsius/master
Sanitize custom Plasma theme name
2021-06-26 16:54:49 +02:00
DarN d2156dfc1d Remove fluff 2021-06-26 16:53:43 +02:00
Linus Dierheimer 2c599b5924 prettier cursor output 2021-06-26 16:48:15 +02:00
DarN 3d3216ba91 Added flexible remove logic 2021-06-26 16:47:47 +02:00
DarN d7a8623f90 Merge branch 'LinusDierheimer:master' into master 2021-06-26 16:07:32 +02:00
Linus Dierheimer 582d5b6473 better naming of strbuf methods 2021-06-26 15:34:01 +02:00
Linus Dierheimer 19b71d9ce0 change cursor size from pt to px 2021-06-26 11:43:36 +02:00
Linus Dierheimer a88cb07dad better cursor support 2021-06-26 11:39:42 +02:00
DarN 4c02b8319b Comment edit 2021-06-26 08:40:23 +02:00
DarN ea8c68bab4 Sanitize custom Plasma theme name 2021-06-26 08:14:11 +02:00
Linus Dierheimer d4d9bc90e6 fix cursor detection in xfce 2021-06-20 22:26:06 +02:00
Linus Dierheimer c0cb55ca9a fix ffParsePropFileConfigValues 2021-06-20 22:17:18 +02:00
Linus Dierheimer 8cb3ff7c2b Cursor support 2021-06-20 21:04:55 +02:00
Linus Dierheimer ef31e08d2d Allow same GTK values in same file to overwrite each other 2021-06-17 13:59:18 +02:00
Linus Dierheimer f148dbacfb Disable linewrap and hide cursor by default 2021-06-17 13:49:25 +02:00
Linus Dierheimer 450ac7f2cd simplified host code 2021-06-16 12:20:56 +02:00
Linus Dierheimer 98f420ad3d handle cpu frequencies right 2021-06-16 11:47:32 +02:00
Linus Dierheimer 27915127a8 improved cpu detection 2021-06-16 11:28:33 +02:00
Linus Dierheimer ea2f6470d8 improved host detection 2021-06-16 10:50:16 +02:00
Linus Dierheimer 9ade52ddc0 Added "Default string" to not allowed host values 2021-06-15 18:12:33 +02:00
Linus Dierheimer 409b1f29a6 renamed calculation threads to detection threads 2021-06-15 13:12:29 +02:00
Linus Dierheimer f8c215775a better zsh and fish version detection 2021-06-15 12:35:44 +02:00
Linus Dierheimer 0bf486fa67 seperator is now always the width of the title 2021-06-15 11:04:44 +02:00
Linus Dierheimer 4fecc76083 specific fish version code 2021-06-15 09:51:55 +02:00
Linus Dierheimer 2bae720e00 removed unnecessary code 2021-06-15 09:27:53 +02:00
Linus Dierheimer bfd76ae455 merge 2021-06-15 09:24:14 +02:00
Linus Dierheimer cab5069be2 fix terminal / shell detection 2021-06-15 09:23:37 +02:00
Linus Dierheimer 5aa5ab2c16 fix terminal / exe detection 2021-06-15 09:21:38 +02:00
Linus Dierheimer 5515eea42d merging 2021-06-14 23:22:44 +02:00
Linus Dierheimer eab26149e7 rewrote shell / terminal detection 2021-06-14 23:22:14 +02:00
Linus Dierheimer d77fc9a9c3 Merge pull request #65 from wizard-28/Code-Quality
Improved code quality
2021-06-14 11:44:28 +02:00
Sourajyoti Basak 0108a3ade0 Revert headers 2021-06-14 14:57:45 +05:30
Sourajyoti Basak e79209c7bf Change shebang 2021-06-14 14:22:51 +05:30
Sourajyoti Basak 399eee1963 Change shebang 2021-06-14 14:22:28 +05:30
Sourajyoti Basak 4f2b3d740e Update run.sh 2021-06-14 12:19:21 +05:30
Sourajyoti Basak bc65c9f9c3 Add shebang 2021-06-14 12:18:39 +05:30
Sourajyoti Basak 756593e160 Fix heading increment 2021-06-14 12:12:17 +05:30
Sourajyoti Basak ed3e5b544f Fix heading punctuation 2021-06-14 12:09:18 +05:30
Sourajyoti Basak 7b58c97ff3 Fix list item idents 2021-06-14 12:07:08 +05:30
Linus Dierheimer ab2cb9e0e4 fix gentoo logo name 2021-06-13 14:28:09 +02:00
Linus Dierheimer 889c8570f2 Merge branch 'master' of github.com:LinusDierheimer/fastfetch 2021-06-13 14:24:12 +02:00
Linus Dierheimer 3ce12dfc47 rewrote logo code 2021-06-13 14:23:35 +02:00
Linus Dierheimer 020fdccaaf Merge pull request #63 from wizard-28/grammer
Improve grammer of the configuration file
2021-06-13 14:15:56 +02:00
Sourajyoti Basak f82a4a6d7d Improve grammer of the configuration file 2021-06-13 17:44:34 +05:30
Linus Dierheimer 32f520bf3b Merge pull request #62 from wizard-28/master
Optimize the images using ImgBot
2021-06-12 18:22:21 +02:00
Sourajyoti Basak fc3cfff1d4 Optimize images
[ImgBot] Optimize images
2021-06-12 20:55:30 +05:30
ImgBotApp 36df822ce6 [ImgBot] Optimize images
*Total -- 119.87kb -> 102.20kb (14.74%)

/screenshots/ffmanjaro.png -- 47.30kb -> 38.92kb (17.71%)
/screenshots/example.png -- 72.56kb -> 63.27kb (12.8%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-06-12 15:07:10 +00:00
Linus Dierheimer 4d565cb7ee Fix snap package count 2021-06-10 22:04:25 +02:00
Linus Dierheimer 580ad45459 Fedora support 2021-06-08 20:01:29 +02:00
Linus Dierheimer 9535aa885c battery don't show full status 2021-05-30 21:37:27 +02:00
Linus Dierheimer 6a65803929 prettier battery output 2021-05-30 21:35:04 +02:00
Linus Dierheimer 602c3767e5 Rewrote WM / DE detection 2021-05-30 20:28:15 +02:00
Linus Dierheimer 0b93feaa94 removed debug puts 2021-05-28 14:14:57 +02:00
Linus Dierheimer 49e7612795 fix logo display 2021-05-28 14:10:53 +02:00
Linus Dierheimer 0e8b1fff8e Merge pull request #57 from alborrajo/master
Custom logos read from files
2021-05-28 13:48:03 +02:00
alborrajo e4516dc548 Clean up buffer after loading logo from file 2021-05-28 13:46:33 +02:00
alborrajo 936c478a9a Moved --logo-file bash completions to path options 2021-05-28 13:46:22 +02:00
alborrajo de72ea6ee6 Separate --logo-file option 2021-05-27 17:13:26 +02:00
alborrajo 53389850c2 Faster removal of \n when loading a custom logo 2021-05-27 16:40:51 +02:00
alborrajo a10aa36b72 Revert "Custom logos explanation in readme"
This reverts commit ec82b80f5a.
2021-05-27 15:57:17 +02:00
alborrajo f20ec0ef9f Color support on custom logos 2021-05-27 15:56:11 +02:00
alborrajo b9c6830a14 Merge branch 'master' of https://github.com/LinusDierheimer/fastfetch 2021-05-27 15:48:15 +02:00
Linus Dierheimer 94e4443464 fix --color option 2021-05-27 14:56:31 +02:00
alborrajo ec82b80f5a Custom logos explanation in readme 2021-05-26 19:20:11 +02:00
alborrajo e630167585 Read custom logos from text files
If the --logo option is specified with a path to a file, use the
contents of the file as the logo
2021-05-26 19:01:31 +02:00
Linus Dierheimer 137b3f7010 mutter wm theme fix 2021-05-24 10:46:55 +02:00
Linus Dierheimer 571913ed44 better mutter theme 2021-05-23 21:50:01 +02:00
Linus Dierheimer 76f9da4b78 added missing const 2021-05-23 21:03:11 +02:00
Linus Dierheimer 72fbcf94ff better Mutter detection 2021-05-23 20:35:24 +02:00
Linus Dierheimer 9f7f57dfd8 Better handling of Mutter in not GNOME systems 2021-05-23 19:56:49 +02:00
Linus Dierheimer bd3c236480 a bit better parsing of fonts 2021-05-23 19:40:34 +02:00
Linus Dierheimer 14932466f7 refactored usage of GSettings 2021-05-20 20:06:08 +02:00
Linus Dierheimer dbfa9dcc1f Merge pull request #54 from DarNCelsius/patch-1
Update README.md
2021-05-20 16:06:46 +02:00
DarN cd56317186 Update README.md 2021-05-19 20:08:29 +02:00
DarN 6b40174656 Update README.md
Added Gnome Terminal to Terminal Fonts section
2021-05-19 19:43:16 +02:00
Linus Dierheimer f1c3b5275f Merge pull request #51 from DarNCelsius/master
Refactored terminal font functions
2021-05-18 15:38:34 +02:00
DarN 5dc5ac6d29 Merge branch 'master' of https://github.com/DarNCelsius/fastfetch 2021-05-17 23:04:48 +02:00
DarN 5b69bd77a6 Terminal font refactor 2021-05-17 23:02:27 +02:00
Linus Dierheimer f19a1f40be Merge pull request #50 from DarNCelsius/master
Added terminal font support for Gnome Terminal
2021-05-17 21:28:17 +02:00
DarN 15ed8504bf Syntax change 2021-05-17 14:32:51 +02:00
DarN 4191bd8865 Merge branch 'master' of https://github.com/DarNCelsius/fastfetch 2021-05-17 14:24:15 +02:00
DarN 00c426c98a Added terminal font support for Gnome Terminal 2021-05-17 14:21:55 +02:00
Linus Dierheimer 2bb4407030 Merge pull request #49 from DarNCelsius/master
Enhanced LXQt DE Support
2021-05-17 11:16:11 +02:00
DarN 67edfd7f59 Added more file checks 2021-05-17 11:10:56 +02:00
DarN a1985bd7e8 Update README.md
Added LXQt version
2021-05-17 07:52:16 +02:00
DarN bfc9384ce3 Added Manjaro screenshot 2021-05-17 07:13:09 +02:00
DarN 82674a7d14 Rewrote with fast and slow modes 2021-05-17 06:34:48 +02:00
DarN 0531afadb7 Merge branch 'LinusDierheimer:master' into master 2021-05-17 06:15:10 +02:00
Linus Dierheimer 142d9c30e0 faster ffGetPropValue 2021-05-16 21:02:06 +02:00
Linus Dierheimer c8a9f06b52 added multiline and xml support to ffGetPropValue 2021-05-16 18:41:00 +02:00
Linus Dierheimer 5f71ef71da small syntactic changes 2021-05-16 15:10:15 +02:00
DarN dbde798a56 Added LXQt DE Version info 2021-05-16 14:00:04 +02:00
Linus Dierheimer 8af1cde10b Merge pull request #48 from DarNCelsius/master
Changed ffStrbufRemoveStringsA() iterator logic
2021-05-16 11:24:03 +02:00
DarN 852d6dcdcb Changed ffStrbufRemoveStringsA iterator logic 2021-05-16 07:36:14 +02:00
Linus Dierheimer c49fed470a Merge pull request #45 from DarNCelsius/patch-1
Change order of WMs in README.md
2021-05-15 18:20:48 +02:00
DarN f7e0e70b2f Tweaks
It's an OCD thing. 😀
2021-05-15 18:18:58 +02:00
Linus Dierheimer 44339971bd pass manjaro pacman branch to format string 2021-05-15 18:17:34 +02:00
Linus Dierheimer d12edd02bf Merge pull request #44 from DarNCelsius/master
Added branch detection for Manjaro
2021-05-15 18:12:55 +02:00
DarN 0dbcce84f8 Implemented changes 2021-05-15 18:12:10 +02:00
DarN 0e2deb3534 Rewrote with suggestions 2021-05-15 17:21:02 +02:00
Linus Dierheimer bbb46e7c66 better ffGetPropValue 2021-05-15 14:58:43 +02:00
DarN 25d23b020e Merge branch 'LinusDierheimer:master' into master 2021-05-15 14:52:28 +02:00
Linus Dierheimer 2ee2c68db5 ffGetPropValue trim result 2021-05-15 14:42:14 +02:00
DarN e400ee4490 Tweak 2021-05-15 13:44:16 +02:00
DarN bbd41e6c7a Merge branch 'LinusDierheimer:master' into master 2021-05-15 13:22:34 +02:00
DarN f24f6756b1 Added branch detection for Manjaro 2021-05-15 13:21:59 +02:00
Linus Dierheimer cedde7eced Merge pull request #43 from DarNCelsius/master
Renamed all instances of 'seperator'
2021-05-15 12:44:31 +02:00
DarN ca08e07c37 Renamed all instances of 'seperator' 2021-05-15 12:36:25 +02:00
Linus Dierheimer a72e3fed29 Merge pull request #42 from DarNCelsius/Grammar-fastfetch.c
Typos and grammar
2021-05-15 11:38:19 +02:00
DarN d815f624db Typos and grammar
I only did comments and help text - You might wanna change the "--seperate" option.
```
sep·a·rate
    a. To set, force, or keep apart: The referee separated the two boxers.
    b. To put space between; space apart or scatter: small farms that were separated one from another by miles of open land.
    c. To form a border or barrier between (two areas or groups): A hedge separates the two yards.
    d. To place in different groups; sort: separate mail by postal zones.
```
2021-05-15 08:39:20 +02:00
Linus Dierheimer 635f31eae1 better Host output 2021-05-14 16:03:19 +02:00
Linus Dierheimer 8128db974b updated README.md 2021-05-14 12:58:27 +02:00
Linus Dierheimer a841489280 updated README.md 2021-05-14 12:53:53 +02:00
Linus Dierheimer 0e94658683 updated README.md 2021-05-14 12:47:12 +02:00
Linus Dierheimer 225bcbe89a Mate support 2021-05-14 12:43:54 +02:00
Linus Dierheimer ae01d28094 btw preset 2021-05-14 10:44:41 +02:00
Linus Dierheimer d14a398f17 updated readme 2021-05-13 21:27:24 +02:00
Linus Dierheimer 1e82780de7 updated screenshot 2021-05-13 21:24:03 +02:00
Linus Dierheimer 35681ec09f flashfetch logo detection at compile time 2021-05-13 17:39:08 +02:00
Linus Dierheimer 0940064759 Merge pull request #40 from DarNCelsius/patch-3
Fixed dconf library load snafu
2021-05-13 14:09:05 +02:00
DarN 959750de3d Fixed dconf library load snafu
Typo :)
2021-05-13 13:53:29 +02:00
Linus Dierheimer 9e597aa253 fix ffGetPropValue 2021-05-13 12:48:00 +02:00
Linus Dierheimer 4d7a06ac97 ffGetPropValue 2021-05-13 12:27:44 +02:00
Linus Dierheimer f8868ae3c4 ffPrintError don't print regexes 2021-05-12 20:17:16 +02:00
Linus Dierheimer 7afc27e52f small syntactic changes 2021-05-12 20:13:56 +02:00
Linus Dierheimer 1ece13c36f Merge pull request #36 from DarNCelsius/master
Added system font recognition for xfce-terminal
2021-05-12 19:54:56 +02:00
DarN c549c7c868 Rewrote function with XFConf 2021-05-11 22:28:10 +02:00
DarN cac0c27807 Merge branch 'LinusDierheimer:master' into master 2021-05-11 20:18:05 +02:00
Linus Dierheimer 039b69f4a7 better ffSettings* functions 2021-05-11 20:06:17 +02:00
Linus Dierheimer 97ab892051 renamed printWMThemeFromConfig to keep consistency 2021-05-09 22:09:01 +02:00
Linus Dierheimer 6cfe826cd7 replaced wrong format string 2021-05-09 22:04:18 +02:00
Linus Dierheimer e89b326d57 basic XFCE support 2021-05-09 21:41:17 +02:00
DarN ef3f4d9fa8 Tweaks
Hopefully an improvement
2021-05-09 20:39:00 +02:00
DarN 89187e9758 Removed auto include 2021-05-09 14:07:20 +02:00
DarN cf4b3c54cf Added check for manual edits 2021-05-09 14:05:57 +02:00
DarN 719a5c999d Added system font recognition for xfce-terminal 2021-05-09 13:40:17 +02:00
Linus Dierheimer 7432fc7a19 fixed WM::getFromProcDir 2021-05-09 12:56:07 +02:00
Linus Dierheimer ed2b72e0cf fixed unnecessary format string evaluation in ffPrintError 2021-05-09 12:39:44 +02:00
Linus Dierheimer b5454c28bf unified some terminal font methods 2021-05-09 12:30:10 +02:00
Linus Dierheimer fc314e6ded Merge pull request #35 from DarNCelsius/master
Added Terminal Font for lxterminal
2021-05-09 12:24:34 +02:00
DarN 479188ec3e Added lxterminal 2021-05-09 09:04:31 +02:00
DarN 70c1b477d0 Added Terminal Font for lxterminal 2021-05-09 09:02:56 +02:00
DarN 0e236c7d6a Cleanup/Note on future changes 2021-05-09 08:03:09 +02:00
Linus Dierheimer cdf82e91b2 Merge pull request #33 from DarNCelsius/patch-2
Update README.md
2021-05-08 22:04:05 +02:00
DarN 476988f695 Update README.md
Tilix, grammar
2021-05-08 22:02:50 +02:00
Linus Dierheimer 123d19bb69 Merge pull request #32 from DarNCelsius/patch-1
Added Terminal Font for Tilix
2021-05-08 21:31:37 +02:00
DarN 099ddeea01 Tweaks
Are we there yet? :D
2021-05-08 21:26:01 +02:00
DarN eac718085e Implemented suggestions
Much better.
2021-05-08 21:17:04 +02:00
Linus Dierheimer 30953a31dc small changes to memory managment 2021-05-08 21:13:58 +02:00
DarN 4f83dc8396 Added Terminal Font for Tilix
This adds Terminal Font recognition for Tilix's default profile. I can't find a way to detect which profile is the active one.
2021-05-08 20:31:18 +02:00
Linus Dierheimer dfeb5c49e1 updated FFvariant 2021-05-08 18:58:40 +02:00
Linus Dierheimer b054fec8d8 updated README 2021-05-08 18:35:51 +02:00
Linus Dierheimer c8cd51da84 variant for dconf 2021-05-08 17:56:56 +02:00
Linus Dierheimer a6cd29057b Merge pull request #31 from DarNCelsius/patch-4
Grammar
2021-05-08 17:21:13 +02:00
Linus Dierheimer 62f1c5d059 dont show gpu index on single gpu systems 2021-05-08 17:18:59 +02:00
DarN e249c1590a Grammar
I couldn't help myself. 😁
2021-05-08 17:13:26 +02:00
Linus Dierheimer e4a61c3572 updated initCacheDir 2021-05-08 16:56:37 +02:00
Linus Dierheimer c749763f05 updated ffAppendFDContent 2021-05-08 16:24:57 +02:00
Linus Dierheimer bd316496a2 better handling of cache directory 2021-05-08 12:14:55 +02:00
Linus Dierheimer 79810542af Merge pull request #29 from DarNCelsius/patch-2
Added .cache folder
2021-05-08 11:48:33 +02:00
DarN f09aae6541 Added .cache folder 2021-05-08 11:37:50 +02:00
Linus Dierheimer 7609a24778 Cinnamon support 2021-05-08 11:20:17 +02:00
Linus Dierheimer eea74b085b prettier GPU output 2021-05-07 17:10:22 +02:00
Linus Dierheimer 4e49416394 fixed DE output when SESSION_DESKTOP == WM 2021-05-07 16:16:23 +02:00
Linus Dierheimer a389b89ff0 supprt for custom / multiple config dirs 2021-05-07 14:55:43 +02:00
Linus Dierheimer ebc42b8f8b default KWin theme 2021-05-07 11:02:09 +02:00
Linus Dierheimer ec941bcb33 improoved GTK detection 2021-05-07 10:34:30 +02:00
Linus Dierheimer 91b7967677 fixed battery status 2021-05-05 21:29:09 +02:00
Linus Dierheimer cb61fde75d fixed X- truncating on DE output number 2 2021-05-05 21:26:42 +02:00
Linus Dierheimer 20322c4fe4 fixed X- truncating on DE output 2021-05-05 21:18:29 +02:00
Linus Dierheimer a7784f5a88 better os output 2021-05-05 21:12:42 +02:00
Linus Dierheimer 89aa4470aa support for gsettings bool value 2021-05-05 20:20:41 +02:00
Linus Dierheimer 14d3e3b1b3 dont show unknown battery status 2021-05-05 19:52:50 +02:00
Linus Dierheimer bc870197d7 remove X- from DE names 2021-05-05 19:49:40 +02:00
Linus Dierheimer 9c26f9d988 fix help gpu-format 2021-05-05 19:31:58 +02:00
Linus Dierheimer 7da0c76703 fixed ffSettingsGetDConf 2021-05-04 21:25:50 +02:00
Linus Dierheimer 7ec32bf5d3 ffSettingsGet* 2021-05-04 19:48:23 +02:00
Linus Dierheimer bd2e1da3b4 small fixes 2021-05-02 21:45:47 +02:00
Linus Dierheimer 88d2530460 better transforming from gsettings to dconf 2021-05-02 21:30:05 +02:00
Linus Dierheimer 286095a09a transform GSettings path to dconf key style 2021-05-02 21:12:03 +02:00
Linus Dierheimer f47a44f47a added ffGSettingsGetValuePath 2021-05-02 21:06:01 +02:00
Linus Dierheimer 35504b33b1 ffGSettingsGetValue 2021-05-02 18:52:42 +02:00
Linus Dierheimer 56c0e9d068 Merge pull request #26 from DarNCelsius/patch-1
Removed leftover puts()
2021-05-02 08:36:28 +02:00
DarN 46306d913f Removed leftover puts() 2021-05-02 05:26:46 +02:00
Linus Dierheimer 6917164271 updated ffDConfGetValue 2021-05-01 21:55:15 +02:00
Linus Dierheimer 39f0f9c483 updated README 2021-05-01 21:31:03 +02:00
Linus Dierheimer fb17273ec4 close stderr of child process 2021-05-01 21:27:46 +02:00
Linus Dierheimer 85c7f4c772 basic gnome support 2021-05-01 21:22:07 +02:00
Linus Dierheimer 6e8e2e6c47 fixed dconf bug #2 2021-05-01 17:25:22 +02:00
Linus Dierheimer 97ae9e9d28 fixed dconf bug 2021-05-01 17:19:17 +02:00
Linus Dierheimer 94caa86998 TTY improvements 2021-05-01 16:54:59 +02:00
Linus Dierheimer e66e78fcc5 removed wrong paths used for testing 2021-05-01 16:06:12 +02:00
Linus Dierheimer 6eddc6105f ffDconfGetValue 2021-05-01 16:01:59 +02:00
Linus Dierheimer 671842d316 merge branch 2021-05-01 13:04:15 +02:00
Linus Dierheimer f3c8ecacbb greatly improved WM / DE detection 2021-05-01 13:03:30 +02:00
Linus Dierheimer 2618465121 Merge pull request #24 from DarNCelsius/patch-1
Update README.md
2021-04-29 15:47:19 +02:00
DarN 7f4dba8856 Update README.md
Typos, grammar and double Muffin
2021-04-29 15:34:55 +02:00
Linus Dierheimer e3f3699b26 improved WM detection 2021-04-28 21:48:23 +02:00
Linus Dierheimer c3f7ed5247 merged changes 2021-04-28 19:54:03 +02:00
Linus Dierheimer 01ada03c5c renamed calculate to detect 2021-04-28 19:53:30 +02:00
Linus Dierheimer 5c93d70341 Merge pull request #23 from DarNCelsius/patch-3
Updated help documentation
2021-04-28 19:52:01 +02:00
Linus Dierheimer bbeddbe403 Merge pull request #22 from DarNCelsius/patch-2
Added Package Manager support for Void Linux
2021-04-28 19:51:49 +02:00
DarN 644843a70d Implemented corrections 2021-04-28 19:50:04 +02:00
DarN d403c035d0 Updated help documentation
Quick grammar corrections as a bonus ;)
2021-04-28 19:45:43 +02:00
DarN dd31e05fde Update packages.c 2021-04-28 16:05:06 +02:00
DarN 99849ccd77 Debloat
...
2021-04-28 07:10:55 +02:00
DarN 4674d6c2fc Removed useless enum 2021-04-28 06:59:44 +02:00
DarN f938847459 Added missing else
No need to compare twice if the first one checks out...
2021-04-28 06:46:20 +02:00
DarN f4d2b35260 Added Package Manager support for Void Linux
This adds support for xbps.
2021-04-27 21:52:55 +02:00
Linus Dierheimer b83499d1ec updated readme 2021-04-26 21:56:21 +02:00
Linus Dierheimer 2449681f30 Merge pull request #21 from DarNCelsius/patch-1
Added additional WM Theme support
2021-04-26 21:50:13 +02:00
DarN dd709461a5 Small correction
Something is adding this include automatically...
2021-04-26 21:49:12 +02:00
DarN dbc576862e Made corrections
Something like this?
2021-04-26 21:46:57 +02:00
DarN a4d4b66679 Implemented suggested changes 2021-04-26 20:47:32 +02:00
DarN 6bcf3bab7c Cleaned up whitespace 2021-04-26 17:39:05 +02:00
DarN 41dc49f170 Update wmtheme.c 2021-04-26 17:27:12 +02:00
Linus Dierheimer 2a29d3ed17 better handling of WM / DE 2021-04-25 21:32:35 +02:00
DarN 6c612cbd9a Added additional WM Theme support
Supports LXQt, LXDE and Openbox
2021-04-25 21:18:56 +02:00
Linus Dierheimer e9cf1c0252 fixed a bug in ffStrbufTrimRight 2021-04-25 19:12:05 +02:00
Linus Dierheimer 7da4e5b69a ffStrbufTrimLeft now sets a null byte at the end 2021-04-25 19:03:59 +02:00
Linus Dierheimer ffd5b45b2c added support status to README.md 2021-04-25 16:16:40 +02:00
Linus Dierheimer 843a9811d0 paleofetch preset 2021-04-25 15:22:35 +02:00
Linus Dierheimer 6b066911dd garuda logo support 2021-04-25 15:07:31 +02:00
Linus Dierheimer f4a691ebd6 neofetch preset 2021-04-25 14:21:28 +02:00
Linus Dierheimer ec76f1c2d9 updated readme 2021-04-25 12:31:39 +02:00
Linus Dierheimer dffbd4f0f0 fixed a compiler warning 2021-04-25 11:56:01 +02:00
Linus Dierheimer f7b6fb2d4f presets 2021-04-25 11:48:25 +02:00
Linus Dierheimer 1b1d28f0ab color in format strings 2021-04-25 09:13:18 +02:00
Linus Dierheimer d469120e9b format string more like template language 2021-04-24 21:42:48 +02:00
Linus Dierheimer 36a4d095ed better handling of custom disk folders string 2021-04-24 16:29:05 +02:00
Linus Dierheimer ad122e9569 better handling of custom battery dir 2021-04-24 14:37:40 +02:00
Linus Dierheimer fc1c17932e fixed battery key index on multi battery systems 2021-04-24 14:25:27 +02:00
Linus Dierheimer 2ba1fb0868 typo in battery 2021-04-24 13:17:30 +02:00
Linus Dierheimer 49e112eeae revert stupid revert 2021-04-24 13:12:40 +02:00
Linus Dierheimer c5c3cecb93 support for more than 5 batteries 2021-04-24 13:05:43 +02:00
Linus Dierheimer 025dbecc4f Merge pull request #17 from DarNCelsius/patch-2
Added support for additional window managers
2021-04-24 11:58:04 +02:00
DarN 85c49f6873 Fixed cinnamon wm name... 2021-04-24 10:39:53 +02:00
DarN adce9fc53d Added support for additional window managers
Added support for Openbox, Cinnamon, XFCE and Wayfire window managers.
2021-04-24 10:25:39 +02:00
Linus Dierheimer c697f8ee80 print 144 instead of 145 refresh rate as this rate is more common 2021-04-23 21:13:50 +02:00
Linus Dierheimer eddc01290a Small improvements to host output 2021-04-23 20:59:13 +02:00
Linus Dierheimer 283e4f56e9 added missing mutex unlocks 2021-04-23 20:44:52 +02:00
Linus Dierheimer e28126f40e better result handling of calculation functions 2021-04-22 21:41:42 +02:00
Linus Dierheimer c12484ec6b Merge pull request #13 from DarNCelsius/patch-1
Added support for Void Linux
2021-04-21 19:50:34 +02:00
DarN b1df0ef98a Added support for Void Linux 2021-04-21 19:45:57 +02:00
Linus Dierheimer a64dc74923 updated screenshot 2021-04-18 22:51:55 +02:00
Linus Dierheimer 3e9ce54f63 --nocache option added 2021-04-18 19:23:54 +02:00
Linus Dierheimer 1d47eb0c60 shell version support 2021-04-18 15:19:21 +02:00
Linus Dierheimer 3e9678c899 small x11 improovement 2021-04-17 15:07:53 +02:00
Linus Dierheimer 4e54117238 better cpu name prettifier 2021-04-17 14:37:32 +02:00
Linus Dierheimer 44ddfc2a03 updated README.md 2021-04-17 12:21:59 +02:00
Linus Dierheimer eae92b6306 Merge pull request #12 from DarNCelsius/master
Thanks for the PR.
2021-04-17 12:03:23 +02:00
DarNCelsius b7c0c18497 Merge pull request #1 from DarNCelsius/DarNCelsius-patch-1
Update logo.c
2021-04-17 11:29:19 +02:00
DarNCelsius 61188b68a4 Update logo.c
Added Manjaro to known logos
2021-04-17 11:25:38 +02:00
Linus Dierheimer 51c38a2b01 terminalfont now gives an error on unknown terminals instead of printing TTY values 2021-04-16 20:44:46 +02:00
Linus Dierheimer 7b65316483 global results are now stored in state 2021-04-16 20:22:38 +02:00
Linus Dierheimer 86abc05cd4 removed obsolete recache condition 2021-04-16 17:51:17 +02:00
Linus Dierheimer 1d878215ae unified FFformatarg printing 2021-04-16 17:46:50 +02:00
Linus Dierheimer a145700500 wayland support 2021-04-16 16:49:33 +02:00
Linus Dierheimer f27f08a6e2 updated screenshot 2021-04-15 19:22:19 +02:00
Linus Dierheimer 9bc1f2a116 much more efficient caching 2021-04-15 18:59:26 +02:00
Linus Dierheimer ed598eaa35 updated readme 2021-04-09 19:48:16 +02:00
Linus Dierheimer 0ed54eddc4 plasma color theme detection 2021-04-09 19:21:56 +02:00
Linus Dierheimer 2b55a14c16 GTK dconf backend doesn't append already set values anymore 2021-04-09 16:11:53 +02:00
Linus Dierheimer 53c781aa21 project structure refactoring 2021-04-09 14:07:05 +02:00
Linus Dierheimer 0649b6e755 format once again works for theme/icons/font 2021-04-07 21:07:32 +02:00
Linus Dierheimer 9321241830 always unlock mutex on errors 2021-04-07 18:46:51 +02:00
Linus Dierheimer c698080581 basic multithreading 2021-04-07 17:36:03 +02:00
Linus Dierheimer ed7e186f4b faster plasma detection 2021-04-07 12:55:11 +02:00
Linus Dierheimer 5f23f2b027 small refactoring 2021-04-06 11:36:05 +02:00
Linus Dierheimer c543bcc29f optional gtk detection via dconf 2021-04-05 23:42:07 +02:00
Linus Dierheimer de312adb52 better gtk detection 2021-04-05 19:25:34 +02:00
Linus Dierheimer af430bd405 fixed a bug in battery where manufacturer was printed twice instead of model 2021-04-03 23:25:01 +02:00
Linus Dierheimer 1086c059b6 updated screenshot 2021-04-03 23:12:59 +02:00
Linus Dierheimer f25bca3dab wm theme detection for kde plasma 2021-04-03 23:10:14 +02:00
Linus Dierheimer e01d03f98d dont use format string if an error occures 2021-04-03 21:51:50 +02:00
Linus Dierheimer 67465d266e more efficient memory usage 2021-04-03 12:14:25 +02:00
Linus Dierheimer 08066a76f0 all keys can now be set! 2021-04-03 11:39:59 +02:00
Linus Dierheimer a8821cf6f0 some terminal improvements 2021-04-02 16:21:52 +02:00
Linus Dierheimer 9c26a76c9d WM is once again shown in a plasma session 2021-03-28 20:36:49 +02:00
Linus Dierheimer 307fc61184 better structure looping 2021-03-28 20:09:29 +02:00
Linus Dierheimer c44bd6dbc1 better disk output 2021-03-28 15:38:52 +02:00
Linus Dierheimer 184eafce20 better cpu output 2021-03-28 13:02:40 +02:00
Linus Dierheimer efc0322ed1 print default structure / modules 2021-03-27 19:26:58 +01:00
Linus Dierheimer b3c3614b6c fixed a bug in os module with version codename 2021-03-27 18:39:13 +01:00
Linus Dierheimer 693f8a33db fixed a bug in os module where it would show apostophes around values 2021-03-27 18:38:00 +01:00
Linus Dierheimer 44549e32d9 removed remaining strcat 2021-03-27 18:35:30 +01:00
Linus Dierheimer a1e90ba35c editorconfig 2021-03-27 18:14:19 +01:00
Linus Dierheimer 46ed1b34cb much faster reading of large files 2021-03-27 17:55:16 +01:00
Linus Dierheimer 23db257af9 much more values in os-format 2021-03-27 17:48:32 +01:00
Linus Dierheimer beb2e9608c dont print line if format evaluates to an empty string 2021-03-27 15:39:56 +01:00
Linus Dierheimer 055804a055 removed ffTrimTrailingWhitespace 2021-03-27 14:22:08 +01:00
Linus Dierheimer f61ffdb149 removed ffStrbufIsEmpty 2021-03-23 19:48:52 +01:00
Linus Dierheimer a0af3b0eb6 ffStrbufPutTo 2021-03-22 20:02:32 +01:00
Linus Dierheimer 2021709d72 if host version is None, dont show it 2021-03-22 19:52:59 +01:00
Linus Dierheimer 4d736fd5d0 updated .gitignore 2021-03-22 19:16:45 +01:00
Linus Dierheimer 02c450c218 rewinding file descriptor before trying reading again 2021-03-22 19:15:51 +01:00
Linus Dierheimer 071b2c6727 print logo in ffPrintAndSaveCachedValue 2021-03-21 20:46:20 +01:00
Linus Dierheimer 2004544528 using strbuf for loading/saving cached values 2021-03-21 20:42:07 +01:00
Linus Dierheimer c783bb9d97 renamed spacer to spacing 2021-03-21 17:02:31 +01:00
Linus Dierheimer 101d19a015 ffGetFileContent now uses FFstrbuf for preventing buffer overflows 2021-03-21 16:52:17 +01:00
Linus Dierheimer 0d3500c66f command help 2021-03-21 11:18:06 +01:00
Linus Dierheimer a848798e97 Merge pull request #7 from saloniamatteo/master
Locale: implement /etc/locale.conf fallbacks
2021-03-21 09:58:49 +01:00
saloniamatteo 9d5481e83a Sane indentation: use tabs instead of spaces 2021-03-20 11:24:35 +01:00
saloniamatteo 05fe281141 Fully implement LANG fallbacks 2021-03-20 11:20:37 +01:00
saloniamatteo 3d27ecaba8 Free pointer to file 2021-03-20 10:47:49 +01:00
saloniamatteo e70f06087c Merge branch 'master' of github.com:saloniamatteo/fastfetch 2021-03-20 10:47:18 +01:00
saloniamatteo e2986b198f Free pointer to file 2021-03-20 10:46:56 +01:00
Salonia Matteo e479b62c73 Merge pull request #2 from LinusDierheimer/master
Sync from upstream pt. 2
2021-03-20 09:45:58 +00:00
saloniamatteo e531278e62 Experimental: use fallback for LANG 2021-03-20 10:39:05 +01:00
Salonia Matteo a8ca2ce825 Sync from upstream 2021-03-20 07:09:02 +00:00
Linus Dierheimer 92b1247683 print remaining logo by default 2021-03-19 21:23:15 +01:00
Linus Dierheimer dd19fcfded Merge pull request #4 from saloniamatteo/master
Thanks for this contribution. Note that i dont test on Ubuntu / Debian (yet), so probably a lot of stuff wont work on those platforms.
2021-03-19 21:03:23 +01:00
Linus Dierheimer db35000e39 Merge branch 'master' into master 2021-03-19 21:02:23 +01:00
Linus Dierheimer 1ceb5c91ac format options for every output! 2021-03-19 20:57:07 +01:00
Matteo cddfc083c3 Merge branch 'master' into master 2021-03-19 18:27:03 +00:00
saloniamatteo f358e07b84 Update logo list 2021-03-19 19:21:22 +01:00
saloniamatteo 74b315a1d1 Update logo list 2021-03-19 19:21:05 +01:00
saloniamatteo 9d28df2922 Sync with upstream 2021-03-19 19:19:46 +01:00
saloniamatteo 016acb8e9e src/logo.c: Fix potential security flaw when using printf
Warning: Format string is not a literal string, when using printf
without "%s" (eg. printf(something), instead of printf("%s", something))
2021-03-19 19:12:52 +01:00
saloniamatteo e53f84bb27 Add debian logo 2021-03-19 19:09:14 +01:00
saloniamatteo fd3215ed3b Add debian logo 2021-03-19 19:08:46 +01:00
saloniamatteo be9272c2e9 Add ubuntu logo 2021-03-19 18:45:30 +01:00
saloniamatteo eb7ce05193 Fix logo spacing 2021-03-19 17:56:30 +01:00
Linus Dierheimer 5c6bbbf677 fixed artix logo width 2021-03-19 17:53:48 +01:00
Linus Dierheimer cf9af7023f fixed bug in artix logo 2021-03-19 17:51:52 +01:00
Linus Dierheimer 370de39169 Merge pull request #3 from saloniamatteo/master
Add support for Artix
2021-03-19 17:48:34 +01:00
saloniamatteo 05f11c6c69 Add support for Artix, fix code block in README 2021-03-19 08:43:29 +01:00
saloniamatteo a0169d83e5 Add support for Artix 2021-03-19 08:39:27 +01:00
Linus Dierheimer 37dd06590a --host-format 2021-03-12 12:16:41 +01:00
Linus Dierheimer 8e9cb079b0 new screenshot 2021-03-12 10:31:39 +01:00
Linus Dierheimer a6c153da95 --os-format 2021-03-12 10:17:49 +01:00
Linus Dierheimer b9c7324aa5 resolution options 2021-03-11 22:15:48 +01:00
Linus Dierheimer c988777c79 small changes to package options 2021-03-11 19:54:34 +01:00
Linus Dierheimer 0169507489 packages options 2021-03-11 19:40:46 +01:00
Linus Dierheimer 33be85c4bc terminal font 2021-03-11 17:21:18 +01:00
Linus Dierheimer abd797a0bf added .gitattributes 2021-03-07 20:56:01 +01:00
Linus Dierheimer aa5b5f3caf kernel options 2021-03-07 20:35:17 +01:00
Linus Dierheimer 6cc99423c9 performance tests 2021-03-07 20:05:00 +01:00
Linus Dierheimer 43022c0129 updated README.md 2021-03-07 13:25:32 +01:00
Linus Dierheimer 6e11609432 some content in README.md 2021-03-07 13:12:45 +01:00
Linus Dierheimer 2240264d02 --host-version 2021-03-07 11:58:24 +01:00
Linus Dierheimer dbeaa342ba bash completion improvements 2021-03-07 11:11:21 +01:00
Linus Dierheimer 16af140f7a basic bash completion 2021-03-06 22:40:05 +01:00
Linus Dierheimer 923faa63e5 --os-architecture option 2021-03-06 11:28:38 +01:00
Linus Dierheimer bc13b51b5a support for --shell-path 2021-03-05 23:26:05 +01:00
Linus Dierheimer 9122daa63a seperator support 2021-03-05 23:00:28 +01:00
Linus Dierheimer 61b369ea19 version information in config file 2021-03-05 21:49:13 +01:00
Linus Dierheimer 22cfe4df1f basic Window Manager detection 2021-03-05 21:38:35 +01:00
Linus Dierheimer f65105d181 battery-format 2021-03-04 20:49:12 +01:00
Linus Dierheimer f67a5f3879 prettier default config 2021-03-03 21:42:40 +01:00
Linus Dierheimer c1fb9525b8 battery options 2021-03-03 21:03:12 +01:00
Linus Dierheimer b95dd4ce17 fixed a missing newline in help output 2021-03-03 20:05:21 +01:00
Linus Dierheimer db6d2eb3a3 structure and values can be set in fastfetch via command line and file! 2021-03-03 19:59:39 +01:00
Linus Dierheimer 4dffbb0bb7 battery rework 2021-03-01 19:53:20 +01:00
Linus Dierheimer 7248daaa48 loading pci libs dynamically 2021-02-28 17:38:26 +01:00
Linus Dierheimer 0617b597f9 flashfetch loads default values too now 2021-02-28 17:10:12 +01:00
Linus Dierheimer e06aab6836 use strcasecmp in some cases 2021-02-27 21:10:27 +01:00
Linus Dierheimer d1cec7d1e5 trim trailing whitespaces from keys 2021-02-27 21:04:50 +01:00
Linus Dierheimer 56751e4581 you can now load default values from files 2021-02-27 20:50:13 +01:00
Linus Dierheimer d3db1066e2 reading structure from file 2021-02-27 20:31:54 +01:00
Linus Dierheimer 0c3a562ec6 minor refactoring 2021-02-27 19:22:28 +01:00
Linus Dierheimer 1cda1eb653 refactored internal state keeping 2021-02-27 18:30:05 +01:00
Linus Dierheimer b44ab16dd1 refactored project structure 2021-02-27 16:40:05 +01:00
Linus Dierheimer 558639086a error handling in resolution.c 2021-02-27 14:46:31 +01:00
Linus Dierheimer 48a436ee78 reworked resolution 2021-02-27 14:40:03 +01:00
Linus Dierheimer 16239be51c no color options 2021-02-25 18:38:00 +01:00
Linus Dierheimer 3d902c1c86 removed testing line from CMakeLists.txt 2021-02-22 20:54:05 +01:00
Linus Dierheimer 0ec63d64fa fixed a bug in caching 2021-02-22 20:53:12 +01:00
Linus Dierheimer fea8e371e4 --version implementation 2021-02-22 20:36:25 +01:00
Linus Dierheimer 7cbe1c2359 increased possible input size for color 2021-02-22 19:34:34 +01:00
Linus Dierheimer 4b4f92eb74 fixed some compiler warnings 2021-02-22 17:14:46 +01:00
Linus Dierheimer df3dcb7ede caching of some values 2021-02-22 16:52:51 +01:00
Linus Dierheimer f14e84606f fixed a bug where the program would coredump on uptime < 60s 2021-02-22 14:01:15 +01:00
Linus Dierheimer 85bcf00cf8 cleaned main up 2021-02-22 13:45:14 +01:00
Linus Dierheimer 11b5d6e138 uint16_t for seperator, for those madmans out ther 2021-02-20 20:12:57 +01:00
Linus Dierheimer e3616d8b72 the seperator can now be set 2021-02-20 20:00:06 +01:00
Linus Dierheimer 61bac93809 setting color works 2021-02-20 19:32:57 +01:00
Linus Dierheimer 5d32a782a2 check if showErrors in ffPrintError 2021-02-20 19:08:23 +01:00
Linus Dierheimer 626badd3c8 fixed a bug where color is not set 2021-02-19 18:07:46 +01:00
Linus Dierheimer 12ed7a9812 logo system reqork 2021-02-19 18:02:36 +01:00
Linus Dierheimer f533767569 removed obsolete ffRead functions 2021-02-19 16:38:54 +01:00
Linus Dierheimer a8f165e5c9 better error handling 2021-02-19 16:23:41 +01:00
Linus Dierheimer c5a9b9d428 fixed a bug that caused segfault with -O3 2021-02-19 10:07:00 +01:00
Linus Dierheimer 41120ef582 not ignoring the result of fscanf 2021-02-18 23:04:31 +01:00
Linus Dierheimer 4b2c4a8821 added LICENSE and README 2021-02-18 22:30:16 +01:00
Linus Dierheimer ebb14afb26 initial work 2021-02-18 22:25:36 +01:00
Linus Dierheimer da5ab1c187 Initial commit 2021-02-18 22:25:20 +01:00
111 changed files with 13338 additions and 67752 deletions
+17
View File
@@ -0,0 +1,17 @@
root = true
[*]
end_of_line = lf
indent_style = space
tab_width = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.{md,fflogo}]
trim_trailing_whitespace = false
[*.{txt,fflogo}]
insert_final_newline = false
[*.yml]
tab_width = 2
+2
View File
@@ -0,0 +1,2 @@
*.h linguist-language=c
*.c linguist-language=c
+1
View File
@@ -0,0 +1 @@
custom: paypal.me/linusdierheimer
+35
View File
@@ -0,0 +1,35 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
Output of `fastfetch --version`:
```
//paste here
```
Output of `fastfetch --load-config devinfo`:
```
//paste here
```
Output of `fastfetch --load-config devinfo-verbose`:
```
//paste here
```
Output of `fastfetch --list-features`:
```
//paste here
```
If you get the following error: `Error: couldn't find config: [...]`, copy the files in [presets](../../presets/) to `/usr/share/fastfetch/presets/` or `~/.local/share/fastfetch/presets/`.
If this isn't possible (or to much work) for you, post the output of `fastfetch --show-errors --recache --multithreading false --disable-linewrap false`.
Often helpful questions:
* Does the issue occur across multiple terminal emulators?
* Does the issue occur across multiple shells? (bash, zsh, fish, etc)
+14
View File
@@ -0,0 +1,14 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
# Current state:
# Wanted state:
# Why the change is sensible:
+101
View File
@@ -0,0 +1,101 @@
name: Manage pushes and pull requests
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
BUILD_TYPE: Release
jobs:
check:
name: Build, Run and Analyze fastfetch
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Update and install packages
run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libxfconf-0-dev librpm-dev libzstd-dev
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Configure
run: cmake -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} .
- name: Build
run: cmake --build . -j$(nproc)
- name: Run fastfetch
run: ./fastfetch --recache --disable-linewrap false --hide-cursor false --show-errors true
- name: Run flashfetch
run: ./flashfetch
- name: Run tests
run: ctest
- name: Build deb package
run: sh packaging/deb/create.sh .
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Create tarball
if: github.event_name == 'push'
run: tar -cvf fastfetch.tar ./fastfetch -C packaging/deb ./fastfetch.deb
- name: Upload tarball
if: github.event_name == 'push'
uses: actions/upload-artifact@v3
with:
name: fastfetch.tar
path: ./fastfetch.tar
deploy:
name: Create GitHub release
runs-on: ubuntu-latest
needs: check
if: github.event_name == 'push'
permissions:
contents: write
steps:
- name: Download tarball
uses: actions/download-artifact@v3
with:
name: fastfetch.tar
path: ./fastfetch.tar
- name: Extract tarball
run: tar --overwrite -xvf ./fastfetch.tar/fastfetch.tar
- name: Get fastfetch version
id: get_version_fastfetch
run: echo "::set-output name=release::$(./fastfetch --version-raw)"
- name: Get latest release version
id: get_version_release
uses: pozetroninc/github-action-get-latest-release@master
with:
repository: ${{ github.repository }}
- name: Push release
if: steps.get_version_fastfetch.outputs.release != steps.get_version_release.outputs.release
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.get_version_fastfetch.outputs.release }}
commit: ${{ github.sha }}
artifacts: ./fastfetch,./fastfetch.deb
+9 -3
View File
@@ -1,3 +1,9 @@
/.cache
/.vscode
/build*
**/build/
**/.vscode/
**/.cache/
**/.kdev4/
cscope.*
tags
fastfetch.kdev4
**/*.deb
packaging/deb/fastfetch
+3
View File
@@ -0,0 +1,3 @@
[submodule "packaging/aur"]
path = packaging/aur
url = https://aur.archlinux.org/fastfetch-git.git
+312
View File
@@ -0,0 +1,312 @@
cmake_minimum_required(VERSION 3.1.0) # Threads::Threads
project(fastfetch
VERSION 1.1.1
LANGUAGES C
)
#e.g. +1
#This allows to track builds between versions, without GitHub creating a new release
set(PROJECT_VERSION_EXTRA "")
include(GNUInstallDirs)
OPTION(ENABLE_LIBPCI "Enable libpci" ON)
OPTION(ENABLE_VULKAN "Enable vulkan" ON)
OPTION(ENABLE_WAYLAND "Enable wayland-client" ON)
OPTION(ENABLE_XCB_RANDR "Enable xcb-randr" ON)
OPTION(ENABLE_XCB "Enable xcb" ON)
OPTION(ENABLE_XRANDR "Enable xrandr" ON)
OPTION(ENABLE_X11 "Enable x11" ON)
OPTION(ENABLE_GIO "Enable gio-2.0" ON)
OPTION(ENABLE_DCONF "Enable dconf" ON)
OPTION(ENABLE_DBUS "Enable dbus-1" ON)
OPTION(ENABLE_XFCONF "Enable libxfconf-0" ON)
OPTION(ENABLE_RPM "Enable rpm" ON)
OPTION(BUILD_TESTS "Build tests" ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wconversion")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-Wl,-O3")
function(fastfetch_load_text FILENAME OUTVAR)
file(READ "${FILENAME}" TEMP)
string(REPLACE "\n" "\\n" TEMP "${TEMP}")
string(REPLACE "\"" "\\\"" TEMP "${TEMP}")
string(REPLACE "$\\" "" TEMP "${TEMP}")
set("${OUTVAR}" "${TEMP}" PARENT_SCOPE)
endfunction(fastfetch_load_text)
fastfetch_load_text(src/data/structure.txt DATATEXT_STRUCTURE)
fastfetch_load_text(src/data/config.txt DATATEXT_CONFIG)
fastfetch_load_text(src/data/modules.txt DATATEXT_MODULES)
fastfetch_load_text(src/data/help.txt DATATEXT_HELP)
fastfetch_load_text(src/data/help_color.txt DATATEXT_HELP_COLOR)
fastfetch_load_text(src/data/help_format.txt DATATEXT_HELP_FORMAT)
fastfetch_load_text(src/data/help_config.txt DATATEXT_HELP_CONFIG)
configure_file(src/fastfetch_config.h.in fastfetch_config.h)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
# Init CMake targets.
add_library(libfastfetch STATIC
src/util/FFstrbuf.c
src/util/FFlist.c
src/util/FFvaluestore.c
src/common/init.c
src/common/threading.c
src/common/io.c
src/common/processing.c
src/common/logo.c
src/common/format.c
src/common/parsing.c
src/common/settings.c
src/common/library.c
src/common/networking.c
src/detection/os.c
src/detection/plasma.c
src/detection/gtk.c
src/detection/terminalShell.c
src/detection/media.c
src/detection/datetime.c
src/detection/displayserver/displayServer.c
src/detection/displayserver/wayland.c
src/detection/displayserver/xcb.c
src/detection/displayserver/xlib.c
src/detection/displayserver/wmde.c
src/modules/break.c
src/modules/custom.c
src/modules/title.c
src/modules/separator.c
src/modules/os.c
src/modules/host.c
src/modules/kernel.c
src/modules/uptime.c
src/modules/processes.c
src/modules/packages.c
src/modules/shell.c
src/modules/resolution.c
src/modules/de.c
src/modules/wm.c
src/modules/wmtheme.c
src/modules/theme.c
src/modules/icons.c
src/modules/font.c
src/modules/cursor.c
src/modules/terminal.c
src/modules/terminalfont.c
src/modules/cpu.c
src/modules/cpuUsage.c
src/modules/gpu.c
src/modules/memory.c
src/modules/disk.c
src/modules/battery.c
src/modules/locale.c
src/modules/localip.c
src/modules/publicip.c
src/modules/player.c
src/modules/song.c
src/modules/datetime.c
src/modules/date.c
src/modules/time.c
src/modules/colors.c
)
if(ENABLE_LIBPCI)
pkg_check_modules (LIBPCI libpci)
if(LIBPCI_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBPCI=1)
else(LIBPCI_FOUND)
message(WARNING "Package libpci not found. Building without support.")
endif(LIBPCI_FOUND)
endif(ENABLE_LIBPCI)
if(ENABLE_VULKAN)
pkg_check_modules (VULKAN vulkan)
if(VULKAN_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_VULKAN=1)
else(VULKAN_FOUND)
message(WARNING "Package vulkan not found. Building without support.")
endif(VULKAN_FOUND)
endif(ENABLE_VULKAN)
if(ENABLE_WAYLAND)
pkg_check_modules (WAYLAND wayland-client)
if(WAYLAND_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_WAYLAND=1)
else(WAYLAND_FOUND)
message(WARNING "Package wayland-client not found. Building without support.")
endif(WAYLAND_FOUND)
endif(ENABLE_WAYLAND)
if(ENABLE_XCB_RANDR)
pkg_check_modules (XCB_RANDR xcb-randr)
if(XCB_RANDR_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XCB_RANDR=1)
else(XCB_RANDR_FOUND)
message(WARNING "Package xcb-randr not found. Building without support.")
endif(XCB_RANDR_FOUND)
endif(ENABLE_XCB_RANDR)
if(ENABLE_XCB)
pkg_check_modules (XCB xcb)
if(XCB_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XCB=1)
else(XCB_FOUND)
message(WARNING "Package xcb not found. Building without support.")
endif(XCB_FOUND)
endif(ENABLE_XCB)
if(ENABLE_XRANDR)
pkg_check_modules (XRANDR xrandr)
if(XRANDR_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XRANDR=1)
else(XRANDR_FOUND)
message(WARNING "Package xrandr not found. Building without support.")
endif(XRANDR_FOUND)
endif(ENABLE_XRANDR)
if(ENABLE_X11)
pkg_check_modules (X11 x11)
if(X11_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_X11=1)
else(X11_FOUND)
message(WARNING "Package x11 not found. Building without support.")
endif(X11_FOUND)
endif(ENABLE_X11)
if(ENABLE_GIO)
pkg_check_modules (GIO gio-2.0)
if(GIO_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_GIO=1)
else(GIO_FOUND)
message(WARNING "Package gio-2.0 not found. Building without support.")
endif(GIO_FOUND)
endif(ENABLE_GIO)
if(ENABLE_DCONF)
pkg_check_modules (DCONF dconf)
if(DCONF_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_DCONF=1)
else(DCONF_FOUND)
message(WARNING "Package dconf not found. Building without support.")
endif(DCONF_FOUND)
endif(ENABLE_DCONF)
if(ENABLE_DBUS)
pkg_check_modules (DBUS dbus-1)
if(DBUS_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_DBUS=1)
else(DBUS_FOUND)
message(WARNING "Package dbus-1 not found. Building without support.")
endif(DBUS_FOUND)
endif(ENABLE_DBUS)
if(ENABLE_XFCONF)
pkg_check_modules (XFCONF libxfconf-0)
if(XFCONF_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_XFCONF=1)
else(XFCONF_FOUND)
message(WARNING "Package libxfconf-0 not found. Building without support.")
endif(XFCONF_FOUND)
endif(ENABLE_XFCONF)
if(ENABLE_RPM)
pkg_check_modules (RPM rpm)
if(RPM_FOUND)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_RPM=1)
else(RPM_FOUND)
message(WARNING "Package librpm not found. Building without support.")
endif(RPM_FOUND)
endif(ENABLE_RPM)
target_include_directories(libfastfetch
PUBLIC ${PROJECT_BINARY_DIR}
PUBLIC ${PROJECT_SOURCE_DIR}/src
PRIVATE ${LIBPCI_INCLUDE_DIRS}
PRIVATE ${VULKAN_INCLUDE_DIRS}
PRIVATE ${WAYLAND_INCLUDE_DIRS}
PRIVATE ${XCB_RANDR_INCLUDE_DIRS}
PRIVATE ${XCB_INCLUDE_DIRS}
PRIVATE ${XRANDR_INCLUDE_DIRS}
PRIVATE ${X11_INCLUDE_DIRS}
PRIVATE ${GIO_INCLUDE_DIRS}
PRIVATE ${DCONF_INCLUDE_DIRS}
PRIVATE ${DBUS_INCLUDE_DIRS}
PRIVATE ${XFCONF_INCLUDE_DIRS}
PRIVATE ${RPM_INCLUDE_DIRS}
)
target_link_libraries(libfastfetch
PRIVATE ${CMAKE_DL_LIBS}
PRIVATE Threads::Threads
)
set_target_properties(libfastfetch PROPERTIES OUTPUT_NAME "fastfetch")
add_executable(fastfetch
src/fastfetch.c
)
target_link_libraries(fastfetch
PRIVATE libfastfetch
)
add_executable(flashfetch
src/flashfetch.c
)
target_link_libraries(flashfetch
PRIVATE libfastfetch
)
# Testing.
if (BUILD_TESTS)
add_executable(fastfetch-test-performance
tests/performance.c
)
target_link_libraries(fastfetch-test-performance
PRIVATE libfastfetch
)
add_executable(fastfetch-test-strbuf
tests/strbuf.c
)
target_link_libraries(fastfetch-test-strbuf
PRIVATE libfastfetch
)
enable_testing()
add_test(NAME test-strbuf COMMAND fastfetch-test-strbuf)
endif()
# Installation.
install(
TARGETS fastfetch
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
FILES completions/bash
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/bash-completion/completions
RENAME ${CMAKE_PROJECT_NAME}
)
install(
DIRECTORY presets
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${CMAKE_PROJECT_NAME}
)
install(
FILES ./LICENSE
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/licenses/${CMAKE_PROJECT_NAME}
)
+42
View File
@@ -0,0 +1,42 @@
# Development help
This file is not an official documentation!
Here i just add things that are easy to forget.
## Features i am planning to implement
### Specific
- [ ] Split the project in different libs / executables, all linking static by default:
- `libffdetect`: contains all the detection stuff. To be used by anything that needs system information
- `libffprint`: contains the printing functions, logos, format etc
- `fastfetch` and `flashfetch`: Executables, that initialize the config of libffprint. Fist one at runtime, second one at compile time
- [ ] Support for printing images as ascii art logos (using imlib2 and libcaca probably)
- [ ] Support for printing images in terminals that support it
- [ ] Better OS output for all possible combinations of /etc/os-release variables.
- [ ] Expose temperatures to CPU format string
- [ ] Expose temperatures to GPU format string
- [ ] Fallback GPU detection using GL (multi GPU suport somehow possible?)
- [ ] Find wayland compositor by looking at \${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY:-wayland-0}
- [ ] Make LocalIP module more configurable
- [ ] Automatic migrate old config files to newer versions
- [ ] ZSH completions
- [ ] Fish completions
- [ ] Make CPU usage detection much faster and more accurate
- [ ] Detect CPU usage in a common detection methode and expose it both to the cpuUsage module and the cpu format string
- [ ] Per session caching
- [ ] RPM packaging
### General
- More presets
- More logos
- More package managers
- More DE detections
- More WM theme detections
- More terminal font detections
- Performance optimizations
- More module specific options
- Better error handling, especially the messages shown when using --show-errors
- Using package managers libraries for package count detection instead own. E.g. using libalpm instead of counting files in /var/lib/pacman/local
- Better documentation (especially default config file and the various --help options. Colored output?)
- General feature parity with neofetch, to demonstrate supremity of c
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Linus Dierheimer
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.
+87 -16
View File
@@ -1,29 +1,100 @@
# Fastfetch Benchmark Data
# fastfetch
This branch stores continuous performance benchmark data for Fastfetch on Linux, macOS, and Windows.
fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying them in a pretty way. It is written in c to achieve much better performance, in return only Linux and Android are supported. It also uses mechanisms like multithreading and caching to finish as fast as possible.
The output on my personal machine with default configurations looks like this:
<img src="screenshots/example.png" alt="example output" width="100%"/>
The data is generated and updated automatically by GitHub Actions. Please do not edit generated benchmark files manually.
## Customization
## Benchmark Dashboard
With customization and speed being two competing goals, this project actually builds two executables.
The main one being `fastfetch`, which can be very greatly configured via flags. These flags can be made persistent in `~/.config/fastfetch/config.conf`. To view the available options run `fastfetch --help`.
The second executable being buildt is called `flashfetch`, which is configured at compile time to eliminate any possible overhead. Configuration of it can be very easily done in [`src/flashfetch.c`](src/flashfetch.c).
At the moment the performance difference is measurable, but too small to be human recognizable. But the leap will get bigger with more and more options coming, and on slow machines this might actually make a difference.
There are some presets defined for fastfech in [`presets`](presets), you can can load them with `--load-config`. They may also serve as a good example for format arguments.
<https://fastfetch-cli.github.io/fastfetch/dev/bench/>
## Dependencies
## Metrics
Fastfetch dynamically loads needed libraries if they are available. Therefore its only hard dependencies are `libc` (any implementation of the c standard library), `libdl` and `libpthread`. They are all shipped with [`glibc`](https://www.gnu.org/software/libc/), which is already installed on most linux distributions, so you probably don't have to worry about it.
The benchmark data includes:
The following libraries are used if present at runtime:
* [`libpci`](https://github.com/pciutils/pciutils): GPU output.
* [`libvulkan`](https://www.vulkan.org/): Fallback for GPU output.
* [`libxcb-randr`](https://xcb.freedesktop.org/),
[`libXrandr`](https://gitlab.freedesktop.org/xorg/lib/libxrandr),
[`libxcb`](https://xcb.freedesktop.org/),
[`libX11`](https://gitlab.freedesktop.org/xorg/lib/libx11): At least one of them sould be present in X11 sessions for better resolution detection and faster WM detection. The `*randr` ones provide multi monitor support. The `libxcb*` ones usually have better performance.
* [`libwayland-client`](https://wayland.freedesktop.org/): Better resolution performance and output in wayland sessions. Supports different refresh rates per monitor.
* [`libGIO`](https://developer.gnome.org/gio/unstable/): Needed for values that are only stored GSettings.
* [`libDConf`](https://developer.gnome.org/dconf/unstable/): Needed for values that are only stored in DConf + Fallback for GSettings.
* [`libDBus`](https://www.freedesktop.org/wiki/Software/dbus): Needed for detecting current media player and song.
* [`libXFConf`](https://gitlab.xfce.org/xfce/xfconf): Needed for XFWM theme and XFCE Terminal font.
* [`librpm`](http://rpm.org/): Needed for rpm package count.
- Execution time for each Fastfetch module, measured in milliseconds;
- Total Fastfetch process execution time, measured in milliseconds.
## Support status
All categories not listed here should work without needing a specific implementation.
All metrics use the “smaller is better” comparison mode.
##### Available Modules
```
Title, Separator, OS, Host, Kernel, Uptime, Processes, Packages, Shell, Resolution, DE, WM, WMTheme, Theme, Icons, Font, Cursor, Terminal, Terminal Font, CPU, CPUUsage, GPU, Memory, Disk, Battery, Player, Song, LocalIP, PublicIP, DateTime, Date, Time, Locale, Colors, Break, Custom
```
## Update Workflow
##### Logos
```
Android, Arch, Arco, Artix, CachyOS, CentOS, Debian, Endeavour, Fedora, Garuda, Gentoo, KDE Neon, Linux, Manjaro, Mint, Pop!_OS, RebornOS, Ubuntu, Void, Zorin
```
* Most of the logos have a small variant. Access it by appending _small to the logo name.
* Some logos have an old variant. Access it by appending _old to the logo name.
* Unknown/unsupported logos will be replaced with a generic linux logo when running fastfetch.
* Get a list of all available logos with `fastfetch --print-logos`.
Benchmark data is generated from the `dev` branch CI workflow and updated after all platform build jobs have completed.
##### Package managers
```
Pacman, dpkg, rpm, emerge, xbps, Flatpak, Snap
```
Related workflow files:
##### WM themes
```
KWin, Mutter, Muffin, Marco, XFWM, Openbox (LXDE, LXQT & without DE)
```
- `.github/workflows/build-benchmark.yml`
- `.github/workflows/ci.yml`
##### DE versions
```
KDE Plasma, Gnome, Cinnamon, Mate, XFCE4, LXQt
```
Commit information and dates are recorded automatically by the Continuous Benchmark Action.
##### Terminal fonts
```
konsole, gnome-terminal-server, tilix, xfce4-terminal, lxterminal, TTY
```
## Building
fastfetch uses [`cmake`](https://cmake.org/) and [`pkg-config`](https://www.freedesktop.org/wiki/Software/pkg-config/) for building. The simplest steps to build the fastfetch and flashfetch binaries are:
```bash
mkdir -p build
cd build
cmake ..
cmake --build . -j$(nproc) --target fastfetch --target flashfetch
```
If pkg-config fails to find the headers for a library listed in [dependencies](#dependencies), fastfetch will simply build without support for that specific feature. This means, it won't look for it at runtime and just act like it isn't available.
## Packaging
* [AUR](https://aur.archlinux.org/packages/fastfetch-git/): Packaged by me. Will install the fastfetch binary, bash completion and the presets. Git version
* [Manjaro Repositories](https://gitlab.manjaro.org/packages/community/fastfetch): Packaged by a manjaro maintainer. Usually a bit outdated.
* [Gentoo (Guru)](https://github.com/gentoo/guru/tree/master/app-misc/fastfetch): Packed by some other guy. May be out of date some times.
* DEB: Run [`packaging/deb/create.sh`](packaging/deb/create.sh) from the root directory. You can give a custom build directory as an argument.
* Manually:
* Build: Follow the [build instructions](#building).
* Install: `sudo cmake --install build --prefix /usr/local`
## FAQ
Q: Why do you need a very performant version of neofetch?
> I like putting neofetch in my ~/.bashrc to have a system overwiew whenever i use the terminal, but the slow speed annoyed me, so i created this. Also neofetch didn't output everything correctly (e.g Font is displayed as "[Plasma], Noto Sans, 10 [GTK2/3]") and writing my own tool gave me the possibility to fine tune it to run perfectly on at least my configuration.
Q: It does not display [*] correctly for me, what can i do?
> This is most likely because your system is not implemented (yet). At the moment i am focusing more on making the core app better, than adding more configurations. Feel free to open a pull request if you want to add support for your configuration
+256
View File
@@ -0,0 +1,256 @@
#!/usr/bin/env bash
__fastfetch_complete_help()
{
local __ff_helps=(
"color"
"format"
"load-config"
"os-format"
"host-format"
"kernel-format"
"packages-format"
"shell-format"
"resolution-format"
"de-format"
"wm-format"
"theme-format"
"icons-format"
"font-format"
"terminal-format"
"terminal-font-format"
"cpu-format"
"cpu-usage-format"
"gpu-format"
"memory-format"
"disk-format"
"battery-format"
"locale-format"
"local-ip-format"
"public-ip-format"
"datetime-format"
"date-format"
"time-format"
)
COMPREPLY=($(compgen -W "${__ff_helps[*]}" -- "$CURRENT_WORD"))
}
__fastfetch_complete_bool()
{
COMPREPLY=($(compgen -W "true false" -- "$CURRENT_WORD"))
}
__fastfetch_complete_string()
{
if [[ $CURRENT_WORD != "" ]]; then
COMPREPLY=("$CURRENT_WORD")
fi
}
__fastfetch_complete_path()
{
COMPREPLY=($(compgen -A file -- "$CURRENT_WORD"))
}
__fastfetch_complete_logo()
{
COMPREPLY=($(compgen -W "$(fastfetch --list-logos-autocompletion)" -- "$CURRENT_WORD"))
}
__fastfetch_complete_option()
{
local FF_OPTIONS_ALL=(
"${FF_OPTIONS_BOOL[@]}"
"${FF_OPTIONS_STRING[@]}"
"${FF_OPTIONS_PATH[@]}"
"${FF_OPTIONS_LOGO[@]}"
)
if [[ $WORD_COUND -lt 3 ]]; then
FF_OPTIONS_ALL+=(
"${FF_OPTIONS_SINGLE[@]}"
"${FF_OPTIONS_HELP[@]}"
)
fi
for ff_word in ${COMP_WORDS[@]}; do
if [[ $ff_word == $CURRENT_WORD ]]; then
break
fi
FF_OPTIONS_ALL=("${FF_OPTIONS_ALL[@]/$ff_word}")
done
COMPREPLY=($(compgen -W "${FF_OPTIONS_ALL[*]}" -- "$CURRENT_WORD"))
}
__fastfetch_previous_matches()
{
for ff_option in "$@"; do
if [[ $ff_option == "$PREVIOUS_WORD" ]]; then
return 0
fi
done
return 1
}
__fastfetch_completion()
{
local CURRENT_WORD="${COMP_WORDS[$COMP_CWORD]}"
local PREVIOUS_WORD="${COMP_WORDS[$COMP_CWORD - 1]}"
local WORD_COUND="${#COMP_WORDS[@]}"
local FF_OPTIONS_SINGLE=(
"-v"
"--version"
"--list-logos"
"--list-modules"
"--list-presets"
"--list-features"
"--print-logos"
"--print-config"
"--print-structure"
)
local FF_OPTIONS_HELP=(
"-h"
"--help"
)
local FF_OPTIONS_BOOL=(
"-r"
"--recache"
"--nocache"
"--show-errors"
"--color-logo"
"--print-remaining-logo"
"--multithreading"
"--allow-slow-operations"
"--disable-linewrap"
"--hide-cursor"
"--localip-show-ipv4"
"--localip-show-ipv6"
"--localip-show-loop"
)
local FF_OPTIONS_STRING=(
"-c"
"--color"
"--spacing"
"-s"
"--separator"
"-x"
"--offsetx"
"--structure"
"--set"
"--os-format"
"--os-key"
"--host-format"
"--host-key"
"--kernel-format"
"--kernel-key"
"--uptime-format"
"--uptime-key"
"--processes-format"
"--processes-key"
"--packages-format"
"--packages-key"
"--shell-format"
"--shell-key"
"--resolution-format"
"--resolution-key"
"--de-format"
"--de-key"
"--wm-format"
"--wm-key"
"--wm-theme-format"
"--wm-theme-key"
"--theme-format"
"--theme-key"
"--icons-format"
"--icons-key"
"--font-format"
"--font-key"
"--cursor-format"
"--cursor-key"
"--terminal-format"
"--terminal-key"
"--terminal-font-format"
"--terminal-font-key"
"--cpu-format"
"--cpu-key"
"--cpu-usage-format"
"--cpu-usage-key"
"--gpu-format"
"--gpu-key"
"--memory-format"
"--memory-key"
"--disk-format"
"--disk-key"
"--battery-format"
"--battery-key"
"--locale-format"
"--locale-key"
"--disk-folders"
"--disk-key"
"--local-ip-format"
"--local-ip-key"
"--public-ip-format"
"--public-ip-key"
"--player-format"
"--player-key"
"--song-format"
"--song-key"
"--datetime-key"
"--datetime-format"
"--date-format"
"--date-key"
"--time-format"
"--time-key"
)
local FF_OPTIONS_PATH=(
"--os-file"
"--lib-PCI"
"--lib-vulkan"
"--lib-wayland"
"--lib-xcb-randr"
"--lib-xcb"
"--lib-Xrandr"
"--lib-X11"
"--lib-gio"
"--lib-DConf"
"--lib-DBus"
"--lib-XFConf"
"--lib-RPM"
"--battery-dir"
"--load-config"
)
local FF_OPTIONS_LOGO=(
"-l"
"--logo"
)
if __fastfetch_previous_matches "${FF_OPTIONS_SINGLE[@]}"; then
return
elif [[ $WORD_COUND -gt 3 && ( ${COMP_WORDS[$COMP_CWORD - 2]} == "--help" || ${COMP_WORDS[$COMP_CWORD - 2]} == "-h" ) ]]; then
return
elif [[ $CURRENT_WORD == "-"* ]]; then
__fastfetch_complete_option
elif __fastfetch_previous_matches "${FF_OPTIONS_HELP[@]}"; then
__fastfetch_complete_help
elif __fastfetch_previous_matches "${FF_OPTIONS_BOOL[@]}"; then
__fastfetch_complete_bool
elif __fastfetch_previous_matches "${FF_OPTIONS_STRING[@]}"; then
__fastfetch_complete_string
elif __fastfetch_previous_matches "${FF_OPTIONS_PATH[@]}"; then
__fastfetch_complete_path
elif __fastfetch_previous_matches "${FF_OPTIONS_LOGO[@]}"; then
__fastfetch_complete_logo
else
__fastfetch_complete_option
fi
}
complete -F __fastfetch_completion fastfetch
-67328
View File
File diff suppressed because it is too large Load Diff
-405
View File
@@ -1,405 +0,0 @@
<!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-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="https://cdn.jsdelivr.net/npm/chart.js/dist/chart.umd.min.js"></script>
<script src="data.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 total process time for one operating system and architecture. Module curves are available from the legend and hidden by default.</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"></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 && entry.commit.timestamp || entry.date;
const date = new Date(raw);
return {
date: Number.isNaN(date.valueOf()) ? String(entry.date || '') : date.toISOString(),
sha: entry.commit && entry.commit.id ? entry.commit.id : 'unknown'
};
}
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'
];
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 = [];
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);
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 });
}
const familyOrder = ['Intel', 'ARM', 'Other'];
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: metric !== 'total process time'
}))
},
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, metric === 'total process time'));
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 (!groups.size) {
tabs.hidden = true;
panels.innerHTML = '<p class="empty">No benchmark data available.</p>';
}
})();
</script>
</body>
</html>
Submodule
+1
Submodule packaging/aur added at 7ac9c5d132
+13
View File
@@ -0,0 +1,13 @@
Package: fastfetch
Version: <VERSION>
Section: utils
Priority: extra
Architecture: amd64
Maintainer: Linus@Dierheimer.de
Homepage: https://github.com/LinusDierheimer/fastfetch
Installed-Size: 410K
Description: A fast alternative to neofetch
fastfetch is a neofetch-like tool for fetching system information and
displaying them in a pretty way. It is written in c to achieve much better
performance, in return only Linux and Android are supported. It also uses
mechanisms like multithreading and caching to finish as fast as possible.
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
# This script is intended to be run from the root of the repository.
# If you don't want to use build/ as build directoy, or already build the elsewere, you can give the build directoy as argument.
BUILD_DIR="${1:-build}"
ROOT_DIR="${2:-.}"
PACKAGE_DIR="${BUILD_DIR}/packaging/deb/fastfetch"
# Create missing files
mkdir -p "${BUILD_DIR}" || exit 1
if [ ! -f "${BUILD_DIR}/cmake_install.cmake" ]; then
cmake -S "${ROOT_DIR}" -B "${BUILD_DIR}" || exit 2
rm -f "${BUILD_DIR}/fastfetch" || exit 3 # Always rebuild the project if we reconfigured it
fi
if [ ! -f "${BUILD_DIR}/fastfetch" ]; then
cmake --build "${BUILD_DIR}" || exit 4
fi
# Populate the files
cmake --install "${BUILD_DIR}" --prefix "${PACKAGE_DIR}/usr" || exit 5
mkdir -p "${PACKAGE_DIR}/DEBIAN" || exit 6
sed "s/<VERSION>/$("${BUILD_DIR}/fastfetch" --version-raw)/g" "${ROOT_DIR}/packaging/deb/control-template" > "${PACKAGE_DIR}/DEBIAN/control" || exit 7
# Create the package
dpkg-deb --build "${PACKAGE_DIR}" || exit 8
+1
View File
@@ -0,0 +1 @@
--structure Title:Separator:OS:Host:Kernel:Uptime:Processes:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:GPU:Memory:Disk:Battery:Player:Song:PublicIP:LocalIP:DateTime:Locale:Break:Colors
+3
View File
@@ -0,0 +1,3 @@
--logo arch
--os-format "Arch Linux ({12})"
--packages-format "{} (pacman)"
+4
View File
@@ -0,0 +1,4 @@
--disable-linewrap false
--multithreading false
--show-errors true
--recache
+2
View File
@@ -0,0 +1,2 @@
--load-config verbose
--load-config devinfo
+4
View File
@@ -0,0 +1,4 @@
--structure Host:Resolution:CPU:GPU:Memory:Disk:Battery
--memory-format "{?2}{2}MiB{?}"
--disk-format "{?2}{2}GB{?}"
--battery-format "{?1}{1} {?}{?2}{2} {?}{?3}({3}){?}"
+10
View File
@@ -0,0 +1,10 @@
--structure "Title:Separator:OS:Host:Kernel:Uptime:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Terminal:TerminalFont:CPU:GPU:Memory:Break:Colors"
--host-format "{/2}{-}{/}{2}{?3} {3}{?}"
--os-format "{3} {12}"
--resolution-format "{}x{}"
--de-format "{2} {3}"
--theme-format "{?1}{1}{?3} {3}{?} [Plasma], {?}{7}"
--terminal-font-format "{/2}{-}{/}{2}{?3} {3}{?}"
--memory-format "{/1}{-}{/}{/2}{-}{/}{}MiB / {}MiB"
--gpu-key "GPU"
--resolution-key "Resolution"
+5
View File
@@ -0,0 +1,5 @@
--structure Title:Separator:OS:Host:Kernel:Uptime:Battery:Break:Packages:Shell:Resolution:Terminal:Break:CPU:GPU:Memory:Break:Colors
--host-format "{/2}{-}{/}{2}{?3} {3}{?}"
--battery-format "{/4}{-}{/}{4}%{?5} [{5}]{?}"
--resolution-key "Resolution"
--gpu-key "GPU"
+1
View File
@@ -0,0 +1 @@
--structure OS:Kernel:Shell:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont
+28
View File
@@ -0,0 +1,28 @@
--load-config all
--os-format "System: {}; Name: {}; Pretty name: {}; ID: {}; ID like: {}; Variant: {}; Variant ID: {}; Version: {}; Version ID: {}; Version codename: {}; Build ID: {}; Architecture: {}"
--host-format "Family: {}; Name: {}; Version: {}"
--kernel-format "Sysname: {}; Release: {}; Version: {}"
--uptime-format "Days: {}; Hours: {}; Minutes: {}; Seconds: {}"
--processes-format "Count: {}"
--packages-format "All: {}; pacman: {}; pacman branch: {}; dpkg: {}; rpm: {}; emerge: {}; xbps: {}; flatpak: {}; snap: {}"
--shell-format "Process name: {}; Process path: {}; Process exe: {}; Process version: {}; User path: {}; User exe: {}; User version: {}"
--resolution-format "Width: {}; Height: {}; Refresh rate: {}"
--de-format "Process name: {}; Pretty name: {}; Version: {}"
--wm-format "Process name: {}; Pretty name: {}; Protocol: {}"
--wm-theme-format "Name: {}"
--theme-format "Plasma: {}; Plasma colors: {}; Plasma colors pretty: {}; GTK2: {}; GTK3: {}; GTK4: {}; GTK: {}"
--icons-format "Plasma: {}; GTK2: {}; GTK3: {}; GTK4: {}; GTK: {}"
--font-format "Plasma raw: {}; Plasma name: {}; Plasma size: {}; Plasma styles: {}; Plasma pretty: {}; GTK2 raw: {}; GTK2 name: {}; GTK2 size: {}; GTK2 styles: {}; GTK2 pretty: {}; GTK3 raw: {}; GTK3 name: {}; GTK3 size: {}; GTK3 styles: {}; GTK3 pretty: {}; GTK4 raw: {}; GTK4 name: {}; GTK4 size: {}; GTK4 styles: {}; GTK4 pretty: {}; GTK: {}"
--cursor-format "Theme: {}; Size: {}"
--terminal-format "Process: {}; Path: {}; Exe: {}"
--terminal-font-format "Raw: {}; Name: {}; Size: {}; Styles: {}; Pretty: {}"
--cpu-format "Name: {}; Pretty: {}; Vendor: {}; Logical online: {}; Logical configured: {}; Physical: {}; Cores: {}; bios: {}; scaling max: {}; scaling min: {}; info max: {}; info min: {}; cpuinfo: {}; frequency: {}"
--cpu-usage-format "Percentage: {}"
--gpu-format "Vendor: {}; Vendor pretty: {}; Name: {}; Name pretty: {}; Driver: {}"
--memory-format "Used: {}; Total: {}; Percentage: {}"
--disk-format "Used: {}; Total: {}; Files: {}; Percentage: {}"
--battery-format "Manufactor: {}; Model: {}; Technology: {}; Capacaty: {}; Status: {}"
--player-format "Name: {}"
--song-format "Song: {}; Artist: {}; Album: {}"
--datetime-format "year: {}; yearShort: {}; month: {}; monthPretty: {}; monthName: {}; monthNameShort: {}; weekNumber: {}; weekday: {}; weekdayShort: {}; dayInYear: {}; dayInMonth: {}; dayInWeek: {}; hour: {}; hourPretty: {}; hour12: {}; hour12Pretty: {}; minute: {}; minutePretty: {}; second: {}; secondPretty: {}"
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/env sh
mkdir -p build/
cd build/
cmake ..
cmake --build . --target fastfetch -j$(nproc)
./fastfetch "$@"
Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

+289
View File
@@ -0,0 +1,289 @@
#include "fastfetch.h"
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
{
if(formatarg->type == FF_FORMAT_ARG_TYPE_INT)
ffStrbufAppendF(buffer, "%i", *(int*)formatarg->value);
else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT)
ffStrbufAppendF(buffer, "%u", *(uint32_t*)formatarg->value);
else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT16)
ffStrbufAppendF(buffer, "%hu", *(uint16_t*)formatarg->value);
else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT8)
ffStrbufAppendF(buffer, "%hhu", *(uint8_t*)formatarg->value);
else if(formatarg->type == FF_FORMAT_ARG_TYPE_STRING)
ffStrbufAppendS(buffer, (const char*)formatarg->value);
else if(formatarg->type == FF_FORMAT_ARG_TYPE_STRBUF)
ffStrbufAppend(buffer, (FFstrbuf*)formatarg->value);
else if(formatarg->type == FF_FORMAT_ARG_TYPE_DOUBLE)
ffStrbufAppendF(buffer, "%g", *(double*)formatarg->value);
else if(formatarg->type == FF_FORMAT_ARG_TYPE_LIST)
{
const FFlist* list = formatarg->value;
for(uint32_t i = 0; i < list->length; i++)
{
ffStrbufAppend(buffer, ffListGet(list, i));
if(i < list->length - 1)
ffStrbufAppendS(buffer, ", ");
}
}
else if(formatarg->type != FF_FORMAT_ARG_TYPE_NULL)
{
fprintf(stderr, "Error: format string \"%s\": argument is not implemented: %i\n", buffer->chars, formatarg->type);
}
}
static inline bool placeholderValueIsForError(const FFstrbuf* placeholderValue)
{
return
ffStrbufIgnCaseCompS(placeholderValue, "e") == 0 ||
ffStrbufIgnCaseCompS(placeholderValue, "error") == 0 ||
ffStrbufIgnCaseCompS(placeholderValue, "0") == 0
;
}
static inline uint32_t getArgumentIndex(const FFstrbuf* placeholderValue)
{
uint32_t result = UINT32_MAX;
if(placeholderValue->chars[0] != '-')
sscanf(placeholderValue->chars, "%u", &result);
return result;
}
static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start, const FFstrbuf* placeholderValue, uint32_t index, uint32_t formatStringLength)
{
ffStrbufAppendS(buffer, start);
ffStrbufAppend(buffer, placeholderValue);
if(index < formatStringLength)
ffStrbufAppendC(buffer, '}');
}
static inline void appendEmptyPlaceholder(FFstrbuf* buffer, const char* placeholder, uint32_t* argCounter, uint32_t numArgs, const FFformatarg* arguments)
{
if(*argCounter > numArgs)
ffStrbufAppendS(buffer, placeholder);
else
ffFormatAppendFormatArg(buffer, &arguments[(*argCounter)++]);
}
static inline bool formatArgSet(const FFformatarg* arg)
{
return arg->value != NULL && (
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_INT && *(int*)arg->value > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_STRING && *(const char*)arg->value != '\0') ||
(arg->type == FF_FORMAT_ARG_TYPE_UINT8 && *(uint8_t*)arg->value > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_UINT16 && *(uint16_t*)arg->value > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_UINT && *(uint32_t*)arg->value > 0)
);
}
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments)
{
uint32_t argCounter = 0;
uint32_t numOpenIfs = 0;
uint32_t numOpenNotIfs = 0;
uint32_t numOpenColors = 0;
for(uint32_t i = 0; i < formatstr->length; ++i)
{
// if we don't have a placeholder start just copy the chars over to output buffer
if(formatstr->chars[i] != '{')
{
ffStrbufAppendC(buffer, formatstr->chars[i]);
continue;
}
// if we have an { at the end handle it as {}
if(i == formatstr->length - 1)
{
appendEmptyPlaceholder(buffer, "{", &argCounter, numArgs, arguments);
continue;
}
// jump to next char, the start of the placeholder value
++i;
// double {{ elvaluates to a single { and doesn't count as start
if(formatstr->chars[i] == '{')
{
ffStrbufAppendC(buffer, '{');
continue;
}
// placeholder is {}
if(formatstr->chars[i] == '}')
{
appendEmptyPlaceholder(buffer, "{}", &argCounter, numArgs, arguments);
continue;
}
FFstrbuf placeholderValue;
ffStrbufInit(&placeholderValue);
while(i < formatstr->length && formatstr->chars[i] != '}')
ffStrbufAppendC(&placeholderValue, formatstr->chars[i++]);
// test for error, if so print it
if(placeholderValueIsForError(&placeholderValue)) {
ffStrbufAppend(buffer, error);
ffStrbufDestroy(&placeholderValue);
continue;
}
// test if for stop, if so break the loop
if(placeholderValue.length == 1 && placeholderValue.chars[0] == '-')
{
ffStrbufDestroy(&placeholderValue);
break;
}
// test for end of an if, if so do nothing
if(placeholderValue.length == 1 && placeholderValue.chars[0] == '?')
{
if(numOpenIfs == 0)
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
else
--numOpenIfs;
ffStrbufDestroy(&placeholderValue);
continue;
}
// test for end of a not if, if so do nothing
if(placeholderValue.length == 1 && placeholderValue.chars[0] == '/')
{
if(numOpenNotIfs == 0)
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
else
--numOpenNotIfs;
ffStrbufDestroy(&placeholderValue);
continue;
}
// test for end of a color, if so do nothing
if(placeholderValue.length == 1 && placeholderValue.chars[0] == '#')
{
if(numOpenColors == 0)
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
else
{
ffStrbufAppendS(buffer, "\033[0m");
--numOpenColors;
}
ffStrbufDestroy(&placeholderValue);
continue;
}
// test for if, if so evaluate it
if(placeholderValue.chars[0] == '?')
{
ffStrbufSubstrAfter(&placeholderValue, 0);
// continue if an error is set
if(placeholderValueIsForError(&placeholderValue) && error != NULL && error->length > 0)
{
++numOpenIfs;
ffStrbufDestroy(&placeholderValue);
continue;
}
uint32_t index = getArgumentIndex(&placeholderValue);
// testing for an invalid index
if(index > numArgs)
{
appendInvalidPlaceholder(buffer, "{?", &placeholderValue, i, formatstr->length);
ffStrbufDestroy(&placeholderValue);
continue;
}
// continue normally if an format arg is set and the value is > 0
if(formatArgSet(&arguments[index - 1]))
{
++numOpenIfs;
ffStrbufDestroy(&placeholderValue);
continue;
}
// fastforward to the end of the if without printing the in between
i = ffStrbufNextIndexS(formatstr, i, "{?}") + 2; // 2 is the length of "{?}" - 1 because the loop will increament it again directly after continue
ffStrbufDestroy(&placeholderValue);
continue;
}
// test for not if, if so evaluate it
if(placeholderValue.chars[0] == '/')
{
ffStrbufSubstrAfter(&placeholderValue, 0);
//continue if an error os not set
if(placeholderValueIsForError(&placeholderValue) && (error == NULL || error->length == 0))
{
++numOpenNotIfs;
ffStrbufDestroy(&placeholderValue);
continue;
}
uint32_t index = getArgumentIndex(&placeholderValue);
// testing for an invalid index
if(index > numArgs)
{
appendInvalidPlaceholder(buffer, "{/", &placeholderValue, i, formatstr->length);
ffStrbufDestroy(&placeholderValue);
continue;
}
//continue normally if an format arg is not set or the value is 0
if(!formatArgSet(&arguments[index - 1]))
{
++numOpenNotIfs;
ffStrbufDestroy(&placeholderValue);
continue;
}
// fastforward to the end of the if without printing the in between
i = ffStrbufNextIndexS(formatstr, i, "{/}") + 2; // 2 is the length of "{/}" - 1 because the loop will increament it again directly after continue
ffStrbufDestroy(&placeholderValue);
continue;
}
//test for color, if so evaluate it
if(placeholderValue.chars[0] == '#')
{
++numOpenColors;
ffStrbufSubstrAfter(&placeholderValue, 0);
ffStrbufAppendS(buffer, "\033[");
ffStrbufAppend(buffer, &placeholderValue);
ffStrbufAppendC(buffer, 'm');
ffStrbufDestroy(&placeholderValue);
continue;
}
uint32_t index = getArgumentIndex(&placeholderValue);
// test for invalid index
if(index > numArgs)
{
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
ffStrbufDestroy(&placeholderValue);
continue;
}
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
ffStrbufDestroy(&placeholderValue);
}
ffStrbufTrimRight(buffer, ' ');
if(numOpenColors > 0)
ffStrbufAppendS(buffer, "\033[0m");
}
+312
View File
@@ -0,0 +1,312 @@
#include "fastfetch.h"
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
static bool strbufEqualsAdapter(const void* first, const void* second)
{
return ffStrbufComp(second, first) == 0;
}
static void initConfigDirs(FFstate* state)
{
ffListInit(&state->configDirs, sizeof(FFstrbuf));
const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
if(xdgConfigHome != NULL)
{
FFstrbuf* buffer = (FFstrbuf*) ffListAdd(&state->configDirs);
ffStrbufInitA(buffer, 64);
ffStrbufAppendS(buffer, xdgConfigHome);
ffStrbufTrimRight(buffer, '/');
}
FFstrbuf xdgConfigDirs;
ffStrbufInitA(&xdgConfigDirs, 64);
ffStrbufAppendS(&xdgConfigDirs, getenv("XDG_CONFIG_DIRS"));
#define FF_ENSURE_ONLY_ONCE_IN_LIST(element) \
if(ffListFirstIndexComp(&state->configDirs, element, strbufEqualsAdapter) < state->configDirs.length - 1) \
--state->configDirs.length;
FFstrbuf* userConfigHome = ffListAdd(&state->configDirs);
ffStrbufInitA(userConfigHome, 64);
ffStrbufAppendS(userConfigHome, state->passwd->pw_dir);
ffStrbufAppendS(userConfigHome, "/.config");
FF_ENSURE_ONLY_ONCE_IN_LIST(userConfigHome)
FFstrbuf* userHome = ffListAdd(&state->configDirs);
ffStrbufInitA(userHome, 64);
ffStrbufAppendS(userHome, state->passwd->pw_dir);
FF_ENSURE_ONLY_ONCE_IN_LIST(userHome)
uint32_t startIndex = 0;
while (startIndex < xdgConfigDirs.length)
{
uint32_t colonIndex = ffStrbufNextIndexC(&xdgConfigDirs, startIndex, ':');
xdgConfigDirs.chars[colonIndex] = '\0';
FFstrbuf* buffer = (FFstrbuf*) ffListAdd(&state->configDirs);
ffStrbufInitA(buffer, 64);
ffStrbufAppendS(buffer, xdgConfigDirs.chars + startIndex);
ffStrbufTrimRight(buffer, '/');
FF_ENSURE_ONLY_ONCE_IN_LIST(buffer);
startIndex = colonIndex + 1;
}
FFstrbuf* systemConfigHome = ffListAdd(&state->configDirs);
ffStrbufInitA(systemConfigHome, 64);
ffStrbufAppendS(systemConfigHome, "/etc/xdg");
FF_ENSURE_ONLY_ONCE_IN_LIST(systemConfigHome)
FFstrbuf* systemConfig = ffListAdd(&state->configDirs);
ffStrbufInitA(systemConfig, 64);
ffStrbufAppendS(systemConfig, "/etc");
FF_ENSURE_ONLY_ONCE_IN_LIST(systemConfig)
#undef FF_ENSURE_ONLY_ONCE_IN_LIST
}
static void initCacheDir(FFstate* state)
{
ffStrbufInitA(&state->cacheDir, 64);
ffStrbufAppendS(&state->cacheDir, getenv("XDG_CACHE_HOME"));
if(state->cacheDir.length == 0)
{
ffStrbufAppendS(&state->cacheDir, state->passwd->pw_dir);
ffStrbufAppendS(&state->cacheDir, "/.cache/");
}
else if(!ffStrbufEndsWithC(&state->cacheDir, '/'))
ffStrbufAppendC(&state->cacheDir, '/');
mkdir(state->cacheDir.chars, S_IRWXU | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH); //I hope everybody has a cache folder, but who knows
ffStrbufAppendS(&state->cacheDir, "fastfetch/");
mkdir(state->cacheDir.chars, S_IRWXU | S_IRGRP | S_IROTH);
}
static void initState(FFstate* state)
{
state->logoWidth = 0;
state->passwd = getpwuid(getuid());
uname(&state->utsname);
sysinfo(&state->sysinfo);
initConfigDirs(state);
initCacheDir(state);
}
static void defaultConfig(FFinstance* instance)
{
ffStrbufInit(&instance->config.color);
ffStrbufInit(&instance->config.separator);
ffStrbufAppendS(&instance->config.separator, ": ");
instance->config.logoKeySpacing = 4;
instance->config.offsetx = 0;
instance->config.colorLogo = true;
instance->config.showErrors = false;
instance->config.recache = false;
instance->config.cacheSave = true;
instance->config.printRemainingLogo = true;
instance->config.allowSlowOperations = false;
instance->config.disableLinewrap = true;
instance->config.hideCursor = true;
instance->config.userLogoIsRaw = false;
for(uint8_t i = 0; i < (uint8_t) FASTFETCH_LOGO_MAX_COLORS; ++i)
ffStrbufInit(&instance->config.logoColors[i]);
ffStrbufInitA(&instance->config.osFormat, 0);
ffStrbufInitA(&instance->config.osKey, 0);
ffStrbufInitA(&instance->config.hostFormat, 0);
ffStrbufInitA(&instance->config.hostKey, 0);
ffStrbufInitA(&instance->config.kernelFormat, 0);
ffStrbufInitA(&instance->config.kernelKey, 0);
ffStrbufInitA(&instance->config.uptimeFormat, 0);
ffStrbufInitA(&instance->config.uptimeKey, 0);
ffStrbufInitA(&instance->config.processesFormat, 0);
ffStrbufInitA(&instance->config.processesKey, 0);
ffStrbufInitA(&instance->config.packagesFormat, 0);
ffStrbufInitA(&instance->config.packagesKey, 0);
ffStrbufInitA(&instance->config.shellFormat, 0);
ffStrbufInitA(&instance->config.shellKey, 0);
ffStrbufInitA(&instance->config.resolutionFormat, 0);
ffStrbufInitA(&instance->config.resolutionKey, 0);
ffStrbufInitA(&instance->config.deFormat, 0);
ffStrbufInitA(&instance->config.deKey, 0);
ffStrbufInitA(&instance->config.wmFormat, 0);
ffStrbufInitA(&instance->config.wmKey, 0);
ffStrbufInitA(&instance->config.wmThemeFormat, 0);
ffStrbufInitA(&instance->config.wmThemeKey, 0);
ffStrbufInitA(&instance->config.themeFormat, 0);
ffStrbufInitA(&instance->config.themeKey, 0);
ffStrbufInitA(&instance->config.iconsFormat, 0);
ffStrbufInitA(&instance->config.iconsKey, 0);
ffStrbufInitA(&instance->config.fontFormat, 0);
ffStrbufInitA(&instance->config.fontKey, 0);
ffStrbufInitA(&instance->config.cursorKey, 0);
ffStrbufInitA(&instance->config.cursorFormat, 0);
ffStrbufInitA(&instance->config.terminalFormat, 0);
ffStrbufInitA(&instance->config.terminalKey, 0);
ffStrbufInitA(&instance->config.termFontFormat, 0);
ffStrbufInitA(&instance->config.termFontKey, 0);
ffStrbufInitA(&instance->config.cpuFormat, 0);
ffStrbufInitA(&instance->config.cpuKey, 0);
ffStrbufInitA(&instance->config.cpuUsageFormat, 0);
ffStrbufInitA(&instance->config.cpuUsageKey, 0);
ffStrbufInitA(&instance->config.gpuFormat, 0);
ffStrbufInitA(&instance->config.gpuKey, 0);
ffStrbufInitA(&instance->config.memoryFormat, 0);
ffStrbufInitA(&instance->config.memoryKey, 0);
ffStrbufInitA(&instance->config.diskFormat, 0);
ffStrbufInitA(&instance->config.diskKey, 0);
ffStrbufInitA(&instance->config.batteryFormat, 0);
ffStrbufInitA(&instance->config.batteryKey, 0);
ffStrbufInitA(&instance->config.localeFormat, 0);
ffStrbufInitA(&instance->config.localeKey, 0);
ffStrbufInitA(&instance->config.localIpKey, 0);
ffStrbufInitA(&instance->config.localIpFormat, 0);
ffStrbufInitA(&instance->config.publicIpKey, 0);
ffStrbufInitA(&instance->config.publicIpFormat, 0);
ffStrbufInitA(&instance->config.playerKey, 0);
ffStrbufInitA(&instance->config.playerFormat, 0);
ffStrbufInitA(&instance->config.songKey, 0);
ffStrbufInitA(&instance->config.songFormat, 0);
ffStrbufInitA(&instance->config.dateTimeKey, 0);
ffStrbufInitA(&instance->config.dateTimeFormat, 0);
ffStrbufInitA(&instance->config.dateKey, 0);
ffStrbufInitA(&instance->config.dateFormat, 0);
ffStrbufInitA(&instance->config.timeKey, 0);
ffStrbufInitA(&instance->config.timeFormat, 0);
ffStrbufInitA(&instance->config.libPCI, 0);
ffStrbufInitA(&instance->config.libVulkan, 0);
ffStrbufInitA(&instance->config.libWayland, 0);
ffStrbufInitA(&instance->config.libXcbRandr, 0);
ffStrbufInitA(&instance->config.libXcb, 0);
ffStrbufInitA(&instance->config.libXrandr, 0);
ffStrbufInitA(&instance->config.libX11, 0);
ffStrbufInitA(&instance->config.libGIO, 0);
ffStrbufInitA(&instance->config.libDConf, 0);
ffStrbufInitA(&instance->config.libDBus, 0);
ffStrbufInitA(&instance->config.libXFConf, 0);
ffStrbufInitA(&instance->config.librpm, 0);
ffStrbufInitA(&instance->config.diskFolders, 0);
ffStrbufInitA(&instance->config.batteryDir, 0);
ffStrbufInitA(&instance->config.separatorString, 0);
instance->config.localIpShowIpV4 = true;
instance->config.localIpShowIpV6 = false;
instance->config.localIpShowLoop = false;
instance->config.publicIpTimeout = 0;
ffStrbufInitA(&instance->config.osFile, 0);
ffStrbufInitA(&instance->config.playerName, 0);
}
void ffInitInstance(FFinstance* instance)
{
initState(&instance->state);
defaultConfig(instance);
ffCacheValidate(instance);
}
static void resetConsole(bool disableLinewrap, bool hideCursor)
{
if(disableLinewrap)
fputs("\033[?7h", stdout);
if(hideCursor)
fputs("\033[?25h", stdout);
}
static volatile bool ffDisableLinewrap = true;
static volatile bool ffHideCursor = true;
static void exitSignalHandler(int signal)
{
FF_UNUSED(signal);
resetConsole(ffDisableLinewrap, ffHideCursor);
exit(0);
}
void ffStart(FFinstance* instance)
{
ffDisableLinewrap = instance->config.disableLinewrap;
ffHideCursor = instance->config.hideCursor;
struct sigaction action = {};
action.sa_handler = exitSignalHandler;
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGQUIT, &action, NULL);
if(instance->config.hideCursor)
fputs("\033[?25l", stdout);
if(instance->config.disableLinewrap)
fputs("\033[?7l", stdout);
}
void ffFinish(FFinstance* instance)
{
if(instance->config.printRemainingLogo)
ffPrintRemainingLogo(instance);
resetConsole(instance->config.disableLinewrap, instance->config.hideCursor);
}
void ffListFeatures()
{
fputs(
#ifdef FF_HAVE_LIBPCI
"libpci\n"
#endif
#ifdef FF_HAVE_VULKAN
"vulkan\n"
#endif
#ifdef FF_HAVE_WAYLAND
"wayland\n"
#endif
#ifdef FF_HAVE_XCB_RANDR
"xcb-randr\n"
#endif
#ifdef FF_HAVE_XCB
"xcb\n"
#endif
#ifdef FF_HAVE_XRANDR
"xrandr\n"
#endif
#ifdef FF_HAVE_X11
"x11\n"
#endif
#ifdef FF_HAVE_GIO
"gio\n"
#endif
#ifdef FF_HAVE_DCONF
"dconf\n"
#endif
#ifdef FF_HAVE_DBUS
"dbus\n"
#endif
#ifdef FF_HAVE_XFCONF
"xfconf\n"
#endif
#ifdef FF_HAVE_RPM
"librpm\n"
#endif
""
, stdout);
}
+505
View File
@@ -0,0 +1,505 @@
#include "fastfetch.h"
#include <malloc.h>
#include <unistd.h>
#include <fcntl.h>
#define FF_IO_CACHE_VALUE_EXTENSION "ffcv"
#define FF_IO_CACHE_SPLIT_EXTENSION "ffcs"
void ffPrintLogoAndKey(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat)
{
ffPrintLogoLine(instance);
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
ffPrintColor(&instance->config.color);
if(customKeyFormat == NULL || customKeyFormat->length == 0)
{
fputs(moduleName, stdout);
if(moduleIndex > 0)
printf(" %hhu", moduleIndex);
}
else
{
FFstrbuf key;
ffStrbufInit(&key);
ffParseFormatString(&key, customKeyFormat, NULL, 1, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_UINT8, &moduleIndex}
});
ffStrbufWriteTo(&key, stdout);
ffStrbufDestroy(&key);
}
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
ffStrbufWriteTo(&instance->config.separator, stdout);
}
void ffPrintError(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numFormatArgs, const char* message, ...)
{
if(!instance->config.showErrors)
return;
va_list arguments;
va_start(arguments, message);
if(formatString == NULL || formatString->length == 0)
{
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
fputs(FASTFETCH_TEXT_MODIFIER_ERROR, stdout);
vprintf(message, arguments);
puts(FASTFETCH_TEXT_MODIFIER_RESET);
}
else
{
FF_STRBUF_CREATE(error);
ffStrbufAppendVF(&error, message, arguments);
// calloc sets all to 0 and FF_FORMAT_ARG_TYPE_NULL also has value 0 so we don't need to explictly set it
FFformatarg* nullArgs = calloc(numFormatArgs, sizeof(FFformatarg));
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, &error, numFormatArgs, nullArgs);
free(nullArgs);
ffStrbufDestroy(&error);
}
va_end(arguments);
}
void ffPrintFormatString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments)
{
FFstrbuf buffer;
ffStrbufInitA(&buffer, 256);
ffParseFormatString(&buffer, formatString, error, numArgs, arguments);
if(buffer.length > 0)
{
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
ffStrbufPutTo(&buffer, stdout);
}
ffStrbufDestroy(&buffer);
}
void ffGetCacheFilePath(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer)
{
ffStrbufAppend(buffer, &instance->state.cacheDir);
ffStrbufAppendS(buffer, moduleName);
if(extension != NULL)
{
ffStrbufAppendC(buffer, '.');
ffStrbufAppendS(buffer, extension);
}
}
void ffReadCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer)
{
FFstrbuf path;
ffStrbufInitA(&path, 64);
ffGetCacheFilePath(instance, moduleName, extension, &path);
ffAppendFileContent(path.chars, buffer);
ffStrbufDestroy(&path);
}
void ffWriteCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* content)
{
FFstrbuf path;
ffStrbufInitA(&path, 64);
ffGetCacheFilePath(instance, moduleName, extension, &path);
ffWriteFileContent(path.chars, content);
ffStrbufDestroy(&path);
}
static bool printCachedValue(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat)
{
FFstrbuf content;
ffStrbufInitA(&content, 512);
ffReadCacheFile(instance, moduleName, FF_IO_CACHE_VALUE_EXTENSION, &content);
ffStrbufTrimRight(&content, '\0'); //Strbuf always appends a '\0' at the end. We want the last null byte to be at the position of the length
if(content.length == 0)
return false;
uint8_t moduleCounter = 1;
uint32_t startIndex = 0;
while(startIndex < content.length)
{
uint32_t nullByteIndex = ffStrbufNextIndexC(&content, startIndex, '\0');
uint8_t moduleIndex = (moduleCounter == 1 && nullByteIndex == content.length) ? 0 : moduleCounter;
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
puts(content.chars + startIndex);
startIndex = nullByteIndex + 1;
++moduleCounter;
}
ffStrbufDestroy(&content);
return moduleCounter > 1;
}
static bool printCachedFormat(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numArgs)
{
FFstrbuf content;
ffStrbufInitA(&content, 512);
ffReadCacheFile(instance, moduleName, FF_IO_CACHE_SPLIT_EXTENSION, &content);
ffStrbufTrimRight(&content, '\0'); //Strbuf always appends a '\0' at the end. We want the last null byte to be at the position of the length
if(content.length == 0)
return false;
uint8_t moduleCounter = 1;
FFformatarg* arguments = calloc(numArgs, sizeof(FFformatarg));
uint32_t argumentCounter = 0;
uint32_t startIndex = 0;
while(startIndex < content.length)
{
arguments[argumentCounter].type = FF_FORMAT_ARG_TYPE_STRING;
arguments[argumentCounter].value = &content.chars[startIndex];
++argumentCounter;
uint32_t nullByteIndex = ffStrbufNextIndexC(&content, startIndex, '\0');
if(argumentCounter == numArgs)
{
uint8_t moduleIndex = (moduleCounter == 1 && nullByteIndex == content.length) ? 0 : moduleCounter;
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, NULL, numArgs, arguments);
++moduleCounter;
argumentCounter = 0;
}
startIndex = nullByteIndex + 1;
}
free(arguments);
ffStrbufDestroy(&content);
return moduleCounter > 1;
}
bool ffPrintFromCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numArgs)
{
if(instance->config.recache)
return false;
if(formatString == NULL || formatString->length == 0)
return printCachedValue(instance, moduleName, customKeyFormat);
else
return printCachedFormat(instance, moduleName, customKeyFormat, formatString, numArgs);
}
void ffPrintAndAppendToCache(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, FFcache* cache, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments)
{
if(formatString == NULL || formatString->length == 0)
{
ffPrintLogoAndKey(instance, moduleName, moduleIndex, customKeyFormat);
ffStrbufPutTo(value, stdout);
}
else
{
ffPrintFormatString(instance, moduleName, moduleIndex, customKeyFormat, formatString, NULL, numArgs, arguments);
}
if(cache->value != NULL)
{
ffStrbufWriteTo(value, cache->value);
fputc('\0', cache->value);
}
if(cache->split == NULL)
return;
for(uint32_t i = 0; i < numArgs; i++)
{
FFstrbuf buffer;
ffStrbufInitA(&buffer, 64);
ffFormatAppendFormatArg(&buffer, &arguments[i]);
ffStrbufWriteTo(&buffer, cache->split);
ffStrbufDestroy(&buffer);
fputc('\0', cache->split);
}
}
void ffPrintAndSaveToCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments)
{
FFcache cache;
ffCacheOpenWrite(instance, moduleName, &cache);
ffPrintAndAppendToCache(instance, moduleName, 0, customKeyFormat, &cache, value, formatString, numArgs, arguments);
ffCacheClose(&cache);
}
void ffCacheValidate(FFinstance* instance)
{
FFstrbuf path;
ffStrbufInitA(&path, 64);
ffGetCacheFilePath(instance, "cacheversion", "ffv", &path);
FFstrbuf content;
ffStrbufInit(&content);
ffAppendFileContent(path.chars, &content);
bool isSameVersion = ffStrbufCompS(&content, FASTFETCH_PROJECT_VERSION) == 0;
ffStrbufDestroy(&content);
if(isSameVersion)
{
ffStrbufDestroy(&path);
return;
}
instance->config.recache = true;
FFstrbuf version;
ffStrbufInit(&version);
ffStrbufAppendS(&version, FASTFETCH_PROJECT_VERSION);
ffWriteFileContent(path.chars, &version);
ffStrbufDestroy(&version);
ffStrbufDestroy(&path);
}
void ffCacheOpenWrite(FFinstance* instance, const char* moduleName, FFcache* cache)
{
FFstrbuf cacheFileValue;
ffStrbufInitA(&cacheFileValue, 64);
ffGetCacheFilePath(instance, moduleName, FF_IO_CACHE_VALUE_EXTENSION, &cacheFileValue);
cache->value = fopen(cacheFileValue.chars, "w");
ffStrbufDestroy(&cacheFileValue);
FFstrbuf cacheFileSplit;
ffStrbufInitA(&cacheFileSplit, 64);
ffGetCacheFilePath(instance, moduleName, FF_IO_CACHE_SPLIT_EXTENSION, &cacheFileSplit);
cache->split = fopen(cacheFileSplit.chars, "w");
ffStrbufDestroy(&cacheFileSplit);
}
void ffCacheClose(FFcache* cache)
{
if(cache->value != NULL)
fclose(cache->value);
if(cache->split != NULL)
fclose(cache->split);
}
bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries)
{
bool* searchedValues = malloc(sizeof(bool) * numQueries);
bool allSet = true;
for(uint32_t i = 0; i < numQueries; i++)
{
if((searchedValues[i] = queries[i].buffer->length == 0))
allSet = false;
}
if(allSet)
{
free(searchedValues);
return true;
}
FILE* file = fopen(filename, "r");
if(file == NULL)
return false;
char* line = NULL;
size_t len = 0;
while (getline(&line, &len, file) != -1)
{
for(uint32_t i = 0; i < numQueries; i++)
{
if(!searchedValues[i])
continue;
uint32_t currentLength = queries[i].buffer->length;
queries[i].buffer->length = 0;
if(!ffGetPropValue(line, queries[i].start, queries[i].buffer))
queries[i].buffer->length = currentLength;
}
}
free(searchedValues);
if(line != NULL)
free(line);
fclose(file);
return true;
}
bool ffParsePropFile(const char* filename, const char* start, FFstrbuf* buffer)
{
return ffParsePropFileValues(filename, 1, (FFpropquery[]){{start, buffer}});
}
bool ffParsePropFileHomeValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries)
{
FFstrbuf absolutePath;
ffStrbufInitA(&absolutePath, 64);
ffStrbufAppendS(&absolutePath, instance->state.passwd->pw_dir);
ffStrbufAppendC(&absolutePath, '/');
ffStrbufAppendS(&absolutePath, relativeFile);
bool result = ffParsePropFileValues(absolutePath.chars, numQueries, queries);
ffStrbufDestroy(&absolutePath);
return result;
}
bool ffParsePropFileHome(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer)
{
return ffParsePropFileHomeValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}});
}
bool ffParsePropFileConfigValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries)
{
bool foundAFile = false;
for(uint32_t i = 0; i < instance->state.configDirs.length; i++)
{
FFstrbuf* baseDir = (FFstrbuf*) ffListGet(&instance->state.configDirs, i);
uint32_t baseDirLength = baseDir->length;
if(*relativeFile != '/')
ffStrbufAppendC(baseDir, '/');
ffStrbufAppendS(baseDir, relativeFile);
if(ffParsePropFileValues(baseDir->chars, numQueries, queries))
foundAFile = true;
ffStrbufSubstrBefore(baseDir, baseDirLength);
bool allSet = true;
for(uint32_t k = 0; k < numQueries; k++)
{
if(queries[k].buffer->length == 0)
{
allSet = false;
break;
}
}
if(allSet)
break;
}
return foundAFile;
}
bool ffParsePropFileConfig(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer)
{
return ffParsePropFileConfigValues(instance, relativeFile, 1, (FFpropquery[]){{start, buffer}});
}
bool ffWriteFDContent(int fd, const FFstrbuf* content)
{
return write(fd, content->chars, content->length) != -1;
}
void ffWriteFileContent(const char* fileName, const FFstrbuf* content)
{
int fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(fd == -1)
return;
ffWriteFDContent(fd, content);
close(fd);
}
void ffAppendFDContent(int fd, FFstrbuf* buffer)
{
ssize_t readed = 0;
ffStrbufEnsureFree(buffer, 31); // 32 - 1 for the null terminator
uint32_t free = ffStrbufGetFree(buffer);
while(
(readed = read(fd, buffer->chars + buffer->length, free)) > 0 &&
(uint32_t) readed == free
) {
buffer->length += (uint32_t) readed;
ffStrbufEnsureCapacity(buffer, (buffer->allocated * 2) - 1); // -1 for null terminator
free = ffStrbufGetFree(buffer);
}
// In case of failure, read returns -1. We don't want to substract the length of the buffer.
if(readed > 0)
buffer->length += (uint32_t) readed;
buffer->chars[buffer->length] = '\0';
ffStrbufTrimRight(buffer, '\n');
ffStrbufTrimRight(buffer, ' ');
}
bool ffAppendFileContent(const char* fileName, FFstrbuf* buffer)
{
int fd = open(fileName, O_RDONLY);
if(fd == -1)
return false;
ffAppendFDContent(fd, buffer);
close(fd);
return true;
}
bool ffGetFileContent(const char* fileName, FFstrbuf* buffer)
{
ffStrbufClear(buffer);
return ffAppendFileContent(fileName, buffer);
}
// Not thread safe!
void ffSuppressIO(bool suppress)
{
static bool init = false;
static int origOut = -1;
static int origErr = -1;
static int nullFile = -1;
if(!init)
{
if(!suppress)
return;
origOut = dup(STDOUT_FILENO);
origErr = dup(STDERR_FILENO);
nullFile = open("/dev/null", O_WRONLY);
init = true;
}
if(nullFile == -1)
return;
fflush(stdout);
fflush(stderr);
dup2(suppress ? nullFile : origOut, STDOUT_FILENO);
dup2(suppress ? nullFile : origErr, STDERR_FILENO);
}
void ffPrintColor(const FFstrbuf* colorValue)
{
fputs("\033[", stdout);
ffStrbufWriteTo(colorValue, stdout);
fputc('m', stdout);
}
bool ffFileExists(const char* fileName, mode_t mode)
{
struct stat fileStat;
return stat(fileName, &fileStat) == 0 && ((fileStat.st_mode & S_IFMT) == mode);
}
+25
View File
@@ -0,0 +1,25 @@
#include "fastfetch.h"
#include <stdarg.h>
void* ffLibraryLoad(const FFstrbuf* userProvidedName, ...)
{
if(userProvidedName->length > 0)
return dlopen(userProvidedName->chars, RTLD_LAZY);
va_list defaultNames;
va_start(defaultNames, userProvidedName);
void* result = NULL;
const char* name = va_arg(defaultNames, const char*);
while(result == NULL && name != NULL)
{
result = dlopen(name, RTLD_LAZY);
name = va_arg(defaultNames, const char*);
}
va_end(defaultNames);
return result;
}
+1399
View File
File diff suppressed because it is too large Load Diff
+69
View File
@@ -0,0 +1,69 @@
#include "fastfetch.h"
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
void ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, FFstrbuf* buffer)
{
struct addrinfo hints = {0};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
struct addrinfo* addr;
if(getaddrinfo(host, "80", &hints, &addr) != 0)
return;
int sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if(sock == -1)
{
freeaddrinfo(addr);
return;
}
if(timeout > 0)
{
struct timeval timev;
timev.tv_sec = 0;
timev.tv_usec = timeout * 1000; //milliseconds to microseconds
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev));
}
if(connect(sock, addr->ai_addr, addr->ai_addrlen) == -1)
{
close(sock);
freeaddrinfo(addr);
return;
}
freeaddrinfo(addr);
FFstrbuf command;
ffStrbufInitA(&command, 64);
ffStrbufAppendS(&command, "GET ");
ffStrbufAppendS(&command, path);
ffStrbufAppendS(&command, " HTTP/1.1\nHost: ");
ffStrbufAppendS(&command, host);
ffStrbufAppendS(&command, "\r\n\r\n");
if(send(sock, command.chars, command.length, 0) == -1)
{
ffStrbufDestroy(&command);
close(sock);
return;
}
ssize_t received = recv(sock, buffer->chars + buffer->length, ffStrbufGetFree(buffer), 0);
if(received > 0)
{
buffer->length += (uint32_t) received;
buffer->chars[buffer->length] = '\0';
}
ffStrbufDestroy(&command);
close(sock);
}
+409
View File
@@ -0,0 +1,409 @@
#include "fastfetch.h"
#include <string.h>
void ffGetGtkPretty(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4)
{
if(gtk2->length > 0 && gtk3->length > 0 && gtk4->length > 0)
{
if((ffStrbufIgnCaseComp(gtk2, gtk3) == 0) && (ffStrbufIgnCaseComp(gtk2, gtk4) == 0))
{
ffStrbufAppend(buffer, gtk4);
ffStrbufAppendS(buffer, " [GTK2/3/4]");
}
else if(ffStrbufIgnCaseComp(gtk2, gtk3) == 0)
{
ffStrbufAppend(buffer, gtk3);
ffStrbufAppendS(buffer, " [GTK2/3], ");
ffStrbufAppend(buffer, gtk4);
ffStrbufAppendS(buffer, " [GTK4]");
}
else if(ffStrbufIgnCaseComp(gtk3, gtk4) == 0)
{
ffStrbufAppend(buffer, gtk2);
ffStrbufAppendS(buffer, " [GTK2], ");
ffStrbufAppend(buffer, gtk4);
ffStrbufAppendS(buffer, " [GTK3/4]");
}
else
{
ffStrbufAppend(buffer, gtk2);
ffStrbufAppendS(buffer, " [GTK2], ");
ffStrbufAppend(buffer, gtk3);
ffStrbufAppendS(buffer, " [GTK3], ");
ffStrbufAppend(buffer, gtk4);
ffStrbufAppendS(buffer, " [GTK4]");
}
}
else if(gtk2->length > 0 && gtk3->length > 0)
{
if(ffStrbufIgnCaseComp(gtk2, gtk3) == 0)
{
ffStrbufAppend(buffer, gtk3);
ffStrbufAppendS(buffer, " [GTK2/3]");
}
else
{
ffStrbufAppend(buffer, gtk2);
ffStrbufAppendS(buffer, " [GTK2], ");
ffStrbufAppend(buffer, gtk3);
ffStrbufAppendS(buffer, " [GTK3]");
}
}
else if(gtk3->length > 0 && gtk4->length > 0)
{
if(ffStrbufIgnCaseComp(gtk3, gtk4) == 0)
{
ffStrbufAppend(buffer, gtk4);
ffStrbufAppendS(buffer, " [GTK3/4]");
}
else
{
ffStrbufAppend(buffer, gtk3);
ffStrbufAppendS(buffer, " [GTK3], ");
ffStrbufAppend(buffer, gtk4);
ffStrbufAppendS(buffer, " [GTK4]");
}
}
else if(gtk2->length > 0)
{
ffStrbufAppend(buffer, gtk2);
ffStrbufAppendS(buffer, " [GTK2]");
}
else if(gtk3->length > 0)
{
ffStrbufAppend(buffer, gtk3);
ffStrbufAppendS(buffer, " [GTK3]");
}
else if(gtk4->length > 0)
{
ffStrbufAppend(buffer, gtk4);
ffStrbufAppendS(buffer, " [GTK4]");
}
}
static void fontInit(FFfont* font)
{
ffStrbufInit(&font->pretty);
ffStrbufInit(&font->name);
ffStrbufInitA(&font->size, 4);
ffListInitA(&font->styles, sizeof(FFstrbuf), 4);
}
static void fontInitPretty(FFfont* font)
{
ffStrbufAppend(&font->pretty, &font->name);
if(font->size.length == 0 && font->styles.length == 0)
return;
else if(font->pretty.length == 0)
ffStrbufAppendS(&font->pretty, "default");
ffStrbufAppendS(&font->pretty, " (");
if(font->size.length > 0)
{
ffStrbufAppend(&font->pretty, &font->size);
ffStrbufAppendS(&font->pretty, "pt");
if(font->styles.length > 0)
ffStrbufAppendS(&font->pretty, ", ");
}
for(uint32_t i = 0; i < font->styles.length; i++)
{
ffStrbufAppend(&font->pretty, ffListGet(&font->styles, i));
if(i < font->styles.length - 1)
ffStrbufAppendS(&font->pretty, ", ");
}
ffStrbufAppendC(&font->pretty, ')');
}
void ffFontInitQt(FFfont* font, const char* data)
{
fontInit(font);
//See https://doc.qt.io/qt-5/qfont.html#toString
//Family
while(*data != ',' && *data != '\0')
{
ffStrbufAppendC(&font->name, *data);
++data;
}
if(*data != '\0')
++data;
ffStrbufTrim(&font->name, ' ');
//Size
while(*data != ',' && *data != '\0')
{
ffStrbufAppendC(&font->size, *data);
++data;
}
if(*data != '\0')
++data;
ffStrbufTrim(&font->size, ' ');
#define FF_FONT_QT_SKIP_VALUE \
while(*data != ',' && *data != '\0') \
++data; \
if(*data != '\0') \
++data;
FF_FONT_QT_SKIP_VALUE //Pixel size
FF_FONT_QT_SKIP_VALUE //Style hint
FF_FONT_QT_SKIP_VALUE //Font weight
FF_FONT_QT_SKIP_VALUE //Font style
FF_FONT_QT_SKIP_VALUE //Underline
FF_FONT_QT_SKIP_VALUE //Strike out
FF_FONT_QT_SKIP_VALUE //Fixed pitch
FF_FONT_QT_SKIP_VALUE //Always 0
#undef FF_FONT_QT_SKIP_VALUE
while(*data != '\0')
{
while(*data == ' ')
++data;
if(*data == '\0')
break;
FFstrbuf* style = ffListAdd(&font->styles);
ffStrbufInit(style);
while(*data != ' ' && *data != '\0')
{
ffStrbufAppendC(style, *data);
++data;
}
}
fontInitPretty(font);
}
static void fontPangoParseWord(const char** data, FFfont* font, FFstrbuf* alternativeBuffer)
{
while(**data == ' ' || **data == '\t' || **data == ',')
++(*data);
const char* wordStart = *data;
while(**data != ' ' && **data != '\t' && **data != ',' && **data != '\0' && **data != '`' && **data != '\\')
++(*data);
uint32_t wordLength = (uint32_t) (*data - wordStart);
if(wordLength == 0)
return;
if(**data == '\0' || **data == '`' || **data == '\\')
{
ffStrbufAppendNS(&font->size, wordLength, wordStart);
if(ffStrbufEndsWithS(&font->size, "px"))
ffStrbufSubstrBefore(&font->size, font->size.length - 2);
double dummy;
if(sscanf(font->size.chars, "%lf", &dummy) == 1)
return;
ffStrbufClear(&font->size);
}
if(
strncasecmp(wordStart, "Ultra", 5) == 0 ||
strncasecmp(wordStart, "Extra", 5) == 0 ||
strncasecmp(wordStart, "Semi", 4) == 0 ||
strncasecmp(wordStart, "Demi", 4) == 0 ||
strncasecmp(wordStart, "Normal", wordLength) == 0 ||
strncasecmp(wordStart, "Roman", wordLength) == 0 ||
strncasecmp(wordStart, "Oblique", wordLength) == 0 ||
strncasecmp(wordStart, "Italic", wordLength) == 0 ||
strncasecmp(wordStart, "Thin", wordLength) == 0 ||
strncasecmp(wordStart, "Light", wordLength) == 0 ||
strncasecmp(wordStart, "Bold", wordLength) == 0 ||
strncasecmp(wordStart, "Black", wordLength) == 0 ||
strncasecmp(wordStart, "Condensed", wordLength) == 0 ||
strncasecmp(wordStart, "Expanded", wordLength) == 0
) {
if(alternativeBuffer == NULL)
{
alternativeBuffer = ffListAdd(&font->styles);
ffStrbufInit(alternativeBuffer);
}
ffStrbufAppendNSExludingC(alternativeBuffer, wordLength, wordStart, '-');
if(
strncasecmp(wordStart, "Ultra ", 6) == 0 ||
strncasecmp(wordStart, "Extra ", 6) == 0 ||
strncasecmp(wordStart, "Semi ", 5) == 0 ||
strncasecmp(wordStart, "Demi ", 5) == 0
) {
fontPangoParseWord(data, font, alternativeBuffer);
}
return;
}
if(alternativeBuffer != NULL)
{
ffStrbufAppendNSExludingC(alternativeBuffer, wordLength, wordStart, '-');
return;
}
if(font->name.length > 0)
ffStrbufAppendC(&font->name, ' ');
ffStrbufAppendNS(&font->name, wordLength, wordStart);
}
void ffFontInitPango(FFfont* font, const char* data)
{
fontInit(font);
while(*data != '\0' && *data != '`' && *data != '\\')
fontPangoParseWord(&data, font, NULL);
fontInitPretty(font);
}
void ffFontInitCopy(FFfont* font, const char* name)
{
fontInit(font);
ffStrbufAppendS(&font->name, name);
fontInitPretty(font);
}
void ffFontDestroy(FFfont* font)
{
ffStrbufDestroy(&font->pretty);
ffStrbufDestroy(&font->name);
ffStrbufDestroy(&font->size);
for(uint32_t i = 0; i < font->styles.length; i++)
ffStrbufDestroy(ffListGet(&font->styles, i));
ffListDestroy(&font->styles);
}
static bool getPropValueLine(const char** line, const char* start, FFstrbuf* buffer)
{
if(**line == '\0')
return false;
//Skip any amount of whitespace at the begin of line
while(**line == ' ' || **line == '\t')
++(*line);
while(*start != '\0')
{
// Any amount of whitespace in the format string matches any amount of whitespace in the line, even none
if(*start == ' ' || *start == '\t')
{
while(*start == ' ' || *start == '\t')
++start;
while(**line == ' ' || **line == '\t')
++(*line);
continue;
}
//Line doesn't match start, skip it
if(**line != *start || **line == '\0')
return false;
//Line and start match, continue testing
++(*line);
++start;
}
char valueEnd = '\n';
//Allow faster parsing of XML
if(*(*line - 1) == '>')
valueEnd = '<';
//Skip any amount of whitespace at the begin of the value
while(**line == ' ' || **line == '\t')
++(*line);
//Allow faster parsing of quotet values
if(**line == '"' || **line == '\'')
{
valueEnd = **line;
++(*line);
}
//Copy the value to the buffer
while(**line != valueEnd && **line != '\n' && **line != '\0')
{
ffStrbufAppendC(buffer, **line);
++(*line);
}
ffStrbufTrimRight(buffer, ' ');
return true;
}
bool ffGetPropValue(const char* line, const char* start, FFstrbuf* buffer)
{
return getPropValueLine(&line, start, buffer);
}
bool ffGetPropValueFromLines(const char* lines, const char* start, FFstrbuf* buffer)
{
while(!getPropValueLine(&lines, start, buffer))
{
while(*lines != '\0' && *lines != '\n')
++lines;
if(*lines == '\0')
return false;
//Skip '\n'
++lines;
}
return true;
}
void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch)
{
if(major->length > 0)
ffStrbufAppend(buffer, major);
else if(minor->length > 0 || patch->length > 0)
ffStrbufAppendC(buffer, '1');
if(minor->length == 0 && patch->length == 0)
return;
ffStrbufAppendC(buffer, '.');
if(minor->length > 0)
ffStrbufAppend(buffer, minor);
else if(patch->length > 0)
ffStrbufAppendC(buffer, '0');
if(patch->length == 0)
return;
ffStrbufAppendC(buffer, '.');
ffStrbufAppend(buffer, patch);
}
bool ffStrSet(const char* str)
{
if(str == NULL)
return false;
while(*str != '\0')
{
if(*str != ' ' && *str != '\t' && *str != '\n' && *str != '\r')
return true;
}
return false;
}
+34
View File
@@ -0,0 +1,34 @@
#include "fastfetch.h"
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
void ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[])
{
int pipes[2];
if(pipe(pipes) == -1)
return;
pid_t childPid = fork();
if(childPid == -1)
return;
//Child
if(childPid == 0)
{
dup2(pipes[1], STDOUT_FILENO);
close(pipes[0]);
close(pipes[1]);
close(STDERR_FILENO);
execvp(argv[0], argv);
exit(901);
}
//Parent
close(pipes[1]);
waitpid(childPid, NULL, 0);
ffAppendFDContent(pipes[0], buffer);
close(pipes[0]);
}
+278
View File
@@ -0,0 +1,278 @@
#include "fastfetch.h"
#include <pthread.h>
#define FF_LIBRARY_DATA_LOAD_INIT(dataObject, userLibraryName, ...) \
static dataObject data; \
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; \
static FFInitState initState = FF_INITSTATE_UNINITIALIZED; \
pthread_mutex_lock(&mutex); \
if(initState != FF_INITSTATE_UNINITIALIZED) {\
pthread_mutex_unlock(&mutex); \
return initState == FF_INITSTATE_SUCCESSFUL ? &data : NULL; \
} \
initState = FF_INITSTATE_SUCCESSFUL; \
void* libraryHandle = ffLibraryLoad(&userLibraryName, __VA_ARGS__, NULL); \
if(libraryHandle == NULL) { \
initState = FF_INITSTATE_FAILED; \
pthread_mutex_unlock(&mutex); \
return NULL; \
} \
#define FF_LIBRARY_DATA_LOAD_SYMBOL(symbolName) \
data.ff ## symbolName = dlsym(libraryHandle, #symbolName); \
if(data.ff ## symbolName == NULL) { \
dlclose(libraryHandle); \
initState = FF_INITSTATE_FAILED; \
pthread_mutex_unlock(&mutex); \
return NULL; \
}
#define FF_LIBRARY_DATA_LOAD_RETURN \
initState = FF_INITSTATE_SUCCESSFUL; \
pthread_mutex_unlock(&mutex); \
return &data;
#define FF_LIBRARY_DATA_LOAD_ERROR \
{ \
dlclose(libraryHandle); \
initState = FF_INITSTATE_FAILED; \
pthread_mutex_unlock(&mutex); \
return NULL; \
}
#ifdef FF_HAVE_GIO
#include <gio/gio.h>
typedef struct GVariantGetters
{
FF_LIBRARY_SYMBOL(g_variant_get_string)
FF_LIBRARY_SYMBOL(g_variant_get_boolean)
FF_LIBRARY_SYMBOL(g_variant_get_int32)
} GVariantGetters;
static FFvariant getGVariantValue(GVariant* variant, FFvarianttype type, const GVariantGetters* variantGetters)
{
if(variant == NULL)
return FF_VARIANT_NULL;
else if(type == FF_VARIANT_TYPE_STRING)
return (FFvariant) {.strValue = variantGetters->ffg_variant_get_string(variant, NULL)};
else if(type == FF_VARIANT_TYPE_BOOL)
return (FFvariant) {.boolValue = (bool) variantGetters->ffg_variant_get_boolean(variant), .boolValueSet = true};
else if(type == FF_VARIANT_TYPE_INT)
return (FFvariant) {.intValue = variantGetters->ffg_variant_get_int32(variant)};
else
return FF_VARIANT_NULL;
}
typedef struct GSettingsData
{
FF_LIBRARY_SYMBOL(g_settings_schema_source_lookup)
FF_LIBRARY_SYMBOL(g_settings_schema_has_key)
FF_LIBRARY_SYMBOL(g_settings_new_full)
FF_LIBRARY_SYMBOL(g_settings_get_value)
FF_LIBRARY_SYMBOL(g_settings_get_user_value)
FF_LIBRARY_SYMBOL(g_settings_get_default_value)
FF_LIBRARY_SYMBOL(g_settings_schema_source_get_default)
GSettingsSchemaSource* schemaSource;
GVariantGetters variantGetters;
} GSettingsData;
static const GSettingsData* getGSettingsData(FFinstance* instance)
{
FF_LIBRARY_DATA_LOAD_INIT(GSettingsData, instance->config.libGIO, "libgio-2.0.so", "libgio-2.0.so.0");
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_schema_source_lookup)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_schema_has_key)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_new_full)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_get_value)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_get_user_value)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_get_default_value)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_settings_schema_source_get_default)
#define data data.variantGetters
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_string)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_boolean)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_int32)
#undef data
data.schemaSource = data.ffg_settings_schema_source_get_default();
if(data.schemaSource == NULL)
FF_LIBRARY_DATA_LOAD_ERROR
FF_LIBRARY_DATA_LOAD_RETURN
}
FFvariant ffSettingsGetGSettings(FFinstance* instance, const char* schemaName, const char* path, const char* key, FFvarianttype type)
{
const GSettingsData* data = getGSettingsData(instance);
if(data == NULL)
return FF_VARIANT_NULL;
GSettingsSchema* schema = data->ffg_settings_schema_source_lookup(data->schemaSource, schemaName, true);
if(schema == NULL)
return FF_VARIANT_NULL;
if(data->ffg_settings_schema_has_key(schema, key) == false)
return FF_VARIANT_NULL;
GSettings* settings = data->ffg_settings_new_full(schema, NULL, path);
if(settings == NULL)
return FF_VARIANT_NULL;
GVariant* variant = data->ffg_settings_get_value(settings, key);
if(variant != NULL)
return getGVariantValue(variant, type, &data->variantGetters);
variant = data->ffg_settings_get_user_value(settings, key);
if(variant != NULL)
return getGVariantValue(variant, type, &data->variantGetters);
variant = data->ffg_settings_get_default_value(settings, key);
return getGVariantValue(variant, type, &data->variantGetters);
}
#else //FF_HAVE_GIO
FFvariant ffSettingsGetGSettings(FFinstance* instance, const char* schemaName, const char* path, const char* key, FFvarianttype type)
{
FF_UNUSED(instance, schemaName, path, key, type)
return FF_VARIANT_NULL;
}
#endif //FF_HAVE_GIO
#ifdef FF_HAVE_DCONF
#include <dconf.h>
typedef struct DConfData
{
FF_LIBRARY_SYMBOL(dconf_client_read_full)
FF_LIBRARY_SYMBOL(dconf_client_new)
GVariantGetters variantGetters;
DConfClient* client;
} DConfData;
static const DConfData* getDConfData(FFinstance* instance)
{
FF_LIBRARY_DATA_LOAD_INIT(DConfData, instance->config.libDConf, "libdconf.so", "libdconf.so.1");
FF_LIBRARY_DATA_LOAD_SYMBOL(dconf_client_read_full)
FF_LIBRARY_DATA_LOAD_SYMBOL(dconf_client_new)
#define data data.variantGetters
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_string)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_boolean)
FF_LIBRARY_DATA_LOAD_SYMBOL(g_variant_get_int32)
#undef data
data.client = data.ffdconf_client_new();
if(data.client == NULL)
FF_LIBRARY_DATA_LOAD_ERROR
FF_LIBRARY_DATA_LOAD_RETURN
}
FFvariant ffSettingsGetDConf(FFinstance* instance, const char* key, FFvarianttype type)
{
const DConfData* data = getDConfData(instance);
if(data == NULL)
return FF_VARIANT_NULL;
GVariant* variant = data->ffdconf_client_read_full(data->client, key, DCONF_READ_FLAGS_NONE, NULL);
if(variant != NULL)
return getGVariantValue(variant, type, &data->variantGetters);
variant = data->ffdconf_client_read_full(data->client, key, DCONF_READ_USER_VALUE, NULL);
if(variant != NULL)
return getGVariantValue(variant, type, &data->variantGetters);
variant = data->ffdconf_client_read_full(data->client, key, DCONF_READ_DEFAULT_VALUE, NULL);
return getGVariantValue(variant, type, &data->variantGetters);
}
#else //FF_HAVE_DCONF
FFvariant ffSettingsGetDConf(FFinstance* instance, const char* key, FFvarianttype type)
{
FF_UNUSED(instance, key, type)
return FF_VARIANT_NULL;
}
#endif //FF_HAVE_DCONF
FFvariant ffSettingsGet(FFinstance* instance, const char* dconfKey, const char* gsettingsSchemaName, const char* gsettingsPath, const char* gsettingsKey, FFvarianttype type)
{
FFvariant gsettings = ffSettingsGetGSettings(instance, gsettingsSchemaName, gsettingsPath, gsettingsKey, type);
if(
(type == FF_VARIANT_TYPE_BOOL && gsettings.boolValueSet) ||
(type != FF_VARIANT_TYPE_BOOL && gsettings.strValue != NULL)
) return gsettings;
return ffSettingsGetDConf(instance, dconfKey, type);
}
#ifdef FF_HAVE_XFCONF
#include <xfconf/xfconf.h>
typedef struct XFConfData
{
FF_LIBRARY_SYMBOL(xfconf_channel_get)
FF_LIBRARY_SYMBOL(xfconf_channel_has_property)
FF_LIBRARY_SYMBOL(xfconf_channel_get_string)
FF_LIBRARY_SYMBOL(xfconf_channel_get_bool)
FF_LIBRARY_SYMBOL(xfconf_channel_get_int)
FF_LIBRARY_SYMBOL(xfconf_init)
} XFConfData;
static const XFConfData* getXFConfData(FFinstance* instance)
{
FF_LIBRARY_DATA_LOAD_INIT(XFConfData, instance->config.libXFConf, "libxfconf-0.so", "libxfconf-0.so.3");
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_get)
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_has_property)
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_get_string)
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_get_bool)
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_channel_get_int)
FF_LIBRARY_DATA_LOAD_SYMBOL(xfconf_init)
if(data.ffxfconf_init(NULL) == false)
FF_LIBRARY_DATA_LOAD_ERROR
FF_LIBRARY_DATA_LOAD_RETURN
}
FFvariant ffSettingsGetXFConf(FFinstance* instance, const char* channelName, const char* propertyName, FFvarianttype type)
{
const XFConfData* data = getXFConfData(instance);
if(data == NULL)
return FF_VARIANT_NULL;
XfconfChannel* channel = data->ffxfconf_channel_get(channelName); // Never fails according to documentation but rather returns an empty channel
if(!data->ffxfconf_channel_has_property(channel, propertyName))
return FF_VARIANT_NULL;
if(type == FF_VARIANT_TYPE_INT)
return (FFvariant) {.intValue = data->ffxfconf_channel_get_int(channel, propertyName, 0)};
if(type == FF_VARIANT_TYPE_STRING)
return (FFvariant) {.strValue = data->ffxfconf_channel_get_string(channel, propertyName, NULL)};
if(type == FF_VARIANT_TYPE_BOOL)
return (FFvariant) {.boolValue = data->ffxfconf_channel_get_bool(channel, propertyName, false), .boolValueSet = true};
return FF_VARIANT_NULL;
}
#else //FF_HAVE_XFCONF
FFvariant ffSettingsGetXFConf(FFinstance* instance, const char* channelName, const char* propertyName, FFvarianttype type)
{
FF_UNUSED(instance, channelName, propertyName, type)
return FF_VARIANT_NULL;
}
#endif //FF_HAVE_XFCONF
#ifdef __ANDROID__
#include <sys/system_properties.h>
bool ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result) {
uint32_t originalLength = result->length;
ffStrbufEnsureFree(result, PROP_VALUE_MAX);
result->length += (uint32_t) __system_property_get(propName, result->chars + result->length);
return result->length > originalLength;
}
#endif
+72
View File
@@ -0,0 +1,72 @@
#include "fastfetch.h"
#include <pthread.h>
static inline void* detectPlasmaThreadMain(void* instance)
{
ffDetectPlasma((FFinstance*)instance);
return NULL;
}
static inline void* detectGTK2ThreadMain(void* instance)
{
ffDetectGTK2((FFinstance*)instance);
return NULL;
}
static inline void* detectGTK3ThreadMain(void* instance)
{
ffDetectGTK3((FFinstance*)instance);
return NULL;
}
static inline void* detectGTK4ThreadMain(void* instance)
{
ffDetectGTK4((FFinstance*)instance);
return NULL;
}
static inline void* connectDisplayServerThreadMain(void* instance)
{
ffConnectDisplayServer((FFinstance*)instance);
return NULL;
}
static inline void* startThreadsThreadMain(void* instance)
{
pthread_t dsThread;
pthread_create(&dsThread, NULL, connectDisplayServerThreadMain, instance);
pthread_detach(dsThread);
pthread_t gtk2Thread;
pthread_create(&gtk2Thread, NULL, detectGTK2ThreadMain, instance);
pthread_detach(gtk2Thread);
pthread_t gtk3Thread;
pthread_create(&gtk3Thread, NULL, detectGTK3ThreadMain, instance);
pthread_detach(gtk3Thread);
pthread_t gtk4Thread;
pthread_create(&gtk4Thread, NULL, detectGTK4ThreadMain, instance);
pthread_detach(gtk4Thread);
pthread_t plasmaThread;
pthread_create(&plasmaThread, NULL, detectPlasmaThreadMain, instance);
pthread_detach(plasmaThread);
return NULL;
}
void ffStartDetectionThreads(FFinstance* instance)
{
//Android needs none of the things that are detected here
//And using gsettings sometimes hangs the program in android for some unknown reason,
//and since we don't need it we just never call it.
#ifdef __ANDROID__
return;
#endif
pthread_t startThreadsThread;
pthread_create(&startThreadsThread, NULL, startThreadsThreadMain, instance);
pthread_detach(startThreadsThread);
}
+224
View File
@@ -0,0 +1,224 @@
# Fastfetch configuration
# Write every argument in different lines.
# Direct arguments will overwrite the corresponding ones in this file.
# Argument keys are not case sensitive.
# Whitespaces are trimmed at the beginning and the end.
# Empty lines or lines starting with # are ignored.
# This file was shipped with $"FASTFETCH_PROJECT_VERSION$".
# Use fastfetch --print-config > ~/.config/fastfetch/config.conf to overwrite this with the current defaults
# Below some often usefull options are listed. Uncomment and modify them so they take affect.
# Note that there are a lot more options than the ones listed here, take a look at "fastfetch --help".
# Of course all of them can be made persistent here too.
# Config option:
# Load additional config files.
# Some are shipped with fastfetch, list them with "fastfetch --list-presets".
# Must be a path to a config file or the name of a shipped preset.
# The config file is completly loaded before continuing in the current file, so the placement of this option matters, as later options overwrite already set ones.
# Can be used multiple times to load multiple config files / presets.
#--load-config /path/to/config.txt
# Structure option:
# Sets the modules to use and their order.
# Must be a list of module names, separated by colons.
# List available modules with "fastfetch --list-modules".
# Get the default structure with "fastfetch --print-structure".
#--structure $"FASTFETCH_DATATEXT_STRUCTURE$"
# Logo option:
# Sets the logo to use.
# List available logos with "fastfetch --list-logos".
# Print available logos with "fastfetch --print-logos".
# Must be the name of an available logo or a path to a text file containing a custom logo.
# Default is the current distribution.
#--logo arch
# Color option:
# Sets the color of the keys.
# Must be linux console color codes or the name of a color.
# Default is the primary color of the logo.
# Use "fastfetch --help color" to learn more and see examples.
#--color magenta
# Spacing option:
# Sets the amount of whitespace between the logo and keys.
# Must be an positive integer.
# Default is 4.
#--spacing 4
# Separator option:
# Sets the string placed between a key and its value.
# Can be any string.
# Default is ": ".
#--separator ": "
# Offset option:
# Sets the amount of whitespace before the logo. Can be negative to cut the logo.
# Must be an integer.
# Default is 0.
#--offsetx 0
# Linewrap option:
# Sets if fastfetch should disable linewrap during the run.
# Must be true or false.
# Default is true.
#--disable-linewrap true
# Cursor option:
# Sets if fastfetch should hide the console cursor during the run.
# Must be true or false.
# Default is true.
#--hide-cursor true
# Remaing logo option:
# Sets if the remaining logo should be printed, if it is higher than the information printed.
# Must be true or false.
# Default is true.
#--print-remaining-logo true
# Multithreading option:
# Sets if fastfetch should use multiple threads to detect the values.
# Must be true or false.
# Default is true.
#--multithreading true
# Slow operations option:
# Sets if fastfetch is allowed to use known slow operations to detect more / better values.
# Must be true or false.
# Default is false.
#--allow-slow-operations false
# Color logo option:
# Sets if the logo should be colored.
# Must be true or false.
# Default is true.
#--color-logo true
# Separator string option:
# Sets the string printed by the "separator" module (usually between title and rest of output)
# Must be any string. It is repated / cut to fit perfectly.
# Default is "-"
#--separator-string -
# Logo color options:
# Overwrite a color in the logo. Also works for user provided logos.
# In the user logo, they replace $[1-9]. Use $$ to print a single $ sign.
# Must be linux console color codes or the name of a color.
# Default is the one specified by the logo.
# Use "fastfetch --help color" to learn more and see examples.
# Valid index range is [1-9].
#--color-1 red
#--color-2 32
# [...]
#--color-9 yellow
# User logo raw option:
# Sets if color replacements should be performed on the user provided logo.
# Must be true or false.
# Default is true.
#--color-raw true
# Public IP timeout option:
# Sets the time to wait for the public ip server to respond.
# Must be a positive integer.
# Default is 0 (disabled).
#--public-ip-timeout 0
# OS file option
# Sets the path to the file containing the operating system information.
# Should be a valid path to an existing file.
# Note that you might need to run fastfetch with --recache once for it to take affect.
# Default is /etc/os-release.
#--os-file /etc/os-release
# Player name option
# Sets the name of the player. This is also used in song detection
# Must be the exact name of the player or a dbus address (e.g. org.mpris.MediaPlayer2.spotify)
# Default is the first match starting with org.mpris.MediaPlayer2.
#--player-name spotify
# Key options:
# Sets the displayed key of a module
# Can be any string. Some of theme take an argument like a format string. See "fastfetch --help format" for help.
#--os-key OS
#--host-key Host
#--kernel-key Kernel
#--uptime-key Uptime
#--processes-key Processes
#--packages-key Packages
#--shell-key Shell
#--resolution-key Resolution {1}
#--de-key DE
#--wm-key WM
#--wm-theme-key WM Theme
#--theme-key Theme
#--icons-key Icons
#--font-key Font
#--cursor-key Cursor
#--terminal-key Terminal
#--terminal-font-key Terminal Font
#--cpu-key CPU
#--cpu-usage-key CPU Usage
#--gpu-key GPU {1}
#--memory-key Memory
#--disk-key Disk ({1})
#--battery-key Battery {1}
#--locale-key Locale
#--local-ip-key Local IP ({1})
#--public-ip-key Public IP
#--player-key Media Player
#--song-key Song
#--datetime-key Date Time
# Format options:
# Sets the format string for module values.
# For information on format strings, see "fastfetch --help format".
# To see the parameter they take and their default value, see "fastfetch --help *-format", e.g. "fastfetch --help os-format".
# An empty format string (As they are currently below) will behave as if it was not set.
#--os-format
#--host-format
#--kernel-format
#--uptime-format
#--processes-format
#--packages-format
#--shell-format
#--resolution-format
#--de-format
#--wm-format
#--wm-theme-format
#--theme-format
#--icons-format
#--font-format
#--cursor-format
#--terminal-format
#--terminal-font-format
#--cpu-format
#--cpu-usage-format
#--gpu-format
#--memory-format
#--disk-format
#--battery-format
#--locale-format
#--local-ip-format
#--public-ip-format
#--player-format
#--song-format
#--datetime-format
# Library options:
# Sets an user specific path to a library to load.
# Must be a valid path to a library.
#--lib-PCI /usr/lib/libpci.so
#--lib-vulkan /usr/lib/libvulkan.so
#--lib-wayland /usr/lib/libwayland-client.so
#--lib-xcb-randr /usr/lib/libxcb-randr.so
#--lib-xcb /usr/lib/libxcb.so
#--lib-Xrandr /usr/lib/libXrandr.so
#--lib-X11 /usr/lib/libX11.so
#--lib-gio /usr/lib/libgio-2.0.so
#--lib-DConf /usr/lib/libdconf.so
#--lib-DBus /usr/lib/libdbus-1.so
#--lib-XFConf /usr/lib/libxfconf-0.so
#--lib-rpm /usr/lib/librpm.so
+132
View File
@@ -0,0 +1,132 @@
Usage: fastfetch <options>
Informative options:
-h, --help: shows this message and exits
-h <command>, --help <command>: shows help for a specific command and exits
-v --version: prints the version of fastfetch and exits
--list-logos: list available logos and exits
--list-modules: lists available modules and exits
--list-presets: list presets fastfetch knows about and exits. They can be loaded with --load-config. (+)
--list-features: list the supported features fastfetch was compiled with and exits. Mainly for development.
--print-logos: show available logos and exits
--print-config: prints the default config and exits
--print-structure: prints the default stucture and exits
General options:
--structure <structure>: sets the structure of the fetch. Must be a colon separated list of keys. Use "fastfetch --list-modules" to see the ones available.
--set <key=value>: hard set the value of a key
-c <color>, --color <color>: sets the color of the keys. Must be a linux console color code or the name of a color (+)
--color-[1-9] <color>: overwrites a color of the logo. Must be a linux console color code or the name of a color
--spacing <width>: sets the distance between logo and text
-s <str>, --separator <str>: sets the separator between key and value. Default is a colon with a space
-x <offset>, --offsetx <offset>: sets the x offset. Can be negative to cut the logo, but no more than logo width.
--show-errors <?value>: print occuring errors
-r <?value> --recache <?value>: generate new cached values
--nocache <?value>: don't use cached values, but also don't overwrite existing ones
--print-remaining-logo <?value>: print the remaining logo, if it is higher than the number of lines shown
--multithreading <?value>: use multiple threads to detect values
--load-config <file>: load a config file (+)
--allow-slow-operations <?value>: Allow operations that are usually very slow for more detailed output
--disable-linewrap <?value>: Disable linewrap during the run
--hide-cursor <?value>: Hide the cursor during the run
--logo-raw <?value>: Print a custom logo as is, without any color replacements
Logo options:
-l <name>, --logo <name>: sets the shown logo. Also changes the main color accordingly. This will also load file contents as logo if the given argument is a valid path.
--color-logo <?value>: if set to false, the logo will be black / white
Format options: Provide the format string for custom output. Use fastfetch --help *-format for specific help.
--os-format <format>
--host-format <format>
--kernel-format <format>
--uptime-format <format>
--processes-format <format>
--packages-format <format>
--shell-format <format>
--resolution-format <format>
--de-format <format>
--wm-format <format>
--wm-theme-format <format>
--theme-format <format>
--icons-format <format>
--font-format <format>
--cursor-format <format>
--terminal-format <format>
--terminal-font-format <format>
--cpu-format <format>
--cpu-usage-format <format>
--gpu-format <format>
--memory-format <format>
--disk-format <format>
--battery-format <format>
--locale-format <format>
--local-ip-format <format>
--public-ip-format <format>
--player-format <format>
--song-format <format>
--datetime-format <format>
--time-format <format>
--date-format <format>
Key options: Provide a custom key for an output
--os-key <key>
--host-key <key>
--kernel-key <key>
--uptime-key <key>
--processes-key <key>
--packages-key <key>
--shell-key <key>
--resolution-key <key>: takes the resolution index as argument
--de-key <key>
--wm-key <key>
--wm-theme-key <key>
--theme-key <key>
--icons-key <key>
--font-key <key>
--cursor-key <key>
--terminal-key <key>
--terminal-font-key <key>
--cpu-key <key>
--cpu-usage-key <key>
--gpu-key <key>: takes the gpu index as format argument
--memory-key <key>
--disk-key <key>: takes the mount path as format argument
--battery-key <key>: takes the battery index as format argument
--locale-key <key>
--local-ip-key <key>: takes the name of this network interface as format argument
--public-ip-key <key>
--player-key <key>
--song-key <key>
--datetime-key <key>
--time-key <key>
--date-key <key>
Library options: Set the path of a library to load
--lib-PCI <path>
--lib-vulkan <path>
--lib-wayland <path>
--lib-xcb-randr <path>
--lib-xcb <path>
--lib-Xrandr <path>
--lib-X11 <path>
--lib-gio <path>
--lib-DConf <path>
--lib-DBus <path>
--lib-XFConf <path>
--lib-RPM <path>
Module specific options:
--separator-string <str>: Set the string printed by the separator module
--os-file <path>: Set the path to the file containing OS informations
--disk-folders <folders>: A colon separated list of folder paths for the disk output. Default is "/:/home"
--battery-dir <folder>: The directory where the battery folders are. Standard: /sys/class/power_supply/
--localip-show-ipv4 <?value>: Show ipv4 addresses in local ip module. Default is true
--localip-show-ipv6 <?value>: Show ipv6 addresses in local ip module. Default is false
--localip-show-loop <?value>: Show loop back addresses (127.0.0.1) in local ip module. Default is false
--public-ip-timeout: Time in milliseconds to wait for the public ip server to respond. Default is disabled (0)
--player-name: The name of the player to use
Parsing is not case sensitive. E.g. "--lib-PCI" is equal to "--Lib-Pci"
If a value starts with a ?, it is optional. "true" will be used if not set.
A (+) at the end indicates that more help can be printed with --help <option>
All options can be made permanent in $XDG_CONFIG_HOME/fastfetch/config.conf
+9
View File
@@ -0,0 +1,9 @@
usage: fastfetch --color <color>
<color> must be a color encoding for linux terminals. It is inserted between "ESC[" and "m".
Infos about them can be found here: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters.
Examples:
--color 35: sets the color to pink
--color 4;92: sets the color to bright Green with underline
--color 5;104: blinking text on a blue background
If no color is set, the main color of the logo will be used.
+9
View File
@@ -0,0 +1,9 @@
usage: fastfetch --load-config <file>
Loads a config file. A config file contains one flag per line. Empty lines or lines starting with # are ignored.
If the file is relative it looks in the following order:
- relative to the current working directory
- relative to ~/.local/share/fastfetch/presets/
- relative to /usr/share/fastfetch/presets/
Fastfetch provides some default presets. List them with --print-available-presets.
Note that this will only print presets fastfetch knows about and not every possible one.
+42
View File
@@ -0,0 +1,42 @@
A format string is a string that contains placeholders for values.
These placeholders begin with a '{', containing the index of the value, and end with a '}'.
For example the format string "Values: {1} ({2})", with the values "First" and "My second val", will produce
The format string can contain placeholders in any order and have multiple occurences.
To include spaces when setting from the command line, surround the whole string with double quotes (").
If the value index is missing, meaning the placeholder is "{}", an internal counter sets the value index.
This means that the format string "Values: {1} ({2})" is equivalent to "Values: {} ({})".
Note that this counter only counts empty placeholders, so the format string "{2} {} {}" will contain the second v
To make formatting easier, a double open curly brace ("{{") will be printed as a single open curly brace and not
If a value index is misformatted or wants a non-existing value, it will be printed as is, with the curly braces aro
If the last placeholder isn't closed, it will be treated like it was at the end of the format string.
To only print something if a variable is set, use "{?<index>} ... {?}".
For example, to only print a second value if it is set, use "{?2} Second value: {2}{?}".
If a "{?}" is found without an opener, it is printed as is.
To only print something if a variable is not set, do the same as with if, just replace every '?' with a '!'.
For example to print a fallback for a second value if it is not set, use "{?2}{2}{?}{/2}Second value fallback{/}"
There is a special variable set if an error occured during detection. You can access it with "{e}", "{error}" o
You can use ifs and not ifs with it like with an index. For example use "{?e}some text{?}", to print a text if an
To stop formatting at any point in the format string, use "{-}".
For example to print an error instead of the normal output if it occured, prefix the format string with "{?e}{e}{-
To print something with color, start a placeholder with a '#' and then the linux terminal color encoding.
"\\033[" at the start, and an 'm' at the end is automatically added, so don't do that.
A "{#}" is equivalent to a "{#0}" and resets everything to normal.
For example to print something pink and underline, use "{#4;35}...{#}".
If not in a format string, fastfetch wraps errors with "{#1;31}error{#}", so you might want to do that to if you
Information about what the numbers mean can be found here: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Sele
Which escape codes are supported and how they look is defined by your terminal.
If a format string evaluates to an empty value, the whole line in the output will be discarded.
You can therefore use --host-format " " to disable host output.
Note that --host-format "" would evaluate as not set, and therefore use the built-in host format
This can be used to print nothing if an error occured: prefix the format string with "{?e}{-}{?}...".
Format string is also the way to go to set a fixed value - just use one without placeholders.
For example when running in headless mode, you could use "--resolution-format "Preferred".
+35
View File
@@ -0,0 +1,35 @@
Battery
Break
Colors
CPU
CPUUsage
Cursor
Date
Datetime
DE
Disk
Font
GPU
Host
Icons
Kernel
Locale
LocalIP
Memory
OS
Packages
Player
Processes
PublicIP
Resolution
Separator
Shell
Song
Terminal
TerminalFont
Theme
Time
Title
Uptime
WM
WMTheme
+1
View File
@@ -0,0 +1 @@
Title:Separator:OS:Host:Kernel:Uptime:Packages:Shell:Resolution:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:GPU:Memory:Disk:Battery:Locale:Break:Colors
+73
View File
@@ -0,0 +1,73 @@
#include "fastfetch.h"
#include <time.h>
#include <pthread.h>
const FFDateTimeResult* ffDetectDateTime(const FFinstance* instance)
{
FF_UNUSED(instance); //We may need it later for additional configuration
static FFDateTimeResult result;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static bool init = false;
pthread_mutex_lock(&mutex);
if (init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
const time_t t = time(NULL);
struct tm* tm = localtime(&t);
result.year = (uint16_t) (tm->tm_year + 1900);
result.yearShort = (uint8_t) (result.year % 100);
result.month = (uint8_t) (tm->tm_mon + 1);
ffStrbufInit(&result.monthPretty);
result.monthPretty.length = (uint32_t) strftime(result.monthPretty.chars, ffStrbufGetFree(&result.monthPretty), "%m", tm);
ffStrbufInit(&result.monthName);
result.monthName.length = (uint32_t) strftime(result.monthName.chars, ffStrbufGetFree(&result.monthName), "%B", tm);
ffStrbufInit(&result.monthNameShort);
result.monthNameShort.length = (uint32_t) strftime(result.monthNameShort.chars, ffStrbufGetFree(&result.monthNameShort), "%b", tm);
result.week = (uint8_t) (tm->tm_yday / 7 + 1);
ffStrbufInit(&result.weekday);
result.weekday.length = (uint32_t) strftime(result.weekday.chars, ffStrbufGetFree(&result.weekday), "%A", tm);
ffStrbufInit(&result.weekdayShort);
result.weekdayShort.length = (uint32_t) strftime(result.weekdayShort.chars, ffStrbufGetFree(&result.weekdayShort), "%a", tm);
result.dayInYear = (uint8_t) (tm->tm_yday + 1);
result.dayInMonth = (uint8_t) tm->tm_mday;
result.dayInWeek = tm->tm_wday == 0 ? 7 : (uint8_t) tm->tm_wday;
result.hour = (uint8_t) tm->tm_hour;
ffStrbufInit(&result.hourPretty);
result.hourPretty.length = (uint32_t) strftime(result.hourPretty.chars, ffStrbufGetFree(&result.hourPretty), "%H", tm);
result.hour12 = (uint8_t) (result.hour % 12);
ffStrbufInit(&result.hour12Pretty);
result.hour12Pretty.length = (uint32_t) strftime(result.hour12Pretty.chars, ffStrbufGetFree(&result.hour12Pretty), "%I", tm);
result.minute = (uint8_t) tm->tm_min;
ffStrbufInit(&result.minutePretty);
result.minutePretty.length = (uint32_t) strftime(result.minutePretty.chars, ffStrbufGetFree(&result.minutePretty), "%M", tm);
result.second = (uint8_t) tm->tm_sec;
ffStrbufInit(&result.secondPretty);
result.secondPretty.length = (uint32_t) strftime(result.secondPretty.chars, ffStrbufGetFree(&result.secondPretty), "%S", tm);
pthread_mutex_unlock(&mutex);
return &result;
}
+132
View File
@@ -0,0 +1,132 @@
#include "displayServer.h"
#include <pthread.h>
#include <dirent.h>
uint32_t ffdsParseRefreshRate(int32_t refreshRate)
{
if(refreshRate <= 0)
return 0;
int remainder = refreshRate % 5;
if(remainder >= 3)
refreshRate += (5 - remainder);
else
refreshRate -= remainder;
//All other typicall refresh rates are dividable by 5
if(refreshRate == 145)
refreshRate = 144;
return (uint32_t) refreshRate;
}
bool ffdsAppendResolution(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate)
{
if(width == 0 || height == 0)
return false;
FFResolutionResult* resolution = ffListAdd(&result->resolutions);
resolution->width = width;
resolution->height = height;
resolution->refreshRate = refreshRate;
return true;
}
static void parseDRM(FFDisplayServerResult* result)
{
const char* drmDirPath = "/sys/class/drm/";
DIR* dirp = opendir(drmDirPath);
if(dirp == NULL)
return;
FFstrbuf drmDir;
ffStrbufInitA(&drmDir, 64);
ffStrbufAppendS(&drmDir, drmDirPath);
uint32_t drmDirLength = drmDir.length;
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
ffStrbufAppendS(&drmDir, entry->d_name);
ffStrbufAppendS(&drmDir, "/modes");
FILE* modeFile = fopen(drmDir.chars, "r");
if(modeFile == NULL)
{
ffStrbufSubstrBefore(&drmDir, drmDirLength);
continue;
}
FFResolutionResult* resolution = ffListAdd(&result->resolutions);
resolution->width = 0;
resolution->height = 0;
resolution->refreshRate = 0;
int scanned = fscanf(modeFile, "%ix%i", &resolution->width, &resolution->height);
if(scanned < 2 || resolution->width == 0 || resolution->height == 0)
--result->resolutions.length;
fclose(modeFile);
ffStrbufSubstrBefore(&drmDir, drmDirLength);
}
closedir(dirp);
ffStrbufDestroy(&drmDir);
}
const FFDisplayServerResult* ffConnectDisplayServer(const FFinstance* instance)
{
static FFDisplayServerResult result;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static bool init = false;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
ffStrbufInit(&result.wmProcessName);
ffStrbufInit(&result.wmPrettyName);
ffStrbufInit(&result.wmProtocolName);
ffStrbufInit(&result.deProcessName);
ffStrbufInit(&result.dePrettyName);
ffStrbufInit(&result.deVersion);
ffListInitA(&result.resolutions, sizeof(FFResolutionResult), 4);
//We try wayland as our prefered display server, as it supports the most features.
//This method can't detect the name of our WM / DE
ffdsConnectWayland(instance, &result);
//Try the x11 libs, from most feature rich to least.
//We use the resolution list to detect if a connection is needed.
//They respect wmProtocolName, and only detect resolution if it is set.
if(result.resolutions.length == 0)
ffdsConnectXcbRandr(instance, &result);
if(result.resolutions.length == 0)
ffdsConnectXrandr(instance, &result);
if(result.resolutions.length == 0)
ffdsConnectXcb(instance, &result);
if(result.resolutions.length == 0)
ffdsConnectXlib(instance, &result);
//This resolution detection method is display server independent.
//Use it if all connections failed
if(result.resolutions.length == 0)
parseDRM(&result);
//This fills in missing information about WM / DE by using env vars and iterating processes
ffdsDetectWMDE(instance, &result);
pthread_mutex_unlock(&mutex);
return &result;
}
@@ -0,0 +1,25 @@
#pragma once
#ifndef FASTFETCH_INCLUDED_DISPLAYSERVER
#define FASTFETCH_INCLUDED_DISPLAYSERVER
#include "fastfetch.h"
#define FF_DISPLAYSERVER_PROTOCOL_WAYLAND "Wayland"
#define FF_DISPLAYSERVER_PROTOCOL_X11 "X11"
#define FF_DISPLAYSERVER_PROTOCOL_TTY "TTY"
uint32_t ffdsParseRefreshRate(int32_t refreshRate);
bool ffdsAppendResolution(FFDisplayServerResult* result, uint32_t width, uint32_t height, uint32_t refreshRate);
void ffdsConnectWayland(const FFinstance* instance, FFDisplayServerResult* result);
void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result);
void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result);
void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result);
void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result);
void ffdsDetectWMDE(const FFinstance* instance, FFDisplayServerResult* result);
#endif
+166
View File
@@ -0,0 +1,166 @@
#include "displayServer.h"
#include <string.h>
#ifdef FF_HAVE_WAYLAND
#include <wayland-client.h>
#include <pthread.h>
typedef struct WaylandData
{
const FFinstance* instance;
FFlist* results;
FF_LIBRARY_SYMBOL(wl_proxy_marshal_constructor_versioned)
FF_LIBRARY_SYMBOL(wl_proxy_add_listener)
FF_LIBRARY_SYMBOL(wl_proxy_destroy)
const struct wl_interface* ffwl_output_interface;
struct wl_output_listener output_listener;
} WaylandData;
static void waylandGlobalRemoveListener(void* data, struct wl_registry* wl_registry, uint32_t name){
FF_UNUSED(data, wl_registry, name);
}
static void waylandOutputGeometryListener(void* data, struct wl_output* wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char* make, const char* model, int32_t transform)
{
FF_UNUSED(data, wl_output, x, y, physical_width, physical_height, subpixel, make, model, transform);
}
static void waylandOutputDoneListener(void* data, struct wl_output* wl_output)
{
FF_UNUSED(data, wl_output);
}
static void waylandOutputScaleListener(void* data, struct wl_output* wl_output, int32_t factor)
{
FF_UNUSED(data, wl_output, factor);
}
static void waylandOutputModeListener(void* data, struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refreshRate)
{
if(!(flags & WL_OUTPUT_MODE_CURRENT))
return;
WaylandData* wldata = (WaylandData*) data;
wldata->ffwl_proxy_destroy((struct wl_proxy*) output);
if(width <= 0 || height <= 0)
return;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
FFResolutionResult* result = ffListAdd(wldata->results);
pthread_mutex_unlock(&mutex);
result->width = (uint32_t) width;
result->height = (uint32_t) height;
result->refreshRate = ffdsParseRefreshRate(refreshRate / 1000);
}
static void waylandGlobalAddListener(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
{
if(strcmp(interface, "wl_output") == 0)
{
WaylandData* wldata = (WaylandData*) data;
struct wl_output* output = (struct wl_output*) wldata->ffwl_proxy_marshal_constructor_versioned((struct wl_proxy *) registry, WL_REGISTRY_BIND, wldata->ffwl_output_interface, version, name, wldata->ffwl_output_interface->name, version, NULL);
if(output == NULL)
return;
wldata->ffwl_proxy_add_listener((struct wl_proxy*) output, (void(**)(void)) &wldata->output_listener, data);
}
}
void ffdsConnectWayland(const FFinstance* instance, FFDisplayServerResult* result)
{
if(getenv("XDG_RUNTIME_DIR") == NULL)
return;
FF_LIBRARY_LOAD(wayland, instance->config.libWayland, , "libwayland-client.so", "libwayland-client.so.0")
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_connect,)
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_dispatch,)
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_roundtrip,)
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_proxy_marshal_constructor,)
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_display_disconnect,)
FF_LIBRARY_LOAD_SYMBOL(wayland, wl_registry_interface,)
WaylandData data;
FF_LIBRARY_LOAD_SYMBOL_ADRESS(wayland, data.ffwl_proxy_marshal_constructor_versioned, wl_proxy_marshal_constructor_versioned,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(wayland, data.ffwl_proxy_add_listener, wl_proxy_add_listener,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(wayland, data.ffwl_output_interface, wl_output_interface,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(wayland, data.ffwl_proxy_destroy, wl_proxy_destroy,)
struct wl_display* display = ffwl_display_connect(NULL);
if(display == NULL)
{
dlclose(wayland);
return;
}
struct wl_registry* registry = (struct wl_registry*) ffwl_proxy_marshal_constructor((struct wl_proxy*) display, WL_DISPLAY_GET_REGISTRY, ffwl_registry_interface, NULL);
if(registry == NULL)
{
ffwl_display_disconnect(display);
dlclose(wayland);
return;
}
data.instance = instance;
data.results = &result->resolutions;
struct wl_registry_listener regestry_listener;
regestry_listener.global = waylandGlobalAddListener;
regestry_listener.global_remove = waylandGlobalRemoveListener;
data.output_listener.geometry = waylandOutputGeometryListener;
data.output_listener.mode = waylandOutputModeListener;
data.output_listener.done = waylandOutputDoneListener;
data.output_listener.scale = waylandOutputScaleListener;
data.ffwl_proxy_add_listener((struct wl_proxy*) registry, (void(**)(void)) &regestry_listener, &data);
ffwl_display_dispatch(display);
ffwl_display_roundtrip(display);
data.ffwl_proxy_destroy((struct wl_proxy*) registry);
ffwl_display_disconnect(display);
dlclose(wayland);
//We successfully connected to wayland and detected the resolution.
//So we can set set the session type to wayland.
//This is used as an indicator that we are running wayland by the x11 backends.
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_WAYLAND);
}
#else
void ffdsConnectWayland(const FFinstance* instance, FFDisplayServerResult* result)
{
FF_UNUSED(instance);
//We try to detect wayland already here, so the x11 methods only to resolution detection.
//Wayland requires this to be set
if(getenv("XDG_RUNTIME_DIR") == NULL)
return;
const char* xdgSessionType = getenv("XDG_SESSION_TYPE");
//If XDG_SESSION_TYPE is set, and doesn't contain "wayland", we are probably not running in a wayland session.
if(xdgSessionType != NULL && strcasecmp(xdgSessionType, "wayland") != 0)
return;
//If XDG_SESSION_TYPE is not set, check if WAYLAND_DISPLAY or WAYLAND_SOCKET is set.
//If not, there is no indicator for a wayland session
if(xdgSessionType == NULL && getenv("WAYLAND_DISPLAY") == NULL && getenv("WAYLAND_SOCKET") == NULL)
return;
//We are probably running a wayland compositor at this point, but fastfetch was compiled without the required library.
//Set the protocol name and use DRM for resolution detection.
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_WAYLAND);
}
#endif
+430
View File
@@ -0,0 +1,430 @@
#include "displayServer.h"
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
static const char* parseEnv()
{
const char* env;
env = getenv("XDG_CURRENT_DESKTOP");
if(ffStrSet(env))
return env;
env = getenv("XDG_SESSION_DESKTOP");
if(ffStrSet(env))
return env;
env = getenv("CURRENT_DESKTOP");
if(ffStrSet(env))
return env;
env = getenv("SESSION_DESKTOP");
if(ffStrSet(env))
return env;
env = getenv("DESKTOP_SESSION");
if(ffStrSet(env))
return env;
if(getenv("KDE_FULL_SESSION") != NULL || getenv("KDE_SESSION_UID") != NULL || getenv("KDE_SESSION_VERSION") != NULL)
return "KDE";
if(getenv("GNOME_DESKTOP_SESSION_ID") != NULL)
return "Gnome";
if(getenv("MATE_DESKTOP_SESSION_ID") != NULL)
return "Mate";
if(getenv("TDE_FULL_SESSION") != NULL)
return "Trinity";
return NULL;
}
static void applyPrettyNameIfWM(FFDisplayServerResult* result, const char* processName)
{
if(!ffStrSet(processName))
return;
if(strcasecmp(processName, "kwin_wayland") == 0 || strcasecmp(processName, "kwin_x11") == 0 || strcasecmp(processName, "kwin") == 0)
ffStrbufSetS(&result->wmPrettyName, "KWin");
else if(strcasecmp(processName, "sway") == 0)
ffStrbufSetS(&result->wmPrettyName, "Sway");
else if(strcasecmp(processName, "weston") == 0)
ffStrbufSetS(&result->wmPrettyName, "Weston");
else if(strcasecmp(processName, "wayfire") == 0)
ffStrbufSetS(&result->wmPrettyName, "Wayfire");
else if(strcasecmp(processName, "openbox") == 0)
ffStrbufSetS(&result->wmPrettyName, "Openbox");
else if(strcasecmp(processName, "xfwm4") == 0)
ffStrbufSetS(&result->wmPrettyName, "Xfwm4");
else if(strcasecmp(processName, "Marco") == 0)
ffStrbufSetS(&result->wmPrettyName, "Marco");
else if(strcasecmp(processName, "xmonad") == 0)
ffStrbufSetS(&result->wmPrettyName, "XMonad");
else if(strcasecmp(processName, "gnome-session-binary") == 0 || strcasecmp(processName, "Mutter") == 0)
ffStrbufSetS(&result->wmPrettyName, "Mutter");
else if(strcasecmp(processName, "cinnamon-session") == 0 || strcasecmp(processName, "Muffin") == 0)
ffStrbufSetS(&result->wmPrettyName, "Muffin");
else if( // WMs where the pretty name matches the process name
strcasecmp(processName, "dwm") == 0 ||
strcasecmp(processName, "bspwm") == 0 ||
strcasecmp(processName, "tinywm") == 0
) ffStrbufSetS(&result->wmPrettyName, processName);
if(result->wmPrettyName.length > 0 && result->wmProcessName.length == 0)
ffStrbufSetS(&result->wmProcessName, processName);
}
static void applyBetterWM(FFDisplayServerResult* result, const char* processName)
{
if(!ffStrSet(processName))
return;
//If it is a known wm, this will set the pretty name
applyPrettyNameIfWM(result, processName);
//If it isn't a known wm, we have to set the process name our self
ffStrbufSetS(&result->wmProcessName, processName);
//If it isn't a known wm, set the pretty name to the process name
if(result->wmPrettyName.length == 0)
ffStrbufAppend(&result->wmPrettyName, &result->wmProcessName);
}
static void getKDE(FFDisplayServerResult* result)
{
ffStrbufSetS(&result->deProcessName, "plasmashell");
ffStrbufSetS(&result->dePrettyName, "KDE Plasma");
ffParsePropFile("/usr/share/xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion);
applyBetterWM(result, getenv("KDEWM"));
}
static void getGnome(FFDisplayServerResult* result)
{
ffStrbufSetS(&result->deProcessName, "gnome-shell");
ffStrbufSetS(&result->dePrettyName, "GNOME");
ffParsePropFile("/usr/share/gnome-shell/org.gnome.Extensions", "version :", &result->deVersion);
}
static void getCinnamon(FFDisplayServerResult* result)
{
ffStrbufSetS(&result->deProcessName, "cinnamon");
ffStrbufSetS(&result->dePrettyName, "Cinnamon");
ffParsePropFile("/usr/share/applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", &result->deVersion);
}
static void getMate(const FFinstance* instance, FFDisplayServerResult* result)
{
ffStrbufSetS(&result->deProcessName, "mate-session");
ffStrbufSetS(&result->dePrettyName, "MATE");
FFstrbuf major;
ffStrbufInit(&major);
FFstrbuf minor;
ffStrbufInit(&minor);
FFstrbuf micro;
ffStrbufInit(&micro);
ffParsePropFileValues("/usr/share/mate-about/mate-version.xml", 3, (FFpropquery[]) {
{"<platform>", &major},
{"<minor>", &minor},
{"<micro>", &micro}
});
ffParseSemver(&result->deVersion, &major, &minor, &micro);
ffStrbufDestroy(&major);
ffStrbufDestroy(&minor);
ffStrbufDestroy(&micro);
if(result->deVersion.length == 0 && instance->config.allowSlowOperations)
{
ffProcessAppendStdOut(&result->deVersion, (char* const[]){
"mate-session",
"--version",
NULL
});
ffStrbufSubstrAfterFirstC(&result->deVersion, ' ');
ffStrbufTrim(&result->deVersion, ' ');
}
}
static void getXFCE4(const FFinstance* instance, FFDisplayServerResult* result)
{
ffStrbufSetS(&result->deProcessName, "xfce4-session");
ffStrbufSetS(&result->dePrettyName, "Xfce4");
ffParsePropFile("/usr/share/gtk-doc/html/libxfce4ui/index.html", "<div><p class=\"releaseinfo\">Version", &result->deVersion);
if(result->deVersion.length == 0 && instance->config.allowSlowOperations)
{
//This is somewhat slow
ffProcessAppendStdOut(&result->deVersion, (char* const[]){
"xfce4-session",
"--version",
NULL
});
ffStrbufSubstrBeforeFirstC(&result->deVersion, '(');
ffStrbufSubstrAfterFirstC(&result->deVersion, ' ');
ffStrbufTrim(&result->deVersion, ' ');
}
}
static void getLXQt(const FFinstance* instance, FFDisplayServerResult* result)
{
ffStrbufSetS(&result->deProcessName, "lxqt-session");
ffStrbufSetS(&result->dePrettyName, "LXQt");
ffParsePropFile("/usr/lib/pkgconfig/lxqt.pc", "Version:", &result->deVersion);
if(result->deVersion.length == 0)
ffParsePropFile("/usr/share/cmake/lxqt/lxqt-config.cmake", "set ( LXQT_VERSION", &result->deVersion);
if(result->deVersion.length == 0)
ffParsePropFile("/usr/share/cmake/lxqt/lxqt-config-version.cmake", "set ( PACKAGE_VERSION", &result->deVersion);
if(result->deVersion.length == 0 && instance->config.allowSlowOperations)
{
//This is really, really, really slow. Thank you, LXQt developers
ffProcessAppendStdOut(&result->deVersion, (char* const[]){
"lxqt-session",
"-v",
NULL
});
result->deVersion.length = 0; //don't set '\0' byte
ffGetPropValueFromLines(result->deVersion.chars , "liblxqt", &result->deVersion);
}
FFstrbuf wmProcessNameBuffer;
ffStrbufInit(&wmProcessNameBuffer);
ffParsePropFileConfig(instance, "lxqt/session.conf", "window_manager =", &wmProcessNameBuffer);
applyBetterWM(result, wmProcessNameBuffer.chars);
ffStrbufDestroy(&wmProcessNameBuffer);
}
static void applyPrettyNameIfDE(const FFinstance* instance, FFDisplayServerResult* result, const char* name)
{
if(!ffStrSet(name))
return;
else if(
strcasecmp(name, "KDE") == 0 ||
strcasecmp(name, "plasma") == 0 ||
strcasecmp(name, "plasmashell") == 0 ||
strcasecmp(name, "plasmawayland") == 0
) getKDE(result);
else if(
strcasecmp(name, "Gnome") == 0 ||
strcasecmp(name, "ubuntu:GNOME") == 0 ||
strcasecmp(name, "ubuntu") == 0 ||
strcasecmp(name, "gnome-shell") == 0
) getGnome(result);
else if(
strcasecmp(name, "X-Cinnamon") == 0 ||
strcasecmp(name, "Cinnamon") == 0
) getCinnamon(result);
else if(
strcasecmp(name, "XFCE") == 0 ||
strcasecmp(name, "X-XFCE") == 0 ||
strcasecmp(name, "XFCE4") == 0 ||
strcasecmp(name, "X-XFCE4") == 0 ||
strcasecmp(name, "xfce4-session") == 0
) getXFCE4(instance, result);
else if(
strcasecmp(name, "MATE") == 0 ||
strcasecmp(name, "X-MATE") == 0 ||
strcasecmp(name, "mate-session") == 0
) getMate(instance, result);
else if(
strcasecmp(name, "LXQt") == 0 ||
strcasecmp(name, "X-LXQT") == 0 ||
strcasecmp(name, "lxqt-session") == 0
) getLXQt(instance, result);
}
static void getWMProtocolNameFromEnv(FFDisplayServerResult* result)
{
//This is only called if all connection attempts to a display server failed
//We don't need to check for wayland here, as the wayland code will always set the protocol name to wayland
const char* env = getenv("XDG_SESSION_TYPE");
if(ffStrSet(env))
{
if(strcasecmp(env, "x11") == 0)
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
else if(strcasecmp(env, "tty") == 0)
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_TTY);
else
ffStrbufSetS(&result->wmProtocolName, env);
return;
}
env = getenv("DISPLAY");
if(ffStrSet(env))
{
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
return;
}
env = getenv("TERM");
if(ffStrSet(env))
{
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_TTY);
return;
}
}
static void getFromProcDir(const FFinstance* instance, FFDisplayServerResult* result)
{
DIR* proc = opendir("/proc");
if(proc == NULL)
return;
FFstrbuf procPath;
ffStrbufInitA(&procPath, 64);
ffStrbufAppendS(&procPath, "/proc/");
uint32_t procPathLength = procPath.length;
FFstrbuf userID;
ffStrbufInit(&userID);
ffStrbufAppendF(&userID, "%i", getuid());
FFstrbuf loginuid;
ffStrbufInit(&loginuid);
FFstrbuf processName;
ffStrbufInitA(&processName, 256); //Some processes have large command lines (looking at you chrome)
struct dirent* dirent;
while((dirent = readdir(proc)) != NULL)
{
//Match only folders starting with a number (the pid folders)
if(dirent->d_type != DT_DIR || !isdigit(dirent->d_name[0]))
continue;
ffStrbufAppendS(&procPath, dirent->d_name);
uint32_t procFolderPathLength = procPath.length;
//Don't check for processes not owend by the current user.
ffStrbufAppendS(&procPath, "/loginuid");
ffGetFileContent(procPath.chars, &loginuid);
if(ffStrbufComp(&userID, &loginuid) != 0)
{
ffStrbufSubstrBefore(&procPath, procPathLength);
continue;
}
ffStrbufSubstrBefore(&procPath, procFolderPathLength);
//We check the cmdline for the process name, because it is not trimmed.
ffStrbufAppendS(&procPath, "/cmdline");
ffGetFileContent(procPath.chars, &processName);
ffStrbufSubstrBeforeFirstC(&processName, '\0'); //Trim the arguments
ffStrbufSubstrAfterLastC(&processName, '/');
ffStrbufSubstrBefore(&procPath, procPathLength);
if(result->dePrettyName.length == 0)
applyPrettyNameIfDE(instance, result, processName.chars);
if(result->wmPrettyName.length == 0)
applyPrettyNameIfWM(result, processName.chars);
if(result->dePrettyName.length > 0 && result->wmPrettyName.length > 0)
break;
}
closedir(proc);
ffStrbufDestroy(&processName);
ffStrbufDestroy(&loginuid);
ffStrbufDestroy(&userID);
ffStrbufDestroy(&procPath);
}
void ffdsDetectWMDE(const FFinstance* instance, FFDisplayServerResult* result)
{
//If all connections failed, use the environment variables to detect protocol name
if(result->wmProtocolName.length == 0)
getWMProtocolNameFromEnv(result);
//We don't want to detect anything in TTY
//This can't happen if a X11 connection succeeded, so we don't need to clear wmProcessName
if(ffStrbufIgnCaseCompS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_TTY) == 0)
return;
const char* env = parseEnv();
if(result->wmProcessName.length > 0)
{
//If we found the processName via display server, use it.
//This will set the pretty name if it is a known WM, otherwise the prettyName to the processName
applyPrettyNameIfWM(result, result->wmProcessName.chars);
if(result->wmPrettyName.length == 0)
ffStrbufSet(&result->wmPrettyName, &result->wmProcessName);
}
else
{
//if env is a known WM, use it
applyPrettyNameIfWM(result, env);
}
//Connecting to a display server only gives WM results, not DE results.
//If we find it in the environment, use that.
applyPrettyNameIfDE(instance, result, env);
//If WM was found by connection to the sever, and DE in the environment, we can return
//This way we never call getFromProcDir(), which has slow initalization time
if(result->dePrettyName.length > 0 && result->wmPrettyName.length > 0)
return;
//Get missing WM / DE from processes.
getFromProcDir(instance, result);
//Return if both wm and de are set, or if env doesn't contain anything
if(
(result->wmPrettyName.length > 0 && result->dePrettyName.length > 0) ||
!ffStrSet(env)
) return;
//If nothing is set, use env as WM
else if(result->wmPrettyName.length == 0 && result->dePrettyName.length == 0)
{
ffStrbufSetS(&result->wmProcessName, env);
ffStrbufSetS(&result->wmPrettyName, env);
}
//If only WM is not set, and DE doesn't equal env, use env as WM
else if(
result->wmPrettyName.length == 0 &&
ffStrbufIgnCaseCompS(&result->deProcessName, env) != 0 &&
ffStrbufIgnCaseCompS(&result->dePrettyName, env) != 0
) {
ffStrbufSetS(&result->wmProcessName, env);
ffStrbufSetS(&result->wmPrettyName, env);
}
//If only DE is not set, and WM doesn't equal env, use env as DE
else if(
result->dePrettyName.length == 0 &&
ffStrbufIgnCaseCompS(&result->wmProcessName, env) != 0 &&
ffStrbufIgnCaseCompS(&result->wmPrettyName, env) != 0
) {
ffStrbufSetS(&result->deProcessName, env);
ffStrbufSetS(&result->dePrettyName, env);
}
}
+393
View File
@@ -0,0 +1,393 @@
#include "displayServer.h"
#ifdef FF_HAVE_XCB
#include <xcb/xcb.h>
#include <string.h>
typedef struct XcbPropertyData
{
FF_LIBRARY_SYMBOL(xcb_intern_atom)
FF_LIBRARY_SYMBOL(xcb_intern_atom_reply)
FF_LIBRARY_SYMBOL(xcb_get_property)
FF_LIBRARY_SYMBOL(xcb_get_property_reply)
FF_LIBRARY_SYMBOL(xcb_get_property_value)
FF_LIBRARY_SYMBOL(xcb_get_property_value_length)
} XcbPropertyData;
static bool xcbInitPropertyData(void* libraryHandle, XcbPropertyData* propertyData)
{
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_intern_atom, xcb_intern_atom, false)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_intern_atom_reply, xcb_intern_atom_reply, false)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property, xcb_get_property, false)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property_reply, xcb_get_property_reply, false)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property_value, xcb_get_property_value, false)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffxcb_get_property_value_length, xcb_get_property_value_length, false)
return true;
}
static void* xcbGetProperty(XcbPropertyData* data, xcb_connection_t* connection, xcb_window_t window, const char* request)
{
xcb_intern_atom_cookie_t requestAtomCookie = data->ffxcb_intern_atom(connection, true, (uint16_t) strlen(request), request);
xcb_intern_atom_reply_t* requestAtomReply = data->ffxcb_intern_atom_reply(connection, requestAtomCookie, NULL);
if(requestAtomReply == NULL)
return NULL;
xcb_get_property_cookie_t propertyCookie = data->ffxcb_get_property(connection, false, window, requestAtomReply->atom, XCB_ATOM_ANY, 0, 64);
free(requestAtomReply);
xcb_get_property_reply_t* propertyReply = data->ffxcb_get_property_reply(connection, propertyCookie, NULL);
if(propertyReply == NULL)
return NULL;
int length = data->ffxcb_get_property_value_length(propertyReply);
if(length <= 0)
{
free(propertyReply);
return NULL;
}
//Why are xcb property strings not null terminated???
void* replyValue = malloc((size_t) (length + 1));
memcpy(replyValue, data->ffxcb_get_property_value(propertyReply), (size_t) length);
((char*) replyValue)[length] = '\0';
free(propertyReply);
return replyValue;
}
static void xcbDetectWMfromEWMH(XcbPropertyData* data, xcb_connection_t* connection, xcb_window_t rootWindow, FFDisplayServerResult* result)
{
if(result->wmProcessName.length > 0)
return;
xcb_window_t* wmWindow = (xcb_window_t*) xcbGetProperty(data, connection, rootWindow, "_NET_SUPPORTING_WM_CHECK");
if(wmWindow == NULL)
return;
char* wmName = (char*) xcbGetProperty(data, connection, *wmWindow, "_NET_WM_NAME");
if(wmName == NULL)
wmName = (char*) xcbGetProperty(data, connection, *wmWindow, "WM_NAME");
free(wmWindow);
if(wmName == NULL)
return;
if(*wmName == '\0')
{
free(wmName);
return;
}
ffStrbufSetS(&result->wmProcessName, wmName);
free(wmName);
}
void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result)
{
FF_LIBRARY_LOAD(xcb, instance->config.libXcb, , "libxcb.so", "libxcb.so.1")
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_connect,)
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_get_setup,)
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_setup_roots_iterator,)
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_screen_next,)
FF_LIBRARY_LOAD_SYMBOL(xcb, xcb_disconnect,)
XcbPropertyData propertyData;
bool propertyDataInitialized = xcbInitPropertyData(xcb, &propertyData);
xcb_connection_t* connection = ffxcb_connect(NULL, NULL);
if(connection == NULL)
{
dlclose(xcb);
return;
}
xcb_screen_iterator_t iterator = ffxcb_setup_roots_iterator(ffxcb_get_setup(connection));
if(iterator.rem > 0 && propertyDataInitialized)
xcbDetectWMfromEWMH(&propertyData, connection, iterator.data->root, result);
while(iterator.rem > 0)
{
ffdsAppendResolution(
result,
(uint32_t) iterator.data->width_in_pixels,
(uint32_t) iterator.data->height_in_pixels,
0
);
ffxcb_screen_next(&iterator);
}
ffxcb_disconnect(connection);
dlclose(xcb);
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
if(result->wmProtocolName.length == 0)
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
}
#else
void ffdsConnectXcb(const FFinstance* instance, FFDisplayServerResult* result)
{
//Do nothing. There are other implementations coming
FF_UNUSED(instance, result)
}
#endif
#ifdef FF_HAVE_XCB_RANDR
#include <xcb/randr.h>
typedef struct XcbRandrData
{
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources)
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources_reply)
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources_modes_iterator)
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_info)
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_info_reply)
FF_LIBRARY_SYMBOL(xcb_randr_mode_info_next)
FF_LIBRARY_SYMBOL(xcb_randr_get_monitors)
FF_LIBRARY_SYMBOL(xcb_randr_get_monitors_reply)
FF_LIBRARY_SYMBOL(xcb_randr_get_monitors_monitors_iterator)
FF_LIBRARY_SYMBOL(xcb_randr_monitor_info_next)
FF_LIBRARY_SYMBOL(xcb_randr_monitor_info_outputs_length)
FF_LIBRARY_SYMBOL(xcb_randr_monitor_info_outputs)
FF_LIBRARY_SYMBOL(xcb_randr_output_next)
FF_LIBRARY_SYMBOL(xcb_randr_get_output_info)
FF_LIBRARY_SYMBOL(xcb_randr_get_output_info_reply)
FF_LIBRARY_SYMBOL(xcb_randr_get_crtc_info)
FF_LIBRARY_SYMBOL(xcb_randr_get_crtc_info_reply)
//init once
xcb_connection_t* connection;
FFDisplayServerResult* result;
//init per screen
uint32_t defaultRefreshRate;
xcb_randr_get_screen_resources_reply_t* screenResources;
} XcbRandrData;
static bool xcbRandrHandleModeInfo(XcbRandrData* data, xcb_randr_mode_info_t* modeInfo)
{
uint32_t refreshRate = ffdsParseRefreshRate((int32_t) (
modeInfo->dot_clock / (uint32_t) (modeInfo->htotal * modeInfo->vtotal)
));
return ffdsAppendResolution(
data->result,
(uint32_t) modeInfo->width,
(uint32_t) modeInfo->height,
refreshRate == 0 ? data->defaultRefreshRate : refreshRate
);
}
static bool xcbRandrHandleMode(XcbRandrData* data, xcb_randr_mode_t mode)
{
//We do the check here, because we want the best fallback resolution if this call failed
if(data->screenResources == NULL)
return false;
xcb_randr_mode_info_iterator_t modesIterator = data->ffxcb_randr_get_screen_resources_modes_iterator(data->screenResources);
while(modesIterator.rem > 0)
{
if(modesIterator.data->id == mode)
return xcbRandrHandleModeInfo(data, modesIterator.data);
data->ffxcb_randr_mode_info_next(&modesIterator);
}
return false;
}
static bool xcbRandrHandleCrtc(XcbRandrData* data, xcb_randr_crtc_t crtc)
{
xcb_randr_get_crtc_info_cookie_t crtcInfoCookie = data->ffxcb_randr_get_crtc_info(data->connection, crtc, XCB_CURRENT_TIME);
xcb_randr_get_crtc_info_reply_t* crtcInfoReply = data->ffxcb_randr_get_crtc_info_reply(data->connection, crtcInfoCookie, NULL);
if(crtcInfoReply == NULL)
return false;
bool res = xcbRandrHandleMode(data, crtcInfoReply->mode);
free(crtcInfoReply);
return res ? true : ffdsAppendResolution(
data->result,
(uint32_t) crtcInfoReply->width,
(uint32_t) crtcInfoReply->height,
data->defaultRefreshRate
);
}
static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output)
{
xcb_randr_get_output_info_cookie_t outputInfoCookie = data->ffxcb_randr_get_output_info(data->connection, output, XCB_CURRENT_TIME);
xcb_randr_get_output_info_reply_t* outputInfoReply = data->ffxcb_randr_get_output_info_reply(data->connection, outputInfoCookie, NULL);
if(outputInfoReply == NULL)
return false;
bool res = xcbRandrHandleCrtc(data, outputInfoReply->crtc);
free(outputInfoReply);
return res;
}
static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t* monitor)
{
//for some reasons, we have to construct this our self
xcb_randr_output_iterator_t outputIterator = {
.index = 0,
.data = data->ffxcb_randr_monitor_info_outputs(monitor),
.rem = data->ffxcb_randr_monitor_info_outputs_length(monitor)
};
bool foundOutput = false;
while(outputIterator.rem > 0)
{
if(xcbRandrHandleOutput(data, *outputIterator.data))
foundOutput = true;
data->ffxcb_randr_output_next(&outputIterator);
};
return foundOutput ? true : ffdsAppendResolution(
data->result,
(uint32_t) monitor->width,
(uint32_t) monitor->height,
data->defaultRefreshRate
);
}
static bool xcbRandrHandleMonitors(XcbRandrData* data, xcb_screen_t* screen)
{
xcb_randr_get_monitors_cookie_t monitorsCookie = data->ffxcb_randr_get_monitors(data->connection, screen->root, true);
xcb_randr_get_monitors_reply_t* monitorsReply = data->ffxcb_randr_get_monitors_reply(data->connection, monitorsCookie, NULL);
if(monitorsReply == NULL)
return false;
xcb_randr_monitor_info_iterator_t monitorInfoIterator = data->ffxcb_randr_get_monitors_monitors_iterator(monitorsReply);
bool foundMonitor = false;
while(monitorInfoIterator.rem > 0)
{
if(xcbRandrHandleMonitor(data, monitorInfoIterator.data))
foundMonitor = true;
data->ffxcb_randr_monitor_info_next(&monitorInfoIterator);
}
free(monitorsReply);
return foundMonitor;
}
static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen)
{
//Init screen info. This is used to get the default refresh rate. If this fails, default refresh rate is simply 0.
xcb_randr_get_screen_info_cookie_t screenInfoCookie = data->ffxcb_randr_get_screen_info(data->connection, screen->root);
xcb_randr_get_screen_info_reply_t* screenInfoReply = data->ffxcb_randr_get_screen_info_reply(data->connection, screenInfoCookie, NULL);
if(screenInfoReply != NULL)
{
data->defaultRefreshRate = screenInfoReply->rate;
free(screenInfoReply);
}
else
data->defaultRefreshRate = 0;
//Init screen resources. They are used to iterate over all modes. xcbRandrHandleMode checks for " == NULL", to fail as late as possible.
xcb_randr_get_screen_resources_cookie_t screenResourcesCookie = data->ffxcb_randr_get_screen_resources(data->connection, screen->root);
data->screenResources = data->ffxcb_randr_get_screen_resources_reply(data->connection, screenResourcesCookie, NULL);
//With all the initialisation done, start the detection
bool ret = xcbRandrHandleMonitors(data, screen);
free(data->screenResources);
if(ret)
return;
//If detetction failed, fallback to screen = monitor, like in the libxcb.so implementation
ffdsAppendResolution(
data->result,
(uint32_t) screen->width_in_pixels,
(uint32_t) screen->height_in_pixels,
data->defaultRefreshRate
);
}
void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result)
{
FF_LIBRARY_LOAD(xcbRandr, instance->config.libXcbRandr, , "libxcb-randr.so", "libxcb-randr.so.0")
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_connect,)
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_get_setup,)
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_setup_roots_iterator,)
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_screen_next,)
FF_LIBRARY_LOAD_SYMBOL(xcbRandr, xcb_disconnect,)
XcbRandrData data;
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_resources, xcb_randr_get_screen_resources,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_resources_reply, xcb_randr_get_screen_resources_reply,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_resources_modes_iterator, xcb_randr_get_screen_resources_modes_iterator,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_info, xcb_randr_get_screen_info,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_screen_info_reply, xcb_randr_get_screen_info_reply,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_mode_info_next, xcb_randr_mode_info_next,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_monitors, xcb_randr_get_monitors,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_monitors_reply, xcb_randr_get_monitors_reply,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_monitors_monitors_iterator, xcb_randr_get_monitors_monitors_iterator,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_monitor_info_next, xcb_randr_monitor_info_next,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_monitor_info_outputs_length, xcb_randr_monitor_info_outputs_length,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_monitor_info_outputs, xcb_randr_monitor_info_outputs,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_output_next, xcb_randr_output_next,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_output_info, xcb_randr_get_output_info,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_output_info_reply, xcb_randr_get_output_info_reply,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_crtc_info, xcb_randr_get_crtc_info,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xcbRandr, data.ffxcb_randr_get_crtc_info_reply, xcb_randr_get_crtc_info_reply,)
XcbPropertyData propertyData;
bool propertyDataInitialized = xcbInitPropertyData(xcbRandr, &propertyData);
data.connection = ffxcb_connect(NULL, NULL);
if(data.connection == NULL)
{
dlclose(xcbRandr);
return;
}
data.result = result;
xcb_screen_iterator_t iterator = ffxcb_setup_roots_iterator(ffxcb_get_setup(data.connection));
if(iterator.rem > 0 && propertyDataInitialized)
xcbDetectWMfromEWMH(&propertyData, data.connection, iterator.data->root, result);
while(iterator.rem > 0)
{
xcbRandrHandleScreen(&data, iterator.data);
ffxcb_screen_next(&iterator);
}
ffxcb_disconnect(data.connection);
dlclose(xcbRandr);
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
if(result->wmProtocolName.length == 0)
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
}
#else
void ffdsConnectXcbRandr(const FFinstance* instance, FFDisplayServerResult* result)
{
//Do nothing. There are other implementations coming
FF_UNUSED(instance, result)
}
#endif
+313
View File
@@ -0,0 +1,313 @@
#include "displayServer.h"
#ifdef FF_HAVE_X11
#include <X11/Xlib.h>
typedef struct X11PropertyData
{
FF_LIBRARY_SYMBOL(XInternAtom)
FF_LIBRARY_SYMBOL(XGetWindowProperty)
} X11PropertyData;
static bool x11InitPropertyData(void* libraryHandle, X11PropertyData* propertyData)
{
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffXInternAtom, XInternAtom, false)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(libraryHandle, propertyData->ffXGetWindowProperty, XGetWindowProperty, false)
return true;
}
static unsigned char* x11GetProperty(X11PropertyData* data, Display* display, Window window, const char* request)
{
Atom requestAtom = data->ffXInternAtom(display, request, False);
if(requestAtom == None)
return NULL;
Atom actualType;
unsigned long unused;
unsigned char* result = NULL;
data->ffXGetWindowProperty(display, window, requestAtom, 0, 64, False, AnyPropertyType, &actualType, (int*) &unused, &unused, &unused, &result);
return result;
}
static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDisplayServerResult* result)
{
if(result->wmProcessName.length > 0)
return;
Window* wmWindow = (Window*) x11GetProperty(data, display, DefaultRootWindow(display), "_NET_SUPPORTING_WM_CHECK");
if(wmWindow == NULL)
return;
const char* wmName = (const char*) x11GetProperty(data, display, *wmWindow, "_NET_WM_NAME");
if(wmName == NULL)
wmName = (const char*) x11GetProperty(data, display, *wmWindow, "WM_NAME");
if(!ffStrSet(wmName))
return;
ffStrbufSetS(&result->wmProcessName, wmName);
}
void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result)
{
FF_LIBRARY_LOAD(x11, instance->config.libX11, , "libX11.so", "libX11.so.6", "libX11-xcb.so", "libX11-xcb.so.1")
FF_LIBRARY_LOAD_SYMBOL(x11, XOpenDisplay,)
FF_LIBRARY_LOAD_SYMBOL(x11, XCloseDisplay,)
X11PropertyData propertyData;
bool propertyDataInitialized = x11InitPropertyData(x11, &propertyData);
Display* display = ffXOpenDisplay(x11);
if(display == NULL)
{
dlclose(x11);
return;
}
if(propertyDataInitialized && ScreenCount(display) > 0)
x11DetectWMFromEWMH(&propertyData, display, result);
for(int i = 0; i < ScreenCount(display); i++)
{
Screen* screen = ScreenOfDisplay(display, i);
ffdsAppendResolution(
result,
(uint32_t) screen->width,
(uint32_t) screen->height,
0
);
}
ffXCloseDisplay(display);
dlclose(x11);
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
if(result->wmProtocolName.length == 0)
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
}
#else
void ffdsConnectXlib(const FFinstance* instance, FFDisplayServerResult* result)
{
//Do nothing. WM / DE detection will use environment vars to detect as much as possible.
FF_UNUSED(instance, result);
}
#endif //FF_HAVE_X11
#ifdef FF_HAVE_XRANDR
#include <X11/extensions/Xrandr.h>
typedef struct XrandrData
{
FF_LIBRARY_SYMBOL(XRRGetScreenInfo)
FF_LIBRARY_SYMBOL(XRRConfigCurrentConfiguration)
FF_LIBRARY_SYMBOL(XRRConfigCurrentRate)
FF_LIBRARY_SYMBOL(XRRGetMonitors)
FF_LIBRARY_SYMBOL(XRRGetScreenResources)
FF_LIBRARY_SYMBOL(XRRGetOutputInfo)
FF_LIBRARY_SYMBOL(XRRGetCrtcInfo)
FF_LIBRARY_SYMBOL(XRRFreeCrtcInfo)
FF_LIBRARY_SYMBOL(XRRFreeOutputInfo)
FF_LIBRARY_SYMBOL(XRRFreeScreenResources)
FF_LIBRARY_SYMBOL(XRRFreeMonitors)
FF_LIBRARY_SYMBOL(XRRFreeScreenConfigInfo)
//Init once
Display* display;
FFDisplayServerResult* result;
//Init per screen
uint32_t defaultRefreshRate;
XRRScreenResources* screenResources;
} XrandrData;
static bool xrandrHandleModeInfo(XrandrData* data, XRRModeInfo* modeInfo)
{
uint32_t refreshRate = ffdsParseRefreshRate((int32_t) (
modeInfo->dotClock / (modeInfo->hTotal * modeInfo->vTotal)
));
return ffdsAppendResolution(
data->result,
(uint32_t) modeInfo->width,
(uint32_t) modeInfo->height,
refreshRate == 0 ? data->defaultRefreshRate : refreshRate
);
}
static bool xrandrHandleMode(XrandrData* data, RRMode mode)
{
for(int i = 0; i < data->screenResources->nmode; i++)
{
if(data->screenResources->modes[i].id == mode)
return xrandrHandleModeInfo(data, &data->screenResources->modes[i]);
}
return false;
}
static bool xrandrHandleCrtc(XrandrData* data, RRCrtc crtc)
{
//We do the check here, because we want the best fallback resolution if this call failed
if(data->screenResources == NULL)
return false;
XRRCrtcInfo* crtcInfo = data->ffXRRGetCrtcInfo(data->display, data->screenResources, crtc);
if(crtcInfo == NULL)
return false;
bool res = xrandrHandleMode(data, crtcInfo->mode);
data->ffXRRFreeCrtcInfo(crtcInfo);
return res ? true : ffdsAppendResolution(
data->result,
(uint32_t) crtcInfo->width,
(uint32_t) crtcInfo->height,
data->defaultRefreshRate
);
}
static bool xrandrHandleOutput(XrandrData* data, RROutput output)
{
XRROutputInfo* outputInfo = data->ffXRRGetOutputInfo(data->display, data->screenResources, output);
if(outputInfo == NULL)
return false;
bool res = xrandrHandleCrtc(data, outputInfo->crtc);
data->ffXRRFreeOutputInfo(outputInfo);
return res;
}
static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo)
{
bool foundOutput = false;
for(int i = 0; i < monitorInfo->noutput; i++)
{
if(xrandrHandleOutput(data, monitorInfo->outputs[i]))
foundOutput = true;
}
return foundOutput ? true : ffdsAppendResolution(
data->result,
(uint32_t) monitorInfo->width,
(uint32_t) monitorInfo->height,
data->defaultRefreshRate
);
}
static bool xrandrHandleMonitors(XrandrData* data, Screen* screen)
{
int numberOfMonitors;
XRRMonitorInfo* monitorInfos = data->ffXRRGetMonitors(data->display, RootWindowOfScreen(screen), True, &numberOfMonitors);
if(monitorInfos == NULL)
return false;
bool foundAMonitor;
for(int i = 0; i < numberOfMonitors; i++)
{
if(xrandrHandleMonitor(data, &monitorInfos[i]))
foundAMonitor = true;
}
data->ffXRRFreeMonitors(monitorInfos);
return foundAMonitor;
}
static void xrandrHandleScreen(XrandrData* data, Screen* screen)
{
//Init screen configuration. This is used to get the default refresh rate. If this fails, default refresh rate is simply 0.
XRRScreenConfiguration* screenConfiguration = data->ffXRRGetScreenInfo(data->display, RootWindowOfScreen(screen));
if(screenConfiguration != NULL)
{
data->defaultRefreshRate = (uint32_t) data->ffXRRConfigCurrentRate(screenConfiguration);
data->ffXRRFreeScreenConfigInfo(screenConfiguration);
}
else
data->defaultRefreshRate = 0;
//Init screen resources
data->screenResources = data->ffXRRGetScreenResources(data->display, RootWindowOfScreen(screen));
bool ret = xrandrHandleMonitors(data, screen);
data->ffXRRFreeScreenResources(data->screenResources);
if(ret)
return;
//Fallback to screen
ffdsAppendResolution(
data->result,
(uint32_t) WidthOfScreen(screen),
(uint32_t) HeightOfScreen(screen),
data->defaultRefreshRate
);
}
void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result)
{
FF_LIBRARY_LOAD(xrandr, instance->config.libXrandr, , "libXrandr.so", "libXrandr.so.2")
FF_LIBRARY_LOAD_SYMBOL(xrandr, XOpenDisplay,)
FF_LIBRARY_LOAD_SYMBOL(xrandr, XCloseDisplay,)
XrandrData data;
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetScreenInfo, XRRGetScreenInfo,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRConfigCurrentRate, XRRConfigCurrentRate,);
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetMonitors, XRRGetMonitors,);
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetScreenResources, XRRGetScreenResources,);
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetOutputInfo, XRRGetOutputInfo,);
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRGetCrtcInfo, XRRGetCrtcInfo,);
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeCrtcInfo, XRRFreeCrtcInfo,);
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeOutputInfo, XRRFreeOutputInfo,);
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeScreenResources, XRRFreeScreenResources,);
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeMonitors, XRRFreeMonitors,);
FF_LIBRARY_LOAD_SYMBOL_ADRESS(xrandr, data.ffXRRFreeScreenConfigInfo, XRRFreeScreenConfigInfo,);
X11PropertyData propertyData;
bool propertyDataInitialized = x11InitPropertyData(xrandr, &propertyData);
data.display = ffXOpenDisplay(NULL);
if(data.display == NULL)
{
dlclose(xrandr);
return;
}
if(propertyDataInitialized && ScreenCount(data.display) > 0)
x11DetectWMFromEWMH(&propertyData, data.display, result);
data.result = result;
for(int i = 0; i < ScreenCount(data.display); i++)
xrandrHandleScreen(&data, ScreenOfDisplay(data.display, i));
ffXCloseDisplay(data.display);
dlclose(xrandr);
//If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland.
if(result->wmProtocolName.length == 0)
ffStrbufSetS(&result->wmProtocolName, FF_DISPLAYSERVER_PROTOCOL_X11);
}
#else
void ffdsConnectXrandr(const FFinstance* instance, FFDisplayServerResult* result)
{
//Do nothing here. There are more x11 implementaions to come.
FF_UNUSED(instance, result);
}
#endif // FF_HAVE_XRANDR
+235
View File
@@ -0,0 +1,235 @@
#include "fastfetch.h"
#include <pthread.h>
static inline bool allPropertiesSet(FFGTKResult* result)
{
return
result->theme.length > 0 &&
result->icons.length > 0 &&
result->font.length > 0;
}
static inline void applyGTKDConfSettings(FFGTKResult* result, const char* themeName, const char* iconsName, const char* fontName, const char* cursorTheme, int cursorSize)
{
if(result->theme.length == 0)
ffStrbufAppendS(&result->theme, themeName);
if(result->icons.length == 0)
ffStrbufAppendS(&result->icons, iconsName);
if(result->font.length == 0)
ffStrbufAppendS(&result->font, fontName);
if(result->cursor.length == 0)
ffStrbufAppendS(&result->cursor, cursorTheme);
if(result->cursorSize.length == 0 && cursorSize > 0)
ffStrbufAppendF(&result->cursorSize, "%i", cursorSize);
}
static void detectGTKFromDConf(FFinstance* instance, FFGTKResult* result)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static const char* themeName = NULL;
static const char* iconsName = NULL;
static const char* fontName = NULL;
static const char* cursorTheme = NULL;
static int cursorSize = 0;
static bool init = false;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
applyGTKDConfSettings(result, themeName, iconsName, fontName, cursorTheme, cursorSize);
return;
}
init = true;
const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Cinnamon") == 0)
{
themeName = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/gtk-theme", "org.cinnamon.desktop.interface", NULL, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue;
iconsName = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/icon-theme", "org.cinnamon.desktop.interface", NULL, "icon-theme", FF_VARIANT_TYPE_STRING).strValue;
fontName = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/font-name", "org.cinnamon.desktop.interface", NULL, "font-name", FF_VARIANT_TYPE_STRING).strValue;
cursorTheme = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/cursor-theme", "org.cinnamon.desktop.interface", NULL, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue;
cursorSize = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/cursor-size", "org.cinnamon.desktop.interface", NULL, "cursor-size", FF_VARIANT_TYPE_INT).intValue;
}
else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Mate") == 0)
{
themeName = ffSettingsGet(instance, "/org/mate/interface/gtk-theme", "org.mate.interface", NULL, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue;
iconsName = ffSettingsGet(instance, "/org/mate/interface/icon-theme", "org.mate.interface", NULL, "icon-theme", FF_VARIANT_TYPE_STRING).strValue;
fontName = ffSettingsGet(instance, "/org/mate/interface/font-name", "org.mate.interface", NULL, "font-name", FF_VARIANT_TYPE_STRING).strValue;
cursorTheme = ffSettingsGet(instance, "/org/mate/peripherals-mouse/cursor-theme", "org.mate.peripherals-mouse", NULL, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue;
cursorSize = ffSettingsGet(instance, "/org/mate/peripherals-mouse/cursor-size", "org.mate.peripherals-mouse", NULL, "cursor-size", FF_VARIANT_TYPE_INT).intValue;
}
//Fallback + Gnome impl
if(themeName == NULL)
themeName = ffSettingsGet(instance, "/org/gnome/desktop/interface/gtk-theme", "org.gnome.desktop.interface", NULL, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue;
if(iconsName == NULL)
iconsName = ffSettingsGet(instance, "/org/gnome/desktop/interface/icon-theme", "org.gnome.desktop.interface", NULL, "icon-theme", FF_VARIANT_TYPE_STRING).strValue;
if(fontName == NULL)
fontName = ffSettingsGet(instance, "/org/gnome/desktop/interface/font-name", "org.gnome.desktop.interface", NULL, "font-name", FF_VARIANT_TYPE_STRING).strValue;
if(cursorTheme == NULL)
cursorTheme = ffSettingsGet(instance, "/org/gnome/desktop/interface/cursor-theme", "org.gnome.desktop.interface", NULL, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue;
if(cursorSize <= 0)
cursorSize = ffSettingsGet(instance, "/org/gnome/desktop/interface/cursor-size", "org.gnome.desktop.interface", NULL, "cursor-size", FF_VARIANT_TYPE_INT).intValue;
pthread_mutex_unlock(&mutex);
applyGTKDConfSettings(result, themeName, iconsName, fontName, cursorTheme, cursorSize);
}
static void detectGTKFromConfigFile(const char* filename, FFGTKResult* result)
{
ffParsePropFileValues(filename, 5, (FFpropquery[]) {
{"gtk-theme-name =", &result->theme},
{"gtk-icon-theme-name =", &result->icons},
{"gtk-font-name =", &result->font},
{"gtk-cursor-theme-name =", &result->cursor},
{"gtk-cursor-theme-size =", &result->cursorSize}
});
}
static void detectGTKFromConfigDir(FFstrbuf* configDir, const char* version, FFGTKResult* result)
{
//In case of an empty env variable
ffStrbufTrim(configDir, ' ');
if(configDir->length == 0)
return;
uint32_t configDirLength = configDir->length;
// <configdir>/gtk-<version>.0/settings.ini
ffStrbufAppendS(configDir, "/gtk-");
ffStrbufAppendS(configDir, version);
ffStrbufAppendS(configDir, ".0/settings.ini");
detectGTKFromConfigFile(configDir->chars, result);
ffStrbufSubstrBefore(configDir, configDirLength);
if(allPropertiesSet(result))
return;
// <configdir>/gtk-<version>.0/gtkrc
ffStrbufAppendS(configDir, "/gtk-");
ffStrbufAppendS(configDir, version);
ffStrbufAppendS(configDir, ".0/gtkrc");
detectGTKFromConfigFile(configDir->chars, result);
ffStrbufSubstrBefore(configDir, configDirLength);
if(allPropertiesSet(result))
return;
// <configdir>/gtkrc-<version>.0
ffStrbufAppendS(configDir, "/gtkrc-");
ffStrbufAppendS(configDir, version);
ffStrbufAppendS(configDir, ".0");
detectGTKFromConfigFile(configDir->chars, result);
ffStrbufSubstrBefore(configDir, configDirLength);
if(allPropertiesSet(result))
return;
// <configdir>/.gtkrc-<version>.0
ffStrbufAppendS(configDir, "/.gtkrc-");
ffStrbufAppendS(configDir, version);
ffStrbufAppendS(configDir, ".0");
detectGTKFromConfigFile(configDir->chars, result);
ffStrbufSubstrBefore(configDir, configDirLength);
}
static void detectGTK(FFinstance* instance, const char* version, const char* envVariable, FFGTKResult* result)
{
FFstrbuf buffer;
ffStrbufInitA(&buffer, 128);
// From ENV: GTK*_RC_FILES
ffStrbufSetS(&buffer, getenv(envVariable));
uint32_t startIndex = 0;
while (startIndex < buffer.length)
{
uint32_t colonIndex = ffStrbufNextIndexC(&buffer, startIndex, ':');
buffer.chars[colonIndex] = '\0';
detectGTKFromConfigFile(buffer.chars + startIndex, result);
if(allPropertiesSet(result))
{
ffStrbufDestroy(&buffer);
return;
}
startIndex = colonIndex + 1;
}
//From DConf / GSettings
detectGTKFromDConf(instance, result);
if(allPropertiesSet(result))
{
ffStrbufDestroy(&buffer);
return;
}
//From config dirs
//We need to do this because we use multiple threads on configDirs
FFstrbuf baseDirCopy;
ffStrbufInitA(&baseDirCopy, 64);
for(uint32_t i = 0; i < instance->state.configDirs.length; i++)
{
FFstrbuf* baseDir = (FFstrbuf*) ffListGet(&instance->state.configDirs, i);
ffStrbufSet(&baseDirCopy, baseDir);
detectGTKFromConfigDir(&baseDirCopy, version, result);
if(allPropertiesSet(result))
break;
}
ffStrbufDestroy(&baseDirCopy);
ffStrbufDestroy(&buffer);
}
#define FF_CALCULATE_GTK_IMPL(version) \
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; \
static FFGTKResult result; \
static bool init = false; \
pthread_mutex_lock(&mutex); \
if(init){ \
pthread_mutex_unlock(&mutex);\
return &result; \
} \
init = true; \
ffStrbufInit(&result.theme); \
ffStrbufInit(&result.icons); \
ffStrbufInit(&result.font); \
ffStrbufInit(&result.cursor); \
ffStrbufInit(&result.cursorSize); \
detectGTK(instance, #version, "GTK"#version"_RC_FILES", &result); \
pthread_mutex_unlock(&mutex); \
return &result;
const FFGTKResult* ffDetectGTK2(FFinstance* instance)
{
FF_CALCULATE_GTK_IMPL(2)
}
const FFGTKResult* ffDetectGTK3(FFinstance* instance)
{
FF_CALCULATE_GTK_IMPL(3)
}
const FFGTKResult* ffDetectGTK4(FFinstance* instance)
{
FF_CALCULATE_GTK_IMPL(4)
}
#undef FF_CALCULATE_GTK_IMPL
+396
View File
@@ -0,0 +1,396 @@
#include "fastfetch.h"
#include <ctype.h>
#include <string.h>
#include <pthread.h>
#define FF_DBUS_MPRIS_PREFIX "org.mpris.MediaPlayer2."
#ifdef FF_HAVE_DBUS
#include <dbus/dbus.h>
#define FF_DBUS_ITER_CONTINUE(iterator) \
{ \
if(!data->ffdbus_message_iter_has_next(&iterator)) \
break; \
data->ffdbus_message_iter_next(&iterator); \
continue; \
}
typedef struct DBusData
{
FF_LIBRARY_SYMBOL(dbus_message_new_method_call)
FF_LIBRARY_SYMBOL(dbus_message_iter_init)
FF_LIBRARY_SYMBOL(dbus_message_iter_init_append)
FF_LIBRARY_SYMBOL(dbus_message_iter_append_basic)
FF_LIBRARY_SYMBOL(dbus_message_iter_get_arg_type)
FF_LIBRARY_SYMBOL(dbus_message_iter_get_basic)
FF_LIBRARY_SYMBOL(dbus_message_iter_recurse)
FF_LIBRARY_SYMBOL(dbus_message_iter_has_next)
FF_LIBRARY_SYMBOL(dbus_message_iter_next)
FF_LIBRARY_SYMBOL(dbus_message_unref)
FF_LIBRARY_SYMBOL(dbus_connection_send_with_reply)
FF_LIBRARY_SYMBOL(dbus_connection_flush)
FF_LIBRARY_SYMBOL(dbus_pending_call_block)
FF_LIBRARY_SYMBOL(dbus_pending_call_steal_reply)
FF_LIBRARY_SYMBOL(dbus_pending_call_unref)
DBusConnection *connection;
} DBusData;
static bool getValue(DBusMessageIter* iter, FFstrbuf* result, DBusData* data)
{
int argType = data->ffdbus_message_iter_get_arg_type(iter);
if(argType == DBUS_TYPE_STRING)
{
const char* value = NULL;
data->ffdbus_message_iter_get_basic(iter, &value);
if(!ffStrSet(value))
return false;
ffStrbufAppendS(result, value);
return true;
}
if(argType != DBUS_TYPE_VARIANT && argType != DBUS_TYPE_ARRAY)
return false;
DBusMessageIter subIter;
data->ffdbus_message_iter_recurse(iter, &subIter);
if(argType == DBUS_TYPE_VARIANT)
return getValue(&subIter, result, data);
//At this point we have an array
bool foundAValue = false;
while(true)
{
if((foundAValue = getValue(&subIter, result, data)))
ffStrbufAppendS(result, ", ");
FF_DBUS_ITER_CONTINUE(subIter);
}
if(foundAValue)
ffStrbufSubstrBefore(result, result->length - 2);
return foundAValue;
}
static DBusMessage* getReply(DBusMessage* request, DBusData* data)
{
DBusPendingCall* pendingCall = NULL;
dbus_bool_t succesfull = data->ffdbus_connection_send_with_reply(data->connection, request, &pendingCall, DBUS_TIMEOUT_USE_DEFAULT);
data->ffdbus_message_unref(request);
if(!succesfull || pendingCall == NULL)
return NULL;
data->ffdbus_connection_flush(data->connection);
data->ffdbus_pending_call_block(pendingCall);
DBusMessage* reply = data->ffdbus_pending_call_steal_reply(pendingCall);
data->ffdbus_pending_call_unref(pendingCall);
return reply;
}
static DBusMessage* getProperty(const char* busName, const char* objectPath, const char* interface, const char* property, DBusData* data)
{
DBusMessage* message = data->ffdbus_message_new_method_call(busName, objectPath, "org.freedesktop.DBus.Properties", "Get");
if(message == NULL)
return NULL;
DBusMessageIter requestIterator;
data->ffdbus_message_iter_init_append(message, &requestIterator);
if(!data->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &interface))
{
data->ffdbus_message_unref(message);
return NULL;
}
if(!data->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &property))
{
data->ffdbus_message_unref(message);
return NULL;
}
return getReply(message, data);
}
static void getPropertyString(const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result, DBusData* data)
{
DBusMessage* reply = getProperty(busName, objectPath, interface, property, data);
if(reply == NULL)
return;
DBusMessageIter rootIterator;
if(!data->ffdbus_message_iter_init(reply, &rootIterator))
{
data->ffdbus_message_unref(reply);
return;
}
getValue(&rootIterator, result, data);
data->ffdbus_message_unref(reply);
}
static bool getBusProperties(const char* busName, FFMediaResult* result, DBusData* data)
{
DBusMessage* reply = getProperty(busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", "Metadata", data);
if(reply == NULL)
return false;
DBusMessageIter rootIterator;
if(!data->ffdbus_message_iter_init(reply, &rootIterator))
{
data->ffdbus_message_unref(reply);
return false;
}
if(data->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_VARIANT)
{
data->ffdbus_message_unref(reply);
return false;
}
DBusMessageIter variantIterator;
data->ffdbus_message_iter_recurse(&rootIterator, &variantIterator);
if(data->ffdbus_message_iter_get_arg_type(&variantIterator) != DBUS_TYPE_ARRAY)
{
data->ffdbus_message_unref(reply);
return false;
}
DBusMessageIter arrayIterator;
data->ffdbus_message_iter_recurse(&variantIterator, &arrayIterator);
while(true)
{
if(data->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY)
FF_DBUS_ITER_CONTINUE(arrayIterator)
DBusMessageIter dictIterator;
data->ffdbus_message_iter_recurse(&arrayIterator, &dictIterator);
if(data->ffdbus_message_iter_get_arg_type(&dictIterator) != DBUS_TYPE_STRING)
FF_DBUS_ITER_CONTINUE(arrayIterator)
if(!data->ffdbus_message_iter_has_next(&dictIterator))
FF_DBUS_ITER_CONTINUE(arrayIterator)
const char* key;
data->ffdbus_message_iter_get_basic(&dictIterator, &key);
data->ffdbus_message_iter_next(&dictIterator);
if(strcmp(key, "xesam:title") == 0)
getValue(&dictIterator, &result->song, data);
else if(strcmp(key, "xesam:album") == 0)
getValue(&dictIterator, &result->album, data);
else if(strcmp(key, "xesam:artist") == 0)
getValue(&dictIterator, &result->artist, data);
else if(strcmp(key, "xesam:url") == 0)
getValue(&dictIterator, &result->url, data);
if(result->song.length > 0 && result->artist.length > 0 && result->album.length > 0)
break;
FF_DBUS_ITER_CONTINUE(arrayIterator)
}
data->ffdbus_message_unref(reply);
if(result->song.length == 0)
{
ffStrbufClear(&result->artist);
ffStrbufClear(&result->album);
return false;
}
//We found a song, get the player name
getPropertyString(busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "Identity", &result->player, data);
if(result->player.length == 0)
getPropertyString(busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "DesktopEntry", &result->player, data);
return true;
}
static void getCustomBus(FFinstance* instance, FFMediaResult* result, DBusData* data)
{
if(ffStrbufStartsWithS(&instance->config.playerName, FF_DBUS_MPRIS_PREFIX))
{
ffStrbufAppendS(&result->busNameShort, instance->config.playerName.chars + sizeof(FF_DBUS_MPRIS_PREFIX) - 1);
getBusProperties(instance->config.playerName.chars, result, data);
return;
}
ffStrbufAppend(&result->busNameShort, &instance->config.playerName);
FFstrbuf busName;
ffStrbufInit(&busName);
ffStrbufAppendS(&busName, FF_DBUS_MPRIS_PREFIX);
ffStrbufAppend(&busName, &instance->config.playerName);
getBusProperties(busName.chars, result, data);
ffStrbufDestroy(&busName);
}
static void getBestBus(FFMediaResult* result, DBusData* data)
{
DBusMessage* message = data->ffdbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
if(message == NULL)
return;
DBusMessage* reply = getReply(message, data);
if(reply == NULL)
return;
DBusMessageIter rootIterator;
if(!data->ffdbus_message_iter_init(reply, &rootIterator) || data->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_ARRAY)
return;
DBusMessageIter arrayIterator;
data->ffdbus_message_iter_recurse(&rootIterator, &arrayIterator);
while(true)
{
if(data->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_STRING)
FF_DBUS_ITER_CONTINUE(arrayIterator)
const char* busName;
data->ffdbus_message_iter_get_basic(&arrayIterator, &busName);
if(strncmp(busName, FF_DBUS_MPRIS_PREFIX, sizeof(FF_DBUS_MPRIS_PREFIX) - 1) != 0)
FF_DBUS_ITER_CONTINUE(arrayIterator)
if(getBusProperties(busName, result, data))
{
ffStrbufAppendS(&result->busNameShort, busName + sizeof(FF_DBUS_MPRIS_PREFIX) - 1);
break;
}
FF_DBUS_ITER_CONTINUE(arrayIterator)
}
data->ffdbus_message_unref(reply);
}
static void getMedia(FFinstance* instance, FFMediaResult* result)
{
DBusData data;
FF_LIBRARY_LOAD(dbus, instance->config.libDBus, , "libdbus-1.so", "libdbus-1.so.3");
FF_LIBRARY_LOAD_SYMBOL(dbus, dbus_bus_get,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_new_method_call, dbus_message_new_method_call,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_init, dbus_message_iter_init,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_init_append, dbus_message_iter_init_append,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_append_basic, dbus_message_iter_append_basic,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_get_arg_type, dbus_message_iter_get_arg_type,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_get_basic, dbus_message_iter_get_basic,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_recurse, dbus_message_iter_recurse,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_has_next, dbus_message_iter_has_next,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_iter_next, dbus_message_iter_next,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_message_unref, dbus_message_unref,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_connection_send_with_reply, dbus_connection_send_with_reply,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_connection_flush, dbus_connection_flush,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_pending_call_block, dbus_pending_call_block,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_pending_call_steal_reply, dbus_pending_call_steal_reply,)
FF_LIBRARY_LOAD_SYMBOL_ADRESS(dbus, data.ffdbus_pending_call_unref, dbus_pending_call_unref,)
data.connection = ffdbus_bus_get(DBUS_BUS_SESSION, NULL);
if(data.connection == NULL)
{
dlclose(dbus);
return;
}
if(instance->config.playerName.length > 0)
getCustomBus(instance, result, &data);
else
getBestBus(result, &data);
dlclose(dbus);
//If we are on a website, prepend the website name
if(ffStrbufStartsWithS(&result->url, "https://www."))
ffStrbufAppendS(&result->playerPretty, result->url.chars + 12);
else if(ffStrbufStartsWithS(&result->url, "http://www."))
ffStrbufAppendS(&result->playerPretty, result->url.chars + 11);
else if(ffStrbufStartsWithS(&result->url, "https://"))
ffStrbufAppendS(&result->playerPretty, result->url.chars + 8);
else if(ffStrbufStartsWithS(&result->url, "http://"))
ffStrbufAppendS(&result->playerPretty, result->url.chars + 7);
//If we found a website name, make it more pretty
if(result->playerPretty.length > 0)
{
ffStrbufSubstrBeforeFirstC(&result->playerPretty, '/'); //Remove the path
ffStrbufSubstrBeforeLastC(&result->playerPretty, '.'); //Remove the TLD
}
//Check again for length, as we may have removed everything.
//If we don't have subdomains, it is usually more pretty to capitalize the first letter.
if(result->playerPretty.length > 0 && ffStrbufFirstIndexC(&result->playerPretty, '.') < result->playerPretty.length)
result->playerPretty.chars[0] = (char) toupper(result->playerPretty.chars[0]);
}
#endif
const FFMediaResult* ffDetectMedia(FFinstance* instance)
{
static FFMediaResult result;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static bool init = false;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
ffStrbufInit(&result.busNameShort);
ffStrbufInit(&result.player);
ffStrbufInit(&result.playerPretty);
ffStrbufInit(&result.song);
ffStrbufInit(&result.artist);
ffStrbufInit(&result.album);
ffStrbufInit(&result.url);
#ifdef FF_HAVE_DBUS
getMedia(instance, &result);
#endif
//Set busNameShort if a custom player was given, but loading dbus failed
if(instance->config.playerName.length > 0 && result.busNameShort.length == 0)
{
if(ffStrbufStartsWithS(&instance->config.playerName, FF_DBUS_MPRIS_PREFIX))
ffStrbufAppendS(&result.busNameShort, instance->config.playerName.chars + sizeof(FF_DBUS_MPRIS_PREFIX) - 1);
else
ffStrbufAppend(&result.busNameShort, &instance->config.playerName);
}
//Set player to busNameShort, if detection failed
if(result.player.length == 0)
ffStrbufAppend(&result.player, &result.busNameShort);
bool hasCustomPrettyName = result.playerPretty.length > 0;
if(hasCustomPrettyName)
ffStrbufAppendS(&result.playerPretty, " (");
ffStrbufAppend(&result.playerPretty, &result.player);
if(hasCustomPrettyName)
ffStrbufAppendC(&result.playerPretty, ')');
pthread_mutex_unlock(&mutex);
return &result;
}
+83
View File
@@ -0,0 +1,83 @@
#include "fastfetch.h"
#include <pthread.h>
#if !defined(__ANDROID__)
static void parseFile(const char* fileName, FFOSResult* result)
{
if(result->name.length > 0 && result->prettyName.length > 0)
return;
ffParsePropFileValues(fileName, 13, (FFpropquery[]) {
{"NAME =", &result->name},
{"DISTRIB_DESCRIPTION =", &result->prettyName},
{"PRETTY_NAME =", &result->prettyName},
{"DISTRIB_ID =", &result->id},
{"ID =", &result->id},
{"ID_LIKE =", &result->idLike},
{"VARIANT =", &result->variant},
{"VARIANT_ID =", &result->variantID},
{"DISTRIB_RELEASE =", &result->version},
{"VERSION =", &result->version},
{"VERSION_ID =", &result->versionID},
{"VERSION_CODENAME =", &result->codename},
{"BUILD_ID =", &result->buildID}
});
}
#endif
const FFOSResult* ffDetectOS(FFinstance* instance)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static FFOSResult result;
static bool init = false;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
ffStrbufInit(&result.systemName);
ffStrbufInit(&result.name);
ffStrbufInit(&result.prettyName);
ffStrbufInit(&result.id);
ffStrbufInit(&result.idLike);
ffStrbufInit(&result.variant);
ffStrbufInit(&result.variantID);
ffStrbufInit(&result.version);
ffStrbufInit(&result.versionID);
ffStrbufInit(&result.codename);
ffStrbufInit(&result.buildID);
ffStrbufInit(&result.architecture);
ffStrbufSetS(&result.systemName, instance->state.utsname.sysname);
ffStrbufSetS(&result.architecture, instance->state.utsname.machine);
#if !__ANDROID__
if(instance->config.osFile.length > 0)
{
parseFile(instance->config.osFile.chars, &result);
}
else
{
parseFile("/etc/os-release", &result);
parseFile("/usr/lib/os-release", &result);
parseFile("/etc/lsb-release", &result);
}
#else
ffStrbufSetS(&result.name, "Android");
ffStrbufSetS(&result.id, "android");
ffSettingsGetAndroidProperty("ro.build.version.release", &result.versionID);
ffSettingsGetAndroidProperty("ro.build.version.release", &result.version);
ffSettingsGetAndroidProperty("ro.build.version.codename", &result.codename);
ffSettingsGetAndroidProperty("ro.build.id", &result.buildID);
#endif
pthread_mutex_unlock(&mutex);
return &result;
}
+143
View File
@@ -0,0 +1,143 @@
#include "fastfetch.h"
#include <string.h>
#include <pthread.h>
typedef enum PlasmaCategory
{
PLASMA_CATEGORY_GENERAL,
PLASMA_CATEGORY_KDE,
PLASMA_CATEGORY_ICONS,
PLASMA_CATEGORY_OTHER
} PlasmaCategory;
static bool detectFromConfigFile(const FFstrbuf* filename, FFPlasmaResult* result)
{
FILE* kdeglobals = fopen(filename->chars, "r");
if(kdeglobals == NULL)
return false;
char* line = NULL;
size_t len = 0;
PlasmaCategory category = PLASMA_CATEGORY_OTHER;
while(getline(&line, &len, kdeglobals) != -1)
{
if(line[0] == '[')
{
char categoryName[32];
sscanf(line, "[%31[^]]", categoryName);
if(strcasecmp(categoryName, "General") == 0)
category = PLASMA_CATEGORY_GENERAL;
else if(strcasecmp(categoryName, "KDE") == 0)
category = PLASMA_CATEGORY_KDE;
else if(strcasecmp(categoryName, "Icons") == 0)
category = PLASMA_CATEGORY_ICONS;
else
category = PLASMA_CATEGORY_OTHER;
continue;
}
if(category == PLASMA_CATEGORY_KDE && result->widgetStyle.length == 0)
ffGetPropValue(line, "widgetStyle =", &result->widgetStyle);
else if(category == PLASMA_CATEGORY_ICONS && result->icons.length == 0)
ffGetPropValue(line, "Theme =", &result->icons);
else if(category == PLASMA_CATEGORY_GENERAL)
{
if(result->colorScheme.length == 0)
ffGetPropValue(line, "ColorScheme =", &result->colorScheme);
if(result->font.length == 0)
ffGetPropValue(line, "font =", &result->font);
//Before plasma 5.23, "Font" was the key instead of "font". Since a lot of distros ship older versions, we test for both.
if(result->font.length == 0)
ffGetPropValue(line, "Font =", &result->font);
}
}
if(line != NULL)
free(line);
fclose(kdeglobals);
return true;
}
const FFPlasmaResult* ffDetectPlasma(FFinstance* instance)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static FFPlasmaResult result;
static bool init = false;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
ffStrbufInit(&result.widgetStyle);
ffStrbufInit(&result.colorScheme);
ffStrbufInit(&result.icons);
ffStrbufInit(&result.font);
const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
if(ffStrbufIgnCaseCompS(&wmde->deProcessName, "plasmashell") != 0)
{
pthread_mutex_unlock(&mutex);
return &result;
}
bool foundAFile = false;
//We need to do this because we use multiple threads on configDirs
FFstrbuf baseDirCopy;
ffStrbufInitA(&baseDirCopy, 64);
for(uint32_t i = 0; i < instance->state.configDirs.length; i++)
{
FFstrbuf* baseDir = (FFstrbuf*) ffListGet(&instance->state.configDirs, i);
ffStrbufSet(&baseDirCopy, baseDir);
ffStrbufAppendS(&baseDirCopy, "/kdeglobals");
if(detectFromConfigFile(&baseDirCopy, &result))
foundAFile = true;
if(
result.widgetStyle.length > 0 &&
result.colorScheme.length > 0 &&
result.icons.length > 0 &&
result.font.length > 0
) break;
}
ffStrbufDestroy(&baseDirCopy);
if(!foundAFile)
{
pthread_mutex_unlock(&mutex);
return &result;
}
//In Plasma the default value is never set in the config file, but the whole key-value is discarded.
///We must set these values by our self if the file exists (it always does here)
if(result.widgetStyle.length == 0)
ffStrbufAppendS(&result.widgetStyle, "Breeze");
if(result.colorScheme.length == 0)
ffStrbufAppendS(&result.colorScheme, "BreezeLight");
if(result.icons.length == 0)
ffStrbufAppendS(&result.icons, "Breeze");
if(result.font.length == 0)
ffStrbufAppendS(&result.font, "Noto Sans, 10");
pthread_mutex_unlock(&mutex);
return &result;
}
+112
View File
@@ -0,0 +1,112 @@
#include "fastfetch.h"
#include <pthread.h>
#include <dirent.h>
#include <string.h>
static bool isTempFile(const char* name)
{
return
name[0] == 't' &&
name[1] == 'e' &&
name[2] == 'm' &&
name[3] == 'p' &&
name[4] >= '0' &&
name[4] <= '9' &&
strncmp(name + 5, "_input", 6) == 0;
}
static bool parseHwmonDir(FFstrbuf* dir, FFTempValue* value)
{
DIR* dirp = opendir(dir->chars);
if(dirp == NULL)
return false;
uint32_t dirLength = dir->length;
struct dirent* dirent;
while((dirent = readdir(dirp)) != NULL)
{
if(isTempFile(dirent->d_name))
{
ffStrbufAppendS(dir, dirent->d_name);
ffGetFileContent(dir->chars, &value->value);
ffStrbufSubstrBefore(dir, dirLength);
break;
}
}
closedir(dirp);
if(value->value.length == 0)
return false;
ffStrbufAppendS(dir, "name");
ffGetFileContent(dir->chars, &value->name);
ffStrbufSubstrBefore(dir, dirLength);
ffStrbufAppendS(dir, "device/class");
ffGetFileContent(dir->chars, &value->deviceClass);
ffStrbufSubstrBefore(dir, dirLength);
return value->name.length > 0 || value->deviceClass.length > 0;
}
const FFTempsResult* ffDetectTemps(const FFinstance* instance)
{
FF_UNUSED(instance)
static FFTempsResult result;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static bool init = false;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
ffListInitA(&result.values, sizeof(FFTempValue), 16);
FFstrbuf baseDir;
ffStrbufInitA(&baseDir, 64);
ffStrbufAppendS(&baseDir, "/sys/class/hwmon/");
uint32_t baseDirLength = baseDir.length;
DIR* dirp = opendir(baseDir.chars);
if(dirp == NULL)
{
pthread_mutex_unlock(&mutex);
return &result;
}
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
if(entry->d_name[0] == '.')
continue;
ffStrbufAppendS(&baseDir, entry->d_name);
ffStrbufAppendC(&baseDir, '/');
FFTempValue* temp = ffListAdd(&result.values);
ffStrbufInit(&temp->name);
ffStrbufInit(&temp->deviceClass);
ffStrbufInit(&temp->value);
if(!parseHwmonDir(&baseDir, temp))
{
ffStrbufDestroy(&temp->name);
ffStrbufDestroy(&temp->deviceClass);
ffStrbufDestroy(&temp->value);
--result.values.length;
}
ffStrbufSubstrBefore(&baseDir, baseDirLength);
}
pthread_mutex_unlock(&mutex);
return &result;
}
+267
View File
@@ -0,0 +1,267 @@
#include "fastfetch.h"
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
static void setExeName(FFstrbuf* exe, const char** exeName)
{
uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/');
if(lastSlashIndex < exe->length)
*exeName = exe->chars + lastSlashIndex + 1;
}
static void getProcessInformation(const char* pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName)
{
FFstrbuf cmdlineFilePath;
ffStrbufInit(&cmdlineFilePath);
ffStrbufAppendS(&cmdlineFilePath, "/proc/");
ffStrbufAppendS(&cmdlineFilePath, pid);
ffStrbufAppendS(&cmdlineFilePath, "/cmdline");
ffGetFileContent(cmdlineFilePath.chars, exe);
ffStrbufSubstrBeforeFirstC(exe, '\0'); //Trim the arguments
ffStrbufTrimLeft(exe, '-'); //Happens in TTY
if(exe->length == 0)
ffStrbufSet(exe, processName);
setExeName(exe, exeName);
ffStrbufDestroy(&cmdlineFilePath);
}
static void getTerminalShell(FFTerminalShellResult* result, const char* pid)
{
FFstrbuf statFilePath;
ffStrbufInit(&statFilePath);
ffStrbufAppendS(&statFilePath, "/proc/");
ffStrbufAppendS(&statFilePath, pid);
ffStrbufAppendS(&statFilePath, "/stat");
FILE* stat = fopen(statFilePath.chars, "r");
ffStrbufDestroy(&statFilePath);
if(stat == NULL)
return;
char name[256];
name[0] = '\0';
char ppid[256];
ppid[0] = '\0';
if(
fscanf(stat, "%*s (%255[^)]) %*c %255s", name, ppid) != 2 || //stat (comm) state ppid
!ffStrSet(name) ||
!ffStrSet(ppid) ||
*ppid == '-' ||
strcasecmp(ppid, "0") == 0
) {
fclose(stat);
return;
}
fclose(stat);
//Common programs that are between terminal and own process, but are not the shell
if(
strcasecmp(name, "sudo") == 0 ||
strcasecmp(name, "su") == 0 ||
strcasecmp(name, "doas") == 0 ||
strcasecmp(name, "strace") == 0 ||
strcasecmp(name, "sshd") == 0 ||
strcasecmp(name, "gdb") == 0
) {
getTerminalShell(result, ppid);
return;
}
//Known shells
if(
strcasecmp(name, "bash") == 0 ||
strcasecmp(name, "sh") == 0 ||
strcasecmp(name, "zsh") == 0 ||
strcasecmp(name, "ksh") == 0 ||
strcasecmp(name, "fish") == 0 ||
strcasecmp(name, "dash") == 0 ||
strcasecmp(name, "git-shell") == 0
) {
ffStrbufAppendS(&result->shellProcessName, name);
getProcessInformation(pid, &result->shellProcessName, &result->shellExe, &result->shellExeName);
getTerminalShell(result, ppid);
return;
}
ffStrbufAppendS(&result->terminalProcessName, name);
getProcessInformation(pid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName);
}
static void getTerminalFromEnv(FFTerminalShellResult* result)
{
if(
result->terminalProcessName.length > 0 &&
!ffStrbufStartsWithIgnCaseS(&result->terminalProcessName, "login") &&
ffStrbufIgnCaseCompS(&result->terminalProcessName, "(login)") != 0 &&
ffStrbufIgnCaseCompS(&result->terminalProcessName, "systemd") != 0 &&
ffStrbufIgnCaseCompS(&result->terminalProcessName, "init") != 0 &&
ffStrbufIgnCaseCompS(&result->terminalProcessName, "(init)") != 0 &&
ffStrbufIgnCaseCompS(&result->terminalProcessName, "0") != 0
) return;
char* term = NULL;
//SSH
if(getenv("SSH_CONNECTION") != NULL)
term = getenv("SSH_TTY");
//Windows Terminal
if(!ffStrSet(term) && getenv("WT_SESSION") != NULL)
term = "Windows Terminal";
//Normal Terminal
if(!ffStrSet(term))
term = getenv("TERM");
//TTY
if(!ffStrSet(term) || strcasecmp(term, "linux") == 0)
term = ttyname(STDIN_FILENO);
ffStrbufSetS(&result->terminalProcessName, term);
ffStrbufSetS(&result->terminalExe, term);
setExeName(&result->terminalExe, &result->terminalExeName);
}
static void getUserShellFromEnv(FFTerminalShellResult* result)
{
ffStrbufAppendS(&result->userShellExe, getenv("SHELL"));
setExeName(&result->userShellExe, &result->userShellExeName);
//If shell detection via processes failed
if(result->shellProcessName.length == 0 && result->userShellExe.length > 0)
{
ffStrbufAppendS(&result->shellProcessName, result->userShellExeName);
ffStrbufSet(&result->shellExe, &result->userShellExe);
setExeName(&result->shellExe, &result->shellExeName);
}
}
static void getShellVersionBash(FFstrbuf* exe, FFstrbuf* version)
{
ffProcessAppendStdOut(version, (char* const[]) {
"env",
"-i",
exe->chars,
"--norc",
"--noprofile",
"-c",
"printf \"%s\" \"$BASH_VERSION\"",
NULL
});
ffStrbufSubstrBeforeFirstC(version, '(');
}
static void getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version)
{
ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"--version",
NULL
});
ffStrbufTrimRight(version, '\n');
ffStrbufSubstrBeforeLastC(version, ' ');
ffStrbufSubstrAfterFirstC(version, ' ');
}
static void getShellVersionFish(FFstrbuf* exe, FFstrbuf* version)
{
ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"--version",
NULL
});
ffStrbufTrimRight(version, '\n');
ffStrbufSubstrAfterLastC(version, ' ');
}
static void getShellVersionGeneric(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
{
FFstrbuf command;
ffStrbufInit(&command);
ffStrbufAppendS(&command, "printf \"%s\" \"$");
ffStrbufAppendTransformS(&command, exeName, toupper);
ffStrbufAppendS(&command, "_VERSION\"");
ffProcessAppendStdOut(version, (char* const[]) {
"env",
"-i",
exe->chars,
"-c",
command.chars,
NULL
});
ffStrbufSubstrBeforeFirstC(version, '(');
ffStrbufRemoveStrings(version, 2, "-release", "release");
ffStrbufDestroy(&command);
}
static void getShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
{
if(strcasecmp(exeName, "bash") == 0)
getShellVersionBash(exe, version);
else if(strcasecmp(exeName, "zsh") == 0)
getShellVersionZsh(exe, version);
else if(strcasecmp(exeName, "fish") == 0)
getShellVersionFish(exe, version);
else
getShellVersionGeneric(exe, exeName, version);
}
const FFTerminalShellResult* ffDetectTerminalShell(FFinstance* instance)
{
FF_UNUSED(instance);
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static FFTerminalShellResult result;
static bool init = false;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
ffStrbufInit(&result.shellProcessName);
ffStrbufInitA(&result.shellExe, 128);
result.shellExeName = result.shellExe.chars;
ffStrbufInit(&result.shellVersion);
ffStrbufInit(&result.terminalProcessName);
ffStrbufInitA(&result.terminalExe, 128);
result.terminalExeName = result.terminalExe.chars;
ffStrbufInit(&result.userShellExe);
result.userShellExeName = result.userShellExe.chars;
ffStrbufInit(&result.userShellVersion);
char ppid[32];
snprintf(ppid, sizeof(ppid) - 1, "%i", getppid());
getTerminalShell(&result, ppid);
getTerminalFromEnv(&result);
getUserShellFromEnv(&result);
getShellVersion(&result.shellExe, result.shellExeName, &result.shellVersion);
if(strcasecmp(result.shellExeName, result.userShellExeName) != 0)
getShellVersion(&result.userShellExe, result.userShellExeName, &result.userShellVersion);
else
ffStrbufSet(&result.userShellVersion, &result.shellVersion);
pthread_mutex_unlock(&mutex);
return &result;
}
+1140
View File
File diff suppressed because it is too large Load Diff
+554
View File
@@ -0,0 +1,554 @@
#pragma once
#ifndef FASTFETCH_INCLUDED
#define FASTFETCH_INCLUDED
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/sysinfo.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include "fastfetch_config.h"
#include "util/FFstrbuf.h"
#include "util/FFlist.h"
#include "util/FFvaluestore.h"
static inline void ffUnused(int dummy, ...) { (void) dummy; }
#define FF_UNUSED(...) ffUnused(0, __VA_ARGS__);
#define FASTFETCH_TEXT_MODIFIER_BOLT "\033[1m"
#define FASTFETCH_TEXT_MODIFIER_ERROR "\033[1;31m"
#define FASTFETCH_TEXT_MODIFIER_RESET "\033[0m"
#define FASTFETCH_LOGO_MAX_COLORS 9 //two digits would make parsing much more complicated (index 1 - 9)
typedef struct FFlogo
{
const char** names; //Null terminated
char* lines;
bool isFromUser;
const char** builtinColors; // [0] is used as key color, if not user specified
} FFlogo;
typedef struct FFconfig
{
const FFlogo* logo;
FFstrbuf logoColors[FASTFETCH_LOGO_MAX_COLORS];
uint16_t logoKeySpacing;
FFstrbuf separator;
int16_t offsetx;
FFstrbuf color;
bool colorLogo;
bool showErrors;
bool recache;
bool cacheSave;
bool printRemainingLogo;
bool allowSlowOperations;
bool disableLinewrap;
bool hideCursor;
bool userLogoIsRaw;
FFstrbuf osFormat;
FFstrbuf osKey;
FFstrbuf hostFormat;
FFstrbuf hostKey;
FFstrbuf kernelFormat;
FFstrbuf kernelKey;
FFstrbuf uptimeFormat;
FFstrbuf uptimeKey;
FFstrbuf processesFormat;
FFstrbuf processesKey;
FFstrbuf packagesFormat;
FFstrbuf packagesKey;
FFstrbuf shellFormat;
FFstrbuf shellKey;
FFstrbuf resolutionFormat;
FFstrbuf resolutionKey;
FFstrbuf deFormat;
FFstrbuf deKey;
FFstrbuf wmFormat;
FFstrbuf wmKey;
FFstrbuf wmThemeFormat;
FFstrbuf wmThemeKey;
FFstrbuf themeFormat;
FFstrbuf themeKey;
FFstrbuf iconsFormat;
FFstrbuf iconsKey;
FFstrbuf fontFormat;
FFstrbuf fontKey;
FFstrbuf cursorFormat;
FFstrbuf cursorKey;
FFstrbuf terminalFormat;
FFstrbuf terminalKey;
FFstrbuf termFontFormat;
FFstrbuf termFontKey;
FFstrbuf cpuFormat;
FFstrbuf cpuKey;
FFstrbuf cpuUsageFormat;
FFstrbuf cpuUsageKey;
FFstrbuf gpuFormat;
FFstrbuf gpuKey;
FFstrbuf memoryFormat;
FFstrbuf memoryKey;
FFstrbuf diskFormat;
FFstrbuf diskKey;
FFstrbuf batteryFormat;
FFstrbuf batteryKey;
FFstrbuf localeFormat;
FFstrbuf localeKey;
FFstrbuf localIpKey;
FFstrbuf localIpFormat;
FFstrbuf publicIpKey;
FFstrbuf publicIpFormat;
FFstrbuf playerKey;
FFstrbuf playerFormat;
FFstrbuf songKey;
FFstrbuf songFormat;
FFstrbuf dateTimeKey;
FFstrbuf dateTimeFormat;
FFstrbuf dateKey;
FFstrbuf dateFormat;
FFstrbuf timeKey;
FFstrbuf timeFormat;
FFstrbuf libPCI;
FFstrbuf libVulkan;
FFstrbuf libWayland;
FFstrbuf libXcbRandr;
FFstrbuf libXcb;
FFstrbuf libXrandr;
FFstrbuf libX11;
FFstrbuf libGIO;
FFstrbuf libDConf;
FFstrbuf libDBus;
FFstrbuf libXFConf;
FFstrbuf librpm;
FFstrbuf diskFolders;
FFstrbuf batteryDir;
FFstrbuf separatorString;
bool localIpShowLoop;
bool localIpShowIpV4;
bool localIpShowIpV6;
uint32_t publicIpTimeout;
FFstrbuf osFile;
FFstrbuf playerName;
} FFconfig;
typedef struct FFstate
{
uint32_t logoWidth;
char* logoLinesIndex;
struct passwd* passwd;
struct utsname utsname;
struct sysinfo sysinfo;
FFlist configDirs;
FFstrbuf cacheDir;
} FFstate;
typedef struct FFinstance
{
FFconfig config;
FFstate state;
} FFinstance;
typedef struct FFTitleResult
{
FFstrbuf userName;
FFstrbuf hostname;
} FFTitleResult;
typedef struct FFOSResult
{
FFstrbuf systemName;
FFstrbuf name;
FFstrbuf prettyName;
FFstrbuf id;
FFstrbuf idLike;
FFstrbuf variant;
FFstrbuf variantID;
FFstrbuf version;
FFstrbuf versionID;
FFstrbuf codename;
FFstrbuf buildID;
FFstrbuf architecture;
} FFOSResult;
typedef struct FFPlasmaResult
{
FFstrbuf widgetStyle;
FFstrbuf colorScheme;
FFstrbuf icons;
FFstrbuf font;
} FFPlasmaResult;
typedef struct FFGTKResult
{
FFstrbuf theme;
FFstrbuf icons;
FFstrbuf font;
FFstrbuf cursor;
FFstrbuf cursorSize;
} FFGTKResult;
typedef struct FFTerminalShellResult
{
FFstrbuf shellProcessName;
FFstrbuf shellExe;
const char* shellExeName; //pointer to a char in shellExe
FFstrbuf shellVersion;
FFstrbuf terminalProcessName;
FFstrbuf terminalExe;
const char* terminalExeName; //pointer to a char in terminalExe
FFstrbuf userShellExe;
const char* userShellExeName; //pointer to a char in userShellExe
FFstrbuf userShellVersion;
} FFTerminalShellResult;
typedef struct FFResolutionResult
{
uint32_t width;
uint32_t height;
uint32_t refreshRate;
} FFResolutionResult;
typedef struct FFDisplayServerResult
{
FFstrbuf wmProcessName;
FFstrbuf wmPrettyName;
FFstrbuf wmProtocolName;
FFstrbuf deProcessName;
FFstrbuf dePrettyName;
FFstrbuf deVersion;
FFlist resolutions; //List of FFResolutionResult
} FFDisplayServerResult;
typedef struct FFTempValue
{
FFstrbuf name;
FFstrbuf value;
FFstrbuf deviceClass;
} FFTempValue;
typedef struct FFTempsResult
{
FFlist values; //List of FFTempValue
} FFTempsResult;
typedef struct FFMediaResult
{
FFstrbuf busNameShort; //e.g. plasma-browser-integration
FFstrbuf player; // e.g. Google Chrome
FFstrbuf playerPretty; // e.g. YouTube (Google Chrome)
FFstrbuf song;
FFstrbuf artist;
FFstrbuf album;
FFstrbuf url;
} FFMediaResult;
typedef struct FFDateTimeResult
{
//Examples for 21.02.2022 - 15:18:37
uint16_t year; //2022
uint8_t yearShort; //22
uint8_t month; //2
FFstrbuf monthPretty; //02
FFstrbuf monthName; //February
FFstrbuf monthNameShort; //Feb
uint8_t week; //8
FFstrbuf weekday; //Monday
FFstrbuf weekdayShort; //Mon
uint16_t dayInYear; //52
uint8_t dayInMonth; //21
uint8_t dayInWeek; //1
uint8_t hour; //15
FFstrbuf hourPretty; //15
uint8_t hour12; //3
FFstrbuf hour12Pretty; //03
uint8_t minute; //18
FFstrbuf minutePretty; //18
uint8_t second; //37
FFstrbuf secondPretty; //37
} FFDateTimeResult;
typedef enum FFformatargtype
{
FF_FORMAT_ARG_TYPE_NULL = 0,
FF_FORMAT_ARG_TYPE_UINT,
FF_FORMAT_ARG_TYPE_UINT16,
FF_FORMAT_ARG_TYPE_UINT8,
FF_FORMAT_ARG_TYPE_INT,
FF_FORMAT_ARG_TYPE_STRING,
FF_FORMAT_ARG_TYPE_STRBUF,
FF_FORMAT_ARG_TYPE_DOUBLE,
FF_FORMAT_ARG_TYPE_LIST
} FFformatargtype;
typedef struct FFformatarg
{
FFformatargtype type;
const void* value;
} FFformatarg;
typedef struct FFcache
{
FILE* value;
FILE* split;
} FFcache;
typedef enum FFvarianttype
{
FF_VARIANT_TYPE_STRING,
FF_VARIANT_TYPE_BOOL,
FF_VARIANT_TYPE_INT
} FFvarianttype;
typedef union FFvariant
{
const char* strValue;
int32_t intValue;
struct
{
bool boolValueSet;
bool boolValue;
};
} FFvariant;
#define FF_VARIANT_NULL ((FFvariant){.strValue = NULL})
typedef struct FFfont
{
FFstrbuf pretty;
FFstrbuf name;
FFstrbuf size;
FFlist styles;
} FFfont;
typedef struct FFpropquery
{
const char* start;
FFstrbuf* buffer;
} FFpropquery;
typedef enum FFInitState
{
FF_INITSTATE_UNINITIALIZED = 0,
FF_INITSTATE_SUCCESSFUL = 1,
FF_INITSTATE_FAILED = 2
} FFInitState;
#define FF_LIBRARY_SYMBOL(symbolName) \
__typeof__(&symbolName) ff ## symbolName;
#define FF_LIBRARY_LOAD(libraryObjectName, userLibraryName, returnValue, ...) \
void* libraryObjectName = ffLibraryLoad(&userLibraryName, __VA_ARGS__, NULL);\
if(libraryObjectName == NULL) \
return returnValue;
#define FF_LIBRARY_LOAD_SYMBOL_ADRESS(library, symbolMapping, symbolName, returnValue) \
symbolMapping = dlsym(library, #symbolName); \
if(symbolMapping == NULL) \
{ \
dlclose(library); \
return returnValue; \
}
#define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \
__typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADRESS(library, ff ## symbolName, symbolName, returnValue);
/*************************/
/* Common util functions */
/*************************/
//common/init.c
void ffInitInstance(FFinstance* instance);
void ffStart(FFinstance* instance);
void ffFinish(FFinstance* instance);
void ffListFeatures();
//common/threading.c
void ffStartDetectionThreads(FFinstance* instance);
//common/io.c
void ffPrintLogoAndKey(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat);
void ffPrintError(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numFormatArgs, const char* message, ...);
void ffPrintFormatString(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments);
void ffGetCacheFilePath(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer);
void ffReadCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* buffer);
void ffWriteCacheFile(FFinstance* instance, const char* moduleName, const char* extension, FFstrbuf* content);
bool ffPrintFromCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* formatString, uint32_t numArgs);
void ffPrintAndSaveToCache(FFinstance* instance, const char* moduleName, const FFstrbuf* customKeyFormat, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments);
void ffPrintAndAppendToCache(FFinstance* instance, const char* moduleName, uint8_t moduleIndex, const FFstrbuf* customKeyFormat, FFcache* cache, const FFstrbuf* value, const FFstrbuf* formatString, uint32_t numArgs, const FFformatarg* arguments);
void ffCacheValidate(FFinstance* instance);
void ffCacheOpenWrite(FFinstance* instance, const char* moduleName, FFcache* cache);
void ffCacheClose(FFcache* cache);
void ffAppendFDContent(int fd, FFstrbuf* buffer);
bool ffAppendFileContent(const char* fileName, FFstrbuf* buffer); //returns true if open() succeeds. This is used to differentiate between <file not found> and <empty file>
bool ffGetFileContent(const char* fileName, FFstrbuf* buffer);
bool ffWriteFDContent(int fd, const FFstrbuf* content);
void ffWriteFileContent(const char* fileName, const FFstrbuf* buffer);
bool ffFileExists(const char* fileName, mode_t mode);
// Not thread safe!
void ffSuppressIO(bool suppress);
void ffPrintColor(const FFstrbuf* colorValue);
// They return true if the file was found, independently if start was found
// Buffers which already contain content are not overwritten
// The last occurence of start in the first file will be the one used
// The *Values methods always return true, if all properties were already found before, without testing if the file exists
bool ffParsePropFileValues(const char* filename, uint32_t numQueries, FFpropquery* queries);
bool ffParsePropFile(const char* filename, const char* start, FFstrbuf* buffer);
bool ffParsePropFileHomeValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries);
bool ffParsePropFileHome(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer);
bool ffParsePropFileConfigValues(const FFinstance* instance, const char* relativeFile, uint32_t numQueries, FFpropquery* queries);
bool ffParsePropFileConfig(const FFinstance* instance, const char* relativeFile, const char* start, FFstrbuf* buffer);
//common/processing.c
void ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[]);
//common/libraries.c
void* ffLibraryLoad(const FFstrbuf* userProvidedName, ...);
//common/networking.c
void ffNetworkingGetHttp(const char* host, const char* path, uint32_t timeout, FFstrbuf* buffer);
//common/logo.c
void ffLoadLogoSet(FFinstance* instance, const char* logo);
void ffLoadLogo(FFinstance* instance);
void ffPrintLogoLine(FFinstance* instance);
void ffPrintRemainingLogo(FFinstance* instance);
void ffPrintLogos(FFinstance* instance);
void ffListLogos();
void ffListLogosForAutocompletion();
//common/format.c
void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg);
void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, const FFstrbuf* error, uint32_t numArgs, const FFformatarg* arguments);
//common/parsing.c
void ffGetGtkPretty(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4);
void ffFontInitQt(FFfont* font, const char* data);
void ffFontInitPango(FFfont* font, const char* data);
void ffFontInitCopy(FFfont* font, const char* name);
void ffFontDestroy(FFfont* font);
bool ffGetPropValue(const char* line, const char* start, FFstrbuf* buffer);
bool ffGetPropValueFromLines(const char* lines, const char* start, FFstrbuf* buffer);
void ffParseSemver(FFstrbuf* buffer, const FFstrbuf* major, const FFstrbuf* minor, const FFstrbuf* patch);
bool ffStrSet(const char* str);
//common/settings.c
FFvariant ffSettingsGetDConf(FFinstance* instance, const char* key, FFvarianttype type);
FFvariant ffSettingsGetGSettings(FFinstance* instance, const char* schemaName, const char* path, const char* key, FFvarianttype type);
FFvariant ffSettingsGet(FFinstance* instance, const char* dconfKey, const char* gsettingsSchemaName, const char* gsettingsPath, const char* gsettingsKey, FFvarianttype type);
FFvariant ffSettingsGetXFConf(FFinstance* instance, const char* channelName, const char* propertyName, FFvarianttype type);
#ifdef __ANDROID__
bool ffSettingsGetAndroidProperty(const char* propName, FFstrbuf* result);
#endif
//detection/os.c
const FFOSResult* ffDetectOS(FFinstance* instance);
//detection/plasma.c
const FFPlasmaResult* ffDetectPlasma(FFinstance* instance);
//detection/gtk.c
const FFGTKResult* ffDetectGTK2(FFinstance* instance);
const FFGTKResult* ffDetectGTK4(FFinstance* instance);
const FFGTKResult* ffDetectGTK3(FFinstance* instance);
//detection/displayServer.c
const FFDisplayServerResult* ffConnectDisplayServer(const FFinstance* instance);
//detection/terminalShell.c
const FFTerminalShellResult* ffDetectTerminalShell(FFinstance* instance);
//detection/temps.c (currently unused)
const FFTempsResult* ffDetectTemps(const FFinstance* instance);
//detection/media.c
const FFMediaResult* ffDetectMedia(FFinstance* instance);
//detection/dateTime.c
const FFDateTimeResult* ffDetectDateTime(const FFinstance* instance);
/********************/
/* Module functions */
/********************/
//Common
const FFTitleResult* ffDetectTitle(FFinstance* instance);
void ffPrintDateTimeFormat(FFinstance* instance, const char* moduleName, const FFstrbuf* key, const FFstrbuf* format);
//Printing
void ffPrintCustom(FFinstance* instance, const char* key, const char* value);
void ffPrintBreak(FFinstance* instance);
void ffPrintTitle(FFinstance* instance);
void ffPrintSeparator(FFinstance* instance);
void ffPrintOS(FFinstance* instance);
void ffPrintHost(FFinstance* instance);
void ffPrintKernel(FFinstance* instance);
void ffPrintUptime(FFinstance* instance);
void ffPrintProcesses(FFinstance* instance);
void ffPrintPackages(FFinstance* instance);
void ffPrintShell(FFinstance* instance);
void ffPrintResolution(FFinstance* instance);
void ffPrintDesktopEnvironment(FFinstance* instance);
void ffPrintWM(FFinstance* instance);
void ffPrintWMTheme(FFinstance* instance);
void ffPrintTheme(FFinstance* instance);
void ffPrintIcons(FFinstance* instance);
void ffPrintFont(FFinstance* instance);
void ffPrintCursor(FFinstance* instance);
void ffPrintTerminal(FFinstance* instance);
void ffPrintTerminalFont(FFinstance* instance);
void ffPrintCPU(FFinstance* instance);
void ffPrintCPUUsage(FFinstance* instance);
void ffPrintGPU(FFinstance* instance);
void ffPrintMemory(FFinstance* instance);
void ffPrintDisk(FFinstance* instance);
void ffPrintBattery(FFinstance* instance);
void ffPrintLocale(FFinstance* instance);
void ffPrintPlayer(FFinstance* instance);
void ffPrintSong(FFinstance* instance);
void ffPrintDateTime(FFinstance* instance);
void ffPrintDate(FFinstance* instance);
void ffPrintTime(FFinstance* instance);
void ffPrintLocalIp(FFinstance* instance);
void ffPrintPublicIp(FFinstance* instance);
void ffPrintColors(FFinstance* instance);
#endif
+17
View File
@@ -0,0 +1,17 @@
#ifndef FASTFETCH_INDLUDED_fastfetch_config_h_in
#define FASTFETCH_INDLUDED_fastfetch_config_h_in
#define FASTFETCH_PROJECT_NAME "@PROJECT_NAME@"
#define FASTFETCH_PROJECT_VERSION "@PROJECT_VERSION@"
#define FASTFETCH_PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define FASTFETCH_PROJECT_VERSION_EXTRA "@PROJECT_VERSION_EXTRA@"
#define FASTFETCH_DATATEXT_STRUCTURE "@DATATEXT_STRUCTURE@"
#define FASTFETCH_DATATEXT_CONFIG "@DATATEXT_CONFIG@" //Requires FASTFETCH_PROJECT_VERSION and FASTFETCH_DATATEXT_STRUCTURE to be set
#define FASTFETCH_DATATEXT_MODULES "@DATATEXT_MODULES@"
#define FASTFETCH_DATATEXT_HELP "@DATATEXT_HELP@"
#define FASTFETCH_DATATEXT_HELP_COLOR "@DATATEXT_HELP_COLOR@"
#define FASTFETCH_DATATEXT_HELP_FORMAT "@DATATEXT_HELP_FORMAT@"
#define FASTFETCH_DATATEXT_HELP_CONFIG "@DATATEXT_HELP_CONFIG@"
#endif // FASTFETCH_INDLUDED_fastfetch_config_h_in
+61
View File
@@ -0,0 +1,61 @@
#include "fastfetch.h"
int main(int argc, char** argv)
{
//Disable compiler warnings
FF_UNUSED(argc, argv);
FFinstance instance;
ffInitInstance(&instance); //This also applys default configuration to instance.config
//Modify instance.config here
ffLoadLogo(&instance); //ffLoadLogoSet(&instance, "my custom logo");
ffStrbufSet(&instance.config.color, &instance.config.logoColors[0]); //Use the primary color of the logo as key color
//Multithreading --> better performance
ffStartDetectionThreads(&instance);
//Does things like disabling line wrap
ffStart(&instance);
//Printing
ffPrintTitle(&instance);
ffPrintSeparator(&instance);
ffPrintOS(&instance);
ffPrintHost(&instance);
ffPrintKernel(&instance);
ffPrintUptime(&instance);
//ffPrintProcesses(&instance);
ffPrintPackages(&instance);
ffPrintShell(&instance);
ffPrintResolution(&instance);
ffPrintDesktopEnvironment(&instance);
ffPrintWM(&instance);
ffPrintWMTheme(&instance);
ffPrintTheme(&instance);
ffPrintIcons(&instance);
ffPrintFont(&instance);
ffPrintCursor(&instance);
ffPrintTerminal(&instance);
ffPrintTerminalFont(&instance);
ffPrintCPU(&instance);
//ffPrintCPUUsage(&instance);
ffPrintGPU(&instance);
ffPrintMemory(&instance);
ffPrintDisk(&instance);
ffPrintBattery(&instance);
//ffPrintPlayer(&instance);
//ffPrintSong(&instance);
//ffPrintLocalIp(&instance);
//ffPrintPublicIp(&instance);
ffPrintLocale(&instance);
//ffPrintDateTime(&instance);
//ffPrintDate(&instance);
//ffPrintTime(&instance);
ffPrintBreak(&instance);
ffPrintColors(&instance);
ffFinish(&instance);
return 0;
}
+182
View File
@@ -0,0 +1,182 @@
#include "fastfetch.h"
#include <unistd.h>
#include <dirent.h>
#define FF_BATTERY_MODULE_NAME "Battery"
#define FF_BATTERY_NUM_FORMAT_ARGS 5
typedef struct BatteryResult
{
FFstrbuf manufacturer;
FFstrbuf modelName;
FFstrbuf technology;
FFstrbuf capacity;
FFstrbuf status;
} BatteryResult;
static void parseBattery(FFstrbuf* dir, FFlist* results)
{
uint32_t dirLength = dir->length;
FFstrbuf testBatteryBuffer;
ffStrbufInit(&testBatteryBuffer);
//type must exist and be "Battery"
ffStrbufAppendS(dir, "/type");
ffGetFileContent(dir->chars, &testBatteryBuffer);
ffStrbufSubstrBefore(dir, dirLength);
if(ffStrbufIgnCaseCompS(&testBatteryBuffer, "Battery") != 0)
{
ffStrbufDestroy(&testBatteryBuffer);
return;
}
//scope may not exist or must not be "Device"
ffStrbufAppendS(dir, "/scope");
ffGetFileContent(dir->chars, &testBatteryBuffer);
ffStrbufSubstrBefore(dir, dirLength);
if(ffStrbufIgnCaseCompS(&testBatteryBuffer, "Device") == 0)
{
ffStrbufDestroy(&testBatteryBuffer);
return;
}
ffStrbufDestroy(&testBatteryBuffer);
BatteryResult* result = ffListAdd(results);
//capacity must exist and be not empty
ffStrbufInit(&result->capacity);
ffStrbufAppendS(dir, "/capacity");
ffGetFileContent(dir->chars, &result->capacity);
ffStrbufSubstrBefore(dir, dirLength);
if(result->capacity.length == 0)
{
ffStrbufDestroy(&result->capacity);
--results->length;
return;
}
//At this point, we have a battery. Try to get as much values as possible.
ffStrbufInit(&result->manufacturer);
ffStrbufAppendS(dir, "/manufacturer");
ffGetFileContent(dir->chars, &result->manufacturer);
ffStrbufSubstrBefore(dir, dirLength);
ffStrbufInit(&result->modelName);
ffStrbufAppendS(dir, "/model_name");
ffGetFileContent(dir->chars, &result->modelName);
ffStrbufSubstrBefore(dir, dirLength);
ffStrbufInit(&result->technology);
ffStrbufAppendS(dir, "/technology");
ffGetFileContent(dir->chars, &result->technology);
ffStrbufSubstrBefore(dir, dirLength);
ffStrbufInit(&result->status);
ffStrbufAppendS(dir, "/status");
ffGetFileContent(dir->chars, &result->status);
ffStrbufSubstrBefore(dir, dirLength);
}
static void printBattery(FFinstance* instance, const BatteryResult* result, uint8_t index)
{
if(instance->config.batteryFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_BATTERY_MODULE_NAME, index, &instance->config.batteryKey);
bool showStatus =
result->status.length > 0 &&
ffStrbufIgnCaseCompS(&result->status, "Unknown") != 0;
if(result->capacity.length > 0)
{
ffStrbufWriteTo(&result->capacity, stdout);
putchar('%');
if(showStatus)
fputs(" [", stdout);
}
if(showStatus)
{
ffStrbufWriteTo(&result->status, stdout);
if(result->capacity.length > 0)
putchar(']');
}
putchar('\n');
}
else
{
ffPrintFormatString(instance, FF_BATTERY_MODULE_NAME, index, &instance->config.batteryKey, &instance->config.batteryFormat, NULL, FF_BATTERY_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &result->manufacturer},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->modelName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->technology},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->capacity},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->status}
});
}
}
void ffPrintBattery(FFinstance* instance)
{
FFstrbuf baseDir;
ffStrbufInitA(&baseDir, 64);
if(instance->config.batteryDir.length > 0)
{
ffStrbufAppend(&baseDir, &instance->config.batteryDir);
if(!ffStrbufEndsWithC(&baseDir, '/'))
ffStrbufAppendC(&baseDir, '/');
}
else
{
ffStrbufAppendS(&baseDir, "/sys/class/power_supply/");
}
uint32_t baseDirLength = baseDir.length;
DIR* dirp = opendir(baseDir.chars);
if(dirp == NULL)
{
ffPrintError(instance, FF_BATTERY_MODULE_NAME, 0, &instance->config.batteryKey, &instance->config.batteryFormat, FF_BATTERY_NUM_FORMAT_ARGS, "opendir(\"%s\") == NULL", baseDir.chars);
ffStrbufDestroy(&baseDir);
return;
}
FFlist results;
ffListInitA(&results, sizeof(BatteryResult), 4);
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
ffStrbufAppendS(&baseDir, entry->d_name);
parseBattery(&baseDir, &results);
ffStrbufSubstrBefore(&baseDir, baseDirLength);
}
closedir(dirp);
for(uint8_t i = 0; i < (uint8_t) results.length; i++)
{
BatteryResult* result = ffListGet(&results, i);
printBattery(instance, result, i);
ffStrbufDestroy(&result->manufacturer);
ffStrbufDestroy(&result->modelName);
ffStrbufDestroy(&result->technology);
ffStrbufDestroy(&result->capacity);
ffStrbufDestroy(&result->status);
}
if(results.length == 0)
ffPrintError(instance, FF_BATTERY_MODULE_NAME, 0, &instance->config.batteryKey, &instance->config.batteryFormat, FF_BATTERY_NUM_FORMAT_ARGS, "%s doesn't contain any battery folder", baseDir.chars);
ffListDestroy(&results);
ffStrbufDestroy(&baseDir);
}
+7
View File
@@ -0,0 +1,7 @@
#include "fastfetch.h"
void ffPrintBreak(FFinstance* instance)
{
ffPrintLogoLine(instance);
putchar('\n');
}
+25
View File
@@ -0,0 +1,25 @@
#include "fastfetch.h"
void ffPrintColors(FFinstance* instance)
{
ffPrintLogoLine(instance);
// 4%d: Set the background color
// 3%d: Set the foreground color
for(uint8_t i = 0; i < 8; i++)
printf("\033[4%d;3%dm███", i, i);
puts(FASTFETCH_TEXT_MODIFIER_RESET);
ffPrintLogoLine(instance);
// 1: Set everything to bolt. This causes normal colors on some systems to be bright.
// 4%d: Set the backgound to the not bright color
// 3%d: Set the foreground to the not bright color
// 10%d: Set the background to the bright color
// 9%d: Set the foreground to the bright color
for(uint8_t i = 0; i < 8; i++)
printf("\033[1;4%d;3%d;10%d;9%dm███", i, i, i, i);
puts(FASTFETCH_TEXT_MODIFIER_RESET);
}
+188
View File
@@ -0,0 +1,188 @@
#include "fastfetch.h"
#include <string.h>
#define FF_CPU_MODULE_NAME "CPU"
#define FF_CPU_NUM_FORMAT_ARGS 14
static double parseHz(FFstrbuf* content)
{
if(content->length == 0)
return 0;
double herz;
if(sscanf(content->chars, "%lf", &herz) != 1)
return 0;
return herz;
}
static double getGhz(const char* policyFile, const char* cpuFile)
{
FFstrbuf content;
ffStrbufInit(&content);
ffGetFileContent(policyFile, &content);
if(content.length == 0)
ffGetFileContent(cpuFile, &content);
double herz = parseHz(&content);
ffStrbufDestroy(&content);
herz /= 1000.0; //to MHz
return herz / 1000.0; //to GHz
}
void ffPrintCPU(FFinstance* instance)
{
if(ffPrintFromCache(instance, FF_CPU_MODULE_NAME, &instance->config.cpuKey, &instance->config.cpuFormat, FF_CPU_NUM_FORMAT_ARGS))
return;
FILE* cpuinfo = fopen("/proc/cpuinfo", "r");
if(cpuinfo == NULL)
{
ffPrintError(instance, FF_CPU_MODULE_NAME, 0, &instance->config.cpuKey, &instance->config.cpuFormat, FF_CPU_NUM_FORMAT_ARGS, "fopen(\"/proc/cpuinfo\", \"r\") == NULL");
return;
}
FFstrbuf name;
ffStrbufInitA(&name, 64);
FFstrbuf vendor;
ffStrbufInitA(&vendor, 64);
FFstrbuf physicalCoresString;
ffStrbufInit(&physicalCoresString);
FFstrbuf procGhzString;
ffStrbufInit(&procGhzString);
char* line = NULL;
size_t len = 0;
while(getline(&line, &len, cpuinfo) != -1)
{
//Stop after the first CPU
if(name.length > 0 && (*line == '\0' || *line == '\n'))
break;
(void)(
ffGetPropValue(line, "model name :", &name) ||
ffGetPropValue(line, "vendor_id :", &vendor) ||
ffGetPropValue(line, "cpu cores :", &physicalCoresString) ||
ffGetPropValue(line, "cpu MHz :", &procGhzString) ||
(name.length == 0 && ffGetPropValue(line, "Hardware :", &name)) //For Android devices
);
}
if(line != NULL)
free(line);
fclose(cpuinfo);
double procGhz = parseHz(&procGhzString) / 1000.0; //to GHz
ffStrbufDestroy(&procGhzString);
double biosLimit = getGhz("/sys/devices/system/cpu/cpufreq/policy0/bios_limit", "/sys/devices/system/cpu/cpu0/cpufreq/bios_limit");
double scalingMaxFreq = getGhz("/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
double scalingMinFreq = getGhz("/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq");
double infoMaxFreq = getGhz("/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_max_freq", "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
double infoMinFreq = getGhz("/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_min_freq", "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq");
int numProcsOnline = get_nprocs();
int numProcsAvailable = get_nprocs_conf();
int physicalCores = 1;
sscanf(physicalCoresString.chars, "%i", &physicalCores);
ffStrbufDestroy(&physicalCoresString);
//The current get_nprocs* returns 1 on failure. It also makes no sense to have a (1) as count
int numProcs = numProcsOnline;
if(numProcs <= 1)
numProcs = numProcsAvailable;
if(numProcs <= 1)
numProcs = physicalCores;
double ghz = biosLimit;
if(ghz == 0)
ghz = scalingMaxFreq;
if(ghz == 0)
ghz = infoMaxFreq;
if(ghz == 0)
ghz = procGhz;
if(ghz == 0)
ghz = scalingMinFreq;
if(ghz == 0)
ghz = infoMinFreq;
if(
name.length == 0 &&
vendor.length == 0 &&
numProcs <= 1 &&
ghz <= 0
) {
ffStrbufDestroy(&name);
ffStrbufDestroy(&vendor);
ffPrintError(instance, FF_CPU_MODULE_NAME, 0, &instance->config.cpuKey, &instance->config.cpuFormat, FF_CPU_NUM_FORMAT_ARGS, "No CPU info found in /proc/cpuinfo");
return;
}
FFstrbuf namePretty;
ffStrbufInitA(&namePretty, 64);
ffStrbufAppend(&namePretty, &name);
const char* removeStrings[] = {
"(R)", "(r)", "(TM)", "(tm)",
" CPU", " FPU", " APU", " Processor",
" Dual-Core", " Quad-Core", " Six-Core", " Eight-Core", " Ten-Core",
" 2-Core", " 4-Core", " 6-Core", " 8-Core", " 10-Core", " 12-Core", " 14-Core", " 16-Core"
};
ffStrbufRemoveStringsA(&namePretty, sizeof(removeStrings) / sizeof(removeStrings[0]), removeStrings);
ffStrbufSubstrBeforeFirstC(&namePretty, '@'); //Cut the speed output in the name as we append our own
ffStrbufTrimRight(&namePretty, ' '); //If we removed the @ in previous step there was most likely a space before it
FFstrbuf cpu;
ffStrbufInitA(&cpu, 128);
if(namePretty.length > 0)
ffStrbufAppend(&cpu, &namePretty);
else if(name.length > 0)
ffStrbufAppend(&cpu, &name);
else if(vendor.length > 0)
{
ffStrbufAppend(&cpu, &vendor);
ffStrbufAppendS(&cpu, " CPU");
}
else
ffStrbufAppendS(&cpu, "CPU");
if(numProcs > 1)
ffStrbufAppendF(&cpu, " (%i)", numProcs);
if(ghz > 0)
ffStrbufAppendF(&cpu, " @ %.9gGHz", ghz);
ffPrintAndSaveToCache(instance, FF_CPU_MODULE_NAME, &instance->config.cpuKey, &cpu, &instance->config.cpuFormat, FF_CPU_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &name},
{FF_FORMAT_ARG_TYPE_STRBUF, &namePretty},
{FF_FORMAT_ARG_TYPE_STRBUF, &vendor},
{FF_FORMAT_ARG_TYPE_INT, &numProcsOnline},
{FF_FORMAT_ARG_TYPE_INT, &numProcsAvailable},
{FF_FORMAT_ARG_TYPE_INT, &physicalCores},
{FF_FORMAT_ARG_TYPE_INT, &numProcs},
{FF_FORMAT_ARG_TYPE_DOUBLE, &biosLimit},
{FF_FORMAT_ARG_TYPE_DOUBLE, &scalingMaxFreq},
{FF_FORMAT_ARG_TYPE_DOUBLE, &scalingMinFreq},
{FF_FORMAT_ARG_TYPE_DOUBLE, &infoMaxFreq},
{FF_FORMAT_ARG_TYPE_DOUBLE, &infoMinFreq},
{FF_FORMAT_ARG_TYPE_DOUBLE, &procGhz},
{FF_FORMAT_ARG_TYPE_DOUBLE, &ghz}
});
ffStrbufDestroy(&cpu);
ffStrbufDestroy(&namePretty);
ffStrbufDestroy(&name);
ffStrbufDestroy(&vendor);
}
+49
View File
@@ -0,0 +1,49 @@
#include "fastfetch.h"
#include <stdio.h>
#include <unistd.h>
#define FF_CPU_USAGE_MODULE_NAME "CPU Usage"
#define FF_CPU_USAGE_NUM_FORMAT_ARGS 1
void ffPrintCPUUsage(FFinstance* instance)
{
long user, nice, system, idle, iowait, irq, softirq;
FILE* procStat = fopen("/proc/stat", "r");
if(procStat == NULL)
{
ffPrintError(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsageKey, &instance->config.cpuUsageFormat, FF_CPU_USAGE_NUM_FORMAT_ARGS, "fopen(\"/proc/stat\", \"r\") == NULL");
return;
}
if (fscanf(procStat, "cpu%ld%ld%ld%ld%ld%ld%ld", &user, &nice, &system, &idle, &iowait, &irq, &softirq) < 0) goto exit;
long workJiffies1 = user + nice + system;
long totalJiffies1 = workJiffies1 + idle + iowait + irq + softirq;
sleep(1);
rewind(procStat);
if (fscanf(procStat, "cpu%ld%ld%ld%ld%ld%ld%ld", &user, &nice, &system, &idle, &iowait, &irq, &softirq) < 0) goto exit;
long workJiffies2 = user + nice + system;
long totalJiffies2 = workJiffies2 + idle + iowait + irq + softirq;
// https://stackoverflow.com/questions/3017162/how-to-get-total-cpu-usage-in-linux-using-c#answer-3017438
long workOverPeriod = workJiffies2 - workJiffies1;
long totalOverPeriod = totalJiffies2 - totalJiffies1;
double cpuPercent = (double)workOverPeriod / (double)totalOverPeriod * 100;
if(instance->config.cpuUsageFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsageKey);
printf("%.2lf%%\n", cpuPercent);
}
else
{
ffPrintFormatString(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsageKey, &instance->config.cpuUsageFormat, NULL, FF_CPU_USAGE_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_DOUBLE, &cpuPercent}
});
}
exit:
fclose(procStat);
}
+232
View File
@@ -0,0 +1,232 @@
#include "fastfetch.h"
#define FF_CURSOR_MODULE_NAME "Cursor"
#define FF_CURSOR_NUM_FORMAT_ARGS 2
static void printCursor(FFinstance* instance, FFstrbuf* cursorTheme, const FFstrbuf* cursorSize)
{
ffStrbufRemoveIgnCaseEndS(cursorTheme, "cursors");
ffStrbufRemoveIgnCaseEndS(cursorTheme, "cursor");
ffStrbufTrimRight(cursorTheme, '_');
ffStrbufTrimRight(cursorTheme, '-');
if(cursorTheme->length == 0)
ffStrbufAppendS(cursorTheme, "default");
if(instance->config.cursorFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursorKey);
ffStrbufWriteTo(cursorTheme, stdout);
if(cursorSize != NULL && cursorSize->length > 0)
{
fputs(" (", stdout);
ffStrbufWriteTo(cursorSize, stdout);
fputs("px)", stdout);
}
putchar('\n');
}
else
{
ffPrintFormatString(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursorKey, &instance->config.cursorFormat, NULL, FF_CURSOR_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, cursorTheme},
{FF_FORMAT_ARG_TYPE_STRBUF, cursorSize}
});
}
}
static void printCursorGTK(FFinstance* instance)
{
const FFGTKResult* gtk = ffDetectGTK4(instance);
if(gtk->cursor.length == 0)
gtk = ffDetectGTK3(instance);
if(gtk->cursor.length == 0)
gtk = ffDetectGTK2(instance);
if(gtk->cursor.length == 0)
{
ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursorKey, &instance->config.cursorFormat, FF_CURSOR_NUM_FORMAT_ARGS, "Couldn't detect GTK Cursor");
return;
}
//gtk->cursor is const, so we don't want to modify it
FFstrbuf theme;
ffStrbufInitCopy(&theme, &gtk->cursor);
printCursor(instance, &theme, &gtk->cursorSize);
ffStrbufDestroy(&theme);
}
static void printCursorXFCE(FFinstance* instance)
{
FFstrbuf cursorTheme;
ffStrbufInit(&cursorTheme);
ffStrbufAppendS(&cursorTheme, ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeName", FF_VARIANT_TYPE_STRING).strValue);
if(cursorTheme.length == 0)
{
ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursorKey, &instance->config.cursorFormat, FF_CURSOR_NUM_FORMAT_ARGS, "Couldn't find xfce cursor in xfconf (xsettings::/Gtk/CursorThemeName)");
return;
}
FFstrbuf cursorSize;
ffStrbufInit(&cursorSize);
int cursorSizeVal = ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeSize", FF_VARIANT_TYPE_INT).intValue;
if(cursorSizeVal > 0)
ffStrbufAppendF(&cursorSize, "%i", cursorSizeVal);
printCursor(instance, &cursorTheme, &cursorSize);
ffStrbufDestroy(&cursorTheme);
ffStrbufDestroy(&cursorSize);
}
static void printCursorFromConfigFile(FFinstance* instance, const char* relativeFilePath, const char* themeStart, const char* themeDefault, const char* sizeStart, const char* sizeDefault)
{
FFstrbuf cursorTheme;
ffStrbufInit(&cursorTheme);
FFstrbuf cursorSize;
ffStrbufInit(&cursorSize);
if(ffParsePropFileConfigValues(instance, relativeFilePath, 2, (FFpropquery[]) {
{themeStart, &cursorTheme},
{sizeStart, &cursorSize}
})) {
if(cursorTheme.length == 0)
ffStrbufAppendS(&cursorTheme, themeDefault);
if(cursorSize.length == 0)
ffStrbufAppendS(&cursorSize, sizeDefault);
}
if(cursorTheme.length == 0)
ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursorKey, &instance->config.cursorFormat, FF_CURSOR_NUM_FORMAT_ARGS, "Couldn't find cursor in %s", relativeFilePath);
else
printCursor(instance, &cursorTheme, &cursorSize);
ffStrbufDestroy(&cursorTheme);
ffStrbufDestroy(&cursorSize);
}
static bool printCursorFromXResources(FFinstance* instance)
{
FFstrbuf theme;
ffStrbufInit(&theme);
FFstrbuf size;
ffStrbufInit(&size);
ffParsePropFileHomeValues(instance, ".Xresources", 2, (FFpropquery[]) {
{"Xcursor.theme :", &theme},
{"Xcursor.size :", &size}
});
if(theme.length == 0)
{
ffStrbufDestroy(&size);
ffStrbufDestroy(&theme);
return false;
}
printCursor(instance, &theme, &size);
ffStrbufDestroy(&size);
ffStrbufDestroy(&theme);
return true;
}
static bool printCursorFromXDG(FFinstance* instance, bool user)
{
FFstrbuf theme;
ffStrbufInit(&theme);
if(user)
ffParsePropFileHome(instance, ".icons/default/index.theme", "Inherits =", &theme);
else
ffParsePropFile("/usr/share/icons/default/index.theme", "Inherits =", &theme);
if(theme.length == 0)
{
ffStrbufDestroy(&theme);
return false;
}
printCursor(instance, &theme, NULL);
ffStrbufDestroy(&theme);
return true;
}
static bool printCursorFromEnv(FFinstance* instance)
{
const char* xcursor_theme = getenv("XCURSOR_THEME");
if(!ffStrSet(xcursor_theme))
return false;
FFstrbuf theme;
ffStrbufInit(&theme);
ffStrbufAppendS(&theme, xcursor_theme);
FFstrbuf size;
ffStrbufInit(&size);
ffStrbufAppendS(&size, getenv("XCURSOR_SIZE"));
printCursor(instance, &theme, &size);
ffStrbufDestroy(&size);
ffStrbufDestroy(&theme);
return true;
}
void ffPrintCursor(FFinstance* instance)
{
#ifdef __ANDROID__
ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursorKey, &instance->config.cursorFormat, FF_CURSOR_NUM_FORMAT_ARGS, "Cursor detection is not supported on Android");
return;
#endif
const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
if(ffStrbufIgnCaseCompS(&wmde->wmProtocolName, "TTY") == 0)
{
ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursorKey, &instance->config.cursorFormat, FF_CURSOR_NUM_FORMAT_ARGS, "Cursor isn't supported in TTY");
return;
}
if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "KDE Plasma") == 0)
{
printCursorFromConfigFile(instance, "kcminputrc", "cursorTheme =", "Breeze", "cursorSize =", "24");
return;
}
if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "XFCE"))
{
printCursorXFCE(instance);
return;
}
if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "LXQt"))
{
printCursorFromConfigFile(instance, "lxqt/session.conf", "cursor_theme =", "Adwaita", "cursor_size =", "24");
return;
}
if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Gnome") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Cinnamon") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Mate") == 0)
{
printCursorGTK(instance);
return;
}
if(
printCursorFromEnv(instance) ||
printCursorFromXDG(instance, true) ||
printCursorFromXResources(instance) ||
printCursorFromXDG(instance, false)
) return;
printCursorGTK(instance);
}
+7
View File
@@ -0,0 +1,7 @@
#include "fastfetch.h"
void ffPrintCustom(FFinstance* instance, const char* key, const char* value)
{
ffPrintLogoAndKey(instance, key, 0, NULL);
puts(value);
}
+18
View File
@@ -0,0 +1,18 @@
#include "fastfetch.h"
#define FF_DATE_MODULE_NAME "Date"
void ffPrintDate(FFinstance* instance)
{
if(instance->config.dateFormat.length > 0)
{
ffPrintDateTimeFormat(instance, FF_DATE_MODULE_NAME, &instance->config.dateKey, &instance->config.dateFormat);
return;
}
const FFDateTimeResult* datetime = ffDetectDateTime(instance);
ffPrintLogoAndKey(instance, FF_DATE_MODULE_NAME, 0, &instance->config.dateKey);
//yyyy-mm-dd
printf("%u-%s-%u\n", datetime->year, datetime->monthPretty.chars, datetime->dayInMonth);
}
+46
View File
@@ -0,0 +1,46 @@
#include "fastfetch.h"
#define FF_DATETIME_MODULE_NAME "Date & Time"
#define FF_DATETIME_NUM_FORMAT_ARGS 20
void ffPrintDateTimeFormat(FFinstance* instance, const char* moduleName, const FFstrbuf* key, const FFstrbuf* format)
{
const FFDateTimeResult* result = ffDetectDateTime(instance);
ffPrintFormatString(instance, moduleName, 0, key, format, NULL, FF_DATETIME_NUM_FORMAT_ARGS, (FFformatarg[]) {
{FF_FORMAT_ARG_TYPE_UINT16, &result->year},
{FF_FORMAT_ARG_TYPE_UINT8, &result->yearShort},
{FF_FORMAT_ARG_TYPE_UINT8, &result->month},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->monthPretty},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->monthName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->monthNameShort},
{FF_FORMAT_ARG_TYPE_UINT8, &result->week},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->weekday},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->weekdayShort},
{FF_FORMAT_ARG_TYPE_UINT16, &result->dayInYear},
{FF_FORMAT_ARG_TYPE_UINT8, &result->dayInMonth},
{FF_FORMAT_ARG_TYPE_UINT8, &result->dayInWeek},
{FF_FORMAT_ARG_TYPE_UINT8, &result->hour},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->hourPretty},
{FF_FORMAT_ARG_TYPE_UINT8, &result->hour12},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->hour12Pretty},
{FF_FORMAT_ARG_TYPE_UINT8, &result->minute},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->minutePretty},
{FF_FORMAT_ARG_TYPE_UINT8, &result->second},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->secondPretty}
});
}
void ffPrintDateTime(FFinstance* instance)
{
if(instance->config.dateTimeFormat.length > 0)
{
ffPrintDateTimeFormat(instance, FF_DATETIME_MODULE_NAME, &instance->config.dateTimeKey, &instance->config.dateTimeFormat);
return;
}
const FFDateTimeResult* datetime = ffDetectDateTime(instance);
ffPrintLogoAndKey(instance, FF_DATETIME_MODULE_NAME, 0, &instance->config.dateTimeKey);
//yyyy-mm-dd hh:mm:ss
printf("%u-%s-%u %s:%s:%s\n", datetime->year, datetime->monthPretty.chars, datetime->dayInMonth, datetime->hourPretty.chars, datetime->minutePretty.chars, datetime->secondPretty.chars);
}
+45
View File
@@ -0,0 +1,45 @@
#include "fastfetch.h"
#include <string.h>
#define FF_DE_MODULE_NAME "DE"
#define FF_DE_NUM_FORMAT_ARGS 3
void ffPrintDesktopEnvironment(FFinstance* instance)
{
#ifdef __ANDROID__
ffPrintError(instance, FF_DE_MODULE_NAME, 0, &instance->config.deKey, &instance->config.deFormat, FF_DE_NUM_FORMAT_ARGS, "DE detection is not supported on Android");
return;
#endif
const FFDisplayServerResult* result = ffConnectDisplayServer(instance);
if(result->dePrettyName.length == 0)
{
ffPrintError(instance, FF_DE_MODULE_NAME, 0, &instance->config.deKey, &instance->config.deFormat, FF_DE_NUM_FORMAT_ARGS, "No DE found");
return;
}
if(instance->config.deFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_DE_MODULE_NAME, 0, &instance->config.deKey);
ffStrbufWriteTo(&result->dePrettyName, stdout);
if(result->deVersion.length > 0)
{
putchar(' ');
ffStrbufWriteTo(&result->deVersion, stdout);
}
putchar('\n');
}
else
{
ffPrintFormatString(instance, FF_DE_MODULE_NAME, 0, &instance->config.deKey, &instance->config.deFormat, NULL, FF_DE_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &result->deProcessName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->dePrettyName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->deVersion}
});
}
}
+128
View File
@@ -0,0 +1,128 @@
#include "fastfetch.h"
#include <sys/statvfs.h>
#define FF_DISK_MODULE_NAME "Disk"
#define FF_DISK_NUM_FORMAT_ARGS 4
static void getKey(FFinstance* instance, FFstrbuf* key, const char* folderPath, bool showFolderPath)
{
if(instance->config.diskKey.length == 0)
{
if(showFolderPath)
ffStrbufAppendF(key, FF_DISK_MODULE_NAME" (%s)", folderPath);
else
ffStrbufSetS(key, FF_DISK_MODULE_NAME);
}
else
{
ffParseFormatString(key, &instance->config.diskKey, NULL, 1, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRING, folderPath}
});
}
}
static void printStatvfs(FFinstance* instance, FFstrbuf* key, struct statvfs* fs)
{
const uint32_t GB = 1024 * 1024 * 1024;
uint32_t total = (uint32_t) ((fs->f_blocks * fs->f_frsize) / GB);
uint32_t available = (uint32_t) ((fs->f_bfree * fs->f_frsize) / GB);
uint32_t used = total - available;
uint8_t percentage = (uint8_t) ((used / (double) total) * 100.0);
uint32_t files = (uint32_t) (fs->f_files - fs->f_ffree);
if(instance->config.diskFormat.length == 0)
{
ffPrintLogoAndKey(instance, key->chars, 0, NULL);
printf("%uGB / %uGB (%u%%)\n", used, total, percentage);
}
else
{
ffPrintFormatString(instance, key->chars, 0, NULL, &instance->config.diskFormat, NULL, FF_DISK_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_UINT, &used},
{FF_FORMAT_ARG_TYPE_UINT, &total},
{FF_FORMAT_ARG_TYPE_UINT, &files},
{FF_FORMAT_ARG_TYPE_UINT8, &percentage}
});
}
}
static void printStatvfsCreateKey(FFinstance* instance, const char* folderPath, struct statvfs* fs)
{
FF_STRBUF_CREATE(key);
getKey(instance, &key, folderPath, true);
printStatvfs(instance, &key, fs);
ffStrbufDestroy(&key);
}
static void printFolder(FFinstance* instance, const char* folderPath)
{
FF_STRBUF_CREATE(key);
getKey(instance, &key, folderPath, true);
struct statvfs fs;
int ret = statvfs(folderPath, &fs);
if(ret != 0 && instance->config.diskFormat.length == 0)
{
ffPrintError(instance, key.chars, 0, NULL, &instance->config.diskFormat, FF_DISK_NUM_FORMAT_ARGS, "statvfs(\"%s\", &fs) != 0 (%i)", folderPath, ret);
ffStrbufDestroy(&key);
return;
}
printStatvfs(instance, &key, &fs);
ffStrbufDestroy(&key);
}
void ffPrintDisk(FFinstance* instance)
{
if(instance->config.diskFolders.length == 0)
{
struct statvfs fsRoot;
int rootRet = statvfs("/", &fsRoot);
struct statvfs fsHome;
int homeRet = statvfs("/home", &fsHome);
if(rootRet != 0 && homeRet != 0)
{
FF_STRBUF_CREATE(key);
getKey(instance, &key, "", false);
ffPrintError(instance, key.chars, 0, NULL, &instance->config.diskFormat, FF_DISK_NUM_FORMAT_ARGS, "statvfs failed for both / and /home");
ffStrbufDestroy(&key);
return;
}
if(rootRet == 0)
printStatvfsCreateKey(instance, "/", &fsRoot);
if(homeRet == 0 && (rootRet != 0 || fsRoot.f_fsid != fsHome.f_fsid))
printStatvfsCreateKey(instance, "/home", &fsHome);
}
else
{
ffStrbufTrim(&instance->config.diskFolders, ':');
if(instance->config.diskFolders.length == 0)
{
FF_STRBUF_CREATE(key);
getKey(instance, &key, "", false);
ffPrintError(instance, key.chars, 0, NULL, &instance->config.diskFormat, FF_DISK_NUM_FORMAT_ARGS, "Custom disk folders string doesn't contain any folders");
ffStrbufDestroy(&key);
return;
}
uint32_t startIndex = 0;
while (startIndex < instance->config.diskFolders.length)
{
uint32_t colonIndex = ffStrbufNextIndexC(&instance->config.diskFolders, startIndex, ':');
instance->config.diskFolders.chars[colonIndex] = '\0';
printFolder(instance, instance->config.diskFolders.chars + startIndex);
startIndex = colonIndex + 1;
}
}
}
+93
View File
@@ -0,0 +1,93 @@
#include "fastfetch.h"
#define FF_FONT_MODULE_NAME "Font"
#define FF_FONT_NUM_FORMAT_ARGS 21
void ffPrintFont(FFinstance* instance)
{
#ifdef __ANDROID__
ffPrintError(instance, FF_FONT_MODULE_NAME, 0, &instance->config.fontKey, &instance->config.fontFormat, FF_FONT_NUM_FORMAT_ARGS, "Font detection is not supported on Android");
return;
#endif
const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
if(ffStrbufIgnCaseCompS(&wmde->wmProtocolName, "TTY") == 0)
{
ffPrintError(instance, FF_FONT_MODULE_NAME, 0, &instance->config.fontKey, &instance->config.fontFormat, FF_FONT_NUM_FORMAT_ARGS, "Font isn't supported in TTY");
return;
}
const FFstrbuf* plasmaRaw = &ffDetectPlasma(instance)->font;
const FFstrbuf* gtk2Raw = &ffDetectGTK2(instance)->font;
const FFstrbuf* gtk3Raw = &ffDetectGTK3(instance)->font;
const FFstrbuf* gtk4Raw = &ffDetectGTK4(instance)->font;
if(plasmaRaw->length == 0 && gtk2Raw->length == 0 && gtk3Raw->length == 0 && gtk4Raw->length == 0)
{
ffPrintError(instance, FF_FONT_MODULE_NAME, 0, &instance->config.fontKey, &instance->config.fontFormat, FF_FONT_NUM_FORMAT_ARGS, "No fonts found");
return;
}
FFfont plasma;
ffFontInitQt(&plasma, plasmaRaw->chars);
FFfont gtk2;
ffFontInitPango(&gtk2, gtk2Raw->chars);
FFfont gtk3;
ffFontInitPango(&gtk3, gtk3Raw->chars);
FFfont gtk4;
ffFontInitPango(&gtk4, gtk4Raw->chars);
FFstrbuf gtk;
ffStrbufInitA(&gtk, 64);
ffGetGtkPretty(&gtk, &gtk2.pretty, &gtk3.pretty, &gtk4.pretty);
if(instance->config.fontFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_FONT_MODULE_NAME, 0, &instance->config.fontKey);
if(plasma.pretty.length > 0)
{
ffStrbufWriteTo(&plasma.pretty, stdout);
fputs(" [Plasma]", stdout);
if(gtk.length > 0)
fputs(", ", stdout);
}
ffStrbufPutTo(&gtk, stdout);
}
else
{
ffPrintFormatString(instance, FF_FONT_MODULE_NAME, 0, &instance->config.fontKey, &instance->config.fontFormat, NULL, FF_FONT_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, plasmaRaw},
{FF_FORMAT_ARG_TYPE_STRBUF, &plasma.name},
{FF_FORMAT_ARG_TYPE_STRBUF, &plasma.size},
{FF_FORMAT_ARG_TYPE_LIST, &plasma.styles},
{FF_FORMAT_ARG_TYPE_STRBUF, &plasma.pretty},
{FF_FORMAT_ARG_TYPE_STRBUF, gtk2Raw},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk2.name},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk2.size},
{FF_FORMAT_ARG_TYPE_LIST, &gtk2.styles},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk2.pretty},
{FF_FORMAT_ARG_TYPE_STRBUF, gtk3Raw},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk3.name},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk3.size},
{FF_FORMAT_ARG_TYPE_LIST, &gtk3.styles},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk3.pretty},
{FF_FORMAT_ARG_TYPE_STRBUF, gtk4Raw},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk4.name},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk4.size},
{FF_FORMAT_ARG_TYPE_LIST, &gtk4.styles},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk4.pretty},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtk}
});
}
ffFontDestroy(&plasma);
ffFontDestroy(&gtk2);
ffFontDestroy(&gtk3);
ffFontDestroy(&gtk4);
ffStrbufDestroy(&gtk);
}
+253
View File
@@ -0,0 +1,253 @@
#include "fastfetch.h"
#include <string.h>
#define FF_GPU_MODULE_NAME "GPU"
#define FF_GPU_NUM_FORMAT_ARGS 5
typedef struct GPUResult
{
FFstrbuf vendor;
FFstrbuf name;
FFstrbuf driver;
} GPUResult;
#ifdef FF_HAVE_VULKAN
#include <vulkan/vulkan.h>
static void vulkanFillGPUs(FFinstance* instance, FFlist* results)
{
FF_LIBRARY_LOAD(vulkan, instance->config.libVulkan, , "libvulkan.so", "libvulkan.so.1")
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkCreateInstance,)
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkDestroyInstance,)
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkEnumeratePhysicalDevices,)
FF_LIBRARY_LOAD_SYMBOL(vulkan, vkGetPhysicalDeviceProperties,)
//Some drivers (nvdc) print messages to stdout
//and thats the best way i found to disable that
ffSuppressIO(true);
const VkApplicationInfo applicationInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = NULL,
.pApplicationName = FASTFETCH_PROJECT_NAME,
.applicationVersion = VK_MAKE_VERSION(FASTFETCH_PROJECT_VERSION_MAJOR, 0, 0),
.pEngineName = "vulkanPrintGPUs",
.engineVersion = VK_MAKE_VERSION(FASTFETCH_PROJECT_VERSION_MAJOR, 0, 0),
.apiVersion = VK_API_VERSION_1_0,
};
const VkInstanceCreateInfo instanceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = NULL,
.pApplicationInfo = &applicationInfo,
.enabledLayerCount = 0,
.ppEnabledLayerNames = NULL,
.enabledExtensionCount = 0,
.ppEnabledExtensionNames = NULL,
.flags = 0
};
VkInstance vkInstance;
if(ffvkCreateInstance(&instanceCreateInfo, NULL, &vkInstance) != VK_SUCCESS)
{
dlclose(vulkan);
ffSuppressIO(false);
return;
}
uint32_t physicalDeviceCount;
if(ffvkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, NULL) != VK_SUCCESS)
{
ffvkDestroyInstance(vkInstance, NULL);
dlclose(vulkan);
ffSuppressIO(false);
return;
}
VkPhysicalDevice* physicalDevices = malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
if(ffvkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, physicalDevices) != VK_SUCCESS)
{
free(physicalDevices);
ffvkDestroyInstance(vkInstance, NULL);
dlclose(vulkan);
ffSuppressIO(false);
return;
}
for(uint32_t i = 0; i < physicalDeviceCount; i++)
{
VkPhysicalDeviceProperties physicalDeviceProperties;
ffvkGetPhysicalDeviceProperties(physicalDevices[i], &physicalDeviceProperties);
//We don't want softare rasterizers
if(physicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU)
continue;
GPUResult* result = ffListAdd(results);
ffStrbufInit(&result->vendor);
ffStrbufInit(&result->name);
ffStrbufInit(&result->driver);
ffStrbufAppendS(&result->name, physicalDeviceProperties.deviceName);
}
free(physicalDevices);
ffvkDestroyInstance(vkInstance, NULL);
dlclose(vulkan);
ffSuppressIO(false);
return;
}
#endif
#ifdef FF_HAVE_LIBPCI
#include <pci/pci.h>
#include <unistd.h>
//see https://github.com/pciutils/pciutils/blob/5bdf63b6b1bc35b59c4b3f47f7ca83ca1868155b/ls-kernel.c#L220
static void pciGetDriver(struct pci_dev* dev, FFstrbuf* driver, char*(*ffpci_get_param)(struct pci_access*, char*))
{
if(dev->access->method != PCI_ACCESS_SYS_BUS_PCI)
return;
const char* base = ffpci_get_param(dev->access, "sysfs.path");
if(!ffStrSet(base))
return;
FFstrbuf path;
ffStrbufInitA(&path, 64);
ffStrbufAppendF(&path, "%s/devices/%04x:%02x:%02x.%d/driver", base, dev->domain, dev->bus, dev->dev, dev->func);
ffStrbufEnsureFree(driver, 1023);
ssize_t resultLength = readlink(path.chars, driver->chars, driver->allocated - 1); //-1 for null terminator
if(resultLength > 0)
{
driver->length = (uint32_t) resultLength;
driver->chars[resultLength] = '\0';
ffStrbufSubstrAfterLastC(driver, '/');
}
ffStrbufDestroy(&path);
}
static void pciFillGPUs(FFinstance* instance, FFlist* results)
{
FF_LIBRARY_LOAD(pci, instance->config.libPCI, , "libpci.so", "libpci.so.3")
FF_LIBRARY_LOAD_SYMBOL(pci, pci_alloc,)
FF_LIBRARY_LOAD_SYMBOL(pci, pci_init,)
FF_LIBRARY_LOAD_SYMBOL(pci, pci_scan_bus,)
FF_LIBRARY_LOAD_SYMBOL(pci, pci_fill_info,)
FF_LIBRARY_LOAD_SYMBOL(pci, pci_lookup_name,)
FF_LIBRARY_LOAD_SYMBOL(pci, pci_get_param,)
FF_LIBRARY_LOAD_SYMBOL(pci, pci_cleanup,)
struct pci_access *pacc = ffpci_alloc();
ffpci_init(pacc);
ffpci_scan_bus(pacc);
struct pci_dev* dev;
for (dev=pacc->devices; dev; dev=dev->next)
{
ffpci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_CLASS);
char class[1024];
ffpci_lookup_name(pacc, class, sizeof(class), PCI_LOOKUP_CLASS, dev->device_class);
if(
strcasecmp("VGA compatible controller", class) == 0 ||
strcasecmp("3D controller", class) == 0 ||
strcasecmp("Display controller", class) == 0
) {
GPUResult* result = ffListAdd(results);
ffStrbufInitA(&result->vendor, 256);
ffpci_lookup_name(pacc, result->vendor.chars, (int) result->vendor.allocated -1, PCI_LOOKUP_VENDOR, dev->vendor_id, dev->device_id);
ffStrbufRecalculateLength(&result->vendor);
ffStrbufInitA(&result->name, 256);
ffpci_lookup_name(pacc, result->name.chars, (int) result->name.allocated - 1, PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id);
ffStrbufRecalculateLength(&result->name);
ffStrbufInit(&result->driver);
if(instance->config.gpuFormat.length > 0) //We only need it for the format string, so don't detect it if it isn't needed
pciGetDriver(dev, &result->driver, ffpci_get_param);
};
}
ffpci_cleanup(pacc);
dlclose(pci);
}
#endif
static void printGPUResult(FFinstance* instance, uint8_t index, FFcache* cache, GPUResult* result)
{
const char* vendorPretty;
if(ffStrbufIgnCaseCompS(&result->vendor, "Advanced Micro Devices, Inc. [AMD/ATI]") == 0)
vendorPretty = "AMD ATI";
else if(ffStrbufIgnCaseCompS(&result->vendor, "NVIDIA Corporation") == 0)
vendorPretty = "Nvidia";
else if(ffStrbufIgnCaseCompS(&result->vendor, "Intel Corporation") == 0)
vendorPretty = "Intel";
else
vendorPretty = result->vendor.chars;
FFstrbuf namePretty;
ffStrbufInitCopy(&namePretty, &result->name);
ffStrbufSubstrBeforeLastC(&namePretty, ']');
ffStrbufSubstrAfterFirstC(&namePretty, '[');
FFstrbuf gpu;
ffStrbufInitA(&gpu, result->vendor.length + 1 + namePretty.length);
if(ffStrSet(vendorPretty))
{
ffStrbufAppendS(&gpu, vendorPretty);
ffStrbufAppendC(&gpu, ' ');
}
ffStrbufAppend(&gpu, &namePretty);
ffPrintAndAppendToCache(instance, FF_GPU_MODULE_NAME, index, &instance->config.gpuKey, cache, &gpu, &instance->config.gpuFormat, FF_GPU_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &result->vendor},
{FF_FORMAT_ARG_TYPE_STRING, vendorPretty},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->name},
{FF_FORMAT_ARG_TYPE_STRBUF, &namePretty},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->driver},
});
ffStrbufDestroy(&result->vendor);
ffStrbufDestroy(&result->name);
ffStrbufDestroy(&result->driver);
ffStrbufDestroy(&gpu);
ffStrbufDestroy(&namePretty);
}
void ffPrintGPU(FFinstance* instance)
{
if(ffPrintFromCache(instance, FF_GPU_MODULE_NAME, &instance->config.gpuKey, &instance->config.gpuFormat, FF_GPU_NUM_FORMAT_ARGS))
return;
FFlist gpus;
ffListInitA(&gpus, sizeof(GPUResult), 4);
FFcache cache;
ffCacheOpenWrite(instance, FF_GPU_MODULE_NAME, &cache);
#ifdef FF_HAVE_LIBPCI
pciFillGPUs(instance, &gpus);
#endif
#ifdef FF_HAVE_VULKAN
if(gpus.length == 0)
vulkanFillGPUs(instance, &gpus);
#endif
for(uint8_t i = 0; i < (uint8_t) gpus.length; i++)
printGPUResult(instance, gpus.length == 1 ? 0 : (uint8_t) (i + 1), &cache, ffListGet(&gpus, i));
if(gpus.length == 0)
ffPrintError(instance, FF_GPU_MODULE_NAME, 0, &instance->config.gpuKey, &instance->config.gpuFormat, FF_GPU_NUM_FORMAT_ARGS, "No GPUs found.");
ffCacheClose(&cache);
ffListDestroy(&gpus);
}
+131
View File
@@ -0,0 +1,131 @@
#include "fastfetch.h"
#include <ctype.h>
#define FF_HOST_MODULE_NAME "Host"
#define FF_HOST_NUM_FORMAT_ARGS 3
static bool hostValueSet(FFstrbuf* value)
{
ffStrbufTrimRight(value, '\n');
ffStrbufTrim(value, ' ');
return
value->length > 0 &&
ffStrbufStartsWithIgnCaseS(value, "To be filled") != true &&
ffStrbufStartsWithIgnCaseS(value, "To be set") != true &&
ffStrbufStartsWithIgnCaseS(value, "OEM") != true &&
ffStrbufStartsWithIgnCaseS(value, "O.E.M.") != true &&
ffStrbufIgnCaseCompS(value, "None") != 0 &&
ffStrbufIgnCaseCompS(value, "System Product") != 0 &&
ffStrbufIgnCaseCompS(value, "System Product Name") != 0 &&
ffStrbufIgnCaseCompS(value, "System Product Version") != 0 &&
ffStrbufIgnCaseCompS(value, "System Name") != 0 &&
ffStrbufIgnCaseCompS(value, "System Version") != 0 &&
ffStrbufIgnCaseCompS(value, "Default string") != 0 &&
ffStrbufIgnCaseCompS(value, "Undefined") != 0 &&
ffStrbufIgnCaseCompS(value, "Not Specified") != 0 &&
ffStrbufIgnCaseCompS(value, "Not Applicable") != 0 &&
ffStrbufIgnCaseCompS(value, "INVALID") != 0 &&
ffStrbufIgnCaseCompS(value, "Type1ProductConfigId") != 0 &&
ffStrbufIgnCaseCompS(value, "All Series") != 0
;
}
#ifndef __ANDROID__
static void getHostValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer)
{
ffGetFileContent(devicesPath, buffer);
if(buffer->length == 0)
ffGetFileContent(classPath, buffer);
}
#endif
void ffPrintHost(FFinstance* instance)
{
if(ffPrintFromCache(instance, FF_HOST_MODULE_NAME, &instance->config.hostKey, &instance->config.hostFormat, FF_HOST_NUM_FORMAT_ARGS))
return;
FFstrbuf family;
ffStrbufInit(&family);
#ifndef __ANDROID__
getHostValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &family);
#else
ffSettingsGetAndroidProperty("ro.product.device", &family);
#endif
bool familySet = hostValueSet(&family);
FFstrbuf name;
ffStrbufInit(&name);
#ifndef __ANDROID__
getHostValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", &name);
if(name.length == 0)
ffGetFileContent("/sys/firmware/devicetree/base/model", &name);
if(name.length == 0)
ffGetFileContent("/tmp/sysinfo/model", &name);
if(ffStrbufStartsWithS(&name, "Standard PC"))
{
FFstrbuf copy;
ffStrbufInitCopy(&copy, &name);
ffStrbufSetS(&name, "KVM/QEMU ");
ffStrbufAppend(&name, &copy);
ffStrbufDestroy(&copy);
}
#else
ffSettingsGetAndroidProperty("ro.product.brand", &name);
if(name.length > 0){
name.chars[0] = (char) toupper(name.chars[0]);
ffStrbufAppendC(&name, ' ');
}
if(!ffSettingsGetAndroidProperty("ro.product.model", &name))
ffSettingsGetAndroidProperty("ro.product.name", &name);
ffStrbufTrimRight(&name, ' ');
#endif
bool nameSet = hostValueSet(&name);
FFstrbuf version;
ffStrbufInit(&version);
#ifndef __ANDROID__
getHostValue("/sys/devices/virtual/dmi/id/product_version", "/sys/class/dmi/id/product_version", &version);
#endif
bool versionSet = hostValueSet(&version);
if(!familySet && !nameSet)
{
ffStrbufDestroy(&family);
ffStrbufDestroy(&name);
ffStrbufDestroy(&version);
ffPrintError(instance, FF_HOST_MODULE_NAME, 0, &instance->config.hostKey, &instance->config.hostFormat, FF_HOST_NUM_FORMAT_ARGS, "neither family nor name is set by O.E.M.");
return;
}
FFstrbuf host;
ffStrbufInit(&host);
if(nameSet)
ffStrbufAppend(&host, &name);
else
ffStrbufAppend(&host, &family);
if(versionSet)
{
ffStrbufAppendC(&host, ' ');
ffStrbufAppend(&host, &version);
}
ffPrintAndSaveToCache(instance, FF_HOST_MODULE_NAME, &instance->config.hostKey, &host, &instance->config.hostFormat, FF_HOST_NUM_FORMAT_ARGS, (FFformatarg[]) {
{FF_FORMAT_ARG_TYPE_STRBUF, &family},
{FF_FORMAT_ARG_TYPE_STRBUF, &name},
{FF_FORMAT_ARG_TYPE_STRBUF, &version}
});
ffStrbufDestroy(&family);
ffStrbufDestroy(&name);
ffStrbufDestroy(&version);
}
+62
View File
@@ -0,0 +1,62 @@
#include "fastfetch.h"
#define FF_ICONS_MODULE_NAME "Icons"
#define FF_ICONS_NUM_FORMAT_ARGS 5
void ffPrintIcons(FFinstance* instance)
{
#ifdef __ANDROID__
ffPrintError(instance, FF_ICONS_MODULE_NAME, 0, &instance->config.iconsKey, &instance->config.iconsFormat, FF_ICONS_NUM_FORMAT_ARGS, "Icons detection is not supported on Android");
return;
#endif
const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
if(ffStrbufIgnCaseCompS(&wmde->wmProtocolName, "TTY") == 0)
{
ffPrintError(instance, FF_ICONS_MODULE_NAME, 0, &instance->config.fontKey, &instance->config.fontFormat, FF_ICONS_NUM_FORMAT_ARGS, "Icons aren't supported in TTY");
return;
}
const FFstrbuf* plasma = &ffDetectPlasma(instance)->icons;
const FFstrbuf* gtk2 = &ffDetectGTK2(instance)->icons;
const FFstrbuf* gtk3 = &ffDetectGTK3(instance)->icons;
const FFstrbuf* gtk4 = &ffDetectGTK4(instance)->icons;
if(plasma->length == 0 && gtk2->length == 0 && gtk3->length == 0 && gtk4->length == 0)
{
ffPrintError(instance, FF_ICONS_MODULE_NAME, 0, &instance->config.iconsKey, &instance->config.iconsFormat, FF_ICONS_NUM_FORMAT_ARGS, "No icons could be found");
return;
}
FF_STRBUF_CREATE(gtkPretty);
ffGetGtkPretty(&gtkPretty, gtk2, gtk3, gtk4);
if(instance->config.iconsFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_ICONS_MODULE_NAME, 0, &instance->config.iconsKey);
if(plasma->length > 0)
{
ffStrbufWriteTo(plasma, stdout);
fputs(" [Plasma]", stdout);
if(gtkPretty.length > 0)
fputs(", ", stdout);
}
ffStrbufPutTo(&gtkPretty, stdout);
}
else
{
ffPrintFormatString(instance, FF_ICONS_MODULE_NAME, 0, &instance->config.iconsKey, &instance->config.iconsFormat, NULL, FF_ICONS_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, plasma},
{FF_FORMAT_ARG_TYPE_STRBUF, gtk2},
{FF_FORMAT_ARG_TYPE_STRBUF, gtk3},
{FF_FORMAT_ARG_TYPE_STRBUF, gtk4},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtkPretty}
});
}
ffStrbufDestroy(&gtkPretty);
}
+21
View File
@@ -0,0 +1,21 @@
#include "fastfetch.h"
#define FF_KERNEL_MODULE_NAME "Kernel"
#define FF_KERNEL_NUM_FORMAT_ARGS 3
void ffPrintKernel(FFinstance* instance)
{
if(instance->config.kernelFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_KERNEL_MODULE_NAME, 0, &instance->config.kernelKey);
puts(instance->state.utsname.release);
}
else
{
ffPrintFormatString(instance, FF_KERNEL_MODULE_NAME, 0, &instance->config.kernelKey, &instance->config.kernelFormat, NULL, FF_KERNEL_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRING, instance->state.utsname.sysname},
{FF_FORMAT_ARG_TYPE_STRING, instance->state.utsname.release},
{FF_FORMAT_ARG_TYPE_STRING, instance->state.utsname.version}
});
}
}
+47
View File
@@ -0,0 +1,47 @@
#include "fastfetch.h"
#define FF_LOCALE_MODULE_NAME "Locale"
#define FF_LOCALE_NUM_FORMAT_ARGS 1
static void getLocaleFromEnv(FFstrbuf* locale)
{
ffStrbufAppendS(locale, getenv("LANG"));
if(locale->length > 0)
return;
ffStrbufAppendS(locale, getenv("LC_ALL"));
if(locale->length > 0)
return;
ffStrbufAppendS(locale, getenv("LC_CTYPE"));
if(locale->length > 0)
return;
ffStrbufAppendS(locale, getenv("LC_MESSAGES"));
}
void ffPrintLocale(FFinstance* instance)
{
if(ffPrintFromCache(instance, FF_LOCALE_MODULE_NAME, &instance->config.localeKey, &instance->config.localeFormat, FF_LOCALE_NUM_FORMAT_ARGS))
return;
FFstrbuf locale;
ffStrbufInit(&locale);
ffParsePropFile("/etc/locale.conf", "LANG =", &locale);
if (locale.length == 0)
getLocaleFromEnv(&locale);
if(locale.length == 0)
{
ffPrintError(instance, FF_LOCALE_MODULE_NAME, 0, &instance->config.localeKey, &instance->config.localeFormat, FF_LOCALE_NUM_FORMAT_ARGS, "No locale found");
return;
}
ffPrintAndSaveToCache(instance, FF_LOCALE_MODULE_NAME, &instance->config.localeKey, &locale, &instance->config.localeFormat, FF_LOCALE_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &locale}
});
ffStrbufDestroy(&locale);
}
+75
View File
@@ -0,0 +1,75 @@
#include "fastfetch.h"
#define FF_LOCALIP_MODULE_NAME "Local IP"
#define FF_LOCALIP_NUM_FORMAT_ARGS 1
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
static void printValue(FFinstance* instance, const char* ifaName, const char* addressBuffer)
{
FF_STRBUF_CREATE(key);
if (instance->config.localIpKey.length == 0) {
ffStrbufAppendF(&key, FF_LOCALIP_MODULE_NAME " (%s)", ifaName);
} else {
ffParseFormatString(&key, &instance->config.localIpKey, NULL, 1, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRING, ifaName}
});
}
if (instance->config.localIpFormat.length == 0) {
ffPrintLogoAndKey(instance, FF_LOCALIP_MODULE_NAME, 0, &key);
puts(addressBuffer);
} else {
ffPrintFormatString(instance, FF_LOCALIP_MODULE_NAME, 0, &key, &instance->config.localIpFormat, NULL, FF_LOCALIP_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRING, addressBuffer}
});
}
ffStrbufDestroy(&key);
}
void ffPrintLocalIp(FFinstance* instance)
{
struct ifaddrs* ifAddrStruct = NULL;
int ret = getifaddrs(&ifAddrStruct);
if (ret < 0) {
ffPrintError(instance, FF_LOCALIP_MODULE_NAME, 0, &instance->config.localIpKey, &instance->config.localIpFormat, FF_LOCALIP_NUM_FORMAT_ARGS, "getifaddrs(&ifAddrStruct) < 0 (%i)", ret);
return;
}
for (struct ifaddrs* ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr)
continue;
// loop back
if (strcmp(ifa->ifa_name, "lo") == 0 && !instance->config.localIpShowLoop)
continue;
if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
// is a valid IP4 Address
if (!instance->config.localIpShowIpV4)
continue;
void* tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
printValue(instance, ifa->ifa_name, addressBuffer);
} else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
// is a valid IP6 Address
if (!instance->config.localIpShowIpV6)
continue;
void* tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
char addressBuffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
printValue(instance, ifa->ifa_name, addressBuffer);
}
}
if (ifAddrStruct) freeifaddrs(ifAddrStruct);
}
+62
View File
@@ -0,0 +1,62 @@
#include "fastfetch.h"
#define FF_MEMORY_MODULE_NAME "Memory"
#define FF_MEMORY_NUM_FORMAT_ARGS 3
// Impl inspired by: https://github.com/sam-barr/paleofetch/blob/b7c58a52c0de39b53c9b5f417889a5886d324bfa/paleofetch.c#L544
void ffPrintMemory(FFinstance* instance)
{
FILE* meminfo = fopen("/proc/meminfo", "r");
if(meminfo == NULL) {
ffPrintError(instance, FF_MEMORY_MODULE_NAME, 0, &instance->config.memoryKey, &instance->config.memoryFormat, FF_MEMORY_NUM_FORMAT_ARGS, "fopen(\"/proc/meminfo\", \"r\") == NULL");
return;
}
char* line = NULL;
size_t len = 0;
uint32_t total = 0,
shared = 0,
memfree = 0,
buffers = 0,
cached = 0,
reclaimable = 0;
while (getline(&line, &len, meminfo) != -1) {
sscanf(line, "MemTotal: %u", &total);
sscanf(line, "Shmem: %u", &shared);
sscanf(line, "MemFree: %u", &memfree);
sscanf(line, "Buffers: %u", &buffers);
sscanf(line, "Cached: %u", &cached);
sscanf(line, "SReclaimable: %u", &reclaimable);
}
if(line != NULL)
free(line);
fclose(meminfo);
uint32_t used_mem = (total + shared - memfree - buffers - cached - reclaimable) / 1024;
uint32_t total_mem = total / 1024;
uint8_t percentage = (uint8_t) ((used_mem / (double) total_mem) * 100);
if(used_mem == 0 && total_mem == 0 && percentage == 0)
{
ffPrintError(instance, FF_MEMORY_MODULE_NAME, 0, &instance->config.memoryKey, &instance->config.memoryFormat, FF_MEMORY_NUM_FORMAT_ARGS, "/proc/meminfo could't be parsed");
return;
}
if(instance->config.memoryFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_MEMORY_MODULE_NAME, 0, &instance->config.memoryKey);
printf("%uMiB / %uMiB (%u%%)\n", used_mem, total_mem, percentage);
}
else
{
ffPrintFormatString(instance, FF_MEMORY_MODULE_NAME, 0, &instance->config.memoryKey, &instance->config.memoryFormat, NULL, FF_MEMORY_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_UINT, &used_mem},
{FF_FORMAT_ARG_TYPE_UINT, &total_mem},
{FF_FORMAT_ARG_TYPE_UINT8, &percentage}
});
}
}
+88
View File
@@ -0,0 +1,88 @@
#include "fastfetch.h"
#define FF_OS_MODULE_NAME "OS"
#define FF_OS_NUM_FORMAT_ARGS 12
void ffPrintOS(FFinstance* instance)
{
if(ffPrintFromCache(instance, FF_OS_MODULE_NAME, &instance->config.osKey, &instance->config.osFormat, FF_OS_NUM_FORMAT_ARGS))
return;
const FFOSResult* result = ffDetectOS(instance);
if(result->name.length == 0 && result->prettyName.length == 0)
{
ffPrintError(instance, FF_OS_MODULE_NAME, 0, &instance->config.osKey, &instance->config.osFormat, FF_OS_NUM_FORMAT_ARGS, "Could not detect OS");
return;
}
FFstrbuf os;
ffStrbufInit(&os);
//Create the basic output
if(result->name.length > 0)
ffStrbufAppend(&os, &result->name);
else if(result->prettyName.length > 0)
ffStrbufAppend(&os, &result->name);
else if(result->id.length > 0)
ffStrbufAppend(&os, &result->id);
else if(result->systemName.length > 0)
ffStrbufAppend(&os, &result->systemName);
else
ffStrbufAppendS(&os, "Linux");
//Append version if it is missing
if(result->versionID.length > 0 && ffStrbufFirstIndex(&os, &result->versionID) == os.length)
{
ffStrbufAppendC(&os, ' ');
ffStrbufAppend(&os, &result->versionID);
}
else if(result->versionID.length == 0 && result->version.length > 0 && ffStrbufFirstIndex(&os, &result->version) == os.length)
{
ffStrbufAppendC(&os, ' ');
ffStrbufAppend(&os, &result->version);
}
//Append variant if it is missing
if(result->variant.length > 0 && ffStrbufFirstIndex(&os, &result->variant) == os.length)
{
ffStrbufAppendS(&os, " (");
ffStrbufAppend(&os, &result->variant);
ffStrbufAppendC(&os, ')');
}
else if(result->variant.length == 0 && result->variantID.length > 0 && ffStrbufFirstIndex(&os, &result->variantID) == os.length)
{
ffStrbufAppendS(&os, " (");
ffStrbufAppend(&os, &result->variantID);
ffStrbufAppendC(&os, ')');
}
//Append architecture if it is missing
if(ffStrbufFirstIndex(&os, &result->architecture) == os.length)
{
ffStrbufAppendS(&os, " [");
ffStrbufAppend(&os, &result->architecture);
ffStrbufAppendC(&os, ']');
}
ffPrintAndSaveToCache(instance, FF_OS_MODULE_NAME, &instance->config.osKey, &os, &instance->config.osFormat, FF_OS_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &result->systemName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->name},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->prettyName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->id},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->idLike},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->variant},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->variantID},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->version},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->versionID},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->codename},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->buildID},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->architecture}
});
ffStrbufDestroy(&os);
}
+227
View File
@@ -0,0 +1,227 @@
#include "fastfetch.h"
#include <string.h>
#include <dirent.h>
#define FF_PACKAGES_MODULE_NAME "Packages"
#define FF_PACKAGES_NUM_FORMAT_ARGS 9
#ifdef FF_HAVE_RPM
#include <rpm/rpmlib.h>
#include <rpm/rpmts.h>
#include <rpm/rpmdb.h>
#include <rpm/rpmlog.h>
static uint32_t getRpmPackageCount(FFinstance* instance)
{
FF_LIBRARY_LOAD(rpm, instance->config.librpm, 0, "librpm.so", "librpm.so.4")
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmReadConfigFiles, 0)
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsCreate, 0)
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsInitIterator, 0)
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbGetIteratorCount, 0)
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbFreeIterator, 0)
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsFree, 0)
FF_LIBRARY_LOAD_SYMBOL(rpm, rpmlogSetMask, 0)
// Don't print any error messages
ffrpmlogSetMask(RPMLOG_MASK(RPMLOG_EMERG));
uint32_t count = 0;
rpmts ts = NULL;
rpmdbMatchIterator mi = NULL;
if (ffrpmReadConfigFiles(NULL, NULL)) goto exit;
if (!(ts = ffrpmtsCreate())) goto exit;
if (!(mi = ffrpmtsInitIterator(ts, RPMDBI_LABEL, NULL, 0))) goto exit;
count = (uint32_t) ffrpmdbGetIteratorCount(mi);
exit:
if (mi) ffrpmdbFreeIterator(mi);
if (ts) ffrpmtsFree(ts);
dlclose(rpm);
return count;
}
#endif
static uint32_t getNumElements(const char* dirname, unsigned char type)
{
DIR* dirp = opendir(dirname);
if(dirp == NULL)
return 0;
uint32_t num_elements = 0;
struct dirent *entry;
while((entry = readdir(dirp)) != NULL) {
if(entry->d_type == type)
++num_elements;
}
if(type == DT_DIR)
num_elements -= 2; // accounting for . and ..
closedir(dirp);
return num_elements;
}
static uint32_t getNumStrings(const char* filename, const char* needle)
{
FILE* file = fopen(filename, "r");
if(file == NULL)
return 0;
uint32_t count = 0;
char* line = NULL;
size_t len = 0;
while(getline(&line, &len, file) != EOF)
{
if(strstr(line, needle) != NULL)
++count;
}
if(line != NULL)
free(line);
fclose(file);
return count;
}
static uint32_t countFilesRecursive(FFstrbuf* baseDirPath, const char* filename)
{
uint32_t baseDirPathLength = baseDirPath->length;
ffStrbufAppendC(baseDirPath, '/');
ffStrbufAppendS(baseDirPath, filename);
bool exists = ffFileExists(baseDirPath->chars, S_IFREG);
ffStrbufSubstrBefore(baseDirPath, baseDirPathLength);
if(exists)
return 1;
DIR* dirp = opendir(baseDirPath->chars);
if(dirp == NULL)
return 0;
ffStrbufAppendC(baseDirPath, '/');
baseDirPathLength = baseDirPath->length;
uint32_t sum = 0;
struct dirent *entry;
while((entry = readdir(dirp)) != NULL) {
// According to the PMS, neither category nor package name can begin with '.', so no need to check for . or .. specifically
if(entry->d_type != DT_DIR || entry->d_name[0] == '.')
continue;
ffStrbufAppendS(baseDirPath, entry->d_name);
sum += countFilesRecursive(baseDirPath, filename);
ffStrbufSubstrBefore(baseDirPath, baseDirPathLength);
}
closedir(dirp);
return sum;
}
static uint32_t countFilesIn(const char* dirname, const char* filename)
{
FFstrbuf baseDirPath;
ffStrbufInitA(&baseDirPath, 128);
ffStrbufAppendS(&baseDirPath, dirname);
uint32_t result = countFilesRecursive(&baseDirPath, filename);
ffStrbufDestroy(&baseDirPath);
return result;
}
void ffPrintPackages(FFinstance* instance)
{
uint32_t pacman = getNumElements("/var/lib/pacman/local", DT_DIR);
uint32_t dpkg = getNumStrings("/var/lib/dpkg/status", "Status: ");
#if __ANDROID__
dpkg += getNumStrings("/data/data/com.termux/files/usr/var/lib/dpkg/status", "Status: ");
#endif
#ifdef FF_HAVE_RPM
uint32_t rpm = getRpmPackageCount(instance);
#else
uint32_t rpm = 0;
#endif
uint32_t emerge = countFilesIn("/var/db/pkg", "SIZE");
uint32_t xbps = getNumElements("/var/db/xbps", DT_REG);
uint32_t flatpak = getNumElements("/var/lib/flatpak/app", DT_DIR);
uint32_t snap = getNumElements("/snap", DT_DIR);
//Accounting for the /snap/bin folder
if(snap > 0)
--snap;
uint32_t all = pacman + dpkg + rpm + emerge + xbps + flatpak + snap;
if(all == 0)
{
ffPrintError(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.packagesKey, &instance->config.packagesFormat, FF_PACKAGES_NUM_FORMAT_ARGS, "No packages from known package managers found");
return;
}
FFstrbuf manjaroBranch;
ffStrbufInit(&manjaroBranch);
if(ffParsePropFile("/etc/pacman-mirrors.conf", "Branch =", &manjaroBranch) && manjaroBranch.length == 0)
ffStrbufSetS(&manjaroBranch, "stable");
if(instance->config.packagesFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.batteryKey);
#define FF_PRINT_PACKAGE(name) \
if(name > 0) \
{ \
printf("%u ("#name")", name); \
if((all = all - name) > 0) \
printf(", "); \
};
if(pacman > 0)
{
printf("%u (pacman)", pacman);
if(manjaroBranch.length > 0)
printf("[%s]", manjaroBranch.chars);
if((all = all - pacman) > 0)
printf(", ");
};
FF_PRINT_PACKAGE(dpkg)
FF_PRINT_PACKAGE(rpm)
FF_PRINT_PACKAGE(emerge)
FF_PRINT_PACKAGE(xbps)
FF_PRINT_PACKAGE(flatpak)
FF_PRINT_PACKAGE(snap)
//Fix linter warning of unused value of all
(void) all;
#undef FF_PRINT_PACKAGE
putchar('\n');
}
else
{
ffPrintFormatString(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.packagesKey, &instance->config.packagesFormat, NULL, FF_PACKAGES_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_UINT, &all},
{FF_FORMAT_ARG_TYPE_UINT, &pacman},
{FF_FORMAT_ARG_TYPE_STRBUF, &manjaroBranch},
{FF_FORMAT_ARG_TYPE_UINT, &dpkg},
{FF_FORMAT_ARG_TYPE_UINT, &rpm},
{FF_FORMAT_ARG_TYPE_UINT, &emerge},
{FF_FORMAT_ARG_TYPE_UINT, &xbps},
{FF_FORMAT_ARG_TYPE_UINT, &flatpak},
{FF_FORMAT_ARG_TYPE_UINT, &snap}
});
}
ffStrbufDestroy(&manjaroBranch);
}
+29
View File
@@ -0,0 +1,29 @@
#include "fastfetch.h"
#define FF_PLAYER_MODULE_NAME "Media Player"
#define FF_PLAYER_NUM_FORMAT_ARGS 3
void ffPrintPlayer(FFinstance* instance)
{
const FFMediaResult* media = ffDetectMedia(instance);
if(media->player.length == 0)
{
ffPrintError(instance, FF_PLAYER_MODULE_NAME, 0, &instance->config.playerKey, &instance->config.playerFormat, FF_PLAYER_NUM_FORMAT_ARGS, "No media player found");
return;
}
if(instance->config.playerFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_PLAYER_MODULE_NAME, 0, &instance->config.playerKey);
ffStrbufPutTo(&media->playerPretty, stdout);
}
else
{
ffPrintFormatString(instance, FF_PLAYER_MODULE_NAME, 0, &instance->config.playerKey, &instance->config.playerFormat, NULL, FF_PLAYER_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &media->playerPretty},
{FF_FORMAT_ARG_TYPE_STRBUF, &media->player},
{FF_FORMAT_ARG_TYPE_STRBUF, &media->busNameShort}
});
}
}
+20
View File
@@ -0,0 +1,20 @@
#include "fastfetch.h"
#define FF_PROCESSES_MODULE_NAME "Processes"
#define FF_PROCESSES_NUM_FORMAT_ARGS 1
void ffPrintProcesses(FFinstance* instance)
{
if(instance->config.processesFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processesKey);
printf("%hu\n", instance->state.sysinfo.procs);
}
else
{
ffPrintFormatString(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processesKey, &instance->config.processesFormat, NULL, FF_PROCESSES_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_UINT16, &instance->state.sysinfo.procs}
});
}
}
+30
View File
@@ -0,0 +1,30 @@
#include "fastfetch.h"
#define FF_PUBLICIP_MODULE_NAME "Public IP"
#define FF_PUBLICIP_NUM_FORMAT_ARGS 1
void ffPrintPublicIp(FFinstance* instance)
{
FFstrbuf result;
ffStrbufInitA(&result, 4096);
ffNetworkingGetHttp("ipinfo.io", "/ip", instance->config.publicIpTimeout, &result);
ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n");
if(result.length == 0)
{
ffPrintError(instance, FF_PUBLICIP_MODULE_NAME, 0, &instance->config.publicIpKey, &instance->config.publicIpFormat, FF_PUBLICIP_NUM_FORMAT_ARGS, "Failed to connect to an IP detection server");
return;
}
if(instance->config.publicIpFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_PUBLICIP_MODULE_NAME, 0, &instance->config.publicIpKey);
ffStrbufPutTo(&result, stdout);
}
else
{
ffPrintFormatString(instance, FF_PUBLICIP_MODULE_NAME, 0, &instance->config.publicIpKey, &instance->config.publicIpFormat, NULL, FF_PUBLICIP_NUM_FORMAT_ARGS, (FFformatarg[]) {
{FF_FORMAT_ARG_TYPE_STRBUF, &result}
});
}
}
+42
View File
@@ -0,0 +1,42 @@
#include "fastfetch.h"
#define FF_RESOLUTION_MODULE_NAME "Resolution"
#define FF_RESOLUTION_NUM_FORMAT_ARGS 3
void ffPrintResolution(FFinstance* instance)
{
#ifdef __ANDROID__
ffPrintError(instance, FF_RESOLUTION_MODULE_NAME, 0, &instance->config.resolutionKey, &instance->config.resolutionFormat, FF_RESOLUTION_NUM_FORMAT_ARGS, "Resolution detection is not supported on Android");
return;
#endif
const FFDisplayServerResult* dsResult = ffConnectDisplayServer(instance);
for(uint32_t i = 0; i < dsResult->resolutions.length; i++)
{
FFResolutionResult* result = ffListGet(&dsResult->resolutions, i);
uint8_t moduleIndex = dsResult->resolutions.length == 1 ? 0 : (uint8_t) (i + 1);
if(instance->config.resolutionFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_RESOLUTION_MODULE_NAME, moduleIndex, &instance->config.resolutionKey);
printf("%ix%i", result->width, result->height);
if(result->refreshRate > 0)
printf(" @ %iHz", result->refreshRate);
putchar('\n');
}
else
{
ffPrintFormatString(instance, FF_RESOLUTION_MODULE_NAME, moduleIndex, &instance->config.resolutionKey, &instance->config.resolutionFormat, NULL, FF_RESOLUTION_NUM_FORMAT_ARGS, (FFformatarg[]) {
{FF_FORMAT_ARG_TYPE_INT, &result->width},
{FF_FORMAT_ARG_TYPE_INT, &result->height},
{FF_FORMAT_ARG_TYPE_INT, &result->refreshRate}
});
}
}
if(dsResult->resolutions.length == 0)
ffPrintError(instance, FF_RESOLUTION_MODULE_NAME, 0, &instance->config.resolutionKey, &instance->config.resolutionFormat, FF_RESOLUTION_NUM_FORMAT_ARGS, "Couldn't detect resolution");
}
+26
View File
@@ -0,0 +1,26 @@
#include "fastfetch.h"
void ffPrintSeparator(FFinstance* instance)
{
const FFTitleResult* result = ffDetectTitle(instance);
uint32_t titleLength = result->userName.length + 1 + result->hostname.length;
ffPrintLogoLine(instance);
if(instance->config.separatorString.length == 0)
{
for(uint32_t i = 0; i < titleLength; i++)
putchar('-');
}
else
{
//Write the whole separator as often as it fits fully into titleLength
for(uint32_t i = 0; i < titleLength / instance->config.separatorString.length; i++)
ffStrbufWriteTo(&instance->config.separatorString, stdout);
//Write as much of the separator as needed to fill titleLength
for(uint32_t i = 0; i < titleLength % instance->config.separatorString.length; i++)
putchar(instance->config.separatorString.chars[i]);
}
putchar('\n');
}
+44
View File
@@ -0,0 +1,44 @@
#include "fastfetch.h"
#include <string.h>
#include <ctype.h>
#define FF_SHELL_MODULE_NAME "Shell"
#define FF_SHELL_NUM_FORMAT_ARGS 7
void ffPrintShell(FFinstance* instance)
{
const FFTerminalShellResult* result = ffDetectTerminalShell(instance);
if(result->shellProcessName.length == 0)
{
ffPrintError(instance, FF_SHELL_MODULE_NAME, 0, &instance->config.shellKey, &instance->config.shellFormat, FF_SHELL_NUM_FORMAT_ARGS, "Couldn't detect shell");
return;
}
if(instance->config.shellFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_SHELL_MODULE_NAME, 0, &instance->config.shellKey);
fputs(result->shellExeName, stdout);
if(result->shellVersion.length > 0)
{
putchar(' ');
ffStrbufWriteTo(&result->shellVersion, stdout);
}
putchar('\n');
}
else
{
ffPrintFormatString(instance, FF_SHELL_MODULE_NAME, 0, &instance->config.shellKey, &instance->config.shellFormat, NULL, FF_SHELL_NUM_FORMAT_ARGS, (FFformatarg[]) {
{FF_FORMAT_ARG_TYPE_STRBUF, &result->shellProcessName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->shellExe},
{FF_FORMAT_ARG_TYPE_STRING, result->shellExeName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->shellVersion},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellExe},
{FF_FORMAT_ARG_TYPE_STRING, result->userShellExeName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellVersion}
});
}
}
+43
View File
@@ -0,0 +1,43 @@
#include "fastfetch.h"
#define FF_SONG_MODULE_NAME "Song"
#define FF_SONG_NUM_FORMAT_ARGS 4
void ffPrintSong(FFinstance* instance)
{
const FFMediaResult* media = ffDetectMedia(instance);
if(media->song.length == 0)
{
ffPrintError(instance, FF_SONG_MODULE_NAME, 0, &instance->config.songKey, &instance->config.songFormat, FF_SONG_NUM_FORMAT_ARGS, "No song detected");
return;
}
if(instance->config.songFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_SONG_MODULE_NAME, 0, &instance->config.songKey);
if(media->artist.length > 0)
{
ffStrbufWriteTo(&media->artist, stdout);
fputs(" - ", stdout);
}
if(media->album.length > 0)
{
ffStrbufWriteTo(&media->album, stdout);
fputs(" - ", stdout);
}
ffStrbufPutTo(&media->song, stdout);
}
else
{
ffPrintFormatString(instance, FF_SONG_MODULE_NAME, 0, &instance->config.songKey, &instance->config.songFormat, NULL, FF_SONG_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &media->song},
{FF_FORMAT_ARG_TYPE_STRBUF, &media->artist},
{FF_FORMAT_ARG_TYPE_STRBUF, &media->album},
{FF_FORMAT_ARG_TYPE_STRBUF, &media->url}
});
}
}
+37
View File
@@ -0,0 +1,37 @@
#include "fastfetch.h"
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#define FF_TERMINAL_MODULE_NAME "Terminal"
#define FF_TERMINAL_NUM_FORMAT_ARGS 3
void ffPrintTerminal(FFinstance* instance)
{
const FFTerminalShellResult* result = ffDetectTerminalShell(instance);
if(result->terminalProcessName.length == 0)
{
ffPrintError(instance, FF_TERMINAL_MODULE_NAME, 0, &instance->config.terminalKey, &instance->config.terminalFormat, FF_TERMINAL_NUM_FORMAT_ARGS, "Couldn't detect terminal");
return;
}
if(instance->config.terminalFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_TERMINAL_MODULE_NAME, 0, &instance->config.terminalKey);
if(strncmp(result->terminalExeName, result->terminalProcessName.chars, result->terminalProcessName.length) == 0) // if exeName starts with processName, print it. Otherwise print processName
puts(result->terminalExeName);
else
ffStrbufPutTo(&result->terminalProcessName, stdout);
}
else
{
ffPrintFormatString(instance, FF_TERMINAL_MODULE_NAME, 0, &instance->config.terminalKey, &instance->config.terminalFormat, NULL, FF_TERMINAL_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalProcessName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalExe},
{FF_FORMAT_ARG_TYPE_STRING, result->terminalExeName}
});
}
}
+232
View File
@@ -0,0 +1,232 @@
#include "fastfetch.h"
#define FF_TERMFONT_MODULE_NAME "Terminal Font"
#define FF_TERMFONT_NUM_FORMAT_ARGS 5
static void printTerminalFont(FFinstance* instance, const char* raw, FFfont* font)
{
if(font->pretty.length == 0)
{
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Terminal font is an empty value");
return;
}
if(instance->config.termFontFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey);
ffStrbufPutTo(&font->pretty, stdout);
}
else
{
ffPrintFormatString(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, NULL, FF_TERMFONT_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRING, raw},
{FF_FORMAT_ARG_TYPE_STRBUF, &font->name},
{FF_FORMAT_ARG_TYPE_STRBUF, &font->size},
{FF_FORMAT_ARG_TYPE_LIST, &font->styles},
{FF_FORMAT_ARG_TYPE_STRBUF, &font->pretty}
});
}
}
static const char* getSystemMonospaceFont(FFinstance* instance)
{
const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Cinnamon") == 0)
{
const char* systemMonospaceFont = ffSettingsGet(instance, "/org/cinnamon/desktop/interface/monospace-font-name", "org.cinnamon.desktop.interface", NULL, "monospace-font-name", FF_VARIANT_TYPE_STRING).strValue;
if(systemMonospaceFont != NULL)
return systemMonospaceFont;
}
else if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Mate") == 0)
{
const char* systemMonospaceFont = ffSettingsGet(instance, "/org/mate/interface/monospace-font-name", "org.mate.interface", NULL, "monospace-font-name", FF_VARIANT_TYPE_STRING).strValue;
if(systemMonospaceFont != NULL)
return systemMonospaceFont;
}
return ffSettingsGet(instance, "/org/gnome/desktop/interface/monospace-font-name", "org.gnome.desktop.interface", NULL, "monospace-font-name", FF_VARIANT_TYPE_STRING).strValue;
}
static void printTerminalFontFromConfigFile(FFinstance* instance, const char* configFile, const char* start)
{
FFstrbuf fontName;
ffStrbufInit(&fontName);
ffParsePropFileConfig(instance, configFile, start, &fontName);
if(fontName.length == 0)
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Couldn't find terminal font in \"$XDG_CONFIG_HOME/%s\"", configFile);
else
{
FFfont font;
ffFontInitPango(&font, fontName.chars);
printTerminalFont(instance, fontName.chars, &font);
ffFontDestroy(&font);
}
ffStrbufDestroy(&fontName);
}
static void printTerminalFontFromGSettings(FFinstance* instance, char* profilePath, char* profileList, char* profile)
{
const char* defaultProfile = ffSettingsGetGSettings(instance, profileList, NULL, "default", FF_VARIANT_TYPE_STRING).strValue;
if(defaultProfile == NULL)
{
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Couldn't get \"default\" profile from gsettings");
return;
}
FFstrbuf path;
ffStrbufInitA(&path, 128);
ffStrbufAppendS(&path, profilePath);
ffStrbufAppendS(&path, defaultProfile);
ffStrbufAppendC(&path, '/');
const char* fontName;
if(!ffSettingsGetGSettings(instance, profile, path.chars, "use-system-font", FF_VARIANT_TYPE_BOOL).boolValue) // custom font
{
fontName = ffSettingsGetGSettings(instance, profile, path.chars, "font", FF_VARIANT_TYPE_STRING).strValue;
if(fontName == NULL)
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Couldn't get terminal font from GSettings (%s::%s::font)", profile, path.chars);
}
else // system font
{
fontName = getSystemMonospaceFont(instance);
if(fontName == NULL)
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Could't get system monospace font name from GSettings / DConf");
}
ffStrbufDestroy(&path);
if(fontName == NULL)
return;
FFfont font;
ffFontInitPango(&font, fontName);
printTerminalFont(instance, fontName, &font);
ffFontDestroy(&font);
}
static void printKonsole(FFinstance* instance)
{
FFstrbuf profile;
ffStrbufInit(&profile);
ffParsePropFileConfig(instance, "konsolerc", "DefaultProfile =", &profile);
if(profile.length == 0)
{
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Couldn't find \"DefaultProfile=%[^\\n]\" in \".config/konsolerc\"");
ffStrbufDestroy(&profile);
return;
}
FFstrbuf profilePath;
ffStrbufInitA(&profilePath, 64);
ffStrbufAppendS(&profilePath, ".local/share/konsole/");
ffStrbufAppend(&profilePath, &profile);
FFstrbuf fontName;
ffStrbufInit(&fontName);
ffParsePropFileHome(instance, profilePath.chars, "Font =", &fontName);
if(fontName.length == 0)
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Couldn't find \"Font=%%[^\\n]\" in \"%s\"", profilePath.chars);
else
{
FFfont font;
ffFontInitQt(&font, fontName.chars);
printTerminalFont(instance, fontName.chars, &font);
ffFontDestroy(&font);
}
ffStrbufDestroy(&fontName);
ffStrbufDestroy(&profilePath);
ffStrbufDestroy(&profile);
}
static void printXCFETerminal(FFinstance* instance)
{
FFstrbuf useSysFont;
ffStrbufInit(&useSysFont);
if(!ffParsePropFileConfig(instance, "xfce4/terminal/terminalrc", "FontUseSystem =", &useSysFont))
{
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Couldn't open \"$XDG_CONFIG_HOME/xfce4/terminal/terminalrc\"");
ffStrbufDestroy(&useSysFont);
return;
}
if(useSysFont.length == 0 || ffStrbufIgnCaseCompS(&useSysFont, "FALSE") == 0)
{
printTerminalFontFromConfigFile(instance, "xfce4/terminal/terminalrc", "FontName =");
ffStrbufDestroy(&useSysFont);
return;
}
ffStrbufDestroy(&useSysFont);
const char* fontName = ffSettingsGetXFConf(instance, "xsettings", "/Gtk/MonospaceFontName", FF_VARIANT_TYPE_STRING).strValue;
if(fontName == NULL)
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Couldn't find \"xsettings::/Gtk/MonospaceFontName\" in XFConf");
else
{
FFfont font;
ffFontInitPango(&font, fontName);
printTerminalFont(instance, fontName, &font);
ffFontDestroy(&font);
}
}
static void printTTY(FFinstance* instance)
{
FFstrbuf fontName;
ffStrbufInit(&fontName);
ffParsePropFile("/etc/vconsole.conf", "Font =", &fontName);
if(fontName.length == 0)
{
ffStrbufAppendS(&fontName, "VGA default kernel font ");
ffProcessAppendStdOut(&fontName, (char* const[]){
"showconsolefont",
"--info",
NULL
});
}
ffStrbufTrimRight(&fontName, ' ');
FFfont font;
ffFontInitCopy(&font, fontName.chars);
printTerminalFont(instance, fontName.chars, &font);
ffFontDestroy(&font);
ffStrbufDestroy(&fontName);
}
void ffPrintTerminalFont(FFinstance* instance)
{
const FFTerminalShellResult* result = ffDetectTerminalShell(instance);
if(result->terminalProcessName.length == 0)
{
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Terminal font needs successfull terminal detection");
return;
}
if(ffStrbufIgnCaseCompS(&result->terminalProcessName, "konsole") == 0)
printKonsole(instance);
else if(ffStrbufIgnCaseCompS(&result->terminalProcessName, "xfce4-terminal") == 0)
printXCFETerminal(instance);
else if(ffStrbufIgnCaseCompS(&result->terminalProcessName, "lxterminal") == 0)
printTerminalFontFromConfigFile(instance, "lxterminal/lxterminal.conf", "fontname =");
else if(ffStrbufIgnCaseCompS(&result->terminalProcessName, "tilix") == 0)
printTerminalFontFromGSettings(instance, "/com/gexperts/Tilix/profiles/", "com.gexperts.Tilix.ProfilesList", "com.gexperts.Tilix.Profile");
else if(ffStrbufIgnCaseCompS(&result->terminalProcessName, "gnome-terminal-") == 0)
printTerminalFontFromGSettings(instance, "/org/gnome/terminal/legacy/profiles:/:", "org.gnome.Terminal.ProfilesList", "org.gnome.Terminal.Legacy.Profile");
else if(ffStrbufStartsWithIgnCaseS(&result->terminalExe, "/dev/tty"))
printTTY(instance);
else
ffPrintError(instance, FF_TERMFONT_MODULE_NAME, 0, &instance->config.termFontKey, &instance->config.termFontFormat, FF_TERMFONT_NUM_FORMAT_ARGS, "Unknown terminal: %s", result->terminalProcessName.chars);
}
+96
View File
@@ -0,0 +1,96 @@
#include "fastfetch.h"
#define FF_THEME_MODULE_NAME "Theme"
#define FF_THEME_NUM_FORMAT_ARGS 7
void ffPrintTheme(FFinstance* instance)
{
#ifdef __ANDROID__
ffPrintError(instance, FF_THEME_MODULE_NAME, 0, &instance->config.themeKey, &instance->config.themeFormat, FF_THEME_NUM_FORMAT_ARGS, "Theme detection is not supported on Android");
return;
#endif
const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance);
if(ffStrbufIgnCaseCompS(&wmde->wmProtocolName, "TTY") == 0)
{
ffPrintError(instance, FF_THEME_MODULE_NAME, 0, &instance->config.fontKey, &instance->config.fontFormat, FF_THEME_NUM_FORMAT_ARGS, "Theme isn't supported in TTY");
return;
}
const FFPlasmaResult* plasma = ffDetectPlasma(instance);
const FFstrbuf* gtk2 = &ffDetectGTK2(instance)->theme;
const FFstrbuf* gtk3 = &ffDetectGTK3(instance)->theme;
const FFstrbuf* gtk4 = &ffDetectGTK4(instance)->theme;
if(plasma->widgetStyle.length == 0 && plasma->colorScheme.length == 0 && gtk2->length == 0 && gtk3->length == 0 && gtk4->length == 0)
{
ffPrintError(instance, FF_THEME_MODULE_NAME, 0, &instance->config.themeKey, &instance->config.themeFormat, FF_THEME_NUM_FORMAT_ARGS, "No themes found");
return;
}
FF_STRBUF_CREATE(plasmaColorPretty);
if(ffStrbufStartsWithIgnCase(&plasma->colorScheme, &plasma->widgetStyle))
ffStrbufAppendNS(&plasmaColorPretty, plasma->colorScheme.length - plasma->widgetStyle.length, &plasma->colorScheme.chars[plasma->widgetStyle.length]);
else
ffStrbufAppend(&plasmaColorPretty, &plasma->colorScheme);
ffStrbufTrim(&plasmaColorPretty, ' ');
FF_STRBUF_CREATE(gtkPretty);
ffGetGtkPretty(&gtkPretty, gtk2, gtk3, gtk4);
if(instance->config.themeFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_THEME_MODULE_NAME, 0, &instance->config.themeKey);
if(plasma->widgetStyle.length > 0)
{
ffStrbufWriteTo(&plasma->widgetStyle, stdout);
if(plasma->colorScheme.length > 0)
{
fputs(" (", stdout);
if(plasmaColorPretty.length > 0)
ffStrbufWriteTo(&plasmaColorPretty, stdout);
else
ffStrbufWriteTo(&plasma->colorScheme, stdout);
putchar(')');
}
}
else if(plasma->colorScheme.length > 0)
{
if(plasmaColorPretty.length > 0)
ffStrbufWriteTo(&plasmaColorPretty, stdout);
else
ffStrbufWriteTo(&plasma->colorScheme, stdout);
}
if(plasma->widgetStyle.length > 0 || plasma->colorScheme.length > 0)
{
fputs(" [Plasma]", stdout);
if(gtkPretty.length > 0)
fputs(", ", stdout);
}
ffStrbufPutTo(&gtkPretty, stdout);
}
else
{
ffPrintFormatString(instance, FF_THEME_MODULE_NAME, 0, &instance->config.themeKey, &instance->config.themeFormat, NULL, FF_THEME_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &plasma->widgetStyle},
{FF_FORMAT_ARG_TYPE_STRBUF, &plasma->colorScheme},
{FF_FORMAT_ARG_TYPE_STRBUF, &plasmaColorPretty},
{FF_FORMAT_ARG_TYPE_STRBUF, gtk2},
{FF_FORMAT_ARG_TYPE_STRBUF, gtk3},
{FF_FORMAT_ARG_TYPE_STRBUF, gtk4},
{FF_FORMAT_ARG_TYPE_STRBUF, &gtkPretty}
});
}
ffStrbufDestroy(&plasmaColorPretty);
ffStrbufDestroy(&gtkPretty);
}
+18
View File
@@ -0,0 +1,18 @@
#include "fastfetch.h"
#define FF_TIME_MODULE_NAME "Time"
void ffPrintTime(FFinstance* instance)
{
if(instance->config.timeFormat.length > 0)
{
ffPrintDateTimeFormat(instance, FF_TIME_MODULE_NAME, &instance->config.timeKey, &instance->config.timeFormat);
return;
}
const FFDateTimeResult* datetime = ffDetectDateTime(instance);
ffPrintLogoAndKey(instance, FF_TIME_MODULE_NAME, 0, &instance->config.timeKey);
//hh:mm:ss
printf("%s:%s:%s\n", datetime->hourPretty.chars, datetime->minutePretty.chars, datetime->secondPretty.chars);
}
+49
View File
@@ -0,0 +1,49 @@
#include "fastfetch.h"
#include <unistd.h>
#include <pthread.h>
const FFTitleResult* ffDetectTitle(FFinstance* instance)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static bool init = false;
static FFTitleResult result;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
ffStrbufInit(&result.userName);
ffStrbufAppendS(&result.userName, instance->state.passwd->pw_name);
ffStrbufInitA(&result.hostname, 256);
gethostname(result.hostname.chars, result.hostname.allocated);
ffStrbufRecalculateLength(&result.hostname);
pthread_mutex_unlock(&mutex);
return &result;
}
static inline void printTitlePart(FFinstance* instance, const FFstrbuf* content)
{
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
ffPrintColor(&instance->config.color);
ffStrbufWriteTo(content, stdout);
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
}
void ffPrintTitle(FFinstance* instance)
{
const FFTitleResult* result = ffDetectTitle(instance);
ffPrintLogoLine(instance);
printTitlePart(instance, &result->userName);
putchar('@');
printTitlePart(instance, &result->hostname);
putchar('\n');
}
+41
View File
@@ -0,0 +1,41 @@
#include "fastfetch.h"
#define FF_UPTIME_MODULE_NAME "Uptime"
#define FF_UPTIME_NUM_FORMAT_ARGS 4
void ffPrintUptime(FFinstance* instance)
{
uint32_t days = (uint32_t) instance->state.sysinfo.uptime / 86400;
uint32_t hours = (uint32_t) (instance->state.sysinfo.uptime - (days * 86400)) / 3600;
uint32_t minutes = (uint32_t) (instance->state.sysinfo.uptime - (days * 86400) - (hours * 3600)) / 60;
uint32_t seconds = (uint32_t) instance->state.sysinfo.uptime - (days * 86400) - (hours * 3600) - (minutes * 60);
if(instance->config.uptimeFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_UPTIME_MODULE_NAME, 0, &instance->config.uptimeKey);
if(days == 0 && hours == 0 && minutes == 0)
{
printf("%u seconds\n", seconds);
}
else
{
if(days > 0)
printf("%u day%s%s, ", days, days <= 1 ? "" : "s", days < 100 ? "" : "(!)");
if(hours > 0)
printf("%u hour%s, ", hours, hours <= 1 ? "" : "s");
if(minutes > 0)
printf("%u min%s", minutes, minutes <= 1 ? "" : "s");
putchar('\n');
}
}
else
{
ffPrintFormatString(instance, FF_UPTIME_MODULE_NAME, 0, &instance->config.uptimeKey, &instance->config.uptimeFormat, NULL, FF_UPTIME_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_UINT, &days},
{FF_FORMAT_ARG_TYPE_UINT, &hours},
{FF_FORMAT_ARG_TYPE_UINT, &minutes},
{FF_FORMAT_ARG_TYPE_UINT, &seconds}
});
}
}
+44
View File
@@ -0,0 +1,44 @@
#include "fastfetch.h"
#define FF_WM_MODULE_NAME "WM"
#define FF_WM_NUM_FORMAT_ARGS 3
void ffPrintWM(FFinstance* instance)
{
#ifdef __ANDROID__
ffPrintError(instance, FF_WM_MODULE_NAME, 0, &instance->config.wmKey, &instance->config.wmFormat, FF_WM_NUM_FORMAT_ARGS, "WM detection is not supported on Android");
return;
#endif
const FFDisplayServerResult* result = ffConnectDisplayServer(instance);
if(result->wmPrettyName.length == 0)
{
ffPrintError(instance, FF_WM_MODULE_NAME, 0, &instance->config.wmKey, &instance->config.wmFormat, FF_WM_NUM_FORMAT_ARGS, "No WM found");
return;
}
if(instance->config.wmFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_WM_MODULE_NAME, 0, &instance->config.wmKey);
ffStrbufWriteTo(&result->wmPrettyName, stdout);
if(result->wmProtocolName.length > 0)
{
fputs(" (", stdout);
ffStrbufWriteTo(&result->wmProtocolName, stdout);
putchar(')');
}
putchar('\n');
}
else
{
ffPrintFormatString(instance, FF_WM_MODULE_NAME, 0, &instance->config.wmKey, &instance->config.wmFormat, NULL, FF_WM_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRBUF, &result->wmProcessName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->wmPrettyName},
{FF_FORMAT_ARG_TYPE_STRBUF, &result->wmProtocolName}
});
}
}
+258
View File
@@ -0,0 +1,258 @@
#include "fastfetch.h"
#include <string.h>
#define FF_WMTHEME_MODULE_NAME "WM Theme"
#define FF_WMTHEME_NUM_FORMAT_ARGS 1
static void printWMTheme(FFinstance* instance, const char* theme)
{
if(instance->config.wmThemeFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey);
puts(theme);
}
else
{
ffPrintFormatString(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, NULL, FF_WMTHEME_NUM_FORMAT_ARGS, (FFformatarg[]){
{FF_FORMAT_ARG_TYPE_STRING, theme}
});
}
}
static void printWMThemeFromConfigFile(FFinstance* instance, const char* configFile, const char* themeRegex, const char* defaultValue)
{
FFstrbuf theme;
ffStrbufInit(&theme);
if(!ffParsePropFileConfig(instance, configFile, themeRegex, &theme))
{
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "Config file %s doesn't exist", configFile);
ffStrbufDestroy(&theme);
return;
}
if(theme.length == 0)
{
ffStrbufDestroy(&theme);
if(defaultValue == NULL)
{
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "Couldn't find WM theme in %s", configFile);
return;
}
printWMTheme(instance, defaultValue);
return;
}
// Remove Plasma-generated prefixes
uint32_t idx = 0;
idx = ffStrbufFirstIndexS(&theme, "qml_");
if(idx != theme.length)
ffStrbufSubstrAfter(&theme, idx + 3);
idx = ffStrbufFirstIndexS(&theme, "svg__");
if(idx != theme.length)
ffStrbufSubstrAfter(&theme, idx + 4);
printWMTheme(instance, theme.chars);
ffStrbufDestroy(&theme);
}
static void printWMThemeFromSettings(FFinstance* instance, const char* dconfKey, const char* gsettingsSchemaName, const char* gsettingsPath, const char* gsettingsKey)
{
const char* theme = ffSettingsGet(instance, dconfKey, gsettingsSchemaName, gsettingsPath, gsettingsKey, FF_VARIANT_TYPE_STRING).strValue;
if(!ffStrSet(theme))
{
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "Couldn't find WM theme in DConf or GSettings");
return;
}
printWMTheme(instance, theme);
}
static void printGTKThemeAsWMTheme(FFinstance* instance)
{
const FFGTKResult* gtk = ffDetectGTK4(instance);
if(gtk->theme.length > 0)
{
printWMTheme(instance, gtk->theme.chars);
return;
}
gtk = ffDetectGTK3(instance);
if(gtk->theme.length > 0)
{
printWMTheme(instance, gtk->theme.chars);
return;
}
gtk = ffDetectGTK2(instance);
if(gtk->theme.length > 0)
{
printWMTheme(instance, gtk->theme.chars);
return;
}
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "Couldn't detect GTK4/3/2 theme");
}
static void printMutter(FFinstance* instance)
{
const char* theme = ffSettingsGet(instance, "/org/gnome/shell/extensions/user-theme/name", "org.gnome.shell.extensions.user-theme", NULL, "name", FF_VARIANT_TYPE_STRING).strValue;
if(ffStrSet(theme))
{
printWMTheme(instance, theme);
return;
}
printGTKThemeAsWMTheme(instance);
}
static void printMuffin(FFinstance* instance)
{
const char* name = ffSettingsGet(instance, "/org/cinnamon/theme/name", "org.cinnamon.theme", NULL, "name", FF_VARIANT_TYPE_STRING).strValue;
const char* theme = ffSettingsGet(instance, "/org/cinnamon/desktop/wm/preferences/theme", "org.cinnamon.desktop.wm.preferences", NULL, "theme", FF_VARIANT_TYPE_STRING).strValue;
if(name == NULL && theme == NULL)
{
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "Couldn't find muffin theme in GSettings / DConf");
return;
}
if(name == NULL)
printWMTheme(instance, theme);
else if(theme == NULL)
printWMTheme(instance, name);
else
{
FFstrbuf buffer;
ffStrbufInit(&buffer);
ffStrbufAppendS(&buffer, name);
ffStrbufAppendS(&buffer, " (");
ffStrbufAppendS(&buffer, theme);
ffStrbufAppendC(&buffer, ')');
printWMTheme(instance, buffer.chars);
ffStrbufDestroy(&buffer);
}
}
static void printXFWM4(FFinstance* instance)
{
const char* theme = ffSettingsGetXFConf(instance, "xfwm4", "/general/theme", FF_VARIANT_TYPE_STRING).strValue;
if(theme == NULL)
{
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "Couldn't find xfwm4::/general/theme in XFConf");
return;
}
printWMTheme(instance, theme);
}
static void printOpenbox(FFinstance* instance, const FFstrbuf* dePrettyName)
{
FFstrbuf absolutePath;
ffStrbufInitA(&absolutePath, 64);
ffStrbufAppendS(&absolutePath, instance->state.passwd->pw_dir);
ffStrbufAppendC(&absolutePath, '/');
if(ffStrbufIgnCaseCompS(dePrettyName, "LXQT") == 0)
ffStrbufAppendS(&absolutePath, ".config/openbox/lxqt-rc.xml");
else if(ffStrbufIgnCaseCompS(dePrettyName, "LXDE") == 0)
ffStrbufAppendS(&absolutePath, ".config/openbox/lxde-rc.xml");
else
ffStrbufAppendS(&absolutePath, ".config/openbox/rc.xml");
char* line = NULL;
size_t len = 0;
FILE* file = fopen(absolutePath.chars, "r");
if(file == NULL)
{
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "Couldn't open \"%s\"", absolutePath.chars);
ffStrbufDestroy(&absolutePath);
return;
}
FFstrbuf theme;
ffStrbufInitA(&theme, 256);
while(getline(&line, &len, file) != -1)
{
if(strstr(line, "<theme>") != 0)
break;
}
while(getline(&line, &len, file) != -1)
{
if(strstr(line, "<name>") != 0)
{
ffStrbufAppendS(&theme, line);
ffStrbufRemoveStrings(&theme, 2, "<name>", "</name>");
ffStrbufTrimRight(&theme, '\n');
ffStrbufTrim(&theme, ' ');
break;
}
if(strstr(line, "</theme>") != 0) // sanity check
break;
}
if(line != NULL)
free(line);
fclose(file);
if(theme.length == 0)
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "Couldn't find theme name in \"%s\"", absolutePath.chars);
else
printWMTheme(instance, theme.chars);
ffStrbufDestroy(&theme);
ffStrbufDestroy(&absolutePath);
}
void ffPrintWMTheme(FFinstance* instance)
{
#ifdef __ANDROID__
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "WM theme detection is not supported on Android");
return;
#endif
const FFDisplayServerResult* result = ffConnectDisplayServer(instance);
if(result->wmPrettyName.length == 0)
{
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "WM Theme needs sucessfull WM detection");
return;
}
if(ffStrbufIgnCaseCompS(&result->wmPrettyName, "KWin") == 0 || ffStrbufIgnCaseCompS(&result->wmPrettyName, "KDE") == 0 || ffStrbufIgnCaseCompS(&result->wmPrettyName, "Plasma") == 0)
printWMThemeFromConfigFile(instance, "kwinrc", "theme =", "Breeze");
else if(ffStrbufIgnCaseCompS(&result->wmPrettyName, "Xfwm4") == 0 || ffStrbufIgnCaseCompS(&result->wmPrettyName, "Xfwm") == 0)
printXFWM4(instance);
else if(ffStrbufIgnCaseCompS(&result->wmPrettyName, "Mutter") == 0)
{
if(ffStrbufIgnCaseCompS(&result->dePrettyName, "Gnome") == 0)
printMutter(instance);
else
printGTKThemeAsWMTheme(instance);
}
else if(ffStrbufIgnCaseCompS(&result->wmPrettyName, "Muffin") == 0)
printMuffin(instance);
else if(ffStrbufIgnCaseCompS(&result->wmPrettyName, "Marco") == 0)
printWMThemeFromSettings(instance, "/org/mate/Marco/general/theme", "org.mate.Marco.general", NULL, "theme");
else if(ffStrbufIgnCaseCompS(&result->wmPrettyName, "Openbox") == 0)
printOpenbox(instance, &result->dePrettyName);
else
ffPrintError(instance, FF_WMTHEME_MODULE_NAME, 0, &instance->config.wmThemeKey, &instance->config.wmThemeFormat, FF_WMTHEME_NUM_FORMAT_ARGS, "Unknown WM: %s", result->wmPrettyName.chars);
}

Some files were not shown because too many files have changed in this diff Show More