From c324f838262426252d44e6b87d8352c5949ad427 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Mon, 8 Dec 2025 09:03:28 +0800 Subject: [PATCH 01/35] Logo (Builtin): drop opak Ref: #1070 --- src/logo/ascii/opak.txt | 13 ------------- src/logo/builtin.c | 6 ------ 2 files changed, 19 deletions(-) delete mode 100644 src/logo/ascii/opak.txt diff --git a/src/logo/ascii/opak.txt b/src/logo/ascii/opak.txt deleted file mode 100644 index 0f07a3e137..0000000000 --- a/src/logo/ascii/opak.txt +++ /dev/null @@ -1,13 +0,0 @@ - .':ldkOOOOkxo:,. - .:d0KKXXXXXXXXXXKkl, - .;xOOO0000KKKKXXXXXXX0l. - .cxxxkkkkOOO0000KKKKXXXXx' - ;ool;,,,,;coxkOOOO0000KKKd. -.clc'.........:oxkkkkOOOO0O, -.ccc:ccclc:,....,:oddxxdxkO; - cllllccccccc;'........'lxx. - 'ooooolllllccc:;,''',,coo, - ,ddddooooollllllccccccl' - cxxxdddddooooollllc; - ,dxxxxxddddddol. - .';::,. \ No newline at end of file diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 54455cf904..ca9fca505c 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -3455,12 +3455,6 @@ static const FFlogo O[] = { FF_COLOR_FG_LIGHT_BLACK, } }, - // Opak - { - .names = {"Opak"}, - .lines = FASTFETCH_DATATEXT_LOGO_OPAK, - .colors = {}, // #1070 - }, // OpenKylin { .names = {"openkylin", "open-kylin"}, From c613e558fdc95ede72abb78ab116e421bbce5e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 18 Nov 2025 15:36:49 +0800 Subject: [PATCH 02/35] Swap (Windows): adds `K32GetPerformanceInfo` fallback --- src/detection/swap/swap_windows.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/detection/swap/swap_windows.c b/src/detection/swap/swap_windows.c index ad49658366..1bbb8098c5 100644 --- a/src/detection/swap/swap_windows.c +++ b/src/detection/swap/swap_windows.c @@ -5,8 +5,9 @@ #include #include #include +#include -const char* ffDetectSwap(FFlist* result) +const char* detectByNqsi(FFlist* result) { uint8_t buffer[4096]; ULONG size = sizeof(buffer); @@ -26,6 +27,27 @@ const char* ffDetectSwap(FFlist* result) if (current->NextEntryOffset == 0) break; } + return NULL; +} + +const char* detectByKgpi(FFlist* result) +{ + PERFORMANCE_INFORMATION pi = {}; + if (!K32GetPerformanceInfo(&pi, sizeof(pi))) + return "K32GetPerformanceInfo(&pi, sizeof(pi)) failed"; + FFSwapResult* swap = ffListAdd(result); + ffStrbufInitS(&swap->name, "Page File"); + swap->bytesTotal = (uint64_t) (pi.CommitLimit > pi.PhysicalTotal ? pi.CommitLimit - pi.PhysicalTotal : 0) * pi.PageSize; + swap->bytesUsed = (uint64_t) (pi.CommitTotal > pi.PhysicalTotal ? pi.CommitTotal - pi.PhysicalTotal : 0) * pi.PageSize; return NULL; } + +const char* ffDetectSwap(FFlist* result) +{ + const char* err = detectByNqsi(result); + if (err == NULL) + return NULL; + + return detectByKgpi(result); +} From 98fb810f1fea7c39665a5867dafc63678e926297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 3 Dec 2025 14:40:47 +0800 Subject: [PATCH 03/35] Platform (Windows): fixes if checks --- src/util/platform/FFPlatform_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/platform/FFPlatform_windows.c b/src/util/platform/FFPlatform_windows.c index 753249df8c..07f30e2c78 100644 --- a/src/util/platform/FFPlatform_windows.c +++ b/src/util/platform/FFPlatform_windows.c @@ -16,7 +16,7 @@ static void getExePath(FFPlatform* platform) { wchar_t exePathW[MAX_PATH]; DWORD exePathWLen = GetModuleFileNameW(NULL, exePathW, MAX_PATH); - if (exePathWLen == 0 && exePathWLen >= MAX_PATH) return; + if (exePathWLen == 0 || exePathWLen >= MAX_PATH) return; FF_AUTO_CLOSE_FD HANDLE hPath = CreateFileW(exePathW, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hPath != INVALID_HANDLE_VALUE) From 100cd9a72beefb01460cf462699df18b193f2d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 3 Dec 2025 14:41:19 +0800 Subject: [PATCH 04/35] Platform (Windows): adds a null check --- src/util/platform/FFPlatform_windows.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/util/platform/FFPlatform_windows.c b/src/util/platform/FFPlatform_windows.c index 07f30e2c78..a916de4fe6 100644 --- a/src/util/platform/FFPlatform_windows.c +++ b/src/util/platform/FFPlatform_windows.c @@ -164,8 +164,12 @@ static void getHostName(FFPlatform* platform) static void getUserShell(FFPlatform* platform) { // Works in MSYS2 - ffStrbufAppendS(&platform->userShell, getenv("SHELL")); - ffStrbufReplaceAllC(&platform->userShell, '\\', '/'); + const char* userShell = getenv("SHELL"); + if (userShell) + { + ffStrbufAppendS(&platform->userShell, userShell); + ffStrbufReplaceAllC(&platform->userShell, '\\', '/'); + } } static const char* detectWine(void) From 0b6ca158eae90acb9af984feae5a56e529ffca15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 8 Dec 2025 16:42:14 +0800 Subject: [PATCH 05/35] Packages (Windows): improves performance for directory enumeration --- src/detection/packages/packages_windows.c | 74 ++++++++++++++++------- src/util/windows/nt.h | 16 +++++ 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c index 019b7629d1..ee9abd46f9 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -2,35 +2,63 @@ #include "common/processing.h" #include "util/stringUtils.h" #include "util/path.h" +#include "util/windows/unicode.h" +#include "util/mallocHelper.h" +#include "common/io/io.h" -#include -#include +#include +#include "util/windows/nt.h" +#include -static uint32_t getNumElements(const char* searchPath /* including `\*` suffix */, DWORD type, const char* ignore) +static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t* ignore) { - uint32_t counter = 0; - bool flag = ignore == NULL; - WIN32_FIND_DATAA wfd; - HANDLE hFind = FindFirstFileA(searchPath, &wfd); + FF_AUTO_CLOSE_FD HANDLE dfd = CreateFileA(searchPath, FILE_LIST_DIRECTORY | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (dfd == INVALID_HANDLE_VALUE) return 0; - if (hFind != INVALID_HANDLE_VALUE) - { - do // Managed to locate and create an handle to that folder. + bool flag = ignore == NULL; + uint32_t counter = 0; + uint8_t buffer[64 * 1024] __attribute__((aligned(8))); // Required for WoA + BOOLEAN firstScan = TRUE; + + while (true) { + IO_STATUS_BLOCK ioStatus = {}; + NTSTATUS status = NtQueryDirectoryFile( + dfd, + NULL, NULL, NULL, + &ioStatus, + buffer, ARRAY_SIZE(buffer), + FileDirectoryInformation, + FALSE, + NULL, + firstScan + ); + firstScan = FALSE; + + if (!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW) break; + + for (FILE_DIRECTORY_INFORMATION* entry = (FILE_DIRECTORY_INFORMATION*) buffer; + ; + entry = (FILE_DIRECTORY_INFORMATION*) ((uint8_t*) entry + entry->NextEntryOffset)) { - if(!(wfd.dwFileAttributes & type)) continue; - if(!flag && ffStrEqualsIgnCase(ignore, wfd.cFileName)) + if (!(entry->FileAttributes & type)) continue; + + if (!flag && _wcsicmp(entry->FileName, ignore) == 0) { flag = true; continue; } + counter++; - } while (FindNextFileA(hFind, &wfd)); - FindClose(hFind); - if(type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2) - counter -= 2; // accounting for . and .. + if (entry->NextEntryOffset == 0) break; + } + + if (status == STATUS_SUCCESS) break; // No next page } + if(type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2) + counter -= 2; // accounting for . and .. + return counter; } @@ -65,8 +93,8 @@ static void detectScoop(FFPackagesResult* result) ffStrbufSet(&scoopPath, &instance.state.platform.homeDir); ffStrbufAppendS(&scoopPath, "/scoop"); } - ffStrbufAppendS(&scoopPath, "/apps/*"); - result->scoopUser = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, "scoop"); + ffStrbufAppendS(&scoopPath, "/apps/"); + result->scoopUser = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, L"scoop"); } { @@ -78,8 +106,8 @@ static void detectScoop(FFPackagesResult* result) ffStrbufSetS(&scoopPath, getenv("ProgramData")); ffStrbufAppendS(&scoopPath, "/scoop"); } - ffStrbufAppendS(&scoopPath, "/apps/*"); - result->scoopGlobal = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, "scoop"); + ffStrbufAppendS(&scoopPath, "/apps/"); + result->scoopGlobal = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, L"scoop"); } } @@ -91,8 +119,8 @@ static void detectChoco(FF_MAYBE_UNUSED FFPackagesResult* result) char chocoPath[MAX_PATH + 3]; char* pend = ffStrCopy(chocoPath, chocoInstall, ARRAY_SIZE(chocoPath)); - ffStrCopy(pend, "/lib/*", ARRAY_SIZE(chocoPath) - (size_t) (pend - chocoPath)); - result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY, "choco"); + ffStrCopy(pend, "/lib/", ARRAY_SIZE(chocoPath) - (size_t) (pend - chocoPath)); + result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY, L"choco"); } static void detectPacman(FFPackagesResult* result) @@ -104,7 +132,7 @@ static void detectPacman(FFPackagesResult* result) // MSYS2 char pacmanPath[MAX_PATH + 3]; char* pend = ffStrCopy(pacmanPath, msystemPrefix, ARRAY_SIZE(pacmanPath)); - ffStrCopy(pend, "/../var/lib/pacman/local/*", ARRAY_SIZE(pacmanPath) - (size_t) (pend - pacmanPath)); + ffStrCopy(pend, "/../var/lib/pacman/local/", ARRAY_SIZE(pacmanPath) - (size_t) (pend - pacmanPath)); result->pacman = getNumElements(pacmanPath, FILE_ATTRIBUTE_DIRECTORY, NULL); } diff --git a/src/util/windows/nt.h b/src/util/windows/nt.h index a33870796a..8d6af4b7e2 100644 --- a/src/util/windows/nt.h +++ b/src/util/windows/nt.h @@ -232,3 +232,19 @@ typedef struct _D3DKMT_NODEMETADATA static_assert(sizeof(D3DKMT_NODEMETADATA) == 0x4E, "D3DKMT_NODEMETADATA structure size mismatch"); #endif + +NTSYSCALLAPI +NTSTATUS +NTAPI +NtQueryDirectoryFile( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG Length, + IN FILE_INFORMATION_CLASS FileInformationClass, + IN BOOLEAN ReturnSingleEntry, + IN PUNICODE_STRING FileName OPTIONAL, + IN BOOLEAN RestartScan); From 7a322154fa1b5c8e8417e5c286e83e3e84648735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 8 Dec 2025 20:11:14 +0800 Subject: [PATCH 06/35] Util (Windows): fixes / adds a new Unicode conversion function --- src/util/windows/unicode.c | 17 ++++++++++++++++- src/util/windows/unicode.h | 7 +++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/util/windows/unicode.c b/src/util/windows/unicode.c index a6d758ff3d..0f78a7281f 100644 --- a/src/util/windows/unicode.c +++ b/src/util/windows/unicode.c @@ -11,7 +11,7 @@ void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) } int size_needed = WideCharToMultiByte(CP_UTF8, 0, source, (int)length, NULL, 0, NULL, NULL); - if (size_needed < 0) + if (size_needed <= 0) { ffStrbufSetF(result, "WCTMB failed: %u", (unsigned) GetLastError()); return; @@ -21,3 +21,18 @@ void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) result->length = (uint32_t)size_needed; result->chars[size_needed] = '\0'; } + +void ffStrbufAppendNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) +{ + if(!length) + return; + + int size_needed = WideCharToMultiByte(CP_UTF8, 0, source, (int)length, NULL, 0, NULL, NULL); + if (size_needed <= 0) + return; + + ffStrbufEnsureFree(result, (uint32_t)size_needed); + WideCharToMultiByte(CP_UTF8, 0, source, (int)length, result->chars + result->length, size_needed, NULL, NULL); + result->length += (uint32_t)size_needed; + result->chars[result->length] = '\0'; +} diff --git a/src/util/windows/unicode.h b/src/util/windows/unicode.h index b34c3ffca7..9c7cfc01d4 100644 --- a/src/util/windows/unicode.h +++ b/src/util/windows/unicode.h @@ -4,6 +4,7 @@ #include void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); +void ffStrbufAppendNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); static inline void ffStrbufSetWS(FFstrbuf* result, const wchar_t* source) { @@ -11,6 +12,12 @@ static inline void ffStrbufSetWS(FFstrbuf* result, const wchar_t* source) return ffStrbufSetNWS(result, (uint32_t)wcslen(source), source); } +static inline void ffStrbufAppendWS(FFstrbuf* result, const wchar_t* source) +{ + if (!source) return; + return ffStrbufAppendNWS(result, (uint32_t)wcslen(source), source); +} + static inline void ffStrbufInitNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) { ffStrbufInit(result); From b952772902e0ac47d7178d6c1ee36ca55a072d67 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 10 Dec 2025 15:55:01 +0800 Subject: [PATCH 07/35] 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 a2cc347fdb..f0e62b4f50 100644 --- a/debian/changelog.tpl +++ b/debian/changelog.tpl @@ -1,3 +1,9 @@ +fastfetch (2.56.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium + + * Update to 2.56.0 + + -- Carter Li Mon, 08 Dec 2025 09:21:58 +0800 + fastfetch (2.55.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium * Update to 2.55.1 From ba6cdc77c9c8a9ea784f7599c741454b0142d60f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 9 Dec 2025 14:37:57 +0800 Subject: [PATCH 08/35] IO (Windows): enhances `openat` --- src/common/io/io.h | 1 + src/common/io/io_windows.c | 55 +++++++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/common/io/io.h b/src/common/io/io.h index 8787f98580..d9c24c3516 100644 --- a/src/common/io/io.h +++ b/src/common/io/io.h @@ -254,4 +254,5 @@ bool ffRemoveFile(const char* fileName); #ifdef _WIN32 // Only O_RDONLY is supported HANDLE openat(HANDLE dfd, const char* fileName, bool directory); +HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory); #endif diff --git a/src/common/io/io_windows.c b/src/common/io/io_windows.c index ae1d584e9a..8724d0ee92 100644 --- a/src/common/io/io_windows.c +++ b/src/common/io/io_windows.c @@ -3,8 +3,8 @@ #include "util/stringUtils.h" #include +#include "util/windows/nt.h" #include -#include static void createSubfolders(const char* fileName) { @@ -102,31 +102,48 @@ bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer) return ffAppendFDBuffer(handle, buffer); } -HANDLE openat(HANDLE dfd, const char* fileName, bool directory) +HANDLE openatW(HANDLE dfd, const wchar_t* fileName, uint16_t fileNameLen, bool directory) { - NTSTATUS ret; - UNICODE_STRING fileNameW; - ret = RtlAnsiStringToUnicodeString(&fileNameW, &(ANSI_STRING) { - .Length = (USHORT) strlen(fileName), - .Buffer = (PCHAR) fileName - }, TRUE); - if (!NT_SUCCESS(ret)) return INVALID_HANDLE_VALUE; - - FF_AUTO_CLOSE_FD HANDLE hFile; + assert(fileNameLen <= 0x7FFF); + + HANDLE hFile; IO_STATUS_BLOCK iosb = {}; - ret = NtOpenFile(&hFile, FILE_READ_DATA | SYNCHRONIZE, &(OBJECT_ATTRIBUTES) { - .Length = sizeof(OBJECT_ATTRIBUTES), - .RootDirectory = dfd, - .ObjectName = &fileNameW, - }, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT | (directory ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE)); - RtlFreeUnicodeString(&fileNameW); - - if(!NT_SUCCESS(ret) || iosb.Information != FILE_OPENED) + if(!NT_SUCCESS(NtOpenFile(&hFile, + (directory ? FILE_LIST_DIRECTORY | FILE_TRAVERSE : FILE_READ_DATA | FILE_READ_EA) | FILE_READ_ATTRIBUTES | SYNCHRONIZE, &(OBJECT_ATTRIBUTES) { + .Length = sizeof(OBJECT_ATTRIBUTES), + .RootDirectory = dfd, + .ObjectName = &(UNICODE_STRING) { + .Buffer = (PWSTR) fileName, + .Length = fileNameLen * (USHORT) sizeof(wchar_t), + .MaximumLength = (fileNameLen + 1) * (USHORT) sizeof(wchar_t), + }, + .Attributes = OBJ_CASE_INSENSITIVE, + }, + &iosb, + FILE_SHARE_READ | (directory ? FILE_SHARE_WRITE | FILE_SHARE_DELETE : 0), + FILE_SYNCHRONOUS_IO_NONALERT | (directory ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE) + ))) return INVALID_HANDLE_VALUE; return hFile; } +HANDLE openat(HANDLE dfd, const char* fileName, bool directory) +{ + wchar_t fileNameW[MAX_PATH]; + int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, fileName, -1, fileNameW, ARRAY_SIZE(fileNameW)); + if (len == 0) return INVALID_HANDLE_VALUE; + fileNameW[len] = L'\0'; + + for (int i = 0; i < len - 1; ++i) + { + if (fileNameW[i] == L'/') + fileNameW[i] = L'\\'; + } + + return openatW(dfd, fileNameW, (uint16_t)(len - 1), directory); +} + bool ffAppendFileBufferRelative(HANDLE dfd, const char* fileName, FFstrbuf* buffer) { HANDLE FF_AUTO_CLOSE_FD fd = openat(dfd, fileName, false); From 88b1b558d70adaa11dde9095cc146722b9cb7b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 10 Dec 2025 14:15:11 +0800 Subject: [PATCH 09/35] CPU: adds option `tempSensor` --- src/modules/cpu/cpu.c | 8 ++++++++ src/modules/cpu/option.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index 2fe858519b..1e2e753787 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -134,6 +134,12 @@ void ffParseCPUJsonObject(FFCPUOptions* options, yyjson_val* module) if (ffTempsParseJsonObject(key, val, &options->temp, &options->tempConfig)) continue; + if (unsafe_yyjson_equals_str(key, "tempSensor")) + { + ffStrbufSetS(&options->tempSensor, unsafe_yyjson_get_str(val)); + continue; + } + if (unsafe_yyjson_equals_str(key, "freqNdigits")) { ffPrintError(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "modules.CPU.freqNdigits has been moved to display.freq.ndigits"); @@ -234,6 +240,7 @@ bool ffGenerateCPUJsonResult(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_ void ffInitCPUOptions(FFCPUOptions* options) { ffOptionInitModuleArg(&options->moduleArgs, ""); + ffStrbufInit(&options->tempSensor); options->temp = false; options->tempConfig = (FFColorRangeConfig) { 60, 80 }; options->showPeCoreCount = false; @@ -242,6 +249,7 @@ void ffInitCPUOptions(FFCPUOptions* options) void ffDestroyCPUOptions(FFCPUOptions* options) { ffOptionDestroyModuleArg(&options->moduleArgs); + ffStrbufDestroy(&options->tempSensor); } FFModuleBaseInfo ffCPUModuleInfo = { diff --git a/src/modules/cpu/option.h b/src/modules/cpu/option.h index e0948af2eb..428b40a6b2 100644 --- a/src/modules/cpu/option.h +++ b/src/modules/cpu/option.h @@ -6,6 +6,7 @@ typedef struct FFCPUOptions { FFModuleArgs moduleArgs; + FFstrbuf tempSensor; bool temp; FFColorRangeConfig tempConfig; bool showPeCoreCount; From c11b88fee5144bf543234c6616dae3d23d3c6dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 10 Dec 2025 14:16:56 +0800 Subject: [PATCH 10/35] CPU (macOS): supports `tempSensor` --- src/detection/cpu/cpu_apple.c | 32 ++++++++++++++++++++------------ src/util/apple/smc_temps.c | 21 +++++++++++++++++++-- src/util/apple/smc_temps.h | 1 + 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/detection/cpu/cpu_apple.c b/src/detection/cpu/cpu_apple.c index f1ca8845a9..efa7bb04ac 100644 --- a/src/detection/cpu/cpu_apple.c +++ b/src/detection/cpu/cpu_apple.c @@ -3,26 +3,34 @@ #include "util/apple/smc_temps.h" #include "util/stringUtils.h" -static double detectCpuTemp(const FFstrbuf* cpuName) +static double detectCpuTemp(const FFCPUOptions* options, const FFstrbuf* cpuName) { double result = 0; const char* error = NULL; - if (ffStrbufStartsWithS(cpuName, "Apple M")) + + if (options->tempSensor.length) + { + error = ffDetectSmcSpecificTemp(options->tempSensor.chars, &result); + } + else { - switch (strtol(cpuName->chars + strlen("Apple M"), NULL, 10)) + if (ffStrbufStartsWithS(cpuName, "Apple M")) { - case 1: error = ffDetectSmcTemps(FF_TEMP_CPU_M1X, &result); break; - case 2: error = ffDetectSmcTemps(FF_TEMP_CPU_M2X, &result); break; - case 3: error = ffDetectSmcTemps(FF_TEMP_CPU_M3X, &result); break; - case 4: error = ffDetectSmcTemps(FF_TEMP_CPU_M4X, &result); break; - default: error = "Unsupported Apple Silicon CPU"; + switch (strtol(cpuName->chars + strlen("Apple M"), NULL, 10)) + { + case 1: error = ffDetectSmcTemps(FF_TEMP_CPU_M1X, &result); break; + case 2: error = ffDetectSmcTemps(FF_TEMP_CPU_M2X, &result); break; + case 3: error = ffDetectSmcTemps(FF_TEMP_CPU_M3X, &result); break; + case 4: error = ffDetectSmcTemps(FF_TEMP_CPU_M4X, &result); break; + default: error = "Unsupported Apple Silicon CPU"; + } } + else // PPC? + error = ffDetectSmcTemps(FF_TEMP_CPU_X64, &result); } - else // PPC? - error = ffDetectSmcTemps(FF_TEMP_CPU_X64, &result); - if(error) + if (error) return FF_CPU_TEMP_UNSET; return result; @@ -130,7 +138,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) detectFrequency(cpu); if (options->showPeCoreCount) detectCoreCount(cpu); - cpu->temperature = options->temp ? detectCpuTemp(&cpu->name) : FF_CPU_TEMP_UNSET; + cpu->temperature = options->temp ? detectCpuTemp(options, &cpu->name) : FF_CPU_TEMP_UNSET; return NULL; } diff --git a/src/util/apple/smc_temps.c b/src/util/apple/smc_temps.c index e415c7300a..d16c6e3d5e 100644 --- a/src/util/apple/smc_temps.c +++ b/src/util/apple/smc_temps.c @@ -277,15 +277,32 @@ static bool detectTemp(io_connect_t conn, const char* sensor, double* sum) return true; } +static io_connect_t conn; + +const char* ffDetectSmcSpecificTemp(const char* sensor, double* result) +{ + if (!conn) + { + if (smcOpen(&conn) != NULL) + conn = (io_connect_t) -1; + } + if (conn == (io_connect_t) -1) + return "Could not open SMC connection"; + + if (!detectTemp(conn, sensor, result)) + return "Could not read SMC temperature"; + + return NULL; +} + const char* ffDetectSmcTemps(enum FFTempType type, double* result) { - static io_connect_t conn; if (!conn) { if (smcOpen(&conn) != NULL) conn = (io_connect_t) -1; } - else if (conn == (io_connect_t) -1) + if (conn == (io_connect_t) -1) return "Could not open SMC connection"; uint32_t count = 0; diff --git a/src/util/apple/smc_temps.h b/src/util/apple/smc_temps.h index 4adc7a607b..031f7c2e1f 100644 --- a/src/util/apple/smc_temps.h +++ b/src/util/apple/smc_temps.h @@ -30,4 +30,5 @@ enum FFTempType FF_TEMP_MEMORY, }; +const char* ffDetectSmcSpecificTemp(const char* sensor, double* result); const char* ffDetectSmcTemps(enum FFTempType type, double* result); From f4401f172ef2573dde991a20b272a6a685b33c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 10 Dec 2025 15:41:21 +0800 Subject: [PATCH 11/35] CPU (Windows): supports `tempSensor` --- src/detection/cpu/cpu_windows.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index fe18c21a9d..c836877694 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -17,7 +17,7 @@ static inline void ffPerfCloseQueryHandle(HANDLE* phQuery) } } -const char* detectThermalTemp(double* result) +const char* detectThermalTemp(const FFCPUOptions* options, double* result) { struct FFPerfQuerySpec { @@ -35,6 +35,14 @@ const char* detectThermalTemp(double* result) .Name = L"\\_TZ.CPUZ", // The standard(?) instance name for CPU temperature in the thermal provider }; + if (options->tempSensor.length > 0) + { + int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, options->tempSensor.chars, (int) options->tempSensor.length, querySpec.Name, (int)(ARRAY_SIZE(querySpec.Name) - 1)); + if (written == 0) + return "Invalid temp sensor string"; + querySpec.Name[written] = L'\0'; + } + DWORD dataSize = 0; if (PerfEnumerateCounterSetInstances(NULL, &querySpec.Identifier.CounterSetGuid, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY) return "PerfEnumerateCounterSetInstances() failed"; @@ -62,6 +70,9 @@ const char* detectThermalTemp(double* result) if (dataSize == 0) { + if (options->tempSensor.length > 0) + return "Unable to find CPU sensor"; + const wchar_t* instanceName = (const wchar_t*)((BYTE*)pHead + sizeof(*pHead)); wcscpy(querySpec.Name, instanceName); // Use the first instance name if the specific one is not found } @@ -301,7 +312,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) detectMaxSpeedBySmbios(cpu); if(options->temp) - detectThermalTemp(&cpu->temperature); + detectThermalTemp(options, &cpu->temperature); return NULL; } From bca56b2049b552d6c63a21318caa89e7d6e2df3e Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 10 Dec 2025 16:13:22 +0800 Subject: [PATCH 12/35] CPU (Linux): supports `tempSensor` --- src/common/io/io.h | 7 +++ src/detection/cpu/cpu_linux.c | 84 +++++++++++++++++++++++++++-------- 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/common/io/io.h b/src/common/io/io.h index d9c24c3516..9e3f44d305 100644 --- a/src/common/io/io.h +++ b/src/common/io/io.h @@ -83,6 +83,13 @@ bool ffAppendFileBuffer(const char* fileName, FFstrbuf* buffer); FF_C_NONNULL(2, 3) bool ffAppendFileBufferRelative(FFNativeFD dfd, const char* fileName, FFstrbuf* buffer); +FF_C_NONNULL(2) +static inline bool ffReadFDBuffer(FFNativeFD fd, FFstrbuf* buffer) +{ + ffStrbufClear(buffer); + return ffAppendFDBuffer(fd, buffer); +} + FF_C_NONNULL(1, 2) static inline bool ffReadFileBuffer(const char* fileName, FFstrbuf* buffer) { diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index ccf5d99b59..3e6b8832f3 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -4,6 +4,7 @@ #include "common/properties.h" #include "util/mallocHelper.h" #include "util/stringUtils.h" +#include "util/path.h" #include #include @@ -13,6 +14,18 @@ #define FF_CPUINFO_PATH "/proc/cpuinfo" +static double readTempFile(int dfd, const char* filename, FFstrbuf* buffer) +{ + if (filename ? !ffReadFileBufferRelative(dfd, filename, buffer) : !ffReadFDBuffer(dfd, buffer)) + return FF_CPU_TEMP_UNSET; + + double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET); // millidegree Celsius + if (value == FF_CPU_TEMP_UNSET) + return FF_CPU_TEMP_UNSET; + + return value / 1000.; +} + static double parseTZDir(int dfd, FFstrbuf* buffer) { if (!ffReadFileBufferRelative(dfd, "type", buffer)) @@ -26,18 +39,12 @@ static double parseTZDir(int dfd, FFstrbuf* buffer) true ) return FF_CPU_TEMP_UNSET; - if (!ffReadFileBufferRelative(dfd, "temp", buffer)) - return FF_CPU_TEMP_UNSET; - - double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET);// millidegree Celsius - if (value == FF_CPU_TEMP_UNSET) - return FF_CPU_TEMP_UNSET; - - return value / 1000.; + return readTempFile(dfd, "temp", buffer); } static double parseHwmonDir(int dfd, FFstrbuf* buffer) { + //https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface if (!ffReadFileBufferRelative(dfd, "name", buffer)) return FF_CPU_TEMP_UNSET; @@ -53,25 +60,64 @@ static double parseHwmonDir(int dfd, FFstrbuf* buffer) true ) return FF_CPU_TEMP_UNSET; - //https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface - if (!ffReadFileBufferRelative(dfd, "temp1_input", buffer)) - return FF_CPU_TEMP_UNSET; - - double value = ffStrbufToDouble(buffer, FF_CPU_TEMP_UNSET);// millidegree Celsius - if (value == FF_CPU_TEMP_UNSET) - return FF_CPU_TEMP_UNSET; - - return value / 1000.; + return readTempFile(dfd, "temp1_input", buffer); } -static double detectCPUTemp(void) +static double detectCPUTemp(const FFCPUOptions* options) { FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + + if (options->tempSensor.length > 0) + { + FF_AUTO_CLOSE_FD int subfd = -1; + const char* fileName = NULL; + if (ffStrbufStartsWithS(&options->tempSensor, "hwmon") && ffCharIsDigit(options->tempSensor.chars[strlen("hwmon")])) + { + FF_AUTO_CLOSE_FD int dfd = open("/sys/class/hwmon/", O_PATH | O_CLOEXEC); + subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (subfd >= 0) + fileName = "temp1_input"; + else + subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_CLOEXEC); + } + else if (ffStrbufStartsWithS(&options->tempSensor, "thermal_zone") && ffCharIsDigit(options->tempSensor.chars[strlen("thermal_zone")])) + { + FF_AUTO_CLOSE_FD int dfd = open("/sys/class/thermal/", O_PATH | O_CLOEXEC); + subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (subfd >= 0) + fileName = "temp"; + else + subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_CLOEXEC); + } + else if (ffStrbufStartsWithS(&options->tempSensor, "cputemp.") && ffCharIsDigit(options->tempSensor.chars[strlen("cputemp.")])) + { + FF_AUTO_CLOSE_FD int dfd = open("/sys/class/platform/", O_PATH | O_CLOEXEC); + subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (subfd >= 0) + fileName = "temp1_input"; + else + subfd = openat(dfd, options->tempSensor.chars, O_RDONLY | O_CLOEXEC); + } + else if (ffIsAbsolutePath(options->tempSensor.chars)) + { + subfd = open(options->tempSensor.chars, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (subfd >= 0) + fileName = "temp1_input"; + else + subfd = open(options->tempSensor.chars, O_RDONLY | O_CLOEXEC); + } + if (subfd < 0) + return FF_CPU_TEMP_UNSET; + + return readTempFile(subfd, fileName, &buffer); + } + { FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/hwmon/"); if(dirp) { int dfd = dirfd(dirp); + struct dirent* entry; while((entry = readdir(dirp)) != NULL) { @@ -902,7 +948,7 @@ FF_MAYBE_UNUSED static const char* detectCPUOthers(const FFCPUOptions* options, const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { - cpu->temperature = options->temp ? detectCPUTemp() : FF_CPU_TEMP_UNSET; + cpu->temperature = options->temp ? detectCPUTemp(options) : FF_CPU_TEMP_UNSET; #if __x86_64__ || __i386__ return detectCPUX86(options, cpu); From af6196fe6e3e216cd9281223c77e1950b7b25180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 10 Dec 2025 16:30:23 +0800 Subject: [PATCH 13/35] CPU (FreeBSD): supports `tempSensor` --- src/detection/cpu/cpu_bsd.c | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/detection/cpu/cpu_bsd.c b/src/detection/cpu/cpu_bsd.c index 1ba7bf8460..aff68c2a24 100644 --- a/src/detection/cpu/cpu_bsd.c +++ b/src/detection/cpu/cpu_bsd.c @@ -8,22 +8,26 @@ #define FF_HAVE_CPUSET 1 #endif -static const char* detectCpuTemp(double* current) +static const char* detectCpuTemp(const FFCPUOptions* options, double* current) { - int temp = ffSysctlGetInt("dev.cpu.0.temperature", -999999); - if (temp == -999999) - return "ffSysctlGetInt(\"dev.cpu.0.temperature\") failed"; - - // In tenth of degrees Kelvin - *current = (double) temp / 10 - 273.15; - return NULL; -} - -static const char* detectThermalTemp(double* current) -{ - int temp = ffSysctlGetInt("hw.acpi.thermal.tz0.temperature", -999999); - if (temp == -999999) - return "ffSysctlGetInt(\"hw.acpi.thermal.tz0.temperature\") failed"; + int temp; + if (options->tempSensor.length > 0) + { + temp = ffSysctlGetInt(options->tempSensor.chars, -999999); + if (temp == -999999) + return "ffSysctlGetInt(options->tempSensor) failed"; + } + else + { + temp = ffSysctlGetInt("dev.cpu.0.temperature", -999999); + if (temp == -999999) + { + // Thermal zone temperature + temp = ffSysctlGetInt("hw.acpi.thermal.tz0.temperature", -999999); + if (temp == -999999) + return "ffSysctlGetInt(\"dev.cpu.0.temperature\" or \"hw.acpi.thermal.tz0.temperature\") failed"; + } + } // In tenth of degrees Kelvin *current = (double) temp / 10 - 273.15; @@ -94,11 +98,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpu->temperature = FF_CPU_TEMP_UNSET; - if (options->temp) - { - if (detectCpuTemp(&cpu->temperature) != NULL) - detectThermalTemp(&cpu->temperature); - } + if (options->temp) detectCpuTemp(options, &cpu->temperature); cpu->numaNodes = (uint16_t) ffSysctlGetInt("vm.ndomains", 0); From b4f39e251c1e693d03ed333b110f9f48e4574dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 10 Dec 2025 16:32:26 +0800 Subject: [PATCH 14/35] CPU (NetBSD): supports `tempSensor` --- src/detection/cpu/cpu_nbsd.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/detection/cpu/cpu_nbsd.c b/src/detection/cpu/cpu_nbsd.c index 4ab134d383..38ee1b3fd6 100644 --- a/src/detection/cpu/cpu_nbsd.c +++ b/src/detection/cpu/cpu_nbsd.c @@ -16,7 +16,7 @@ static void freePropDict(prop_dictionary_t* pdict) prop_object_release(*pdict); } -static const char* detectCpuTemp(double* current) +static const char* detectCpuTemp(const FFCPUOptions* options, double* current) { FF_AUTO_CLOSE_FD int fd = open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC); if (fd < 0) return "open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC) failed"; @@ -25,11 +25,21 @@ static const char* detectCpuTemp(double* current) if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &root) < 0) return "prop_dictionary_recv_ioctl(ENVSYS_GETDICTIONARY) failed"; - prop_array_t array = prop_dictionary_get(root, "coretemp0"); - if (!array) array = prop_dictionary_get(root, "amdzentemp0"); - if (!array) array = prop_dictionary_get(root, "viac7temp0"); - if (!array) array = prop_dictionary_get(root, "acpitz0"); // Thermal Zones - if (!array) return "No temp data found in root dictionary"; + prop_array_t array; + + if (options->tempSensor.length > 0) + { + array = prop_dictionary_get(root, options->tempSensor.chars); + if (!array) return "No temp data found in specified sensor"; + } + else + { + array = prop_dictionary_get(root, "coretemp0"); + if (!array) array = prop_dictionary_get(root, "amdzentemp0"); + if (!array) array = prop_dictionary_get(root, "viac7temp0"); + if (!array) array = prop_dictionary_get(root, "acpitz0"); // Thermal Zones + if (!array) return "No temp data found in root dictionary"; + } if (prop_array_count(array) != 2) return "Unexpected `xtemp0` data"; @@ -73,7 +83,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpu->temperature = FF_CPU_TEMP_UNSET; - if (options->temp) detectCpuTemp(&cpu->temperature); + if (options->temp) detectCpuTemp(options, &cpu->temperature); return NULL; } From 6e7b4bab7532918b3f3f4913410aa888938c6137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 10 Dec 2025 16:41:24 +0800 Subject: [PATCH 15/35] CPU (OpenBSD): supports `tempSensor` --- src/detection/cpu/cpu_obsd.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/detection/cpu/cpu_obsd.c b/src/detection/cpu/cpu_obsd.c index ea2d279349..3ac5893f62 100644 --- a/src/detection/cpu/cpu_obsd.c +++ b/src/detection/cpu/cpu_obsd.c @@ -6,7 +6,7 @@ #include #include -static const char* detectCPUTemp(FFCPUResult* cpu) +static const char* detectCPUTemp(const FFCPUOptions* options, FFCPUResult* cpu) { int mib[5] = {CTL_HW, HW_SENSORS, 0, SENSOR_TEMP, 0}; @@ -23,8 +23,16 @@ static const char* detectCPUTemp(FFCPUResult* cpu) return "sysctl(sensordev) failed"; } - if (!ffStrStartsWith(sensordev.xname, "cpu")) - continue; + if (options->tempSensor.length > 0) + { + if (!ffStrbufEqualS(&options->tempSensor, sensordev.xname)) + continue; + } + else + { + if (!ffStrStartsWith(sensordev.xname, "cpu")) + continue; + } for (mib[4] = 0; mib[4] < sensordev.maxnumt[SENSOR_TEMP]; mib[4]++) { @@ -62,7 +70,7 @@ const char *ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) if (cpuspeed > cpu->frequencyBase) cpu->frequencyBase = cpuspeed; cpu->temperature = FF_CPU_TEMP_UNSET; - if (options->temp) detectCPUTemp(cpu); + if (options->temp) detectCPUTemp(options, cpu); return NULL; } From f2a29309f33f3923d2e7d275be209c2257ad5927 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 11 Dec 2025 09:50:35 +0800 Subject: [PATCH 16/35] CPU (Linux): prevents variables from integer overflow --- src/detection/cpu/cpu_linux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 3e6b8832f3..9edcac48f4 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -862,9 +862,9 @@ FF_MAYBE_UNUSED static uint16_t getLoongarchPropCount(FFstrbuf* cpuinfo, const c char* pend; unsigned long id = strtoul(p, &pend, 10); if (__builtin_expect(id > 64, false)) - high |= 1 << (id - 64); + high |= 1UL << (id - 64); else - low |= 1 << id; + low |= 1UL << id; p = pend; } From cb1f0300b9b79309058825aeb7d2025cbdb186d6 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 11 Dec 2025 09:50:59 +0800 Subject: [PATCH 17/35] GPU (Linux): add comments --- src/detection/gpu/gpu_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index abe52ea153..0fb3051d92 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -605,7 +605,7 @@ static const char* drmDetectGPUs(const FFGPUOptions* options, FFlist* gpus) if (ffStrbufStartsWithS(&buffer, "pci:")) detectPci(options, gpus, &buffer, &drmDir, entry->d_name); - else if (ffStrbufStartsWithS(&buffer, "of:")) + else if (ffStrbufStartsWithS(&buffer, "of:")) // Open Firmware detectOf(gpus, &buffer, &drmDir, entry->d_name); ffStrbufSubstrBefore(&drmDir, drmDirLength); From e766809e3a746c70d27a2a609d8547d4170aae6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 11 Dec 2025 14:24:07 +0800 Subject: [PATCH 18/35] CPU (SunOS): supports `tempSensor` --- src/detection/cpu/cpu_sunos.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/detection/cpu/cpu_sunos.c b/src/detection/cpu/cpu_sunos.c index 3185919b98..4d0a887cc7 100644 --- a/src/detection/cpu/cpu_sunos.c +++ b/src/detection/cpu/cpu_sunos.c @@ -3,10 +3,15 @@ #include "util/stringUtils.h" #include -static const char* detectCPUTempByKstat(kstat_ctl_t* kc, FFCPUResult* cpu) +static const char* detectCPUTempByKstat(FFCPUOptions* options, kstat_ctl_t* kc, FFCPUResult* cpu) { const char* possibleModules[] = {"temperature", "cpu_temp", "acpi_thermal", NULL}; + if (options->tempSensor.length > 0) { + possibleModules[0] = options->tempSensor.chars; + possibleModules[1] = NULL; + } + for (int i = 0; possibleModules[i] != NULL; i++) { kstat_t* ks = kstat_lookup(kc, possibleModules[i], -1, NULL); if (ks && kstat_read(kc, ks, NULL) >= 0) { @@ -141,7 +146,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) if (options->temp) { - if (detectCPUTempByKstat(kc, cpu) != NULL) + if (detectCPUTempByKstat(options, kc, cpu) != NULL) detectCPUTempByIpmiTool(cpu); } From 42e7a7d2f7a5d8235bb09275bf8edccb9154c3cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 11 Dec 2025 14:28:12 +0800 Subject: [PATCH 19/35] Doc: clarifies that we supports illumos only --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a087b5e0df..4630b13ee3 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 SunOS. +Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and illumos (SunOS). From 60ec8c90974695d3574105ff3c1a0beb73c1b144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 11 Dec 2025 14:28:29 +0800 Subject: [PATCH 20/35] JsonSchema: documents `cpu.tempSensor` Fixes #2073 --- doc/json_schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/json_schema.json b/doc/json_schema.json index 0bdf56babb..57096616f1 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -1880,6 +1880,10 @@ "temp": { "$ref": "#/$defs/temperature" }, + "tempSensor": { + "description": "Set the temperature sensor to use for CPU temperature detection\n* Linux: `hwmon` or `thermal` path name (eg. `hwmonN`, `thermal_zoneN)`\n* macOS: SMC sensor key (eg. `Tp01`)\n* Windows: thermal zone key (eg. `\\_TZ.CPUZ`)\n* FreeBSD: sysctl key (eg. `dev.cpu.0.temperature`)\n* NetBSD: sysmon sensor key (eg. `coretemp0`)", + "type": "string" + }, "showPeCoreCount": { "description": "Detect and display CPU frequency of different core types (eg. Pcore and Ecore) if supported", "type": "boolean", From d2368501193a51f2dd0644005e08bdcc46d32813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 11 Dec 2025 14:37:09 +0800 Subject: [PATCH 21/35] CPUCache (macOS): fixes cache line size detection --- src/detection/cpucache/cpucache_apple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detection/cpucache/cpucache_apple.c b/src/detection/cpucache/cpucache_apple.c index b0e315a143..cacb85df60 100644 --- a/src/detection/cpucache/cpucache_apple.c +++ b/src/detection/cpucache/cpucache_apple.c @@ -9,7 +9,7 @@ const char* ffDetectCPUCache(FFCPUCacheResult* result) if (nPerfLevels <= 0) return "sysctl(hw.nperflevels) failed"; // macOS provides the global system cache line size - uint32_t lineSize = (uint32_t) ffSysctlGetInt("hw.cachelinesize", 0); + uint32_t lineSize = (uint32_t) ffSysctlGetInt64("hw.cachelinesize", 0); char sysctlKey[128] = "hw.perflevelN."; char* pNum = sysctlKey + strlen("hw.perflevel"); From dc69e44829e81c1eedcd05095be226bfdfe25917 Mon Sep 17 00:00:00 2001 From: Yelninei Date: Thu, 11 Dec 2025 13:08:39 +0000 Subject: [PATCH 22/35] CPU(Linux): Fix build on GNU. --- src/detection/cpu/cpu_linux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 9edcac48f4..db9e4ce7e7 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -14,6 +14,10 @@ #define FF_CPUINFO_PATH "/proc/cpuinfo" +#ifndef O_PATH +#define O_PATH 0 +#endif + static double readTempFile(int dfd, const char* filename, FFstrbuf* buffer) { if (filename ? !ffReadFileBufferRelative(dfd, filename, buffer) : !ffReadFDBuffer(dfd, buffer)) From bc869f1be9b2129663dd34f4281a2abd34a5e9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 12 Dec 2025 09:03:18 +0800 Subject: [PATCH 23/35] Chore: refines usage of `PATH_MAX` --- src/detection/brightness/brightness_linux.c | 2 +- src/util/platform/FFPlatform_unix.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/detection/brightness/brightness_linux.c b/src/detection/brightness/brightness_linux.c index 678c89a6ad..da68251119 100644 --- a/src/detection/brightness/brightness_linux.c +++ b/src/detection/brightness/brightness_linux.c @@ -41,7 +41,7 @@ static const char* detectWithBacklight(FFlist* result) FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); ffStrbufSubstrBeforeLastC(&backlightDir, '/'); ffStrbufAppendS(&backlightDir, "/device"); - ffStrbufInitA(&brightness->name, PATH_MAX + 1); + ffStrbufInitA(&brightness->name, PATH_MAX); if(realpath(backlightDir.chars, brightness->name.chars)) { ffStrbufRecalculateLength(&brightness->name); diff --git a/src/util/platform/FFPlatform_unix.c b/src/util/platform/FFPlatform_unix.c index 85e624a0d6..5badc958ba 100644 --- a/src/util/platform/FFPlatform_unix.c +++ b/src/util/platform/FFPlatform_unix.c @@ -22,7 +22,7 @@ static void getExePath(FFPlatform* platform) { - char exePath[PATH_MAX + 1]; + char exePath[PATH_MAX]; #if defined(__linux__) || defined (__GNU__) ssize_t exePathLen = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); if (exePathLen >= 0) @@ -58,7 +58,7 @@ static void getExePath(FFPlatform* platform) while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) { if (info.type == B_APP_IMAGE) { - exePathLen = strlcpy(exePath, info.name, PATH_MAX); + exePathLen = strlcpy(exePath, info.name, sizeof(exePath)); break; } } From bdc5174f683bc2b298dc74144dd932fdfbc635e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 15 Dec 2025 13:25:26 +0800 Subject: [PATCH 24/35] DisplayServer (Linux): upgrades kde-output-device-v2 wayland protocol files (again) Fixes #2093 --- .../kde-output-device-v2-client-protocol.h | 55 +++++++++++++++++++ .../wayland/kde-output-device-v2-protocol.c | 10 ++-- .../displayserver/linux/wayland/kde-output.c | 1 + 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/detection/displayserver/linux/wayland/kde-output-device-v2-client-protocol.h b/src/detection/displayserver/linux/wayland/kde-output-device-v2-client-protocol.h index 9e824fce9c..bec7f9e37b 100644 --- a/src/detection/displayserver/linux/wayland/kde-output-device-v2-client-protocol.h +++ b/src/detection/displayserver/linux/wayland/kde-output-device-v2-client-protocol.h @@ -239,6 +239,11 @@ enum kde_output_device_v2_capability { * @since 17 */ KDE_OUTPUT_DEVICE_V2_CAPABILITY_SHARPNESS = 0x1000, + /** + * if this outputdevice supports custom modes + * @since 18 + */ + KDE_OUTPUT_DEVICE_V2_CAPABILITY_CUSTOM_MODES = 0x2000, }; /** * @ingroup iface_kde_output_device_v2 @@ -280,6 +285,10 @@ enum kde_output_device_v2_capability { * @ingroup iface_kde_output_device_v2 */ #define KDE_OUTPUT_DEVICE_V2_CAPABILITY_SHARPNESS_SINCE_VERSION 17 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_CAPABILITY_CUSTOM_MODES_SINCE_VERSION 18 #endif /* KDE_OUTPUT_DEVICE_V2_CAPABILITY_ENUM */ #ifndef KDE_OUTPUT_DEVICE_V2_VRR_POLICY_ENUM @@ -794,6 +803,23 @@ struct kde_output_device_v2_listener { void (*sharpness)(void *data, struct kde_output_device_v2 *kde_output_device_v2, uint32_t sharpness); + /** + * output priority + * + * Describes the position of the output in the output order list, + * with lower values being earlier in the list. There's no specific + * value the list has to start at, this value is only used in + * sorting outputs. + * + * Note that the output order protocol is not sufficient for this, + * as an output may not be in the output order if it's disabled or + * mirroring another screen. + * @param priority priority + * @since 18 + */ + void (*priority)(void *data, + struct kde_output_device_v2 *kde_output_device_v2, + uint32_t priority); }; /** @@ -943,6 +969,10 @@ kde_output_device_v2_add_listener(struct kde_output_device_v2 *kde_output_device * @ingroup iface_kde_output_device_v2 */ #define KDE_OUTPUT_DEVICE_V2_SHARPNESS_SINCE_VERSION 17 +/** + * @ingroup iface_kde_output_device_v2 + */ +#define KDE_OUTPUT_DEVICE_V2_PRIORITY_SINCE_VERSION 18 /** @ingroup iface_kde_output_device_v2 */ @@ -972,6 +1002,18 @@ kde_output_device_v2_destroy(struct kde_output_device_v2 *kde_output_device_v2) wl_proxy_destroy((struct wl_proxy *) kde_output_device_v2); } +#ifndef KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_ENUM +#define KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_ENUM +/** + * @ingroup iface_kde_output_device_mode_v2 + * mode flags + */ +enum kde_output_device_mode_v2_flags { + KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_CUSTOM = 0x1, + KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_REDUCED_BLANKING = 0x2, +}; +#endif /* KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_ENUM */ + /** * @ingroup iface_kde_output_device_mode_v2 * @struct kde_output_device_mode_v2_listener @@ -1017,6 +1059,15 @@ struct kde_output_device_mode_v2_listener { */ void (*removed)(void *data, struct kde_output_device_mode_v2 *kde_output_device_mode_v2); + /** + * mode flags + * + * This event describes the mode's flags. + * @since 19 + */ + void (*flags)(void *data, + struct kde_output_device_mode_v2 *kde_output_device_mode_v2, + uint32_t flags); }; /** @@ -1046,6 +1097,10 @@ kde_output_device_mode_v2_add_listener(struct kde_output_device_mode_v2 *kde_out * @ingroup iface_kde_output_device_mode_v2 */ #define KDE_OUTPUT_DEVICE_MODE_V2_REMOVED_SINCE_VERSION 1 +/** + * @ingroup iface_kde_output_device_mode_v2 + */ +#define KDE_OUTPUT_DEVICE_MODE_V2_FLAGS_SINCE_VERSION 19 /** @ingroup iface_kde_output_device_mode_v2 */ diff --git a/src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c b/src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c index 889d372549..1c4fe0ef5f 100644 --- a/src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c +++ b/src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c @@ -67,12 +67,13 @@ static const struct wl_message kde_output_device_v2_events[] = { { "automatic_max_bits_per_color_limit", "15u", kde_output_device_v2_types + 0 }, { "edr_policy", "16u", kde_output_device_v2_types + 0 }, { "sharpness", "17u", kde_output_device_v2_types + 0 }, + { "priority", "18u", kde_output_device_v2_types + 0 }, }; WL_EXPORT const struct wl_interface kde_output_device_v2_interface = { - "kde_output_device_v2", 17, + "kde_output_device_v2", 19, 0, NULL, - 34, kde_output_device_v2_events, + 35, kde_output_device_v2_events, }; static const struct wl_message kde_output_device_mode_v2_events[] = { @@ -80,12 +81,13 @@ static const struct wl_message kde_output_device_mode_v2_events[] = { { "refresh", "i", kde_output_device_v2_types + 0 }, { "preferred", "", kde_output_device_v2_types + 0 }, { "removed", "", kde_output_device_v2_types + 0 }, + { "flags", "19u", kde_output_device_v2_types + 0 }, }; WL_EXPORT const struct wl_interface kde_output_device_mode_v2_interface = { - "kde_output_device_mode_v2", 1, + "kde_output_device_mode_v2", 19, 0, NULL, - 4, kde_output_device_mode_v2_events, + 5, kde_output_device_mode_v2_events, }; #endif diff --git a/src/detection/displayserver/linux/wayland/kde-output.c b/src/detection/displayserver/linux/wayland/kde-output.c index 9b18f9061e..7d986ba142 100644 --- a/src/detection/displayserver/linux/wayland/kde-output.c +++ b/src/detection/displayserver/linux/wayland/kde-output.c @@ -178,6 +178,7 @@ static struct kde_output_device_v2_listener outputListener = { .automatic_max_bits_per_color_limit = (void*) stubListener, .edr_policy = (void*) stubListener, .sharpness = (void*) stubListener, + .priority = (void*) stubListener, }; const char* ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) From 608382109cda6623e53f318e8aced54cf8e5a042 Mon Sep 17 00:00:00 2001 From: gitee-zeqi Date: Mon, 15 Dec 2025 13:30:47 +0800 Subject: [PATCH 25/35] Logo (Builtin): adds gxde logo (#2094) * add gxde-logo * add gxdeos * add gxdeos * add gxdeos --- src/logo/ascii/gxde.txt | 20 ++++++++++++++++++++ src/logo/builtin.c | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/logo/ascii/gxde.txt diff --git a/src/logo/ascii/gxde.txt b/src/logo/ascii/gxde.txt new file mode 100644 index 0000000000..388c37746e --- /dev/null +++ b/src/logo/ascii/gxde.txt @@ -0,0 +1,20 @@ + ################ + ######################## + ##########-- --########## + #######* ****** *####### + ###### --*######--- ###### + ##### --- *#### ##### + ##### *-- #*#*#* * #### ##### +#####* ##- ##*-#* ### ### *##### +##### ###* -##*#* *# *# *### ##### +##### ###* -*##*-*## *#* ### ##### +##### #### - --#### - ##* ### ##### +##### *####* * - ###- ##* ##### +#####* #######- *###- ### *##### + #####* ***#####****--- ### *##### + ###### ----------- - ### ###### + ###### ----- ### ###### + ####### ####### + ##########*----*########## + ###################### + ############## diff --git a/src/logo/builtin.c b/src/logo/builtin.c index ca9fca505c..713a195038 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -2157,7 +2157,7 @@ static const FFlogo G[] = { // GXDE { .names = {"GXDE"}, - .lines = FASTFETCH_DATATEXT_LOGO_DEEPIN, + .lines = FASTFETCH_DATATEXT_LOGO_GXDE, .colors = { FF_COLOR_FG_RED, }, From 2bbd7233210a8dba9d90bfd475f28f8d17156d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 16 Dec 2025 10:16:36 +0800 Subject: [PATCH 26/35] Release: v2.56.1 --- CHANGELOG.md | 11 +++++++++++ CMakeLists.txt | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 323b08afc3..611464eb59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# 2.56.1 + +Features: +* Improves compatibility with KDE Plasma 6.5 (#2093, Display) +* Adds a `tempSensor` option to specify the sensor name used for CPU temperature detection (CPU) + * Example: `{ "type": "cpu", "tempSensor": "hwmon0" /* Use /sys/class/hwmon/hwmon0 for temperature detection */ }` +* Minor optimizations + +Bugfixes: +* Fixes cache line size detection (CPU, macOS) + # 2.56.0 Features: diff --git a/CMakeLists.txt b/CMakeLists.txt index ef74527801..47def7927a 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.56.0 + VERSION 2.56.1 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" From 94941daf4fc31cd5be92a30369776aa7f262667f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 16 Dec 2025 10:41:15 +0800 Subject: [PATCH 27/35] Memory (macOS): uses `hw.memsize_usable` --- CHANGELOG.md | 1 + src/detection/memory/memory_apple.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 611464eb59..886a7e692a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Features: * Adds a `tempSensor` option to specify the sensor name used for CPU temperature detection (CPU) * Example: `{ "type": "cpu", "tempSensor": "hwmon0" /* Use /sys/class/hwmon/hwmon0 for temperature detection */ }` * Minor optimizations +* Reports usable RAM size to match other platforms (Memory, macOS) Bugfixes: * Fixes cache line size detection (CPU, macOS) diff --git a/src/detection/memory/memory_apple.c b/src/detection/memory/memory_apple.c index cabdab267d..812def5e8e 100644 --- a/src/detection/memory/memory_apple.c +++ b/src/detection/memory/memory_apple.c @@ -8,8 +8,11 @@ const char* ffDetectMemory(FFMemoryResult* ram) { size_t length = sizeof(ram->bytesTotal); - if (sysctl((int[]){ CTL_HW, HW_MEMSIZE }, 2, &ram->bytesTotal, &length, NULL, 0)) - return "Failed to read hw.memsize"; + if (sysctlbyname("hw.memsize_usable", &ram->bytesTotal, &length, NULL, 0) != 0) + { + if (sysctl((int[]){ CTL_HW, HW_MEMSIZE }, 2, &ram->bytesTotal, &length, NULL, 0) != 0) + return "Failed to read hw.memsize"; + } mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; vm_statistics64_data_t vmstat; From 0130f610e97d610809dab8dc3022ed58b27fcd65 Mon Sep 17 00:00:00 2001 From: Dariqq <77271900+Dariqq@users.noreply.github.com> Date: Wed, 17 Dec 2025 00:53:26 +0000 Subject: [PATCH 28/35] Chore (Windows): Standardize windows header casing. (#2096) [ci skip] Followup to 0b6ca158eae90acb9af984feae5a56e529ffca15. Co-authored-by: Dariqq --- 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 ee9abd46f9..aa6f6f5797 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -6,7 +6,7 @@ #include "util/mallocHelper.h" #include "common/io/io.h" -#include +#include #include "util/windows/nt.h" #include From 3e1167260deac6c4a211fbe41338d5739c84de0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Dec 2025 09:38:06 +0800 Subject: [PATCH 29/35] IO (Windows): removes unneeded null terminator appending --- src/common/io/io_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/io/io_windows.c b/src/common/io/io_windows.c index 8724d0ee92..33f9c19411 100644 --- a/src/common/io/io_windows.c +++ b/src/common/io/io_windows.c @@ -133,7 +133,7 @@ HANDLE openat(HANDLE dfd, const char* fileName, bool directory) wchar_t fileNameW[MAX_PATH]; int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, fileName, -1, fileNameW, ARRAY_SIZE(fileNameW)); if (len == 0) return INVALID_HANDLE_VALUE; - fileNameW[len] = L'\0'; + // Implies `fileNameW[len] = L'\0';` and `len` includes the null terminator for (int i = 0; i < len - 1; ++i) { From 1d4d68f6e0e48736d9b5125f9b998fd456852657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Dec 2025 09:38:23 +0800 Subject: [PATCH 30/35] CPU (SunOS): adds `const` for consistancy --- src/detection/cpu/cpu_sunos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/detection/cpu/cpu_sunos.c b/src/detection/cpu/cpu_sunos.c index 4d0a887cc7..1ae1f77c68 100644 --- a/src/detection/cpu/cpu_sunos.c +++ b/src/detection/cpu/cpu_sunos.c @@ -3,7 +3,7 @@ #include "util/stringUtils.h" #include -static const char* detectCPUTempByKstat(FFCPUOptions* options, kstat_ctl_t* kc, FFCPUResult* cpu) +static const char* detectCPUTempByKstat(const FFCPUOptions* options, kstat_ctl_t* kc, FFCPUResult* cpu) { const char* possibleModules[] = {"temperature", "cpu_temp", "acpi_thermal", NULL}; From 945aed7160ef04affc707c88e977aa192cd22ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 17 Dec 2025 13:55:57 +0800 Subject: [PATCH 31/35] Host (macOS): prefers `hw.product` than `hw.model` ... because `HW_MODEL` is deprecated --- src/detection/host/host_apple.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/detection/host/host_apple.c b/src/detection/host/host_apple.c index 2477ea7e94..d61fbc0287 100644 --- a/src/detection/host/host_apple.c +++ b/src/detection/host/host_apple.c @@ -45,7 +45,8 @@ const char* getOthersByIokit(FFHostResult* host) const char* ffDetectHost(FFHostResult* host) { - const char* error = ffSysctlGetString("hw.model", &host->family); + const char* error = ffSysctlGetString("hw.product", &host->family); + if (error) error = ffSysctlGetString("hw.model", &host->family); if (error) return error; ffStrbufSetStatic(&host->name, ffHostGetMacProductNameWithHwModel(&host->family)); From 716c0e2b5cba261148f04f3a481ac33b0c91ec1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Tue, 16 Dec 2025 10:44:51 +0800 Subject: [PATCH 32/35] Doc: update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 886a7e692a..06283fe7ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ Features: Bugfixes: * Fixes cache line size detection (CPU, macOS) +Logos: +* Removes Opak +* Updates GXDE + # 2.56.0 Features: From 610fc2ea36b92d6656d1d3c806cab0084dad9b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Dec 2025 13:36:38 +0800 Subject: [PATCH 33/35] Memory (macOS): Refines Memory usage detection on macOS to match Activity Monitor more closely --- CHANGELOG.md | 2 +- src/detection/memory/memory_apple.c | 28 +++++++++++++--------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06283fe7ba..a8fe63c725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,8 @@ Features: * Improves compatibility with KDE Plasma 6.5 (#2093, Display) * Adds a `tempSensor` option to specify the sensor name used for CPU temperature detection (CPU) * Example: `{ "type": "cpu", "tempSensor": "hwmon0" /* Use /sys/class/hwmon/hwmon0 for temperature detection */ }` +* Refines Memory usage detection on macOS to match Activity Monitor more closely (Memory, macOS) * Minor optimizations -* Reports usable RAM size to match other platforms (Memory, macOS) Bugfixes: * Fixes cache line size detection (CPU, macOS) diff --git a/src/detection/memory/memory_apple.c b/src/detection/memory/memory_apple.c index 812def5e8e..a24dba32e5 100644 --- a/src/detection/memory/memory_apple.c +++ b/src/detection/memory/memory_apple.c @@ -8,27 +8,25 @@ const char* ffDetectMemory(FFMemoryResult* ram) { size_t length = sizeof(ram->bytesTotal); - if (sysctlbyname("hw.memsize_usable", &ram->bytesTotal, &length, NULL, 0) != 0) - { - if (sysctl((int[]){ CTL_HW, HW_MEMSIZE }, 2, &ram->bytesTotal, &length, NULL, 0) != 0) - return "Failed to read hw.memsize"; - } + if (sysctl((int[]){ CTL_HW, HW_MEMSIZE }, 2, &ram->bytesTotal, &length, NULL, 0) != 0) + return "Failed to read hw.memsize"; + uint64_t usableMemory = 0; + length = sizeof(usableMemory); + if (sysctlbyname("hw.memsize_usable", &usableMemory, &length, NULL, 0) != 0) + usableMemory = ram->bytesTotal; mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; vm_statistics64_data_t vmstat; if(host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t) (&vmstat), &count) != KERN_SUCCESS) return "Failed to read host_statistics64"; - // https://github.com/exelban/stats/blob/master/Modules/RAM/readers.swift#L56 - ram->bytesUsed = ((uint64_t) - + vmstat.active_count - + vmstat.inactive_count - + vmstat.speculative_count - + vmstat.wire_count - + vmstat.compressor_page_count - - vmstat.purgeable_count - - vmstat.external_page_count - ) * instance.state.platform.sysinfo.pageSize; + uint64_t pageSize = instance.state.platform.sysinfo.pageSize; + uint64_t appMemory = vmstat.internal_page_count * pageSize; + uint64_t wiredMemory = vmstat.wire_count * pageSize; + uint64_t compressed = vmstat.compressor_page_count * pageSize; + uint64_t reserved = ram->bytesTotal - usableMemory; + + ram->bytesUsed = appMemory + wiredMemory + compressed + reserved; return NULL; } From 288d25e17c3ca153e251c9d9137372543b638588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Dec 2025 14:21:55 +0800 Subject: [PATCH 34/35] Packages (Windows): fixes possible UB Reported by Github Copilot --- src/detection/packages/packages_windows.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c index aa6f6f5797..1ff16842da 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -20,6 +20,8 @@ static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t uint8_t buffer[64 * 1024] __attribute__((aligned(8))); // Required for WoA BOOLEAN firstScan = TRUE; + size_t ignoreLen = ignore ? wcslen(ignore) : 0; + while (true) { IO_STATUS_BLOCK ioStatus = {}; NTSTATUS status = NtQueryDirectoryFile( @@ -42,7 +44,9 @@ static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t { if (!(entry->FileAttributes & type)) continue; - if (!flag && _wcsicmp(entry->FileName, ignore) == 0) + if (!flag && + ignoreLen == entry->FileNameLength / sizeof(*entry->FileName) && + _wcsnicmp(entry->FileName, ignore, ignoreLen) == 0) { flag = true; continue; From fa92bb84aeb95435845ad155b2095b1a63608ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 18 Dec 2025 14:35:21 +0800 Subject: [PATCH 35/35] JsonSchema: refines comments [ci skip] --- doc/json_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/json_schema.json b/doc/json_schema.json index 57096616f1..64ca981e56 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -1881,7 +1881,7 @@ "$ref": "#/$defs/temperature" }, "tempSensor": { - "description": "Set the temperature sensor to use for CPU temperature detection\n* Linux: `hwmon` or `thermal` path name (eg. `hwmonN`, `thermal_zoneN)`\n* macOS: SMC sensor key (eg. `Tp01`)\n* Windows: thermal zone key (eg. `\\_TZ.CPUZ`)\n* FreeBSD: sysctl key (eg. `dev.cpu.0.temperature`)\n* NetBSD: sysmon sensor key (eg. `coretemp0`)", + "description": "Set the temperature sensor to use for CPU temperature detection\n* Linux: `hwmon` or `thermal` path name (eg. `hwmon0`, `thermal_zone0)`\n* macOS: SMC sensor key (eg. `Tp01`)\n* Windows: thermal zone key (eg. `\\_TZ.CPUZ`)\n* FreeBSD: sysctl key (eg. `dev.cpu.0.temperature`)\n* NetBSD: sysmon sensor key (eg. `coretemp0`)", "type": "string" }, "showPeCoreCount": {