Compare commits

...

103 Commits

Author SHA1 Message Date
Carter Li ba8a21142f Merge pull request #1873 from fastfetch-cli/dev
Release: v2.49.0
2025-07-31 13:54:49 +08:00
李通洲 eca3625165 Battery / Uptime: update document of custom format 2025-07-31 13:32:18 +08:00
李通洲 677e920dc8 Battery: address copilot feedback 2025-07-31 10:51:53 +08:00
李通洲 38c0b24daa WM (Linux): fix a copy-paste error. 2025-07-31 10:50:45 +08:00
李通洲 4e05502d7a Release: v2.49.0 2025-07-31 10:42:09 +08:00
李通洲 d0928a63bf Doc: update readme 2025-07-30 16:18:04 +08:00
李通洲 aa0bbd5aee Percent: add option --percent-width 2025-07-30 15:06:16 +08:00
李通洲 ce554dbdd0 Uptime / Battery: add formatted duration string in custom format 2025-07-30 15:03:23 +08:00
李通洲 a0174e8666 Global: add new unit related options
1. `--<module>-space-before-unit`
2. `--duration-abbreviation`
3. various code refactoring and cleanups
2025-07-30 15:03:23 +08:00
李通洲 8be419cfd6 DiskIO: fix wrong /s assignment in custom format
Fix #1871
2025-07-30 09:05:33 +08:00
李通洲 ca79bae36e Packages (Windows): find scoop installation path by parsing scoop config file 2025-07-29 19:29:40 +08:00
Carter Li b1f0248fb3 Font (Linux): parse unicode escaped string stored by qt5ct
Fix #1864
2025-07-29 18:25:34 +08:00
Carter Li 77dd18466b FFstrbuf: add function ffStrbufAppendUtf32CodePoint 2025-07-29 16:36:21 +08:00
李通洲 5bb6e22679 GPU (Intel): read render engine temperature only 2025-07-29 14:23:56 +08:00
Carter Li 9ea9291ab8 WM (Linux): add labwc version detection support 2025-07-29 10:20:05 +08:00
Carter Li 6ec73863ab CPU (Android): make SOC detection more loose
It's less accurate, but higher chance to match.
2025-07-29 09:28:41 +08:00
Carter Li 8c2f93151d CPU (Android): add more QSD SOCs 2025-07-29 09:28:36 +08:00
李通洲 71ddc4ffc6 Disk: update default value of hideFolders
Fix #1869
2025-07-28 23:49:27 +08:00
李通洲 4909d693e3 Packages (Windows): don't rely on the existance of $SCOOP when detecting scoop packages
Fix #1868
2025-07-28 23:41:48 +08:00
李通洲 fe76bbe68a Logo (Builtin): remove Evolinx 2025-07-28 23:06:15 +08:00
李通洲 d1f20822f1 GPU (macOS): silence compiler warnings 2025-07-28 15:39:35 +08:00
李通洲 1ed804db52 Wifi (macOS): use sudo wdutil info on Tahoe
sudo, you know it.
2025-07-28 15:35:02 +08:00
李通洲 0e8b9bec2d FFstrbuf: add function ffStrbufSubstr 2025-07-28 15:35:02 +08:00
李通洲 a3182c31cb Chore: add FF_C_NONNULL 2025-07-28 15:35:02 +08:00
Darloli d2f2c76dfc Version: correct the variable name 'name' to 'project-name' 2025-07-26 08:33:34 +08:00
Darloli 2d1ae1b4c5 Users: correct the variable name 'session' to 'session-name' 2025-07-26 08:33:34 +08:00
李通洲 316409ecc3 LocalIP: cleanup printing code 2025-07-24 20:12:48 +08:00
李通洲 f0fc38bcb5 LocalIP (Windows): fix link speed calcuation 2025-07-24 20:11:04 +08:00
李通洲 e7a6b84107 LocalIP (Windows): ignore stopped interfaces 2025-07-24 19:31:41 +08:00
李通洲 02c3254961 Netif (Windows): correct default route detection (v2) 2025-07-24 19:31:41 +08:00
李通洲 a90e1d7ccf JsonSchema: improve definitions of colors and conditions [ci skip]
Fix #1862
2025-07-23 09:31:25 +08:00
Carter Li 6cdd64ac55 CI (Haiku): remove work around 2025-07-22 22:10:00 +08:00
Carter Li aa1201ffb8 Editor: correct the variable name full-path to path
Fix #1861
2025-07-22 22:06:26 +08:00
Carter Li 9b491c2f49 Wifi (Linux): fix interface status when the interface is up but not connected 2025-07-22 22:03:53 +08:00
李通洲 be3e2ba521 Presets: improve 25.jsonc; add verbose comments [ci skip] 2025-07-22 15:46:36 +08:00
李通洲 ccbb9e90ea Netif (Windows): fix default route detection when the default adapter is disabled
This commit addresses an issue where the default route detection on Windows incorrectly identified a disabled adapter as the default route. Previously, when a user disabled the adapter configured as the default route, the system still reported it as active due to stale routing table entries.
2025-07-21 20:24:43 +08:00
李通洲 d07fe997a9 CI (NetBSD): remove opencl support to fix building 2025-07-21 14:41:02 +08:00
李通洲 b60996bcd1 Packages (Linux): remove qi support
Close #1858
2025-07-21 14:19:12 +08:00
李通洲 272c0aad9c Packages (Linux): improve accuracy of flatpak count detection
Fix #1856
2025-07-21 14:06:19 +08:00
Carter Li 7566f10bb6 Logo (Builtin): add Aeon
Fix  #1855
2025-07-20 16:47:35 +08:00
Carter Li 847b76b863 GPU (Linux): add GPU temp detection for nouveau driver 2025-07-19 18:17:28 +08:00
Carter Li 5c0ad030cc GPU (Linux): support advanced detection for nouveau driver 2025-07-19 16:47:42 +08:00
Carter Li 02aafbd7f4 Merge pull request #1852 from fastfetch-cli/dev
Release: v2.48.1
2025-07-18 19:28:54 +08:00
Carter Li 72da8150bd Release: v2.48.1 2025-07-18 18:50:30 +08:00
Carter Li aca2b5336d WM (Linux): fix a copy-paste error 2025-07-18 18:04:00 +08:00
李通洲 c79b67efc1 Modules: don't report errors when condition key is set 2025-07-18 16:16:38 +08:00
李通洲 30af91ce8e Packages: add new option --packages-combined 2025-07-18 15:55:33 +08:00
李通洲 6c56348e54 JsonConfig: add modules[n].condition to run modules conditionally on different platforms 2025-07-18 15:45:25 +08:00
李通洲 531b25dc2f Version: replace WIN32 with Windows and Darwin with macOS 2025-07-18 15:08:40 +08:00
李通洲 8c6dc34654 Processing (*nix): fix compiling on Android 7 2025-07-18 13:27:17 +08:00
李通洲 cf2034b93e Processing (Windows): redirect unused std handles to NUL to avoid potential errors 2025-07-18 10:39:11 +08:00
李通洲 f7db03b8c4 Processing (*nix): use traditional fork-exec on Android and OpenBSD
Fix https://github.com/termux/termux-packages/issues/25369
2025-07-18 10:37:52 +08:00
Carter Li 8647e366f9 Packaging: update debian stuff [ci skip] 2025-07-17 16:27:07 +08:00
李通洲 1bf41d68f9 WM (Linux): add Openbox version detection 2025-07-17 14:15:35 +08:00
Carter Li 1ac1f1f889 Chore: silense compiler warnings 2025-07-16 15:19:48 +08:00
Carter Li 14f1278485 Release: v2.48.0 (#1848)
Co-authored-by: Dariqq <77271900+Dariqq@users.noreply.github.com>
Co-authored-by: Robert Nilson <109800336+ZBookCMD@users.noreply.github.com>
Co-authored-by: Juan Llamas <38849891+xoltia@users.noreply.github.com>
Co-authored-by: Angel Gustavo Lopez <143856402+Xray-OS@users.noreply.github.com>
2025-07-16 15:09:29 +08:00
李通洲 c2c6856159 Doc: optimize README; fix typo 2025-07-16 14:45:56 +08:00
李通洲 c89f3d2896 Release: v2.48.0 2025-07-16 14:33:22 +08:00
李通洲 8e773470df Logo: partially revert c5c0e54 2025-07-16 14:29:41 +08:00
李通洲 ea4fc7bdda Separator: remove formatArgs
It makes no sense.
2025-07-16 10:46:47 +08:00
李通洲 468d1e0903 Display: add option --fraction-ndigits
to control number of digits to print when formatting ordinary fraction numbers
2025-07-16 10:33:20 +08:00
李通洲 6851e7b082 Uptime / Users: add years, days-of-year and year-fraction
Like what `Disk` has
2025-07-16 09:53:09 +08:00
Carter Li a734f18fd5 Chore (Linux): silience compiler warnings 2025-07-15 23:33:26 +08:00
李通洲 30b1e29785 JsonSchema: update xxxFormat [ci skip] 2025-07-15 22:44:56 +08:00
李通洲 dde0c22195 Disk: add keys years, days-of-year and years-fraction
Fix #1847
2025-07-15 22:38:31 +08:00
Angel Gustavo Lopez 1311a809db Logo (Builtin): rename ada to xray_os (#1845)
* going back to xray_os

* going back to xray_os

---------

Co-authored-by: xray_os <xray_os>
2025-07-15 17:52:09 +08:00
李通洲 60a54f4880 CMake: reconfigure after adding / removing logos 2025-07-15 11:02:31 +08:00
李通洲 b4e65e7e48 README: document how to run multiple modules on command line [ci skip] 2025-07-15 10:48:37 +08:00
李通洲 6f3ed4a7b4 Processing (Linux): cache the index of $LANG 2025-07-15 10:22:56 +08:00
李通洲 cc8f8ff0f7 README: add DeepWiki badge [ci skip]
An interesting website
2025-07-15 08:51:15 +08:00
李通洲 8a00f2d901 GPU (Windows): fix compiling on x86-32 2025-07-15 00:04:43 +08:00
李通洲 c5c0e543e4 Logo: don't print builtin logo if the provided logo source cannot be found 2025-07-13 20:32:31 +08:00
李通洲 97255f37da Logo: load .raw files as raw image file 2025-07-13 20:32:31 +08:00
李通洲 fb8a04f97a Logo: don't load text files as image file
Fix #1843
2025-07-13 20:32:13 +08:00
李通洲 c3ce5ac947 Logo: don't parse file paths as builtin logo name 2025-07-13 20:15:10 +08:00
李通洲 eae69049b2 GPU (Windows): more method to detect device name 2025-07-13 01:39:38 +08:00
李通洲 102e8fcc72 GPU (Windows): detect GPU frequency by default
Supported on Windows 11 only
2025-07-13 01:16:46 +08:00
李通洲 8372708f98 GPU (Intel): prefer matching GPU with bus id 2025-07-13 01:16:46 +08:00
Juan Llamas 5011edc9d9 Packages (Linux): clean up nix user package detection 2025-07-12 23:57:51 +08:00
Juan Llamas 94a5228ffc Packages (Linux): add /etc/profiles/per-user detection for nix user packages 2025-07-12 23:57:51 +08:00
李通洲 382c0cd7c3 GPU (Windows): ignore invalid temps 2025-07-12 15:16:36 +08:00
李通洲 ab496b23b7 GPU (Windows): greatly improve performance of GPU temp detection
... when `--gpu-driver-specific` is not set. Supported on Windows 10 and later
2025-07-12 01:26:33 +08:00
李通洲 939f428da7 Logo (Builtin): add AxOS
Fix #1841
2025-07-11 14:01:30 +08:00
Robert Nilson 2d496c91e1 Logo (Builtin): add the Minimal System logo (#1837)
* Added logo of Minimal System

* Changed to spaces instead of tabs

* Removed paddings for minimal.txt

* Changed

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-07-10 03:31:49 -05:00
李通洲 442d58b646 Revert "GPU (macOS): update how integrated GPUs are detected"
This reverts commit 5381395c54.
2025-07-10 16:16:45 +08:00
李通洲 4d377b94b7 CI (Haiku): Apply work-around for work directory
From <cross-platform-actions/action#104 (comment)>
2025-07-10 14:40:25 +08:00
李通洲 7248868e33 Sound (macOS): improve device id detection 2025-07-10 10:42:23 +08:00
Carter Li 2eda6464ad Processing (Linux): use posix_spawnp for better performance 2025-07-09 23:56:02 +08:00
Carter Li 2622fc12a6 Chore (Linux): silence compiler warnings 2025-07-09 20:23:28 +08:00
李通洲 b178e43428 GPU (Windows): use nvapi to detect GPU type 2025-07-09 00:28:16 +08:00
李通洲 d00fca26c5 GPU (Windows): clean up 2025-07-09 00:08:13 +08:00
李通洲 23e4d1c4aa Chore (Windows): tidy 2025-07-07 22:34:23 +08:00
李通洲 6871c8bb73 GPU (Windows): fix Win7 compatibility 2025-07-07 22:34:04 +08:00
李通洲 61adc7cf64 GPU (Windows): new method to detect device type 2025-07-07 22:03:30 +08:00
李通洲 9ba30adcba GPU (Windows): add driver version fall back 2025-07-06 18:23:14 +08:00
李通洲 010b2c6262 CMake (Windows): remove setupapi dependency 2025-07-06 17:48:39 +08:00
李通洲 c65fb1a868 Battery (Windows): use CMAPI instead of SetupAPI 2025-07-06 17:04:51 +08:00
李通洲 2029679ea5 GPU (Windows): use CMAPI instead of SetupAPI
Improve performance
2025-07-06 17:04:31 +08:00
Carter Li 817f70c2cd Presets: add a new example
Fix #1832
2025-07-05 08:33:55 +08:00
Carter Li 3a1f0485f4 PhysicalDisk: unifying white space trimming 2025-07-05 08:31:21 +08:00
李通洲 64910d0781 OS (Linux): detect fedora variants
Fix #1830
2025-07-04 10:31:59 +08:00
Dariqq 821462e152 Chore: Add a missing semicolon (#1828)
Followup to a9f021ccb9
2025-07-03 20:16:14 -05:00
Carter Li b015fd579c Packaging: update debian stuff [ci skip] 2025-07-03 14:53:14 +08:00
154 changed files with 3367 additions and 814 deletions
+3 -5
View File
@@ -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
@@ -683,7 +683,6 @@ jobs:
haiku-amd64:
name: Haiku-amd64
runs-on: ubuntu-latest
if: false
permissions:
security-events: write
contents: read
@@ -695,13 +694,12 @@ jobs:
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
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
cmake --build . --target package --verbose -j4
@@ -709,7 +707,6 @@ jobs:
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
@@ -861,6 +858,7 @@ jobs:
- openbsd-amd64
- netbsd-amd64
- sunos-amd64
- haiku-amd64
- windows-amd64
- windows-aarch64
permissions:
+105
View File
@@ -1,3 +1,108 @@
# 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-with` to pad the percent value with spaces to a fixed width
* For example: `--percent-with 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:
+6 -4
View File
@@ -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.49.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
@@ -1582,7 +1585,6 @@ elseif(WIN32)
PRIVATE "ws2_32"
PRIVATE "ntdll"
PRIVATE "version"
PRIVATE "setupapi"
PRIVATE "hid"
PRIVATE "wtsapi32"
PRIVATE "imagehlp"
+2 -1
View File
@@ -10,6 +10,7 @@
[![GitHub release (with filter)](https://img.shields.io/github/v/release/fastfetch-cli/fastfetch?logo=github)](https://github.com/fastfetch-cli/fastfetch/releases)
[![latest packaged version(s)](https://repology.org/badge/latest-versions/fastfetch.svg)](https://repology.org/project/fastfetch/versions)
[![Packaging status](https://repology.org/badge/tiny-repos/fastfetch.svg)](https://repology.org/project/fastfetch/versions)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](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.
@@ -92,7 +93,7 @@ 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`
+12
View File
@@ -1,3 +1,15 @@
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
+1 -1
View File
@@ -1 +1 @@
fastfetch_2.46.0_source.buildinfo universe/utils optional
fastfetch_2.48.0_source.buildinfo universe/utils optional
+485 -14
View File
File diff suppressed because it is too large Load Diff
+94 -20
View File
@@ -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
*/
+49
View File
@@ -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"
]
}
+4 -1
View File
@@ -16,7 +16,10 @@
"format": "{release}"
},
"uptime",
"packages",
{
"type": "packages",
"combined": true
},
"shell",
{
"type": "display",
+87
View File
@@ -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');
}
}
}
+3
View File
@@ -0,0 +1,3 @@
#include "fastfetch.h"
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result);
+9 -2
View File
@@ -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");
@@ -108,6 +114,7 @@ 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_FLOAT && *(float*)arg->value > 0.0) || //Also is false for NaN
(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)) ||
+17
View File
@@ -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;
}
+3
View File
@@ -0,0 +1,3 @@
#include "fastfetch.h"
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result);
+21 -1
View File
@@ -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);
+9
View File
@@ -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;
}
+21 -2
View File
@@ -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;
}
+43
View File
@@ -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"
@@ -170,6 +171,25 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module)
}
}
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 +217,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
+1 -1
View File
@@ -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);
+2 -1
View File
@@ -3,7 +3,8 @@
#ifndef _WIN32
#include <net/if.h>
#else
#define IF_NAMESIZE 0
#include <ws2tcpip.h>
#include <iphlpapi.h>
#endif
bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex);
-3
View File
@@ -2,8 +2,5 @@
#include "fastfetch.h"
#ifndef _WIN32
const char* ffNetifGetDefaultRouteIfName();
#endif
uint32_t ffNetifGetDefaultRouteIfIndex();
+28 -6
View File
@@ -4,15 +4,15 @@
#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 ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
{
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)
{
@@ -24,9 +24,31 @@ bool ffNetifGetDefaultRouteImpl(FF_MAYBE_UNUSED char iface[IF_NAMESIZE + 1], uin
(row->DestinationPrefix.Prefix.Ipv6.sin6_family == AF_INET6 &&
IN6_IS_ADDR_UNSPECIFIED(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr))))
{
*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 = (row->DestinationPrefix.Prefix.Ipv4.sin_family == AF_INET) ? AF_INET : 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;
*ifIndex = row->InterfaceIndex;
if (WideCharToMultiByte(CP_UTF8, 0, ifRow.Alias, -1, iface, IF_NAMESIZE, NULL, NULL) > 0)
iface[IF_NAMESIZE] = '\0';
foundDefault = true;
}
}
}
}
+1 -1
View File
@@ -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>
-106
View File
@@ -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
-4
View File
@@ -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);
+2 -2
View File
@@ -175,7 +175,6 @@ void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConf
ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
else
ffStrbufAppendF(buffer, "\e[%sm", colorGreen);
}
else
{
@@ -187,7 +186,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)
{
+102 -12
View File
@@ -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)
+7 -1
View File
@@ -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);
+42
View File
@@ -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;
}
}
+3
View File
@@ -0,0 +1,3 @@
#include "fastfetch.h"
void ffSizeAppendNum(uint64_t bytes, FFstrbuf* result);
+6 -3
View File
@@ -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;
}
+44
View File
@@ -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;
}
+101 -2
View File
@@ -454,7 +454,8 @@
"remark": "Auto-detected based on isatty(1) by default",
"arg": {
"type": "bool",
"optional": true
"optional": true,
"default": false
}
},
{
@@ -500,6 +501,28 @@
"type": "color"
}
},
{
"long": "duration-abbreviation",
"desc": "Specify whether to abbreviate duration values",
"remark": "If true, the output will be in the form of \"1h 2m\" instead of \"1 hour, 2 mins\"",
"arg": {
"type": "bool",
"optional": true,
"default": false
}
},
{
"long": "duration-space-before-unit",
"desc": "Specify whether to put a space before the unit in duration values",
"arg": {
"type": "enum",
"enum": {
"default": "Use the default behavior of the module",
"always": "Always put a space before the unit",
"never": "Never put a space before the unit"
}
}
},
{
"long": "key-width",
"desc": "Align the width of keys to <num> characters",
@@ -623,6 +646,27 @@
"default": "light_red"
}
},
{
"long": "percent-space-before-unit",
"desc": "Specify whether to put a space before the percentage symbol",
"arg": {
"type": "enum",
"enum": {
"default": "Use the default behavior of the module",
"always": "Always put a space before the unit",
"never": "Never put a space before the unit"
}
}
},
{
"long": "percent-width",
"desc": "Specify the width of the percentage number, in number of characters",
"remark": "This option affects only percentage numbers, not bars",
"arg": {
"type": "num",
"default": 0
}
},
{
"long": "bar-char-elapsed",
"desc": "Set the character to use in the elapsed part of percentage bars",
@@ -711,6 +755,18 @@
"default": "YB"
}
},
{
"long": "size-space-before-unit",
"desc": "Specify whether to put a space before the unit",
"arg": {
"type": "enum",
"enum": {
"default": "Use the default behavior of the module",
"always": "Always put a space before the unit",
"never": "Never put a space before the unit"
}
}
},
{
"long": "freq-ndigits",
"desc": "Set the number of digits to keep after the decimal point when printing CPU/GPU frequency in GHz",
@@ -719,6 +775,27 @@
"default": 2
}
},
{
"long": "freq-space-before-unit",
"desc": "Specify whether to put a space before the unit",
"arg": {
"type": "enum",
"enum": {
"default": "Use the default behavior of the module",
"always": "Always put a space before the unit",
"never": "Never put a space before the unit"
}
}
},
{
"long": "fraction-ndigits",
"desc": "Set the number of digits to keep after the decimal point when printing ordinary fraction numbers",
"remark": "If negative, the number of digits will be automatically determined based on the value",
"arg": {
"type": "num",
"default": -1
}
},
{
"long": "temp-unit",
"desc": "Set the temperature unit",
@@ -767,6 +844,18 @@
"type": "color",
"default": "light_red"
}
},
{
"long": "temp-space-before-unit",
"desc": "Specify whether to put a space before the unit",
"arg": {
"type": "enum",
"enum": {
"default": "Use the default behavior of the module",
"always": "Always put a space before the unit",
"never": "Never put a space before the unit"
}
}
}
],
"Module specific": [
@@ -845,7 +934,7 @@
"desc": "A colon (semicolon on Windows) separated list of folder paths to hide from the disk output",
"arg": {
"type": "path",
"default": "/efi:/boot:/boot/efi"
"default": "/efi:/boot:/boot/efi:/boot/firmware"
}
},
{
@@ -981,6 +1070,16 @@
"default": "winget"
}
},
{
"long": "packages-combined",
"desc": "Whether to combine related package managers into single counts",
"remark": "e.g., nix-system + nix-user = nix",
"arg": {
"type": "bool",
"optional": true,
"default": false
}
},
{
"long": "display-compact-type",
"desc": "Specify whether all displays should be printed in one line",
+1 -1
View File
@@ -15,7 +15,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);
+19 -28
View File
@@ -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);
}
{
@@ -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);
}
+61 -71
View File
@@ -100,85 +100,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 -1
View File
@@ -283,7 +283,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
View File
@@ -67,6 +67,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);
+4 -1
View File
@@ -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)
+2
View File
@@ -118,6 +118,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) {
+25 -1
View File
@@ -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) &paramsGlobal,
.pointer = (uintptr_t) &paramsGlobal,
.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"
+17 -16
View File
@@ -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;
}
}
+49
View File
@@ -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) {
+51 -31
View File
@@ -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;
+193 -84
View File
@@ -1,18 +1,15 @@
#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"
#define INITGUID
#include <windows.h>
#include <setupapi.h>
#include <devguid.h>
static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev)
{
if(*hdev)
SetupDiDestroyDeviceInfoList(*hdev);
}
#include <cfgmgr32.h>
#include <ntddvdeo.h>
#include <devpkey.h>
#include "util/windows/nt.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 };
@@ -24,21 +21,38 @@ const uint32_t regDriverKeyPrefixLength = (uint32_t) __builtin_strlen("SYSTEM\\C
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
{
HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) =
SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, DIGCF_PRESENT);
ULONG devInfListSize = 0;
if (CM_Get_Device_Interface_List_SizeW(&devInfListSize, (LPGUID)&GUID_DEVINTERFACE_DISPLAY_ADAPTER, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || devInfListSize <= 1)
return "No display devices found";
if(hdev == INVALID_HANDLE_VALUE)
return "SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY) failed";
FF_AUTO_FREE DEVINSTID_W devInfList = malloc(devInfListSize * sizeof(*devInfList));
SP_DEVINFO_DATA did = { .cbSize = sizeof(did) };
for (DWORD idev = 0; SetupDiEnumDeviceInfo(hdev, idev, &did); ++idev)
if (CM_Get_Device_Interface_ListW((LPGUID)&GUID_DEVINTERFACE_DISPLAY_ADAPTER, NULL, devInfList, devInfListSize, CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)
return "CM_Get_Device_Interface_ListW failed";
for (wchar_t* devInf = devInfList; *devInf; devInf += wcslen(devInf) + 1)
{
if (wcsncmp(devInf, L"\\\\?\\ROOT#BasicDisplay#", 22) == 0)
continue; // Skip Microsoft Basic Display Adapter
DEVINST devInst = 0;
wchar_t buffer[256];
ULONG bufferLen = 0;
{
DEVPROPTYPE propertyType;
bufferLen = sizeof(buffer);
if (CM_Get_Device_Interface_PropertyW(devInf, &DEVPKEY_Device_InstanceId, &propertyType, (PBYTE) buffer, &bufferLen, 0) != CR_SUCCESS ||
CM_Locate_DevNodeW(&devInst, buffer, CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS)
continue;
}
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
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,86 +62,91 @@ 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))
{
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
}
unsigned vendorId = 0, deviceId = 0, subSystemId = 0, revId = 0;
if (swscanf(buffer, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId) == 4)
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
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
}
}
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;
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))
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))
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;
}
if (gpu->vendor.length == 0)
{
uint32_t vendorId = 0;
if(ffRegReadUint(hDirectxKey, L"VendorId", &vendorId, NULL) && vendorId)
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
}
uint64_t dedicatedSystemMemory, sharedSystemMemory;
if(ffRegReadUint64(hDirectxKey, L"DedicatedSystemMemory", &dedicatedSystemMemory, NULL) &&
ffRegReadUint64(hDirectxKey, L"SharedSystemMemory", &sharedSystemMemory, NULL))
{
gpu->dedicated.total = dedicatedVideoMemory + dedicatedSystemMemory;
gpu->shared.total = sharedSystemMemory;
}
ffRegReadStrbuf(hDirectxKey, L"Description", &gpu->name, NULL);
if (ffRegReadUint64(hDirectxKey, L"AdapterLuid", &adapterLuid, NULL))
{
if (!gpu->deviceId) gpu->deviceId = adapterLuid;
}
ffRegReadUint64(hDirectxKey, L"DedicatedVideoMemory", &gpu->dedicated.total, NULL);
if (ffRegReadUint64(hDirectxKey, L"DedicatedSystemMemory", &gpu->shared.total, NULL))
{
uint64_t sharedSystemMemory = 0;
if (ffRegReadUint64(hDirectxKey, L"SharedSystemMemory", &sharedSystemMemory, NULL))
gpu->shared.total += sharedSystemMemory;
}
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);
if (ffRegReadUint64(hDirectxKey, L"AdapterLuid", &adapterLuid, NULL))
{
if (!gpu->deviceId) gpu->deviceId = adapterLuid;
}
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
);
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
);
}
}
}
}
if (gpu->vendor.length == 0)
if (gpu->vendor.length == 0 || gpu->name.length == 0)
{
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)
{
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))
if (gpu->vendor.length == 0 && ffRegReadStrbuf(hRegDriverKey, L"ProviderName", &gpu->vendor, NULL))
{
if (ffStrbufContainS(&gpu->vendor, "Intel"))
ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL);
@@ -136,6 +155,19 @@ 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);
if (gpu->driver.length == 0)
ffRegReadStrbuf(hRegDriverKey, L"DriverVersion", &gpu->driver, NULL);
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;
}
}
}
}
}
@@ -143,18 +175,11 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
__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));
}
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 = {
@@ -189,9 +214,93 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
if (!gpu->name.length)
{
if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_DEVICEDESC, NULL, (PBYTE) buffer, sizeof(buffer), NULL))
bufferLen = sizeof(buffer);
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_DEVICEDESC, NULL, buffer, &bufferLen, 0) == CR_SUCCESS)
ffStrbufSetWS(&gpu->name, buffer);
}
if (gpu->type == FF_GPU_TYPE_UNKNOWN && adapterLuid > 0)
{
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)))
{
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
{
if (adapterType.HybridDiscrete)
gpu->type = FF_GPU_TYPE_DISCRETE;
else if (adapterType.HybridIntegrated)
gpu->type = FF_GPU_TYPE_INTEGRATED;
}
if (gpu->frequency == FF_GPU_FREQUENCY_UNSET)
{
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))) break; // Windows 10 and later
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D) 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);
break;
}
}
}
D3DKMT_CLOSEADAPTER closeAdapter = { .hAdapter = openAdapterFromLuid.hAdapter };
(void) D3DKMTCloseAdapter(&closeAdapter);
openAdapterFromLuid.hAdapter = 0;
}
if (options->temp && gpu->temperature != 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;
}
}
}
}
if (gpu->type == FF_GPU_TYPE_UNKNOWN)
{
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL)
gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_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;
}
}
return NULL;
+22 -2
View File
@@ -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;
+9
View File
@@ -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);
+36 -6
View File
@@ -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 -1
View File
@@ -10,7 +10,7 @@ typedef struct FFLocalIpResult
FFstrbuf mac;
FFstrbuf flags;
int32_t mtu;
int32_t speed;
int32_t speed; // in Mbps
bool defaultRoute;
} FFLocalIpResult;
+4 -1
View File
@@ -95,6 +95,9 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
// Iterate through all of the adapters
for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next)
{
if (adapter->OperStatus != IfOperStatusUp)
continue;
bool isDefaultRoute = adapter->IfIndex == defaultRouteIfIndex;
if ((options->showType & FF_LOCALIP_TYPE_DEFAULT_ROUTE_ONLY_BIT) && !isDefaultRoute)
continue;
@@ -170,7 +173,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results)
{
FFLocalIpResult* result = FF_LIST_GET(FFLocalIpResult, *results, results->length - 1);
if (options->showType & FF_LOCALIP_TYPE_SPEED_BIT)
result->speed = (int32_t) (adapter->ReceiveLinkSpeed / 1000);
result->speed = (int32_t) (adapter->ReceiveLinkSpeed / 1000000);
if (options->showType & FF_LOCALIP_TYPE_MTU_BIT)
result->mtu = (int32_t) adapter->Mtu;
if (options->showType & FF_LOCALIP_TYPE_FLAGS_BIT)
+19 -2
View File
@@ -291,6 +291,21 @@ FF_MAYBE_UNUSED static bool detectDebianDerived(FFOSResult* result)
return false;
}
FF_MAYBE_UNUSED static bool detectFedoraVariant(FFOSResult* result)
{
if (ffStrbufEqualS(&result->variantID, "coreos")
|| ffStrbufEqualS(&result->variantID, "kinoite")
|| ffStrbufEqualS(&result->variantID, "sericea")
|| ffStrbufEqualS(&result->variantID, "silverblue"))
{
ffStrbufAppendC(&result->id, '-');
ffStrbufAppend(&result->id, &result->variantID);
ffStrbufSetStatic(&result->idLike, "fedora");
return true;
}
return false;
}
static void detectOS(FFOSResult* os)
{
#ifdef FF_CUSTOM_OS_RELEASE_PATH
@@ -341,13 +356,15 @@ void ffDetectOSImpl(FFOSResult* os)
detectOS(os);
#ifdef __linux__
if(ffStrbufIgnCaseEqualS(&os->id, "ubuntu"))
if(ffStrbufEqualS(&os->id, "ubuntu"))
getUbuntuFlavour(os);
else if(ffStrbufIgnCaseEqualS(&os->id, "debian"))
else if(ffStrbufEqualS(&os->id, "debian"))
{
if (!detectDebianDerived(os))
getDebianVersion(os);
}
else if(ffStrbufEqualS(&os->id, "fedora"))
detectFedoraVariant(os);
else if(ffStrbufEqualS(&os->id, "linuxmint"))
{
if (ffStrbufEqualS(&os->name, "LMDE"))
+2 -2
View File
@@ -36,9 +36,9 @@ typedef struct FFPackagesResult
uint32_t pkg;
uint32_t pkgsrc;
uint32_t pkgtool;
uint32_t qi;
uint32_t rpm;
uint32_t scoop;
uint32_t scoopUser;
uint32_t scoopGlobal;
uint32_t snap;
uint32_t soar;
uint32_t sorcery;
+41 -21
View File
@@ -519,6 +519,31 @@ static inline uint32_t getFlatpakRuntimePackages(FFstrbuf* baseDir)
return num_elements;
}
static inline uint32_t getFlatpakAppPackages(FFstrbuf* baseDir)
{
ffStrbufAppendS(baseDir, "app/");
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(baseDir->chars);
if (dirp == NULL)
return 0;
uint32_t appDirLength = baseDir->length;
uint32_t num_elements = 0;
struct dirent *entry;
while ((entry = readdir(dirp)) != NULL)
{
if(entry->d_type == DT_DIR && entry->d_name[0] != '.')
{
ffStrbufAppendS(baseDir, entry->d_name);
ffStrbufAppendS(baseDir, "/current");
if (ffPathExists(baseDir->chars, FF_PATHTYPE_ANY)) // Exclude deleted apps, #1856
++num_elements;
ffStrbufSubstrBefore(baseDir, appDirLength);
}
}
return num_elements;
}
static uint32_t getFlatpakPackages(FFstrbuf* baseDir, const char* dirname)
{
uint32_t num_elements = 0;
@@ -527,8 +552,7 @@ static uint32_t getFlatpakPackages(FFstrbuf* baseDir, const char* dirname)
ffStrbufAppendS(baseDir, "/flatpak/");
uint32_t flatpakDirLength = baseDir->length;
ffStrbufAppendS(baseDir, "app");
num_elements += ffPackagesGetNumElements(baseDir->chars, true);
num_elements += getFlatpakAppPackages(baseDir);
ffStrbufSubstrBefore(baseDir, flatpakDirLength);
num_elements += getFlatpakRuntimePackages(baseDir);
@@ -576,7 +600,6 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts,
}
if (!(options->disabled & FF_PACKAGES_FLAG_LINGLONG_BIT)) packageCounts->linglong += getNumElements(baseDir, "/var/lib/linglong/repo/refs/heads/main", true);
if (!(options->disabled & FF_PACKAGES_FLAG_PACSTALL_BIT)) packageCounts->pacstall += getNumElements(baseDir, "/var/lib/pacstall/metadata", false);
if (!(options->disabled & FF_PACKAGES_FLAG_QI_BIT)) packageCounts->qi += getNumStrings(baseDir, "/var/qi/installed_packages.list", "\n", "qi");
if (!(options->disabled & FF_PACKAGES_FLAG_PISI_BIT)) packageCounts->pisi += getNumElements(baseDir, "/var/lib/pisi/package", true);
if (!(options->disabled & FF_PACKAGES_FLAG_PKGSRC_BIT)) packageCounts->pkgsrc += getNumElements(baseDir, "/usr/pkg/pkgdb", DT_DIR);
}
@@ -646,33 +669,30 @@ void ffDetectPackagesImpl(FFPackagesResult* result, FFPackagesOptions* options)
#endif
ffStrbufSet(&baseDir, &instance.state.platform.homeDir);
if (!(options->disabled & FF_PACKAGES_FLAG_NIX_BIT))
{
// check if ~/.nix-profile exists
FF_STRBUF_AUTO_DESTROY profilePath = ffStrbufCreateCopy(&baseDir);
ffStrbufAppendS(&profilePath, ".nix-profile");
if (ffPathExists(profilePath.chars, FF_PATHTYPE_DIRECTORY))
{
result->nixUser += getNixPackages(&baseDir, ".nix-profile");
}
// Count packages from $HOME/.nix-profile
result->nixUser += getNixPackages(&baseDir, ".nix-profile");
// check if $XDG_STATE_HOME/nix/profile exists
FF_STRBUF_AUTO_DESTROY stateDir = ffStrbufCreate();
const char* stateHome = getenv("XDG_STATE_HOME");
if(ffStrSet(stateHome))
// Check in $XDG_STATE_HOME/nix/profile
FF_STRBUF_AUTO_DESTROY stateHome = ffStrbufCreate();
const char* stateHomeEnv = getenv("XDG_STATE_HOME");
if (ffStrSet(stateHomeEnv))
{
ffStrbufSetS(&stateDir, stateHome);
ffStrbufEnsureEndsWithC(&stateDir, '/');
ffStrbufSetS(&stateHome, stateHomeEnv);
ffStrbufEnsureEndsWithC(&stateHome, '/');
}
else
{
ffStrbufSet(&stateDir, &instance.state.platform.homeDir);
ffStrbufAppendS(&stateDir, ".local/state/");
ffStrbufSet(&stateHome, &instance.state.platform.homeDir);
ffStrbufAppendS(&stateHome, ".local/state/");
}
result->nixUser += getNixPackages(&stateHome, "nix/profile");
ffStrbufSet(&profilePath, &stateDir);
ffStrbufAppendS(&profilePath, "nix/profile");
result->nixUser += getNixPackages(&stateDir, "nix/profile");
// Check in /etc/profiles/per-user/$USER
FF_STRBUF_AUTO_DESTROY userPkgsDir = ffStrbufCreateStatic("/etc/profiles/per-user/");
result->nixUser += getNixPackages(&userPkgsDir, instance.state.platform.userName.chars);
}
if (!(options->disabled & FF_PACKAGES_FLAG_GUIX_BIT))
+39 -7
View File
@@ -1,6 +1,7 @@
#include "packages.h"
#include "common/processing.h"
#include "util/stringUtils.h"
#include "util/path.h"
#include <handleapi.h>
#include <fileapi.h>
@@ -33,22 +34,53 @@ static uint32_t getNumElements(const char* searchPath /* including `\*` suffix *
return counter;
}
static inline void wrapYyjsonFree(yyjson_doc** doc)
{
assert(doc);
if (*doc)
yyjson_doc_free(*doc);
}
static void detectScoop(FFPackagesResult* result)
{
FF_STRBUF_AUTO_DESTROY scoopPath = ffStrbufCreateA(MAX_PATH + 3);
ffStrbufAppendS(&scoopPath, instance.state.platform.homeDir.chars);
ffStrbufAppendS(&scoopPath, ".config/scoop/config.json");
const char* scoopEnv = getenv("SCOOP");
if(ffStrSet(scoopEnv))
yyjson_val* root = NULL;
yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_file(scoopPath.chars, 0, NULL, NULL);
if (doc)
{
ffStrbufAppendS(&scoopPath, scoopEnv);
root = yyjson_doc_get_root(doc);
if (!yyjson_is_obj(root)) root = NULL;
}
{
ffStrbufClear(&scoopPath);
if (root)
ffStrbufSetS(&scoopPath, yyjson_get_str(yyjson_obj_get(root, "root_path")));
if (scoopPath.length == 0)
{
ffStrbufSet(&scoopPath, &instance.state.platform.homeDir);
ffStrbufAppendS(&scoopPath, "/scoop");
}
ffStrbufAppendS(&scoopPath, "/apps/*");
result->scoopUser = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, "scoop");
}
else
{
ffStrbufAppendS(&scoopPath, instance.state.platform.homeDir.chars);
ffStrbufAppendS(&scoopPath, "/scoop/apps/*");
ffStrbufClear(&scoopPath);
if (root)
ffStrbufSetS(&scoopPath, yyjson_get_str(yyjson_obj_get(root, "global_path")));
if (scoopPath.length == 0)
{
ffStrbufSetS(&scoopPath, getenv("ProgramData"));
ffStrbufAppendS(&scoopPath, "/scoop");
}
ffStrbufAppendS(&scoopPath, "/apps/*");
result->scoopGlobal = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, "scoop");
}
result->scoop = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, "scoop");
}
static void detectChoco(FF_MAYBE_UNUSED FFPackagesResult* result)
@@ -113,9 +113,9 @@ const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options)
if (deviceCharacteristics)
{
ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductSerialNumberKey), &device->serial);
ffStrbufTrim(&device->serial, ' ');
ffStrbufTrimSpace(&device->serial);
ffCfDictGetString(deviceCharacteristics, CFSTR(kIOPropertyProductRevisionLevelKey), &device->revision);
ffStrbufTrim(&device->revision, ' ');
ffStrbufTrimRightSpace(&device->revision);
CFStringRef mediumType = (CFStringRef) CFDictionaryGetValue(deviceCharacteristics, CFSTR(kIOPropertyMediumTypeKey));
if (mediumType)
@@ -65,7 +65,7 @@ const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options)
FFPhysicalDiskResult* device = (FFPhysicalDiskResult*) ffListAdd(result);
ffStrbufInitF(&device->devPath, "/dev/%s", provider->lg_name);
ffStrbufInitMove(&device->serial, &identifier);
ffStrbufTrim(&device->serial, ' ');
ffStrbufTrimSpace(&device->serial);
ffStrbufInit(&device->revision);
ffStrbufInit(&device->interconnect);
switch (snapIter->device_type & DEVSTAT_TYPE_IF_MASK)
@@ -153,7 +153,7 @@ static void parsePhysicalDisk(int dfd, const char* devName, FFPhysicalDiskOption
{
ffStrbufInit(&device->serial);
if (ffReadFileBufferRelative(devfd, "serial", &device->serial))
ffStrbufTrim(&device->serial, ' ');
ffStrbufTrimSpace(&device->serial);
}
{
@@ -36,9 +36,15 @@ static int walkDevTree(di_node_t node, di_minor_t minor, struct FFWalkTreeBundle
char* buf;
if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-serial-no", &buf) > 0)
{
ffStrbufSetS(&device->serial, buf);
ffStrbufTrimSpace(&device->serial);
}
if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-revision-id", &buf) > 0)
{
ffStrbufSetS(&device->revision, buf);
ffStrbufTrimRightSpace(&device->revision);
}
if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "class", &buf) > 0)
ffStrbufSetS(&device->interconnect, buf);
@@ -62,14 +62,14 @@ static bool detectPhysicalDisk(const wchar_t* szDevice, FFlist* result, FFPhysic
if (sdd->SerialNumberOffset != 0)
{
ffStrbufSetS(&device->serial, (const char*) sddBuffer + sdd->SerialNumberOffset);
ffStrbufTrim(&device->serial, ' ');
ffStrbufTrimSpace(&device->serial);
}
ffStrbufInit(&device->revision);
if (sdd->ProductRevisionOffset != 0)
{
ffStrbufSetS(&device->revision, (const char*) sddBuffer + sdd->ProductRevisionOffset);
ffStrbufTrim(&device->revision, ' ');
ffStrbufTrimRightSpace(&device->revision);
}
device->type |= sdd->RemovableMedia ? FF_PHYSICALDISK_TYPE_REMOVABLE : FF_PHYSICALDISK_TYPE_FIXED;
+23 -11
View File
@@ -32,15 +32,6 @@ const char* ffDetectSound(FFlist* devices /* List of FFSoundDevice */)
{
AudioDeviceID deviceId = deviceIds[index];
FF_CFTYPE_AUTO_RELEASE CFStringRef name = NULL;
dataSize = sizeof(name);
if(AudioObjectGetPropertyData(deviceId, &(AudioObjectPropertyAddress){
kAudioObjectPropertyName,
kAudioObjectPropertyScopeOutput,
kAudioObjectPropertyElementMain
}, 0, NULL, &dataSize, &name) != kAudioHardwareNoError)
continue;
// Ignore input devices
if(AudioObjectGetPropertyDataSize(deviceId, &(AudioObjectPropertyAddress){
kAudioDevicePropertyStreams,
@@ -71,10 +62,31 @@ const char* ffDetectSound(FFlist* devices /* List of FFSoundDevice */)
device->main = deviceId == mainDeviceId;
device->active = false;
device->volume = FF_SOUND_VOLUME_UNKNOWN;
ffStrbufInitF(&device->identifier, "%u", (unsigned) deviceId);
ffStrbufInit(&device->identifier);
ffStrbufInit(&device->name);
ffStrbufInitStatic(&device->platformApi, "Core Audio");
ffCfStrGetString(name, &device->name);
FF_CFTYPE_AUTO_RELEASE CFStringRef uid = NULL;
dataSize = sizeof(uid);
if(AudioObjectGetPropertyData(deviceId, &(AudioObjectPropertyAddress) {
kAudioDevicePropertyDeviceUID,
kAudioObjectPropertyScopeOutput,
kAudioObjectPropertyElementMain
}, 0, NULL, &dataSize, &uid) == kAudioHardwareNoError)
ffCfStrGetString(uid, &device->identifier);
else
ffStrbufAppendF(&device->identifier, "ID-%u", (unsigned) deviceId);
FF_CFTYPE_AUTO_RELEASE CFStringRef name = NULL;
dataSize = sizeof(name);
if(AudioObjectGetPropertyData(deviceId, &(AudioObjectPropertyAddress){
kAudioObjectPropertyName,
kAudioObjectPropertyScopeOutput,
kAudioObjectPropertyElementMain
}, 0, NULL, &dataSize, &name) == kAudioHardwareNoError)
ffCfStrGetString(name, &device->name);
else
ffStrbufSet(&device->name, &device->identifier);
uint32_t muted;
dataSize = sizeof(muted);
+2 -2
View File
@@ -43,9 +43,9 @@
#elif defined(__FreeBSD__)
#define FF_SYSNAME "FreeBSD"
#elif defined(__APPLE__)
#define FF_SYSNAME "Darwin"
#define FF_SYSNAME "macOS"
#elif defined(_WIN32)
#define FF_SYSNAME "WIN32"
#define FF_SYSNAME "Windows"
#elif defined(__sun)
#define FF_SYSNAME "SunOS"
#elif defined(__OpenBSD__)
File diff suppressed because one or more lines are too long
+31 -4
View File
@@ -554,21 +554,48 @@ const char* ffDetectWifi(FF_MAYBE_UNUSED FFlist* result)
item->conn.channel = 0;
item->conn.frequency = 0;
char operstate;
ffStrbufSetF(&buffer, "/sys/class/net/%s/operstate", i->if_name);
if (!ffAppendFileBuffer(buffer.chars, &item->inf.status))
if (!ffReadFileData(buffer.chars, 1, &operstate))
{
FF_DEBUG("Failed to read operstate file");
continue;
}
ffStrbufTrimRightSpace(&item->inf.status);
FF_DEBUG("Interface status: %s", item->inf.status.chars);
if (!ffStrbufEqualS(&item->inf.status, "up"))
FF_DEBUG("Connection status: %c", operstate);
if (operstate != 'u')
{
FF_DEBUG("Skipping interface as it's not up");
ffStrbufSetStatic(&item->conn.status, "disconnected");
ffStrbufSetF(&buffer, "/sys/class/net/%s/flags", i->if_name);
char flags[16];
ssize_t len = ffReadFileData(buffer.chars, sizeof(flags), flags);
if (len <= 0)
{
FF_DEBUG("Failed to read flags file");
ffStrbufSetStatic(&item->inf.status, "unknown");
continue;
}
flags[len] = '\0';
FF_DEBUG("Interface flags: %s", flags);
unsigned flagsVal = (unsigned) strtoul(flags, NULL, 16);
if (flagsVal & IFF_UP)
{
ffStrbufSetStatic(&item->inf.status, "up");
FF_DEBUG("Interface is up but not connected");
}
else
{
ffStrbufSetStatic(&item->inf.status, "down");
FF_DEBUG("Interface is down");
}
continue;
}
ffStrbufSetStatic(&item->inf.status, "up");
FF_DEBUG("Trying to detect wifi with iw");
if (detectWifiWithIw(item, &buffer) != NULL)
{
+55 -3
View File
@@ -176,7 +176,7 @@ static const char* getWslg(FFstrbuf* result)
return NULL;
}
static bool extractCtFvWmVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, void *userdata)
static bool extractCommonWmVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, void *userdata)
{
int count = 0;
sscanf(line, "%*d.%*d.%*d%n", &count);
@@ -192,7 +192,7 @@ static const char* getCtwm(FFstrbuf* result)
const char* error = ffFindExecutableInPath("ctwm", &path);
if (error) return "Failed to find ctwm executable path";
ffBinaryExtractStrings(path.chars, extractCtFvWmVersion, result, (uint32_t) strlen("0.0.0"));
ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0"));
if (result->length > 0) return NULL;
if (ffProcessAppendStdOut(result, (char* const[]){
@@ -215,7 +215,7 @@ static const char* getFvwm(FFstrbuf* result)
const char* error = ffFindExecutableInPath("fvwm", &path);
if (error) return "Failed to find fvwm executable path";
ffBinaryExtractStrings(path.chars, extractCtFvWmVersion, result, (uint32_t) strlen("0.0.0"));
ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0"));
if (result->length > 0) return NULL;
if (ffProcessAppendStdOut(result, (char* const[]){
@@ -232,6 +232,52 @@ static const char* getFvwm(FFstrbuf* result)
return "Failed to run command `fvwm -version`";
}
static const char* getOpenbox(FFstrbuf* result)
{
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
const char* error = ffFindExecutableInPath("openbox", &path);
if (error) return "Failed to find openbox executable path";
ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0"));
if (result->length > 0) return NULL;
if (ffProcessAppendStdOut(result, (char* const[]){
path.chars,
"--version",
NULL
}) == NULL)
{ // Openbox 3.6.1\n...
ffStrbufSubstrBeforeFirstC(result, '\n');
ffStrbufSubstrAfterLastC(result, ' ');
return NULL;
}
return "Failed to run command `openbox --version`";
}
static const char* getLabwc(FFstrbuf* result)
{
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
const char* error = ffFindExecutableInPath("labwc", &path);
if (error) return "Failed to find labwc executable path";
ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0"));
if (result->length > 0) return NULL;
if (ffProcessAppendStdOut(result, (char* const[]){
path.chars,
"--version",
NULL
}) == NULL)
{ // labwc 0.9.0 (+xwayland +nls +rsvg +libsfdo)
ffStrbufSubstrAfterFirstC(result, ' ');
ffStrbufSubstrBeforeFirstC(result, ' ');
return NULL;
}
return "Failed to run command `labwc --version`";
}
const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE_UNUSED FFWMOptions* options)
{
if (!wmName)
@@ -255,5 +301,11 @@ const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE
if (ffStrbufEqualS(wmName, "fvwm"))
return getFvwm(result);
if (ffStrbufEqualS(wmName, "Openbox"))
return getOpenbox(result);
if (ffStrbufEqualS(wmName, "labwc"))
return getLabwc(result);
return "Unsupported WM";
}
+14
View File
@@ -0,0 +1,14 @@
⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷
⣿⡇ ⢸⣿
⣿⡇ ⢀⣀ ⣀⡀ ⢸⣿
⣿⣇ ⠸⣿⣄ ⣠⣿⠇ ⣸⣿
⢹⣿⡄ ⠙⠻⠿⠿⠟⠋ ⢠⣿⡏
⠹⣿⣦⡀ ⢀⣴⣿⠏
⠈⠛⢿⣶⣤⣄ ⣠⣤⣶⡿⠛⠁
$2 ⣠⣴⡿⠿⠛ ⠛⠿⢿⣦⣄
⣠⣾⠟⠉ ⠉⠻⣷⣄
⢰⣿⠏ ⢀⣤⣶⣶⣤⡀ ⠹⣿⡆
⣿⡟ ⢰⣿⠏⠁⠈⠹⣿⡆ ⢿⣿
⣿⡇ ⠈⠋ ⠙⠁ ⢸⣿
⣿⡇ ⢸⣿
⣿⣷⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣾⣿
+16
View File
@@ -0,0 +1,16 @@
▂🬭𜴧⎻𜷗𜴗𜴦𜵎⎻𜴧🬭▂
▁▂𜴧━𜵼𜵶𜶮▂🬭🬭𜴐🭗 🬁𜴜🬭🬭▂𜶮▁𜵁━𜴧▂▁
𜷓𜵐🬂🬂🮂𜶮𜴸𜴪━𜴆🮂▔ ▔🮂𜴆━𜴩𜵳𜴸🮂🬂🬂𜶚🬿
🭄🬨▍ 🬜🮂▔ ▁▂▂🬭🬭▂▂▁ ▔🮂🬪 🮈🬕𜶿
🭄🭙▐▎𜵫 🭃██████████🭎 🭢𜶡🮇▌🭤𜶿
🭄🭙 ▐🭄🭙 🭋█████🭜𜴦█████🭀 🭤🭏▌ 🭤🭏
𜵫🭗 🮉🭛 🭃███🭝🭙 🭥🭒███🭎 🭦▋ 🭢𜶡
▟🬮𜴧━🭷🭘 🭋████▄▂ ▂▄████🭀 🭣𜴇━𜴧🬯🭏
🭕𜴸𜴆╾🭺🬽 🭃██🭡 🭖██🭐 🭈𜵡╼𜴆🬡🭠
𜴤🬼 🮉🭀 🭋███΄ 🭤███🭀 🭋▊ 🭇𜵐
𜴠🬾 ▐𜴢🬾▁▁ 🭃██𜴍 𜶘██🭐 ▁▁🭉🭫▌ 🭉🭠
𜴠🬾🮈▎𜶚 ▔🮂🬂▀🬎🬎 🮅🬎▀🬂🮂▔ 𜵐🮇▌🭉𜴏
𜴢🭿▍ 𜴬▂▁ ▁▂🬜΄🮈🬲𜴏
🭥𜶡🬭🬭▂𜶭▁𜵁━𜴧▂▁ ▁▂𜴧━𜶷𜶭𜶮▂🬭🬭𜵫🭚
▔⎺𜴆━𜴩▔𜶮🮂🬂🬂𜴜🬼 🭇𜴐🬂🬂🮂𜶮▔𜴨━𜴆⎺▔
⠈🮂🭷𜴆╾𜶦𜷞🭄𜴬╼𜴆🬂🮂΄
-10
View File
@@ -1,10 +0,0 @@
#-----------------------------------------------------------------------#
|#SSSSSSSS\*SS\****SS\**SSSSSS\**SS\*******SSSSSS\*SS\***SS\*SS\***SS\*#|
|#SS**_____|SS*|***SS*|SS**__SS\*SS*|******\_SS**_|SSS\**SS*|SS*|**SS*|#|
|#SS*|******SS*|***SS*|SS*/**SS*|SS*|********SS*|**SSSS\*SS*|\SS\*SS**|#|
|#SSSSS\****\SS\**SS**|SS*|**SS*|SS*|********SS*|**SS*SS\SS*|*\SSSS**/*#|
|#SS**__|****\SS\SS**/*SS*|**SS*|SS*|********SS*|**SS*\SSSS*|*SS**SS<**#|
|#SS*|********\SSS**/**SS*|**SS*|SS*|********SS*|**SS*|\SSS*|SS**/\SS\*#|
|#SSSSSSSS\****\S**/****SSSSSS**|SSSSSSSS\*SSSSSS\*SS*|*\SS*|SS*/**SS*|#|
|#\________|****\_/*****\______/*\________|\______|\__|**\__|\__|**\__|#|
#-----------------------------------------------------------------------#
+18
View File
@@ -0,0 +1,18 @@
#####
#####
#####
######
######
#####
##### #####
###### #####
###### #####
###### ######
##### ######
##### #####
#####
######
######
######
#####
#####
+42 -28
View File
@@ -11,16 +11,6 @@ const FFlogo ffLogoUnknown = {
};
static const FFlogo A[] = {
//Ada
{
.names = {"Ada"},
.lines = FASTFETCH_DATATEXT_LOGO_ADA,
.colors = {
FF_COLOR_FG_256 "15",
FF_COLOR_FG_256 "14",
FF_COLOR_FG_256 "16",
}
},
// Adélie
{
.names = {"Adélie", "Adelie"},
@@ -40,6 +30,15 @@ static const FFlogo A[] = {
FF_COLOR_FG_CYAN,
},
},
// Aeon
{
.names = {"Aeon"},
.lines = FASTFETCH_DATATEXT_LOGO_AEON,
.colors = {
FF_COLOR_FG_256 "36",
FF_COLOR_FG_256 "36",
},
},
// Afterglow
{
.names = {"Afterglow"},
@@ -701,6 +700,15 @@ static const FFlogo A[] = {
FF_COLOR_FG_WHITE,
},
},
// AxOS
{
.names = {"AxOS"},
.lines = FASTFETCH_DATATEXT_LOGO_AXOS,
.colors = {
FF_COLOR_FG_RGB "222;6;255",
FF_COLOR_FG_RGB "222;6;255",
},
},
// Azos
{
.names = {"Azos"},
@@ -1615,16 +1623,6 @@ static const FFlogo E[] = {
.colorKeys = FF_COLOR_FG_BLUE,
.colorTitle = FF_COLOR_FG_DEFAULT,
},
// Evolinx
{
.names = {"Evolinx"},
.lines = FASTFETCH_DATATEXT_LOGO_EVOLINX,
.colors = {
FF_COLOR_FG_BLUE,
},
.colorKeys = FF_COLOR_FG_BLUE,
.colorTitle = FF_COLOR_FG_BLUE,
},
// EvolutionOS
{
.names = {"EvolutionOS"},
@@ -1771,8 +1769,7 @@ static const FFlogo F[] = {
},
// FedoraSilverblue
{
.names = {"Fedora_silverblue", "fedora-silverblue"},
.type = FF_LOGO_LINE_TYPE_ALTER_BIT,
.names = {"Fedora-Silverblue"},
.lines = FASTFETCH_DATATEXT_LOGO_FEDORA_SILVERBLUE,
.colors = {
FF_COLOR_FG_BLUE,
@@ -1784,8 +1781,7 @@ static const FFlogo F[] = {
},
// FedoraKinoite
{
.names = {"Fedora_kinoite", "fedora-kinoite"},
.type = FF_LOGO_LINE_TYPE_ALTER_BIT,
.names = {"Fedora-Kinoite"},
.lines = FASTFETCH_DATATEXT_LOGO_FEDORA_KINOITE,
.colors = {
FF_COLOR_FG_BLUE,
@@ -1796,8 +1792,7 @@ static const FFlogo F[] = {
},
// FedoraSericea
{
.names = {"Fedora_sericea", "fedora-sericea"},
.type = FF_LOGO_LINE_TYPE_ALTER_BIT,
.names = {"Fedora-Sericea"},
.lines = FASTFETCH_DATATEXT_LOGO_FEDORA_SERICEA,
.colors = {
FF_COLOR_FG_BLUE,
@@ -1808,8 +1803,7 @@ static const FFlogo F[] = {
},
// FedoraCoreOS
{
.names = {"Fedora_coreos", "fedora-coreos"},
.type = FF_LOGO_LINE_TYPE_ALTER_BIT,
.names = {"Fedora-CoreOS"},
.lines = FASTFETCH_DATATEXT_LOGO_FEDORA_COREOS,
.colors = {
FF_COLOR_FG_BLUE,
@@ -3109,6 +3103,16 @@ static const FFlogo M[] = {
.colorKeys = FF_COLOR_FG_LIGHT_BLACK,
.colorTitle = FF_COLOR_FG_DEFAULT,
},
// Minimal System
{
.names = {"Minimal_System"},
.lines = FASTFETCH_DATATEXT_LOGO_MINIMAL,
.colors = {
FF_COLOR_FG_RED,
},
.colorKeys = FF_COLOR_FG_CYAN,
.colorTitle = FF_COLOR_FG_DEFAULT,
},
// Minix
{
.names = {"Minix"},
@@ -5391,6 +5395,16 @@ static const FFlogo X[] = {
FF_COLOR_FG_DEFAULT,
},
},
//Xray_OS
{
.names = {"Xray_OS"},
.lines = FASTFETCH_DATATEXT_LOGO_XRAY_OS,
.colors = {
FF_COLOR_FG_256 "15",
FF_COLOR_FG_256 "14",
FF_COLOR_FG_256 "16",
}
},
// LAST
{},
};
+61 -29
View File
@@ -436,6 +436,13 @@ static void logoPrintNone(void)
static bool logoPrintBuiltinIfExists(const FFstrbuf* name, FFLogoSize size)
{
if(name->chars[0] == '~' || name->chars[0] == '.' || name->chars[0] == '/'
#if _WIN32
|| (ffCharIsEnglishAlphabet(name->chars[0]) && name->chars[1] == ':') // Windows drive letter
#endif
)
return false; // Paths
if(ffStrbufIgnCaseEqualS(name, "none"))
{
logoPrintNone();
@@ -466,21 +473,22 @@ static bool logoPrintData(bool doColorReplacement, FFstrbuf* source)
return true;
}
static void updateLogoPath(void)
static bool updateLogoPath(void)
{
FFOptionsLogo* options = &instance.config.logo;
if(ffPathExists(options->source.chars, FF_PATHTYPE_FILE))
return;
return true;
if (ffStrbufEqualS(&options->source, "-")) // stdin
return;
return true;
FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreate();
if (ffPathExpandEnv(options->source.chars, &fullPath) && ffPathExists(fullPath.chars, FF_PATHTYPE_FILE))
{
ffStrbufSet(&options->source, &fullPath);
return;
ffStrbufDestroy(&options->source);
ffStrbufInitMove(&options->source, &fullPath);
return true;
}
FF_LIST_FOR_EACH(FFstrbuf, dataDir, instance.state.platform.dataDirs)
@@ -492,10 +500,13 @@ static void updateLogoPath(void)
if(ffPathExists(fullPath.chars, FF_PATHTYPE_FILE))
{
ffStrbufSet(&options->source, &fullPath);
break;
ffStrbufDestroy(&options->source);
ffStrbufInitMove(&options->source, &fullPath);
return true;
}
}
return false;
}
static bool logoPrintFileIfExists(bool doColorReplacement, bool raw)
@@ -579,7 +590,13 @@ static bool logoTryKnownType(void)
return logoPrintData(false, &source);
}
updateLogoPath(); //We sure have a file, resolve relative paths
//We sure have a file, resolve relative paths
if (!updateLogoPath())
{
if (instance.config.display.showErrors)
fprintf(stderr, "Logo: Failed to resolve logo source: %s\n", options->source.chars);
return false;
}
if(options->type == FF_LOGO_TYPE_FILE)
return logoPrintFileIfExists(true, false);
@@ -642,31 +659,46 @@ void ffLogoPrint(void)
return;
//Make sure the logo path is set correctly.
updateLogoPath();
if (updateLogoPath())
{
if (ffStrbufEndsWithIgnCaseS(&options->source, ".raw"))
{
if(logoPrintFileIfExists(false, true))
return;
}
const FFTerminalResult* terminal = ffDetectTerminal();
if (!ffStrbufEndsWithIgnCaseS(&options->source, ".txt"))
{
const FFTerminalResult* terminal = ffDetectTerminal();
//Terminal emulators that support kitty graphics protocol.
bool supportsKitty =
ffStrbufIgnCaseEqualS(&terminal->processName, "kitty") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "konsole") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "wezterm") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "wayst") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "ghostty") ||
#ifdef __APPLE__
ffStrbufIgnCaseEqualS(&terminal->processName, "WarpTerminal") ||
#else
ffStrbufIgnCaseEqualS(&terminal->processName, "warp") ||
#endif
false;
//Terminal emulators that support kitty graphics protocol.
bool supportsKitty =
ffStrbufIgnCaseEqualS(&terminal->processName, "kitty") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "konsole") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "wezterm") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "wayst") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "ghostty") ||
#ifdef __APPLE__
ffStrbufIgnCaseEqualS(&terminal->processName, "WarpTerminal") ||
#else
ffStrbufIgnCaseEqualS(&terminal->processName, "warp") ||
#endif
false;
//Try to load the logo as an image. If it succeeds, print it and return.
if(logoPrintImageIfExists(supportsKitty ? FF_LOGO_TYPE_IMAGE_KITTY : FF_LOGO_TYPE_IMAGE_CHAFA, false))
return;
//Try to load the logo as an image. If it succeeds, print it and return.
if(logoPrintImageIfExists(supportsKitty ? FF_LOGO_TYPE_IMAGE_KITTY : FF_LOGO_TYPE_IMAGE_CHAFA, false))
return;
}
//Try to load the logo as a file. If it succeeds, print it and return.
if(logoPrintFileIfExists(true, false))
return;
//Try to load the logo as a file. If it succeeds, print it and return.
if(logoPrintFileIfExists(true, false))
return;
}
else
{
if (instance.config.display.showErrors)
fprintf(stderr, "Logo: Failed to resolve logo source: %s\n", options->source.chars);
}
logoPrintDetected(FF_LOGO_SIZE_UNKNOWN);
}
+10 -3
View File
@@ -1,7 +1,7 @@
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/percent.h"
#include "common/parsing.h"
#include "common/duration.h"
#include "common/temps.h"
#include "detection/battery/battery.h"
#include "modules/battery/battery.h"
@@ -59,7 +59,7 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin
if(str.length > 0)
ffStrbufAppendS(&str, " (");
ffParseDuration((uint32_t) result->timeRemaining, &str);
ffDurationAppendNum((uint32_t) result->timeRemaining, &str);
ffStrbufAppendS(&str, " remaining)");
}
}
@@ -101,6 +101,9 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin
ffPercentAppendBar(&capacityBar, result->capacity, options->percent, &options->moduleArgs);
FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate();
ffTempsAppendNum(result->temperature, &tempStr, options->tempConfig, &options->moduleArgs);
FF_STRBUF_AUTO_DESTROY timeStr = ffStrbufCreate();
if (result->timeRemaining > 0)
ffDurationAppendNum((uint32_t) result->timeRemaining, &timeStr);
FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
FF_FORMAT_ARG(result->manufacturer, "manufacturer"),
@@ -117,6 +120,7 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin
FF_FORMAT_ARG(hours, "time-hours"),
FF_FORMAT_ARG(minutes, "time-minutes"),
FF_FORMAT_ARG(seconds, "time-seconds"),
FF_FORMAT_ARG(timeStr, "time-formatted"),
}));
}
}
@@ -186,7 +190,7 @@ void ffParseBatteryJsonObject(FFBatteryOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
@@ -254,6 +258,8 @@ void ffGenerateBatteryJsonResult(FFBatteryOptions* options, yyjson_mut_doc* doc,
yyjson_mut_obj_add_uint(doc, obj, "cycleCount", battery->cycleCount);
if (battery->timeRemaining > 0)
yyjson_mut_obj_add_int(doc, obj, "timeRemaining", battery->timeRemaining);
else
yyjson_mut_obj_add_null(doc, obj, "timeRemaining");
}
FF_LIST_FOR_EACH(FFBatteryResult, battery, results)
@@ -290,6 +296,7 @@ static FFModuleBaseInfo ffModuleInfo = {
{"Battery time remaining hours", "time-hours"},
{"Battery time remaining minutes", "time-minutes"},
{"Battery time remaining seconds", "time-seconds"},
{"Battery time remaining (formatted)", "time-formatted"},
}))
};
+1 -1
View File
@@ -92,7 +92,7 @@ void ffParseBiosJsonObject(FFBiosOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -123,7 +123,7 @@ void ffParseBluetoothJsonObject(FFBluetoothOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -122,7 +122,7 @@ void ffParseBluetoothRadioJsonObject(FFBluetoothRadioOptions* options, yyjson_va
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -67,7 +67,7 @@ void ffParseBoardJsonObject(FFBoardOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -67,7 +67,7 @@ void ffParseBootmgrJsonObject(FFBootmgrOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -144,7 +144,7 @@ void ffParseBrightnessJsonObject(FFBrightnessOptions* options, yyjson_val* modul
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+7 -6
View File
@@ -1,6 +1,7 @@
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/percent.h"
#include "common/size.h"
#include "detection/btrfs/btrfs.h"
#include "modules/btrfs/btrfs.h"
#include "util/stringUtils.h"
@@ -34,11 +35,11 @@ static void printBtrfs(FFBtrfsOptions* options, FFBtrfsResult* result, uint8_t i
}
FF_STRBUF_AUTO_DESTROY usedPretty = ffStrbufCreate();
ffParseSize(used, &usedPretty);
ffSizeAppendNum(used, &usedPretty);
FF_STRBUF_AUTO_DESTROY allocatedPretty = ffStrbufCreate();
ffParseSize(allocated, &allocatedPretty);
ffSizeAppendNum(allocated, &allocatedPretty);
FF_STRBUF_AUTO_DESTROY totalPretty = ffStrbufCreate();
ffParseSize(total, &totalPretty);
ffSizeAppendNum(total, &totalPretty);
double usedPercentage = total > 0 ? (double) used / (double) total * 100.0 : 0;
double allocatedPercentage = total > 0 ? (double) allocated / (double) total * 100.0 : 0;
@@ -74,9 +75,9 @@ static void printBtrfs(FFBtrfsOptions* options, FFBtrfsResult* result, uint8_t i
ffPercentAppendBar(&allocatedPercentageBar, allocatedPercentage, options->percent, &options->moduleArgs);
FF_STRBUF_AUTO_DESTROY nodeSizePretty = ffStrbufCreate();
ffParseSize(result->nodeSize, &nodeSizePretty);
ffSizeAppendNum(result->nodeSize, &nodeSizePretty);
FF_STRBUF_AUTO_DESTROY sectorSizePretty = ffStrbufCreate();
ffParseSize(result->sectorSize, &sectorSizePretty);
ffSizeAppendNum(result->sectorSize, &sectorSizePretty);
FF_PRINT_FORMAT_CHECKED(buffer.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
FF_FORMAT_ARG(result->name, "name"),
@@ -149,7 +150,7 @@ void ffParseBtrfsJsonObject(FFBtrfsOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -84,7 +84,7 @@ void ffParseCameraJsonObject(FFCameraOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -68,7 +68,7 @@ void ffParseChassisJsonObject(FFChassisOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -181,7 +181,7 @@ void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -78,7 +78,7 @@ void ffParseCommandJsonObject(FFCommandOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+5 -4
View File
@@ -2,6 +2,7 @@
#include "common/jsonconfig.h"
#include "common/parsing.h"
#include "common/temps.h"
#include "common/frequency.h"
#include "detection/cpu/cpu.h"
#include "modules/cpu/cpu.h"
#include "util/stringUtils.h"
@@ -77,7 +78,7 @@ void ffPrintCPU(FFCPUOptions* options)
if(freq > 0)
{
ffStrbufAppendS(&str, " @ ");
ffParseFrequency(freq, &str);
ffFreqAppendNum(freq, &str);
}
if(cpu.temperature == cpu.temperature) //FF_CPU_TEMP_UNSET
@@ -91,9 +92,9 @@ void ffPrintCPU(FFCPUOptions* options)
else
{
FF_STRBUF_AUTO_DESTROY freqBase = ffStrbufCreate();
ffParseFrequency(cpu.frequencyBase, &freqBase);
ffFreqAppendNum(cpu.frequencyBase, &freqBase);
FF_STRBUF_AUTO_DESTROY freqMax = ffStrbufCreate();
ffParseFrequency(cpu.frequencyMax, &freqMax);
ffFreqAppendNum(cpu.frequencyMax, &freqMax);
FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate();
ffTempsAppendNum(cpu.temperature, &tempStr, options->tempConfig, &options->moduleArgs);
@@ -142,7 +143,7 @@ void ffParseCPUJsonObject(FFCPUOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+6 -5
View File
@@ -1,5 +1,6 @@
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/size.h"
#include "detection/cpucache/cpucache.h"
#include "modules/cpucache/cpucache.h"
#include "util/stringUtils.h"
@@ -45,7 +46,7 @@ static void printCPUCacheNormal(const FFCPUCacheResult* result, FFCPUCacheOption
ffStrbufAppendS(&buffer, ", ");
if (src->num > 1)
ffStrbufAppendF(&buffer, "%ux", src->num);
ffParseSize(src->size, &buffer);
ffSizeAppendNum(src->size, &buffer);
ffStrbufAppendF(&buffer, " (%c)", typeStr);
sum += src->size * src->num;
@@ -59,7 +60,7 @@ static void printCPUCacheNormal(const FFCPUCacheResult* result, FFCPUCacheOption
else
{
FF_STRBUF_AUTO_DESTROY buffer2 = ffStrbufCreate();
ffParseSize(sum, &buffer2);
ffSizeAppendNum(sum, &buffer2);
FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
FF_FORMAT_ARG(buffer, "result"),
FF_FORMAT_ARG(buffer2, "sum"),
@@ -79,7 +80,7 @@ static void printCPUCacheCompact(const FFCPUCacheResult* result, FFCPUCacheOptio
uint32_t value = 0;
FF_LIST_FOR_EACH(FFCPUCache, src, result->caches[i])
value += src->size * src->num;
ffParseSize(value, &buffer);
ffSizeAppendNum(value, &buffer);
ffStrbufAppendF(&buffer, " (L%u)", i + 1);
sum += value;
}
@@ -92,7 +93,7 @@ static void printCPUCacheCompact(const FFCPUCacheResult* result, FFCPUCacheOptio
else
{
FF_STRBUF_AUTO_DESTROY buffer2 = ffStrbufCreate();
ffParseSize(sum, &buffer2);
ffSizeAppendNum(sum, &buffer2);
FF_PRINT_FORMAT_CHECKED(FF_CPUCACHE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) {
FF_FORMAT_ARG(buffer, "result"),
FF_FORMAT_ARG(buffer2, "sum"),
@@ -154,7 +155,7 @@ void ffParseCPUCacheJsonObject(FFCPUCacheOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -148,7 +148,7 @@ void ffParseCPUUsageJsonObject(FFCPUUsageOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -65,7 +65,7 @@ void ffParseCursorJsonObject(FFCursorOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -34,7 +34,7 @@ void ffParseCustomJsonObject(FFCustomOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -134,7 +134,7 @@ void ffParseDateTimeJsonObject(FFDateTimeOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -65,7 +65,7 @@ void ffParseDEJsonObject(FFDEOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+13 -5
View File
@@ -1,7 +1,7 @@
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/parsing.h"
#include "common/percent.h"
#include "common/size.h"
#include "common/time.h"
#include "detection/disk/disk.h"
#include "modules/disk/disk.h"
@@ -44,10 +44,10 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index
}
FF_STRBUF_AUTO_DESTROY usedPretty = ffStrbufCreate();
ffParseSize(disk->bytesUsed, &usedPretty);
ffSizeAppendNum(disk->bytesUsed, &usedPretty);
FF_STRBUF_AUTO_DESTROY totalPretty = ffStrbufCreate();
ffParseSize(disk->bytesTotal, &totalPretty);
ffSizeAppendNum(disk->bytesTotal, &totalPretty);
double bytesPercentage = disk->bytesTotal > 0 ? (double) disk->bytesUsed / (double) disk->bytesTotal * 100.0 : 0;
FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type;
@@ -125,7 +125,8 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index
bool isHidden = !!(disk->type & FF_DISK_VOLUME_TYPE_HIDDEN_BIT);
bool isReadOnly = !!(disk->type & FF_DISK_VOLUME_TYPE_READONLY_BIT);
uint64_t duration = ffTimeGetNow() - disk->createTime;
uint64_t now = ffTimeGetNow();
uint64_t duration = now - disk->createTime;
uint32_t milliseconds = (uint32_t) (duration % 1000);
duration /= 1000;
uint32_t seconds = (uint32_t) (duration % 60);
@@ -136,6 +137,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index
duration /= 24;
uint32_t days = (uint32_t) duration;
FFTimeGetAgeResult age = ffTimeGetAge(disk->createTime, now);
FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
FF_FORMAT_ARG(usedPretty, "size-used"),
FF_FORMAT_ARG(totalPretty, "size-total"),
@@ -158,6 +160,9 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index
FF_FORMAT_ARG(milliseconds, "milliseconds"),
FF_FORMAT_ARG(disk->mountpoint, "mountpoint"),
FF_FORMAT_ARG(disk->mountFrom, "mount-from"),
FF_FORMAT_ARG(age.years, "years"),
FF_FORMAT_ARG(age.daysOfYear, "days-of-year"),
FF_FORMAT_ARG(age.yearsFraction, "years-fraction"),
}));
}
}
@@ -299,7 +304,7 @@ void ffParseDiskJsonObject(FFDiskOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
@@ -523,6 +528,9 @@ static FFModuleBaseInfo ffModuleInfo = {
{"Milliseconds after creation", "milliseconds"},
{"Mount point / drive letter", "mountpoint"},
{"Mount from (device path)", "mount-from"},
{"Years integer after creation", "years"},
{"Days of year after creation", "days-of-year"},
{"Years fraction after creation", "years-fraction"},
}))
};
+8 -8
View File
@@ -1,6 +1,6 @@
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/parsing.h"
#include "common/size.h"
#include "detection/diskio/diskio.h"
#include "modules/diskio/diskio.h"
#include "util/stringUtils.h"
@@ -57,22 +57,22 @@ void ffPrintDiskIO(FFDiskIOOptions* options)
{
ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
ffParseSize(dev->bytesRead, &buffer);
ffSizeAppendNum(dev->bytesRead, &buffer);
if (!options->detectTotal) ffStrbufAppendS(&buffer, "/s");
ffStrbufAppendS(&buffer, " (R) - ");
ffParseSize(dev->bytesWritten, &buffer);
ffSizeAppendNum(dev->bytesWritten, &buffer);
if (!options->detectTotal) ffStrbufAppendS(&buffer, "/s");
ffStrbufAppendS(&buffer, " (W)");
ffStrbufPutTo(&buffer, stdout);
}
else
{
ffSizeAppendNum(dev->bytesRead, &buffer);
if (!options->detectTotal) ffStrbufAppendS(&buffer, "/s");
ffStrbufClear(&buffer2);
ffParseSize(dev->bytesRead, &buffer);
if (!options->detectTotal) ffStrbufAppendS(&buffer, "/s");
ffParseSize(dev->bytesWritten, &buffer2);
if (!options->detectTotal) ffStrbufAppendS(&buffer, "/s");
ffSizeAppendNum(dev->bytesWritten, &buffer2);
if (!options->detectTotal) ffStrbufAppendS(&buffer2, "/s");
FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]){
FF_FORMAT_ARG(buffer, "size-read"),
@@ -130,7 +130,7 @@ void ffParseDiskIOJsonObject(FFDiskIOOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+8 -5
View File
@@ -1,5 +1,6 @@
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/size.h"
#include "detection/displayserver/displayserver.h"
#include "modules/display/display.h"
#include "util/stringUtils.h"
@@ -51,10 +52,11 @@ void ffPrintDisplay(FFDisplayOptions* options)
{
if (result->refreshRate > 0)
{
const char* space = instance.config.display.freqSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "";
if (options->preciseRefreshRate)
ffStrbufAppendF(&buffer, " @ %gHz", result->refreshRate);
ffStrbufAppendF(&buffer, " @ %g%sHz", result->refreshRate, space);
else
ffStrbufAppendF(&buffer, " @ %iHz", (uint32_t) (result->refreshRate + 0.5));
ffStrbufAppendF(&buffer, " @ %i%sHz", (uint32_t) (result->refreshRate + 0.5), space);
}
ffStrbufAppendS(&buffer, ", ");
}
@@ -108,10 +110,11 @@ void ffPrintDisplay(FFDisplayOptions* options)
if(result->refreshRate > 0)
{
const char* space = instance.config.display.freqSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_NEVER ? "" : " ";
if(options->preciseRefreshRate)
ffStrbufAppendF(&buffer, " @ %g Hz", ((int) (result->refreshRate * 1000 + 0.5)) / 1000.0);
ffStrbufAppendF(&buffer, " @ %g%sHz", ((int) (result->refreshRate * 1000 + 0.5)) / 1000.0, space);
else
ffStrbufAppendF(&buffer, " @ %i Hz", (uint32_t) (result->refreshRate + 0.5));
ffStrbufAppendF(&buffer, " @ %i%sHz", (uint32_t) (result->refreshRate + 0.5), space);
}
if(
@@ -261,7 +264,7 @@ void ffParseDisplayJsonObject(FFDisplayOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -85,7 +85,7 @@ void ffParseDNSJsonObject(FFDNSOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+2 -2
View File
@@ -71,7 +71,7 @@ void ffParseEditorJsonObject(FFEditorOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
@@ -130,7 +130,7 @@ static FFModuleBaseInfo ffModuleInfo = {
{"Type (Visual / Editor)", "type"},
{"Name", "name"},
{"Exe name of real path", "exe-name"},
{"Full path of real path", "full-path"},
{"Full path of real path", "path"},
{"Version", "version"},
}))
};
+1 -1
View File
@@ -58,7 +58,7 @@ void ffParseFontJsonObject(FFFontOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -97,7 +97,7 @@ void ffParseGamepadJsonObject(FFGamepadOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+11 -10
View File
@@ -1,8 +1,9 @@
#include "common/percent.h"
#include "common/parsing.h"
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/temps.h"
#include "common/size.h"
#include "common/frequency.h"
#include "detection/host/host.h"
#include "detection/gpu/gpu.h"
#include "modules/gpu/gpu.h"
@@ -42,7 +43,7 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu
if(gpu->frequency > 0)
{
ffStrbufAppendS(&output, " @ ");
ffParseFrequency(gpu->frequency, &output);
ffFreqAppendNum(gpu->frequency, &output);
}
if(gpu->temperature == gpu->temperature) //FF_GPU_TEMP_UNSET
@@ -59,10 +60,10 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu
{
if(gpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET)
{
ffParseSize(gpu->dedicated.used, &output);
ffSizeAppendNum(gpu->dedicated.used, &output);
ffStrbufAppendS(&output, " / ");
}
ffParseSize(gpu->dedicated.total, &output);
ffSizeAppendNum(gpu->dedicated.total, &output);
}
if(gpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET)
{
@@ -94,8 +95,8 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu
FF_STRBUF_AUTO_DESTROY dUsed = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY dPercentNum = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY dPercentBar = ffStrbufCreate();
if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) ffParseSize(gpu->dedicated.total, &dTotal);
if (gpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET) ffParseSize(gpu->dedicated.used, &dUsed);
if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) ffSizeAppendNum(gpu->dedicated.total, &dTotal);
if (gpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET) ffSizeAppendNum(gpu->dedicated.used, &dUsed);
if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET && gpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET)
{
double percent = (double) gpu->dedicated.used / (double) gpu->dedicated.total * 100.0;
@@ -109,8 +110,8 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu
FF_STRBUF_AUTO_DESTROY sUsed = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY sPercentNum = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY sPercentBar = ffStrbufCreate();
if (gpu->shared.total != FF_GPU_VMEM_SIZE_UNSET) ffParseSize(gpu->shared.total, &sTotal);
if (gpu->shared.used != FF_GPU_VMEM_SIZE_UNSET) ffParseSize(gpu->shared.used, &sUsed);
if (gpu->shared.total != FF_GPU_VMEM_SIZE_UNSET) ffSizeAppendNum(gpu->shared.total, &sTotal);
if (gpu->shared.used != FF_GPU_VMEM_SIZE_UNSET) ffSizeAppendNum(gpu->shared.used, &sUsed);
if (gpu->shared.total != FF_GPU_VMEM_SIZE_UNSET && gpu->shared.used != FF_GPU_VMEM_SIZE_UNSET)
{
double percent = (double) gpu->shared.used / (double) gpu->shared.total * 100.0;
@@ -121,7 +122,7 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu
}
FF_STRBUF_AUTO_DESTROY frequency = ffStrbufCreate();
ffParseFrequency(gpu->frequency, &frequency);
ffFreqAppendNum(gpu->frequency, &frequency);
FF_STRBUF_AUTO_DESTROY coreUsageNum = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY coreUsageBar = ffStrbufCreate();
@@ -255,7 +256,7 @@ void ffParseGPUJsonObject(FFGPUOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))
+1 -1
View File
@@ -84,7 +84,7 @@ void ffParseHostJsonObject(FFHostOptions* options, yyjson_val* module)
yyjson_obj_foreach(module, idx, max, key_, val)
{
const char* key = yyjson_get_str(key_);
if(ffStrEqualsIgnCase(key, "type"))
if(ffStrEqualsIgnCase(key, "type") || ffStrEqualsIgnCase(key, "condition"))
continue;
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs))

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