mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 18:32:10 +02:00
Compare commits
188 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a11ff8ddb | |||
| a99b6d602d | |||
| 40921156b8 | |||
| bc3703140b | |||
| bd5b385909 | |||
| 0fe3db419e | |||
| e80bb480c4 | |||
| d4773212b7 | |||
| 9ebff9fd22 | |||
| 5d8edc0c8b | |||
| 78530b4e34 | |||
| 61f2823903 | |||
| 7446828866 | |||
| 0b37acec5b | |||
| 53ecf57f19 | |||
| e1c4365c10 | |||
| ebc60ac66c | |||
| 4004501d0f | |||
| a0265c3204 | |||
| 3ef84834fc | |||
| 3276949663 | |||
| 1ec69d6838 | |||
| 7f1063d09b | |||
| 883923de4a | |||
| 146981009b | |||
| 060b69293c | |||
| 3345c1e9dd | |||
| 1c8e2e34ba | |||
| 5ab51c07d1 | |||
| eee98e54d0 | |||
| b0340d5581 | |||
| 1124342eda | |||
| 329cb229f8 | |||
| bedb5eb6c5 | |||
| f184f8dab5 | |||
| 7ee0e8726e | |||
| a7347130de | |||
| f3c82ad837 | |||
| 1bde2b31f9 | |||
| 1edc617b55 | |||
| 2bb451d9e4 | |||
| 4588644d39 | |||
| ed94b4c46c | |||
| f96ddb4c10 | |||
| 1b0a65ecf2 | |||
| 33983c5402 | |||
| 8cfee9d693 | |||
| 64568b31d1 | |||
| 02c999725c | |||
| be11013ae2 | |||
| a84c4dc1f4 | |||
| d9455adc7f | |||
| b20aea7ab8 | |||
| 5e770dc8b0 | |||
| 137d5dfe90 | |||
| 655d11c1cc | |||
| e32987c408 | |||
| fa4e11c73b | |||
| e753d4d3a4 | |||
| 75d2c668d0 | |||
| 559082806b | |||
| 6f8d276545 | |||
| 582268f44f | |||
| 9b68674c4f | |||
| badae2ba9d | |||
| dd4f006b5f | |||
| c491c9e8fc | |||
| 094d736d38 | |||
| 22bfaf3c69 | |||
| 980e552eba | |||
| 4a141c3f57 | |||
| 59289bef6c | |||
| 0b02543ef2 | |||
| 3cef51ec4a | |||
| 6d86813647 | |||
| fb267acac3 | |||
| ba3e26f2b4 | |||
| 4f8d8de749 | |||
| 54c5bc5110 | |||
| c6675f82f0 | |||
| afe2c85047 | |||
| 671b11157e | |||
| 78fb55c35c | |||
| 559905da60 | |||
| e093c8aa72 | |||
| ba8a21142f | |||
| eca3625165 | |||
| 677e920dc8 | |||
| 38c0b24daa | |||
| 4e05502d7a | |||
| d0928a63bf | |||
| aa0bbd5aee | |||
| ce554dbdd0 | |||
| a0174e8666 | |||
| 8be419cfd6 | |||
| ca79bae36e | |||
| b1f0248fb3 | |||
| 77dd18466b | |||
| 5bb6e22679 | |||
| 9ea9291ab8 | |||
| 6ec73863ab | |||
| 8c2f93151d | |||
| 71ddc4ffc6 | |||
| 4909d693e3 | |||
| fe76bbe68a | |||
| d1f20822f1 | |||
| 1ed804db52 | |||
| 0e8b9bec2d | |||
| a3182c31cb | |||
| d2f2c76dfc | |||
| 2d1ae1b4c5 | |||
| 316409ecc3 | |||
| f0fc38bcb5 | |||
| e7a6b84107 | |||
| 02c3254961 | |||
| a90e1d7ccf | |||
| 6cdd64ac55 | |||
| aa1201ffb8 | |||
| 9b491c2f49 | |||
| be3e2ba521 | |||
| ccbb9e90ea | |||
| d07fe997a9 | |||
| b60996bcd1 | |||
| 272c0aad9c | |||
| 7566f10bb6 | |||
| 847b76b863 | |||
| 5c0ad030cc | |||
| 02aafbd7f4 | |||
| 72da8150bd | |||
| aca2b5336d | |||
| c79b67efc1 | |||
| 30af91ce8e | |||
| 6c56348e54 | |||
| 531b25dc2f | |||
| 8c6dc34654 | |||
| cf2034b93e | |||
| f7db03b8c4 | |||
| 8647e366f9 | |||
| 1bf41d68f9 | |||
| 1ac1f1f889 | |||
| 14f1278485 | |||
| c2c6856159 | |||
| c89f3d2896 | |||
| 8e773470df | |||
| ea4fc7bdda | |||
| 468d1e0903 | |||
| 6851e7b082 | |||
| a734f18fd5 | |||
| 30b1e29785 | |||
| dde0c22195 | |||
| 1311a809db | |||
| 60a54f4880 | |||
| b4e65e7e48 | |||
| 6f3ed4a7b4 | |||
| cc8f8ff0f7 | |||
| 8a00f2d901 | |||
| c5c0e543e4 | |||
| 97255f37da | |||
| fb8a04f97a | |||
| c3ce5ac947 | |||
| eae69049b2 | |||
| 102e8fcc72 | |||
| 8372708f98 | |||
| 5011edc9d9 | |||
| 94a5228ffc | |||
| 382c0cd7c3 | |||
| ab496b23b7 | |||
| 939f428da7 | |||
| 2d496c91e1 | |||
| 442d58b646 | |||
| 4d377b94b7 | |||
| 7248868e33 | |||
| 2eda6464ad | |||
| 2622fc12a6 | |||
| b178e43428 | |||
| d00fca26c5 | |||
| 23e4d1c4aa | |||
| 6871c8bb73 | |||
| 61adc7cf64 | |||
| 9ba30adcba | |||
| 010b2c6262 | |||
| c65fb1a868 | |||
| 2029679ea5 | |||
| 817f70c2cd | |||
| 3a1f0485f4 | |||
| 64910d0781 | |||
| 821462e152 | |||
| b015fd579c |
+24
-26
@@ -10,7 +10,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install codespell
|
||||
shell: bash
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
ffversion: ${{ steps.ffversion.outputs.ffversion }}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
@@ -198,7 +198,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
@@ -237,7 +237,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
@@ -273,7 +273,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
@@ -308,7 +308,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
@@ -343,7 +343,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
@@ -374,7 +374,7 @@ jobs:
|
||||
name: Musl-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: setup alpine linux
|
||||
uses: jirutka/setup-alpine@master
|
||||
@@ -416,7 +416,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
@@ -463,7 +463,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
@@ -507,7 +507,7 @@ jobs:
|
||||
name: SunOS-amd64
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/omnios-vm@v1
|
||||
@@ -543,7 +543,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
@@ -580,7 +580,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
@@ -616,7 +616,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
@@ -628,7 +628,7 @@ jobs:
|
||||
version: '10.1'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkgin -y install cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick opencl-headers ocl-icd
|
||||
sudo pkgin -y install cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
@@ -653,7 +653,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/dragonflybsd-vm@v1
|
||||
@@ -683,33 +683,30 @@ jobs:
|
||||
haiku-amd64:
|
||||
name: Haiku-amd64
|
||||
runs-on: ubuntu-latest
|
||||
if: false
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
operating_system: haiku
|
||||
version: 'r1beta5'
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: 'r1beta5'
|
||||
run: |
|
||||
uname -a
|
||||
ls -l
|
||||
pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10
|
||||
pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10 || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
@@ -729,7 +726,7 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: setup-msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
@@ -791,7 +788,7 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: setup-msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
@@ -861,6 +858,7 @@ jobs:
|
||||
- openbsd-amd64
|
||||
- netbsd-amd64
|
||||
- sunos-amd64
|
||||
- haiku-amd64
|
||||
- windows-amd64
|
||||
- windows-aarch64
|
||||
permissions:
|
||||
|
||||
+150
@@ -1,3 +1,153 @@
|
||||
# 2.50.0
|
||||
|
||||
Changes:
|
||||
* Keys in JSON configuration files are now case-sensitive, as stated in v2.49.0.
|
||||
* This is a breaking change, but it should not affect most users as long as your config file passes JSON schema validation.
|
||||
* All module config flags have been removed, as stated in v2.49.0.
|
||||
* To configure modules via the command line, use: `echo '{"modules": [{"type":"custom","format":"Hello Fastfetch!"}]}' | fastfetch -c -`.
|
||||
* The percent bar config `display.bar.*` options have been replaced with a more organized, nested object structure.
|
||||
* `display.bar.charElapsed` has been renamed to `display.bar.char.elapsed`.
|
||||
* `display.bar.charTotal` has been renamed to `display.bar.char.total`.
|
||||
* `display.bar.borderLeft` has been renamed to `display.bar.border.left`.
|
||||
* `display.bar.borderRight` has been renamed to `display.bar.border.right`.
|
||||
* The undocumented flag `--load-config` has been removed.
|
||||
* Use `--config` or `-c` instead.
|
||||
* Flashfetch, a simplified fastfetch variant that used a hardcoded module list with direct function calls to reduce startup overhead, has been changed to a version that aims to match neofetch's behavior as closely as possible, for demonstration purposes.
|
||||
* Flashfetch is intended to be built from source (like [st](https://st.suckless.org/)). We do not provide prebuilt binaries in distributions.
|
||||
|
||||
Features:
|
||||
* Added support for reading JSON config from stdin using `--config -` or `-c -`.
|
||||
* Added `display.bar.border.{leftElapsed,rightElapsed}` for using the border as part of the bar content. (#1875)
|
||||
* `display.bar.border: null` has been added as a shorthand to disable bar borders.
|
||||
* Added `display.bar.color.{elapsed,total,border}` to customize the color of the elapsed, total, and border sections of the percent bar.
|
||||
* `display.bar.color: null` has been added as a shorthand to disable bar colors.
|
||||
* Improved Bedrock Linux detection (#1881, OS / Disk, Linux)
|
||||
* Added the command flag `--gen-config-full`, which generates a JSON config file containing all optional module options.
|
||||
* Improved the default IP address display when `localip.showAllIPs` is not set (LocalIP)
|
||||
* For IPv4, the preferred source address (if detected) is shown.
|
||||
* For IPv6, the first GUA or ULA that is not deprecated or temporary is shown.
|
||||
* Added support for interface speed detection on SunOS (LocalIP, SunOS)
|
||||
* Added detection support for Xlibre (#1888, WM, Linux)
|
||||
* Improved the accuracy of color detection (Cursor, macOS)
|
||||
* Improved the proformance of `Nix` package manager detection on macOS by porting optimizations form Linux port (#1893, Packages, macOS)
|
||||
|
||||
Bugfixes:
|
||||
* Fixed custom object inheriting a key from the previous custom object if the key is blank (#1477)
|
||||
* Fixed a possible segfault when parsing color strings in the JSON config (#1878)
|
||||
* Fixed GPU driver detection when DRM is used (GPU, FreeBSD)
|
||||
* Fixed default route detection on DragonFly BSD (LocalIP, DFBSD)
|
||||
* Fixed lliurex detection (#1882, OS, Linux)
|
||||
* Fixed compatibility with `-ffast-math` (#1894)
|
||||
* Fixed physical GPU sometimes being ignored (#1896, GPU, Windows)
|
||||
|
||||
Logos:
|
||||
* Added ObsidianOS (#1890)
|
||||
|
||||
# 2.49.0
|
||||
|
||||
Deprecation Notice:
|
||||
* In fastfetch v2, the JSONC configuration format has been introduced, while command line configuration flags are kept for compatibility. Although they have the same effects, they use two different code paths, and as the number of flags grows, the codebase is becoming increasingly difficult to maintain.
|
||||
* Removal of module config flags is planned for **v2.50.0**, which will also fix a long-standing issue #1477.
|
||||
* Removal of most other config flags is also planned for later versions.
|
||||
* Keys of JSON configuration files will be all case-sensitive. Currently they are inconsistent. Planned for **v2.50.0**.
|
||||
|
||||
Changes:
|
||||
* Due to more restrictive permissions in macOS Tahoe, SSID detection on macOS 26+ requires root privileges. `<redacted>` will be displayed otherwise.
|
||||
|
||||
Features:
|
||||
* Improve `nouveau` driver support for `--gpu-driver-specific` (GPU, Linux)
|
||||
* VRAM size detection
|
||||
* GPU temperature detection
|
||||
* Core count detection (when available)
|
||||
* Improve Scoop package manager detection (Packages, Windows)
|
||||
* Support [`scoop-global`](https://github.com/ScoopInstaller/Install?tab=readme-ov-file#advanced-installation)
|
||||
* Read Scoop's config file to find the installation path of Scoop
|
||||
* Improve ARM SoC detection (CPU, Android)
|
||||
* Make SoC detection more lenient. Higher chance to match at the cost of accuracy.
|
||||
* Add more Snapdragon SoC names
|
||||
* Support labwc WM version detection, used for XFCE4 on Wayland (WM, Linux)
|
||||
* Improve accuracy of GPU temperature detection for Intel dedicated GPUs on Windows (GPU, Windows)
|
||||
* Parse unicode escaped strings generated by qt5ct (#1864, Font, Linux)
|
||||
* Add `--{duration,percent,size,freq,temp}-space-before-unit [always|never]` options to add a space before the unit when printing duration, percent, size, frequency and temperature values
|
||||
* Add `--duration-abbreviation` to abbreviate duration values in custom format
|
||||
* For example: `1 day, 2 hours, 3 mins` will be displayed as `1d 2h 3m`
|
||||
* Add `--percent-width` to pad the percent value with spaces to a fixed width
|
||||
* For example: `--percent-width 3` will display ` 50%` instead of `50%`; useful for aligning percent values in custom format
|
||||
|
||||
Bugfixes:
|
||||
* Improve accuracy of Flatpak count detection (#1856, Packages, Linux)
|
||||
* Remove qi package manager support (#1858, Packages, Linux)
|
||||
* Fix LocalIP module on Windows (LocalIP, Windows)
|
||||
* Fix default route detection when multiple network interfaces are connected
|
||||
* Fix link speed calculation
|
||||
* Fix interface status when the interface is up but not connected (Wifi, Linux)
|
||||
* Fix variable names in custom format (#1861)
|
||||
* `full-path` to `path` (Editor)
|
||||
* `session` to `session-name` (Users)
|
||||
* `name` to `project-name` (Version)
|
||||
* Fix wrong /s assignment in custom format (#1871, DiskIO)
|
||||
|
||||
Logos:
|
||||
* Add `Aeon`
|
||||
* Remove `Evolinx`
|
||||
|
||||
# 2.48.1
|
||||
|
||||
Features:
|
||||
* Add support for detecting Openbox WM version (WM, Linux)
|
||||
* Improve reliability of child process spawning on Windows (Windows)
|
||||
* Add a new option `--packages-combined`, which combines related package managers into single counts (#1851, Packages)
|
||||
* For example: if you have both `flatpak-system` and `flatpak-user` packages installed, they will be combined into a single `flatpak` count with `--packages-combined` enabled.
|
||||
* Add `modules[n].condition` to conditionally enable modules on different platforms
|
||||
* Useful when sharing configuration files across platforms
|
||||
* For example:
|
||||
```jsonc
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "This string will be printed on Intel macOS only",
|
||||
"condition": {
|
||||
"system": "macOS", // Can be an array, optional
|
||||
"arch": "x86_64" // Can be an array, optional too
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Bugfixes:
|
||||
* Revert the change of `posix_spawn` in v2.48.0 for Android and OpenBSD (Android / OpenBSD)
|
||||
* Fix completion for Android 7 (Required by Termux)
|
||||
|
||||
# 2.48.0
|
||||
|
||||
Features:
|
||||
* Add support for detecting Fedora variants (#1830, OS, Linux)
|
||||
* Currently supported variants: CoreOS, Kinoite, Sericea, Silverblue
|
||||
* Optimize GPU detection on Windows when `--gpu-driver-specific` is not used (GPU, Windows)
|
||||
* Improve accuracy of GPU type detection. Previously it was guessed based on the dedicated vmem size, which causes issues on newer AMD integrated GPUs such as 9000 HX and AI 9 HX series. Supported on Windows 8.1 or later.
|
||||
* Add support for generic frequency detection of GPU 3D engine on Windows 11 22H2 or later.
|
||||
* Improve performance. GPU temperature detection is significantly improved when `--gpu-driver-specific` is not used.
|
||||
* Improve performance and security when spawning child processes by replacing `fork-exec` with `posix_spawn` (*nix)
|
||||
* Improve accuracy of sound device detection on macOS (Sound, macOS)
|
||||
* Trim leading and trailing whitespaces in disk serial numbers (PhysicalDisk)
|
||||
* Add `/etc/profiles/per-user` detection for Nix user packages (#1782, Packages, Linux)
|
||||
* Introduce `years` (whole years as integer), `days-of-year` (days since the last anniversary) and `years-fraction` (years as fraction, e.g. 1.5 means 1 year and 6 months) formatting placeholder to `Disk` (since disk creation), `Users` (since user login) and `Uptime` (since system boot) modules
|
||||
* For example: `fastfetch -s disk --disk-key 'OS Installation' --disk-format '{years} years {days-of-year} days'`
|
||||
* Add `--fraction-ndigits` option to specify the number of digits after the decimal point when displaying ordinary fractional values
|
||||
* Typically used with `{years-fraction}` above
|
||||
* This option does not affect percentage values, sizes, etc, which are controlled by individual options.
|
||||
|
||||
Bugfixes:
|
||||
* Fix compilation issues when not using `-DBINARY_LINK_TYPE=dlopen`
|
||||
* Regression from v2.47.0
|
||||
* Note: this option was added for debugging purposes only and is not recommended for production use
|
||||
* Replace `MTLDevice::hasUnifiedMemory` with `MTLDevice::location` for GPU type Detection (GPU, macOS)
|
||||
* This should resolve the issue where discrete GPUs were detected as integrated GPUs on Intel MacBooks with multi-GPU configurations.
|
||||
* Prevent text files from being loaded as image files (#1843, Logo)
|
||||
|
||||
Logos:
|
||||
* Add Minimal System
|
||||
* Add AxOS
|
||||
* Rename Ada to Xray OS
|
||||
|
||||
# 2.47.0
|
||||
|
||||
Features:
|
||||
|
||||
+11
-7
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
|
||||
|
||||
project(fastfetch
|
||||
VERSION 2.47.0
|
||||
VERSION 2.50.0
|
||||
LANGUAGES C
|
||||
DESCRIPTION "Fast neofetch-like system information tool"
|
||||
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
|
||||
@@ -336,7 +336,7 @@ endif()
|
||||
# Ascii image data #
|
||||
####################
|
||||
|
||||
file(GLOB LOGO_FILES "src/logo/ascii/*.txt")
|
||||
file(GLOB LOGO_FILES CONFIGURE_DEPENDS "src/logo/ascii/*.txt")
|
||||
set(LOGO_BUILTIN_H "#pragma once\n#pragma GCC diagnostic ignored \"-Wtrigraphs\"\n\n")
|
||||
foreach(file ${LOGO_FILES})
|
||||
fastfetch_load_text("${file}" content)
|
||||
@@ -351,10 +351,11 @@ file(GENERATE OUTPUT logo_builtin.h CONTENT "${LOGO_BUILTIN_H}")
|
||||
#######################
|
||||
|
||||
set(LIBFASTFETCH_SRC
|
||||
src/common/percent.c
|
||||
src/common/commandoption.c
|
||||
src/common/duration.c
|
||||
src/common/font.c
|
||||
src/common/format.c
|
||||
src/common/frequency.c
|
||||
src/common/init.c
|
||||
src/common/jsonconfig.c
|
||||
src/common/library.c
|
||||
@@ -363,9 +364,11 @@ set(LIBFASTFETCH_SRC
|
||||
src/common/networking/networking_common.c
|
||||
src/common/option.c
|
||||
src/common/parsing.c
|
||||
src/common/percent.c
|
||||
src/common/printing.c
|
||||
src/common/properties.c
|
||||
src/common/settings.c
|
||||
src/common/size.c
|
||||
src/common/temps.c
|
||||
src/detection/bluetoothradio/bluetoothradio.c
|
||||
src/detection/bootmgr/bootmgr.c
|
||||
@@ -471,7 +474,6 @@ set(LIBFASTFETCH_SRC
|
||||
src/modules/wmtheme/wmtheme.c
|
||||
src/modules/zpool/zpool.c
|
||||
src/options/display.c
|
||||
src/options/modules.c
|
||||
src/options/logo.c
|
||||
src/options/general.c
|
||||
src/util/edidHelper.c
|
||||
@@ -545,6 +547,7 @@ if(LINUX)
|
||||
src/detection/opengl/opengl_linux.c
|
||||
src/detection/os/os_linux.c
|
||||
src/detection/packages/packages_linux.c
|
||||
src/detection/packages/packages_nix.c
|
||||
src/detection/poweradapter/poweradapter_linux.c
|
||||
src/detection/processes/processes_linux.c
|
||||
src/detection/gtk_qt/qt.c
|
||||
@@ -611,6 +614,7 @@ elseif(ANDROID)
|
||||
src/detection/opengl/opengl_linux.c
|
||||
src/detection/os/os_android.c
|
||||
src/detection/packages/packages_linux.c
|
||||
src/detection/packages/packages_nix.c
|
||||
src/detection/poweradapter/poweradapter_nosupport.c
|
||||
src/detection/processes/processes_linux.c
|
||||
src/detection/sound/sound_nosupport.c
|
||||
@@ -893,7 +897,7 @@ elseif(OpenBSD)
|
||||
elseif(APPLE)
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
src/common/io/io_unix.c
|
||||
src/common/netif/netif_bsd.c
|
||||
src/common/netif/netif_apple.c
|
||||
src/common/networking/networking_linux.c
|
||||
src/common/processing_linux.c
|
||||
src/common/sysctl.c
|
||||
@@ -937,6 +941,7 @@ elseif(APPLE)
|
||||
src/detection/opengl/opengl_apple.c
|
||||
src/detection/os/os_apple.m
|
||||
src/detection/packages/packages_apple.c
|
||||
src/detection/packages/packages_nix.c
|
||||
src/detection/poweradapter/poweradapter_apple.c
|
||||
src/detection/processes/processes_bsd.c
|
||||
src/detection/sound/sound_apple.c
|
||||
@@ -1039,7 +1044,7 @@ elseif(SunOS)
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
src/common/dbus.c
|
||||
src/common/io/io_unix.c
|
||||
src/common/netif/netif_bsd.c
|
||||
src/common/netif/netif_apple.c
|
||||
src/common/networking/networking_linux.c
|
||||
src/common/processing_linux.c
|
||||
src/detection/battery/battery_nosupport.c
|
||||
@@ -1582,7 +1587,6 @@ elseif(WIN32)
|
||||
PRIVATE "ws2_32"
|
||||
PRIVATE "ntdll"
|
||||
PRIVATE "version"
|
||||
PRIVATE "setupapi"
|
||||
PRIVATE "hid"
|
||||
PRIVATE "wtsapi32"
|
||||
PRIVATE "imagehlp"
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[](https://github.com/fastfetch-cli/fastfetch/releases)
|
||||
[](https://repology.org/project/fastfetch/versions)
|
||||
[](https://repology.org/project/fastfetch/versions)
|
||||
[](https://deepwiki.com/fastfetch-cli/fastfetch)
|
||||
|
||||
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and SunOS.
|
||||
|
||||
@@ -35,7 +36,6 @@ Some distributions package outdated versions of fastfetch. Older versions receiv
|
||||
<img src="https://repology.org/badge/vertical-allrepos/fastfetch.svg?columns=2" alt="Packaging status" align="right">
|
||||
</a>
|
||||
|
||||
* Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (for Ubuntu 22.04 or newer)
|
||||
* Debian: `apt install fastfetch` (for Debian 13 or newer)
|
||||
* Debian / Ubuntu: Download `fastfetch-linux-<proper architecture>.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and double-click it (for Ubuntu 20.04 or newer and Debian 11 or newer).
|
||||
* Arch Linux: `pacman -S fastfetch`
|
||||
@@ -57,7 +57,7 @@ If fastfetch is not packaged for your distribution or an outdated version is pac
|
||||
|
||||
### macOS
|
||||
|
||||
* [HomeBrew](https://formulae.brew.sh/formula/fastfetch#default): `brew install fastfetch`
|
||||
* [Homebrew](https://formulae.brew.sh/formula/fastfetch#default): `brew install fastfetch`
|
||||
* [MacPorts](https://ports.macports.org/port/fastfetch/): `sudo port install fastfetch`
|
||||
|
||||
### Windows
|
||||
@@ -92,9 +92,10 @@ See the Wiki: https://github.com/fastfetch-cli/fastfetch/wiki/Building
|
||||
|
||||
* Run with default configuration: `fastfetch`
|
||||
* Run with [all supported modules](https://github.com/fastfetch-cli/fastfetch/wiki/Support+Status#available-modules) to find what interests you: `fastfetch -c all.jsonc`
|
||||
* View all data that fastfetch detects: `fastfetch -s <module> --format json`
|
||||
* View all data that fastfetch detects: `fastfetch -s <module1>[:<module2>][:<module3>] --format json`
|
||||
* Display help messages: `fastfetch --help`
|
||||
* Generate a config file based on command line arguments: `fastfetch --arg1 --arg2 --gen-config`
|
||||
* Generate a minimal config file: `fastfetch [-s <module1>[:<module2>]] --gen-config [</path/to/config.jsonc>]`
|
||||
* Use: `--gen-config-full` to generate a full config file with all optional options
|
||||
|
||||
## Customization
|
||||
|
||||
|
||||
Vendored
+18
@@ -1,3 +1,21 @@
|
||||
fastfetch (2.49.0) jammy; urgency=medium
|
||||
|
||||
* Update to 2.49.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Thu, 31 Jul 2025 14:32:59 +0800
|
||||
|
||||
fastfetch (2.48.0) jammy; urgency=medium
|
||||
|
||||
* Update to 2.48.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Thu, 17 Jul 2025 16:16:52 +0800
|
||||
|
||||
fastfetch (2.47.0) jammy; urgency=medium
|
||||
|
||||
* Update to 2.47.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Thu, 03 Jul 2025 14:51:45 +0800
|
||||
|
||||
fastfetch (2.46.0) jammy; urgency=medium
|
||||
|
||||
* Update to 2.46.0
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
fastfetch_2.46.0_source.buildinfo universe/utils optional
|
||||
fastfetch_2.49.0_source.buildinfo universe/utils optional
|
||||
|
||||
+975
-182
File diff suppressed because it is too large
Load Diff
@@ -12,10 +12,11 @@
|
||||
"type": ["bar", "hide-others"]
|
||||
},
|
||||
"bar": {
|
||||
"borderLeft": "",
|
||||
"borderRight": "",
|
||||
"charElapsed": "█",
|
||||
"charTotal": "░",
|
||||
"border": null,
|
||||
"char": {
|
||||
"elapsed": "█",
|
||||
"total": "░"
|
||||
},
|
||||
"width": 40
|
||||
},
|
||||
"constants": [
|
||||
|
||||
+94
-20
@@ -6,11 +6,13 @@
|
||||
"keys": "blue"
|
||||
},
|
||||
"separator": "",
|
||||
// Constants are reusable strings referenced by {$1}, {$2}, etc.
|
||||
// These contain ANSI escape codes for cursor positioning
|
||||
"constants": [
|
||||
"──────────────────────────────────────────────",
|
||||
"\u001b[47D",
|
||||
"\u001b[47C",
|
||||
"\u001b[46C"
|
||||
"──────────────────────────────────────────────", // {$1} - horizontal line for borders
|
||||
"\u001b[47D", // {$2} - move cursor left 47 columns
|
||||
"\u001b[47C", // {$3} - move cursor right 47 columns
|
||||
"\u001b[46C" // {$4} - move cursor right 46 columns
|
||||
],
|
||||
"brightColor": false
|
||||
},
|
||||
@@ -22,6 +24,24 @@
|
||||
},
|
||||
{
|
||||
"type": "os",
|
||||
// Key format breakdown for OS module:
|
||||
// "│ {icon} \u001b[s{sysname}\u001b[u\u001b[10C│{$3}│{$2}"
|
||||
//
|
||||
// │ - Left border of key block
|
||||
// {icon} - OS icon (defined internally by fastfetch)
|
||||
// \u001b[s - ANSI escape: save cursor position (ESC[s)
|
||||
// {sysname} - Format variable: system name (e.g., "Linux", "Darwin")
|
||||
// \u001b[u - ANSI escape: restore cursor to saved position (ESC[u)
|
||||
// Necessary because the length of `{sysname}` differs between different platforms
|
||||
// \u001b[10C - ANSI escape: move cursor right 10 columns (ESC[10C)
|
||||
// │ - Right border of key block (always 10 columns from left border)
|
||||
// {$3} - Reference to constants[2]: move cursor right 47 columns
|
||||
// │ - Right border of value block
|
||||
// {$2} - Reference to constants[1]: move cursor left 47 columns
|
||||
//
|
||||
// This creates a fixed-width layout where the key block is exactly 10 columns wide,
|
||||
// regardless of the actual content length. The cursor manipulation ensures proper
|
||||
// alignment for the table-like structure.
|
||||
"key": "│ {icon} \u001b[s{sysname}\u001b[u\u001b[10C│{$3}│{$2}"
|
||||
},
|
||||
{
|
||||
@@ -34,7 +54,7 @@
|
||||
"key": "│ {icon} Locale │{$3}│{$2}"
|
||||
},
|
||||
|
||||
// Hardware
|
||||
// Hardware section with cyan color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#cyan}┌──────────────┬{$1}┐{#keys}│\u001b[37D",
|
||||
@@ -42,6 +62,11 @@
|
||||
},
|
||||
{
|
||||
"type": "chassis",
|
||||
// Similar structure but with cyan color formatting:
|
||||
// │{#cyan}│ - Left border with cyan color
|
||||
// {icon} - Chassis icon
|
||||
// Chassis - Fixed label text
|
||||
// │{$4}│{#keys}│{$2} - Positioning and borders for value area
|
||||
"key": "│{#cyan}│ {icon} Chassis │{$4}│{#keys}│{$2}"
|
||||
},
|
||||
{
|
||||
@@ -76,7 +101,7 @@
|
||||
"format": ""
|
||||
},
|
||||
|
||||
// Desktop
|
||||
// Desktop section with green color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#green}┌──────────────┬{$1}┐{#keys}│\u001b[37D",
|
||||
@@ -106,7 +131,7 @@
|
||||
"format": ""
|
||||
},
|
||||
|
||||
// Terminal
|
||||
// Terminal section with yellow color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#yellow}┌──────────────┬{$1}┐{#keys}│\u001b[37D",
|
||||
@@ -138,7 +163,7 @@
|
||||
"format": ""
|
||||
},
|
||||
|
||||
// Development
|
||||
// Development section with red color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#red}┌──────────────┬{$1}┐{#keys}│\u001b[39D",
|
||||
@@ -146,31 +171,44 @@
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"keyIcon": "",
|
||||
"keyIcon": "", // Custom icon override
|
||||
"key": "│{#red}│ {icon} Rust │{$4}│{#keys}│{$2}",
|
||||
"text": "rustc --version | cut -d' ' -f2",
|
||||
"format": "rustc {}"
|
||||
"text": "rustc --version",
|
||||
"format": "rustc {~6,13}" // Print 6th to 13th characters (version number)
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"condition": {
|
||||
"!system": "Windows" // Posix version
|
||||
},
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Clang │{$4}│{#keys}│{$2}",
|
||||
"text": "clang --version | head -1 | awk '{print $NF}'",
|
||||
"format": "clang {}"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"condition": {
|
||||
"system": "Windows" // Windows version
|
||||
},
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Clang │{$4}│{#keys}│{$2}",
|
||||
"text": "clang --version | findstr version", // Finds the line with "version"
|
||||
"format": "clang {~-6}" // Prints the last 6 characters (version number)
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} NodeJS │{$4}│{#keys}│{$2}",
|
||||
"text": "node --version",
|
||||
"format": "node {~1}"
|
||||
"format": "node {~1}" // {~1} removes first character (v)
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Go │{$4}│{#keys}│{$2}",
|
||||
"text": "go version | cut -d' ' -f3",
|
||||
"format": "go {~2}"
|
||||
"format": "go {~2}" // {~2} removes first 2 characters (go)
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
@@ -200,7 +238,7 @@
|
||||
"format": ""
|
||||
},
|
||||
|
||||
// Uptime
|
||||
// Uptime section with magenta color theme
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│{#magenta}┌──────────────┬{$1}┐{#keys}│\u001b[36D",
|
||||
@@ -212,15 +250,28 @@
|
||||
},
|
||||
{
|
||||
"type": "users",
|
||||
"myselfOnly": true,
|
||||
"myselfOnly": true, // Only show current user
|
||||
"keyIcon": "",
|
||||
"key": "│{#magenta}│ {icon} Login │{$4}│{#keys}│{$2}"
|
||||
},
|
||||
{
|
||||
"condition": { // Conditional module: only show on non-macOS
|
||||
"!system": "macOS"
|
||||
},
|
||||
"type": "disk",
|
||||
"keyIcon": "",
|
||||
"key": "│{#magenta}│ {icon} OS Age │{$4}│{#keys}│{$2}",
|
||||
"folders": "/", // On macOS, "/System/Volumes/VM" works for me
|
||||
"folders": "/", // Check root filesystem
|
||||
"format": "{create-time:10} [{days} days]" // Show creation time and age in days
|
||||
},
|
||||
{
|
||||
"condition": { // Conditional module: only show on macOS
|
||||
"system": "macOS"
|
||||
},
|
||||
"type": "disk",
|
||||
"keyIcon": "",
|
||||
"key": "│{#magenta}│ {icon} OS Age │{$4}│{#keys}│{$2}",
|
||||
"folders": "/System/Volumes/VM", // Work around for APFS on macOS
|
||||
"format": "{create-time:10} [{days} days]"
|
||||
},
|
||||
{
|
||||
@@ -230,12 +281,35 @@
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "└─────────────────{$1}┘",
|
||||
"key": "└─────────────────{$1}┘", // Bottom border of the entire layout
|
||||
"format": ""
|
||||
},
|
||||
|
||||
// End
|
||||
"break",
|
||||
"colors"
|
||||
// End with color palette and line break
|
||||
"break", // Add a blank line
|
||||
"colors" // Display color palette
|
||||
]
|
||||
}
|
||||
|
||||
/*
|
||||
Key Format Structure Explanation:
|
||||
|
||||
The key format uses a combination of:
|
||||
1. Unicode box drawing characters (│ ┌ ┐ └ ┘ ┬ ┴) for borders
|
||||
2. ANSI escape codes for cursor positioning (\u001b[...)
|
||||
3. Format variables ({icon}, {sysname}, etc.)
|
||||
4. Constant references ({$1}, {$2}, etc.)
|
||||
5. Color formatting ({#color})
|
||||
|
||||
ANSI Escape Codes Used:
|
||||
- \u001b[s - Save cursor position (ESC[s)
|
||||
- \u001b[u - Restore cursor position (ESC[u)
|
||||
- \u001b[nC - Move cursor right n columns (ESC[nC)
|
||||
- \u001b[nD - Move cursor left n columns (ESC[nD)
|
||||
|
||||
This creates a table-like layout with fixed column widths and proper alignment,
|
||||
regardless of the actual content length in each field.
|
||||
|
||||
For more ANSI escape code reference, see:
|
||||
https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797#cursor-controls
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
"type": "small",
|
||||
"padding": {
|
||||
"top": 1
|
||||
}
|
||||
},
|
||||
"display": {
|
||||
"separator": " "
|
||||
},
|
||||
"modules": [
|
||||
"break",
|
||||
"title",
|
||||
{
|
||||
"type": "os",
|
||||
"key": "os ",
|
||||
"keyColor": "red"
|
||||
},
|
||||
{
|
||||
"type": "kernel",
|
||||
"key": "kernel",
|
||||
"keyColor": "green"
|
||||
},
|
||||
{
|
||||
"type": "host",
|
||||
"format": "{vendor} {family}",
|
||||
"key": "host ",
|
||||
"keyColor": "yellow"
|
||||
},
|
||||
{
|
||||
"type": "packages",
|
||||
"key": "pkgs ",
|
||||
"keyColor": "blue"
|
||||
},
|
||||
{
|
||||
"type": "uptime",
|
||||
"format": "{?days}{days}d {?}{hours}h {minutes}m",
|
||||
"key": "uptime",
|
||||
"keyColor": "magenta"
|
||||
},
|
||||
{
|
||||
"type": "memory",
|
||||
"key": "memory",
|
||||
"keyColor": "cyan"
|
||||
},
|
||||
"break"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": {
|
||||
"type": "small"
|
||||
},
|
||||
"display": {
|
||||
"separator": " ",
|
||||
"key": {
|
||||
"type": "both"
|
||||
},
|
||||
"bar": {
|
||||
"border": {
|
||||
"left": "\uee00",
|
||||
"leftElapsed": "\uee03",
|
||||
"right": "\uee02",
|
||||
"rightElapsed": "\uee05"
|
||||
},
|
||||
"char": {
|
||||
"total": "\uee01",
|
||||
"elapsed": "\uee04"
|
||||
},
|
||||
"color": {
|
||||
"total": null
|
||||
}
|
||||
},
|
||||
"percent": {
|
||||
"type": [
|
||||
"bar",
|
||||
"bar-monochrome"
|
||||
]
|
||||
}
|
||||
},
|
||||
"modules": [
|
||||
"title",
|
||||
"separator",
|
||||
{
|
||||
"type": "memory",
|
||||
"key": "MEM"
|
||||
},
|
||||
{
|
||||
"type": "swap",
|
||||
"key": "SWP"
|
||||
},
|
||||
{
|
||||
"type": "disk",
|
||||
"folders": "/",
|
||||
"key": "DSK"
|
||||
},
|
||||
{
|
||||
"type": "battery",
|
||||
"key": "BAT"
|
||||
},
|
||||
{
|
||||
"type": "brightness",
|
||||
"key": "BGT"
|
||||
},
|
||||
{
|
||||
"type": "colors",
|
||||
"paddingLeft": 6,
|
||||
"symbol": "circle"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// #1887
|
||||
{
|
||||
"$schema": "file:///Users/carter/fastfetch/doc/json_schema.json",
|
||||
"logo": null,
|
||||
"display": {
|
||||
"constants": [
|
||||
"\u001b[s\u001b[33C│\u001b[u",
|
||||
" » "
|
||||
],
|
||||
"separator": "",
|
||||
"percent": {
|
||||
"type": ["num", "bar"]
|
||||
},
|
||||
"brightColor": false,
|
||||
"bar": {
|
||||
"border": {
|
||||
"left": "[",
|
||||
"leftElapsed": "[─",
|
||||
"right": "]",
|
||||
"rightElapsed": "─]"
|
||||
},
|
||||
"char": {
|
||||
"elapsed": "─",
|
||||
"total": "─"
|
||||
},
|
||||
"color": {
|
||||
"elapsed": "default",
|
||||
"total": "light_black"
|
||||
},
|
||||
"width": 14
|
||||
},
|
||||
"color": {
|
||||
"separator": "default",
|
||||
"keys": "default",
|
||||
"output": "default"
|
||||
}
|
||||
},
|
||||
"modules": [
|
||||
"title",
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "┌────「 {#1}OS{#} 」────────────────────────────┐"
|
||||
},
|
||||
{
|
||||
"type": "os",
|
||||
"key": "│ {icon}{$2}{$1}"
|
||||
},
|
||||
{
|
||||
"type": "disk",
|
||||
"folders": "/",
|
||||
"key": "│ {$1}",
|
||||
"format": "{size-percentage-bar} {size-percentage}"
|
||||
},
|
||||
{
|
||||
"type": "disk",
|
||||
"folders": "/",
|
||||
"key": "│ {$1}",
|
||||
"format": "{size-used} / {size-total}"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "└────────────────────────────────────────┘"
|
||||
},
|
||||
"break",
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "┌────「 {#1}UI{#} 」────────────────────────────┐"
|
||||
},
|
||||
{
|
||||
"type": "wm",
|
||||
"key": "│ {icon}{$2}{$1}"
|
||||
},
|
||||
{
|
||||
"type": "wmtheme",
|
||||
"key": "│ {icon}{$2}{$1}"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│ {$1}"
|
||||
},
|
||||
{
|
||||
"type": "display",
|
||||
"key": "│ {icon}{$2}{$1}",
|
||||
"format": "{width}x{height} @ {refresh-rate} Hz"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│ {$1}"
|
||||
},
|
||||
{
|
||||
"type": "terminal",
|
||||
"key": "│ {icon}{$2}{$1}"
|
||||
},
|
||||
{
|
||||
"type": "terminalfont",
|
||||
"key": "│ {icon}{$2}{$1}"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "└────────────────────────────────────────┘"
|
||||
},
|
||||
"break",
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "┌────「 {#1}HW{#} 」────────────────────────────┐"
|
||||
},
|
||||
{
|
||||
"type": "cpu",
|
||||
"key": "│ {icon}{$2}{$1}",
|
||||
"format": "{name}"
|
||||
},
|
||||
{
|
||||
"type": "gpu",
|
||||
"key": "│ {icon}{$2}{$1}",
|
||||
"format": "{name}"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"key": "│ {$1}"
|
||||
},
|
||||
{
|
||||
"type": "memory",
|
||||
"key": "│ {icon}{$2}{$1}",
|
||||
"format": "{percentage-bar} {percentage}"
|
||||
},
|
||||
{
|
||||
"type": "memory",
|
||||
"key": "│ {$1}",
|
||||
"format": "{used} / {total}"
|
||||
},
|
||||
{
|
||||
"type": "custom",
|
||||
"format": "└────────────────────────────────────────┘"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -8,8 +8,10 @@
|
||||
"width": 11
|
||||
},
|
||||
"bar": {
|
||||
"charElapsed": "=",
|
||||
"charTotal": "-",
|
||||
"char": {
|
||||
"elapsed": "=",
|
||||
"total": "-"
|
||||
},
|
||||
"width": 13
|
||||
},
|
||||
"percent": {
|
||||
|
||||
+13
-4
@@ -3,7 +3,12 @@
|
||||
"display": {
|
||||
"size": {
|
||||
"maxPrefix": "MB",
|
||||
"ndigits": 0
|
||||
"ndigits": 0,
|
||||
"spaceBeforeUnit": "never"
|
||||
},
|
||||
"freq": {
|
||||
"ndigits": 3,
|
||||
"spaceBeforeUnit": "never"
|
||||
}
|
||||
},
|
||||
"modules": [
|
||||
@@ -16,7 +21,10 @@
|
||||
"format": "{release}"
|
||||
},
|
||||
"uptime",
|
||||
"packages",
|
||||
{
|
||||
"type": "packages",
|
||||
"combined": true
|
||||
},
|
||||
"shell",
|
||||
{
|
||||
"type": "display",
|
||||
@@ -36,11 +44,12 @@
|
||||
"cpu",
|
||||
{
|
||||
"type": "gpu",
|
||||
"key": "GPU"
|
||||
"key": "GPU",
|
||||
"format": "{name}"
|
||||
},
|
||||
{
|
||||
"type": "memory",
|
||||
"format": "{} / {}"
|
||||
"format": "{used} / {total}"
|
||||
},
|
||||
"break",
|
||||
"colors"
|
||||
|
||||
+43
-29
@@ -10,21 +10,8 @@
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
bool ffParseModuleOptions(const char* key, const char* value)
|
||||
{
|
||||
if (!ffStrStartsWith(key, "--") || !ffCharIsEnglishAlphabet(key[2])) return false;
|
||||
|
||||
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(key[2]) - 'A']; *modules; ++modules)
|
||||
{
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
if (baseInfo->parseCommandOptions(baseInfo, key, value)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffPrepareCommandOption(FFdata* data)
|
||||
{
|
||||
FFOptionsModules* const options = &instance.config.modules;
|
||||
//If we don't have a custom structure, use the default one
|
||||
if(data->structure.length == 0)
|
||||
ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
|
||||
@@ -33,54 +20,78 @@ void ffPrepareCommandOption(FFdata* data)
|
||||
ffPrepareCPUUsage();
|
||||
|
||||
if(ffStrbufContainIgnCaseS(&data->structure, FF_DISKIO_MODULE_NAME))
|
||||
ffPrepareDiskIO(&options->diskIo);
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
|
||||
ffInitDiskIOOptions(&options);
|
||||
ffPrepareDiskIO(&options);
|
||||
}
|
||||
|
||||
if(ffStrbufContainIgnCaseS(&data->structure, FF_NETIO_MODULE_NAME))
|
||||
ffPrepareNetIO(&options->netIo);
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
|
||||
ffInitNetIOOptions(&options);
|
||||
ffPrepareNetIO(&options);
|
||||
}
|
||||
|
||||
if(instance.config.general.multithreading)
|
||||
{
|
||||
if(ffStrbufContainIgnCaseS(&data->structure, FF_PUBLICIP_MODULE_NAME))
|
||||
ffPreparePublicIp(&options->publicIP);
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
|
||||
ffInitPublicIpOptions(&options);
|
||||
ffPreparePublicIp(&options);
|
||||
}
|
||||
|
||||
if(ffStrbufContainIgnCaseS(&data->structure, FF_WEATHER_MODULE_NAME))
|
||||
ffPrepareWeather(&options->weather);
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
|
||||
ffInitWeatherOptions(&options);
|
||||
ffPrepareWeather(&options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonConfig(FFModuleBaseInfo* baseInfo, yyjson_mut_doc* doc)
|
||||
static void genJsonConfig(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
|
||||
{
|
||||
yyjson_mut_val* modules = yyjson_mut_obj_get(doc->root, "modules");
|
||||
if (!modules)
|
||||
modules = yyjson_mut_obj_add_arr(doc, doc->root, "modules");
|
||||
|
||||
yyjson_mut_val* module = yyjson_mut_obj(doc);
|
||||
FF_STRBUF_AUTO_DESTROY type = ffStrbufCreateS(baseInfo->name);
|
||||
ffStrbufLowerCase(&type);
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "type", &type);
|
||||
|
||||
if (baseInfo->generateJsonConfig)
|
||||
baseInfo->generateJsonConfig(baseInfo, doc, module);
|
||||
if (instance.state.fullConfig)
|
||||
{
|
||||
yyjson_mut_val* module = yyjson_mut_obj(doc);
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "type", &type);
|
||||
|
||||
if (yyjson_mut_obj_size(module) > 1)
|
||||
yyjson_mut_arr_add_val(modules, module);
|
||||
if (baseInfo->generateJsonConfig)
|
||||
baseInfo->generateJsonConfig(options, doc, module);
|
||||
|
||||
if (yyjson_mut_obj_size(module) > 1)
|
||||
yyjson_mut_arr_add_val(modules, module);
|
||||
else
|
||||
yyjson_mut_arr_add_strbuf(doc, modules, &type);
|
||||
}
|
||||
else
|
||||
{
|
||||
yyjson_mut_arr_add_strbuf(doc, modules, &type);
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonResult(FFModuleBaseInfo* baseInfo, yyjson_mut_doc* doc)
|
||||
static void genJsonResult(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
|
||||
{
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
|
||||
yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name);
|
||||
if (baseInfo->generateJsonResult)
|
||||
baseInfo->generateJsonResult(baseInfo, doc, module);
|
||||
baseInfo->generateJsonResult(options, doc, module);
|
||||
else
|
||||
yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format");
|
||||
}
|
||||
|
||||
static void parseStructureCommand(
|
||||
const char* line,
|
||||
void (*fn)(FFModuleBaseInfo *baseInfo, yyjson_mut_doc* jsonDoc),
|
||||
void (*fn)(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* jsonDoc),
|
||||
yyjson_mut_doc* jsonDoc
|
||||
)
|
||||
{
|
||||
@@ -91,10 +102,13 @@ static void parseStructureCommand(
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
if (ffStrEqualsIgnCase(line, baseInfo->name))
|
||||
{
|
||||
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
|
||||
baseInfo->initOptions(optionBuf);
|
||||
if (__builtin_expect(jsonDoc != NULL, false))
|
||||
fn(baseInfo, jsonDoc);
|
||||
fn(baseInfo, optionBuf, jsonDoc);
|
||||
else
|
||||
baseInfo->printModule(baseInfo);
|
||||
baseInfo->printModule(optionBuf);
|
||||
baseInfo->destroyOptions(optionBuf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ typedef struct FFdata
|
||||
bool configLoaded;
|
||||
} FFdata;
|
||||
|
||||
bool ffParseModuleOptions(const char* key, const char* value);
|
||||
void ffPrepareCommandOption(FFdata* data);
|
||||
void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc);
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data, yyjson_mut_doc* jsonDoc);
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "duration.h"
|
||||
|
||||
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result)
|
||||
{
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
|
||||
const char* space = instance.config.display.durationSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER ? " " : "";
|
||||
|
||||
if(totalSeconds < 60)
|
||||
{
|
||||
ffStrbufAppendF(result, options->durationAbbreviation ? "%u%ssec" : "%u%ssecond", (unsigned) totalSeconds, space);
|
||||
if (totalSeconds != 1)
|
||||
ffStrbufAppendC(result, 's');
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t seconds = (uint32_t) (totalSeconds % 60);
|
||||
totalSeconds /= 60;
|
||||
if (seconds >= 30)
|
||||
totalSeconds++;
|
||||
|
||||
uint32_t minutes = (uint32_t) (totalSeconds % 60);
|
||||
totalSeconds /= 60;
|
||||
uint32_t hours = (uint32_t) (totalSeconds % 24);
|
||||
totalSeconds /= 24;
|
||||
uint32_t days = (uint32_t) totalSeconds;
|
||||
|
||||
if(days > 0)
|
||||
{
|
||||
if(options->durationAbbreviation)
|
||||
{
|
||||
ffStrbufAppendF(result, "%u%sd", days, space);
|
||||
|
||||
if(hours > 0 || minutes > 0)
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufAppendF(result, "%u%sday", days, space);
|
||||
|
||||
if(days > 1)
|
||||
ffStrbufAppendC(result, 's');
|
||||
|
||||
if(days >= 100)
|
||||
ffStrbufAppendS(result, "(!)");
|
||||
|
||||
if(hours > 0 || minutes > 0)
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
if(hours > 0)
|
||||
{
|
||||
if(options->durationAbbreviation)
|
||||
{
|
||||
ffStrbufAppendF(result, "%u%sh", hours, space);
|
||||
|
||||
if (minutes > 0)
|
||||
ffStrbufAppendC(result, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufAppendF(result, "%u%shour", hours, space);
|
||||
|
||||
if(hours > 1)
|
||||
ffStrbufAppendC(result, 's');
|
||||
|
||||
if(minutes > 0)
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
if(minutes > 0)
|
||||
{
|
||||
if(options->durationAbbreviation)
|
||||
{
|
||||
ffStrbufAppendF(result, "%u%sm", minutes, space);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufAppendF(result, "%u%smin", minutes, space);
|
||||
|
||||
if(minutes > 1)
|
||||
ffStrbufAppendC(result, 's');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result);
|
||||
+10
-3
@@ -32,10 +32,16 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
|
||||
ffStrbufAppend(buffer, (FFstrbuf*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_FLOAT:
|
||||
ffStrbufAppendF(buffer, "%f", *(float*)formatarg->value);
|
||||
if (instance.config.display.fractionNdigits >= 0)
|
||||
ffStrbufAppendF(buffer, "%.*f", instance.config.display.fractionNdigits, *(float*)formatarg->value);
|
||||
else
|
||||
ffStrbufAppendF(buffer, "%g", *(float*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_DOUBLE:
|
||||
ffStrbufAppendF(buffer, "%g", *(double*)formatarg->value);
|
||||
if (instance.config.display.fractionNdigits >= 0)
|
||||
ffStrbufAppendF(buffer, "%.*f", instance.config.display.fractionNdigits, *(double*)formatarg->value);
|
||||
else
|
||||
ffStrbufAppendF(buffer, "%g", *(double*)formatarg->value);
|
||||
break;
|
||||
case FF_FORMAT_ARG_TYPE_BOOL:
|
||||
ffStrbufAppendS(buffer, *(bool*)formatarg->value ? "true" : "false");
|
||||
@@ -107,7 +113,8 @@ static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start,
|
||||
static inline bool formatArgSet(const FFformatarg* arg)
|
||||
{
|
||||
return arg->value != NULL && (
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) || //Also is false for NaN
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_INT && *(int*)arg->value > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
|
||||
(arg->type == FF_FORMAT_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) ||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "frequency.h"
|
||||
|
||||
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result)
|
||||
{
|
||||
if (mhz == 0)
|
||||
return false;
|
||||
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
const char* space = options->freqSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_NEVER ? "" : " ";
|
||||
int8_t ndigits = options->freqNdigits;
|
||||
|
||||
if (ndigits >= 0)
|
||||
ffStrbufAppendF(result, "%.*f%sGHz", ndigits, mhz / 1000., space);
|
||||
else
|
||||
ffStrbufAppendF(result, "%u%sMHz", (unsigned) mhz, space);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result);
|
||||
+2
-8
@@ -25,6 +25,7 @@ static void initState(FFstate* state)
|
||||
state->logoHeight = 0;
|
||||
state->keysHeight = 0;
|
||||
state->terminalLightTheme = false;
|
||||
state->titleFqdn = false;
|
||||
|
||||
ffPlatformInit(&state->platform);
|
||||
state->configDoc = NULL;
|
||||
@@ -42,14 +43,13 @@ static void defaultConfig(void)
|
||||
{
|
||||
ffOptionsInitLogo(&instance.config.logo);
|
||||
ffOptionsInitGeneral(&instance.config.general);
|
||||
ffOptionsInitModules(&instance.config.modules);
|
||||
ffOptionsInitDisplay(&instance.config.display);
|
||||
}
|
||||
|
||||
void ffInitInstance(void)
|
||||
{
|
||||
#ifdef WIN32
|
||||
//https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendations&view=msvc-170#utf-8-support
|
||||
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendat>
|
||||
setlocale(LC_ALL, ".UTF8");
|
||||
#else
|
||||
// Never use `setlocale(LC_ALL, "")`
|
||||
@@ -96,11 +96,6 @@ static void chldSignalHandler(FF_MAYBE_UNUSED int signal)
|
||||
|
||||
void ffStart(void)
|
||||
{
|
||||
#ifdef FF_START_DETECTION_THREADS
|
||||
if(instance.config.general.multithreading)
|
||||
startDetectionThreads();
|
||||
#endif
|
||||
|
||||
ffDisableLinewrap = instance.config.display.disableLinewrap && !instance.config.display.pipe && !instance.state.resultDoc;
|
||||
ffHideCursor = instance.config.display.hideCursor && !instance.config.display.pipe && !instance.state.resultDoc;
|
||||
|
||||
@@ -150,7 +145,6 @@ static void destroyConfig(void)
|
||||
{
|
||||
ffOptionsDestroyLogo(&instance.config.logo);
|
||||
ffOptionsDestroyGeneral(&instance.config.general);
|
||||
ffOptionsDestroyModules(&instance.config.modules);
|
||||
ffOptionsDestroyDisplay(&instance.config.display);
|
||||
}
|
||||
|
||||
|
||||
+21
-1
@@ -31,6 +31,7 @@ static inline FFNativeFD FFUnixFD2NativeFD(int unixfd)
|
||||
#endif
|
||||
}
|
||||
|
||||
FF_C_NONNULL(3)
|
||||
static inline bool ffWriteFDData(FFNativeFD fd, size_t dataSize, const void* data)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
@@ -41,18 +42,22 @@ static inline bool ffWriteFDData(FFNativeFD fd, size_t dataSize, const void* dat
|
||||
#endif
|
||||
}
|
||||
|
||||
FF_C_NONNULL(2)
|
||||
static inline bool ffWriteFDBuffer(FFNativeFD fd, const FFstrbuf* content)
|
||||
{
|
||||
return ffWriteFDData(fd, content->length, content->chars);
|
||||
}
|
||||
|
||||
FF_C_NONNULL(1, 3)
|
||||
bool ffWriteFileData(const char* fileName, size_t dataSize, const void* data);
|
||||
|
||||
FF_C_NONNULL(1, 2)
|
||||
static inline bool ffWriteFileBuffer(const char* fileName, const FFstrbuf* buffer)
|
||||
{
|
||||
return ffWriteFileData(fileName, buffer->length, buffer->chars);
|
||||
}
|
||||
|
||||
FF_C_NONNULL(3)
|
||||
static inline ssize_t ffReadFDData(FFNativeFD fd, size_t dataSize, void* data)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
@@ -66,26 +71,32 @@ static inline ssize_t ffReadFDData(FFNativeFD fd, size_t dataSize, void* data)
|
||||
#endif
|
||||
}
|
||||
|
||||
FF_C_NONNULL(1, 3)
|
||||
ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data);
|
||||
FF_C_NONNULL(2, 4)
|
||||
ssize_t ffReadFileDataRelative(FFNativeFD dfd, const char* fileName, size_t dataSize, void* data);
|
||||
|
||||
FF_C_NONNULL(2)
|
||||
bool ffAppendFDBuffer(FFNativeFD fd, FFstrbuf* buffer);
|
||||
FF_C_NONNULL(1, 2)
|
||||
bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer);
|
||||
FF_C_NONNULL(2, 3)
|
||||
bool ffAppendFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer);
|
||||
|
||||
FF_C_NONNULL(1, 2)
|
||||
static inline bool ffReadFileBuffer(const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
ffStrbufClear(buffer);
|
||||
return ffAppendFileBuffer(fileName, buffer);
|
||||
}
|
||||
|
||||
FF_C_NONNULL(2, 3)
|
||||
static inline bool ffReadFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer)
|
||||
{
|
||||
ffStrbufClear(buffer);
|
||||
return ffAppendFileBufferRelative(dfd, fileName, buffer);
|
||||
}
|
||||
|
||||
//Bit flags, combine with |
|
||||
typedef enum __attribute__((__packed__)) FFPathType
|
||||
{
|
||||
FF_PATHTYPE_FILE = 1 << 0,
|
||||
@@ -94,6 +105,7 @@ typedef enum __attribute__((__packed__)) FFPathType
|
||||
FF_PATHTYPE_FORCE_UNSIGNED = UINT8_MAX,
|
||||
} FFPathType;
|
||||
|
||||
FF_C_NONNULL(1)
|
||||
static inline bool ffPathExists(const char* path, FFPathType pathType)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@@ -136,11 +148,13 @@ static inline bool ffPathExists(const char* path, FFPathType pathType)
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_C_NONNULL(1, 2)
|
||||
bool ffPathExpandEnv(const char* in, FFstrbuf* out);
|
||||
|
||||
#define FF_IO_TERM_RESP_WAIT_MS 100 // #554
|
||||
|
||||
FF_C_SCANF(3, 4)
|
||||
FF_C_NONNULL(1, 3)
|
||||
const char* ffGetTerminalResponse(const char* request, int nParams, const char* format, ...);
|
||||
|
||||
// Not thread safe!
|
||||
@@ -157,6 +171,7 @@ static inline void ffUnsuppressIO(bool* suppressed)
|
||||
|
||||
void ffListFilesRecursively(const char* path, bool pretty);
|
||||
|
||||
FF_C_NONNULL(1)
|
||||
static inline bool wrapClose(FFNativeFD* pfd)
|
||||
{
|
||||
assert(pfd);
|
||||
@@ -176,6 +191,7 @@ static inline bool wrapClose(FFNativeFD* pfd)
|
||||
}
|
||||
#define FF_AUTO_CLOSE_FD __attribute__((__cleanup__(wrapClose)))
|
||||
|
||||
FF_C_NONNULL(1)
|
||||
static inline bool wrapFclose(FILE** pfile)
|
||||
{
|
||||
assert(pfile);
|
||||
@@ -186,6 +202,7 @@ static inline bool wrapFclose(FILE** pfile)
|
||||
}
|
||||
#define FF_AUTO_CLOSE_FILE __attribute__((__cleanup__(wrapFclose)))
|
||||
|
||||
FF_C_NONNULL(1)
|
||||
#ifndef _WIN32
|
||||
static inline bool wrapClosedir(DIR** pdir)
|
||||
{
|
||||
@@ -207,6 +224,7 @@ static inline bool wrapClosedir(HANDLE* pdir)
|
||||
#endif
|
||||
#define FF_AUTO_CLOSE_DIR __attribute__((__cleanup__(wrapClosedir)))
|
||||
|
||||
FF_C_NONNULL(1, 2, 3)
|
||||
static inline bool ffSearchUserConfigFile(const FFlist* configDirs, const char* fileSubpath, FFstrbuf* result)
|
||||
{
|
||||
// configDirs is a list of FFstrbufs include the trailing slash
|
||||
@@ -222,6 +240,8 @@ static inline bool ffSearchUserConfigFile(const FFlist* configDirs, const char*
|
||||
return false;
|
||||
}
|
||||
|
||||
FFNativeFD ffGetNullFD(void);
|
||||
|
||||
#ifdef _WIN32
|
||||
// Only O_RDONLY is supported
|
||||
HANDLE openat(HANDLE dfd, const char* fileName, bool directory);
|
||||
|
||||
@@ -358,3 +358,12 @@ void ffListFilesRecursively(const char* path, bool pretty)
|
||||
ffStrbufEnsureEndsWithC(&folder, '/');
|
||||
listFilesRecursively(folder.length, &folder, 0, NULL, pretty);
|
||||
}
|
||||
|
||||
FFNativeFD ffGetNullFD(void)
|
||||
{
|
||||
static FFNativeFD hNullFile = -1;
|
||||
if (hNullFile != -1)
|
||||
return hNullFile;
|
||||
hNullFile = open("/dev/null", O_WRONLY | O_CLOEXEC);
|
||||
return hNullFile;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ bool ffSuppressIO(bool suppress)
|
||||
static bool init = false;
|
||||
static HANDLE hOrigOut = INVALID_HANDLE_VALUE;
|
||||
static HANDLE hOrigErr = INVALID_HANDLE_VALUE;
|
||||
static HANDLE hNullFile = INVALID_HANDLE_VALUE;
|
||||
HANDLE hNullFile = ffGetNullFD();
|
||||
static int fOrigOut = -1;
|
||||
static int fOrigErr = -1;
|
||||
static int fNullFile = -1;
|
||||
@@ -187,7 +187,6 @@ bool ffSuppressIO(bool suppress)
|
||||
|
||||
hOrigOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
hOrigErr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
hNullFile = CreateFileW(L"NUL", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, NULL);
|
||||
fOrigOut = _dup(STDOUT_FILENO);
|
||||
fOrigErr = _dup(STDERR_FILENO);
|
||||
fNullFile = _open_osfhandle((intptr_t) hNullFile, 0);
|
||||
@@ -352,3 +351,23 @@ const char* ffGetTerminalResponse(const char* request, int nParams, const char*
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FFNativeFD ffGetNullFD(void)
|
||||
{
|
||||
static FFNativeFD hNullFile = INVALID_HANDLE_VALUE;
|
||||
if (hNullFile != INVALID_HANDLE_VALUE)
|
||||
return hNullFile;
|
||||
hNullFile = CreateFileW(
|
||||
L"NUL",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
&(SECURITY_ATTRIBUTES){
|
||||
.nLength = sizeof(SECURITY_ATTRIBUTES),
|
||||
.lpSecurityDescriptor = NULL,
|
||||
.bInheritHandle = TRUE,
|
||||
});
|
||||
return hNullFile;
|
||||
}
|
||||
|
||||
+88
-30
@@ -4,6 +4,7 @@
|
||||
#include "common/printing.h"
|
||||
#include "common/io/io.h"
|
||||
#include "common/time.h"
|
||||
#include "detection/version/version.h"
|
||||
#include "modules/modules.h"
|
||||
#include "util/stringUtils.h"
|
||||
|
||||
@@ -11,52 +12,57 @@
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
bool ffJsonConfigParseModuleArgs(const char* key, yyjson_val* val, FFModuleArgs* moduleArgs)
|
||||
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs)
|
||||
{
|
||||
if(ffStrEqualsIgnCase(key, "key"))
|
||||
if (unsafe_yyjson_equals_str(key, "type") || unsafe_yyjson_equals_str(key, "condition"))
|
||||
return true;
|
||||
|
||||
if (unsafe_yyjson_equals_str(key, "key"))
|
||||
{
|
||||
ffStrbufSetNS(&moduleArgs->key, (uint32_t) yyjson_get_len(val), yyjson_get_str(val));
|
||||
ffStrbufSetJsonVal(&moduleArgs->key, val);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(key, "format"))
|
||||
else if (unsafe_yyjson_equals_str(key, "format"))
|
||||
{
|
||||
ffStrbufSetNS(&moduleArgs->outputFormat, (uint32_t) yyjson_get_len(val), yyjson_get_str(val));
|
||||
ffStrbufSetJsonVal(&moduleArgs->outputFormat, val);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(key, "outputColor"))
|
||||
else if (unsafe_yyjson_equals_str(key, "outputColor"))
|
||||
{
|
||||
ffOptionParseColor(yyjson_get_str(val), &moduleArgs->outputColor);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(key, "keyColor"))
|
||||
else if (unsafe_yyjson_equals_str(key, "keyColor"))
|
||||
{
|
||||
ffOptionParseColor(yyjson_get_str(val), &moduleArgs->keyColor);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(key, "keyWidth"))
|
||||
else if (unsafe_yyjson_equals_str(key, "keyWidth"))
|
||||
{
|
||||
moduleArgs->keyWidth = (uint32_t) yyjson_get_uint(val);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(key, "keyIcon"))
|
||||
else if (unsafe_yyjson_equals_str(key, "keyIcon"))
|
||||
{
|
||||
ffStrbufSetS(&moduleArgs->keyIcon, yyjson_get_str(val));
|
||||
ffStrbufSetJsonVal(&moduleArgs->keyIcon, val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* defaultModuleArgs, FFModuleArgs* moduleArgs)
|
||||
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs)
|
||||
{
|
||||
if (!ffStrbufEqual(&defaultModuleArgs->key, &moduleArgs->key))
|
||||
if (moduleArgs->key.length > 0)
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "key", &moduleArgs->key);
|
||||
if (!ffStrbufEqual(&defaultModuleArgs->outputFormat, &moduleArgs->outputFormat))
|
||||
if (moduleArgs->outputFormat.length > 0)
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "format", &moduleArgs->outputFormat);
|
||||
if (!ffStrbufEqual(&defaultModuleArgs->keyColor, &moduleArgs->keyColor))
|
||||
if (moduleArgs->outputColor.length > 0)
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "outputColor", &moduleArgs->outputColor);
|
||||
if (moduleArgs->keyColor.length > 0)
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "keyColor", &moduleArgs->keyColor);
|
||||
if (moduleArgs->keyWidth != defaultModuleArgs->keyWidth)
|
||||
if (moduleArgs->keyWidth > 0)
|
||||
yyjson_mut_obj_add_uint(doc, module, "keyWidth", moduleArgs->keyWidth);
|
||||
if (!ffStrbufEqual(&defaultModuleArgs->keyIcon, &moduleArgs->keyIcon))
|
||||
if (moduleArgs->keyIcon.length > 0)
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "keyIcon", &moduleArgs->keyIcon);
|
||||
}
|
||||
|
||||
@@ -95,12 +101,12 @@ const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair p
|
||||
return "Invalid enum value type; must be a string or integer";
|
||||
}
|
||||
|
||||
static inline void genJsonResult(FFModuleBaseInfo* baseInfo, yyjson_mut_doc* doc)
|
||||
static inline void genJsonResult(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
|
||||
{
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
|
||||
yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name);
|
||||
if (baseInfo->generateJsonResult)
|
||||
baseInfo->generateJsonResult(baseInfo, doc, module);
|
||||
baseInfo->generateJsonResult(options, doc, module);
|
||||
else
|
||||
yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format");
|
||||
}
|
||||
@@ -114,11 +120,14 @@ static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal, yyjson_
|
||||
FFModuleBaseInfo* baseInfo = *modules;
|
||||
if (ffStrEqualsIgnCase(type, baseInfo->name))
|
||||
{
|
||||
if (jsonVal) baseInfo->parseJsonObject(baseInfo, jsonVal);
|
||||
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
|
||||
baseInfo->initOptions(optionBuf);
|
||||
if (jsonVal) baseInfo->parseJsonObject(optionBuf, jsonVal);
|
||||
if (__builtin_expect(jsonDoc != NULL, false))
|
||||
genJsonResult(baseInfo, jsonDoc);
|
||||
genJsonResult(baseInfo, optionBuf, jsonDoc);
|
||||
else
|
||||
baseInfo->printModule(baseInfo);
|
||||
baseInfo->printModule(optionBuf);
|
||||
baseInfo->destroyOptions(optionBuf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +136,6 @@ static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal, yyjson_
|
||||
|
||||
static void prepareModuleJsonObject(const char* type, yyjson_val* module)
|
||||
{
|
||||
FFconfig* cfg = &instance.config;
|
||||
switch (type[0])
|
||||
{
|
||||
case 'b': case 'B': {
|
||||
@@ -138,38 +146,65 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module)
|
||||
case 'd': case 'D': {
|
||||
if (ffStrEqualsIgnCase(type, FF_DISKIO_MODULE_NAME))
|
||||
{
|
||||
if (module) cfg->modules.diskIo.moduleInfo.parseJsonObject(&cfg->modules.diskIo, module);
|
||||
ffPrepareDiskIO(&cfg->modules.diskIo);
|
||||
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
|
||||
ffInitDiskIOOptions(&options);
|
||||
if (module) ffDiskIOModuleInfo.parseJsonObject(&options, module);
|
||||
ffPrepareDiskIO(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'n': case 'N': {
|
||||
if (ffStrEqualsIgnCase(type, FF_NETIO_MODULE_NAME))
|
||||
{
|
||||
if (module) cfg->modules.netIo.moduleInfo.parseJsonObject(&cfg->modules.netIo, module);
|
||||
ffPrepareNetIO(&cfg->modules.netIo);
|
||||
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
|
||||
ffInitNetIOOptions(&options);
|
||||
if (module) ffNetIOModuleInfo.parseJsonObject(&options, module);
|
||||
ffPrepareNetIO(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p': case 'P': {
|
||||
if (ffStrEqualsIgnCase(type, FF_PUBLICIP_MODULE_NAME))
|
||||
{
|
||||
if (module) cfg->modules.publicIP.moduleInfo.parseJsonObject(&cfg->modules.publicIP, module);
|
||||
ffPreparePublicIp(&cfg->modules.publicIP);
|
||||
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
|
||||
ffInitPublicIpOptions(&options);
|
||||
if (module) ffPublicIPModuleInfo.parseJsonObject(&options, module);
|
||||
ffPreparePublicIp(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'w': case 'W': {
|
||||
if (ffStrEqualsIgnCase(type, FF_WEATHER_MODULE_NAME))
|
||||
{
|
||||
if (module) cfg->modules.weather.moduleInfo.parseJsonObject(&cfg->modules.weather, module);
|
||||
ffPrepareWeather(&cfg->modules.weather);
|
||||
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
|
||||
ffInitWeatherOptions(&options);
|
||||
if (module) ffWeatherModuleInfo.parseJsonObject(&options, module);
|
||||
ffPrepareWeather(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool matchesJsonArray(const char* str, yyjson_val* val)
|
||||
{
|
||||
assert(val);
|
||||
|
||||
if (unsafe_yyjson_is_str(val))
|
||||
return ffStrEqualsIgnCase(str, unsafe_yyjson_get_str(val));
|
||||
|
||||
if (!unsafe_yyjson_is_arr(val)) return false;
|
||||
|
||||
size_t idx, max;
|
||||
yyjson_val* item;
|
||||
yyjson_arr_foreach(val, idx, max, item)
|
||||
{
|
||||
if (yyjson_is_str(item) && ffStrEqualsIgnCase(str, unsafe_yyjson_get_str(item)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
|
||||
{
|
||||
yyjson_val* const root = yyjson_doc_get_root(instance.state.configDoc);
|
||||
@@ -197,6 +232,29 @@ static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
|
||||
module = NULL;
|
||||
else if (yyjson_is_obj(module))
|
||||
{
|
||||
yyjson_val* conditions = yyjson_obj_get(module, "condition");
|
||||
if (conditions)
|
||||
{
|
||||
if (!yyjson_is_obj(conditions))
|
||||
return "Property 'conditions' must be an object";
|
||||
|
||||
yyjson_val* system = yyjson_obj_get(conditions, "system");
|
||||
if (system && !matchesJsonArray(ffVersionResult.sysName, system))
|
||||
continue;
|
||||
|
||||
system = yyjson_obj_get(conditions, "!system");
|
||||
if (system && matchesJsonArray(ffVersionResult.sysName, system))
|
||||
continue;
|
||||
|
||||
yyjson_val* arch = yyjson_obj_get(conditions, "arch");
|
||||
if (arch && !matchesJsonArray(ffVersionResult.architecture, arch))
|
||||
continue;
|
||||
|
||||
arch = yyjson_obj_get(conditions, "!arch");
|
||||
if (arch && matchesJsonArray(ffVersionResult.architecture, arch))
|
||||
continue;
|
||||
}
|
||||
|
||||
type = yyjson_get_str(yyjson_obj_get(module, "type"));
|
||||
if (!type) return "module object must contain a \"type\" key ( case sensitive )";
|
||||
if (yyjson_obj_size(module) == 1) // contains only Property type
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
bool ffJsonConfigParseModuleArgs(const char* key, yyjson_val* val, FFModuleArgs* moduleArgs);
|
||||
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs);
|
||||
const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair pairs[]);
|
||||
void ffPrintJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc);
|
||||
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* defaultModuleArgs, FFModuleArgs* moduleArgs);
|
||||
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs);
|
||||
|
||||
yyjson_api_inline yyjson_mut_val* yyjson_mut_strbuf(yyjson_mut_doc *doc, const FFstrbuf* buf) {
|
||||
return yyjson_mut_strncpy(doc, buf->chars, buf->length);
|
||||
|
||||
@@ -74,7 +74,7 @@ void* ffLibraryLoad(const char* path, int maxVersion, ...);
|
||||
FF_MAYBE_UNUSED void* libraryObjectName = NULL; // Placeholder
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, symbolMapping, symbolName, returnValue) \
|
||||
symbolMapping = (__typeof__(&symbolName)) &symbolName
|
||||
symbolMapping = (__typeof__(&symbolName)) &symbolName;
|
||||
|
||||
#define FF_LIBRARY_LOAD_SYMBOL(library, symbolName, returnValue) \
|
||||
FF_MAYBE_UNUSED __typeof__(&symbolName) FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, ff ## symbolName, symbolName, returnValue);
|
||||
|
||||
+74
-74
@@ -1,69 +1,69 @@
|
||||
#include "fastfetch.h"
|
||||
#include "modules/modules.h"
|
||||
|
||||
static FFModuleBaseInfo* A[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* B[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.battery,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.bios,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.bluetooth,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.bluetoothRadio,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.board,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.bootmgr,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.break_,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.brightness,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.btrfs,
|
||||
&ffBatteryModuleInfo,
|
||||
&ffBiosModuleInfo,
|
||||
&ffBluetoothModuleInfo,
|
||||
&ffBluetoothRadioModuleInfo,
|
||||
&ffBoardModuleInfo,
|
||||
&ffBootmgrModuleInfo,
|
||||
&ffBreakModuleInfo,
|
||||
&ffBrightnessModuleInfo,
|
||||
&ffBtrfsModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* C[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.camera,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.chassis,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.command,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.colors,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.cpu,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.cpuCache,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.cpuUsage,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.cursor,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.custom,
|
||||
&ffCameraModuleInfo,
|
||||
&ffChassisModuleInfo,
|
||||
&ffCommandModuleInfo,
|
||||
&ffColorsModuleInfo,
|
||||
&ffCPUModuleInfo,
|
||||
&ffCPUCacheModuleInfo,
|
||||
&ffCPUUsageModuleInfo,
|
||||
&ffCursorModuleInfo,
|
||||
&ffCustomModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* D[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.dateTime,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.de,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.display,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.disk,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.diskIo,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.dns,
|
||||
&ffDateTimeModuleInfo,
|
||||
&ffDEModuleInfo,
|
||||
&ffDisplayModuleInfo,
|
||||
&ffDiskModuleInfo,
|
||||
&ffDiskIOModuleInfo,
|
||||
&ffDNSModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* E[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.editor,
|
||||
&ffEditorModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* F[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.font,
|
||||
&ffFontModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* G[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.gamepad,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.gpu,
|
||||
&ffGamepadModuleInfo,
|
||||
&ffGPUModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* H[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.host,
|
||||
&ffHostModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* I[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.icons,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.initSystem,
|
||||
&ffIconsModuleInfo,
|
||||
&ffInitSystemModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -72,47 +72,47 @@ static FFModuleBaseInfo* J[] = {
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* K[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.kernel,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.keyboard,
|
||||
&ffKernelModuleInfo,
|
||||
&ffKeyboardModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* L[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.lm,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.loadavg,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.locale,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.localIP,
|
||||
&ffLMModuleInfo,
|
||||
&ffLoadavgModuleInfo,
|
||||
&ffLocaleModuleInfo,
|
||||
&ffLocalIPModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* M[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.media,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.memory,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.monitor,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.mouse,
|
||||
&ffMediaModuleInfo,
|
||||
&ffMemoryModuleInfo,
|
||||
&ffMonitorModuleInfo,
|
||||
&ffMouseModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* N[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.netIo,
|
||||
&ffNetIOModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* O[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.openCL,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.openGL,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.os,
|
||||
&ffOpenCLModuleInfo,
|
||||
&ffOpenGLModuleInfo,
|
||||
&ffOSModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* P[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.packages,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.physicalDisk,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.physicalMemory,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.player,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.powerAdapter,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.processes,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.publicIP,
|
||||
&ffPackagesModuleInfo,
|
||||
&ffPhysicalDiskModuleInfo,
|
||||
&ffPhysicalMemoryModuleInfo,
|
||||
&ffPlayerModuleInfo,
|
||||
&ffPowerAdapterModuleInfo,
|
||||
&ffProcessesModuleInfo,
|
||||
&ffPublicIPModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -125,42 +125,42 @@ static FFModuleBaseInfo* R[] = {
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* S[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.separator,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.shell,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.sound,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.swap,
|
||||
&ffSeparatorModuleInfo,
|
||||
&ffShellModuleInfo,
|
||||
&ffSoundModuleInfo,
|
||||
&ffSwapModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* T[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.terminal,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.terminalFont,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.terminalSize,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.terminalTheme,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.title,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.theme,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.tpm,
|
||||
&ffTerminalModuleInfo,
|
||||
&ffTerminalFontModuleInfo,
|
||||
&ffTerminalSizeModuleInfo,
|
||||
&ffTerminalThemeModuleInfo,
|
||||
&ffTitleModuleInfo,
|
||||
&ffThemeModuleInfo,
|
||||
&ffTPMModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* U[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.uptime,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.users,
|
||||
&ffUptimeModuleInfo,
|
||||
&ffUsersModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* V[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.version,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.vulkan,
|
||||
&ffVersionModuleInfo,
|
||||
&ffVulkanModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* W[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.wallpaper,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.weather,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.wm,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.wifi,
|
||||
(FFModuleBaseInfo*) &instance.config.modules.wmTheme,
|
||||
&ffWallpaperModuleInfo,
|
||||
&ffWeatherModuleInfo,
|
||||
&ffWMModuleInfo,
|
||||
&ffWifiModuleInfo,
|
||||
&ffWMThemeModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -173,7 +173,7 @@ static FFModuleBaseInfo* Y[] = {
|
||||
};
|
||||
|
||||
static FFModuleBaseInfo* Z[] = {
|
||||
(FFModuleBaseInfo*) &instance.config.modules.zpool,
|
||||
&ffZpoolModuleInfo,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
+13
-19
@@ -2,29 +2,23 @@
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <net/if.h>
|
||||
#else
|
||||
#define IF_NAMESIZE 0
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex);
|
||||
enum { IF_INDEX_UNINITIALIZED = (uint32_t) -1, IF_INDEX_INVALID = (uint32_t) -2 };
|
||||
static uint32_t ifIndex = IF_INDEX_UNINITIALIZED;
|
||||
static char ifName[IF_NAMESIZE + 1];
|
||||
|
||||
static inline void init()
|
||||
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV4(void)
|
||||
{
|
||||
if (ifIndex == (uint32_t) IF_INDEX_UNINITIALIZED && !ffNetifGetDefaultRouteImpl(ifName, &ifIndex))
|
||||
ifIndex = (uint32_t) IF_INDEX_INVALID;
|
||||
static FFNetifDefaultRouteResult result;
|
||||
if (result.status == FF_NETIF_UNINITIALIZED) {
|
||||
result.status = ffNetifGetDefaultRouteImplV4(&result) ? FF_NETIF_OK : FF_NETIF_INVALID;
|
||||
}
|
||||
return &result;
|
||||
}
|
||||
|
||||
const char* ffNetifGetDefaultRouteIfName()
|
||||
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV6(void)
|
||||
{
|
||||
init();
|
||||
return ifName;
|
||||
}
|
||||
|
||||
uint32_t ffNetifGetDefaultRouteIfIndex()
|
||||
{
|
||||
init();
|
||||
return ifIndex;
|
||||
static FFNetifDefaultRouteResult result;
|
||||
if (result.status == FF_NETIF_UNINITIALIZED) {
|
||||
result.status = ffNetifGetDefaultRouteImplV6(&result) ? FF_NETIF_OK : FF_NETIF_INVALID;
|
||||
}
|
||||
return &result;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,28 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
const char* ffNetifGetDefaultRouteIfName();
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
uint32_t ffNetifGetDefaultRouteIfIndex();
|
||||
typedef enum __attribute__((__packed__)) FFNetifDefaultRouteResultStatus {
|
||||
FF_NETIF_UNINITIALIZED,
|
||||
FF_NETIF_INVALID,
|
||||
FF_NETIF_OK
|
||||
} FFNetifDefaultRouteResultStatus;
|
||||
|
||||
typedef struct FFNetifDefaultRouteResult {
|
||||
uint32_t ifIndex;
|
||||
|
||||
#ifndef _WIN32
|
||||
char ifName[IF_NAMESIZE + 1];
|
||||
uint32_t preferredSourceAddrV4;
|
||||
#endif
|
||||
enum FFNetifDefaultRouteResultStatus status;
|
||||
} FFNetifDefaultRouteResult;
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result);
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result);
|
||||
|
||||
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV4(void);
|
||||
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV6(void);
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
#include "netif.h"
|
||||
|
||||
#include "common/io/io.h"
|
||||
#include "util/mallocHelper.h"
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n))
|
||||
|
||||
#if __APPLE__
|
||||
// https://github.com/apple-oss-distributions/network_cmds/blob/8f38231438e6a4d16ef8015e97e12c2c05105644/rtsol.tproj/if.c#L243
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(uint32_t))
|
||||
#elif __sun
|
||||
// https://github.com/illumos/illumos-gate/blob/95b8c88950fa7b19af46bc63230137cf96b0bff7/usr/src/cmd/cmd-inet/usr.sbin/route.c#L339
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#else
|
||||
#error unknown platform
|
||||
#endif
|
||||
|
||||
static struct sockaddr *
|
||||
get_rt_address(struct rt_msghdr *rtm, int desired)
|
||||
{
|
||||
struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
|
||||
|
||||
for (int i = 0; i < RTAX_MAX; i++)
|
||||
{
|
||||
if (rtm->rtm_addrs & (1 << i))
|
||||
{
|
||||
if ((1 << i) == desired)
|
||||
return sa;
|
||||
|
||||
#ifndef __sun
|
||||
uint32_t salen = sa->sa_len;
|
||||
#else
|
||||
uint32_t salen;
|
||||
// https://github.com/illumos/illumos-gate/blob/95b8c88950fa7b19af46bc63230137cf96b0bff7/usr/src/cmd/cmd-inet/usr.sbin/route.c#L2941
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
salen = sizeof (struct sockaddr_in);
|
||||
break;
|
||||
case AF_LINK:
|
||||
salen = sizeof (struct sockaddr_dl);
|
||||
break;
|
||||
case AF_INET6:
|
||||
salen = sizeof (struct sockaddr_in6);
|
||||
break;
|
||||
default:
|
||||
salen = sizeof (struct sockaddr);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
sa = (struct sockaddr *)(ROUNDUP(salen) + (char *)sa);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
//https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
|
||||
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET);
|
||||
if (pfRoute < 0)
|
||||
return false;
|
||||
|
||||
{
|
||||
struct timeval timeout = {1, 0};
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
int pid = getpid();
|
||||
|
||||
struct {
|
||||
struct rt_msghdr hdr;
|
||||
struct sockaddr_in dst;
|
||||
uint8_t data[512];
|
||||
} rtmsg = {
|
||||
.hdr = {
|
||||
.rtm_type = RTM_GET,
|
||||
.rtm_flags = RTF_UP | RTF_GATEWAY,
|
||||
.rtm_version = RTM_VERSION,
|
||||
.rtm_addrs = RTA_DST | RTA_IFP | RTA_IFA,
|
||||
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
|
||||
.rtm_pid = pid,
|
||||
.rtm_seq = 1,
|
||||
},
|
||||
.dst = {
|
||||
.sin_family = AF_INET,
|
||||
#ifndef __sun
|
||||
.sin_len = sizeof(rtmsg.dst),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
|
||||
return false;
|
||||
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid))
|
||||
;
|
||||
|
||||
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
|
||||
if ((rtmsg.hdr.rtm_flags & (RTF_UP | RTF_GATEWAY)) == (RTF_UP | RTF_GATEWAY))
|
||||
#endif
|
||||
{
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP);
|
||||
if (sdl
|
||||
#ifndef __sun
|
||||
&& sdl->sdl_len
|
||||
#endif
|
||||
)
|
||||
{
|
||||
assert(sdl->sdl_nlen <= IF_NAMESIZE);
|
||||
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
|
||||
result->ifName[sdl->sdl_nlen] = '\0';
|
||||
result->ifIndex = sdl->sdl_index;
|
||||
|
||||
// Get the preferred source address
|
||||
struct sockaddr_in* src = (struct sockaddr_in*)get_rt_address(&rtmsg.hdr, RTA_IFA);
|
||||
if (src && src->sin_family == AF_INET)
|
||||
result->preferredSourceAddrV4 = src->sin_addr.s_addr;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
//https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
|
||||
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET6);
|
||||
if (pfRoute < 0)
|
||||
return false;
|
||||
|
||||
{
|
||||
struct timeval timeout = {1, 0};
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
int pid = getpid();
|
||||
|
||||
struct {
|
||||
struct rt_msghdr hdr;
|
||||
struct sockaddr_in6 dst;
|
||||
uint8_t data[512];
|
||||
} rtmsg = {
|
||||
.hdr = {
|
||||
.rtm_type = RTM_GET,
|
||||
.rtm_flags = RTF_UP | RTF_GATEWAY,
|
||||
.rtm_version = RTM_VERSION,
|
||||
.rtm_addrs = RTA_DST | RTA_IFP,
|
||||
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
|
||||
.rtm_pid = pid,
|
||||
.rtm_seq = 2,
|
||||
},
|
||||
.dst = {
|
||||
.sin6_family = AF_INET6,
|
||||
#ifndef __sun
|
||||
.sin6_len = sizeof(rtmsg.dst),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
|
||||
return false;
|
||||
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == pid))
|
||||
;
|
||||
|
||||
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
|
||||
if ((rtmsg.hdr.rtm_flags & (RTF_UP | RTF_GATEWAY)) == (RTF_UP | RTF_GATEWAY))
|
||||
#endif
|
||||
{
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP);
|
||||
if (sdl
|
||||
#ifndef __sun
|
||||
&& sdl->sdl_len
|
||||
#endif
|
||||
)
|
||||
{
|
||||
assert(sdl->sdl_nlen <= IF_NAMESIZE);
|
||||
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
|
||||
result->ifName[sdl->sdl_nlen] = '\0';
|
||||
result->ifIndex = sdl->sdl_index;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -8,25 +8,24 @@
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#if defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n))
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define ROUNDUP(a) ROUNDUP2((a), sizeof(int))
|
||||
#elif defined(__NetBSD__)
|
||||
# define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t))
|
||||
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
# define ROUNDUP(a) ROUNDUP2((a), sizeof(int))
|
||||
#elif defined(__OpenBSD__)
|
||||
# define ROUNDUP(a) ROUNDUP2((a), sizeof(int))
|
||||
#elif defined(__sun)
|
||||
# define ROUNDUP(a) ROUNDUP2((a), _SS_ALIGNSIZE)
|
||||
#if __DragonFly__
|
||||
// https://github.com/DragonFlyBSD/DragonFlyBSD/blob/cf0aa2f1e47a3f0a6055fe427563cb3f3e627064/sys/net/route.h#L315C9-L315C19
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#elif __FreeBSD__
|
||||
// https://github.com/freebsd/freebsd-src/blob/e4c0ecba44b20ebb2e4d80978c2cb6d16b730cb9/sys/net/route.h#L368C9-L368C16
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#elif __NetBSD__
|
||||
// https://github.com/NetBSD/src/blob/29beb637d057520c0ed37ac2cde966f7cc0cadf4/sys/net/route.h#L330
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t))
|
||||
#elif __OpenBSD__
|
||||
// https://github.com/openbsd/src/blob/ca647cfa4ec3ccb8360714bc0ebc32a394f7fb6a/regress/sys/netinet/bindconnect/bindconnect.c#L250
|
||||
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
|
||||
#else
|
||||
# error unknown platform
|
||||
#error unknown platform
|
||||
#endif
|
||||
|
||||
static struct sockaddr *
|
||||
@@ -40,23 +39,18 @@ get_rt_address(struct rt_msghdr *rtm, int desired)
|
||||
{
|
||||
if ((1 << i) == desired)
|
||||
return sa;
|
||||
#ifdef __sun
|
||||
sa = (struct sockaddr *)(ROUNDUP(sizeof(struct sockaddr)) + (char *)sa);
|
||||
#else
|
||||
sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
#if defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
int mib[6] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY};
|
||||
size_t needed;
|
||||
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0)
|
||||
return false;
|
||||
|
||||
FF_AUTO_FREE char* buf = malloc(needed);
|
||||
@@ -71,80 +65,62 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
|
||||
rtm = (struct rt_msghdr *)next;
|
||||
struct sockaddr* sa = (struct sockaddr *)(rtm + 1);
|
||||
|
||||
if ((rtm->rtm_flags & RTF_GATEWAY) && (sa->sa_family == AF_INET))
|
||||
if ((rtm->rtm_flags & RTF_GATEWAY) && !(rtm->rtm_flags & RTF_REJECT) && (sa->sa_family == AF_INET))
|
||||
{
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP);
|
||||
if (sdl->sdl_family == AF_LINK)
|
||||
if (sdl && sdl->sdl_family == AF_LINK)
|
||||
{
|
||||
assert(sdl->sdl_nlen <= IF_NAMESIZE);
|
||||
memcpy(iface, sdl->sdl_data, sdl->sdl_nlen);
|
||||
iface[sdl->sdl_nlen] = '\0';
|
||||
*ifIndex = sdl->sdl_index;
|
||||
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
|
||||
result->ifName[sdl->sdl_nlen] = '\0';
|
||||
result->ifIndex = sdl->sdl_index;
|
||||
|
||||
// Get the preferred source address
|
||||
struct sockaddr_in* src = (struct sockaddr_in*)get_rt_address(rtm, RTA_IFA);
|
||||
if (src && src->sin_family == AF_INET)
|
||||
result->preferredSourceAddrV4 = src->sin_addr.s_addr;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
//https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET);
|
||||
if (pfRoute < 0)
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
int mib[6] = {CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_FLAGS, RTF_GATEWAY};
|
||||
size_t needed;
|
||||
|
||||
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0)
|
||||
return false;
|
||||
|
||||
{
|
||||
struct timeval timeout = {1, 0};
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
}
|
||||
FF_AUTO_FREE char* buf = malloc(needed);
|
||||
|
||||
int pid = getpid();
|
||||
|
||||
struct {
|
||||
struct rt_msghdr hdr;
|
||||
struct sockaddr_in dst;
|
||||
uint8_t data[512];
|
||||
} rtmsg = {
|
||||
.hdr = {
|
||||
.rtm_type = RTM_GET,
|
||||
.rtm_flags = RTF_UP | RTF_GATEWAY,
|
||||
.rtm_version = RTM_VERSION,
|
||||
.rtm_addrs = RTA_DST | RTA_IFP,
|
||||
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
|
||||
.rtm_pid = pid,
|
||||
.rtm_seq = 1,
|
||||
},
|
||||
.dst = {
|
||||
.sin_family = AF_INET,
|
||||
#ifndef __sun
|
||||
.sin_len = sizeof(rtmsg.dst),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
if (write(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen) != rtmsg.hdr.rtm_msglen)
|
||||
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
|
||||
return false;
|
||||
|
||||
while (read(pfRoute, &rtmsg, sizeof(rtmsg)) > 0)
|
||||
char* lim = buf + needed;
|
||||
struct rt_msghdr* rtm;
|
||||
for (char* next = buf; next < lim; next += rtm->rtm_msglen)
|
||||
{
|
||||
if (rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid)
|
||||
rtm = (struct rt_msghdr *)next;
|
||||
struct sockaddr* sa = (struct sockaddr *)(rtm + 1);
|
||||
|
||||
if ((rtm->rtm_flags & RTF_GATEWAY) && !(rtm->rtm_flags & RTF_REJECT) && (sa->sa_family == AF_INET6))
|
||||
{
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP);
|
||||
if (sdl
|
||||
#ifndef __sun
|
||||
&& sdl->sdl_len
|
||||
#endif
|
||||
)
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP);
|
||||
if (sdl && sdl->sdl_family == AF_LINK)
|
||||
{
|
||||
assert(sdl->sdl_nlen <= IF_NAMESIZE);
|
||||
memcpy(iface, sdl->sdl_data, sdl->sdl_nlen);
|
||||
iface[sdl->sdl_nlen] = '\0';
|
||||
*ifIndex = sdl->sdl_index;
|
||||
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
|
||||
result->ifName[sdl->sdl_nlen] = '\0';
|
||||
result->ifIndex = sdl->sdl_index;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
|
||||
// loosely based on Haiku's src/bin/network/route/route.cpp
|
||||
|
||||
bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
// TODO: AF_INET6
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(AF_INET, SOCK_RAW, AF_INET);
|
||||
if (pfRoute < 0)
|
||||
return false;
|
||||
@@ -42,8 +41,11 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
|
||||
|
||||
while (interface < end) {
|
||||
if (interface->ifr_route.flags & RTF_DEFAULT) {
|
||||
strlcpy(iface, interface->ifr_name, IF_NAMESIZE);
|
||||
*ifIndex = if_nametoindex(interface->ifr_name);
|
||||
// interface->ifr_metric?
|
||||
strlcpy(result->ifName, interface->ifr_name, IF_NAMESIZE);
|
||||
result->ifIndex = if_nametoindex(interface->ifr_name);
|
||||
if (interface->ifr_route.source)
|
||||
result->preferredSourceAddrV4 = ((struct sockaddr_in*)interface->ifr_route.source)->sin_addr.s_addr;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -55,9 +57,56 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
|
||||
if (interface->ifr_route.gateway != NULL)
|
||||
addressSize += interface->ifr_route.gateway->sa_len;
|
||||
|
||||
interface = (struct ifreq*)((addr_t)interface + IF_NAMESIZE
|
||||
+ sizeof(struct route_entry) + addressSize);
|
||||
interface = (struct ifreq*)((addr_t)interface + IF_NAMESIZE + sizeof(struct route_entry) + addressSize);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD int pfRoute = socket(AF_INET, SOCK_RAW, AF_INET6);
|
||||
if (pfRoute < 0)
|
||||
return false;
|
||||
|
||||
struct ifconf config;
|
||||
config.ifc_len = sizeof(config.ifc_value);
|
||||
if (ioctl(pfRoute, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0)
|
||||
return false;
|
||||
|
||||
int size = config.ifc_value;
|
||||
if (size <= 0)
|
||||
return false;
|
||||
|
||||
FF_AUTO_FREE void *buffer = malloc((size_t) size);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config.ifc_len = size;
|
||||
config.ifc_buf = buffer;
|
||||
if (ioctl(pfRoute, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0)
|
||||
return false;
|
||||
|
||||
struct ifreq *interface = (struct ifreq*)buffer;
|
||||
struct ifreq *end = (struct ifreq*)((uint8_t*)buffer + size);
|
||||
|
||||
while (interface < end) {
|
||||
if (interface->ifr_route.flags & RTF_DEFAULT) {
|
||||
strlcpy(result->ifName, interface->ifr_name, IF_NAMESIZE);
|
||||
result->ifIndex = if_nametoindex(interface->ifr_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t addressSize = 0;
|
||||
if (interface->ifr_route.destination != NULL)
|
||||
addressSize += interface->ifr_route.destination->sa_len;
|
||||
if (interface->ifr_route.mask != NULL)
|
||||
addressSize += interface->ifr_route.mask->sa_len;
|
||||
if (interface->ifr_route.gateway != NULL)
|
||||
addressSize += interface->ifr_route.gateway->sa_len;
|
||||
|
||||
interface = (struct ifreq*)((addr_t)interface + IF_NAMESIZE + sizeof(struct route_entry) + addressSize);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+318
-28
@@ -1,52 +1,342 @@
|
||||
#include "netif.h"
|
||||
#include "common/io/io.h"
|
||||
#include "util/mallocHelper.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/if.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define FF_STR_INDIR(x) #x
|
||||
#define FF_STR(x) FF_STR_INDIR(x)
|
||||
|
||||
static bool getDefaultRouteIPv4(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/net/route", "r");
|
||||
if (!netRoute) return false;
|
||||
FF_AUTO_CLOSE_FD int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (sock_fd < 0)
|
||||
return false;
|
||||
|
||||
// skip first line
|
||||
FF_UNUSED(fscanf(netRoute, "%*[^\n]\n"));
|
||||
unsigned pid = (unsigned) getpid();
|
||||
|
||||
unsigned long long destination; //, gateway, flags, refCount, use, metric, mask, mtu, ...
|
||||
while (fscanf(netRoute, "%" FF_STR(IF_NAMESIZE) "s%llx%*[^\n]", iface, &destination) == 2)
|
||||
// Bind socket
|
||||
struct sockaddr_nl addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pid = pid,
|
||||
.nl_groups = 0, // No multicast groups
|
||||
};
|
||||
|
||||
if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
struct rtmsg rtm;
|
||||
struct rtattr rta;
|
||||
uint32_t table;
|
||||
} req = {
|
||||
// Netlink message header
|
||||
.nlh = {
|
||||
.nlmsg_len = sizeof(req),
|
||||
.nlmsg_type = RTM_GETROUTE,
|
||||
.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
|
||||
.nlmsg_seq = 1,
|
||||
.nlmsg_pid = pid,
|
||||
},
|
||||
// Route message
|
||||
.rtm = {
|
||||
.rtm_family = AF_INET,
|
||||
.rtm_dst_len = 0, // Match all destinations
|
||||
.rtm_src_len = 0, // Match all sources
|
||||
.rtm_tos = 0,
|
||||
.rtm_table = RT_TABLE_UNSPEC,
|
||||
.rtm_protocol = RTPROT_UNSPEC,
|
||||
.rtm_scope = RT_SCOPE_UNIVERSE,
|
||||
.rtm_type = RTN_UNSPEC,
|
||||
.rtm_flags = 0,
|
||||
},
|
||||
// Route attribute for main table
|
||||
.rta = {
|
||||
.rta_len = RTA_LENGTH(sizeof(uint32_t)),
|
||||
.rta_type = RTA_TABLE,
|
||||
},
|
||||
.table = RT_TABLE_MAIN,
|
||||
};
|
||||
|
||||
struct sockaddr_nl dest_addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pid = 0, // Kernel
|
||||
.nl_groups = 0, // No multicast groups
|
||||
};
|
||||
|
||||
ssize_t sent = sendto(sock_fd, &req, sizeof(req), 0,
|
||||
(struct sockaddr*)&dest_addr, sizeof(dest_addr));
|
||||
|
||||
if (sent != sizeof(req)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_nl src_addr = {};
|
||||
socklen_t src_addr_len = sizeof(src_addr);
|
||||
|
||||
struct iovec iov = {};
|
||||
struct msghdr msg = {
|
||||
.msg_name = &src_addr,
|
||||
.msg_namelen = sizeof(src_addr),
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
|
||||
ssize_t peek_size = recvmsg(sock_fd, &msg, MSG_PEEK | MSG_TRUNC);
|
||||
if (peek_size < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_AUTO_FREE uint8_t* buffer = malloc((size_t)peek_size);
|
||||
|
||||
ssize_t received = recvfrom(sock_fd, buffer, (size_t)peek_size, 0,
|
||||
(struct sockaddr*)&src_addr, &src_addr_len);
|
||||
if (received != peek_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct {
|
||||
uint32_t metric;
|
||||
uint32_t ifindex;
|
||||
uint32_t prefsrc;
|
||||
} entry;
|
||||
uint32_t minMetric = UINT32_MAX;
|
||||
|
||||
for (const struct nlmsghdr* nlh = (struct nlmsghdr*)buffer;
|
||||
NLMSG_OK(nlh, received);
|
||||
nlh = NLMSG_NEXT(nlh, received)) {
|
||||
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid)
|
||||
continue;
|
||||
if (nlh->nlmsg_type == NLMSG_DONE)
|
||||
break;
|
||||
|
||||
if (nlh->nlmsg_type != RTM_NEWROUTE)
|
||||
continue;
|
||||
|
||||
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nlh);
|
||||
if (rtm->rtm_family != AF_INET)
|
||||
continue;
|
||||
|
||||
if (rtm->rtm_dst_len != 0)
|
||||
continue;
|
||||
|
||||
entry = (__typeof__(entry)) { .metric = UINT32_MAX };
|
||||
|
||||
// Parse route attributes
|
||||
size_t rtm_len = RTM_PAYLOAD(nlh);
|
||||
for (struct rtattr* rta = RTM_RTA(rtm);
|
||||
RTA_OK(rta, rtm_len);
|
||||
rta = RTA_NEXT(rta, rtm_len))
|
||||
{
|
||||
if (RTA_PAYLOAD(rta) < sizeof(uint32_t))
|
||||
continue; // Skip invalid attributes
|
||||
|
||||
uint32_t rta_data = *(uint32_t*) RTA_DATA(rta);
|
||||
switch (rta->rta_type) {
|
||||
case RTA_DST:
|
||||
if (rta_data != 0) goto next;
|
||||
break;
|
||||
case RTA_OIF:
|
||||
entry.ifindex = rta_data;
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
if (rta_data == 0) goto next;
|
||||
break;
|
||||
case RTA_PRIORITY:
|
||||
if (rta_data >= minMetric) goto next;
|
||||
entry.metric = rta_data;
|
||||
break;
|
||||
case RTA_PREFSRC:
|
||||
entry.prefsrc = rta_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.metric >= minMetric)
|
||||
{
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
minMetric = entry.metric;
|
||||
result->ifIndex = entry.ifindex;
|
||||
result->preferredSourceAddrV4 = entry.prefsrc;
|
||||
}
|
||||
|
||||
if (minMetric < UINT32_MAX)
|
||||
{
|
||||
if (destination != 0) continue;
|
||||
*ifIndex = if_nametoindex(iface);
|
||||
if_indextoname(result->ifIndex, result->ifName);
|
||||
return true;
|
||||
}
|
||||
iface[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getDefaultRouteIPv6(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/net/ipv6_route", "r");
|
||||
if (!netRoute) return false;
|
||||
FF_AUTO_CLOSE_FD int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
|
||||
if (sock_fd < 0)
|
||||
return false;
|
||||
|
||||
uint32_t prefixLen;
|
||||
//destination, dest_prefix_len, source, src_prefix_len, next hop, metric, ref counter, use counter, flags, iface
|
||||
while (fscanf(netRoute, "%*s %x %*s %*s %*s %*s %*s %*s %*s %" FF_STR(IF_NAMESIZE) "s", &prefixLen, iface) == 2)
|
||||
unsigned pid = (unsigned) getpid();
|
||||
|
||||
// Bind socket
|
||||
struct sockaddr_nl addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pid = pid,
|
||||
.nl_groups = 0, // No multicast groups
|
||||
};
|
||||
|
||||
if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct {
|
||||
struct nlmsghdr nlh;
|
||||
struct rtmsg rtm;
|
||||
struct rtattr rta;
|
||||
uint32_t table;
|
||||
} req = {
|
||||
// Netlink message header
|
||||
.nlh = {
|
||||
.nlmsg_len = sizeof(req),
|
||||
.nlmsg_type = RTM_GETROUTE,
|
||||
.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
|
||||
.nlmsg_seq = 1,
|
||||
.nlmsg_pid = pid,
|
||||
},
|
||||
// Route message
|
||||
.rtm = {
|
||||
.rtm_family = AF_INET6, // IPv6 instead of IPv4
|
||||
.rtm_dst_len = 0, // Match all destinations
|
||||
.rtm_src_len = 0, // Match all sources
|
||||
.rtm_tos = 0,
|
||||
.rtm_table = RT_TABLE_UNSPEC,
|
||||
.rtm_protocol = RTPROT_UNSPEC,
|
||||
.rtm_scope = RT_SCOPE_UNIVERSE,
|
||||
.rtm_type = RTN_UNSPEC,
|
||||
.rtm_flags = 0,
|
||||
},
|
||||
// Route attribute for main table
|
||||
.rta = {
|
||||
.rta_len = RTA_LENGTH(sizeof(uint32_t)),
|
||||
.rta_type = RTA_TABLE,
|
||||
},
|
||||
.table = RT_TABLE_MAIN,
|
||||
};
|
||||
|
||||
struct sockaddr_nl dest_addr = {
|
||||
.nl_family = AF_NETLINK,
|
||||
.nl_pid = 0, // Kernel
|
||||
.nl_groups = 0, // No multicast groups
|
||||
};
|
||||
|
||||
ssize_t sent = sendto(sock_fd, &req, sizeof(req), 0,
|
||||
(struct sockaddr*)&dest_addr, sizeof(dest_addr));
|
||||
|
||||
if (sent != sizeof(req)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_nl src_addr = {};
|
||||
socklen_t src_addr_len = sizeof(src_addr);
|
||||
|
||||
struct iovec iov = {};
|
||||
struct msghdr msg = {
|
||||
.msg_name = &src_addr,
|
||||
.msg_namelen = sizeof(src_addr),
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
|
||||
ssize_t peek_size = recvmsg(sock_fd, &msg, MSG_PEEK | MSG_TRUNC);
|
||||
if (peek_size < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_AUTO_FREE uint8_t* buffer = malloc((size_t)peek_size);
|
||||
|
||||
ssize_t received = recvfrom(sock_fd, buffer, (size_t)peek_size, 0,
|
||||
(struct sockaddr*)&src_addr, &src_addr_len);
|
||||
if (received != peek_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct {
|
||||
uint32_t metric;
|
||||
uint32_t ifindex;
|
||||
} entry;
|
||||
uint32_t minMetric = UINT32_MAX;
|
||||
|
||||
for (const struct nlmsghdr* nlh = (struct nlmsghdr*)buffer;
|
||||
NLMSG_OK(nlh, received);
|
||||
nlh = NLMSG_NEXT(nlh, received)) {
|
||||
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid)
|
||||
continue;
|
||||
if (nlh->nlmsg_type == NLMSG_DONE)
|
||||
break;
|
||||
|
||||
if (nlh->nlmsg_type != RTM_NEWROUTE)
|
||||
continue;
|
||||
|
||||
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nlh);
|
||||
if (rtm->rtm_family != AF_INET6)
|
||||
continue;
|
||||
|
||||
if (rtm->rtm_dst_len != 0)
|
||||
continue;
|
||||
|
||||
entry = (__typeof__(entry)) { .metric = UINT32_MAX };
|
||||
|
||||
// Parse route attributes
|
||||
size_t rtm_len = RTM_PAYLOAD(nlh);
|
||||
for (struct rtattr* rta = RTM_RTA(rtm);
|
||||
RTA_OK(rta, rtm_len);
|
||||
rta = RTA_NEXT(rta, rtm_len))
|
||||
{
|
||||
switch (rta->rta_type) {
|
||||
case RTA_DST:
|
||||
if (RTA_PAYLOAD(rta) >= sizeof(struct in6_addr)) {
|
||||
struct in6_addr* dst = (struct in6_addr*) RTA_DATA(rta);
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(dst)) goto next;
|
||||
}
|
||||
break;
|
||||
case RTA_OIF:
|
||||
if (RTA_PAYLOAD(rta) >= sizeof(uint32_t)) {
|
||||
entry.ifindex = *(uint32_t*) RTA_DATA(rta);
|
||||
}
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
if (RTA_PAYLOAD(rta) >= sizeof(struct in6_addr)) {
|
||||
struct in6_addr* gw = (struct in6_addr*) RTA_DATA(rta);
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(gw)) goto next;
|
||||
}
|
||||
break;
|
||||
case RTA_PRIORITY:
|
||||
if (RTA_PAYLOAD(rta) >= sizeof(uint32_t)) {
|
||||
uint32_t metric = *(uint32_t*) RTA_DATA(rta);
|
||||
if (metric >= minMetric) goto next;
|
||||
entry.metric = metric;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.metric >= minMetric)
|
||||
{
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
minMetric = entry.metric;
|
||||
result->ifIndex = entry.ifindex;
|
||||
}
|
||||
|
||||
if (minMetric < UINT32_MAX)
|
||||
{
|
||||
if (prefixLen != 0) continue;
|
||||
*ifIndex = if_nametoindex(iface);
|
||||
if_indextoname(result->ifIndex, result->ifName);
|
||||
return true;
|
||||
}
|
||||
iface[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
|
||||
{
|
||||
if (getDefaultRouteIPv4(iface, ifIndex))
|
||||
return true;
|
||||
|
||||
return getDefaultRouteIPv6(iface, ifIndex);
|
||||
}
|
||||
|
||||
@@ -4,29 +4,96 @@
|
||||
#include <ws2tcpip.h> // AF_INET6, IN6_IS_ADDR_UNSPECIFIED
|
||||
#include <iphlpapi.h>
|
||||
|
||||
bool ffNetifGetDefaultRouteImpl(FF_MAYBE_UNUSED char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
|
||||
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL;
|
||||
DWORD result = GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable);
|
||||
|
||||
if (result != NO_ERROR)
|
||||
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable)))
|
||||
return false;
|
||||
|
||||
bool foundDefault = false;
|
||||
uint32_t smallestMetric = UINT32_MAX;
|
||||
|
||||
for (ULONG i = 0; i < pIpForwardTable->NumEntries; ++i)
|
||||
{
|
||||
MIB_IPFORWARD_ROW2* row = &pIpForwardTable->Table[i];
|
||||
|
||||
if ((row->DestinationPrefix.PrefixLength == 0) &&
|
||||
((row->DestinationPrefix.Prefix.Ipv4.sin_family == AF_INET &&
|
||||
row->DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr == 0) ||
|
||||
(row->DestinationPrefix.Prefix.Ipv6.sin6_family == AF_INET6 &&
|
||||
IN6_IS_ADDR_UNSPECIFIED(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr))))
|
||||
if (row->DestinationPrefix.PrefixLength == 0 &&
|
||||
row->DestinationPrefix.Prefix.Ipv4.sin_family == AF_INET &&
|
||||
row->DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr == 0)
|
||||
{
|
||||
*ifIndex = row->InterfaceIndex;
|
||||
foundDefault = true;
|
||||
break;
|
||||
MIB_IF_ROW2 ifRow = {
|
||||
.InterfaceIndex = row->InterfaceIndex,
|
||||
};
|
||||
if (NETIO_SUCCESS(GetIfEntry2(&ifRow)) && ifRow.OperStatus == IfOperStatusUp)
|
||||
{
|
||||
MIB_IPINTERFACE_ROW ipInterfaceRow = {
|
||||
.Family = AF_INET,
|
||||
.InterfaceIndex = row->InterfaceIndex,
|
||||
};
|
||||
|
||||
uint32_t realMetric = row->Metric /* Metric offset */;
|
||||
|
||||
if (NETIO_SUCCESS(GetIpInterfaceEntry(&ipInterfaceRow)))
|
||||
realMetric += ipInterfaceRow.Metric /* Interface metric */;
|
||||
|
||||
if (realMetric < smallestMetric)
|
||||
{
|
||||
smallestMetric = realMetric;
|
||||
result->ifIndex = row->InterfaceIndex;
|
||||
foundDefault = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FreeMibTable(pIpForwardTable);
|
||||
|
||||
return foundDefault;
|
||||
}
|
||||
|
||||
|
||||
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
{
|
||||
PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL;
|
||||
|
||||
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable)))
|
||||
return false;
|
||||
|
||||
bool foundDefault = false;
|
||||
uint32_t smallestMetric = UINT32_MAX;
|
||||
|
||||
for (ULONG i = 0; i < pIpForwardTable->NumEntries; ++i)
|
||||
{
|
||||
MIB_IPFORWARD_ROW2* row = &pIpForwardTable->Table[i];
|
||||
|
||||
if (row->DestinationPrefix.PrefixLength == 0 &&
|
||||
row->DestinationPrefix.Prefix.Ipv6.sin6_family == AF_INET6 &&
|
||||
IN6_IS_ADDR_UNSPECIFIED(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr))
|
||||
{
|
||||
MIB_IF_ROW2 ifRow = {
|
||||
.InterfaceIndex = row->InterfaceIndex,
|
||||
};
|
||||
if (NETIO_SUCCESS(GetIfEntry2(&ifRow)) && ifRow.OperStatus == IfOperStatusUp)
|
||||
{
|
||||
MIB_IPINTERFACE_ROW ipInterfaceRow = {
|
||||
.Family = AF_INET6,
|
||||
.InterfaceIndex = row->InterfaceIndex,
|
||||
};
|
||||
|
||||
uint32_t realMetric = row->Metric /* Metric offset */;
|
||||
|
||||
if (NETIO_SUCCESS(GetIpInterfaceEntry(&ipInterfaceRow)))
|
||||
realMetric += ipInterfaceRow.Metric /* Interface metric */;
|
||||
|
||||
if (realMetric < smallestMetric)
|
||||
{
|
||||
smallestMetric = realMetric;
|
||||
result->ifIndex = row->InterfaceIndex;
|
||||
foundDefault = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "util/debug.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/poll.h>
|
||||
#include <poll.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
@@ -346,7 +346,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
|
||||
FF_DEBUG("Multithreading mode enabled, creating connection thread");
|
||||
state->thread = ffThreadCreate(connectAndSendThreadMain, state);
|
||||
if (state->thread) {
|
||||
FF_DEBUG("Thread creation successful: thread=%p", (void*)state->thread);
|
||||
FF_DEBUG("Thread creation successful: thread=%p", (void*)(uintptr_t)state->thread);
|
||||
return NULL;
|
||||
}
|
||||
FF_DEBUG("Thread creation failed");
|
||||
|
||||
+2
-49
@@ -28,55 +28,6 @@ const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName)
|
||||
return subKey;
|
||||
}
|
||||
|
||||
bool ffOptionParseModuleArgs(const char* argumentKey, const char* subKey, const char* value, FFModuleArgs* result)
|
||||
{
|
||||
if(ffStrEqualsIgnCase(subKey, "key"))
|
||||
{
|
||||
ffOptionParseString(argumentKey, value, &result->key);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(subKey, "format"))
|
||||
{
|
||||
ffOptionParseString(argumentKey, value, &result->outputFormat);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(subKey, "output-color"))
|
||||
{
|
||||
if(value == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error: usage: %s <str>\n", argumentKey);
|
||||
exit(477);
|
||||
}
|
||||
ffOptionParseColor(value, &result->outputColor);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrStartsWithIgnCase(subKey, "key-"))
|
||||
{
|
||||
const char* subKey2 = subKey + strlen("key-");
|
||||
if(ffStrEqualsIgnCase(subKey2, "color"))
|
||||
{
|
||||
if(value == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error: usage: %s <str>\n", argumentKey);
|
||||
exit(477);
|
||||
}
|
||||
ffOptionParseColor(value, &result->keyColor);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(subKey2, "width"))
|
||||
{
|
||||
result->keyWidth = ffOptionParseUInt32(argumentKey, value);
|
||||
return true;
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(subKey2, "icon"))
|
||||
{
|
||||
ffOptionParseString(argumentKey, value, &result->keyIcon);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* buffer)
|
||||
{
|
||||
if(value == NULL)
|
||||
@@ -157,6 +108,8 @@ bool ffOptionParseBoolean(const char* str)
|
||||
|
||||
void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer)
|
||||
{
|
||||
if (!value || value[0] == '\0') return;
|
||||
|
||||
// If value is already an ANSI escape code, use it
|
||||
if (value[0] == '\e' && value[1] == '[')
|
||||
{
|
||||
|
||||
+4
-2
@@ -29,7 +29,8 @@ typedef struct FFModuleBaseInfo
|
||||
// This is UB, because `void*` is not compatible with `FF*Options*`.
|
||||
// However we can't do it better unless we move to C++, so that `option` becomes a `this` pointer
|
||||
// https://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type
|
||||
bool (*parseCommandOptions)(void* options, const char* key, const char* value);
|
||||
void (*initOptions)(void* options);
|
||||
void (*destroyOptions)(void* options);
|
||||
void (*parseJsonObject)(void* options, struct yyjson_val *module);
|
||||
void (*printModule)(void* options);
|
||||
void (*generateJsonResult)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* module);
|
||||
@@ -63,7 +64,6 @@ typedef struct FFKeyValuePair
|
||||
} FFKeyValuePair;
|
||||
|
||||
const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName);
|
||||
bool ffOptionParseModuleArgs(const char* argumentKey, const char* pkey, const char* value, FFModuleArgs* result);
|
||||
void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* buffer);
|
||||
FF_C_NODISCARD uint32_t ffOptionParseUInt32(const char* argumentKey, const char* value);
|
||||
FF_C_NODISCARD int32_t ffOptionParseInt32(const char* argumentKey, const char* value);
|
||||
@@ -94,3 +94,5 @@ static inline void ffOptionDestroyModuleArg(FFModuleArgs* args)
|
||||
ffStrbufDestroy(&args->outputFormat);
|
||||
ffStrbufDestroy(&args->outputColor);
|
||||
}
|
||||
|
||||
enum { FF_OPTION_MAX_SIZE = 1 << 8 }; // Maximum size of a single option value, used for static allocation
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "common/parsing.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma GCC diagnostic push
|
||||
@@ -60,56 +59,6 @@ void ffVersionToPretty(const FFVersion* version, FFstrbuf* pretty)
|
||||
ffStrbufAppendF(pretty, ".%u", version->patch);
|
||||
}
|
||||
|
||||
static void parseSize(FFstrbuf* result, uint64_t bytes, uint32_t base, const char** prefixes)
|
||||
{
|
||||
double size = (double) bytes;
|
||||
uint8_t counter = 0;
|
||||
|
||||
while(size >= base && counter < instance.config.display.sizeMaxPrefix && prefixes[counter + 1])
|
||||
{
|
||||
size /= base;
|
||||
counter++;
|
||||
}
|
||||
|
||||
if(counter == 0)
|
||||
ffStrbufAppendF(result, "%" PRIu64 " %s", bytes, prefixes[0]);
|
||||
else
|
||||
ffStrbufAppendF(result, "%.*f %s", instance.config.display.sizeNdigits, size, prefixes[counter]);
|
||||
}
|
||||
|
||||
void ffParseSize(uint64_t bytes, FFstrbuf* result)
|
||||
{
|
||||
switch (instance.config.display.sizeBinaryPrefix)
|
||||
{
|
||||
case FF_SIZE_BINARY_PREFIX_TYPE_IEC:
|
||||
parseSize(result, bytes, 1024, (const char*[]) {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", NULL});
|
||||
break;
|
||||
case FF_SIZE_BINARY_PREFIX_TYPE_SI:
|
||||
parseSize(result, bytes, 1000, (const char*[]) {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL});
|
||||
break;
|
||||
case FF_SIZE_BINARY_PREFIX_TYPE_JEDEC:
|
||||
parseSize(result, bytes, 1024, (const char*[]) {"B", "KB", "MB", "GB", "TB", NULL});
|
||||
break;
|
||||
default:
|
||||
parseSize(result, bytes, 1024, (const char*[]) {"B", NULL});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ffParseFrequency(uint32_t mhz, FFstrbuf* result)
|
||||
{
|
||||
if (mhz == 0)
|
||||
return false;
|
||||
|
||||
int8_t ndigits = instance.config.display.freqNdigits;
|
||||
|
||||
if (ndigits >= 0)
|
||||
ffStrbufAppendF(result, "%.*f GHz", ndigits, mhz / 1000.);
|
||||
else
|
||||
ffStrbufAppendF(result, "%u MHz", (unsigned) mhz);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, const FFstrbuf* gtk4)
|
||||
{
|
||||
if(gtk2->length > 0 && gtk3->length > 0 && gtk4->length > 0)
|
||||
@@ -190,61 +139,6 @@ void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, co
|
||||
}
|
||||
}
|
||||
|
||||
void ffParseDuration(uint64_t totalSeconds, FFstrbuf* result)
|
||||
{
|
||||
if(totalSeconds < 60)
|
||||
{
|
||||
ffStrbufAppendF(result, "%u second", (unsigned) totalSeconds);
|
||||
if (totalSeconds != 1)
|
||||
ffStrbufAppendC(result, 's');
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t seconds = (uint32_t) (totalSeconds % 60);
|
||||
totalSeconds /= 60;
|
||||
if (seconds >= 30)
|
||||
totalSeconds++;
|
||||
|
||||
uint32_t minutes = (uint32_t) (totalSeconds % 60);
|
||||
totalSeconds /= 60;
|
||||
uint32_t hours = (uint32_t) (totalSeconds % 24);
|
||||
totalSeconds /= 24;
|
||||
uint32_t days = (uint32_t) totalSeconds;
|
||||
|
||||
if(days > 0)
|
||||
{
|
||||
ffStrbufAppendF(result, "%u day", days);
|
||||
|
||||
if(days > 1)
|
||||
ffStrbufAppendC(result, 's');
|
||||
|
||||
if(days >= 100)
|
||||
ffStrbufAppendS(result, "(!)");
|
||||
|
||||
if(hours > 0 || minutes > 0)
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
|
||||
if(hours > 0)
|
||||
{
|
||||
ffStrbufAppendF(result, "%u hour", hours);
|
||||
|
||||
if(hours > 1)
|
||||
ffStrbufAppendC(result, 's');
|
||||
|
||||
if(minutes > 0)
|
||||
ffStrbufAppendS(result, ", ");
|
||||
}
|
||||
|
||||
if(minutes > 0)
|
||||
{
|
||||
ffStrbufAppendF(result, "%u min", minutes);
|
||||
|
||||
if(minutes > 1)
|
||||
ffStrbufAppendC(result, 's');
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
@@ -24,7 +24,3 @@ void ffParseGTK(FFstrbuf* buffer, const FFstrbuf* gtk2, const FFstrbuf* gtk3, co
|
||||
|
||||
void ffVersionToPretty(const FFVersion* version, FFstrbuf* pretty);
|
||||
int8_t ffVersionCompare(const FFVersion* version1, const FFVersion* version2);
|
||||
|
||||
void ffParseSize(uint64_t bytes, FFstrbuf* result);
|
||||
bool ffParseFrequency(uint32_t mhz, FFstrbuf* result);
|
||||
void ffParseDuration(uint64_t totalSeconds, FFstrbuf* result);
|
||||
|
||||
+54
-38
@@ -60,23 +60,32 @@ void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConf
|
||||
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
|
||||
uint32_t blocksPercent = (uint32_t) (percent / 100.0 * options->barWidth + 0.5);
|
||||
const bool borderAsValue = options->barBorderLeftElapsed.length && options->barBorderRightElapsed.length;
|
||||
|
||||
uint8_t blocksPercent = (uint8_t) (percent / 100.0 * options->barWidth + 0.5);
|
||||
assert(blocksPercent <= options->barWidth);
|
||||
|
||||
if(options->barBorderLeft.length)
|
||||
if(!borderAsValue && options->barBorderLeft.length)
|
||||
{
|
||||
if(!options->pipe)
|
||||
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_WHITE "m");
|
||||
if(!options->pipe && options->barColorBorder.length > 0)
|
||||
ffStrbufAppendF(buffer, "\e[%sm", options->barColorBorder.chars);
|
||||
ffStrbufAppend(buffer, &options->barBorderLeft);
|
||||
}
|
||||
|
||||
if (percent != percent)
|
||||
if (percent == -DBL_MAX)
|
||||
{
|
||||
if(!options->pipe)
|
||||
// Use total color for simplification
|
||||
if(!options->pipe && options->barColorTotal.length > 0)
|
||||
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_BLACK "m");
|
||||
|
||||
for (uint32_t i = 0; i < options->barWidth; ++i)
|
||||
ffStrbufAppend(buffer, &options->barCharElapsed);
|
||||
for (uint8_t i = 0; i < options->barWidth; ++i)
|
||||
{
|
||||
ffStrbufAppend(buffer, borderAsValue && i == 0
|
||||
? &options->barBorderLeft
|
||||
: borderAsValue && i == options->barWidth - 1
|
||||
? &options->barBorderRight
|
||||
: &options->barCharTotal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -85,16 +94,19 @@ void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConf
|
||||
const char* colorRed = options->percentColorRed.chars;
|
||||
|
||||
FFPercentageTypeFlags percentType = config.type == 0 ? options->percentType : config.type;
|
||||
bool monochrome = !!(percentType & FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT);
|
||||
|
||||
for (uint32_t i = 0; i < blocksPercent; ++i)
|
||||
bool autoColorElapsed = ffStrbufIgnCaseEqualS(&options->barColorElapsed, "auto");
|
||||
|
||||
for (uint8_t i = 0; i < blocksPercent; ++i)
|
||||
{
|
||||
if(!options->pipe)
|
||||
if(!options->pipe && options->barColorElapsed.length > 0)
|
||||
{
|
||||
if (monochrome)
|
||||
if ((percentType & FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT) || !autoColorElapsed)
|
||||
{
|
||||
const char* color = NULL;
|
||||
if (green <= yellow)
|
||||
if (!autoColorElapsed)
|
||||
color = options->barColorElapsed.chars;
|
||||
else if (green <= yellow)
|
||||
{
|
||||
if (percent < green) color = colorGreen;
|
||||
else if (percent < yellow) color = colorYellow;
|
||||
@@ -120,26 +132,36 @@ void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConf
|
||||
ffStrbufAppendF(buffer, "\e[%sm", (green <= yellow ? colorGreen : colorRed));
|
||||
}
|
||||
}
|
||||
ffStrbufAppend(buffer, &options->barCharElapsed);
|
||||
ffStrbufAppend(buffer, borderAsValue && i == 0
|
||||
? &options->barBorderLeftElapsed
|
||||
: borderAsValue && i == options->barWidth - 1
|
||||
? &options->barBorderRightElapsed
|
||||
: &options->barCharElapsed);
|
||||
}
|
||||
|
||||
if (blocksPercent < options->barWidth)
|
||||
{
|
||||
if(!options->pipe)
|
||||
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_WHITE "m");
|
||||
for (uint32_t i = blocksPercent; i < options->barWidth; ++i)
|
||||
ffStrbufAppend(buffer, &options->barCharTotal);
|
||||
if(!options->pipe && options->barColorTotal.length > 0)
|
||||
ffStrbufAppendF(buffer, "\e[%sm", options->barColorTotal.chars);
|
||||
for (uint8_t i = blocksPercent; i < options->barWidth; ++i)
|
||||
{
|
||||
ffStrbufAppend(buffer, borderAsValue && i == 0
|
||||
? &options->barBorderLeftElapsed
|
||||
: borderAsValue && i == options->barWidth - 1
|
||||
? &options->barBorderRightElapsed
|
||||
: &options->barCharTotal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(options->barBorderRight.length)
|
||||
if(!borderAsValue && options->barBorderRight.length)
|
||||
{
|
||||
if(!options->pipe)
|
||||
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_WHITE "m");
|
||||
if(!options->pipe && options->barColorBorder.length > 0)
|
||||
ffStrbufAppendF(buffer, "\e[%sm", options->barColorBorder.chars);
|
||||
ffStrbufAppend(buffer, &options->barBorderRight);
|
||||
}
|
||||
|
||||
if(!options->pipe)
|
||||
if(!options->pipe && (options->barColorElapsed.length > 0 || options->barColorTotal.length > 0 || options->barColorBorder.length > 0))
|
||||
{
|
||||
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
appendOutputColor(buffer, module);
|
||||
@@ -165,7 +187,7 @@ void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConf
|
||||
const char* colorYellow = options->percentColorYellow.chars;
|
||||
const char* colorRed = options->percentColorRed.chars;
|
||||
|
||||
if(percent != percent)
|
||||
if(percent == -DBL_MAX)
|
||||
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_BLACK "m");
|
||||
else if(green <= yellow)
|
||||
{
|
||||
@@ -175,7 +197,6 @@ void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConf
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
|
||||
else
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorGreen);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -187,7 +208,8 @@ void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConf
|
||||
ffStrbufAppendF(buffer, "\e[%sm", colorGreen);
|
||||
}
|
||||
}
|
||||
ffStrbufAppendF(buffer, "%.*f%%", options->percentNdigits, percent);
|
||||
ffStrbufAppendF(buffer, "%*.*f%s%%", options->percentWidth, options->percentNdigits, percent,
|
||||
options->percentSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "");
|
||||
|
||||
if (colored && !options->pipe)
|
||||
{
|
||||
@@ -239,14 +261,14 @@ bool ffPercentParseCommandOptions(const char* key, const char* subkey, const cha
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffPercentParseJsonObject(const char* key, yyjson_val* value, FFPercentageModuleConfig* config)
|
||||
bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageModuleConfig* config)
|
||||
{
|
||||
if (!ffStrEqualsIgnCase(key, "percent"))
|
||||
if (!unsafe_yyjson_equals_str(key, "percent"))
|
||||
return false;
|
||||
|
||||
if (!yyjson_is_obj(value))
|
||||
{
|
||||
fprintf(stderr, "Error: usage: %s must be an object\n", key);
|
||||
fprintf(stderr, "Error: usage: %s must be an object\n", unsafe_yyjson_get_str(key));
|
||||
exit(480);
|
||||
}
|
||||
|
||||
@@ -288,16 +310,10 @@ bool ffPercentParseJsonObject(const char* key, yyjson_val* value, FFPercentageMo
|
||||
return true;
|
||||
}
|
||||
|
||||
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig defaultConfig, FFPercentageModuleConfig config)
|
||||
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig config)
|
||||
{
|
||||
if (config.green == defaultConfig.green && config.yellow == defaultConfig.yellow)
|
||||
return;
|
||||
|
||||
yyjson_mut_val* percent = yyjson_mut_obj_add_obj(doc, module, "percent");
|
||||
if (config.green != defaultConfig.green)
|
||||
yyjson_mut_obj_add_uint(doc, percent, "green", config.green);
|
||||
if (config.yellow != defaultConfig.yellow)
|
||||
yyjson_mut_obj_add_uint(doc, percent, "yellow", config.yellow);
|
||||
if (config.type != defaultConfig.type)
|
||||
yyjson_mut_obj_add_uint(doc, percent, "type", config.type);
|
||||
yyjson_mut_obj_add_uint(doc, percent, "green", config.green);
|
||||
yyjson_mut_obj_add_uint(doc, percent, "yellow", config.yellow);
|
||||
yyjson_mut_obj_add_uint(doc, percent, "type", config.type);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ typedef enum __attribute__((__packed__)) FFPercentageTypeFlags
|
||||
FF_PERCENTAGE_TYPE_BAR_BIT = 1 << 1,
|
||||
FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT = 1 << 2,
|
||||
FF_PERCENTAGE_TYPE_NUM_COLOR_BIT = 1 << 3,
|
||||
FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT = FF_PERCENTAGE_TYPE_NUM_COLOR_BIT,
|
||||
FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT = 1 << 4,
|
||||
FF_PERCENTAGE_TYPE_FORCE_UNSIGNED_ = UINT8_MAX,
|
||||
} FFPercentageTypeFlags;
|
||||
static_assert(sizeof(FFPercentageTypeFlags) == 1, "");
|
||||
@@ -40,6 +40,6 @@ typedef struct yyjson_val yyjson_val;
|
||||
typedef struct yyjson_mut_doc yyjson_mut_doc;
|
||||
typedef struct yyjson_mut_val yyjson_mut_val;
|
||||
bool ffPercentParseCommandOptions(const char* key, const char* subkey, const char* value, FFPercentageModuleConfig* config);
|
||||
bool ffPercentParseJsonObject(const char* key, yyjson_val* value, FFPercentageModuleConfig* config);
|
||||
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig defaultConfig, FFPercentageModuleConfig config);
|
||||
bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageModuleConfig* config);
|
||||
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig config);
|
||||
const char* ffPercentParseTypeJsonConfig(yyjson_val* value, FFPercentageTypeFlags* result);
|
||||
|
||||
+102
-12
@@ -12,6 +12,10 @@
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#if !(__ANDROID__ || __OpenBSD__)
|
||||
#include <spawn.h>
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
@@ -33,6 +37,10 @@
|
||||
#include <image.h>
|
||||
#endif
|
||||
|
||||
#ifndef environ
|
||||
extern char** environ;
|
||||
#endif
|
||||
|
||||
enum { FF_PIPE_BUFSIZ = 8192 };
|
||||
|
||||
static inline int ffPipe2(int* fds, int flags)
|
||||
@@ -48,6 +56,8 @@ static inline int ffPipe2(int* fds, int flags)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Not thread-safe
|
||||
const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool useStdErr)
|
||||
{
|
||||
int pipes[2];
|
||||
@@ -56,7 +66,77 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
if(ffPipe2(pipes, O_CLOEXEC) == -1)
|
||||
return "pipe() failed";
|
||||
|
||||
pid_t childPid = fork();
|
||||
pid_t childPid = -1;
|
||||
int nullFile = ffGetNullFD();
|
||||
|
||||
#if !(__ANDROID__ || __OpenBSD__)
|
||||
|
||||
// NetBSD / Darwin: native syscall
|
||||
// Linux (glibc): clone3-execve
|
||||
// FreeBSD: vfork-execve
|
||||
// illumos: vforkx-execve
|
||||
// OpenBSD / Android (bionic): fork-execve
|
||||
|
||||
posix_spawn_file_actions_t file_actions;
|
||||
posix_spawn_file_actions_init(&file_actions);
|
||||
posix_spawn_file_actions_adddup2(&file_actions, pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO);
|
||||
posix_spawn_file_actions_adddup2(&file_actions, nullFile, useStdErr ? STDOUT_FILENO : STDERR_FILENO);
|
||||
|
||||
static char* oldLang = NULL;
|
||||
static int langIndex = -1;
|
||||
|
||||
if (langIndex >= 0)
|
||||
{
|
||||
// Found before
|
||||
if (oldLang) // oldLang was set only if it needed to be changed
|
||||
{
|
||||
if (environ[langIndex] != oldLang)
|
||||
{
|
||||
// environ is changed outside of this function
|
||||
langIndex = -1;
|
||||
}
|
||||
else
|
||||
environ[langIndex] = (char*) "LANG=C.UTF-8";
|
||||
}
|
||||
}
|
||||
if (langIndex < 0)
|
||||
{
|
||||
for (int i = 0; environ[i] != NULL; i++)
|
||||
{
|
||||
if (ffStrStartsWith(environ[i], "LANG="))
|
||||
{
|
||||
langIndex = i;
|
||||
const char* langValue = environ[i] + 5; // Skip "LANG="
|
||||
if (ffStrEqualsIgnCase(langValue, "C") ||
|
||||
ffStrStartsWithIgnCase(environ[i], "C.") ||
|
||||
ffStrEqualsIgnCase(langValue, "en_US") ||
|
||||
ffStrStartsWithIgnCase(langValue, "en_US."))
|
||||
break; // No need to change LANG
|
||||
oldLang = environ[i];
|
||||
environ[i] = (char*) "LANG=C.UTF-8"; // Set LANG to C.UTF-8 for consistent output
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ret = posix_spawnp(&childPid, argv[0], &file_actions, NULL, argv, environ);
|
||||
|
||||
if (oldLang)
|
||||
environ[langIndex] = oldLang;
|
||||
|
||||
posix_spawn_file_actions_destroy(&file_actions);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
close(pipes[0]);
|
||||
close(pipes[1]);
|
||||
return "posix_spawnp() failed";
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// https://github.com/termux/termux-packages/issues/25369
|
||||
childPid = fork();
|
||||
if(childPid == -1)
|
||||
{
|
||||
close(pipes[0]);
|
||||
@@ -64,18 +144,18 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
return "fork() failed";
|
||||
}
|
||||
|
||||
//Child
|
||||
if(childPid == 0)
|
||||
{
|
||||
int nullFile = open("/dev/null", O_WRONLY | O_CLOEXEC);
|
||||
//Child
|
||||
dup2(pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO);
|
||||
dup2(nullFile, useStdErr ? STDOUT_FILENO : STDERR_FILENO);
|
||||
setenv("LANG", "C", 1);
|
||||
putenv("LANG=C.UTF-8");
|
||||
execvp(argv[0], argv);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
//Parent
|
||||
#endif
|
||||
|
||||
close(pipes[1]);
|
||||
|
||||
int FF_AUTO_CLOSE_FD childPipeFd = pipes[0];
|
||||
@@ -86,20 +166,30 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
if (timeout >= 0)
|
||||
{
|
||||
struct pollfd pollfd = { childPipeFd, POLLIN, 0 };
|
||||
if (poll(&pollfd, 1, timeout) == 0)
|
||||
int pollret = poll(&pollfd, 1, timeout);
|
||||
if (pollret == 0)
|
||||
{
|
||||
kill(childPid, SIGTERM);
|
||||
waitpid(childPid, NULL, 0);
|
||||
return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)";
|
||||
}
|
||||
else if (errno == EINTR)
|
||||
else if (pollret < 0)
|
||||
{
|
||||
// The child process has been terminated. See `chldSignalHandler` in `common/init.c`
|
||||
if (waitpid(childPid, NULL, WNOHANG) == childPid)
|
||||
if (errno == EINTR)
|
||||
{
|
||||
// Read remaining data from the pipe
|
||||
fcntl(childPipeFd, F_SETFL, O_CLOEXEC | O_NONBLOCK);
|
||||
childPid = -1;
|
||||
// The child process has been terminated. See `chldSignalHandler` in `common/init.c`
|
||||
if (waitpid(childPid, NULL, WNOHANG) == childPid)
|
||||
{
|
||||
// Read remaining data from the pipe
|
||||
fcntl(childPipeFd, F_SETFL, O_CLOEXEC | O_NONBLOCK);
|
||||
childPid = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kill(childPid, SIGTERM);
|
||||
waitpid(childPid, NULL, 0);
|
||||
return "poll(&pollfd, 1, timeout) error";
|
||||
}
|
||||
}
|
||||
else if (pollfd.revents & POLLERR)
|
||||
|
||||
@@ -81,7 +81,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
if (hChildPipeWrite == INVALID_HANDLE_VALUE)
|
||||
return "CreateFileW(L\"\\\\.\\pipe\\FASTFETCH-$(PID)\") failed";
|
||||
|
||||
PROCESS_INFORMATION piProcInfo = {0};
|
||||
PROCESS_INFORMATION piProcInfo = {};
|
||||
|
||||
BOOL success;
|
||||
|
||||
@@ -91,9 +91,15 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
.dwFlags = STARTF_USESTDHANDLES,
|
||||
};
|
||||
if (useStdErr)
|
||||
{
|
||||
siStartInfo.hStdOutput = ffGetNullFD();
|
||||
siStartInfo.hStdError = hChildPipeWrite;
|
||||
}
|
||||
else
|
||||
{
|
||||
siStartInfo.hStdOutput = hChildPipeWrite;
|
||||
siStartInfo.hStdError = ffGetNullFD();
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY cmdline = ffStrbufCreate();
|
||||
argvToCmdline(argv, &cmdline);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "size.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
static void appendNum(FFstrbuf* result, uint64_t bytes, uint32_t base, const char** prefixes)
|
||||
{
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
double size = (double) bytes;
|
||||
uint8_t counter = 0;
|
||||
|
||||
while(size >= base && counter < options->sizeMaxPrefix && prefixes[counter + 1])
|
||||
{
|
||||
size /= base;
|
||||
counter++;
|
||||
}
|
||||
|
||||
const char* space = options->sizeSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_NEVER ? "" : " ";
|
||||
if(counter == 0)
|
||||
ffStrbufAppendF(result, "%" PRIu64 "%s%s", bytes, space, prefixes[0]);
|
||||
else
|
||||
ffStrbufAppendF(result, "%.*f%s%s", options->sizeNdigits, size, space, prefixes[counter]);
|
||||
}
|
||||
|
||||
void ffSizeAppendNum(uint64_t bytes, FFstrbuf* result)
|
||||
{
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
switch (options->sizeBinaryPrefix)
|
||||
{
|
||||
case FF_SIZE_BINARY_PREFIX_TYPE_IEC:
|
||||
appendNum(result, bytes, 1024, (const char*[]) {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", NULL});
|
||||
break;
|
||||
case FF_SIZE_BINARY_PREFIX_TYPE_SI:
|
||||
appendNum(result, bytes, 1000, (const char*[]) {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL});
|
||||
break;
|
||||
case FF_SIZE_BINARY_PREFIX_TYPE_JEDEC:
|
||||
appendNum(result, bytes, 1024, (const char*[]) {"B", "KB", "MB", "GB", "TB", NULL});
|
||||
break;
|
||||
default:
|
||||
appendNum(result, bytes, 1024, (const char*[]) {"B", NULL});
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
void ffSizeAppendNum(uint64_t bytes, FFstrbuf* result);
|
||||
+15
-21
@@ -5,7 +5,7 @@
|
||||
|
||||
void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig config, const FFModuleArgs* module)
|
||||
{
|
||||
if (celsius != celsius) // ignores NaN
|
||||
if (celsius == -DBL_MAX) // ignores invalid value
|
||||
return;
|
||||
|
||||
const FFOptionsDisplay* options = &instance.config.display;
|
||||
@@ -41,13 +41,16 @@ void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig confi
|
||||
{
|
||||
case FF_TEMPERATURE_UNIT_DEFAULT:
|
||||
case FF_TEMPERATURE_UNIT_CELSIUS:
|
||||
ffStrbufAppendF(buffer, "%.*f°C", options->tempNdigits, celsius);
|
||||
ffStrbufAppendF(buffer, "%.*f%s°C", options->tempNdigits, celsius,
|
||||
options->tempSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "");
|
||||
break;
|
||||
case FF_TEMPERATURE_UNIT_FAHRENHEIT:
|
||||
ffStrbufAppendF(buffer, "%.*f°F", options->tempNdigits, celsius * 1.8 + 32);
|
||||
ffStrbufAppendF(buffer, "%.*f%s°F", options->tempNdigits, celsius * 1.8 + 32,
|
||||
options->tempSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "");
|
||||
break;
|
||||
case FF_TEMPERATURE_UNIT_KELVIN:
|
||||
ffStrbufAppendF(buffer, "%.*f K", options->tempNdigits, celsius + 273.15);
|
||||
ffStrbufAppendF(buffer, "%.*f%sK", options->tempNdigits, celsius + 273.15,
|
||||
options->tempSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_NEVER ? "" : " ");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -104,9 +107,9 @@ bool ffTempsParseCommandOptions(const char* key, const char* subkey, const char*
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ffTempsParseJsonObject(const char* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config)
|
||||
bool ffTempsParseJsonObject(yyjson_val* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config)
|
||||
{
|
||||
if (!ffStrEqualsIgnCase(key, "temp"))
|
||||
if (!unsafe_yyjson_equals_str(key, "temp"))
|
||||
return false;
|
||||
|
||||
if (yyjson_is_bool(value))
|
||||
@@ -123,7 +126,7 @@ bool ffTempsParseJsonObject(const char* key, yyjson_val* value, bool* useTemp, F
|
||||
|
||||
if (!yyjson_is_obj(value))
|
||||
{
|
||||
fprintf(stderr, "Error: usage: %s must be an object or a boolean\n", key);
|
||||
fprintf(stderr, "Error: usage: %s must be an object or a boolean\n", unsafe_yyjson_get_str(key));
|
||||
exit(480);
|
||||
}
|
||||
|
||||
@@ -156,23 +159,14 @@ bool ffTempsParseJsonObject(const char* key, yyjson_val* value, bool* useTemp, F
|
||||
return true;
|
||||
}
|
||||
|
||||
void ffTempsGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FF_MAYBE_UNUSED bool defaultTemp, FFColorRangeConfig defaultConfig, bool temp, FFColorRangeConfig config)
|
||||
void ffTempsGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, bool temp, FFColorRangeConfig config)
|
||||
{
|
||||
assert(defaultTemp == false); // assume defaultTemp is always false
|
||||
|
||||
if (!temp)
|
||||
return;
|
||||
|
||||
if (config.green != defaultConfig.green || config.yellow != defaultConfig.yellow)
|
||||
{
|
||||
yyjson_mut_val* temp = yyjson_mut_obj_add_obj(doc, module, "temp");
|
||||
if (config.green != defaultConfig.green)
|
||||
yyjson_mut_obj_add_uint(doc, temp, "green", config.green);
|
||||
if (config.yellow != defaultConfig.yellow)
|
||||
yyjson_mut_obj_add_uint(doc, temp, "yellow", config.yellow);
|
||||
}
|
||||
yyjson_mut_obj_add_bool(doc, module, "temp", false);
|
||||
else
|
||||
{
|
||||
yyjson_mut_obj_add_bool(doc, module, "temp", true);
|
||||
yyjson_mut_val* temp = yyjson_mut_obj_add_obj(doc, module, "temp");
|
||||
yyjson_mut_obj_add_uint(doc, temp, "green", config.green);
|
||||
yyjson_mut_obj_add_uint(doc, temp, "yellow", config.yellow);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,5 +5,5 @@
|
||||
|
||||
void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig config, const FFModuleArgs* module);
|
||||
bool ffTempsParseCommandOptions(const char* key, const char* subkey, const char* value, bool* useTemp, FFColorRangeConfig* config);
|
||||
bool ffTempsParseJsonObject(const char* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config);
|
||||
void ffTempsGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FF_MAYBE_UNUSED bool defaultTemp, FFColorRangeConfig defaultConfig, bool temp, FFColorRangeConfig config);
|
||||
bool ffTempsParseJsonObject(yyjson_val* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config);
|
||||
void ffTempsGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, bool temp, FFColorRangeConfig config);
|
||||
|
||||
@@ -97,3 +97,47 @@ static inline const char* ffTimeToTimeStr(uint64_t msec)
|
||||
#ifdef _WIN32
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
typedef struct FFTimeGetAgeResult
|
||||
{
|
||||
uint32_t years;
|
||||
uint32_t daysOfYear;
|
||||
double yearsFraction;
|
||||
} FFTimeGetAgeResult;
|
||||
|
||||
static inline FFTimeGetAgeResult ffTimeGetAge(uint64_t birthMs, uint64_t nowMs)
|
||||
{
|
||||
FFTimeGetAgeResult result = {};
|
||||
if (__builtin_expect(birthMs == 0 || nowMs < birthMs, 0))
|
||||
return result;
|
||||
|
||||
time_t birth_s = (time_t) (birthMs / 1000);
|
||||
struct tm birth_tm;
|
||||
#ifdef _WIN32
|
||||
localtime_s(&birth_tm, &birth_s);
|
||||
#else
|
||||
localtime_r(&birth_s, &birth_tm);
|
||||
#endif
|
||||
|
||||
time_t now_s = (time_t) (nowMs / 1000);
|
||||
struct tm now_tm;
|
||||
#ifdef _WIN32
|
||||
localtime_s(&now_tm, &now_s);
|
||||
#else
|
||||
localtime_r(&now_s, &now_tm);
|
||||
#endif
|
||||
|
||||
result.years = (uint32_t) (now_tm.tm_year - birth_tm.tm_year);
|
||||
if (now_tm.tm_yday < birth_tm.tm_yday)
|
||||
result.years--;
|
||||
|
||||
birth_tm.tm_year += (int) result.years;
|
||||
birth_s = mktime(&birth_tm);
|
||||
uint32_t diff_s = (uint32_t) (now_s - birth_s);
|
||||
result.daysOfYear = diff_s / (24 * 60 * 60);
|
||||
|
||||
birth_tm.tm_year += 1;
|
||||
result.yearsFraction = (double) diff_s / (double) (mktime(&birth_tm) - birth_s) + result.years;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+135
-887
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/battery/option.h"
|
||||
|
||||
#define FF_BATTERY_TEMP_UNSET (0/0.0)
|
||||
#define FF_BATTERY_TEMP_UNSET (-DBL_MAX)
|
||||
|
||||
typedef struct FFBatteryResult
|
||||
{
|
||||
@@ -15,7 +16,7 @@ typedef struct FFBatteryResult
|
||||
double capacity;
|
||||
double temperature;
|
||||
uint32_t cycleCount;
|
||||
int32_t timeRemaining;
|
||||
int32_t timeRemaining; // in seconds, -1 if unknown
|
||||
} FFBatteryResult;
|
||||
|
||||
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results);
|
||||
|
||||
@@ -96,6 +96,7 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results)
|
||||
battery->temperature = FF_BATTERY_TEMP_UNSET;
|
||||
battery->cycleCount = 0;
|
||||
battery->timeRemaining = -1;
|
||||
battery->capacity = 0;
|
||||
ffStrbufInit(&battery->manufacturer);
|
||||
ffStrbufInit(&battery->modelName);
|
||||
ffStrbufInit(&battery->status);
|
||||
@@ -124,11 +125,11 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results)
|
||||
{
|
||||
double level = 0, scale = 0;
|
||||
if (ffParsePropLines(start, "level: ", &temp))
|
||||
level = ffStrbufToDouble(&temp);
|
||||
level = ffStrbufToDouble(&temp, -DBL_MAX);
|
||||
ffStrbufClear(&temp);
|
||||
|
||||
if (ffParsePropLines(start, "scale: ", &temp))
|
||||
scale = ffStrbufToDouble(&temp);
|
||||
scale = ffStrbufToDouble(&temp, -DBL_MAX);
|
||||
ffStrbufClear(&temp);
|
||||
|
||||
if (level > 0 && scale > 0)
|
||||
@@ -138,7 +139,7 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results)
|
||||
if(options->temp)
|
||||
{
|
||||
if (ffParsePropLines(start, "temperature: ", &temp))
|
||||
battery->temperature = ffStrbufToDouble(&temp);
|
||||
battery->temperature = ffStrbufToDouble(&temp, FF_BATTERY_TEMP_UNSET);
|
||||
ffStrbufClear(&temp);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ static void parseBattery(int dfd, const char* id, FFBatteryOptions* options, FFl
|
||||
return;
|
||||
|
||||
FFBatteryResult* result = ffListAdd(results);
|
||||
result->capacity = ffStrbufToDouble(&tmpBuffer);
|
||||
result->capacity = ffStrbufToDouble(&tmpBuffer, 0);
|
||||
|
||||
//At this point, we have a battery. Try to get as much values as possible.
|
||||
|
||||
@@ -154,7 +154,11 @@ static void parseBattery(int dfd, const char* id, FFBatteryOptions* options, FFl
|
||||
if (options->temp)
|
||||
{
|
||||
if (ffReadFileBufferRelative(dfd, "temp", &tmpBuffer))
|
||||
result->temperature = ffStrbufToDouble(&tmpBuffer) / 10;
|
||||
{
|
||||
result->temperature = ffStrbufToDouble(&tmpBuffer, FF_BATTERY_TEMP_UNSET);
|
||||
if (result->temperature != FF_BATTERY_TEMP_UNSET)
|
||||
result->temperature /= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,40 +8,31 @@
|
||||
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <batclass.h>
|
||||
#include <devguid.h>
|
||||
#include <cfgmgr32.h>
|
||||
#include <winternl.h>
|
||||
|
||||
static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev)
|
||||
static const char* detectWithCmApi(FFBatteryOptions* options, FFlist* results)
|
||||
{
|
||||
if(*hdev)
|
||||
SetupDiDestroyDeviceInfoList(*hdev);
|
||||
}
|
||||
//https://learn.microsoft.com/en-us/windows-hardware/drivers/install/using-device-interfaces
|
||||
ULONG cchDeviceInterfaces = 0;
|
||||
CONFIGRET cr = CM_Get_Device_Interface_List_SizeW(&cchDeviceInterfaces, (LPGUID)&GUID_DEVCLASS_BATTERY, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
|
||||
if (cr != CR_SUCCESS)
|
||||
return "CM_Get_Device_Interface_List_SizeW() failed";
|
||||
|
||||
static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results)
|
||||
{
|
||||
//https://learn.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices
|
||||
HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) =
|
||||
SetupDiGetClassDevsW(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
if(hdev == INVALID_HANDLE_VALUE)
|
||||
return "SetupDiGetClassDevsW(&GUID_DEVCLASS_BATTERY) failed";
|
||||
if (cchDeviceInterfaces <= 1)
|
||||
return NULL; // Not found
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA did = { .cbSize = sizeof(did) };
|
||||
for(DWORD idev = 0; SetupDiEnumDeviceInterfaces(hdev, NULL, &GUID_DEVCLASS_BATTERY, idev, &did); idev++)
|
||||
wchar_t* FF_AUTO_FREE mszDeviceInterfaces = (wchar_t*)malloc(cchDeviceInterfaces * sizeof(wchar_t));
|
||||
cr = CM_Get_Device_Interface_ListW((LPGUID)&GUID_DEVCLASS_BATTERY, NULL, mszDeviceInterfaces, cchDeviceInterfaces, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
|
||||
if (cr != CR_SUCCESS)
|
||||
return "CM_Get_Device_Interface_ListW() failed";
|
||||
|
||||
for (const wchar_t* pDeviceInterface = mszDeviceInterfaces; *pDeviceInterface; pDeviceInterface += wcslen(pDeviceInterface) + 1)
|
||||
{
|
||||
DWORD cbRequired = 0;
|
||||
SetupDiGetDeviceInterfaceDetailW(hdev, &did, NULL, 0, &cbRequired, NULL); //Fail with not enough buffer
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_W* FF_AUTO_FREE pdidd = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)malloc(cbRequired);
|
||||
if(!pdidd)
|
||||
break; //Out of memory
|
||||
|
||||
pdidd->cbSize = sizeof(*pdidd);
|
||||
if(!SetupDiGetDeviceInterfaceDetailW(hdev, &did, pdidd, cbRequired, &cbRequired, NULL))
|
||||
continue;
|
||||
|
||||
HANDLE FF_AUTO_CLOSE_FD hBattery =
|
||||
CreateFileW(pdidd->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
CreateFileW(pDeviceInterface, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if(hBattery == INVALID_HANDLE_VALUE)
|
||||
continue;
|
||||
@@ -99,7 +90,7 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results
|
||||
bqi.InformationLevel = BatteryManufactureDate;
|
||||
BATTERY_MANUFACTURE_DATE date;
|
||||
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &date, sizeof(date), &dwOut, NULL))
|
||||
ffStrbufSetF(&battery->manufactureDate, "%.4d-%.2d-%.2d", date.Year + 1900, date.Month, date.Day);
|
||||
ffStrbufSetF(&battery->manufactureDate, "%.4d-%.2d-%.2d", date.Year < 1000 ? date.Year + 1900 : date.Year, date.Month, date.Day);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -112,7 +103,7 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results
|
||||
|
||||
battery->cycleCount = bi.CycleCount;
|
||||
|
||||
battery->temperature = 0.0/0.0;
|
||||
battery->temperature = FF_BATTERY_TEMP_UNSET;
|
||||
if(options->temp)
|
||||
{
|
||||
bqi.InformationLevel = BatteryTemperature;
|
||||
@@ -131,7 +122,7 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results
|
||||
{
|
||||
BATTERY_STATUS bs;
|
||||
BATTERY_WAIT_STATUS bws = { .BatteryTag = bqi.BatteryTag };
|
||||
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY)
|
||||
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY && bi.FullChargedCapacity != 0)
|
||||
battery->capacity = bs.Capacity * 100.0 / bi.FullChargedCapacity;
|
||||
else
|
||||
battery->capacity = 0;
|
||||
@@ -271,6 +262,6 @@ static const char* detectWithNtApi(FF_MAYBE_UNUSED FFBatteryOptions* options, FF
|
||||
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results)
|
||||
{
|
||||
return options->useSetupApi
|
||||
? detectWithSetupApi(options, results)
|
||||
? detectWithCmApi(options, results)
|
||||
: detectWithNtApi(options, results);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/bios/option.h"
|
||||
|
||||
typedef struct FFBiosResult
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/bluetooth/option.h"
|
||||
|
||||
typedef struct FFBluetoothResult
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/bluetoothradio/option.h"
|
||||
|
||||
typedef struct FFBluetoothRadioResult
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/board/option.h"
|
||||
|
||||
typedef struct FFBoardResult
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/bootmgr/option.h"
|
||||
|
||||
typedef struct FFBootmgrResult
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "util/FFlist.h"
|
||||
#include "modules/brightness/option.h"
|
||||
|
||||
typedef struct FFBrightnessResult
|
||||
{
|
||||
|
||||
@@ -32,7 +32,7 @@ static const char* detectWithBacklight(FFlist* result)
|
||||
ffStrbufAppendS(&backlightDir, "/brightness");
|
||||
if(ffReadFileBuffer(backlightDir.chars, &buffer))
|
||||
{
|
||||
double actualBrightness = ffStrbufToDouble(&buffer);
|
||||
double actualBrightness = ffStrbufToDouble(&buffer, 0);
|
||||
ffStrbufSubstrBefore(&backlightDir, backlightDirLength);
|
||||
ffStrbufAppendS(&backlightDir, entry->d_name);
|
||||
ffStrbufAppendS(&backlightDir, "/max_brightness");
|
||||
@@ -71,7 +71,7 @@ static const char* detectWithBacklight(FFlist* result)
|
||||
}
|
||||
else
|
||||
ffStrbufInitS(&brightness->name, entry->d_name);
|
||||
brightness->max = ffStrbufToDouble(&buffer);
|
||||
brightness->max = ffStrbufToDouble(&buffer, 0);
|
||||
brightness->min = 0;
|
||||
brightness->current = actualBrightness;
|
||||
brightness->builtin = true;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/btrfs/option.h"
|
||||
|
||||
typedef struct FFBtrfsDiskUsage
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/camera/option.h"
|
||||
|
||||
typedef struct FFCameraResult
|
||||
{
|
||||
|
||||
@@ -45,7 +45,7 @@ const char* ffDetectCamera(FF_MAYBE_UNUSED FFlist* result)
|
||||
ffStrbufInitStatic(&camera->name, "Unknown");
|
||||
}
|
||||
ffStrbufInit(&camera->vendor);
|
||||
ffStrbufInitS(&camera->id, yyjson_get_str(yyjson_obj_get(device, "id")));
|
||||
ffStrbufInitJsonVal(&camera->id, yyjson_obj_get(device, "id"));
|
||||
yyjson_val* sizes = yyjson_arr_get_first(yyjson_obj_get(device, "jpeg_output_sizes"));
|
||||
if (yyjson_is_obj(sizes))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/chassis/option.h"
|
||||
|
||||
typedef struct FFChassisResult
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/cpu/option.h"
|
||||
|
||||
#define FF_CPU_TEMP_UNSET (0/0.0)
|
||||
#define FF_CPU_TEMP_UNSET (-DBL_MAX)
|
||||
|
||||
typedef struct FFCPUCore
|
||||
{
|
||||
|
||||
@@ -28,19 +28,19 @@ static double parseHwmonDir(FFstrbuf* dir, FFstrbuf* buffer)
|
||||
ffStrbufAppendS(dir, "temp1_input");
|
||||
|
||||
if(!ffReadFileBuffer(dir->chars, buffer))
|
||||
return 0.0/0.0;
|
||||
return FF_CPU_TEMP_UNSET;
|
||||
}
|
||||
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
|
||||
double value = ffStrbufToDouble(buffer);// millidegree Celsius
|
||||
double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET);// millidegree Celsius
|
||||
|
||||
if(value != value)
|
||||
return 0.0/0.0;
|
||||
if(value == FF_CPU_TEMP_UNSET)
|
||||
return FF_CPU_TEMP_UNSET;
|
||||
|
||||
ffStrbufAppendS(dir, "name");
|
||||
if (!ffReadFileBuffer(dir->chars, buffer))
|
||||
return 0.0/0.0;
|
||||
return FF_CPU_TEMP_UNSET;
|
||||
|
||||
ffStrbufTrimRightSpace(buffer);
|
||||
|
||||
@@ -51,14 +51,17 @@ static double parseHwmonDir(FFstrbuf* dir, FFstrbuf* buffer)
|
||||
ffStrbufEqualS(buffer, "coretemp") // Intel
|
||||
) return value / 1000.;
|
||||
|
||||
return 0.0/0.0;
|
||||
return FF_CPU_TEMP_UNSET;
|
||||
}
|
||||
|
||||
static double detectTZTemp(FFstrbuf* buffer)
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/class/thermal/thermal_zone0/temp", buffer))
|
||||
return ffStrbufToDouble(buffer) / 1000.;
|
||||
return 0.0/0.0;
|
||||
{
|
||||
double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET);// millidegree Celsius
|
||||
return value != FF_CPU_TEMP_UNSET ? value / 1000. : FF_CPU_TEMP_UNSET;
|
||||
}
|
||||
return FF_CPU_TEMP_UNSET;
|
||||
}
|
||||
|
||||
static double detectCPUTemp(void)
|
||||
@@ -72,7 +75,7 @@ static double detectCPUTemp(void)
|
||||
|
||||
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(baseDir.chars);
|
||||
if(dirp == NULL)
|
||||
return 0.0/0.0;
|
||||
return FF_CPU_TEMP_UNSET;
|
||||
|
||||
struct dirent* entry;
|
||||
while((entry = readdir(dirp)) != NULL)
|
||||
@@ -84,7 +87,7 @@ static double detectCPUTemp(void)
|
||||
ffStrbufAppendC(&baseDir, '/');
|
||||
|
||||
double result = parseHwmonDir(&baseDir, &buffer);
|
||||
if (result == result)
|
||||
if (result != FF_CPU_TEMP_UNSET)
|
||||
return result;
|
||||
|
||||
ffStrbufSubstrBefore(&baseDir, baseDirLength);
|
||||
@@ -100,85 +103,75 @@ static void detectQualcomm(FFCPUResult* cpu)
|
||||
{
|
||||
// https://en.wikipedia.org/wiki/List_of_Qualcomm_Snapdragon_systems_on_chips
|
||||
|
||||
if (ffStrbufEqualS(&cpu->name, "SM8750-AC"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8 Elite for Galaxy [SM8750-AC]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM8750-3"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8 Elite [SM8750-3]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM8750"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8 Elite [SM8750]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM8635"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8s Gen 3 [SM8635]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM8650-AC"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8 Gen 3 for Galaxy [SM8650-AC]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM8650"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8 Gen 3 [SM8650]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM8550-AC"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8 Gen 2 for Galaxy [SM8550-AC]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM8550"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8 Gen 2 [SM8550]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM8475"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8+ Gen 1 [SM8475]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM8450"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 8 Gen 1 [SM8450]");
|
||||
uint32_t code = (uint32_t) strtoul(cpu->name.chars + 2, NULL, 10);
|
||||
const char* name = NULL;
|
||||
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM7675"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 7+ Gen 3 [SM7675]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM7635"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 7s Gen 3 [SM7635]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM7550"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 7 Gen 3 [SM7550]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM7475"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 7+ Gen 2 [SM7550]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM7435"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 7s Gen 2 [SM7435]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM7450"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 7 Gen 1 [SM7450]");
|
||||
switch (code)
|
||||
{
|
||||
case 8735: name = "8s Gen 4"; break;
|
||||
case 8750: name = "8 Elite"; break;
|
||||
case 8635: name = "8s Gen 3"; break;
|
||||
case 8650: name = "8 Gen 3"; break;
|
||||
case 8550: name = "8 Gen 2"; break;
|
||||
case 8475: name = "8+ Gen 1"; break;
|
||||
case 8450: name = "8 Gen 1"; break;
|
||||
case 7750: name = "7 Gen 4"; break;
|
||||
case 7675: name = "7+ Gen 3"; break;
|
||||
case 7635: name = "7s Gen 3"; break;
|
||||
case 7550: name = "7 Gen 3"; break;
|
||||
case 7475: name = "7+ Gen 2"; break;
|
||||
case 7435: name = "7s Gen 2"; break;
|
||||
case 7450: name = "7 Gen 1"; break;
|
||||
case 6650: name = "6 Gen 4"; break;
|
||||
case 6375: name = "6s Gen 3"; break;
|
||||
case 6475: name = "6 Gen 3"; break;
|
||||
case 6115: name = "6s Gen 1"; break;
|
||||
case 6450: name = "6 Gen 1"; break;
|
||||
case 4635: name = "4s Gen 2"; break;
|
||||
case 4450: name = "4 Gen 2"; break;
|
||||
case 4375: name = "4 Gen 1"; break;
|
||||
}
|
||||
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM6375-AC"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 6s Gen 3 [SM6375-AC]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM6475"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 6 Gen 3 [SM6475]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM6115"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 6s Gen 1 [SM6115]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM6450"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 6 Gen 1 [SM6450]");
|
||||
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM4635"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 4s Gen 2 [SM4635]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM4450"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 4 Gen 2 [SM4450]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "SM4375"))
|
||||
ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 4 Gen 1 [SM4375]");
|
||||
if (name)
|
||||
{
|
||||
char str[32];
|
||||
ffStrCopy(str, cpu->name.chars, sizeof(str));
|
||||
ffStrbufSetF(&cpu->name, "Qualcomm Snapdragon %s [%s]", name, str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void detectMediaTek(FFCPUResult* cpu)
|
||||
{
|
||||
// https://en.wikipedia.org/wiki/List_of_MediaTek_systems_on_chips
|
||||
|
||||
if (ffStrbufEqualS(&cpu->name, "MT6991"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9400 [MT6991]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT6991Z"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9400 [MT6991Z]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT6989Z"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9300+ [MT6989Z]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT8796Z"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9300+ [MT8796Z]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT6989"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9300 [MT6989]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT8796"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9300 [MT8796]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT6985W"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9200+ [MT6985W]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT6985"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9200 [MT6985]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT6983W"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9000+ [MT6983W]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT8798Z/T"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9000+ [MT8798Z/T]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT6983Z"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9000 [MT6983Z]");
|
||||
else if (ffStrbufEqualS(&cpu->name, "MT8798Z/C"))
|
||||
ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9000 [MT8798Z/C]");
|
||||
uint32_t code = (uint32_t) strtoul(cpu->name.chars + 2, NULL, 10);
|
||||
const char* name = NULL;
|
||||
|
||||
switch (code) // The SOC code of MTK Dimensity series is full of mess
|
||||
{
|
||||
case 6991: name = "9400"; break;
|
||||
case 6989:
|
||||
case 8796: name = "9300"; break;
|
||||
case 6985: name = "9200"; break;
|
||||
case 6983:
|
||||
case 8798: name = "9000"; break;
|
||||
|
||||
case 6899: name = "8400"; break;
|
||||
case 6897:
|
||||
case 8792: name = "8300"; break;
|
||||
case 6896: name = "8200"; break;
|
||||
case 8795: name = "8100"; break;
|
||||
case 6895: name = "8000"; break;
|
||||
}
|
||||
|
||||
if (name)
|
||||
{
|
||||
char str[32];
|
||||
ffStrCopy(str, cpu->name.chars, sizeof(str));
|
||||
ffStrbufSetF(&cpu->name, "MediaTek Dimensity %s [%s]", name, str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void detectAndroid(FFCPUResult* cpu)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/cpucache/option.h"
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFCPUCacheType
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/cpuusage/option.h"
|
||||
|
||||
typedef struct FFCpuUsageInfo {
|
||||
uint64_t inUseAll;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/cursor/option.h"
|
||||
|
||||
typedef struct FFCursorResult
|
||||
{
|
||||
|
||||
@@ -2,19 +2,30 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
static void appendColor(FFstrbuf* str, NSDictionary* color)
|
||||
static void appendColor(FFstrbuf* str, NSDictionary* dict)
|
||||
{
|
||||
int r = (int) (((NSNumber*) color[@"red"]).doubleValue * 255);
|
||||
int g = (int) (((NSNumber*) color[@"green"]).doubleValue * 255);
|
||||
int b = (int) (((NSNumber*) color[@"blue"]).doubleValue * 255);
|
||||
int a = (int) (((NSNumber*) color[@"alpha"]).doubleValue * 255);
|
||||
uint32_t r = (uint32_t) (((NSNumber*) dict[@"red"]).doubleValue * 255 + .5);
|
||||
uint32_t g = (uint32_t) (((NSNumber*) dict[@"green"]).doubleValue * 255 + .5);
|
||||
uint32_t b = (uint32_t) (((NSNumber*) dict[@"blue"]).doubleValue * 255 + .5);
|
||||
uint32_t a = (uint32_t) (((NSNumber*) dict[@"alpha"]).doubleValue * 255 + .5);
|
||||
uint32_t color = (r << 24) | (g << 16) | (b << 8) | a;
|
||||
|
||||
if (r == 255 && g == 255 && b == 255 && a == 255)
|
||||
ffStrbufAppendS(str, "White");
|
||||
else if (r == 0 && g == 0 && b == 0 && a == 255)
|
||||
ffStrbufAppendS(str, "Black");
|
||||
else
|
||||
ffStrbufAppendF(str, "#%02X%02X%02X%02X", r, g, b, a);
|
||||
switch (color)
|
||||
{
|
||||
case 0x000000FF: ffStrbufAppendS(str, "Black"); return;
|
||||
case 0x0433FFFF: ffStrbufAppendS(str, "Blue"); return;
|
||||
case 0xAA7942FF: ffStrbufAppendS(str, "Brown"); return;
|
||||
case 0x00FDFFFF: ffStrbufAppendS(str, "Cyan"); return;
|
||||
case 0x00F900FF: ffStrbufAppendS(str, "Green"); return;
|
||||
case 0xFF40FFFF: ffStrbufAppendS(str, "Magenta"); return;
|
||||
case 0xFF9300FF: ffStrbufAppendS(str, "Orange"); return;
|
||||
case 0x942192FF: ffStrbufAppendS(str, "Purple"); return;
|
||||
case 0xFF2600FF: ffStrbufAppendS(str, "Red"); return;
|
||||
case 0xFFFB00FF: ffStrbufAppendS(str, "Yellow"); return;
|
||||
case 0xFFFFFFFF: ffStrbufAppendS(str, "White"); return;
|
||||
case 0x00000000: ffStrbufAppendS(str, "Transparent"); return;
|
||||
default: ffStrbufAppendF(str, "#%08X", color); return;
|
||||
}
|
||||
}
|
||||
|
||||
void ffDetectCursor(FFCursorResult* result)
|
||||
@@ -46,7 +57,7 @@ void ffDetectCursor(FFCursorResult* result)
|
||||
|
||||
NSNumber* mouseDriverCursorSize = dict[@"mouseDriverCursorSize"];
|
||||
if (mouseDriverCursorSize)
|
||||
ffStrbufAppendF(&result->size, "%d", (int) (mouseDriverCursorSize.doubleValue * 32));
|
||||
ffStrbufAppendF(&result->size, "%d", (int) (mouseDriverCursorSize.doubleValue * 32 + 0.5));
|
||||
else
|
||||
ffStrbufAppendS(&result->size, "32");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/de/option.h"
|
||||
|
||||
const char* ffDetectDEVersion(const FFstrbuf* deName, FFstrbuf* result, FFDEOptions* options);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/disk/option.h"
|
||||
|
||||
typedef struct FFDisk
|
||||
{
|
||||
|
||||
@@ -55,6 +55,9 @@ static bool isPhysicalDevice(const struct mntent* device)
|
||||
ffStrStartsWith(device->mnt_fsname + 5, "fd") //Ignore fd devices
|
||||
) return false;
|
||||
|
||||
if (ffStrStartsWith(device->mnt_dir, "/bedrock/")) // Ignore Bedrock Linux subvolumes
|
||||
return false;
|
||||
|
||||
struct stat deviceStat;
|
||||
if(stat(device->mnt_fsname, &deviceStat) != 0)
|
||||
return false;
|
||||
@@ -283,7 +286,7 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
|
||||
while((device = getmntent(mountsFile)))
|
||||
{
|
||||
if (__builtin_expect(options->folders.length, 0))
|
||||
if (__builtin_expect(options->folders.length > 0, false))
|
||||
{
|
||||
if (!ffDiskMatchMountpoint(&options->folders, device->mnt_dir))
|
||||
continue;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/diskio/option.h"
|
||||
|
||||
typedef struct FFDiskIOResult
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/display/option.h"
|
||||
|
||||
#define FF_DE_PRETTY_PLASMA "KDE Plasma"
|
||||
#define FF_DE_PRETTY_GNOME "GNOME"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/time.h"
|
||||
#include "util/edidHelper.h"
|
||||
#include "util/mallocHelper.h"
|
||||
#include "util/stringUtils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -24,6 +25,9 @@ typedef struct XcbPropertyData
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name_name)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name_name_length)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_setup)
|
||||
FF_LIBRARY_SYMBOL(xcb_setup_vendor)
|
||||
FF_LIBRARY_SYMBOL(xcb_setup_vendor_length)
|
||||
} XcbPropertyData;
|
||||
|
||||
static bool xcbInitPropertyData(FF_MAYBE_UNUSED void* libraryHandle, XcbPropertyData* propertyData)
|
||||
@@ -38,6 +42,9 @@ static bool xcbInitPropertyData(FF_MAYBE_UNUSED void* libraryHandle, XcbProperty
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_atom_name_name, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_atom_name_name_length, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_atom_name_reply, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_setup, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_setup_vendor, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_setup_vendor_length, false)
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -86,6 +93,23 @@ static void xcbDetectWMfromEWMH(XcbPropertyData* data, xcb_connection_t* connect
|
||||
ffStrbufSetS(&result->wmProcessName, wmName);
|
||||
}
|
||||
|
||||
static void xcbFetchServerVendor(XcbPropertyData* data, xcb_connection_t* connection, FFDisplayServerResult* result)
|
||||
{
|
||||
const xcb_setup_t* setup = data->ffxcb_get_setup(connection);
|
||||
|
||||
int length = data->ffxcb_setup_vendor_length(setup);
|
||||
if(length <= 0)
|
||||
return;
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY serverVendor = ffStrbufCreateNS((uint32_t) length, data->ffxcb_setup_vendor(setup));
|
||||
|
||||
if (!ffStrbufEqualS(&serverVendor, "The X.Org Foundation")) // Original
|
||||
{
|
||||
ffStrbufDestroy(&result->wmProtocolName);
|
||||
ffStrbufInitMove(&result->wmProtocolName, &serverVendor);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct XcbRandrData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources_current)
|
||||
@@ -381,8 +405,10 @@ const char* ffdsConnectXcbRandr(FFDisplayServerResult* result)
|
||||
xcb_screen_iterator_t iterator = ffxcb_setup_roots_iterator(ffxcb_get_setup(data.connection));
|
||||
|
||||
|
||||
if(iterator.rem > 0 && propertyDataInitialized)
|
||||
if(iterator.rem > 0 && propertyDataInitialized) {
|
||||
xcbDetectWMfromEWMH(&data.propData, data.connection, iterator.data->root, result);
|
||||
xcbFetchServerVendor(&data.propData, data.connection, result);
|
||||
}
|
||||
|
||||
|
||||
while(iterator.rem > 0)
|
||||
|
||||
@@ -14,6 +14,7 @@ typedef struct X11PropertyData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(XInternAtom)
|
||||
FF_LIBRARY_SYMBOL(XGetWindowProperty)
|
||||
FF_LIBRARY_SYMBOL(XServerVendor)
|
||||
FF_LIBRARY_SYMBOL(XFree)
|
||||
} X11PropertyData;
|
||||
|
||||
@@ -21,6 +22,7 @@ static bool x11InitPropertyData(FF_MAYBE_UNUSED void* libraryHandle, X11Property
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XInternAtom, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XGetWindowProperty, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XServerVendor, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XFree, false)
|
||||
|
||||
return true;
|
||||
@@ -62,6 +64,16 @@ static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDispl
|
||||
data->ffXFree(wmWindow);
|
||||
}
|
||||
|
||||
static void x11FetchServerVendor(X11PropertyData* data, Display* display, FFDisplayServerResult* result)
|
||||
{
|
||||
const char* serverVendor = data->ffXServerVendor(display);
|
||||
if (serverVendor && !ffStrEquals(serverVendor, "The X.Org Foundation")) {
|
||||
ffStrbufSetS(&result->wmProtocolName, serverVendor);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
typedef struct XrandrData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(XInternAtom)
|
||||
@@ -305,8 +317,10 @@ const char* ffdsConnectXrandr(FFDisplayServerResult* result)
|
||||
if(data.display == NULL)
|
||||
return "XOpenDisplay() failed";
|
||||
|
||||
if(propertyDataInitialized && ScreenCount(data.display) > 0)
|
||||
if(propertyDataInitialized && ScreenCount(data.display) > 0) {
|
||||
x11DetectWMFromEWMH(&propertyData, data.display, result);
|
||||
x11FetchServerVendor(&propertyData, data.display, result);
|
||||
}
|
||||
|
||||
data.result = result;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/dns/option.h"
|
||||
|
||||
const char* ffDetectDNS(FFDNSOptions* options, FFlist* results /* list of FFstrbuf */);
|
||||
|
||||
@@ -37,7 +37,7 @@ const char* ffDetectDNS(FFDNSOptions* options, FFlist* results)
|
||||
return "GetAdaptersAddresses() failed";
|
||||
}
|
||||
|
||||
uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteIfIndex();
|
||||
uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteV4()->ifIndex;
|
||||
// Iterate through all of the adapters
|
||||
for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/editor/option.h"
|
||||
|
||||
typedef struct FFEditorResult
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/font/option.h"
|
||||
|
||||
#define FF_DETECT_FONT_NUM_FONTS 4
|
||||
enum { FF_DETECT_FONT_NUM_FONTS = 4 };
|
||||
|
||||
typedef struct FFFontResult
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "detection/vulkan/vulkan.h"
|
||||
#include "detection/opencl/opencl.h"
|
||||
#include "detection/opengl/opengl.h"
|
||||
#include "modules/opengl/opengl.h"
|
||||
|
||||
const char* FF_GPU_VENDOR_NAME_APPLE = "Apple";
|
||||
const char* FF_GPU_VENDOR_NAME_AMD = "AMD";
|
||||
@@ -56,7 +57,10 @@ const char* detectByOpenGL(FFlist* gpus)
|
||||
ffStrbufInit(&result.slv);
|
||||
ffStrbufInit(&result.library);
|
||||
|
||||
const char* error = ffDetectOpenGL(&instance.config.modules.openGL, &result);
|
||||
__attribute__((__cleanup__(ffDestroyOpenGLOptions))) FFOpenGLOptions options;
|
||||
ffInitOpenGLOptions(&options);
|
||||
const char* error = ffDetectOpenGL(&options, &result);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
FFGPUResult* gpu = (FFGPUResult*) ffListAdd(gpus);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/gpu/option.h"
|
||||
|
||||
#define FF_GPU_TEMP_UNSET (0/0.0)
|
||||
#define FF_GPU_TEMP_UNSET (-DBL_MAX)
|
||||
#define FF_GPU_CORE_COUNT_UNSET -1
|
||||
#define FF_GPU_VMEM_SIZE_UNSET ((uint64_t)-1)
|
||||
#define FF_GPU_FREQUENCY_UNSET 0
|
||||
#define FF_GPU_CORE_USAGE_UNSET (0/0.0)
|
||||
#define FF_GPU_CORE_USAGE_UNSET (-DBL_MAX)
|
||||
#define FF_GPU_INDEX_UNSET ((uint32_t)-1)
|
||||
|
||||
extern const char* FF_GPU_VENDOR_NAME_APPLE;
|
||||
@@ -67,6 +68,7 @@ const char* ffDrmDetectAmdgpu(const FFGPUOptions* options, FFGPUResult* gpu, con
|
||||
const char* ffDrmDetectI915(FFGPUResult* gpu, int fd);
|
||||
const char* ffDrmDetectXe(FFGPUResult* gpu, int fd);
|
||||
const char* ffDrmDetectAsahi(FFGPUResult* gpu, int fd);
|
||||
const char* ffDrmDetectNouveau(FFGPUResult* gpu, int fd);
|
||||
#endif // FF_HAVE_DRM
|
||||
|
||||
const char* ffGPUDetectDriverSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFGpuDriverPciBusId pciBusId);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#include "gpu.h"
|
||||
#include "common/library.h"
|
||||
#include "detection/cpu/cpu.h"
|
||||
#include "util/apple/cf_helpers.h"
|
||||
#include "util/apple/smc_temps.h"
|
||||
|
||||
@@ -109,7 +107,7 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus)
|
||||
gpu->type = FF_GPU_TYPE_UNKNOWN;
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
IORegistryEntryGetRegistryEntryID(registryEntry, &gpu->deviceId);
|
||||
ffStrbufInitStatic(&gpu->platformApi, "Metal");
|
||||
ffStrbufInitStatic(&gpu->platformApi, "IOKit");
|
||||
|
||||
ffStrbufInit(&gpu->driver); // Ok for both Apple and Intel
|
||||
ffCfDictGetString(properties, CFSTR("CFBundleIdentifier"), &gpu->driver);
|
||||
|
||||
@@ -60,10 +60,13 @@ const char* ffGpuDetectMetal(FFlist* gpus)
|
||||
else if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1])
|
||||
ffStrbufSetStatic(&gpu->platformApi, "Metal Feature Set 1");
|
||||
#else // MAC_OS_X_VERSION_10_15
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
||||
if ([device supportsFamily:MTLGPUFamilyMetal4])
|
||||
ffStrbufSetStatic(&gpu->platformApi, "Metal 4");
|
||||
else if ([device supportsFamily:MTLGPUFamilyMetal3])
|
||||
ffStrbufSetStatic(&gpu->platformApi, "Metal 3");
|
||||
#pragma clang diagnostic pop
|
||||
else if ([device supportsFamily:MTLGPUFamilyCommon3])
|
||||
ffStrbufSetStatic(&gpu->platformApi, "Metal Common 3");
|
||||
else if ([device supportsFamily:MTLGPUFamilyCommon2])
|
||||
@@ -71,7 +74,7 @@ const char* ffGpuDetectMetal(FFlist* gpus)
|
||||
else if ([device supportsFamily:MTLGPUFamilyCommon1])
|
||||
ffStrbufSetStatic(&gpu->platformApi, "Metal Common 1");
|
||||
|
||||
gpu->type = device.location == MTLDeviceLocationBuiltIn ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
gpu->type = device.hasUnifiedMemory ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
gpu->index = (uint32_t) device.locationNumber;
|
||||
|
||||
if (device.hasUnifiedMemory && device.recommendedMaxWorkingSetSize > 0)
|
||||
|
||||
@@ -106,7 +106,10 @@ static const char* detectByDrm(const FFGPUOptions* options, FFlist* gpus)
|
||||
.name_len = ARRAY_SIZE(driverName),
|
||||
};
|
||||
if (ioctl(fd, DRM_IOCTL_VERSION, &ver) == 0)
|
||||
ffStrbufSetF(&gpu->driver, "%*s %d.%d.%d", (int) ver.name_len, ver.name, ver.version_major, ver.version_minor, ver.version_patchlevel);
|
||||
{
|
||||
driverName[ver.name_len] = '\0';
|
||||
ffStrbufSetF(&gpu->driver, "%s %d.%d.%d", ver.name, ver.version_major, ver.version_minor, ver.version_patchlevel);
|
||||
}
|
||||
|
||||
if (ffStrStartsWith(driverName, "i915"))
|
||||
ffDrmDetectI915(gpu, fd);
|
||||
@@ -118,6 +121,8 @@ static const char* detectByDrm(const FFGPUOptions* options, FFlist* gpus)
|
||||
ffDrmDetectXe(gpu, fd);
|
||||
else if (ffStrStartsWith(driverName, "asahi"))
|
||||
ffDrmDetectAsahi(gpu, fd);
|
||||
else if (ffStrStartsWith(driverName, "nouveau"))
|
||||
ffDrmDetectNouveau(gpu, fd);
|
||||
else if (dev->bustype == DRM_BUS_PCI)
|
||||
{
|
||||
ffGPUDetectDriverSpecific(options, gpu, (FFGpuDriverPciBusId) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "intel_drm.h"
|
||||
#include "asahi_drm.h"
|
||||
#include <radeon_drm.h>
|
||||
#include <nouveau_drm.h>
|
||||
|
||||
const char* ffDrmDetectRadeon(const FFGPUOptions* options, FFGPUResult* gpu, const char* renderPath)
|
||||
{
|
||||
@@ -305,7 +306,7 @@ const char* ffDrmDetectAsahi(FFGPUResult* gpu, int fd)
|
||||
struct drm_asahi_params_global paramsGlobal = {};
|
||||
if (ioctl(fd, DRM_IOCTL_ASAHI_GET_PARAMS, &(struct drm_asahi_get_params) {
|
||||
.param_group = DRM_ASAHI_GET_PARAMS,
|
||||
.pointer = (uint64_t) ¶msGlobal,
|
||||
.pointer = (uintptr_t) ¶msGlobal,
|
||||
.size = sizeof(paramsGlobal),
|
||||
}) >= 0)
|
||||
{
|
||||
@@ -343,6 +344,29 @@ const char* ffDrmDetectAsahi(FFGPUResult* gpu, int fd)
|
||||
return "Failed to query Asahi GPU information";
|
||||
}
|
||||
|
||||
#ifndef DRM_IOCTL_NOUVEAU_GETPARAM
|
||||
#define DRM_IOCTL_NOUVEAU_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_GETPARAM, struct drm_nouveau_getparam)
|
||||
#endif
|
||||
|
||||
const char* ffDrmDetectNouveau(FFGPUResult* gpu, int fd)
|
||||
{
|
||||
struct drm_nouveau_getparam getparam = { };
|
||||
|
||||
getparam.param = NOUVEAU_GETPARAM_FB_SIZE;
|
||||
if (ioctl(fd, DRM_IOCTL_NOUVEAU_GETPARAM, &getparam) == 0)
|
||||
gpu->dedicated.total = getparam.value;
|
||||
|
||||
getparam.param = NOUVEAU_GETPARAM_AGP_SIZE;
|
||||
if (ioctl(fd, DRM_IOCTL_NOUVEAU_GETPARAM, &getparam) == 0)
|
||||
gpu->shared.total = getparam.value;
|
||||
|
||||
getparam.param = NOUVEAU_GETPARAM_GRAPH_UNITS;
|
||||
if (ioctl(fd, DRM_IOCTL_NOUVEAU_GETPARAM, &getparam) == 0 && getparam.value < INT32_MAX)
|
||||
gpu->coreCount = (int32_t) getparam.value;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // FF_HAVE_DRM
|
||||
|
||||
#include "gpu_driver_specific.h"
|
||||
|
||||
@@ -10,7 +10,7 @@ struct FFIgclData {
|
||||
FF_LIBRARY_SYMBOL(ctlEnumerateDevices)
|
||||
FF_LIBRARY_SYMBOL(ctlGetDeviceProperties)
|
||||
FF_LIBRARY_SYMBOL(ctlEnumTemperatureSensors)
|
||||
FF_LIBRARY_SYMBOL(ctlTemperatureGetState)
|
||||
FF_LIBRARY_SYMBOL(ctlTemperatureGetProperties)
|
||||
FF_LIBRARY_SYMBOL(ctlEnumMemoryModules)
|
||||
FF_LIBRARY_SYMBOL(ctlMemoryGetProperties)
|
||||
FF_LIBRARY_SYMBOL(ctlMemoryGetState)
|
||||
@@ -41,7 +41,7 @@ const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverRe
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumerateDevices)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlGetDeviceProperties)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumTemperatureSensors)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlTemperatureGetState)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlTemperatureGetProperties)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumMemoryModules)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlMemoryGetProperties)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlMemoryGetState)
|
||||
@@ -89,19 +89,19 @@ const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverRe
|
||||
if (properties.device_type != CTL_DEVICE_TYPE_GRAPHICS)
|
||||
continue;
|
||||
|
||||
if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_LUID)
|
||||
if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID)
|
||||
{
|
||||
if (cond->luid == deviceId)
|
||||
if (cond->pciBusId.bus == properties.adapter_bdf.bus &&
|
||||
cond->pciBusId.device == properties.adapter_bdf.device &&
|
||||
cond->pciBusId.func == properties.adapter_bdf.function)
|
||||
{
|
||||
device = devices[iDev];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID)
|
||||
else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_LUID)
|
||||
{
|
||||
if (cond->pciBusId.bus == properties.adapter_bdf.bus &&
|
||||
cond->pciBusId.device == properties.adapter_bdf.device &&
|
||||
cond->pciBusId.func == properties.adapter_bdf.function)
|
||||
if (cond->luid == deviceId)
|
||||
{
|
||||
device = devices[iDev];
|
||||
break;
|
||||
@@ -204,19 +204,20 @@ const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverRe
|
||||
uint32_t sensorCount = ARRAY_SIZE(sensors);
|
||||
if (igclData.ffctlEnumTemperatureSensors(device, &sensorCount, sensors) == CTL_RESULT_SUCCESS && sensorCount > 0)
|
||||
{
|
||||
double sumValue = 0;
|
||||
uint32_t availableCount = 0;
|
||||
for (uint32_t iSensor = 0; iSensor < sensorCount; iSensor++)
|
||||
{
|
||||
double value;
|
||||
if (igclData.ffctlTemperatureGetState(sensors[iSensor], &value) == CTL_RESULT_SUCCESS)
|
||||
ctl_temp_properties_t props = { .Size = sizeof(props) };
|
||||
// The official sample code does not set Version
|
||||
// https://github.com/intel/drivers.gpu.control-library/blob/1bbacbf3814f2fd0d2b930cdf42fad83f3628db9/Samples/Telemetry_Samples/Sample_TelemetryAPP.cpp#L256
|
||||
if (igclData.ffctlTemperatureGetProperties(sensors[iSensor], &props) == CTL_RESULT_SUCCESS)
|
||||
{
|
||||
sumValue += value;
|
||||
availableCount++;
|
||||
if (props.type == CTL_TEMP_SENSORS_GPU)
|
||||
{
|
||||
*result.temp = props.maxTemperature;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (availableCount > 0)
|
||||
*result.temp = sumValue / availableCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -263,6 +263,49 @@ static const char* drmDetectIntelSpecific(FFGPUResult* gpu, const char* drmKey,
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char* pciDetectNouveauSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
|
||||
{
|
||||
if (options->temp)
|
||||
{
|
||||
const uint32_t pciDirLen = pciDir->length;
|
||||
ffStrbufAppendS(pciDir, "/hwmon/");
|
||||
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDir->chars);
|
||||
if (dirp)
|
||||
{
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dirp)))
|
||||
{
|
||||
if (entry->d_name[0] == '.') continue;
|
||||
ffStrbufAppendS(pciDir, entry->d_name);
|
||||
ffStrbufAppendS(pciDir, "/temp1_input");
|
||||
if (ffReadFileBuffer(pciDir->chars, buffer))
|
||||
{
|
||||
uint64_t value = ffStrbufToUInt(buffer, 0);
|
||||
if (value > 0) gpu->temperature = (double) value / 1000.0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ffStrbufSubstrBefore(pciDir, pciDirLen);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* drmDetectNouveauSpecific(FFGPUResult* gpu, const char* drmKey, FFstrbuf* buffer)
|
||||
{
|
||||
#if FF_HAVE_DRM
|
||||
ffStrbufSetS(buffer, "/dev/dri/");
|
||||
ffStrbufAppendS(buffer, drmKey);
|
||||
FF_AUTO_CLOSE_FD int fd = open(buffer->chars, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) return "Failed to open drm device";
|
||||
|
||||
return ffDrmDetectNouveau(gpu, fd);
|
||||
#else
|
||||
FF_UNUSED(gpu, drmKey, buffer);
|
||||
return "Fastfetch is not compiled with drm support";
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* deviceDir, const char* drmKey)
|
||||
{
|
||||
const uint32_t drmDirPathLength = deviceDir->length;
|
||||
@@ -369,6 +412,12 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
|
||||
if (options->driverSpecific && drmKey)
|
||||
drmDetectIntelSpecific(gpu, drmKey, buffer);
|
||||
}
|
||||
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && ffStrbufEqualS(&gpu->driver, "nouveau"))
|
||||
{
|
||||
pciDetectNouveauSpecific(options, gpu, deviceDir, buffer);
|
||||
if (options->driverSpecific && drmKey)
|
||||
drmDetectNouveauSpecific(gpu, drmKey, buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffGPUDetectDriverSpecific(options, gpu, (FFGpuDriverPciBusId) {
|
||||
|
||||
@@ -29,11 +29,12 @@ struct FFNvapiData {
|
||||
FF_LIBRARY_SYMBOL(nvapi_Unload)
|
||||
FF_LIBRARY_SYMBOL(nvapi_EnumPhysicalGPUs)
|
||||
FF_LIBRARY_SYMBOL(nvapi_GPU_GetRamType)
|
||||
FF_LIBRARY_SYMBOL(nvapi_GPU_GetGPUType)
|
||||
|
||||
bool inited;
|
||||
} nvapiData;
|
||||
|
||||
static const char* detectMemTypeByNvapi(FFGpuDriverResult* result)
|
||||
static const char* detectMoreByNvapi(FFGpuDriverResult* result)
|
||||
{
|
||||
if (!nvapiData.inited)
|
||||
{
|
||||
@@ -55,6 +56,7 @@ static const char* detectMemTypeByNvapi(FFGpuDriverResult* result)
|
||||
FF_NVAPI_INTERFACE(nvapi_Unload, NVAPI_INTERFACE_OFFSET_UNLOAD)
|
||||
FF_NVAPI_INTERFACE(nvapi_EnumPhysicalGPUs, NVAPI_INTERFACE_OFFSET_ENUM_PHYSICAL_GPUS)
|
||||
FF_NVAPI_INTERFACE(nvapi_GPU_GetRamType, NVAPI_INTERFACE_OFFSET_GPU_GET_RAM_TYPE)
|
||||
FF_NVAPI_INTERFACE(nvapi_GPU_GetGPUType, NVAPI_INTERFACE_OFFSET_GPU_GET_GPU_TYPE)
|
||||
#undef FF_NVAPI_INTERFACE
|
||||
|
||||
if (ffnvapi_Initialize() < 0)
|
||||
@@ -62,6 +64,7 @@ static const char* detectMemTypeByNvapi(FFGpuDriverResult* result)
|
||||
|
||||
nvapiData.ffnvapi_EnumPhysicalGPUs = ffnvapi_EnumPhysicalGPUs;
|
||||
nvapiData.ffnvapi_GPU_GetRamType = ffnvapi_GPU_GetRamType;
|
||||
nvapiData.ffnvapi_GPU_GetGPUType = ffnvapi_GPU_GetGPUType;
|
||||
nvapiData.ffnvapi_Unload = ffnvapi_Unload;
|
||||
|
||||
atexit((void*) ffnvapi_Unload);
|
||||
@@ -86,36 +89,53 @@ static const char* detectMemTypeByNvapi(FFGpuDriverResult* result)
|
||||
NvPhysicalGpuHandle gpuHandle = handles[gpuIndex];
|
||||
|
||||
NvApiGPUMemoryType memType;
|
||||
if (nvapiData.ffnvapi_GPU_GetRamType(gpuHandle, &memType) < 0)
|
||||
return "NvAPI_GPU_GetRamType() failed";
|
||||
|
||||
switch (memType)
|
||||
if (result->memoryType && nvapiData.ffnvapi_GPU_GetRamType(gpuHandle, &memType) == 0)
|
||||
{
|
||||
#define FF_NVAPI_MEMORY_TYPE(type) \
|
||||
case NVAPI_GPU_MEMORY_TYPE_##type: \
|
||||
ffStrbufSetStatic(result->memoryType, #type); \
|
||||
switch (memType)
|
||||
{
|
||||
#define FF_NVAPI_MEMORY_TYPE(type) \
|
||||
case NVAPI_GPU_MEMORY_TYPE_##type: \
|
||||
ffStrbufSetStatic(result->memoryType, #type); \
|
||||
break;
|
||||
FF_NVAPI_MEMORY_TYPE(UNKNOWN)
|
||||
FF_NVAPI_MEMORY_TYPE(SDRAM)
|
||||
FF_NVAPI_MEMORY_TYPE(DDR1)
|
||||
FF_NVAPI_MEMORY_TYPE(DDR2)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR2)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR3)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR4)
|
||||
FF_NVAPI_MEMORY_TYPE(DDR3)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR5)
|
||||
FF_NVAPI_MEMORY_TYPE(LPDDR2)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR5X)
|
||||
FF_NVAPI_MEMORY_TYPE(LPDDR3)
|
||||
FF_NVAPI_MEMORY_TYPE(LPDDR4)
|
||||
FF_NVAPI_MEMORY_TYPE(LPDDR5)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR6)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR6X)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR7)
|
||||
#undef FF_NVAPI_MEMORY_TYPE
|
||||
default:
|
||||
ffStrbufSetF(result->memoryType, "Unknown (%d)", memType);
|
||||
break;
|
||||
FF_NVAPI_MEMORY_TYPE(UNKNOWN)
|
||||
FF_NVAPI_MEMORY_TYPE(SDRAM)
|
||||
FF_NVAPI_MEMORY_TYPE(DDR1)
|
||||
FF_NVAPI_MEMORY_TYPE(DDR2)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR2)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR3)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR4)
|
||||
FF_NVAPI_MEMORY_TYPE(DDR3)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR5)
|
||||
FF_NVAPI_MEMORY_TYPE(LPDDR2)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR5X)
|
||||
FF_NVAPI_MEMORY_TYPE(LPDDR3)
|
||||
FF_NVAPI_MEMORY_TYPE(LPDDR4)
|
||||
FF_NVAPI_MEMORY_TYPE(LPDDR5)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR6)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR6X)
|
||||
FF_NVAPI_MEMORY_TYPE(GDDR7)
|
||||
#undef FF_NVAPI_MEMORY_TYPE
|
||||
default:
|
||||
ffStrbufSetF(result->memoryType, "Unknown (%d)", memType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NvApiGPUType gpuType;
|
||||
if (result->type && nvapiData.ffnvapi_GPU_GetGPUType(gpuHandle, &gpuType) == 0)
|
||||
{
|
||||
switch (gpuType)
|
||||
{
|
||||
case NV_SYSTEM_TYPE_IGPU:
|
||||
*result->type = FF_GPU_TYPE_INTEGRATED;
|
||||
break;
|
||||
case NV_SYSTEM_TYPE_DGPU:
|
||||
*result->type = FF_GPU_TYPE_DISCRETE;
|
||||
break;
|
||||
default:
|
||||
*result->type = FF_GPU_TYPE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -222,13 +242,13 @@ const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverR
|
||||
{
|
||||
*result.index = value;
|
||||
#ifdef _WIN32
|
||||
// Don't bother loading nvapi for GPU type detection only
|
||||
if (result.memoryType)
|
||||
detectMemTypeByNvapi(&result);
|
||||
detectMoreByNvapi(&result);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (result.temp)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
+357
-86
@@ -1,18 +1,13 @@
|
||||
#include "gpu.h"
|
||||
#include "common/library.h"
|
||||
#include "detection/gpu/gpu_driver_specific.h"
|
||||
#include "util/windows/unicode.h"
|
||||
#include "util/windows/registry.h"
|
||||
#include "util/mallocHelper.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/windows/nt.h"
|
||||
|
||||
#define INITGUID
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <devguid.h>
|
||||
|
||||
static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev)
|
||||
{
|
||||
if(*hdev)
|
||||
SetupDiDestroyDeviceInfoList(*hdev);
|
||||
}
|
||||
#include <cfgmgr32.h>
|
||||
|
||||
#define FF_EMPTY_GUID_STR L"{00000000-0000-0000-0000-000000000000}"
|
||||
enum { FF_GUID_STRLEN = sizeof(FF_EMPTY_GUID_STR) / sizeof(wchar_t) - 1 };
|
||||
@@ -22,23 +17,60 @@ const uint32_t regDirectxKeyPrefixLength = (uint32_t) __builtin_strlen("SOFTWARE
|
||||
wchar_t regDriverKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Class\\" FF_EMPTY_GUID_STR L"\\0000";
|
||||
const uint32_t regDriverKeyPrefixLength = (uint32_t) __builtin_strlen("SYSTEM\\CurrentControlSet\\Control\\Class\\");
|
||||
|
||||
#define GUID_DEVCLASS_DISPLAY_STRING L"{4d36e968-e325-11ce-bfc1-08002be10318}" // Found in <devguid.h>
|
||||
|
||||
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) =
|
||||
SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, DIGCF_PRESENT);
|
||||
FF_DEBUG("Starting GPU detection");
|
||||
|
||||
if(hdev == INVALID_HANDLE_VALUE)
|
||||
return "SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY) failed";
|
||||
|
||||
SP_DEVINFO_DATA did = { .cbSize = sizeof(did) };
|
||||
for (DWORD idev = 0; SetupDiEnumDeviceInfo(hdev, idev, &did); ++idev)
|
||||
ULONG devIdListSize = 0;
|
||||
if (CM_Get_Device_ID_List_SizeW(&devIdListSize, GUID_DEVCLASS_DISPLAY_STRING, CM_GETIDLIST_FILTER_CLASS | CM_GETIDLIST_FILTER_PRESENT) != CR_SUCCESS || devIdListSize <= 1)
|
||||
{
|
||||
FF_DEBUG("No display devices found, list size: %lu", devIdListSize);
|
||||
return "No display devices found";
|
||||
}
|
||||
|
||||
FF_DEBUG("Found device ID list size: %lu", devIdListSize);
|
||||
|
||||
FF_AUTO_FREE DEVINSTID_W devIdList = malloc(devIdListSize * sizeof(*devIdList));
|
||||
|
||||
if (CM_Get_Device_ID_ListW(GUID_DEVCLASS_DISPLAY_STRING, devIdList, devIdListSize, CM_GETIDLIST_FILTER_CLASS | CM_GETIDLIST_FILTER_PRESENT) != CR_SUCCESS)
|
||||
{
|
||||
FF_DEBUG("CM_Get_Device_ID_ListW failed");
|
||||
return "CM_Get_Device_ID_ListW failed";
|
||||
}
|
||||
|
||||
int deviceCount = 0;
|
||||
for (wchar_t* devId = devIdList; *devId; devId += wcslen(devId) + 1)
|
||||
{
|
||||
FF_DEBUG("Processing device ID: %ls", devId);
|
||||
|
||||
if (wcsncmp(devId, L"SWD\\", 4) == 0)
|
||||
{
|
||||
FF_DEBUG("Skipping SWD device interface to avoid duplicates");
|
||||
continue;
|
||||
}
|
||||
|
||||
DEVINST devInst = 0;
|
||||
|
||||
{
|
||||
if (CM_Locate_DevNodeW(&devInst, devId, CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS)
|
||||
{
|
||||
FF_DEBUG("Failed to get device instance ID or locate device node");
|
||||
continue;
|
||||
}
|
||||
FF_DEBUG("Device instance ID: %lu", devInst);
|
||||
}
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
|
||||
deviceCount++;
|
||||
FF_DEBUG("Added GPU #%d to list", deviceCount);
|
||||
|
||||
ffStrbufInit(&gpu->vendor);
|
||||
ffStrbufInit(&gpu->name);
|
||||
ffStrbufInit(&gpu->driver);
|
||||
ffStrbufInit(&gpu->memoryType);
|
||||
ffStrbufInitStatic(&gpu->platformApi, "SetupAPI");
|
||||
ffStrbufInitStatic(&gpu->platformApi, "CM API");
|
||||
gpu->index = FF_GPU_INDEX_UNSET;
|
||||
gpu->temperature = FF_GPU_TEMP_UNSET;
|
||||
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
|
||||
@@ -48,87 +80,150 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
gpu->deviceId = 0;
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
|
||||
uint32_t pciBus = 0, pciAddr = 0, pciDev = 0, pciFunc = 0;
|
||||
if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_BUSNUMBER, NULL, (PBYTE) &pciBus, sizeof(pciBus), NULL) &&
|
||||
SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_ADDRESS, NULL, (PBYTE) &pciAddr, sizeof(pciAddr), NULL))
|
||||
unsigned vendorId = 0, deviceId = 0, subSystemId = 0, revId = 0;
|
||||
if (swscanf(devId, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId) == 4)
|
||||
{
|
||||
pciDev = (pciAddr >> 16) & 0xFFFF;
|
||||
pciFunc = pciAddr & 0xFFFF;
|
||||
gpu->deviceId = (pciBus * 1000ull) + (pciDev * 10ull) + pciFunc;
|
||||
pciAddr = 1; // Set to 1 to indicate that the device is a PCI device
|
||||
FF_DEBUG("Parsed PCI IDs - Vendor: 0x%x, Device: 0x%x, SubSystem: 0x%x, Rev: 0x%x", vendorId, deviceId, subSystemId, revId);
|
||||
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to parse PCI device information from instance ID");
|
||||
}
|
||||
|
||||
wchar_t buffer[256];
|
||||
uint32_t pciBus = 0, pciAddr = 0, pciDev = 0, pciFunc = 0;
|
||||
|
||||
ULONG pciBufLen = sizeof(pciBus);
|
||||
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_BUSNUMBER, NULL, &pciBus, &pciBufLen, 0) == CR_SUCCESS)
|
||||
{
|
||||
pciBufLen = sizeof(pciAddr);
|
||||
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_ADDRESS, NULL, &pciAddr, &pciBufLen, 0) == CR_SUCCESS)
|
||||
{
|
||||
pciDev = (pciAddr >> 16) & 0xFFFF;
|
||||
pciFunc = pciAddr & 0xFFFF;
|
||||
gpu->deviceId = (pciBus * 1000ull) + (pciDev * 10ull) + pciFunc;
|
||||
pciAddr = 1; // Set to 1 to indicate that the device is a PCI device
|
||||
FF_DEBUG("PCI location - Bus: %u, Device: %u, Function: %u, DeviceID: %llu", pciBus, pciDev, pciFunc, gpu->deviceId);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get PCI address");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get PCI bus number");
|
||||
}
|
||||
|
||||
uint64_t adapterLuid = 0;
|
||||
|
||||
FF_HKEY_AUTO_DESTROY hVideoIdKey = SetupDiOpenDevRegKey(hdev, &did, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
|
||||
if (!hVideoIdKey) continue;
|
||||
DWORD bufferLen = sizeof(buffer);
|
||||
if (RegGetValueW(hVideoIdKey, NULL, L"VideoID", RRF_RT_REG_SZ, NULL, buffer, &bufferLen) == ERROR_SUCCESS &&
|
||||
bufferLen == (FF_GUID_STRLEN + 1) * sizeof(wchar_t))
|
||||
FF_HKEY_AUTO_DESTROY hVideoIdKey = NULL;
|
||||
|
||||
wchar_t buffer[256];
|
||||
ULONG bufferLen = 0;
|
||||
if (CM_Open_DevNode_Key(devInst, KEY_QUERY_VALUE, 0, RegDisposition_OpenExisting, &hVideoIdKey, CM_REGISTRY_HARDWARE) == CR_SUCCESS)
|
||||
{
|
||||
wmemcpy(regDirectxKey + regDirectxKeyPrefixLength, buffer, FF_GUID_STRLEN);
|
||||
FF_HKEY_AUTO_DESTROY hDirectxKey = NULL;
|
||||
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDirectxKey, &hDirectxKey, NULL))
|
||||
FF_DEBUG("Opened device node registry key");
|
||||
bufferLen = sizeof(buffer);
|
||||
if (RegGetValueW(hVideoIdKey, NULL, L"VideoID", RRF_RT_REG_SZ, NULL, buffer, &bufferLen) == ERROR_SUCCESS &&
|
||||
bufferLen == (FF_GUID_STRLEN + 1) * sizeof(wchar_t))
|
||||
{
|
||||
uint32_t vendorId = 0;
|
||||
if(ffRegReadUint(hDirectxKey, L"VendorId", &vendorId, NULL) && vendorId)
|
||||
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
|
||||
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL)
|
||||
gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
|
||||
uint64_t dedicatedVideoMemory = 0;
|
||||
if(ffRegReadUint64(hDirectxKey, L"DedicatedVideoMemory", &dedicatedVideoMemory, NULL))
|
||||
FF_DEBUG("Found VideoID: %ls", buffer);
|
||||
wmemcpy(regDirectxKey + regDirectxKeyPrefixLength, buffer, FF_GUID_STRLEN);
|
||||
FF_HKEY_AUTO_DESTROY hDirectxKey = NULL;
|
||||
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDirectxKey, &hDirectxKey, NULL))
|
||||
{
|
||||
if (gpu->type == FF_GPU_TYPE_UNKNOWN)
|
||||
gpu->type = dedicatedVideoMemory >= 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED;
|
||||
FF_DEBUG("Opened DirectX registry key");
|
||||
|
||||
if (gpu->vendor.length == 0)
|
||||
{
|
||||
uint32_t vendorId = 0;
|
||||
if(ffRegReadUint(hDirectxKey, L"VendorId", &vendorId, NULL) && vendorId)
|
||||
{
|
||||
FF_DEBUG("Found vendor ID from DirectX registry: 0x%x", vendorId);
|
||||
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
|
||||
}
|
||||
}
|
||||
|
||||
if (ffRegReadStrbuf(hDirectxKey, L"Description", &gpu->name, NULL))
|
||||
FF_DEBUG("Found GPU description: %s", gpu->name.chars);
|
||||
|
||||
if (ffRegReadUint64(hDirectxKey, L"DedicatedVideoMemory", &gpu->dedicated.total, NULL))
|
||||
FF_DEBUG("Found dedicated video memory: %llu bytes", gpu->dedicated.total);
|
||||
|
||||
if (ffRegReadUint64(hDirectxKey, L"DedicatedSystemMemory", &gpu->shared.total, NULL))
|
||||
{
|
||||
FF_DEBUG("Found dedicated system memory: %llu bytes", gpu->shared.total);
|
||||
uint64_t sharedSystemMemory = 0;
|
||||
if (ffRegReadUint64(hDirectxKey, L"SharedSystemMemory", &sharedSystemMemory, NULL))
|
||||
{
|
||||
gpu->shared.total += sharedSystemMemory;
|
||||
FF_DEBUG("Added shared system memory: %llu bytes, total shared: %llu bytes", sharedSystemMemory, gpu->shared.total);
|
||||
}
|
||||
}
|
||||
|
||||
if (ffRegReadUint64(hDirectxKey, L"AdapterLuid", &adapterLuid, NULL))
|
||||
{
|
||||
FF_DEBUG("Found adapter LUID: %llu", adapterLuid);
|
||||
if (!gpu->deviceId) gpu->deviceId = adapterLuid;
|
||||
}
|
||||
|
||||
uint32_t featureLevel = 0;
|
||||
if(ffRegReadUint(hDirectxKey, L"MaxD3D12FeatureLevel", &featureLevel, NULL) && featureLevel)
|
||||
{
|
||||
FF_DEBUG("Found D3D12 feature level: 0x%x", featureLevel);
|
||||
ffStrbufSetF(&gpu->platformApi, "Direct3D 12.%u", (featureLevel & 0x0F00) >> 8);
|
||||
}
|
||||
else if(ffRegReadUint(hDirectxKey, L"MaxD3D11FeatureLevel", &featureLevel, NULL) && featureLevel)
|
||||
{
|
||||
FF_DEBUG("Found D3D11 feature level: 0x%x", featureLevel);
|
||||
ffStrbufSetF(&gpu->platformApi, "Direct3D 11.%u", (featureLevel & 0x0F00) >> 8);
|
||||
}
|
||||
|
||||
uint64_t driverVersion = 0;
|
||||
if(ffRegReadUint64(hDirectxKey, L"DriverVersion", &driverVersion, NULL) && driverVersion)
|
||||
{
|
||||
FF_DEBUG("Found driver version: %llu", driverVersion);
|
||||
ffStrbufSetF(&gpu->driver, "%u.%u.%u.%u",
|
||||
(unsigned) (driverVersion >> 48) & 0xFFFF,
|
||||
(unsigned) (driverVersion >> 32) & 0xFFFF,
|
||||
(unsigned) (driverVersion >> 16) & 0xFFFF,
|
||||
(unsigned) (driverVersion >> 0) & 0xFFFF
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t dedicatedSystemMemory, sharedSystemMemory;
|
||||
if(ffRegReadUint64(hDirectxKey, L"DedicatedSystemMemory", &dedicatedSystemMemory, NULL) &&
|
||||
ffRegReadUint64(hDirectxKey, L"SharedSystemMemory", &sharedSystemMemory, NULL))
|
||||
else
|
||||
{
|
||||
gpu->dedicated.total = dedicatedVideoMemory + dedicatedSystemMemory;
|
||||
gpu->shared.total = sharedSystemMemory;
|
||||
}
|
||||
|
||||
if (ffRegReadUint64(hDirectxKey, L"AdapterLuid", &adapterLuid, NULL))
|
||||
{
|
||||
if (!gpu->deviceId) gpu->deviceId = adapterLuid;
|
||||
}
|
||||
|
||||
uint32_t featureLevel = 0;
|
||||
if(ffRegReadUint(hDirectxKey, L"MaxD3D12FeatureLevel", &featureLevel, NULL) && featureLevel)
|
||||
ffStrbufSetF(&gpu->platformApi, "Direct3D 12.%u", (featureLevel & 0x0F00) >> 8);
|
||||
else if(ffRegReadUint(hDirectxKey, L"MaxD3D11FeatureLevel", &featureLevel, NULL) && featureLevel)
|
||||
ffStrbufSetF(&gpu->platformApi, "Direct3D 11.%u", (featureLevel & 0x0F00) >> 8);
|
||||
|
||||
uint64_t driverVersion = 0;
|
||||
if(ffRegReadUint64(hDirectxKey, L"DriverVersion", &driverVersion, NULL) && driverVersion)
|
||||
{
|
||||
ffStrbufSetF(&gpu->driver, "%u.%u.%u.%u",
|
||||
(unsigned) (driverVersion >> 48) & 0xFFFF,
|
||||
(unsigned) (driverVersion >> 32) & 0xFFFF,
|
||||
(unsigned) (driverVersion >> 16) & 0xFFFF,
|
||||
(unsigned) (driverVersion >> 0) & 0xFFFF
|
||||
);
|
||||
FF_DEBUG("Failed to open DirectX registry key");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get VideoID or invalid buffer length");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to open device node registry key");
|
||||
}
|
||||
|
||||
if (gpu->vendor.length == 0)
|
||||
if (gpu->vendor.length == 0 || gpu->name.length == 0)
|
||||
{
|
||||
FF_DEBUG("Trying fallback registry method for vendor/name");
|
||||
bufferLen = sizeof(buffer);
|
||||
if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_DRIVER, NULL, (PBYTE) buffer, sizeof(buffer), &bufferLen) && bufferLen == (FF_GUID_STRLEN + strlen("\\0000") + 1) * 2)
|
||||
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_DRIVER, NULL, buffer, &bufferLen, 0) == CR_SUCCESS &&
|
||||
bufferLen == (FF_GUID_STRLEN + strlen("\\0000") + 1) * 2)
|
||||
{
|
||||
FF_DEBUG("Found driver GUID: %ls", buffer);
|
||||
wmemcpy(regDriverKey + regDriverKeyPrefixLength, buffer, FF_GUID_STRLEN + strlen("\\0000"));
|
||||
FF_HKEY_AUTO_DESTROY hRegDriverKey = NULL;
|
||||
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDriverKey, &hRegDriverKey, NULL))
|
||||
{
|
||||
if (ffRegReadStrbuf(hRegDriverKey, L"ProviderName", &gpu->vendor, NULL))
|
||||
FF_DEBUG("Opened driver registry key");
|
||||
|
||||
if (gpu->vendor.length == 0 && ffRegReadStrbuf(hRegDriverKey, L"ProviderName", &gpu->vendor, NULL))
|
||||
{
|
||||
FF_DEBUG("Found provider name: %s", gpu->vendor.chars);
|
||||
if (ffStrbufContainS(&gpu->vendor, "Intel"))
|
||||
ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL);
|
||||
else if (ffStrbufContainS(&gpu->vendor, "NVIDIA"))
|
||||
@@ -136,25 +231,47 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
else if (ffStrbufContainS(&gpu->vendor, "AMD") || ffStrbufContainS(&gpu->vendor, "ATI"))
|
||||
ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD);
|
||||
}
|
||||
if (gpu->name.length == 0 && ffRegReadStrbuf(hRegDriverKey, L"DriverDesc", &gpu->name, NULL))
|
||||
FF_DEBUG("Found driver description: %s", gpu->name.chars);
|
||||
if (gpu->driver.length == 0 && ffRegReadStrbuf(hRegDriverKey, L"DriverVersion", &gpu->driver, NULL))
|
||||
FF_DEBUG("Found driver version: %s", gpu->driver.chars);
|
||||
if (gpu->dedicated.total == FF_GPU_VMEM_SIZE_UNSET)
|
||||
{
|
||||
if (!ffRegReadUint64(hRegDriverKey, L"HardwareInformation.qwMemorySize", &gpu->dedicated.total, NULL))
|
||||
{
|
||||
uint32_t memorySize = 0;
|
||||
if (ffRegReadUint(hRegDriverKey, L"HardwareInformation.MemorySize", &memorySize, NULL))
|
||||
{
|
||||
gpu->dedicated.total = memorySize;
|
||||
FF_DEBUG("Found memory size from hardware info: %u bytes", memorySize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Found qwMemorySize from hardware info: %llu bytes", gpu->dedicated.total);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to open driver registry key");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get driver GUID or invalid buffer length");
|
||||
}
|
||||
}
|
||||
|
||||
__typeof__(&ffDetectNvidiaGpuInfo) detectFn;
|
||||
const char* dllName;
|
||||
|
||||
if (getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName) && (options->temp || options->driverSpecific))
|
||||
if (options->driverSpecific && getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName))
|
||||
{
|
||||
unsigned vendorId = 0, deviceId = 0, subSystemId = 0, revId = 0;
|
||||
if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_HARDWAREID, NULL, (PBYTE) buffer, sizeof(buffer), NULL))
|
||||
{
|
||||
swscanf(buffer, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId);
|
||||
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
|
||||
}
|
||||
|
||||
FF_DEBUG("Calling driver-specific detection function for vendor: %s, DLL: %s", gpu->vendor.chars, dllName);
|
||||
detectFn(
|
||||
&(FFGpuDriverCondition) {
|
||||
.type = FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID
|
||||
.type = (deviceId > 0 ? FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID : 0)
|
||||
| (adapterLuid > 0 ? FF_GPU_DRIVER_CONDITION_TYPE_LUID : 0)
|
||||
| (pciAddr > 0 ? FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID : 0),
|
||||
.pciDeviceId = {
|
||||
@@ -185,14 +302,168 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
},
|
||||
dllName
|
||||
);
|
||||
FF_DEBUG("Driver-specific detection completed");
|
||||
}
|
||||
else if (options->driverSpecific)
|
||||
{
|
||||
FF_DEBUG("No driver-specific detection function found for vendor: %s", gpu->vendor.chars);
|
||||
}
|
||||
|
||||
if (!gpu->name.length)
|
||||
{
|
||||
if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_DEVICEDESC, NULL, (PBYTE) buffer, sizeof(buffer), NULL))
|
||||
FF_DEBUG("Trying to get device description as fallback");
|
||||
bufferLen = sizeof(buffer);
|
||||
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_DEVICEDESC, NULL, buffer, &bufferLen, 0) == CR_SUCCESS)
|
||||
{
|
||||
ffStrbufSetWS(&gpu->name, buffer);
|
||||
FF_DEBUG("Found device description: %s", gpu->name.chars);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get device description");
|
||||
}
|
||||
}
|
||||
|
||||
if (gpu->type == FF_GPU_TYPE_UNKNOWN && adapterLuid > 0)
|
||||
{
|
||||
FF_DEBUG("Trying to determine GPU type using D3DKMT APIs");
|
||||
HMODULE hgdi32 = GetModuleHandleW(L"gdi32.dll");
|
||||
if (hgdi32)
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL_LAZY(hgdi32, D3DKMTOpenAdapterFromLuid);
|
||||
if (ffD3DKMTOpenAdapterFromLuid) // Windows 8 and later
|
||||
{
|
||||
D3DKMT_OPENADAPTERFROMLUID openAdapterFromLuid = { .AdapterLuid = *(LUID*)&adapterLuid };
|
||||
if (NT_SUCCESS(ffD3DKMTOpenAdapterFromLuid(&openAdapterFromLuid)))
|
||||
{
|
||||
FF_DEBUG("Successfully opened adapter from LUID");
|
||||
|
||||
D3DKMT_ADAPTERTYPE adapterType = {};
|
||||
D3DKMT_QUERYADAPTERINFO queryAdapterInfo = {
|
||||
.hAdapter = openAdapterFromLuid.hAdapter,
|
||||
.Type = KMTQAITYPE_ADAPTERTYPE,
|
||||
.pPrivateDriverData = &adapterType,
|
||||
.PrivateDriverDataSize = sizeof(adapterType),
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo))) // Vista and later
|
||||
{
|
||||
FF_DEBUG("Queried adapter type - HybridDiscrete: %d, HybridIntegrated: %d", adapterType.HybridDiscrete, adapterType.HybridIntegrated);
|
||||
if (adapterType.HybridDiscrete)
|
||||
gpu->type = FF_GPU_TYPE_DISCRETE;
|
||||
else if (adapterType.HybridIntegrated)
|
||||
gpu->type = FF_GPU_TYPE_INTEGRATED;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to query adapter type");
|
||||
}
|
||||
|
||||
if (gpu->frequency == FF_GPU_FREQUENCY_UNSET)
|
||||
{
|
||||
FF_DEBUG("Trying to get GPU frequency information");
|
||||
for (ULONG nodeIdx = 0; ; nodeIdx++)
|
||||
{
|
||||
D3DKMT_NODEMETADATA nodeMetadata = {
|
||||
.NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx,
|
||||
};
|
||||
queryAdapterInfo = (D3DKMT_QUERYADAPTERINFO) {
|
||||
.hAdapter = openAdapterFromLuid.hAdapter,
|
||||
.Type = KMTQAITYPE_NODEMETADATA,
|
||||
.pPrivateDriverData = &nodeMetadata,
|
||||
.PrivateDriverDataSize = sizeof(nodeMetadata),
|
||||
};
|
||||
if (!NT_SUCCESS(D3DKMTQueryAdapterInfo(&queryAdapterInfo)))
|
||||
{
|
||||
FF_DEBUG("No more nodes to query (index %lu)", nodeIdx);
|
||||
break; // Windows 10 and later
|
||||
}
|
||||
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D)
|
||||
{
|
||||
FF_DEBUG("Skipping node %lu (not 3D engine)", nodeIdx);
|
||||
continue;
|
||||
}
|
||||
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_NODE2,
|
||||
.AdapterLuid = *(LUID*)&adapterLuid,
|
||||
.QueryNode2 = { .PhysicalAdapterIndex = 0, .NodeOrdinal = (UINT16) nodeIdx },
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics))) // Windows 11 (22H2) and later
|
||||
{
|
||||
gpu->frequency = (uint32_t) (queryStatistics.QueryResult.NodeInformation.NodePerfData.MaxFrequency / 1000 / 1000);
|
||||
FF_DEBUG("Found GPU frequency: %u MHz", gpu->frequency);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to query node statistics for node %lu", nodeIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
D3DKMT_CLOSEADAPTER closeAdapter = { .hAdapter = openAdapterFromLuid.hAdapter };
|
||||
(void) D3DKMTCloseAdapter(&closeAdapter);
|
||||
openAdapterFromLuid.hAdapter = 0;
|
||||
FF_DEBUG("Closed adapter handle");
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to open adapter from LUID");
|
||||
}
|
||||
|
||||
if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET)
|
||||
{
|
||||
FF_DEBUG("Trying to get GPU temperature");
|
||||
D3DKMT_QUERYSTATISTICS queryStatistics = {
|
||||
.Type = D3DKMT_QUERYSTATISTICS_PHYSICAL_ADAPTER,
|
||||
.AdapterLuid = *(LUID*)&adapterLuid,
|
||||
.QueryPhysAdapter = { .PhysicalAdapterIndex = 0 },
|
||||
};
|
||||
if (NT_SUCCESS(D3DKMTQueryStatistics(&queryStatistics)) &&
|
||||
queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature != 0)
|
||||
{
|
||||
gpu->temperature = queryStatistics.QueryResult.PhysAdapterInformation.AdapterPerfData.Temperature / 10.0;
|
||||
FF_DEBUG("Found GPU temperature: %.1f°C", gpu->temperature);
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get GPU temperature or temperature is 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("D3DKMTOpenAdapterFromLuid not available");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Failed to get gdi32.dll module handle");
|
||||
}
|
||||
}
|
||||
|
||||
if (gpu->type == FF_GPU_TYPE_UNKNOWN)
|
||||
{
|
||||
FF_DEBUG("Using fallback GPU type detection");
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL)
|
||||
{
|
||||
gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
FF_DEBUG("Intel GPU type determined: %s", gpu->type == FF_GPU_TYPE_INTEGRATED ? "Integrated" : "Discrete");
|
||||
}
|
||||
else if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET)
|
||||
{
|
||||
gpu->type = gpu->dedicated.total >= 1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED;
|
||||
FF_DEBUG("GPU type determined by memory size (%llu bytes): %s", gpu->dedicated.total, gpu->type == FF_GPU_TYPE_DISCRETE ? "Discrete" : "Integrated");
|
||||
}
|
||||
else
|
||||
{
|
||||
FF_DEBUG("Unable to determine GPU type");
|
||||
}
|
||||
}
|
||||
|
||||
FF_DEBUG("Completed processing GPU #%d - Vendor: %s, Name: %s, Type: %d", deviceCount, gpu->vendor.chars, gpu->name.chars, gpu->type);
|
||||
}
|
||||
|
||||
FF_DEBUG("GPU detection completed, found %d devices", deviceCount);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -120,8 +120,28 @@ typedef struct ctl_temp_handle_t* ctl_temp_handle_t;
|
||||
|
||||
// https://intel.github.io/drivers.gpu.control-library/Control/api.html#ctlenumtemperaturesensors
|
||||
extern ctl_result_t ctlEnumTemperatureSensors(ctl_device_adapter_handle_t hDAhandle, uint32_t* pCount, ctl_temp_handle_t* phTemperature);
|
||||
// https://intel.github.io/drivers.gpu.control-library/Control/api.html#ctltemperaturegetstate
|
||||
extern ctl_result_t ctlTemperatureGetState(ctl_temp_handle_t hTemperature, double* pTemperature);
|
||||
// https://intel.github.io/drivers.gpu.control-library/Control/api.html#_CPPv427ctlTemperatureGetProperties17ctl_temp_handle_tP21ctl_temp_properties_t
|
||||
|
||||
typedef enum ctl_temp_sensors_t
|
||||
{
|
||||
CTL_TEMP_SENSORS_GLOBAL = 0,
|
||||
CTL_TEMP_SENSORS_GPU = 1,
|
||||
CTL_TEMP_SENSORS_MEMORY = 2,
|
||||
CTL_TEMP_SENSORS_GLOBAL_MIN = 3,
|
||||
CTL_TEMP_SENSORS_GPU_MIN = 4,
|
||||
CTL_TEMP_SENSORS_MEMORY_MIN = 5,
|
||||
CTL_TEMP_SENSORS_MAX
|
||||
} ctl_temp_sensors_t;
|
||||
|
||||
typedef struct _ctl_temp_properties_t
|
||||
{
|
||||
uint32_t Size;
|
||||
uint8_t Version;
|
||||
ctl_temp_sensors_t type;
|
||||
double maxTemperature;
|
||||
} ctl_temp_properties_t;
|
||||
|
||||
extern ctl_result_t ctlTemperatureGetProperties(ctl_temp_handle_t hTemperature, ctl_temp_properties_t* pTemperature);
|
||||
// https://intel.github.io/drivers.gpu.control-library/Control/api.html#_CPPv420ctlEnumMemoryModules27ctl_device_adapter_handle_tP8uint32_tP16ctl_mem_handle_t
|
||||
|
||||
typedef struct ctl_mem_handle_t* ctl_mem_handle_t;
|
||||
|
||||
@@ -23,6 +23,13 @@ typedef enum NvApiGPUMemoryType
|
||||
NVAPI_GPU_MEMORY_TYPE_GDDR7,
|
||||
} NvApiGPUMemoryType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NV_SYSTEM_TYPE_GPU_UNKNOWN = 0,
|
||||
NV_SYSTEM_TYPE_IGPU = 1, // Integrated
|
||||
NV_SYSTEM_TYPE_DGPU = 2, // Discrete
|
||||
} NvApiGPUType;
|
||||
|
||||
typedef int NvAPI_Status; // 0 = success; < 0 = error
|
||||
typedef struct NvPhysicalGpuHandle* NvPhysicalGpuHandle;
|
||||
|
||||
@@ -32,6 +39,7 @@ typedef enum
|
||||
NVAPI_INTERFACE_OFFSET_UNLOAD = 0xD22BDD7E,
|
||||
NVAPI_INTERFACE_OFFSET_ENUM_PHYSICAL_GPUS = 0xE5AC921F,
|
||||
NVAPI_INTERFACE_OFFSET_GPU_GET_RAM_TYPE = 0x57F7CAAC,
|
||||
NVAPI_INTERFACE_OFFSET_GPU_GET_GPU_TYPE = 0xC33BAEB1,
|
||||
|
||||
NVAPI_INTERFACE_OFFSET_FORCE_UINT32 = 0xFFFFFFFF
|
||||
} NvApiInterfaceOffsets;
|
||||
@@ -42,3 +50,4 @@ extern NvAPI_Status nvapi_Initialize(void);
|
||||
extern NvAPI_Status nvapi_Unload(void);
|
||||
extern NvAPI_Status nvapi_EnumPhysicalGPUs(NvPhysicalGpuHandle* handles, int* count);
|
||||
extern NvAPI_Status nvapi_GPU_GetRamType(NvPhysicalGpuHandle handle, NvApiGPUMemoryType* memtype);
|
||||
extern NvAPI_Status nvapi_GPU_GetGPUType(NvPhysicalGpuHandle handle, NvApiGPUType* gpuType);
|
||||
|
||||
@@ -140,18 +140,45 @@ static void detectQtCt(char qver, FFQtResult* result)
|
||||
// by the same author and qt6ct understands qt5ct in qt6 applications as well.
|
||||
char file[] = "qtXct/qtXct.conf";
|
||||
file[2] = file[8] = qver;
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY font = ffStrbufCreate();
|
||||
|
||||
ffParsePropFileConfigValues(file, 3, (FFpropquery[]) {
|
||||
{"style=", &result->widgetStyle},
|
||||
{"icon_theme=", &result->icons},
|
||||
{"general=", &result->font}
|
||||
{"general=", &font}
|
||||
});
|
||||
|
||||
if (ffStrbufStartsWithC(&result->font, '@'))
|
||||
if (ffStrbufStartsWithC(&font, '@'))
|
||||
{
|
||||
// See QVariant notes on https://doc.qt.io/qt-5/qsettings.html and
|
||||
// https://github.com/fastfetch-cli/fastfetch/issues/1053#issuecomment-2197254769
|
||||
// Thankfully, newer versions use the more common font encoding.
|
||||
ffStrbufSetNS(&result->font, 5, file);
|
||||
ffStrbufSetNS(&font, 5, file);
|
||||
}
|
||||
else if (qver == '5')
|
||||
{
|
||||
// #1864
|
||||
const char *p = font.chars;
|
||||
|
||||
while (*p)
|
||||
{
|
||||
if (p[0] == '\\' && p[1] == 'x' && isxdigit(p[2]) && isxdigit(p[3]) && isxdigit(p[4]) && isxdigit(p[5]))
|
||||
{
|
||||
uint32_t codepoint = (uint32_t)strtoul((char[]) { p[2], p[3], p[4], p[5], '\0' }, NULL, 16);
|
||||
ffStrbufAppendUtf32CodePoint(&result->font, codepoint);
|
||||
p += 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufAppendC(&result->font, *p++);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufDestroy(&result->font);
|
||||
ffStrbufInitMove(&result->font, &font);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,14 +205,17 @@ const FFQtResult* ffDetectQt(void)
|
||||
ffStrbufInit(&result.wallpaper);
|
||||
|
||||
const FFDisplayServerResult* wmde = ffConnectDisplayServer();
|
||||
const char *qplatformtheme = getenv("QT_QPA_PLATFORMTHEME");
|
||||
|
||||
if(ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_PLASMA))
|
||||
detectPlasma(&result);
|
||||
else if(ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_LXQT))
|
||||
detectLXQt(&result);
|
||||
else if(ffStrSet(qplatformtheme) && (ffStrEquals(qplatformtheme, "qt5ct") || ffStrEquals(qplatformtheme, "qt6ct")))
|
||||
detectQtCt(qplatformtheme[2], &result);
|
||||
else
|
||||
{
|
||||
const char *qPlatformTheme = getenv("QT_QPA_PLATFORMTHEME");
|
||||
if(qPlatformTheme && (ffStrEquals(qPlatformTheme, "qt5ct") || ffStrEquals(qPlatformTheme, "qt6ct")))
|
||||
detectQtCt(qPlatformTheme[2], &result);
|
||||
}
|
||||
|
||||
if(ffStrbufEqualS(&result.widgetStyle, "kvantum") || ffStrbufEqualS(&result.widgetStyle, "kvantum-dark"))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/host/option.h"
|
||||
|
||||
typedef struct FFHostResult
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/icons/option.h"
|
||||
|
||||
typedef struct FFIconsResult
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/initsystem/option.h"
|
||||
|
||||
typedef struct FFInitSystemResult
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/lm/option.h"
|
||||
|
||||
typedef struct FFLMResult
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user