Compare commits

...

73 Commits

Author SHA1 Message Date
Carter Li aa3a916d5a Merge pull request #2183 from fastfetch-cli/dev
Release: v2.59.0
2026-02-14 00:15:11 +08:00
Carter Li 6e64b758bf Chore: silences a compiler warning 2026-02-13 23:37:40 +08:00
李通洲 49ef7fc51f Packages (Windows): code cleanups 2026-02-13 20:50:45 +08:00
李通洲 0e9a5acdbd Platform (Windows): adds init / destroy of field sid 2026-02-13 20:42:35 +08:00
李通洲 4f6e80e940 Release: v2.59.0 2026-02-13 19:39:33 +08:00
李通洲 df87cdfa6e Windows: defines NtCurrentProcess and uses it 2026-02-13 19:38:22 +08:00
李通洲 dee4b36a46 Bluetooth (Windows): use modern dlls 2026-02-13 19:25:06 +08:00
李通洲 d04a26337a Swap (Windows): removes unused code 2026-02-13 19:25:06 +08:00
Dark Knightz 2afe8d11d1 Common: fixes discarded-qualifiers warnings (#2181) 2026-02-13 18:05:07 +08:00
李通洲 f31d3b8b21 Doc: update changelog 2026-02-13 10:04:54 +08:00
李通洲 8a20af9917 Doc: update JSON schema 2026-02-13 10:02:28 +08:00
李通洲 fc5c2f6213 Title: adds {user-id} in custom format 2026-02-13 09:38:22 +08:00
Carter Li 6c314e148a Logo (Builtin): updates nixos_small too
Ref #2180
2026-02-13 09:09:05 +08:00
Konnor Klercke a86d0017a9 Logo (Builtin): adds 6-color support to nixos logo (#2180)
Co-authored-by: Konnor Klercke <kklercke@brite.com>
2026-02-13 08:58:48 +08:00
李通洲 4038ed2656 GPU: adds debug logs
Thanks Github Copilot
2026-02-12 18:56:42 +08:00
李通洲 0f4bb47c2f Global: fixes compiler warnings 2026-02-12 16:25:33 +08:00
李通洲 19f7c67ede Platform: adds pid field and use it 2026-02-12 14:29:49 +08:00
李通洲 ff7e81ed81 Memory (Windows): adds comments [ci skip] 2026-02-11 21:47:20 +08:00
李通洲 681a102a2b Processing (Windows): adds TODO [ci skip] 2026-02-11 21:47:09 +08:00
李通洲 ba27f4f2ef CI: adds Solaris 2026-02-11 15:50:58 +08:00
李通洲 cbe33a4860 CI (DragonFly): disables packaging .pkg file to fix a weird CI error 2026-02-11 15:50:58 +08:00
李通洲 5847e3f269 FFPlatform (Windows): fixes memory corruption 2026-02-11 10:03:29 +08:00
李通洲 b248dfa01c Platform: prefers system API to retreive user name 2026-02-10 23:42:57 +08:00
李通洲 357aa77e21 Media (Solaris): fixes another compiling error
Fixes #2173
2026-02-10 22:42:36 +08:00
李通洲 a44bde6f94 Platform: adds UID / SID detection 2026-02-10 20:33:28 +08:00
李通洲 5825bce57d Disk (Windows): replaces most Kernel32 calls to ntdll calls
* Improves accurancy of detection of drive create time
* Improves performance slightly
* Removes remove drive check, `hideFolders` should be used instead
2026-02-10 18:50:35 +08:00
李通洲 996cdd3155 Refactor: moves memrchr to global impl for potential future use 2026-02-10 18:16:58 +08:00
李通洲 61cdfcabf8 Windows: uses SHGetKnownFolderPath instead of the unreliable getenv 2026-02-10 17:32:34 +08:00
李通洲 359a77097c Terminal (Windows): uses NtQueryInformationProcess to retrieve information of conhost.exe 2026-02-10 17:32:34 +08:00
李通洲 1323d007d3 Global: uses alignas where applicable 2026-02-10 17:32:34 +08:00
李通洲 d2215f26ac Processing (Windows): uses NtQueryInformationProcess instead of its wrapper 2026-02-10 17:32:34 +08:00
李通洲 958e7dbd8f Bluetooth (Windows): simplifies code 2026-02-10 17:32:34 +08:00
Verdgil 5b10cde59f Global: Add support for Oracle Solaris (#2176)
* Fix Oracle Solaris zpool build

* Add memrchr fallback

* Change to include fron extern

* add new line

* remove ; in zpool.c

* Change wrapper to macro

Also change README.md

* Tweaks

---------

Co-authored-by: Carter Li <CarterLi@users.noreply.github.com>
2026-02-10 17:32:20 +08:00
李通洲 e8f2b9a1c7 Common: uses _Countof in ARRAY_SIZE definition when available 2026-02-02 11:08:26 +08:00
Carter Li 0686d1db4a GPU (Hurd): uses native API; removes libpciaccess dep 2026-01-30 20:34:47 +08:00
李通洲 7c284f9ee8 CMake (macOS): disable ENABLE_APPLE_MEMSIZE_USABLE by default 2026-01-29 13:11:48 +08:00
李通洲 dc04273bd7 Memory (macOS): updates algorithm in order not to use hw.memsize_usable which is available on newer macOS version
Adds a new CMake option to use usable memory size at the same time

Fixes #2171
2026-01-29 09:34:50 +08:00
李通洲 d698be6239 CPU (ARM): removes duplicated cases 2026-01-27 14:48:05 +08:00
李通洲 e44df27c96 Memory (macOS): adds debug info
Usage: `fastfetch -s memory:swap -l none --dynamic-interval 1000 --debug --no-buffer`
See https://github.com/fastfetch-cli/fastfetch/issues/2171#issuecomment-3803309478
2026-01-27 14:24:38 +08:00
李通洲 b365d4cca1 CPU (ARM): adds more ARM parts 2026-01-26 10:17:28 +08:00
李通洲 19d3c948ad DE (Android): clarifies that Google Pixel and other OSes also report ro.build.display.id 2026-01-26 10:17:28 +08:00
李通洲 4a581039d5 CI: fixes building 2026-01-24 09:17:13 +08:00
李通洲 79849fb2b5 CI: updates dependencies 2026-01-23 10:26:04 +08:00
李通洲 c116a5abdf Chore: uses elvis operators where applicatable 2026-01-22 15:36:42 +08:00
Carter Li 8f082730a8 Packaging: update debian stuff [ci skip] 2026-01-22 15:35:44 +08:00
Carter Li b81c4a04db Merge pull request #2164 from fastfetch-cli/dev
Release: v2.58.0
2026-01-21 15:03:51 +08:00
李通洲 e26a05e3ac DE (Linux): ensures process returns successfully before using its output 2026-01-21 14:28:40 +08:00
李通洲 f08a1fb0b4 Release: v2.58.0 2026-01-21 14:12:53 +08:00
李通洲 2a82009ffe WM (macOS): checks the version of WindowServer instead of WindowManager 2026-01-21 09:39:31 +08:00
李通洲 e851f3a7ed Packages (Linux): optimises pacman detection code 2026-01-20 10:06:02 +08:00
Sean Will 3a865df09e Packages (Linux): Auto detect the DBPath directory for pacman on archlinux (#2154)
Fixes #2068
2026-01-19 19:44:30 -06:00
berezka bae80e2932 Add new KISS Linux logo based off favicon 2026-01-20 09:42:28 +08:00
berezka 4445e1e78d Fix Bedrock Linux version detection & futureproof for 0.8 2026-01-20 09:42:28 +08:00
Carter Li f0e3d3d8e1 Host (Linux): uses /sys/firmware/devicetree/base/smbios if available 2026-01-20 09:20:48 +08:00
Carter Li a313fd1572 Linux: uses the standard /sys/firmware/devicetree/base instead of the outdated /proc/device-tree 2026-01-20 09:11:47 +08:00
Carter Li 927d44d919 Chassis (Linux): uses /proc/device-tree/smbios if available 2026-01-20 08:59:22 +08:00
Carter Li 3b9a91e8af DE (Linux): silences compiler warnings [ci skip] 2026-01-20 08:34:46 +08:00
李通洲 70b4582bb6 DE (Linux): removes option slowVersionDetection
It's always true now

Fixes #2149
2026-01-19 10:55:30 +08:00
李通洲 461552b8d0 DynamicInterval: reruns prepare code before next interval
Fixes #2152
2026-01-16 20:02:03 +08:00
Carter Li 0f3db37199 Chassis (Linux): supports arm devices 2026-01-16 16:47:51 +08:00
Carter Li 67f16b269a DisplayServer (Linux): regenerates wlr-output-management-unstable-v1*
NOOP
2026-01-16 10:40:53 +08:00
李通洲 25671fa4b4 DisplayServer (Linux): fixes crashes on KDE beta
Fixes #2150
2026-01-16 10:13:50 +08:00
Kirill Rekhov 4eaa04d4a4 Presets: update examples to replace deprecated --load-config with --config 2026-01-16 00:43:03 +08:00
李通洲 7592885b9f GPU (Windows): ensures devId is uppercase for consistent parsing 2026-01-15 15:46:21 +08:00
李通洲 508a3fde36 Windows: fixes building 2026-01-15 15:43:05 +08:00
李通洲 f0273224e0 JsonSchema: update [ci skip] 2026-01-15 15:24:54 +08:00
李通洲 31e833c02e Colors: updates module description
Fixes #2147
2026-01-15 15:20:38 +08:00
李通洲 a20d915412 JsonConfig: supports --structure-disabled 2026-01-15 15:11:08 +08:00
李通洲 d3453de73c Global: puts more fastfetch-only properties into FFdata 2026-01-15 15:06:23 +08:00
李通洲 79a11c4728 CommandOption: adds a new option --structure-disabled (WIP)
To disable modules temporarily
2026-01-15 10:35:21 +08:00
李通洲 24591417ce FFstrbuf: clarify usage requirements for ffStrbufGetdelim and friends 2026-01-15 10:30:40 +08:00
李通洲 de6d066fc9 LICENSE: update copyright year to 2026 [ci skip] 2026-01-14 16:33:51 +08:00
Carter Li 6362c896f8 Packaging: updates debian stuff [ci skip] 2026-01-14 14:10:46 +08:00
98 changed files with 1327 additions and 657 deletions
+79 -42
View File
@@ -13,7 +13,7 @@ jobs:
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install codespell
shell: bash
@@ -32,7 +32,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: uname -a
run: uname -a
@@ -78,7 +78,7 @@ jobs:
ffversion: ${{ steps.ffversion.outputs.ffversion }}
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: uname -a
run: uname -a
@@ -102,7 +102,7 @@ jobs:
- name: Initialize CodeQL
if: matrix.arch == 'amd64'
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: c
@@ -114,7 +114,7 @@ jobs:
- name: perform CodeQL analysis
if: matrix.arch == 'amd64'
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
- name: list features
run: ./fastfetch --list-features
@@ -151,7 +151,7 @@ jobs:
cpack -V
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-linux-${{ matrix.arch }}
path: ./fastfetch-*.*
@@ -164,7 +164,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: uname -a
run: uname -a
@@ -217,7 +217,7 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-linux-i686
path: ./fastfetch-*.*
@@ -230,7 +230,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -257,7 +257,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-linux-armv7l
path: ./fastfetch-*.*
@@ -270,7 +270,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -293,7 +293,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-linux-armv6l
path: ./fastfetch-*.*
@@ -312,7 +312,7 @@ jobs:
- arch: s390x
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: uraimo/run-on-arch-action@v3
@@ -336,7 +336,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-linux-${{ matrix.arch }}
path: ./fastfetch-*.*
@@ -345,7 +345,7 @@ jobs:
name: Musl-amd64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: setup alpine linux
uses: jirutka/setup-alpine@master
@@ -374,7 +374,7 @@ jobs:
shell: alpine.sh {0}
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-musl-amd64
path: ./fastfetch-*.*
@@ -394,7 +394,7 @@ jobs:
runs-on: macos-latest
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: uname -a
run: uname -a
@@ -428,17 +428,17 @@ jobs:
run: ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-macos-${{ matrix.arch }}
path: ./fastfetch-*.*
sunos-amd64:
omnios-amd64:
runs-on: ubuntu-latest
name: SunOS-amd64
name: OmniOS-amd64
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: vmactions/omnios-vm@v1
@@ -447,23 +447,59 @@ jobs:
prepare: |
uname -a
pkg update --accept
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja
run: |
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
cmake --build . --target package --verbose -j4
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
cmake --build . --verbose -j4
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
time ./fastfetch -c presets/ci.jsonc --format json
time ./flashfetch
ldd fastfetch
ctest --output-on-failure
cpack
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-omnios-amd64")' >> CPackConfig.cmake
cpack -V
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-sunos-amd64
name: fastfetch-omnios-amd64
path: ./fastfetch-*.*
solaris-amd64:
runs-on: ubuntu-latest
name: Solaris-amd64
steps:
- name: checkout repository
uses: actions/checkout@v6
- name: run VM
uses: vmactions/solaris-vm@v1
with:
usesh: true
release: "11.4-gcc-14"
prepare: |
uname -a
pkg install cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja dconf mesa
run: |
export PKG_CONFIG_PATH=/usr/lib/64/pkgconfig
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
cmake --build . --verbose -j4
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
time ./fastfetch -c presets/ci.jsonc --format json
time ./flashfetch
ldd fastfetch
ctest --output-on-failure
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-solaris-amd64")' >> CPackConfig.cmake
cpack -V
- name: upload artifacts
uses: actions/upload-artifact@v6
with:
name: fastfetch-solaris-amd64
path: ./fastfetch-*.*
freebsd-amd64:
@@ -474,7 +510,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: cross-platform-actions/action@master
@@ -483,7 +519,7 @@ jobs:
architecture: x86-64
cpu_count: 4
shell: bash
version: '14.3'
version: '15.0'
run: |
uname -a
sudo pkg update
@@ -498,7 +534,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-freebsd-amd64
path: ./fastfetch-*.*
@@ -511,7 +547,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: cross-platform-actions/action@master
@@ -535,7 +571,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-openbsd-amd64
path: ./fastfetch-*.*
@@ -548,7 +584,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: cross-platform-actions/action@master
@@ -571,7 +607,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-netbsd-amd64
path: ./fastfetch-*.*
@@ -584,7 +620,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: vmactions/dragonflybsd-vm@v1
@@ -606,7 +642,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-dragonfly-amd64
path: ./fastfetch-*.*
@@ -619,7 +655,7 @@ jobs:
contents: read
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: run VM
uses: cross-platform-actions/action@master
@@ -641,7 +677,7 @@ jobs:
ctest --output-on-failure
- name: upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-haiku-amd64
path: ./fastfetch-*.*
@@ -681,7 +717,7 @@ jobs:
shell: msys2 {0}
steps:
- name: checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: setup-msys2
uses: msys2/setup-msys2@v2
@@ -723,7 +759,7 @@ jobs:
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
id: upload-unsigned-artifact
name: upload artifacts for signing
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
path: |
@@ -750,7 +786,7 @@ jobs:
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
- name: upload true artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
path: ./fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.*
@@ -772,7 +808,8 @@ jobs:
- openbsd-amd64
- netbsd-amd64
- dragonfly-amd64
- sunos-amd64
- solaris-amd64
- omnios-amd64
- haiku-amd64
- windows-hosts
permissions:
+44
View File
@@ -1,3 +1,47 @@
# 2.59.0
Changes:
* Fastfetch no longer relies on the unreliable environment variables `$USER` or `%USERPROFILE%` to determine the current username (Title)
* People who set `$USER` to customize the Fastfetch title should use `{ "type": "title", "format": "your-custom-user-name" }` to achieve the same result.
* Fastfetch no longer tries to probe inaccessible remote disk drives on Windows (Disk, Windows)
* People who have remote drives may use `{ "type": "disk", "hideFolders": "X:\\" }` to ignore problematic ones.
* This change removes some ugly hacks from the codebase and matches the behavior on `*nix`.
Features:
* Adds Oracle Solaris support (#2176, SunOS)
* Adds UID / SID detection (Title)
* In custom format: `{user-id}`
* Switches to native GPU detection on GNU/Hurd and removes the `libpciaccess` dependency (GPU, Hurd)
* Improves memory size detection on macOS (Memory, macOS)
* Avoids relying on `hw.memsize_usable` by default, which may not be available on older macOS versions
* Improves Windows disk detection accuracy and performance (Disk, Windows)
* Adds more ARM CPU parts and removes duplicated cases (CPU, ARM)
Logos:
* Adds 6-color support to the NixOS logo (including the small variant) (#2180)
# 2.58.0
An early release to fix compatibility issues with KDE Plasma 6.6.
Breaking changes:
* The `de.slowVersionDetection` option has been removed. Slow version detection is now always enabled, as required on non-FHS-compliant distros (e.g., NixOS). (#2149, DE, Linux)
Features:
* Adds the `--structure-disabled <modules...>` command-line flag to temporarily disable module structure printing.
* For example: `fastfetch --structure-disabled colors` removes the color blocks from the default output.
* Supports chassis type detection on Linux ARM devices when reported via the device tree (Chassis, Linux)
* Supports Bedrock Linux version detection (#2155, OS, Linux)
* Honors the `DBPath` and `RootDir` settings in `pacman.conf` when detecting Pacman packages (#2154, Packages, Linux)
Bugfixes:
* Fixes a crash issues on KDE Plasma 6.6 (Display, Linux)
* Fixes the Command module not working with `--dynamic-interval` (#2152, Command)
* Fixes Quartz Compositor version detection. It now correctly reports the version of `WindowServer` (`SkyLight`) instead of `WindowManager`. (WM, macOS)
Logos:
* Adds Kiss2
# 2.57.1
Features:
+30 -8
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.57.1
VERSION 2.59.0
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -92,7 +92,6 @@ 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_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)
@@ -110,6 +109,9 @@ option(ENABLE_LIBZFS "Enable libzfs" ON)
if(WIN32 AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
option(ENABLE_WIN7_COMPAT "Enable Windows 7 compatibility" ON)
endif()
if(APPLE)
option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF)
endif()
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
set(BINARY_LINK_TYPE dlopen CACHE STRING "How to link fastfetch")
@@ -1259,7 +1261,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_general.c
src/detection/gpu/gpu_gnu.c
src/detection/gpu/gpu_pci.c
src/detection/gtk_qt/gtk.c
src/detection/host/host_nosupport.c
@@ -1323,6 +1325,10 @@ endif()
if(NOT WIN32)
check_function_exists(pipe2 HAVE_PIPE2)
endif()
check_function_exists(memrchr HAVE_MEMRCHR)
if(NOT HAVE_MEMRCHR)
list(APPEND LIBFASTFETCH_SRC src/common/impl/memrchr.c)
endif()
if(ENABLE_SYSTEM_YYJSON)
find_package(yyjson)
@@ -1341,6 +1347,15 @@ add_library(libfastfetch OBJECT
${LIBFASTFETCH_SRC}
)
include(CheckCSourceCompiles)
check_c_source_compiles("int main(void){int arr[1];return _Countof(arr);}" COMPILER_SUPPORTS_COUNT_OF)
if(COMPILER_SUPPORTS_COUNT_OF)
message(STATUS "_Countof is supported by the compiler")
target_compile_definitions(libfastfetch PUBLIC FF_SUPPORTS_COUNT_OF=1)
else()
message(STATUS "_Countof is NOT supported by the compiler")
endif()
if(yyjson_FOUND)
target_compile_definitions(libfastfetch PUBLIC FF_USE_SYSTEM_YYJSON=1)
target_link_libraries(libfastfetch PUBLIC yyjson::yyjson)
@@ -1424,6 +1439,14 @@ elseif(GNU)
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE PATH_MAX=4096 O_PATH=0)
endif()
if(APPLE)
if(ENABLE_APPLE_MEMSIZE_USABLE)
target_compile_definitions(libfastfetch PUBLIC FF_APPLE_MEMSIZE_USABLE=1)
else()
target_compile_definitions(libfastfetch PUBLIC FF_APPLE_MEMSIZE_USABLE=0)
endif()
endif()
if(WIN32)
check_function_exists(_msize HAVE_MSVC_MSIZE)
if(HAVE_MSVC_MSIZE)
@@ -1630,10 +1653,6 @@ ff_lib_enable(DIRECTX_HEADERS
"DirectX-Headers"
"DirectX-Headers"
)
ff_lib_enable(PCIACCESS
"pciaccess"
"pciaccess"
)
if(ENABLE_THREADS)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS=1)
@@ -1657,6 +1676,9 @@ if(ENABLE_LIBZFS)
)
endif()
endif()
if(NOT HAVE_MEMRCHR)
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_CUSTOM_MEMRCHR=1)
endif()
if(LINUX)
target_link_libraries(libfastfetch
@@ -2089,7 +2111,7 @@ if(LINUX)
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
endif()
endif()
elseif(FreeBSD)
elseif(FreeBSD AND NOT DragonFly)
set(CPACK_FREEBSD_PACKAGE_LICENSE "MIT")
set(CPACK_GENERATOR "${CPACK_GENERATOR};FREEBSD")
endif()
+1 -1
View File
@@ -1,7 +1,7 @@
MIT License
Copyright (c) 2021-2023 Linus Dierheimer
Copyright (c) 2022-2025 Carter Li
Copyright (c) 2022-2026 Carter Li
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -1
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 illumos (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, illumos (SunOS), and Solaris.
<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" />
+12
View File
@@ -1,3 +1,15 @@
fastfetch (2.58.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.58.0
-- Carter Li <zhangsongcui@live.cn> Thu, 22 Jan 2026 15:30:58 +0800
fastfetch (2.57.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.57.1
-- Carter Li <zhangsongcui@live.cn> Wed, 14 Jan 2026 14:00:17 +0800
fastfetch (2.57.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.57.0
+5 -5
View File
@@ -318,7 +318,7 @@
"type": "string"
},
"cpuFormat": {
"description": "Output format of the module `CPU`. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: X86-64 CPU microarchitecture",
"description": "Output format of the module `CPU`. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: CPU microarchitecture\n 12. {numa-nodes}: NUMA node count",
"type": "string"
},
"cpucacheFormat": {
@@ -442,7 +442,7 @@
"type": "string"
},
"packagesFormat": {
"description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of emerge packages\n 7. {eopkg}: Number of eopkg packages\n 8. {xbps}: Number of xbps packages\n 9. {nix-system}: Number of nix-system packages\n 10. {nix-user}: Number of nix-user packages\n 11. {nix-default}: Number of nix-default packages\n 12. {apk}: Number of apk packages\n 13. {pkg}: Number of pkg packages\n 14. {flatpak-system}: Number of flatpak-system app packages\n 15. {flatpak-user}: Number of flatpak-user app packages\n 16. {snap}: Number of snap packages\n 17. {brew}: Number of brew packages\n 18. {brew-cask}: Number of brew-cask packages\n 19. {macports}: Number of macports packages\n 20. {scoop-user}: Number of scoop-user packages\n 21. {scoop-global}: Number of scoop-global packages\n 22. {choco}: Number of choco packages\n 23. {pkgtool}: Number of pkgtool packages\n 24. {paludis}: Number of paludis packages\n 25. {winget}: Number of winget packages\n 26. {opkg}: Number of opkg packages\n 27. {am-system}: Number of am-system packages\n 28. {sorcery}: Number of sorcery packages\n 29. {lpkg}: Number of lpkg packages\n 30. {lpkgbuild}: Number of lpkgbuild packages\n 31. {guix-system}: Number of guix-system packages\n 32. {guix-user}: Number of guix-user packages\n 33. {guix-home}: Number of guix-home packages\n 34. {linglong}: Number of linglong packages\n 35. {pacstall}: Number of pacstall packages\n 36. {mport}: Number of mport packages\n 37. {am-user}: Number of am-user (aka appman) packages\n 38. {pkgsrc}: Number of pkgsrc packages\n 39. {hpkg-system}: Number of hpkg-system packages\n 40. {hpkg-user}: Number of hpkg-user packages\n 41. {pisi}: Number of pisi packages\n 42. {soar}: Number of soar packages\n 43. {nix-all}: Total number of all nix packages\n 44. {flatpak-all}: Total number of all flatpak app packages\n 45. {brew-all}: Total number of all brew packages\n 46. {guix-all}: Total number of all guix packages\n 47. {hpkg-all}: Total number of all hpkg packages",
"description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of emerge packages\n 7. {eopkg}: Number of eopkg packages\n 8. {xbps}: Number of xbps packages\n 9. {nix-system}: Number of nix-system packages\n 10. {nix-user}: Number of nix-user packages\n 11. {nix-default}: Number of nix-default packages\n 12. {apk}: Number of apk packages\n 13. {pkg}: Number of pkg packages\n 14. {flatpak-system}: Number of flatpak-system app packages\n 15. {flatpak-user}: Number of flatpak-user app packages\n 16. {snap}: Number of snap packages\n 17. {brew}: Number of brew packages\n 18. {brew-cask}: Number of brew-cask packages\n 19. {macports}: Number of macports packages\n 20. {scoop-user}: Number of scoop-user packages\n 21. {scoop-global}: Number of scoop-global packages\n 22. {choco}: Number of choco packages\n 23. {pkgtool}: Number of pkgtool packages\n 24. {paludis}: Number of paludis packages\n 25. {winget}: Number of winget packages\n 26. {opkg}: Number of opkg packages\n 27. {am-system}: Number of am-system packages\n 28. {sorcery}: Number of sorcery packages\n 29. {lpkg}: Number of lpkg packages\n 30. {lpkgbuild}: Number of lpkgbuild packages\n 31. {guix-system}: Number of guix-system packages\n 32. {guix-user}: Number of guix-user packages\n 33. {guix-home}: Number of guix-home packages\n 34. {linglong}: Number of linglong packages\n 35. {pacstall}: Number of pacstall packages\n 36. {mport}: Number of mport packages\n 37. {am-user}: Number of am-user (aka appman) packages\n 38. {pkgsrc}: Number of pkgsrc packages\n 39. {hpkg-system}: Number of hpkg-system packages\n 40. {hpkg-user}: Number of hpkg-user packages\n 41. {pisi}: Number of pisi packages\n 42. {soar}: Number of soar packages\n 43. {kiss}: Number of kiss packages\n 44. {nix-all}: Total number of all nix packages\n 45. {flatpak-all}: Total number of all flatpak app packages\n 46. {brew-all}: Total number of all brew packages\n 47. {guix-all}: Total number of all guix packages\n 48. {hpkg-all}: Total number of all hpkg packages",
"type": "string"
},
"physicaldiskFormat": {
@@ -498,7 +498,7 @@
"type": "string"
},
"titleFormat": {
"description": "Output format of the module `Title`. See Wiki for formatting syntax\n 1. {user-name}: User name\n 2. {host-name}: Host name\n 3. {home-dir}: Home directory\n 4. {exe-path}: Executable path of current process\n 5. {user-shell}: User's default shell\n 6. {user-name-colored}: User name (colored)\n 7. {at-symbol-colored}: @ symbol (colored)\n 8. {host-name-colored}: Host name (colored)\n 9. {full-user-name}: Full user name",
"description": "Output format of the module `Title`. See Wiki for formatting syntax\n 1. {user-name}: User name\n 2. {host-name}: Host name\n 3. {home-dir}: Home directory\n 4. {exe-path}: Executable path of current process\n 5. {user-shell}: User's default shell\n 6. {user-name-colored}: User name (colored)\n 7. {at-symbol-colored}: @ symbol (colored)\n 8. {host-name-colored}: Host name (colored)\n 9. {full-user-name}: Full user name\n 10. {user-id}: UID (*nix) / SID (Windows)\n 11. {pid}: PID of current process",
"type": "string"
},
"themeFormat": {
@@ -546,7 +546,7 @@
"type": "string"
},
"zpoolFormat": {
"description": "Output format of the module `Zpool`. See Wiki for formatting syntax\n 1. {name}: Zpool name\n 2. {state}: Zpool state\n 3. {used}: Size used\n 4. {total}: Size total\n 5. {used-percentage}: Size percentage num\n 6. {fragmentation-percentage}: Fragmentation percentage num\n 7. {used-percentage-bar}: Size percentage bar\n 8. {fragmentation-percentage-bar}: Fragmentation percentage bar",
"description": "Output format of the module `Zpool`. See Wiki for formatting syntax\n 1. {name}: Zpool name\n 2. {guid}: Zpool guid\n 3. {state}: Zpool state\n 4. {used}: Size used\n 5. {allocated}: Size allocated\n 6. {total}: Size total\n 7. {used-percentage}: Size used percentage num\n 8. {allocated-percentage}: Size allocated percentage num\n 9. {fragmentation-percentage}: Fragmentation percentage num\n 10. {used-percentage-bar}: Size used percentage bar\n 11. {allocated-percentage-bar}: Size allocated percentage bar\n 12. {fragmentation-percentage-bar}: Fragmentation percentage bar\n 13. {is-readonly}: Is read-only",
"type": "string"
}
},
@@ -1999,7 +1999,7 @@
"additionalProperties": false,
"properties": {
"type": {
"description": "Print some colored blocks",
"description": "Display the terminal's 16-color palette",
"const": "colors"
},
"symbol": {
+1 -1
View File
@@ -1,4 +1,4 @@
// Load with --load-config examples/2.jsonc
// Load with --config examples/2.jsonc
// Note that you must replace the image path to an existing image to display it.
{
+1 -1
View File
@@ -1,4 +1,4 @@
// Load with --load-config examples/2.jsonc
// Load with --config examples/2.jsonc
// Note that you must replace the image path to an existing image to display it.
{
+1 -1
View File
@@ -1,4 +1,4 @@
// Load with --load-config examples/3.jsonc
// Load with --config examples/3.jsonc
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
+1 -1
View File
@@ -1,4 +1,4 @@
// Load with --load-config examples/4.jsonc
// Load with --config examples/4.jsonc
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
+1 -1
View File
@@ -1,4 +1,4 @@
// Load with --load-config examples/5.jsonc
// Load with --config examples/5.jsonc
{
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
+1 -1
View File
@@ -1,4 +1,4 @@
// Load with --load-config examples/2.jsonc
// Load with --config examples/2.jsonc
// Note that you must replace the image path to an existing image to display it.
{
+1 -1
View File
@@ -1,4 +1,4 @@
// Load with --load-config examples/2.jsonc
// Load with --config examples/2.jsonc
// Note that you must replace the image path to an existing image to display it.
{
+7 -1
View File
@@ -18,8 +18,14 @@ typedef struct FFPlatform
FFstrbuf cacheDir; // Trailing slash included
FFlist configDirs; // List of FFstrbuf, trailing slash included
FFlist dataDirs; // List of FFstrbuf, trailing slash included
FFstrbuf exePath; // The real path of current exe
FFstrbuf exePath; // The real path of current exe (empty if unavailable)
uint32_t pid;
#ifndef _WIN32
uint32_t uid;
#else
FFstrbuf sid;
#endif
FFstrbuf userName;
FFstrbuf fullUserName;
FFstrbuf hostName;
+12 -4
View File
@@ -103,6 +103,10 @@ FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultV
void ffStrbufUpperCase(FFstrbuf* strbuf);
void ffStrbufLowerCase(FFstrbuf* strbuf);
// Function alters the buffer to extract lines or delimited segments (replaces the delimiter with '\0')
// so that buffer MUST be heap allocated (NOT a static string)
// `lineptr` must be `NULL` and `n` MUST be `0` for the first call
// Caller MUST NOT free `*lineptr`
bool ffStrbufGetdelim(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
@@ -111,10 +115,14 @@ void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf
*
* @details Behaves like getline(3) but reads from a FFstrbuf.
*
* @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line.
* Can be NULL for the first call.
* @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line
(points to buffer's internal memory address to avoid memory allocation and copy).
MUST NOT be freed by the caller, unlike `getline(3)`.
* MUST be NULL for the first call.
* @param[in,out] n The pointer to the size of the buffer of lineptr.
* @param[in] buffer The buffer to read from. The buffer must not be a string literal.
MUST be 0 for the first call.
* @param[in] buffer The buffer to read from.
MUST be heap allocated (NOT a static string).
*
* @return true if a line has been read, false if the end of the buffer has been reached.
*/
@@ -124,7 +132,7 @@ static inline bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer)
}
/**
* @brief Restore the end of a line that was modified by ffStrbufGetline.
* @warning This function should be called before breaking an ffStrbufGetline loop.
* @warning This function should be called before breaking an ffStrbufGetline loop if `buffer` will be used later.
*/
static inline void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer)
{
+2 -2
View File
@@ -166,7 +166,7 @@ static const char *smcReadValue(io_connect_t conn, const UInt32Char_t key, doubl
case 1: *value = *(uint8_t *)(val.bytes); break;
case 2: *value = ntohs(*(uint16_t *)(val.bytes)); break;
case 4: *value = ntohl(*(uint32_t *)(val.bytes)); break;
case 8: *value = ntohll(*(uint64_t *)(val.bytes)); break;
case 8: *value = (double) ntohll(*(uint64_t *)(val.bytes)); break;
default:
return "Unsupported SMC unsigned integer data size";
}
@@ -213,7 +213,7 @@ static const char *smcReadValue(io_connect_t conn, const UInt32Char_t key, doubl
case 1: *value = *(int8_t *)(val.bytes); break;
case 2: *value = ntohs(*(int16_t *)(val.bytes)); break;
case 4: *value = ntohl(*(int32_t *)(val.bytes)); break;
case 8: *value = ntohll(*(int64_t *)(val.bytes)); break;
case 8: *value = (double)ntohll(*(int64_t *)(val.bytes)); break;
default: return "Unsupported SMC signed integer data size";
}
}
+3 -1
View File
@@ -3,7 +3,9 @@
#include <assert.h>
#ifdef __has_builtin
#if __has_builtin(__is_array)
#if !__cplusplus && FF_SUPPORTS_COUNT_OF
#define ARRAY_SIZE(x) _Countof(x)
#elif __has_builtin(__is_array)
#define ARRAY_SIZE(x) ({ static_assert(__is_array(__typeof__(x)), "Must be an array"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
#elif __has_builtin(__builtin_types_compatible_p)
#define ARRAY_SIZE(x) ({ static_assert(!__builtin_types_compatible_p(__typeof__(x), __typeof__(&*(x))), "Must not be a pointer"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
+3 -10
View File
@@ -1,15 +1,8 @@
#pragma once
#include "fastfetch.h"
// Things only needed by fastfetch
typedef struct FFdata
{
FFstrbuf structure;
bool configLoaded;
} FFdata;
#include "common/ffdata.h"
void ffPrepareCommandOption(FFdata* data);
void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc);
void ffMigrateCommandOptionToJsonc(FFdata* data, yyjson_mut_doc* jsonDoc);
void ffPrintCommandOption(FFdata* data);
void ffMigrateCommandOptionToJsonc(FFdata* data);
bool ffParseModuleOptions(const char* key, const char* value);
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "common/FFstrbuf.h"
typedef enum __attribute__((__packed__)) FFDataResultDocType
{
FF_RESULT_DOC_TYPE_DEFAULT = 0,
FF_RESULT_DOC_TYPE_JSON,
FF_RESULT_DOC_TYPE_CONFIG,
FF_RESULT_DOC_TYPE_CONFIG_FULL,
} FFDataResultDocType;
// FFdata aggregates configuration, generation parameters, and output state used by fastfetch.
// It holds the parsed configuration document, a mutable JSON document for results, and related metadata.
typedef struct FFdata
{
yyjson_doc* configDoc; // Parsed JSON configuration document
yyjson_mut_doc* resultDoc; // Mutable JSON document for storing results
FFstrbuf structure; // Custom output structure from command line
FFstrbuf structureDisabled; // Disabled modules in the output structure from command line
FFstrbuf genConfigPath; // Path to generate configuration file
FFDataResultDocType docType; // Type of result document
bool configLoaded;
} FFdata;
+8
View File
@@ -16,6 +16,10 @@ void ffPlatformInit(FFPlatform* platform)
ffStrbufInit(&platform->hostName);
ffStrbufInit(&platform->userShell);
#ifdef _WIN32
ffStrbufInit(&platform->sid);
#endif
FFPlatformSysinfo* info = &platform->sysinfo;
ffStrbufInit(&info->name);
@@ -51,6 +55,10 @@ void ffPlatformDestroy(FFPlatform* platform)
ffStrbufDestroy(&platform->userShell);
ffStrbufDestroy(&platform->fullUserName);
#ifdef _WIN32
ffStrbufDestroy(&platform->sid);
#endif
FFPlatformSysinfo* info = &platform->sysinfo;
ffStrbufDestroy(&info->architecture);
ffStrbufDestroy(&info->name);
+12 -11
View File
@@ -28,15 +28,15 @@ static void getExePath(FFPlatform* platform)
if (exePathLen >= 0)
exePath[exePathLen] = '\0';
#elif defined(__APPLE__)
int exePathLen = proc_pidpath((int) getpid(), exePath, sizeof(exePath));
int exePathLen = proc_pidpath((pid_t) platform->pid, exePath, sizeof(exePath));
#elif defined(__FreeBSD__) || defined(__NetBSD__)
size_t exePathLen = sizeof(exePath);
if(sysctl(
(int[]){CTL_KERN,
#ifdef __FreeBSD__
KERN_PROC, KERN_PROC_PATHNAME, (int) getpid()
KERN_PROC, KERN_PROC_PATHNAME, (pid_t) platform->pid
#else
KERN_PROC_ARGS, (int) getpid(), KERN_PROC_PATHNAME
KERN_PROC_ARGS, platform->pid, KERN_PROC_PATHNAME
#endif
}, 4,
exePath, &exePathLen,
@@ -177,17 +177,16 @@ static void getDataDirs(FFPlatform* platform)
static void getUserName(FFPlatform* platform, const struct passwd* pwd)
{
const char* user = getenv("USER");
if(!ffStrSet(user) && pwd)
user = pwd->pw_name;
ffStrbufAppendS(&platform->userName, user);
if (pwd)
{
ffStrbufAppendS(&platform->fullUserName, pwd->pw_gecos);
ffStrbufSetS(&platform->userName, pwd->pw_name);
ffStrbufSetS(&platform->fullUserName, pwd->pw_gecos);
ffStrbufTrimSpace(&platform->fullUserName);
}
else
{
ffStrbufSetS(&platform->userName, getenv("USER"));
}
}
static void getHostName(FFPlatform* platform, const struct utsname* uts)
@@ -227,7 +226,9 @@ static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts)
void ffPlatformInitImpl(FFPlatform* platform)
{
struct passwd* pwd = getpwuid(getuid());
platform->pid = (uint32_t) getpid();
platform->uid = getuid();
struct passwd* pwd = getpwuid(platform->uid);
struct utsname uts;
if(uname(&uts) < 0)
+34 -15
View File
@@ -1,6 +1,7 @@
#include "FFPlatform_private.h"
#include "common/io.h"
#include "common/library.h"
#include "common/mallocHelper.h"
#include "common/stringUtils.h"
#include "common/windows/unicode.h"
#include "common/windows/registry.h"
@@ -8,6 +9,7 @@
#include <windows.h>
#include <shlobj.h>
#include <sddl.h>
#define SECURITY_WIN32 1 // For secext.h
#include <secext.h>
@@ -70,15 +72,15 @@ static void getCacheDir(FFPlatform* platform)
static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId)
{
PWSTR pPath = NULL;
if(SUCCEEDED(SHGetKnownFolderPath(folderId, 0, NULL, &pPath)))
if (SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT, NULL, &pPath)))
{
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateWS(pPath);
CoTaskMemFree(pPath);
ffStrbufReplaceAllC(&buffer, '\\', '/');
ffStrbufEnsureEndsWithC(&buffer, '/');
if (!ffListContains(dirs, &buffer, (void*) ffStrbufEqual))
ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer);
}
CoTaskMemFree(pPath);
}
static void platformPathAddEnvSuffix(FFlist* dirs, const char* env, const char* suffix)
@@ -136,21 +138,37 @@ static void getDataDirs(FFPlatform* platform)
static void getUserName(FFPlatform* platform)
{
const char* userName = getenv("USERNAME");
if (ffStrSet(userName))
ffStrbufSetS(&platform->userName, userName);
else
{
wchar_t buffer[256];
DWORD len = ARRAY_SIZE(buffer);
if(GetUserNameW(buffer, &len))
ffStrbufSetWS(&platform->userName, buffer);
}
wchar_t buffer[256];
DWORD len = ARRAY_SIZE(buffer);
if (GetUserNameExW(NameDisplay, buffer, &len))
DWORD size = ARRAY_SIZE(buffer);
if (GetUserNameExW(NameDisplay, buffer, &size))
ffStrbufSetWS(&platform->fullUserName, buffer);
size = ARRAY_SIZE(buffer);
if (GetUserNameW(buffer, &size)) // GetUserNameExW(10002)?
{
ffStrbufSetWS(&platform->userName, buffer);
size = 0;
DWORD refDomainSize = 0;
SID_NAME_USE sidNameUse = SidTypeUnknown;
LookupAccountNameW(NULL, buffer, NULL, &size, NULL, &refDomainSize, &sidNameUse);
if (size > 0)
{
FF_AUTO_FREE PSID sid = (PSID) malloc(size);
FF_AUTO_FREE LPWSTR refDomain = (LPWSTR) malloc(refDomainSize * sizeof(wchar_t));
if (LookupAccountNameW(NULL, buffer, sid, &size, refDomain, &refDomainSize, &sidNameUse))
{
LPWSTR sidString;
if (ConvertSidToStringSidW(sid, &sidString))
{
ffStrbufSetWS(&platform->sid, sidString);
LocalFree(sidString);
}
}
}
}
else
ffStrbufSetS(&platform->userName, getenv("USERNAME"));
}
static void getHostName(FFPlatform* platform)
@@ -289,6 +307,7 @@ static void getSystemArchitectureAndPageSize(FFPlatformSysinfo* info)
void ffPlatformInitImpl(FFPlatform* platform)
{
platform->pid = (uint32_t) GetCurrentProcessId();
getExePath(platform);
getHomeDir(platform);
getCacheDir(platform);
+1 -1
View File
@@ -183,7 +183,7 @@ const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char unti
if(value == NULL)
return NULL;
char* end = strchr(value, until);
const char* end = strchr(value, until);
if(end == NULL)
ffStrbufAppendS(strbuf, value);
else
+79 -54
View File
@@ -61,7 +61,7 @@ bool ffParseModuleOptions(const char* key, const char* value)
}
fprintf(stderr, "Error: Unsupported module option: %s\n", key);
fputs(" Support of module options has been removed. Please add the flag to the JSON config instead.\n", stderr);
fprintf(stderr, " Example (demonstration only): `{ \"modules\": [ { \"type\": \"%s\", \"%s\": %s%s%s } ] }`\n", moduleName.chars, jsonKey.chars, value ? "\"" : "", value ? value : "true", value ? "\"" : "");
fprintf(stderr, " Example (demonstration only): `{ \"modules\": [ { \"type\": \"%s\", \"%s\": %s%s%s } ] }`\n", moduleName.chars, jsonKey.chars, value ? "\"" : "", value ?: "true", value ? "\"" : "");
fputs(" See <https://github.com/fastfetch-cli/fastfetch/wiki/Configuration> for more information.\n", stderr);
exit(477);
}
@@ -71,40 +71,66 @@ bool ffParseModuleOptions(const char* key, const char* value)
void ffPrepareCommandOption(FFdata* data)
{
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_CPUUSAGE_MODULE_NAME, ':'))
ffPrepareCPUUsage();
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_DISKIO_MODULE_NAME, ':'))
char* moduleType = NULL;
size_t moduleLen = 0;
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure))
{
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
ffInitDiskIOOptions(&options);
ffPrepareDiskIO(&options);
}
#define FF_IF_MODULE_MATCH(moduleNameConstant) if (moduleLen == strlen(moduleNameConstant) \
&& ffStrEqualsIgnCase(moduleType, moduleNameConstant) \
&& !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':'))
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_NETIO_MODULE_NAME, ':'))
{
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
ffInitNetIOOptions(&options);
ffPrepareNetIO(&options);
}
switch (moduleType[0])
{
case 'C': case 'c':
FF_IF_MODULE_MATCH(FF_CPUUSAGE_MODULE_NAME)
ffPrepareCPUUsage();
break;
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_PUBLICIP_MODULE_NAME, ':'))
{
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
ffInitPublicIpOptions(&options);
ffPreparePublicIp(&options);
}
case 'D': case 'd':
FF_IF_MODULE_MATCH(FF_DISKIO_MODULE_NAME)
{
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
ffInitDiskIOOptions(&options);
ffPrepareDiskIO(&options);
}
break;
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_WEATHER_MODULE_NAME, ':'))
{
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
ffInitWeatherOptions(&options);
ffPrepareWeather(&options);
case 'N': case 'n':
FF_IF_MODULE_MATCH(FF_NETIO_MODULE_NAME)
{
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
ffInitNetIOOptions(&options);
ffPrepareNetIO(&options);
}
break;
case 'P': case 'p':
FF_IF_MODULE_MATCH(FF_PUBLICIP_MODULE_NAME)
{
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
ffInitPublicIpOptions(&options);
ffPreparePublicIp(&options);
}
break;
case 'W': case 'w':
FF_IF_MODULE_MATCH(FF_WEATHER_MODULE_NAME)
{
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
ffInitWeatherOptions(&options);
ffPrepareWeather(&options);
}
break;
}
#undef FF_IF_MODULE_MATCH
}
}
static void genJsonConfig(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
static void genJsonConfig(FFdata* data, FFModuleBaseInfo* baseInfo, void* options)
{
yyjson_mut_doc* doc = data->resultDoc;
yyjson_mut_val* modules = yyjson_mut_obj_get(doc->root, "modules");
if (!modules)
modules = yyjson_mut_obj_add_arr(doc, doc->root, "modules");
@@ -112,7 +138,7 @@ static void genJsonConfig(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_
FF_STRBUF_AUTO_DESTROY type = ffStrbufCreateS(baseInfo->name);
ffStrbufLowerCase(&type);
if (instance.state.fullConfig)
if (data->docType == FF_RESULT_DOC_TYPE_CONFIG_FULL)
{
yyjson_mut_val* module = yyjson_mut_obj(doc);
yyjson_mut_obj_add_strbuf(doc, module, "type", &type);
@@ -131,8 +157,9 @@ static void genJsonConfig(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_
}
}
static void genJsonResult(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
static void genJsonResult(FFdata* data, FFModuleBaseInfo* baseInfo, void* options)
{
yyjson_mut_doc* doc = data->resultDoc;
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name);
if (baseInfo->generateJsonResult)
@@ -142,9 +169,9 @@ static void genJsonResult(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_
}
static void parseStructureCommand(
FFdata* data,
const char* line,
void (*fn)(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* jsonDoc),
yyjson_mut_doc* jsonDoc
void (*fn)(FFdata*, FFModuleBaseInfo* baseInfo, void* options)
)
{
if(ffCharIsEnglishAlphabet(line[0]))
@@ -156,8 +183,8 @@ static void parseStructureCommand(
{
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
baseInfo->initOptions(optionBuf);
if (__builtin_expect(jsonDoc != NULL, false))
fn(baseInfo, optionBuf, jsonDoc);
if (__builtin_expect(data->resultDoc != NULL, false))
fn(data, baseInfo, optionBuf);
else
baseInfo->printModule(optionBuf);
baseInfo->destroyOptions(optionBuf);
@@ -169,30 +196,32 @@ static void parseStructureCommand(
ffPrintError(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "<no implementation provided>");
}
void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc)
void ffPrintCommandOption(FFdata* data)
{
//Parse the structure and call the modules
int32_t thres = instance.config.display.stat;
uint32_t startIndex = 0;
while (startIndex < data->structure.length)
char* moduleType = NULL;
size_t moduleLen = 0;
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure))
{
uint32_t colonIndex = ffStrbufNextIndexC(&data->structure, startIndex, ':');
data->structure.chars[colonIndex] = '\0';
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':'))
continue;
double ms = 0;
if(thres >= 0)
ms = ffTimeGetTick();
parseStructureCommand(data->structure.chars + startIndex, genJsonResult, jsonDoc);
parseStructureCommand(data, moduleType, genJsonResult);
if(thres >= 0)
{
ms = ffTimeGetTick() - ms;
if (jsonDoc)
if (data->resultDoc)
{
yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(jsonDoc->root);
yyjson_mut_obj_add_real(jsonDoc, moduleJson, "stat", ms);
yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(data->resultDoc->root);
yyjson_mut_obj_add_real(data->resultDoc, moduleJson, "stat", ms);
}
else
{
@@ -205,28 +234,24 @@ void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc)
}
#if defined(_WIN32)
if (!jsonDoc && !instance.config.display.noBuffer) fflush(stdout);
if (!data->resultDoc && !instance.config.display.noBuffer) fflush(stdout);
#endif
startIndex = colonIndex + 1;
}
}
void ffMigrateCommandOptionToJsonc(FFdata* data, yyjson_mut_doc* jsonDoc)
void ffMigrateCommandOptionToJsonc(FFdata* data)
{
//If we don't have a custom structure, use the default one
if(data->structure.length == 0)
ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
//Parse the structure and call the modules
uint32_t startIndex = 0;
while (startIndex < data->structure.length)
char* moduleType = NULL;
size_t moduleLen = 0;
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure))
{
uint32_t colonIndex = ffStrbufNextIndexC(&data->structure, startIndex, ':');
data->structure.chars[colonIndex] = '\0';
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':'))
continue;
parseStructureCommand(data->structure.chars + startIndex, genJsonConfig, jsonDoc);
startIndex = colonIndex + 1;
parseStructureCommand(data, moduleType, genJsonConfig);
}
}
+6 -13
View File
@@ -28,8 +28,6 @@ static void initState(FFstate* state)
state->titleFqdn = false;
ffPlatformInit(&state->platform);
state->configDoc = NULL;
state->resultDoc = NULL;
state->dynamicInterval = 0;
{
@@ -96,8 +94,8 @@ static void exitSignalHandler(FF_MAYBE_UNUSED int signal)
void ffStart(void)
{
ffDisableLinewrap = instance.config.display.disableLinewrap && !instance.config.display.pipe && !instance.state.resultDoc;
ffHideCursor = instance.config.display.hideCursor && !instance.config.display.pipe && !instance.state.resultDoc;
ffDisableLinewrap = instance.config.display.disableLinewrap && !instance.config.display.pipe;
ffHideCursor = instance.config.display.hideCursor && !instance.config.display.pipe;
#ifdef _WIN32
SetErrorMode(SEM_FAILCRITICALERRORS);
@@ -124,7 +122,7 @@ void ffStart(void)
#endif
//reset everything to default before we start printing
if(!instance.config.display.pipe && !instance.state.resultDoc)
if(!instance.config.display.pipe)
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
if(ffHideCursor)
@@ -138,8 +136,6 @@ void ffStart(void)
fputs("\033[?1049h\033[H", stdout); // Enable alternate buffer
fflush(stdout);
}
ffLogoPrint();
}
void ffFinish(void)
@@ -157,9 +153,6 @@ static void destroyConfig(void)
static void destroyState(void)
{
ffPlatformDestroy(&instance.state.platform);
yyjson_doc_free(instance.state.configDoc);
yyjson_mut_doc_free(instance.state.resultDoc);
ffStrbufDestroy(&instance.state.genConfigPath);
}
void ffDestroyInstance(void)
@@ -232,9 +225,6 @@ void ffListFeatures(void)
#if FF_HAVE_FREETYPE
"freetype\n"
#endif
#if FF_HAVE_PCIACCESS
"libpciaccess\n"
#endif
#if FF_HAVE_PULSE
"libpulse\n"
#endif
@@ -265,6 +255,9 @@ void ffListFeatures(void)
#if FF_WIN7_COMPAT
"Windows 7 Compatibility\n"
#endif
#if FF_APPLE_MEMSIZE_USABLE
"Apple memsize_usable\n"
#endif
""
, stdout);
}
+1 -1
View File
@@ -23,7 +23,7 @@ static void createSubfolders(const char* fileName)
{
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
char *token = NULL;
const char *token = NULL;
while((token = strchr(fileName, '/')) != NULL)
{
ffStrbufAppendNS(&path, (uint32_t)(token - fileName + 1), fileName);
+9 -4
View File
@@ -230,9 +230,10 @@ static bool matchesJsonArray(const char* str, yyjson_val* val)
return false;
}
static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
static const char* printJsonConfig(FFdata* data, bool prepare)
{
yyjson_val* const root = yyjson_doc_get_root(instance.state.configDoc);
yyjson_mut_doc* jsonDoc = data->resultDoc;
yyjson_val* const root = yyjson_doc_get_root(data->configDoc);
assert(root);
if (!yyjson_is_obj(root))
@@ -298,6 +299,9 @@ static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
else
return "modules must be an array of strings or objects";
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, type, ':'))
continue;
if(prepare)
prepareModuleJsonObject(type, module);
else
@@ -329,9 +333,10 @@ static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
return NULL;
}
void ffPrintJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
void ffPrintJsonConfig(FFdata* data, bool prepare)
{
const char* error = printJsonConfig(prepare, jsonDoc);
yyjson_mut_doc* jsonDoc = data->resultDoc;
const char* error = printJsonConfig(data, prepare);
if (error)
{
if (jsonDoc)
+19
View File
@@ -0,0 +1,19 @@
#include "common/memrchr.h"
#include <stddef.h>
#include <stdint.h>
void* memrchr(const void* s, int c, size_t n)
{
if (n == 0) return NULL;
const uint8_t uc = (uint8_t) c;
const uint8_t* p = (const uint8_t*) s + n;
while (n--)
{
if (*--p == uc) return (void*) p;
}
return NULL;
}
+6 -6
View File
@@ -72,7 +72,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
}
int pid = getpid();
uint32_t pid = instance.state.platform.pid;
struct {
struct rt_msghdr hdr;
@@ -85,7 +85,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
.rtm_version = RTM_VERSION,
.rtm_addrs = RTA_DST | RTA_IFP | RTA_IFA,
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
.rtm_pid = pid,
.rtm_pid = (pid_t) pid,
.rtm_seq = 1,
},
.dst = {
@@ -99,7 +99,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
return false;
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid))
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == (pid_t) pid))
;
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
@@ -145,7 +145,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
}
int pid = getpid();
uint32_t pid = instance.state.platform.pid;
struct {
struct rt_msghdr hdr;
@@ -158,7 +158,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
.rtm_version = RTM_VERSION,
.rtm_addrs = RTA_DST | RTA_IFP,
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
.rtm_pid = pid,
.rtm_pid = (pid_t) pid,
.rtm_seq = 2,
},
.dst = {
@@ -172,7 +172,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
return false;
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == pid))
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == (pid_t) pid))
;
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
+2 -2
View File
@@ -19,7 +19,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
}
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
unsigned pid = (unsigned) getpid();
uint32_t pid = instance.state.platform.pid;
FF_DEBUG("Process PID: %u", pid);
// Bind socket
@@ -235,7 +235,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
}
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
unsigned pid = (unsigned) getpid();
uint32_t pid = instance.state.platform.pid;
FF_DEBUG("Process PID: %u", pid);
// Bind socket
+23 -10
View File
@@ -1,7 +1,10 @@
#include "fastfetch.h"
#include "common/processing.h"
#include "common/io.h"
#include "common/windows/unicode.h"
#include "common/windows/nt.h"
#include <stdalign.h>
#include <windows.h>
#include <ntstatus.h>
#include <winternl.h>
@@ -51,7 +54,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
wchar_t pipeName[32];
static unsigned pidCounter = 0;
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u-%u", GetCurrentProcessId(), ++pidCounter);
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u-%u", instance.state.platform.pid, ++pidCounter);
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = CreateNamedPipeW(
pipeName,
@@ -193,9 +196,16 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
exit:
{
DWORD exitCode = 0;
if (GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE && exitCode != 0)
return "Child process exited with an error";
PROCESS_BASIC_INFORMATION info = {};
ULONG size;
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size)))
{
assert(size == sizeof(info));
if (info.ExitStatus != STILL_ACTIVE && info.ExitStatus != 0)
return "Child process exited with an error";
}
else
return "NtQueryInformationProcess(ProcessBasicInformation) failed";
}
return NULL;
@@ -204,7 +214,7 @@ exit:
bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui)
{
FF_AUTO_CLOSE_FD HANDLE hProcess = pid == 0
? GetCurrentProcess()
? NtCurrentProcess()
: OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if (hProcess == NULL)
@@ -227,12 +237,15 @@ bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFst
}
if(exe)
{
DWORD bufSize = exe->allocated;
if(QueryFullProcessImageNameA(hProcess, 0, exe->chars, &bufSize))
// TODO: It's possible to query the command line with `NtQueryInformationProcess(60/*ProcessCommandLineInformation*/)` since Windows 8.1
alignas(alignof(UNICODE_STRING)) uint8_t buffer[4096];
ULONG size;
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, &buffer, sizeof(buffer), &size)))
{
// We use full path here
// Querying command line of remote processes in Windows requires either WMI or ReadProcessMemory
exe->length = bufSize;
UNICODE_STRING* imageName = (UNICODE_STRING*)buffer;
ffStrbufSetNWS(exe, imageName->Length / sizeof(wchar_t), imageName->Buffer);
if (exePath) ffStrbufSet(exePath, exe);
}
else
+5 -3
View File
@@ -1,11 +1,10 @@
#pragma once
#include "fastfetch.h"
#include "common/ffdata.h"
#include "common/option.h"
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs);
const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair pairs[]);
void ffPrintJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc);
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs);
yyjson_api_inline yyjson_mut_val* yyjson_mut_strbuf(yyjson_mut_doc *doc, const FFstrbuf* buf) {
return yyjson_mut_strncpy(doc, buf->chars, buf->length);
@@ -23,3 +22,6 @@ yyjson_api_inline bool yyjson_mut_arr_add_strbuf(yyjson_mut_doc *doc,
const FFstrbuf* buf) {
return yyjson_mut_arr_add_strncpy(doc, obj, buf->chars, buf->length);
}
void ffPrintJsonConfig(FFdata* data, bool prepare);
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs);
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// `memrchr` is a GNU extension and may not be declared by system headers even when the symbol exists.
// Declare it unconditionally; the build system provides a fallback implementation when missing.
void* memrchr(const void* s, int c, size_t n);
#ifdef __cplusplus
}
#endif
+28 -6
View File
@@ -6,12 +6,12 @@
#define D3DKMT_ALIGN64 __attribute__((aligned(8)))
typedef struct _PROCESSOR_POWER_INFORMATION {
ULONG Number;
ULONG MaxMhz;
ULONG CurrentMhz;
ULONG MhzLimit;
ULONG MaxIdleState;
ULONG CurrentIdleState;
ULONG Number;
ULONG MaxMhz;
ULONG CurrentMhz;
ULONG MhzLimit;
ULONG MaxIdleState;
ULONG CurrentIdleState;
} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
NTSTATUS NTAPI NtPowerInformation(
@@ -248,3 +248,25 @@ NtQueryDirectoryFile(
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileName OPTIONAL,
IN BOOLEAN RestartScan);
// https://ntdoc.m417z.com/process_devicemap_information_ex
typedef struct _PROCESS_DEVICEMAP_INFORMATION_EX
{
union
{
struct
{
HANDLE DirectoryHandle; // A handle to a directory object that can be set as the new device map for the process. This handle must have DIRECTORY_TRAVERSE access.
} Set;
struct
{
ULONG DriveMap; // A bitmask that indicates which drive letters are currently in use in the process's device map.
UCHAR DriveType[32]; // A value that indicates the type of each drive (e.g., local disk, network drive, etc.). // DRIVE_* WinBase.h
} Query;
};
ULONG Flags; // PROCESS_LUID_DOSDEVICES_ONLY
} PROCESS_DEVICEMAP_INFORMATION_EX, *PPROCESS_DEVICEMAP_INFORMATION_EX;
#ifndef NtCurrentProcess
#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
#endif
+5
View File
@@ -13,6 +13,11 @@ static inline void ffStrbufInitWSV(FFstrbuf* result, const std::wstring_view sou
return ffStrbufInitNWS(result, (uint32_t) source.size(), source.data());
}
static inline FFstrbuf ffStrbufCreateWSV(const std::wstring_view source)
{
return ffStrbufCreateNWS((uint32_t) source.size(), source.data());
}
static inline void ffStrbufSetWSV(FFstrbuf* result, const std::wstring_view source)
{
return ffStrbufSetNWS(result, (uint32_t) source.size(), source.data());
+8
View File
@@ -468,6 +468,14 @@
"default": "\"fastfetch --print-structure\""
}
},
{
"long": "structure-disabled",
"desc": "Disable specific modules in the structure",
"remark": "Must be a colon-separated list of keys",
"arg": {
"type": "structure"
}
},
{
"long": "stat",
"desc": "Show time usage (in ms) for individual modules",
+7 -1
View File
@@ -10,7 +10,13 @@
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */)
{
// Actually bluetoothapis.dll, but it's missing on Windows 7
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bthprops.cpl", 1)
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis,
#if FF_WIN7_COMPAT
"bthprops.cpl"
#else
"bluetoothapis.dll"
#endif
, 1)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstDevice)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextDevice)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindDeviceClose)
@@ -79,8 +79,7 @@ const char* ffBluetoothDetectBattery(FFlist* devices)
batt = data.get<uint8_t>();
else
{
FF_STRBUF_AUTO_DESTROY addr; // MAC address without colon
ffStrbufInitWSV(&addr, data.get<std::wstring_view>());
FF_STRBUF_AUTO_DESTROY addr = ffStrbufCreateWSV(data.get<std::wstring_view>()); // MAC address without colon
if (__builtin_expect(addr.length != 12, 0))
continue;
@@ -54,7 +54,13 @@ static_assert(sizeof(BTH_LOCAL_RADIO_INFO) == 292, "BTH_LOCAL_RADIO_INFO should
const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */)
{
// Actually bluetoothapis.dll, but it's missing on Windows 7
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bthprops.cpl", 1)
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis,
#if FF_WIN7_COMPAT
"bthprops.cpl"
#else
"bluetoothapis.dll"
#endif
, 1)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstRadio)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextRadio)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindRadioClose)
+8 -4
View File
@@ -2,8 +2,6 @@
#include "common/io.h"
#include "common/smbiosHelper.h"
#include <stdlib.h>
const char* ffDetectBoard(FFBoardResult* board)
{
if (ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_name", "/sys/class/dmi/id/board_name", &board->name))
@@ -12,11 +10,17 @@ const char* ffDetectBoard(FFBoardResult* board)
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_vendor", "/sys/class/dmi/id/board_vendor", &board->vendor);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_version", "/sys/class/dmi/id/board_version", &board->version);
}
else if (ffReadFileBuffer("/proc/device-tree/board", &board->name))
else if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/baseboard/product", &board->name))
{
ffStrbufTrimRight(&board->name, '\0');
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/baseboard/manufacturer", &board->vendor))
ffStrbufTrimRight(&board->vendor, '\0');
}
else if (ffReadFileBuffer("/sys/firmware/devicetree/base/board", &board->name))
{
ffStrbufTrimRightSpace(&board->name);
}
else if (ffReadFileBuffer("/proc/device-tree/compatible", &board->vendor))
else if (ffReadFileBuffer("/sys/firmware/devicetree/base/compatible", &board->vendor))
{
uint32_t comma = ffStrbufFirstIndexC(&board->vendor, ',');
if (comma < board->vendor.length)
+2 -1
View File
@@ -9,6 +9,7 @@
#endif
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdalign.h>
#ifdef __NetBSD__
typedef uint16_t efi_char;
@@ -23,7 +24,7 @@ const char* ffDetectBootmgr(FFBootmgrResult* result)
FF_AUTO_CLOSE_FD int efifd = open("/dev/efi", O_RDWR | O_CLOEXEC);
if (efifd < 0) return "open(/dev/efi) failed";
uint8_t buffer[2048];
alignas(uint16_t) uint8_t buffer[2048];
struct efi_var_ioc ioc = {
.vendor = EFI_GLOBAL_VARIABLE,
.data = buffer,
+3 -1
View File
@@ -2,11 +2,13 @@
#include "common/io.h"
#include "efi_helper.h"
#include <stdalign.h>
#define FF_EFIVARS_PATH_PREFIX "/sys/firmware/efi/efivars/"
const char* ffDetectBootmgr(FFBootmgrResult* result)
{
uint8_t buffer[2048];
alignas(uint16_t) uint8_t buffer[2048];
if (ffReadFileData(FF_EFIVARS_PATH_PREFIX "BootCurrent-" FF_EFI_GLOBAL_GUID, sizeof(buffer), buffer) != 6)
return "Failed to read efivar: BootCurrent";
+2 -1
View File
@@ -1,13 +1,14 @@
#include "bootmgr.h"
#include "efi_helper.h"
#include "common/io.h"
#include "common/windows/nt.h"
#include <windows.h>
const char* enablePrivilege(const wchar_t* privilege)
{
FF_AUTO_CLOSE_FD HANDLE token = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
if (!OpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
return "OpenProcessToken() failed";
TOKEN_PRIVILEGES tp = {
+32 -10
View File
@@ -2,20 +2,42 @@
#include "common/io.h"
#include "common/smbiosHelper.h"
#include <stdlib.h>
#include <ctype.h>
const char* ffDetectChassis(FFChassisResult* result)
{
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_type", "/sys/class/dmi/id/chassis_type", &result->type);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_serial", "/sys/class/dmi/id/chassis_serial", &result->serial);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_vendor", "/sys/class/dmi/id/chassis_vendor", &result->vendor);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_version", "/sys/class/dmi/id/chassis_version", &result->version);
if(result->type.length)
if (ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_type", "/sys/class/dmi/id/chassis_type", &result->type))
{
const char* typeStr = ffChassisTypeToString((uint32_t) ffStrbufToUInt(&result->type, 9999));
if(typeStr)
ffStrbufSetS(&result->type, typeStr);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_serial", "/sys/class/dmi/id/chassis_serial", &result->serial);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_vendor", "/sys/class/dmi/id/chassis_vendor", &result->vendor);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_version", "/sys/class/dmi/id/chassis_version", &result->version);
if(result->type.length)
{
const char* typeStr = ffChassisTypeToString((uint32_t) ffStrbufToUInt(&result->type, 9999));
if(typeStr)
ffStrbufSetStatic(&result->type, typeStr);
}
}
else
{
// Available on Asahi Linux
uint32_t chassisType = 0;
if (ffReadFileData("/sys/firmware/devicetree/base/smbios/smbios/chassis/chassis-type", sizeof(chassisType), &chassisType)) // big endian
{
chassisType = __builtin_bswap32(chassisType);
const char* typeStr = ffChassisTypeToString(chassisType);
if(typeStr)
ffStrbufSetStatic(&result->type, typeStr);
if(ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/chassis/manufacturer", &result->vendor) && result->vendor.length > 0)
ffStrbufTrimRight(&result->vendor, '\0');
}
else if(ffReadFileBuffer("/sys/firmware/devicetree/base/chassis-type", &result->type) && result->type.length > 0)
{
ffStrbufTrimRight(&result->type, '\0');
result->type.chars[0] = (char) toupper(result->type.chars[0]);
}
}
return NULL;
}
+4 -2
View File
@@ -46,7 +46,7 @@ static const char* armPartId2name(uint32_t partId)
case 0xa20: return "ARM1020";
case 0xa22: return "ARM1022";
case 0xa26: return "ARM1026";
case 0xb02: return "ARM11 MPCore";
case 0xb02: return "ARM11-MPCore";
case 0xb36: return "ARM1136";
case 0xb56: return "ARM1156";
case 0xb76: return "ARM1176";
@@ -82,10 +82,12 @@ static const char* armPartId2name(uint32_t partId)
case 0xd0d: return "Cortex-A77";
case 0xd0e: return "Cortex-A76AE";
case 0xd13: return "Cortex-R52";
case 0xd14: return "Cortex-R82AE";
case 0xd15: return "Cortex-R82";
case 0xd16: return "Cortex-R52+";
case 0xd20: return "Cortex-M23";
case 0xd21: return "Cortex-M33";
case 0xd24: return "Cortex-M52";
case 0xd22: return "Cortex-M55";
case 0xd23: return "Cortex-M85";
case 0xd40: return "Neoverse-V1";
@@ -215,7 +217,7 @@ static const char* nvidiaPartId2name(uint32_t partId)
switch (partId)
{
case 0x000: return "Denver";
case 0x003: return "Denver 2";
case 0x003: return "Denver-2";
case 0x004: return "Carmel";
case 0x010: return "Olympus";
default: return NULL;
+1 -1
View File
@@ -741,7 +741,7 @@ FF_MAYBE_UNUSED static void detectSocName(FFCPUResult* cpu)
// [x-vendor,x-model\0]*N
char content[512];
ssize_t length = ffReadFileData("/proc/device-tree/compatible", ARRAY_SIZE(content), content);
ssize_t length = ffReadFileData("/sys/firmware/devicetree/base/compatible", ARRAY_SIZE(content), content);
if (length < 4) return; // v,m\0
if (content[length - 1] != '\0') return; // must end with \0
+2 -1
View File
@@ -9,7 +9,8 @@ static uint64_t startTime;
void ffPrepareCPUUsage(void)
{
assert(cpuTimes1.elementSize == 0);
if (cpuTimes1.elementSize != 0) return; // Already prepared
ffListInit(&cpuTimes1, sizeof(FFCpuUsageInfo));
ffGetCpuUsageInfo(&cpuTimes1);
startTime = ffTimeGetNow();
+17 -19
View File
@@ -22,7 +22,7 @@
#define _PATH_LOCALBASE "/usr/pkg"
#endif
static void getKDE(FFstrbuf* result, FFDEOptions* options)
static void getKDE(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
{
#ifdef _PATH_LOCALBASE
ffParsePropFile(_PATH_LOCALBASE "/share/wayland-sessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result);
@@ -44,7 +44,7 @@ static void getKDE(FFstrbuf* result, FFDEOptions* options)
if(result->length == 0)
ffParsePropFileData("wayland-sessions/plasmawayland5.desktop", "X-KDE-PluginInfo-Version =", result);
if(result->length == 0 && options->slowVersionDetection)
if(result->length == 0)
{
if (ffProcessAppendStdOut(result, (char* const[]){
"plasmashell",
@@ -73,7 +73,7 @@ static void getGnome(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
{
getGnomeByDbus(result);
if (result->length == 0 && options->slowVersionDetection)
if (result->length == 0)
{
if (ffProcessAppendStdOut(result, (char* const[]){
"gnome-shell",
@@ -91,7 +91,7 @@ static void getCinnamon(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
if (result->length == 0)
ffParsePropFileData("applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", result);
if (result->length == 0 && options->slowVersionDetection)
if (result->length == 0)
{
if (ffProcessAppendStdOut(result, (char* const[]){
"cinnamon",
@@ -102,7 +102,7 @@ static void getCinnamon(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
}
}
static void getMate(FFstrbuf* result, FFDEOptions* options)
static void getMate(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
{
FF_STRBUF_AUTO_DESTROY major = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY minor = ffStrbufCreate();
@@ -116,7 +116,7 @@ static void getMate(FFstrbuf* result, FFDEOptions* options)
ffParseSemver(result, &major, &minor, &micro);
if(result->length == 0 && options->slowVersionDetection)
if(result->length == 0)
{
ffProcessAppendStdOut(result, (char* const[]){
"mate-session",
@@ -143,11 +143,11 @@ static const char* getXfce4ByLib(FFstrbuf* result)
#endif
}
static void getXFCE4(FFstrbuf* result, FFDEOptions* options)
static void getXFCE4(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
{
getXfce4ByLib(result);
if(result->length == 0 && options->slowVersionDetection)
if(result->length == 0)
{
//This is somewhat slow
ffProcessAppendStdOut(result, (char* const[]){
@@ -162,7 +162,7 @@ static void getXFCE4(FFstrbuf* result, FFDEOptions* options)
}
}
static void getLXQt(FFstrbuf* result, FFDEOptions* options)
static void getLXQt(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
{
ffParsePropFileData("gconfig/lxqt.pc", "Version:", result);
@@ -171,7 +171,7 @@ static void getLXQt(FFstrbuf* result, FFDEOptions* options)
if(result->length == 0)
ffParsePropFileData("cmake/lxqt/lxqt-config-version.cmake", "set ( PACKAGE_VERSION", result);
if(result->length == 0 && options->slowVersionDetection)
if(result->length == 0)
{
//This is really, really, really slow. Thank you, LXQt developers
ffProcessAppendStdOut(result, (char* const[]){
@@ -206,7 +206,7 @@ static bool extractTdeVersion(const char* line, uint32_t len, void *userdata)
return false;
}
static const char* getTrinity(FFstrbuf* result, FFDEOptions* options)
static const char* getTrinity(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
{
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
const char* error = ffFindExecutableInPath("tde-config", &path);
@@ -218,15 +218,13 @@ static const char* getTrinity(FFstrbuf* result, FFDEOptions* options)
if (ffBinaryExtractStrings(path.chars, extractTdeVersion, result, strlen("R0.0.0")) == NULL)
return NULL;
if (options->slowVersionDetection)
ffStrbufClear(&path);
if (ffProcessAppendStdOut(&path, (char* const[]){
"tde-config",
"--version",
NULL
}) == NULL)
{
ffStrbufClear(&path);
ffProcessAppendStdOut(&path, (char* const[]){
"tde-config",
"--version",
NULL
});
ffParsePropLines(path.chars , "TDE: ", result);
return NULL;
}
+56 -66
View File
@@ -1,77 +1,67 @@
#include "disk.h"
#include "common/io.h"
#include "common/thread.h"
#include "common/windows/unicode.h"
#include "common/windows/nt.h"
#include <windows.h>
#include <winioctl.h>
static unsigned __stdcall testRemoteVolumeAccessible(void* mountpoint)
{
FF_AUTO_CLOSE_FD HANDLE handle = CreateFileW(
(wchar_t*) mountpoint,
READ_CONTROL,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
return 0;
}
#include <ntstatus.h>
#include <stdalign.h>
const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
{
wchar_t buf[MAX_PATH + 1];
uint32_t length = GetLogicalDriveStringsW(ARRAY_SIZE(buf), buf);
if (length == 0 || length >= ARRAY_SIZE(buf))
return "GetLogicalDriveStringsW(ARRAY_SIZE(buf), buf) failed";
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
PROCESS_DEVICEMAP_INFORMATION_EX info = {};
ULONG size = 0;
if(!NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(), ProcessDeviceMap, &info, sizeof(info), &size)))
return "NtQueryInformationProcess(ProcessDeviceMap) failed";
// For cross-platform portability; used by `presets/examples/13.jsonc`
if (__builtin_expect(options->folders.length == 1 && options->folders.chars[0] == '/', 0))
if (options->folders.length == 1 && options->folders.chars[0] == '/')
{
wchar_t path[MAX_PATH + 1];
GetSystemWindowsDirectoryW(path, ARRAY_SIZE(path));
ffStrbufSetF(&options->folders, "%c:\\", (char) path[0]);
options->folders.chars[0] = (char) path[0];
ffStrbufAppendS(&options->folders, ":\\");
}
for(uint32_t i = 0; i < length; i++)
wchar_t mountpointW[] = L"X:\\";
char mountpointA[] = "X:\\";
for (wchar_t i = L'A'; i <= L'Z'; i++)
{
wchar_t* mountpoint = buf + i;
if (!(info.Query.DriveMap & (1 << (i - L'A'))))
continue;
mountpointW[0] = i;
mountpointA[0] = (char) i;
ffStrbufSetWS(&buffer, mountpoint);
i += buffer.length;
UINT driveType = GetDriveTypeW(mountpoint);
UINT driveType = info.Query.DriveType[i - L'A'];
if (__builtin_expect((long) options->folders.length, 0))
{
if (!ffStrbufSeparatedContain(&options->folders, &buffer, FF_DISK_FOLDER_SEPARATOR))
if (!ffStrbufSeparatedContainNS(&options->folders, 3, mountpointA, FF_DISK_FOLDER_SEPARATOR))
continue;
}
else if(driveType == DRIVE_NO_ROOT_DIR)
continue;
if (options->hideFolders.length && ffStrbufSeparatedContain(&options->hideFolders, &buffer, FF_DISK_FOLDER_SEPARATOR))
if (options->hideFolders.length && ffStrbufSeparatedContainNS(&options->hideFolders, 3, mountpointA, FF_DISK_FOLDER_SEPARATOR))
continue;
wchar_t diskName[MAX_PATH + 1], diskFileSystem[MAX_PATH + 1];
FF_AUTO_CLOSE_FD HANDLE handle = CreateFileW(mountpointW, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (handle == INVALID_HANDLE_VALUE)
continue;
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)
);
IO_STATUS_BLOCK iosb;
alignas(FILE_FS_ATTRIBUTE_INFORMATION) uint8_t bufFsAttr[1024];
FILE_FS_ATTRIBUTE_INFORMATION* fsAttr = NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, bufFsAttr, sizeof(bufFsAttr), FileFsAttributeInformation))
? (FILE_FS_ATTRIBUTE_INFORMATION*) bufFsAttr
: NULL;
FF_STRBUF_AUTO_DESTROY diskFileSystemBuf = ffStrbufCreate();
if (volumeInfoAvailable)
if (fsAttr)
{
ffStrbufSetWS(&diskFileSystemBuf, diskFileSystem);
ffStrbufSetNWS(&diskFileSystemBuf, fsAttr->FileSystemNameLength / sizeof(WCHAR), fsAttr->FileSystemName);
if (options->hideFS.length && ffStrbufSeparatedContain(&options->hideFS, &diskFileSystemBuf, ':'))
continue;
}
@@ -87,7 +77,7 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
disk->createTime = 0;
ffStrbufInit(&disk->filesystem);
ffStrbufInit(&disk->name);
ffStrbufInitMove(&disk->mountpoint, &buffer);
ffStrbufInitNS(&disk->mountpoint, 3, mountpointA);
ffStrbufInit(&disk->mountFrom);
disk->type = driveType == DRIVE_REMOVABLE || driveType == DRIVE_REMOTE || driveType == DRIVE_CDROM
? FF_DISK_VOLUME_TYPE_EXTERNAL_BIT
@@ -95,42 +85,42 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
? FF_DISK_VOLUME_TYPE_REGULAR_BIT
: FF_DISK_VOLUME_TYPE_HIDDEN_BIT;
if (mountpoint[2] == L'\\' && mountpoint[3] == L'\0')
{
wchar_t volumeName[MAX_PATH + 1];
mountpoint[2] = L'\0';
if(QueryDosDeviceW(mountpoint, volumeName, ARRAY_SIZE(volumeName)))
mountpointW[2] = L'\0';
if(QueryDosDeviceW(mountpointW, volumeName, ARRAY_SIZE(volumeName)))
ffStrbufSetWS(&disk->mountFrom, volumeName);
mountpoint[2] = L'\\';
mountpointW[2] = L'\\';
}
#ifdef FF_HAVE_THREADS
if (driveType == DRIVE_REMOTE)
alignas(FILE_FS_VOLUME_INFORMATION) uint8_t bufFsVolume[1024];
FILE_FS_VOLUME_INFORMATION* fsVolume = NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, bufFsVolume, sizeof(bufFsVolume), FileFsVolumeInformation))
? (FILE_FS_VOLUME_INFORMATION*) bufFsVolume
: NULL;
if (fsVolume)
{
FFThreadType thread = ffThreadCreate(testRemoteVolumeAccessible, mountpoint);
if (!ffThreadJoin(thread, 500))
continue;
if (fsVolume->VolumeLabelLength > 0)
ffStrbufSetNWS(&disk->name, fsVolume->VolumeLabelLength / sizeof(WCHAR), fsVolume->VolumeLabel);
if (fsVolume->VolumeCreationTime.QuadPart)
disk->createTime = ((uint64_t) fsVolume->VolumeCreationTime.QuadPart - 116444736000000000ull) / 10000ull;
}
#endif
GetDiskFreeSpaceExW(
mountpoint,
(PULARGE_INTEGER)&disk->bytesAvailable,
(PULARGE_INTEGER)&disk->bytesTotal,
(PULARGE_INTEGER)&disk->bytesFree
);
if(volumeInfoAvailable)
if (fsAttr)
{
ffStrbufInitMove(&disk->filesystem, &diskFileSystemBuf);
ffStrbufSetWS(&disk->name, diskName);
if(diskFlags & FILE_READ_ONLY_VOLUME)
if(fsAttr->FileSystemAttributes & FILE_READ_ONLY_VOLUME)
disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT;
}
WIN32_FILE_ATTRIBUTE_DATA data;
if(GetFileAttributesExW(mountpoint, GetFileExInfoStandard, &data) && data.ftCreationTime.dwHighDateTime > 0)
disk->createTime = (*(uint64_t*) &data.ftCreationTime - 116444736000000000ull) / 10000ull;
FILE_FS_FULL_SIZE_INFORMATION fsFullSize;
if (NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, &fsFullSize, sizeof(fsFullSize), FileFsFullSizeInformation)))
{
uint64_t units = fsFullSize.BytesPerSector * fsFullSize.SectorsPerAllocationUnit;
disk->bytesTotal = (uint64_t) fsFullSize.TotalAllocationUnits.QuadPart * units;
disk->bytesFree = (uint64_t) fsFullSize.ActualAvailableAllocationUnits.QuadPart * units;
disk->bytesAvailable = (uint64_t) fsFullSize.CallerAvailableAllocationUnits.QuadPart * units;
}
}
return NULL;
+2
View File
@@ -11,6 +11,8 @@ void ffPrepareDiskIO(FFDiskIOOptions* options)
{
if (options->detectTotal) return;
if (time1 != 0) return; // Already prepared
ffListInit(&ioCounters1, sizeof(FFDiskIOResult));
ffDiskIOGetIoCounters(&ioCounters1, options);
time1 = ffTimeGetNow();
@@ -186,9 +186,12 @@ static bool detectDE(FFDisplayServerResult* ds)
ffStrbufPrependS(&ds->dePrettyName, "OxygenOS");
return true;
}
if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName) && ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS"))
if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName))
{
ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' ');
if (ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS"))
ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' ');
// Google Pixel uses native Android
return true;
}
@@ -39,6 +39,7 @@ static const struct kde_output_device_mode_v2_listener modeListener = {
.refresh = waylandKdeModeRefreshListener,
.preferred = waylandKdeModePreferredListener,
.removed = (void*) stubListener,
.flags = (void*) stubListener,
};
static void waylandKdeModeListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* _, struct kde_output_device_mode_v2 *mode)
@@ -1,4 +1,4 @@
/* Generated by wayland-scanner 1.22.0 */
/* Generated by wayland-scanner 1.24.0 */
#ifndef WLR_OUTPUT_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
#define WLR_OUTPUT_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
@@ -445,9 +445,8 @@ struct zwlr_output_head_v1_listener {
* However, do not assume that the name is a reflection of an
* underlying DRM connector, X11 connection, etc.
*
* If the compositor implements the xdg-output protocol and this
* head is enabled, the xdg_output.name event must report the same
* name.
* If this head matches a wl_output, the wl_output.name event must
* report the same name.
*
* The name event is sent after a wlr_output_head object is
* created. This event is only sent once per object, and the name
@@ -468,9 +467,8 @@ struct zwlr_output_head_v1_listener {
* underlying DRM connector or the display name of the underlying
* X11 connection, etc.
*
* If the compositor implements xdg-output and this head is
* enabled, the xdg_output.description must report the same
* description.
* If this head matches a wl_output, the wl_output.description
* event must report the same name.
*
* The description event is sent after a wlr_output_head object is
* created. This event is only sent once per object, and the
@@ -486,6 +484,11 @@ struct zwlr_output_head_v1_listener {
* This event describes the physical size of the head. This event
* is only sent if the head has a physical size (e.g. is not a
* projector or a virtual device).
*
* The physical size event is sent after a wlr_output_head object
* is created. This event is only sent once per object, and the
* physical size does not change over the lifetime of the
* wlr_output_head object.
* @param width width in millimeters of the output
* @param height height in millimeters of the output
*/
@@ -568,9 +571,6 @@ struct zwlr_output_head_v1_listener {
*
* This event describes the manufacturer of the head.
*
* This must report the same make as the wl_output interface does
* in its geometry event.
*
* Together with the model and serial_number events the purpose is
* to allow clients to recognize heads from previous sessions and
* for example load head-specific configurations back.
@@ -583,6 +583,10 @@ struct zwlr_output_head_v1_listener {
* other events but should be aware that there is an increased risk
* of false positives.
*
* If sent, the make event is sent after a wlr_output_head object
* is created and only sent once per object. The make does not
* change over the lifetime of the wlr_output_head object.
*
* It is not recommended to display the make string in UI to users.
* For that the string provided by the description event should be
* preferred.
@@ -596,9 +600,6 @@ struct zwlr_output_head_v1_listener {
*
* This event describes the model of the head.
*
* This must report the same model as the wl_output interface does
* in its geometry event.
*
* Together with the make and serial_number events the purpose is
* to allow clients to recognize heads from previous sessions and
* for example load head-specific configurations back.
@@ -611,6 +612,10 @@ struct zwlr_output_head_v1_listener {
* information from other events but should be aware that there is
* an increased risk of false positives.
*
* If sent, the model event is sent after a wlr_output_head object
* is created and only sent once per object. The model does not
* change over the lifetime of the wlr_output_head object.
*
* It is not recommended to display the model string in UI to
* users. For that the string provided by the description event
* should be preferred.
@@ -636,6 +641,11 @@ struct zwlr_output_head_v1_listener {
* events but should be aware that there is an increased risk of
* false positives.
*
* If sent, the serial number event is sent after a wlr_output_head
* object is created and only sent once per object. The serial
* number does not change over the lifetime of the wlr_output_head
* object.
*
* It is not recommended to display the serial_number string in UI
* to users. For that the string provided by the description event
* should be preferred.
@@ -1,6 +1,6 @@
#ifdef FF_HAVE_WAYLAND
/* Generated by wayland-scanner 1.22.0 */
/* Generated by wayland-scanner 1.24.0 */
/*
* Copyright © 2019 Purism SPC
@@ -27,6 +27,7 @@
* THIS SOFTWARE.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <wayland-util.h>
+1 -1
View File
@@ -266,7 +266,7 @@ static void applyPrettyNameIfDE(FFDisplayServerResult* result, const char* name)
static const char* getFromProcesses(FFDisplayServerResult* result)
{
uint32_t userId = getuid();
uint32_t userId = instance.state.platform.uid;
#if __FreeBSD__
#ifdef __DragonFly__
+50 -2
View File
@@ -1,4 +1,5 @@
#include "gpu.h"
#include "common/debug.h"
#include "detection/vulkan/vulkan.h"
#include "detection/opencl/opencl.h"
#include "detection/opengl/opengl.h"
@@ -50,6 +51,8 @@ const char* ffGPUGetVendorString(unsigned vendorId)
const char* detectByOpenGL(FFlist* gpus)
{
FF_DEBUG("Starting OpenGL GPU detection fallback");
FFOpenGLResult result;
ffStrbufInit(&result.version);
ffStrbufInit(&result.renderer);
@@ -60,6 +63,7 @@ const char* detectByOpenGL(FFlist* gpus)
__attribute__((__cleanup__(ffDestroyOpenGLOptions))) FFOpenGLOptions options;
ffInitOpenGLOptions(&options);
const char* error = ffDetectOpenGL(&options, &result);
FF_DEBUG("OpenGL detection returns: %s", error ?: "success");
if (!error)
{
@@ -78,6 +82,11 @@ const char* detectByOpenGL(FFlist* gpus)
gpu->dedicated = gpu->shared = (FFGPUMemory){0, 0};
gpu->deviceId = 0;
FF_DEBUG("OpenGL reported renderer='%s', vendor='%s', version='%s'",
gpu->name.chars,
gpu->vendor.chars,
result.version.chars);
if (ffStrbufContainS(&gpu->name, "Apple"))
{
ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE);
@@ -92,6 +101,11 @@ const char* detectByOpenGL(FFlist* gpus)
else if (ffStrbufContainS(&gpu->name, "MTT"))
ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_MTHREADS);
FF_DEBUG("OpenGL fallback produced GPU: name='%s', vendor='%s', type=%u",
gpu->name.chars,
gpu->vendor.chars,
gpu->type);
}
ffStrbufDestroy(&result.version);
@@ -104,43 +118,77 @@ const char* detectByOpenGL(FFlist* gpus)
const char* ffDetectGPU(const FFGPUOptions* options, FFlist* result)
{
FF_DEBUG("Starting GPU detection with method=%d", (int) options->detectionMethod);
if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_PCI)
{
FF_DEBUG("Trying PCI/native GPU detection");
const char* error = ffDetectGPUImpl(options, result);
if (!error && result->length > 0) return NULL;
if (!error && result->length > 0)
{
FF_DEBUG("PCI/native GPU detection succeeded with %u GPU(s)", result->length);
return NULL;
}
FF_DEBUG("PCI/native GPU detection did not produce results (error=%s, gpuCount=%u)",
error ?: "none",
result->length);
}
if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_VULKAN)
{
FF_DEBUG("Trying Vulkan GPU detection fallback");
FFVulkanResult* vulkan = ffDetectVulkan();
if (!vulkan->error && vulkan->gpus.length > 0)
{
FF_DEBUG("Vulkan detection succeeded with %u GPU(s)", vulkan->gpus.length);
ffListDestroy(result);
ffListInitMove(result, &vulkan->gpus);
#ifdef __ANDROID__
double ffGPUDetectTempFromTZ(void);
if (options->temp && result->length == 1)
{
FF_DEBUG("Applying Android thermal-zone temperature to single Vulkan GPU");
FF_LIST_GET(FFGPUResult, *result, 0)->temperature = ffGPUDetectTempFromTZ();
}
#endif
return NULL;
}
FF_DEBUG("Vulkan detection did not produce results (error=%s, gpuCount=%u)",
vulkan->error ?: "none",
vulkan->gpus.length);
}
if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_OPENCL)
{
FF_DEBUG("Trying OpenCL GPU detection fallback");
FFOpenCLResult* opencl = ffDetectOpenCL();
if (!opencl->error && opencl->gpus.length > 0)
{
FF_DEBUG("OpenCL detection succeeded with %u GPU(s)", opencl->gpus.length);
ffListDestroy(result);
ffListInitMove(result, &opencl->gpus);
return NULL;
}
FF_DEBUG("OpenCL detection did not produce results (error=%s, gpuCount=%u)",
opencl->error ?: "none",
opencl->gpus.length);
}
if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_OPENGL)
{
if (detectByOpenGL(result) == NULL)
FF_DEBUG("Trying OpenGL GPU detection fallback");
const char* error = detectByOpenGL(result);
if (error == NULL)
{
FF_DEBUG("OpenGL fallback succeeded with %u GPU(s)", result->length);
return NULL;
}
FF_DEBUG("OpenGL fallback failed: %s", error);
}
FF_DEBUG("GPU detection failed in all enabled backends");
return "GPU detection failed";
}
-70
View File
@@ -1,70 +0,0 @@
#include "gpu.h"
#ifdef FF_HAVE_PCIACCESS
#include "common/io.h"
#include "common/library.h"
#include <pciaccess.h>
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
{
FF_LIBRARY_LOAD_MESSAGE(pciaccess, "libpciaccess" FF_LIBRARY_EXTENSION, 0)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_system_init)
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
// Same behavior can be observed with `cp $(which scanpci) /tmp/ && /tmp/scanpci`
FF_SUPPRESS_IO();
if (ffpci_system_init() < 0)
return "pci_system_init() failed";
}
struct pci_device_iterator* iter = ffpci_slot_match_iterator_create(NULL);
for (struct pci_device* dev = NULL; (dev = ffpci_device_next(iter)); )
{
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);
ffStrbufInit(&gpu->driver);
ffStrbufInitStatic(&gpu->platformApi, "libpciaccess");
ffStrbufInit(&gpu->memoryType);
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(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(subclass, dev->vendor_id, dev->device_id, gpu);
}
ffpci_iterator_destroy(iter);
ffpci_system_cleanup();
return NULL;
}
#else
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FF_MAYBE_UNUSED FFlist* gpus)
{
return "Fastfetch was built without libpciaccess support";
}
#endif
+123
View File
@@ -0,0 +1,123 @@
#include "gpu.h"
#include "common/io.h"
#include <hurd.h>
#include <hurd/pci.h>
#include <hurd/paths.h>
enum {
PCI_VENDOR_ID = 0x00,
PCI_DEVICE_ID = 0x02,
PCI_REVISION_ID = 0x08,
PCI_CLASS_PROG = 0x09,
PCI_SUBCLASS = 0x0a,
PCI_CLASS_DEVICE = 0x0b,
PCI_CONF_SIZE = 0x40,
};
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
{
int dDomainFd = open(_SERVERS_BUS "/pci/0000", O_RDONLY | O_CLOEXEC);
if (dDomainFd < 0) return "open(_SERVERS_BUS \"/pci/0000\") failed";
FF_AUTO_CLOSE_DIR DIR* dirDomain = fdopendir(dDomainFd);
if (dirDomain == NULL) return "fdopendir(domain) failed";
struct dirent* busEntry;
while ((busEntry = readdir(dirDomain)) != NULL)
{
if (busEntry->d_type != DT_DIR || busEntry->d_name[0] == '.')
continue;
char* endptr;
uint16_t pciBus = (uint16_t) strtoul(busEntry->d_name, &endptr, 16);
if (*endptr != '\0') continue;
int dBusFd = openat(dDomainFd, busEntry->d_name, O_RDONLY | O_CLOEXEC);
if (dBusFd < 0) continue;
FF_AUTO_CLOSE_DIR DIR* dirBus = fdopendir(dBusFd);
if (dirBus == NULL) continue;
struct dirent* devEntry;
while ((devEntry = readdir(dirBus)) != NULL)
{
if (devEntry->d_type != DT_DIR || devEntry->d_name[0] == '.')
continue;
uint8_t pciDev = (uint8_t) strtoul(devEntry->d_name, &endptr, 16);
if (*endptr != '\0') continue;
int dDevFd = openat(dBusFd, devEntry->d_name, O_RDONLY | O_CLOEXEC);
if (dDevFd < 0) continue;
FF_AUTO_CLOSE_DIR DIR* dirDev = fdopendir(dDevFd);
if (dirDev == NULL) continue;
struct dirent* funcEntry;
while ((funcEntry = readdir(dirDev)) != NULL)
{
if (funcEntry->d_type != DT_DIR || funcEntry->d_name[0] == '.')
continue;
uint8_t pciFunc = (uint8_t) strtoul(funcEntry->d_name, &endptr, 16);
if (*endptr != '\0') continue;
char subpath[PATH_MAX];
snprintf(subpath, ARRAY_SIZE(subpath), "%s/%s/%s/%s/config", _SERVERS_BUS "/pci/0000", busEntry->d_name, devEntry->d_name, funcEntry->d_name);
mach_port_t devicePort = file_name_lookup(subpath, 0, 0);
if (devicePort == MACH_PORT_NULL)
continue;
mach_msg_type_number_t nread = 0;
uint8_t data[PCI_CONF_SIZE];
data_t pData = (data_t) data;
kern_return_t kr = pci_conf_read(devicePort, 0, &pData, &nread, PCI_CONF_SIZE);
mach_port_deallocate(mach_task_self(), devicePort);
if (kr != KERN_SUCCESS || nread < PCI_CONF_SIZE) continue;
if (pData != (data_t) data)
{
memcpy(data, pData, PCI_CONF_SIZE);
vm_deallocate(mach_task_self(), (vm_address_t)pData, nread);
}
uint8_t classBase = data[PCI_CLASS_DEVICE];
if (classBase != 0x03 /*PCI_BASE_CLASS_DISPLAY*/)
continue;
uint8_t classSub = data[PCI_SUBCLASS];
if (pciFunc > 0 && classSub == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/) // Likely an auxiliary display controller (#2034)
continue;
uint8_t revision = data[PCI_REVISION_ID];
uint16_t vendorId = data[PCI_VENDOR_ID] | (data[PCI_VENDOR_ID + 1] << 8);
uint16_t deviceId = data[PCI_DEVICE_ID] | (data[PCI_DEVICE_ID + 1] << 8);
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
ffStrbufInit(&gpu->name);
ffStrbufInit(&gpu->driver);
ffStrbufInitStatic(&gpu->platformApi, "/servers/bus/pci");
ffStrbufInit(&gpu->memoryType);
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, pciBus, pciDev, pciFunc);
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
ffGPUQueryAmdGpuName(deviceId, revision, gpu);
if (gpu->name.length == 0)
ffGPUFillVendorAndName(classSub, vendorId, deviceId, gpu);
}
}
}
return NULL;
}
+1 -1
View File
@@ -560,7 +560,7 @@ static const char* detectOf(FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir, co
if (!gpu->name.length)
{
ffStrbufSetS(&gpu->name, name ? name : compatible);
ffStrbufSetS(&gpu->name, name ?: compatible);
ffStrbufTrimRightSpace(&gpu->name);
}
if (!gpu->vendor.length && name)
+1
View File
@@ -1,6 +1,7 @@
#include "gpu.h"
#include "common/io.h"
#include "common/properties.h"
#include "common/memrchr.h"
#include <stdlib.h>
#ifdef __FreeBSD__
+7 -4
View File
@@ -54,6 +54,12 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
}
FF_DEBUG("Device instance ID: %lu", devInst);
for (wchar_t* p = devId; *p; p++)
{
if (*p >= L'a' && *p <= L'z')
*p -= L'a' - L'A';
}
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
deviceCount++;
FF_DEBUG("Added GPU #%d to list", deviceCount);
@@ -73,10 +79,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
unsigned vendorId = 0, deviceId = 0, subSystemId = 0, revId = 0;
if (
(*devId == 'P' && swscanf(devId, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId) == 4) ||
(*devId == 'p' && swscanf(devId, L"pci\\ven_%x&dev_%x&subsys_%x&rev_%x", &vendorId, &deviceId, &subSystemId, &revId) == 4) // Windows 7
)
if (swscanf(devId, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId) == 4)
{
FF_DEBUG("Parsed PCI IDs - Vendor: 0x%x, Device: 0x%x, SubSystem: 0x%x, Rev: 0x%x", vendorId, deviceId, subSystemId, revId);
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
+12 -17
View File
@@ -9,18 +9,14 @@ static void getHostProductName(FFstrbuf* name)
{
if (ffReadFileBuffer("/sys/firmware/devicetree/base/model", name))
{
ffStrbufTrimRightSpace(name);
ffStrbufTrimRight(name, '\0');
if(ffIsSmbiosValueSet(name))
return;
return;
}
if (ffReadFileBuffer("/sys/firmware/devicetree/base/banner-name", name))
{
ffStrbufTrimRightSpace(name);
ffStrbufTrimRight(name, '\0');
if(ffIsSmbiosValueSet(name))
return;
return;
}
if (ffReadFileBuffer("/tmp/sysinfo/model", name))
@@ -36,20 +32,19 @@ static void getHostProductName(FFstrbuf* name)
static void getHostSerialNumber(FFstrbuf* serial)
{
if (ffReadFileBuffer("/sys/firmware/devicetree/base/serial-number", serial))
{
ffStrbufTrimRightSpace(serial);
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/serial", serial))
ffStrbufTrimRight(serial, '\0');
else if (ffReadFileBuffer("/sys/firmware/devicetree/base/serial-number", serial))
ffStrbufTrimRight(serial, '\0');
if(ffIsSmbiosValueSet(serial))
return;
}
ffStrbufClear(serial);
}
const char* ffDetectHost(FFHostResult* host)
{
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->family);
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->family))
{
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/product", &host->family))
ffStrbufTrimRight(&host->family, '\0');
}
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", &host->name))
getHostProductName(&host->name);
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_version", "/sys/class/dmi/id/product_version", &host->version);
@@ -59,8 +54,8 @@ const char* ffDetectHost(FFHostResult* host)
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_uuid", "/sys/class/dmi/id/product_uuid", &host->uuid);
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/sys_vendor", "/sys/class/dmi/id/sys_vendor", &host->vendor))
{
if (ffStrbufStartsWithS(&host->name, "Apple "))
ffStrbufSetStatic(&host->vendor, "Apple Inc.");
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/manufacturer", &host->vendor))
ffStrbufTrimRight(&host->vendor, '\0');
}
#ifdef __x86_64__
+1 -1
View File
@@ -142,7 +142,7 @@ const char* ffDetectLM(FFLMResult* result)
{
// On some incorrectly configured systems, $XDG_SESSION_ID is not set. Try finding it ourself
// WARNING: This is private data. Do not parse
ffStrbufSetF(&path, FF_SYSTEMD_USERS_PATH "%d", getuid());
ffStrbufSetF(&path, FF_SYSTEMD_USERS_PATH "%d", instance.state.platform.uid);
// This is actually buggy, and assumes current user is using DE
// `sd_pid_get_session` can be a better option, but we need to find a pid to use
+1
View File
@@ -1,6 +1,7 @@
#include "fastfetch.h"
#include "common/io.h"
#include "common/stringUtils.h"
#include "common/memrchr.h"
#include "detection/media/media.h"
#include <string.h>
+11 -10
View File
@@ -1,4 +1,5 @@
#include "memory.h"
#include "common/debug.h"
#include <string.h>
#include <mach/mach.h>
@@ -8,25 +9,25 @@
const char* ffDetectMemory(FFMemoryResult* ram)
{
size_t length = sizeof(ram->bytesTotal);
#if FF_APPLE_MEMSIZE_USABLE
if (sysctlbyname("hw.memsize_usable", &ram->bytesTotal, &length, NULL, 0) != 0)
return "Failed to read hw.memsize_usable";
#else
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;
#endif
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";
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;
// https://github.com/st3fan/osx-10.9/blob/34e34a6a539b5a822cda4074e56a7ced9b57da71/system_cmds-597.1.1/vm_stat.tproj/vm_stat.c#L139
ram->bytesUsed = appMemory + wiredMemory + compressed + reserved;
uint64_t pagesFree = vmstat.free_count - vmstat.speculative_count;
uint64_t pagesFileBacked = vmstat.external_page_count; // Cached files
ram->bytesUsed = ram->bytesTotal - (pagesFree + pagesFileBacked) * instance.state.platform.sysinfo.pageSize;
return NULL;
}
+3
View File
@@ -7,6 +7,9 @@ const char* ffDetectMemory(FFMemoryResult* ram)
MEMORYSTATUSEX statex = {
.dwLength = sizeof(statex),
};
// GlobalMemoryStatusEx() internally uses
// NtQuerySystemInformation(SystemBasicPerformanceInformation) in Win 7, and
// NtQuerySystemInformation(SystemMemoryUsageInformation) in Win 10
if (!GlobalMemoryStatusEx(&statex))
return "GlobalMemoryStatusEx() failed";
+2
View File
@@ -9,6 +9,8 @@ void ffPrepareNetIO(FFNetIOOptions* options)
{
if (options->detectTotal) return;
if (time1 != 0) return; // Already prepared
ffListInit(&ioCounters1, sizeof(FFNetIOResult));
ffNetIOGetIoCounters(&ioCounters1, options);
time1 = ffTimeGetNow();
+72 -1
View File
@@ -1,4 +1,5 @@
#include "opengl.h"
#include "common/debug.h"
#include "common/library.h"
#if __has_include(<GL/gl.h>)
@@ -56,26 +57,48 @@ typedef struct EGLData
static const char* eglHandleContext(FFOpenGLResult* result, EGLData* data)
{
FF_DEBUG("Making EGL context current");
if(data->ffeglMakeCurrent(data->display, data->surface, data->surface, data->context) != EGL_TRUE)
{
FF_DEBUG("eglMakeCurrent() returned EGL_FALSE");
return "eglMakeCurrent returned EGL_FALSE";
}
ffOpenGLHandleResult(result, data->ffglGetString);
ffStrbufSetF(&result->library, "EGL %s", data->ffeglQueryString(data->display, EGL_VERSION));
FF_DEBUG("OpenGL via EGL detected: version='%s', renderer='%s', vendor='%s', slv='%s', library='%s'",
result->version.chars,
result->renderer.chars,
result->vendor.chars,
result->slv.chars,
result->library.chars);
return NULL;
}
static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data, bool gles)
{
FF_DEBUG("Creating EGL context (preferred API=%s, client version=%d)", gles ? "OpenGL ES" : "OpenGL", gles ? 2 : 1);
data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){
EGL_CONTEXT_CLIENT_VERSION, gles ? 2 : 1, // Try GLES 2.0+ first
EGL_NONE
});
if(data->context == EGL_NO_CONTEXT && gles) // Some ANGLE builds support GLES 1.1 only
{
FF_DEBUG("EGL context creation with GLES 2.x failed, retrying with default attributes (GLES 1.1 fallback)");
data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){EGL_NONE});
}
if(data->context == EGL_NO_CONTEXT)
{
FF_DEBUG("eglCreateContext() returned EGL_NO_CONTEXT");
return "eglCreateContext returned EGL_NO_CONTEXT";
}
FF_DEBUG("EGL context created successfully");
const char* error = eglHandleContext(result, data);
FF_DEBUG("eglHandleContext() returns: %s", error ?: "success");
FF_DEBUG("Destroying EGL context");
data->ffeglDestroyContext(data->display, data->context);
return error;
}
@@ -84,13 +107,21 @@ static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data)
{
// try use OpenGL API. If failed, use the default API (usually OpenGL ES)
bool gles = !data->ffeglBindAPI(EGL_OPENGL_API);
FF_DEBUG("eglBindAPI(EGL_OPENGL_API) %s, effective API=%s",
gles ? "failed" : "succeeded",
gles ? "default (usually OpenGL ES)" : "OpenGL");
EGLint eglConfigCount;
data->ffeglGetConfigs(data->display, &data->config, 1, &eglConfigCount);
FF_DEBUG("eglGetConfigs() returned %d config(s)", eglConfigCount);
if(eglConfigCount == 0)
{
FF_DEBUG("No EGL config is available");
return "eglGetConfigs returned 0 configs";
}
FF_DEBUG("Creating EGL pbuffer surface (%dx%d)", FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT);
data->surface = data->ffeglCreatePbufferSurface(data->display, data->config, (EGLint[]){
EGL_WIDTH, FF_OPENGL_BUFFER_WIDTH,
EGL_HEIGHT, FF_OPENGL_BUFFER_HEIGHT,
@@ -98,39 +129,71 @@ static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data)
});
if(data->surface == EGL_NO_SURFACE)
{
FF_DEBUG("eglCreatePbufferSurface() returned EGL_NO_SURFACE");
return "eglCreatePbufferSurface returned EGL_NO_SURFACE";
}
FF_DEBUG("EGL pbuffer surface created successfully");
const char* error = eglHandleSurface(result, data, gles);
FF_DEBUG("eglHandleSurface() returns: %s", error ?: "success");
FF_DEBUG("Destroying EGL surface");
data->ffeglDestroySurface(data->display, data->surface);
return error;
}
static const char* eglHandleData(FFOpenGLResult* result, EGLData* data)
{
FF_DEBUG("Resolving glGetString via eglGetProcAddress()");
data->ffglGetString = (__typeof__(&glGetString)) data->ffeglGetProcAddress("glGetString");
if(!data->ffglGetString)
{
FF_DEBUG("eglGetProcAddress('glGetString') returned NULL");
return "eglGetProcAddress(glGetString) returned NULL";
}
#if EGL_VERSION_1_5
PFNEGLGETPLATFORMDISPLAYEXTPROC ffeglGetPlatformDisplay = (PFNEGLGETPLATFORMDISPLAYEXTPROC) data->ffeglGetProcAddress("eglGetPlatformDisplay");
if (ffeglGetPlatformDisplay)
{
FF_DEBUG("Trying eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA)");
data->display = ffeglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, NULL, NULL);
FF_DEBUG("eglGetPlatformDisplay() %s", data->display == EGL_NO_DISPLAY ? "failed" : "succeeded");
}
else
FF_DEBUG("eglGetPlatformDisplay is unavailable, falling back to eglGetDisplay");
if(!ffeglGetPlatformDisplay || data->display == EGL_NO_DISPLAY)
#endif
{
FF_DEBUG("Trying eglGetDisplay(EGL_DEFAULT_DISPLAY)");
data->display = data->ffeglGetDisplay(EGL_DEFAULT_DISPLAY);
if(data->display == EGL_NO_DISPLAY)
{
FF_DEBUG("eglGetDisplay() returned EGL_NO_DISPLAY");
return "eglGetDisplay returned EGL_NO_DISPLAY";
}
FF_DEBUG("eglGetDisplay() succeeded");
}
EGLint major, minor;
if(data->ffeglInitialize(data->display, &major, &minor) == EGL_FALSE)
{
FF_DEBUG("eglInitialize() returned EGL_FALSE");
return "eglInitialize returned EGL_FALSE";
}
FF_DEBUG("EGL initialized successfully: %d.%d", major, minor);
const char* error = eglHandleDisplay(result, data);
FF_DEBUG("eglHandleDisplay() returns: %s", error ?: "success");
FF_DEBUG("Terminating EGL display connection");
data->ffeglTerminate(data->display);
return error;
}
@@ -138,6 +201,7 @@ static const char* eglHandleData(FFOpenGLResult* result, EGLData* data)
const char* ffOpenGLDetectByEGL(FFOpenGLResult* result)
{
FF_DEBUG("Starting OpenGL detection via EGL");
EGLData eglData;
FF_LIBRARY_LOAD_MESSAGE(egl, "libEGL" FF_LIBRARY_EXTENSION, 1);
@@ -154,8 +218,15 @@ const char* ffOpenGLDetectByEGL(FFOpenGLResult* result)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroySurface);
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglTerminate);
FF_DEBUG("Loaded EGL library and required symbols");
FF_SUPPRESS_IO();
return eglHandleData(result, &eglData);
FF_DEBUG("Suppressed stdout/stderr during EGL probing");
const char* error = eglHandleData(result, &eglData);
FF_DEBUG("OpenGL detection via EGL returns: %s", error ?: "success");
return error;
}
#endif //FF_HAVE_EGL
+1 -15
View File
@@ -313,21 +313,7 @@ static bool detectBedrock(FFOSResult* os)
{
const char* bedrockRestrict = getenv("BEDROCK_RESTRICT");
if(bedrockRestrict && bedrockRestrict[0] == '1') return false;
if(parseOsRelease(FASTFETCH_TARGET_DIR_ROOT "/bedrock" FASTFETCH_TARGET_DIR_ETC "/bedrock-release", os))
{
if(os->id.length == 0)
ffStrbufAppendS(&os->id, "bedrock");
if(os->name.length == 0)
ffStrbufAppendS(&os->name, "Bedrock");
if(os->prettyName.length == 0)
ffStrbufAppendS(&os->prettyName, "Bedrock Linux");
parseOsRelease("/bedrock" FASTFETCH_TARGET_DIR_ETC "/os-release", os);
return true;
}
return false;
return parseOsRelease(FASTFETCH_TARGET_DIR_ROOT "/bedrock/strata/bedrock/etc/os-release", os);
}
static void detectOS(FFOSResult* os)
+40 -1
View File
@@ -412,6 +412,45 @@ static uint32_t getFlatpakPackages(FFstrbuf* baseDir, const char* dirname)
return num_elements;
}
static uint32_t getPacmanPackages(FFstrbuf* baseDir)
{
FF_STRBUF_AUTO_DESTROY dbPath = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY rootDir = ffStrbufCreate();
// Get path to pacman.conf
uint32_t baseDirLen = baseDir->length;
ffStrbufAppendS(baseDir, "/etc/pacman.conf");
bool confFound = ffParsePropFileValues(baseDir->chars, 2, (FFpropquery[]){
{ "DBPath =", &dbPath },
{ "RootDir =", &rootDir },
});
ffStrbufSubstrBefore(baseDir, baseDirLen);
if (confFound)
{
if (dbPath.length > 0)
{
// If DBPath is specified, use it
ffStrbufEnsureEndsWithC(&dbPath, '/');
ffStrbufAppendS(&dbPath, "local");
}
else if (rootDir.length > 0)
{
// ... otherwise, use RootDir
ffStrbufDestroy(&dbPath);
ffStrbufInitMove(&dbPath, &rootDir);
ffStrbufEnsureEndsWithC(&dbPath, '/');
ffStrbufAppendS(&dbPath, "var/lib/pacman/local");
}
}
if (dbPath.length == 0)
ffStrbufSetStatic(&dbPath, "/var/lib/pacman/local");
return getNumElements(baseDir, dbPath.chars, true);
}
static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, FFPackagesOptions* options)
{
if (!(options->disabled & FF_PACKAGES_FLAG_APK_BIT)) packageCounts->apk += getNumStrings(baseDir, "/lib/apk/db/installed", "C:Q", "apk");
@@ -426,7 +465,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts,
packageCounts->nixDefault += ffPackagesGetNix(baseDir, "/nix/var/nix/profiles/default");
packageCounts->nixSystem += ffPackagesGetNix(baseDir, "/run/current-system");
}
if (!(options->disabled & FF_PACKAGES_FLAG_PACMAN_BIT)) packageCounts->pacman += getNumElements(baseDir, "/var/lib/pacman/local", true);
if (!(options->disabled & FF_PACKAGES_FLAG_PACMAN_BIT)) packageCounts->pacman += getPacmanPackages(baseDir);
if (!(options->disabled & FF_PACKAGES_FLAG_LPKGBUILD_BIT)) packageCounts->lpkgbuild += getNumElements(baseDir, "/opt/Loc-OS-LPKG/lpkgbuild/remove", false);
if (!(options->disabled & FF_PACKAGES_FLAG_PKGTOOL_BIT)) packageCounts->pkgtool += getNumElements(baseDir, "/var/log/packages", false);
if (!(options->disabled & FF_PACKAGES_FLAG_RPM_BIT))
+10 -3
View File
@@ -6,9 +6,11 @@
#include "common/mallocHelper.h"
#include "common/io.h"
#include <stdalign.h>
#include <windows.h>
#include "common/windows/nt.h"
#include <ntstatus.h>
#include <shlobj.h>
static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t* ignore)
{
@@ -17,7 +19,7 @@ static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t
bool flag = ignore == NULL;
uint32_t counter = 0;
uint8_t buffer[64 * 1024] __attribute__((aligned(8))); // Required for WoA
alignas(8) uint8_t buffer[64 * 1024];
BOOLEAN firstScan = TRUE;
size_t ignoreLen = ignore ? wcslen(ignore) : 0;
@@ -76,7 +78,7 @@ static inline void wrapYyjsonFree(yyjson_doc** doc)
static void detectScoop(FFPackagesResult* result)
{
FF_STRBUF_AUTO_DESTROY scoopPath = ffStrbufCreateA(MAX_PATH + 3);
ffStrbufAppendS(&scoopPath, instance.state.platform.homeDir.chars);
ffStrbufAppend(&scoopPath, &instance.state.platform.homeDir);
ffStrbufAppendS(&scoopPath, ".config/scoop/config.json");
yyjson_val* root = NULL;
@@ -107,7 +109,12 @@ static void detectScoop(FFPackagesResult* result)
ffStrbufSetJsonVal(&scoopPath, yyjson_obj_get(root, "global_path"));
if (scoopPath.length == 0)
{
ffStrbufSetS(&scoopPath, getenv("ProgramData"));
PWSTR pPath = NULL;
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramData, KF_FLAG_DEFAULT, NULL, &pPath)))
{
ffStrbufSetWS(&scoopPath, pPath);
CoTaskMemFree(pPath);
}
ffStrbufAppendS(&scoopPath, "/scoop");
}
ffStrbufAppendS(&scoopPath, "/apps/");
@@ -2,6 +2,7 @@
#include "common/io.h"
#include "common/windows/unicode.h"
#include <stdalign.h>
#include <windows.h>
#include <winioctl.h>
@@ -135,7 +136,7 @@ static bool detectPhysicalDisk(const wchar_t* szDevice, FFlist* result, FFPhysic
}
{
uint8_t buffer[sizeof(GET_MEDIA_TYPES) + sizeof(DEVICE_MEDIA_INFO) * 7] = "";
alignas(GET_MEDIA_TYPES) uint8_t buffer[sizeof(GET_MEDIA_TYPES) + sizeof(DEVICE_MEDIA_INFO) * 7] = {};
GET_MEDIA_TYPES* gmt = (GET_MEDIA_TYPES*) buffer;
if(DeviceIoControl(
hDevice,
+2 -1
View File
@@ -2,13 +2,14 @@
#include <sys/stat.h>
#include <sys/swap.h>
#include <limits.h>
#include <stdalign.h>
enum { FFMaxNSwap = 8 };
const char* ffDetectSwap(FFlist* result)
{
char strings[FFMaxNSwap][PATH_MAX];
uint8_t buffer[sizeof(swaptbl_t) + sizeof(swapent_t) * (FFMaxNSwap - 1)] = {};
alignas(swaptbl_t) uint8_t buffer[sizeof(swaptbl_t) + sizeof(swapent_t) * (FFMaxNSwap - 1)] = {};
swaptbl_t* table = (swaptbl_t*) buffer;
table->swt_n = FFMaxNSwap;
for (int i = 0; i < FFMaxNSwap; ++i)
+3 -25
View File
@@ -5,11 +5,11 @@
#include <winternl.h>
#include <ntstatus.h>
#include <windows.h>
#include <psapi.h>
#include <stdalign.h>
const char* detectByNqsi(FFlist* result)
const char* ffDetectSwap(FFlist* result)
{
uint8_t buffer[4096];
alignas(SYSTEM_PAGEFILE_INFORMATION) uint8_t buffer[4096];
ULONG size = sizeof(buffer);
SYSTEM_PAGEFILE_INFORMATION* pstart = (SYSTEM_PAGEFILE_INFORMATION*) buffer;
if(!NT_SUCCESS(NtQuerySystemInformation(SystemPagefileInformation, pstart, size, &size)))
@@ -29,25 +29,3 @@ const char* detectByNqsi(FFlist* result)
}
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);
}
@@ -6,11 +6,16 @@
#include "common/windows/registry.h"
#include "common/windows/unicode.h"
#include "common/windows/version.h"
#include "common/windows/nt.h"
#include "common/stringUtils.h"
#include <stdalign.h>
#include <windows.h>
#include <wchar.h>
#include <tlhelp32.h>
#include <ntstatus.h>
#include <winternl.h>
#include <shlobj.h>
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
@@ -186,17 +191,29 @@ static bool detectDefaultTerminal(FFTerminalResult* result)
{
ffStrbufSetS(&result->processName, "WindowsTerminal.exe");
ffStrbufSetS(&result->prettyName, "WindowsTerminal");
ffStrbufSetF(&result->exe, "%s\\WindowsApps\\%s\\WindowsTerminal.exe", getenv("ProgramFiles"), path.chars);
if(ffPathExists(result->exe.chars, FF_PATHTYPE_FILE))
PWSTR programFiles = NULL;
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &programFiles)))
{
result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1;
ffStrbufSet(&result->exePath, &result->exe);
}
else
{
ffStrbufDestroy(&result->exe);
ffStrbufInitMove(&result->exe, &path);
result->exeName = "";
ffStrbufSetWS(&result->exe, programFiles);
CoTaskMemFree(programFiles);
programFiles = NULL;
ffStrbufAppendS(&result->exe, "\\WindowsApps\\");
ffStrbufAppend(&result->exe, &path);
ffStrbufAppendS(&result->exe, "\\WindowsTerminal.exe");
if(ffPathExists(result->exe.chars, FF_PATHTYPE_FILE))
{
result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1;
ffStrbufSet(&result->exePath, &result->exe);
}
else
{
ffStrbufDestroy(&result->exe);
ffStrbufInitMove(&result->exe, &path);
result->exeName = "";
}
}
return true;
}
@@ -204,14 +221,19 @@ static bool detectDefaultTerminal(FFTerminalResult* result)
}
}
conhost:
ffStrbufSetF(&result->exe, "%s\\System32\\conhost.exe", getenv("SystemRoot"));
if(ffPathExists(result->exe.chars, FF_PATHTYPE_FILE))
conhost:;
ULONG_PTR conhostPid = 0;
ULONG size;
if(NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(), ProcessConsoleHostProcess, &conhostPid, sizeof(conhostPid), &size)) && conhostPid != 0)
{
ffStrbufSetS(&result->processName, "conhost.exe");
ffStrbufSetS(&result->prettyName, "conhost");
result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1;
return true;
// For Windows Terminal, it reports the PID of OpenConsole
if(ffProcessGetInfoWindows((uint32_t) conhostPid, NULL, &result->processName, &result->exe, &result->exeName, &result->exePath, NULL))
{
ffStrbufSet(&result->prettyName, &result->processName);
if(ffStrbufEndsWithIgnCaseS(&result->prettyName, ".exe"))
ffStrbufSubstrBefore(&result->prettyName, result->prettyName.length - 4);
return true;
}
}
ffStrbufClear(&result->exe);
+1 -1
View File
@@ -100,7 +100,7 @@ const char* detectBySystemd(FFUsersOptions* options, FFlist* users)
if (options->myselfOnly)
{
ffStrbufAppendUInt(&pathUsers, getuid());
ffStrbufAppendUInt(&pathUsers, instance.state.platform.uid);
detectUserBySystemd(&pathUsers, users);
}
else
+71
View File
@@ -1,4 +1,5 @@
#include "fastfetch.h"
#include "common/debug.h"
#include "detection/gpu/gpu.h"
#include "detection/vulkan/vulkan.h"
@@ -39,6 +40,8 @@ static void applyDriverName(VkPhysicalDeviceDriverPropertiesKHR* properties, FFs
static const char* detectVulkan(FFVulkanResult* result)
{
FF_DEBUG("Starting Vulkan detection");
FF_LIBRARY_LOAD_MESSAGE(vulkan,
#if __APPLE__
"libMoltenVK" FF_LIBRARY_EXTENSION, -1
@@ -56,6 +59,7 @@ static const char* detectVulkan(FFVulkanResult* result)
//Some drivers (nvdc) print messages to stdout
//and that is the best way I found to disable that
FF_SUPPRESS_IO();
FF_DEBUG("Suppressed stdout/stderr during Vulkan probing to avoid noisy drivers");
FFVersion instanceVersion = FF_VERSION_INIT;
@@ -66,8 +70,15 @@ static const char* detectVulkan(FFVulkanResult* result)
{
uint32_t version;
if(ffvkEnumerateInstanceVersion(&version) == VK_SUCCESS)
{
applyVulkanVersion(version, &instanceVersion);
FF_DEBUG("Detected Vulkan instance version: %u.%u.%u", instanceVersion.major, instanceVersion.minor, instanceVersion.patch);
}
else
FF_DEBUG("vkEnumerateInstanceVersion() is available but returned a non-success status");
}
else
FF_DEBUG("vkEnumerateInstanceVersion() is unavailable (likely Vulkan 1.0 runtime)");
const uint32_t projectVersion = VK_MAKE_VERSION(
FASTFETCH_PROJECT_VERSION_MAJOR,
@@ -76,6 +87,7 @@ static const char* detectVulkan(FFVulkanResult* result)
);
VkInstance vkInstance;
FF_DEBUG("Creating Vulkan instance with requested API version %s", instanceVersion.minor >= 1 ? "1.1" : "1.0");
VkResult res = ffvkCreateInstance(&(VkInstanceCreateInfo) {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = NULL,
@@ -98,6 +110,7 @@ static const char* detectVulkan(FFVulkanResult* result)
}, NULL, &vkInstance);
if(res != VK_SUCCESS)
{
FF_DEBUG("ffvkCreateInstance() failed with VkResult=%d", res);
switch (res)
{
case VK_ERROR_OUT_OF_HOST_MEMORY:
@@ -116,17 +129,22 @@ static const char* detectVulkan(FFVulkanResult* result)
return "ffvkCreateInstance() failed: unknown error";
}
}
FF_DEBUG("Vulkan instance created successfully");
//if instance creation succeeded, but vkEnumerateInstanceVersion didn't, this means we are running against a vulkan 1.0 implementation
//explicitly set this version, if no device is found, so we still have at least this info
if(instanceVersion.major == 0 && instanceVersion.minor == 0 && instanceVersion.patch == 0)
{
instanceVersion.major = 1;
FF_DEBUG("Falling back to Vulkan instance version 1.0 due to unavailable enumerate call");
}
VkPhysicalDevice physicalDevices[128];
uint32_t physicalDeviceCount = (uint32_t) ARRAY_SIZE(physicalDevices);
res = ffvkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, physicalDevices);
if(res != VK_SUCCESS)
{
FF_DEBUG("ffvkEnumeratePhysicalDevices() failed with VkResult=%d", res);
ffvkDestroyInstance(vkInstance, NULL);
switch (res)
{
@@ -142,13 +160,22 @@ static const char* detectVulkan(FFVulkanResult* result)
return "ffvkEnumeratePhysicalDevices() failed";
}
}
FF_DEBUG("Enumerated %u Vulkan physical device(s)", physicalDeviceCount);
PFN_vkGetPhysicalDeviceProperties ffvkGetPhysicalDeviceProperties = NULL;
PFN_vkGetPhysicalDeviceProperties2 ffvkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceProperties2"); // 1.1
if(!ffvkGetPhysicalDeviceProperties2)
ffvkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceProperties");
FF_DEBUG("Using %s for querying physical device properties", ffvkGetPhysicalDeviceProperties2 ? "vkGetPhysicalDeviceProperties2" : "vkGetPhysicalDeviceProperties");
PFN_vkGetPhysicalDeviceMemoryProperties ffvkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceMemoryProperties");
if(!ffvkGetPhysicalDeviceMemoryProperties)
{
FF_DEBUG("vkGetPhysicalDeviceMemoryProperties is unavailable");
ffvkDestroyInstance(vkInstance, NULL);
return "vkGetPhysicalDeviceMemoryProperties is not available";
}
FFVersion maxDeviceApiVersion = FF_VERSION_INIT;
FFVersion maxDeviceConformanceVersion = FF_VERSION_INIT;
@@ -172,10 +199,19 @@ static const char* detectVulkan(FFVulkanResult* result)
else
ffvkGetPhysicalDeviceProperties(physicalDevices[i], &physicalDeviceProperties.properties);
FF_DEBUG("Processing Vulkan device #%u: name='%s', vendorId=0x%04X, deviceId=0x%04X, type=%u", i,
physicalDeviceProperties.properties.deviceName,
physicalDeviceProperties.properties.vendorID,
physicalDeviceProperties.properties.deviceID,
physicalDeviceProperties.properties.deviceType);
//We don't want software rasterizers to show up as physical gpu
if(physicalDeviceProperties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU)
{
FF_DEBUG("Skipping CPU Vulkan device '%s'", physicalDeviceProperties.properties.deviceName);
continue;
}
//If the device api version is higher than the current highest device api version, overwrite it
//In this case, also use the current device driver name as the shown driver name
@@ -186,6 +222,11 @@ static const char* detectVulkan(FFVulkanResult* result)
{
maxDeviceApiVersion = deviceAPIVersion;
applyDriverName(&driverProperties, &result->driver);
FF_DEBUG("Updated max Vulkan device API version to %u.%u.%u (driver='%s')",
maxDeviceApiVersion.major,
maxDeviceApiVersion.minor,
maxDeviceApiVersion.patch,
result->driver.chars);
}
//If the device conformance version is higher than the current highest device conformance version, overwrite it
@@ -198,7 +239,13 @@ static const char* detectVulkan(FFVulkanResult* result)
};
if(ffVersionCompare(&deviceConformanceVersion, &maxDeviceConformanceVersion) > 0)
{
maxDeviceConformanceVersion = deviceConformanceVersion;
FF_DEBUG("Updated max Vulkan conformance version to %u.%u.%u",
maxDeviceConformanceVersion.major,
maxDeviceConformanceVersion.minor,
maxDeviceConformanceVersion.patch);
}
}
//Add the device to the list of devices shown by the GPU module
@@ -207,7 +254,10 @@ static const char* detectVulkan(FFVulkanResult* result)
FF_LIST_FOR_EACH(FFGPUResult, gpu, result->gpus)
{
if (gpu->deviceId == physicalDeviceProperties.properties.deviceID)
{
FF_DEBUG("Skipping duplicate Vulkan GPU entry for deviceId=0x%04X", physicalDeviceProperties.properties.deviceID);
goto next;
}
}
FFGPUResult* gpu = ffListAdd(&result->gpus);
@@ -221,6 +271,10 @@ static const char* detectVulkan(FFVulkanResult* result)
ffStrbufInitS(&gpu->vendor, ffGPUGetVendorString(physicalDeviceProperties.properties.vendorID));
ffStrbufInitS(&gpu->driver, driverProperties.driverInfo);
ffStrbufInit(&gpu->memoryType);
FF_DEBUG("Added Vulkan GPU '%s' (vendor='%s', type=%s)",
gpu->name.chars,
gpu->vendor.chars,
gpu->type == FF_GPU_TYPE_DISCRETE ? "discrete" : "integrated");
VkPhysicalDeviceMemoryProperties memoryProperties = {};
ffvkGetPhysicalDeviceMemoryProperties(physicalDevices[i], &memoryProperties);
@@ -233,6 +287,10 @@ static const char* detectVulkan(FFVulkanResult* result)
FFGPUMemory* vmem = gpu->type == FF_GPU_TYPE_DISCRETE && (heap->flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) ? &gpu->dedicated : &gpu->shared;
vmem->total += heap->size;
}
FF_DEBUG("Computed memory for '%s': dedicatedTotal=%llu, sharedTotal=%llu",
gpu->name.chars,
(unsigned long long) gpu->dedicated.total,
(unsigned long long) gpu->shared.total);
//No way to detect those using vulkan
gpu->index = FF_GPU_INDEX_UNSET;
@@ -249,7 +307,14 @@ static const char* detectVulkan(FFVulkanResult* result)
ffVersionToPretty(&maxDeviceApiVersion, &result->apiVersion);
ffVersionToPretty(&maxDeviceConformanceVersion, &result->conformanceVersion);
FF_DEBUG("Vulkan detection finished: instanceVersion=%s, apiVersion=%s, conformanceVersion=%s, gpuCount=%u",
result->instanceVersion.chars,
result->apiVersion.chars,
result->conformanceVersion.chars,
result->gpus.length);
ffvkDestroyInstance(vkInstance, NULL);
FF_DEBUG("Destroyed Vulkan instance");
return NULL;
}
@@ -261,6 +326,7 @@ FFVulkanResult* ffDetectVulkan(void)
if (result.gpus.elementSize == 0)
{
FF_DEBUG("Initializing Vulkan detection cache");
ffStrbufInit(&result.driver);
ffStrbufInit(&result.apiVersion);
ffStrbufInit(&result.conformanceVersion);
@@ -269,10 +335,15 @@ FFVulkanResult* ffDetectVulkan(void)
#ifdef FF_HAVE_VULKAN
result.error = detectVulkan(&result);
if (result.error)
FF_DEBUG("Vulkan detection returned error: %s", result.error);
#else
result.error = "fastfetch was compiled without vulkan support";
FF_DEBUG("Vulkan support is disabled at compile time");
#endif
}
else
FF_DEBUG("Reusing cached Vulkan detection result");
return &result;
}
+9 -2
View File
@@ -50,9 +50,16 @@ const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE
if (ffStrbufEqualS(wmName, "WindowServer"))
{
NSError* error;
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"file:///System/Library/CoreServices/WindowManager.app/Contents/version.plist"]
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"file:///System/Library/PrivateFrameworks/SkyLight.framework/Resources/version.plist"]
error:&error];
ffStrbufInitS(result, ((NSString*) dict[@"CFBundleVersion"]).UTF8String);
if (!dict)
{
dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"file:///System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/version.plist"]
error:&error];
}
if (dict)
ffStrbufInitS(result, ((NSString*) dict[@"CFBundleShortVersionString"]).UTF8String);
}
return NULL;
+5
View File
@@ -6,6 +6,11 @@
#ifdef __sun
#include <libzfs.h>
#ifndef __illumos__
// On Solaris 11, zpool_get_prop has only 5 arguments. #2173
#define ffzpool_get_prop(zhp, prop, buf, len, srctype, literal) \
ffzpool_get_prop(zhp, prop, buf, len, srctype)
#endif
#else
#include "libzfs_simplified.h"
#endif
+81 -64
View File
@@ -1,4 +1,5 @@
#include "fastfetch.h"
#include "common/ffdata.h"
#include "detection/version/version.h"
#include "logo/logo.h"
#include "common/commandoption.h"
@@ -379,16 +380,16 @@ static void listModules(bool pretty)
}
}
static bool parseJsoncFile(const char* path, yyjson_read_flag flg)
static bool parseJsoncFile(FFdata* data, const char* path, yyjson_read_flag flg)
{
assert(!instance.state.configDoc);
assert(!data->configDoc);
{
yyjson_read_err error;
instance.state.configDoc = path
data->configDoc = path
? yyjson_read_file(path, flg, NULL, &error)
: yyjson_read_fp(stdin, flg, NULL, &error);
if (!instance.state.configDoc)
if (!data->configDoc)
{
if (error.code != YYJSON_READ_ERROR_FILE_OPEN)
{
@@ -412,7 +413,7 @@ static bool parseJsoncFile(const char* path, yyjson_read_flag flg)
{
const char* error = NULL;
yyjson_val* const root = yyjson_doc_get_root(instance.state.configDoc);
yyjson_val* const root = yyjson_doc_get_root(data->configDoc);
if (!yyjson_is_obj(root))
error = "Invalid JSON config format. Root value must be an object";
@@ -432,8 +433,14 @@ static bool parseJsoncFile(const char* path, yyjson_read_flag flg)
}
static void generateConfigFile(bool force, const char* filePath, bool fullConfig)
static void generateConfigFile(FFdata* data, bool force, const char* filePath, bool fullConfig)
{
if (data->resultDoc)
{
fprintf(stderr, "Error: duplicated `--gen-config` or `--format json` flags found\n");
exit(477);
}
if (!filePath)
{
if (instance.state.platform.configDirs.length == 0)
@@ -443,22 +450,23 @@ static void generateConfigFile(bool force, const char* filePath, bool fullConfig
}
FFstrbuf* configDir = FF_LIST_FIRST(FFstrbuf, instance.state.platform.configDirs);
ffStrbufEnsureFixedLengthFree(&instance.state.genConfigPath, configDir->length + strlen("fastfetch/config.jsonc"));
ffStrbufSet(&instance.state.genConfigPath, configDir);
ffStrbufAppendS(&instance.state.genConfigPath, "fastfetch/config.jsonc");
ffStrbufEnsureFixedLengthFree(&data->genConfigPath, configDir->length + strlen("fastfetch/config.jsonc"));
ffStrbufSet(&data->genConfigPath, configDir);
ffStrbufAppendS(&data->genConfigPath, "fastfetch/config.jsonc");
}
else
{
ffStrbufSetS(&instance.state.genConfigPath, filePath);
ffStrbufSetS(&data->genConfigPath, filePath);
}
if (!force && ffPathExists(instance.state.genConfigPath.chars, FF_PATHTYPE_ANY))
if (!force && ffPathExists(data->genConfigPath.chars, FF_PATHTYPE_ANY))
{
fprintf(stderr, "Error: file `%s` exists. Use `--gen-config%s-force` to overwrite\n", instance.state.genConfigPath.chars, fullConfig ? "-full" : "");
fprintf(stderr, "Error: file `%s` exists. Use `--gen-config%s-force` to overwrite\n", data->genConfigPath.chars, fullConfig ? "-full" : "");
exit(477);
}
instance.state.fullConfig = fullConfig;
data->docType = fullConfig ? FF_RESULT_DOC_TYPE_CONFIG_FULL : FF_RESULT_DOC_TYPE_CONFIG;
data->resultDoc = yyjson_mut_doc_new(NULL);
}
static void optionParseConfigFile(FFdata* data, const char* key, const char* value)
@@ -482,7 +490,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
if (value[0] == '-' && value[1] == '\0')
{
parseJsoncFile(NULL, false);
parseJsoncFile(data, NULL, false);
return;
}
@@ -503,7 +511,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
? YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS
: YYJSON_READ_JSON5;
if (parseJsoncFile(absolutePath.chars, flag)) return;
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
//Try to load as a relative path with the config directory
@@ -515,7 +523,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
if (needExtension)
ffStrbufAppendS(&absolutePath, ".jsonc");
if (parseJsoncFile(absolutePath.chars, flag)) return;
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
}
//Try to load as a preset
@@ -528,7 +536,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
if (needExtension)
ffStrbufAppendS(&absolutePath, ".jsonc");
if (parseJsoncFile(absolutePath.chars, flag)) return;
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
}
//Try to load as a relative path with the directory of fastfetch binary, for Windows support
@@ -543,7 +551,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
ffStrbufAppendS(&absolutePath, value);
if (needExtension)
ffStrbufAppendS(&absolutePath, ".jsonc");
if (parseJsoncFile(absolutePath.chars, flag)) return;
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
// Try {exePath}/presets/
ffStrbufSubstrBefore(&absolutePath, lastSlash);
@@ -551,7 +559,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
ffStrbufAppendS(&absolutePath, value);
if (needExtension)
ffStrbufAppendS(&absolutePath, ".jsonc");
if (parseJsoncFile(absolutePath.chars, flag)) return;
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
}
//File not found
@@ -566,24 +574,17 @@ static void printVersion()
printf("%s %s%s%s (%s)\n", result->projectName, result->version, result->versionTweak, result->debugMode ? "-debug" : "", result->architecture);
}
static void enableJsonOutput(bool enable)
static void enableJsonOutput(FFdata* data)
{
if (!enable)
if (data->resultDoc)
{
if (instance.state.resultDoc)
{
yyjson_mut_doc_free(instance.state.resultDoc);
instance.state.resultDoc = NULL;
}
}
else
{
if (!instance.state.resultDoc)
{
instance.state.resultDoc = yyjson_mut_doc_new(NULL);
yyjson_mut_doc_set_root(instance.state.resultDoc, yyjson_mut_arr(instance.state.resultDoc));
}
fprintf(stderr, "Error: duplicated `--gen-config` or `--format json` flags found\n");
exit(477);
}
data->resultDoc = yyjson_mut_doc_new(NULL);
data->docType = FF_RESULT_DOC_TYPE_JSON;
yyjson_mut_doc_set_root(data->resultDoc, yyjson_mut_arr(data->resultDoc));
}
static void parseCommand(FFdata* data, char* key, char* value)
@@ -668,24 +669,26 @@ static void parseCommand(FFdata* data, char* key, char* value)
exit(0);
}
else if(ffStrEqualsIgnCase(key, "--gen-config"))
generateConfigFile(false, value, false);
generateConfigFile(data, false, value, false);
else if(ffStrEqualsIgnCase(key, "--gen-config-force"))
generateConfigFile(true, value, false);
generateConfigFile(data, true, value, false);
else if(ffStrEqualsIgnCase(key, "--gen-config-full"))
generateConfigFile(false, value, true);
generateConfigFile(data, false, value, true);
else if(ffStrEqualsIgnCase(key, "--gen-config-full-force"))
generateConfigFile(true, value, true);
generateConfigFile(data, true, value, true);
else if(ffStrEqualsIgnCase(key, "-c") || ffStrEqualsIgnCase(key, "--config"))
optionParseConfigFile(data, key, value);
else if(ffStrEqualsIgnCase(key, "-j") || ffStrEqualsIgnCase(key, "--json"))
enableJsonOutput(ffOptionParseBoolean(value));
{
if (ffOptionParseBoolean(value)) enableJsonOutput(data);
}
else if(ffStrEqualsIgnCase(key, "--format"))
{
enableJsonOutput(!!ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
if (!!ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
{ "default", false},
{ "json", true },
{},
}));
})) enableJsonOutput(data);
}
else if(ffStrEqualsIgnCase(key, "--dynamic-interval"))
instance.state.dynamicInterval = ffOptionParseUInt32(key, value); // seconds to milliseconds
@@ -710,6 +713,9 @@ static void parseOption(FFdata* data, const char* key, const char* value)
ffParseModuleOptions(key, value)
) {}
else if(ffStrEqualsIgnCase(key, "--structure-disabled"))
ffOptionParseString(key, value, &data->structureDisabled);
else
{
fprintf(stderr, "Error: unknown option: %s\n", key);
@@ -717,21 +723,21 @@ static void parseOption(FFdata* data, const char* key, const char* value)
}
}
static void parseConfigFiles(void)
static void parseConfigFiles(FFdata* data)
{
if (__builtin_expect(instance.state.genConfigPath.length == 0, true))
if (__builtin_expect(data->genConfigPath.length == 0, true))
{
FF_LIST_FOR_EACH(FFstrbuf, dir, instance.state.platform.configDirs)
{
uint32_t dirLength = dir->length;
ffStrbufAppendS(dir, "fastfetch/config.jsonc");
bool success = parseJsoncFile(dir->chars, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS);
bool success = parseJsoncFile(data, dir->chars, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS);
ffStrbufSubstrBefore(dir, dirLength);
if (success) return;
ffStrbufAppendS(dir, "fastfetch/config.json5");
success = parseJsoncFile(dir->chars, YYJSON_READ_JSON5);
success = parseJsoncFile(data, dir->chars, YYJSON_READ_JSON5);
ffStrbufSubstrBefore(dir, dirLength);
if (success) return;
}
@@ -769,10 +775,10 @@ static void parseArguments(FFdata* data, int argc, char** argv, void (*parser)(F
static void run(FFdata* data)
{
const bool useJsonConfig = data->structure.length == 0 && instance.state.configDoc;
const bool useJsonConfig = data->structure.length == 0 && data->configDoc;
if (useJsonConfig)
ffPrintJsonConfig(true /* prepare */, instance.state.resultDoc);
ffPrintJsonConfig(data, true /* prepare */);
else
{
//If we don't have a custom structure, use the default one
@@ -783,6 +789,9 @@ static void run(FFdata* data)
ffStart();
if (!data->resultDoc)
ffLogoPrint();
#if defined(_WIN32)
if (!instance.config.display.noBuffer) fflush(stdout);
#endif
@@ -790,9 +799,9 @@ static void run(FFdata* data)
while (true)
{
if (useJsonConfig)
ffPrintJsonConfig(false, instance.state.resultDoc);
ffPrintJsonConfig(data, false);
else
ffPrintCommandOption(data, instance.state.resultDoc);
ffPrintCommandOption(data);
if (instance.state.dynamicInterval > 0)
{
@@ -802,10 +811,15 @@ static void run(FFdata* data)
}
else
break;
if (useJsonConfig)
ffPrintJsonConfig(data, true /* prepare */);
else
ffPrepareCommandOption(data);
}
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);
if (data->resultDoc)
yyjson_mut_write_fp(stdout, data->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)
@@ -816,20 +830,20 @@ static void run(FFdata* data)
static void writeConfigFile(FFdata* data)
{
const FFstrbuf* filename = &instance.state.genConfigPath;
const FFstrbuf* filename = &data->genConfigPath;
yyjson_mut_doc* doc = yyjson_mut_doc_new(NULL);
yyjson_mut_doc* doc = data->resultDoc;
yyjson_mut_val* root = yyjson_mut_obj(doc);
yyjson_mut_doc_set_root(doc, root);
yyjson_mut_obj_add_str(doc, root, "$schema", "https://github.com/fastfetch-cli/fastfetch/raw/master/doc/json_schema.json");
if (instance.state.fullConfig)
if (data->docType == FF_RESULT_DOC_TYPE_CONFIG_FULL)
{
ffOptionsGenerateLogoJsonConfig(&instance.config.logo, doc);
ffOptionsGenerateDisplayJsonConfig(&instance.config.display, doc);
ffOptionsGenerateGeneralJsonConfig(&instance.config.general, doc);
ffOptionsGenerateLogoJsonConfig(data, &instance.config.logo);
ffOptionsGenerateDisplayJsonConfig(data, &instance.config.display);
ffOptionsGenerateGeneralJsonConfig(data, &instance.config.general);
}
ffMigrateCommandOptionToJsonc(data, doc);
ffMigrateCommandOptionToJsonc(data);
if (ffStrbufEqualS(filename, "-"))
yyjson_mut_write_fp(stdout, doc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES | YYJSON_WRITE_NEWLINE_AT_END, NULL, NULL);
@@ -854,8 +868,6 @@ static void writeConfigFile(FFdata* data)
exit(1);
}
}
yyjson_mut_doc_free(doc);
}
int main(int argc, char** argv)
@@ -866,24 +878,29 @@ int main(int argc, char** argv)
//Data stores things only needed for the configuration of fastfetch
FFdata data = {
.structure = ffStrbufCreate(),
.configLoaded = false,
.structureDisabled = ffStrbufCreate(),
.genConfigPath = ffStrbufCreate(),
};
parseArguments(&data, argc, argv, parseCommand);
if(instance.state.dynamicInterval && instance.state.resultDoc)
if(instance.state.dynamicInterval && data.resultDoc)
{
fprintf(stderr, "Error: --dynamic-interval cannot be used with --json\n");
exit(400);
}
if(!data.configLoaded && !getenv("NO_CONFIG"))
parseConfigFiles();
parseConfigFiles(&data);
parseArguments(&data, argc, argv, (void*) parseOption);
if (__builtin_expect(instance.state.genConfigPath.length == 0, true))
if (__builtin_expect(data.genConfigPath.length == 0, true))
run(&data);
else
writeConfigFile(&data);
ffStrbufDestroy(&data.structure);
ffStrbufDestroy(&data.structureDisabled);
yyjson_doc_free(data.configDoc);
yyjson_mut_doc_free(data.resultDoc);
ffStrbufDestroy(&data.genConfigPath);
}
+1 -12
View File
@@ -5,12 +5,6 @@
#include <stdint.h>
#include <stdbool.h>
#ifdef FF_USE_SYSTEM_YYJSON
#include <yyjson.h>
#else
#include "3rdparty/yyjson/yyjson.h"
#endif
#ifdef _MSC_VER
#define __attribute__(x)
#endif
@@ -40,13 +34,8 @@ typedef struct FFstate
uint32_t keysHeight;
bool terminalLightTheme;
bool titleFqdn;
FFPlatform platform;
yyjson_doc* configDoc;
yyjson_mut_doc* resultDoc;
FFstrbuf genConfigPath;
bool fullConfig;
uint32_t dynamicInterval;
FFPlatform platform;
} FFstate;
typedef struct FFinstance
+4 -1
View File
@@ -16,9 +16,12 @@ int main(void)
instance.config.display.freqSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_NEVER;
instance.config.display.sizeSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_NEVER;
// Logo printing and other preparation stuff
// Some preparation stuff
ffStart();
// Print logo
ffLogoPrint();
// Print all modules
{
__attribute__((cleanup(ffDestroyTitleOptions))) FFTitleOptions options;
+11
View File
@@ -0,0 +1,11 @@
██████ ██████
██$2██████$1████$2██████$1██
██$3████$2████████████████$1██
██$2██$3████$2██████████████████$1██
██$2██$3██$2████$3████████████$2████████$1██
██$2██████$3████████████████$2██████$1██
██$2████████████████████████████$1██
██$2████████████████████████$1██
██$2██████$3████████$2██████$1██
████$2████████████$1████
████████████
+20 -20
View File
@@ -1,20 +1,20 @@
$1 ▗▄▄▄ $2▗▄▄▄▄ ▄▄▄▖
$1 ▜███▙ $2▜███▙ ▟███▛
$1 ▜███▙ $2▜███▙▟███▛
$1 ▜███▙ $2▜██████▛
$1 ▟█████████████████▙ $2▜████▛ $1▟▙
$1 ▟███████████████████▙ $2▜███▙ $1▟██▙
$2 ▄▄▄▄▖ ▜███▙ $1▟███▛
$2 ▟███▛ ▜██▛ $1▟███▛
$2 ▟███▛ ▜▛ $1▟███▛
$2▟███████████▛ $1▟██████████▙
$2▜██████████▛ $1▟███████████▛
$2 ▟███▛ $1▟▙ ▟███▛
$2 ▟███▛ $1▟██▙ ▟███▛
$2 ▟███▛ $1▜███▙ ▝▀▀▀▀
$2 ▜██▛ $1▜███▙ $2▜██████████████████▛
$2 ▜▛ $1▟████▙ $2▜████████████████▛
$1 ▟██████▙ $2▜███▙
$1 ▟███▛▜███▙ $2▜███▙
$1 ▟███▛ ▜███▙ $2▜███▙
$1 ▝▀▀▀ ▀▀▀▀▘ $2▀▀▀▘
$1▗▄▄▄ $2▗▄▄▄▄ ▄▄▄▖
$1▜███▙ $2▜███▙ ▟███▛
$1▜███▙ $2▜███▙▟███▛
$1▜███▙ $2▜██████▛
$1▟█████████████████▙ $2▜████▛ $3▟▙
$1▟███████████████████▙ $2▜███▙ $3▟██▙
$6▄▄▄▄▖ $2▜███▙ $3▟███▛
$6▟███▛ $2▜██▛ $3▟███▛
$6▟███▛ $2▜▛ $3▟███▛
$6▟███████████▛ $3▟██████████▙
$6▜██████████▛ $3▟███████████▛
$6▟███▛ $5▟▙ $3▟███▛
$6▟███▛ $5▟██▙ $3▟███▛
$6▟███▛ $5▜███▙ $3▝▀▀▀▀
$6▜██▛ $5▜███▙ $4▜██████████████████▛
$6▜▛ $5▟████▙ $4▜████████████████▛
$5▟██████▙ $4▜███▙
$5▟███▛▜███▙ $4▜███▙
$5▟███▛ ▜███▙ $4▜███▙
$5▝▀▀▀ ▀▀▀▀▘ $4▀▀▀▘
+6 -6
View File
@@ -1,7 +1,7 @@
$1 ▗▄ $2▗▄ ▄▖
$1 ▄▄🬸█▄▄▄$2🬸█▛ $1
$2 ▟▛ ▜$1▃▟🬕
$2🬋🬋🬫█ $1█🬛🬋🬋
$2 🬷▛🮃$1▙ ▟▛
$2 🮃 $1▟█🬴$2▀▀▀█🬴▀▀
$1 ▝▀ ▀▘ $2▀▘
$1 ▄▄🬸█▄▄▄$2🬸█▛ $3
$6 ▟▛ ▜$3▃▟🬕
$6🬋🬋🬫█ $3█🬛🬋🬋
$6 🬷▛🮃$5$4▟▛
$6 🮃 $5▟█🬴$4▀▀▀█🬴▀▀
$5▝▀ ▀▘ $4▀▘
+20
View File
@@ -2536,6 +2536,18 @@ static const FFlogo K[] = {
.colorKeys = FF_COLOR_FG_MAGENTA,
.colorTitle = FF_COLOR_FG_BLUE,
},
// KISSLinux2
{
.names = {"kiss2"},
.lines = FASTFETCH_DATATEXT_LOGO_KISS2,
.colors = {
FF_COLOR_FG_BLACK,
FF_COLOR_FG_RED,
FF_COLOR_FG_WHITE,
},
.colorKeys = FF_COLOR_FG_RED,
.colorTitle = FF_COLOR_FG_RED,
},
// Kogaion
{
.names = {"Kogaion"},
@@ -3293,6 +3305,10 @@ static const FFlogo N[] = {
.colors = {
FF_COLOR_FG_BLUE,
FF_COLOR_FG_CYAN,
FF_COLOR_FG_BLUE,
FF_COLOR_FG_CYAN,
FF_COLOR_FG_BLUE,
FF_COLOR_FG_CYAN,
},
},
// NixOSSmall
@@ -3303,6 +3319,10 @@ static const FFlogo N[] = {
.colors = {
FF_COLOR_FG_BLUE,
FF_COLOR_FG_CYAN,
FF_COLOR_FG_BLUE,
FF_COLOR_FG_CYAN,
FF_COLOR_FG_BLUE,
FF_COLOR_FG_CYAN,
},
},
// NixOSOld
+5 -14
View File
@@ -307,10 +307,10 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement)
static void logoApplyColors(const FFlogo* logo, bool replacement)
{
if(instance.config.display.colorTitle.length == 0)
ffStrbufAppendS(&instance.config.display.colorTitle, logo->colorTitle ? logo->colorTitle : logo->colors[0]);
ffStrbufAppendS(&instance.config.display.colorTitle, logo->colorTitle ?: logo->colors[0]);
if(instance.config.display.colorKeys.length == 0)
ffStrbufAppendS(&instance.config.display.colorKeys, logo->colorKeys ? logo->colorKeys : logo->colors[1]);
ffStrbufAppendS(&instance.config.display.colorKeys, logo->colorKeys ?: logo->colors[1]);
if (replacement)
{
@@ -424,7 +424,8 @@ static void logoPrintStruct(const FFlogo* logo)
static void logoPrintNone(void)
{
logoApplyColors(logoGetBuiltinDetected(FF_LOGO_SIZE_NORMAL), false);
if (!instance.config.display.pipe)
logoApplyColors(logoGetBuiltinDetected(FF_LOGO_SIZE_NORMAL), false);
instance.state.logoHeight = 0;
instance.state.logoWidth = 0;
}
@@ -614,16 +615,6 @@ static bool logoTryKnownType(void)
void ffLogoPrint(void)
{
//When generate JSON result, we don't have a logo or padding.
//We also don't need to set main color, because it won't be printed anyway.
//So we can return quickly here.
if(instance.state.resultDoc)
{
instance.state.logoHeight = 0;
instance.state.logoWidth = 0;
return;
}
const FFOptionsLogo* options = &instance.config.logo;
if (options->type == FF_LOGO_TYPE_NONE)
@@ -711,7 +702,7 @@ void ffLogoPrintLine(void)
printf("\033[%uC", instance.state.logoWidth);
if (instance.state.dynamicInterval > 0)
fputs("\033[K", stdout);
fputs("\033[K", stdout); // Clear to the end of the line
++instance.state.keysHeight;
}
+1 -1
View File
@@ -249,7 +249,7 @@ void ffDestroyColorsOptions(FFColorsOptions* options)
FFModuleBaseInfo ffColorsModuleInfo = {
.name = FF_COLORS_MODULE_NAME,
.description = "Print some colored blocks",
.description = "Display the terminal's 16-color palette",
.initOptions = (void*) ffInitColorsOptions,
.destroyOptions = (void*) ffDestroyColorsOptions,
.parseJsonObject = (void*) ffParseColorsJsonObject,
+1 -5
View File
@@ -55,7 +55,7 @@ void ffParseDEJsonObject(FFDEOptions* options, yyjson_val* module)
if (unsafe_yyjson_equals_str(key, "slowVersionDetection"))
{
options->slowVersionDetection = yyjson_get_bool(val);
ffPrintError(FF_DE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Key `slowVersionDetection` is deprecated, it's always true");
continue;
}
@@ -66,8 +66,6 @@ void ffParseDEJsonObject(FFDEOptions* options, yyjson_val* module)
void ffGenerateDEJsonConfig(FFDEOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module)
{
ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
yyjson_mut_obj_add_bool(doc, module, "slowVersionDetection", options->slowVersionDetection);
}
bool ffGenerateDEJsonResult(FF_MAYBE_UNUSED FFDEOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module)
@@ -93,8 +91,6 @@ bool ffGenerateDEJsonResult(FF_MAYBE_UNUSED FFDEOptions* options, yyjson_mut_doc
void ffInitDEOptions(FFDEOptions* options)
{
ffOptionInitModuleArg(&options->moduleArgs, "");
options->slowVersionDetection = false;
}
void ffDestroyDEOptions(FFDEOptions* options)
-2
View File
@@ -5,8 +5,6 @@
typedef struct FFDEOptions
{
FFModuleArgs moduleArgs;
bool slowVersionDetection;
} FFDEOptions;
static_assert(sizeof(FFDEOptions) <= FF_OPTION_MAX_SIZE, "FFDEOptions size exceeds maximum allowed size");
+14
View File
@@ -64,6 +64,12 @@ bool ffPrintTitle(FFTitleOptions* options)
FF_FORMAT_ARG(atColored, "at-symbol-colored"),
FF_FORMAT_ARG(hostNameColored, "host-name-colored"),
FF_FORMAT_ARG(instance.state.platform.fullUserName, "full-user-name"),
#ifndef _WIN32
FF_FORMAT_ARG(instance.state.platform.uid, "user-id"),
#else
FF_FORMAT_ARG(instance.state.platform.sid, "user-id"),
#endif
FF_FORMAT_ARG(instance.state.platform.pid, "pid"),
}));
}
@@ -121,12 +127,18 @@ void ffGenerateTitleJsonConfig(FFTitleOptions* options, yyjson_mut_doc* doc, yyj
bool ffGenerateTitleJsonResult(FF_MAYBE_UNUSED FFTitleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module)
{
yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result");
#ifdef _WIN32
yyjson_mut_obj_add_strbuf(doc, obj, "userId", &instance.state.platform.sid);
#else
yyjson_mut_obj_add_uint(doc, obj, "userId", instance.state.platform.uid);
#endif
yyjson_mut_obj_add_strbuf(doc, obj, "userName", &instance.state.platform.userName);
yyjson_mut_obj_add_strbuf(doc, obj, "fullUserName", &instance.state.platform.fullUserName);
yyjson_mut_obj_add_strbuf(doc, obj, "hostName", &instance.state.platform.hostName);
yyjson_mut_obj_add_strbuf(doc, obj, "homeDir", &instance.state.platform.homeDir);
yyjson_mut_obj_add_strbuf(doc, obj, "exePath", &instance.state.platform.exePath);
yyjson_mut_obj_add_strbuf(doc, obj, "userShell", &instance.state.platform.userShell);
yyjson_mut_obj_add_uint(doc, obj, "pid", instance.state.platform.pid);
return true;
}
@@ -169,5 +181,7 @@ FFModuleBaseInfo ffTitleModuleInfo = {
{"@ symbol (colored)", "at-symbol-colored"},
{"Host name (colored)", "host-name-colored"},
{"Full user name", "full-user-name"},
{"UID (*nix) / SID (Windows)", "user-id"},
{"PID of current process", "pid"},
}))
};
+2 -1
View File
@@ -909,8 +909,9 @@ void ffOptionsDestroyDisplay(FFOptionsDisplay* options)
ffListDestroy(&options->constants);
}
void ffOptionsGenerateDisplayJsonConfig(FFOptionsDisplay* options, yyjson_mut_doc* doc)
void ffOptionsGenerateDisplayJsonConfig(FFdata* data, FFOptionsDisplay* options)
{
yyjson_mut_doc* doc = data->resultDoc;
yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, doc->root, "display");
if (options->stat <= 0)
+2 -1
View File
@@ -1,5 +1,6 @@
#pragma once
#include "common/ffdata.h"
#include "common/percent.h"
#include "common/FFstrbuf.h"
#include "common/FFlist.h"
@@ -98,4 +99,4 @@ const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_va
bool ffOptionsParseDisplayCommandLine(FFOptionsDisplay* options, const char* key, const char* value);
void ffOptionsInitDisplay(FFOptionsDisplay* options);
void ffOptionsDestroyDisplay(FFOptionsDisplay* options);
void ffOptionsGenerateDisplayJsonConfig(FFOptionsDisplay* options, yyjson_mut_doc* doc);
void ffOptionsGenerateDisplayJsonConfig(FFdata* data, FFOptionsDisplay* options);
+2 -1
View File
@@ -117,8 +117,9 @@ void ffOptionsDestroyGeneral(FF_MAYBE_UNUSED FFOptionsGeneral* options)
#endif
}
void ffOptionsGenerateGeneralJsonConfig(FFOptionsGeneral* options, yyjson_mut_doc* doc)
void ffOptionsGenerateGeneralJsonConfig(FFdata* data, FFOptionsGeneral* options)
{
yyjson_mut_doc* doc = data->resultDoc;
yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, doc->root, "general");
yyjson_mut_obj_add_bool(doc, obj, "thread", options->multithreading);
+2 -2
View File
@@ -1,6 +1,6 @@
#pragma once
#include "common/FFstrbuf.h"
#include "common/ffdata.h"
typedef enum __attribute__((__packed__)) FFDsForceDrmType
{
@@ -28,4 +28,4 @@ const char* ffOptionsParseGeneralJsonConfig(FFOptionsGeneral* options, yyjson_va
bool ffOptionsParseGeneralCommandLine(FFOptionsGeneral* options, const char* key, const char* value);
void ffOptionsInitGeneral(FFOptionsGeneral* options);
void ffOptionsDestroyGeneral(FFOptionsGeneral* options);
void ffOptionsGenerateGeneralJsonConfig(FFOptionsGeneral* options, yyjson_mut_doc* doc);
void ffOptionsGenerateGeneralJsonConfig(FFdata* data, FFOptionsGeneral* options);
+2 -1
View File
@@ -466,8 +466,9 @@ const char* ffOptionsParseLogoJsonConfig(FFOptionsLogo* options, yyjson_val* roo
return NULL;
}
void ffOptionsGenerateLogoJsonConfig(FFOptionsLogo* options, yyjson_mut_doc* doc)
void ffOptionsGenerateLogoJsonConfig(FFdata* data, FFOptionsLogo* options)
{
yyjson_mut_doc* doc = data->resultDoc;
yyjson_mut_val* obj = yyjson_mut_obj(doc);
switch (options->type)
+19 -19
View File
@@ -1,28 +1,28 @@
#pragma once
#include "common/FFstrbuf.h"
#include "common/ffdata.h"
#define FASTFETCH_LOGO_MAX_NAMES 9
#define FASTFETCH_LOGO_MAX_COLORS 9 //two digits would make parsing much more complicated (index 1 - 9)
#define FASTFETCH_LOGO_MAX_COLORS 9 // two digits would make parsing much more complicated (index 1 - 9)
typedef enum __attribute__((__packed__)) FFLogoType
{
FF_LOGO_TYPE_AUTO, //if something is given, first try builtin, then file. Otherwise detect logo
FF_LOGO_TYPE_BUILTIN, //builtin ascii art
FF_LOGO_TYPE_SMALL, //builtin ascii art, small version
FF_LOGO_TYPE_FILE, //text file, printed with color code replacement
FF_LOGO_TYPE_FILE_RAW, //text file, printed as is
FF_LOGO_TYPE_DATA, //text data, printed with color code replacement
FF_LOGO_TYPE_DATA_RAW, //text data, printed as is
FF_LOGO_TYPE_COMMAND_RAW, //command to generate text data, printed as is
FF_LOGO_TYPE_IMAGE_SIXEL, //image file, printed as sixel codes
FF_LOGO_TYPE_IMAGE_KITTY, //image file, printed as kitty graphics protocol
FF_LOGO_TYPE_IMAGE_KITTY_DIRECT, //image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm)
FF_LOGO_TYPE_IMAGE_KITTY_ICAT, //image file, use `kitten icat` to display the image. Requires binary `kitten` to be installed"
FF_LOGO_TYPE_IMAGE_ITERM, //image file, printed as iterm graphics protocol
FF_LOGO_TYPE_IMAGE_CHAFA, //image file, printed as ascii art using libchafa
FF_LOGO_TYPE_IMAGE_RAW, //image file, printed as raw binary string
FF_LOGO_TYPE_NONE, //--logo none
FF_LOGO_TYPE_AUTO, // if something is given, first try builtin, then file. Otherwise detect logo
FF_LOGO_TYPE_BUILTIN, // builtin ascii art
FF_LOGO_TYPE_SMALL, // builtin ascii art, small version
FF_LOGO_TYPE_FILE, // text file, printed with color code replacement
FF_LOGO_TYPE_FILE_RAW, // text file, printed as is
FF_LOGO_TYPE_DATA, // text data, printed with color code replacement
FF_LOGO_TYPE_DATA_RAW, // text data, printed as is
FF_LOGO_TYPE_COMMAND_RAW, // command to generate text data, printed as is
FF_LOGO_TYPE_IMAGE_SIXEL, // image file, printed as sixel codes
FF_LOGO_TYPE_IMAGE_KITTY, // image file, printed as kitty graphics protocol
FF_LOGO_TYPE_IMAGE_KITTY_DIRECT, // image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm)
FF_LOGO_TYPE_IMAGE_KITTY_ICAT, // image file, use `kitten icat` to display the image. Requires binary `kitten` to be installed"
FF_LOGO_TYPE_IMAGE_ITERM, // image file, printed as iterm graphics protocol
FF_LOGO_TYPE_IMAGE_CHAFA, // image file, printed as ascii art using libchafa
FF_LOGO_TYPE_IMAGE_RAW, // image file, printed as raw binary string
FF_LOGO_TYPE_NONE, // `--logo none`, but still applies colors to the system information output (unless `--pipe` is set)
} FFLogoType;
typedef enum __attribute__((__packed__)) FFLogoPosition
@@ -58,4 +58,4 @@ void ffOptionsInitLogo(FFOptionsLogo* options);
bool ffOptionsParseLogoCommandLine(FFOptionsLogo* options, const char* key, const char* value);
void ffOptionsDestroyLogo(FFOptionsLogo* options);
const char* ffOptionsParseLogoJsonConfig(FFOptionsLogo* options, yyjson_val* root);
void ffOptionsGenerateLogoJsonConfig(FFOptionsLogo* options, yyjson_mut_doc* doc);
void ffOptionsGenerateLogoJsonConfig(FFdata* data, FFOptionsLogo* options);