From 8f082730a841e57bc2742ebece01f5f0857b0775 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 22 Jan 2026 15:35:44 +0800 Subject: [PATCH 01/44] Packaging: update debian stuff [ci skip] --- debian/changelog.tpl | 6 ++++++ 1 file changed, 6 insertions(+) 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 From c116a5abdf378670d04312a27ad34fd4fbe15fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 22 Jan 2026 09:06:23 +0800 Subject: [PATCH 02/44] Chore: uses elvis operators where applicatable --- src/common/impl/commandoption.c | 2 +- src/detection/gpu/gpu_linux.c | 2 +- src/logo/logo.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) 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/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/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) { From 79849fb2b5da6f7c811ee86947108f634d542020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 23 Jan 2026 10:26:04 +0800 Subject: [PATCH 03/44] CI: updates dependencies --- .github/workflows/ci.yml | 66 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a160881cb..3fdbb2e8b1 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 @@ -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,7 +428,7 @@ 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-*.* @@ -438,7 +438,7 @@ jobs: name: SunOS-amd64 steps: - name: checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: run VM uses: vmactions/omnios-vm@v1 @@ -461,7 +461,7 @@ jobs: cpack - name: upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: fastfetch-sunos-amd64 path: ./fastfetch-*.* @@ -474,7 +474,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 +483,7 @@ jobs: architecture: x86-64 cpu_count: 4 shell: bash - version: '14.3' + version: '15.0' run: | uname -a sudo pkg update @@ -498,7 +498,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 +511,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 +535,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 +548,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 +571,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 +584,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 +606,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 +619,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 +641,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 +681,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 +723,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 +750,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 }}.* From 4a581039d5d7ebbee39a27b7ecd7e3c14deb32db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sat, 24 Jan 2026 09:17:13 +0800 Subject: [PATCH 04/44] CI: fixes building --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fdbb2e8b1..646bd67d45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 From 19d3c948adad7070aee4204b83bc4c99d312d53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 26 Jan 2026 10:16:48 +0800 Subject: [PATCH 05/44] DE (Android): clarifies that Google Pixel and other OSes also report `ro.build.display.id` --- src/detection/displayserver/displayserver_android.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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; } From b365d4cca1fff502e27f1b99ea21942abc12ba41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 26 Jan 2026 10:17:19 +0800 Subject: [PATCH 06/44] CPU (ARM): adds more ARM parts --- src/detection/cpu/cpu_arm.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/detection/cpu/cpu_arm.h b/src/detection/cpu/cpu_arm.h index 77cacbf877..aec66577f9 100644 --- a/src/detection/cpu/cpu_arm.h +++ b/src/detection/cpu/cpu_arm.h @@ -36,6 +36,9 @@ static const char* armPartId2name(uint32_t partId) { switch (partId) { + case 0xb36: return "ARM1136j-s"; + case 0xb56: return "ARM1156t2-s"; + case 0xb76: return "ARM1176jz-s"; case 0x810: return "ARM810"; case 0x920: return "ARM920"; case 0x922: return "ARM922"; @@ -82,10 +85,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"; From e44df27c961374c72079e029064b311a342be271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 27 Jan 2026 14:24:38 +0800 Subject: [PATCH 07/44] Memory (macOS): adds debug info Usage: `fastfetch -s memory:swap -l none --dynamic-interval 1000 --debug --no-buffer` See https://github.com/fastfetch-cli/fastfetch/issues/2171#issuecomment-3803309478 --- src/detection/memory/memory_apple.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/detection/memory/memory_apple.c b/src/detection/memory/memory_apple.c index a24dba32e5..21a6d15aee 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 @@ -21,11 +22,16 @@ const char* ffDetectMemory(FFMemoryResult* ram) return "Failed to read host_statistics64"; uint64_t pageSize = instance.state.platform.sysinfo.pageSize; - uint64_t appMemory = vmstat.internal_page_count * pageSize; + uint64_t appMemory = vmstat.internal_page_count * pageSize; // memory allocated by `mmap(..., MAP_ANON | MAP_PRIVATE, ...)`, i.e. malloc(3) uint64_t wiredMemory = vmstat.wire_count * pageSize; uint64_t compressed = vmstat.compressor_page_count * pageSize; uint64_t reserved = ram->bytesTotal - usableMemory; + FF_DEBUG("App: %.2f GiB", (double) appMemory / 1024. / 1024. / 1024.); + FF_DEBUG("Wired: %.2f GiB", (double) wiredMemory / 1024. / 1024. / 1024.); + FF_DEBUG("Compressed: %.2f GiB", (double) compressed / 1024. / 1024. / 1024.); + FF_DEBUG("Reserved: %.2f GiB", (double) reserved / 1024. / 1024. / 1024.); + ram->bytesUsed = appMemory + wiredMemory + compressed + reserved; return NULL; From d698be6239c2d4ad63dcf218c8471c4a0a30627a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 27 Jan 2026 14:48:05 +0800 Subject: [PATCH 08/44] CPU (ARM): removes duplicated cases --- src/detection/cpu/cpu_arm.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/detection/cpu/cpu_arm.h b/src/detection/cpu/cpu_arm.h index aec66577f9..9f9a45042c 100644 --- a/src/detection/cpu/cpu_arm.h +++ b/src/detection/cpu/cpu_arm.h @@ -36,9 +36,6 @@ static const char* armPartId2name(uint32_t partId) { switch (partId) { - case 0xb36: return "ARM1136j-s"; - case 0xb56: return "ARM1156t2-s"; - case 0xb76: return "ARM1176jz-s"; case 0x810: return "ARM810"; case 0x920: return "ARM920"; case 0x922: return "ARM922"; @@ -49,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"; @@ -220,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; From dc04273bd7c994619703d75d4bc30ba0bdf4377d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 29 Jan 2026 09:34:50 +0800 Subject: [PATCH 09/44] Memory (macOS): updates algorithm in order not to use `hw.memsize_usable` which is available on newer macOS version Adds a new CMake option to use usable memory size at the same time Fixes #2171 --- CMakeLists.txt | 11 +++++++++++ src/common/impl/init.c | 3 +++ src/detection/memory/memory_apple.c | 25 ++++++++++--------------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07e197f02e..cd09fd47a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,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" ON) +endif() set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static) set(BINARY_LINK_TYPE dlopen CACHE STRING "How to link fastfetch") @@ -1424,6 +1427,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) diff --git a/src/common/impl/init.c b/src/common/impl/init.c index ab9d0ad53c..0825aec0a4 100644 --- a/src/common/impl/init.c +++ b/src/common/impl/init.c @@ -258,6 +258,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/detection/memory/memory_apple.c b/src/detection/memory/memory_apple.c index 21a6d15aee..234db11864 100644 --- a/src/detection/memory/memory_apple.c +++ b/src/detection/memory/memory_apple.c @@ -9,30 +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; // memory allocated by `mmap(..., MAP_ANON | MAP_PRIVATE, ...)`, i.e. malloc(3) - uint64_t wiredMemory = vmstat.wire_count * pageSize; - uint64_t compressed = vmstat.compressor_page_count * pageSize; - uint64_t reserved = ram->bytesTotal - usableMemory; - - FF_DEBUG("App: %.2f GiB", (double) appMemory / 1024. / 1024. / 1024.); - FF_DEBUG("Wired: %.2f GiB", (double) wiredMemory / 1024. / 1024. / 1024.); - FF_DEBUG("Compressed: %.2f GiB", (double) compressed / 1024. / 1024. / 1024.); - FF_DEBUG("Reserved: %.2f GiB", (double) reserved / 1024. / 1024. / 1024.); + // 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; } From 7c284f9ee8eeaf17a8b3510e53f12399c9db73e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 29 Jan 2026 13:11:48 +0800 Subject: [PATCH 10/44] CMake (macOS): disable `ENABLE_APPLE_MEMSIZE_USABLE` by default --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd09fd47a7..f4cd8ddae1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,7 +111,7 @@ 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" ON) + 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) From 0686d1db4a81740ea37df2b2d697977f56dde6df Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 30 Jan 2026 18:49:26 +0800 Subject: [PATCH 11/44] GPU (Hurd): uses native API; removes libpciaccess dep --- CMakeLists.txt | 7 +- src/common/impl/init.c | 3 - src/detection/gpu/gpu_general.c | 70 ------------------ src/detection/gpu/gpu_gnu.c | 123 ++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 79 deletions(-) delete mode 100644 src/detection/gpu/gpu_general.c create mode 100644 src/detection/gpu/gpu_gnu.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f4cd8ddae1..3357fef619 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) @@ -1262,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 @@ -1641,10 +1640,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) diff --git a/src/common/impl/init.c b/src/common/impl/init.c index 0825aec0a4..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 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; +} From e8f2b9a1c7d7b152e5fd85e40421fc6460a6f86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 2 Feb 2026 11:08:08 +0800 Subject: [PATCH 12/44] Common: uses `_Countof` in ARRAY_SIZE definition when available --- CMakeLists.txt | 9 +++++++++ src/common/arrayUtils.h | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3357fef619..28778aacbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1343,6 +1343,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) 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))); }) From 5b10cde59f5b364c57ec1e9b387b98b9865c6671 Mon Sep 17 00:00:00 2001 From: Verdgil Date: Tue, 10 Feb 2026 12:32:20 +0300 Subject: [PATCH 13/44] Global: Add support for Oracle Solaris (#2176) * Fix Oracle Solaris zpool build * Add memrchr fallback * Change to include fron extern * add new line * remove ; in zpool.c * Change wrapper to macro Also change README.md * Tweaks --------- Co-authored-by: Carter Li --- CMakeLists.txt | 1 + README.md | 2 +- src/common/solaris/memrchr.c | 15 +++++++++++++++ src/common/solaris/memrchr.h | 6 ++++++ src/detection/gpu/gpu_pci.c | 4 ++++ src/detection/zpool/zpool.c | 5 +++++ 6 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/common/solaris/memrchr.c create mode 100644 src/common/solaris/memrchr.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 28778aacbb..58fc2a11d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1115,6 +1115,7 @@ elseif(SunOS) src/detection/displayserver/linux/xcb.c src/detection/displayserver/linux/xlib.c src/detection/font/font_linux.c + src/common/solaris/memrchr.c src/detection/gpu/gpu_sunos.c src/detection/gpu/gpu_pci.c src/detection/gtk_qt/gtk.c diff --git a/README.md b/README.md index 4630b13ee3..7fc0a17051 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/fastfetch-cli/fastfetch) [![中文README](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-README-red)](README-cn.md) -Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and illumos (SunOS). +Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, illumos (SunOS), and Solaris. diff --git a/src/common/solaris/memrchr.c b/src/common/solaris/memrchr.c new file mode 100644 index 0000000000..0e0c03d991 --- /dev/null +++ b/src/common/solaris/memrchr.c @@ -0,0 +1,15 @@ +#if defined(__sun) && ! defined(__illumos__) +#include "memrchr.h" + +void *memrchr(const void *s, int c, size_t n) +{ + if(n == 0) return NULL; + const unsigned char *p = (const unsigned char *)s + n; + while (n--) { + if (*(--p) == (unsigned char) c) + return (void*) p; + } + + return NULL; +} +#endif diff --git a/src/common/solaris/memrchr.h b/src/common/solaris/memrchr.h new file mode 100644 index 0000000000..6e6453d821 --- /dev/null +++ b/src/common/solaris/memrchr.h @@ -0,0 +1,6 @@ +#pragma once +#include + +#if defined(__sun) && !defined(__illumos__) +void *memrchr(const void *s, int c, size_t n); +#endif diff --git a/src/detection/gpu/gpu_pci.c b/src/detection/gpu/gpu_pci.c index 02923a286f..8d66bd9779 100644 --- a/src/detection/gpu/gpu_pci.c +++ b/src/detection/gpu/gpu_pci.c @@ -24,6 +24,10 @@ #define FF_STR_INDIR(x) #x #define FF_STR(x) FF_STR_INDIR(x) +#if defined(__sun) && ! defined(__illumos__) +#include "common/solaris/memrchr.h" +#endif + static const FFstrbuf* loadPciIds() { static FFstrbuf pciids; 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 From 958e7dbd8f5ee006cce6c8f6e078653158290f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 9 Feb 2026 13:33:18 +0800 Subject: [PATCH 14/44] Bluetooth (Windows): simplifies code --- src/common/windows/unicode.hpp | 5 +++++ src/detection/bluetooth/bluetooth_windows.cpp | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) 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.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; From d2215f26acb5bf394b2fc234b5fcaf5e93121e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 9 Feb 2026 16:12:24 +0800 Subject: [PATCH 15/44] Processing (Windows): uses `NtQueryInformationProcess` instead of its wrapper --- src/common/impl/processing_windows.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/common/impl/processing_windows.c b/src/common/impl/processing_windows.c index b128d14387..1c63013ead 100644 --- a/src/common/impl/processing_windows.c +++ b/src/common/impl/processing_windows.c @@ -1,7 +1,9 @@ #include "fastfetch.h" #include "common/processing.h" #include "common/io.h" +#include "common/windows/unicode.h" +#include #include #include #include @@ -193,9 +195,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; @@ -227,12 +236,13 @@ bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFst } if(exe) { - DWORD bufSize = exe->allocated; - if(QueryFullProcessImageNameA(hProcess, 0, exe->chars, &bufSize)) + 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 From 1323d007d31bf7b1c807ed43665676a3dfb5aa35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 9 Feb 2026 16:22:17 +0800 Subject: [PATCH 16/44] Global: uses `alignas` where applicable --- src/detection/bootmgr/bootmgr_bsd.c | 3 ++- src/detection/bootmgr/bootmgr_linux.c | 4 +++- src/detection/packages/packages_windows.c | 3 ++- src/detection/physicaldisk/physicaldisk_windows.c | 3 ++- src/detection/swap/swap_sunos.c | 3 ++- src/detection/swap/swap_windows.c | 3 ++- 6 files changed, 13 insertions(+), 6 deletions(-) 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/packages/packages_windows.c b/src/detection/packages/packages_windows.c index e981f50aeb..ec4c6e1af7 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -6,6 +6,7 @@ #include "common/mallocHelper.h" #include "common/io.h" +#include #include #include "common/windows/nt.h" #include @@ -17,7 +18,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; 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..b209b0f7ea 100644 --- a/src/detection/swap/swap_windows.c +++ b/src/detection/swap/swap_windows.c @@ -6,10 +6,11 @@ #include #include #include +#include const char* detectByNqsi(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))) From 359a77097cd2bbb79c744405d60f6a6dbaab266e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 10 Feb 2026 14:11:33 +0800 Subject: [PATCH 17/44] Terminal (Windows): uses `NtQueryInformationProcess` to retrieve information of `conhost.exe` --- .../terminalshell/terminalshell_windows.c | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index 7dff190e38..16aac7d90a 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -8,9 +8,12 @@ #include "common/windows/version.h" #include "common/stringUtils.h" +#include #include #include #include +#include +#include bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); @@ -204,14 +207,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(GetCurrentProcess(), 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; + // 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); From 61cdfcabf8c0a0d4dfadc969c9f03eeffd170d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 10 Feb 2026 14:28:11 +0800 Subject: [PATCH 18/44] Windows: uses `SHGetKnownFolderPath` instead of the unreliable `getenv` --- src/common/impl/FFPlatform_windows.c | 4 +- src/detection/packages/packages_windows.c | 8 +++- .../terminalshell/terminalshell_windows.c | 37 +++++++++++++------ 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/common/impl/FFPlatform_windows.c b/src/common/impl/FFPlatform_windows.c index cd138dc22a..25062ba83f 100644 --- a/src/common/impl/FFPlatform_windows.c +++ b/src/common/impl/FFPlatform_windows.c @@ -70,15 +70,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) diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c index ec4c6e1af7..5c2b639a4e 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -10,6 +10,7 @@ #include #include "common/windows/nt.h" #include +#include static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t* ignore) { @@ -108,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/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index 16aac7d90a..9384b41bbf 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -14,6 +14,7 @@ #include #include #include +#include bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); @@ -189,17 +190,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; } @@ -214,11 +227,11 @@ conhost:; { // 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; + return true; } } From 996cdd31559beb91f24f9031c824f6e02819a88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 10 Feb 2026 18:16:58 +0800 Subject: [PATCH 19/44] Refactor: moves `memrchr` to global impl for potential future use --- CMakeLists.txt | 8 +++++++- src/common/impl/memrchr.c | 19 +++++++++++++++++++ src/common/memrchr.h | 15 +++++++++++++++ src/common/solaris/memrchr.c | 15 --------------- src/common/solaris/memrchr.h | 6 ------ src/detection/gpu/gpu_pci.c | 5 +---- 6 files changed, 42 insertions(+), 26 deletions(-) create mode 100644 src/common/impl/memrchr.c create mode 100644 src/common/memrchr.h delete mode 100644 src/common/solaris/memrchr.c delete mode 100644 src/common/solaris/memrchr.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 58fc2a11d9..6d017ad8c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1115,7 +1115,6 @@ elseif(SunOS) src/detection/displayserver/linux/xcb.c src/detection/displayserver/linux/xlib.c src/detection/font/font_linux.c - src/common/solaris/memrchr.c src/detection/gpu/gpu_sunos.c src/detection/gpu/gpu_pci.c src/detection/gtk_qt/gtk.c @@ -1326,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) @@ -1673,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 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/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/solaris/memrchr.c b/src/common/solaris/memrchr.c deleted file mode 100644 index 0e0c03d991..0000000000 --- a/src/common/solaris/memrchr.c +++ /dev/null @@ -1,15 +0,0 @@ -#if defined(__sun) && ! defined(__illumos__) -#include "memrchr.h" - -void *memrchr(const void *s, int c, size_t n) -{ - if(n == 0) return NULL; - const unsigned char *p = (const unsigned char *)s + n; - while (n--) { - if (*(--p) == (unsigned char) c) - return (void*) p; - } - - return NULL; -} -#endif diff --git a/src/common/solaris/memrchr.h b/src/common/solaris/memrchr.h deleted file mode 100644 index 6e6453d821..0000000000 --- a/src/common/solaris/memrchr.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include - -#if defined(__sun) && !defined(__illumos__) -void *memrchr(const void *s, int c, size_t n); -#endif diff --git a/src/detection/gpu/gpu_pci.c b/src/detection/gpu/gpu_pci.c index 8d66bd9779..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__ @@ -24,10 +25,6 @@ #define FF_STR_INDIR(x) #x #define FF_STR(x) FF_STR_INDIR(x) -#if defined(__sun) && ! defined(__illumos__) -#include "common/solaris/memrchr.h" -#endif - static const FFstrbuf* loadPciIds() { static FFstrbuf pciids; From 5825bce57d996c6b328094dd89b4015f619dffe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 10 Feb 2026 18:50:35 +0800 Subject: [PATCH 20/44] Disk (Windows): replaces most Kernel32 calls to ntdll calls * Improves accurancy of detection of drive create time * Improves performance slightly * Removes remove drive check, `hideFolders` should be used instead --- src/common/windows/nt.h | 30 ++++++-- src/detection/disk/disk_windows.c | 122 ++++++++++++++---------------- 2 files changed, 80 insertions(+), 72 deletions(-) diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h index 2110836ab3..66e3b44762 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,21 @@ 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; diff --git a/src/detection/disk/disk_windows.c b/src/detection/disk/disk_windows.c index e13cec90a4..63aac61992 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(GetCurrentProcess(), 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; From a44bde6f94fc6e303aa90795c85c0552de6fe965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 10 Feb 2026 20:25:50 +0800 Subject: [PATCH 21/44] Platform: adds UID / SID detection --- src/common/FFPlatform.h | 5 +++++ src/common/impl/FFPlatform_unix.c | 3 ++- src/common/impl/FFPlatform_windows.c | 25 ++++++++++++++++++++++-- src/detection/displayserver/linux/wmde.c | 2 +- src/detection/lm/lm_linux.c | 2 +- src/detection/users/users_linux.c | 2 +- src/modules/title/title.c | 6 ++++++ 7 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/common/FFPlatform.h b/src/common/FFPlatform.h index 2962cbd171..e174469ad2 100644 --- a/src/common/FFPlatform.h +++ b/src/common/FFPlatform.h @@ -20,6 +20,11 @@ typedef struct FFPlatform FFlist dataDirs; // List of FFstrbuf, trailing slash included FFstrbuf exePath; // The real path of current exe (empty if unavailable) + #ifndef _WIN32 + uint32_t uid; + #else + FFstrbuf sid; + #endif FFstrbuf userName; FFstrbuf fullUserName; FFstrbuf hostName; diff --git a/src/common/impl/FFPlatform_unix.c b/src/common/impl/FFPlatform_unix.c index 6b25bd537a..5d6ac07b39 100644 --- a/src/common/impl/FFPlatform_unix.c +++ b/src/common/impl/FFPlatform_unix.c @@ -227,7 +227,8 @@ static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts) void ffPlatformInitImpl(FFPlatform* platform) { - struct passwd* pwd = getpwuid(getuid()); + 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 25062ba83f..022a39e35c 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 @@ -148,9 +150,28 @@ static void getUserName(FFPlatform* platform) } 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 = 0; + DWORD refDomainSize = 0; + SID_NAME_USE sidNameUse = SidTypeUnknown; + LookupAccountNameA(NULL, userName, NULL, &size, NULL, &refDomainSize, &sidNameUse); + if (size > 0) + { + FF_AUTO_FREE PSID sid = (PSID) malloc(size); + FF_AUTO_FREE char* refDomain = (char*) malloc(refDomainSize); + if (LookupAccountNameA(NULL, userName, sid, &size, refDomain, &refDomainSize, &sidNameUse)) + { + LPWSTR sidString; + if (ConvertSidToStringSidW(sid, &sidString)) + { + ffStrbufSetWS(&platform->sid, sidString); + LocalFree(sidString); + } + } + } } static void getHostName(FFPlatform* platform) 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/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/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/modules/title/title.c b/src/modules/title/title.c index d846b2817b..d75446e559 100644 --- a/src/modules/title/title.c +++ b/src/modules/title/title.c @@ -121,6 +121,11 @@ 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); @@ -169,5 +174,6 @@ FFModuleBaseInfo ffTitleModuleInfo = { {"@ symbol (colored)", "at-symbol-colored"}, {"Host name (colored)", "host-name-colored"}, {"Full user name", "full-user-name"}, + {"UID / SID", "user-id"}, })) }; From 357aa77e2183adc2230a2976ade81a3ef4624ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 10 Feb 2026 22:42:09 +0800 Subject: [PATCH 22/44] Media (Solaris): fixes another compiling error Fixes #2173 --- src/detection/media/media_linux.c | 1 + 1 file changed, 1 insertion(+) 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 From b248dfa01ce2d6de2ed159788f7564f925a9fd92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 10 Feb 2026 23:42:57 +0800 Subject: [PATCH 23/44] Platform: prefers system API to retreive user name --- src/common/impl/FFPlatform_unix.c | 13 ++++----- src/common/impl/FFPlatform_windows.c | 43 +++++++++++++--------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/common/impl/FFPlatform_unix.c b/src/common/impl/FFPlatform_unix.c index 5d6ac07b39..3ca836fb63 100644 --- a/src/common/impl/FFPlatform_unix.c +++ b/src/common/impl/FFPlatform_unix.c @@ -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) diff --git a/src/common/impl/FFPlatform_windows.c b/src/common/impl/FFPlatform_windows.c index 022a39e35c..58c6e8de14 100644 --- a/src/common/impl/FFPlatform_windows.c +++ b/src/common/impl/FFPlatform_windows.c @@ -138,40 +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 size = ARRAY_SIZE(buffer); if (GetUserNameExW(NameDisplay, buffer, &size)) ffStrbufSetWS(&platform->fullUserName, buffer); - size = 0; - DWORD refDomainSize = 0; - SID_NAME_USE sidNameUse = SidTypeUnknown; - LookupAccountNameA(NULL, userName, NULL, &size, NULL, &refDomainSize, &sidNameUse); - if (size > 0) + size = ARRAY_SIZE(buffer); + if (GetUserNameW(buffer, &size)) // GetUserNameExW(10002)? { - FF_AUTO_FREE PSID sid = (PSID) malloc(size); - FF_AUTO_FREE char* refDomain = (char*) malloc(refDomainSize); - if (LookupAccountNameA(NULL, userName, sid, &size, refDomain, &refDomainSize, &sidNameUse)) + 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) { - LPWSTR sidString; - if (ConvertSidToStringSidW(sid, &sidString)) + FF_AUTO_FREE PSID sid = (PSID) malloc(size); + FF_AUTO_FREE LPWSTR refDomain = (LPWSTR) malloc(refDomainSize); + if (LookupAccountNameW(NULL, buffer, sid, &size, refDomain, &refDomainSize, &sidNameUse)) { - ffStrbufSetWS(&platform->sid, sidString); - LocalFree(sidString); + LPWSTR sidString; + if (ConvertSidToStringSidW(sid, &sidString)) + { + ffStrbufSetWS(&platform->sid, sidString); + LocalFree(sidString); + } } } } + else + ffStrbufSetS(&platform->userName, getenv("USERNAME")); } static void getHostName(FFPlatform* platform) From 5847e3f26904065b44351ac971f35624d8de96a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 11 Feb 2026 10:03:29 +0800 Subject: [PATCH 24/44] FFPlatform (Windows): fixes memory corruption --- src/common/impl/FFPlatform_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/impl/FFPlatform_windows.c b/src/common/impl/FFPlatform_windows.c index 58c6e8de14..259491caed 100644 --- a/src/common/impl/FFPlatform_windows.c +++ b/src/common/impl/FFPlatform_windows.c @@ -155,7 +155,7 @@ static void getUserName(FFPlatform* platform) if (size > 0) { FF_AUTO_FREE PSID sid = (PSID) malloc(size); - FF_AUTO_FREE LPWSTR refDomain = (LPWSTR) malloc(refDomainSize); + FF_AUTO_FREE LPWSTR refDomain = (LPWSTR) malloc(refDomainSize * sizeof(wchar_t)); if (LookupAccountNameW(NULL, buffer, sid, &size, refDomain, &refDomainSize, &sidNameUse)) { LPWSTR sidString; From cbe33a48603bae4fc7281fb42ad3e181ff6419c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 11 Feb 2026 14:58:30 +0800 Subject: [PATCH 25/44] CI (DragonFly): disables packaging `.pkg` file to fix a weird CI error --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d017ad8c4..2ba02048a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2111,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() From ba27f4f2ef6f584a23ef21cde9a239a413855182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 11 Feb 2026 10:34:30 +0800 Subject: [PATCH 26/44] CI: adds Solaris --- .github/workflows/ci.yml | 53 ++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 646bd67d45..0d46ae320f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -433,9 +433,9 @@ jobs: 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@v6 @@ -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@v6 with: - name: fastfetch-sunos-amd64 + name: fastfetch-solaris-amd64 path: ./fastfetch-*.* freebsd-amd64: @@ -772,7 +808,8 @@ jobs: - openbsd-amd64 - netbsd-amd64 - dragonfly-amd64 - - sunos-amd64 + - solaris-amd64 + - omnios-amd64 - haiku-amd64 - windows-hosts permissions: From 681a102a2b428804513c5ba2b9c65124a195d313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 11 Feb 2026 21:02:14 +0800 Subject: [PATCH 27/44] Processing (Windows): adds TODO [ci skip] --- src/common/impl/processing_windows.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/impl/processing_windows.c b/src/common/impl/processing_windows.c index 1c63013ead..f07344cda1 100644 --- a/src/common/impl/processing_windows.c +++ b/src/common/impl/processing_windows.c @@ -236,6 +236,8 @@ bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFst } if(exe) { + // 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))) From ff7e81ed81e608d864d81d5dabebe8136a3c4ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 11 Feb 2026 21:17:36 +0800 Subject: [PATCH 28/44] Memory (Windows): adds comments [ci skip] --- src/detection/memory/memory_windows.c | 3 +++ 1 file changed, 3 insertions(+) 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"; From 19f7c67ede2453339e4c7f2818d89f6ef5b58bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 12 Feb 2026 13:56:55 +0800 Subject: [PATCH 29/44] Platform: adds `pid` field and use it --- src/common/FFPlatform.h | 1 + src/common/impl/FFPlatform_unix.c | 7 ++++--- src/common/impl/FFPlatform_windows.c | 1 + src/common/impl/netif_apple.c | 4 ++-- src/common/impl/netif_linux.c | 4 ++-- src/common/impl/processing_windows.c | 2 +- src/modules/title/title.c | 3 +++ 7 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/common/FFPlatform.h b/src/common/FFPlatform.h index e174469ad2..c714b5dda8 100644 --- a/src/common/FFPlatform.h +++ b/src/common/FFPlatform.h @@ -20,6 +20,7 @@ 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 diff --git a/src/common/impl/FFPlatform_unix.c b/src/common/impl/FFPlatform_unix.c index 3ca836fb63..25c3f86a09 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(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, platform->pid #else - KERN_PROC_ARGS, (int) getpid(), KERN_PROC_PATHNAME + KERN_PROC_ARGS, platform->pid, KERN_PROC_PATHNAME #endif }, 4, exePath, &exePathLen, @@ -226,6 +226,7 @@ static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts) void ffPlatformInitImpl(FFPlatform* platform) { + platform->pid = (uint32_t) getpid(); platform->uid = getuid(); struct passwd* pwd = getpwuid(platform->uid); diff --git a/src/common/impl/FFPlatform_windows.c b/src/common/impl/FFPlatform_windows.c index 259491caed..c09cdc2770 100644 --- a/src/common/impl/FFPlatform_windows.c +++ b/src/common/impl/FFPlatform_windows.c @@ -307,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/netif_apple.c b/src/common/impl/netif_apple.c index 54128ec9cc..64b1e99f12 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; @@ -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; diff --git a/src/common/impl/netif_linux.c b/src/common/impl/netif_linux.c index 03ade7d9d1..6e7631e7ae 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(); + int 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 f07344cda1..ebfe5d82b1 100644 --- a/src/common/impl/processing_windows.c +++ b/src/common/impl/processing_windows.c @@ -53,7 +53,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, diff --git a/src/modules/title/title.c b/src/modules/title/title.c index d75446e559..4e8f586d43 100644 --- a/src/modules/title/title.c +++ b/src/modules/title/title.c @@ -64,6 +64,7 @@ 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"), + FF_FORMAT_ARG(instance.state.platform.pid, "pid"), })); } @@ -132,6 +133,7 @@ bool ffGenerateTitleJsonResult(FF_MAYBE_UNUSED FFTitleOptions* options, yyjson_m 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; } @@ -175,5 +177,6 @@ FFModuleBaseInfo ffTitleModuleInfo = { {"Host name (colored)", "host-name-colored"}, {"Full user name", "full-user-name"}, {"UID / SID", "user-id"}, + {"Fastfetch PID", "pid"}, })) }; From 0f4bb47c2fdbf2ec389bd1567496e14e03de2944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 12 Feb 2026 14:08:47 +0800 Subject: [PATCH 30/44] Global: fixes compiler warnings --- src/common/apple/smc_temps.c | 4 ++-- src/common/impl/FFPlatform_unix.c | 2 +- src/common/impl/netif_apple.c | 8 ++++---- src/common/impl/netif_linux.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) 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/impl/FFPlatform_unix.c b/src/common/impl/FFPlatform_unix.c index 25c3f86a09..6431dc86ce 100644 --- a/src/common/impl/FFPlatform_unix.c +++ b/src/common/impl/FFPlatform_unix.c @@ -28,7 +28,7 @@ static void getExePath(FFPlatform* platform) if (exePathLen >= 0) exePath[exePathLen] = '\0'; #elif defined(__APPLE__) - int exePathLen = proc_pidpath(platform->pid, 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( diff --git a/src/common/impl/netif_apple.c b/src/common/impl/netif_apple.c index 64b1e99f12..346ce89373 100644 --- a/src/common/impl/netif_apple.c +++ b/src/common/impl/netif_apple.c @@ -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 @@ -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 6e7631e7ae..092d919fb6 100644 --- a/src/common/impl/netif_linux.c +++ b/src/common/impl/netif_linux.c @@ -235,7 +235,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result) } FF_DEBUG("Created netlink socket: fd=%d", sock_fd); - int pid = instance.state.platform.pid; + uint32_t pid = instance.state.platform.pid; FF_DEBUG("Process PID: %u", pid); // Bind socket From 4038ed265627e177909e72ab1a1fab0aacc39754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 12 Feb 2026 18:30:02 +0800 Subject: [PATCH 31/44] GPU: adds debug logs Thanks Github Copilot --- src/detection/gpu/gpu.c | 52 +++++++++++++++++++- src/detection/opengl/opengl_shared.c | 73 +++++++++++++++++++++++++++- src/detection/vulkan/vulkan.c | 71 +++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 3 deletions(-) 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/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/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; } From a86d0017a978e5aa7589c63f7dd6cc366bff2ae0 Mon Sep 17 00:00:00 2001 From: Konnor Klercke Date: Thu, 12 Feb 2026 19:58:48 -0500 Subject: [PATCH 32/44] Logo (Builtin): adds 6-color support to nixos logo (#2180) Co-authored-by: Konnor Klercke --- src/logo/ascii/nixos.txt | 40 ++++++++++++++++++++-------------------- src/logo/builtin.c | 4 ++++ 2 files changed, 24 insertions(+), 20 deletions(-) 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/builtin.c b/src/logo/builtin.c index 7b4b366341..44633621c5 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 From 6c314e148a4f66e3ea4bfcb2010660ed5dfa09eb Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 13 Feb 2026 09:09:05 +0800 Subject: [PATCH 33/44] Logo (Builtin): updates nixos_small too Ref #2180 --- src/logo/ascii/nixos_small.txt | 12 ++++++------ src/logo/builtin.c | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) 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 44633621c5..c2f220778c 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -3319,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 From fc5c2f6213e97521521f20715055086d3208a446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 13 Feb 2026 09:38:22 +0800 Subject: [PATCH 34/44] Title: adds `{user-id}` in custom format --- src/modules/title/title.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/modules/title/title.c b/src/modules/title/title.c index 4e8f586d43..b3948429a3 100644 --- a/src/modules/title/title.c +++ b/src/modules/title/title.c @@ -64,6 +64,11 @@ 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"), })); } @@ -176,7 +181,7 @@ FFModuleBaseInfo ffTitleModuleInfo = { {"@ symbol (colored)", "at-symbol-colored"}, {"Host name (colored)", "host-name-colored"}, {"Full user name", "full-user-name"}, - {"UID / SID", "user-id"}, - {"Fastfetch PID", "pid"}, + {"UID (*nix) / SID (Windows)", "user-id"}, + {"PID of current process", "pid"}, })) }; From 8a20af99176d90babb97a18c8c7e0baa79beaf91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 13 Feb 2026 10:02:28 +0800 Subject: [PATCH 35/44] Doc: update JSON schema --- doc/json_schema.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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" } }, From f31d3b8b211576fde19ecf9e2f9378bc3f73eb5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 13 Feb 2026 10:04:54 +0800 Subject: [PATCH 36/44] Doc: update changelog --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) 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. From 2afe8d11d12c60370428d996f24211cb72412a64 Mon Sep 17 00:00:00 2001 From: Dark Knightz <19180457+fam007e@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:05:07 +0600 Subject: [PATCH 37/44] Common: fixes discarded-qualifiers warnings (#2181) --- src/common/impl/FFstrbuf.c | 2 +- src/common/impl/io_unix.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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); From d04a26337ababf8580ed5d73a57409e75843afb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 13 Feb 2026 15:38:09 +0800 Subject: [PATCH 38/44] Swap (Windows): removes unused code --- src/detection/swap/swap_windows.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/detection/swap/swap_windows.c b/src/detection/swap/swap_windows.c index b209b0f7ea..0909267ecf 100644 --- a/src/detection/swap/swap_windows.c +++ b/src/detection/swap/swap_windows.c @@ -5,10 +5,9 @@ #include #include #include -#include #include -const char* detectByNqsi(FFlist* result) +const char* ffDetectSwap(FFlist* result) { alignas(SYSTEM_PAGEFILE_INFORMATION) uint8_t buffer[4096]; ULONG size = sizeof(buffer); @@ -30,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); -} From dee4b36a465c483dec1e15108d07ed1de4da0d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 13 Feb 2026 15:39:07 +0800 Subject: [PATCH 39/44] Bluetooth (Windows): use modern dlls --- src/detection/bluetooth/bluetooth_windows.c | 8 +++++++- src/detection/bluetoothradio/bluetoothradio_windows.c | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) 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/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) From df87cdfa6ef2abcbe5b63a1f37d36d1df65b28d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 13 Feb 2026 16:18:24 +0800 Subject: [PATCH 40/44] Windows: defines `NtCurrentProcess` and uses it --- src/common/impl/processing_windows.c | 3 ++- src/common/windows/nt.h | 4 ++++ src/detection/bootmgr/bootmgr_windows.c | 3 ++- src/detection/disk/disk_windows.c | 2 +- src/detection/terminalshell/terminalshell_windows.c | 3 ++- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/common/impl/processing_windows.c b/src/common/impl/processing_windows.c index ebfe5d82b1..f0dc096cd1 100644 --- a/src/common/impl/processing_windows.c +++ b/src/common/impl/processing_windows.c @@ -2,6 +2,7 @@ #include "common/processing.h" #include "common/io.h" #include "common/windows/unicode.h" +#include "common/windows/nt.h" #include #include @@ -213,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) diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h index 66e3b44762..b4d83d66aa 100644 --- a/src/common/windows/nt.h +++ b/src/common/windows/nt.h @@ -266,3 +266,7 @@ typedef struct _PROCESS_DEVICEMAP_INFORMATION_EX }; 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/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/disk/disk_windows.c b/src/detection/disk/disk_windows.c index 63aac61992..47cd3f504e 100644 --- a/src/detection/disk/disk_windows.c +++ b/src/detection/disk/disk_windows.c @@ -12,7 +12,7 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks) { PROCESS_DEVICEMAP_INFORMATION_EX info = {}; ULONG size = 0; - if(!NT_SUCCESS(NtQueryInformationProcess(GetCurrentProcess(), ProcessDeviceMap, &info, sizeof(info), &size))) + if(!NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(), ProcessDeviceMap, &info, sizeof(info), &size))) return "NtQueryInformationProcess(ProcessDeviceMap) failed"; // For cross-platform portability; used by `presets/examples/13.jsonc` diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index 9384b41bbf..b412d5800f 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -6,6 +6,7 @@ #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 @@ -223,7 +224,7 @@ static bool detectDefaultTerminal(FFTerminalResult* result) conhost:; ULONG_PTR conhostPid = 0; ULONG size; - if(NT_SUCCESS(NtQueryInformationProcess(GetCurrentProcess(), ProcessConsoleHostProcess, &conhostPid, sizeof(conhostPid), &size)) && conhostPid != 0) + if(NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(), ProcessConsoleHostProcess, &conhostPid, sizeof(conhostPid), &size)) && conhostPid != 0) { // For Windows Terminal, it reports the PID of OpenConsole if(ffProcessGetInfoWindows((uint32_t) conhostPid, NULL, &result->processName, &result->exe, &result->exeName, &result->exePath, NULL)) From 4f6e80e9402daf26d5aa079a3ac96387b30b9d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 13 Feb 2026 19:39:33 +0800 Subject: [PATCH 41/44] Release: v2.59.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ba02048a1..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" From 0e9a5acdbdc936c021d7eac39be99af47fc1bd9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 13 Feb 2026 20:42:35 +0800 Subject: [PATCH 42/44] Platform (Windows): adds init / destroy of field `sid` --- src/common/impl/FFPlatform.c | 8 ++++++++ 1 file changed, 8 insertions(+) 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); From 49ef7fc51fbc21442652f4dbb87eae41db0d09dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 13 Feb 2026 20:50:45 +0800 Subject: [PATCH 43/44] Packages (Windows): code cleanups --- src/detection/packages/packages_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c index 5c2b639a4e..1ec5cb07ab 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -78,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; From 6e64b758bf9c82ca3f2b7bd5ed3f503eeb5c7753 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 13 Feb 2026 23:37:40 +0800 Subject: [PATCH 44/44] Chore: silences a compiler warning --- src/common/impl/FFPlatform_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/impl/FFPlatform_unix.c b/src/common/impl/FFPlatform_unix.c index 6431dc86ce..985494bf9e 100644 --- a/src/common/impl/FFPlatform_unix.c +++ b/src/common/impl/FFPlatform_unix.c @@ -34,7 +34,7 @@ static void getExePath(FFPlatform* platform) if(sysctl( (int[]){CTL_KERN, #ifdef __FreeBSD__ - KERN_PROC, KERN_PROC_PATHNAME, platform->pid + KERN_PROC, KERN_PROC_PATHNAME, (pid_t) platform->pid #else KERN_PROC_ARGS, platform->pid, KERN_PROC_PATHNAME #endif