diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6a160881cb..0d46ae320f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -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
+ echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-omnios-amd64")' >> CPackConfig.cmake
+ cpack -V
+
+ - name: upload artifacts
+ uses: actions/upload-artifact@v6
+ with:
+ 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
- cpack
+ echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-solaris-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-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:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab8888753a..d053e5ec1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,25 @@
+# 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.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 07e197f02e..725b2b668d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
project(fastfetch
- VERSION 2.58.0
+ 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()
diff --git a/README.md b/README.md
index 4630b13ee3..7fc0a17051 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@
[](https://deepwiki.com/fastfetch-cli/fastfetch)
[](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.
diff --git a/debian/changelog.tpl b/debian/changelog.tpl
index d89914a894..2144517a68 100644
--- a/debian/changelog.tpl
+++ b/debian/changelog.tpl
@@ -1,3 +1,9 @@
+fastfetch (2.58.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
+
+ * Update to 2.58.0
+
+ -- Carter Li Thu, 22 Jan 2026 15:30:58 +0800
+
fastfetch (2.57.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.57.1
diff --git a/doc/json_schema.json b/doc/json_schema.json
index 37c2aba412..dac6715f52 100644
--- a/doc/json_schema.json
+++ b/doc/json_schema.json
@@ -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"
}
},
diff --git a/src/common/FFPlatform.h b/src/common/FFPlatform.h
index 2962cbd171..c714b5dda8 100644
--- a/src/common/FFPlatform.h
+++ b/src/common/FFPlatform.h
@@ -20,6 +20,12 @@ typedef struct FFPlatform
FFlist dataDirs; // List of FFstrbuf, trailing slash included
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;
diff --git a/src/common/apple/smc_temps.c b/src/common/apple/smc_temps.c
index 708ae74370..0a31a50527 100644
--- a/src/common/apple/smc_temps.c
+++ b/src/common/apple/smc_temps.c
@@ -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";
}
}
diff --git a/src/common/arrayUtils.h b/src/common/arrayUtils.h
index ad251ba1ed..83a49d6aa0 100644
--- a/src/common/arrayUtils.h
+++ b/src/common/arrayUtils.h
@@ -3,7 +3,9 @@
#include
#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))); })
diff --git a/src/common/impl/FFPlatform.c b/src/common/impl/FFPlatform.c
index 399559799f..0c42a649f3 100644
--- a/src/common/impl/FFPlatform.c
+++ b/src/common/impl/FFPlatform.c
@@ -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);
diff --git a/src/common/impl/FFPlatform_unix.c b/src/common/impl/FFPlatform_unix.c
index 6b25bd537a..985494bf9e 100644
--- a/src/common/impl/FFPlatform_unix.c
+++ b/src/common/impl/FFPlatform_unix.c
@@ -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)
diff --git a/src/common/impl/FFPlatform_windows.c b/src/common/impl/FFPlatform_windows.c
index cd138dc22a..c09cdc2770 100644
--- a/src/common/impl/FFPlatform_windows.c
+++ b/src/common/impl/FFPlatform_windows.c
@@ -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
#include
+#include
#define SECURITY_WIN32 1 // For secext.h
#include
@@ -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);
diff --git a/src/common/impl/FFstrbuf.c b/src/common/impl/FFstrbuf.c
index 1f59bf1a13..09e79b3551 100644
--- a/src/common/impl/FFstrbuf.c
+++ b/src/common/impl/FFstrbuf.c
@@ -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
diff --git a/src/common/impl/commandoption.c b/src/common/impl/commandoption.c
index 9b89a811ae..7bfc489993 100644
--- a/src/common/impl/commandoption.c
+++ b/src/common/impl/commandoption.c
@@ -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 for more information.\n", stderr);
exit(477);
}
diff --git a/src/common/impl/init.c b/src/common/impl/init.c
index ab9d0ad53c..4b9033a6fe 100644
--- a/src/common/impl/init.c
+++ b/src/common/impl/init.c
@@ -225,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
@@ -258,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);
}
diff --git a/src/common/impl/io_unix.c b/src/common/impl/io_unix.c
index 63fc81e4d2..31fefe91d0 100644
--- a/src/common/impl/io_unix.c
+++ b/src/common/impl/io_unix.c
@@ -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);
diff --git a/src/common/impl/memrchr.c b/src/common/impl/memrchr.c
new file mode 100644
index 0000000000..730a3ae3a5
--- /dev/null
+++ b/src/common/impl/memrchr.c
@@ -0,0 +1,19 @@
+#include "common/memrchr.h"
+#include
+#include
+
+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;
+}
diff --git a/src/common/impl/netif_apple.c b/src/common/impl/netif_apple.c
index 54128ec9cc..346ce89373 100644
--- a/src/common/impl/netif_apple.c
+++ b/src/common/impl/netif_apple.c
@@ -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
diff --git a/src/common/impl/netif_linux.c b/src/common/impl/netif_linux.c
index 03ade7d9d1..092d919fb6 100644
--- a/src/common/impl/netif_linux.c
+++ b/src/common/impl/netif_linux.c
@@ -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
diff --git a/src/common/impl/processing_windows.c b/src/common/impl/processing_windows.c
index b128d14387..f0dc096cd1 100644
--- a/src/common/impl/processing_windows.c
+++ b/src/common/impl/processing_windows.c
@@ -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
#include
#include
#include
@@ -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 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
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
diff --git a/src/common/memrchr.h b/src/common/memrchr.h
new file mode 100644
index 0000000000..6905a33176
--- /dev/null
+++ b/src/common/memrchr.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include
+
+#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
diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h
index 2110836ab3..b4d83d66aa 100644
--- a/src/common/windows/nt.h
+++ b/src/common/windows/nt.h
@@ -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
diff --git a/src/common/windows/unicode.hpp b/src/common/windows/unicode.hpp
index f3bca6f48f..418a206df8 100644
--- a/src/common/windows/unicode.hpp
+++ b/src/common/windows/unicode.hpp
@@ -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());
diff --git a/src/detection/bluetooth/bluetooth_windows.c b/src/detection/bluetooth/bluetooth_windows.c
index 050929fee8..bfae307d0f 100644
--- a/src/detection/bluetooth/bluetooth_windows.c
+++ b/src/detection/bluetooth/bluetooth_windows.c
@@ -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)
diff --git a/src/detection/bluetooth/bluetooth_windows.cpp b/src/detection/bluetooth/bluetooth_windows.cpp
index f4ac9c9c7e..b54882d613 100644
--- a/src/detection/bluetooth/bluetooth_windows.cpp
+++ b/src/detection/bluetooth/bluetooth_windows.cpp
@@ -79,8 +79,7 @@ const char* ffBluetoothDetectBattery(FFlist* devices)
batt = data.get();
else
{
- FF_STRBUF_AUTO_DESTROY addr; // MAC address without colon
- ffStrbufInitWSV(&addr, data.get());
+ FF_STRBUF_AUTO_DESTROY addr = ffStrbufCreateWSV(data.get()); // MAC address without colon
if (__builtin_expect(addr.length != 12, 0))
continue;
diff --git a/src/detection/bluetoothradio/bluetoothradio_windows.c b/src/detection/bluetoothradio/bluetoothradio_windows.c
index 057bf29330..b773b511eb 100644
--- a/src/detection/bluetoothradio/bluetoothradio_windows.c
+++ b/src/detection/bluetoothradio/bluetoothradio_windows.c
@@ -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)
diff --git a/src/detection/bootmgr/bootmgr_bsd.c b/src/detection/bootmgr/bootmgr_bsd.c
index ce9663b619..c734854703 100644
--- a/src/detection/bootmgr/bootmgr_bsd.c
+++ b/src/detection/bootmgr/bootmgr_bsd.c
@@ -9,6 +9,7 @@
#endif
#include
#include
+#include
#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,
diff --git a/src/detection/bootmgr/bootmgr_linux.c b/src/detection/bootmgr/bootmgr_linux.c
index aef323ad6c..e2aebcd429 100644
--- a/src/detection/bootmgr/bootmgr_linux.c
+++ b/src/detection/bootmgr/bootmgr_linux.c
@@ -2,11 +2,13 @@
#include "common/io.h"
#include "efi_helper.h"
+#include
+
#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";
diff --git a/src/detection/bootmgr/bootmgr_windows.c b/src/detection/bootmgr/bootmgr_windows.c
index cb6a974b3f..3fe46b161f 100644
--- a/src/detection/bootmgr/bootmgr_windows.c
+++ b/src/detection/bootmgr/bootmgr_windows.c
@@ -1,13 +1,14 @@
#include "bootmgr.h"
#include "efi_helper.h"
#include "common/io.h"
+#include "common/windows/nt.h"
#include
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 = {
diff --git a/src/detection/cpu/cpu_arm.h b/src/detection/cpu/cpu_arm.h
index 77cacbf877..9f9a45042c 100644
--- a/src/detection/cpu/cpu_arm.h
+++ b/src/detection/cpu/cpu_arm.h
@@ -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;
diff --git a/src/detection/disk/disk_windows.c b/src/detection/disk/disk_windows.c
index e13cec90a4..47cd3f504e 100644
--- a/src/detection/disk/disk_windows.c
+++ b/src/detection/disk/disk_windows.c
@@ -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
#include
-
-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
+#include
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* mountpoint = buf + i;
+ wchar_t mountpointW[] = L"X:\\";
+ char mountpointA[] = "X:\\";
- ffStrbufSetWS(&buffer, mountpoint);
- i += buffer.length;
+ for (wchar_t i = L'A'; i <= L'Z'; i++)
+ {
+ if (!(info.Query.DriveMap & (1 << (i - L'A'))))
+ continue;
+ mountpointW[0] = i;
+ mountpointA[0] = (char) i;
- 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;
- FF_STRBUF_AUTO_DESTROY diskFileSystemBuf = ffStrbufCreate();
+ 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;
- if (volumeInfoAvailable)
+ FF_STRBUF_AUTO_DESTROY diskFileSystemBuf = ffStrbufCreate();
+ 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;
diff --git a/src/detection/displayserver/displayserver_android.c b/src/detection/displayserver/displayserver_android.c
index 76e152de47..d90e7bd029 100644
--- a/src/detection/displayserver/displayserver_android.c
+++ b/src/detection/displayserver/displayserver_android.c
@@ -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;
}
diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c
index 58c66fba9c..5037215894 100644
--- a/src/detection/displayserver/linux/wmde.c
+++ b/src/detection/displayserver/linux/wmde.c
@@ -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__
diff --git a/src/detection/gpu/gpu.c b/src/detection/gpu/gpu.c
index c66d780e6a..b2130d56dc 100644
--- a/src/detection/gpu/gpu.c
+++ b/src/detection/gpu/gpu.c
@@ -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";
}
diff --git a/src/detection/gpu/gpu_general.c b/src/detection/gpu/gpu_general.c
deleted file mode 100644
index 6fcb6829b3..0000000000
--- a/src/detection/gpu/gpu_general.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "gpu.h"
-
-#ifdef FF_HAVE_PCIACCESS
-
-#include "common/io.h"
-#include "common/library.h"
-
-#include
-
-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
diff --git a/src/detection/gpu/gpu_gnu.c b/src/detection/gpu/gpu_gnu.c
new file mode 100644
index 0000000000..be1eedba80
--- /dev/null
+++ b/src/detection/gpu/gpu_gnu.c
@@ -0,0 +1,123 @@
+#include "gpu.h"
+#include "common/io.h"
+
+#include
+#include
+#include
+
+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;
+}
diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c
index 26a6529a7e..68dcc52a48 100644
--- a/src/detection/gpu/gpu_linux.c
+++ b/src/detection/gpu/gpu_linux.c
@@ -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)
diff --git a/src/detection/gpu/gpu_pci.c b/src/detection/gpu/gpu_pci.c
index 02923a286f..a6ca9bbebc 100644
--- a/src/detection/gpu/gpu_pci.c
+++ b/src/detection/gpu/gpu_pci.c
@@ -1,6 +1,7 @@
#include "gpu.h"
#include "common/io.h"
#include "common/properties.h"
+#include "common/memrchr.h"
#include
#ifdef __FreeBSD__
diff --git a/src/detection/lm/lm_linux.c b/src/detection/lm/lm_linux.c
index c336e5796b..c73adf0cc6 100644
--- a/src/detection/lm/lm_linux.c
+++ b/src/detection/lm/lm_linux.c
@@ -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
diff --git a/src/detection/media/media_linux.c b/src/detection/media/media_linux.c
index e46d77e879..e7d5f0ad02 100644
--- a/src/detection/media/media_linux.c
+++ b/src/detection/media/media_linux.c
@@ -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
diff --git a/src/detection/memory/memory_apple.c b/src/detection/memory/memory_apple.c
index a24dba32e5..234db11864 100644
--- a/src/detection/memory/memory_apple.c
+++ b/src/detection/memory/memory_apple.c
@@ -1,4 +1,5 @@
#include "memory.h"
+#include "common/debug.h"
#include
#include
@@ -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;
}
diff --git a/src/detection/memory/memory_windows.c b/src/detection/memory/memory_windows.c
index 666d797275..38362fd402 100644
--- a/src/detection/memory/memory_windows.c
+++ b/src/detection/memory/memory_windows.c
@@ -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";
diff --git a/src/detection/opengl/opengl_shared.c b/src/detection/opengl/opengl_shared.c
index db72fb80d8..fa70abf748 100644
--- a/src/detection/opengl/opengl_shared.c
+++ b/src/detection/opengl/opengl_shared.c
@@ -1,4 +1,5 @@
#include "opengl.h"
+#include "common/debug.h"
#include "common/library.h"
#if __has_include()
@@ -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
diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c
index e981f50aeb..1ec5cb07ab 100644
--- a/src/detection/packages/packages_windows.c
+++ b/src/detection/packages/packages_windows.c
@@ -6,9 +6,11 @@
#include "common/mallocHelper.h"
#include "common/io.h"
+#include
#include
#include "common/windows/nt.h"
#include
+#include
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/");
diff --git a/src/detection/physicaldisk/physicaldisk_windows.c b/src/detection/physicaldisk/physicaldisk_windows.c
index 8d90f01eab..2266e15006 100644
--- a/src/detection/physicaldisk/physicaldisk_windows.c
+++ b/src/detection/physicaldisk/physicaldisk_windows.c
@@ -2,6 +2,7 @@
#include "common/io.h"
#include "common/windows/unicode.h"
+#include
#include
#include
@@ -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,
diff --git a/src/detection/swap/swap_sunos.c b/src/detection/swap/swap_sunos.c
index c5e5548445..87361b56be 100644
--- a/src/detection/swap/swap_sunos.c
+++ b/src/detection/swap/swap_sunos.c
@@ -2,13 +2,14 @@
#include
#include
#include
+#include
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)
diff --git a/src/detection/swap/swap_windows.c b/src/detection/swap/swap_windows.c
index 833293b11a..0909267ecf 100644
--- a/src/detection/swap/swap_windows.c
+++ b/src/detection/swap/swap_windows.c
@@ -5,11 +5,11 @@
#include
#include
#include
-#include
+#include
-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);
-}
diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c
index 7dff190e38..b412d5800f 100644
--- a/src/detection/terminalshell/terminalshell_windows.c
+++ b/src/detection/terminalshell/terminalshell_windows.c
@@ -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
#include
#include
#include
+#include
+#include
+#include
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))
- {
- result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1;
- ffStrbufSet(&result->exePath, &result->exe);
- }
- else
+
+ PWSTR programFiles = NULL;
+ if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &programFiles)))
{
- 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);
diff --git a/src/detection/users/users_linux.c b/src/detection/users/users_linux.c
index 46d8a02e56..357c1ab7d8 100644
--- a/src/detection/users/users_linux.c
+++ b/src/detection/users/users_linux.c
@@ -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
diff --git a/src/detection/vulkan/vulkan.c b/src/detection/vulkan/vulkan.c
index 085c7cb644..4dc2b775f9 100644
--- a/src/detection/vulkan/vulkan.c
+++ b/src/detection/vulkan/vulkan.c
@@ -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;
}
diff --git a/src/detection/zpool/zpool.c b/src/detection/zpool/zpool.c
index ca2c31a281..2f4a4a83fc 100644
--- a/src/detection/zpool/zpool.c
+++ b/src/detection/zpool/zpool.c
@@ -6,6 +6,11 @@
#ifdef __sun
#include
+ #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
diff --git a/src/logo/ascii/nixos.txt b/src/logo/ascii/nixos.txt
index 7bbf9b63aa..b42080552d 100644
--- a/src/logo/ascii/nixos.txt
+++ b/src/logo/ascii/nixos.txt
@@ -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▀▀▀▘
\ No newline at end of file
+ $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▀▀▀▘
diff --git a/src/logo/ascii/nixos_small.txt b/src/logo/ascii/nixos_small.txt
index 1ac92d85c9..524f96c2ee 100644
--- a/src/logo/ascii/nixos_small.txt
+++ b/src/logo/ascii/nixos_small.txt
@@ -1,7 +1,7 @@
$1 ▗▄ $2▗▄ ▄▖
-$1 ▄▄🬸█▄▄▄$2🬸█▛ $1▃
-$2 ▟▛ ▜$1▃▟🬕
-$2🬋🬋🬫█ $1█🬛🬋🬋
-$2 🬷▛🮃$1▙ ▟▛
-$2 🮃 $1▟█🬴$2▀▀▀█🬴▀▀
-$1 ▝▀ ▀▘ $2▀▘
\ No newline at end of file
+$1 ▄▄🬸█▄▄▄$2🬸█▛ $3▃
+$6 ▟▛ ▜$3▃▟🬕
+$6🬋🬋🬫█ $3█🬛🬋🬋
+$6 🬷▛🮃$5▙ $4▟▛
+$6 🮃 $5▟█🬴$4▀▀▀█🬴▀▀
+ $5▝▀ ▀▘ $4▀▘
\ No newline at end of file
diff --git a/src/logo/builtin.c b/src/logo/builtin.c
index 7b4b366341..c2f220778c 100644
--- a/src/logo/builtin.c
+++ b/src/logo/builtin.c
@@ -3305,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
@@ -3315,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
diff --git a/src/logo/logo.c b/src/logo/logo.c
index 1bd6cb8e94..d4c682a1fd 100644
--- a/src/logo/logo.c
+++ b/src/logo/logo.c
@@ -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)
{
diff --git a/src/modules/title/title.c b/src/modules/title/title.c
index d846b2817b..b3948429a3 100644
--- a/src/modules/title/title.c
+++ b/src/modules/title/title.c
@@ -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"},
}))
};