Compare commits

...

137 Commits

Author SHA1 Message Date
Carter Li a9f43ff466 Packaging: update debian stuff 2025-12-18 15:00:51 +08:00
Carter Li 924d78d3c4 Release: v2.56.1 (#2097)
* Logo (Builtin): drop opak

Ref: #1070

* Swap (Windows): adds `K32GetPerformanceInfo` fallback

* Platform (Windows): fixes if checks

* Platform (Windows): adds a null check

* Packages (Windows): improves performance for directory enumeration

* Util (Windows): fixes / adds a new Unicode conversion function

* Packaging: update debian stuff [ci skip]

* IO (Windows): enhances `openat`

* CPU: adds option `tempSensor`

* CPU (macOS): supports `tempSensor`

* CPU (Windows): supports `tempSensor`

* CPU (Linux): supports `tempSensor`

* CPU (FreeBSD): supports `tempSensor`

* CPU (NetBSD): supports `tempSensor`

* CPU (OpenBSD): supports `tempSensor`

* CPU (Linux): prevents variables from integer overflow

* GPU (Linux): add comments

* CPU (SunOS): supports `tempSensor`

* Doc: clarifies that we supports illumos only

* JsonSchema: documents `cpu.tempSensor`

Fixes #2073

* CPUCache (macOS): fixes cache line size detection

* CPU(Linux): Fix build on GNU.

* Chore: refines usage of `PATH_MAX`

* DisplayServer (Linux): upgrades kde-output-device-v2 wayland protocol files (again)

Fixes #2093

* Logo (Builtin): adds gxde logo (#2094)

* add gxde-logo

* add gxdeos

* add gxdeos

* add gxdeos

* Release: v2.56.1

* Memory (macOS): uses `hw.memsize_usable`

* Chore (Windows): Standardize windows header casing. (#2096) [ci skip]

Followup to 0b6ca158ea.

Co-authored-by: Dariqq <dariqq@noreply.codeberg.org>

* IO (Windows): removes unneeded null terminator appending

* CPU (SunOS): adds `const` for consistancy

* Host (macOS): prefers `hw.product` than `hw.model`

... because `HW_MODEL` is deprecated

* Doc: update changelog

* Memory (macOS): Refines Memory usage detection on macOS to match Activity Monitor more closely

* Packages (Windows): fixes possible UB

Reported by Github Copilot

* JsonSchema: refines comments [ci skip]

---------

Co-authored-by: Yelninei <yelninei@tutamail.com>
Co-authored-by: gitee-zeqi <a202128502@163.com>
Co-authored-by: Dariqq <77271900+Dariqq@users.noreply.github.com>
Co-authored-by: Dariqq <dariqq@noreply.codeberg.org>
2025-12-18 14:46:24 +08:00
李通洲 fa92bb84ae JsonSchema: refines comments [ci skip] 2025-12-18 14:35:21 +08:00
李通洲 288d25e17c Packages (Windows): fixes possible UB
Reported by Github Copilot
2025-12-18 14:21:55 +08:00
李通洲 610fc2ea36 Memory (macOS): Refines Memory usage detection on macOS to match Activity Monitor more closely 2025-12-18 13:36:38 +08:00
李通洲 716c0e2b5c Doc: update changelog 2025-12-18 10:00:05 +08:00
李通洲 945aed7160 Host (macOS): prefers hw.product than hw.model
... because `HW_MODEL` is deprecated
2025-12-18 10:00:05 +08:00
李通洲 1d4d68f6e0 CPU (SunOS): adds const for consistancy 2025-12-17 14:30:07 +08:00
李通洲 3e1167260d IO (Windows): removes unneeded null terminator appending 2025-12-17 14:30:07 +08:00
Dariqq 0130f610e9 Chore (Windows): Standardize windows header casing. (#2096) [ci skip]
Followup to 0b6ca158ea.

Co-authored-by: Dariqq <dariqq@noreply.codeberg.org>
2025-12-16 18:53:26 -06:00
李通洲 94941daf4f Memory (macOS): uses hw.memsize_usable 2025-12-16 10:41:27 +08:00
李通洲 2bbd723321 Release: v2.56.1 2025-12-16 10:16:36 +08:00
gitee-zeqi 608382109c Logo (Builtin): adds gxde logo (#2094)
* add gxde-logo

* add gxdeos

* add gxdeos

* add gxdeos
2025-12-15 13:30:47 +08:00
李通洲 bdc5174f68 DisplayServer (Linux): upgrades kde-output-device-v2 wayland protocol files (again)
Fixes #2093
2025-12-15 13:25:26 +08:00
李通洲 bc869f1be9 Chore: refines usage of PATH_MAX 2025-12-12 09:03:18 +08:00
Yelninei dc69e44829 CPU(Linux): Fix build on GNU. 2025-12-11 21:32:45 +08:00
李通洲 d236850119 CPUCache (macOS): fixes cache line size detection 2025-12-11 14:38:06 +08:00
李通洲 60ec8c9097 JsonSchema: documents cpu.tempSensor
Fixes #2073
2025-12-11 14:37:28 +08:00
李通洲 42e7a7d2f7 Doc: clarifies that we supports illumos only 2025-12-11 14:28:12 +08:00
李通洲 e766809e3a CPU (SunOS): supports tempSensor 2025-12-11 14:24:07 +08:00
Carter Li cb1f0300b9 GPU (Linux): add comments 2025-12-11 09:52:50 +08:00
Carter Li f2a29309f3 CPU (Linux): prevents variables from integer overflow 2025-12-11 09:52:50 +08:00
李通洲 6e7b4bab75 CPU (OpenBSD): supports tempSensor 2025-12-11 09:52:50 +08:00
李通洲 b4f39e251c CPU (NetBSD): supports tempSensor 2025-12-11 09:52:50 +08:00
李通洲 af6196fe6e CPU (FreeBSD): supports tempSensor 2025-12-11 09:52:50 +08:00
Carter Li bca56b2049 CPU (Linux): supports tempSensor 2025-12-11 09:52:50 +08:00
李通洲 f4401f172e CPU (Windows): supports tempSensor 2025-12-11 09:52:50 +08:00
李通洲 c11b88fee5 CPU (macOS): supports tempSensor 2025-12-11 09:52:50 +08:00
李通洲 88b1b558d7 CPU: adds option tempSensor 2025-12-11 09:52:50 +08:00
李通洲 ba6cdc77c9 IO (Windows): enhances openat 2025-12-11 09:52:50 +08:00
Carter Li b952772902 Packaging: update debian stuff [ci skip] 2025-12-11 09:52:50 +08:00
李通洲 7a322154fa Util (Windows): fixes / adds a new Unicode conversion function 2025-12-08 20:11:14 +08:00
李通洲 0b6ca158ea Packages (Windows): improves performance for directory enumeration 2025-12-08 16:42:14 +08:00
李通洲 100cd9a72b Platform (Windows): adds a null check 2025-12-08 09:49:54 +08:00
李通洲 98fb810f1f Platform (Windows): fixes if checks 2025-12-08 09:49:54 +08:00
李通洲 c613e558fd Swap (Windows): adds K32GetPerformanceInfo fallback 2025-12-08 09:49:41 +08:00
Carter Li c324f83826 Logo (Builtin): drop opak
Ref: #1070
2025-12-08 09:03:28 +08:00
Carter Li e50421a6ce Merge pull request #2083 from fastfetch-cli/dev
Release: v2.56.0
2025-12-05 22:42:51 +08:00
李通洲 417b944e1c Doc: updates changelog 2025-12-05 21:12:11 +08:00
李通洲 cb8b4b335d FFstrbuf: removes an unnecessary return 2025-12-05 21:10:19 +08:00
李通洲 c0160537b0 Zpool: fixes inconsistant CMake variables 2025-12-05 21:09:00 +08:00
李通洲 4cb7a6424a Logo (Builtin): tidy 2025-12-05 21:08:28 +08:00
李通洲 7d6c79d73e Doc: documents -c - 2025-12-05 10:41:18 +08:00
李通洲 8652737f64 Fastfetch: enhances config file loading with support for config directory
Fixes #2080
2025-12-05 10:32:42 +08:00
李通洲 d6ac89ea8d Release: v2.56.0 2025-12-04 16:15:20 +08:00
李通洲 2b620cf487 CI (SunOS): fixes a CI error 2025-12-04 16:15:20 +08:00
李通洲 f644df481c CPU: exposes numa node count in custom format 2025-12-04 15:21:27 +08:00
李通洲 e99ecacb52 Chore: adds ffIsValidNativeFD and uses it 2025-12-03 09:26:32 +08:00
李通洲 3e22dd4b0e Chore: uses _WIN32 always 2025-12-03 09:26:32 +08:00
Noa Himesaka 19f30ec687 Logo (Builtin): updates Codex Linux new logo (#2071)
* fix: Codex Linux new logo

reMarkable changed their logo so reflecting that change here too

* Modify ASCII art in codex.txt

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-12-03 09:09:07 +08:00
Alxhr0 76bee78200 Chore: fixes comment for AerynOS in builtin.c (#2078) 2025-12-03 09:00:09 +08:00
李通洲 ce7455d8f0 Zpool: allows disabling module 2025-12-01 17:20:50 +08:00
李通洲 5e27ee0f57 Shell: fixes ksh version detection on Linux 2025-12-01 10:04:40 +08:00
全てずんだもんの所爲です 84a5c6ebe8 Packages (Linux): add kiss packages count (#2072) 2025-11-30 19:10:07 -06:00
李通洲 ad7e23f64c TerminalFont: supports the newest Alacritty config format
Fixes #2070
2025-11-28 16:35:14 +08:00
李通洲 2738f20eea GPU (Linux): detect driver specific info for zhaoxin 2025-11-28 03:06:23 +00:00
李通洲 09362b236c Util (Windows): fixes compiling 2025-11-25 19:10:43 +08:00
Carter Li 3cfef81877 Users (Linux): always report the newest session info 2025-11-25 19:04:36 +08:00
Carter Li 3a04ae2542 Users (Linux): adds systemd fallback for user detection on Linux
Fixes #2064
2025-11-25 16:40:52 +08:00
Carter Li dbaf8a319d FFstrbuf: adds ffStrbufGetdelim(Restore) 2025-11-25 15:08:42 +08:00
Carter Li 2fe2a21bfe Terminal: reports sshd if $SSH_TTY is not available 2025-11-25 14:33:35 +08:00
Carter Li 37639c3582 Utils: inlines ffIsAbsolutePath 2025-11-25 14:33:35 +08:00
李通洲 9a74e6d7bc Users (Linux): adds missing endutxent 2025-11-25 14:32:36 +08:00
Carter Li 5d13389827 TerminalShell: uses absolute path when detecting version if available
Closes #2067
2025-11-25 09:10:30 +08:00
李通洲 1c2c6644d5 DE: adds Android OEM UI detection
Ref #1900
2025-11-24 15:17:52 +08:00
李通洲 ffcddf30e3 Logo: improve builtin logo printing for piping and buffering
Fixes #2065
2025-11-24 14:18:45 +08:00
李通洲 22c92c7b05 Doc: recommand Ubuntu PPA [ci skip] 2025-11-21 15:56:43 +08:00
李通洲 b569048010 CMake (SunOS): remove static link of libzfs 2025-11-21 15:56:43 +08:00
李通洲 f703d9ac89 Zpool: simplifies implementation by dropping platform-specific code 2025-11-20 18:18:25 +08:00
Steve Gilissen ed6abfd626 Logo (Builtin): adds OS/2 Warp logo (#2062)
* Add OS2 Warp logo

* Update ASCII art in os2warp.txt

* Refactor OS/2 Warp logo names for consistency

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-11-19 20:16:19 -06:00
李通洲 50388efc78 Zpool: uses runtime lookup for zpool properties to ensure portability
Fixes #2051
2025-11-19 20:26:18 +08:00
李通洲 7e8fb0fa14 Zpool: simplifies impl; exposes allocated/guid/readOnly 2025-11-19 19:27:19 +08:00
Steve Gilissen 9d70469665 Logo (Builtin): adds Amiga logo (#2061)
* Add Amiga logo

* Update ASCII art in amiga.txt

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2025-11-17 19:52:07 -06:00
李通洲 a94f0efb5b CPU: adds NUMA node detection 2025-11-18 09:30:47 +08:00
Carter Li da3a9be7c6 Packaging: update debian stuff [ci skip] 2025-11-18 09:11:54 +08:00
李通洲 cd8ab8e55a Command: Adds splitLines option 2025-11-17 11:01:32 +08:00
李通洲 1b5b32afad GPU (macOS): fixes a comment [ci skip] 2025-11-17 08:59:39 +08:00
Carter Li 917f784438 Merge pull request #2060 from fastfetch-cli/dev
Release: v2.55.1
2025-11-16 09:56:42 +08:00
李通洲 c2d5d5cca1 Release: v2.55.1 2025-11-16 09:29:24 +08:00
Carter Li b435738c34 CMake (macOS): corrects rpath addition for dlopen builds 2025-11-16 09:15:11 +08:00
apocelipes 934905e63a GPU: silence unused parameter warnings 2025-11-16 08:28:28 +08:00
apocelipes d15bb8a232 Display: fix an uninitialized FFDisplayOptions field 2025-11-15 22:34:33 +08:00
李通洲 c22bca1f87 Processing (Linux): blocks SIGCHLD and simplify poll error handling
Partially reverts e9ab67b380

Fixes #2056 & unfixes #1418
2025-11-15 19:34:13 +08:00
Carter Li e04cc9c9d5 Packaging: update debian changelog [ci skip] 2025-11-12 09:24:50 +08:00
Carter Li 7a2bf4e0ac Merge pull request #2049 from fastfetch-cli/dev
Releases: v2.55.0
2025-11-11 18:51:24 +08:00
李通洲 662f5127fa Shell: makes fish shell version detection more robust (v2) 2025-11-11 15:38:53 +08:00
李通洲 fb9117553b Shell: makes fish shell version detection more robust 2025-11-11 15:19:40 +08:00
李通洲 9f24e4363f Media (Linux): handles invalid percent-encoding in cover metadata 2025-11-11 15:16:12 +08:00
李通洲 084f9294f2 Media: updates naming convention from mediacover to media-cover for media cover logo sources 2025-11-11 14:42:55 +08:00
李通洲 a9f1f9a647 Doc: update changelog 2025-11-11 14:31:23 +08:00
李通洲 0aed89036b Chore: silenses some compiler warnings 2025-11-11 14:24:32 +08:00
李通洲 df15bbc5f2 Media: moves media cover cleanup logic to media detection 2025-11-11 14:00:43 +08:00
李通洲 e6b0bd3817 Media: fixes album cover serialization in JSON result 2025-11-11 13:58:45 +08:00
李通洲 11cd7153a2 Logo (Builtin): tidy [ci skip] 2025-11-11 13:53:34 +08:00
李通洲 f8bc1cb605 Releases: v2.55.0 2025-11-11 11:01:54 +08:00
李通洲 8b8eb6f290 TerminalShell (Linux): ignores run-parts
Ref: #2048
2025-11-11 10:10:47 +08:00
李通洲 056fe21ee2 Examples/25: removes parallel: true since we had it enabled by default [ci skip] 2025-11-11 10:07:58 +08:00
Carter Li cb0a88df22 [Command] enables parallel command execution by default 2025-11-11 09:28:44 +08:00
Carter Li 298c0431c3 Examples: improves clang version extraction method in 25.jsonc [ci skip] 2025-11-11 09:00:01 +08:00
Carter Li 448de9afa5 Command: makes compiler happy 2025-11-10 22:48:22 +08:00
Carter Li 9401a4b9ad Processing: adds a missing header 2025-11-10 18:09:19 +08:00
李通洲 ea92ad3785 Command: adds parallel and useStdErr options
Fixes #2045
2025-11-10 16:34:23 +08:00
李通洲 7aaad5e03e Processing: refactors process execution to separate spawning and reading 2025-11-10 16:34:23 +08:00
李通洲 645bf5c839 Disk: moves disk hiding by folder and FS type to detection code
Don't probe if hided

Fixes #2043
2025-11-06 13:39:30 +08:00
李通洲 7638e0e4a1 CMake: improves wordexp/glob detection and messaging 2025-11-05 10:21:05 +08:00
李通洲 302b8a7b1b CommandLine: adds dynamic output refresh support with interval option 2025-11-05 10:21:05 +08:00
李通洲 dbb9d8a660 Display: adds missing #include 2025-11-05 09:59:27 +08:00
李通洲 c51a164042 PublicIP / Weather: cleans up states after detection 2025-11-05 09:56:23 +08:00
李通洲 6b8a62da6e Linux (Host): improves Mac family detection by using prefix match 2025-11-04 09:12:35 +08:00
李通洲 578ca23e75 GPU (Hurd): adds support (requires libpciaccess) 2025-11-01 09:57:52 +00:00
李通洲 2e73a90835 GPU (OpenBSD): adds native GPU detection support 2025-11-01 17:23:59 +08:00
李通洲 cc6273fecc GPU (NetBSD): don't use system function name 2025-11-01 17:19:25 +08:00
李通洲 e63b777916 GPU: handles missing GPU type as null in JSON output 2025-11-01 16:44:36 +08:00
李通洲 907567f511 GPU (NetBSD): improves performance; drops libpciaccess dependency 2025-11-01 16:42:19 +08:00
李通洲 e0fd7308be Logo (Builtin): add Vincent OS
Fixes #2030
2025-10-31 11:04:18 +08:00
李通洲 b761034aae OS (Linux): prints debian version on Raspbery Pi OS
Fixes #2032
2025-10-31 11:04:18 +08:00
李通洲 04d6b1453d GPU (NetBSD): adds native GPU detection support 2025-10-31 11:04:18 +08:00
Carter Li 11d1754686 GPU: skips auxiliary display controllers
Fixes #2034
2025-10-30 14:46:30 +08:00
Carter Li 25e84decff GPU: unifies non-pci GPU device ID formatting across platforms 2025-10-30 11:01:06 +08:00
Carter Li 7d665b203f GPU: unifies GPU PCI device ID calculation logic across different platforms 2025-10-30 10:26:35 +08:00
李通洲 ac62442798 CMake: refines yyjson config and Apple rpath handling
Fixes #1998
2025-10-28 09:30:46 +08:00
李通洲 758f96de60 Icons (Windows): hides module when no icons are found
Fixes #2023
2025-10-24 14:46:09 +08:00
李通洲 db623b9c86 CI (Linux): adds Debian i386 packaging and verify deb package in CI
Fixes #2019
2025-10-22 17:45:50 +08:00
李通洲 c353bf3c96 Logo: adds automatic cleanup of temporary media cover files 2025-10-22 13:51:36 +08:00
李通洲 338b552499 Logo: adds support for using media cover as logo source 2025-10-21 15:05:25 +08:00
李通洲 5d73cc1232 Media: save media cover to temp file 2025-10-21 14:36:35 +08:00
李通洲 faddef7a3b Media (Windows): adds media cover extraction 2025-10-21 14:01:26 +08:00
Carter Li 2d9bbc8049 Media (Linux): adds media cover extraction 2025-10-21 09:44:36 +08:00
李通洲 64afaa07e7 Media (macOS): adds media cover extraction 2025-10-21 09:44:36 +08:00
李通洲 5cf3ab23b0 Utils (macOS): adds ffCfDictGetDataAsString 2025-10-21 08:49:34 +08:00
李通洲 3fe926d3a0 Doc: adds code-signing credit and privacy statement in README [ci skip]
As required by signpath
2025-10-20 23:43:32 +08:00
李通洲 f71bb682de Logo (Builtin): adds MacaroniOS
Fixes #2018
2025-10-20 15:12:11 +08:00
Carter Li 904f860f3c Packaging: builds for all Ubuntu versions [ci skip] 2025-10-20 14:58:23 +08:00
Carter Li 8f15c9604c Merge remote-tracking branch 'origin/master' into dev 2025-10-20 14:57:59 +08:00
李通洲 ff28f5807b Shell: supports brush version detection 2025-10-19 10:25:28 +08:00
李通洲 6a317366d7 Shell: fix fish version detection when LC_ALL is set
Fixes #2014
2025-10-19 10:25:28 +08:00
李通洲 dc3e4a2d18 JsonSchema: fixes copy-paste errors [ci skip]
Fixes #2013
2025-10-18 18:38:39 +08:00
126 changed files with 2530 additions and 808 deletions
+4 -2
View File
@@ -190,11 +190,14 @@ jobs:
run: cmake --version
- name: configure project
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_DIRECTX_HEADERS=Off .
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_DIRECTX_HEADERS=Off .
- name: build project
run: cmake --build . --target package --verbose -j4
- name: check deb package
run: dpkg -I fastfetch-*.deb
- name: list features
run: ./fastfetch --list-features
@@ -443,7 +446,6 @@ jobs:
usesh: true
prepare: |
uname -a
pkg install pkg:/package/pkg
pkg update --accept
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick
+111 -1
View File
@@ -1,6 +1,116 @@
# 2.56.1
Features:
* Improves compatibility with KDE Plasma 6.5 (#2093, Display)
* Adds a `tempSensor` option to specify the sensor name used for CPU temperature detection (CPU)
* Example: `{ "type": "cpu", "tempSensor": "hwmon0" /* Use /sys/class/hwmon/hwmon0 for temperature detection */ }`
* Refines Memory usage detection on macOS to match Activity Monitor more closely (Memory, macOS)
* Minor optimizations
Bugfixes:
* Fixes cache line size detection (CPU, macOS)
Logos:
* Removes Opak
* Updates GXDE
# 2.56.0
Features:
* Enhances config file loading. `--config` and `-c` with relative path now also searches paths defined in `fastfetch --list-config-paths` (typically `~/.config/fastfetch/`)
* This allows users to use `fastfetch -c my-config` without needing to specify the full path.
* Adds NUMA node count detection (CPU)
* Exposed via `{numa-nodes}` in custom format
* Supported on Linux, FreeBSD and Windows
* Supports the newest Alacritty config format (#2070, TerminalFont)
* Detects driver specific info for Zhaoxin GPUs (GPU, Linux)
* Detects Android OEM UI for certain OSes (DE, Android)
* Improves users detection on Linux (#2064, Users, Linux)
* Adds systemd fallback when utmp is unavailable
* Fixes resource leaks
* Always reports the newest session info
* Adds kiss package manager support (#2072, Packages, Linux)
* Reports `sshd` if `$SSH_TTY` is not available (Terminal)
* Zpool module rewrite (#2051, Zpool)
* Adds new Zpool properties: allocated, guid, readOnly
* Zpool module now uses runtime lookup for properties to ensure portability
* Adds NetBSD (requires `sudo`) and macOS support
* Adds `splitLines` option for Command module, which splits the output into sub modules, each containing one line of the output (Command)
```
* Command output:
Line 1
Line 2
Line 3
* Old behavior:
Command: Line 1
Line 2
Line 3
* With `"splitLines": true`:
Command 1: Line 1
Command 2: Line 2
Command 3: Line 3
```
Bugfixes:
* Fixes {m,o}ksh version detection on Linux (Shell)
* Fixes Alacritty config parsing for TOML format (#2070, TerminalFont)
* Improves builtin logo printing for piping and buffering (#2065, Logo)
* Uses absolute path when detecting shell and terminal version if available (#2067, TerminalShell)
Logos:
* Updates Codex Linux logo (#2071)
* Adds OS/2 Warp logo (#2062)
* Adds Amiga logo (#2061)
# 2.55.1
Bugfixes:
* Fix parallel command execution breaks randomly (#2056 / #2058, Command)
* Regression from v2.55.0
* Fix `dylib` searching path on macOS (macOS)
* Regression from v2.55.0
* Fix an uninitialized field (#2057, Display)
# 2.55.0
Changes:
* Commands are now executed in parallel by default to improve performance (#2045, Command)
* This behavior can be disabled in the config file with `"parallel": false` if it causes problems with certain scripts
* Folder/filesystem hiding is moved to the detection stage; hidden entries are no longer probed, improving performance (#2043, Disk)
Features:
* Adds `command.parallel` and `command.useStdErr` config options (Command)
* `parallel`: set to `false` to disable parallel execution (see Changes above)
* `useStdErr`: set to `true` to use stderr output instead of stdout
* Adds the command-line flag `--dynamic-interval <interval-in-ms>` to enable dynamic output auto-refresh (#2041)
* Due to internal limitations, some modules do not support dynamic updates (notably Display and Media)
* Adds support for using the current playing media's cover art as a logo source (Media / Logo)
* Usage: `"logo": { "type": "<image-protocol>", "source": "media-cover" }` in JSON config; or `--<image-protocol> media-cover` in command line
* Supports local sources only
* Adds native GPU detection support on OpenBSD and NetBSD (instead of depending on `libpciaccess`) (GPU)
* No functional changes
* Root privileges are required to access PCI config space on OpenBSD (as always)
* Adds GPU detection support on GNU/Hurd (GPU)
* Requires building with `libpciaccess`
* Shows Debian point release on Raspberry Pi OS (#2032, OS, Linux)
* Adds `Brush` shell version detection (Shell)
* Improves Mac family detection via prefix matching (Host)
Bugfixes:
* Ignores `run-parts` during terminal/shell detection (#2048, Terminal / Shell, Linux)
* Fixes fish version detection when `LC_ALL` is set (#2014, Shell, Linux)
* Hides the module when no desktop icons are found (#2023, Icons, Windows)
* Skips auxiliary display controllers to prevent the module from reporting duplicate entries (#2034, GPU, Linux)
* Refines Apple rpath handling; fixes building for the Homebrew version on macOS (#1998, CMake)
Logos:
* Adds Vincent OS and MacaroniOS
# 2.54.0
Windows binaries in Release page are now signed using SignPath.
Windows binaries in Release page are now signed by SignPath.
Changes:
* Moves macOS and Windows design language detection from the DE module to the Theme module
+30 -43
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.54.0
VERSION 2.56.1
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -92,8 +92,7 @@ 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 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" OFF)
cmake_dependent_option(ENABLE_PCIACCESS "Enable libpciaccess" ON "GNU" OFF)
option(ENABLE_ZLIB "Enable zlib" ON)
option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embedded) yyjson library" OFF)
@@ -107,6 +106,7 @@ option(INSTALL_LICENSE "Install license into /usr/share/licenses" ON)
option(ENABLE_EMBEDDED_PCIIDS "Embed pci.ids into fastfetch, requires `python`" OFF)
option(ENABLE_EMBEDDED_AMDGPUIDS "Embed amdgpu.ids into fastfetch, requires `python`" OFF)
option(ENABLE_WORDEXP "Enable using of wordexp(3) if available, instead of glob(3)" ON)
option(ENABLE_LIBZFS "Enable libzfs" ON)
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
set(BINARY_LINK_TYPE dlopen CACHE STRING "How to link fastfetch")
@@ -389,6 +389,7 @@ set(LIBFASTFETCH_SRC
src/detection/chassis/chassis.c
src/detection/cpu/cpu.c
src/detection/cpuusage/cpuusage.c
src/detection/command/command.c
src/detection/disk/disk.c
src/detection/diskio/diskio.c
src/detection/displayserver/displayserver.c
@@ -409,6 +410,7 @@ set(LIBFASTFETCH_SRC
src/detection/version/version.c
src/detection/vulkan/vulkan.c
src/detection/weather/weather.c
src/detection/zpool/zpool.c
src/logo/builtin.c
src/logo/image/im6.c
src/logo/image/im7.c
@@ -581,7 +583,6 @@ if(LINUX)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_linux.c
src/detection/zpool/zpool_linux.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -655,7 +656,6 @@ elseif(ANDROID)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_android.c
src/detection/zpool/zpool_nosupport.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -738,7 +738,6 @@ elseif(FreeBSD)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_linux.c
src/detection/zpool/zpool_linux.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -794,7 +793,7 @@ elseif(NetBSD)
src/detection/displayserver/linux/xcb.c
src/detection/displayserver/linux/xlib.c
src/detection/font/font_linux.c
src/detection/gpu/gpu_general.c
src/detection/gpu/gpu_nbsd.c
src/detection/gpu/gpu_pci.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_nbsd.c
@@ -832,7 +831,6 @@ elseif(NetBSD)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_linux.c
src/detection/zpool/zpool_linux.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -878,7 +876,7 @@ elseif(OpenBSD)
src/detection/displayserver/linux/xlib.c
src/detection/font/font_linux.c
src/detection/gpu/gpu_pci.c
src/detection/gpu/gpu_general.c
src/detection/gpu/gpu_obsd.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_obsd.c
src/detection/lm/lm_nosupport.c
@@ -915,7 +913,6 @@ elseif(OpenBSD)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_linux.c
src/detection/zpool/zpool_nosupport.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
src/util/smbiosHelper.c
@@ -985,7 +982,6 @@ elseif(APPLE)
src/detection/de/de_nosupport.c
src/detection/wmtheme/wmtheme_apple.m
src/detection/camera/camera_apple.m
src/detection/zpool/zpool_nosupport.c
src/util/apple/cf_helpers.c
src/util/apple/osascript.m
src/util/apple/smc_temps.c
@@ -1055,7 +1051,6 @@ elseif(WIN32)
src/detection/de/de_nosupport.c
src/detection/wmtheme/wmtheme_windows.c
src/detection/camera/camera_windows.cpp
src/detection/zpool/zpool_nosupport.c
src/util/windows/getline.c
src/util/windows/com.cpp
src/util/windows/registry.c
@@ -1144,7 +1139,6 @@ elseif(SunOS)
src/detection/de/de_linux.c
src/detection/wmtheme/wmtheme_linux.c
src/detection/camera/camera_nosupport.c
src/detection/zpool/zpool_linux.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
)
@@ -1213,7 +1207,6 @@ elseif(Haiku)
src/detection/de/de_nosupport.c
src/detection/wmtheme/wmtheme_nosupport.c
src/detection/camera/camera_nosupport.c
src/detection/zpool/zpool_nosupport.c
src/util/platform/FFPlatform_unix.c
src/util/binary_linux.c
src/util/haiku/version.cpp
@@ -1254,7 +1247,7 @@ elseif(GNU)
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_general.c
src/detection/gpu/gpu_pci.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_nosupport.c
@@ -1293,7 +1286,6 @@ elseif(GNU)
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
)
@@ -1340,15 +1332,15 @@ add_library(libfastfetch OBJECT
)
if(yyjson_FOUND)
target_compile_definitions(libfastfetch PUBLIC FF_USE_SYSTEM_YYJSON)
target_compile_definitions(libfastfetch PUBLIC FF_USE_SYSTEM_YYJSON=1)
target_link_libraries(libfastfetch PUBLIC yyjson::yyjson)
# `target_link_libraries(yyjson::yyjson)` sets rpath implicitly
else()
# Used for dlopen finding dylibs installed by homebrew
# `/opt/homebrew/lib` is not on in dlopen search path by default
if(APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib")
endif()
endif()
# Used for dlopen finding dylibs installed by homebrew
# `/opt/homebrew/lib` is not on in dlopen search path by default
if(APPLE AND BINARY_LINK_TYPE STREQUAL "dlopen")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib")
endif()
if(ANDROID)
@@ -1610,24 +1602,6 @@ ff_lib_enable(PCIACCESS
"pciaccess"
"pciaccess"
)
# The system <libzfs.h> is only usable on SunOS. We provide our local copy of it so it's always available.
if(ENABLE_LIBZFS)
if(BINARY_LINK_TYPE STREQUAL "dlopen")
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBZFS=1)
else()
set(CMAKE_REQUIRED_LIBRARIES_BACKUP ${CMAKE_REQUIRED_LIBRARIES})
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} zfs)
check_function_exists("libzfs_init" LIBZFS_FOUND)
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES_BACKUP})
if(NOT LIBZFS_FOUND)
message(STATUS "Library: missing: LIBZFS")
else()
message(STATUS "Library: found LIBZFS")
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBZFS=1)
target_link_libraries(libfastfetch PRIVATE zfs)
endif()
endif()
endif()
if(ENABLE_THREADS)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS=1)
@@ -1642,6 +1616,15 @@ endif()
if(ENABLE_EMBEDDED_AMDGPUIDS)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_EMBEDDED_AMDGPUIDS=1)
endif()
if(ENABLE_LIBZFS)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBZFS=1)
if(NOT BINARY_LINK_TYPE STREQUAL "dlopen")
target_link_libraries(libfastfetch
PRIVATE "zfs"
)
endif()
endif()
if(LINUX)
target_link_libraries(libfastfetch
@@ -1732,8 +1715,6 @@ elseif(SunOS)
PRIVATE "socket"
PRIVATE "kstat"
PRIVATE "proc"
PRIVATE "zfs"
PRIVATE "nvpair"
PRIVATE "devinfo"
)
elseif(GNU)
@@ -1823,8 +1804,14 @@ if(NOT WIN32)
CHECK_INCLUDE_FILE("wordexp.h" HAVE_WORDEXP)
if(HAVE_WORDEXP)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_WORDEXP=1)
message(STATUS "wordexp.h found, wordexp support enabled")
else()
set(ENABLE_WORDEXP OFF)
endif()
endif()
if(NOT ENABLE_WORDEXP)
message(STATUS "wordexp.h not found or disabled, glob used instead")
endif()
if(ENABLE_THREADS AND CMAKE_USE_PTHREADS_INIT)
CHECK_INCLUDE_FILE("pthread_np.h" HAVE_PTHREAD_NP)
if(HAVE_PTHREAD_NP)
+8 -2
View File
@@ -13,7 +13,7 @@
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/fastfetch-cli/fastfetch)
[![中文README](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-README-red)](README-cn.md)
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and SunOS.
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and illumos (SunOS).
<img src="screenshots/example1.png" width="49%" align="left" />
<img src="https://upload.wikimedia.org/wikipedia/commons/2/24/Transparent_Square_Tiles_Texture.png" width="49%" height="16px" align="left" />
@@ -37,7 +37,8 @@ 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>
* Debian: `apt install fastfetch` (for Debian 13 or newer)
* Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (Ubuntu 22.04 or newer; latest version)
* Debian / Ubuntu: `apt install fastfetch` (Debian 13 or newer; Ubuntu 25.04 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`
* Fedora: `dnf install fastfetch`
@@ -322,6 +323,11 @@ If you find Fastfetch useful, please consider donating.
* Current maintainer: [@CarterLi](https://paypal.me/zhangsongcui)
* Original author: [@LinusDierheimer](https://github.com/sponsors/LinusDierheimer)
## Code signing
* Free code signing provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
* This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it
## Star History
Give us a star to show your support!
+37 -1
View File
@@ -1,3 +1,40 @@
fastfetch (2.56.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.56.1
-- Carter Li <zhangsongcui@live.cn> Thu, 18 Dec 2025 14:57:36 +0800
fastfetch (2.56.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.56.0
-- Carter Li <zhangsongcui@live.cn> Mon, 08 Dec 2025 09:21:58 +0800
fastfetch (2.55.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.55.1
-- Carter Li <zhangsongcui@live.cn> Mon, 17 Nov 2025 10:15:44 +0800
fastfetch (2.55.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.55.0
-- Carter Li <zhangsongcui@live.cn> Wed, 12 Nov 2025 09:15:24 +0800
fastfetch (2.54.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Fix building on plucky
-- Carter Li <zhangsongcui@live.cn> Mon, 20 Oct 2025 14:27:02 +0800
fastfetch (2.54.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.54.0
* Test independent changelog entries for different Ubuntu releases
-- Carter Li <zhangsongcui@live.cn> Mon, 20 Oct 2025 10:47:02 +0800
fastfetch (2.53.0) jammy; urgency=medium
* Update to 2.53.0
@@ -435,4 +472,3 @@ fastfetch (2.7.1) jammy; urgency=medium
* Initial release.
-- Carter Li <zhangsongcui@live.cn> Tue, 06 Feb 2024 15:01:11 +0800
+1 -1
View File
@@ -2,7 +2,7 @@ Source: fastfetch
Section: universe/utils
Priority: optional
Maintainer: Carter Li <zhangsongcui@live.cn>
Build-Depends: libelf-dev, libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, ocl-icd-opencl-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build
Build-Depends: libelf-dev, libvulkan-dev, libwayland-dev, libxrandr-dev, libxcb-randr0-dev, libdconf-dev, libdbus-1-dev, libmagickcore-dev, libsqlite3-dev, librpm-dev, libegl-dev, libglx-dev, ocl-icd-opencl-dev, libpulse-dev, libdrm-dev, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build, python3-setuptools
Standards-Version: 4.0.0
Homepage: https://github.com/fastfetch-cli/fastfetch
-1
View File
@@ -1 +0,0 @@
fastfetch_2.53.0_source.buildinfo universe/utils optional
+74 -2
View File
@@ -1,4 +1,76 @@
#!/usr/bin/env bash
# WARNING: DO NOT RUN THIS SCRIPT UNLESS YOU KNOW WHAT YOU ARE DOING.
debuild -S -i -I
dput ppa:zhangsongcui3371/fastfetch ~/fastfetch_*.changes
set -euo pipefail
command -v debuild >/dev/null 2>&1 || { echo "debuild not found. Install devscripts." >&2; exit 1; }
command -v dput >/dev/null 2>&1 || { echo "dput not found." >&2; exit 1; }
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." >/dev/null 2>&1 && pwd)"
OUT_DIR="$(dirname "$ROOT_DIR")"
PPA="ppa:zhangsongcui3371/fastfetch"
CODENAMES=( jammy noble plucky questing resolute )
DRY_RUN=0
if [[ -d "$OUT_DIR/build" ]]; then
echo "Output directory exists: $OUT_DIR/build" >&2
echo "Please move or delete it before proceeding." >&2
exit 1
fi
TEMPLATE="$ROOT_DIR/debian/changelog.tpl"
CHANGELOG_DEBIAN="$ROOT_DIR/debian/changelog"
if [[ ! -f "$TEMPLATE" ]]; then
echo "Template not found: $TEMPLATE" >&2
exit 1
fi
if ! grep -q '#UBUNTU_CODENAME#' "$TEMPLATE"; then
echo "Template missing placeholder: #UBUNTU_CODENAME#" >&2
exit 1
fi
echo "IMPORTANT: Before proceeding, please ensure that 'debian/changelog.tpl' is up-to-date"
echo " with the correct version number, release notes, and maintainer information."
echo
echo " The template must contain a placeholder like '#UBUNTU_CODENAME#' for the codename."
echo
read -p "Have you reviewed and updated 'debian/changelog.tpl'? (y/N): " -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
cleanup() {
rm -f "$CHANGELOG_DEBIAN"
rm -f "$ROOT_DIR/debian/files"
}
trap cleanup EXIT
shopt -s nullglob
for codename in "${CODENAMES[@]}"; do
echo "==> Publishing distro: $codename"
sed "s/#UBUNTU_CODENAME#/${codename}/g" "$TEMPLATE" > "$CHANGELOG_DEBIAN"
( cd "$ROOT_DIR" && debuild -S -i -I )
changes=( "$OUT_DIR"/fastfetch_*~${codename}_source.changes )
if [[ ${#changes[@]} -ne 1 ]]; then
echo "Unable to uniquely identify .changes for '$codename' in: $OUT_DIR" >&2
printf 'Found:\n'; printf ' %s\n' "${changes[@]}" >&2 || true
exit 1
fi
if [[ $DRY_RUN -eq 1 ]]; then
echo "[DRY-RUN] dput \"$PPA\" \"${changes[0]}\""
else
dput "$PPA" "${changes[0]}"
fi
rm -f "$OUT_DIR"/fastfetch_*~${codename}_source.{changes,dsc,tar.*}
echo "<== Done: $codename"
done
+21 -3
View File
@@ -1008,7 +1008,7 @@
},
"type": {
"type": "string",
"description": "Set whether to show icon before string keys",
"description": "Set whether to show builtin icon before string keys",
"oneOf": [
{
"const": "none",
@@ -1020,7 +1020,7 @@
},
{
"const": "icon",
"description": "Show icon keys (requires newest nerd font)"
"description": "Show builtin icon (requires newest nerd font)"
},
{
"const": "both",
@@ -1047,7 +1047,6 @@
"description": "Show both icon and string with 4 spaces between them"
}
],
],
"default": "string"
},
"paddingLeft": {
@@ -1881,6 +1880,10 @@
"temp": {
"$ref": "#/$defs/temperature"
},
"tempSensor": {
"description": "Set the temperature sensor to use for CPU temperature detection\n* Linux: `hwmon` or `thermal` path name (eg. `hwmon0`, `thermal_zone0)`\n* macOS: SMC sensor key (eg. `Tp01`)\n* Windows: thermal zone key (eg. `\\_TZ.CPUZ`)\n* FreeBSD: sysctl key (eg. `dev.cpu.0.temperature`)\n* NetBSD: sysmon sensor key (eg. `coretemp0`)",
"type": "string"
},
"showPeCoreCount": {
"description": "Detect and display CPU frequency of different core types (eg. Pcore and Ecore) if supported",
"type": "boolean",
@@ -2102,6 +2105,21 @@
"description": "Set the command text to be executed",
"type": "string"
},
"useStdErr": {
"description": "Set if stderr should be used instead of stdout for command output",
"type": "boolean",
"default": false
},
"parallel": {
"description": "Set if the command should be executed in parallel with other commands\nImprove performance when using multiple commands, but may cause issues with some commands",
"type": "boolean",
"default": true
},
"splitLines": {
"description": "Set if the command output should be split into multiple lines",
"type": "boolean",
"default": false
},
"key": {
"$ref": "#/$defs/key"
},
+1 -1
View File
@@ -183,7 +183,7 @@
},
"keyIcon": "",
"key": "│{#red}│ {icon} Clang │{$4}│{#keys}│{$2}",
"text": "clang --version | head -1 | awk '{print $NF}'",
"text": "clang --version | sed -n 's/.*version \\([0-9][0-9.]*\\).*/\\1/p'",
"format": "clang {}"
},
{
+17
View File
@@ -1,4 +1,6 @@
#include "fastfetch.h"
#include "util/FFlist.h"
#include "util/FFstrbuf.h"
#include "common/font.h"
#include <string.h>
@@ -192,6 +194,21 @@ void ffFontInitValues(FFfont* font, const char* name, const char* size)
fontInitPretty(font);
}
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style)
{
ffFontInit(font);
if (name) ffStrbufInitMove(&font->name, name);
if (size) ffStrbufInitMove(&font->size, size);
if (style)
{
FFstrbuf* styleBuf = FF_LIST_ADD(FFstrbuf, font->styles);
ffStrbufInitMove(styleBuf, style);
}
fontInitPretty(font);
}
void ffFontInitWithSpace(FFfont* font, const char* rawName)
{
const char* pspace = strrchr(rawName, ' ');
+1
View File
@@ -15,6 +15,7 @@ void ffFontInit(FFfont* font);
void ffFontInitQt(FFfont* font, const char* data);
void ffFontInitPango(FFfont* font, const char* data);
void ffFontInitValues(FFfont* font, const char* name, const char* size);
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style);
void ffFontInitWithSpace(FFfont* font, const char* rawName);
void ffFontDestroy(FFfont* font);
+15 -9
View File
@@ -30,6 +30,7 @@ static void initState(FFstate* state)
ffPlatformInit(&state->platform);
state->configDoc = NULL;
state->resultDoc = NULL;
state->dynamicInterval = 0;
{
// don't enable bright color if the terminal is in light mode
@@ -48,7 +49,7 @@ static void defaultConfig(void)
void ffInitInstance(void)
{
#ifdef WIN32
#ifdef _WIN32
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendat>
setlocale(LC_ALL, ".UTF8");
#else
@@ -74,6 +75,9 @@ static void resetConsole(void)
#if defined(_WIN32)
fflush(stdout);
#endif
if(instance.state.dynamicInterval > 0)
fputs("\033[?1049l", stdout); // Disable alternate buffer
}
#ifdef _WIN32
@@ -88,10 +92,6 @@ static void exitSignalHandler(FF_MAYBE_UNUSED int signal)
resetConsole();
exit(0);
}
static void chldSignalHandler(FF_MAYBE_UNUSED int signal)
{
// empty; used to interrupt the poll and read syscalls
}
#endif
void ffStart(void)
@@ -117,7 +117,10 @@ void ffStart(void)
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGQUIT, &action, NULL);
sigaction(SIGCHLD, &(struct sigaction) { .sa_handler = chldSignalHandler }, NULL);
sigset_t newmask;
sigemptyset(&newmask);
sigaddset(&newmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &newmask, NULL);
#endif
//reset everything to default before we start printing
@@ -130,14 +133,17 @@ void ffStart(void)
if(ffDisableLinewrap)
fputs("\033[?7l", stdout);
if(instance.state.dynamicInterval > 0)
{
fputs("\033[?1049h\033[H", stdout); // Enable alternate buffer
fflush(stdout);
}
ffLogoPrint();
}
void ffFinish(void)
{
if(instance.config.logo.printRemaining)
ffLogoPrintRemaining();
resetConsole();
}
+23 -6
View File
@@ -83,6 +83,13 @@ bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer);
FF_C_NONNULL(2, 3)
bool ffAppendFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer);
FF_C_NONNULL(2)
static inline bool ffReadFDBuffer(FFNativeFD fd, FFstrbuf* buffer)
{
ffStrbufClear(buffer);
return ffAppendFDBuffer(fd, buffer);
}
FF_C_NONNULL(1, 2)
static inline bool ffReadFileBuffer(const char* fileName, FFstrbuf* buffer)
{
@@ -171,19 +178,27 @@ static inline void ffUnsuppressIO(bool* suppressed)
void ffListFilesRecursively(const char* path, bool pretty);
static inline bool ffIsValidNativeFD(FFNativeFD fd)
{
#ifndef _WIN32
return fd >= 0;
#else
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
return fd != INVALID_HANDLE_VALUE && fd != NULL;
#endif
}
FF_C_NONNULL(1)
static inline bool wrapClose(FFNativeFD* pfd)
{
assert(pfd);
#ifndef WIN32
if (*pfd < 0)
return false;
if (!ffIsValidNativeFD(*pfd))
return false;
#ifndef _WIN32
close(*pfd);
#else
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
if (*pfd == NULL || *pfd == INVALID_HANDLE_VALUE)
return false;
CloseHandle(*pfd);
#endif
@@ -241,8 +256,10 @@ static inline bool ffSearchUserConfigFile(const FFlist* configDirs, const char*
}
FFNativeFD ffGetNullFD(void);
bool ffRemoveFile(const char* fileName);
#ifdef _WIN32
// Only O_RDONLY is supported
HANDLE openat(HANDLE dfd, const char* fileName, bool directory);
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory);
#endif
+5 -1
View File
@@ -16,7 +16,6 @@
#if FF_HAVE_WORDEXP
#include <wordexp.h>
#else
#warning "<wordexp.h> is not available, use glob(3) instead"
#include <glob.h>
#endif
@@ -372,3 +371,8 @@ FFNativeFD ffGetNullFD(void)
hNullFile = open("/dev/null", O_WRONLY | O_CLOEXEC);
return hNullFile;
}
bool ffRemoveFile(const char* fileName)
{
return unlink(fileName) == 0;
}
+40 -18
View File
@@ -3,8 +3,8 @@
#include "util/stringUtils.h"
#include <windows.h>
#include "util/windows/nt.h"
#include <ntstatus.h>
#include <winternl.h>
static void createSubfolders(const char* fileName)
{
@@ -102,31 +102,48 @@ bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer)
return ffAppendFDBuffer(handle, buffer);
}
HANDLE openat(HANDLE dfd, const char* fileName, bool directory)
HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory)
{
NTSTATUS ret;
UNICODE_STRING fileNameW;
ret = RtlAnsiStringToUnicodeString(&fileNameW, &(ANSI_STRING) {
.Length = (USHORT) strlen(fileName),
.Buffer = (PCHAR) fileName
}, TRUE);
if (!NT_SUCCESS(ret)) return INVALID_HANDLE_VALUE;
assert(fileNameLen <= 0x7FFF);
FF_AUTO_CLOSE_FD HANDLE hFile;
HANDLE hFile;
IO_STATUS_BLOCK iosb = {};
ret = NtOpenFile(&hFile, FILE_READ_DATA | SYNCHRONIZE, &(OBJECT_ATTRIBUTES) {
.Length = sizeof(OBJECT_ATTRIBUTES),
.RootDirectory = dfd,
.ObjectName = &fileNameW,
}, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | (directory ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE));
RtlFreeUnicodeString(&fileNameW);
if(!NT_SUCCESS(ret) || iosb.Information != FILE_OPENED)
if(!NT_SUCCESS(NtOpenFile(&hFile,
(directory ? FILE_LIST_DIRECTORY | FILE_TRAVERSE : FILE_READ_DATA | FILE_READ_EA) | FILE_READ_ATTRIBUTES | SYNCHRONIZE, &(OBJECT_ATTRIBUTES) {
.Length = sizeof(OBJECT_ATTRIBUTES),
.RootDirectory = dfd,
.ObjectName = &(UNICODE_STRING) {
.Buffer = (PWSTR) fileName,
.Length = fileNameLen * (USHORT) sizeof(wchar_t),
.MaximumLength = (fileNameLen + 1) * (USHORT) sizeof(wchar_t),
},
.Attributes = OBJ_CASE_INSENSITIVE,
},
&iosb,
FILE_SHARE_READ | (directory ? FILE_SHARE_WRITE | FILE_SHARE_DELETE : 0),
FILE_SYNCHRONOUS_IO_NONALERT | (directory ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE)
)))
return INVALID_HANDLE_VALUE;
return hFile;
}
HANDLE openat(HANDLE dfd, const char* fileName, bool directory)
{
wchar_t fileNameW[MAX_PATH];
int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, fileName, -1, fileNameW, ARRAY_SIZE(fileNameW));
if (len == 0) return INVALID_HANDLE_VALUE;
// Implies `fileNameW[len] = L'\0';` and `len` includes the null terminator
for (int i = 0; i < len - 1; ++i)
{
if (fileNameW[i] == L'/')
fileNameW[i] = L'\\';
}
return openatW(dfd, fileNameW, (uint16_t)(len - 1), directory);
}
bool ffAppendFileBufferRelative(HANDLE dfd, const char* fileName, FFstrbuf* buffer)
{
HANDLE FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, false);
@@ -371,3 +388,8 @@ FFNativeFD ffGetNullFD(void)
});
return hNullFile;
}
bool ffRemoveFile(const char* fileName)
{
return DeleteFileA(fileName) != FALSE;
}
+10
View File
@@ -158,6 +158,16 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module)
ffPrepareCPUUsage();
break;
}
case 'c': case 'C': {
if (ffStrEqualsIgnCase(type, FF_COMMAND_MODULE_NAME))
{
__attribute__((__cleanup__(ffDestroyCommandOptions))) FFCommandOptions options;
ffInitCommandOptions(&options);
if (module) ffCommandModuleInfo.parseJsonObject(&options, module);
ffPrepareCommand(&options);
}
break;
}
case 'd': case 'D': {
if (ffStrEqualsIgnCase(type, FF_DISKIO_MODULE_NAME))
{
+2 -2
View File
@@ -89,7 +89,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
uint32_t minMetric = UINT32_MAX;
int routeCount = 0;
FF_MAYBE_UNUSED int routeCount = 0;
while (true)
{
@@ -299,7 +299,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
uint32_t minMetric = UINT32_MAX;
int routeCount = 0;
FF_MAYBE_UNUSED int routeCount = 0;
while (true)
{
+25 -4
View File
@@ -2,13 +2,30 @@
#include "util/FFstrbuf.h"
#include <sys/types.h>
#ifndef _WIN32
#include <sys/types.h> // pid_t
#endif
const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool useStdErr);
typedef struct FFProcessHandle {
#if _WIN32
void* pid; // HANDLE
void* pipeRead; // HANDLE
#else
pid_t pid;
int pipeRead;
#endif
} FFProcessHandle;
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle);
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer); // Destroys handle internally
static inline const char* ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[])
{
const char* error = ffProcessAppendOutput(buffer, argv, false);
FFProcessHandle handle;
const char* error = ffProcessSpawn(argv, false, &handle);
if (error) return error;
error = ffProcessReadOutput(&handle, buffer);
if (!error)
ffStrbufTrimRightSpace(buffer);
return error;
@@ -16,7 +33,11 @@ static inline const char* ffProcessAppendStdOut(FFstrbuf* buffer, char* const ar
static inline const char* ffProcessAppendStdErr(FFstrbuf* buffer, char* const argv[])
{
const char* error = ffProcessAppendOutput(buffer, argv, true);
FFProcessHandle handle;
const char* error = ffProcessSpawn(argv, true, &handle);
if (error) return error;
error = ffProcessReadOutput(&handle, buffer);
if (!error)
ffStrbufTrimRightSpace(buffer);
return error;
+28 -35
View File
@@ -58,11 +58,9 @@ static inline int ffPipe2(int* fds, int flags)
// Not thread-safe
const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool useStdErr)
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle)
{
int pipes[2];
const int timeout = instance.config.general.processingTimeout;
if(ffPipe2(pipes, O_CLOEXEC) == -1)
return "pipe() failed";
@@ -146,7 +144,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
if(childPid == 0)
{
//Child
// Child process
dup2(pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO);
dup2(nullFile, useStdErr ? STDOUT_FILENO : STDERR_FILENO);
putenv("LANG=C.UTF-8");
@@ -157,11 +155,24 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
#endif
close(pipes[1]);
outHandle->pid = childPid;
outHandle->pipeRead = pipes[0];
return NULL;
}
int FF_AUTO_CLOSE_FD childPipeFd = pipes[0];
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
{
assert(handle->pipeRead != -1);
assert(handle->pid != -1);
const int32_t timeout = instance.config.general.processingTimeout;
FF_AUTO_CLOSE_FD int childPipeFd = handle->pipeRead;
pid_t childPid = handle->pid;
handle->pipeRead = -1;
handle->pid = -1;
char str[FF_PIPE_BUFSIZ];
while(1)
for (;;)
{
if (timeout >= 0)
{
@@ -173,30 +184,13 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
waitpid(childPid, NULL, 0);
return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)";
}
else if (pollret < 0)
{
if (errno == EINTR)
{
// 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)
else if (pollret < 0 || (pollfd.revents & POLLERR))
{
kill(childPid, SIGTERM);
waitpid(childPid, NULL, 0);
return "poll(&pollfd, 1, timeout) error";
return pollret < 0
? "poll(&pollfd, 1, timeout) error: pollret < 0"
: "poll(&pollfd, 1, timeout) error: pollfd.revents & POLLERR";
}
}
@@ -211,20 +205,15 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
if (!WIFEXITED(stat_loc))
return "child process exited abnormally";
if (WEXITSTATUS(stat_loc) == 127)
return "command was not found";
return "command not found";
// We only handle 127 as an error. See `getTerminalVersionUrxvt` in `terminalshell.c`
return NULL;
}
return NULL;
}
else if (nRead < 0)
{
if (errno == EAGAIN)
return NULL;
else
break;
}
};
break;
}
return "read(childPipeFd, str, FF_PIPE_BUFSIZ) failed";
}
@@ -284,6 +273,10 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
ffStrbufEnsureFixedLengthFree(exePath, (uint32_t)length);
ffStrbufAppendNS(exePath, (uint32_t)length, buf);
}
else
{
ffStrbufSetS(exePath, filePath);
}
}
#elif defined(__APPLE__)
+63 -40
View File
@@ -45,12 +45,13 @@ static void argvToCmdline(char* const argv[], FFstrbuf* result)
}
}
const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool useStdErr)
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle)
{
int timeout = instance.config.general.processingTimeout;
const int32_t timeout = instance.config.general.processingTimeout;
wchar_t pipeName[32];
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u", GetCurrentProcessId());
static unsigned pidCounter = 0;
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u-%u", GetCurrentProcessId(), ++pidCounter);
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = CreateNamedPipeW(
pipeName,
@@ -82,52 +83,67 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
return "CreateFileW(L\"\\\\.\\pipe\\FASTFETCH-$(PID)\") failed";
PROCESS_INFORMATION piProcInfo = {};
BOOL success;
STARTUPINFOA siStartInfo = {
.cb = sizeof(siStartInfo),
.dwFlags = STARTF_USESTDHANDLES,
};
if (useStdErr)
{
STARTUPINFOA siStartInfo = {
.cb = sizeof(siStartInfo),
.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);
success = CreateProcessA(
NULL, // application name
cmdline.chars, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo // receives PROCESS_INFORMATION
);
siStartInfo.hStdOutput = ffGetNullFD();
siStartInfo.hStdError = hChildPipeWrite;
}
else
{
siStartInfo.hStdOutput = hChildPipeWrite;
siStartInfo.hStdError = ffGetNullFD();
}
FF_STRBUF_AUTO_DESTROY cmdline = ffStrbufCreate();
argvToCmdline(argv, &cmdline);
BOOL success = CreateProcessA(
NULL, // application name
cmdline.chars, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo // receives PROCESS_INFORMATION
);
CloseHandle(hChildPipeWrite);
if(!success)
{
if (GetLastError() == ERROR_FILE_NOT_FOUND)
return "command not found";
return "CreateProcessA() failed";
}
FF_AUTO_CLOSE_FD HANDLE hProcess = piProcInfo.hProcess;
FF_MAYBE_UNUSED FF_AUTO_CLOSE_FD HANDLE hThread = piProcInfo.hThread;
CloseHandle(piProcInfo.hThread); // we don't need the thread handle
outHandle->pid = piProcInfo.hProcess;
outHandle->pipeRead = hChildPipeRead;
hChildPipeRead = INVALID_HANDLE_VALUE; // ownership transferred, don't close it
return NULL;
}
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
{
assert(handle->pipeRead != INVALID_HANDLE_VALUE);
assert(handle->pid != INVALID_HANDLE_VALUE);
int32_t timeout = instance.config.general.processingTimeout;
FF_AUTO_CLOSE_FD HANDLE hProcess = handle->pid;
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = handle->pipeRead;
handle->pid = INVALID_HANDLE_VALUE;
handle->pipeRead = INVALID_HANDLE_VALUE;
char str[FF_PIPE_BUFSIZ];
DWORD nRead = 0;
OVERLAPPED overlapped = { };
OVERLAPPED overlapped = {};
// ReadFile always completes synchronously if the pipe is not created with FILE_FLAG_OVERLAPPED
do
{
@@ -164,7 +180,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
break;
case ERROR_BROKEN_PIPE:
return NULL;
goto exit;
default:
CancelIo(hChildPipeRead);
@@ -175,6 +191,13 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
ffStrbufAppendNS(buffer, nRead, str);
} while (nRead > 0);
exit:
{
DWORD exitCode = 0;
if (GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE && exitCode != 0)
return "Child process exited with an error";
}
return NULL;
}
+10 -1
View File
@@ -74,6 +74,15 @@
},
"desc": "Enable or disable JSON output",
"remark": "Shortcut for `--format json`"
},
{
"long": "dynamic-interval",
"desc": "Keep fastfetch open and update the output every <num> milliseconds",
"remark": "0 (default) to disable the behavior; don't work with --json",
"arg": {
"type": "num",
"default": 0
}
}
],
"Config": [
@@ -81,7 +90,7 @@
"short": "c",
"long": "config",
"desc": "Specify the config file or preset to load",
"remark": "The file will be searched according to the order shown by \"fastfetch --list-config-paths\". Use \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for more info",
"remark": "The file will be searched according to the order shown by \"fastfetch --list-config-paths\". Use \"-\" to read config from stdin or \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for more info",
"arg": {
"type": "config"
}
+1 -1
View File
@@ -41,7 +41,7 @@ static const char* detectWithBacklight(FFlist* result)
FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result);
ffStrbufSubstrBeforeLastC(&backlightDir, '/');
ffStrbufAppendS(&backlightDir, "/device");
ffStrbufInitA(&brightness->name, PATH_MAX + 1);
ffStrbufInitA(&brightness->name, PATH_MAX);
if(realpath(backlightDir.chars, brightness->name.chars))
{
ffStrbufRecalculateLength(&brightness->name);
+60
View File
@@ -0,0 +1,60 @@
#include "common/processing.h"
#include "util/FFstrbuf.h"
#include "detection/command/command.h"
typedef struct FFCommandResultBundle
{
FFProcessHandle handle;
const char* error;
} FFCommandResultBundle;
// FIFO, non-thread-safe list of running commands
static FFlist commandQueue = {
.elementSize = sizeof(FFCommandResultBundle),
};
static const char* spawnProcess(FFCommandOptions* options, FFProcessHandle* handle)
{
if (options->text.length == 0)
return "No command text specified";
return ffProcessSpawn(options->param.length ? (char* const[]){
options->shell.chars,
options->param.chars,
options->text.chars,
NULL
} : (char* const[]){
options->shell.chars,
options->text.chars,
NULL
}, options->useStdErr, handle);
}
bool ffPrepareCommand(FFCommandOptions* options)
{
if (!options->parallel) return false;
FFCommandResultBundle* bundle = ffListAdd(&commandQueue);
bundle->error = spawnProcess(options, &bundle->handle);
return true;
}
const char* ffDetectCommand(FFCommandOptions* options, FFstrbuf* result)
{
FFCommandResultBundle bundle = {};
if (!options->parallel)
bundle.error = spawnProcess(options, &bundle.handle);
else if (!ffListShift(&commandQueue, &bundle))
return "[BUG] command queue is empty";
if (bundle.error)
return bundle.error;
bundle.error = ffProcessReadOutput(&bundle.handle, result);
if (bundle.error)
return bundle.error;
ffStrbufTrimRightSpace(result);
return NULL;
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "fastfetch.h"
#include "modules/command/option.h"
const char* ffDetectCommand(FFCommandOptions* options, FFstrbuf* result);
+1
View File
@@ -21,6 +21,7 @@ typedef struct FFCPUResult
uint16_t coresPhysical;
uint16_t coresLogical;
uint16_t coresOnline;
uint16_t numaNodes;
uint32_t frequencyBase; // GHz
uint32_t frequencyMax; // GHz
+24 -16
View File
@@ -3,26 +3,34 @@
#include "util/apple/smc_temps.h"
#include "util/stringUtils.h"
static double detectCpuTemp(const FFstrbuf* cpuName)
static double detectCpuTemp(const FFCPUOptions* options, const FFstrbuf* cpuName)
{
double result = 0;
const char* error = NULL;
if (ffStrbufStartsWithS(cpuName, "Apple M"))
{
switch (strtol(cpuName->chars + strlen("Apple M"), NULL, 10))
{
case 1: error = ffDetectSmcTemps(FF_TEMP_CPU_M1X, &result); break;
case 2: error = ffDetectSmcTemps(FF_TEMP_CPU_M2X, &result); break;
case 3: error = ffDetectSmcTemps(FF_TEMP_CPU_M3X, &result); break;
case 4: error = ffDetectSmcTemps(FF_TEMP_CPU_M4X, &result); break;
default: error = "Unsupported Apple Silicon CPU";
}
}
else // PPC?
error = ffDetectSmcTemps(FF_TEMP_CPU_X64, &result);
if(error)
if (options->tempSensor.length)
{
error = ffDetectSmcSpecificTemp(options->tempSensor.chars, &result);
}
else
{
if (ffStrbufStartsWithS(cpuName, "Apple M"))
{
switch (strtol(cpuName->chars + strlen("Apple M"), NULL, 10))
{
case 1: error = ffDetectSmcTemps(FF_TEMP_CPU_M1X, &result); break;
case 2: error = ffDetectSmcTemps(FF_TEMP_CPU_M2X, &result); break;
case 3: error = ffDetectSmcTemps(FF_TEMP_CPU_M3X, &result); break;
case 4: error = ffDetectSmcTemps(FF_TEMP_CPU_M4X, &result); break;
default: error = "Unsupported Apple Silicon CPU";
}
}
else // PPC?
error = ffDetectSmcTemps(FF_TEMP_CPU_X64, &result);
}
if (error)
return FF_CPU_TEMP_UNSET;
return result;
@@ -130,7 +138,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
detectFrequency(cpu);
if (options->showPeCoreCount) detectCoreCount(cpu);
cpu->temperature = options->temp ? detectCpuTemp(&cpu->name) : FF_CPU_TEMP_UNSET;
cpu->temperature = options->temp ? detectCpuTemp(options, &cpu->name) : FF_CPU_TEMP_UNSET;
return NULL;
}
+22 -20
View File
@@ -8,22 +8,26 @@
#define FF_HAVE_CPUSET 1
#endif
static const char* detectCpuTemp(double* current)
static const char* detectCpuTemp(const FFCPUOptions* options, double* current)
{
int temp = ffSysctlGetInt("dev.cpu.0.temperature", -999999);
if (temp == -999999)
return "ffSysctlGetInt(\"dev.cpu.0.temperature\") failed";
// In tenth of degrees Kelvin
*current = (double) temp / 10 - 273.15;
return NULL;
}
static const char* detectThermalTemp(double* current)
{
int temp = ffSysctlGetInt("hw.acpi.thermal.tz0.temperature", -999999);
if (temp == -999999)
return "ffSysctlGetInt(\"hw.acpi.thermal.tz0.temperature\") failed";
int temp;
if (options->tempSensor.length > 0)
{
temp = ffSysctlGetInt(options->tempSensor.chars, -999999);
if (temp == -999999)
return "ffSysctlGetInt(options->tempSensor) failed";
}
else
{
temp = ffSysctlGetInt("dev.cpu.0.temperature", -999999);
if (temp == -999999)
{
// Thermal zone temperature
temp = ffSysctlGetInt("hw.acpi.thermal.tz0.temperature", -999999);
if (temp == -999999)
return "ffSysctlGetInt(\"dev.cpu.0.temperature\" or \"hw.acpi.thermal.tz0.temperature\") failed";
}
}
// In tenth of degrees Kelvin
*current = (double) temp / 10 - 273.15;
@@ -94,11 +98,9 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
cpu->temperature = FF_CPU_TEMP_UNSET;
if (options->temp)
{
if (detectCpuTemp(&cpu->temperature) != NULL)
detectThermalTemp(&cpu->temperature);
}
if (options->temp) detectCpuTemp(options, &cpu->temperature);
cpu->numaNodes = (uint16_t) ffSysctlGetInt("vm.ndomains", 0);
return NULL;
}
+89 -21
View File
@@ -4,6 +4,7 @@
#include "common/properties.h"
#include "util/mallocHelper.h"
#include "util/stringUtils.h"
#include "util/path.h"
#include <sys/sysinfo.h>
#include <stdlib.h>
@@ -13,6 +14,22 @@
#define FF_CPUINFO_PATH "/proc/cpuinfo"
#ifndef O_PATH
#define O_PATH 0
#endif
static double readTempFile(int dfd, const char* filename, FFstrbuf* buffer)
{
if (filename ? !ffReadFileBufferRelative(dfd, filename, buffer) : !ffReadFDBuffer(dfd, buffer))
return FF_CPU_TEMP_UNSET;
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 parseTZDir(int dfd, FFstrbuf* buffer)
{
if (!ffReadFileBufferRelative(dfd, "type", buffer))
@@ -26,18 +43,12 @@ static double parseTZDir(int dfd, FFstrbuf* buffer)
true
) return FF_CPU_TEMP_UNSET;
if (!ffReadFileBufferRelative(dfd, "temp", buffer))
return FF_CPU_TEMP_UNSET;
double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET);// millidegree Celsius
if (value == FF_CPU_TEMP_UNSET)
return FF_CPU_TEMP_UNSET;
return value / 1000.;
return readTempFile(dfd, "temp", buffer);
}
static double parseHwmonDir(int dfd, FFstrbuf* buffer)
{
//https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
if (!ffReadFileBufferRelative(dfd, "name", buffer))
return FF_CPU_TEMP_UNSET;
@@ -53,25 +64,64 @@ static double parseHwmonDir(int dfd, FFstrbuf* buffer)
true
) return FF_CPU_TEMP_UNSET;
//https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
if (!ffReadFileBufferRelative(dfd, "temp1_input", buffer))
return FF_CPU_TEMP_UNSET;
double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET);// millidegree Celsius
if (value == FF_CPU_TEMP_UNSET)
return FF_CPU_TEMP_UNSET;
return value / 1000.;
return readTempFile(dfd, "temp1_input", buffer);
}
static double detectCPUTemp(void)
static double detectCPUTemp(const FFCPUOptions* options)
{
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
if (options->tempSensor.length > 0)
{
FF_AUTO_CLOSE_FD int subfd = -1;
const char* fileName = NULL;
if (ffStrbufStartsWithS(&options->tempSensor, "hwmon") && ffCharIsDigit(options->tempSensor.chars[strlen("hwmon")]))
{
FF_AUTO_CLOSE_FD int dfd = open("/sys/class/hwmon/", O_PATH | O_CLOEXEC);
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (subfd >= 0)
fileName = "temp1_input";
else
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_CLOEXEC);
}
else if (ffStrbufStartsWithS(&options->tempSensor, "thermal_zone") && ffCharIsDigit(options->tempSensor.chars[strlen("thermal_zone")]))
{
FF_AUTO_CLOSE_FD int dfd = open("/sys/class/thermal/", O_PATH | O_CLOEXEC);
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (subfd >= 0)
fileName = "temp";
else
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_CLOEXEC);
}
else if (ffStrbufStartsWithS(&options->tempSensor, "cputemp.") && ffCharIsDigit(options->tempSensor.chars[strlen("cputemp.")]))
{
FF_AUTO_CLOSE_FD int dfd = open("/sys/class/platform/", O_PATH | O_CLOEXEC);
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (subfd >= 0)
fileName = "temp1_input";
else
subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_CLOEXEC);
}
else if (ffIsAbsolutePath(options->tempSensor.chars))
{
subfd = open(options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (subfd >= 0)
fileName = "temp1_input";
else
subfd = open(options->tempSensor.chars, O_RDONLY | O_CLOEXEC);
}
if (subfd < 0)
return FF_CPU_TEMP_UNSET;
return readTempFile(subfd, fileName, &buffer);
}
{
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/hwmon/");
if(dirp)
{
int dfd = dirfd(dirp);
struct dirent* entry;
while((entry = readdir(dirp)) != NULL)
{
@@ -138,6 +188,21 @@ static double detectCPUTemp(void)
return FF_CPU_TEMP_UNSET;
}
static void detectNumaNodes(FFCPUResult* cpu)
{
FF_AUTO_CLOSE_DIR DIR* dir = opendir("/sys/devices/system/node/");
if (!dir) return;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL)
{
if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)
continue;
if (ffStrStartsWith(entry->d_name, "node") && ffCharIsDigit(entry->d_name[strlen("node")]))
cpu->numaNodes++;
}
}
#ifdef __ANDROID__
#include "common/settings.h"
@@ -568,6 +633,8 @@ FF_MAYBE_UNUSED static const char* detectCPUX86(const FFCPUOptions* options, FFC
if (!detectFrequency(cpu, options) || cpu->frequencyBase == 0)
cpu->frequencyBase = (uint32_t) ffStrbufToUInt(&cpuMHz, 0);
detectNumaNodes(cpu);
return NULL;
}
@@ -799,9 +866,9 @@ FF_MAYBE_UNUSED static uint16_t getLoongarchPropCount(FFstrbuf* cpuinfo, const c
char* pend;
unsigned long id = strtoul(p, &pend, 10);
if (__builtin_expect(id > 64, false))
high |= 1 << (id - 64);
high |= 1UL << (id - 64);
else
low |= 1 << id;
low |= 1UL << id;
p = pend;
}
@@ -877,6 +944,7 @@ FF_MAYBE_UNUSED static const char* detectCPUOthers(const FFCPUOptions* options,
detectPhysicalCores(cpu);
ffCPUDetectByCpuid(cpu);
detectNumaNodes(cpu);
return NULL;
}
@@ -884,7 +952,7 @@ FF_MAYBE_UNUSED static const char* detectCPUOthers(const FFCPUOptions* options,
const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
{
cpu->temperature = options->temp ? detectCPUTemp() : FF_CPU_TEMP_UNSET;
cpu->temperature = options->temp ? detectCPUTemp(options) : FF_CPU_TEMP_UNSET;
#if __x86_64__ || __i386__
return detectCPUX86(options, cpu);
+17 -7
View File
@@ -16,7 +16,7 @@ static void freePropDict(prop_dictionary_t* pdict)
prop_object_release(*pdict);
}
static const char* detectCpuTemp(double* current)
static const char* detectCpuTemp(const FFCPUOptions* options, double* current)
{
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";
@@ -25,11 +25,21 @@ static const char* detectCpuTemp(double* current)
if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &root) < 0)
return "prop_dictionary_recv_ioctl(ENVSYS_GETDICTIONARY) failed";
prop_array_t array = prop_dictionary_get(root, "coretemp0");
if (!array) array = prop_dictionary_get(root, "amdzentemp0");
if (!array) array = prop_dictionary_get(root, "viac7temp0");
if (!array) array = prop_dictionary_get(root, "acpitz0"); // Thermal Zones
if (!array) return "No temp data found in root dictionary";
prop_array_t array;
if (options->tempSensor.length > 0)
{
array = prop_dictionary_get(root, options->tempSensor.chars);
if (!array) return "No temp data found in specified sensor";
}
else
{
array = prop_dictionary_get(root, "coretemp0");
if (!array) array = prop_dictionary_get(root, "amdzentemp0");
if (!array) array = prop_dictionary_get(root, "viac7temp0");
if (!array) array = prop_dictionary_get(root, "acpitz0"); // Thermal Zones
if (!array) return "No temp data found in root dictionary";
}
if (prop_array_count(array) != 2)
return "Unexpected `xtemp0` data";
@@ -73,7 +83,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
cpu->temperature = FF_CPU_TEMP_UNSET;
if (options->temp) detectCpuTemp(&cpu->temperature);
if (options->temp) detectCpuTemp(options, &cpu->temperature);
return NULL;
}
+12 -4
View File
@@ -6,7 +6,7 @@
#include <sys/time.h>
#include <sys/sensors.h>
static const char* detectCPUTemp(FFCPUResult* cpu)
static const char* detectCPUTemp(const FFCPUOptions* options, FFCPUResult* cpu)
{
int mib[5] = {CTL_HW, HW_SENSORS, 0, SENSOR_TEMP, 0};
@@ -23,8 +23,16 @@ static const char* detectCPUTemp(FFCPUResult* cpu)
return "sysctl(sensordev) failed";
}
if (!ffStrStartsWith(sensordev.xname, "cpu"))
continue;
if (options->tempSensor.length > 0)
{
if (!ffStrbufEqualS(&options->tempSensor, sensordev.xname))
continue;
}
else
{
if (!ffStrStartsWith(sensordev.xname, "cpu"))
continue;
}
for (mib[4] = 0; mib[4] < sensordev.maxnumt[SENSOR_TEMP]; mib[4]++)
{
@@ -62,7 +70,7 @@ const char *ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
if (cpuspeed > cpu->frequencyBase) cpu->frequencyBase = cpuspeed;
cpu->temperature = FF_CPU_TEMP_UNSET;
if (options->temp) detectCPUTemp(cpu);
if (options->temp) detectCPUTemp(options, cpu);
return NULL;
}
+7 -2
View File
@@ -3,10 +3,15 @@
#include "util/stringUtils.h"
#include <kstat.h>
static const char* detectCPUTempByKstat(kstat_ctl_t* kc, FFCPUResult* cpu)
static const char* detectCPUTempByKstat(const FFCPUOptions* options, kstat_ctl_t* kc, FFCPUResult* cpu)
{
const char* possibleModules[] = {"temperature", "cpu_temp", "acpi_thermal", NULL};
if (options->tempSensor.length > 0) {
possibleModules[0] = options->tempSensor.chars;
possibleModules[1] = NULL;
}
for (int i = 0; possibleModules[i] != NULL; i++) {
kstat_t* ks = kstat_lookup(kc, possibleModules[i], -1, NULL);
if (ks && kstat_read(kc, ks, NULL) >= 0) {
@@ -141,7 +146,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
if (options->temp)
{
if (detectCPUTempByKstat(kc, cpu) != NULL)
if (detectCPUTempByKstat(options, kc, cpu) != NULL)
detectCPUTempByIpmiTool(cpu);
}
+15 -2
View File
@@ -17,7 +17,7 @@ static inline void ffPerfCloseQueryHandle(HANDLE* phQuery)
}
}
const char* detectThermalTemp(double* result)
const char* detectThermalTemp(const FFCPUOptions* options, double* result)
{
struct FFPerfQuerySpec
{
@@ -35,6 +35,14 @@ const char* detectThermalTemp(double* result)
.Name = L"\\_TZ.CPUZ", // The standard(?) instance name for CPU temperature in the thermal provider
};
if (options->tempSensor.length > 0)
{
int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, options->tempSensor.chars, (int) options->tempSensor.length, querySpec.Name, (int)(ARRAY_SIZE(querySpec.Name) - 1));
if (written == 0)
return "Invalid temp sensor string";
querySpec.Name[written] = L'\0';
}
DWORD dataSize = 0;
if (PerfEnumerateCounterSetInstances(NULL, &querySpec.Identifier.CounterSetGuid, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY)
return "PerfEnumerateCounterSetInstances() failed";
@@ -62,6 +70,9 @@ const char* detectThermalTemp(double* result)
if (dataSize == 0)
{
if (options->tempSensor.length > 0)
return "Unable to find CPU sensor";
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
}
@@ -230,6 +241,8 @@ static const char* detectNCores(FFCPUResult* cpu)
++cpu->coresPhysical;
else if (ptr->Relationship == RelationProcessorPackage)
++cpu->packages;
else if (ptr->Relationship == RelationNumaNode)
++cpu->numaNodes;
}
return NULL;
@@ -299,7 +312,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
detectMaxSpeedBySmbios(cpu);
if(options->temp)
detectThermalTemp(&cpu->temperature);
detectThermalTemp(options, &cpu->temperature);
return NULL;
}
+1 -1
View File
@@ -9,7 +9,7 @@ const char* ffDetectCPUCache(FFCPUCacheResult* result)
if (nPerfLevels <= 0) return "sysctl(hw.nperflevels) failed";
// macOS provides the global system cache line size
uint32_t lineSize = (uint32_t) ffSysctlGetInt("hw.cachelinesize", 0);
uint32_t lineSize = (uint32_t) ffSysctlGetInt64("hw.cachelinesize", 0);
char sysctlKey[128] = "hw.perflevelN.";
char* pNum = sysctlKey + strlen("hw.perflevel");
+25 -1
View File
@@ -10,7 +10,7 @@ const char* ffDetectDisks(FFDiskOptions* options, FFlist* disks)
const char* error = ffDetectDisksImpl(options, disks);
if (error) return error;
if (disks->length == 0) return "No disks found";
if (disks->length == 0) return NULL;
//We need to sort the disks, so that we can detect, which disk a path resides on
// For example for /boot/efi/bootmgr we need to check /boot/efi before /boot
@@ -31,3 +31,27 @@ const char* ffDetectDisks(FFDiskOptions* options, FFlist* disks)
return NULL;
}
#ifndef _WIN32
#include <fnmatch.h>
bool ffDiskMatchesFolderPatterns(FFstrbuf* folders, const char* path, char separator)
{
uint32_t startIndex = 0;
while(startIndex < folders->length)
{
uint32_t sepIndex = ffStrbufNextIndexC(folders, startIndex, separator);
char savedSep = folders->chars[sepIndex]; // Can be '\0' if at end
folders->chars[sepIndex] = '\0';
bool matched = fnmatch(&folders->chars[startIndex], path, 0) == 0;
folders->chars[sepIndex] = savedSep;
if (matched) return true;
startIndex = sepIndex + 1;
}
return false;
}
#endif
+4
View File
@@ -35,3 +35,7 @@ typedef struct FFDisk
const char* ffDetectDisks(FFDiskOptions* options, FFlist* disks /* list of FFDisk */);
const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks);
#ifndef _WIN32
bool ffDiskMatchesFolderPatterns(FFstrbuf* folders, const char* path, char separator);
#endif
+6
View File
@@ -151,6 +151,12 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
else if(!ffStrEquals(fs->f_mntonname, "/") && !ffStrStartsWith(fs->f_mntfromname, "/dev/") && !ffStrEquals(fs->f_fstypename, "zfs") && !ffStrEquals(fs->f_fstypename, "fusefs.sshfs"))
continue;
if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, fs->f_mntonname, FF_DISK_FOLDER_SEPARATOR))
continue;
if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, fs->f_fstypename, ':'))
continue;
#ifdef __FreeBSD__
// f_bavail and f_ffree are signed on FreeBSD...
if(fs->f_bavail < 0) fs->f_bavail = 0;
+6
View File
@@ -27,6 +27,12 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
continue;
}
if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, path.Path(), FF_DISK_FOLDER_SEPARATOR))
continue;
if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, fs.fsh_name, ':'))
continue;
FFDisk* disk = (FFDisk*) ffListAdd(disks);
disk->bytesTotal = (uint64_t)fs.total_blocks * (uint64_t) fs.block_size;
+6
View File
@@ -297,6 +297,12 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
else if(!isPhysicalDevice(device))
continue;
if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, device->mnt_dir, FF_DISK_FOLDER_SEPARATOR))
continue;
if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, device->mnt_type, ':'))
continue;
//We have a valid device, add it to the list
FFDisk* disk = ffListAdd(disks);
disk->type = FF_DISK_VOLUME_TYPE_NONE;
+6
View File
@@ -119,6 +119,12 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
else if(!isPhysicalDevice(&device))
continue;
if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, device.mnt_mountp, FF_DISK_FOLDER_SEPARATOR))
continue;
if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, device.mnt_fstype, ':'))
continue;
//We have a valid device, add it to the list
FFDisk* disk = ffListAdd(disks);
disk->type = FF_DISK_VOLUME_TYPE_NONE;
+25 -13
View File
@@ -53,6 +53,29 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
else if(driveType == DRIVE_NO_ROOT_DIR)
continue;
if (options->hideFolders.length && ffStrbufSeparatedContain(&options->hideFolders, &buffer, FF_DISK_FOLDER_SEPARATOR))
continue;
wchar_t diskName[MAX_PATH + 1], diskFileSystem[MAX_PATH + 1];
DWORD diskFlags;
BOOL volumeInfoAvailable = GetVolumeInformationW(mountpoint,
diskName, ARRAY_SIZE(diskName), //Volume name
NULL, //Serial number
NULL, //Max component length
&diskFlags, //File system flags
diskFileSystem, ARRAY_SIZE(diskFileSystem)
);
FF_STRBUF_AUTO_DESTROY diskFileSystemBuf = ffStrbufCreate();
if (volumeInfoAvailable)
{
ffStrbufSetWS(&diskFileSystemBuf, diskFileSystem);
if (options->hideFS.length && ffStrbufSeparatedContain(&options->hideFS, &diskFileSystemBuf, ':'))
continue;
}
FFDisk* disk = ffListAdd(disks);
disk->filesUsed = 0;
@@ -97,20 +120,9 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
(PULARGE_INTEGER)&disk->bytesFree
);
wchar_t diskName[MAX_PATH + 1], diskFileSystem[MAX_PATH + 1];
DWORD diskFlags;
BOOL result = GetVolumeInformationW(mountpoint,
diskName, ARRAY_SIZE(diskName), //Volume name
NULL, //Serial number
NULL, //Max component length
&diskFlags, //File system flags
diskFileSystem, ARRAY_SIZE(diskFileSystem)
);
if(result)
if(volumeInfoAvailable)
{
ffStrbufSetWS(&disk->filesystem, diskFileSystem);
ffStrbufInitMove(&disk->filesystem, &diskFileSystemBuf);
ffStrbufSetWS(&disk->name, diskName);
if(diskFlags & FILE_READ_ONLY_VOLUME)
disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT;
@@ -140,6 +140,61 @@ static bool detectWithGetprop(FFDisplayServerResult* ds)
return false;
}
static bool detectDE(FFDisplayServerResult* ds)
{
if (ffSettingsGetAndroidProperty("ro.vivo.os.build.display.id", &ds->dePrettyName)) // OriginOS 6
{
ffStrbufAppendC(&ds->dePrettyName, ' ');
ffSettingsGetAndroidProperty("ro.vivo.product.version", &ds->dePrettyName); // PD2505D_xxx
return true;
}
if (ffSettingsGetAndroidProperty("ro.build.version.magic", &ds->dePrettyName) ||
ffSettingsGetAndroidProperty("ro.build.version.emui", &ds->dePrettyName))
{
ffStrbufReplaceAllC(&ds->dePrettyName, '_', ' ');
return true;
}
if (ffSettingsGetAndroidProperty("ro.mi.os.version.name", &ds->dePrettyName))
{
// MiUI like
ffStrbufClear(&ds->dePrettyName);
ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName); // Detail version number
if (ffStrbufStartsWithS(&ds->dePrettyName, "OS"))
{
ds->dePrettyName.chars[0] = 'S';
ds->dePrettyName.chars[1] = ' ';
ffStrbufPrependS(&ds->dePrettyName, "HyperO");
}
else if (ffStrbufStartsWithS(&ds->dePrettyName, "V"))
{
ds->dePrettyName.chars[0] = ' ';
ffStrbufPrependS(&ds->dePrettyName, "MiUI");
}
else
ffStrbufSetStatic(&ds->dePrettyName, "MiUI");
return true;
}
if (ffSettingsGetAndroidProperty("ro.build.version.oplusrom", &ds->dePrettyName))
{
if (ffStrbufStartsWithS(&ds->dePrettyName, "V"))
ffStrbufSubstrAfter(&ds->dePrettyName, 0);
ffStrbufPrependS(&ds->dePrettyName, "ColorOS");
return true;
}
if (ffSettingsGetAndroidProperty("ro.oxygen.version", &ds->dePrettyName))
{
ffStrbufPrependS(&ds->dePrettyName, "OxygenOS");
return true;
}
if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName) && ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS"))
{
ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' ');
return true;
}
return false;
}
void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
{
const char* error = ffdsConnectXcbRandr(ds);
@@ -158,4 +213,6 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
if (!detectWithGetprop(ds))
detectWithDumpsys(ds);
detectDE(ds);
}
@@ -239,6 +239,11 @@ enum kde_output_device_v2_capability {
* @since 17
*/
KDE_OUTPUT_DEVICE_V2_CAPABILITY_SHARPNESS = 0x1000,
/**
* if this outputdevice supports custom modes
* @since 18
*/
KDE_OUTPUT_DEVICE_V2_CAPABILITY_CUSTOM_MODES = 0x2000,
};
/**
* @ingroup iface_kde_output_device_v2
@@ -280,6 +285,10 @@ enum kde_output_device_v2_capability {
* @ingroup iface_kde_output_device_v2
*/
#define KDE_OUTPUT_DEVICE_V2_CAPABILITY_SHARPNESS_SINCE_VERSION 17
/**
* @ingroup iface_kde_output_device_v2
*/
#define KDE_OUTPUT_DEVICE_V2_CAPABILITY_CUSTOM_MODES_SINCE_VERSION 18
#endif /* KDE_OUTPUT_DEVICE_V2_CAPABILITY_ENUM */
#ifndef KDE_OUTPUT_DEVICE_V2_VRR_POLICY_ENUM
@@ -794,6 +803,23 @@ struct kde_output_device_v2_listener {
void (*sharpness)(void *data,
struct kde_output_device_v2 *kde_output_device_v2,
uint32_t sharpness);
/**
* output priority
*
* Describes the position of the output in the output order list,
* with lower values being earlier in the list. There's no specific
* value the list has to start at, this value is only used in
* sorting outputs.
*
* Note that the output order protocol is not sufficient for this,
* as an output may not be in the output order if it's disabled or
* mirroring another screen.
* @param priority priority
* @since 18
*/
void (*priority)(void *data,
struct kde_output_device_v2 *kde_output_device_v2,
uint32_t priority);
};
/**
@@ -943,6 +969,10 @@ kde_output_device_v2_add_listener(struct kde_output_device_v2 *kde_output_device
* @ingroup iface_kde_output_device_v2
*/
#define KDE_OUTPUT_DEVICE_V2_SHARPNESS_SINCE_VERSION 17
/**
* @ingroup iface_kde_output_device_v2
*/
#define KDE_OUTPUT_DEVICE_V2_PRIORITY_SINCE_VERSION 18
/** @ingroup iface_kde_output_device_v2 */
@@ -972,6 +1002,18 @@ kde_output_device_v2_destroy(struct kde_output_device_v2 *kde_output_device_v2)
wl_proxy_destroy((struct wl_proxy *) kde_output_device_v2);
}
#ifndef KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_ENUM
#define KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_ENUM
/**
* @ingroup iface_kde_output_device_mode_v2
* mode flags
*/
enum kde_output_device_mode_v2_flags {
KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_CUSTOM = 0x1,
KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_REDUCED_BLANKING = 0x2,
};
#endif /* KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_ENUM */
/**
* @ingroup iface_kde_output_device_mode_v2
* @struct kde_output_device_mode_v2_listener
@@ -1017,6 +1059,15 @@ struct kde_output_device_mode_v2_listener {
*/
void (*removed)(void *data,
struct kde_output_device_mode_v2 *kde_output_device_mode_v2);
/**
* mode flags
*
* This event describes the mode's flags.
* @since 19
*/
void (*flags)(void *data,
struct kde_output_device_mode_v2 *kde_output_device_mode_v2,
uint32_t flags);
};
/**
@@ -1046,6 +1097,10 @@ kde_output_device_mode_v2_add_listener(struct kde_output_device_mode_v2 *kde_out
* @ingroup iface_kde_output_device_mode_v2
*/
#define KDE_OUTPUT_DEVICE_MODE_V2_REMOVED_SINCE_VERSION 1
/**
* @ingroup iface_kde_output_device_mode_v2
*/
#define KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_SINCE_VERSION 19
/** @ingroup iface_kde_output_device_mode_v2 */
@@ -67,12 +67,13 @@ static const struct wl_message kde_output_device_v2_events[] = {
{ "automatic_max_bits_per_color_limit", "15u", kde_output_device_v2_types + 0 },
{ "edr_policy", "16u", kde_output_device_v2_types + 0 },
{ "sharpness", "17u", kde_output_device_v2_types + 0 },
{ "priority", "18u", kde_output_device_v2_types + 0 },
};
WL_EXPORT const struct wl_interface kde_output_device_v2_interface = {
"kde_output_device_v2", 17,
"kde_output_device_v2", 19,
0, NULL,
34, kde_output_device_v2_events,
35, kde_output_device_v2_events,
};
static const struct wl_message kde_output_device_mode_v2_events[] = {
@@ -80,12 +81,13 @@ static const struct wl_message kde_output_device_mode_v2_events[] = {
{ "refresh", "i", kde_output_device_v2_types + 0 },
{ "preferred", "", kde_output_device_v2_types + 0 },
{ "removed", "", kde_output_device_v2_types + 0 },
{ "flags", "19u", kde_output_device_v2_types + 0 },
};
WL_EXPORT const struct wl_interface kde_output_device_mode_v2_interface = {
"kde_output_device_mode_v2", 1,
"kde_output_device_mode_v2", 19,
0, NULL,
4, kde_output_device_mode_v2_events,
5, kde_output_device_mode_v2_events,
};
#endif
@@ -178,6 +178,7 @@ static struct kde_output_device_v2_listener outputListener = {
.automatic_max_bits_per_color_limit = (void*) stubListener,
.edr_policy = (void*) stubListener,
.sharpness = (void*) stubListener,
.priority = (void*) stubListener,
};
const char* ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version)
+19 -1
View File
@@ -15,11 +15,18 @@ extern const char* FF_GPU_VENDOR_NAME_AMD;
extern const char* FF_GPU_VENDOR_NAME_INTEL;
extern const char* FF_GPU_VENDOR_NAME_NVIDIA;
extern const char* FF_GPU_VENDOR_NAME_MTHREADS;
extern const char* FF_GPU_VENDOR_NAME_QUALCOMM;
extern const char* FF_GPU_VENDOR_NAME_MTK;
extern const char* FF_GPU_VENDOR_NAME_VMWARE;
extern const char* FF_GPU_VENDOR_NAME_PARALLEL;
extern const char* FF_GPU_VENDOR_NAME_MICROSOFT;
extern const char* FF_GPU_VENDOR_NAME_REDHAT;
extern const char* FF_GPU_VENDOR_NAME_ORACLE;
extern const char* FF_GPU_VENDOR_NAME_BROADCOM;
extern const char* FF_GPU_VENDOR_NAME_LOONGSON;
extern const char* FF_GPU_VENDOR_NAME_JINGJIA_MICRO;
extern const char* FF_GPU_VENDOR_NAME_HUAWEI;
extern const char* FF_GPU_VENDOR_NAME_ZHAOXIN;
typedef struct FFGPUMemory
{
@@ -58,7 +65,7 @@ typedef struct FFGpuDriverPciBusId
uint32_t func;
} FFGpuDriverPciBusId;
#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) || defined(__GNU__)
void ffGPUFillVendorAndName(uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu);
void ffGPUQueryAmdGpuName(uint16_t deviceId, uint8_t revisionId, FFGPUResult* gpu);
@@ -73,3 +80,14 @@ const char* ffDrmDetectNouveau(FFGPUResult* gpu, int fd);
const char* ffGPUDetectDriverSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFGpuDriverPciBusId pciBusId);
#endif // defined(XXX)
static inline uint64_t ffGPUPciAddr2Id(uint64_t domain, uint64_t bus, uint64_t device, uint64_t function)
{
return (domain << 16) | (bus << 8) | (device << 3) | function;
}
static inline uint64_t ffGPUGeneral2Id(uint64_t originalId)
{
// Note: originalId may already have the MSB set
return (1ULL << 63) | originalId;
}
+1 -1
View File
@@ -57,7 +57,7 @@ static const char* detectFrequency(FFGPUResult* gpu)
if (!freqProperty || CFGetTypeID(freqProperty) != CFDataGetTypeID())
return "\"voltage-states9-sram\" in \"pmgr\" is not found";
// voltage-states5-sram stores supported <frequency / voltage> pairs of gpu from the lowest to the highest
// voltage-states9-sram stores supported <frequency / voltage> pairs of gpu from the lowest to the highest
CFIndex propLength = CFDataGetLength(freqProperty);
if (propLength == 0 || propLength % (CFIndex) sizeof(uint32_t) * 2 != 0)
return "Invalid \"voltage-states9-sram\" length";
+6 -3
View File
@@ -30,7 +30,7 @@ static void fillGPUTypeGeneric(FFGPUResult* gpu)
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL)
{
// 0000:00:02.0 is reserved for Intel integrated graphics
gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
}
}
}
@@ -80,7 +80,7 @@ static const char* detectByDrm(const FFGPUOptions* options, FFlist* gpus)
{
case DRM_BUS_PCI:
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(dev->deviceinfo.pci->vendor_id));
gpu->deviceId = (dev->businfo.pci->domain * 100000ull) + (dev->businfo.pci->bus * 1000ull) + (dev->businfo.pci->dev * 10ull) + dev->businfo.pci->func;
gpu->deviceId = ffGPUPciAddr2Id(dev->businfo.pci->domain, dev->businfo.pci->bus, dev->businfo.pci->dev, dev->businfo.pci->func);
break;
case DRM_BUS_HOST1X:
ffStrbufSetS(&gpu->name, dev->deviceinfo.host1x->compatible[0]);
@@ -179,6 +179,9 @@ static const char* detectByPci(const FFGPUOptions* options, FFlist* gpus)
{
struct pci_conf* pc = &confs[i];
if (pc->pc_sel.pc_func > 0 && pc->pc_subclass == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/)
continue; // Likely an auxiliary display controller (#2034)
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(pc->pc_vendor));
ffStrbufInit(&gpu->name);
@@ -191,7 +194,7 @@ static const char* detectByPci(const FFGPUOptions* options, FFlist* gpus)
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
gpu->type = FF_GPU_TYPE_UNKNOWN;
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
gpu->deviceId = (pc->pc_sel.pc_domain * 100000ull) + (pc->pc_sel.pc_bus * 1000ull) + (pc->pc_sel.pc_dev * 10ull) + pc->pc_sel.pc_func;
gpu->deviceId = ffGPUPciAddr2Id(pc->pc_sel.pc_domain, pc->pc_sel.pc_bus, pc->pc_sel.pc_dev, pc->pc_sel.pc_func);
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
ffGPUDetectDriverSpecific(options, gpu, (FFGpuDriverPciBusId) {
+2 -2
View File
@@ -153,7 +153,7 @@ const char* ffDrmDetectAmdgpu(const FFGPUOptions* options, FFGPUResult* gpu, con
return NULL;
#else
FF_UNUSED(gpu, renderPath);
FF_UNUSED(options, gpu, renderPath);
return "Fastfetch is compiled without libdrm support";
#endif
}
@@ -313,7 +313,7 @@ const char* ffDrmDetectAsahi(FFGPUResult* gpu, int fd)
// They removed `unstable_uabi_version` from the struct. Hopefully they won't introduce new ABI changes.
gpu->coreCount = (int32_t) (paramsGlobal.num_clusters_total * paramsGlobal.num_cores_per_cluster);
gpu->frequency = paramsGlobal.max_frequency_khz / 1000;
gpu->deviceId = paramsGlobal.chip_id;
gpu->deviceId = ffGPUGeneral2Id(paramsGlobal.chip_id);
if (!gpu->name.length)
{
+9 -2
View File
@@ -14,6 +14,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_slot_match_iterator_create)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_device_next)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_system_cleanup)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_iterator_destroy)
{
// Requires root access
@@ -29,6 +30,11 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
if (dev->device_class >> 16 != 0x03 /*PCI_BASE_CLASS_DISPLAY*/)
continue;
uint8_t subclass = (dev->device_class >> 8) & 0xFF;
if (dev->func > 0 && subclass == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/)
continue; // Likely an auxiliary display controller (#2034)
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(dev->vendor_id));
ffStrbufInit(&gpu->name);
@@ -40,15 +46,16 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
gpu->type = FF_GPU_TYPE_UNKNOWN;
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
gpu->deviceId = ((uint64_t) dev->domain << 6) | ((uint64_t) dev->bus << 4) | ((uint64_t) dev->dev << 2) | (uint64_t) dev->func;
gpu->deviceId = ffGPUPciAddr2Id(dev->domain, dev->bus, dev->dev, dev->func);
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
ffGPUQueryAmdGpuName(dev->device_id, dev->revision, gpu);
if (gpu->name.length == 0)
ffGPUFillVendorAndName((dev->device_class >> 8) & 0xFF, dev->vendor_id, dev->device_id, gpu);
ffGPUFillVendorAndName(subclass, dev->vendor_id, dev->device_id, gpu);
}
ffpci_iterator_destroy(iter);
ffpci_system_cleanup();
return NULL;
+4 -1
View File
@@ -19,6 +19,9 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
if (dev.class_base != 0x03 /*PCI_BASE_CLASS_DISPLAY*/)
continue;
if (dev.function > 0 && dev.class_sub == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/)
continue; // Likely an auxiliary display controller (#2034)
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(dev.vendor_id));
ffStrbufInit(&gpu->name);
@@ -30,7 +33,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
gpu->type = FF_GPU_TYPE_UNKNOWN;
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
gpu->deviceId = ((uint64_t) dev.bus << 4) | ((uint64_t) dev.device << 2) | (uint64_t) dev.function;
gpu->deviceId = ffGPUPciAddr2Id(0, dev.bus, dev.device, dev.function);
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
+59 -6
View File
@@ -4,10 +4,13 @@
#include "detection/gpu/gpu_driver_specific.h"
#include "common/io/io.h"
#include "common/library.h"
#include "modules/gpu/option.h"
#include "util/FFstrbuf.h"
#include "util/stringUtils.h"
#include "util/mallocHelper.h"
#include <inttypes.h>
#include <stdint.h>
#ifdef FF_HAVE_DRM_AMDGPU
#include <amdgpu.h>
@@ -120,7 +123,7 @@ static const char* drmDetectAmdSpecific(const FFGPUOptions* options, FFGPUResult
#endif
}
#else
FF_UNUSED(gpu, drmKey, buffer);
FF_UNUSED(options, gpu, drmKey, buffer);
return "Fastfetch is not compiled with drm support";
#endif
}
@@ -198,7 +201,7 @@ static void pciDetectIntelSpecific(const FFGPUOptions* options, FFGPUResult* gpu
// https://patchwork.kernel.org/project/intel-gfx/patch/1422039866-11572-3-git-send-email-ville.syrjala@linux.intel.com/
// 0000:00:02.0 is reserved for Intel integrated graphics
gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
if (!drmKey) return;
@@ -263,7 +266,7 @@ static const char* drmDetectIntelSpecific(FFGPUResult* gpu, const char* drmKey,
#endif
}
static const char* pciDetectNouveauSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
static const char* pciDetectTempGeneral(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
{
if (options->temp)
{
@@ -306,6 +309,48 @@ static const char* drmDetectNouveauSpecific(FFGPUResult* gpu, const char* drmKey
#endif
}
static const char* pciDetectZxSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
{
gpu->type = FF_GPU_TYPE_INTEGRATED;
const uint32_t pciDirLen = pciDir->length;
ffStrbufAppendS(pciDir, "/zx_info/eclk");
if (ffReadFileBuffer(pciDir->chars, buffer))
gpu->frequency = (uint32_t) ffStrbufToUInt(buffer, FF_GPU_FREQUENCY_UNSET);
ffStrbufSubstrBefore(pciDir, pciDirLen);
if (options->driverSpecific)
{
ffStrbufAppendS(pciDir, "/zx_info/engine_3d_usage");
if (ffReadFileBuffer(pciDir->chars, buffer))
gpu->coreUsage = ffStrbufToDouble(buffer, FF_GPU_CORE_USAGE_UNSET);
ffStrbufSubstrBefore(pciDir, pciDirLen);
ffStrbufAppendS(pciDir, "/zx_info/fb_size");
if (ffReadFileBuffer(pciDir->chars, buffer))
gpu->shared.total = ffStrbufToUInt(buffer, FF_GPU_VMEM_SIZE_UNSET);
ffStrbufSubstrBefore(pciDir, pciDirLen);
if (gpu->shared.total != FF_GPU_VMEM_SIZE_UNSET)
{
gpu->shared.total *= 1024 * 1024;
ffStrbufAppendS(pciDir, "/zx_info/free_fb_mem");
if (ffReadFileBuffer(pciDir->chars, buffer))
gpu->shared.used = ffStrbufToUInt(buffer, FF_GPU_VMEM_SIZE_UNSET);
ffStrbufSubstrBefore(pciDir, pciDirLen);
if (gpu->shared.used != FF_GPU_VMEM_SIZE_UNSET)
{
gpu->shared.used *= 1024 * 1024;
gpu->shared.used = gpu->shared.total - gpu->shared.used;
}
}
}
return NULL;
}
static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* deviceDir, const char* drmKey)
{
const uint32_t drmDirPathLength = deviceDir->length;
@@ -342,6 +387,9 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
if (sscanf(pPciPath, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4)
return "Invalid PCI device path";
if (pciFunc > 0 && subclassId == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/)
return "Likely an auxiliary display controller"; // #2034
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString((uint16_t) vendorId));
ffStrbufInit(&gpu->name);
@@ -354,7 +402,7 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
gpu->type = FF_GPU_TYPE_UNKNOWN;
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
gpu->deviceId = (pciDomain * 100000ull) + (pciBus * 1000ull) + (pciDevice * 10ull) + pciFunc;
gpu->deviceId = ffGPUPciAddr2Id(pciDomain, pciBus, pciDevice, pciFunc);
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
char drmKeyBuffer[8];
@@ -414,10 +462,15 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
}
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && ffStrbufEqualS(&gpu->driver, "nouveau"))
{
pciDetectNouveauSpecific(options, gpu, deviceDir, buffer);
pciDetectTempGeneral(options, gpu, deviceDir, buffer);
if (options->driverSpecific && drmKey)
drmDetectNouveauSpecific(gpu, drmKey, buffer);
}
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_ZHAOXIN && ffStrbufStartsWithS(&gpu->driver, "zx"))
{
pciDetectTempGeneral(options, gpu, deviceDir, buffer);
pciDetectZxSpecific(options, gpu, deviceDir, buffer);
}
else
{
ffGPUDetectDriverSpecific(options, gpu, (FFGpuDriverPciBusId) {
@@ -552,7 +605,7 @@ static const char* drmDetectGPUs(const FFGPUOptions* options, FFlist* gpus)
if (ffStrbufStartsWithS(&buffer, "pci:"))
detectPci(options, gpus, &buffer, &drmDir, entry->d_name);
else if (ffStrbufStartsWithS(&buffer, "of:"))
else if (ffStrbufStartsWithS(&buffer, "of:")) // Open Firmware
detectOf(gpus, &buffer, &drmDir, entry->d_name);
ffStrbufSubstrBefore(&drmDir, drmDirLength);
+113
View File
@@ -0,0 +1,113 @@
#include "gpu.h"
#include "common/io/io.h"
#include <sys/param.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pciio.h>
static inline int pciReadConf(int fd, uint32_t bus, uint32_t device, uint32_t func, uint32_t reg, uint32_t* result)
{
struct pciio_bdf_cfgreg bdfr = {
.bus = bus,
.device = device,
.function = func,
.cfgreg = {
.reg = reg,
},
};
if (ioctl(fd, PCI_IOC_BDF_CFGREAD, &bdfr) == -1)
return -1;
*result = bdfr.cfgreg.val;
return 0;
}
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
{
char pciDevPath[] = "/dev/pciXXX";
for (uint32_t idev = 0; idev <= 255; idev++)
{
snprintf(pciDevPath + strlen("/dev/pci"), 4, "%u", idev);
FF_AUTO_CLOSE_FD int pcifd = open(pciDevPath, O_RDONLY | O_CLOEXEC);
if (pcifd < 0)
{
if (errno == ENOENT)
break; // No more /dev/pciN devices
return "open(\"/dev/pciN\", O_RDONLY | O_CLOEXEC) failed";
}
struct pciio_businfo businfo;
if (ioctl(pcifd, PCI_IOC_BUSINFO, &businfo) != 0)
continue;
uint32_t bus = businfo.busno;
for (uint32_t dev = 0; dev < businfo.maxdevs; dev++)
{
uint32_t maxfuncs = 0;
for (uint32_t func = 0; func <= maxfuncs; func++)
{
uint32_t pciid, pciclass;
if (pciReadConf(pcifd, bus, dev, func, PCI_ID_REG, &pciid) != 0)
continue;
if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID || PCI_VENDOR(pciid) == 0)
continue;
if (pciReadConf(pcifd, bus, dev, func, PCI_CLASS_REG, &pciclass) != 0)
continue;
if (func == 0)
{
// For some reason, pciReadConf returns success even for non-existing devices.
// So we need to check for `PCI_VENDOR(pciid) == PCI_VENDOR_INVALID` above to filter them out.
uint32_t bhlcr;
if (pciReadConf(pcifd, bus, dev, 0, PCI_BHLC_REG, &bhlcr) != 0)
continue;
if (PCI_HDRTYPE_MULTIFN(bhlcr)) maxfuncs = 7;
}
if (PCI_CLASS(pciclass) != PCI_CLASS_DISPLAY)
continue;
if (func > 0 && PCI_SUBCLASS(pciclass) == PCI_SUBCLASS_DISPLAY_MISC)
continue; // Likely an auxiliary display controller (#2034)
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(PCI_VENDOR(pciid)));
ffStrbufInit(&gpu->name);
ffStrbufInit(&gpu->driver);
ffStrbufInitS(&gpu->platformApi, pciDevPath);
ffStrbufInit(&gpu->memoryType);
gpu->index = FF_GPU_INDEX_UNSET;
gpu->temperature = FF_GPU_TEMP_UNSET;
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
gpu->type = FF_GPU_TYPE_UNKNOWN;
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
gpu->deviceId = ffGPUPciAddr2Id(0, bus, dev, func);
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
ffGPUQueryAmdGpuName(PCI_PRODUCT(pciid), PCI_REVISION(pciid), gpu);
if (gpu->name.length == 0)
ffGPUFillVendorAndName(PCI_SUBCLASS(pciclass), PCI_VENDOR(pciid), PCI_PRODUCT(pciid), gpu);
struct pciio_drvname drvname = {
.device = dev,
.function = func,
};
if (ioctl(pcifd, PCI_IOC_DRVNAME, &drvname) == 0)
ffStrbufInitS(&gpu->driver, drvname.name);
}
}
}
return NULL;
}
+95
View File
@@ -0,0 +1,95 @@
#include "gpu.h"
#include "common/io/io.h"
#include <sys/param.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <sys/pciio.h>
static inline int pciReadConf(int fd, uint32_t bus, uint32_t device, uint32_t func, uint32_t reg, uint32_t* result)
{
struct pci_io bdfr = {
.pi_sel = {
.pc_bus = bus,
.pc_dev = device,
.pc_func = func,
},
.pi_reg = reg,
.pi_width = 4,
};
if (ioctl(fd, PCIOCREAD, &bdfr) == -1)
return -1;
*result = bdfr.pi_data;
return 0;
}
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
{
char pciDevPath[] = "/dev/pci0";
FF_AUTO_CLOSE_FD int pcifd = open(pciDevPath, O_RDONLY | O_CLOEXEC);
if (pcifd < 0)
return "open(\"/dev/pci0\", O_RDONLY | O_CLOEXEC) failed";
for (uint32_t bus = 0; bus <= 255; bus++)
{
for (uint32_t dev = 0; dev < 32; dev++)
{
uint32_t maxfuncs = 0;
for (uint32_t func = 0; func <= maxfuncs; func++)
{
uint32_t pciid, pciclass;
if (pciReadConf(pcifd, bus, dev, func, PCI_ID_REG, &pciid) != 0)
continue;
if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID || PCI_VENDOR(pciid) == 0)
continue;
if (pciReadConf(pcifd, bus, dev, func, PCI_CLASS_REG, &pciclass) != 0)
continue;
if (func == 0)
{
// For some reason, pciReadConf returns success even for non-existing devices.
// So we need to check for `PCI_VENDOR(pciid) == PCI_VENDOR_INVALID` above to filter them out.
uint32_t bhlcr;
if (pciReadConf(pcifd, bus, dev, 0, PCI_BHLC_REG, &bhlcr) != 0)
continue;
if (PCI_HDRTYPE_MULTIFN(bhlcr)) maxfuncs = 7;
}
if (PCI_CLASS(pciclass) != PCI_CLASS_DISPLAY)
continue;
if (func > 0 && PCI_SUBCLASS(pciclass) == PCI_SUBCLASS_DISPLAY_MISC)
continue; // Likely an auxiliary display controller (#2034)
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(PCI_VENDOR(pciid)));
ffStrbufInit(&gpu->name);
ffStrbufInit(&gpu->driver);
ffStrbufInitS(&gpu->platformApi, "/dev/pci0");
ffStrbufInit(&gpu->memoryType);
gpu->index = FF_GPU_INDEX_UNSET;
gpu->temperature = FF_GPU_TEMP_UNSET;
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
gpu->type = FF_GPU_TYPE_UNKNOWN;
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
gpu->deviceId = ffGPUPciAddr2Id(0, bus, dev, func);
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
ffGPUQueryAmdGpuName(PCI_PRODUCT(pciid), PCI_REVISION(pciid), gpu);
if (gpu->name.length == 0)
ffGPUFillVendorAndName(PCI_SUBCLASS(pciclass), PCI_VENDOR(pciid), PCI_PRODUCT(pciid), gpu);
}
}
}
return NULL;
}
+3 -3
View File
@@ -95,7 +95,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
{
pciDev = (pciAddr >> 16) & 0xFFFF;
pciFunc = pciAddr & 0xFFFF;
gpu->deviceId = (pciBus * 1000ull) + (pciDev * 10ull) + pciFunc;
gpu->deviceId = ffGPUPciAddr2Id(0, pciBus, pciDev, pciFunc);
pciAddr = 1; // Set to 1 to indicate that the device is a PCI device
FF_DEBUG("PCI location - Bus: %u, Device: %u, Function: %u, DeviceID: %llu", pciBus, pciDev, pciFunc, gpu->deviceId);
}
@@ -182,7 +182,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
if (ffRegReadUint64(hDirectxKey, L"AdapterLuid", &adapterLuid, NULL))
{
FF_DEBUG("Found adapter LUID: %llu", adapterLuid);
if (!gpu->deviceId) gpu->deviceId = adapterLuid;
if (!gpu->deviceId) gpu->deviceId = ffGPUGeneral2Id(adapterLuid);
}
uint32_t featureLevel = 0;
@@ -449,7 +449,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
FF_DEBUG("Using fallback GPU type detection");
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL)
{
gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
FF_DEBUG("Intel GPU type determined: %s", gpu->type == FF_GPU_TYPE_INTEGRATED ? "Integrated" : "Discrete");
}
else if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET)
+7
View File
@@ -79,6 +79,13 @@ const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FF
gpu->deviceId = 0;
ffStrbufInitStatic(&gpu->platformApi, "DXCore");
LUID luid;
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::InstanceLuid, sizeof(luid), &luid)))
{
static_assert(sizeof(luid) == sizeof(uint64_t), "LUID size mismatch");
gpu->deviceId = ffGPUGeneral2Id(*(uint64_t*)&luid);
}
ffStrbufInit(&gpu->driver);
uint64_t value = 0;
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::DriverVersion, sizeof(value), &value)))
+2 -1
View File
@@ -45,7 +45,8 @@ const char* getOthersByIokit(FFHostResult* host)
const char* ffDetectHost(FFHostResult* host)
{
const char* error = ffSysctlGetString("hw.model", &host->family);
const char* error = ffSysctlGetString("hw.product", &host->family);
if (error) error = ffSysctlGetString("hw.model", &host->family);
if (error) return error;
ffStrbufSetStatic(&host->name, ffHostGetMacProductNameWithHwModel(&host->family));
+1 -1
View File
@@ -181,7 +181,7 @@ const char* ffHostGetMacProductNameWithHwModel(const FFstrbuf* hwModel)
#ifdef __x86_64__
bool ffHostDetectMac(FFHostResult* host)
{
if (ffStrbufEqualS(&host->family, "Mac") && ffStrbufEqualS(&host->vendor, "Apple Inc."))
if (ffStrbufStartsWithS(&host->family, "Mac") && ffStrbufEqualS(&host->vendor, "Apple Inc."))
{
const char* productName = ffHostGetMacProductNameWithHwModel(&host->name);
if (productName)
+3
View File
@@ -16,6 +16,9 @@ const char* ffDetectIcons(FFIconsResult* result)
ffRegReadUint(hKey, L"{645FF040-5081-101B-9F08-00AA002F954E}", &RecycleBin, NULL);
ffRegReadUint(hKey, L"{5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0}", &ControlPanel, NULL);
if (ThisPC && UsersFiles && RemoteNetwork && RecycleBin && ControlPanel)
return "All icons are hidden";
if (!ThisPC)
ffStrbufAppendS(&result->icons1, "This PC, ");
if (!UsersFiles)
+19 -4
View File
@@ -1,11 +1,21 @@
#include "media.h"
#include "common/io/io.h"
void ffDetectMediaImpl(FFMediaResult* media);
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover);
const FFMediaResult* ffDetectMedia(void)
static FFMediaResult result;
static void removeMediaCoverFile(void)
{
static FFMediaResult result;
if (result.cover.length > 0)
{
ffRemoveFile(result.cover.chars);
ffStrbufDestroy(&result.cover);
}
}
const FFMediaResult* ffDetectMedia(bool saveCover)
{
if (result.error.chars == NULL)
{
ffStrbufInit(&result.error);
@@ -16,7 +26,9 @@ const FFMediaResult* ffDetectMedia(void)
ffStrbufInit(&result.album);
ffStrbufInit(&result.url);
ffStrbufInit(&result.status);
ffDetectMediaImpl(&result);
ffStrbufInit(&result.cover);
result.removeCoverAfterUse = false;
ffDetectMediaImpl(&result, saveCover);
if(result.song.length == 0 && result.error.length == 0)
ffStrbufAppendS(&result.error, "No media found");
@@ -24,6 +36,9 @@ const FFMediaResult* ffDetectMedia(void)
ffStrbufTrimRightSpace(&result.artist);
ffStrbufTrimRightSpace(&result.album);
ffStrbufTrimRightSpace(&result.player);
if (saveCover && result.removeCoverAfterUse)
atexit(removeMediaCoverFile);
}
return &result;
+3 -1
View File
@@ -13,6 +13,8 @@ typedef struct FFMediaResult
FFstrbuf album;
FFstrbuf url;
FFstrbuf status;
FFstrbuf cover;
bool removeCoverAfterUse;
} FFMediaResult;
const FFMediaResult* ffDetectMedia();
const FFMediaResult* ffDetectMedia(bool saveCover);
+34 -9
View File
@@ -5,6 +5,7 @@
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import <CoreServices/CoreServices.h>
// https://github.com/andrewwiik/iOS-Blocks/blob/master/Widgets/Music/MediaRemote.h
extern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t dispatcher, void(^callback)(_Nullable CFDictionaryRef info)) __attribute__((weak_import));
@@ -12,7 +13,7 @@ extern void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queu
extern void MRMediaRemoteGetNowPlayingApplicationDisplayID(dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef displayID)) __attribute__((weak_import));
extern void MRMediaRemoteGetNowPlayingApplicationDisplayName(int unknown, dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef name)) __attribute__((weak_import));
static const char* getMediaByMediaRemote(FFMediaResult* result)
static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
{
#define FF_TEST_FN_EXISTANCE(fn) if (!fn) return "MediaRemote function " #fn " is not available"
FF_TEST_FN_EXISTANCE(MRMediaRemoteGetNowPlayingInfo);
@@ -30,6 +31,25 @@ static const char* getMediaByMediaRemote(FFMediaResult* result)
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoTitle"), &result->song);
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtist"), &result->artist);
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoAlbum"), &result->album);
if (saveCover)
{
NSData* artworkData = (__bridge NSData*) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkData"));
if (artworkData)
{
CFStringRef mime = (CFStringRef) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkMIMEType"));
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
FF_CFTYPE_AUTO_RELEASE CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mime, NULL);
FF_CFTYPE_AUTO_RELEASE CFStringRef ext = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
#pragma clang diagnostic pop
NSString *tmpDir = NSTemporaryDirectory();
NSString *uuid = NSUUID.UUID.UUIDString;
NSString *path = [tmpDir stringByAppendingPathComponent:[NSString stringWithFormat:@"ff_%@.%@", uuid, ext ? (__bridge NSString *) ext : @"img"]];
if ([artworkData writeToFile:path atomically:NO])
ffStrbufSetS(&result->cover, path.UTF8String);
}
}
}
else
error = "MRMediaRemoteGetNowPlayingInfo() failed";
@@ -71,7 +91,7 @@ static const char* getMediaByMediaRemote(FFMediaResult* result)
}
__attribute__((visibility("default"), used))
int ffPrintMediaByMediaRemote(void)
int ffPrintMediaByMediaRemote(bool saveCover)
{
FFMediaResult media = {
.status = ffStrbufCreate(),
@@ -80,8 +100,9 @@ int ffPrintMediaByMediaRemote(void)
.album = ffStrbufCreate(),
.playerId = ffStrbufCreate(),
.player = ffStrbufCreate(),
.cover = ffStrbufCreate(),
};
if (getMediaByMediaRemote(&media) != NULL)
if (getMediaByMediaRemote(&media, saveCover) != NULL)
return 1;
ffStrbufPutTo(&media.status, stdout);
ffStrbufPutTo(&media.song, stdout);
@@ -89,19 +110,21 @@ int ffPrintMediaByMediaRemote(void)
ffStrbufPutTo(&media.album, stdout);
ffStrbufPutTo(&media.playerId, stdout);
ffStrbufPutTo(&media.player, stdout);
ffStrbufWriteTo(&media.cover, stdout);
ffStrbufDestroy(&media.status);
ffStrbufDestroy(&media.song);
ffStrbufDestroy(&media.artist);
ffStrbufDestroy(&media.album);
ffStrbufDestroy(&media.playerId);
ffStrbufDestroy(&media.player);
ffStrbufDestroy(&media.cover);
return 0;
}
static const char* getMediaByAuthorizedProcess(FFMediaResult* result)
static const char* getMediaByAuthorizedProcess(FFMediaResult* result, bool saveCover)
{
// #1737
FF_STRBUF_AUTO_DESTROY script = ffStrbufCreateF("import ctypes;ctypes.CDLL('%s').ffPrintMediaByMediaRemote()", instance.state.platform.exePath.chars);
FF_STRBUF_AUTO_DESTROY script = ffStrbufCreateF("import ctypes;ctypes.CDLL('%s').ffPrintMediaByMediaRemote(%s)", instance.state.platform.exePath.chars, saveCover ? "True" : "False");
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
const char* error = ffProcessAppendStdOut(&buffer, (char* const[]) {
@@ -114,7 +137,7 @@ static const char* getMediaByAuthorizedProcess(FFMediaResult* result)
if (buffer.length == 0) return "No media found";
// status\ntitle\nartist\nalbum\nbundleName\nappName
FFstrbuf* const varList[] = { &result->status, &result->song, &result->artist, &result->album, &result->playerId, &result->player };
FFstrbuf* const varList[] = { &result->status, &result->song, &result->artist, &result->album, &result->playerId, &result->player, &result->cover };
char* line = NULL;
size_t len = 0;
for (uint32_t i = 0; i < ARRAY_SIZE(varList) && ffStrbufGetline(&line, &len, &buffer); ++i)
@@ -122,13 +145,13 @@ static const char* getMediaByAuthorizedProcess(FFMediaResult* result)
return NULL;
}
void ffDetectMediaImpl(FFMediaResult* media)
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
{
const char* error;
if (@available(macOS 15.4, *))
error = getMediaByAuthorizedProcess(media);
error = getMediaByAuthorizedProcess(media, saveCover);
else
error = getMediaByMediaRemote(media);
error = getMediaByMediaRemote(media, saveCover);
if (error)
ffStrbufAppendS(&media->error, error);
else if (media->player.length == 0 && media->playerId.length > 0)
@@ -137,5 +160,7 @@ void ffDetectMediaImpl(FFMediaResult* media)
if (ffStrbufStartsWithIgnCaseS(&media->player, "com."))
ffStrbufSubstrAfter(&media->player, strlen("com.") - 1);
ffStrbufReplaceAllC(&media->player, '.', ' ');
if (media->cover.length > 0)
media->removeCoverAfterUse = true;
}
}
+53 -15
View File
@@ -1,6 +1,8 @@
#include "common/io/io.h"
#include "fastfetch.h"
#include "detection/media/media.h"
#include "util/stringUtils.h"
#include "util/unused.h"
#include <string.h>
@@ -52,21 +54,56 @@ static bool parseMprisMetadata(FFDBusData* data, DBusMessageIter* rootIterator,
data->lib->ffdbus_message_iter_next(&dictIterator);
if(!ffStrStartsWith(key, "xesam:"))
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
if(ffStrStartsWith(key, "xesam:"))
{
const char* xesam = key + strlen("xesam:");
if(ffStrEquals(xesam, "title"))
ffDBusGetString(data, &dictIterator, &result->song);
else if(ffStrEquals(xesam, "album"))
ffDBusGetString(data, &dictIterator, &result->album);
else if(ffStrEquals(xesam, "artist"))
ffDBusGetString(data, &dictIterator, &result->artist);
else if(ffStrEquals(xesam, "url"))
ffDBusGetString(data, &dictIterator, &result->url);
key += strlen("xesam:");
if(ffStrEquals(key, "title"))
ffDBusGetString(data, &dictIterator, &result->song);
else if(ffStrEquals(key, "album"))
ffDBusGetString(data, &dictIterator, &result->album);
else if(ffStrEquals(key, "artist"))
ffDBusGetString(data, &dictIterator, &result->artist);
else if(ffStrEquals(key, "url"))
ffDBusGetString(data, &dictIterator, &result->url);
if(result->song.length > 0 && result->artist.length > 0 && result->album.length > 0 && result->url.length > 0)
break;
if(result->song.length > 0 && result->artist.length > 0 && result->album.length > 0 && result->url.length > 0)
break;
}
else if (ffStrStartsWith(key, "mpris:"))
{
const char* xesam = key + strlen("mpris:");
if(ffStrEquals(xesam, "artUrl"))
{
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
ffDBusGetString(data, &dictIterator, &path);
if (ffStrbufStartsWithS(&path, "file:///"))
{
ffStrbufEnsureFree(&result->cover, path.length - (uint32_t) strlen("file://"));
for (uint32_t i = (uint32_t) strlen("file://"); i < path.length; ++i)
{
if (path.chars[i] == '%')
{
if (i + 2 >= path.length)
break;
char str[] = { path.chars[i + 1], path.chars[i + 2], 0 };
char* end = NULL;
const char decodedChar = (char) strtoul(str, &end, 16);
if (end == &str[2])
{
i += 2;
ffStrbufAppendC(&result->cover, decodedChar);
}
else
ffStrbufAppendC(&result->cover, '%');
}
else
{
ffStrbufAppendC(&result->cover, path.chars[i]);
}
}
}
}
}
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
}
@@ -241,8 +278,9 @@ static const char* getMedia(FFMediaResult* result)
#endif
void ffDetectMediaImpl(FFMediaResult* media)
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
{
FF_UNUSED(saveCover); // We don't save the cover to a file for Mpris implementation
#ifdef FF_HAVE_DBUS
const char* error = getMedia(media);
ffStrbufAppendS(&media->error, error);
+7 -4
View File
@@ -3,7 +3,7 @@
#include "media.h"
#include "media_windows.dll.h"
static const char* getMedia(FFMediaResult* media)
static const char* getMedia(FFMediaResult* media, bool saveCover)
{
FF_LIBRARY_LOAD(libffwinrt, "dlopen libffwinrt" FF_LIBRARY_EXTENSION " failed", "libffwinrt" FF_LIBRARY_EXTENSION, 0)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libffwinrt, ffWinrtDetectMedia)
@@ -11,7 +11,7 @@ static const char* getMedia(FFMediaResult* media)
FFWinrtMediaResult result = {};
const char* error = ffffWinrtDetectMedia(&result);
const char* error = ffffWinrtDetectMedia(&result, saveCover);
if (error)
{
ffStrbufSetStatic(&media->error, error);
@@ -32,12 +32,15 @@ static const char* getMedia(FFMediaResult* media)
ffStrbufSetWS(&media->song, result.song);
ffStrbufSetWS(&media->artist, result.artist);
ffStrbufSetWS(&media->album, result.album);
ffStrbufSetWS(&media->cover, result.cover);
ffStrbufSetStatic(&media->status, result.status);
if (media->cover.length > 0)
media->removeCoverAfterUse = true;
return NULL;
}
void ffDetectMediaImpl(FFMediaResult* media)
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
{
const char* error = getMedia(media);
const char* error = getMedia(media, saveCover);
ffStrbufAppendS(&media->error, error);
}
+45 -2
View File
@@ -1,13 +1,16 @@
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Media.Control.h>
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Storage.h>
#include <wchar.h>
#include <windows.h>
extern "C"
{
#include "media_windows.dll.h"
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result)
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result, bool saveCover)
{
// C++/WinRT requires Windows 8.1+ and C++ runtime (std::string, exceptions and other stuff)
// Make it a separate dll in order not to break Windows 7 support
@@ -47,15 +50,55 @@ const char* ffWinrtDetectMedia(FFWinrtMediaResult* result)
}
::wcsncpy(result->playerId, session.SourceAppUserModelId().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
result->playerId[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
::wcsncpy(result->song, mediaProps.Title().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
result->song[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
::wcsncpy(result->artist, mediaProps.Artist().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
result->artist[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
::wcsncpy(result->album, mediaProps.AlbumTitle().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
result->album[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
try
{
// Only works for UWP apps
::wcsncpy(result->playerName, AppInfo::GetFromAppUserModelId(session.SourceAppUserModelId()).DisplayInfo().DisplayName().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
result->playerName[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
} catch (...) { }
if (saveCover)
{
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Storage::Streams;
if (auto thumbRef = mediaProps.Thumbnail())
{
try
{
if (auto stream = thumbRef.OpenReadAsync().get())
{
if (stream.Size() > 0)
{
Buffer buffer(static_cast<uint32_t>(stream.Size()));
stream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None).get();
wchar_t tempPath[MAX_PATH];
if (GetTempPathW(MAX_PATH, tempPath) > 0)
{
auto tempFolder = StorageFolder::GetFolderFromPathAsync(tempPath).get();
auto tempFile = tempFolder.CreateFileAsync(L"ff_thumb.img", CreationCollisionOption::GenerateUniqueName).get();
FileIO::WriteBufferAsync(tempFile, buffer).get();
::wcsncpy(result->cover, tempFile.Path().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
result->cover[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
}
}
}
}
catch (...)
{
// Ignore thumbnail errors
}
}
}
return NULL;
}
catch (...)
+2 -1
View File
@@ -9,8 +9,9 @@ typedef struct FFWinrtMediaResult
wchar_t song[FF_MEDIA_WIN_RESULT_BUFLEN];
wchar_t artist[FF_MEDIA_WIN_RESULT_BUFLEN];
wchar_t album[FF_MEDIA_WIN_RESULT_BUFLEN];
wchar_t cover[FF_MEDIA_WIN_RESULT_BUFLEN];
const char* status;
} FFWinrtMediaResult;
__attribute__((__dllexport__))
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result);
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result, bool saveCover);
+12 -11
View File
@@ -8,24 +8,25 @@
const char* ffDetectMemory(FFMemoryResult* ram)
{
size_t length = sizeof(ram->bytesTotal);
if (sysctl((int[]){ CTL_HW, HW_MEMSIZE }, 2, &ram->bytesTotal, &length, NULL, 0))
if (sysctl((int[]){ CTL_HW, HW_MEMSIZE }, 2, &ram->bytesTotal, &length, NULL, 0) != 0)
return "Failed to read hw.memsize";
uint64_t usableMemory = 0;
length = sizeof(usableMemory);
if (sysctlbyname("hw.memsize_usable", &usableMemory, &length, NULL, 0) != 0)
usableMemory = ram->bytesTotal;
mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
vm_statistics64_data_t vmstat;
if(host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t) (&vmstat), &count) != KERN_SUCCESS)
return "Failed to read host_statistics64";
// https://github.com/exelban/stats/blob/master/Modules/RAM/readers.swift#L56
ram->bytesUsed = ((uint64_t)
+ vmstat.active_count
+ vmstat.inactive_count
+ vmstat.speculative_count
+ vmstat.wire_count
+ vmstat.compressor_page_count
- vmstat.purgeable_count
- vmstat.external_page_count
) * instance.state.platform.sysinfo.pageSize;
uint64_t pageSize = instance.state.platform.sysinfo.pageSize;
uint64_t appMemory = vmstat.internal_page_count * pageSize;
uint64_t wiredMemory = vmstat.wire_count * pageSize;
uint64_t compressed = vmstat.compressor_page_count * pageSize;
uint64_t reserved = ram->bytesTotal - usableMemory;
ram->bytesUsed = appMemory + wiredMemory + compressed + reserved;
return NULL;
}
+1 -1
View File
@@ -240,7 +240,7 @@ FF_MAYBE_UNUSED static bool detectDebianDerived(FFOSResult* result)
ffStrbufSetStatic(&result->id, "raspbian");
ffStrbufSetStatic(&result->idLike, "debian");
ffStrbufSetStatic(&result->name, "Raspberry Pi OS");
ffStrbufSetStatic(&result->prettyName, "Raspberry Pi OS");
getDebianVersion(result);
return true;
}
else if (ffPathExists("/boot/dietpi/.version", FF_PATHTYPE_FILE))
+1
View File
@@ -21,6 +21,7 @@ typedef struct FFPackagesResult
uint32_t guixUser;
uint32_t hpkgSystem;
uint32_t hpkgUser;
uint32_t kiss;
uint32_t linglong;
uint32_t lpkg;
uint32_t lpkgbuild;
+1
View File
@@ -420,6 +420,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts,
if (!(options->disabled & FF_PACKAGES_FLAG_EMERGE_BIT)) packageCounts->emerge += countFilesRecursive(baseDir, "/var/db/pkg", "SIZE");
if (!(options->disabled & FF_PACKAGES_FLAG_EOPKG_BIT)) packageCounts->eopkg += getNumElements(baseDir, "/var/lib/eopkg/package", true);
if (!(options->disabled & FF_PACKAGES_FLAG_FLATPAK_BIT)) packageCounts->flatpakSystem += getFlatpakPackages(baseDir, "/var/lib");
if (!(options->disabled & FF_PACKAGES_FLAG_KISS_BIT)) packageCounts->kiss += getNumElements(baseDir, "/var/db/kiss/installed", true);
if (!(options->disabled & FF_PACKAGES_FLAG_NIX_BIT))
{
packageCounts->nixDefault += ffPackagesGetNix(baseDir, "/nix/var/nix/profiles/default");
+56 -24
View File
@@ -2,35 +2,67 @@
#include "common/processing.h"
#include "util/stringUtils.h"
#include "util/path.h"
#include "util/windows/unicode.h"
#include "util/mallocHelper.h"
#include "common/io/io.h"
#include <handleapi.h>
#include <fileapi.h>
#include <windows.h>
#include "util/windows/nt.h"
#include <ntstatus.h>
static uint32_t getNumElements(const char* searchPath /* including `\*` suffix */, DWORD type, const char* ignore)
static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t* ignore)
{
uint32_t counter = 0;
bool flag = ignore == NULL;
WIN32_FIND_DATAA wfd;
HANDLE hFind = FindFirstFileA(searchPath, &wfd);
FF_AUTO_CLOSE_FD HANDLE dfd = CreateFileA(searchPath, FILE_LIST_DIRECTORY | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (dfd == INVALID_HANDLE_VALUE) return 0;
if (hFind != INVALID_HANDLE_VALUE)
{
do // Managed to locate and create an handle to that folder.
bool flag = ignore == NULL;
uint32_t counter = 0;
uint8_t buffer[64 * 1024] __attribute__((aligned(8))); // Required for WoA
BOOLEAN firstScan = TRUE;
size_t ignoreLen = ignore ? wcslen(ignore) : 0;
while (true) {
IO_STATUS_BLOCK ioStatus = {};
NTSTATUS status = NtQueryDirectoryFile(
dfd,
NULL, NULL, NULL,
&ioStatus,
buffer, ARRAY_SIZE(buffer),
FileDirectoryInformation,
FALSE,
NULL,
firstScan
);
firstScan = FALSE;
if (!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW) break;
for (FILE_DIRECTORY_INFORMATION* entry = (FILE_DIRECTORY_INFORMATION*) buffer;
;
entry = (FILE_DIRECTORY_INFORMATION*) ((uint8_t*) entry + entry->NextEntryOffset))
{
if(!(wfd.dwFileAttributes & type)) continue;
if(!flag && ffStrEqualsIgnCase(ignore, wfd.cFileName))
if (!(entry->FileAttributes & type)) continue;
if (!flag &&
ignoreLen == entry->FileNameLength / sizeof(*entry->FileName) &&
_wcsnicmp(entry->FileName, ignore, ignoreLen) == 0)
{
flag = true;
continue;
}
counter++;
} while (FindNextFileA(hFind, &wfd));
FindClose(hFind);
if(type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2)
counter -= 2; // accounting for . and ..
counter++;
if (entry->NextEntryOffset == 0) break;
}
if (status == STATUS_SUCCESS) break; // No next page
}
if(type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2)
counter -= 2; // accounting for . and ..
return counter;
}
@@ -65,8 +97,8 @@ static void detectScoop(FFPackagesResult* result)
ffStrbufSet(&scoopPath, &instance.state.platform.homeDir);
ffStrbufAppendS(&scoopPath, "/scoop");
}
ffStrbufAppendS(&scoopPath, "/apps/*");
result->scoopUser = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, "scoop");
ffStrbufAppendS(&scoopPath, "/apps/");
result->scoopUser = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, L"scoop");
}
{
@@ -78,8 +110,8 @@ static void detectScoop(FFPackagesResult* result)
ffStrbufSetS(&scoopPath, getenv("ProgramData"));
ffStrbufAppendS(&scoopPath, "/scoop");
}
ffStrbufAppendS(&scoopPath, "/apps/*");
result->scoopGlobal = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, "scoop");
ffStrbufAppendS(&scoopPath, "/apps/");
result->scoopGlobal = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, L"scoop");
}
}
@@ -91,8 +123,8 @@ static void detectChoco(FF_MAYBE_UNUSED FFPackagesResult* result)
char chocoPath[MAX_PATH + 3];
char* pend = ffStrCopy(chocoPath, chocoInstall, ARRAY_SIZE(chocoPath));
ffStrCopy(pend, "/lib/*", ARRAY_SIZE(chocoPath) - (size_t) (pend - chocoPath));
result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY, "choco");
ffStrCopy(pend, "/lib/", ARRAY_SIZE(chocoPath) - (size_t) (pend - chocoPath));
result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY, L"choco");
}
static void detectPacman(FFPackagesResult* result)
@@ -104,7 +136,7 @@ static void detectPacman(FFPackagesResult* result)
// MSYS2
char pacmanPath[MAX_PATH + 3];
char* pend = ffStrCopy(pacmanPath, msystemPrefix, ARRAY_SIZE(pacmanPath));
ffStrCopy(pend, "/../var/lib/pacman/local/*", ARRAY_SIZE(pacmanPath) - (size_t) (pend - pacmanPath));
ffStrCopy(pend, "/../var/lib/pacman/local/", ARRAY_SIZE(pacmanPath) - (size_t) (pend - pacmanPath));
result->pacman = getNumElements(pacmanPath, FILE_ATTRIBUTE_DIRECTORY, NULL);
}
+4
View File
@@ -70,6 +70,10 @@ const char* ffDetectPublicIp(FFPublicIPOptions* options, FFPublicIpResult* resul
FF_STRBUF_AUTO_DESTROY response = ffStrbufCreateA(4096);
const char* error = ffNetworkingRecvHttpResponse(state, &response);
*state = (FFNetworkingState){};
*status = FF_UNITIALIZED;
if (error == NULL)
ffStrbufSubstrAfterFirstS(&response, "\r\n\r\n");
else
+23 -1
View File
@@ -5,8 +5,9 @@
#include <winternl.h>
#include <ntstatus.h>
#include <windows.h>
#include <psapi.h>
const char* ffDetectSwap(FFlist* result)
const char* detectByNqsi(FFlist* result)
{
uint8_t buffer[4096];
ULONG size = sizeof(buffer);
@@ -26,6 +27,27 @@ const char* ffDetectSwap(FFlist* result)
if (current->NextEntryOffset == 0)
break;
}
return NULL;
}
const char* detectByKgpi(FFlist* result)
{
PERFORMANCE_INFORMATION pi = {};
if (!K32GetPerformanceInfo(&pi, sizeof(pi)))
return "K32GetPerformanceInfo(&pi, sizeof(pi)) failed";
FFSwapResult* swap = ffListAdd(result);
ffStrbufInitS(&swap->name, "Page File");
swap->bytesTotal = (uint64_t) (pi.CommitLimit > pi.PhysicalTotal ? pi.CommitLimit - pi.PhysicalTotal : 0) * pi.PageSize;
swap->bytesUsed = (uint64_t) (pi.CommitTotal > pi.PhysicalTotal ? pi.CommitTotal - pi.PhysicalTotal : 0) * pi.PageSize;
return NULL;
}
const char* ffDetectSwap(FFlist* result)
{
const char* err = detectByNqsi(result);
if (err == NULL)
return NULL;
return detectByKgpi(result);
}
+36 -21
View File
@@ -8,13 +8,18 @@
static void detectAlacritty(FFTerminalFontResult* terminalFont)
{
FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreate();
// Maybe using a toml parser to read the config file is better?
// https://github.com/cktan/tomlc17
// Doc: https://alacritty.org/config-alacritty.html#s26
FF_STRBUF_AUTO_DESTROY fontNormal = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontFamily = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontStyle = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate();
do {
// Latest alacritty uses toml instead of yaml
FFpropquery fontQueryToml[] = {
{"family =", &fontName},
{"normal =", &fontNormal},
{"size =", &fontSize},
};
@@ -25,29 +30,39 @@ static void detectAlacritty(FFTerminalFontResult* terminalFont)
break;
if(ffParsePropFileConfigValues(".alacritty.toml", 2, fontQueryToml))
break;
FFpropquery fontQueryYaml[] = {
{"family:", &fontName},
{"size:", &fontSize},
};
if(ffParsePropFileConfigValues("alacritty/alacritty.yml", 2, fontQueryYaml))
break;
if(ffParsePropFileConfigValues("alacritty.yml", 2, fontQueryYaml))
break;
if(ffParsePropFileConfigValues(".alacritty.yml", 2, fontQueryYaml))
break;
} while (false);
//by default alacritty uses its own font called alacritty
if(fontName.length == 0)
ffStrbufAppendS(&fontName, "alacritty");
if(fontNormal.length > 0)
{
// { family = "Fira Code", style = "Medium" }
ffStrbufTrimSpace(&fontNormal);
ffStrbufTrimRight(&fontNormal, '}');
ffStrbufTrimLeft(&fontNormal, '{');
ffStrbufTrimSpace(&fontNormal);
// family = "Fira Code", style = "Medium"
ffStrbufReplaceAllC(&fontNormal, ',', '\n'); // Assume no commas in font names
ffParsePropLines(fontNormal.chars, "family =", &fontFamily);
ffParsePropLines(fontNormal.chars, "style =", &fontStyle);
}
if (fontFamily.length == 0)
{
#if __APPLE__
ffStrbufSetStatic(&fontFamily, "Menlo");
#elif _WIN32
ffStrbufSetStatic(&fontFamily, "Consolas");
#else
ffStrbufSetStatic(&fontFamily, "monospace");
#endif
}
if (fontStyle.length == 0)
ffStrbufSetStatic(&fontStyle, "Regular");
// the default font size is 11
if(fontSize.length == 0)
ffStrbufAppendS(&fontSize, "11");
ffStrbufSetStatic(&fontSize, "11.25");
ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars);
ffFontInitMoveValues(&terminalFont->font, &fontFamily, &fontSize, &fontStyle);
}
static void detectGhostty(const FFstrbuf* exe, FFTerminalFontResult* terminalFont)
+83 -39
View File
@@ -2,10 +2,12 @@
#include "common/io/io.h"
#include "common/processing.h"
#include "common/properties.h"
#include "util/path.h"
#include "util/stringUtils.h"
#include "util/binary.h"
#include <ctype.h>
#include <stdint.h>
#ifdef __FreeBSD__
#include <paths.h>
#ifndef _PATH_LOCALBASE
@@ -63,12 +65,9 @@ static bool extractBashVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, v
return false;
}
static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* exePath, FFstrbuf* version)
static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* version)
{
const char* path = exePath->chars;
if (*path == '\0')
path = exe->chars;
ffBinaryExtractStrings(path, extractBashVersion, version, (uint32_t) strlen("@(#)Bash version 0.0.0(0) release GNU"));
ffBinaryExtractStrings(exe->chars, extractBashVersion, version, (uint32_t) strlen("@(#)Bash version 0.0.0(0) release GNU"));
if (version->length > 0) return true;
if(!getExeVersionRaw(exe, version))
@@ -85,8 +84,10 @@ static bool getShellVersionFish(FFstrbuf* exe, FFstrbuf* version)
if(!getExeVersionRaw(exe, version))
return false;
//fish, version 4.0.2-1 (Built by MSYS2 project)
ffStrbufSubstrAfterFirstS(version, "version ");
//fish, version 4.0.2-1 (Built by MSYS2 project) // version can be localized if LC_ALL is set
if (version->length < strlen("fish, v") || !ffStrbufStartsWithS(version, "fish")) return false;
uint32_t index = ffStrbufNextIndexC(version, strlen("fish, "), ' ');
ffStrbufSubstrAfter(version, index);
ffStrbufSubstrBeforeFirstC(version, ' ');
return true;
}
@@ -119,35 +120,39 @@ static bool getShellVersionPwsh(FFstrbuf* exe, FFstrbuf* version)
static bool getShellVersionKsh(FFstrbuf* exe, FFstrbuf* version)
{
#if __OpenBSD__ || __NetBSD__
if(ffProcessAppendStdErr(version, (char* const[]) {
exe->chars,
"--version",
NULL
}) == NULL && ffStrbufSubstrAfterFirstS(version, " (AT&T Research) "))
{
// version sh (AT&T Research) 93u+ 2012-08-01
ffStrbufSubstrBeforeFirstC(version, ' ');
return true;
}
ffStrbufClear(version);
if(ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"-c",
"echo $KSH_VERSION",
NULL
}) != NULL)
return false;
}) == NULL && ffStrbufSubstrAfterFirstS(version, " KSH "))
{
// OKSH: @(#)PD KSH v5.2.14 99/07/13.2
// MKSH: @(#)MIRBSD KSH R59 2025/04/26 +Debian
// $OKSH_VERSION doesn't exist on OpenBSD
ffStrbufSubstrBeforeFirstC(version, ' ');
ffStrbufTrimLeft(version, 'v');
return true;
}
// @(#)PD KSH v5.2.14 99/07/13.2
ffStrbufSubstrAfterFirstC(version, 'v');
ffStrbufSubstrBeforeFirstC(version, ' ');
#else
if(ffProcessAppendStdErr(version, (char* const[]) {
exe->chars,
"--version",
NULL
}) != NULL)
return false;
// version sh (AT&T Research) 93u+ 2012-08-01
ffStrbufSubstrAfterLastC(version, ')');
ffStrbufTrim(version, ' ');
#endif
return true;
return false;
}
static bool getShellVersionOksh(FFstrbuf* exe, FFstrbuf* version)
{
// Homebrew version
if(ffProcessAppendStdOut(version, (char* const[]) {
exe->chars,
"-c",
@@ -227,13 +232,9 @@ static bool extractZshVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, vo
return false;
}
static bool getShellVersionZsh(FFstrbuf* exe, FFstrbuf* exePath, FFstrbuf* version)
static bool getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version)
{
const char* path = exePath->chars;
if (*path == '\0')
path = exe->chars;
ffBinaryExtractStrings(path, extractZshVersion, version, (uint32_t) strlen("zsh-0.0-0"));
ffBinaryExtractStrings(exe->chars, extractZshVersion, version, (uint32_t) strlen("zsh-0.0-0"));
if (version->length) return true;
return getExeVersionGeneral(exe, version); //zsh 5.9 (arm-apple-darwin21.3.0)
@@ -260,7 +261,7 @@ static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version)
}
#endif
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, FFstrbuf* version)
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
{
if (!instance.config.general.detectVersion) return false;
@@ -268,9 +269,9 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath,
return false;
if(ffStrEqualsIgnCase(exeName, "bash"))
return getShellVersionBash(exe, exePath, version);
return getShellVersionBash(exe, version);
if(ffStrEqualsIgnCase(exeName, "zsh"))
return getShellVersionZsh(exe, exePath, version);
return getShellVersionZsh(exe, version);
if(ffStrEqualsIgnCase(exeName, "fish"))
return getShellVersionFish(exe, version);
if(ffStrEqualsIgnCase(exeName, "pwsh"))
@@ -279,7 +280,7 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath,
return getExeVersionGeneral(exe, version); //tcsh 6.24.07 (Astron) 2022-12-21 (aarch64-apple-darwin) options wide,nls,dl,al,kan,sm,rh,color,filec
if(ffStrEqualsIgnCase(exeName, "nu"))
return getShellVersionNushell(exe, version);
if(ffStrEqualsIgnCase(exeName, "ksh"))
if(ffStrEqualsIgnCase(exeName, "ksh") || ffStrEqualsIgnCase(exeName, "mksh"))
return getShellVersionKsh(exe, version);
if(ffStrEqualsIgnCase(exeName, "oksh"))
return getShellVersionOksh(exe, version);
@@ -291,6 +292,8 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath,
return getShellVersionAsh(exe, version);
if(ffStrEqualsIgnCase(exeName, "xonsh"))
return getShellVersionXonsh(exe, version);
if(ffStrEqualsIgnCase(exeName, "brush"))
return getExeVersionGeneral(exe, version); // brush 0.2.23 (git:2835487)
#ifdef _WIN32
if(ffStrEqualsIgnCase(exeName, "powershell") || ffStrEqualsIgnCase(exeName, "powershell_ise"))
@@ -320,7 +323,7 @@ static bool extractGeneralVersion(const char *str, FF_MAYBE_UNUSED uint32_t len,
FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* version)
{
if (exe->chars[0] == '/')
if (ffIsAbsolutePath(exe->chars))
{
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
if (version->length) return true;
@@ -340,7 +343,7 @@ FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* ver
FF_MAYBE_UNUSED static bool getTerminalVersionXfce4Terminal(FFstrbuf* exe, FFstrbuf* version)
{
if (exe->chars[0] == '/')
if (ffIsAbsolutePath(exe->chars))
{
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
if (version->length) return true;
@@ -577,6 +580,44 @@ static bool getTerminalVersionZed(FFstrbuf* exe, FFstrbuf* version)
return true;
}
static bool extractSshdVersion(const char *str, FF_MAYBE_UNUSED uint32_t len, void *userdata)
{
if (!ffStrStartsWith(str, "OpenSSH_") || !ffCharIsDigit(str[strlen("OpenSSH_")])) return true;
str += strlen("OpenSSH_");
int count = 0;
sscanf(str, "%*d.%*dp%*d%n", &count);
if (count == 0) return true;
ffStrbufSetS((FFstrbuf*) userdata, str);
return false;
}
static bool getTerminalVersionSshd(FFstrbuf* exe, FFstrbuf* version)
{
FF_STRBUF_AUTO_DESTROY exePath = ffStrbufCreate();
if (ffIsAbsolutePath(exe->chars))
ffStrbufSet(&exePath, exe);
else if (ffFindExecutableInPath("sshd", &exePath) != NULL)
return false;
ffBinaryExtractStrings(exePath.chars, extractSshdVersion, version, (uint32_t) strlen("OpenSSH0.0"));
if (version->length) return true;
if(ffProcessAppendStdOut(version, (char* const[]) {
exePath.chars,
"-V",
NULL
}) != NULL)
return false;
if (ffStrbufStartsWithS(version, "unknown ")) // `unknown option -- V` (ancient OpenSSH version)
ffStrbufSubstrAfterFirstC(version, '\n');
// OpenSSH_10.0p2 Ubuntu-5ubuntu5, OpenSSL 3.5.3 16 Sep 2025
ffStrbufSubstrAfterFirstC(version, '_');
ffStrbufSubstrBeforeFirstC(version, ',');
return true;
}
#ifndef _WIN32
static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version)
{
@@ -676,7 +717,7 @@ FF_MAYBE_UNUSED static bool getTerminalVersionPtyxis(FF_MAYBE_UNUSED FFstrbuf* e
FF_MAYBE_UNUSED static bool getTerminalVersionTilix(FFstrbuf* exe, FFstrbuf* version)
{
if (exe->chars[0] == '/')
if (ffIsAbsolutePath(exe->chars))
{
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
if (version->length) return true;
@@ -920,6 +961,9 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe
if(ffStrbufStartsWithIgnCaseS(processName, "tmux"))
return getTerminalVersionTmux(exe, version);
if(ffStrbufIgnCaseEqualS(processName, "sshd") || ffStrbufStartsWithIgnCaseS(processName, "sshd-"))
return getTerminalVersionSshd(exe, version);
#ifdef _WIN32
return getFileVersion(exe, NULL, version);
@@ -57,6 +57,9 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid)
ffStrbufEqualS(&result->processName, "flashfetch") ||
ffStrbufEqualS(&result->processName, "proot") ||
ffStrbufEqualS(&result->processName, "script") ||
#ifdef __linux__
ffStrbufEqualS(&result->processName, "run-parts") ||
#endif
ffStrbufContainS(&result->processName, "debug") ||
ffStrbufContainS(&result->processName, "command-not-") ||
ffStrbufEndsWithS(&result->processName, ".sh")
@@ -113,6 +116,7 @@ static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid)
#ifdef __linux__
ffStrbufStartsWithS(&result->processName, "Relay(") || // Unknown process in WSL2
ffStrbufStartsWithS(&result->processName, "flatpak-") || // #707
ffStrbufEqualS(&result->processName, "run-parts") || // #2048
#endif
ffStrbufEndsWithS(&result->processName, ".sh")
)
@@ -288,14 +292,14 @@ static void getUserShellFromEnv(FFShellResult* result)
}
}
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, FFstrbuf* version);
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version);
static void setShellInfoDetails(FFShellResult* result)
{
ffStrbufClear(&result->version);
fftsGetShellVersion(&result->exe, result->exeName, &result->exePath, &result->version);
fftsGetShellVersion(result->exePath.length > 0 ? &result->exePath : &result->exe, result->exeName, &result->version);
if(ffStrbufEqualS(&result->processName, "pwsh"))
ffStrbufInitStatic(&result->prettyName, "PowerShell");
@@ -327,7 +331,12 @@ static void setTerminalInfoDetails(FFTerminalResult* result)
else if(ffStrbufStartsWithS(&result->processName, "screen-"))
ffStrbufInitStatic(&result->prettyName, "screen");
else if(ffStrbufEqualS(&result->processName, "sshd") || ffStrbufStartsWithS(&result->processName, "sshd-"))
ffStrbufInitCopy(&result->prettyName, &result->tty);
{
if (result->tty.length)
ffStrbufInitCopy(&result->prettyName, &result->tty);
else
ffStrbufSetStatic(&result->prettyName, "sshd");
}
#if defined(__ANDROID__)
@@ -379,7 +388,7 @@ static void setTerminalInfoDetails(FFTerminalResult* result)
else
ffStrbufInitCopy(&result->prettyName, &result->processName);
fftsGetTerminalVersion(&result->processName, &result->exe, &result->version);
fftsGetTerminalVersion(&result->processName, result->exePath.length > 0 ? &result->exePath : &result->exe, &result->version);
}
#if defined(MAXPATH)
@@ -12,7 +12,7 @@
#include <wchar.h>
#include <tlhelp32.h>
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, const FFstrbuf* exePath, FFstrbuf* version);
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
static uint32_t getShellInfo(FFShellResult* result, uint32_t pid)
{
@@ -332,7 +332,7 @@ const FFShellResult* ffDetectShell(void)
strcpy(tmp, result.exeName);
char* ext = strrchr(tmp, '.');
if (ext) *ext = '\0';
fftsGetShellVersion(&result.exe, tmp, &result.exePath, &result.version);
fftsGetShellVersion(result.exePath.length > 0 ? &result.exePath : &result.exe, tmp, &result.version);
}
return &result;
@@ -368,7 +368,7 @@ const FFTerminalResult* ffDetectTerminal(void)
if(result.processName.length > 0)
{
setTerminalInfoDetails(&result);
fftsGetTerminalVersion(&result.processName, &result.exe, &result.version);
fftsGetTerminalVersion(&result.processName, result.exePath.length > 0 ? &result.exePath : &result.exe, &result.version);
}
return &result;
+164 -23
View File
@@ -1,6 +1,10 @@
#include "common/io/io.h"
#include "common/properties.h"
#include "fastfetch.h"
#include "users.h"
#include <unistd.h>
#if FF_HAVE_UTMPX
#include <utmpx.h>
#else
@@ -15,7 +19,140 @@
#include <arpa/inet.h>
#endif
const char* ffDetectUsers(FFUsersOptions* options, FFlist* users)
#if __linux__
bool detectUserBySystemd(const FFstrbuf* pathUsers, FFlist* users)
{
FF_STRBUF_AUTO_DESTROY state = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY userName = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY loginTime = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY sessions = ffStrbufCreate();
// WARNING: This is private data. Do not parse
if (!ffParsePropFileValues(pathUsers->chars, 4, (FFpropquery[]) {
{"NAME=", &userName},
{"STATE=", &state},
{"REALTIME=", &loginTime},
{"ONLINE_SESSIONS=", &sessions},
}) || !ffStrbufEqualS(&state, "active"))
return false;
FFUserResult* user = FF_LIST_ADD(FFUserResult, *users);
ffStrbufInitMove(&user->name, &userName);
ffStrbufInit(&user->hostName);
ffStrbufInit(&user->sessionName);
ffStrbufInit(&user->clientIp);
ffStrbufSubstrBefore(&loginTime, loginTime.length - 3); // converts us to ms
user->loginTime = ffStrbufToUInt(&loginTime, 0);
FF_STRBUF_AUTO_DESTROY pathSessions = ffStrbufCreateS("/run/systemd/sessions/");
const uint32_t pathSessionsBaseLen = pathSessions.length;
FF_STRBUF_AUTO_DESTROY tty = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY remoteHost = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY service = ffStrbufCreate();
char* token = NULL;
size_t n = 0;
while (ffStrbufGetdelim(&token, &n, ' ', &sessions))
{
ffStrbufSubstrBefore(&pathSessions, pathSessionsBaseLen);
ffStrbufAppendS(&pathSessions, token);
ffStrbufClear(&remoteHost);
ffStrbufClear(&service);
ffStrbufClear(&tty);
ffStrbufClear(&loginTime);
// WARNING: This is private data. Do not parse
if (ffParsePropFileValues(pathSessions.chars, 4, (FFpropquery[]) {
{"REMOTE_HOST=", &remoteHost},
{"TTY=", &tty},
{"SERVICE=", &service},
{"REALTIME=", &loginTime},
}) && !ffStrbufEqualS(&service, "systemd-user"))
{
if (remoteHost.length)
{
ffStrbufTrimRight(&remoteHost, ']');
ffStrbufTrimLeft(&remoteHost, '[');
ffStrbufInitMove(&user->hostName, &remoteHost);
}
else
ffStrbufSetStatic(&user->hostName, "localhost");
ffStrbufInitMove(&user->sessionName, tty.length ? &tty : &service);
if (loginTime.length)
{
ffStrbufSubstrBefore(&loginTime, loginTime.length - 3); // converts us to ms
user->loginTime = ffStrbufToUInt(&loginTime, 0);
}
break;
}
}
return true;
}
const char* detectBySystemd(FFUsersOptions* options, FFlist* users)
{
// For some reason, debian/ubuntu no longer updates `/var/run/utmp` (#2064)
// Query systemd instead
FF_STRBUF_AUTO_DESTROY pathUsers = ffStrbufCreateS("/run/systemd/users/");
if (options->myselfOnly)
{
ffStrbufAppendUInt(&pathUsers, getuid());
detectUserBySystemd(&pathUsers, users);
}
else
{
const uint32_t pathUsersBaseLen = pathUsers.length;
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pathUsers.chars);
if (!dirp) return "opendir(\"/run/systemd/users/\") failed";
struct dirent* entry;
while ((entry = readdir(dirp)))
{
if (entry->d_type != DT_REG) continue;
ffStrbufAppendS(&pathUsers, entry->d_name);
detectUserBySystemd(&pathUsers, users);
ffStrbufSubstrBefore(&pathUsers, pathUsersBaseLen);
}
}
return NULL;
}
#endif
#if __linux__ || __GNU__
static void fillUtmpIpAddr(FFUserResult* user, struct utmpx* n)
{
bool isIpv6 = false;
for (int i = 1; i < 4; ++i) {
if (n->ut_addr_v6[i] != 0) {
isIpv6 = true;
break;
}
}
if (isIpv6) {
char ipv6_str[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, n->ut_addr_v6, ipv6_str, INET6_ADDRSTRLEN) != NULL) {
ffStrbufSetS(&user->clientIp, ipv6_str);
}
} else if (n->ut_addr_v6[0] != 0) {
char ipv4_str[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, n->ut_addr_v6, ipv4_str, INET_ADDRSTRLEN) != NULL) {
ffStrbufSetS(&user->clientIp, ipv4_str);
}
}
}
#else
static void fillUtmpIpAddr(FF_MAYBE_UNUSED FFUserResult* user, FF_MAYBE_UNUSED struct utmpx* n)
{
}
#endif
const char* detectByUtmp(FFUsersOptions* options, FFlist* users)
{
struct utmpx* n = NULL;
setutxent();
@@ -32,37 +169,41 @@ next:
FF_LIST_FOR_EACH(FFUserResult, user, *users)
{
if(ffStrbufEqualS(&user->name, n->ut_user))
{
uint64_t newLoginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000;
if (newLoginTime > user->loginTime)
{
ffStrbufSetS(&user->hostName, n->ut_host);
ffStrbufSetS(&user->sessionName, n->ut_line);
fillUtmpIpAddr(user, n);
user->loginTime = newLoginTime;
}
goto next;
}
}
FFUserResult* user = (FFUserResult*) ffListAdd(users);
FFUserResult* user = FF_LIST_ADD(FFUserResult, *users);
ffStrbufInitS(&user->name, n->ut_user);
ffStrbufInitS(&user->hostName, n->ut_host);
ffStrbufInitS(&user->sessionName, n->ut_line);
ffStrbufInit(&user->clientIp);
#if __linux__ || __GNU__
bool isIpv6 = false;
for (int i = 1; i < 4; ++i) {
if (n->ut_addr_v6[i] != 0) {
isIpv6 = true;
break;
}
}
if (isIpv6) {
char ipv6_str[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, n->ut_addr_v6, ipv6_str, INET6_ADDRSTRLEN) != NULL) {
ffStrbufSetS(&user->clientIp, ipv6_str);
}
} else if (n->ut_addr_v6[0] != 0) {
char ipv4_str[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, n->ut_addr_v6, ipv4_str, INET_ADDRSTRLEN) != NULL) {
ffStrbufSetS(&user->clientIp, ipv4_str);
}
}
#endif
fillUtmpIpAddr(user, n);
user->loginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000;
}
endutxent();
return NULL;
}
const char* ffDetectUsers(FFUsersOptions* options, FFlist* users)
{
const char* err = detectByUtmp(options, users);
if (err) return err;
#if __linux__
if (users->length == 0) detectBySystemd(options, users);
#endif
return NULL;
}
+4
View File
@@ -44,6 +44,10 @@ const char* ffDetectWeather(FFWeatherOptions* options, FFstrbuf* result)
ffStrbufEnsureFree(result, 4095);
const char* error = ffNetworkingRecvHttpResponse(&state, result);
state = (FFNetworkingState){};
status = FF_UNITIALIZED;
if (error == NULL)
{
ffStrbufSubstrAfterFirstS(result, "\r\n\r\n");
+14 -55
View File
@@ -25,65 +25,24 @@
* CDDL HEADER END
*/
typedef enum {
ZPOOL_PROP_INVAL = -1,
ZPOOL_PROP_NAME,
ZPOOL_PROP_SIZE,
ZPOOL_PROP_CAPACITY,
ZPOOL_PROP_ALTROOT,
ZPOOL_PROP_HEALTH,
ZPOOL_PROP_GUID,
ZPOOL_PROP_VERSION,
ZPOOL_PROP_BOOTFS,
ZPOOL_PROP_DELEGATION,
ZPOOL_PROP_AUTOREPLACE,
ZPOOL_PROP_CACHEFILE,
ZPOOL_PROP_FAILUREMODE,
ZPOOL_PROP_LISTSNAPS,
ZPOOL_PROP_AUTOEXPAND,
ZPOOL_PROP_DEDUPDITTO,
ZPOOL_PROP_DEDUPRATIO,
ZPOOL_PROP_FREE,
ZPOOL_PROP_ALLOCATED,
ZPOOL_PROP_READONLY,
ZPOOL_PROP_ASHIFT,
ZPOOL_PROP_COMMENT,
ZPOOL_PROP_EXPANDSZ,
ZPOOL_PROP_FREEING,
ZPOOL_PROP_FRAGMENTATION,
ZPOOL_PROP_LEAKED,
ZPOOL_PROP_MAXBLOCKSIZE,
ZPOOL_PROP_TNAME,
ZPOOL_PROP_MAXDNODESIZE,
ZPOOL_PROP_MULTIHOST,
ZPOOL_PROP_CHECKPOINT,
ZPOOL_PROP_LOAD_GUID,
ZPOOL_PROP_AUTOTRIM,
ZPOOL_PROP_COMPATIBILITY,
ZPOOL_PROP_BCLONEUSED,
ZPOOL_PROP_BCLONESAVED,
ZPOOL_PROP_BCLONERATIO,
ZPOOL_NUM_PROPS
} zpool_prop_t;
typedef enum {
ZPROP_SRC_NONE = 0x1,
ZPROP_SRC_DEFAULT = 0x2,
ZPROP_SRC_TEMPORARY = 0x4,
ZPROP_SRC_LOCAL = 0x8,
ZPROP_SRC_INHERITED = 0x10,
ZPROP_SRC_RECEIVED = 0x20
} zprop_source_t;
typedef bool boolean_t;
// zpool_prop_t and zprop_source_t were previously enums in upstream OpenZFS.
// However, the enum values for these types vary greatly between different platforms,
// making it unsafe to use the enum values directly. To ensure portability,
// we define them as simple int typedefs and use zpool_name_to_prop to look up
// the correct value for a property at runtime.
typedef int zpool_prop_t;
typedef int zprop_source_t;
typedef int boolean_t;
typedef struct libzfs_handle libzfs_handle_t;
typedef struct zpool_handle zpool_handle_t;
typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
extern libzfs_handle_t *libzfs_init(void);
extern void libzfs_fini(libzfs_handle_t *);
extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t, zprop_source_t *);
extern const char *zpool_get_name(zpool_handle_t *);
extern const char *zpool_get_state_str(zpool_handle_t *);
extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
extern zpool_prop_t zpool_name_to_prop(const char *);
// https://github.com/openzfs/zfs/blob/06c73cffabc30b61a695988ec8e290f43cb3768d/lib/libzfs/libzfs_pool.c#L300
extern uint64_t zpool_get_prop_int(zpool_handle_t *zhp, zpool_prop_t prop, zprop_source_t *srctype);
extern int zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len, zprop_source_t *srctype, boolean_t literal);
extern void zpool_close(zpool_handle_t *);
+126
View File
@@ -0,0 +1,126 @@
#include "zpool.h"
#if FF_HAVE_LIBZFS
#include "util/kmod.h"
#ifdef __sun
#include <libzfs.h>
#else
#include "libzfs_simplified.h"
#endif
#include "common/library.h"
typedef struct FFZfsData
{
FF_LIBRARY_SYMBOL(libzfs_fini)
FF_LIBRARY_SYMBOL(zpool_get_prop_int)
FF_LIBRARY_SYMBOL(zpool_get_prop)
FF_LIBRARY_SYMBOL(zpool_close)
// The fields in this struct store property IDs returned by `zpool_name_to_prop`,
// not the property values themselves.
struct {
int name;
int health;
int guid;
int size;
int free;
int allocated;
int fragmentation;
int readonly;
} props;
libzfs_handle_t* handle;
FFlist* result;
} FFZfsData;
static inline void cleanLibzfs(FFZfsData* data)
{
if (data->fflibzfs_fini && data->handle)
{
data->fflibzfs_fini(data->handle);
data->handle = NULL;
}
}
static int enumZpoolCallback(zpool_handle_t* zpool, void* param)
{
FFZfsData* data = (FFZfsData*) param;
zprop_source_t source;
FFZpoolResult* item = ffListAdd(data->result);
char buf[1024];
if (data->ffzpool_get_prop(zpool, data->props.name, buf, ARRAY_SIZE(buf), &source, false) == 0)
ffStrbufInitS(&item->name, buf);
else
ffStrbufInitStatic(&item->name, "unknown");
if (data->ffzpool_get_prop(zpool, data->props.health, buf, ARRAY_SIZE(buf), &source, false) == 0)
ffStrbufInitS(&item->state, buf);
else
ffStrbufInitStatic(&item->state, "unknown");
item->guid = data->ffzpool_get_prop_int(zpool, data->props.guid, &source);
item->total = data->ffzpool_get_prop_int(zpool, data->props.size, &source);
item->used = item->total - data->ffzpool_get_prop_int(zpool, data->props.free, &source);
item->allocated = data->ffzpool_get_prop_int(zpool, data->props.allocated, &source);
uint64_t fragmentation = data->ffzpool_get_prop_int(zpool, data->props.fragmentation, &source);
item->fragmentation = fragmentation == UINT64_MAX ? -DBL_MAX : (double) fragmentation;
item->readOnly = (bool) data->ffzpool_get_prop_int(zpool, data->props.readonly, &source);
data->ffzpool_close(zpool);
return 0;
}
const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */)
{
FF_LIBRARY_LOAD(libzfs, "dlopen libzfs" FF_LIBRARY_EXTENSION " failed", "libzfs" FF_LIBRARY_EXTENSION, 6);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, libzfs_init);
libzfs_handle_t* handle = fflibzfs_init();
if (!handle)
{
if (!ffKmodLoaded("zfs")) return "`zfs` kernel module is not loaded";
return "libzfs_init() failed";
}
__attribute__((__cleanup__(cleanLibzfs))) FFZfsData data = {
.handle = handle,
.result = result,
};
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, zpool_name_to_prop);
#define FF_QUERY_ZPOOL_PROP_FROM_NAME(prop_name) do { \
data.props.prop_name = ffzpool_name_to_prop(#prop_name); \
if (data.props.prop_name < 0) \
return "Failed to query prop: " #prop_name; \
} while (false)
FF_QUERY_ZPOOL_PROP_FROM_NAME(name);
FF_QUERY_ZPOOL_PROP_FROM_NAME(health);
FF_QUERY_ZPOOL_PROP_FROM_NAME(guid);
FF_QUERY_ZPOOL_PROP_FROM_NAME(size);
FF_QUERY_ZPOOL_PROP_FROM_NAME(free);
FF_QUERY_ZPOOL_PROP_FROM_NAME(allocated);
FF_QUERY_ZPOOL_PROP_FROM_NAME(fragmentation);
FF_QUERY_ZPOOL_PROP_FROM_NAME(readonly);
#undef FF_QUERY_ZPOOL_PROP_FROM_NAME
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, zpool_iter);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, libzfs_fini);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_prop_int);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_prop);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_close);
if (ffzpool_iter(handle, enumZpoolCallback, &data) < 0)
return "zpool_iter() failed";
return NULL;
}
#else
const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result)
{
return "fastfetch was compiled without libzfs support";
}
#endif
+3 -1
View File
@@ -7,10 +7,12 @@ typedef struct FFZpoolResult
{
FFstrbuf name;
FFstrbuf state;
uint64_t guid;
uint64_t used;
uint64_t total;
uint64_t version;
uint64_t allocated;
double fragmentation;
bool readOnly;
} FFZpoolResult;
const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */);
-117
View File
@@ -1,117 +0,0 @@
#include "zpool.h"
#ifdef FF_HAVE_LIBZFS
#include "util/kmod.h"
#ifdef __sun
#define FF_DISABLE_DLOPEN
#include <libzfs.h>
const char* zpool_get_state_str(zpool_handle_t* zpool)
{
if (zpool_get_state(zpool) == POOL_STATE_UNAVAIL)
return "FAULTED";
else
{
const char *str;
zpool_errata_t errata;
zpool_status_t status = zpool_get_status(zpool, (char**) &str, &errata);
if (status == ZPOOL_STATUS_IO_FAILURE_WAIT ||
status == ZPOOL_STATUS_IO_FAILURE_CONTINUE ||
status == ZPOOL_STATUS_IO_FAILURE_MMP)
return "SUSPENDED";
else
{
nvlist_t *nvroot = fnvlist_lookup_nvlist(zpool_get_config(zpool, NULL), ZPOOL_CONFIG_VDEV_TREE);
uint_t vsc;
vdev_stat_t *vs;
#ifdef __x86_64__
if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint64_t**) &vs, &vsc) != 0)
#else
if (nvlist_lookup_uint32_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint32_t**) &vs, &vsc) != 0)
#endif
return "UNKNOWN";
else
return zpool_state_to_name(vs->vs_state, vs->vs_aux);
}
}
}
#else
#include "libzfs_simplified.h"
#endif
#include "common/library.h"
typedef struct FFZfsData
{
FF_LIBRARY_SYMBOL(libzfs_fini)
FF_LIBRARY_SYMBOL(zpool_iter)
FF_LIBRARY_SYMBOL(zpool_get_prop_int)
FF_LIBRARY_SYMBOL(zpool_get_name)
FF_LIBRARY_SYMBOL(zpool_get_state_str)
libzfs_handle_t* handle;
FFlist* result;
} FFZfsData;
static inline void cleanLibzfs(FFZfsData* data)
{
if (data->fflibzfs_fini && data->handle)
{
data->fflibzfs_fini(data->handle);
data->handle = NULL;
}
}
static int enumZpoolCallback(zpool_handle_t* zpool, void* param)
{
FFZfsData* data = (FFZfsData*) param;
zprop_source_t source;
FFZpoolResult* item = ffListAdd(data->result);
ffStrbufInitS(&item->name, data->ffzpool_get_name(zpool));
ffStrbufInitS(&item->state, data->ffzpool_get_state_str(zpool));
item->version = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_VERSION, &source);
item->total = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_SIZE, &source);
item->used = item->total - data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_FREE, &source);
uint64_t fragmentation = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_FRAGMENTATION, &source);
item->fragmentation = fragmentation == UINT64_MAX ? -DBL_MAX : (double) fragmentation;
return 0;
}
const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */)
{
FF_LIBRARY_LOAD(libzfs, "dlopen libzfs" FF_LIBRARY_EXTENSION " failed", "libzfs" FF_LIBRARY_EXTENSION, 6);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, libzfs_init);
libzfs_handle_t* handle = fflibzfs_init();
if (!handle)
{
if (!ffKmodLoaded("zfs")) return "`zfs` kernel module is not loaded";
return "libzfs_init() failed";
}
__attribute__((__cleanup__(cleanLibzfs))) FFZfsData data = {
.handle = handle,
.result = result,
};
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, zpool_iter);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, libzfs_fini);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_prop_int);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_name);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_state_str);
if (ffzpool_iter(handle, enumZpoolCallback, &data) < 0)
return "zpool_iter() failed";
return NULL;
}
#else
const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result)
{
return "Fastfetch was compiled without libzfs support";
}
#endif
-6
View File
@@ -1,6 +0,0 @@
#include "zpool.h"
const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result /* list of FFZpoolResult */)
{
return "Not supported on this platform";
}
+45 -7
View File
@@ -3,6 +3,7 @@
#include "common/init.h"
#include "common/io/io.h"
#include "common/jsonconfig.h"
#include "common/time.h"
#include "detection/version/version.h"
#include "logo/logo.h"
#include "util/stringUtils.h"
@@ -13,7 +14,7 @@
#include <ctype.h>
#include <string.h>
#ifdef WIN32
#ifdef _WIN32
#include "util/windows/getline.h"
#endif
@@ -501,7 +502,20 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
if (parseJsoncFile(absolutePath.chars, flag)) return;
//Try to load as a relative path
//Try to load as a relative path with the config directory
FF_LIST_FOR_EACH(FFstrbuf, path, instance.state.platform.configDirs)
{
ffStrbufSet(&absolutePath, path);
ffStrbufAppendS(&absolutePath, "fastfetch/");
ffStrbufAppendS(&absolutePath, value);
if (needExtension)
ffStrbufAppendS(&absolutePath, ".jsonc");
if (parseJsoncFile(absolutePath.chars, flag)) return;
}
//Try to load as a preset
FF_LIST_FOR_EACH(FFstrbuf, path, instance.state.platform.dataDirs)
{
@@ -514,7 +528,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
if (parseJsoncFile(absolutePath.chars, flag)) return;
}
//Try to load as a relative path with the directory of fastfetch binary
//Try to load as a relative path with the directory of fastfetch binary, for Windows support
if (instance.state.platform.exePath.length)
{
@@ -670,6 +684,8 @@ static void parseCommand(FFdata* data, char* key, char* value)
{},
}));
}
else if(ffStrEqualsIgnCase(key, "--dynamic-interval"))
instance.state.dynamicInterval = ffOptionParseUInt32(key, value); // seconds to milliseconds
else
return;
@@ -768,15 +784,31 @@ static void run(FFdata* data)
if (!instance.config.display.noBuffer) fflush(stdout);
#endif
if (useJsonConfig)
ffPrintJsonConfig(false, instance.state.resultDoc);
else
ffPrintCommandOption(data, instance.state.resultDoc);
while (true)
{
if (useJsonConfig)
ffPrintJsonConfig(false, instance.state.resultDoc);
else
ffPrintCommandOption(data, instance.state.resultDoc);
if (instance.state.dynamicInterval > 0)
{
fflush(stdout);
ffTimeSleep(instance.state.dynamicInterval);
fputs("\e[H", stdout);
}
else
break;
}
if (instance.state.resultDoc)
yyjson_mut_write_fp(stdout, instance.state.resultDoc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES | YYJSON_WRITE_NEWLINE_AT_END, NULL, NULL);
else
{
if (instance.config.logo.printRemaining)
ffLogoPrintRemaining();
ffFinish();
}
}
static void writeConfigFile(FFdata* data)
@@ -835,6 +867,12 @@ int main(int argc, char** argv)
};
parseArguments(&data, argc, argv, parseCommand);
if(instance.state.dynamicInterval && instance.state.resultDoc)
{
fprintf(stderr, "Error: --dynamic-interval cannot be used with --json\n");
exit(400);
}
if(!data.configLoaded && !getenv("NO_CONFIG"))
parseConfigFiles();
parseArguments(&data, argc, argv, (void*) parseOption);
+1
View File
@@ -46,6 +46,7 @@ typedef struct FFstate
yyjson_mut_doc* resultDoc;
FFstrbuf genConfigPath;
bool fullConfig;
uint32_t dynamicInterval;
} FFstate;
typedef struct FFinstance
+2
View File
@@ -1,6 +1,7 @@
#include "fastfetch.h"
#include "common/init.h"
#include "logo/logo.h"
#include "modules/modules.h"
// A dirty replicate of neofetch
@@ -136,6 +137,7 @@ int main(void)
ffPrintColors(&options);
}
ffLogoPrintRemaining();
ffFinish();
ffDestroyInstance();
return 0;
+13
View File
@@ -0,0 +1,13 @@
----.---
:-==.-==-
$2 -=== ===:
===-:===.
$3 -+++:=++=
=+++ +++.
$6 .+++-=++=
$4.::. ::: $6 :**+ +**-
$4 :--:.---. $6 .+**=-***.
$5 :--- ---::$6***:***+
$5 .---::-$6=##*.*##-
$7 ----+$6##=:##*.
$7 :-$6###-*##+
+12 -12
View File
@@ -1,12 +1,12 @@
^"^"^". `"^"""`
";;;;;:. `;;;;;;.
^;":;:;: .;,,;:;:
.'`",, '":::. ^;'';;;;, .:: ,;;;:
';;;;:":::::' ^;` `;;;;" ::. ,;;;:
";:;:. ^;` ^;;;;^ ,;. ,;;;:
";;;: ^;` ";;;;^ ";' ,;;;:
";;;: ^;` ,;;;;`^;` ,;;;:
";;;: ^;` ,;;;;;^ ,;;;:
";:;: ^;` .:;:;" ,;:;:
,;;;;. ";" .:;, :;;;;.
."""""` ^"^ '" ."""""`
#: :+#@@@@%#
@@#: .*@@@@@@@@@
@@@@#- .@@@@@@@@@@@
@@@@@@%- %@@@@@@@@@@@
@@@@@@@@%= :@@@@%%%%@@@@
@@@@@@@@@@%==*-: .:=#
:*@@@@@@@@@@=
:*@@@@@@@@=
.*@@@@@@=
.+@@@@=
.+%@=
+=
+20
View File
@@ -0,0 +1,20 @@
################
########################
##########-- --##########
#######* ****** *#######
###### --*######--- ######
##### --- *#### #####
##### *-- #*#*#* * #### #####
#####* ##- ##*-#* ### ### *#####
##### ###* -##*#* *# *# *### #####
##### ###* -*##*-*## *#* ### #####
##### #### - --#### - ##* ### #####
##### *####* * - ###- ##* #####
#####* #######- *###- ### *#####
#####* ***#####****--- ### *#####
###### ----------- - ### ######
###### ----- ### ######
####### #######
##########*----*##########
######################
##############
+11
View File
@@ -0,0 +1,11 @@
.::-::::. :======---:.
.-=++++++++==-:. .:-++===--=**+=:
.=. ...:::::--=****+++=-:. --
-: $2.-==-.$1 .::. ::
. $2:*%@@@@@#. .:::.
:#%%%%%@%#+. :*##%%%%#+.
*%%%%%%*. =. +*#%%%%%%%#:
*%%%%%%- +%%%%%%#@=
:%%%%%%#-. .:+%%%%%%%%+
:*%%%%%%+. .-+*###*=:
:=+=-.
-13
View File
@@ -1,13 +0,0 @@
.':ldkOOOOkxo:,.
.:d0KKXXXXXXXXXXKkl,
.;xOOO0000KKKKXXXXXXX0l.
.cxxxkkkkOOO0000KKKKXXXXx'
;ool;,,,,;coxkOOOO0000KKKd.
.clc'.........:oxkkkkOOOO0O,
.ccc:ccclc:,....,:oddxxdxkO;
cllllccccccc;'........'lxx.
'ooooolllllccc:;,''',,coo,
,ddddooooollllllccccccl'
cxxxdddddooooollllc;
,dxxxxxddddddol.
.';::,.
+17
View File
@@ -0,0 +1,17 @@
.-==: . .
-+:::+= * *- = -. -%*
** *# :%- . :.*=: -%%
%@: -@% =%@%%*: - :. -%=
-@@: :@@- -+#%%+ .: :*.
-@@: :@@-- -% = .*
.%@- -%% ++ # . -: .
#* ** =.:*: +- = #%%%%#+-*:
-=====- .:- ..
$2 =*+ -+*:
.*+ :*+= -+*++**-
+*-++++ -++-:. .:-=++.=*+. =*-
-***:-+:*++=*++*: +*+=:-**+=**++-
:- :***==*+.=++ +*+=++*=.=*+.
.=-=*+***+*= +*+ :**+=++
. -*+ +*+ .=:
+16
View File
@@ -0,0 +1,16 @@
__agggg
_g@@@@@@@P
a@@@@@@@@F
_@@@@@@@@F
$2a@@@@@@_ _@@@@@@_$1`
$2_a@@@~@@@@, @@@@~M@@&_
_@@@~ `@@@@ @@@@~ `@@@_
@@@" ~@@@@ @@@@~ "@@@
@@@ ~@@@@ @@@@F @@@
@@@ 4@@@@ @@@@F @@@
@@@_ 4@@@b @@@@F _@@@
"@@@_ 4@@@yy@@@F _@@@"
"R@@g_ 5@@@@@@@ _g@@@"
~@@@gy_ @@@@@@ _yg@@@~
`T@@@@gyyy@@@@yyag@@@@F~
~~FRR@@@@@@RPF~~
+45 -8
View File
@@ -39,7 +39,7 @@ static const FFlogo A[] = {
FF_COLOR_FG_256 "36",
},
},
// Aeon
// AerynOS
{
.names = {"AerynOS"},
.lines = FASTFETCH_DATATEXT_LOGO_AERYNOS,
@@ -181,6 +181,20 @@ static const FFlogo A[] = {
FF_COLOR_FG_256 "178",
}
},
// Amiga
{
.names = {"Amiga"},
.lines = FASTFETCH_DATATEXT_LOGO_AMIGA,
.colors = {
FF_COLOR_FG_RED,
FF_COLOR_FG_LIGHT_RED,
FF_COLOR_FG_YELLOW,
FF_COLOR_FG_BLUE,
FF_COLOR_FG_CYAN,
FF_COLOR_FG_LIGHT_YELLOW,
FF_COLOR_FG_GREEN,
}
},
// AmogOS
{
.names = {"AmogOS"},
@@ -2143,7 +2157,7 @@ static const FFlogo G[] = {
// GXDE
{
.names = {"GXDE"},
.lines = FASTFETCH_DATATEXT_LOGO_DEEPIN,
.lines = FASTFETCH_DATATEXT_LOGO_GXDE,
.colors = {
FF_COLOR_FG_RED,
},
@@ -2856,6 +2870,17 @@ static const FFlogo L[] = {
};
static const FFlogo M[] = {
// Macaroni
{
.names = {"Macaroni"},
.lines = FASTFETCH_DATATEXT_LOGO_MACARONIOS,
.colors = {
FF_COLOR_FG_YELLOW,
FF_COLOR_FG_WHITE,
},
.colorKeys = FF_COLOR_FG_YELLOW,
.colorTitle = FF_COLOR_FG_GREEN,
},
// MacOS
{
.names = {"macos", "mac"},
@@ -3430,12 +3455,6 @@ static const FFlogo O[] = {
FF_COLOR_FG_LIGHT_BLACK,
}
},
// Opak
{
.names = {"Opak"},
.lines = FASTFETCH_DATATEXT_LOGO_OPAK,
.colors = {}, // #1070
},
// OpenKylin
{
.names = {"openkylin", "open-kylin"},
@@ -3675,6 +3694,15 @@ static const FFlogo O[] = {
FF_COLOR_FG_DEFAULT,
},
},
// OS/2 Warp
{
.names = {"OS2Warp"},
.lines = FASTFETCH_DATATEXT_LOGO_OS2WARP,
.colors = {
FF_COLOR_FG_LIGHT_WHITE,
FF_COLOR_FG_LIGHT_BLUE,
}
},
// OS_Elbrus
{
.names = {"OS Elbrus"},
@@ -5200,6 +5228,15 @@ static const FFlogo V[] = {
FF_COLOR_FG_BLUE,
},
},
// VincentOS
{
.names = {"VincentOS"},
.lines = FASTFETCH_DATATEXT_LOGO_VINCENTOS,
.colors = {
FF_COLOR_FG_GREEN,
FF_COLOR_FG_DEFAULT,
},
},
// Vnux
{
.names = {"Vnux"},
+2 -2
View File
@@ -891,13 +891,13 @@ static bool printCachedPixel(FFLogoRequestData* requestData)
if(requestData->type == FF_LOGO_TYPE_IMAGE_KITTY)
{
fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_COMPRESSED);
if(fd == FF_INVALID_FD)
if(!ffIsValidNativeFD(fd))
fd = getCacheFD(requestData, FF_CACHE_FILE_KITTY_UNCOMPRESSED);
}
else if(requestData->type == FF_LOGO_TYPE_IMAGE_SIXEL)
fd = getCacheFD(requestData, FF_CACHE_FILE_SIXEL);
if(fd == FF_INVALID_FD)
if(!ffIsValidNativeFD(fd))
return false;
ffPrintCharTimes('\n', options->paddingTop);
+22 -7
View File
@@ -2,12 +2,14 @@
#include "common/io/io.h"
#include "common/printing.h"
#include "common/processing.h"
#include "detection/media/media.h"
#include "detection/os/os.h"
#include "detection/terminalshell/terminalshell.h"
#include "util/textModifier.h"
#include "util/stringUtils.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
typedef enum __attribute__((__packed__)) FFLogoSize
@@ -483,6 +485,15 @@ static bool updateLogoPath(void)
if (ffStrbufEqualS(&options->source, "-")) // stdin
return true;
if (ffStrbufIgnCaseEqualS(&options->source, "media-cover"))
{
const FFMediaResult* media = ffDetectMedia(true);
if (media->cover.length == 0)
return false;
ffStrbufSet(&options->source, &media->cover);
return true;
}
FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreate();
if (ffPathExpandEnv(options->source.chars, &fullPath) && ffPathExists(fullPath.chars, FF_PATHTYPE_FILE))
{
@@ -706,6 +717,9 @@ void ffLogoPrintLine(void)
if(instance.state.logoWidth > 0)
printf("\033[%uC", instance.state.logoWidth);
if (instance.state.dynamicInterval > 0)
fputs("\033[K", stdout);
++instance.state.keysHeight;
}
@@ -719,22 +733,23 @@ void ffLogoPrintRemaining(void)
void ffLogoBuiltinPrint(void)
{
FFOptionsLogo* options = &instance.config.logo;
options->position = FF_LOGO_POSITION_TOP;
options->paddingRight = 2; // empty line after logo printing
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
for(uint8_t ch = 0; ch < 26; ++ch)
{
for(const FFlogo* logo = ffLogoBuiltins[ch]; *logo->names; ++logo)
{
printf("\033[%sm%s:\033[0m\n", logo->colors[0], logo->names[0]);
if (instance.config.display.pipe)
ffStrbufSetF(&buf, "%s:\n", logo->names[0]);
else
ffStrbufSetF(&buf, "\e[%sm%s:\e[0m\n", logo->colors[0], logo->names[0]);
ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf);
logoPrintStruct(logo);
ffLogoPrintRemaining();
//reset everything
instance.state.logoHeight = 0;
instance.state.keysHeight = 0;
for(uint8_t i = 0; i < FASTFETCH_LOGO_MAX_COLORS; i++)
ffStrbufClear(&options->colors[i]);
putchar('\n');
}
}
}
+68 -33
View File
@@ -1,45 +1,58 @@
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/processing.h"
#include "modules/command/command.h"
#include "util/stringUtils.h"
#include "detection/command/command.h"
bool ffPrintCommand(FFCommandOptions* options)
{
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
const char* error = ffProcessAppendStdOut(&result, options->param.length ? (char* const[]){
options->shell.chars,
options->param.chars,
options->text.chars,
NULL
} : (char* const[]){
options->shell.chars,
options->text.chars,
NULL
});
const char* error = ffDetectCommand(options, &result);
if(error)
if (error)
{
ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
return false;
}
if(!result.length)
if (!result.length)
{
ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No result generated");
return false;
}
if (options->moduleArgs.outputFormat.length == 0)
if (options->splitLines)
{
ffPrintLogoAndKey(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
ffStrbufPutTo(&result, stdout);
uint8_t index = 0;
char* line = NULL;
size_t len = 0;
while (ffStrbufGetline(&line, &len, &result))
{
if (options->moduleArgs.outputFormat.length == 0)
{
ffPrintLogoAndKey(FF_COMMAND_MODULE_NAME, ++index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
puts(line);
}
else
{
FF_PRINT_FORMAT_CHECKED(FF_COMMAND_MODULE_NAME, ++index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){
FF_FORMAT_ARG(line, "result")
}));
}
}
}
else
{
FF_PRINT_FORMAT_CHECKED(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){
FF_FORMAT_ARG(result, "result")
}));
if (options->moduleArgs.outputFormat.length == 0)
{
ffPrintLogoAndKey(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
ffStrbufPutTo(&result, stdout);
}
else
{
FF_PRINT_FORMAT_CHECKED(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){
FF_FORMAT_ARG(result, "result")
}));
}
}
return true;
@@ -72,6 +85,24 @@ void ffParseCommandJsonObject(FFCommandOptions* options, yyjson_val* module)
continue;
}
if (unsafe_yyjson_equals_str(key, "useStdErr"))
{
options->useStdErr = yyjson_get_bool(val);
continue;
}
if (unsafe_yyjson_equals_str(key, "parallel"))
{
options->parallel = yyjson_get_bool(val);
continue;
}
if (unsafe_yyjson_equals_str(key, "splitLines"))
{
options->splitLines = yyjson_get_bool(val);
continue;
}
ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
}
}
@@ -81,25 +112,17 @@ void ffGenerateCommandJsonConfig(FFCommandOptions* options, yyjson_mut_doc* doc,
ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
yyjson_mut_obj_add_strbuf(doc, module, "shell", &options->shell);
yyjson_mut_obj_add_strbuf(doc, module, "param", &options->param);
yyjson_mut_obj_add_strbuf(doc, module, "text", &options->text);
yyjson_mut_obj_add_bool(doc, module, "useStdErr", options->useStdErr);
yyjson_mut_obj_add_bool(doc, module, "parallel", options->parallel);
yyjson_mut_obj_add_bool(doc, module, "splitLines", options->splitLines);
}
bool ffGenerateCommandJsonResult(FF_MAYBE_UNUSED FFCommandOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module)
{
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
const char* error = ffProcessAppendStdOut(&result, options->param.length ? (char* const[]){
options->shell.chars,
options->param.chars,
options->text.chars,
NULL
} : (char* const[]){
options->shell.chars,
options->text.chars,
NULL
});
const char* error = ffDetectCommand(options, &result);
if(error)
{
@@ -113,7 +136,16 @@ bool ffGenerateCommandJsonResult(FF_MAYBE_UNUSED FFCommandOptions* options, yyjs
return false;
}
yyjson_mut_obj_add_strbuf(doc, module, "result", &result);
if (options->splitLines)
{
yyjson_mut_val* jsonArray = yyjson_mut_obj_add_arr(doc, module, "result");
char* line = NULL;
size_t len = 0;
while (ffStrbufGetline(&line, &len, &result))
yyjson_mut_arr_add_strncpy(doc, jsonArray, line, len);
}
else
yyjson_mut_obj_add_strbuf(doc, module, "result", &result);
return true;
}
@@ -137,6 +169,9 @@ void ffInitCommandOptions(FFCommandOptions* options)
#endif
);
ffStrbufInit(&options->text);
options->useStdErr = false;
options->parallel = true;
options->splitLines = false;
}
void ffDestroyCommandOptions(FFCommandOptions* options)
+2
View File
@@ -4,6 +4,8 @@
#define FF_COMMAND_MODULE_NAME "Command"
bool ffPrepareCommand(FFCommandOptions* options);
bool ffPrintCommand(FFCommandOptions* options);
void ffInitCommandOptions(FFCommandOptions* options);
void ffDestroyCommandOptions(FFCommandOptions* options);
+3
View File
@@ -9,6 +9,9 @@ typedef struct FFCommandOptions
FFstrbuf shell;
FFstrbuf param;
FFstrbuf text;
bool useStdErr;
bool parallel;
bool splitLines;
} FFCommandOptions;
static_assert(sizeof(FFCommandOptions) <= FF_OPTION_MAX_SIZE, "FFCommandOptions size exceeds maximum allowed size");

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