Compare commits

..

381 Commits

Author SHA1 Message Date
Carter Li 1242fda66f Merge pull request #1938 from fastfetch-cli/dev
Release: v2.51.1
2025-08-29 14:05:34 +08:00
李通洲 5a2b618d27 Release: v2.51.1 2025-08-29 13:55:52 +08:00
李通洲 d836d7569f CPU (macOS): removes reliance on unavailable ARM capability headers on macOS
Fixes https://github.com/Homebrew/homebrew-core/pull/235425
2025-08-29 13:45:16 +08:00
Carter Li 8a412e6823 Packaging: update debian stuff [ci skip] 2025-08-29 08:56:30 +08:00
Carter Li 16b6aac764 Merge pull request #1933 from fastfetch-cli/dev
Release: v2.51.0
2025-08-29 08:43:08 +08:00
Carter Li 9f5046d69f CPU/GPU (Linux): Improves CPU/GPU temp detection accuracy on Linux/Android 2025-08-28 18:58:00 +08:00
李通洲 cace1a7156 Revert "Uptime (Windows): use QueryUnbiasedInterruptTime"
Fixes #1935

This reverts commit b812f3ccde.
2025-08-28 18:22:06 +08:00
李通洲 9c0cf863ba JsonSchema: update 2025-08-28 16:36:00 +08:00
Carter Li ffe55d9eee DisplayServer (Linux): upgrade kde-output-device-v2 2025-08-28 16:09:47 +08:00
李通洲 28bc9523d6 Release: v2.51.0 2025-08-28 14:40:31 +08:00
李通洲 ebf810ad4b GPU (Android): adds GPU temp detection support 2025-08-28 14:29:00 +08:00
Carter Li 5c9a0a1f88 Fastfetch: clarify that --debug is debug-mode only 2025-08-28 09:32:01 +08:00
李通洲 bc05a30724 CPU (Linux): improve temp detection
Support Android
2025-08-28 09:29:33 +08:00
李通洲 98c7803a24 Battery (Android): fix battery temp detection 2025-08-27 16:28:54 +08:00
李通洲 0893013e78 DisplayServer (Android): fix display server name 2025-08-27 15:43:50 +08:00
李通洲 e6985a8832 Global: handles missing config directories more gracefully 2025-08-27 15:29:38 +08:00
Carter Li 7a87638fec InitSystem (GNU): support shepherd detection 2025-08-27 14:46:20 +08:00
李通洲 00f163be32 CPU (Windows): detects march on WoA
Tested in VM
2025-08-27 09:22:02 +08:00
李通洲 68bbb2980e CPU (macOS): detects march on Apple Silicon 2025-08-27 09:22:02 +08:00
Carter Li dd1a799abb CPU (Linux): detects march on aarch64 2025-08-27 09:22:02 +08:00
Carter Li 7611d672b7 Merge pull request #1931 from CarterLi/dev
Add GNU/Hurd support
2025-08-25 13:41:53 +08:00
carter bfb93cd8f5 Processing (Linux): enhances robustness when extracting process name and related fields
... by accurately handling process names with spaces or special characters.
2025-08-25 10:39:56 +08:00
李通洲 0998b1e59e DiskIO (Linux): trim trailing white spaces 2025-08-25 09:26:04 +08:00
李通洲 588c4c0026 Chore: silense compiler warnings 2025-08-25 09:25:25 +08:00
Carter Li a80ea2b117 CPUUsage: adds user guidance when CPU time fails to increase 2025-08-24 22:23:07 +08:00
Carter Li 13110e3074 LocalIP (GNU): adds MAC addr detection support 2025-08-24 22:15:06 +08:00
Carter Li 0b2c0445c0 InitSystem: tidy 2025-08-24 22:09:22 +08:00
Carter Li 062963d6cc CMake (GNU): disable wifi support 2025-08-24 22:04:58 +08:00
Carter Li 4b7f231b5a Processing (GNU): fix command name reading 2025-08-24 21:52:38 +08:00
Carter Li 74e0732390 Common (GNU): more __GNU__ tests 2025-08-24 21:40:22 +08:00
Carter Li e9104c38c6 Terminal (GNU): enable version detection 2025-08-24 13:04:12 +01:00
Carter Li 4a7b3a8493 Processing (GNU): hurd uses cmdline instead of comm 2025-08-24 19:57:54 +08:00
Carter Li 20fafa5a21 WM (GNU): enable 2025-08-24 19:56:52 +08:00
Carter Li 1d32344cf7 Global (GNU): code cleanups 2025-08-24 19:37:06 +08:00
Carter Li 82a1eaf276 Chore: tidy tabs 2025-08-24 19:09:51 +08:00
Carter Li 75a023f4d7 CMake (GNU): enable a lot of things 2025-08-24 12:05:14 +01:00
Yelninei 53424300e3 localip: Remove AF_LINK on GNU.
https://salsa.debian.org/glibc-team/glibc/-/blob/sid/debian/patches/hurd-i386/submitted-AF_LINK.diff
2025-08-24 11:29:02 +01:00
Yelninei d553bc4893 WIP: shepherd initsystem 2025-08-24 11:29:01 +01:00
Yelninei add2bfd108 Fix localip_linux on GNU 2025-08-24 11:29:01 +01:00
Yelninei e508100a15 disable ffPackagesGetNix on when it is not defined 2025-08-24 11:29:01 +01:00
Yelninei 1a2f4b4c1f Port netif_gnu to the new api. 2025-08-24 11:29:01 +01:00
Yelninei e968bca89d Fork netif_linux to netif_gnu.
netif_linux now uses linux specifics which do not work on GNU.
2025-08-24 11:29:01 +01:00
Yelninei 71fbe04367 Start work on GNU 2025-08-24 11:29:01 +01:00
李通洲 f2f9b10b18 TerminalShell: remove special handling of hyfetch
Ref: https://github.com/search?q=repo%3Ahykilpikonna%2Fhyfetch%20FFTS_IGNORE_PARENT&type=code
2025-08-24 00:00:10 +08:00
李通洲 fae4892643 Fastfetch: improves config file generation message with tips 2025-08-23 20:37:29 +08:00
李通洲 8589bf2392 Chore: silence compiler warnings 2025-08-23 15:31:42 +08:00
李通洲 8b18e69980 CPU: fix building on non-x86 2025-08-23 15:28:48 +08:00
李通洲 9ae968fbd3 CPU: adds CPU microarchitecture detection for x64 and reporting
Fixes #1928
2025-08-23 15:17:57 +08:00
李通洲 8f6608db74 Disk: adds formatted free and available disk size fields
Fixes #1929
2025-08-23 13:59:48 +08:00
李通洲 45b6a333ec Logo (Builtin): adds AerynOS
Fixes #1926
2025-08-22 14:23:52 +08:00
李通洲 5012bc250b Processes (Linux): be compatible with Hurd 2025-08-22 14:08:18 +08:00
李通洲 5775409cb9 DisplayServer (Android): update WM name 2025-08-22 14:07:58 +08:00
李通洲 64da80d520 Fastfetch: improves format help output for config and variables
Fixes #1927
2025-08-21 23:57:37 +08:00
Carter Li 8b1bfcb34b WM (Linux): uses the same X11 props as neofetch to query WM name
Fixes twin detection
2025-08-21 18:35:09 +08:00
Carter Li 784a7a92c8 DE (Linux): adds Trinity version detection support 2025-08-21 18:19:23 +08:00
李通洲 8331242f42 GPU (BSD): fallbacks to pciids/pci.ids 2025-08-21 10:54:34 +08:00
李通洲 79fea61c90 Merge remote-tracking branch 'origin' into dev 2025-08-21 10:41:04 +08:00
Carter Li a8e7d38afd Packaging: update debian stuff [ci skip] 2025-08-21 10:39:59 +08:00
Carter Li 24467e8c46 GPU (BSD): always use pci.ids provided by hwdata
Fixes #1924
2025-08-21 09:23:01 +08:00
李通洲 6867b87c3e Disk: disable hyperlink by default and move it to custom keys 2025-08-21 08:53:51 +08:00
Carter Li cb13d43a7a TerminalShell (Linux): always ignore pid1 (v2) 2025-08-21 00:19:52 +08:00
Carter Li bd7190ca6d TerminalShell (Linux): always ignore pid1 2025-08-21 00:12:41 +08:00
Carter Li 49982588cf Disk (Linux): remove unused headers 2025-08-20 23:52:53 +08:00
Carter Li de2dff5da0 Common: corrects the position for appending milliseconds 2025-08-20 11:07:53 +08:00
Carter Li b05c54486f LocalIP (Linux): improves netlink route parsing by avoiding possible message truncation
Fixes #1919
2025-08-20 11:07:06 +08:00
Carter Li 1f1d7cf936 FFstrbuf: handles double rounding and zero representation in formatter 2025-08-20 10:10:30 +08:00
李通洲 26253f8863 GPU (Windows): improves GPU detection by refining device filtering 2025-08-19 19:03:55 +08:00
李通洲 08e6db090b Shell (Windows): be compatible with fish 4 built by MSYS2 2025-08-19 17:25:04 +08:00
李通洲 ec0fa89cc8 Common: refactors number formatting to use type-specific append functions 2025-08-19 17:22:11 +08:00
李通洲 7f48500cfd Format: replaces format specifiers with type-safe append functions 2025-08-19 16:04:00 +08:00
李通洲 a7eecf6fc7 FFstrbuf: improves double formatting precision handling 2025-08-19 16:03:04 +08:00
李通洲 8e282a3865 Duration: use ffStrbufAppendUInt 2025-08-19 15:19:12 +08:00
李通洲 dcbf76b233 FFstrbuf: adds number appending functions 2025-08-19 15:00:30 +08:00
李通洲 70b11d953c Logo (Builtin): honor logo.width 2025-08-19 10:35:11 +08:00
李通洲 fa3b634c07 JsonConfig: support JSON5 2025-08-19 10:25:52 +08:00
李通洲 ea86273a0e Common: refactors time formatting to use a shared buffer and utility macro 2025-08-19 09:49:43 +08:00
李通洲 4f88e1f26d 3rdparty: upgrade yyjson to 0.12.0 2025-08-19 08:43:38 +08:00
李通洲 805e277429 JsonConfig: adds 'succeeded' module condition to JSON config
Fixes #1908
2025-08-18 14:18:37 +08:00
李通洲 3ce3de2963 OS: set the icon of unknown OS type as a cog 2025-08-18 14:03:52 +08:00
李通洲 3b5e9c74d6 Modules: return bool to indicate whether the detection is succeeded 2025-08-18 13:38:30 +08:00
李通洲 a9aaa36b2c CI (DFBSD): enable 2025-08-17 23:08:43 +08:00
李通洲 bef4ee1fbc CI (SunOS): fix build 2025-08-17 23:03:45 +08:00
李通洲 b3ddd084ba Merge remote-tracking branch 'upstream/master' into dev 2025-08-17 23:03:17 +08:00
Mathias Korsbäck fef3b7c647 CPU (macOS): changes CPU to M2 Pro for Mac14,12 host (#1915)
Currently states it's a M2 but according to https://support.apple.com/en-us/102852 and https://everymac.com/ultimate-mac-lookup/?search_keywords=Mac14%2C12 it's a M2 Pro.
2025-08-17 21:37:17 +08:00
Carter Li 4dfba31cae Merge pull request #1911 from fastfetch-cli/dev
Release: v2.50.2
2025-08-16 21:50:39 +08:00
Carter Li 3004f29c08 Release: v2.50.2 2025-08-16 21:18:07 +08:00
李通洲 b994ce5b55 LocalIP: refines default route detection and JSON output for local IPs
Add verbose log for Windows
2025-08-16 18:27:30 +08:00
李通洲 6356f9d0e5 Netif (Linux): improves default route detection and error handling
Ref: #1902
2025-08-16 17:15:44 +08:00
李通洲 597c6f3dbc Netif (Linux): add verbose logs 2025-08-16 15:13:07 +08:00
李通洲 41cffac80c CI: adds default build type to CI environment 2025-08-16 13:10:35 +08:00
李通洲 c4cb6c65a9 LocalIP (Linux): add verbose debug log 2025-08-16 04:43:28 +00:00
李通洲 46081a3e52 LocalIP (Linux): fixes incorrect return and ensures struct initialization 2025-08-16 04:24:26 +00:00
李通洲 a5fd671b92 LocalIP: fix showMac does not honor defaultRouteOnly 2025-08-15 20:20:43 +08:00
李通洲 8319512c83 Merge remote-tracking branch 'origin' into dev 2025-08-15 08:58:15 +08:00
Ashish SHUKLA c1db692982 FFstrbuf: wrap yyjson.h include in conditional 2025-08-15 08:49:53 +08:00
Guiling Qiyu aec2bfd2a4 Packages (Linux): fixes Linglong detection by counting linglong layers (#1903)
* Packages (Linux): fixes Linglong detection on Deepin

* Packages (Linux): fixes Linglong detection to count layers

* Update src/detection/packages/packages_linux.c

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-08-14 19:47:20 -05:00
Carter Li 7bacf20f72 Merge pull request #1901 from fastfetch-cli/dev
Release: v2.50.1
2025-08-14 22:35:06 +08:00
Carter Li 1479de19ef Release: v2.50.1 2025-08-14 18:34:30 +08:00
Guiling Qiyu bb9ca73db1 Packages (Linux): fixes Linglong detection on Debian (#1899)
Fixes #1898
2025-08-14 18:06:42 +08:00
李通洲 81bac8bca4 Percent: refactors bar color logic for clarity and consistency; update examples 2025-08-14 16:47:30 +08:00
李通洲 56d22292b0 Revert "Percent: fix a copy-paste error"
This reverts commit 3345c1e9dd.
2025-08-14 16:04:55 +08:00
李通洲 9a11ff8ddb Packages (Android): fix building 2025-08-14 14:42:09 +08:00
Carter Li a99b6d602d Merge pull request #1897 from fastfetch-cli/dev
Release: v2.50.0
2025-08-14 14:24:33 +08:00
李通洲 40921156b8 Doc: update changelog [ci skip] 2025-08-14 14:23:37 +08:00
李通洲 bc3703140b Doc: update changelog 2025-08-14 13:58:21 +08:00
李通洲 bd5b385909 Flashfetch: disable decimal digits in Disk module 2025-08-14 13:32:21 +08:00
李通洲 0fe3db419e Separator: honors FQDN settings in Title module 2025-08-14 13:30:02 +08:00
李通洲 e80bb480c4 Doc: update changelog 2025-08-14 11:03:26 +08:00
李通洲 d4773212b7 Fastfetch: adds support for reading JSON config from stdin 2025-08-14 11:00:35 +08:00
李通洲 9ebff9fd22 Chore: removes undocumented flag --load-config 2025-08-14 10:43:22 +08:00
李通洲 5d8edc0c8b Doc: update changelog [ci skip] 2025-08-14 10:27:02 +08:00
李通洲 78530b4e34 Release: v2.50.0 2025-08-14 10:20:05 +08:00
李通洲 61f2823903 Global: fixes -ffast-math compatibility
Unifies unset double handling using `-DBL_MAX` sentinel

Fixes #1894
2025-08-14 10:01:34 +08:00
李通洲 7446828866 GPU (macOS): don't set platformApi to Metal when Metal API is not used 2025-08-14 08:34:56 +08:00
李通洲 0b37acec5b GPU (Windows): ignore software adapters; add verbose logs 2025-08-14 01:07:39 +08:00
李通洲 53ecf57f19 Doc: update changelog [ci skip] 2025-08-13 10:08:25 +08:00
李通洲 e1c4365c10 Cursor (macOS): improves color detection and rounding for cursor info 2025-08-13 09:28:25 +08:00
NEOAPPS ebc60ac66c Logo (Builtin): add ObsidianOS (#1890) 2025-08-12 21:32:38 +08:00
李通洲 4004501d0f Packages: unifies Nix package detection code
Fixes: #1893
2025-08-12 19:37:52 +08:00
李通洲 a0265c3204 CI: upgrade to actions/checkout@v5 2025-08-12 09:20:04 +08:00
Osama Albahrani 3ef84834fc README: HomeBrew -> Homebrew (#1891) [ci skip]
See https://brew.sh
2025-08-11 20:08:11 -05:00
李通洲 3276949663 Doc: update README & Changelog 2025-08-11 16:42:53 +08:00
李通洲 1ec69d6838 OS (Linux): tidy 2025-08-11 16:38:39 +08:00
李通洲 7f1063d09b Revert "CPU (macOS): use CPUID for freq detection on Intel"
This reverts commit 7ee0e8726e.
2025-08-11 16:35:35 +08:00
李通洲 883923de4a CI (Haiku): reduce errors when installing deps (hopefully) 2025-08-11 15:27:20 +08:00
李通洲 146981009b CommandLine: add --gen-config-full 2025-08-11 14:49:22 +08:00
李通洲 060b69293c Presets: add an example
Fix #1887
2025-08-11 14:19:09 +08:00
李通洲 3345c1e9dd Percent: fix a copy-paste error 2025-08-11 14:18:41 +08:00
李通洲 1c8e2e34ba JsonSchema: remove unused properties [ci skip] 2025-08-11 14:16:13 +08:00
李通洲 5ab51c07d1 Doc: don't recommand Ubuntu PPA [ci skip] 2025-08-11 09:58:44 +08:00
Juanma eee98e54d0 OS (Linux): fix lliurex detection (#1882)
* Proper detection of lliurex distribution

* fix newline

* Trim version and not versionID as assigned to lliurex-version  output

* Update os_linux.c

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-08-10 20:45:53 -05:00
Carter Li b0340d5581 DisplayServer: merge serverVender into wmProtocolName
Fix #1888
2025-08-11 09:16:05 +08:00
aequabit 1124342eda Display (Linux): show brand of Xserver in WM section (#1888)
* Display: add server vendor string

* Display (X11): fetch server vendor string using XCB/Xlib

* WM: show server vendor string when not empty

* Display (XCB): use proper string helper function

* Display (X11): shorten "The X.Org Foundation" to "Xorg"
2025-08-11 09:03:23 +08:00
李通洲 329cb229f8 Chore: remove unused functions 2025-08-10 23:15:13 +08:00
李通洲 bedb5eb6c5 Netif (SunOS): work around a bug that fails to detect default route for IPv4 2025-08-10 21:42:01 +08:00
李通洲 f184f8dab5 GPU (FreeBSD): fix gpu driver detection when DRM is used 2025-08-10 08:48:00 +08:00
Carter Li 7ee0e8726e CPU (macOS): use CPUID for freq detection on Intel 2025-08-10 00:14:16 +08:00
Carter Li a7347130de LocalIP (Linux): fix building 2025-08-09 22:20:04 +08:00
Carter Li f3c82ad837 LocalIP (SunOS): add speed detection support 2025-08-09 19:53:31 +08:00
Carter Li 1bde2b31f9 LocalIP (SunOS): support isIPv6AddressPreferred 2025-08-09 19:27:07 +08:00
Carter Li 1edc617b55 LocalIP (SunOS): add more if flags 2025-08-09 19:19:50 +08:00
李通洲 2bb451d9e4 LocalIP (Linux): fix compiler warnings 2025-08-09 18:44:37 +08:00
李通洲 4588644d39 Networking (SunOS): fix compiler warnings 2025-08-09 18:35:53 +08:00
李通洲 ed94b4c46c Netif (SunOS): fix compiler warnings 2025-08-09 18:34:25 +08:00
Carter Li f96ddb4c10 LocalIP (BSD): fix compiling 2025-08-08 15:32:12 +08:00
李通洲 1b0a65ecf2 Wifi: updates default WiFi percentage thresholds 2025-08-08 14:59:39 +08:00
李通洲 33983c5402 LocalIP: improve preferred IPv6 address detection 2025-08-08 14:40:04 +08:00
李通洲 8cfee9d693 Netif: unifies BSD netif handling 2025-08-08 11:03:04 +08:00
Carter Li 64568b31d1 Netif (Haiku): add preferred source addr 2025-08-08 10:03:43 +08:00
李通洲 02c999725c NetIO (SunOS): fix build 2025-08-07 23:30:53 +08:00
李通洲 be11013ae2 LocalIP (BSD): fix building 2025-08-07 23:28:54 +08:00
Carter Li a84c4dc1f4 Netif: refactor API for dual-stack route detection
Unifies IPv4 and IPv6 default route queries under a shared result structure and per-family functions. Improves clarity and extensibility by centralizing route logic, streamlining detection of preferred interfaces, and updating all consumers to use the new API.

Prepares for more robust multi-protocol network information handling.
2025-08-07 16:35:42 +08:00
李通洲 d9455adc7f Display: fix invalid size.maxPrefix default value in generated config file 2025-08-06 21:57:21 +08:00
李通洲 b20aea7ab8 Netif (BSD): split different implementations 2025-08-06 13:58:41 +08:00
Jannis Pohl 5e770dc8b0 LocalIP: consider preferred source address when retrieving default route (#1883)
* LocalIP: consider preferred source address when retrieving default route

* LocalIP: fix logic error when there is no preferred source address

* LocalIP: use macros rather than manual free/close

* LocalIP(BSD): fix build error

* LocalIP: address Copilot suggestions
2025-08-05 23:30:06 +08:00
李通洲 137d5dfe90 LocalIP (Windows): show only preferred IP when showAllIPs is not set
Ref: #1883
2025-08-05 19:35:54 +08:00
李通洲 655d11c1cc Chore: fix building on Windows 2025-08-05 19:15:52 +08:00
李通洲 e32987c408 JsonConfig: make all non-module keys case-sensitive 2025-08-05 16:47:48 +08:00
李通洲 fa4e11c73b JsonConfig: write all flags when generating config file 2025-08-05 16:47:48 +08:00
李通洲 e753d4d3a4 Doc: remove command line flags of modules 2025-08-04 15:08:30 +08:00
李通洲 75d2c668d0 Modules: refactor module option handling for static allocation
Moves module option structs out of global config and removes dynamic allocation, using static buffers and size assertions for safer, simpler management.

Refactors module initialization and destruction to work with isolated option structs, eliminating the need for the previous modules container and related code.

Unifies module option header usage and updates detection logic to leverage statically-sized option buffers, improving encapsulation and maintainability.

Simplifies module function signatures and reduces coupling between modules and config.

Enhances future extensibility and reliability by enforcing max size constraints and removing unnecessary indirections.
2025-08-04 14:46:01 +08:00
李通洲 559082806b Modules: export module info 2025-08-04 00:06:28 +08:00
李通洲 6f8d276545 Modules: add init and destroy option callbacks to module info 2025-08-03 18:40:53 +08:00
李通洲 582268f44f Disk (Linux): ignore Bedrock Linux subvolumes
Ref: https://github.com/fastfetch-cli/fastfetch/issues/1881#issuecomment-3148302531
2025-08-03 18:19:45 +08:00
李通洲 9b68674c4f OS (Linux): don't detect Bedrock linux when ran restricted
Fix #1881
2025-08-03 18:18:12 +08:00
李通洲 badae2ba9d JsonSchema: Improves JSON schema enums with oneOf objects containing descriptive explanations for allowed values 2025-08-03 10:22:32 +08:00
李通洲 dd4f006b5f GPU: support "hideType": null 2025-08-03 10:22:32 +08:00
李通洲 c491c9e8fc Display: add compactMode: null; improve json schema 2025-08-03 10:22:32 +08:00
李通洲 094d736d38 Modules: remove all parseCommandOptions and related stuff 2025-08-03 10:22:32 +08:00
李通洲 22bfaf3c69 JsonSchema: small improvements 2025-08-03 10:22:32 +08:00
李通洲 980e552eba Display: allow "compactType": null; improve json schema 2025-08-03 10:22:32 +08:00
李通洲 4a141c3f57 Disk: add missing JSON config options 2025-08-03 10:22:32 +08:00
Carter Li 59289bef6c Percent: silense compiler warnings 2025-08-02 19:03:42 +08:00
李通洲 0b02543ef2 JsonConfig: refactor JSON key handling to improve safety and consistency
Also make keys case-sensitive
2025-08-02 18:51:03 +08:00
李通洲 3cef51ec4a JsonConfig: refactor string buffer JSON handling for robustness 2025-08-02 17:41:19 +08:00
李通洲 6d86813647 Doc: update changelog; fix typo 2025-08-01 16:36:32 +08:00
李通洲 fb267acac3 Presets: add new config; fix old config 2025-08-01 16:06:03 +08:00
李通洲 ba3e26f2b4 Option: fix a possible null reference segfault 2025-08-01 16:05:18 +08:00
李通洲 4f8d8de749 Doc: update changelog [ci skip] 2025-08-01 15:39:19 +08:00
李通洲 54c5bc5110 Bar: refactor config to use nested objects and deprecates old fields 2025-08-01 15:12:30 +08:00
李通洲 c6675f82f0 Bar: add customizable bar color options for percentage bars 2025-08-01 15:03:24 +08:00
李通洲 afe2c85047 Bar: add configurable elapsed bar borders for percent bars
Fix #1875
2025-08-01 15:03:24 +08:00
李通洲 671b11157e Wifi (macOS): disable using of ipconfig on 15.6 too
Ref: #1874
2025-08-01 08:54:31 +08:00
李通洲 78fb55c35c Chore: remove sensitive data [ci skip] 2025-07-31 18:58:57 +08:00
李通洲 559905da60 Changelog: fix typo [ci skip] 2025-07-31 14:54:53 +08:00
Carter Li e093c8aa72 Packaging: update debian stuff [ci skip] 2025-07-31 14:34:18 +08:00
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
Carter Li 8b40a81e22 Merge pull request #1826 from fastfetch-cli/dev
Release: v2.47.0
2025-07-03 14:05:23 +08:00
李通洲 8be64714ba OS (Linux): remove duplicated code 2025-07-03 11:03:14 +08:00
李通洲 fe5d739cba Doc: update README [ci skip] 2025-07-03 10:51:42 +08:00
李通洲 dba0672a11 CMake: remove the unused option ENABLE_CPUUSAGE_PERFLIB 2025-07-03 10:47:44 +08:00
李通洲 3ed6acf3ca Release: v2.47.0 2025-07-03 10:45:41 +08:00
李通洲 8c74c54bb0 README 2025-07-03 10:27:44 +08:00
Carter Li 818473102c Logo (Builtin): always use FG_DEFAULT on title and keys 2025-07-03 09:39:32 +08:00
Carter Li f1cc4a7805 Logo (Builtin): fix colors of raw Linux logo 2025-07-03 09:36:39 +08:00
Carter Li c574af97a4 Logo (Builtin): fix colors of DragonFly 2025-07-03 09:33:04 +08:00
Carter Li 74ae84e719 Revert "Logo (Builtin): :%s/FG_WHITE/FG_DEFAULT/g"
This reverts commit bdbc885811.
2025-07-03 09:03:23 +08:00
李通洲 98738b6920 CPUUsage (Windows): detect perflib support dynamically based on OS version 2025-07-02 19:40:22 +08:00
李通洲 2e5769def2 OS (Linux): add support of Xubuntu
Fix #1825
2025-07-02 09:41:07 +08:00
李通洲 18a6929662 CPUUsage (Windows): disable ENABLE_CPUUSAGE_PERFLIB by default
Because Windows 7 doesn't support it
2025-07-01 22:08:08 +08:00
李通洲 0e61d1134d CMake: fix typo 2025-07-01 21:05:37 +08:00
李通洲 4abb183735 CPUUsage(Windows): add new method using Windows Performance Library 2025-07-01 20:53:46 +08:00
李通洲 d5b56be4ff CMake (SunOS): drop pciaccess dependency 2025-07-01 20:32:45 +08:00
李通洲 1854953d8c Chore (Windows): move perflib_.h to somewhere global 2025-07-01 14:57:40 +08:00
李通洲 2406015c81 Terminal (Linux): ignore init and systemd 2025-06-30 19:41:59 +08:00
李通洲 ec7f14ab96 CpuUsage (Windows): refine KernelTime calculation by removing unused DPC and interrupt time 2025-06-30 19:37:04 +08:00
李通洲 d94cdf4424 CpuUsage (Linux / BSD): include interrupt and softirq times in CPU usage calculations 2025-06-30 19:36:46 +08:00
李通洲 40e57b7698 IO: increase PROC_FILE_BUFFSIZ to avoid possible short reads 2025-06-30 19:16:08 +08:00
李通洲 974c658fff README: add the OpenBSD and DragonFly installation method [ci skip]
Fix https://github.com/fastfetch-cli/fastfetch/issues/1819#issuecomment-3015070008
2025-06-28 23:39:01 +08:00
李通洲 ad0ddfd7f1 Media (macOS): support old macOS version 2025-06-27 16:00:27 +08:00
李通洲 1da8367f55 Media (macOS): simplify now playing handling
Also improve performance
2025-06-27 14:28:45 +08:00
李通洲 bdbc885811 Logo (Builtin): :%s/FG_WHITE/FG_DEFAULT/g
To improve visibility on white background
2025-06-26 22:36:15 +08:00
李通洲 86e5a5997b Display (FreeBSD): silence a compiler warning 2025-06-26 21:44:06 +08:00
李通洲 9832a0e45b GPU (FreeBSD): fix compiling when libdrm is not available
Fix #1820
2025-06-26 21:34:34 +08:00
李通洲 addf674621 OS (Linux): append version string for Ubuntu variants 2025-06-26 21:31:16 +08:00
李通洲 863c5583ef GPU (OpenBSD): fix pci.ids file location
Fix #1819

Signed-off-by: kevlo <kevlo@openbsd.org>
2025-06-25 13:45:49 +08:00
李通洲 45cbc6de83 OS (macOS): force reporting 26 on macOS Tahoe
Fix #1809
2025-06-25 09:19:08 +08:00
李通洲 e68f766b4d OS (Linux): improve RPIOS detection
Ref: https://github.com/fastfetch-cli/fastfetch/issues/1816#issuecomment-2996316164
2025-06-23 22:10:50 +08:00
李通洲 29d7a1acac OS (Linux): use ffStrbufSetStatic where applicatable 2025-06-23 19:41:57 +08:00
李通洲 8d59d14b80 OS (Linux): clean up dietpi related code 2025-06-23 19:41:31 +08:00
Carter Li ed1883909a OS (Linux): detect DietPi
Fix #1816
2025-06-23 18:30:04 +08:00
李通洲 049701a8a9 CI (FreeBSD): run with 14.3 2025-06-23 16:13:40 +08:00
Carter Li 13b7bc89eb Sound (SunOS): rework; drop pulseaudio dependency 2025-06-22 20:35:29 +08:00
Carter Li 92fe44183d Shell (SunOS): ignore leading - 2025-06-22 12:12:49 +08:00
Carter Li 8ebc980c17 GPU (SunOS): simplify 2025-06-22 11:20:19 +08:00
Carter Li dbeb338bda PhysicalDisk (SunOS): rework; support more device types 2025-06-22 11:14:29 +08:00
Carter Li 65ff11a8d0 PhysicalDisk (SunOS): add support 2025-06-22 01:16:50 +08:00
Carter Li 7a73211484 Logo (Builtin): use FG_DEFAULT instead of FG_WHITE 2025-06-21 23:51:40 +08:00
Carter Li ca049997b1 GPU (SunOS): rework GPU detection 2025-06-21 23:22:37 +08:00
李通洲 a9f021ccb9 Chore: remove useless semicolons 2025-06-21 20:46:51 +08:00
Carter Li 55249c2294 BIOS (SunOS): detect BIOS type 2025-06-21 20:46:51 +08:00
李通洲 c13da56959 OS (macOS): remove useless code 2025-06-20 19:28:03 +08:00
Carter Li 665c7cd5d3 Packaging: update debian stuff [ci skip] 2025-06-20 15:02:23 +08:00
Carter Li 469e21cce9 Merge pull request #1813 from fastfetch-cli/dev
Release: v2.46.0
2025-06-20 14:30:06 +08:00
李通洲 925e09c88a Doc: update changelog [ci skip] 2025-06-20 14:28:33 +08:00
李通洲 488454c1aa Logo (Builtin): simplify kali linux 2025-06-20 14:19:05 +08:00
李通洲 c67c52d606 Logo (Builtin): add Kalpa Desktop
Fix #1807
2025-06-20 14:18:21 +08:00
李通洲 e4e11ee509 Release: v2.46.0 2025-06-20 09:51:54 +08:00
李通洲 a3e2592555 Doc: update help.json 2025-06-20 09:51:40 +08:00
Carter Li e0e966dadf Logo (Builtin): rename hydra framework to hydrapwk
Fix #1812
2025-06-20 08:59:53 +08:00
李通洲 b079a8643b Chore: code cleanups 2025-06-19 22:41:30 +08:00
apocelipes 3291a9945f Global: add missing O_CLOEXEC to open() 2025-06-19 22:40:06 +08:00
李通洲 68fd21d0e4 GPU (Linux): fix an issue which vram_usage is bigger t han vram_visible 2025-06-19 22:00:48 +08:00
李通洲 0e1ef96992 GPU (Linux): detect shared vram usage of AMD GPU 2025-06-19 15:54:26 +08:00
李通洲 c377872438 GPU (Linux): correctly call radeon driver 2025-06-19 15:43:37 +08:00
李通洲 4f42d38e4d GPU (FreeBSD): fix mem leaks 2025-06-18 20:20:49 +08:00
李通洲 7b2243496b GPU (Windows): fix build 2025-06-18 18:53:29 +08:00
Carter Li 960a817d03 GPU (Linux): fix building without features 2025-06-18 18:37:30 +08:00
Carter Li 6917f9479e GPU (Linux / FreeBSD): add DRM detection support for old radeon driver
Ref: #1810
2025-06-18 16:41:02 +08:00
Carter Li 8559cc45eb GPU (Linux / FreeBSD): unifies DRM related code and code clean up 2025-06-18 10:01:32 +08:00
李通洲 40e33c676d GPU (FreeBSD): support detection by DRM [wip] 2025-06-17 20:58:09 +08:00
李通洲 d20371040e WM (FreeBSD): fix Hyprland version detection 2025-06-17 20:38:10 +08:00
李通洲 71faf1c7e3 GPU (macOS): detect Metal 4 support 2025-06-17 14:07:18 +08:00
李通洲 d182f3f743 OS (macOS): seems macOS 26 Beta 1 still reports itself as macOS 16. Honor it. 2025-06-17 14:01:22 +08:00
李通洲 39e009b945 CI: upgrade OpenBSD version 2025-06-17 09:30:14 +08:00
李通洲 a1873f3e9e CI: split amd64 and arm64 builds
So we can test both platforms in CI
2025-06-17 09:01:38 +08:00
李通洲 636bcaaaf9 Kmod: remove Chinese comments [ci skip] 2025-06-17 08:43:09 +08:00
李通洲 3fcbd80ef0 Board (macOS): detect board-id if available
Ref: https://github.com/GuillaumeGomez/sysinfo/pull/1545
2025-06-17 00:06:31 +08:00
李通洲 38887e7fbd Logo (Builtin): add GhostFreak
Fix #1801
2025-06-15 12:28:45 +08:00
AVSISP da958e11f9 Logo (Builtin): added AnushOS (#1806)
* Added AnushOS to builtin.c

Added AnushOS config to builtin.c

* Create anushos.txt

Added anushos.txt to ASCII logos

* Update builtin.c

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-06-15 12:23:27 +08:00
李通洲 c2ed71c4eb Global: set SetErrorMode(SEM_FAILCRITICALERRORS) globally
Fix #1805
2025-06-15 00:00:22 +08:00
ᡥᠠᡳᡤᡳᠶᠠ ᡥᠠᠯᠠ·ᠨᡝᡴᠣ 猫 2e1ddcf9f8 Logo (Builtin): Add HarmonyOS support (#1804)
* Logo (Builtin): add HarmonyOS

* OS (HarmonyOS): detect HarmonyOS

* Update src/detection/os/os_linux.c

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update os_linux.c

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-14 09:03:57 +08:00
Carter Li f1cc2d124c Packages (OpenBSD): fix counting by ignoring hidden folders 2025-06-13 20:38:51 +08:00
Techflash 1e32a95f83 CPU (NetBSD): properly support PowerPC (#1802)
Some devices store their CPU name in hw.model, so check that as well
before giving up.  Tested on NetBSD 10.1 evbppc/wii, and allows it to
detect the CPU as "750 (Revision 2.0)".

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-06-11 00:15:40 -05:00
CopticFelo 9e959bc57f WM (MacOS): Added Aerospace WM detection 2025-06-10 11:22:31 +08:00
李通洲 e68406ee22 DE (macOS): report Liquid Glass on macOS 26+ 2025-06-10 10:56:13 +08:00
李通洲 ba69e316b2 OS (MacOS): add macOS 26 Tahoe 2025-06-10 10:48:35 +08:00
李通洲 f3e8e8af52 Logo (Builtin): detect and add TrueNAS Scale 2025-06-10 10:22:13 +08:00
李通洲 c9adab3892 OS (Linux): detect Raspberry Pi OS
Fix #1773
2025-06-10 09:55:13 +08:00
李通洲 c8d1d29017 Disk (Linux): hide /boot/firmware by default 2025-06-10 09:54:11 +08:00
Carter Li 541acca7c0 Logo (Builtin): add xenia_old; update colors of xenia
#1797
2025-06-10 09:13:21 +08:00
Derek Passen ddf5099461 TerminalFont: support rio terminal emulator (#1789)
* Enable terminal font detection for rio terminal emulator

https://rioterm.com
https://rioterm.com/docs/config
https://rioterm.com/docs/config#fonts

* Update src/detection/terminalfont/terminalfont.c

Fix default rio font name

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

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-06-08 00:00:03 +08:00
李通洲 781a692b06 JsonSchema: document swap.separate 2025-06-07 19:23:25 +08:00
李通洲 9c37f1b370 CPU (Windows): use perflib.h instead of pdh.h for querying CPU temp
Fix #1787
2025-06-07 19:17:46 +08:00
Carter Li a66c26f4df Logo (Builtin): improve colors of bedrock_small
Fix #1790
2025-06-07 17:39:45 +08:00
Carter Li 3bd750bedf Host (Linux): detect Linux Binary Compatibility on FreeBSD
Fix #1786
2025-06-06 15:38:46 +08:00
Carter Li 4a1ada604c Host (Linux): more detailed info of WSL 2025-06-06 14:49:32 +08:00
Noah Michaels e5f57a34a2 Logo (Builtin): Add Fedora2_small (#1785)
* Add 1 more small Fedora logo

* Update builtin.c

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-06-05 20:12:44 -05:00
Carter Li 20a3737dce Packaging: update debian stuff [ci skip] 2025-06-05 10:59:16 +08:00
497 changed files with 19216 additions and 13247 deletions
+84 -35
View File
@@ -4,13 +4,16 @@ on:
- push
- pull_request
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
jobs:
spellcheck:
runs-on: ubuntu-latest
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install codespell
shell: bash
@@ -29,7 +32,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: uname -a
run: uname -a
@@ -68,7 +71,7 @@ jobs:
ffversion: ${{ steps.ffversion.outputs.ffversion }}
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: uname -a
run: uname -a
@@ -137,7 +140,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: uname -a
run: uname -a
@@ -198,7 +201,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -237,7 +240,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -273,7 +276,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -308,7 +311,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -343,7 +346,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -374,7 +377,7 @@ jobs:
name: Musl-amd64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: setup alpine linux
uses: jirutka/setup-alpine@master
@@ -408,15 +411,15 @@ jobs:
name: fastfetch-musl-amd64
path: ./fastfetch-*.*
macos-universal:
name: macOS-universal
runs-on: macos-latest
macos-amd64:
name: macOS-amd64
runs-on: macos-13
permissions:
security-events: write
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: uname -a
run: uname -a
@@ -426,7 +429,7 @@ jobs:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa
- name: configure project
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' .
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
- name: build project
run: cmake --build . --target package --verbose -j4
@@ -452,7 +455,54 @@ jobs:
- name: upload artifacts
uses: actions/upload-artifact@v4
with:
name: fastfetch-macos-universal
name: fastfetch-macos-amd64
path: ./fastfetch-*.*
macos-aarch64:
name: macOS-aarch64
runs-on: macos-latest
permissions:
security-events: write
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
- name: uname -a
run: uname -a
- name: install required packages
run: |
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa
- name: configure project
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
- name: build project
run: cmake --build . --target package --verbose -j4
- name: list features
run: ./fastfetch --list-features
- name: run fastfetch
run: time ./fastfetch -c presets/ci.jsonc --stat false
- name: run fastfetch --format json
run: time ./fastfetch -c presets/ci.jsonc --format json
- name: run flashfetch
run: time ./flashfetch
- name: print dependencies
run: otool -L fastfetch
- name: run tests
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
with:
name: fastfetch-macos-aarch64
path: ./fastfetch-*.*
sunos-amd64:
@@ -460,7 +510,7 @@ jobs:
name: SunOS-amd64
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: vmactions/omnios-vm@v1
@@ -468,8 +518,9 @@ jobs:
usesh: true
prepare: |
uname -a
pkg install pkg:/package/pkg
pkg update --accept
pkg install gcc13 cmake git pkg-config glib2 dbus sqlite-3 imagemagick
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick
run: |
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
@@ -496,7 +547,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: cross-platform-actions/action@master
@@ -505,7 +556,7 @@ jobs:
architecture: x86-64
cpu_count: 4
shell: bash
version: '14.2'
version: '14.3'
run: |
uname -a
sudo pkg update
@@ -533,7 +584,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: cross-platform-actions/action@master
@@ -542,7 +593,7 @@ jobs:
architecture: x86-64
cpu_count: 4
shell: bash
version: '7.6'
version: '7.7'
run: |
uname -a
sudo pkg_add -r cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 xfconf imagemagick chafa
@@ -569,7 +620,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: cross-platform-actions/action@master
@@ -581,7 +632,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
@@ -600,13 +651,12 @@ jobs:
dragonfly-amd64:
name: DragonFly-amd64
runs-on: ubuntu-latest
if: false
permissions:
security-events: write
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: vmactions/dragonflybsd-vm@v1
@@ -636,33 +686,30 @@ jobs:
haiku-amd64:
name: Haiku-amd64
runs-on: ubuntu-latest
if: false
permissions:
security-events: write
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: run VM
uses: cross-platform-actions/action@master
with:
operating_system: haiku
version: 'r1beta5'
architecture: x86-64
cpu_count: 4
shell: bash
version: 'r1beta5'
run: |
uname -a
ls -l
pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10
pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10 || pkgman install -y git dbus_devel mesa_devel libelf_devel imagemagick_devel opencl_headers ocl_icd_devel vulkan_devel zlib_devel chafa_devel cmake gcc make pkgconfig python3.10
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
cmake --build . --target package --verbose -j4
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
time ./fastfetch -c presets/ci.jsonc --format json
time ./flashfetch
ldd fastfetch
ctest --output-on-failure
- name: upload artifacts
@@ -682,7 +729,7 @@ jobs:
shell: msys2 {0}
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: setup-msys2
uses: msys2/setup-msys2@v2
@@ -744,7 +791,7 @@ jobs:
shell: msys2 {0}
steps:
- name: checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: setup-msys2
uses: msys2/setup-msys2@v2
@@ -808,11 +855,13 @@ jobs:
- linux-ppc64le
- linux-s390x
- musl-amd64
- macos-universal
- macos-amd64
- macos-aarch64
- freebsd-amd64
- openbsd-amd64
- netbsd-amd64
- sunos-amd64
- haiku-amd64
- windows-amd64
- windows-aarch64
permissions:
+295
View File
@@ -1,3 +1,298 @@
# 2.51.1
Bugfixes:
* Fix building on macOS 14 or older; no functional changes (CPU, macOS)
# 2.51.0
Changes:
* Fastfetch now requires [yyjson 0.12](https://github.com/ibireme/yyjson/releases/tag/0.12.0) to build when using `-DENABLE_SYSTEM_YYJSON=ON`.
* The Disk module no longer shows hyperlink mountpoints by default, which cause issues on some real consoles (Disk)
* Instead, the custom key for the Disk module now supports `{mountpoint-link}` and `{name-link}` to show hyperlinks for mountpoints and names. For example, `{ "type": "disk", "key": "Disk ({mountpoint-link})" }` can be used to restore the old behavior.
Features:
* Adds `succeeded` module condition to JSONC config. When set to `false`, the module will only run if the last module failed (#1908)
* Useful for displaying fallback placeholders when a module fails. For example:
```jsonc
{
"host",
// If fastfetch fails to detect host info, display "DIY PC" instead
{
"type": "custom",
"condition": {
"succeeded": false
},
"key": "Host",
"format": "DIY PC"
}
}
```
* By upgrading to yyjson 0.12, fastfetch now adds [JSON5](https://json5.org/) format support for configuration files (#1907)
* [JSON5](https://json5.org/) is a superset of JSONC that allows unquoted keys, single quotes, multi-line strings, etc., and is fully compatible with JSONC and strict JSON.
* To use JSON5, simply name your config file with a `.json5` extension. The `.jsonc` extension is still supported and used as the default extension for better IDE syntax highlighting support.
* Fastfetch has been ported to [`GNU/Hurd`](https://www.gnu.org/software/hurd/) (#1895)
* Thanks to the efforts of @yelninei!
* Built-in logos now honor `logo.width` (#1905)
* When its value is larger than the actual logo width, the logo will be padded with spaces to the right
* Adds Trinity DE version detection (#1917, DE, Linux)
* Adds formatted free and available disk size fields (#1929, Disk)
* `{size-free}`: free size of the disk
* `{size-available}`: available size of the disk
* See [askubuntu.com](https://askubuntu.com/questions/249387/df-h-used-space-avail-free-space-is-less-than-the-total-size-of-home) for the difference between free and available size
* Adds [x86_64 micro-architecture level](https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels) detection (#1928, CPU)
* Useful when installing software that requires or is optimized for specific CPU features. E.g., [CachyOS](https://wiki.cachyos.org/features/optimized_repos/)
* Exposed via `{march}` in custom format
* Adds [Aarch64 micro-architecture level](https://en.wikipedia.org/wiki/AArch64#Profiles) detection (CPU)
* Supported on Linux (including Android), macOS and Windows
* This is not fully accurate because there are many optional features across different levels, and not all levels are detectable.
* Exposed via `{march}` in custom format.
* Adds shepherd detection support (InitSystem, Linux)
Bugfixes:
* Refines GPU detection logic to correctly handle virtual devices (#1920, GPU, Windows)
* Fixes possible default route detection failure when the route table is very large (#1919, LocalIP, Linux)
* Fastfetch now correctly parses `hwdata/pci.ids` files alongside `pciids/pci.ids` on FreeBSD when detecting GPU names (#1924, GPU, FreeBSD)
* Fixes twin WM detection (#1917, WM, Linux)
* Various fixes for Android support
* Corrects WM name for Android (WM, Android)
* Fixes battery temperature detection when running in ADB (Battery, Android)
* Adds CPU and GPU temperature detection support (CPU, Android)
Logos:
* Adds AerynOS
# 2.50.2
Bugfixes:
* Fixes linglong package detection V2 (#1903, Packages, Linux)
* Fixes building with `-DENABLE_SYSTEM_YYJSON=ON` (#1904)
* Fixes `showMac` does not honor `defaultRouteOnly` (#1902, LocalIP, Linux)
* Fixes failing to acquire default route on Linux in certain cases (#1902, LocalIP, Linux)
# 2.50.1
Bugfixes:
* Fixes percentage bar not displaying correctly in certain cases
* Fixes linglong package detection on Debian 13 (#1899, Packages, Linux)
# 2.50.0
Changes:
* Keys in JSON configuration files are now case-sensitive, as stated in v2.49.0.
* This is a breaking change, but it should not affect most users as long as your config file passes JSON schema validation.
* All module config flags have been removed, as stated in v2.49.0.
* To configure modules via the command line, use: `echo '{"modules": [{"type":"custom","format":"Hello Fastfetch!"}]}' | fastfetch -c -`.
* The percent bar config `display.bar.*` options have been replaced with a more organized, nested object structure.
* `display.bar.charElapsed` has been renamed to `display.bar.char.elapsed`.
* `display.bar.charTotal` has been renamed to `display.bar.char.total`.
* `display.bar.borderLeft` has been renamed to `display.bar.border.left`.
* `display.bar.borderRight` has been renamed to `display.bar.border.right`.
* The undocumented flag `--load-config` has been removed.
* Use `--config` or `-c` instead.
* Flashfetch, a simplified fastfetch variant that used a hardcoded module list with direct function calls to reduce startup overhead, has been changed to a version that aims to match neofetch's behavior as closely as possible, for demonstration purposes.
* Flashfetch is intended to be built from source (like [st](https://st.suckless.org/)). We do not provide prebuilt binaries in distributions.
Features:
* Added support for reading JSON config from stdin using `--config -` or `-c -`.
* Added `display.bar.border.{leftElapsed,rightElapsed}` for using the border as part of the bar content. (#1875)
* `display.bar.border: null` has been added as a shorthand to disable bar borders.
* Added `display.bar.color.{elapsed,total,border}` to customize the color of the elapsed, total, and border sections of the percent bar.
* `display.bar.color: null` has been added as a shorthand to disable bar colors.
* Improved Bedrock Linux detection (#1881, OS / Disk, Linux)
* Added the command flag `--gen-config-full`, which generates a JSON config file containing all optional module options.
* Improved the default IP address display when `localip.showAllIPs` is not set (LocalIP)
* For IPv4, the preferred source address (if detected) is shown.
* For IPv6, the first GUA or ULA that is not deprecated or temporary is shown.
* Added support for interface speed detection on SunOS (LocalIP, SunOS)
* Added detection support for Xlibre (#1888, WM, Linux)
* Improved the accuracy of color detection (Cursor, macOS)
* Improved the proformance of `Nix` package manager detection on macOS by porting optimizations form Linux port (#1893, Packages, macOS)
Bugfixes:
* Fixed custom object inheriting a key from the previous custom object if the key is blank (#1477)
* Fixed a possible segfault when parsing color strings in the JSON config (#1878)
* Fixed GPU driver detection when DRM is used (GPU, FreeBSD)
* Fixed default route detection on DragonFly BSD (LocalIP, DFBSD)
* Fixed lliurex detection (#1882, OS, Linux)
* Fixed compatibility with `-ffast-math` (#1894)
* Fixed physical GPU sometimes being ignored (#1896, GPU, Windows)
Logos:
* Added ObsidianOS (#1890)
# 2.49.0
Deprecation Notice:
* In fastfetch v2, the JSONC configuration format has been introduced, while command line configuration flags are kept for compatibility. Although they have the same effects, they use two different code paths, and as the number of flags grows, the codebase is becoming increasingly difficult to maintain.
* Removal of module config flags is planned for **v2.50.0**, which will also fix a long-standing issue #1477.
* Removal of most other config flags is also planned for later versions.
* Keys of JSON configuration files will be all case-sensitive. Currently they are inconsistent. Planned for **v2.50.0**.
Changes:
* Due to more restrictive permissions in macOS Tahoe, SSID detection on macOS 26+ requires root privileges. `<redacted>` will be displayed otherwise.
Features:
* Improve `nouveau` driver support for `--gpu-driver-specific` (GPU, Linux)
* VRAM size detection
* GPU temperature detection
* Core count detection (when available)
* Improve Scoop package manager detection (Packages, Windows)
* Support [`scoop-global`](https://github.com/ScoopInstaller/Install?tab=readme-ov-file#advanced-installation)
* Read Scoop's config file to find the installation path of Scoop
* Improve ARM SoC detection (CPU, Android)
* Make SoC detection more lenient. Higher chance to match at the cost of accuracy.
* Add more Snapdragon SoC names
* Support labwc WM version detection, used for XFCE4 on Wayland (WM, Linux)
* Improve accuracy of GPU temperature detection for Intel dedicated GPUs on Windows (GPU, Windows)
* Parse unicode escaped strings generated by qt5ct (#1864, Font, Linux)
* Add `--{duration,percent,size,freq,temp}-space-before-unit [always|never]` options to add a space before the unit when printing duration, percent, size, frequency and temperature values
* Add `--duration-abbreviation` to abbreviate duration values in custom format
* For example: `1 day, 2 hours, 3 mins` will be displayed as `1d 2h 3m`
* Add `--percent-width` to pad the percent value with spaces to a fixed width
* For example: `--percent-width 3` will display ` 50%` instead of `50%`; useful for aligning percent values in custom format
Bugfixes:
* Improve accuracy of Flatpak count detection (#1856, Packages, Linux)
* Remove qi package manager support (#1858, Packages, Linux)
* Fix LocalIP module on Windows (LocalIP, Windows)
* Fix default route detection when multiple network interfaces are connected
* Fix link speed calculation
* Fix interface status when the interface is up but not connected (Wifi, Linux)
* Fix variable names in custom format (#1861)
* `full-path` to `path` (Editor)
* `session` to `session-name` (Users)
* `name` to `project-name` (Version)
* Fix wrong /s assignment in custom format (#1871, DiskIO)
Logos:
* Add `Aeon`
* Remove `Evolinx`
# 2.48.1
Features:
* Add support for detecting Openbox WM version (WM, Linux)
* Improve reliability of child process spawning on Windows (Windows)
* Add a new option `--packages-combined`, which combines related package managers into single counts (#1851, Packages)
* For example: if you have both `flatpak-system` and `flatpak-user` packages installed, they will be combined into a single `flatpak` count with `--packages-combined` enabled.
* Add `modules[n].condition` to conditionally enable modules on different platforms
* Useful when sharing configuration files across platforms
* For example:
```jsonc
{
"type": "custom",
"format": "This string will be printed on Intel macOS only",
"condition": {
"system": "macOS", // Can be an array, optional
"arch": "x86_64" // Can be an array, optional too
}
}
```
Bugfixes:
* Revert the change of `posix_spawn` in v2.48.0 for Android and OpenBSD (Android / OpenBSD)
* Fix completion for Android 7 (Required by Termux)
# 2.48.0
Features:
* Add support for detecting Fedora variants (#1830, OS, Linux)
* Currently supported variants: CoreOS, Kinoite, Sericea, Silverblue
* Optimize GPU detection on Windows when `--gpu-driver-specific` is not used (GPU, Windows)
* Improve accuracy of GPU type detection. Previously it was guessed based on the dedicated vmem size, which causes issues on newer AMD integrated GPUs such as 9000 HX and AI 9 HX series. Supported on Windows 8.1 or later.
* Add support for generic frequency detection of GPU 3D engine on Windows 11 22H2 or later.
* Improve performance. GPU temperature detection is significantly improved when `--gpu-driver-specific` is not used.
* Improve performance and security when spawning child processes by replacing `fork-exec` with `posix_spawn` (*nix)
* Improve accuracy of sound device detection on macOS (Sound, macOS)
* Trim leading and trailing whitespaces in disk serial numbers (PhysicalDisk)
* Add `/etc/profiles/per-user` detection for Nix user packages (#1782, Packages, Linux)
* Introduce `years` (whole years as integer), `days-of-year` (days since the last anniversary) and `years-fraction` (years as fraction, e.g. 1.5 means 1 year and 6 months) formatting placeholder to `Disk` (since disk creation), `Users` (since user login) and `Uptime` (since system boot) modules
* For example: `fastfetch -s disk --disk-key 'OS Installation' --disk-format '{years} years {days-of-year} days'`
* Add `--fraction-ndigits` option to specify the number of digits after the decimal point when displaying ordinary fractional values
* Typically used with `{years-fraction}` above
* This option does not affect percentage values, sizes, etc, which are controlled by individual options.
Bugfixes:
* Fix compilation issues when not using `-DBINARY_LINK_TYPE=dlopen`
* Regression from v2.47.0
* Note: this option was added for debugging purposes only and is not recommended for production use
* Replace `MTLDevice::hasUnifiedMemory` with `MTLDevice::location` for GPU type Detection (GPU, macOS)
* This should resolve the issue where discrete GPUs were detected as integrated GPUs on Intel MacBooks with multi-GPU configurations.
* Prevent text files from being loaded as image files (#1843, Logo)
Logos:
* Add Minimal System
* Add AxOS
* Rename Ada to Xray OS
# 2.47.0
Features:
* Various improvements for Solaris / OpenIndiana support
* Support BIOS (UEFI or legacy) type detection (BIOS)
* Support physical disk detection (PhysicalDisk)
* Remove leading `-` from login shells (Shell)
* Improve GPU detection performance (GPU)
* Drop `libpciaccess` dependency
* Use native API to detect sound devices (Sound)
* Drop `PulseAudio` dependency
* Improve DietPi OS and Raspberry Pi OS detection (#1816, OS, Linux)
* Force reporting version 26 on macOS Tahoe (OS, macOS)
* Append version string to Ubuntu variants (OS, Linux)
* Improve performance of media detection for macOS 15.4+ (Media, macOS)
* Increase `PROC_FILE_BUFFSIZ` to avoid possible short reads (Linux)
* Fix potential bugs in `DiskIO`, `NetIO` and `CPUUsage` modules
* Improve accuracy of CPU usage calculations by including interrupt and softirq times (CPUUsage, Linux / *BSD)
* Ignore `init` and `systemd` processes when detecting terminals (Terminal, Linux)
* Improve accuracy of CPU usage detection on Windows 10+ with perflib, which matches values reported by Task Manager (CPUUsage, Windows)
Bugfixes:
* Fix `pci.ids` file location (#1819, GPU, OpenBSD)
* Fix compiling on FreeBSD when `libdrm` support is disabled (#1820, GPU, FreeBSD)
Logos:
* Improve visibility on white-background terminals for some logos by replacing white with the default foreground color
* According to Wikipedia, the default foreground color is implementation-defined. It's usually black for white themes and white for dark themes. However, some terminals, notably Konsole with the default theme, use a different color, which may cause issues with some logos.
* Add Xubuntu
# 2.46.0
Features:
* Support Rio terminal font detection (#1789, TerminalFont, Linux)
* Support GPU detection by DRM on FreeBSD (GPU, FreeBSD)
* Enable by `--gpu-detection-method auto`
* Require proper DRM drivers installed and loaded
* Support PowerPC CPU detection on NetBSD (#1802, CPU, NetBSD)
* Support Aerospace WM detection (#1796, WM, macOS)
* Improve Raspberry Pi OS for RPI5 detection (#1773, OS, Linux)
* Support Linux Binary Compatibility detection on FreeBSD (#1786, Host, Linux)
* Use `board-id` as board name if available (Board, macOS)
* Intel only
* Support shared VRAM usage detection for AMD GPUs (GPU, Linux)
* Use `perflib.h` instead of `pdh.h` for CPU temperature querying to get rid of `pdh.dll` dependency (#1787, CPU, Windows)
* Support GPU info detection for old ATI radeon driver (#1810, GPU, Linux)
* Add macOS 26 Tahoe support (macOS)
* Report macOS 26 code name (OS)
* Report Liquid Glass DE on macOS 26+ (DE)
* Detect Metal 4 support (GPU)
Bugfixes:
* Fix packages counting by ignoring hidden folders (Packages, OpenBSD)
* Fix Hyprland version detection (WM, FreeBSD)
* Don't show `Please insert a disk into drive D:` error dialogs (#1805, Disk, Windows)
* Hide `/boot/firmware` by default (Disk, Linux)
Logos:
* Rename Hydra Framework to HydraPwk (#1812)
* Add AnushOS (#1806)
* Add HarmonyOS (#1804)
* Add GhostFreak (#1801)
* Add TrueNAS Scale (#1795)
* Add Fedora2_small (#1785)
* Add xenia_old; update colors of xenia (#1797)
* Improve colors of bedrock_small (#1790)
* Add Kalpa Desktop (#1807)
# 2.45.0
Features:
+136 -46
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.45.0
VERSION 2.51.1
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -32,6 +32,8 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS")
set(SunOS TRUE CACHE BOOL "..." FORCE)
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Haiku")
set(Haiku TRUE CACHE BOOL "..." FORCE)
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "GNU")
set(GNU TRUE CACHE BOOL "..." FORCE)
elseif(NOT APPLE AND NOT WIN32)
message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}")
endif()
@@ -56,33 +58,33 @@ include(CheckIncludeFile)
include(CMakeDependentOption)
cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS OR Haiku" OFF)
cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD" OFF)
cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_DRM_AMDGPU "Enable libdrm_amdgpu" ON "LINUX" OFF)
cmake_dependent_option(ENABLE_GIO "Enable gio-2.0" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_DCONF "Enable dconf" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_DBUS "Enable dbus-1" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR Haiku" OFF)
cmake_dependent_option(ENABLE_XFCONF "Enable libxfconf-0" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_SQLITE3 "Enable sqlite3" ON "LINUX OR FreeBSD OR APPLE OR OpenBSD OR NetBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_RPM "Enable rpm" ON "LINUX" OFF)
cmake_dependent_option(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR APPLE OR WIN32 OR SunOS OR Haiku" OFF)
cmake_dependent_option(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR APPLE OR SunOS" OFF)
cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR GNU" OFF)
cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_DRM_AMDGPU "Enable libdrm_amdgpu" ON "LINUX OR FreeBSD OR GNU" OFF)
cmake_dependent_option(ENABLE_GIO "Enable gio-2.0" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_DCONF "Enable dconf" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_DBUS "Enable dbus-1" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_XFCONF "Enable libxfconf-0" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_SQLITE3 "Enable sqlite3" ON "LINUX OR FreeBSD OR APPLE OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_RPM "Enable rpm" ON "LINUX OR GNU" OFF)
cmake_dependent_option(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR APPLE OR WIN32 OR SunOS OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR APPLE OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_CHAFA "Enable chafa" ON "ENABLE_IMAGEMAGICK6 OR ENABLE_IMAGEMAGICK7" OFF)
cmake_dependent_option(ENABLE_ZLIB "Enable zlib" ON "ENABLE_IMAGEMAGICK6 OR ENABLE_IMAGEMAGICK7" OFF)
cmake_dependent_option(ENABLE_EGL "Enable egl" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR WIN32 OR SunOS OR Haiku" OFF)
cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS" OFF)
cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS OR Haiku" OFF)
cmake_dependent_option(ENABLE_EGL "Enable egl" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR WIN32 OR SunOS OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF)
cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF)
cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR SunOS" OFF)
cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR GNU" OFF)
cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF)
cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" ON "LINUX" OFF)
cmake_dependent_option(ENABLE_ELF "Enable libelf" ON "LINUX OR ANDROID OR DragonFly OR Haiku" OFF)
cmake_dependent_option(ENABLE_ELF "Enable libelf" ON "LINUX OR ANDROID OR DragonFly OR Haiku OR GNU" OFF)
cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF)
cmake_dependent_option(ENABLE_LIBZFS "Enable libzfs" ON "LINUX OR FreeBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_PCIACCESS "Enable libpciaccess" ON "NetBSD OR OpenBSD OR SunOS" OFF)
cmake_dependent_option(ENABLE_PCIACCESS "Enable libpciaccess" ON "NetBSD OR OpenBSD" OFF)
option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embedded) yyjson library" OFF)
option(ENABLE_ASAN "Build fastfetch with ASAN (address sanitizer)" OFF)
@@ -326,11 +328,6 @@ endif()
fastfetch_encode_c_string("${DATATEXT_JSON_HELP}" DATATEXT_JSON_HELP)
fastfetch_load_text(src/data/structure.txt DATATEXT_STRUCTURE)
if(APPLE)
# See src/detection/media/media_apple.m
fastfetch_load_text(src/data/nowPlaying.scptd DATATEXT_NOWPLAYING)
endif()
configure_file(src/fastfetch_config.h.in fastfetch_config.h @ONLY)
configure_file(src/fastfetch_datatext.h.in fastfetch_datatext.h @ONLY)
if(APPLE)
@@ -341,7 +338,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)
@@ -356,10 +353,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
@@ -368,10 +366,13 @@ 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/common/time.c
src/detection/bluetoothradio/bluetoothradio.c
src/detection/bootmgr/bootmgr.c
src/detection/chassis/chassis.c
@@ -476,7 +477,6 @@ set(LIBFASTFETCH_SRC
src/modules/wmtheme/wmtheme.c
src/modules/zpool/zpool.c
src/options/display.c
src/options/modules.c
src/options/logo.c
src/options/general.c
src/util/edidHelper.c
@@ -529,6 +529,7 @@ if(LINUX)
src/detection/displayserver/linux/xlib.c
src/detection/font/font_linux.c
src/detection/gpu/gpu_linux.c
src/detection/gpu/gpu_drm.c
src/detection/gpu/gpu_pci.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_linux.c
@@ -549,6 +550,7 @@ if(LINUX)
src/detection/opengl/opengl_linux.c
src/detection/os/os_linux.c
src/detection/packages/packages_linux.c
src/detection/packages/packages_nix.c
src/detection/poweradapter/poweradapter_linux.c
src/detection/processes/processes_linux.c
src/detection/gtk_qt/qt.c
@@ -597,7 +599,7 @@ elseif(ANDROID)
src/detection/diskio/diskio_linux.c
src/detection/displayserver/displayserver_android.c
src/detection/font/font_nosupport.c
src/detection/gpu/gpu_nosupport.c
src/detection/gpu/gpu_android.c
src/detection/host/host_android.c
src/detection/icons/icons_nosupport.c
src/detection/initsystem/initsystem_linux.c
@@ -615,6 +617,7 @@ elseif(ANDROID)
src/detection/opengl/opengl_linux.c
src/detection/os/os_android.c
src/detection/packages/packages_linux.c
src/detection/packages/packages_nix.c
src/detection/poweradapter/poweradapter_nosupport.c
src/detection/processes/processes_linux.c
src/detection/sound/sound_nosupport.c
@@ -676,6 +679,7 @@ elseif(FreeBSD)
src/detection/displayserver/linux/xlib.c
src/detection/font/font_linux.c
src/detection/gpu/gpu_bsd.c
src/detection/gpu/gpu_drm.c
src/detection/gpu/gpu_pci.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_bsd.c
@@ -896,7 +900,7 @@ elseif(OpenBSD)
elseif(APPLE)
list(APPEND LIBFASTFETCH_SRC
src/common/io/io_unix.c
src/common/netif/netif_bsd.c
src/common/netif/netif_apple.c
src/common/networking/networking_linux.c
src/common/processing_linux.c
src/common/sysctl.c
@@ -940,6 +944,7 @@ elseif(APPLE)
src/detection/opengl/opengl_apple.c
src/detection/os/os_apple.m
src/detection/packages/packages_apple.c
src/detection/packages/packages_nix.c
src/detection/poweradapter/poweradapter_apple.c
src/detection/processes/processes_bsd.c
src/detection/sound/sound_apple.c
@@ -1042,7 +1047,7 @@ elseif(SunOS)
list(APPEND LIBFASTFETCH_SRC
src/common/dbus.c
src/common/io/io_unix.c
src/common/netif/netif_bsd.c
src/common/netif/netif_apple.c
src/common/networking/networking_linux.c
src/common/processing_linux.c
src/detection/battery/battery_nosupport.c
@@ -1060,7 +1065,7 @@ elseif(SunOS)
src/detection/bluetoothradio/bluetoothradio_nosupport.c
src/detection/disk/disk_sunos.c
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_nosupport.c
src/detection/physicaldisk/physicaldisk_sunos.c
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/diskio/diskio_sunos.c
src/detection/displayserver/linux/displayserver_linux.c
@@ -1100,7 +1105,7 @@ elseif(SunOS)
src/detection/poweradapter/poweradapter_nosupport.c
src/detection/processes/processes_linux.c
src/detection/gtk_qt/qt.c
src/detection/sound/sound_linux.c
src/detection/sound/sound_sunos.c
src/detection/swap/swap_sunos.c
src/detection/terminalfont/terminalfont_linux.c
src/detection/terminalshell/terminalshell_linux.c
@@ -1189,6 +1194,84 @@ elseif(Haiku)
src/util/binary_linux.c
src/util/haiku/version.cpp
)
elseif(GNU)
list(APPEND LIBFASTFETCH_SRC
src/common/dbus.c
src/common/io/io_unix.c
src/common/netif/netif_gnu.c
src/common/networking/networking_linux.c
src/common/processing_linux.c
src/detection/battery/battery_nosupport.c
src/detection/bios/bios_nosupport.c
src/detection/board/board_nosupport.c
src/detection/bootmgr/bootmgr_nosupport.c
src/detection/brightness/brightness_nosupport.c
src/detection/btrfs/btrfs_nosupport.c
src/detection/chassis/chassis_nosupport.c
src/detection/cpu/cpu_linux.c
src/detection/cpucache/cpucache_nosupport.c
src/detection/cpuusage/cpuusage_linux.c
src/detection/cursor/cursor_linux.c
src/detection/bluetooth/bluetooth_linux.c
src/detection/bluetoothradio/bluetoothradio_linux.c
src/detection/disk/disk_linux.c
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_nosupport.c
src/detection/physicalmemory/physicalmemory_nosupport.c
src/detection/diskio/diskio_nosupport.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/drm.c
src/detection/displayserver/linux/wayland/wayland.c
src/detection/displayserver/linux/wayland/global-output.c
src/detection/displayserver/linux/wayland/zwlr-output.c
src/detection/displayserver/linux/wayland/wlr-output-management-unstable-v1-protocol.c
src/detection/displayserver/linux/wmde.c
src/detection/displayserver/linux/xcb.c
src/detection/displayserver/linux/xlib.c
src/detection/font/font_linux.c
src/detection/gpu/gpu_nosupport.c
src/detection/gpu/gpu_pci.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_nosupport.c
src/detection/icons/icons_linux.c
src/detection/initsystem/initsystem_linux.c
src/detection/keyboard/keyboard_nosupport.c
src/detection/libc/libc_linux.c
src/detection/lm/lm_linux.c
src/detection/loadavg/loadavg_linux.c
src/detection/locale/locale_linux.c
src/detection/localip/localip_linux.c
src/detection/gamepad/gamepad_nosupport.c
src/detection/media/media_linux.c
src/detection/memory/memory_linux.c
src/detection/mouse/mouse_nosupport.c
src/detection/netio/netio_nosupport.c
src/detection/opengl/opengl_linux.c
src/detection/os/os_linux.c
src/detection/packages/packages_linux.c
src/detection/packages/packages_nix.c
src/detection/poweradapter/poweradapter_nosupport.c
src/detection/processes/processes_linux.c
src/detection/gtk_qt/qt.c
src/detection/sound/sound_linux.c
src/detection/swap/swap_linux.c
src/detection/terminalfont/terminalfont_linux.c
src/detection/terminalshell/terminalshell_linux.c
src/detection/terminalsize/terminalsize_linux.c
src/detection/theme/theme_linux.c
src/detection/tpm/tpm_nosupport.c
src/detection/uptime/uptime_linux.c
src/detection/users/users_linux.c
src/detection/wallpaper/wallpaper_linux.c
src/detection/wifi/wifi_nosupport.c
src/detection/wm/wm_linux.c
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_nosupport.c
src/detection/zpool/zpool_nosupport.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
endif()
if(ENABLE_DIRECTX_HEADERS)
@@ -1312,6 +1395,10 @@ elseif(NetBSD)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/usr/X11R7/lib -Wl,-rpath,/usr/pkg/lib") # ditto
elseif(Haiku)
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE)
elseif(GNU)
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE)
# On Hurd PATH_MAX is not defined. Set an arbitrary limit as workaround.
target_compile_definitions(libfastfetch PUBLIC PATH_MAX=4096)
endif()
if(FreeBSD OR OpenBSD OR NetBSD)
@@ -1585,7 +1672,6 @@ elseif(WIN32)
PRIVATE "ws2_32"
PRIVATE "ntdll"
PRIVATE "version"
PRIVATE "setupapi"
PRIVATE "hid"
PRIVATE "wtsapi32"
PRIVATE "imagehlp"
@@ -1593,7 +1679,6 @@ elseif(WIN32)
PRIVATE "winbrand"
PRIVATE "propsys"
PRIVATE "secur32"
PRIVATE "pdh"
)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
# WoA only works on Windows 10 or higher
@@ -1636,6 +1721,11 @@ elseif(SunOS)
PRIVATE "proc"
PRIVATE "zfs"
PRIVATE "nvpair"
PRIVATE "devinfo"
)
elseif(GNU)
target_link_libraries(libfastfetch
PRIVATE "m"
)
elseif(ANDROID)
CHECK_LIBRARY_EXISTS(-l:libandroid-wordexp.a wordexp "" HAVE_LIBANDROID_WORDEXP_STATIC)
@@ -1925,17 +2015,17 @@ install(
##################
set(CPACK_GENERATOR "TGZ;ZIP")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set(CMAKE_SYSTEM_PROCESSOR "amd64")
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(CMAKE_SYSTEM_PROCESSOR "aarch64")
endif()
if(APPLE)
string(TOLOWER "${CMAKE_PROJECT_NAME}-macos-universal" CPACK_PACKAGE_FILE_NAME)
else() # We don't use this in Windows
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set(CMAKE_SYSTEM_PROCESSOR "amd64")
endif()
if(IS_MUSL)
string(TOLOWER "${CMAKE_PROJECT_NAME}-musl-${CMAKE_SYSTEM_PROCESSOR}" CPACK_PACKAGE_FILE_NAME)
else()
string(TOLOWER "${CMAKE_PROJECT_NAME}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}" CPACK_PACKAGE_FILE_NAME)
endif()
string(TOLOWER "${CMAKE_PROJECT_NAME}-macos-${CMAKE_SYSTEM_PROCESSOR}" CPACK_PACKAGE_FILE_NAME) # use macos instead of darwin
elseif(IS_MUSL)
string(TOLOWER "${CMAKE_PROJECT_NAME}-musl-${CMAKE_SYSTEM_PROCESSOR}" CPACK_PACKAGE_FILE_NAME)
else()
string(TOLOWER "${CMAKE_PROJECT_NAME}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}" CPACK_PACKAGE_FILE_NAME)
endif()
if(LINUX)
+10 -10
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.
@@ -35,7 +36,6 @@ Some distributions package outdated versions of fastfetch. Older versions receiv
<img src="https://repology.org/badge/vertical-allrepos/fastfetch.svg?columns=2" alt="Packaging status" align="right">
</a>
* Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (for Ubuntu 22.04 or newer)
* Debian: `apt install fastfetch` (for Debian 13 or newer)
* Debian / Ubuntu: Download `fastfetch-linux-<proper architecture>.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and double-click it (for Ubuntu 20.04 or newer and Debian 11 or newer).
* Arch Linux: `pacman -S fastfetch`
@@ -57,7 +57,7 @@ If fastfetch is not packaged for your distribution or an outdated version is pac
### macOS
* [HomeBrew](https://formulae.brew.sh/formula/fastfetch#default): `brew install fastfetch`
* [Homebrew](https://formulae.brew.sh/formula/fastfetch#default): `brew install fastfetch`
* [MacPorts](https://ports.macports.org/port/fastfetch/): `sudo port install fastfetch`
### Windows
@@ -69,13 +69,12 @@ If fastfetch is not packaged for your distribution or an outdated version is pac
You may also download the program directly from [the GitHub releases page](https://github.com/fastfetch-cli/fastfetch/releases/latest) in the form of an archive file.
### FreeBSD
### BSD systems
* `pkg install fastfetch`
### NetBSD
* `pkgin in fastfetch`
* FreeBSD: `pkg install fastfetch`
* NetBSD: `pkgin in fastfetch`
* OpenBSD: `pkg_add fastfetch` (Snapshots only)
* DragonFly BSD: `pkg install fastfetch` (Snapshots only)
### Android (Termux)
@@ -93,9 +92,10 @@ See the Wiki: https://github.com/fastfetch-cli/fastfetch/wiki/Building
* Run with default configuration: `fastfetch`
* Run with [all supported modules](https://github.com/fastfetch-cli/fastfetch/wiki/Support+Status#available-modules) to find what interests you: `fastfetch -c all.jsonc`
* View all data that fastfetch detects: `fastfetch -s <module> --format json`
* View all data that fastfetch detects: `fastfetch -s <module1>[:<module2>][:<module3>] --format json`
* Display help messages: `fastfetch --help`
* Generate a config file based on command line arguments: `fastfetch --arg1 --arg2 --gen-config`
* Generate a minimal config file: `fastfetch [-s <module1>[:<module2>]] --gen-config [</path/to/config.jsonc>]`
* Use: `--gen-config-full` to generate a full config file with all optional options
## Customization
+42
View File
@@ -1,3 +1,45 @@
fastfetch (2.51.0) jammy; urgency=medium
* Update to 2.51.0
-- Carter Li <zhangsongcui@live.cn> Fri, 29 Aug 2025 08:55:03 +0800
fastfetch (2.50.2) jammy; urgency=medium
* Update to 2.50.2
-- Carter Li <zhangsongcui@live.cn> Thu, 21 Aug 2025 10:33:07 +0800
fastfetch (2.49.0) jammy; urgency=medium
* Update to 2.49.0
-- Carter Li <zhangsongcui@live.cn> Thu, 31 Jul 2025 14:32:59 +0800
fastfetch (2.48.0) jammy; urgency=medium
* Update to 2.48.0
-- Carter Li <zhangsongcui@live.cn> Thu, 17 Jul 2025 16:16:52 +0800
fastfetch (2.47.0) jammy; urgency=medium
* Update to 2.47.0
-- Carter Li <zhangsongcui@live.cn> Thu, 03 Jul 2025 14:51:45 +0800
fastfetch (2.46.0) jammy; urgency=medium
* Update to 2.46.0
-- Carter Li <zhangsongcui@live.cn> Fri, 20 Jun 2025 15:01:21 +0800
fastfetch (2.45.0) jammy; urgency=medium
* Update to 2.45.0
-- Carter Li <zhangsongcui@live.cn> Thu, 05 Jun 2025 10:56:38 +0800
fastfetch (2.44.0) jammy; urgency=medium
* Update to 2.44.0
+1 -1
View File
@@ -1 +1 @@
fastfetch_2.44.0_source.buildinfo universe/utils optional
fastfetch_2.51.0_source.buildinfo universe/utils optional
+1005 -186
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -52,7 +52,10 @@
},
"memory",
"physicalmemory",
"swap",
{
"type": "swap",
"separate": true
},
"disk",
"btrfs",
"zpool",
+4 -1
View File
@@ -54,7 +54,10 @@
},
"memory",
"physicalmemory",
"swap",
{
"type": "swap",
"separate": true
},
"disk",
"btrfs",
"zpool",
+5 -4
View File
@@ -12,10 +12,11 @@
"type": ["bar", "hide-others"]
},
"bar": {
"borderLeft": "",
"borderRight": "",
"charElapsed": "█",
"charTotal": "░",
"border": null,
"char": {
"elapsed": "█",
"total": "░"
},
"width": 40
},
"constants": [
+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"
]
}
+63
View File
@@ -0,0 +1,63 @@
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
"logo": {
"type": "small"
},
"display": {
"separator": " ",
"key": {
"type": "both"
},
"bar": {
"border": {
"left": "\uee00",
"leftElapsed": "\uee03",
"right": "\uee02",
"rightElapsed": "\uee05"
},
"char": {
"total": "\uee01",
"elapsed": "\uee04"
},
"color": {
"total": null
}
},
"percent": {
"type": [
"bar",
"bar-monochrome"
]
}
},
"modules": [
"title",
"separator",
{
"type": "memory",
"key": "MEM"
},
{
"type": "swap",
"key": "SWP"
},
{
"type": "disk",
"folders": "/",
"key": "DSK"
},
{
"type": "battery",
"key": "BAT"
},
{
"type": "brightness",
"key": "BGT"
},
{
"type": "colors",
"paddingLeft": 6,
"symbol": "circle"
}
]
}
+136
View File
@@ -0,0 +1,136 @@
// #1887
{
"$schema": "file:///Users/carter/fastfetch/doc/json_schema.json",
"logo": null,
"display": {
"constants": [
"\u001b[s\u001b[33C│\u001b[u",
" » "
],
"separator": "",
"percent": {
"type": ["num", "bar"]
},
"brightColor": false,
"bar": {
"border": {
"left": "[",
"leftElapsed": "[",
"right": "]",
"rightElapsed": "]"
},
"char": {
"elapsed": "─",
"total": "─"
},
"color": {
"elapsed": "default",
"total": "light_black"
},
"width": 16
},
"color": {
"separator": "default",
"keys": "default",
"output": "default"
}
},
"modules": [
"title",
{
"type": "custom",
"format": "┌────「 {#1}OS{#} 」────────────────────────────┐"
},
{
"type": "os",
"key": "│ {icon}{$2}{$1}"
},
{
"type": "disk",
"folders": "/",
"key": "│ {$1}",
"format": "{size-percentage-bar} {size-percentage}"
},
{
"type": "disk",
"folders": "/",
"key": "│ {$1}",
"format": "{size-used} / {size-total}"
},
{
"type": "custom",
"format": "└────────────────────────────────────────┘"
},
"break",
{
"type": "custom",
"format": "┌────「 {#1}UI{#} 」────────────────────────────┐"
},
{
"type": "wm",
"key": "│ {icon}{$2}{$1}"
},
{
"type": "wmtheme",
"key": "│ {icon}{$2}{$1}"
},
{
"type": "custom",
"key": "│ {$1}"
},
{
"type": "display",
"key": "│ {icon}{$2}{$1}",
"format": "{width}x{height} @ {refresh-rate} Hz"
},
{
"type": "custom",
"key": "│ {$1}"
},
{
"type": "terminal",
"key": "│ {icon}{$2}{$1}"
},
{
"type": "terminalfont",
"key": "│ {icon}{$2}{$1}"
},
{
"type": "custom",
"format": "└────────────────────────────────────────┘"
},
"break",
{
"type": "custom",
"format": "┌────「 {#1}HW{#} 」────────────────────────────┐"
},
{
"type": "cpu",
"key": "│ {icon}{$2}{$1}",
"format": "{name}"
},
{
"type": "gpu",
"key": "│ {icon}{$2}{$1}",
"format": "{name}"
},
{
"type": "custom",
"key": "│ {$1}"
},
{
"type": "memory",
"key": "│ {icon}{$2}{$1}",
"format": "{percentage-bar} {percentage}"
},
{
"type": "memory",
"key": "│ {$1}",
"format": "{used} / {total}"
},
{
"type": "custom",
"format": "└────────────────────────────────────────┘"
}
]
}
+4 -2
View File
@@ -8,8 +8,10 @@
"width": 11
},
"bar": {
"charElapsed": "=",
"charTotal": "-",
"char": {
"elapsed": "=",
"total": "-"
},
"width": 13
},
"percent": {
+13 -4
View File
@@ -3,7 +3,12 @@
"display": {
"size": {
"maxPrefix": "MB",
"ndigits": 0
"ndigits": 0,
"spaceBeforeUnit": "never"
},
"freq": {
"ndigits": 3,
"spaceBeforeUnit": "never"
}
},
"modules": [
@@ -16,7 +21,10 @@
"format": "{release}"
},
"uptime",
"packages",
{
"type": "packages",
"combined": true
},
"shell",
{
"type": "display",
@@ -36,11 +44,12 @@
"cpu",
{
"type": "gpu",
"key": "GPU"
"key": "GPU",
"format": "{name}"
},
{
"type": "memory",
"format": "{} / {}"
"format": "{used} / {total}"
},
"break",
"colors"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"home": "https://github.com/ibireme/yyjson",
"license": "MIT ( embed in source )",
"version": "0.11.1",
"version": "0.12.0",
"author": "ibireme"
}
+4196 -3943
View File
File diff suppressed because it is too large Load Diff
+174 -116
View File
@@ -32,7 +32,7 @@
/*==============================================================================
* Header Files
* MARK: - Header Files
*============================================================================*/
#include <stdio.h>
@@ -45,7 +45,7 @@
/*==============================================================================
* Compile-time Options
* MARK: - Compile-time Options
*============================================================================*/
/*
@@ -89,14 +89,8 @@
#endif
/*
Define as 1 to disable non-standard JSON features support at compile-time:
- YYJSON_READ_ALLOW_INF_AND_NAN
- YYJSON_READ_ALLOW_COMMENTS
- YYJSON_READ_ALLOW_TRAILING_COMMAS
- YYJSON_READ_ALLOW_INVALID_UNICODE
- YYJSON_READ_ALLOW_BOM
- YYJSON_WRITE_ALLOW_INF_AND_NAN
- YYJSON_WRITE_ALLOW_INVALID_UNICODE
Define as 1 to disable non-standard JSON features support at compile-time,
such as YYJSON_READ_ALLOW_XXX and YYJSON_WRITE_ALLOW_XXX.
This reduces binary size by about 10%, and slightly improves performance.
*/
@@ -151,7 +145,7 @@
/*==============================================================================
* Compiler Macros
* MARK: - Compiler Macros
*============================================================================*/
/** compiler version (MSVC) */
@@ -179,8 +173,10 @@
#endif
/** real gcc check */
#if !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__ICC) && \
defined(__GNUC__)
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
!defined(__clang__) && !defined(__llvm__) && \
!defined(__INTEL_COMPILER) && !defined(__ICC) && \
!defined(__NVCC__) && !defined(__PGI) && !defined(__TINYC__)
# define YYJSON_IS_REAL_GCC 1
#else
# define YYJSON_IS_REAL_GCC 0
@@ -465,7 +461,7 @@
/*==============================================================================
* Compile Hint Begin
* MARK: - Compile Hint Begin
*============================================================================*/
/* extern "C" begin */
@@ -492,23 +488,23 @@ extern "C" {
/*==============================================================================
* Version
* MARK: - Version
*============================================================================*/
/** The major version of yyjson. */
#define YYJSON_VERSION_MAJOR 0
/** The minor version of yyjson. */
#define YYJSON_VERSION_MINOR 11
#define YYJSON_VERSION_MINOR 12
/** The patch version of yyjson. */
#define YYJSON_VERSION_PATCH 1
#define YYJSON_VERSION_PATCH 0
/** The version of yyjson in hex: `(major << 16) | (minor << 8) | (patch)`. */
#define YYJSON_VERSION_HEX 0x000B01
#define YYJSON_VERSION_HEX 0x000C00
/** The version string of yyjson. */
#define YYJSON_VERSION_STRING "0.11.1"
#define YYJSON_VERSION_STRING "0.12.0"
/** The version of yyjson in hex, same as `YYJSON_VERSION_HEX`. */
yyjson_api uint32_t yyjson_version(void);
@@ -516,7 +512,7 @@ yyjson_api uint32_t yyjson_version(void);
/*==============================================================================
* JSON Types
* MARK: - JSON Types
*============================================================================*/
/** Type of a JSON value (3 bit). */
@@ -578,7 +574,7 @@ typedef uint8_t yyjson_subtype;
/*==============================================================================
* Allocator
* MARK: - Allocator
*============================================================================*/
/**
@@ -662,7 +658,7 @@ yyjson_api void yyjson_alc_dyn_free(yyjson_alc *alc);
/*==============================================================================
* Text Locating
* MARK: - Text Locating
*============================================================================*/
/**
@@ -686,7 +682,7 @@ yyjson_api bool yyjson_locate_pos(const char *str, size_t len, size_t pos,
/*==============================================================================
* JSON Structure
* MARK: - JSON Structure
*============================================================================*/
/**
@@ -720,7 +716,7 @@ typedef struct yyjson_mut_val yyjson_mut_val;
/*==============================================================================
* JSON Reader API
* MARK: - JSON Reader API
*============================================================================*/
/** Run-time options for JSON reader. */
@@ -734,7 +730,7 @@ typedef uint32_t yyjson_read_flag;
- Report error if double number is infinity.
- Report error if string contains invalid UTF-8 character or BOM.
- Report error on trailing commas, comments, inf and nan literals. */
static const yyjson_read_flag YYJSON_READ_NOFLAG = 0;
static const yyjson_read_flag YYJSON_READ_NOFLAG = 0;
/** Read the input data in-situ.
This option allows the reader to modify and use input data to store string
@@ -742,27 +738,27 @@ static const yyjson_read_flag YYJSON_READ_NOFLAG = 0;
The caller should hold the input data before free the document.
The input data must be padded by at least `YYJSON_PADDING_SIZE` bytes.
For example: `[1,2]` should be `[1,2]\0\0\0\0`, input length should be 5. */
static const yyjson_read_flag YYJSON_READ_INSITU = 1 << 0;
static const yyjson_read_flag YYJSON_READ_INSITU = 1 << 0;
/** Stop when done instead of issuing an error if there's additional content
after a JSON document. This option may be used to parse small pieces of JSON
in larger data, such as `NDJSON`. */
static const yyjson_read_flag YYJSON_READ_STOP_WHEN_DONE = 1 << 1;
static const yyjson_read_flag YYJSON_READ_STOP_WHEN_DONE = 1 << 1;
/** Allow single trailing comma at the end of an object or array,
such as `[1,2,3,]`, `{"a":1,"b":2,}` (non-standard). */
static const yyjson_read_flag YYJSON_READ_ALLOW_TRAILING_COMMAS = 1 << 2;
static const yyjson_read_flag YYJSON_READ_ALLOW_TRAILING_COMMAS = 1 << 2;
/** Allow C-style single line and multiple line comments (non-standard). */
static const yyjson_read_flag YYJSON_READ_ALLOW_COMMENTS = 1 << 3;
/** Allow C-style single-line and mult-line comments (non-standard). */
static const yyjson_read_flag YYJSON_READ_ALLOW_COMMENTS = 1 << 3;
/** Allow inf/nan number and literal, case-insensitive,
such as 1e999, NaN, inf, -Infinity (non-standard). */
static const yyjson_read_flag YYJSON_READ_ALLOW_INF_AND_NAN = 1 << 4;
static const yyjson_read_flag YYJSON_READ_ALLOW_INF_AND_NAN = 1 << 4;
/** Read all numbers as raw strings (value with `YYJSON_TYPE_RAW` type),
inf/nan literal is also read as raw with `ALLOW_INF_AND_NAN` flag. */
static const yyjson_read_flag YYJSON_READ_NUMBER_AS_RAW = 1 << 5;
static const yyjson_read_flag YYJSON_READ_NUMBER_AS_RAW = 1 << 5;
/** Allow reading invalid unicode when parsing string values (non-standard).
Invalid characters will be allowed to appear in the string values, but
@@ -772,16 +768,62 @@ static const yyjson_read_flag YYJSON_READ_NUMBER_AS_RAW = 1 << 5;
@warning Strings in JSON values may contain incorrect encoding when this
option is used, you need to handle these strings carefully to avoid security
risks. */
static const yyjson_read_flag YYJSON_READ_ALLOW_INVALID_UNICODE = 1 << 6;
static const yyjson_read_flag YYJSON_READ_ALLOW_INVALID_UNICODE = 1 << 6;
/** Read big numbers as raw strings. These big numbers include integers that
cannot be represented by `int64_t` and `uint64_t`, and floating-point
numbers that cannot be represented by finite `double`.
The flag will be overridden by `YYJSON_READ_NUMBER_AS_RAW` flag. */
static const yyjson_read_flag YYJSON_READ_BIGNUM_AS_RAW = 1 << 7;
static const yyjson_read_flag YYJSON_READ_BIGNUM_AS_RAW = 1 << 7;
/** Allow UTF-8 BOM and skip it before parsing if any (non-standard). */
static const yyjson_read_flag YYJSON_READ_ALLOW_BOM = 1 << 8;
static const yyjson_read_flag YYJSON_READ_ALLOW_BOM = 1 << 8;
/** Allow extended number formats (non-standard):
- Hexadecimal numbers, such as `0x7B`.
- Numbers with leading or trailing decimal point, such as `.123`, `123.`.
- Numbers with a leading plus sign, such as `+123`. */
static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_NUMBER = 1 << 9;
/** Allow extended escape sequences in strings (non-standard):
- Additional escapes: `\a`, `\e`, `\v`, ``\'``, `\?`, `\0`.
- Hex escapes: `\xNN`, such as `\x7B`.
- Line continuation: backslash followed by line terminator sequences.
- Unknown escape: if backslash is followed by an unsupported character,
the backslash will be removed and the character will be kept as-is.
However, `\1`-`\9` will still trigger an error. */
static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_ESCAPE = 1 << 10;
/** Allow extended whitespace characters (non-standard):
- Vertical tab `\v` and form feed `\f`.
- Line separator `\u2028` and paragraph separator `\u2029`.
- Non-breaking space `\xA0`.
- Byte order mark: `\uFEFF`.
- Other Unicode characters in the Zs (Separator, space) category. */
static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_WHITESPACE = 1 << 11;
/** Allow strings enclosed in single quotes (non-standard), such as ``'ab'``. */
static const yyjson_read_flag YYJSON_READ_ALLOW_SINGLE_QUOTED_STR = 1 << 12;
/** Allow object keys without quotes (non-standard), such as `{a:1,b:2}`.
This extends the ECMAScript IdentifierName rule by allowing any
non-whitespace character with code point above `U+007F`. */
static const yyjson_read_flag YYJSON_READ_ALLOW_UNQUOTED_KEY = 1 << 13;
/** Allow JSON5 format, see: [https://json5.org].
This flag supports all JSON5 features with some additional extensions:
- Accepts more escape sequences than JSON5 (e.g. `\a`, `\e`).
- Unquoted keys are not limited to ECMAScript IdentifierName.
- Allow case-insensitive `NaN`, `Inf` and `Infinity` literals. */
static const yyjson_read_flag YYJSON_READ_JSON5 =
(1 << 2) | /* YYJSON_READ_ALLOW_TRAILING_COMMAS */
(1 << 3) | /* YYJSON_READ_ALLOW_COMMENTS */
(1 << 4) | /* YYJSON_READ_ALLOW_INF_AND_NAN */
(1 << 9) | /* YYJSON_READ_ALLOW_EXT_NUMBER */
(1 << 10) | /* YYJSON_READ_ALLOW_EXT_ESCAPE */
(1 << 11) | /* YYJSON_READ_ALLOW_EXT_WHITESPACE */
(1 << 12) | /* YYJSON_READ_ALLOW_SINGLE_QUOTED_STR */
(1 << 13); /* YYJSON_READ_ALLOW_UNQUOTED_KEY */
@@ -794,7 +836,7 @@ static const yyjson_read_code YYJSON_READ_SUCCESS = 0;
/** Invalid parameter, such as NULL input string or 0 input length. */
static const yyjson_read_code YYJSON_READ_ERROR_INVALID_PARAMETER = 1;
/** Memory allocation failure occurs. */
/** Memory allocation failed. */
static const yyjson_read_code YYJSON_READ_ERROR_MEMORY_ALLOCATION = 2;
/** Input JSON string is empty. */
@@ -803,7 +845,7 @@ static const yyjson_read_code YYJSON_READ_ERROR_EMPTY_CONTENT = 3;
/** Unexpected content after document, such as `[123]abc`. */
static const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_CONTENT = 4;
/** Unexpected ending, such as `[123`. */
/** Unexpected end of input, the parsed part is valid, such as `[123`. */
static const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_END = 5;
/** Unexpected character inside the document, such as `[abc]`. */
@@ -812,7 +854,7 @@ static const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_CHARACTER = 6;
/** Invalid JSON structure, such as `[1,]`. */
static const yyjson_read_code YYJSON_READ_ERROR_JSON_STRUCTURE = 7;
/** Invalid comment, such as unclosed multi-line comment. */
/** Invalid comment, deprecated, use `UNEXPECTED_END` for unclosed comment. */
static const yyjson_read_code YYJSON_READ_ERROR_INVALID_COMMENT = 8;
/** Invalid number, such as `123.e12`, `000`. */
@@ -830,7 +872,7 @@ static const yyjson_read_code YYJSON_READ_ERROR_FILE_OPEN = 12;
/** Failed to read a file. */
static const yyjson_read_code YYJSON_READ_ERROR_FILE_READ = 13;
/** Unexpected ending during incremental parsing. Parsing state is saved. */
/** Incomplete input during incremental parsing; parsing state is preserved. */
static const yyjson_read_code YYJSON_READ_ERROR_MORE = 14;
/** Error information for JSON reader. */
@@ -884,6 +926,7 @@ yyjson_api yyjson_doc *yyjson_read_opts(char *dat,
2. The `alc` is thread-safe or NULL.
@param path The JSON file's path.
This should be a null-terminated string using the system's native encoding.
If this path is NULL or invalid, the function will fail and return NULL.
@param flg The JSON read options.
Multiple options can be combined with `|` operator. 0 means no options.
@@ -960,6 +1003,9 @@ typedef struct yyjson_incr_state yyjson_incr_state;
2. Call `yyjson_incr_read()` repeatedly.
3. Call `yyjson_incr_free()` to free the state.
Note: The incremental JSON reader only supports standard JSON.
Flags for non-standard features (e.g. comments, trailing commas) are ignored.
@param buf The JSON data, null-terminator is not required.
If this parameter is NULL, the function will fail and return NULL.
@param buf_len The length of the JSON data in `buf`.
@@ -1088,27 +1134,7 @@ yyjson_api const char *yyjson_read_number(const char *dat,
const yyjson_alc *alc,
yyjson_read_err *err);
/**
Read a JSON number.
This function is thread-safe when data is not modified by other threads.
@param dat The JSON data (UTF-8 without BOM), null-terminator is required.
If this parameter is NULL, the function will fail and return NULL.
@param val The output value where result is stored.
If this parameter is NULL, the function will fail and return NULL.
The value will hold either UINT or SINT or REAL number;
@param flg The JSON read options.
Multiple options can be combined with `|` operator. 0 means no options.
Supports `YYJSON_READ_NUMBER_AS_RAW` and `YYJSON_READ_ALLOW_INF_AND_NAN`.
@param alc The memory allocator used for long number.
It is only used when the built-in floating point reader is disabled.
Pass NULL to use the libc's default allocator.
@param err A pointer to receive error information.
Pass NULL if you don't need error information.
@return If successful, a pointer to the character after the last character
used in the conversion, NULL if an error occurs.
*/
/** Same as `yyjson_read_number()`. */
yyjson_api_inline const char *yyjson_mut_read_number(const char *dat,
yyjson_mut_val *val,
yyjson_read_flag flg,
@@ -1122,7 +1148,7 @@ yyjson_api_inline const char *yyjson_mut_read_number(const char *dat,
/*==============================================================================
* JSON Writer API
* MARK: - JSON Writer API
*============================================================================*/
/** Run-time options for JSON writer. */
@@ -1230,7 +1256,7 @@ typedef struct yyjson_write_err {
#if !defined(YYJSON_DISABLE_WRITER) || !YYJSON_DISABLE_WRITER
/*==============================================================================
* JSON Document Writer API
* MARK: - JSON Document Writer API
*============================================================================*/
/**
@@ -1267,6 +1293,7 @@ yyjson_api char *yyjson_write_opts(const yyjson_doc *doc,
2. The `alc` is thread-safe or NULL.
@param path The JSON file's path.
This should be a null-terminated string using the system's native encoding.
If this path is NULL or invalid, the function will fail and return false.
If this file is not empty, the content will be discarded.
@param doc The JSON document.
@@ -1370,6 +1397,7 @@ yyjson_api char *yyjson_mut_write_opts(const yyjson_mut_doc *doc,
3. The `alc` is thread-safe or NULL.
@param path The JSON file's path.
This should be a null-terminated string using the system's native encoding.
If this path is NULL or invalid, the function will fail and return false.
If this file is not empty, the content will be discarded.
@param doc The mutable JSON document.
@@ -1439,7 +1467,7 @@ yyjson_api_inline char *yyjson_mut_write(const yyjson_mut_doc *doc,
/*==============================================================================
* JSON Value Writer API
* MARK: - JSON Value Writer API
*============================================================================*/
/**
@@ -1476,6 +1504,7 @@ yyjson_api char *yyjson_val_write_opts(const yyjson_val *val,
2. The `alc` is thread-safe or NULL.
@param path The JSON file's path.
This should be a null-terminated string using the system's native encoding.
If this path is NULL or invalid, the function will fail and return false.
If this file is not empty, the content will be discarded.
@param val The JSON root value.
@@ -1577,6 +1606,7 @@ yyjson_api char *yyjson_mut_val_write_opts(const yyjson_mut_val *val,
3. The `alc` is thread-safe or NULL.
@param path The JSON file's path.
This should be a null-terminated string using the system's native encoding.
If this path is NULL or invalid, the function will fail and return false.
If this file is not empty, the content will be discarded.
@param val The mutable JSON root value.
@@ -1643,12 +1673,40 @@ yyjson_api_inline char *yyjson_mut_val_write(const yyjson_mut_val *val,
return yyjson_mut_val_write_opts(val, flg, NULL, len, NULL);
}
/**
Write a JSON number.
@param val A JSON number value to be converted to a string.
If this parameter is invalid, the function will fail and return NULL.
@param buf A buffer to store the resulting null-terminated string.
If this parameter is NULL, the function will fail and return NULL.
For integer values, the buffer must be at least 21 bytes.
For floating-point values, the buffer must be at least 40 bytes.
@return On success, returns a pointer to the character after the last
written character. On failure, returns NULL.
@note
- This function is thread-safe and does not allocate memory
(when `YYJSON_DISABLE_FAST_FP_CONV` is not defined).
- This function will fail and return NULL only in the following cases:
1) `val` or `buf` is NULL;
2) `val` is not a number type;
3) `val` is `inf` or `nan`, and non-standard JSON is explicitly disabled
via the `YYJSON_DISABLE_NON_STANDARD` flag.
*/
yyjson_api char *yyjson_write_number(const yyjson_val *val, char *buf);
/** Same as `yyjson_write_number()`. */
yyjson_api_inline char *yyjson_mut_write_number(const yyjson_mut_val *val,
char *buf) {
return yyjson_write_number((const yyjson_val *)val, buf);
}
#endif /* YYJSON_DISABLE_WRITER */
/*==============================================================================
* JSON Document API
* MARK: - JSON Document API
*============================================================================*/
/** Returns the root value of this JSON document.
@@ -1673,7 +1731,7 @@ yyjson_api_inline void yyjson_doc_free(yyjson_doc *doc);
/*==============================================================================
* JSON Value Type API
* MARK: - JSON Value Type API
*============================================================================*/
/** Returns whether the JSON value is raw.
@@ -1735,7 +1793,7 @@ yyjson_api_inline bool yyjson_is_ctn(yyjson_val *val);
/*==============================================================================
* JSON Value Content API
* MARK: - JSON Value Content API
*============================================================================*/
/** Returns the JSON value's type.
@@ -1760,7 +1818,7 @@ yyjson_api_inline const char *yyjson_get_type_desc(yyjson_val *val);
yyjson_api_inline const char *yyjson_get_raw(yyjson_val *val);
/** Returns the content if the value is bool.
Returns NULL if `val` is NULL or type is not bool. */
Returns false if `val` is NULL or type is not bool. */
yyjson_api_inline bool yyjson_get_bool(yyjson_val *val);
/** Returns the content and cast to uint64_t.
@@ -1888,7 +1946,7 @@ yyjson_api_inline bool yyjson_set_str_noesc(yyjson_val *val, bool noesc);
/*==============================================================================
* JSON Array API
* MARK: - JSON Array API
*============================================================================*/
/** Returns the number of elements in this array.
@@ -1914,7 +1972,7 @@ yyjson_api_inline yyjson_val *yyjson_arr_get_last(yyjson_val *arr);
/*==============================================================================
* JSON Array Iterator API
* MARK: - JSON Array Iterator API
*============================================================================*/
/**
@@ -1996,7 +2054,7 @@ yyjson_api_inline yyjson_val *yyjson_arr_iter_next(yyjson_arr_iter *iter);
/*==============================================================================
* JSON Object API
* MARK: - JSON Object API
*============================================================================*/
/** Returns the number of key-value pairs in this object.
@@ -2026,7 +2084,7 @@ yyjson_api_inline yyjson_val *yyjson_obj_getn(yyjson_val *obj, const char *key,
/*==============================================================================
* JSON Object Iterator API
* MARK: - JSON Object Iterator API
*============================================================================*/
/**
@@ -2169,7 +2227,7 @@ yyjson_api_inline yyjson_val *yyjson_obj_iter_getn(yyjson_obj_iter *iter,
/*==============================================================================
* Mutable JSON Document API
* MARK: - Mutable JSON Document API
*============================================================================*/
/** Returns the root value of this JSON document.
@@ -2273,7 +2331,7 @@ yyjson_api yyjson_doc *yyjson_mut_val_imut_copy(yyjson_mut_val *val,
/*==============================================================================
* Mutable JSON Value Type API
* MARK: - Mutable JSON Value Type API
*============================================================================*/
/** Returns whether the JSON value is raw.
@@ -2335,7 +2393,7 @@ yyjson_api_inline bool yyjson_mut_is_ctn(yyjson_mut_val *val);
/*==============================================================================
* Mutable JSON Value Content API
* MARK: - Mutable JSON Value Content API
*============================================================================*/
/** Returns the JSON value's type.
@@ -2504,7 +2562,7 @@ yyjson_api_inline bool yyjson_mut_set_obj(yyjson_mut_val *val);
/*==============================================================================
* Mutable JSON Value Creation API
* MARK: - Mutable JSON Value Creation API
*============================================================================*/
/** Creates and returns a raw value, returns NULL on error.
@@ -2605,7 +2663,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_strncpy(yyjson_mut_doc *doc,
/*==============================================================================
* Mutable JSON Array API
* MARK: - Mutable JSON Array API
*============================================================================*/
/** Returns the number of elements in this array.
@@ -2629,7 +2687,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_last(yyjson_mut_val *arr);
/*==============================================================================
* Mutable JSON Array Iterator API
* MARK: - Mutable JSON Array Iterator API
*============================================================================*/
/**
@@ -2731,7 +2789,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_remove(
/*==============================================================================
* Mutable JSON Array Creation API
* MARK: - Mutable JSON Array Creation API
*============================================================================*/
/**
@@ -3092,7 +3150,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strncpy(
/*==============================================================================
* Mutable JSON Array Modification API
* MARK: - Mutable JSON Array Modification API
*============================================================================*/
/**
@@ -3205,7 +3263,7 @@ yyjson_api_inline bool yyjson_mut_arr_rotate(yyjson_mut_val *arr,
/*==============================================================================
* Mutable JSON Array Modification Convenience API
* MARK: - Mutable JSON Array Modification Convenience API
*============================================================================*/
/**
@@ -3411,7 +3469,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_add_obj(yyjson_mut_doc *doc,
/*==============================================================================
* Mutable JSON Object API
* MARK: - Mutable JSON Object API
*============================================================================*/
/** Returns the number of key-value pairs in this object.
@@ -3443,7 +3501,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_getn(yyjson_mut_val *obj,
/*==============================================================================
* Mutable JSON Object Iterator API
* MARK: - Mutable JSON Object Iterator API
*============================================================================*/
/**
@@ -3605,7 +3663,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_getn(
/*==============================================================================
* Mutable JSON Object Creation API
* MARK: - Mutable JSON Object Creation API
*============================================================================*/
/** Creates and returns a mutable object, returns NULL on error. */
@@ -3652,7 +3710,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_kv(yyjson_mut_doc *doc,
/*==============================================================================
* Mutable JSON Object Modification API
* MARK: - Mutable JSON Object Modification API
*============================================================================*/
/**
@@ -3762,7 +3820,7 @@ yyjson_api_inline bool yyjson_mut_obj_rotate(yyjson_mut_val *obj,
/*==============================================================================
* Mutable JSON Object Modification Convenience API
* MARK: - Mutable JSON Object Modification Convenience API
*============================================================================*/
/** Adds a `null` value at the end of the object.
@@ -4002,7 +4060,7 @@ yyjson_api_inline bool yyjson_mut_obj_rename_keyn(yyjson_mut_doc *doc,
#if !defined(YYJSON_DISABLE_UTILS) || !YYJSON_DISABLE_UTILS
/*==============================================================================
* JSON Pointer API (RFC 6901)
* MARK: - JSON Pointer API (RFC 6901)
* https://tools.ietf.org/html/rfc6901
*============================================================================*/
@@ -4587,7 +4645,7 @@ yyjson_api_inline bool yyjson_ptr_ctx_remove(yyjson_ptr_ctx *ctx);
/*==============================================================================
* JSON Patch API (RFC 6902)
* MARK: - JSON Patch API (RFC 6902)
* https://tools.ietf.org/html/rfc6902
*============================================================================*/
@@ -4655,7 +4713,7 @@ yyjson_api yyjson_mut_val *yyjson_mut_patch(yyjson_mut_doc *doc,
/*==============================================================================
* JSON Merge-Patch API (RFC 7386)
* MARK: - JSON Merge-Patch API (RFC 7386)
* https://tools.ietf.org/html/rfc7386
*============================================================================*/
@@ -4688,7 +4746,7 @@ yyjson_api yyjson_mut_val *yyjson_mut_merge_patch(yyjson_mut_doc *doc,
/*==============================================================================
* JSON Structure (Implementation)
* MARK: - JSON Structure (Implementation)
*============================================================================*/
/** Payload of a JSON value (8 bytes). */
@@ -4725,7 +4783,7 @@ struct yyjson_doc {
/*==============================================================================
* Unsafe JSON Value API (Implementation)
* MARK: - Unsafe JSON Value API (Implementation)
*============================================================================*/
/*
@@ -5054,7 +5112,7 @@ yyjson_api_inline void unsafe_yyjson_set_obj(void *val, size_t size) {
/*==============================================================================
* JSON Document API (Implementation)
* MARK: - JSON Document API (Implementation)
*============================================================================*/
yyjson_api_inline yyjson_val *yyjson_doc_get_root(yyjson_doc *doc) {
@@ -5081,7 +5139,7 @@ yyjson_api_inline void yyjson_doc_free(yyjson_doc *doc) {
/*==============================================================================
* JSON Value Type API (Implementation)
* MARK: - JSON Value Type API (Implementation)
*============================================================================*/
yyjson_api_inline bool yyjson_is_raw(yyjson_val *val) {
@@ -5143,7 +5201,7 @@ yyjson_api_inline bool yyjson_is_ctn(yyjson_val *val) {
/*==============================================================================
* JSON Value Content API (Implementation)
* MARK: - JSON Value Content API (Implementation)
*============================================================================*/
yyjson_api_inline yyjson_type yyjson_get_type(yyjson_val *val) {
@@ -5326,7 +5384,7 @@ yyjson_api_inline bool yyjson_set_str_noesc(yyjson_val *val, bool noesc) {
/*==============================================================================
* JSON Array API (Implementation)
* MARK: - JSON Array API (Implementation)
*============================================================================*/
yyjson_api_inline size_t yyjson_arr_size(yyjson_val *arr) {
@@ -5376,7 +5434,7 @@ yyjson_api_inline yyjson_val *yyjson_arr_get_last(yyjson_val *arr) {
/*==============================================================================
* JSON Array Iterator API (Implementation)
* MARK: - JSON Array Iterator API (Implementation)
*============================================================================*/
yyjson_api_inline bool yyjson_arr_iter_init(yyjson_val *arr,
@@ -5415,7 +5473,7 @@ yyjson_api_inline yyjson_val *yyjson_arr_iter_next(yyjson_arr_iter *iter) {
/*==============================================================================
* JSON Object API (Implementation)
* MARK: - JSON Object API (Implementation)
*============================================================================*/
yyjson_api_inline size_t yyjson_obj_size(yyjson_val *obj) {
@@ -5444,7 +5502,7 @@ yyjson_api_inline yyjson_val *yyjson_obj_getn(yyjson_val *obj,
/*==============================================================================
* JSON Object Iterator API (Implementation)
* MARK: - JSON Object Iterator API (Implementation)
*============================================================================*/
yyjson_api_inline bool yyjson_obj_iter_init(yyjson_val *obj,
@@ -5521,7 +5579,7 @@ yyjson_api_inline yyjson_val *yyjson_obj_iter_getn(yyjson_obj_iter *iter,
/*==============================================================================
* Mutable JSON Structure (Implementation)
* MARK: - Mutable JSON Structure (Implementation)
*============================================================================*/
/**
@@ -5637,7 +5695,7 @@ yyjson_api_inline yyjson_mut_val *unsafe_yyjson_mut_val(yyjson_mut_doc *doc,
/*==============================================================================
* Mutable JSON Document API (Implementation)
* MARK: - Mutable JSON Document API (Implementation)
*============================================================================*/
yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_get_root(yyjson_mut_doc *doc) {
@@ -5652,7 +5710,7 @@ yyjson_api_inline void yyjson_mut_doc_set_root(yyjson_mut_doc *doc,
/*==============================================================================
* Mutable JSON Value Type API (Implementation)
* MARK: - Mutable JSON Value Type API (Implementation)
*============================================================================*/
yyjson_api_inline bool yyjson_mut_is_raw(yyjson_mut_val *val) {
@@ -5714,7 +5772,7 @@ yyjson_api_inline bool yyjson_mut_is_ctn(yyjson_mut_val *val) {
/*==============================================================================
* Mutable JSON Value Content API (Implementation)
* MARK: - Mutable JSON Value Content API (Implementation)
*============================================================================*/
yyjson_api_inline yyjson_type yyjson_mut_get_type(yyjson_mut_val *val) {
@@ -5893,7 +5951,7 @@ yyjson_api_inline bool yyjson_mut_set_obj(yyjson_mut_val *val) {
/*==============================================================================
* Mutable JSON Value Creation API (Implementation)
* MARK: - Mutable JSON Value Creation API (Implementation)
*============================================================================*/
#define yyjson_mut_val_one(func) \
@@ -6034,7 +6092,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_strncpy(yyjson_mut_doc *doc,
/*==============================================================================
* Mutable JSON Array API (Implementation)
* MARK: - Mutable JSON Array API (Implementation)
*============================================================================*/
yyjson_api_inline size_t yyjson_mut_arr_size(yyjson_mut_val *arr) {
@@ -6070,7 +6128,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_last(
/*==============================================================================
* Mutable JSON Array Iterator API (Implementation)
* MARK: - Mutable JSON Array Iterator API (Implementation)
*============================================================================*/
yyjson_api_inline bool yyjson_mut_arr_iter_init(yyjson_mut_val *arr,
@@ -6130,7 +6188,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_remove(
/*==============================================================================
* Mutable JSON Array Creation API (Implementation)
* MARK: - Mutable JSON Array Creation API (Implementation)
*============================================================================*/
yyjson_api_inline yyjson_mut_val *yyjson_mut_arr(yyjson_mut_doc *doc) {
@@ -6310,7 +6368,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strncpy(
/*==============================================================================
* Mutable JSON Array Modification API (Implementation)
* MARK: - Mutable JSON Array Modification API (Implementation)
*============================================================================*/
yyjson_api_inline bool yyjson_mut_arr_insert(yyjson_mut_val *arr,
@@ -6520,7 +6578,7 @@ yyjson_api_inline bool yyjson_mut_arr_rotate(yyjson_mut_val *arr,
/*==============================================================================
* Mutable JSON Array Modification Convenience API (Implementation)
* MARK: - Mutable JSON Array Modification Convenience API (Implementation)
*============================================================================*/
yyjson_api_inline bool yyjson_mut_arr_add_val(yyjson_mut_val *arr,
@@ -6686,7 +6744,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_add_obj(yyjson_mut_doc *doc,
/*==============================================================================
* Mutable JSON Object API (Implementation)
* MARK: - Mutable JSON Object API (Implementation)
*============================================================================*/
yyjson_api_inline size_t yyjson_mut_obj_size(yyjson_mut_val *obj) {
@@ -6715,7 +6773,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_getn(yyjson_mut_val *obj,
/*==============================================================================
* Mutable JSON Object Iterator API (Implementation)
* MARK: - Mutable JSON Object Iterator API (Implementation)
*============================================================================*/
yyjson_api_inline bool yyjson_mut_obj_iter_init(yyjson_mut_val *obj,
@@ -6806,7 +6864,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_getn(
/*==============================================================================
* Mutable JSON Object Creation API (Implementation)
* MARK: - Mutable JSON Object Creation API (Implementation)
*============================================================================*/
yyjson_api_inline yyjson_mut_val *yyjson_mut_obj(yyjson_mut_doc *doc) {
@@ -6886,7 +6944,7 @@ yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_kv(yyjson_mut_doc *doc,
/*==============================================================================
* Mutable JSON Object Modification API (Implementation)
* MARK: - Mutable JSON Object Modification API (Implementation)
*============================================================================*/
yyjson_api_inline void unsafe_yyjson_mut_obj_add(yyjson_mut_val *obj,
@@ -7079,7 +7137,7 @@ yyjson_api_inline bool yyjson_mut_obj_rotate(yyjson_mut_val *obj,
/*==============================================================================
* Mutable JSON Object Modification Convenience API (Implementation)
* MARK: - Mutable JSON Object Modification Convenience API (Implementation)
*============================================================================*/
#define yyjson_mut_obj_add_func(func) \
@@ -7306,7 +7364,7 @@ yyjson_api_inline bool yyjson_mut_obj_rename_keyn(yyjson_mut_doc *doc,
#if !defined(YYJSON_DISABLE_UTILS) || !YYJSON_DISABLE_UTILS
/*==============================================================================
* JSON Pointer API (Implementation)
* MARK: - JSON Pointer API (Implementation)
*============================================================================*/
#define yyjson_ptr_set_err(_code, _msg) do { \
@@ -7965,7 +8023,7 @@ yyjson_api_inline bool yyjson_ptr_ctx_remove(yyjson_ptr_ctx *ctx) {
/*==============================================================================
* JSON Value at Pointer API (Implementation)
* MARK: - JSON Value at Pointer API (Implementation)
*============================================================================*/
/**
@@ -8068,7 +8126,7 @@ yyjson_api_inline bool yyjson_ptr_get_str(
/*==============================================================================
* Deprecated
* MARK: - Deprecated
*============================================================================*/
/** @deprecated renamed to `yyjson_doc_ptr_get` */
@@ -8152,7 +8210,7 @@ yyjson_api_inline yyjson_mut_val *unsafe_yyjson_mut_get_pointer(
/*==============================================================================
* Compiler Hint End
* MARK: - Compiler Hint End
*============================================================================*/
#if defined(__clang__)
+43 -29
View File
@@ -10,21 +10,8 @@
#include <ctype.h>
#include <inttypes.h>
bool ffParseModuleOptions(const char* key, const char* value)
{
if (!ffStrStartsWith(key, "--") || !ffCharIsEnglishAlphabet(key[2])) return false;
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(key[2]) - 'A']; *modules; ++modules)
{
FFModuleBaseInfo* baseInfo = *modules;
if (baseInfo->parseCommandOptions(baseInfo, key, value)) return true;
}
return false;
}
void ffPrepareCommandOption(FFdata* data)
{
FFOptionsModules* const options = &instance.config.modules;
//If we don't have a custom structure, use the default one
if(data->structure.length == 0)
ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
@@ -33,54 +20,78 @@ void ffPrepareCommandOption(FFdata* data)
ffPrepareCPUUsage();
if(ffStrbufContainIgnCaseS(&data->structure, FF_DISKIO_MODULE_NAME))
ffPrepareDiskIO(&options->diskIo);
{
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
ffInitDiskIOOptions(&options);
ffPrepareDiskIO(&options);
}
if(ffStrbufContainIgnCaseS(&data->structure, FF_NETIO_MODULE_NAME))
ffPrepareNetIO(&options->netIo);
{
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
ffInitNetIOOptions(&options);
ffPrepareNetIO(&options);
}
if(instance.config.general.multithreading)
{
if(ffStrbufContainIgnCaseS(&data->structure, FF_PUBLICIP_MODULE_NAME))
ffPreparePublicIp(&options->publicIP);
{
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
ffInitPublicIpOptions(&options);
ffPreparePublicIp(&options);
}
if(ffStrbufContainIgnCaseS(&data->structure, FF_WEATHER_MODULE_NAME))
ffPrepareWeather(&options->weather);
{
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
ffInitWeatherOptions(&options);
ffPrepareWeather(&options);
}
}
}
static void genJsonConfig(FFModuleBaseInfo* baseInfo, yyjson_mut_doc* doc)
static void genJsonConfig(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
{
yyjson_mut_val* modules = yyjson_mut_obj_get(doc->root, "modules");
if (!modules)
modules = yyjson_mut_obj_add_arr(doc, doc->root, "modules");
yyjson_mut_val* module = yyjson_mut_obj(doc);
FF_STRBUF_AUTO_DESTROY type = ffStrbufCreateS(baseInfo->name);
ffStrbufLowerCase(&type);
yyjson_mut_obj_add_strbuf(doc, module, "type", &type);
if (baseInfo->generateJsonConfig)
baseInfo->generateJsonConfig(baseInfo, doc, module);
if (instance.state.fullConfig)
{
yyjson_mut_val* module = yyjson_mut_obj(doc);
yyjson_mut_obj_add_strbuf(doc, module, "type", &type);
if (yyjson_mut_obj_size(module) > 1)
yyjson_mut_arr_add_val(modules, module);
if (baseInfo->generateJsonConfig)
baseInfo->generateJsonConfig(options, doc, module);
if (yyjson_mut_obj_size(module) > 1)
yyjson_mut_arr_add_val(modules, module);
else
yyjson_mut_arr_add_strbuf(doc, modules, &type);
}
else
{
yyjson_mut_arr_add_strbuf(doc, modules, &type);
}
}
static void genJsonResult(FFModuleBaseInfo* baseInfo, yyjson_mut_doc* doc)
static void genJsonResult(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
{
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name);
if (baseInfo->generateJsonResult)
baseInfo->generateJsonResult(baseInfo, doc, module);
baseInfo->generateJsonResult(options, doc, module);
else
yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format");
}
static void parseStructureCommand(
const char* line,
void (*fn)(FFModuleBaseInfo *baseInfo, yyjson_mut_doc* jsonDoc),
void (*fn)(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* jsonDoc),
yyjson_mut_doc* jsonDoc
)
{
@@ -91,10 +102,13 @@ static void parseStructureCommand(
FFModuleBaseInfo* baseInfo = *modules;
if (ffStrEqualsIgnCase(line, baseInfo->name))
{
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
baseInfo->initOptions(optionBuf);
if (__builtin_expect(jsonDoc != NULL, false))
fn(baseInfo, jsonDoc);
fn(baseInfo, optionBuf, jsonDoc);
else
baseInfo->printModule(baseInfo);
baseInfo->printModule(optionBuf);
baseInfo->destroyOptions(optionBuf);
return;
}
}
-1
View File
@@ -9,7 +9,6 @@ typedef struct FFdata
bool configLoaded;
} FFdata;
bool ffParseModuleOptions(const char* key, const char* value);
void ffPrepareCommandOption(FFdata* data);
void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc);
void ffMigrateCommandOptionToJsonc(FFdata* data, yyjson_mut_doc* jsonDoc);
+82
View File
@@ -0,0 +1,82 @@
#include "duration.h"
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result)
{
const FFOptionsDisplay* options = &instance.config.display;
bool spaceBeforeUnit = options->durationSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER;
if (totalSeconds < 60)
{
ffStrbufAppendUInt(result, totalSeconds);
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
ffStrbufAppendS(result, options->durationAbbreviation ? "sec" : "second");
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)
{
ffStrbufAppendUInt(result, days);
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
if (options->durationAbbreviation)
{
ffStrbufAppendC(result, 'd');
if (hours > 0 || minutes > 0)
ffStrbufAppendC(result, ' ');
}
else
{
ffStrbufAppendS(result, days == 1 ? "day" : "days");
if (days >= 100)
ffStrbufAppendS(result, "(!)");
if (hours > 0 || minutes > 0)
ffStrbufAppendS(result, ", ");
}
}
if (hours > 0)
{
ffStrbufAppendUInt(result, hours);
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
if (options->durationAbbreviation)
{
ffStrbufAppendC(result, 'h');
if (minutes > 0)
ffStrbufAppendC(result, ' ');
}
else
{
ffStrbufAppendS(result, hours == 1 ? "hour" : "hours");
if (minutes > 0)
ffStrbufAppendS(result, ", ");
}
}
if (minutes > 0)
{
ffStrbufAppendUInt(result, minutes);
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
if (options->durationAbbreviation)
ffStrbufAppendC(result, 'm');
else
ffStrbufAppendS(result, minutes == 1 ? "min" : "mins");
}
}
+3
View File
@@ -0,0 +1,3 @@
#include "fastfetch.h"
void ffDurationAppendNum(uint64_t totalSeconds, FFstrbuf* result);
+10 -9
View File
@@ -11,31 +11,31 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg)
switch(formatarg->type)
{
case FF_FORMAT_ARG_TYPE_INT:
ffStrbufAppendF(buffer, "%" PRIi32, *(int32_t*)formatarg->value);
ffStrbufAppendSInt(buffer, *(int32_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_UINT:
ffStrbufAppendF(buffer, "%" PRIu32, *(uint32_t*)formatarg->value);
ffStrbufAppendUInt(buffer, *(uint32_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_UINT64:
ffStrbufAppendF(buffer, "%" PRIu64, *(uint64_t*)formatarg->value);
ffStrbufAppendUInt(buffer, *(uint64_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_UINT16:
ffStrbufAppendF(buffer, "%" PRIu16, *(uint16_t*)formatarg->value);
ffStrbufAppendUInt(buffer, *(uint16_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_UINT8:
ffStrbufAppendF(buffer, "%" PRIu8, *(uint8_t*)formatarg->value);
ffStrbufAppendUInt(buffer, *(uint8_t*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_STRING:
ffStrbufAppendS(buffer, (const char*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_STRBUF:
ffStrbufAppend(buffer, (FFstrbuf*)formatarg->value);
ffStrbufAppend(buffer, (const FFstrbuf*)formatarg->value);
break;
case FF_FORMAT_ARG_TYPE_FLOAT:
ffStrbufAppendF(buffer, "%f", *(float*)formatarg->value);
ffStrbufAppendDouble(buffer, *(float*)formatarg->value, instance.config.display.fractionNdigits);
break;
case FF_FORMAT_ARG_TYPE_DOUBLE:
ffStrbufAppendF(buffer, "%g", *(double*)formatarg->value);
ffStrbufAppendDouble(buffer, *(double*)formatarg->value, instance.config.display.fractionNdigits);
break;
case FF_FORMAT_ARG_TYPE_BOOL:
ffStrbufAppendS(buffer, *(bool*)formatarg->value ? "true" : "false");
@@ -107,7 +107,8 @@ static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start,
static inline bool formatArgSet(const FFformatarg* arg)
{
return arg->value != NULL && (
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) || //Also is false for NaN
(arg->type == FF_FORMAT_ARG_TYPE_DOUBLE && *(double*)arg->value > 0.0) ||
(arg->type == FF_FORMAT_ARG_TYPE_FLOAT && *(float*)arg->value > 0.0) ||
(arg->type == FF_FORMAT_ARG_TYPE_INT && *(int*)arg->value > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_STRBUF && ((FFstrbuf*)arg->value)->length > 0) ||
(arg->type == FF_FORMAT_ARG_TYPE_STRING && ffStrSet((char*)arg->value)) ||
+25
View File
@@ -0,0 +1,25 @@
#include "frequency.h"
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result)
{
if (mhz == 0)
return false;
const FFOptionsDisplay* options = &instance.config.display;
bool spaceBeforeUnit = options->freqSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER;
int8_t ndigits = options->freqNdigits;
if (ndigits >= 0)
{
ffStrbufAppendDouble(result, mhz / 1000., ndigits);
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
ffStrbufAppendS(result, "GHz");
}
else
{
ffStrbufAppendUInt(result, mhz);
if (spaceBeforeUnit) ffStrbufAppendC(result, ' ');
ffStrbufAppendS(result, "MHz");
}
return true;
}
+3
View File
@@ -0,0 +1,3 @@
#include "fastfetch.h"
bool ffFreqAppendNum(uint32_t mhz, FFstrbuf* result);
+3 -8
View File
@@ -25,6 +25,7 @@ static void initState(FFstate* state)
state->logoHeight = 0;
state->keysHeight = 0;
state->terminalLightTheme = false;
state->titleFqdn = false;
ffPlatformInit(&state->platform);
state->configDoc = NULL;
@@ -42,14 +43,13 @@ static void defaultConfig(void)
{
ffOptionsInitLogo(&instance.config.logo);
ffOptionsInitGeneral(&instance.config.general);
ffOptionsInitModules(&instance.config.modules);
ffOptionsInitDisplay(&instance.config.display);
}
void ffInitInstance(void)
{
#ifdef WIN32
//https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendations&view=msvc-170#utf-8-support
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendat>
setlocale(LC_ALL, ".UTF8");
#else
// Never use `setlocale(LC_ALL, "")`
@@ -96,15 +96,11 @@ static void chldSignalHandler(FF_MAYBE_UNUSED int signal)
void ffStart(void)
{
#ifdef FF_START_DETECTION_THREADS
if(instance.config.general.multithreading)
startDetectionThreads();
#endif
ffDisableLinewrap = instance.config.display.disableLinewrap && !instance.config.display.pipe && !instance.state.resultDoc;
ffHideCursor = instance.config.display.hideCursor && !instance.config.display.pipe && !instance.state.resultDoc;
#ifdef _WIN32
SetErrorMode(SEM_FAILCRITICALERRORS);
if (instance.config.display.noBuffer)
setvbuf(stdout, NULL, _IONBF, 0);
else
@@ -149,7 +145,6 @@ static void destroyConfig(void)
{
ffOptionsDestroyLogo(&instance.config.logo);
ffOptionsDestroyGeneral(&instance.config.general);
ffOptionsDestroyModules(&instance.config.modules);
ffOptionsDestroyDisplay(&instance.config.display);
}
+22 -3
View File
@@ -19,8 +19,7 @@
#define FF_INVALID_FD (-1)
// procfs's file can be changed between read calls such as /proc/meminfo and /proc/uptime.
// one safe way to read correct data is reading the whole file in a single read syscall
// 8192 comes from procps-ng: https://gitlab.com/procps-ng/procps/-/blob/master/library/meminfo.c?ref_type=heads#L39
#define PROC_FILE_BUFFSIZ 8192
#define PROC_FILE_BUFFSIZ (32 * 1024)
#endif
static inline FFNativeFD FFUnixFD2NativeFD(int unixfd)
@@ -32,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
@@ -42,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
@@ -67,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,
@@ -95,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
@@ -137,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!
@@ -158,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);
@@ -177,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);
@@ -187,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)
{
@@ -208,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
@@ -223,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);
+10 -1
View File
@@ -290,7 +290,7 @@ bool ffSuppressIO(bool suppress)
void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indentation, const char* folderName, bool pretty)
{
FF_AUTO_CLOSE_FD int dfd = open(folder->chars, O_RDONLY);
FF_AUTO_CLOSE_FD int dfd = open(folder->chars, O_RDONLY | O_CLOEXEC);
if (dfd < 0)
return;
@@ -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;
}
+125 -42
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"
@@ -11,52 +12,57 @@
#include <ctype.h>
#include <inttypes.h>
bool ffJsonConfigParseModuleArgs(const char* key, yyjson_val* val, FFModuleArgs* moduleArgs)
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs)
{
if(ffStrEqualsIgnCase(key, "key"))
if (unsafe_yyjson_equals_str(key, "type") || unsafe_yyjson_equals_str(key, "condition"))
return true;
if (unsafe_yyjson_equals_str(key, "key"))
{
ffStrbufSetNS(&moduleArgs->key, (uint32_t) yyjson_get_len(val), yyjson_get_str(val));
ffStrbufSetJsonVal(&moduleArgs->key, val);
return true;
}
else if(ffStrEqualsIgnCase(key, "format"))
else if (unsafe_yyjson_equals_str(key, "format"))
{
ffStrbufSetNS(&moduleArgs->outputFormat, (uint32_t) yyjson_get_len(val), yyjson_get_str(val));
ffStrbufSetJsonVal(&moduleArgs->outputFormat, val);
return true;
}
else if(ffStrEqualsIgnCase(key, "outputColor"))
else if (unsafe_yyjson_equals_str(key, "outputColor"))
{
ffOptionParseColor(yyjson_get_str(val), &moduleArgs->outputColor);
return true;
}
else if(ffStrEqualsIgnCase(key, "keyColor"))
else if (unsafe_yyjson_equals_str(key, "keyColor"))
{
ffOptionParseColor(yyjson_get_str(val), &moduleArgs->keyColor);
return true;
}
else if(ffStrEqualsIgnCase(key, "keyWidth"))
else if (unsafe_yyjson_equals_str(key, "keyWidth"))
{
moduleArgs->keyWidth = (uint32_t) yyjson_get_uint(val);
return true;
}
else if(ffStrEqualsIgnCase(key, "keyIcon"))
else if (unsafe_yyjson_equals_str(key, "keyIcon"))
{
ffStrbufSetS(&moduleArgs->keyIcon, yyjson_get_str(val));
ffStrbufSetJsonVal(&moduleArgs->keyIcon, val);
return true;
}
return false;
}
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* defaultModuleArgs, FFModuleArgs* moduleArgs)
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs)
{
if (!ffStrbufEqual(&defaultModuleArgs->key, &moduleArgs->key))
if (moduleArgs->key.length > 0)
yyjson_mut_obj_add_strbuf(doc, module, "key", &moduleArgs->key);
if (!ffStrbufEqual(&defaultModuleArgs->outputFormat, &moduleArgs->outputFormat))
if (moduleArgs->outputFormat.length > 0)
yyjson_mut_obj_add_strbuf(doc, module, "format", &moduleArgs->outputFormat);
if (!ffStrbufEqual(&defaultModuleArgs->keyColor, &moduleArgs->keyColor))
if (moduleArgs->outputColor.length > 0)
yyjson_mut_obj_add_strbuf(doc, module, "outputColor", &moduleArgs->outputColor);
if (moduleArgs->keyColor.length > 0)
yyjson_mut_obj_add_strbuf(doc, module, "keyColor", &moduleArgs->keyColor);
if (moduleArgs->keyWidth != defaultModuleArgs->keyWidth)
if (moduleArgs->keyWidth > 0)
yyjson_mut_obj_add_uint(doc, module, "keyWidth", moduleArgs->keyWidth);
if (!ffStrbufEqual(&defaultModuleArgs->keyIcon, &moduleArgs->keyIcon))
if (moduleArgs->keyIcon.length > 0)
yyjson_mut_obj_add_strbuf(doc, module, "keyIcon", &moduleArgs->keyIcon);
}
@@ -95,16 +101,6 @@ const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair p
return "Invalid enum value type; must be a string or integer";
}
static inline void genJsonResult(FFModuleBaseInfo* baseInfo, yyjson_mut_doc* doc)
{
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name);
if (baseInfo->generateJsonResult)
baseInfo->generateJsonResult(baseInfo, doc, module);
else
yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format");
}
static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal, yyjson_mut_doc* jsonDoc)
{
if(!ffCharIsEnglishAlphabet(type[0])) return false;
@@ -114,20 +110,47 @@ static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal, yyjson_
FFModuleBaseInfo* baseInfo = *modules;
if (ffStrEqualsIgnCase(type, baseInfo->name))
{
if (jsonVal) baseInfo->parseJsonObject(baseInfo, jsonVal);
if (__builtin_expect(jsonDoc != NULL, false))
genJsonResult(baseInfo, jsonDoc);
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
baseInfo->initOptions(optionBuf);
if (jsonVal) baseInfo->parseJsonObject(optionBuf, jsonVal);
bool succeeded;
if (jsonDoc)
{
yyjson_mut_val* module = yyjson_mut_arr_add_obj(jsonDoc, jsonDoc->root);
yyjson_mut_obj_add_str(jsonDoc, module, "type", baseInfo->name);
if (baseInfo->generateJsonResult)
succeeded = baseInfo->generateJsonResult(optionBuf, jsonDoc, module);
else
{
yyjson_mut_obj_add_str(jsonDoc, module, "error", "Unsupported for JSON format");
succeeded = false;
}
}
else
baseInfo->printModule(baseInfo);
return true;
succeeded = baseInfo->printModule(optionBuf);
baseInfo->destroyOptions(optionBuf);
return succeeded;
}
}
if (jsonDoc)
{
yyjson_mut_val* module = yyjson_mut_arr_add_obj(jsonDoc, jsonDoc->root);
yyjson_mut_obj_add_strcpy(jsonDoc, module, "type", type);
yyjson_mut_obj_add_str(jsonDoc, module, "error", "Unknown module type");
}
else
{
FFModuleArgs moduleArgs;
ffOptionInitModuleArg(&moduleArgs, "");
ffPrintError(type, 0, &moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown module type");
ffOptionDestroyModuleArg(&moduleArgs);
}
return false;
}
static void prepareModuleJsonObject(const char* type, yyjson_val* module)
{
FFconfig* cfg = &instance.config;
switch (type[0])
{
case 'b': case 'B': {
@@ -138,38 +161,65 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module)
case 'd': case 'D': {
if (ffStrEqualsIgnCase(type, FF_DISKIO_MODULE_NAME))
{
if (module) cfg->modules.diskIo.moduleInfo.parseJsonObject(&cfg->modules.diskIo, module);
ffPrepareDiskIO(&cfg->modules.diskIo);
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
ffInitDiskIOOptions(&options);
if (module) ffDiskIOModuleInfo.parseJsonObject(&options, module);
ffPrepareDiskIO(&options);
}
break;
}
case 'n': case 'N': {
if (ffStrEqualsIgnCase(type, FF_NETIO_MODULE_NAME))
{
if (module) cfg->modules.netIo.moduleInfo.parseJsonObject(&cfg->modules.netIo, module);
ffPrepareNetIO(&cfg->modules.netIo);
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
ffInitNetIOOptions(&options);
if (module) ffNetIOModuleInfo.parseJsonObject(&options, module);
ffPrepareNetIO(&options);
}
break;
}
case 'p': case 'P': {
if (ffStrEqualsIgnCase(type, FF_PUBLICIP_MODULE_NAME))
{
if (module) cfg->modules.publicIP.moduleInfo.parseJsonObject(&cfg->modules.publicIP, module);
ffPreparePublicIp(&cfg->modules.publicIP);
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
ffInitPublicIpOptions(&options);
if (module) ffPublicIPModuleInfo.parseJsonObject(&options, module);
ffPreparePublicIp(&options);
}
break;
}
case 'w': case 'W': {
if (ffStrEqualsIgnCase(type, FF_WEATHER_MODULE_NAME))
{
if (module) cfg->modules.weather.moduleInfo.parseJsonObject(&cfg->modules.weather, module);
ffPrepareWeather(&cfg->modules.weather);
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
ffInitWeatherOptions(&options);
if (module) ffWeatherModuleInfo.parseJsonObject(&options, module);
ffPrepareWeather(&options);
}
break;
}
}
}
static bool matchesJsonArray(const char* str, yyjson_val* val)
{
assert(val);
if (unsafe_yyjson_is_str(val))
return ffStrEqualsIgnCase(str, unsafe_yyjson_get_str(val));
if (!unsafe_yyjson_is_arr(val)) return false;
size_t idx, max;
yyjson_val* item;
yyjson_arr_foreach(val, idx, max, item)
{
if (yyjson_is_str(item) && ffStrEqualsIgnCase(str, unsafe_yyjson_get_str(item)))
return true;
}
return false;
}
static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
{
yyjson_val* const root = yyjson_doc_get_root(instance.state.configDoc);
@@ -182,6 +232,7 @@ static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
if (!modules) return NULL;
if (!yyjson_is_arr(modules)) return "Property 'modules' must be an array of strings or objects";
bool succeeded = true;
int32_t thres = instance.config.display.stat;
yyjson_val* item;
size_t idx, max;
@@ -197,6 +248,38 @@ 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;
yyjson_val* previousSucceeded = yyjson_obj_get(conditions, "succeeded");
if (previousSucceeded && !unsafe_yyjson_is_null(previousSucceeded))
{
if (!unsafe_yyjson_is_bool(previousSucceeded))
return "Property 'succeeded' in 'condition' must be a boolean";
if (succeeded != unsafe_yyjson_get_bool(previousSucceeded))
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
@@ -207,8 +290,8 @@ static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
if(prepare)
prepareModuleJsonObject(type, module);
else if(!parseModuleJsonObject(type, module, jsonDoc))
return "Unknown module type";
else
succeeded = parseModuleJsonObject(type, module, jsonDoc);
if(!prepare && thres >= 0)
{
+2 -2
View File
@@ -2,10 +2,10 @@
#include "fastfetch.h"
bool ffJsonConfigParseModuleArgs(const char* key, yyjson_val* val, FFModuleArgs* moduleArgs);
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs);
const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair pairs[]);
void ffPrintJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc);
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* defaultModuleArgs, FFModuleArgs* moduleArgs);
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs);
yyjson_api_inline yyjson_mut_val* yyjson_mut_strbuf(yyjson_mut_doc *doc, const FFstrbuf* buf) {
return yyjson_mut_strncpy(doc, buf->chars, buf->length);
+1 -1
View File
@@ -52,7 +52,7 @@ static void* libraryLoad(const char* path, int maxVersion)
for(int i = maxVersion; i >= 0; --i)
{
uint32_t originalLength = pathbuf.length;
ffStrbufAppendF(&pathbuf, "%i", i);
ffStrbufAppendSInt(&pathbuf, i);
result = dlopen(pathbuf.chars, FF_DLOPEN_FLAGS);
if(result != NULL)
+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);
+74 -74
View File
@@ -1,69 +1,69 @@
#include "fastfetch.h"
#include "modules/modules.h"
static FFModuleBaseInfo* A[] = {
NULL,
};
static FFModuleBaseInfo* B[] = {
(FFModuleBaseInfo*) &instance.config.modules.battery,
(FFModuleBaseInfo*) &instance.config.modules.bios,
(FFModuleBaseInfo*) &instance.config.modules.bluetooth,
(FFModuleBaseInfo*) &instance.config.modules.bluetoothRadio,
(FFModuleBaseInfo*) &instance.config.modules.board,
(FFModuleBaseInfo*) &instance.config.modules.bootmgr,
(FFModuleBaseInfo*) &instance.config.modules.break_,
(FFModuleBaseInfo*) &instance.config.modules.brightness,
(FFModuleBaseInfo*) &instance.config.modules.btrfs,
&ffBatteryModuleInfo,
&ffBiosModuleInfo,
&ffBluetoothModuleInfo,
&ffBluetoothRadioModuleInfo,
&ffBoardModuleInfo,
&ffBootmgrModuleInfo,
&ffBreakModuleInfo,
&ffBrightnessModuleInfo,
&ffBtrfsModuleInfo,
NULL,
};
static FFModuleBaseInfo* C[] = {
(FFModuleBaseInfo*) &instance.config.modules.camera,
(FFModuleBaseInfo*) &instance.config.modules.chassis,
(FFModuleBaseInfo*) &instance.config.modules.command,
(FFModuleBaseInfo*) &instance.config.modules.colors,
(FFModuleBaseInfo*) &instance.config.modules.cpu,
(FFModuleBaseInfo*) &instance.config.modules.cpuCache,
(FFModuleBaseInfo*) &instance.config.modules.cpuUsage,
(FFModuleBaseInfo*) &instance.config.modules.cursor,
(FFModuleBaseInfo*) &instance.config.modules.custom,
&ffCameraModuleInfo,
&ffChassisModuleInfo,
&ffCommandModuleInfo,
&ffColorsModuleInfo,
&ffCPUModuleInfo,
&ffCPUCacheModuleInfo,
&ffCPUUsageModuleInfo,
&ffCursorModuleInfo,
&ffCustomModuleInfo,
NULL,
};
static FFModuleBaseInfo* D[] = {
(FFModuleBaseInfo*) &instance.config.modules.dateTime,
(FFModuleBaseInfo*) &instance.config.modules.de,
(FFModuleBaseInfo*) &instance.config.modules.display,
(FFModuleBaseInfo*) &instance.config.modules.disk,
(FFModuleBaseInfo*) &instance.config.modules.diskIo,
(FFModuleBaseInfo*) &instance.config.modules.dns,
&ffDateTimeModuleInfo,
&ffDEModuleInfo,
&ffDisplayModuleInfo,
&ffDiskModuleInfo,
&ffDiskIOModuleInfo,
&ffDNSModuleInfo,
NULL,
};
static FFModuleBaseInfo* E[] = {
(FFModuleBaseInfo*) &instance.config.modules.editor,
&ffEditorModuleInfo,
NULL,
};
static FFModuleBaseInfo* F[] = {
(FFModuleBaseInfo*) &instance.config.modules.font,
&ffFontModuleInfo,
NULL,
};
static FFModuleBaseInfo* G[] = {
(FFModuleBaseInfo*) &instance.config.modules.gamepad,
(FFModuleBaseInfo*) &instance.config.modules.gpu,
&ffGamepadModuleInfo,
&ffGPUModuleInfo,
NULL,
};
static FFModuleBaseInfo* H[] = {
(FFModuleBaseInfo*) &instance.config.modules.host,
&ffHostModuleInfo,
NULL,
};
static FFModuleBaseInfo* I[] = {
(FFModuleBaseInfo*) &instance.config.modules.icons,
(FFModuleBaseInfo*) &instance.config.modules.initSystem,
&ffIconsModuleInfo,
&ffInitSystemModuleInfo,
NULL,
};
@@ -72,47 +72,47 @@ static FFModuleBaseInfo* J[] = {
};
static FFModuleBaseInfo* K[] = {
(FFModuleBaseInfo*) &instance.config.modules.kernel,
(FFModuleBaseInfo*) &instance.config.modules.keyboard,
&ffKernelModuleInfo,
&ffKeyboardModuleInfo,
NULL,
};
static FFModuleBaseInfo* L[] = {
(FFModuleBaseInfo*) &instance.config.modules.lm,
(FFModuleBaseInfo*) &instance.config.modules.loadavg,
(FFModuleBaseInfo*) &instance.config.modules.locale,
(FFModuleBaseInfo*) &instance.config.modules.localIP,
&ffLMModuleInfo,
&ffLoadavgModuleInfo,
&ffLocaleModuleInfo,
&ffLocalIPModuleInfo,
NULL,
};
static FFModuleBaseInfo* M[] = {
(FFModuleBaseInfo*) &instance.config.modules.media,
(FFModuleBaseInfo*) &instance.config.modules.memory,
(FFModuleBaseInfo*) &instance.config.modules.monitor,
(FFModuleBaseInfo*) &instance.config.modules.mouse,
&ffMediaModuleInfo,
&ffMemoryModuleInfo,
&ffMonitorModuleInfo,
&ffMouseModuleInfo,
NULL,
};
static FFModuleBaseInfo* N[] = {
(FFModuleBaseInfo*) &instance.config.modules.netIo,
&ffNetIOModuleInfo,
NULL,
};
static FFModuleBaseInfo* O[] = {
(FFModuleBaseInfo*) &instance.config.modules.openCL,
(FFModuleBaseInfo*) &instance.config.modules.openGL,
(FFModuleBaseInfo*) &instance.config.modules.os,
&ffOpenCLModuleInfo,
&ffOpenGLModuleInfo,
&ffOSModuleInfo,
NULL,
};
static FFModuleBaseInfo* P[] = {
(FFModuleBaseInfo*) &instance.config.modules.packages,
(FFModuleBaseInfo*) &instance.config.modules.physicalDisk,
(FFModuleBaseInfo*) &instance.config.modules.physicalMemory,
(FFModuleBaseInfo*) &instance.config.modules.player,
(FFModuleBaseInfo*) &instance.config.modules.powerAdapter,
(FFModuleBaseInfo*) &instance.config.modules.processes,
(FFModuleBaseInfo*) &instance.config.modules.publicIP,
&ffPackagesModuleInfo,
&ffPhysicalDiskModuleInfo,
&ffPhysicalMemoryModuleInfo,
&ffPlayerModuleInfo,
&ffPowerAdapterModuleInfo,
&ffProcessesModuleInfo,
&ffPublicIPModuleInfo,
NULL,
};
@@ -125,42 +125,42 @@ static FFModuleBaseInfo* R[] = {
};
static FFModuleBaseInfo* S[] = {
(FFModuleBaseInfo*) &instance.config.modules.separator,
(FFModuleBaseInfo*) &instance.config.modules.shell,
(FFModuleBaseInfo*) &instance.config.modules.sound,
(FFModuleBaseInfo*) &instance.config.modules.swap,
&ffSeparatorModuleInfo,
&ffShellModuleInfo,
&ffSoundModuleInfo,
&ffSwapModuleInfo,
NULL,
};
static FFModuleBaseInfo* T[] = {
(FFModuleBaseInfo*) &instance.config.modules.terminal,
(FFModuleBaseInfo*) &instance.config.modules.terminalFont,
(FFModuleBaseInfo*) &instance.config.modules.terminalSize,
(FFModuleBaseInfo*) &instance.config.modules.terminalTheme,
(FFModuleBaseInfo*) &instance.config.modules.title,
(FFModuleBaseInfo*) &instance.config.modules.theme,
(FFModuleBaseInfo*) &instance.config.modules.tpm,
&ffTerminalModuleInfo,
&ffTerminalFontModuleInfo,
&ffTerminalSizeModuleInfo,
&ffTerminalThemeModuleInfo,
&ffTitleModuleInfo,
&ffThemeModuleInfo,
&ffTPMModuleInfo,
NULL,
};
static FFModuleBaseInfo* U[] = {
(FFModuleBaseInfo*) &instance.config.modules.uptime,
(FFModuleBaseInfo*) &instance.config.modules.users,
&ffUptimeModuleInfo,
&ffUsersModuleInfo,
NULL,
};
static FFModuleBaseInfo* V[] = {
(FFModuleBaseInfo*) &instance.config.modules.version,
(FFModuleBaseInfo*) &instance.config.modules.vulkan,
&ffVersionModuleInfo,
&ffVulkanModuleInfo,
NULL,
};
static FFModuleBaseInfo* W[] = {
(FFModuleBaseInfo*) &instance.config.modules.wallpaper,
(FFModuleBaseInfo*) &instance.config.modules.weather,
(FFModuleBaseInfo*) &instance.config.modules.wm,
(FFModuleBaseInfo*) &instance.config.modules.wifi,
(FFModuleBaseInfo*) &instance.config.modules.wmTheme,
&ffWallpaperModuleInfo,
&ffWeatherModuleInfo,
&ffWMModuleInfo,
&ffWifiModuleInfo,
&ffWMThemeModuleInfo,
NULL,
};
@@ -173,7 +173,7 @@ static FFModuleBaseInfo* Y[] = {
};
static FFModuleBaseInfo* Z[] = {
(FFModuleBaseInfo*) &instance.config.modules.zpool,
&ffZpoolModuleInfo,
NULL,
};
+13 -19
View File
@@ -2,29 +2,23 @@
#ifndef _WIN32
#include <net/if.h>
#else
#define IF_NAMESIZE 0
#include <netinet/in.h>
#endif
bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex);
enum { IF_INDEX_UNINITIALIZED = (uint32_t) -1, IF_INDEX_INVALID = (uint32_t) -2 };
static uint32_t ifIndex = IF_INDEX_UNINITIALIZED;
static char ifName[IF_NAMESIZE + 1];
static inline void init()
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV4(void)
{
if (ifIndex == (uint32_t) IF_INDEX_UNINITIALIZED && !ffNetifGetDefaultRouteImpl(ifName, &ifIndex))
ifIndex = (uint32_t) IF_INDEX_INVALID;
static FFNetifDefaultRouteResult result;
if (result.status == FF_NETIF_UNINITIALIZED) {
result.status = ffNetifGetDefaultRouteImplV4(&result) ? FF_NETIF_OK : FF_NETIF_INVALID;
}
return &result;
}
const char* ffNetifGetDefaultRouteIfName()
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV6(void)
{
init();
return ifName;
}
uint32_t ffNetifGetDefaultRouteIfIndex()
{
init();
return ifIndex;
static FFNetifDefaultRouteResult result;
if (result.status == FF_NETIF_UNINITIALIZED) {
result.status = ffNetifGetDefaultRouteImplV6(&result) ? FF_NETIF_OK : FF_NETIF_INVALID;
}
return &result;
}
+23 -2
View File
@@ -3,7 +3,28 @@
#include "fastfetch.h"
#ifndef _WIN32
const char* ffNetifGetDefaultRouteIfName();
#include <net/if.h>
#include <netinet/in.h>
#endif
uint32_t ffNetifGetDefaultRouteIfIndex();
typedef enum __attribute__((__packed__)) FFNetifDefaultRouteResultStatus {
FF_NETIF_UNINITIALIZED,
FF_NETIF_INVALID,
FF_NETIF_OK
} FFNetifDefaultRouteResultStatus;
typedef struct FFNetifDefaultRouteResult {
uint32_t ifIndex;
#ifndef _WIN32
char ifName[IF_NAMESIZE + 1];
uint32_t preferredSourceAddrV4;
#endif
enum FFNetifDefaultRouteResultStatus status;
} FFNetifDefaultRouteResult;
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result);
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result);
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV4(void);
const FFNetifDefaultRouteResult* ffNetifGetDefaultRouteV6(void);
+201
View File
@@ -0,0 +1,201 @@
#include "netif.h"
#include "common/io/io.h"
#include "util/mallocHelper.h"
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n))
#if __APPLE__
// https://github.com/apple-oss-distributions/network_cmds/blob/8f38231438e6a4d16ef8015e97e12c2c05105644/rtsol.tproj/if.c#L243
#define ROUNDUP(a) ROUNDUP2((a), sizeof(uint32_t))
#elif __sun
// https://github.com/illumos/illumos-gate/blob/95b8c88950fa7b19af46bc63230137cf96b0bff7/usr/src/cmd/cmd-inet/usr.sbin/route.c#L339
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
#else
#error unknown platform
#endif
static struct sockaddr *
get_rt_address(struct rt_msghdr *rtm, int desired)
{
struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
for (int i = 0; i < RTAX_MAX; i++)
{
if (rtm->rtm_addrs & (1 << i))
{
if ((1 << i) == desired)
return sa;
#ifndef __sun
uint32_t salen = sa->sa_len;
#else
uint32_t salen;
// https://github.com/illumos/illumos-gate/blob/95b8c88950fa7b19af46bc63230137cf96b0bff7/usr/src/cmd/cmd-inet/usr.sbin/route.c#L2941
switch (sa->sa_family) {
case AF_INET:
salen = sizeof (struct sockaddr_in);
break;
case AF_LINK:
salen = sizeof (struct sockaddr_dl);
break;
case AF_INET6:
salen = sizeof (struct sockaddr_in6);
break;
default:
salen = sizeof (struct sockaddr);
break;
}
#endif
sa = (struct sockaddr *)(ROUNDUP(salen) + (char *)sa);
}
}
return NULL;
}
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
{
//https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET);
if (pfRoute < 0)
return false;
{
struct timeval timeout = {1, 0};
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
}
int pid = getpid();
struct {
struct rt_msghdr hdr;
struct sockaddr_in dst;
uint8_t data[512];
} rtmsg = {
.hdr = {
.rtm_type = RTM_GET,
.rtm_flags = RTF_UP | RTF_GATEWAY,
.rtm_version = RTM_VERSION,
.rtm_addrs = RTA_DST | RTA_IFP | RTA_IFA,
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
.rtm_pid = pid,
.rtm_seq = 1,
},
.dst = {
.sin_family = AF_INET,
#ifndef __sun
.sin_len = sizeof(rtmsg.dst),
#endif
},
};
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
return false;
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid))
;
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
if ((rtmsg.hdr.rtm_flags & (RTF_UP | RTF_GATEWAY)) == (RTF_UP | RTF_GATEWAY))
#endif
{
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP);
if (sdl
#ifndef __sun
&& sdl->sdl_len
#endif
)
{
assert(sdl->sdl_nlen <= IF_NAMESIZE);
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
result->ifName[sdl->sdl_nlen] = '\0';
result->ifIndex = sdl->sdl_index;
// Get the preferred source address
struct sockaddr_in* src = (struct sockaddr_in*)get_rt_address(&rtmsg.hdr, RTA_IFA);
if (src && src->sin_family == AF_INET)
result->preferredSourceAddrV4 = src->sin_addr.s_addr;
return true;
}
return false;
}
return false;
}
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
{
//https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET6);
if (pfRoute < 0)
return false;
{
struct timeval timeout = {1, 0};
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
}
int pid = getpid();
struct {
struct rt_msghdr hdr;
struct sockaddr_in6 dst;
uint8_t data[512];
} rtmsg = {
.hdr = {
.rtm_type = RTM_GET,
.rtm_flags = RTF_UP | RTF_GATEWAY,
.rtm_version = RTM_VERSION,
.rtm_addrs = RTA_DST | RTA_IFP,
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
.rtm_pid = pid,
.rtm_seq = 2,
},
.dst = {
.sin6_family = AF_INET6,
#ifndef __sun
.sin6_len = sizeof(rtmsg.dst),
#endif
},
};
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
return false;
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == pid))
;
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
if ((rtmsg.hdr.rtm_flags & (RTF_UP | RTF_GATEWAY)) == (RTF_UP | RTF_GATEWAY))
#endif
{
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP);
if (sdl
#ifndef __sun
&& sdl->sdl_len
#endif
)
{
assert(sdl->sdl_nlen <= IF_NAMESIZE);
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
result->ifName[sdl->sdl_nlen] = '\0';
result->ifIndex = sdl->sdl_index;
return true;
}
return false;
}
return false;
}
+50 -74
View File
@@ -8,25 +8,24 @@
#include <net/route.h>
#include <netinet/in.h>
#include <sys/socket.h>
#if defined(__OpenBSD__) || defined(__DragonFly__)
#include <sys/sysctl.h>
#endif
#include <sys/sysctl.h>
#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n))
#if defined(__APPLE__)
# define ROUNDUP(a) ROUNDUP2((a), sizeof(int))
#elif defined(__NetBSD__)
# define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t))
#elif defined(__FreeBSD__) || defined(__DragonFly__)
# define ROUNDUP(a) ROUNDUP2((a), sizeof(int))
#elif defined(__OpenBSD__)
# define ROUNDUP(a) ROUNDUP2((a), sizeof(int))
#elif defined(__sun)
# define ROUNDUP(a) ROUNDUP2((a), _SS_ALIGNSIZE)
#if __DragonFly__
// https://github.com/DragonFlyBSD/DragonFlyBSD/blob/cf0aa2f1e47a3f0a6055fe427563cb3f3e627064/sys/net/route.h#L315C9-L315C19
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
#elif __FreeBSD__
// https://github.com/freebsd/freebsd-src/blob/e4c0ecba44b20ebb2e4d80978c2cb6d16b730cb9/sys/net/route.h#L368C9-L368C16
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
#elif __NetBSD__
// https://github.com/NetBSD/src/blob/29beb637d057520c0ed37ac2cde966f7cc0cadf4/sys/net/route.h#L330
#define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t))
#elif __OpenBSD__
// https://github.com/openbsd/src/blob/ca647cfa4ec3ccb8360714bc0ebc32a394f7fb6a/regress/sys/netinet/bindconnect/bindconnect.c#L250
#define ROUNDUP(a) ROUNDUP2((a), sizeof(long))
#else
# error unknown platform
#error unknown platform
#endif
static struct sockaddr *
@@ -40,23 +39,18 @@ get_rt_address(struct rt_msghdr *rtm, int desired)
{
if ((1 << i) == desired)
return sa;
#ifdef __sun
sa = (struct sockaddr *)(ROUNDUP(sizeof(struct sockaddr)) + (char *)sa);
#else
sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa);
#endif
}
}
return NULL;
}
bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
{
#if defined(__OpenBSD__) || defined(__DragonFly__)
int mib[6] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY};
size_t needed;
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0)
return false;
FF_AUTO_FREE char* buf = malloc(needed);
@@ -71,80 +65,62 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
rtm = (struct rt_msghdr *)next;
struct sockaddr* sa = (struct sockaddr *)(rtm + 1);
if ((rtm->rtm_flags & RTF_GATEWAY) && (sa->sa_family == AF_INET))
if ((rtm->rtm_flags & RTF_GATEWAY) && !(rtm->rtm_flags & RTF_REJECT) && (sa->sa_family == AF_INET))
{
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP);
if (sdl->sdl_family == AF_LINK)
if (sdl && sdl->sdl_family == AF_LINK)
{
assert(sdl->sdl_nlen <= IF_NAMESIZE);
memcpy(iface, sdl->sdl_data, sdl->sdl_nlen);
iface[sdl->sdl_nlen] = '\0';
*ifIndex = sdl->sdl_index;
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
result->ifName[sdl->sdl_nlen] = '\0';
result->ifIndex = sdl->sdl_index;
// Get the preferred source address
struct sockaddr_in* src = (struct sockaddr_in*)get_rt_address(rtm, RTA_IFA);
if (src && src->sin_family == AF_INET)
result->preferredSourceAddrV4 = src->sin_addr.s_addr;
return true;
}
}
}
#else
//https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104
return false;
}
FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET);
if (pfRoute < 0)
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
{
int mib[6] = {CTL_NET, PF_ROUTE, 0, AF_INET6, NET_RT_FLAGS, RTF_GATEWAY};
size_t needed;
if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0 || needed == 0)
return false;
{
struct timeval timeout = {1, 0};
setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
}
FF_AUTO_FREE char* buf = malloc(needed);
int pid = getpid();
struct {
struct rt_msghdr hdr;
struct sockaddr_in dst;
uint8_t data[512];
} rtmsg = {
.hdr = {
.rtm_type = RTM_GET,
.rtm_flags = RTF_UP | RTF_GATEWAY,
.rtm_version = RTM_VERSION,
.rtm_addrs = RTA_DST | RTA_IFP,
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
.rtm_pid = pid,
.rtm_seq = 1,
},
.dst = {
.sin_family = AF_INET,
#ifndef __sun
.sin_len = sizeof(rtmsg.dst),
#endif
},
};
if (write(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen) != rtmsg.hdr.rtm_msglen)
if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
return false;
while (read(pfRoute, &rtmsg, sizeof(rtmsg)) > 0)
char* lim = buf + needed;
struct rt_msghdr* rtm;
for (char* next = buf; next < lim; next += rtm->rtm_msglen)
{
if (rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid)
rtm = (struct rt_msghdr *)next;
struct sockaddr* sa = (struct sockaddr *)(rtm + 1);
if ((rtm->rtm_flags & RTF_GATEWAY) && !(rtm->rtm_flags & RTF_REJECT) && (sa->sa_family == AF_INET6))
{
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP);
if (sdl
#ifndef __sun
&& sdl->sdl_len
#endif
)
struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP);
if (sdl && sdl->sdl_family == AF_LINK)
{
assert(sdl->sdl_nlen <= IF_NAMESIZE);
memcpy(iface, sdl->sdl_data, sdl->sdl_nlen);
iface[sdl->sdl_nlen] = '\0';
*ifIndex = sdl->sdl_index;
memcpy(result->ifName, sdl->sdl_data, sdl->sdl_nlen);
result->ifName[sdl->sdl_nlen] = '\0';
result->ifIndex = sdl->sdl_index;
return true;
}
return false;
}
}
#endif
return false;
}
+35
View File
@@ -0,0 +1,35 @@
#include "netif.h"
#include "common/io/io.h"
#include <net/if.h>
#include <stdio.h>
#define FF_STR_INDIR(x) #x
#define FF_STR(x) FF_STR_INDIR(x)
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
{
FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/route", "r");
if (!netRoute) return false;
// skip first line
FF_UNUSED(fscanf(netRoute, "%*[^\n]\n"));
unsigned long long destination; //, gateway, flags, refCount, use, metric, mask, mtu, ...
while (fscanf(netRoute, "%" FF_STR(IF_NAMESIZE) "s%llx%*[^\n]", result->ifName, &destination) == 2)
{
if (destination != 0) continue;
result->ifIndex = if_nametoindex(result->ifName);
// TODO: Get the preferred source address
return true;
}
result->ifName[0] = '0';
return false;
}
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
{
// TODO: AF_INET6
FF_UNUSED(result);
return false;
}
+55 -6
View File
@@ -11,9 +11,8 @@
// loosely based on Haiku's src/bin/network/route/route.cpp
bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
{
// TODO: AF_INET6
FF_AUTO_CLOSE_FD int pfRoute = socket(AF_INET, SOCK_RAW, AF_INET);
if (pfRoute < 0)
return false;
@@ -42,8 +41,11 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
while (interface < end) {
if (interface->ifr_route.flags & RTF_DEFAULT) {
strlcpy(iface, interface->ifr_name, IF_NAMESIZE);
*ifIndex = if_nametoindex(interface->ifr_name);
// interface->ifr_metric?
strlcpy(result->ifName, interface->ifr_name, IF_NAMESIZE);
result->ifIndex = if_nametoindex(interface->ifr_name);
if (interface->ifr_route.source)
result->preferredSourceAddrV4 = ((struct sockaddr_in*)interface->ifr_route.source)->sin_addr.s_addr;
return true;
}
@@ -55,9 +57,56 @@ bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
if (interface->ifr_route.gateway != NULL)
addressSize += interface->ifr_route.gateway->sa_len;
interface = (struct ifreq*)((addr_t)interface + IF_NAMESIZE
+ sizeof(struct route_entry) + addressSize);
interface = (struct ifreq*)((addr_t)interface + IF_NAMESIZE + sizeof(struct route_entry) + addressSize);
}
return false;
}
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
{
FF_AUTO_CLOSE_FD int pfRoute = socket(AF_INET, SOCK_RAW, AF_INET6);
if (pfRoute < 0)
return false;
struct ifconf config;
config.ifc_len = sizeof(config.ifc_value);
if (ioctl(pfRoute, SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0)
return false;
int size = config.ifc_value;
if (size <= 0)
return false;
FF_AUTO_FREE void *buffer = malloc((size_t) size);
if (buffer == NULL) {
return false;
}
config.ifc_len = size;
config.ifc_buf = buffer;
if (ioctl(pfRoute, SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0)
return false;
struct ifreq *interface = (struct ifreq*)buffer;
struct ifreq *end = (struct ifreq*)((uint8_t*)buffer + size);
while (interface < end) {
if (interface->ifr_route.flags & RTF_DEFAULT) {
strlcpy(result->ifName, interface->ifr_name, IF_NAMESIZE);
result->ifIndex = if_nametoindex(interface->ifr_name);
return true;
}
size_t addressSize = 0;
if (interface->ifr_route.destination != NULL)
addressSize += interface->ifr_route.destination->sa_len;
if (interface->ifr_route.mask != NULL)
addressSize += interface->ifr_route.mask->sa_len;
if (interface->ifr_route.gateway != NULL)
addressSize += interface->ifr_route.gateway->sa_len;
interface = (struct ifreq*)((addr_t)interface + IF_NAMESIZE + sizeof(struct route_entry) + addressSize);
}
return false;
}
+412 -32
View File
@@ -1,52 +1,432 @@
#include "netif.h"
#include "common/io/io.h"
#include "util/mallocHelper.h"
#include "util/debug.h"
#include <arpa/inet.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <stdio.h>
#define FF_STR_INDIR(x) #x
#define FF_STR(x) FF_STR_INDIR(x)
static bool getDefaultRouteIPv4(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
{
FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/net/route", "r");
if (!netRoute) return false;
FF_DEBUG("Starting IPv4 default route detection");
// skip first line
FF_UNUSED(fscanf(netRoute, "%*[^\n]\n"));
unsigned long long destination; //, gateway, flags, refCount, use, metric, mask, mtu, ...
while (fscanf(netRoute, "%" FF_STR(IF_NAMESIZE) "s%llx%*[^\n]", iface, &destination) == 2)
FF_AUTO_CLOSE_FD int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sock_fd < 0)
{
if (destination != 0) continue;
*ifIndex = if_nametoindex(iface);
FF_DEBUG("Failed to create netlink socket: %s", strerror(errno));
return false;
}
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
unsigned pid = (unsigned) getpid();
FF_DEBUG("Process PID: %u", pid);
// Bind socket
struct sockaddr_nl addr = {
.nl_family = AF_NETLINK,
.nl_pid = 0, // Let kernel choose PID
.nl_groups = 0, // No multicast groups
};
if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
FF_DEBUG("Failed to bind socket: %s", strerror(errno));
return false;
}
FF_DEBUG("Successfully bound socket");
struct __attribute__((__packed__)) {
struct nlmsghdr nlh;
struct rtmsg rtm;
struct rtattr rta;
uint32_t table;
} req = {
// Netlink message header
.nlh = {
.nlmsg_len = sizeof(req),
.nlmsg_type = RTM_GETROUTE,
.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
.nlmsg_seq = 1,
.nlmsg_pid = pid,
},
// Route message
.rtm = {
.rtm_family = AF_INET,
.rtm_dst_len = 0, // Match all destinations
.rtm_src_len = 0, // Match all sources
.rtm_tos = 0,
.rtm_table = RT_TABLE_UNSPEC,
.rtm_protocol = RTPROT_UNSPEC,
.rtm_scope = RT_SCOPE_UNIVERSE,
.rtm_type = RTN_UNSPEC,
.rtm_flags = 0,
},
// Route attribute for main table
.rta = {
.rta_len = RTA_LENGTH(sizeof(uint32_t)),
.rta_type = RTA_TABLE,
},
.table = RT_TABLE_MAIN,
};
struct sockaddr_nl dest_addr = {
.nl_family = AF_NETLINK,
.nl_pid = 0, // Kernel
.nl_groups = 0, // No multicast groups
};
ssize_t sent = sendto(sock_fd, &req, sizeof(req), 0,
(struct sockaddr*)&dest_addr, sizeof(dest_addr));
if (sent != sizeof(req)) {
FF_DEBUG("Failed to send netlink request: sent=%zd, expected=%zu", sent, sizeof(req));
return false;
}
FF_DEBUG("Sent netlink request: %zd bytes", sent);
struct sockaddr_nl src_addr = {};
socklen_t src_addr_len = sizeof(src_addr);
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
uint32_t minMetric = UINT32_MAX;
int routeCount = 0;
while (true)
{
ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&src_addr, &src_addr_len);
if (received < 0) {
FF_DEBUG("Failed to receive netlink response: %s", strerror(errno));
return false;
}
if (received >= (ssize_t)sizeof(buffer)) {
FF_DEBUG("Received truncated message: received %zd, bufsize %zu", received, sizeof(buffer));
return false;
}
FF_DEBUG("Received netlink response: %zd bytes", received);
if (received == 0) {
FF_DEBUG("Received zero-length netlink response, ending processing");
break;
}
struct {
uint32_t metric;
uint32_t ifindex;
uint32_t prefsrc;
} entry;
for (const struct nlmsghdr* nlh = (struct nlmsghdr*)buffer;
NLMSG_OK(nlh, received);
nlh = NLMSG_NEXT(nlh, received))
{
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid)
continue;
if (nlh->nlmsg_type == NLMSG_DONE)
{
FF_DEBUG("Received NLMSG_DONE, processed %d routes", routeCount);
goto exit;
}
if (nlh->nlmsg_type == NLMSG_ERROR) {
FF_DEBUG("Netlink reports error: %s", strerror(-((struct nlmsgerr*)NLMSG_DATA(nlh))->error));
continue;
}
if (nlh->nlmsg_type != RTM_NEWROUTE) {
FF_DEBUG("Skipping non-route message: type=%d", nlh->nlmsg_type);
continue;
}
routeCount++;
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nlh);
if (rtm->rtm_family != AF_INET) {
FF_DEBUG("Skipping non-IPv4 route #%d (family=%d)", routeCount, rtm->rtm_family);
continue;
}
if (rtm->rtm_dst_len != 0) {
FF_DEBUG("Skipping non-default route #%d (dst_len=%d)", routeCount, rtm->rtm_dst_len);
continue;
}
FF_DEBUG("Processing IPv4 default route candidate #%d", routeCount);
entry = (__typeof__(entry)) { }; // Default to zero metric (no RTA_PRIORITY found)
// Parse route attributes
size_t rtm_len = RTM_PAYLOAD(nlh);
for (struct rtattr* rta = RTM_RTA(rtm);
RTA_OK(rta, rtm_len);
rta = RTA_NEXT(rta, rtm_len))
{
if (RTA_PAYLOAD(rta) < sizeof(uint32_t))
continue; // Skip invalid attributes
uint32_t rta_data = *(uint32_t*) RTA_DATA(rta);
switch (rta->rta_type) {
case RTA_DST:
FF_DEBUG("Unexpected RTA_DST: %s (len=%u)", inet_ntoa((struct in_addr) { .s_addr = rta_data }), rtm->rtm_dst_len);
goto next;
case RTA_OIF:
entry.ifindex = rta_data;
FF_DEBUG("Found interface index: %u", entry.ifindex);
break;
case RTA_GATEWAY:
FF_DEBUG("Found gateway: %s", inet_ntoa(*(struct in_addr*)&rta_data));
if (rta_data == 0) goto next;
break;
case RTA_PRIORITY:
FF_DEBUG("Found metric: %u", rta_data);
if (rta_data >= minMetric) goto next;
entry.metric = rta_data;
break;
case RTA_PREFSRC:
entry.prefsrc = rta_data;
FF_DEBUG("Found preferred source: %s", inet_ntoa(*(struct in_addr*)&rta_data));
break;
}
}
if (entry.ifindex == 0 || entry.metric >= minMetric)
{
next:
FF_DEBUG("Skipping route: ifindex=%u, metric=%u", entry.ifindex, entry.metric);
continue;
}
minMetric = entry.metric;
result->ifIndex = entry.ifindex;
FF_DEBUG("Updated best route: ifindex=%u, metric=%u, prefsrc=%x", entry.ifindex, entry.metric, entry.prefsrc);
result->preferredSourceAddrV4 = entry.prefsrc;
if (minMetric == 0)
{
FF_DEBUG("Found zero metric route, stopping further processing");
break; // Stop processing if we found a zero metric route
}
}
}
exit:
if (minMetric < UINT32_MAX)
{
if_indextoname(result->ifIndex, result->ifName);
FF_DEBUG("Found default IPv4 route: interface=%s, index=%u, metric=%u", result->ifName, result->ifIndex, minMetric);
return true;
}
iface[0] = '\0';
FF_DEBUG("No IPv4 default route found");
return false;
}
static bool getDefaultRouteIPv6(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
{
FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/net/ipv6_route", "r");
if (!netRoute) return false;
FF_DEBUG("Starting IPv6 default route detection");
uint32_t prefixLen;
//destination, dest_prefix_len, source, src_prefix_len, next hop, metric, ref counter, use counter, flags, iface
while (fscanf(netRoute, "%*s %x %*s %*s %*s %*s %*s %*s %*s %" FF_STR(IF_NAMESIZE) "s", &prefixLen, iface) == 2)
FF_AUTO_CLOSE_FD int sock_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
if (sock_fd < 0)
{
if (prefixLen != 0) continue;
*ifIndex = if_nametoindex(iface);
FF_DEBUG("Failed to create netlink socket: %s", strerror(errno));
return false;
}
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
unsigned pid = (unsigned) getpid();
FF_DEBUG("Process PID: %u", pid);
// Bind socket
struct sockaddr_nl addr = {
.nl_family = AF_NETLINK,
.nl_pid = 0, // Let kernel choose PID
.nl_groups = 0, // No multicast groups
};
if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
FF_DEBUG("Failed to bind socket: %s", strerror(errno));
return false;
}
FF_DEBUG("Successfully bound socket");
struct __attribute__((__packed__)) {
struct nlmsghdr nlh;
struct rtmsg rtm;
struct rtattr rta;
uint32_t table;
} req = {
// Netlink message header
.nlh = {
.nlmsg_len = sizeof(req),
.nlmsg_type = RTM_GETROUTE,
.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
.nlmsg_seq = 1,
.nlmsg_pid = pid,
},
// Route message
.rtm = {
.rtm_family = AF_INET6, // IPv6 instead of IPv4
.rtm_dst_len = 0, // Match all destinations
.rtm_src_len = 0, // Match all sources
.rtm_tos = 0,
.rtm_table = RT_TABLE_UNSPEC,
.rtm_protocol = RTPROT_UNSPEC,
.rtm_scope = RT_SCOPE_UNIVERSE,
.rtm_type = RTN_UNSPEC,
.rtm_flags = 0,
},
// Route attribute for main table
.rta = {
.rta_len = RTA_LENGTH(sizeof(uint32_t)),
.rta_type = RTA_TABLE,
},
.table = RT_TABLE_MAIN,
};
struct sockaddr_nl dest_addr = {
.nl_family = AF_NETLINK,
.nl_pid = 0, // Kernel
.nl_groups = 0, // No multicast groups
};
ssize_t sent = sendto(sock_fd, &req, sizeof(req), 0,
(struct sockaddr*)&dest_addr, sizeof(dest_addr));
if (sent != sizeof(req)) {
FF_DEBUG("Failed to send netlink request: sent=%zd, expected=%zu", sent, sizeof(req));
return false;
}
FF_DEBUG("Sent netlink request: %zd bytes", sent);
struct sockaddr_nl src_addr = {};
socklen_t src_addr_len = sizeof(src_addr);
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
uint32_t minMetric = UINT32_MAX;
int routeCount = 0;
while (true)
{
ssize_t received = recvfrom(sock_fd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&src_addr, &src_addr_len);
if (received < 0) {
FF_DEBUG("Failed to receive netlink response: %s", strerror(errno));
return false;
}
if (received >= (ssize_t)sizeof(buffer)) {
FF_DEBUG("Received truncated message: received %zd, bufsize %zu", received, sizeof(buffer));
return false;
}
FF_DEBUG("Received netlink response: %zd bytes", received);
if (received == 0) {
FF_DEBUG("Received zero-length netlink response, ending processing");
break;
}
struct {
uint32_t metric;
uint32_t ifindex;
} entry;
for (const struct nlmsghdr* nlh = (struct nlmsghdr*)buffer;
NLMSG_OK(nlh, received);
nlh = NLMSG_NEXT(nlh, received))
{
if (nlh->nlmsg_seq != 1 || nlh->nlmsg_pid != pid)
continue;
if (nlh->nlmsg_type == NLMSG_DONE)
{
FF_DEBUG("Received NLMSG_DONE, processed %d routes", routeCount);
goto exit;
}
if (nlh->nlmsg_type == NLMSG_ERROR) {
FF_DEBUG("Netlink reports error: %s", strerror(-((struct nlmsgerr*)NLMSG_DATA(nlh))->error));
continue;
}
if (nlh->nlmsg_type != RTM_NEWROUTE) {
FF_DEBUG("Skipping non-route message: type=%d", nlh->nlmsg_type);
continue;
}
routeCount++;
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nlh);
if (rtm->rtm_family != AF_INET6) {
FF_DEBUG("Skipping non-IPv6 route #%d (family=%d)", routeCount, rtm->rtm_family);
continue;
}
if (rtm->rtm_dst_len != 0) {
FF_DEBUG("Skipping non-default route #%d (dst_len=%d)", routeCount, rtm->rtm_dst_len);
continue;
}
FF_DEBUG("Processing IPv6 default route candidate #%d", routeCount);
entry = (__typeof__(entry)) { }; // Default to zero metric (no RTA_PRIORITY found)
// Parse route attributes
size_t rtm_len = RTM_PAYLOAD(nlh);
for (struct rtattr* rta = RTM_RTA(rtm);
RTA_OK(rta, rtm_len);
rta = RTA_NEXT(rta, rtm_len))
{
switch (rta->rta_type) {
case RTA_DST:
if (RTA_PAYLOAD(rta) >= sizeof(struct in6_addr)) {
FF_MAYBE_UNUSED char str[INET6_ADDRSTRLEN];
FF_DEBUG("Unexpected RTA_DST: %s", inet_ntop(AF_INET6, RTA_DATA(rta), str, sizeof(str)));
goto next;
}
break;
case RTA_OIF:
if (RTA_PAYLOAD(rta) >= sizeof(uint32_t)) {
entry.ifindex = *(uint32_t*) RTA_DATA(rta);
FF_DEBUG("Found interface index: %u", entry.ifindex);
}
break;
case RTA_GATEWAY:
if (RTA_PAYLOAD(rta) >= sizeof(struct in6_addr)) {
struct in6_addr* gw = (struct in6_addr*) RTA_DATA(rta);
if (IN6_IS_ADDR_UNSPECIFIED(gw)) goto next;
FF_MAYBE_UNUSED char str[INET6_ADDRSTRLEN];
FF_DEBUG("Found gateway: %s", inet_ntop(AF_INET6, gw, str, sizeof(str)));
}
break;
case RTA_PRIORITY:
if (RTA_PAYLOAD(rta) >= sizeof(uint32_t)) {
uint32_t metric = *(uint32_t*) RTA_DATA(rta);
FF_DEBUG("Found metric: %u", metric);
if (metric >= minMetric) goto next;
entry.metric = metric;
}
break;
}
}
if (entry.ifindex == 0 || entry.metric >= minMetric)
{
next:
FF_DEBUG("Skipping route: ifindex=%u, metric=%u", entry.ifindex, entry.metric);
continue;
}
minMetric = entry.metric;
result->ifIndex = entry.ifindex;
FF_DEBUG("Updated best route: ifindex=%u, metric=%u", entry.ifindex, entry.metric);
if (minMetric == 0)
{
FF_DEBUG("Found zero metric route, stopping further processing");
break; // Stop processing if we found a zero metric route
}
}
}
exit:
if (minMetric < UINT32_MAX)
{
if_indextoname(result->ifIndex, result->ifName);
FF_DEBUG("Found default IPv6 route: interface=%s, index=%u, metric=%u", result->ifName, result->ifIndex, minMetric);
return true;
}
iface[0] = '\0';
FF_DEBUG("No IPv6 default route found");
return false;
}
bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
{
if (getDefaultRouteIPv4(iface, ifIndex))
return true;
return getDefaultRouteIPv6(iface, ifIndex);
}
+78 -11
View File
@@ -4,29 +4,96 @@
#include <ws2tcpip.h> // AF_INET6, IN6_IS_ADDR_UNSPECIFIED
#include <iphlpapi.h>
bool ffNetifGetDefaultRouteImpl(FF_MAYBE_UNUSED char iface[IF_NAMESIZE + 1], uint32_t* ifIndex)
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
{
PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL;
DWORD result = GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable);
if (result != NO_ERROR)
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable)))
return false;
bool foundDefault = false;
uint32_t smallestMetric = UINT32_MAX;
for (ULONG i = 0; i < pIpForwardTable->NumEntries; ++i)
{
MIB_IPFORWARD_ROW2* row = &pIpForwardTable->Table[i];
if ((row->DestinationPrefix.PrefixLength == 0) &&
((row->DestinationPrefix.Prefix.Ipv4.sin_family == AF_INET &&
row->DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr == 0) ||
(row->DestinationPrefix.Prefix.Ipv6.sin6_family == AF_INET6 &&
IN6_IS_ADDR_UNSPECIFIED(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr))))
if (row->DestinationPrefix.PrefixLength == 0 &&
row->DestinationPrefix.Prefix.Ipv4.sin_family == AF_INET &&
row->DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr == 0)
{
*ifIndex = row->InterfaceIndex;
foundDefault = true;
break;
MIB_IF_ROW2 ifRow = {
.InterfaceIndex = row->InterfaceIndex,
};
if (NETIO_SUCCESS(GetIfEntry2(&ifRow)) && ifRow.OperStatus == IfOperStatusUp)
{
MIB_IPINTERFACE_ROW ipInterfaceRow = {
.Family = AF_INET,
.InterfaceIndex = row->InterfaceIndex,
};
uint32_t realMetric = row->Metric /* Metric offset */;
if (NETIO_SUCCESS(GetIpInterfaceEntry(&ipInterfaceRow)))
realMetric += ipInterfaceRow.Metric /* Interface metric */;
if (realMetric < smallestMetric)
{
smallestMetric = realMetric;
result->ifIndex = row->InterfaceIndex;
foundDefault = true;
break;
}
}
}
}
FreeMibTable(pIpForwardTable);
return foundDefault;
}
bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
{
PMIB_IPFORWARD_TABLE2 pIpForwardTable = NULL;
if (!NETIO_SUCCESS(GetIpForwardTable2(AF_UNSPEC, &pIpForwardTable)))
return false;
bool foundDefault = false;
uint32_t smallestMetric = UINT32_MAX;
for (ULONG i = 0; i < pIpForwardTable->NumEntries; ++i)
{
MIB_IPFORWARD_ROW2* row = &pIpForwardTable->Table[i];
if (row->DestinationPrefix.PrefixLength == 0 &&
row->DestinationPrefix.Prefix.Ipv6.sin6_family == AF_INET6 &&
IN6_IS_ADDR_UNSPECIFIED(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr))
{
MIB_IF_ROW2 ifRow = {
.InterfaceIndex = row->InterfaceIndex,
};
if (NETIO_SUCCESS(GetIfEntry2(&ifRow)) && ifRow.OperStatus == IfOperStatusUp)
{
MIB_IPINTERFACE_ROW ipInterfaceRow = {
.Family = AF_INET6,
.InterfaceIndex = row->InterfaceIndex,
};
uint32_t realMetric = row->Metric /* Metric offset */;
if (NETIO_SUCCESS(GetIpInterfaceEntry(&ipInterfaceRow)))
realMetric += ipInterfaceRow.Metric /* Interface metric */;
if (realMetric < smallestMetric)
{
smallestMetric = realMetric;
result->ifIndex = row->InterfaceIndex;
foundDefault = true;
}
}
}
}
+5 -5
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>
@@ -22,7 +22,7 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state)
if (!state->tfo)
{
#ifdef __linux__
#if __linux__ || __GNU__
// Linux doesn't support sendto() on unconnected sockets
FF_DEBUG("TCP Fast Open disabled, skipping");
return "TCP Fast Open disabled";
@@ -34,7 +34,7 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state)
#ifndef __APPLE__ // On macOS, TCP_FASTOPEN doesn't seem to be needed
// Set TCP Fast Open
#ifdef __linux__
#if __linux__ || __GNU__
int flag = 5; // the queue length of pending packets
#else
int flag = 1; // enable TCP Fast Open
@@ -50,7 +50,7 @@ static const char* tryNonThreadingFastPath(FFNetworkingState* state)
FF_DEBUG("Failed to set TCP_FASTOPEN option: %s", strerror(errno));
return "setsockopt(TCP_FASTOPEN) failed";
} else {
#ifdef __linux__
#if __linux__ || __GNU__
FF_DEBUG("Successfully set TCP_FASTOPEN option, queue length: %d", flag);
#elif defined(__APPLE__)
FF_DEBUG("Successfully set TCP_FASTOPEN_FORCE_ENABLE option");
@@ -346,7 +346,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
FF_DEBUG("Multithreading mode enabled, creating connection thread");
state->thread = ffThreadCreate(connectAndSendThreadMain, state);
if (state->thread) {
FF_DEBUG("Thread creation successful: thread=%p", (void*)state->thread);
FF_DEBUG("Thread creation successful: thread=%p", (void*)(uintptr_t)state->thread);
return NULL;
}
FF_DEBUG("Thread creation failed");
+2 -49
View File
@@ -28,55 +28,6 @@ const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName)
return subKey;
}
bool ffOptionParseModuleArgs(const char* argumentKey, const char* subKey, const char* value, FFModuleArgs* result)
{
if(ffStrEqualsIgnCase(subKey, "key"))
{
ffOptionParseString(argumentKey, value, &result->key);
return true;
}
else if(ffStrEqualsIgnCase(subKey, "format"))
{
ffOptionParseString(argumentKey, value, &result->outputFormat);
return true;
}
else if(ffStrEqualsIgnCase(subKey, "output-color"))
{
if(value == NULL)
{
fprintf(stderr, "Error: usage: %s <str>\n", argumentKey);
exit(477);
}
ffOptionParseColor(value, &result->outputColor);
return true;
}
else if(ffStrStartsWithIgnCase(subKey, "key-"))
{
const char* subKey2 = subKey + strlen("key-");
if(ffStrEqualsIgnCase(subKey2, "color"))
{
if(value == NULL)
{
fprintf(stderr, "Error: usage: %s <str>\n", argumentKey);
exit(477);
}
ffOptionParseColor(value, &result->keyColor);
return true;
}
else if(ffStrEqualsIgnCase(subKey2, "width"))
{
result->keyWidth = ffOptionParseUInt32(argumentKey, value);
return true;
}
else if(ffStrEqualsIgnCase(subKey2, "icon"))
{
ffOptionParseString(argumentKey, value, &result->keyIcon);
return true;
}
}
return false;
}
void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* buffer)
{
if(value == NULL)
@@ -157,6 +108,8 @@ bool ffOptionParseBoolean(const char* str)
void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer)
{
if (!value || value[0] == '\0') return;
// If value is already an ANSI escape code, use it
if (value[0] == '\e' && value[1] == '[')
{
+7 -4
View File
@@ -29,10 +29,12 @@ typedef struct FFModuleBaseInfo
// This is UB, because `void*` is not compatible with `FF*Options*`.
// However we can't do it better unless we move to C++, so that `option` becomes a `this` pointer
// https://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type
bool (*parseCommandOptions)(void* options, const char* key, const char* value);
void (*initOptions)(void* options);
void (*destroyOptions)(void* options);
void (*parseJsonObject)(void* options, struct yyjson_val *module);
void (*printModule)(void* options);
void (*generateJsonResult)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* module);
bool (*printModule)(void* options); // true on success
bool (*generateJsonResult)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* module); // true on success
void (*generateJsonConfig)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* obj);
FFModuleFormatArgList formatArgs;
} FFModuleBaseInfo;
@@ -63,7 +65,6 @@ typedef struct FFKeyValuePair
} FFKeyValuePair;
const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName);
bool ffOptionParseModuleArgs(const char* argumentKey, const char* pkey, const char* value, FFModuleArgs* result);
void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* buffer);
FF_C_NODISCARD uint32_t ffOptionParseUInt32(const char* argumentKey, const char* value);
FF_C_NODISCARD int32_t ffOptionParseInt32(const char* argumentKey, const char* value);
@@ -94,3 +95,5 @@ static inline void ffOptionDestroyModuleArg(FFModuleArgs* args)
ffStrbufDestroy(&args->outputFormat);
ffStrbufDestroy(&args->outputColor);
}
enum { FF_OPTION_MAX_SIZE = 1 << 8 }; // Maximum size of a single option value, used for static allocation
+9 -107
View File
@@ -2,7 +2,6 @@
#include "common/parsing.h"
#include <ctype.h>
#include <inttypes.h>
#ifdef _WIN32
#pragma GCC diagnostic push
@@ -51,63 +50,21 @@ int8_t ffVersionCompare(const FFVersion* version1, const FFVersion* version2)
void ffVersionToPretty(const FFVersion* version, FFstrbuf* pretty)
{
if(version->major > 0 || version->minor > 0 || version->patch > 0)
ffStrbufAppendF(pretty, "%u", version->major);
{
ffStrbufAppendUInt(pretty, version->major);
}
if(version->minor > 0 || version->patch > 0)
ffStrbufAppendF(pretty, ".%u", version->minor);
{
ffStrbufAppendC(pretty, '.');
ffStrbufAppendUInt(pretty, version->minor);
}
if(version->patch > 0)
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++;
ffStrbufAppendC(pretty, '.');
ffStrbufAppendUInt(pretty, version->patch);
}
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)
@@ -190,61 +147,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);
+78 -64
View File
@@ -60,23 +60,32 @@ void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConf
const FFOptionsDisplay* options = &instance.config.display;
uint32_t blocksPercent = (uint32_t) (percent / 100.0 * options->barWidth + 0.5);
const bool borderAsValue = options->barBorderLeftElapsed.length && options->barBorderRightElapsed.length;
uint8_t blocksPercent = (uint8_t) (percent / 100.0 * options->barWidth + 0.5);
assert(blocksPercent <= options->barWidth);
if(options->barBorderLeft.length)
if(!borderAsValue && options->barBorderLeft.length)
{
if(!options->pipe)
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_WHITE "m");
if(!options->pipe && options->barColorBorder.length > 0)
ffStrbufAppendF(buffer, "\e[%sm", options->barColorBorder.chars);
ffStrbufAppend(buffer, &options->barBorderLeft);
}
if (percent != percent)
if (percent == -DBL_MAX)
{
if(!options->pipe)
// Use total color for simplification
if(!options->pipe && options->barColorTotal.length > 0)
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_BLACK "m");
for (uint32_t i = 0; i < options->barWidth; ++i)
ffStrbufAppend(buffer, &options->barCharElapsed);
for (uint8_t i = 0; i < options->barWidth; ++i)
{
ffStrbufAppend(buffer, borderAsValue && i == 0
? &options->barBorderLeft
: borderAsValue && i == options->barWidth - 1
? &options->barBorderRight
: &options->barCharTotal);
}
}
else
{
@@ -85,61 +94,72 @@ void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConf
const char* colorRed = options->percentColorRed.chars;
FFPercentageTypeFlags percentType = config.type == 0 ? options->percentType : config.type;
bool monochrome = !!(percentType & FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT);
for (uint32_t i = 0; i < blocksPercent; ++i)
bool autoColorElapsed = ffStrbufIgnCaseEqualS(&options->barColorElapsed, "auto");
bool monochrome = (percentType & FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT) || !autoColorElapsed;
if (!options->pipe && options->barColorElapsed.length > 0 && monochrome)
{
if(!options->pipe)
const char* color = NULL;
if (!autoColorElapsed)
color = options->barColorElapsed.chars;
else if (green <= yellow)
{
if (monochrome)
{
const char* color = NULL;
if (green <= yellow)
{
if (percent < green) color = colorGreen;
else if (percent < yellow) color = colorYellow;
else color = colorRed;
}
else
{
if (percent < yellow) color = colorRed;
else if (percent < green) color = colorYellow;
else color = colorGreen;
}
ffStrbufAppendF(buffer, "\e[%sm", color);
}
else
{
uint32_t section1Begin = (uint32_t) ((green <= yellow ? green : yellow) / 100.0 * options->barWidth + 0.5);
uint32_t section2Begin = (uint32_t) ((green > yellow ? green : yellow) / 100.0 * options->barWidth + 0.5);
if (i == section2Begin)
ffStrbufAppendF(buffer, "\e[%sm", (green > yellow ? colorGreen : colorRed));
else if (i == section1Begin)
ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
else if (i == 0)
ffStrbufAppendF(buffer, "\e[%sm", (green <= yellow ? colorGreen : colorRed));
}
if (percent < green) color = colorGreen;
else if (percent < yellow) color = colorYellow;
else color = colorRed;
}
ffStrbufAppend(buffer, &options->barCharElapsed);
else
{
if (percent < yellow) color = colorRed;
else if (percent < green) color = colorYellow;
else color = colorGreen;
}
ffStrbufAppendF(buffer, "\e[%sm", color);
}
for (uint8_t i = 0; i < blocksPercent; ++i)
{
if (!options->pipe && options->barColorElapsed.length > 0 && !monochrome)
{
uint32_t section1Begin = (uint32_t) ((green <= yellow ? green : yellow) / 100.0 * options->barWidth + 0.5);
uint32_t section2Begin = (uint32_t) ((green > yellow ? green : yellow) / 100.0 * options->barWidth + 0.5);
if (i == section2Begin)
ffStrbufAppendF(buffer, "\e[%sm", (green > yellow ? colorGreen : colorRed));
else if (i == section1Begin)
ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
else if (i == 0)
ffStrbufAppendF(buffer, "\e[%sm", (green <= yellow ? colorGreen : colorRed));
}
ffStrbufAppend(buffer, borderAsValue && i == 0
? &options->barBorderLeftElapsed
: borderAsValue && i == options->barWidth - 1
? &options->barBorderRightElapsed
: &options->barCharElapsed);
}
if (blocksPercent < options->barWidth)
{
if(!options->pipe)
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_WHITE "m");
for (uint32_t i = blocksPercent; i < options->barWidth; ++i)
ffStrbufAppend(buffer, &options->barCharTotal);
if(!options->pipe && options->barColorTotal.length > 0)
ffStrbufAppendF(buffer, "\e[%sm", options->barColorTotal.chars);
for (uint8_t i = blocksPercent; i < options->barWidth; ++i)
{
ffStrbufAppend(buffer, borderAsValue && i == 0
? &options->barBorderLeft
: borderAsValue && i == options->barWidth - 1
? &options->barBorderRight
: &options->barCharTotal);
}
}
}
if(options->barBorderRight.length)
if(!borderAsValue && options->barBorderRight.length)
{
if(!options->pipe)
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_WHITE "m");
if(!options->pipe && options->barColorBorder.length > 0)
ffStrbufAppendF(buffer, "\e[%sm", options->barColorBorder.chars);
ffStrbufAppend(buffer, &options->barBorderRight);
}
if(!options->pipe)
if(!options->pipe && (options->barColorElapsed.length > 0 || options->barColorTotal.length > 0 || options->barColorBorder.length > 0))
{
ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
appendOutputColor(buffer, module);
@@ -165,7 +185,7 @@ void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConf
const char* colorYellow = options->percentColorYellow.chars;
const char* colorRed = options->percentColorRed.chars;
if(percent != percent)
if(percent == -DBL_MAX)
ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_BLACK "m");
else if(green <= yellow)
{
@@ -175,7 +195,6 @@ void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConf
ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
else
ffStrbufAppendF(buffer, "\e[%sm", colorGreen);
}
else
{
@@ -187,7 +206,8 @@ void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConf
ffStrbufAppendF(buffer, "\e[%sm", colorGreen);
}
}
ffStrbufAppendF(buffer, "%.*f%%", options->percentNdigits, percent);
ffStrbufAppendF(buffer, "%*.*f%s%%", options->percentWidth, options->percentNdigits, percent,
options->percentSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "");
if (colored && !options->pipe)
{
@@ -239,14 +259,14 @@ bool ffPercentParseCommandOptions(const char* key, const char* subkey, const cha
return false;
}
bool ffPercentParseJsonObject(const char* key, yyjson_val* value, FFPercentageModuleConfig* config)
bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageModuleConfig* config)
{
if (!ffStrEqualsIgnCase(key, "percent"))
if (!unsafe_yyjson_equals_str(key, "percent"))
return false;
if (!yyjson_is_obj(value))
{
fprintf(stderr, "Error: usage: %s must be an object\n", key);
fprintf(stderr, "Error: usage: %s must be an object\n", unsafe_yyjson_get_str(key));
exit(480);
}
@@ -288,16 +308,10 @@ bool ffPercentParseJsonObject(const char* key, yyjson_val* value, FFPercentageMo
return true;
}
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig defaultConfig, FFPercentageModuleConfig config)
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig config)
{
if (config.green == defaultConfig.green && config.yellow == defaultConfig.yellow)
return;
yyjson_mut_val* percent = yyjson_mut_obj_add_obj(doc, module, "percent");
if (config.green != defaultConfig.green)
yyjson_mut_obj_add_uint(doc, percent, "green", config.green);
if (config.yellow != defaultConfig.yellow)
yyjson_mut_obj_add_uint(doc, percent, "yellow", config.yellow);
if (config.type != defaultConfig.type)
yyjson_mut_obj_add_uint(doc, percent, "type", config.type);
yyjson_mut_obj_add_uint(doc, percent, "green", config.green);
yyjson_mut_obj_add_uint(doc, percent, "yellow", config.yellow);
yyjson_mut_obj_add_uint(doc, percent, "type", config.type);
}
+3 -3
View File
@@ -11,7 +11,7 @@ typedef enum __attribute__((__packed__)) FFPercentageTypeFlags
FF_PERCENTAGE_TYPE_BAR_BIT = 1 << 1,
FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT = 1 << 2,
FF_PERCENTAGE_TYPE_NUM_COLOR_BIT = 1 << 3,
FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT = FF_PERCENTAGE_TYPE_NUM_COLOR_BIT,
FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT = 1 << 4,
FF_PERCENTAGE_TYPE_FORCE_UNSIGNED_ = UINT8_MAX,
} FFPercentageTypeFlags;
static_assert(sizeof(FFPercentageTypeFlags) == 1, "");
@@ -40,6 +40,6 @@ typedef struct yyjson_val yyjson_val;
typedef struct yyjson_mut_doc yyjson_mut_doc;
typedef struct yyjson_mut_val yyjson_mut_val;
bool ffPercentParseCommandOptions(const char* key, const char* subkey, const char* value, FFPercentageModuleConfig* config);
bool ffPercentParseJsonObject(const char* key, yyjson_val* value, FFPercentageModuleConfig* config);
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig defaultConfig, FFPercentageModuleConfig config);
bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageModuleConfig* config);
void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig config);
const char* ffPercentParseTypeJsonConfig(yyjson_val* value, FFPercentageTypeFlags* result);
+172 -32
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)
@@ -144,15 +234,42 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
assert(processName->length > 0);
ffStrbufClear(exe);
#ifdef __linux__
#if defined(__linux__) || defined(__GNU__)
char filePath[64];
snprintf(filePath, sizeof(filePath), "/proc/%d/cmdline", (int)pid);
if(ffReadFileBuffer(filePath, exe))
{
ffStrbufRecalculateLength(exe); //Trim the arguments
ffStrbufTrimRightSpace(exe);
const char* p = exe->chars;
uint32_t len = (uint32_t) strlen(p);
if (len + 1 < exe->length)
{
const char* name = memrchr(p, '/', len);
if (name) name++; else name = p;
// For interpreters, try to find the real script path in the arguments
if (ffStrStartsWith(name, "python")
#ifndef __ANDROID__
|| ffStrEquals(name, "guile") // for shepherd
#endif
)
{
// `cmdline` always ends with a trailing '\0', and ffReadFileBuffer appends another \0
// So `exe->chars` is always double '\0' terminated
for (p = p + len + 1; *p && *p == '-'; p += strlen(p) + 1) // Skip arguments
assert(p - exe->chars < exe->allocated);
if (*p)
{
len = (uint32_t) strlen(p);
memmove(exe->chars, p, len + 1);
}
}
}
assert(len < exe->allocated);
exe->length = len;
ffStrbufTrimLeft(exe, '-'); //Login shells start with a dash
}
@@ -279,8 +396,10 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
psinfo_t proc;
if (ffReadFileData(filePath, sizeof(proc), &proc) == sizeof(proc))
{
ffStrbufSetS(exe, proc.pr_psargs);
ffStrbufSubstrBeforeFirstC(exe, ' ');
const char* args = proc.pr_psargs;
if (args[0] == '-') ++args;
const char* end = strchr(args, ' ');
ffStrbufSetNS(exe, end ? (uint32_t) (end - args) : (uint32_t) strlen(args), args);
}
if (exePath)
@@ -344,33 +463,53 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
if (pid <= 0)
return "Invalid pid";
#ifdef __linux__
#if defined(__linux__) || defined(__GNU__)
char procFilePath[64];
if (ppid)
#if __linux__
if (ppid || tty)
#endif
{
snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/stat", (int)pid);
char buf[PROC_FILE_BUFFSIZ];
ssize_t nRead = ffReadFileData(procFilePath, sizeof(buf) - 1, buf);
if(nRead <= 8)
return "ffReadFileData(/proc/pid/stat, PROC_FILE_BUFFSIZ-1, buf) failed";
buf[nRead] = '\0';
buf[nRead] = '\0'; // pid (comm) state ppid pgrp session tty
*ppid = 0;
static_assert(sizeof(*ppid) == sizeof(int), "");
const char* pState = NULL;
ffStrbufEnsureFixedLengthFree(name, 255);
int tty_;
if(
sscanf(buf, "%*s (%255[^)]) %*c %d %*d %*d %d", name->chars, ppid, &tty_) < 2 || //stat (comm) state ppid pgrp session tty
name->chars[0] == '\0'
)
return "sscanf(stat) failed";
{
// comm in `/proc/pid/stat` is not encoded, and may contain ' ', ')' or even `\n`
const char* start = memchr(buf, '(', (size_t) nRead);
if (!start)
return "memchr(stat, '(') failed";
start++;
const char* end = memrchr(start, ')', (size_t) nRead - (size_t) (start - buf));
if (!end)
return "memrchr(stat, ')') failed";
ffStrbufSetNS(name, (uint32_t) (end - start), start);
ffStrbufTrimRightSpace(name);
if (name->chars[0] == '\0')
return "process name is empty";
pState = end + 2; // skip ") "
}
ffStrbufRecalculateLength(name);
if (tty)
*tty = tty_ & 0xFF;
#if !__linux__
if (ppid || tty)
#endif
{
int ppid_, tty_;
if(sscanf(pState + 2, "%d %*d %*d %d", &ppid_, &tty_) < 2)
return "sscanf(stat) failed";
if (ppid)
*ppid = (pid_t) ppid_;
if (tty)
*tty = tty_ & 0xFF;
}
}
#if __linux__
else
{
snprintf(procFilePath, sizeof(procFilePath), "/proc/%d/comm", (int)pid);
@@ -379,6 +518,7 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
return "ffReadFileBuffer(/proc/pid/comm, name) failed";
ffStrbufTrimRightSpace(name);
}
#endif
#elif defined(__APPLE__)
+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);
+44
View File
@@ -0,0 +1,44 @@
#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++;
}
if (counter == 0)
ffStrbufAppendUInt(result, bytes);
else
ffStrbufAppendDouble(result, size, (int8_t) options->sizeNdigits);
if (options->sizeSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER)
ffStrbufAppendC(result, ' ');
ffStrbufAppendS(result, 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);
+15 -21
View File
@@ -5,7 +5,7 @@
void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig config, const FFModuleArgs* module)
{
if (celsius != celsius) // ignores NaN
if (celsius == -DBL_MAX) // ignores invalid value
return;
const FFOptionsDisplay* options = &instance.config.display;
@@ -41,13 +41,16 @@ void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig confi
{
case FF_TEMPERATURE_UNIT_DEFAULT:
case FF_TEMPERATURE_UNIT_CELSIUS:
ffStrbufAppendF(buffer, "%.*f°C", options->tempNdigits, celsius);
ffStrbufAppendF(buffer, "%.*f%s°C", options->tempNdigits, celsius,
options->tempSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "");
break;
case FF_TEMPERATURE_UNIT_FAHRENHEIT:
ffStrbufAppendF(buffer, "%.*f°F", options->tempNdigits, celsius * 1.8 + 32);
ffStrbufAppendF(buffer, "%.*f%s°F", options->tempNdigits, celsius * 1.8 + 32,
options->tempSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "");
break;
case FF_TEMPERATURE_UNIT_KELVIN:
ffStrbufAppendF(buffer, "%.*f K", options->tempNdigits, celsius + 273.15);
ffStrbufAppendF(buffer, "%.*f%sK", options->tempNdigits, celsius + 273.15,
options->tempSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_NEVER ? "" : " ");
break;
}
@@ -104,9 +107,9 @@ bool ffTempsParseCommandOptions(const char* key, const char* subkey, const char*
return false;
}
bool ffTempsParseJsonObject(const char* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config)
bool ffTempsParseJsonObject(yyjson_val* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config)
{
if (!ffStrEqualsIgnCase(key, "temp"))
if (!unsafe_yyjson_equals_str(key, "temp"))
return false;
if (yyjson_is_bool(value))
@@ -123,7 +126,7 @@ bool ffTempsParseJsonObject(const char* key, yyjson_val* value, bool* useTemp, F
if (!yyjson_is_obj(value))
{
fprintf(stderr, "Error: usage: %s must be an object or a boolean\n", key);
fprintf(stderr, "Error: usage: %s must be an object or a boolean\n", unsafe_yyjson_get_str(key));
exit(480);
}
@@ -156,23 +159,14 @@ bool ffTempsParseJsonObject(const char* key, yyjson_val* value, bool* useTemp, F
return true;
}
void ffTempsGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FF_MAYBE_UNUSED bool defaultTemp, FFColorRangeConfig defaultConfig, bool temp, FFColorRangeConfig config)
void ffTempsGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, bool temp, FFColorRangeConfig config)
{
assert(defaultTemp == false); // assume defaultTemp is always false
if (!temp)
return;
if (config.green != defaultConfig.green || config.yellow != defaultConfig.yellow)
{
yyjson_mut_val* temp = yyjson_mut_obj_add_obj(doc, module, "temp");
if (config.green != defaultConfig.green)
yyjson_mut_obj_add_uint(doc, temp, "green", config.green);
if (config.yellow != defaultConfig.yellow)
yyjson_mut_obj_add_uint(doc, temp, "yellow", config.yellow);
}
yyjson_mut_obj_add_bool(doc, module, "temp", false);
else
{
yyjson_mut_obj_add_bool(doc, module, "temp", true);
yyjson_mut_val* temp = yyjson_mut_obj_add_obj(doc, module, "temp");
yyjson_mut_obj_add_uint(doc, temp, "green", config.green);
yyjson_mut_obj_add_uint(doc, temp, "yellow", config.yellow);
}
}
+2 -2
View File
@@ -5,5 +5,5 @@
void ffTempsAppendNum(double celsius, FFstrbuf* buffer, FFColorRangeConfig config, const FFModuleArgs* module);
bool ffTempsParseCommandOptions(const char* key, const char* subkey, const char* value, bool* useTemp, FFColorRangeConfig* config);
bool ffTempsParseJsonObject(const char* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config);
void ffTempsGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FF_MAYBE_UNUSED bool defaultTemp, FFColorRangeConfig defaultConfig, bool temp, FFColorRangeConfig config);
bool ffTempsParseJsonObject(yyjson_val* key, yyjson_val* value, bool* useTemp, FFColorRangeConfig* config);
void ffTempsGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, bool temp, FFColorRangeConfig config);
+3
View File
@@ -0,0 +1,3 @@
#include <time.h>
char ffTimeInternalBuffer[64]; // Reduce memory usage and prevent redundant allocations
+59 -12
View File
@@ -11,6 +11,8 @@
#include <OS.h>
#endif
#include "util/arrayUtils.h"
static inline double ffTimeGetTick(void) //In msec
{
#ifdef _WIN32
@@ -64,11 +66,12 @@ static inline const char* ffTimeToFullStr(uint64_t msec)
time_t tsec = (time_t) (msec / 1000);
const struct tm* tm = localtime(&tsec);
static char buf[32];
strftime(buf, __builtin_strlen("0000-00-00T00:00:00") + 1, "%FT%T", tm);
snprintf(buf + __builtin_strlen("0000-00-00T00:00:00"), __builtin_strlen(".000") + 1, ".%03u", (unsigned) (msec % 1000));
strftime(buf + __builtin_strlen("0000-00-00T00:00:00.000"), __builtin_strlen("+0000") + 1, "%z", tm);
return buf;
extern char ffTimeInternalBuffer[64];
uint32_t len = 0;
len += (uint32_t) strftime(ffTimeInternalBuffer, ARRAY_SIZE(ffTimeInternalBuffer) - len, "%FT%T", tm);
len += (uint32_t) snprintf(ffTimeInternalBuffer + len, ARRAY_SIZE(ffTimeInternalBuffer) - len, ".%03u", (unsigned) (msec % 1000));
len += (uint32_t) strftime(ffTimeInternalBuffer + len, ARRAY_SIZE(ffTimeInternalBuffer) - len, "%z", tm);
return ffTimeInternalBuffer;
}
// Not thread-safe
@@ -77,9 +80,9 @@ static inline const char* ffTimeToShortStr(uint64_t msec)
if (msec == 0) return "";
time_t tsec = (time_t) (msec / 1000);
static char buf[32];
strftime(buf, sizeof(buf), "%F %T", localtime(&tsec));
return buf;
extern char ffTimeInternalBuffer[64];
strftime(ffTimeInternalBuffer, ARRAY_SIZE(ffTimeInternalBuffer), "%F %T", localtime(&tsec));
return ffTimeInternalBuffer;
}
// Not thread-safe
@@ -88,12 +91,56 @@ static inline const char* ffTimeToTimeStr(uint64_t msec)
if (msec == 0) return "";
time_t tsec = (time_t) (msec / 1000);
static char buf[32];
strftime(buf, sizeof(buf), "%T", localtime(&tsec));
sprintf(buf + __builtin_strlen("00:00:00"), ".%03u", (unsigned) (msec % 1000));
return buf;
extern char ffTimeInternalBuffer[64];
uint32_t len = (uint32_t) strftime(ffTimeInternalBuffer, ARRAY_SIZE(ffTimeInternalBuffer), "%T", localtime(&tsec));
sprintf(ffTimeInternalBuffer + len, ".%03u", (unsigned) (msec % 1000));
return ffTimeInternalBuffer;
}
#ifdef _WIN32
#pragma GCC diagnostic pop
#endif
typedef struct FFTimeGetAgeResult
{
uint32_t years;
uint32_t daysOfYear;
double yearsFraction;
} FFTimeGetAgeResult;
static inline FFTimeGetAgeResult ffTimeGetAge(uint64_t birthMs, uint64_t nowMs)
{
FFTimeGetAgeResult result = {};
if (__builtin_expect(birthMs == 0 || nowMs < birthMs, 0))
return result;
time_t birth_s = (time_t) (birthMs / 1000);
struct tm birth_tm;
#ifdef _WIN32
localtime_s(&birth_tm, &birth_s);
#else
localtime_r(&birth_s, &birth_tm);
#endif
time_t now_s = (time_t) (nowMs / 1000);
struct tm now_tm;
#ifdef _WIN32
localtime_s(&now_tm, &now_s);
#else
localtime_r(&now_s, &now_tm);
#endif
result.years = (uint32_t) (now_tm.tm_year - birth_tm.tm_year);
if (now_tm.tm_yday < birth_tm.tm_yday)
result.years--;
birth_tm.tm_year += (int) result.years;
birth_s = mktime(&birth_tm);
uint32_t diff_s = (uint32_t) (now_s - birth_s);
result.daysOfYear = diff_s / (24 * 60 * 60);
birth_tm.tm_year += 1;
result.yearsFraction = (double) diff_s / (double) (mktime(&birth_tm) - birth_s) + result.years;
return result;
}
+135 -887
View File
File diff suppressed because it is too large Load Diff
-24
View File
@@ -1,24 +0,0 @@
use scripting additions
use framework "/System/Library/PrivateFrameworks/MediaRemote.framework"
set MRNowPlayingRequest to current application's NSClassFromString("MRNowPlayingRequest")
if MRNowPlayingRequest's localNowPlayingItem() is missing value then
return
end if
if MRNowPlayingRequest's localIsPlaying() then
set status to "Playing"
else
set status to "Paused"
end if
set infoDict to MRNowPlayingRequest's localNowPlayingItem()'s nowPlayingInfo()
set bundleObj to MRNowPlayingRequest's localNowPlayingPlayerPath()'s client()
return status & "
" & (infoDict's valueForKey:"kMRMediaRemoteNowPlayingInfoTitle" as text) & "
" & (infoDict's valueForKey:"kMRMediaRemoteNowPlayingInfoArtist" as text) & "
" & (infoDict's valueForKey:"kMRMediaRemoteNowPlayingInfoAlbum" as text) & "
" & (bundleObj's bundleIdentifier() as text) & "
" & (bundleObj's displayName() as text)
+3 -2
View File
@@ -1,8 +1,9 @@
#pragma once
#include "fastfetch.h"
#include "modules/battery/option.h"
#define FF_BATTERY_TEMP_UNSET (0/0.0)
#define FF_BATTERY_TEMP_UNSET (-DBL_MAX)
typedef struct FFBatteryResult
{
@@ -15,7 +16,7 @@ typedef struct FFBatteryResult
double capacity;
double temperature;
uint32_t cycleCount;
int32_t timeRemaining;
int32_t timeRemaining; // in seconds, -1 if unknown
} FFBatteryResult;
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results);
+8 -3
View File
@@ -96,6 +96,7 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results)
battery->temperature = FF_BATTERY_TEMP_UNSET;
battery->cycleCount = 0;
battery->timeRemaining = -1;
battery->capacity = 0;
ffStrbufInit(&battery->manufacturer);
ffStrbufInit(&battery->modelName);
ffStrbufInit(&battery->status);
@@ -124,11 +125,11 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results)
{
double level = 0, scale = 0;
if (ffParsePropLines(start, "level: ", &temp))
level = ffStrbufToDouble(&temp);
level = ffStrbufToDouble(&temp, -DBL_MAX);
ffStrbufClear(&temp);
if (ffParsePropLines(start, "scale: ", &temp))
scale = ffStrbufToDouble(&temp);
scale = ffStrbufToDouble(&temp, -DBL_MAX);
ffStrbufClear(&temp);
if (level > 0 && scale > 0)
@@ -138,7 +139,11 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results)
if(options->temp)
{
if (ffParsePropLines(start, "temperature: ", &temp))
battery->temperature = ffStrbufToDouble(&temp);
{
battery->temperature = ffStrbufToDouble(&temp, FF_BATTERY_TEMP_UNSET);
if (battery->temperature != FF_BATTERY_TEMP_UNSET)
battery->temperature /= 10.0; // Android returns temperature in tenths of a degree
}
ffStrbufClear(&temp);
}
+6 -2
View File
@@ -45,7 +45,7 @@ static void parseBattery(int dfd, const char* id, FFBatteryOptions* options, FFl
return;
FFBatteryResult* result = ffListAdd(results);
result->capacity = ffStrbufToDouble(&tmpBuffer);
result->capacity = ffStrbufToDouble(&tmpBuffer, 0);
//At this point, we have a battery. Try to get as much values as possible.
@@ -154,7 +154,11 @@ static void parseBattery(int dfd, const char* id, FFBatteryOptions* options, FFl
if (options->temp)
{
if (ffReadFileBufferRelative(dfd, "temp", &tmpBuffer))
result->temperature = ffStrbufToDouble(&tmpBuffer) / 10;
{
result->temperature = ffStrbufToDouble(&tmpBuffer, FF_BATTERY_TEMP_UNSET);
if (result->temperature != FF_BATTERY_TEMP_UNSET)
result->temperature /= 10;
}
}
}
+2 -2
View File
@@ -16,8 +16,8 @@
const char* ffDetectBattery(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* results)
{
FF_AUTO_CLOSE_FD int fd = open(_PATH_SYSMON, O_RDONLY);
if (fd < 0) return "open(_PATH_SYSMON, O_RDONLY) failed";
FF_AUTO_CLOSE_FD int fd = open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC);
if (fd < 0) return "open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC) failed";
prop_dictionary_t root = NULL;
if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &root) < 0)
+2 -2
View File
@@ -8,9 +8,9 @@
const char* ffDetectBattery(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* result)
{
FF_AUTO_CLOSE_FD int devfd = open("/dev/apm", O_RDONLY);
FF_AUTO_CLOSE_FD int devfd = open("/dev/apm", O_RDONLY | O_CLOEXEC);
if (devfd < 0) return "open(dev/apm, O_RDONLY) failed";
if (devfd < 0) return "open(dev/apm, O_RDONLY | O_CLOEXEC) failed";
struct apm_power_info info = {};
+20 -29
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);
}
{
@@ -112,7 +103,7 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results
battery->cycleCount = bi.CycleCount;
battery->temperature = 0.0/0.0;
battery->temperature = FF_BATTERY_TEMP_UNSET;
if(options->temp)
{
bqi.InformationLevel = BatteryTemperature;
@@ -131,7 +122,7 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results
{
BATTERY_STATUS bs;
BATTERY_WAIT_STATUS bws = { .BatteryTag = bqi.BatteryTag };
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY)
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY && bi.FullChargedCapacity != 0)
battery->capacity = bs.Capacity * 100.0 / bi.FullChargedCapacity;
else
battery->capacity = 0;
@@ -271,6 +262,6 @@ static const char* detectWithNtApi(FF_MAYBE_UNUSED FFBatteryOptions* options, FF
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results)
{
return options->useSetupApi
? detectWithSetupApi(options, results)
? detectWithCmApi(options, results)
: detectWithNtApi(options, results);
}
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/bios/option.h"
typedef struct FFBiosResult
{
+16
View File
@@ -32,6 +32,10 @@ typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION
#include <fcntl.h>
#include <unistd.h>
#elif __sun
#include <libdevinfo.h>
#include <sys/sunddi.h>
#endif
typedef struct FFSmbiosBios
@@ -59,6 +63,7 @@ typedef struct FFSmbiosBios
static_assert(offsetof(FFSmbiosBios, ExtendedBiosRomSize) == 0x18,
"FFSmbiosBios: Wrong struct alignment");
const char* ffDetectBios(FFBiosResult* bios)
{
const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable();
@@ -94,6 +99,17 @@ const char* ffDetectBios(FFBiosResult* bios)
default: break;
}
}
#elif __sun
di_node_t rootNode = di_init("/", DINFOPROP);
if (rootNode != DI_NODE_NIL)
{
char* efiVersion = NULL;
if (di_prop_lookup_strings(DDI_DEV_T_ANY, rootNode, "efi-version", &efiVersion) > 0)
ffStrbufSetStatic(&bios->type, "UEFI");
else
ffStrbufSetStatic(&bios->type, "BIOS");
}
di_fini(rootNode);
#elif __HAIKU__ || __OpenBSD__
// Currently SMBIOS detection is supported in legancy BIOS only
ffStrbufSetStatic(&bios->type, "BIOS");
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/bluetooth/option.h"
typedef struct FFBluetoothResult
{
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/bluetoothradio/option.h"
typedef struct FFBluetoothRadioResult
{
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/board/option.h"
typedef struct FFBoardResult
{
+13 -3
View File
@@ -9,9 +9,19 @@ const char* ffDetectBoard(FFBoardResult* result)
if (!service)
return "No IOPlatformExpertDevice found";
io_name_t name;
if (IORegistryEntryGetName(service, name) == kIOReturnSuccess)
ffStrbufSetS(&result->name, name);
FF_CFTYPE_AUTO_RELEASE CFTypeRef boardId = IORegistryEntryCreateCFProperty(service, CFSTR("board-id"), kCFAllocatorDefault, kNilOptions);
if (boardId)
ffCfStrGetString(boardId, &result->name);
else
{
io_name_t name;
if (IORegistryEntryGetName(service, name) == kIOReturnSuccess)
ffStrbufSetS(&result->name, name);
}
FF_CFTYPE_AUTO_RELEASE CFStringRef version = IORegistryEntryCreateCFProperty(service, CFSTR("version"), kCFAllocatorDefault, kNilOptions);
if (version)
ffCfStrGetString(version, &result->version);
FF_CFTYPE_AUTO_RELEASE CFTypeRef manufacturer = IORegistryEntryCreateCFProperty(service, CFSTR("manufacturer"), kCFAllocatorDefault, kNilOptions);
if (manufacturer)
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/bootmgr/option.h"
typedef struct FFBootmgrResult
{
+1 -1
View File
@@ -20,7 +20,7 @@
const char* ffDetectBootmgr(FFBootmgrResult* result)
{
FF_AUTO_CLOSE_FD int efifd = open("/dev/efi", O_RDWR);
FF_AUTO_CLOSE_FD int efifd = open("/dev/efi", O_RDWR | O_CLOEXEC);
if (efifd < 0) return "open(/dev/efi) failed";
uint8_t buffer[2048];
+1 -1
View File
@@ -1,7 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "util/FFlist.h"
#include "modules/brightness/option.h"
typedef struct FFBrightnessResult
{
+2 -2
View File
@@ -32,7 +32,7 @@ static const char* detectWithBacklight(FFlist* result)
ffStrbufAppendS(&backlightDir, "/brightness");
if(ffReadFileBuffer(backlightDir.chars, &buffer))
{
double actualBrightness = ffStrbufToDouble(&buffer);
double actualBrightness = ffStrbufToDouble(&buffer, 0);
ffStrbufSubstrBefore(&backlightDir, backlightDirLength);
ffStrbufAppendS(&backlightDir, entry->d_name);
ffStrbufAppendS(&backlightDir, "/max_brightness");
@@ -71,7 +71,7 @@ static const char* detectWithBacklight(FFlist* result)
}
else
ffStrbufInitS(&brightness->name, entry->d_name);
brightness->max = ffStrbufToDouble(&buffer);
brightness->max = ffStrbufToDouble(&buffer, 0);
brightness->min = 0;
brightness->current = actualBrightness;
brightness->builtin = true;
+1 -1
View File
@@ -12,7 +12,7 @@ const char* ffDetectBrightness(FF_MAYBE_UNUSED FFBrightnessOptions* options, FFl
for (char i = '0'; i <= '9'; ++i) {
path[strlen("/dev/ttyC")] = i;
FF_AUTO_CLOSE_FD int devfd = open(path, O_RDONLY);
FF_AUTO_CLOSE_FD int devfd = open(path, O_RDONLY | O_CLOEXEC);
if (devfd < 0) {
if (errno == EACCES && i == '0')
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/btrfs/option.h"
typedef struct FFBtrfsDiskUsage
{
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/camera/option.h"
typedef struct FFCameraResult
{
+1 -1
View File
@@ -45,7 +45,7 @@ const char* ffDetectCamera(FF_MAYBE_UNUSED FFlist* result)
ffStrbufInitStatic(&camera->name, "Unknown");
}
ffStrbufInit(&camera->vendor);
ffStrbufInitS(&camera->id, yyjson_get_str(yyjson_obj_get(device, "id")));
ffStrbufInitJsonVal(&camera->id, yyjson_obj_get(device, "id"));
yyjson_val* sizes = yyjson_arr_get_first(yyjson_obj_get(device, "jpeg_output_sizes"));
if (yyjson_is_obj(sizes))
{
+1 -1
View File
@@ -20,7 +20,7 @@ const char* ffDetectCamera(FFlist* result)
for (uint32_t i = 0; i <= 9; ++i)
{
path[ARRAY_SIZE(path) - 2] = (char) (i + '0');
FF_AUTO_CLOSE_FD int fd = open(path, O_RDONLY);
FF_AUTO_CLOSE_FD int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
{
if (errno == ENOENT)
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/chassis/option.h"
typedef struct FFChassisResult
{
+471
View File
@@ -60,3 +60,474 @@ const char* ffCPUQualcommCodeToName(uint32_t code)
default: return NULL;
}
}
#if defined(__x86_64__) || defined(__i386__)
#include <cpuid.h>
void ffCPUDetectByCpuid(FFCPUResult* cpu)
{
uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
if (__get_cpuid(0x16, &eax, &ebx, &ecx, &edx))
{
// WARNING: CPUID may report frequencies of efficient cores
// cpuid returns 0 MHz when hypervisor is enabled
if (eax) cpu->frequencyBase = eax;
if (ebx) cpu->frequencyMax = ebx;
}
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx))
{
// Feature tests (leaf1.ecx, leaf7.ebx)
bool sse2 = (ecx & bit_SSE2) != 0;
bool sse4_2 = (ecx & bit_SSE4_2) != 0;
bool pclmul = (ecx & bit_PCLMUL) != 0;
bool popcnt = (ecx & bit_POPCNT) != 0;
bool fma = (ecx & bit_FMA) != 0;
bool osxsave = (ecx & bit_OSXSAVE) != 0;
unsigned int eax7 = 0, ebx7 = 0, ecx7 = 0, edx7 = 0;
__get_cpuid_count(7, 0, &eax7, &ebx7, &ecx7, &edx7);
bool avx2 = (ebx7 & bit_AVX2) != 0;
bool bmi2 = (ebx7 & bit_BMI2) != 0;
bool avx512f = (ebx7 & bit_AVX512F) != 0;
bool avx512bw = (ebx7 & bit_AVX512BW) != 0;
bool avx512dq = (ebx7 & bit_AVX512DQ) != 0;
// OS support for AVX/AVX512: check XGETBV (requires OSXSAVE)
bool avx_os = false;
bool avx512_os = false;
if (osxsave)
{
__asm__ __volatile__(
"xgetbv"
: "=a"(eax), "=d"(edx)
: "c"(0)
:
);
uint64_t xcr0 = ((uint64_t)edx << 32) | eax;
// AVX requires XCR0[1:2] == 11b (XMM and YMM state)
avx_os = (xcr0 & 0x6ULL) == 0x6ULL;
// AVX512 requires XCR0[7,5,6] etc. common mask 0xE6 (bits 1,2,5,6,7)
avx512_os = (xcr0 & 0xE6ULL) == 0xE6ULL;
}
cpu->march = "unknown";
if (avx512f && avx512bw && avx512dq && avx512_os) cpu->march = "x86_64-v4";
else if (avx2 && fma && bmi2 && avx_os) cpu->march = "x86_64-v3";
else if (sse4_2 && popcnt && pclmul) cpu->march = "x86_64-v2";
else if (sse2) cpu->march = "x86_64-v1";
}
}
#elif defined(__aarch64__)
// This is not accurate because a lot of flags are optional from old versions
// https://developer.arm.com/documentation/109697/2025_06/Feature-descriptions?lang=en
// https://en.wikipedia.org/wiki/AArch64#ARM-A_(application_architecture)
// Worth noting: Apple M1 is marked as ARMv8.5-A on Wikipedia, but it lacks BTI (mandatory in v8.5)
#ifdef __linux__
#include "common/io/io.h"
#include <elf.h>
#include <asm/hwcap.h>
#ifndef HWCAP2_SME
#define HWCAP2_SME (1UL << 23)
#endif
#ifndef HWCAP2_SME2
#define HWCAP2_SME2 (1UL << 37)
#endif
#ifndef HWCAP2_CSSC
#define HWCAP2_CSSC (1UL << 34)
#endif
#ifndef HWCAP2_SME2P1
#define HWCAP2_SME2P1 (1UL << 38)
#endif
#ifndef HWCAP2_MOPS
#define HWCAP2_MOPS (1UL << 43)
#endif
#ifndef HWCAP2_F8E4M3
#define HWCAP2_F8E4M3 (1UL << 55)
#endif
#ifndef HWCAP2_F8E5M2
#define HWCAP2_F8E5M2 (1UL << 56)
#endif
#ifndef HWCAP_CMPBR
#define HWCAP_CMPBR (1UL << 33)
#endif
#ifndef HWCAP_FPRCVT
#define HWCAP_FPRCVT (1UL << 34)
#endif
void ffCPUDetectByCpuid(FFCPUResult* cpu)
{
char buf[PROC_FILE_BUFFSIZ];
ssize_t nRead = ffReadFileData("/proc/self/auxv", ARRAY_SIZE(buf), buf);
if (nRead < (ssize_t) sizeof(Elf64_auxv_t)) return;
uint64_t hwcap = 0, hwcap2 = 0;
for (Elf64_auxv_t* auxv = (Elf64_auxv_t*)buf; (char*)auxv < buf + nRead; ++auxv)
{
if (auxv->a_type == AT_HWCAP)
{
hwcap = auxv->a_un.a_val;
}
else if (auxv->a_type == AT_HWCAP2)
{
hwcap2 = auxv->a_un.a_val;
}
}
if (!hwcap) return;
cpu->march = "unknown";
// ARMv8-A
bool has_fp = (hwcap & HWCAP_FP) != 0;
bool has_asimd = (hwcap & HWCAP_ASIMD) != 0;
// ARMv8.1-A
bool has_atomics = (hwcap & HWCAP_ATOMICS) != 0; // optional from v8.0
bool has_crc32 = (hwcap & HWCAP_CRC32) != 0; // optional from v8.0
bool has_asimdrdm = (hwcap & HWCAP_ASIMDRDM) != 0; // optional from v8.0
// ARMv8.2-A
bool has_fphp = (hwcap & HWCAP_FPHP) != 0; // optional
bool has_dcpop = (hwcap & HWCAP_DCPOP) != 0; // DC CVAP, optional from v8.1
// ARMv8.3-A
bool has_paca = (hwcap & HWCAP_PACA) != 0; // optional from v8.2
bool has_lrcpc = (hwcap & HWCAP_LRCPC) != 0; // optional from v8.2
bool has_fcma = (hwcap & HWCAP_FCMA) != 0; // optional from v8.2
bool has_jscvt = (hwcap & HWCAP_JSCVT) != 0; // optional from v8.2
// ARMv8.4-A
bool has_dit = (hwcap & HWCAP_DIT) != 0; // optional from v8.3
bool has_flagm = (hwcap & HWCAP_FLAGM) != 0; // optional from v8.1
bool has_ilrcpc = (hwcap & HWCAP_ILRCPC) != 0; // optional from v8.2
// ARMv8.5-A
bool has_bti = (hwcap2 & HWCAP2_BTI) != 0; // optional from v8.4
bool has_sb = (hwcap & HWCAP_SB) != 0; // optional from v8.0
bool has_dcpodp = (hwcap2 & HWCAP2_DCPODP) != 0; // optional from v8.1
bool has_flagm2 = (hwcap2 & HWCAP2_FLAGM2) != 0; // optional from v8.4
bool has_frint = (hwcap2 & HWCAP2_FRINT) != 0; // optional from v8.4
// ARMv9.0-A
bool has_sve2 = (hwcap2 & HWCAP2_SVE2) != 0;
// ARMv9.1-A
// ARMv8.6-A
bool has_bf16 = (hwcap2 & HWCAP2_BF16) != 0; // optional from v8.2
bool has_i8mm = (hwcap2 & HWCAP2_I8MM) != 0; // optional from v8.1
// ARMv8.7-A
bool has_afp = (hwcap2 & HWCAP2_AFP) != 0; // optional from v8.6
// ARMv9.2-A
bool has_sme = (hwcap2 & HWCAP2_SME) != 0;
// ARMv9.3-A
bool has_sme2 = (hwcap2 & HWCAP2_SME2) != 0; // optional from v9.2
// ARMv8.8-A
bool has_mops = (hwcap2 & HWCAP2_MOPS) != 0; // optional from v8.7
// ARMv8.9-A
bool has_cssc = (hwcap2 & HWCAP2_CSSC) != 0; // optional from v8.7
// ARMv9.4-A
bool has_sme2p1 = (hwcap2 & HWCAP2_SME2P1) != 0; // optional from v9.2
// ARMv9.5-A
bool has_f8e4m3 = (hwcap2 & HWCAP2_F8E4M3) != 0; // optional from v9.2
bool has_f8e5m2 = (hwcap2 & HWCAP2_F8E5M2) != 0; // optional from v9.2
// ARMv9.6-A
bool has_cmpbr = (hwcap & HWCAP_CMPBR) != 0; // optional from v9.5
bool has_fprcvt = (hwcap & HWCAP_FPRCVT) != 0; // optional from v9.5
if (has_sve2 || has_sme) {
// ARMv9
if (has_cmpbr && has_fprcvt) {
cpu->march = "ARMv9.6-A";
} else if (has_f8e5m2 && has_f8e4m3) {
cpu->march = "ARMv9.5-A";
} else if (has_sme2p1) {
cpu->march = "ARMv9.4-A";
} else if (has_sme2) {
cpu->march = "ARMv9.3-A";
} else if (has_sme) {
cpu->march = "ARMv9.2-A";
} else if (has_i8mm && has_bf16) {
cpu->march = "ARMv9.1-A";
} else {
cpu->march = "ARMv9.0-A";
}
} else {
// ARMv8
if (has_cssc) {
cpu->march = "ARMv8.9-A";
} else if (has_mops) {
cpu->march = "ARMv8.8-A";
} else if (has_afp) {
cpu->march = "ARMv8.7-A";
} else if (has_i8mm && has_bf16) {
cpu->march = "ARMv8.6-A";
} else if (has_bti && has_sb && has_dcpodp && has_flagm2 && has_frint) {
cpu->march = "ARMv8.5-A";
} else if (has_dit && has_flagm && has_ilrcpc) {
cpu->march = "ARMv8.4-A";
} else if (has_paca && has_lrcpc && has_fcma && has_jscvt) {
cpu->march = "ARMv8.3-A";
} else if (has_fphp && has_dcpop) {
cpu->march = "ARMv8.2-A";
} else if (has_atomics && has_crc32 && has_asimdrdm) {
cpu->march = "ARMv8.1-A";
} else if (has_asimd && has_fp) {
cpu->march = "ARMv8-A";
}
}
}
#elif __APPLE__
#include <sys/sysctl.h>
// #include <arm/cpu_capabilities_public.h> // Not available in macOS 14-
void ffCPUDetectByCpuid(FFCPUResult* cpu)
{
uint64_t caps[2] = {0}; // 80-bit capability mask, split into two 64-bit values
size_t size = sizeof(caps);
if (sysctlbyname("hw.optional.arm.caps", caps, &size, NULL, 0) != 0) return;
// Helper macro to test bit in 80-bit capability mask
#define FF_HAS_CAP(bit) \
(((bit) < 64) ? ((caps[0] >> (bit)) & 1ULL) : ((caps[1] >> ((bit) - 64U)) & 1ULL))
cpu->march = "unknown";
// ARMv8-A
bool has_fp = FF_HAS_CAP(50); /* CAP_BIT_AdvSIMD_HPFPCvt */ // Full FP16 support (implies FP/ASIMD)
bool has_asimd = FF_HAS_CAP(49); /* CAP_BIT_AdvSIMD */ // Advanced SIMD (NEON)
// ARMv8.1-A
bool has_lse = FF_HAS_CAP(6); /* CAP_BIT_FEAT_LSE */ // Large System Extensions, optional in v8.0
bool has_crc32 = FF_HAS_CAP(51); /* CAP_BIT_FEAT_CRC32 */ // CRC32 instructions, optional in v8.0
bool has_rdm = FF_HAS_CAP(5); /* CAP_BIT_FEAT_RDM */ // AdvSIMD rounding double multiply accumulate, optional in v8.0
// ARMv8.2-A
bool has_fp16 = FF_HAS_CAP(34); /* CAP_BIT_FEAT_FP16 */ // Half-precision FP support, optional
bool has_dpb = FF_HAS_CAP(22); /* CAP_BIT_FEAT_DPB */ // DC CVAP, optional from v8.1
// ARMv8.3-A
bool has_pauth = FF_HAS_CAP(19); /* CAP_BIT_FEAT_PAuth */ // Pointer Authentication (PAC), optional from v8.2
bool has_lrcpc = FF_HAS_CAP(15); /* CAP_BIT_FEAT_LRCPC */ // LDAPR/LR with RCPC semantics, optional from v8.2
bool has_fcma = FF_HAS_CAP(17); /* CAP_BIT_FEAT_FCMA */ // Complex number multiply-add, optional from v8.2
bool has_jscvt = FF_HAS_CAP(18); /* CAP_BIT_FEAT_JSCVT */ // JavaScript-style conversion (FJCVTZS), optional from v8.2
// ARMv8.4-A
bool has_lse2 = FF_HAS_CAP(30); /* CAP_BIT_FEAT_LSE2 */ // Large System Extensions version 2, optional from v8.2
bool has_dit = FF_HAS_CAP(33); /* CAP_BIT_FEAT_DIT */ // Data Independent Timing, optional from v8.3
bool has_flagm = FF_HAS_CAP(0); /* CAP_BIT_FEAT_FlagM */ // Flag manipulation (FMOV/FCVT), optional from v8.1
bool has_lrcpc2 = FF_HAS_CAP(16); /* CAP_BIT_FEAT_LRCPC2 */ // Enhanced RCPC (LDAPUR/LDAPST), optional from v8.2
// ARMv8.5-A
bool has_bti = FF_HAS_CAP(36); /* CAP_BIT_FEAT_BTI */ // Branch Target Identification, optional from v8.4
bool has_sb = FF_HAS_CAP(13); /* CAP_BIT_FEAT_SB */ // Speculative Barrier, optional from v8.0
bool has_dpb2 = FF_HAS_CAP(23); /* CAP_BIT_FEAT_DPB2 */ // DC CVADP (DPB2), optional from v8.1
bool has_flagm2 = FF_HAS_CAP(1); /* CAP_BIT_FEAT_FlagM2 */ // Enhanced FlagM, optional from v8.4
bool has_frintts = FF_HAS_CAP(14); /* CAP_BIT_FEAT_FRINTTS */ // Floating-point to integer instructions, optional from v8.4
// ARMv9.0-A
bool has_sve2 = false; // Not exposed and not supported by Apple M4
// ARMv9.1-A
// ARMv8.6-A
bool has_bf16 = FF_HAS_CAP(24); /* CAP_BIT_FEAT_BF16 */ // Brain float16, optional from v8.2
bool has_i8mm = FF_HAS_CAP(25); /* CAP_BIT_FEAT_I8MM */ // Int8 Matrix Multiply, optional from v8.1
// ARMv8.7-A
bool has_afp = FF_HAS_CAP(29); /* CAP_BIT_FEAT_AFP */ // Alternate FP16 (FEXPA), optional from v8.6
// ARMv9.2-A
bool has_sme = FF_HAS_CAP(40); /* CAP_BIT_FEAT_SME */ // Scalable Matrix Extension, optional from v9.2
// ARMv9.3-A
bool has_sme2 = FF_HAS_CAP(41); /* CAP_BIT_FEAT_SME2 */ // SME2, optional from v9.2
// ARMv8.8-A
bool has_hbc = FF_HAS_CAP(64); /* CAP_BIT_FEAT_HBC */ // Hinted conditional branches, optional from v8.7
// ARMv8.9-A
bool has_cssc = FF_HAS_CAP(67); /* CAP_BIT_FEAT_CSSC */ // Common Short String Compare, optional from v8.7
// ARMv9.4-A+ are not exposed yet
if (has_sve2 || has_sme) {
// ARMv9 family
if (has_sme2) {
cpu->march = "ARMv9.3-A";
} else if (has_sme) {
cpu->march = "ARMv9.2-A";
} else if (has_i8mm && has_bf16) {
cpu->march = "ARMv9.1-A";
} else {
cpu->march = "ARMv9.0-A";
}
} else {
// ARMv8 family
if (has_cssc) {
cpu->march = "ARMv8.9-A";
} else if (has_hbc) {
cpu->march = "ARMv8.8-A";
} else if (has_afp) {
cpu->march = "ARMv8.7-A";
} else if (has_i8mm && has_bf16) {
cpu->march = "ARMv8.6-A";
} else if (has_bti && has_sb && has_dpb2 && has_flagm2 && has_frintts) {
cpu->march = "ARMv8.5-A";
} else if (has_lse2 && has_dit && has_flagm && has_lrcpc2) {
cpu->march = "ARMv8.4-A";
} else if (has_pauth && has_lrcpc && has_fcma && has_jscvt) {
cpu->march = "ARMv8.3-A";
} else if (has_fp16 && has_dpb) {
cpu->march = "ARMv8.2-A";
} else if (has_lse && has_crc32 && has_rdm) {
cpu->march = "ARMv8.1-A";
} else if (has_asimd && has_fp) {
cpu->march = "ARMv8-A";
}
}
#undef HAS_CAP
}
#elif _WIN32
#include <processthreadsapi.h>
// Missing from winnt.h of MinGW-w64
#define PF_ARM_LSE2_AVAILABLE 62
#define PF_RESERVED_FEATURE 63
#define PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE 64
#define PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE 65
#define PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE 66
#define PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE 67
#define PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE 68
#define PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE 69
#define PF_ARM_SME_INSTRUCTIONS_AVAILABLE 70
#define PF_ARM_SME2_INSTRUCTIONS_AVAILABLE 71
#define PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE 72
#define PF_ARM_SME2_2_INSTRUCTIONS_AVAILABLE 73
#define PF_ARM_SME_AES_INSTRUCTIONS_AVAILABLE 74
#define PF_ARM_SME_SBITPERM_INSTRUCTIONS_AVAILABLE 75
#define PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE 76
#define PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE 77
#define PF_ARM_SME_SF8DP2_INSTRUCTIONS_AVAILABLE 78
#define PF_ARM_SME_SF8DP4_INSTRUCTIONS_AVAILABLE 79
#define PF_ARM_SME_SF8FMA_INSTRUCTIONS_AVAILABLE 80
#define PF_ARM_SME_F8F32_INSTRUCTIONS_AVAILABLE 81
#define PF_ARM_SME_F8F16_INSTRUCTIONS_AVAILABLE 82
#define PF_ARM_SME_F16F16_INSTRUCTIONS_AVAILABLE 83
#define PF_ARM_SME_B16B16_INSTRUCTIONS_AVAILABLE 84
#define PF_ARM_SME_F64F64_INSTRUCTIONS_AVAILABLE 85
#define PF_ARM_SME_I16I64_INSTRUCTIONS_AVAILABLE 86
#define PF_ARM_SME_LUTv2_INSTRUCTIONS_AVAILABLE 87
#define PF_ARM_SME_FA64_INSTRUCTIONS_AVAILABLE 88
void ffCPUDetectByCpuid(FFCPUResult* cpu)
{
// ARMv8-A
bool has_vfp = IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE); // Implies basic FP support
bool has_neon = IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE); // NEON (ASIMD)
// ARMv8.1-A
bool has_atomics = IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE); // LSE atomics
bool has_crc32 = IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE); // CRC32
// ARMv8.2-A
bool has_fp16 = IsProcessorFeaturePresent(PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE); // Half-precision FP
// ARMv8.3-A
bool has_lrcpc = IsProcessorFeaturePresent(PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE); // LDAPR/LR with RCPC semantics
bool has_jscvt = IsProcessorFeaturePresent(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE); // FJCVTZS
// ARMv8.4-A
// My CPU (Apple M1 Pro in VM) does support LSE2, but Windows doesn't detect it for some reason
// bool has_lse2 = IsProcessorFeaturePresent(PF_ARM_LSE2_AVAILABLE); // Large System Extensions version 2, optional from v8.2
bool has_dp = IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE); // DotProd, optional from v8.1 (*)
// ARMv9.0-A
bool has_sve2 = IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE); // SVE2
// ARMv9.1-A
// ARMv8.6-A
bool has_bf16 = IsProcessorFeaturePresent(PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE); // BF16, optional from v8.2
bool has_i8mm = IsProcessorFeaturePresent(PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE); // Int8 matrix multiply, optional from v8.2
// ARMv8.7-A
bool has_ebf16 = IsProcessorFeaturePresent(PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE); // Extended BFloat16 behaviors, optional from v8.2
// ARMv9.2-A
bool has_sme = IsProcessorFeaturePresent(PF_ARM_SME_INSTRUCTIONS_AVAILABLE); // SME
// ARMv9.3-A
bool has_sme2 = IsProcessorFeaturePresent(PF_ARM_SME2_INSTRUCTIONS_AVAILABLE); // SME2
// ARMv9.4-A
bool has_sme2p1 = IsProcessorFeaturePresent(PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE); // SME2.1
if (has_sve2 || has_sme)
{
// ARMv9 family
if (has_sme2p1) {
cpu->march = "ARMv9.4-A";
} else if (has_sme2) {
cpu->march = "ARMv9.3-A";
} else if (has_sme) {
cpu->march = "ARMv9.2-A";
} else if (has_i8mm && has_bf16) {
cpu->march = "ARMv9.1-A";
} else {
cpu->march = "ARMv9.0-A";
}
}
else
{
// ARMv8 family
if (has_ebf16) {
cpu->march = "ARMv8.7-A";
} else if (has_i8mm && has_bf16) {
cpu->march = "ARMv8.6-A";
} else if (has_dp) {
cpu->march = "ARMv8.4-A";
} else if (has_lrcpc && has_jscvt) {
cpu->march = "ARMv8.3-A";
} else if (has_fp16) {
cpu->march = "ARMv8.2-A";
} else if (has_atomics && has_crc32) {
cpu->march = "ARMv8.1-A";
} else if (has_neon && has_vfp) {
cpu->march = "ARMv8-A";
}
}
}
#else
void ffCPUDetectByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu)
{
// Unsupported system
}
#endif
#else
void ffCPUDetectByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu)
{
// Unsupported architecture
}
#endif
+4 -27
View File
@@ -1,8 +1,9 @@
#pragma once
#include "fastfetch.h"
#include "modules/cpu/option.h"
#define FF_CPU_TEMP_UNSET (0/0.0)
#define FF_CPU_TEMP_UNSET (-DBL_MAX)
typedef struct FFCPUCore
{
@@ -14,6 +15,7 @@ typedef struct FFCPUResult
{
FFstrbuf name;
FFstrbuf vendor;
const char* march; // Microarchitecture
uint16_t packages;
uint16_t coresPhysical;
@@ -31,29 +33,4 @@ typedef struct FFCPUResult
const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu);
const char* ffCPUAppleCodeToName(uint32_t code);
const char* ffCPUQualcommCodeToName(uint32_t code);
#if defined(__x86_64__) || defined(__i386__)
#include <cpuid.h>
// WARNING: CPUID may report frequencies of efficient cores
inline static const char* ffCPUDetectSpeedByCpuid(FFCPUResult* cpu)
{
uint32_t base = 0, max = 0, bus = 0, unused = 0;
if (!__get_cpuid(0x16, &base, &max, &bus, &unused))
return "Unsupported instruction";
// cpuid returns 0 MHz when hyper-v is enabled
if (base) cpu->frequencyBase = base;
if (max) cpu->frequencyMax = max;
return NULL;
}
#else
inline static const char* ffCPUDetectSpeedByCpuid(FF_MAYBE_UNUSED FFCPUResult* cpu)
{
return "Unsupported platform";
}
#endif
void ffCPUDetectByCpuid(FFCPUResult* cpu);
+1
View File
@@ -126,6 +126,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
if(cpu->coresOnline == 1)
cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.activecpu", 1);
ffCPUDetectByCpuid(cpu);
detectFrequency(cpu);
if (options->showPeCoreCount) detectCoreCount(cpu);
+1 -1
View File
@@ -70,7 +70,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset_t), &currentCPU);
#endif
ffCPUDetectSpeedByCpuid(cpu);
ffCPUDetectByCpuid(cpu);
uint32_t clockrate = (uint32_t) ffSysctlGetInt("hw.clockrate", 0);
if (clockrate > cpu->frequencyBase) cpu->frequencyBase = clockrate;
+1 -1
View File
@@ -55,7 +55,7 @@ const char* ffDetectCPUImpl(FF_MAYBE_UNUSED const FFCPUOptions* options, FFCPURe
ffStrbufSetF(&cpu->name, "(Unknown %" B_PRIx32 ")", cpuModel);
ffStrbufSetS(&cpu->vendor, get_cpu_vendor_string(cpuVendor));
ffCPUDetectSpeedByCpuid(cpu);
ffCPUDetectByCpuid(cpu);
if (cpu->frequencyBase < frequency) cpu->frequencyBase = frequency;
cpu->packages = packages;
cpu->coresPhysical = cores;
+166 -129
View File
@@ -13,84 +13,129 @@
#define FF_CPUINFO_PATH "/proc/cpuinfo"
static double parseHwmonDir(FFstrbuf* dir, FFstrbuf* buffer)
static double parseTZDir(int dfd, FFstrbuf* buffer)
{
//https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
uint32_t dirLength = dir->length;
ffStrbufAppendS(dir, "temp1_input");
if (!ffReadFileBufferRelative(dfd, "type", buffer))
return FF_CPU_TEMP_UNSET;
if(!ffReadFileBuffer(dir->chars, buffer))
{
// Some badly implemented system put temp file in /hwmonN/device
ffStrbufSubstrBefore(dir, dirLength);
ffStrbufAppendS(dir, "device/");
dirLength = dir->length;
ffStrbufAppendS(dir, "temp1_input");
if (!ffStrbufStartsWithS(buffer, "cpu") &&
!ffStrbufStartsWithS(buffer, "soc") &&
#if __x86_64__ || __i386__
!ffStrbufEqualS(buffer, "x86_pkg_temp") &&
#endif
true
) return FF_CPU_TEMP_UNSET;
if(!ffReadFileBuffer(dir->chars, buffer))
return 0.0/0.0;
}
if (!ffReadFileBufferRelative(dfd, "temp", buffer))
return FF_CPU_TEMP_UNSET;
ffStrbufSubstrBefore(dir, dirLength);
double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET);// millidegree Celsius
if (value == FF_CPU_TEMP_UNSET)
return FF_CPU_TEMP_UNSET;
double value = ffStrbufToDouble(buffer);// millidegree Celsius
return value / 1000.;
}
if(value != value)
return 0.0/0.0;
ffStrbufAppendS(dir, "name");
if (!ffReadFileBuffer(dir->chars, buffer))
return 0.0/0.0;
static double parseHwmonDir(int dfd, FFstrbuf* buffer)
{
if (!ffReadFileBufferRelative(dfd, "name", buffer))
return FF_CPU_TEMP_UNSET;
ffStrbufTrimRightSpace(buffer);
if(
ffStrbufContainS(buffer, "cpu") ||
ffStrbufEqualS(buffer, "k10temp") || // AMD
ffStrbufEqualS(buffer, "fam15h_power") || // AMD
ffStrbufEqualS(buffer, "coretemp") // Intel
) return value / 1000.;
if (
!ffStrbufContainS(buffer, "cpu") &&
#if __x86_64__ || __i386__
!ffStrbufEqualS(buffer, "k10temp") && // AMD
!ffStrbufEqualS(buffer, "fam15h_power") && // AMD
!ffStrbufEqualS(buffer, "coretemp") && // Intel
#endif
true
) return FF_CPU_TEMP_UNSET;
return 0.0/0.0;
}
//https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
if (!ffReadFileBufferRelative(dfd, "temp1_input", buffer))
return FF_CPU_TEMP_UNSET;
static double detectTZTemp(FFstrbuf* buffer)
{
if (ffReadFileBuffer("/sys/class/thermal/thermal_zone0/temp", buffer))
return ffStrbufToDouble(buffer) / 1000.;
return 0.0/0.0;
double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET);// millidegree Celsius
if (value == FF_CPU_TEMP_UNSET)
return FF_CPU_TEMP_UNSET;
return value / 1000.;
}
static double detectCPUTemp(void)
{
FF_STRBUF_AUTO_DESTROY baseDir = ffStrbufCreateA(64);
ffStrbufAppendS(&baseDir, "/sys/class/hwmon/");
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
uint32_t baseDirLength = baseDir.length;
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(baseDir.chars);
if(dirp == NULL)
return 0.0/0.0;
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
if(entry->d_name[0] == '.')
continue;
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/hwmon/");
if(dirp)
{
int dfd = dirfd(dirp);
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
if(entry->d_name[0] == '.')
continue;
ffStrbufAppendS(&baseDir, entry->d_name);
ffStrbufAppendC(&baseDir, '/');
FF_AUTO_CLOSE_FD int subfd = openat(dfd, entry->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if(subfd < 0)
continue;
double result = parseHwmonDir(&baseDir, &buffer);
if (result == result)
return result;
double result = parseHwmonDir(subfd, &buffer);
if (result != FF_CPU_TEMP_UNSET)
return result;
}
}
}
{
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/thermal/");
if(dirp)
{
int dfd = dirfd(dirp);
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
if(entry->d_name[0] == '.')
continue;
if(!ffStrStartsWith(entry->d_name, "thermal_zone"))
continue;
ffStrbufSubstrBefore(&baseDir, baseDirLength);
FF_AUTO_CLOSE_FD int subfd = openat(dfd, entry->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if(subfd < 0)
continue;
double result = parseTZDir(subfd, &buffer);
if (result != FF_CPU_TEMP_UNSET)
return result;
}
}
}
{
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/devices/platform/");
if(dirp)
{
int dfd = dirfd(dirp);
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
if(entry->d_name[0] == '.')
continue;
if(!ffStrStartsWith(entry->d_name, "cputemp."))
continue;
FF_AUTO_CLOSE_FD int subfd = openat(dfd, entry->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if(subfd < 0)
continue;
double result = parseHwmonDir(subfd, &buffer);
if (result != FF_CPU_TEMP_UNSET)
return result;
}
}
}
return detectTZTemp(&buffer);
return FF_CPU_TEMP_UNSET;
}
#ifdef __ANDROID__
@@ -100,85 +145,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)
@@ -463,7 +498,7 @@ FF_MAYBE_UNUSED static const char* detectCPUX86(const FFCPUOptions* options, FFC
cpu->coresPhysical *= cpu->packages;
// Ref https://github.com/fastfetch-cli/fastfetch/issues/1194#issuecomment-2295058252
ffCPUDetectSpeedByCpuid(cpu);
ffCPUDetectByCpuid(cpu);
if (!detectFrequency(cpu, options) || cpu->frequencyBase == 0)
cpu->frequencyBase = (uint32_t) ffStrbufToUInt(&cpuMHz, 0);
@@ -474,7 +509,7 @@ FF_MAYBE_UNUSED static const char* detectCPUX86(const FFCPUOptions* options, FFC
static const char* detectPhysicalCores(FFCPUResult* cpu)
{
int dfd = open("/sys/devices/system/cpu/", O_RDONLY | O_DIRECTORY);
int dfd = open("/sys/devices/system/cpu/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (dfd < 0) return "open(\"/sys/devices/system/cpu/\") failed";
FF_AUTO_CLOSE_DIR DIR* dir = fdopendir(dfd);
@@ -741,6 +776,8 @@ FF_MAYBE_UNUSED static const char* detectCPUOthers(const FFCPUOptions* options,
if (cpu->coresPhysical == 0)
detectPhysicalCores(cpu);
ffCPUDetectByCpuid(cpu);
return NULL;
}
#endif
+5 -4
View File
@@ -18,8 +18,8 @@ static void freePropDict(prop_dictionary_t* pdict)
static const char* detectCpuTemp(double* current)
{
FF_AUTO_CLOSE_FD int fd = open(_PATH_SYSMON, O_RDONLY);
if (fd < 0) return "open(_PATH_SYSMON, O_RDONLY) failed";
FF_AUTO_CLOSE_FD int fd = open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC);
if (fd < 0) return "open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC) failed";
__attribute__((__cleanup__(freePropDict))) prop_dictionary_t root = NULL;
if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &root) < 0)
@@ -51,7 +51,8 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
{
if (ffSysctlGetString("machdep.cpu_brand", &cpu->name) != NULL &&
ffSysctlGetString("machdep.dmi.processor-version", &cpu->name) != NULL &&
ffSysctlGetString("hw.cpu0.name", &cpu->name) != NULL)
ffSysctlGetString("hw.cpu0.name", &cpu->name) != NULL &&
ffSysctlGetString("hw.model", &cpu->name) != NULL)
{
ffStrbufSetS(&cpu->name, "Unknown CPU");
}
@@ -63,7 +64,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
cpu->coresLogical = cpu->coresPhysical;
cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.ncpuonline", cpu->coresLogical);
ffCPUDetectSpeedByCpuid(cpu);
ffCPUDetectByCpuid(cpu);
uint32_t freq = (uint32_t) ffSysctlGetInt("machdep.cpu.frequency.target", 0);
if (freq == 0) freq = (uint32_t) (ffSysctlGetInt64("hw.cpu0.clock_frequency", 0) / 1000000);
+1 -1
View File
@@ -56,7 +56,7 @@ const char *ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
cpu->coresLogical = cpu->coresPhysical;
cpu->coresOnline = (uint16_t) ffSysctlGetInt(CTL_HW, HW_NCPUONLINE, cpu->coresLogical);
ffCPUDetectSpeedByCpuid(cpu);
ffCPUDetectByCpuid(cpu);
uint32_t cpuspeed = (uint32_t) ffSysctlGetInt(CTL_HW, HW_CPUSPEED, 0);
if (cpuspeed > cpu->frequencyBase) cpu->frequencyBase = cpuspeed;
+1 -1
View File
@@ -122,7 +122,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
kstat_named_t* kn = kstat_data_lookup(ks, "vendor_id");
if (kn) ffStrbufSetNS(&cpu->vendor, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn));
}
ffCPUDetectSpeedByCpuid(cpu);
ffCPUDetectByCpuid(cpu);
{
kstat_named_t* kn = kstat_data_lookup(ks, "clock_MHz");
if (kn && kn->value.ui32 > cpu->frequencyBase)
+98 -24
View File
@@ -4,42 +4,116 @@
#include "util/mallocHelper.h"
#include "util/smbiosHelper.h"
#include <pdh.h>
#include <windows.h>
#include "util/windows/perflib_.h"
#include <wchar.h>
static void ffPdhOpenCloseQuery(HQUERY* query)
static inline void ffPerfCloseQueryHandle(HANDLE* phQuery)
{
assert(query);
if (*query)
if (*phQuery != NULL)
{
PdhCloseQuery(*query);
*query = NULL;
PerfCloseQueryHandle(*phQuery);
*phQuery = NULL;
}
}
static const char* detectThermalTemp(double* result)
const char* detectThermalTemp(double* result)
{
// typeperf.exe -sc 1 "\Thermal Zone Information(*)\Temperature"
struct FFPerfQuerySpec
{
PERF_COUNTER_IDENTIFIER Identifier;
WCHAR Name[16];
} querySpec = {
.Identifier = {
// Thermal Zone Information
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers\{383487a6-3676-4870-a4e7-d45b30c35629}\{52bc5412-dac2-449c-8bc2-96443888fe6b}
.CounterSetGuid = { 0x52bc5412, 0xdac2, 0x449c, {0x8b, 0xc2, 0x96, 0x44, 0x38, 0x88, 0xfe, 0x6b} },
.Size = sizeof(querySpec),
.CounterId = PERF_WILDCARD_COUNTER,
.InstanceId = PERF_WILDCARD_COUNTER,
},
.Name = L"\\_TZ.CPUZ", // The standard(?) instance name for CPU temperature in the thermal provider
};
__attribute__((__cleanup__(ffPdhOpenCloseQuery))) HQUERY query = NULL;
DWORD dataSize = 0;
if (PerfEnumerateCounterSetInstances(NULL, &querySpec.Identifier.CounterSetGuid, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY)
return "PerfEnumerateCounterSetInstances() failed";
if (PdhOpenQueryW(NULL, 0, &query) != ERROR_SUCCESS)
return "Failed to open PDH query";
if (dataSize <= sizeof(PERF_INSTANCE_HEADER))
return "No `Thermal Zone Information` instances found";
HCOUNTER counter = NULL;
if (PdhAddEnglishCounterW(query,
L"\\Thermal Zone Information(*)\\Temperature",
0,
&counter) != ERROR_SUCCESS)
return "Failed to add TZI temperature counter";
{
FF_AUTO_FREE PERF_INSTANCE_HEADER* const pHead = malloc(dataSize);
if (PerfEnumerateCounterSetInstances(NULL, &querySpec.Identifier.CounterSetGuid, pHead, dataSize, &dataSize) != ERROR_SUCCESS)
return "PerfEnumerateCounterSetInstances() failed to get instance headers";
if (PdhCollectQueryData(query) != ERROR_SUCCESS)
return "Failed to collect query data";
PERF_INSTANCE_HEADER* pInstanceHeader = pHead;
while (1)
{
const wchar_t* instanceName = (const wchar_t*)((BYTE*)pInstanceHeader + sizeof(*pInstanceHeader));
if (wcscmp(instanceName, querySpec.Name) == 0)
break;
PDH_FMT_COUNTERVALUE value;
if (PdhGetFormattedCounterValue(counter, PDH_FMT_DOUBLE, NULL, &value) != ERROR_SUCCESS)
return "Failed to format counter value";
dataSize -= pInstanceHeader->Size;
if (dataSize == 0)
break;
pInstanceHeader = (PERF_INSTANCE_HEADER*)((BYTE*)pInstanceHeader + pInstanceHeader->Size);
}
*result = value.doubleValue - 273;
if (dataSize == 0)
{
const wchar_t* instanceName = (const wchar_t*)((BYTE*)pHead + sizeof(*pHead));
wcscpy(querySpec.Name, instanceName); // Use the first instance name if the specific one is not found
}
}
__attribute__((__cleanup__(ffPerfCloseQueryHandle)))
HANDLE hQuery = NULL;
if (PerfOpenQueryHandle(NULL, &hQuery) != ERROR_SUCCESS)
return "PerfOpenQueryHandle() failed";
if (PerfAddCounters(hQuery, &querySpec.Identifier, sizeof(querySpec)) != ERROR_SUCCESS)
return "PerfAddCounters() failed";
if (querySpec.Identifier.Status != ERROR_SUCCESS)
return "PerfAddCounters() reports invalid identifier";
if (PerfQueryCounterData(hQuery, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY)
return "PerfQueryCounterData(NULL) failed";
if (dataSize <= sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER)) // PERF_ERROR_RETURN, should not happen
return "instance doesn't exist";
FF_AUTO_FREE PERF_DATA_HEADER* const pDataHeader = malloc(dataSize);
if (PerfQueryCounterData(hQuery, pDataHeader, dataSize, &dataSize) != ERROR_SUCCESS)
return "PerfQueryCounterData(pDataHeader) failed";
PERF_COUNTER_HEADER* pCounterHeader = (PERF_COUNTER_HEADER*)(pDataHeader + 1);
if (pCounterHeader->dwType != PERF_MULTIPLE_COUNTERS)
return "Invalid counter type";
PERF_MULTI_COUNTERS* pMultiCounters = (PERF_MULTI_COUNTERS*)(pCounterHeader + 1);
PERF_COUNTER_DATA* pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pMultiCounters + pMultiCounters->dwSize);
for (ULONG iCounter = 0; iCounter != pMultiCounters->dwCounters; iCounter++)
{
if (pCounterData->dwDataSize == sizeof(int32_t))
{
DWORD* pCounterIds = (DWORD*)(pMultiCounters + 1);
switch (pCounterIds[iCounter]) {
case 0: // Temperature
*result = *(int32_t*)(pCounterData + 1) - 273;
break;
case 3: // High Precision Temperature
*result = *(int32_t*)(pCounterData + 1) / 10.0 - 273;
break;
}
}
pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pCounterData + pCounterData->dwSize);
}
return NULL;
}
@@ -213,7 +287,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
if (error)
return error;
ffCPUDetectSpeedByCpuid(cpu);
ffCPUDetectByCpuid(cpu);
if (options->showPeCoreCount) detectCoreTypes(cpu);
if (cpu->frequencyMax == 0)
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/cpucache/option.h"
typedef enum __attribute__((__packed__)) FFCPUCacheType
{
+10
View File
@@ -5,12 +5,14 @@
#include <stdint.h>
static FFlist cpuTimes1;
static uint64_t startTime;
void ffPrepareCPUUsage(void)
{
assert(cpuTimes1.elementSize == 0);
ffListInit(&cpuTimes1, sizeof(FFCpuUsageInfo));
ffGetCpuUsageInfo(&cpuTimes1);
startTime = ffTimeGetNow();
}
const char* ffGetCpuUsageResult(FFCPUUsageOptions* options, FFlist* result)
@@ -23,6 +25,12 @@ const char* ffGetCpuUsageResult(FFCPUUsageOptions* options, FFlist* result)
if(error) return error;
ffTimeSleep(options->waitTime);
}
else
{
uint64_t elapsedTime = ffTimeGetNow() - startTime;
if (elapsedTime < options->waitTime)
ffTimeSleep(options->waitTime - (uint32_t)elapsedTime);
}
if(cpuTimes1.length == 0) return "No CPU cores found";
@@ -46,6 +54,7 @@ retry:
ffTimeSleep(options->waitTime);
goto retry;
}
return "CPU time did not increase. Try increasing wait time.";
}
}
@@ -57,5 +66,6 @@ retry:
cpuTime1->inUseAll = cpuTime2->inUseAll;
cpuTime1->totalAll = cpuTime2->totalAll;
}
startTime = ffTimeGetNow();
return NULL;
}
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/cpuusage/option.h"
typedef struct FFCpuUsageInfo {
uint64_t inUseAll;
+2 -2
View File
@@ -42,8 +42,8 @@ const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
for (uint32_t i = 0; i < coreCount; ++i)
{
uint64_t* cpTime = cpTimes[i];
uint64_t inUse = cpTime[CP_USER] + cpTime[CP_NICE] + cpTime[CP_SYS];
uint64_t total = inUse + cpTime[CP_INTR] + cpTime[CP_IDLE];
uint64_t inUse = cpTime[CP_USER] + cpTime[CP_NICE] + cpTime[CP_SYS] + cpTime[CP_INTR];
uint64_t total = inUse + cpTime[CP_IDLE];
FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes);
*info = (FFCpuUsageInfo) {
+2 -2
View File
@@ -31,8 +31,8 @@ const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
{
if(sscanf(start, "cpu%*d%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%*[^\n]\n", &user, &nice, &system, &idle, &iowait, &irq, &softirq) == 7)
{
uint64_t inUse = user + nice + system;
uint64_t total = inUse + idle + iowait + irq + softirq;
uint64_t inUse = user + nice + system + irq + softirq;
uint64_t total = inUse + idle + iowait;
FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes);
*info = (FFCpuUsageInfo) {
+139 -5
View File
@@ -5,8 +5,11 @@
#include <ntstatus.h>
#include <winternl.h>
#include <windows.h>
#include <wchar.h>
#include "util/windows/perflib_.h"
const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
static const char* getInfoByNqsi(FFlist* cpuTimes)
{
ULONG size = 0;
if(NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL, 0, &size) != STATUS_INFO_LENGTH_MISMATCH)
@@ -20,11 +23,8 @@ const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
{
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* coreInfo = pinfo + i;
// KernelTime includes idle time.
LONGLONG dpcTime = coreInfo->Reserved1[0].QuadPart;
LONGLONG interruptTime = coreInfo->Reserved1[1].QuadPart;
// KernelTime includes IdleTime and DpcTime.
coreInfo->KernelTime.QuadPart -= coreInfo->IdleTime.QuadPart;
coreInfo->KernelTime.QuadPart += dpcTime + interruptTime;
uint64_t inUse = (uint64_t) (coreInfo->UserTime.QuadPart + coreInfo->KernelTime.QuadPart);
uint64_t total = inUse + (uint64_t)coreInfo->IdleTime.QuadPart;
@@ -38,3 +38,137 @@ const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
return NULL;
}
static const char* getInfoByPerflib(FFlist* cpuTimes)
{
static HANDLE hQuery = NULL;
if (hQuery == NULL)
{
struct FFPerfQuerySpec
{
PERF_COUNTER_IDENTIFIER Identifier;
WCHAR Name[16];
} querySpec = {
.Identifier = {
// Processor Information GUID
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers\{383487a6-3676-4870-a4e7-d45b30c35629}\{b4fc721a-0378-476f-89ba-a5a79f810b36}
.CounterSetGuid = { 0xb4fc721a, 0x0378, 0x476f, {0x89, 0xba, 0xa5, 0xa7, 0x9f, 0x81, 0x0b, 0x36} },
.Size = sizeof(querySpec),
.CounterId = PERF_WILDCARD_COUNTER, // https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data
.InstanceId = PERF_WILDCARD_COUNTER,
},
.Name = PERF_WILDCARD_INSTANCE,
};
if (PerfOpenQueryHandle(NULL, &hQuery) != ERROR_SUCCESS)
{
PerfCloseQueryHandle(hQuery);
hQuery = INVALID_HANDLE_VALUE;
return "PerfOpenQueryHandle() failed";
}
if (PerfAddCounters(hQuery, &querySpec.Identifier, sizeof(querySpec)) != ERROR_SUCCESS)
{
PerfCloseQueryHandle(hQuery);
hQuery = INVALID_HANDLE_VALUE;
return "PerfAddCounters() failed";
}
if (querySpec.Identifier.Status != ERROR_SUCCESS)
{
PerfCloseQueryHandle(hQuery);
hQuery = INVALID_HANDLE_VALUE;
return "PerfAddCounters() reports invalid identifier";
}
}
if (hQuery == INVALID_HANDLE_VALUE)
return "Init hQuery failed";
DWORD dataSize = 0;
if (PerfQueryCounterData(hQuery, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY)
return "PerfQueryCounterData(NULL) failed";
if (dataSize <= sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER))
return "instance doesn't exist";
FF_AUTO_FREE PERF_DATA_HEADER* const pDataHeader = (PERF_DATA_HEADER*)malloc(dataSize);
if (PerfQueryCounterData(hQuery, pDataHeader, dataSize, &dataSize) != ERROR_SUCCESS)
return "PerfQueryCounterData(pDataHeader) failed";
PERF_COUNTER_HEADER* pCounterHeader = (PERF_COUNTER_HEADER*)(pDataHeader + 1);
if (pCounterHeader->dwType != PERF_COUNTERSET)
return "Invalid counter type";
PERF_MULTI_COUNTERS* pMultiCounters = (PERF_MULTI_COUNTERS*)(pCounterHeader + 1);
if (pMultiCounters->dwCounters == 0)
return "No CPU counters found";
PERF_MULTI_INSTANCES* pMultiInstances = (PERF_MULTI_INSTANCES*)((BYTE*)pMultiCounters + pMultiCounters->dwSize);
if (pMultiInstances->dwInstances == 0)
return "No CPU instances found";
PERF_INSTANCE_HEADER* pInstanceHeader = (PERF_INSTANCE_HEADER*)(pMultiInstances + 1);
for (DWORD iInstance = 0; iInstance < pMultiInstances->dwInstances; ++iInstance)
{
const wchar_t* instanceName = (const wchar_t*)((BYTE*)pInstanceHeader + sizeof(*pInstanceHeader));
PERF_COUNTER_DATA* pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pInstanceHeader + pInstanceHeader->Size);
uint64_t processorUtility = UINT64_MAX, utilityBase = UINT64_MAX;
for (ULONG iCounter = 0; iCounter != pMultiCounters->dwCounters; iCounter++)
{
DWORD* pCounterIds = (DWORD*)(pMultiCounters + 1);
// https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data
switch (pCounterIds[iCounter]) {
case 26: // % Processor Utility (#26, Type=PERF_AVERAGE_BULK)
assert(pCounterData->dwDataSize == sizeof(uint64_t));
processorUtility = *(uint64_t*)(pCounterData + 1);
break;
case 27: // % Utility Base (#27, Type=PERF_AVERAGE_BASE)
assert(pCounterData->dwDataSize == sizeof(uint32_t));
utilityBase = *(uint32_t*)(pCounterData + 1) * 100LLU;
break;
}
pCounterData = (PERF_COUNTER_DATA*)((BYTE*)pCounterData + pCounterData->dwSize);
}
if (wcschr(instanceName, L'_') == NULL /* ignore `_Total` */)
{
if (processorUtility == UINT64_MAX)
return "Counter \"% Processor Utility\" are not supported";
FFCpuUsageInfo* info = (FFCpuUsageInfo*) ffListAdd(cpuTimes);
*info = (FFCpuUsageInfo) {
.inUseAll = processorUtility,
.totalAll = utilityBase,
};
}
pInstanceHeader = (PERF_INSTANCE_HEADER*)pCounterData;
}
return NULL;
}
const char* ffGetCpuUsageInfo(FFlist* cpuTimes)
{
#if __aarch64__
static uint8_t winver = 10; // Assume Windows 10 or later for WoA
#else
static uint8_t winver = 0;
if (winver == 0)
winver = (uint8_t) ffStrbufToUInt(&instance.state.platform.sysinfo.release, 1);
#endif
if (winver >= 10)
{
if (getInfoByPerflib(cpuTimes) == NULL) return NULL;
ffListClear(cpuTimes);
winver = 1; // Fall back to NQSI
}
return getInfoByNqsi(cpuTimes);
}
+1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "fastfetch.h"
#include "modules/cursor/option.h"
typedef struct FFCursorResult
{
+23 -12
View File
@@ -2,19 +2,30 @@
#import <Foundation/Foundation.h>
static void appendColor(FFstrbuf* str, NSDictionary* color)
static void appendColor(FFstrbuf* str, NSDictionary* dict)
{
int r = (int) (((NSNumber*) color[@"red"]).doubleValue * 255);
int g = (int) (((NSNumber*) color[@"green"]).doubleValue * 255);
int b = (int) (((NSNumber*) color[@"blue"]).doubleValue * 255);
int a = (int) (((NSNumber*) color[@"alpha"]).doubleValue * 255);
uint32_t r = (uint32_t) (((NSNumber*) dict[@"red"]).doubleValue * 255 + .5);
uint32_t g = (uint32_t) (((NSNumber*) dict[@"green"]).doubleValue * 255 + .5);
uint32_t b = (uint32_t) (((NSNumber*) dict[@"blue"]).doubleValue * 255 + .5);
uint32_t a = (uint32_t) (((NSNumber*) dict[@"alpha"]).doubleValue * 255 + .5);
uint32_t color = (r << 24) | (g << 16) | (b << 8) | a;
if (r == 255 && g == 255 && b == 255 && a == 255)
ffStrbufAppendS(str, "White");
else if (r == 0 && g == 0 && b == 0 && a == 255)
ffStrbufAppendS(str, "Black");
else
ffStrbufAppendF(str, "#%02X%02X%02X%02X", r, g, b, a);
switch (color)
{
case 0x000000FF: ffStrbufAppendS(str, "Black"); return;
case 0x0433FFFF: ffStrbufAppendS(str, "Blue"); return;
case 0xAA7942FF: ffStrbufAppendS(str, "Brown"); return;
case 0x00FDFFFF: ffStrbufAppendS(str, "Cyan"); return;
case 0x00F900FF: ffStrbufAppendS(str, "Green"); return;
case 0xFF40FFFF: ffStrbufAppendS(str, "Magenta"); return;
case 0xFF9300FF: ffStrbufAppendS(str, "Orange"); return;
case 0x942192FF: ffStrbufAppendS(str, "Purple"); return;
case 0xFF2600FF: ffStrbufAppendS(str, "Red"); return;
case 0xFFFB00FF: ffStrbufAppendS(str, "Yellow"); return;
case 0xFFFFFFFF: ffStrbufAppendS(str, "White"); return;
case 0x00000000: ffStrbufAppendS(str, "Transparent"); return;
default: ffStrbufAppendF(str, "#%08X", color); return;
}
}
void ffDetectCursor(FFCursorResult* result)
@@ -46,7 +57,7 @@ void ffDetectCursor(FFCursorResult* result)
NSNumber* mouseDriverCursorSize = dict[@"mouseDriverCursorSize"];
if (mouseDriverCursorSize)
ffStrbufAppendF(&result->size, "%d", (int) (mouseDriverCursorSize.doubleValue * 32));
ffStrbufAppendF(&result->size, "%d", (int) (mouseDriverCursorSize.doubleValue * 32 + 0.5));
else
ffStrbufAppendS(&result->size, "32");
}

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