From 01bb73b9e80fc2d0e73bea3c8ce98ea79445f5d7 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 6 Aug 2026 16:40:33 +0800 Subject: [PATCH 01/30] Packaging: updates debian stuff [ci skip] --- debian/changelog.tpl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog.tpl b/debian/changelog.tpl index 5907db1c6..84e5dda3f 100644 --- a/debian/changelog.tpl +++ b/debian/changelog.tpl @@ -1,3 +1,9 @@ +fastfetch (2.67.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium + + * Update to 2.67.0 + + -- Carter Li Thu, 06 Aug 2026 16:22:22 +0800 + fastfetch (2.66.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium * Update to 2.66.0 From 51a4f34bb518a8262a3e5a65a1f29e68d8c548c2 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 7 Aug 2026 09:37:11 +0800 Subject: [PATCH 02/30] Codec (macOS): fixes a symbol not found error when running on 10.15 --- src/detection/codec/codec_apple.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/detection/codec/codec_apple.c b/src/detection/codec/codec_apple.c index 2cf2a48b2..e6cb3d382 100644 --- a/src/detection/codec/codec_apple.c +++ b/src/detection/codec/codec_apple.c @@ -3,6 +3,10 @@ #include #include "common/apple/cf_helpers.h" +#ifdef MAC_OS_VERSION_11_0 +[[clang::weak_import]] VT_EXPORT void VTRegisterSupplementalVideoDecoderIfAvailable(CMVideoCodecType codecType); +#endif + static const struct { CMVideoCodecType codec; FFCodecType type; @@ -70,9 +74,11 @@ static FFCodecType ffCodecDetectDecoders() { continue; } - if (__builtin_available(macOS 11.0, *)) { +#ifdef MAC_OS_VERSION_11_0 + if (VTRegisterSupplementalVideoDecoderIfAvailable) { VTRegisterSupplementalVideoDecoderIfAvailable(codec.codec); } +#endif bool supported = VTIsHardwareDecodeSupported(codec.codec); if (!supported) { continue; From ba60d7975c5e74477d4e9a9f443cb66543af6c34 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 7 Aug 2026 09:37:31 +0800 Subject: [PATCH 03/30] Camera (macOS): simplifies code --- src/detection/camera/camera_apple.m | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/detection/camera/camera_apple.m b/src/detection/camera/camera_apple.m index da6884486..ba1509e0d 100644 --- a/src/detection/camera/camera_apple.m +++ b/src/detection/camera/camera_apple.m @@ -19,12 +19,8 @@ const char* ffDetectCamera(FFlist* result) AVCaptureDeviceType deviceType = nullptr; #ifdef MAC_OS_VERSION_14_0 - // Strangely `@available(macOS 14.0, *)` doesn't work here (#1594) - if (@available(macOS 14.0, *)) - { - if (&AVCaptureDeviceTypeExternal) - deviceType = AVCaptureDeviceTypeExternal; - } + if (&AVCaptureDeviceTypeExternal) + deviceType = AVCaptureDeviceTypeExternal; #endif if (deviceType == nullptr) deviceType = AVCaptureDeviceTypeExternalUnknown; From 39b3dc001ae717a8333232fe9e97494d9f6d6769 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 7 Aug 2026 10:34:23 +0800 Subject: [PATCH 04/30] CMake: adds C23 feature detection and compatibility workarounds for Apple clang 17 --- CMakeLists.txt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cd0df0d1..61582f4e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1469,10 +1469,24 @@ endforeach() 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") + message(STATUS "C2Y _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") + message(STATUS "C2Y _Countof is NOT supported by the compiler") +endif() + +# Test C23 compiler support +check_c_source_compiles("enum my_enum: long{MY_ENUM_ZERO}; void test(void*p){} int main(int,const char**){[[gnu::cleanup(test)]] void* x=nullptr; return (int)MY_ENUM_ZERO;}" COMPILER_SUPPORTS_C23) +if(NOT COMPILER_SUPPORTS_C23) + message(FATAL_ERROR "Compiler does not support C23. Please use a newer compiler.") +endif() + +# A hack for AppleClang 17, which supports most C23 features we use except for the `auto` keyword. +# See https://github.com/Homebrew/homebrew-core/pull/297403 +check_c_source_compiles("int main(void){auto v = 12.34; return (int)v;}" COMPILER_SUPPORTS_AUTO) +if(NOT COMPILER_SUPPORTS_AUTO) + message(WARNING "Compiler does not support C23 'auto' keyword. A workaround will be applied.") + target_compile_definitions(libfastfetch PUBLIC $<$:auto=__auto_type>) endif() if(yyjson_FOUND) From 8686820c1a0b19e2433f5f545a9a1201384e9f3b Mon Sep 17 00:00:00 2001 From: Yelninei Date: Thu, 6 Aug 2026 14:10:33 +0000 Subject: [PATCH 05/30] CMake (Hurd): Define O_PATH to O_NORW. This is a noop because O_RW is 0 but O_NORW is the correct constant to open a file without read/write permissions. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61582f4e1..5ff536538 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1569,7 +1569,7 @@ elseif(Haiku) target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE) elseif(GNU) # On Hurd PATH_MAX is not defined. Set an arbitrary limit as workaround. - target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE PATH_MAX=4096 O_PATH=0) + target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE PATH_MAX=4096 O_PATH=O_NORW) endif() if(APPLE) From ac8fc63c288b746ea950e62e288b100ab6848019 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 7 Aug 2026 16:38:40 +0800 Subject: [PATCH 06/30] Doc: updates changelog [ci skip] --- CHANGELOG.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ab97abc3..1088ace29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 2.67.1 + +Bugfixes: +* Fixed `Symbol not found` error when running on macOS 10.15 (macOS) +* Fixed building on Apple clang 16 (the default compiler of macOS 14) +* Added a C23 compiler check so the build process exits during CMake configuration + # 2.67.0 Changes: @@ -31,12 +38,12 @@ Bugfixes: * Fixed invalid URL parsing in the PublicIP module. (PublicIP) * Correctly reported virtual GPUs on Windows. (#2461, GPU, Windows) * Added a size limit for network responses to prevent excessive memory usage and mitigate potential attacks. (PublicIP / Weather) -* Fixed Ubuntu Studio Core detection (OS, Linux) +* Fixed Ubuntu Studio Core detection. (OS, Linux) * Relaxed the HTTP response check so that both `HTTP/1.0` and `HTTP/1.1` responses are accepted when fetching data over the network. (PublicIP / Weather) * Various internal cleanups and optimizations: - * Fixed multiple memory leaks (Separator, Camera, Codec, Display) - * Added integer overflow checks to the string buffer implementation - * Various code cleanups and compiler warning fixes + * Fixed multiple memory leaks (Separator, Camera, Codec, Display). + * Added integer overflow checks to the string buffer implementation. + * Various code cleanups and compiler warning fixes. Logos: * Removed Hypros, MagpieOS, Furreto, EmperorOS and Magix @@ -49,9 +56,9 @@ Changes: Bugfixes: * Fixed compatibility with WSL 2.9.3 (#2433, WM, Linux) -* Fixed accurate AMD GPU names are not queried in `driverSpecific` mode (GPU, Linux) +* Fixed AMD GPU names not being queried accurately in `driverSpecific` mode (GPU, Linux) * Fixed GPU core counting for Apple Silicon GPU on Asahi Linux (GPU, Linux) -* Some internal cleanups and optimizations +* Some internal cleanups and optimizations. Features: * Added Astra Linux version detection (OS, Linux) @@ -784,7 +791,7 @@ Features: * Added support for interface speed detection on SunOS (LocalIP, SunOS) * Added detection support for Xlibre (#1888, WM, Linux) * Improved the accuracy of color detection (Cursor, macOS) -* Improved the proformance of `Nix` package manager detection on macOS by porting optimizations form Linux port (#1893, Packages, macOS) +* Improved the performance of `Nix` package manager detection on macOS by porting optimizations from the Linux port (#1893, Packages, macOS) Bugfixes: * Fixed custom object inheriting a key from the previous custom object if the key is blank (#1477) @@ -1295,7 +1302,7 @@ Features: * Change package manager name of NetBSD from `pkg` to `pkgsrc` (#1515, Packages, NetBSD) * Detect SOC name on RISCV (#1519, CPU, Linux) * Report marketing name of new QS8Es (CPU, Android) -* Acquire acquire more os info from lsb-release if missing from os-release (#1521) +* Acquire more OS info from lsb-release if missing from os-release (#1521) * CMake: add option `-DCUSTOM_LSB_RELEASE_PATH` to specify the path of `lsb-release` file * `-DCUSTOM_OS_RELEASE_PATH` has been supported since `v2.11.4` * Report more SOC names on Android (CPU, Android) @@ -1763,7 +1770,7 @@ Logos: Bugfixes: * Fix bad Intel Arc GPU name detection, which was supposed to be fixed in the last version but the change was reverted accidentally (#1177, GPU, Linux) -* Fix arm32 CPU name detection no longer work. Regression of 2.21.2 (CPU, Linux) +* Fix arm32 CPU name detection no longer working. Regression of 2.21.2 (CPU, Linux) # 2.21.2 From 881730d0bd778195ae4a4ed2ccc9c473efec4171 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Sat, 8 Aug 2026 22:38:53 +0800 Subject: [PATCH 07/30] TerminalFont: improves Ghostty config loading with fallback support Fixes: #2500 --- src/detection/terminalfont/terminalfont.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index b7c2d0a5a..68aadea2c 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -69,7 +69,7 @@ static void detectAlacritty(FFTerminalFontResult* terminalFont) { ffFontInitMoveValues(&terminalFont->font, &fontFamily, &fontSize, &fontStyle); } -static void parseGhosttyConfig(const FFstrbuf* path, FFstrbuf* fontName, FFstrbuf* fontNameFallback, FFstrbuf* fontSize, FFlist* configFiles /* list of FFstrbuf */) { +static void parseGhosttyConfig(FFstrbuf* path, FFstrbuf* fontName, FFstrbuf* fontNameFallback, FFstrbuf* fontSize, FFlist* configFiles /* list of FFstrbuf */) { // Maximum number of `config-file` directives to follow, guarding against runaway includes enum { FF_GHOSTTY_MAX_CONFIG_FILES = 16 }; @@ -79,7 +79,18 @@ static void parseGhosttyConfig(const FFstrbuf* path, FFstrbuf* fontName, FFstrbu FF_STRBUF_AUTO_DESTROY temp = ffStrbufCreate(); if (!ffAppendFileBuffer(path->chars, &buffer)) { FF_DEBUG("cannot read config: %s", path->chars); - return; + if (ffStrbufEndsWithS(path, ".ghostty")) { + FF_DEBUG("Trying to load config without .ghostty extension"); + path->chars[path->length - strlen(".ghostty")] = '\0'; + bool ok = ffAppendFileBuffer(path->chars, &buffer); + path->chars[path->length - strlen(".ghostty")] = '.'; + if (!ok) { + FF_DEBUG("cannot read config: %s", path->chars); + return; + } + } else { + return; + } } char* line = nullptr; @@ -394,7 +405,7 @@ static bool detectTerminalFontCommon(const FFTerminalResult* terminal, FFTermina } else if (ffStrbufStartsWithIgnCaseS(&terminal->processName, "contour")) { detectContour(&terminal->exe, terminalFont); } else if (ffStrbufStartsWithIgnCaseS(&terminal->processName, "ghostty")) { - detectGhostty(terminalFont, "com.mitchellh.ghostty/config", "ghostty/config"); + detectGhostty(terminalFont, "com.mitchellh.ghostty/config.ghostty", "ghostty/config.ghostty"); } else if (ffStrbufStartsWithIgnCaseS(&terminal->processName, "Muxy")) { detectGhostty(terminalFont, "Muxy/ghostty.conf", "muxy/ghostty.conf"); } else if (ffStrbufStartsWithIgnCaseS(&terminal->processName, "rio")) { From 558a90d4e054a023453383e64f2260ac6fe2fff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 9 Aug 2026 00:56:04 +0800 Subject: [PATCH 08/30] Loadavg (Haiku): adds support on R1B6 --- CMakeLists.txt | 11 +++++++++-- src/detection/loadavg/loadavg_sunos.c | 6 +++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ff536538..536979b1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ if(NOT PKG_CONFIG_FOUND) endif() include(CheckIncludeFile) +include(CheckFunctionExists) if(LINUX) CHECK_INCLUDE_FILE("linux/version.h" HAVE_LINUX_VERSION_H) @@ -1281,7 +1282,6 @@ elseif(Haiku) src/detection/keyboard/keyboard_haiku.cpp src/detection/libc/libc_nosupport.c src/detection/lm/lm_nosupport.c - src/detection/loadavg/loadavg_nosupport.c src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_haiku.cpp @@ -1311,6 +1311,13 @@ elseif(Haiku) src/detection/wmtheme/wmtheme_haiku.cpp src/detection/camera/camera_nosupport.c ) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} bsd) + check_function_exists(getloadavg HAVE_GETLOADAVG) + if(HAVE_GETLOADAVG) + list(APPEND LIBFASTFETCH_SRC src/detection/loadavg/loadavg_sunos.c) + else() + list(APPEND LIBFASTFETCH_SRC src/detection/loadavg/loadavg_nosupport.c) + endif() elseif(GNU) list(APPEND LIBFASTFETCH_SRC src/common/impl/dbus.c @@ -1428,7 +1435,6 @@ if(WIN32) endif() endif() endif() -include(CheckFunctionExists) if(NOT WIN32) check_function_exists(pipe2 HAVE_PIPE2) endif() @@ -2020,6 +2026,7 @@ elseif(Haiku) PRIVATE "GL" PRIVATE "be" PRIVATE "gnu" + PRIVATE "bsd" ) endif() diff --git a/src/detection/loadavg/loadavg_sunos.c b/src/detection/loadavg/loadavg_sunos.c index 7cf63eec6..7deedb10b 100644 --- a/src/detection/loadavg/loadavg_sunos.c +++ b/src/detection/loadavg/loadavg_sunos.c @@ -1,6 +1,10 @@ #include "detection/loadavg/loadavg.h" -#include +#if __has_include() + #include +#else + #include +#endif const char* ffDetectLoadavg(double result[3]) { return getloadavg(result, 3) == 3 ? nullptr : "getloadavg() failed"; From 4361c5e7876b3beaf6ad2cbd4741050c427748e8 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Sat, 8 Aug 2026 22:18:27 +0800 Subject: [PATCH 09/30] Library: adds function `ffLibraryIterateDynamicLibs` --- src/common/impl/library.c | 75 +++++++++++++++++++++++++++++++++++++++ src/common/library.h | 6 +++- src/common/windows/nt.h | 16 +++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/common/impl/library.c b/src/common/impl/library.c index 440ea1336..f4cfd1421 100644 --- a/src/common/impl/library.c +++ b/src/common/impl/library.c @@ -178,3 +178,78 @@ void* ffLibraryGetModule(const wchar_t* libraryFileName) { return module; } #endif + +struct LibraryIterateDynamicLibsBundle { + FFLibraryIterateCallback callback; + void* userData; +}; + +#if _WIN32 + +static void ffLibraryIterateDynamicLibsCallback(PLDR_DATA_TABLE_ENTRY DataTableEntry, PVOID Context, BOOLEAN* StopEnumeration) { + if (DataTableEntry->FullDllName.Buffer == nullptr || DataTableEntry->FullDllName.Buffer[0] == L'\0') { + return; + } + + char path[PATH_MAX * 3]; + ULONG outBytes; + if (NT_SUCCESS(RtlUnicodeToUTF8N(path, sizeof(path), &outBytes, DataTableEntry->FullDllName.Buffer, (uint32_t) (DataTableEntry->FullDllName.Length + sizeof(wchar_t))))) { + struct LibraryIterateDynamicLibsBundle* bundle = (struct LibraryIterateDynamicLibsBundle*) Context; + *StopEnumeration = !bundle->callback(path, bundle->userData); + } +} + +void ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData) { + struct LibraryIterateDynamicLibsBundle bundle = { + .callback = callback, + .userData = userData, + }; + LdrEnumerateLoadedModules(FALSE, ffLibraryIterateDynamicLibsCallback, &bundle); +} + +#elif defined(__APPLE__) + +#include + +void ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData) { + uint32_t imageCount = _dyld_image_count(); + for (uint32_t i = 0; i < imageCount; ++i) { + const char* name = _dyld_get_image_name(i); + if (name == nullptr || name[0] == '\0') { + continue; + } + + if (!callback(name, userData)) { + break; + } + } +} + +#elif __has_include() + #include + +static int ffLibraryIterateDynamicLibsCallback(struct dl_phdr_info* info, size_t, void* data) { + if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') { + return 0; + } + + struct LibraryIterateDynamicLibsBundle* bundle = (struct LibraryIterateDynamicLibsBundle*) data; + return !bundle->callback(info->dlpi_name, bundle->userData); +} + +void ffLibraryIterateDynamicLibs(bool (*callback)(const char* name, void* userData), void* userData) { + dl_iterate_phdr( + ffLibraryIterateDynamicLibsCallback, + &(struct LibraryIterateDynamicLibsBundle) { + .callback = callback, + .userData = userData, + }); +} + +#else + +void ffLibraryIterateDynamicLibs(FFLibraryIterateCallback, void*) { + // Not implemented for this platform +} + +#endif diff --git a/src/common/library.h b/src/common/library.h index 06003cc93..f2b2cca33 100644 --- a/src/common/library.h +++ b/src/common/library.h @@ -1,6 +1,6 @@ #pragma once -#include "fastfetch.h" +#include "fastfetch.h" // IWYU pragma: keep #ifndef FF_DISABLE_DLOPEN @@ -111,3 +111,7 @@ void* ffLibraryLoadMulti(const char* path, int maxVersion, ...); #if _WIN32 void* ffLibraryGetModule(const wchar_t* libraryFileName); #endif + +// Return false to stop iterating, true to continue +typedef bool (*FFLibraryIterateCallback)(const char* name, void* userData); +void ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData); diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h index 77e0a00c1..f00384984 100644 --- a/src/common/windows/nt.h +++ b/src/common/windows/nt.h @@ -1279,6 +1279,22 @@ NTSYSAPI NTSTATUS NTAPI LdrGetProcedureAddress( _In_opt_ ULONG ProcedureNumber, _Out_ PVOID* ProcedureAddress); +typedef _Function_class_(LDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION) +VOID NTAPI LDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION( + _In_ PLDR_DATA_TABLE_ENTRY DataTableEntry, + _In_opt_ PVOID Context, + _Inout_ BOOLEAN* StopEnumeration +); + +NTSYSAPI +NTSTATUS +NTAPI +LdrEnumerateLoadedModules( + _In_ BOOLEAN ReservedFlag, + _In_ LDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION* EnumProc, + _In_opt_ PVOID Context +); + typedef enum _SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 From 8bc4464b64c96c0f6c781e494a121939b8bc74fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 9 Aug 2026 10:11:25 +0800 Subject: [PATCH 10/30] Library: makes `ffLibraryIterateDynamicLibs` return false on failure --- src/common/impl/library.c | 14 +++++++++----- src/common/library.h | 3 ++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/common/impl/library.c b/src/common/impl/library.c index f4cfd1421..3c34c79f8 100644 --- a/src/common/impl/library.c +++ b/src/common/impl/library.c @@ -199,19 +199,19 @@ static void ffLibraryIterateDynamicLibsCallback(PLDR_DATA_TABLE_ENTRY DataTableE } } -void ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData) { +bool ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData) { struct LibraryIterateDynamicLibsBundle bundle = { .callback = callback, .userData = userData, }; - LdrEnumerateLoadedModules(FALSE, ffLibraryIterateDynamicLibsCallback, &bundle); + return NT_SUCCESS(LdrEnumerateLoadedModules(FALSE, ffLibraryIterateDynamicLibsCallback, &bundle)); } #elif defined(__APPLE__) #include -void ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData) { +bool ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData) { uint32_t imageCount = _dyld_image_count(); for (uint32_t i = 0; i < imageCount; ++i) { const char* name = _dyld_get_image_name(i); @@ -223,6 +223,8 @@ void ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userDa break; } } + + return true; } #elif __has_include() @@ -237,19 +239,21 @@ static int ffLibraryIterateDynamicLibsCallback(struct dl_phdr_info* info, size_t return !bundle->callback(info->dlpi_name, bundle->userData); } -void ffLibraryIterateDynamicLibs(bool (*callback)(const char* name, void* userData), void* userData) { +bool ffLibraryIterateDynamicLibs(bool (*callback)(const char* name, void* userData), void* userData) { dl_iterate_phdr( ffLibraryIterateDynamicLibsCallback, &(struct LibraryIterateDynamicLibsBundle) { .callback = callback, .userData = userData, }); + return true; } #else -void ffLibraryIterateDynamicLibs(FFLibraryIterateCallback, void*) { +bool ffLibraryIterateDynamicLibs(FFLibraryIterateCallback, void*) { // Not implemented for this platform + return false; } #endif diff --git a/src/common/library.h b/src/common/library.h index f2b2cca33..fc5ddbe95 100644 --- a/src/common/library.h +++ b/src/common/library.h @@ -114,4 +114,5 @@ void* ffLibraryGetModule(const wchar_t* libraryFileName); // Return false to stop iterating, true to continue typedef bool (*FFLibraryIterateCallback)(const char* name, void* userData); -void ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData); +// Iterate over all loaded dynamic libraries. Returns true on success, false on failure. +bool ffLibraryIterateDynamicLibs(FFLibraryIterateCallback callback, void* userData); From 8b8efcdcce100594fc81f10b79df5b13632754c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 9 Aug 2026 10:12:37 +0800 Subject: [PATCH 11/30] Locale (macOS): queries system-wide locale preference --- CMakeLists.txt | 2 +- src/detection/locale/locale_apple.c | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/detection/locale/locale_apple.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 536979b1d..238537f9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1038,7 +1038,7 @@ elseif(APPLE) src/detection/lm/lm_nosupport.c src/detection/loadavg/loadavg_bsd.c src/detection/libc/libc_apple.c - src/detection/locale/locale_linux.c + src/detection/locale/locale_apple.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_apple.c src/detection/media/media_apple.m diff --git a/src/detection/locale/locale_apple.c b/src/detection/locale/locale_apple.c new file mode 100644 index 000000000..c5a5e8e1a --- /dev/null +++ b/src/detection/locale/locale_apple.c @@ -0,0 +1,13 @@ +#include "detection/locale/locale.h" +#include "common/apple/cf_helpers.h" + +const char* ffDetectLocale(FFstrbuf* result) { + // Read the system-wide locale preference (equivalent to `defaults read -g AppleLocale`), + // which is NOT affected by user environment variables (LANG, LC_ALL, etc.) + FF_CFTYPE_AUTO_RELEASE CFStringRef appleLocale = CFPreferencesCopyAppValue(CFSTR("AppleLocale"), kCFPreferencesAnyApplication); + if (!appleLocale) { + return "Failed to read AppleLocale"; + } + + return ffCfStrGetString(appleLocale, result); +} From da42108732b4411da2afd7bdc4d85038da174d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 9 Aug 2026 10:22:21 +0800 Subject: [PATCH 12/30] Cursor (macOS): migrates impl to pure C using CoreFoundation --- CMakeLists.txt | 2 +- src/detection/cursor/cursor_apple.c | 70 +++++++++++++++++++++++++++++ src/detection/cursor/cursor_apple.m | 63 -------------------------- 3 files changed, 71 insertions(+), 64 deletions(-) create mode 100644 src/detection/cursor/cursor_apple.c delete mode 100644 src/detection/cursor/cursor_apple.m diff --git a/CMakeLists.txt b/CMakeLists.txt index 238537f9f..49a8f2f8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1023,7 +1023,7 @@ elseif(APPLE) src/detection/cpu/cpu_apple.c src/detection/cpucache/cpucache_apple.c src/detection/cpuusage/cpuusage_apple.c - src/detection/cursor/cursor_apple.m + src/detection/cursor/cursor_apple.c src/detection/disk/disk_bsd.c src/detection/dns/dns_apple.c src/detection/physicaldisk/physicaldisk_apple.c diff --git a/src/detection/cursor/cursor_apple.c b/src/detection/cursor/cursor_apple.c new file mode 100644 index 000000000..957d08935 --- /dev/null +++ b/src/detection/cursor/cursor_apple.c @@ -0,0 +1,70 @@ +#include "cursor.h" +#include "common/apple/cf_helpers.h" + +static bool appendColor(FFstrbuf* str, CFDictionaryRef dict) { + double r, g, b, a; + if ( + ffCfDictGetDouble(dict, CFSTR("red"), &r) || + ffCfDictGetDouble(dict, CFSTR("green"), &g) || + ffCfDictGetDouble(dict, CFSTR("blue"), &b) || + ffCfDictGetDouble(dict, CFSTR("alpha"), &a)) { + return false; + } + + r = r * 255 + .5; + g = g * 255 + .5; + b = b * 255 + .5; + a = a * 255 + .5; + + uint32_t color = ((uint32_t) r << 24) | ((uint32_t) g << 16) | ((uint32_t) b << 8) | ((uint32_t) a); + + // clang-format off + switch (color) + { + case 0x000000FF: ffStrbufAppendS(str, "Black"); break; + case 0x0433FFFF: ffStrbufAppendS(str, "Blue"); break; + case 0xAA7942FF: ffStrbufAppendS(str, "Brown"); break; + case 0x00FDFFFF: ffStrbufAppendS(str, "Cyan"); break; + case 0x00F900FF: ffStrbufAppendS(str, "Green"); break; + case 0xFF40FFFF: ffStrbufAppendS(str, "Magenta"); break; + case 0xFF9300FF: ffStrbufAppendS(str, "Orange"); break; + case 0x942192FF: ffStrbufAppendS(str, "Purple"); break; + case 0xFF2600FF: ffStrbufAppendS(str, "Red"); break; + case 0xFFFB00FF: ffStrbufAppendS(str, "Yellow"); break; + case 0xFFFFFFFF: ffStrbufAppendS(str, "White"); break; + case 0x00000000: ffStrbufAppendS(str, "Transparent"); break; + default: ffStrbufAppendF(str, "#%08X", color); break; + } + // clang-format on + + return true; +} + +void ffDetectCursor(FFCursorResult* result) { + // Read via cfprefsd (equivalent to `defaults read com.apple.universalaccess `) + ffStrbufAppendS(&result->theme, "Fill - "); + { + FF_CFTYPE_AUTO_RELEASE CFTypeRef color = CFPreferencesCopyAppValue(CFSTR("cursorFill"), CFSTR("com.apple.universalaccess")); + if (!color || CFGetTypeID(color) != CFDictionaryGetTypeID() || !appendColor(&result->theme, (CFDictionaryRef) color)) { + ffStrbufAppendS(&result->theme, "Black"); + } + } + + ffStrbufAppendS(&result->theme, ", Outline - "); + { + FF_CFTYPE_AUTO_RELEASE CFTypeRef color = CFPreferencesCopyAppValue(CFSTR("cursorOutline"), CFSTR("com.apple.universalaccess")); + if (!color || CFGetTypeID(color) != CFDictionaryGetTypeID() || !appendColor(&result->theme, (CFDictionaryRef) color)) { + ffStrbufAppendS(&result->theme, "White"); + } + } + + { + FF_CFTYPE_AUTO_RELEASE CFTypeRef mouseDriverCursorSize = CFPreferencesCopyAppValue(CFSTR("mouseDriverCursorSize"), CFSTR("com.apple.universalaccess")); + double size = 32; + if (mouseDriverCursorSize && ffCfNumGetDouble(mouseDriverCursorSize, &size) == nullptr) { + ffStrbufAppendUInt(&result->size, (uint32_t) (size * 32 + 0.5)); + } else { + ffStrbufAppendS(&result->size, "32"); + } + } +} diff --git a/src/detection/cursor/cursor_apple.m b/src/detection/cursor/cursor_apple.m deleted file mode 100644 index 610bd1ed8..000000000 --- a/src/detection/cursor/cursor_apple.m +++ /dev/null @@ -1,63 +0,0 @@ -#include "cursor.h" - -#import - -static void appendColor(FFstrbuf* str, NSDictionary* dict) -{ - uint32_t r = (uint32_t) (((NSNumber*) dict[@"red"]).doubleValue * 255 + .5); - uint32_t g = (uint32_t) (((NSNumber*) dict[@"green"]).doubleValue * 255 + .5); - uint32_t b = (uint32_t) (((NSNumber*) dict[@"blue"]).doubleValue * 255 + .5); - uint32_t a = (uint32_t) (((NSNumber*) dict[@"alpha"]).doubleValue * 255 + .5); - uint32_t color = (r << 24) | (g << 16) | (b << 8) | a; - - switch (color) - { - case 0x000000FF: ffStrbufAppendS(str, "Black"); return; - case 0x0433FFFF: ffStrbufAppendS(str, "Blue"); return; - case 0xAA7942FF: ffStrbufAppendS(str, "Brown"); return; - case 0x00FDFFFF: ffStrbufAppendS(str, "Cyan"); return; - case 0x00F900FF: ffStrbufAppendS(str, "Green"); return; - case 0xFF40FFFF: ffStrbufAppendS(str, "Magenta"); return; - case 0xFF9300FF: ffStrbufAppendS(str, "Orange"); return; - case 0x942192FF: ffStrbufAppendS(str, "Purple"); return; - case 0xFF2600FF: ffStrbufAppendS(str, "Red"); return; - case 0xFFFB00FF: ffStrbufAppendS(str, "Yellow"); return; - case 0xFFFFFFFF: ffStrbufAppendS(str, "White"); return; - case 0x00000000: ffStrbufAppendS(str, "Transparent"); return; - default: ffStrbufAppendF(str, "#%08X", color); return; - } -} - -void ffDetectCursor(FFCursorResult* result) -{ - NSError* error; - NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Preferences/com.apple.universalaccess.plist", instance.state.platform.homeDir.chars]; - NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName] - error:&error]; - if(error) - { - ffStrbufAppendS(&result->error, error.localizedDescription.UTF8String); - return; - } - - NSDictionary* color; - - ffStrbufAppendS(&result->theme, "Fill - "); - if ((color = dict[@"cursorFill"])) - appendColor(&result->theme, color); - else - ffStrbufAppendS(&result->theme, "Black"); - - ffStrbufAppendS(&result->theme, ", Outline - "); - - if ((color = dict[@"cursorOutline"])) - appendColor(&result->theme, color); - else - ffStrbufAppendS(&result->theme, "White"); - - NSNumber* mouseDriverCursorSize = dict[@"mouseDriverCursorSize"]; - if (mouseDriverCursorSize) - ffStrbufAppendF(&result->size, "%d", (int) (mouseDriverCursorSize.doubleValue * 32 + 0.5)); - else - ffStrbufAppendS(&result->size, "32"); -} From 1d4b0236ac2be3dd61833b912700540b122871ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 9 Aug 2026 10:25:13 +0800 Subject: [PATCH 13/30] WMTheme (macOS): migrates impl to pure C using CoreFoundation --- CMakeLists.txt | 2 +- src/detection/wmtheme/wmtheme_apple.c | 52 +++++++++++++++++++++++++++ src/detection/wmtheme/wmtheme_apple.m | 40 --------------------- 3 files changed, 53 insertions(+), 41 deletions(-) create mode 100644 src/detection/wmtheme/wmtheme_apple.c delete mode 100644 src/detection/wmtheme/wmtheme_apple.m diff --git a/CMakeLists.txt b/CMakeLists.txt index 49a8f2f8d..9e86d3d40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1064,7 +1064,7 @@ elseif(APPLE) src/detection/wifi/wifi_apple.m src/detection/wm/wm_apple.m src/detection/de/de_nosupport.c - src/detection/wmtheme/wmtheme_apple.m + src/detection/wmtheme/wmtheme_apple.c src/detection/camera/camera_apple.m ) # CMAKE_SYSTEM_PROCESSOR has been normalized before diff --git a/src/detection/wmtheme/wmtheme_apple.c b/src/detection/wmtheme/wmtheme_apple.c new file mode 100644 index 000000000..ef30757c5 --- /dev/null +++ b/src/detection/wmtheme/wmtheme_apple.c @@ -0,0 +1,52 @@ +#include "fastfetch.h" +#include "wmtheme.h" +#include "common/apple/cf_helpers.h" + +bool ffDetectWmTheme(FFstrbuf* themeOrError) { + // Read via cfprefsd instead of the raw plist file to keep cache consistency with the system + FF_CFTYPE_AUTO_RELEASE CFTypeRef wmThemeColor = CFPreferencesCopyAppValue(CFSTR("AppleAccentColor"), kCFPreferencesAnyApplication); + int32_t accentColor = -2; // -2: not set; -1 is Graphite + if (wmThemeColor && ffCfNumGetInt(wmThemeColor, &accentColor) == nullptr) { + switch (accentColor) { + case -1: + ffStrbufAppendS(themeOrError, "Graphite"); + break; + case 0: + ffStrbufAppendS(themeOrError, "Red"); + break; + case 1: + ffStrbufAppendS(themeOrError, "Orange"); + break; + case 2: + ffStrbufAppendS(themeOrError, "Yellow"); + break; + case 3: + ffStrbufAppendS(themeOrError, "Green"); + break; + case 4: + ffStrbufAppendS(themeOrError, "Blue"); + break; + case 5: + ffStrbufAppendS(themeOrError, "Purple"); + break; + case 6: + ffStrbufAppendS(themeOrError, "Pink"); + break; + default: + ffStrbufAppendS(themeOrError, "Unknown"); + break; + } + } else { + ffStrbufAppendS(themeOrError, "Multicolor"); + } + + FF_STRBUF_AUTO_DESTROY style = ffStrbufCreate(); + FF_CFTYPE_AUTO_RELEASE CFTypeRef wmTheme = CFPreferencesCopyAppValue(CFSTR("AppleInterfaceStyle"), kCFPreferencesAnyApplication); + if (wmTheme && ffCfStrGetString(wmTheme, &style) == nullptr) { + ffStrbufAppendF(themeOrError, " (%s)", style.chars); + } else { + ffStrbufAppendS(themeOrError, " (Light)"); + } + + return true; +} diff --git a/src/detection/wmtheme/wmtheme_apple.m b/src/detection/wmtheme/wmtheme_apple.m deleted file mode 100644 index 8c43877b1..000000000 --- a/src/detection/wmtheme/wmtheme_apple.m +++ /dev/null @@ -1,40 +0,0 @@ -#include "fastfetch.h" -#include "wmtheme.h" - -#import - -bool ffDetectWmTheme(FFstrbuf* themeOrError) -{ - NSError* error; - NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Preferences/.GlobalPreferences.plist", instance.state.platform.homeDir.chars]; - NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName] - error:&error]; - if(error) - { - ffStrbufAppendS(themeOrError, error.localizedDescription.UTF8String); - return false; - } - - NSNumber* wmThemeColor = dict[@"AppleAccentColor"]; - if(!wmThemeColor) - ffStrbufAppendS(themeOrError, "Multicolor"); - else - { - switch(wmThemeColor.intValue) - { - case -1: ffStrbufAppendS(themeOrError, "Graphite"); break; - case 0: ffStrbufAppendS(themeOrError, "Red"); break; - case 1: ffStrbufAppendS(themeOrError, "Orange"); break; - case 2: ffStrbufAppendS(themeOrError, "Yellow"); break; - case 3: ffStrbufAppendS(themeOrError, "Green"); break; - case 4: ffStrbufAppendS(themeOrError, "Blue"); break; - case 5: ffStrbufAppendS(themeOrError, "Purple"); break; - case 6: ffStrbufAppendS(themeOrError, "Pink"); break; - default: ffStrbufAppendS(themeOrError, "Unknown"); break; - } - } - - NSString* wmTheme = dict[@"AppleInterfaceStyle"]; - ffStrbufAppendF(themeOrError, " (%s)", wmTheme ? wmTheme.UTF8String : "Light"); - return true; -} From e3b8e94055154a884b3e3b342d46dec22294e1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sun, 9 Aug 2026 10:27:19 +0800 Subject: [PATCH 14/30] Wallpaper (macOS): uses the official API if possible --- .gitignore | 1 + src/detection/wallpaper/wallpaper_apple.m | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/.gitignore b/.gitignore index db8564382..1394fe931 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ fastfetch.kdev4 *.user *.user.* *.swp +*.log diff --git a/src/detection/wallpaper/wallpaper_apple.m b/src/detection/wallpaper/wallpaper_apple.m index f4ba8a682..63acae325 100644 --- a/src/detection/wallpaper/wallpaper_apple.m +++ b/src/detection/wallpaper/wallpaper_apple.m @@ -3,9 +3,20 @@ #include "common/apple/osascript.h" #import +#import const char* ffDetectWallpaper(FFstrbuf* result) { + { + // Reliable for user-picked static images. + NSURL* url = [NSWorkspace.sharedWorkspace desktopImageURLForScreen:NSScreen.mainScreen]; + if (url.fileURL && ![url.path isEqualToString:@"/System/Library/CoreServices/DefaultDesktop.heic"] /* dynamic wallpapers */) + { + ffStrbufSetS(result, url.path.UTF8String); + return nullptr; + } + } + { // For Sonoma // https://github.com/JohnCoates/Aerial/issues/1332 From 4c2af4071c0bbcf1edafdc2e44d605b4fe5d4e7c Mon Sep 17 00:00:00 2001 From: HashimAbdulaziz Date: Sun, 9 Aug 2026 09:27:11 +0300 Subject: [PATCH 15/30] TerminalFont (Alacritty): fix font size never being parsed from config The query array gained a third entry in dd30c82b83667ce45d71fb4d32b5b0c2cb727e19 but numQueries stayed at 2, so `size =` was never queried and every Alacritty user silently got the hardcoded 11.25 fallback. --- src/detection/terminalfont/terminalfont.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index 68aadea2c..8c54a8500 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -23,13 +23,13 @@ static void detectAlacritty(FFTerminalFontResult* terminalFont) { }; // alacritty parses config files in this order - if (ffParsePropFileConfigValues("alacritty/alacritty.toml", 2, fontQueryToml)) { + if (ffParsePropFileConfigValues("alacritty/alacritty.toml", 3, fontQueryToml)) { break; } - if (ffParsePropFileConfigValues("alacritty.toml", 2, fontQueryToml)) { + if (ffParsePropFileConfigValues("alacritty.toml", 3, fontQueryToml)) { break; } - if (ffParsePropFileConfigValues(".alacritty.toml", 2, fontQueryToml)) { + if (ffParsePropFileConfigValues(".alacritty.toml", 3, fontQueryToml)) { break; } } while (false); From ebf38e829da1982ef52e7d52356de8fac68ac5b2 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Tue, 11 Aug 2026 11:00:17 +0800 Subject: [PATCH 16/30] OS (Android): reports real code names --- src/detection/os/os_android.c | 50 ++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/detection/os/os_android.c b/src/detection/os/os_android.c index 47ea40b46..0e8a7c1b2 100644 --- a/src/detection/os/os_android.c +++ b/src/detection/os/os_android.c @@ -1,6 +1,43 @@ #include "os.h" #include "common/settings.h" +// https://en.wikipedia.org/wiki/Android_version_history +static const struct { + const char* version; + const char* codename; +} androidCodenames[] = { + { "17", "Cinnamon Bun" }, + { "16", "Baklava" }, + { "15", "Vanilla Ice Cream" }, + { "14", "Upside Down Cake" }, + { "13", "Tiramisu" }, + { "12", "Snow Cone" }, + { "11", "Red Velvet Cake" }, + { "10", "Quince Tart" }, + { "9", "Pie" }, + { "8.1", "Oreo" }, + { "8.0", "Oreo" }, + { "7.1", "Nougat" }, + { "7.0", "Nougat" }, + { "6.0", "Marshmallow" }, + { "5.1", "Lollipop" }, + { "5.0", "Lollipop" }, + { "4.4", "KitKat" }, + { "4.3", "Jelly Bean" }, + { "4.2", "Jelly Bean" }, + { "4.1", "Jelly Bean" }, + { "4.0", "Ice Cream Sandwich" }, + { "3.2", "Honeycomb" }, + { "3.1", "Honeycomb" }, + { "3.0", "Honeycomb" }, + { "2.3", "Gingerbread" }, + { "2.2", "Froyo" }, + { "2.1", "Eclair" }, + { "2.0", "Eclair" }, + { "1.6", "Donut" }, + { "1.5", "Cupcake" }, +}; + void ffDetectOSImpl(FFOSResult* os) { ffStrbufSetStatic(&os->name, "Android"); @@ -8,9 +45,20 @@ void ffDetectOSImpl(FFOSResult* os) { ffSettingsGetAndroidProperty("ro.build.version.release", &os->version); - ffSettingsGetAndroidProperty("ro.build.version.release", &os->versionID); + ffStrbufSet(&os->versionID, &os->version); ffSettingsGetAndroidProperty("ro.build.version.codename", &os->codename); + // On release builds, ro.build.version.codename reports "REL" instead of the real codename. + // In that case, look up the codename from the version table. + if (ffStrbufEqualS(&os->codename, "REL")) { + for (size_t i = 0; i < ARRAY_SIZE(androidCodenames); i++) { + if (ffStrbufEqualS(&os->version, androidCodenames[i].version)) { + ffStrbufSetStatic(&os->codename, androidCodenames[i].codename); + break; + } + } + } + ffSettingsGetAndroidProperty("ro.build.id", &os->buildID); } From a0452b8323aaa9d3b5b6ded435ed6660cee2bbb9 Mon Sep 17 00:00:00 2001 From: JunaidQrysh <163889347+JunaidQrysh@users.noreply.github.com> Date: Tue, 11 Aug 2026 15:11:45 +0530 Subject: [PATCH 17/30] Logo (Image): don't block images support in zellij --- src/logo/image/image.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logo/image/image.c b/src/logo/image/image.c index bc33e67f7..c97e1f7ba 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -996,7 +996,7 @@ bool ffLogoPrintImageIfExists(FFLogoType type, bool printError) { } const char* term = getenv("TERM"); - if ((term && ffStrEquals(term, "screen")) || getenv("ZELLIJ")) { + if (term && ffStrEquals(term, "screen")) { if (printError) { fputs("Logo: Image logo is not supported in terminal multiplexers\n", stderr); } From 51cfd4b5edc589b5a632d527636b994dadecc447 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Tue, 11 Aug 2026 13:52:59 +0800 Subject: [PATCH 18/30] OS (Android): optimises code --- src/detection/os/os_android.c | 76 +++++++++++++---------------------- 1 file changed, 29 insertions(+), 47 deletions(-) diff --git a/src/detection/os/os_android.c b/src/detection/os/os_android.c index 0e8a7c1b2..1e1279d3f 100644 --- a/src/detection/os/os_android.c +++ b/src/detection/os/os_android.c @@ -1,43 +1,6 @@ #include "os.h" #include "common/settings.h" -// https://en.wikipedia.org/wiki/Android_version_history -static const struct { - const char* version; - const char* codename; -} androidCodenames[] = { - { "17", "Cinnamon Bun" }, - { "16", "Baklava" }, - { "15", "Vanilla Ice Cream" }, - { "14", "Upside Down Cake" }, - { "13", "Tiramisu" }, - { "12", "Snow Cone" }, - { "11", "Red Velvet Cake" }, - { "10", "Quince Tart" }, - { "9", "Pie" }, - { "8.1", "Oreo" }, - { "8.0", "Oreo" }, - { "7.1", "Nougat" }, - { "7.0", "Nougat" }, - { "6.0", "Marshmallow" }, - { "5.1", "Lollipop" }, - { "5.0", "Lollipop" }, - { "4.4", "KitKat" }, - { "4.3", "Jelly Bean" }, - { "4.2", "Jelly Bean" }, - { "4.1", "Jelly Bean" }, - { "4.0", "Ice Cream Sandwich" }, - { "3.2", "Honeycomb" }, - { "3.1", "Honeycomb" }, - { "3.0", "Honeycomb" }, - { "2.3", "Gingerbread" }, - { "2.2", "Froyo" }, - { "2.1", "Eclair" }, - { "2.0", "Eclair" }, - { "1.6", "Donut" }, - { "1.5", "Cupcake" }, -}; - void ffDetectOSImpl(FFOSResult* os) { ffStrbufSetStatic(&os->name, "Android"); @@ -47,17 +10,36 @@ void ffDetectOSImpl(FFOSResult* os) { ffStrbufSet(&os->versionID, &os->version); - ffSettingsGetAndroidProperty("ro.build.version.codename", &os->codename); + unsigned major = 0; + for (const char* p = os->version.chars; *p >= '0' && *p <= '9'; ++p) { + major = major * 10 + (unsigned) (*p - '0'); + } - // On release builds, ro.build.version.codename reports "REL" instead of the real codename. - // In that case, look up the codename from the version table. - if (ffStrbufEqualS(&os->codename, "REL")) { - for (size_t i = 0; i < ARRAY_SIZE(androidCodenames); i++) { - if (ffStrbufEqualS(&os->version, androidCodenames[i].version)) { - ffStrbufSetStatic(&os->codename, androidCodenames[i].codename); - break; - } - } + const char* codename = NULL; + // https://en.wikipedia.org/wiki/Android_version_history + // Android 5 is the oldest version supported by Termux + // clang-format off + switch (major) { + case 17: codename = "Cinnamon Bun"; break; + case 16: codename = "Baklava"; break; + case 15: codename = "Vanilla Ice Cream"; break; + case 14: codename = "Upside Down Cake"; break; + case 13: codename = "Tiramisu"; break; + case 12: codename = "Snow Cone"; break; + case 11: codename = "Red Velvet Cake"; break; + case 10: codename = "Quince Tart"; break; + case 9: codename = "Pie"; break; + case 8: codename = "Oreo"; break; + case 7: codename = "Nougat"; break; + case 6: codename = "Marshmallow"; break; + case 5: codename = "Lollipop"; break; + } + // clang-format on + + if (codename) { + ffStrbufSetStatic(&os->codename, codename); + } else { + ffSettingsGetAndroidProperty("ro.build.version.codename", &os->codename); } ffSettingsGetAndroidProperty("ro.build.id", &os->buildID); From 16b10c162b11e0cea3a368d6d5d4f4a6eb206eaa Mon Sep 17 00:00:00 2001 From: Carter Li Date: Wed, 12 Aug 2026 10:07:34 +0800 Subject: [PATCH 19/30] Revert "Locale (macOS): queries system-wide locale preference" This reverts commit 8b8efcdcce100594fc81f10b79df5b13632754c3. --- CMakeLists.txt | 2 +- src/detection/locale/locale_apple.c | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 src/detection/locale/locale_apple.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e86d3d40..94400df85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1038,7 +1038,7 @@ elseif(APPLE) src/detection/lm/lm_nosupport.c src/detection/loadavg/loadavg_bsd.c src/detection/libc/libc_apple.c - src/detection/locale/locale_apple.c + src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_apple.c src/detection/media/media_apple.m diff --git a/src/detection/locale/locale_apple.c b/src/detection/locale/locale_apple.c deleted file mode 100644 index c5a5e8e1a..000000000 --- a/src/detection/locale/locale_apple.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "detection/locale/locale.h" -#include "common/apple/cf_helpers.h" - -const char* ffDetectLocale(FFstrbuf* result) { - // Read the system-wide locale preference (equivalent to `defaults read -g AppleLocale`), - // which is NOT affected by user environment variables (LANG, LC_ALL, etc.) - FF_CFTYPE_AUTO_RELEASE CFStringRef appleLocale = CFPreferencesCopyAppValue(CFSTR("AppleLocale"), kCFPreferencesAnyApplication); - if (!appleLocale) { - return "Failed to read AppleLocale"; - } - - return ffCfStrGetString(appleLocale, result); -} From 1d120035557c04002fbb5660e43e6175dbfa3871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 12 Aug 2026 10:19:22 +0800 Subject: [PATCH 20/30] Locale (Windows): uses $LANG if available e.g. MSYS2 --- src/detection/locale/locale_windows.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/detection/locale/locale_windows.c b/src/detection/locale/locale_windows.c index 0f17e8bfa..4f5896667 100644 --- a/src/detection/locale/locale_windows.c +++ b/src/detection/locale/locale_windows.c @@ -1,9 +1,19 @@ #include "detection/locale/locale.h" #include "common/windows/unicode.h" -#include +#include const char* ffDetectLocale(FFstrbuf* result) { + ffStrbufAppendS(result, getenv("LC_ALL")); + if (result->length > 0) { + return nullptr; + } + + ffStrbufAppendS(result, getenv("LANG")); // Available in MSYS2 and Cygwin + if (result->length > 0) { + return nullptr; + } + wchar_t name[LOCALE_NAME_MAX_LENGTH]; int size = GetUserDefaultLocaleName(name, LOCALE_NAME_MAX_LENGTH); if (size <= 1) { // including '\0' From c4695b8cf10faabe69fce8f8971d034678453886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 12 Aug 2026 10:29:29 +0800 Subject: [PATCH 21/30] Processing (Windows): sets `LANG=C` when spawning child processes --- src/common/impl/processing_windows.c | 56 +++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/common/impl/processing_windows.c b/src/common/impl/processing_windows.c index 5df9f9057..c3f579261 100644 --- a/src/common/impl/processing_windows.c +++ b/src/common/impl/processing_windows.c @@ -48,6 +48,41 @@ static void argvToCmdline(char* const argv[], FFstrbuf* result) { } } +static wchar_t* createChildEnvironment(void) { + wchar_t* source = GetEnvironmentStringsW(); + if (!source) { + return nullptr; + } + + size_t sourceLength = 0; + for (const wchar_t* entry = source; *entry; entry += wcslen(entry) + 1) { + sourceLength += wcslen(entry) + 1; + } + + wchar_t* result = malloc((sourceLength + 7) * sizeof(wchar_t)); + wchar_t* write = result; + bool foundLang = false; + for (const wchar_t* entry = source; *entry; entry += wcslen(entry) + 1) { + if (_wcsnicmp(entry, L"LANG=", 5) == 0) { + wcscpy(write, L"LANG=C"); + write += 6; + foundLang = true; + } else { + size_t entryLength = wcslen(entry) + 1; + memcpy(write, entry, entryLength * sizeof(wchar_t)); + write += entryLength; + } + } + if (!foundLang) { + wcscpy(write, L"LANG=C"); + write += 6; + } + *write = L'\0'; + + FreeEnvironmentStringsW(source); + return result; +} + const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle) { const int32_t timeout = instance.config.general.processingTimeout; @@ -108,17 +143,18 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* } } + FF_AUTO_FREE wchar_t* environment = createChildEnvironment(); BOOL success = CreateProcessW( - nullptr, // application name - cmdline, // command line - nullptr, // process security attributes - nullptr, // primary thread security attributes - TRUE, // handles are inherited - 0, // creation flags - nullptr, // use parent's environment - nullptr, // use parent's current directory - &siStartInfo, // STARTUPINFO pointer - &piProcInfo // receives PROCESS_INFORMATION + nullptr, // application name + cmdline, // command line + nullptr, // process security attributes + nullptr, // primary thread security attributes + TRUE, // handles are inherited + CREATE_UNICODE_ENVIRONMENT, // creation flags + environment, // child environment + nullptr, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo // receives PROCESS_INFORMATION ); NtClose(hChildPipeWrite); From 681749ed5401c74c0e68e0062fa6d3a838748dfe Mon Sep 17 00:00:00 2001 From: njk Date: Wed, 12 Aug 2026 20:04:19 -0500 Subject: [PATCH 22/30] TerminalFont (Ghostty): fixes font-family fallback order (#2510) --- src/detection/terminalfont/terminalfont.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index 8c54a8500..463abeed7 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -98,13 +98,17 @@ static void parseGhosttyConfig(FFstrbuf* path, FFstrbuf* fontName, FFstrbuf* fon while (ffStrbufGetline(&line, &len, &buffer)) { if (ffParsePropLine(line, "font-family =", &temp)) { FF_DEBUG("found font-family='%s' in %s", temp.chars, path->chars); - // Latter overrides former; former becomes the fallback font - if (fontName->length > 0) { - ffStrbufDestroy(fontNameFallback); - ffStrbufInitMove(fontNameFallback, fontName); + // Ghostty appends to a fallback list; the first entry is the primary font. + // An empty value resets the list. + if (temp.length == 0) { + ffStrbufClear(fontName); + ffStrbufClear(fontNameFallback); + } else if (fontName->length == 0) { + ffStrbufSet(fontName, &temp); + } else if (fontNameFallback->length == 0) { + ffStrbufSet(fontNameFallback, &temp); } - ffStrbufDestroy(fontName); - ffStrbufInitMove(fontName, &temp); + ffStrbufClear(&temp); } else if (ffParsePropLine(line, "font-size =", &temp)) { FF_DEBUG("found font-size='%s' in %s", temp.chars, path->chars); // Latter overrides former From 7098cd86cb4f2c3d12175e583f4d53707a10541d Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 13 Aug 2026 09:46:58 +0800 Subject: [PATCH 23/30] Display: fixes bright colours incorrectly active in light theme Fixes #2509 --- src/common/impl/init.c | 12 ------------ src/fastfetch.h | 1 - src/options/display.c | 29 ++++++++++++++++++++++------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/common/impl/init.c b/src/common/impl/init.c index 8f6edbe7f..885b0b6ee 100644 --- a/src/common/impl/init.c +++ b/src/common/impl/init.c @@ -5,7 +5,6 @@ #include "common/textModifier.h" #include "common/strutil.h" #include "detection/displayserver/displayserver.h" -#include "detection/terminaltheme/terminaltheme.h" #include "logo/logo.h" #include @@ -27,21 +26,10 @@ static void initState(FFstate* state) { state->logoWidth = 0; state->logoHeight = 0; state->keysHeight = 0; - state->terminalLightTheme = false; state->titleFqdn = false; ffPlatformInit(&state->platform); state->dynamicInterval = 0; - - #if !FF_MODULE_DISABLE_TERMINALTHEME - { - // don't enable bright color if the terminal is in light mode - FFTerminalThemeResult result; - if (ffDetectTerminalTheme(&result, true /* forceEnv for performance */) && !result.bg.dark) { - state->terminalLightTheme = true; - } - } - #endif } static void defaultConfig(void) { diff --git a/src/fastfetch.h b/src/fastfetch.h index ac9fa6c26..a8d34869f 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -31,7 +31,6 @@ typedef struct FFstate { uint32_t logoWidth; uint32_t logoHeight; uint32_t keysHeight; - bool terminalLightTheme; bool titleFqdn; uint32_t dynamicInterval; FFPlatform platform; diff --git a/src/options/display.c b/src/options/display.c index a1136e7b1..d515e92c1 100644 --- a/src/options/display.c +++ b/src/options/display.c @@ -5,6 +5,10 @@ #include "common/strutil.h" #include "options/display.h" +#if !FF_MODULE_DISABLE_TERMINALTHEME + #include "detection/terminaltheme/terminaltheme.h" +#endif + #include const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_val* root, yyjson_val** pkey) { @@ -825,11 +829,22 @@ bool ffOptionsParseDisplayCommandLine(FFOptionsDisplay* options, const char* key } void ffOptionsInitDisplay(FFOptionsDisplay* options) { + bool terminalLightTheme = false; + #if !FF_MODULE_DISABLE_TERMINALTHEME + { + // don't enable bright color if the terminal is in light mode + FFTerminalThemeResult result; + if (ffDetectTerminalTheme(&result, true /* forceEnv for performance */) && !result.bg.dark) { + terminalLightTheme = true; + } + } + #endif + ffStrbufInit(&options->colorKeys); ffStrbufInit(&options->colorTitle); ffStrbufInit(&options->colorOutput); ffStrbufInit(&options->colorSeparator); - options->brightColor = !instance.state.terminalLightTheme; + options->brightColor = !terminalLightTheme; ffStrbufInitStatic(&options->keyValueSeparator, ": "); options->showErrors = false; @@ -856,8 +871,8 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) { options->tempUnit = FF_TEMPERATURE_UNIT_DEFAULT; options->tempNdigits = 1; ffStrbufInitStatic(&options->tempColorGreen, FF_COLOR_FG_GREEN); - ffStrbufInitStatic(&options->tempColorYellow, instance.state.terminalLightTheme ? FF_COLOR_FG_YELLOW : FF_COLOR_FG_LIGHT_YELLOW); - ffStrbufInitStatic(&options->tempColorRed, instance.state.terminalLightTheme ? FF_COLOR_FG_RED : FF_COLOR_FG_LIGHT_RED); + ffStrbufInitStatic(&options->tempColorYellow, terminalLightTheme ? FF_COLOR_FG_YELLOW : FF_COLOR_FG_LIGHT_YELLOW); + ffStrbufInitStatic(&options->tempColorRed, terminalLightTheme ? FF_COLOR_FG_RED : FF_COLOR_FG_LIGHT_RED); options->tempSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_DEFAULT; ffStrbufInitStatic(&options->barCharElapsed, "■"); @@ -867,8 +882,8 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) { ffStrbufInit(&options->barBorderLeftElapsed); ffStrbufInit(&options->barBorderRightElapsed); ffStrbufInitStatic(&options->barColorElapsed, "auto"); - ffStrbufInitStatic(&options->barColorTotal, instance.state.terminalLightTheme ? FF_COLOR_FG_WHITE : FF_COLOR_FG_LIGHT_WHITE); - ffStrbufInitStatic(&options->barColorBorder, instance.state.terminalLightTheme ? FF_COLOR_FG_WHITE : FF_COLOR_FG_LIGHT_WHITE); + ffStrbufInitStatic(&options->barColorTotal, terminalLightTheme ? FF_COLOR_FG_WHITE : FF_COLOR_FG_LIGHT_WHITE); + ffStrbufInitStatic(&options->barColorBorder, terminalLightTheme ? FF_COLOR_FG_WHITE : FF_COLOR_FG_LIGHT_WHITE); options->barWidth = 10; options->durationAbbreviation = false; @@ -876,8 +891,8 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) { options->percentType = FF_PERCENTAGE_TYPE_NUM_BIT | FF_PERCENTAGE_TYPE_NUM_COLOR_BIT; options->percentNdigits = 0; ffStrbufInitStatic(&options->percentColorGreen, FF_COLOR_FG_GREEN); - ffStrbufInitStatic(&options->percentColorYellow, instance.state.terminalLightTheme ? FF_COLOR_FG_YELLOW : FF_COLOR_FG_LIGHT_YELLOW); - ffStrbufInitStatic(&options->percentColorRed, instance.state.terminalLightTheme ? FF_COLOR_FG_RED : FF_COLOR_FG_LIGHT_RED); + ffStrbufInitStatic(&options->percentColorYellow, terminalLightTheme ? FF_COLOR_FG_YELLOW : FF_COLOR_FG_LIGHT_YELLOW); + ffStrbufInitStatic(&options->percentColorRed, terminalLightTheme ? FF_COLOR_FG_RED : FF_COLOR_FG_LIGHT_RED); options->percentSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_DEFAULT; options->percentWidth = 0; From 8df0a1c97e32a5009d8f238d856104be44f9f416 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 13 Aug 2026 09:50:04 +0800 Subject: [PATCH 24/30] Doc: removes Star History in README.md Fixes #2508 --- README.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/README.md b/README.md index d2b0a53d9..32c228314 100644 --- a/README.md +++ b/README.md @@ -330,15 +330,3 @@ If you find Fastfetch useful, please consider donating. * Free code signing provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/) * This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it - -## Star History - -Give us a star to show your support! - - - - - - Star History Chart - - From 368a4c5a73ceceba97ee0eb069a4c04350dd4839 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 13 Aug 2026 10:58:11 +0800 Subject: [PATCH 25/30] Release v2.67.1 --- CHANGELOG.md | 17 ++++++++++++++--- CMakeLists.txt | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1088ace29..2bfcb457c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,20 @@ # 2.67.1 Bugfixes: -* Fixed `Symbol not found` error when running on macOS 10.15 (macOS) -* Fixed building on Apple clang 16 (the default compiler of macOS 14) -* Added a C23 compiler check so the build process exits during CMake configuration +* Fixed a `Symbol not found` error when running on macOS 10.15. (Codec, macOS) +* Fixed Alacritty font size never being parsed from the TOML config. (TerminalFont) +* Fixed Ghostty `font-family` fallback order. (#2510, TerminalFont) +* Fixed bright colors incorrectly being active when the terminal uses a light theme. (#2509, Display) +* Fixed Ghostty terminal font detection by trying to load the config with the `.ghostty` extension. (#2500, TerminalFont) +* Added compatibility workarounds for Apple Clang 14 to fix building on macOS 14. (CMake, macOS) +* Some internal cleanups and optimizations. + +Features: +* Added a C23 compiler check so the build process gracefully exits during CMake configuration if the compiler is unsupported. (CMake) +* Reported real Android version codenames (e.g., "Baklava", "Vanilla Ice Cream") instead of "REL". (OS, Android) +* Added Loadavg detection support on Haiku R1B6. (Loadavg, Haiku) +* Enabled image logo support in the Zellij terminal multiplexer. (Logo) +* Improved Locale detection on Windows to use `$LC_ALL` or `$LANG` environment variables if available (e.g., in MSYS2 or Cygwin). (Locale, Windows) # 2.67.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 94400df85..687e89b5e 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.67.0 + VERSION 2.67.1 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" From 5ea299a8d2f6121d09bd67f467ccf5aa8f0970f8 Mon Sep 17 00:00:00 2001 From: OctoBored <212877535+OctoBored@users.noreply.github.com> Date: Thu, 13 Aug 2026 09:26:14 +0000 Subject: [PATCH 26/30] Doc: restores Star History in README.md The Star History chart was removed in 8df0a1c9 (Fixes #2508) because the previous chart was broken due to GitHub stargazer API restrictions. This restores it at the same location using a working data source that requires no API token, so the chart renders correctly again. --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 32c228314..7f2481cbd 100644 --- a/README.md +++ b/README.md @@ -330,3 +330,15 @@ If you find Fastfetch useful, please consider donating. * Free code signing provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/) * This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it + +## Star History + +Give us a star to show your support! + + + + + + Star History Chart + + From 00603357ea297237c325122e3b5578a10aa9a2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Thu, 13 Aug 2026 15:41:43 +0800 Subject: [PATCH 27/30] Processing (Windows): improves performance of environement string creation --- src/common/impl/processing_windows.c | 48 ++++++++++++++-------------- src/common/windows/nt.h | 3 ++ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/common/impl/processing_windows.c b/src/common/impl/processing_windows.c index c3f579261..e90195dfe 100644 --- a/src/common/impl/processing_windows.c +++ b/src/common/impl/processing_windows.c @@ -49,37 +49,37 @@ static void argvToCmdline(char* const argv[], FFstrbuf* result) { } static wchar_t* createChildEnvironment(void) { - wchar_t* source = GetEnvironmentStringsW(); - if (!source) { + auto pparams = ffGetPeb()->ProcessParameters; + const wchar_t* readStart = pparams->Environment; + + if (!readStart) { return nullptr; } - size_t sourceLength = 0; - for (const wchar_t* entry = source; *entry; entry += wcslen(entry) + 1) { - sourceLength += wcslen(entry) + 1; - } + RtlAcquirePebLock(); + wchar_t* result = malloc(pparams->EnvironmentSize + sizeof(L"LANG=C.UTF-8")); - wchar_t* result = malloc((sourceLength + 7) * sizeof(wchar_t)); - wchar_t* write = result; - bool foundLang = false; - for (const wchar_t* entry = source; *entry; entry += wcslen(entry) + 1) { - if (_wcsnicmp(entry, L"LANG=", 5) == 0) { - wcscpy(write, L"LANG=C"); - write += 6; - foundLang = true; - } else { - size_t entryLength = wcslen(entry) + 1; - memcpy(write, entry, entryLength * sizeof(wchar_t)); - write += entryLength; + const wchar_t* entry = readStart; + wchar_t* writeStart = result; + + // Copy all environment variables except LANG + while (*entry) { + assert((const uint8_t*) entry <= (const uint8_t*) pparams->Environment + pparams->EnvironmentSize); + size_t entryLength = wcslen(entry); + if (entryLength >= strlen("LANG=") && memcmp(entry, L"LANG=", strlen("LANG=") * sizeof(wchar_t)) == 0) { + memcpy(writeStart, readStart, (size_t) (entry - readStart) * sizeof(wchar_t)); + writeStart += (size_t) (entry - readStart); + readStart = entry + entryLength + 1; } + entry += entryLength + 1; } - if (!foundLang) { - wcscpy(write, L"LANG=C"); - write += 6; - } - *write = L'\0'; + // Copy the remaining environment variables + memcpy(writeStart, readStart, (size_t) (entry - readStart) * sizeof(wchar_t)); + writeStart += (size_t) (entry - readStart); + // Add LANG=C.UTF-8 and double null terminator + memcpy(writeStart, L"LANG=C.UTF-8\0", sizeof(L"LANG=C.UTF-8\0")); - FreeEnvironmentStringsW(source); + RtlReleasePebLock(); return result; } diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h index f00384984..730a75ba8 100644 --- a/src/common/windows/nt.h +++ b/src/common/windows/nt.h @@ -1339,3 +1339,6 @@ NTSYSAPI NTSTATUS NTAPI NtCancelIoFileEx( NTSYSAPI NTSTATUS NTAPI NtTerminateProcess( _In_opt_ HANDLE ProcessHandle, _In_ NTSTATUS ExitStatus); + +NTSYSAPI NTSTATUS NTAPI RtlAcquirePebLock(VOID); +NTSYSAPI NTSTATUS NTAPI RtlReleasePebLock(VOID); From 965ed4ba791865b6d92c5df715ff841a6d03bff5 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 13 Aug 2026 16:08:34 +0800 Subject: [PATCH 28/30] Library (Windows): don't assume `DataTableEntry->FullDllName` is null-terminated --- src/common/impl/library.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/impl/library.c b/src/common/impl/library.c index 3c34c79f8..83a315d97 100644 --- a/src/common/impl/library.c +++ b/src/common/impl/library.c @@ -193,7 +193,8 @@ static void ffLibraryIterateDynamicLibsCallback(PLDR_DATA_TABLE_ENTRY DataTableE char path[PATH_MAX * 3]; ULONG outBytes; - if (NT_SUCCESS(RtlUnicodeToUTF8N(path, sizeof(path), &outBytes, DataTableEntry->FullDllName.Buffer, (uint32_t) (DataTableEntry->FullDllName.Length + sizeof(wchar_t))))) { + if (NT_SUCCESS(RtlUnicodeToUTF8N(path, sizeof(path), &outBytes, DataTableEntry->FullDllName.Buffer, DataTableEntry->FullDllName.Length))) { + path[outBytes] = '\0'; struct LibraryIterateDynamicLibsBundle* bundle = (struct LibraryIterateDynamicLibsBundle*) Context; *StopEnumeration = !bundle->callback(path, bundle->userData); } From 7b2b3405ae5f412b78a9a5e99a3451f9c0d89242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 14 Aug 2026 11:01:22 +0800 Subject: [PATCH 29/30] Locale (Windows): reports the code page used by the console --- src/common/FFPlatform.h | 4 ++ src/common/impl/FFPlatform.c | 1 + src/common/impl/init.c | 6 ++- src/detection/locale/locale_windows.c | 63 ++++++++++++++++++++++++++- 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/common/FFPlatform.h b/src/common/FFPlatform.h index cf8224dda..8655563e3 100644 --- a/src/common/FFPlatform.h +++ b/src/common/FFPlatform.h @@ -31,6 +31,10 @@ typedef struct FFPlatform { FFstrbuf userShell; FFPlatformSysinfo sysinfo; + +#if _WIN32 + uint32_t initCP; // The code page used by the console when the program started +#endif } FFPlatform; void ffPlatformInit(FFPlatform* platform); diff --git a/src/common/impl/FFPlatform.c b/src/common/impl/FFPlatform.c index ff4c45cd2..5af48610b 100644 --- a/src/common/impl/FFPlatform.c +++ b/src/common/impl/FFPlatform.c @@ -18,6 +18,7 @@ void ffPlatformInit(FFPlatform* platform) { #ifdef _WIN32 ffStrbufInit(&platform->sid); + platform->initCP = 0; #endif FFPlatformSysinfo* info = &platform->sysinfo; diff --git a/src/common/impl/init.c b/src/common/impl/init.c index 885b0b6ee..f4307e1f9 100644 --- a/src/common/impl/init.c +++ b/src/common/impl/init.c @@ -39,7 +39,7 @@ static void defaultConfig(void) { } #ifdef _WIN32 -static volatile UINT oldCp = CP_UTF8; +static UINT oldCp = CP_UTF8; void resetConsoleCP(void) { if (oldCp != CP_UTF8) { SetConsoleOutputCP(oldCp); @@ -69,6 +69,10 @@ void ffInitInstance(void) { defaultConfig(); initState(&instance.state); + +#ifdef _WIN32 + instance.state.platform.initCP = oldCp; +#endif } static volatile bool ffDisableLinewrap = false; diff --git a/src/detection/locale/locale_windows.c b/src/detection/locale/locale_windows.c index 4f5896667..75a0e47f0 100644 --- a/src/detection/locale/locale_windows.c +++ b/src/detection/locale/locale_windows.c @@ -19,8 +19,69 @@ const char* ffDetectLocale(FFstrbuf* result) { if (size <= 1) { // including '\0' return "GetUserDefaultLocaleName() failed"; } + ffStrbufAppendNWS(result, (uint32_t) size - 1, name); + if (result->length > 2 && result->chars[2] == '-') { // Windows uses '-' instead of '_' + result->chars[2] = '_'; + } - ffStrbufSetNWS(result, (uint32_t) size - 1, name); + uint32_t codePage = instance.state.platform.initCP; + if (__builtin_expect(codePage != 0, true)) { + ffStrbufAppendC(result, '.'); + + switch (codePage) { + case CP_UTF8: + ffStrbufAppendS(result, "UTF-8"); + break; + case CP_UTF7: + ffStrbufAppendS(result, "UTF-7"); + break; + case 1200: + ffStrbufAppendS(result, "UTF-16LE"); + break; + case 1201: + ffStrbufAppendS(result, "UTF-16BE"); + break; + case 936: + ffStrbufAppendS(result, "GBK"); + break; + case 54936: + ffStrbufAppendS(result, "GB18030"); + break; + case 950: + ffStrbufAppendS(result, "BIG5"); + break; + case 932: + ffStrbufAppendS(result, "Shift_JIS"); + break; + case 949: + ffStrbufAppendS(result, "EUC-KR"); + break; + case 20932: + ffStrbufAppendS(result, "EUC-JP"); + break; + + case 20866: + ffStrbufAppendS(result, "KOI8-R"); + break; + case 21866: + ffStrbufAppendS(result, "KOI8-U"); + break; + case 20127: + ffStrbufAppendS(result, "US-ASCII"); + break; + + case 874: + case 1250 ... 1258: + ffStrbufAppendS(result, "Windows-"); + ffStrbufAppendUInt(result, codePage); + break; + + default: + ffStrbufAppendS(result, "CP"); + ffStrbufAppendUInt(result, codePage); + break; + } + } return nullptr; } From 60431c032166324ab5082ebbf0798b5113d6ccce Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 14 Aug 2026 13:50:22 +0800 Subject: [PATCH 30/30] Doc: update changelog [ci skip] --- CHANGELOG.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bfcb457c..598addb3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,18 +3,21 @@ Bugfixes: * Fixed a `Symbol not found` error when running on macOS 10.15. (Codec, macOS) * Fixed Alacritty font size never being parsed from the TOML config. (TerminalFont) -* Fixed Ghostty `font-family` fallback order. (#2510, TerminalFont) * Fixed bright colors incorrectly being active when the terminal uses a light theme. (#2509, Display) -* Fixed Ghostty terminal font detection by trying to load the config with the `.ghostty` extension. (#2500, TerminalFont) -* Added compatibility workarounds for Apple Clang 14 to fix building on macOS 14. (CMake, macOS) +* Fixed Ghostty terminal font detection (TerminalFont) + * Now tries to load the config with the `.ghostty` extension. (#2500) + * Fixed `font-family` fallback order. (#2510) * Some internal cleanups and optimizations. Features: * Added a C23 compiler check so the build process gracefully exits during CMake configuration if the compiler is unsupported. (CMake) +* Added compatibility workarounds for Apple Clang 16 to fix building on macOS 14. (CMake, macOS) * Reported real Android version codenames (e.g., "Baklava", "Vanilla Ice Cream") instead of "REL". (OS, Android) * Added Loadavg detection support on Haiku R1B6. (Loadavg, Haiku) -* Enabled image logo support in the Zellij terminal multiplexer. (Logo) -* Improved Locale detection on Windows to use `$LC_ALL` or `$LANG` environment variables if available (e.g., in MSYS2 or Cygwin). (Locale, Windows) +* Enabled image logo support in the Zellij terminal multiplexer. (#2507, Logo) +* Improved Locale detection on Windows (Locale, Windows) + * Now respects `LC_ALL` and `LANG` environment variables if set for better compatibility with MSYS2/cygwin. + * Appends the active system Code Page (e.g., `.UTF-8` or `.Windows-1252`) to the default system locale fallback. # 2.67.0