Compare commits

...

14 Commits

Author SHA1 Message Date
Carter Li 636471c366 Merge pull request #2375 from fastfetch-cli/dev
Release v2.64.2
2026-06-05 18:36:58 +08:00
李通洲 cbc84fda6c Common (Lua): addresses AI's feedback 2026-06-05 18:21:28 +08:00
李通洲 fc7a0a8484 Codec (Linux): supports BINARY_LINK_TYPE=dynamic 2026-06-05 18:05:54 +08:00
李通洲 4d3b6c3b1b Common (QJS): supports BINARY_LINK_TYPE=dynamic 2026-06-05 17:49:49 +08:00
李通洲 70e67177fe Common (Lua): supports BINARY_LINK_TYPE=dynamic 2026-06-05 17:22:16 +08:00
李通洲 9fe7d6dbfa Logo: addresses review comments of AI 2026-06-05 16:34:02 +08:00
Carter Li a139a8c4a5 Doc: updates changelog [ci skip] 2026-06-05 16:22:04 +08:00
Valeriy Kosikhin da568c986e Logo (Builtin): update OpenWrt logo
Update the large logo for OpenWrt based on the official branding.
Add a small logo version.
Keep the old logo as openwrt_old.txt

Signed-off-by: Valeriy Kosikhin <vkosikhin@gmail.com>
2026-06-05 16:15:46 +08:00
Carter Li 727fbc0276 Doc: updates changelog 2026-06-05 15:29:00 +08:00
Carter Li 20aabdfd78 Global: resets keysHeight after every refresh 2026-06-05 15:27:25 +08:00
Carter Li e79aaf5ce1 Global: don't clear line in --logo-position right 2026-06-05 15:27:15 +08:00
Carter Li 53c2d07161 Release: v2.64.2 2026-06-05 14:51:50 +08:00
Carter Li 6b9f594620 Global: prevents ascii logo from being overwritten in --dynamic-interval mode 2026-06-05 14:47:01 +08:00
Carter Li ba3968784f Logo (Image): fixes image renders but quickly gets wiped
Regression from v2.64.0

Fixes: #2374
2026-06-05 13:12:07 +08:00
10 changed files with 147 additions and 62 deletions
+12
View File
@@ -1,3 +1,15 @@
# 2.64.2
Bugfixes:
* Fixes image rendering being wiped quickly (#2374)
* Regression from v2.64.0
* Fixes ASCII logo being overwritten in `--dynamic-interval` mode
* Regression from v2.64.0
Logos:
* Updates OpenWrt and adds a small variant (#2376)
* The old one is renamed to `openwrt_old`
# 2.64.1
Features:
+8 -3
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
project(fastfetch
VERSION 2.64.1
VERSION 2.64.2
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -1638,6 +1638,11 @@ function(ff_lib_enable VARNAME PKGCONFIG_NAMES CMAKE_NAME) # [CMAKE_TARGET_NAME]
set(${VARNAME}_LIBRARIES ${_FF_CMAKE_TARGET})
endif()
endif()
if(NOT BINARY_LINK_TYPE STREQUAL "dlopen")
# INTERFACE_LINK_LIBRARIES doesn't expose libvarname.a
target_link_libraries(libfastfetch PRIVATE ${_FF_CMAKE_TARGET})
endif()
endif()
else()
set(${VARNAME}_INCLUDE_DIRS ${${CMAKE_NAME}_INCLUDE_DIRS})
@@ -1691,8 +1696,8 @@ ff_lib_enable(DRM
"Libdrm"
)
ff_lib_enable(VA
"libva"
"Libva"
"libva-drm"
"Libva-drm"
)
ff_lib_enable(VDPAU
"vdpau"
+5 -4
View File
@@ -9,9 +9,7 @@ static yyjson_mut_val* lua2yyjson(lua_State* L, int idx, yyjson_mut_doc* doc, in
if (__builtin_expect(depth > 15, false)) {
yyjson_mut_doc_free(doc);
lua_pushlstring(
L, "yyjson: recursion depth exceeded; possible circular reference",
strlen("yyjson: recursion depth exceeded; possible circular reference")
);
L, "yyjson: recursion depth exceeded; possible circular reference", strlen("yyjson: recursion depth exceeded; possible circular reference"));
lua_error(L); // noreturn
__builtin_unreachable();
}
@@ -140,7 +138,7 @@ static int yyjsonEncode(lua_State* L) {
}
}
const char* ffLuaLoadState() {
const char* ffLuaLoadState(void) {
if (luaData.inited) {
if (luaData.L == NULL) {
return "Lua library is not available";
@@ -180,6 +178,8 @@ const char* ffLuaLoadState() {
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_string)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_table)
#endif
#if !FF_DISABLE_DLOPEN
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, luaL_checkany)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, luaL_loadbufferx)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, luaL_tolstring)
@@ -208,6 +208,7 @@ const char* ffLuaLoadState() {
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_tolstring)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_tonumberx)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_type)
#endif
lua_State* L = ffluaL_newstate();
if (L == NULL) {
+21 -14
View File
@@ -3,16 +3,19 @@
#include "fastfetch.h"
#if FF_HAVE_LUA
// Hack. LUA_API is defined as extern which prevents us from implementing the functions ourselves.
#include <luaconf.h>
#undef LUA_API
#undef LUALIB_API
#undef LUAMOD_API
#define LUA_API static inline
#define LUALIB_API LUA_API
#define LUAMOD_API LUA_API
#if !FF_DISABLE_DLOPEN
// Hack. LUA_API is defined as extern which prevents us from implementing the functions ourselves.
#include <luaconf.h>
#undef LUA_API
#undef LUALIB_API
#undef LUAMOD_API
#define LUA_API static inline
#define LUALIB_API LUA_API
#define LUAMOD_API LUA_API
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#pragma GCC diagnostic ignored "-Wunused-function"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
@@ -24,6 +27,7 @@
#include "common/library.h"
extern struct FFLuaData {
#if !FF_DISABLE_DLOPEN
FF_LIBRARY_SYMBOL(luaL_checkany)
FF_LIBRARY_SYMBOL(luaL_loadbufferx)
FF_LIBRARY_SYMBOL(luaL_tolstring)
@@ -52,13 +56,13 @@ extern struct FFLuaData {
FF_LIBRARY_SYMBOL(lua_tolstring)
FF_LIBRARY_SYMBOL(lua_tonumberx)
FF_LIBRARY_SYMBOL(lua_type)
#endif
lua_State* L;
bool inited;
} luaData;
const char* ffLuaLoadState();
#if !FF_DISABLE_DLOPEN
FF_A_ALWAYS_INLINE void(lua_settop)(lua_State* L, int idx) {
return luaData.fflua_settop(L, idx);
}
@@ -148,11 +152,11 @@ FF_A_ALWAYS_INLINE int(lua_rawgeti)(lua_State* L, int idx, lua_Integer n) {
}
FF_A_ALWAYS_INLINE
#if LUA_VERSION_NUM > 503
#if LUA_VERSION_NUM > 503
lua_Unsigned
#else
#else
size_t
#endif
#endif
(lua_rawlen)(lua_State* L, int idx) {
return luaData.fflua_rawlen(L, idx);
}
@@ -176,5 +180,8 @@ FF_A_ALWAYS_INLINE lua_Number(lua_tonumberx)(lua_State* L, int idx, int* isnum)
FF_A_ALWAYS_INLINE int(lua_type)(lua_State* L, int idx) {
return luaData.fflua_type(L, idx);
}
#endif
const char* ffLuaLoadState(void);
#endif
+4 -1
View File
@@ -765,9 +765,12 @@ static void run(FFdata* data) {
}
if (instance.state.dynamicInterval > 0) {
ffLogoPrintRemaining(); // `logoLineCacheClear` inside so that ffLogoPrintLine will use `\e[nC` to move the cursor to the right position instead of reprinting the logo
fputs("\e[J", stdout); // Clear from cursor to the end of the screen to prevent artifacts when the new output is shorter than the previous one
fflush(stdout);
ffTimeSleep(instance.state.dynamicInterval);
fputs("\e[H", stdout);
fputs("\e[H", stdout); // Move cursor to the top left corner to overwrite the previous output
instance.state.keysHeight = 0; // Reset keysHeight so `ffLogoPrintRemaining` will recalculate it
} else {
break;
}
+28
View File
@@ -291,6 +291,34 @@ static const FFlogo O[] = {
{
.names = { "openwrt" },
.lines = FASTFETCH_DATATEXT_LOGO_OPENWRT,
.colors = {
FF_COLOR_FG_DEFAULT,
FF_COLOR_FG_BLUE,
},
.colorKeys = FF_COLOR_FG_BLUE,
.colorTitle = FF_COLOR_FG_DEFAULT,
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_OPENWRT_SMALL
// OpenWrtSmall
{
.names = { "openwrt_small" },
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_OPENWRT_SMALL,
.colors = {
FF_COLOR_FG_DEFAULT,
FF_COLOR_FG_BLUE,
},
.colorKeys = FF_COLOR_FG_BLUE,
.colorTitle = FF_COLOR_FG_DEFAULT,
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_OPENWRT_OLD
// OpenWrtOld
{
.names = { "openwrt_old" },
.type = FF_LOGO_LINE_TYPE_ALTER_BIT,
.lines = FASTFETCH_DATATEXT_LOGO_OPENWRT_OLD,
.colors = {
FF_COLOR_FG_BLUE,
},
+18 -9
View File
@@ -1,9 +1,18 @@
_______
| |.-----.-----.-----.
| - || _ | -__| |
|_______|| __|_____|__|__|
|__|
________ __
| | | |.----.| |_
| | | || _|| _|
|________||__| |____|
..,,..
,;odOMMMMMMMMObo:.
,odOMMMMMO""''""OMMMMMObo.
.dMMMM" .,,. "OMMMb,
'0" ,;ddMMMMMMMMbb:. "0'
oOMMMMO""''""OMMMMOo
$2;. $1'0" .,,. "0' $2.:
.OMM* $1.odMMMMMMbo. $2*MMO.
MMMO $1"0"````"0" $2OMMM
iOMM $1.oo. $2MMMi
OMMl $1:MMMM: $2lMMO
iMMM $1'oo' $2MMMi
OMMb $2dMMO
'OMMO. ,OMMO'
"MMMb. ,dMMM"
"MMMMbgo-,,-ogdMMMM"
"*MMMMMMMMMM*"
`'""'`
+9
View File
@@ -0,0 +1,9 @@
_______
| |.-----.-----.-----.
| - || _ | -__| |
|_______|| __|_____|__|__|
|__|
________ __
| | | |.----.| |_
| | | || _|| _|
|________||__| |____|
+9
View File
@@ -0,0 +1,9 @@
..,..
,odM"""""Mbo,
`.odMMMMMbo.`
$2d,$1" ,mmm, "$2,b
iM $1" . " $2Mi
Ml $1dMb $2lM
'M, $1' $2.M'
"0b. ,d0"
`"WWWWW"`
+33 -31
View File
@@ -461,7 +461,7 @@ static bool updateLogoPath(void) {
return true;
}
#if !FF_MODULE_DISABLE_MEDIA
#if !FF_MODULE_DISABLE_MEDIA
if (ffStrbufIgnCaseEqualS(&options->source, "media-cover")) {
const FFMediaResult* media = ffDetectMedia(true);
if (media->cover.length == 0) {
@@ -470,7 +470,7 @@ static bool updateLogoPath(void) {
ffStrbufSet(&options->source, &media->cover);
return true;
}
#endif
#endif
FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreateA(128);
if (ffPathExpandEnv(options->source.chars, &fullPath) && ffPathExists(fullPath.chars, FF_PATHTYPE_FILE)) {
@@ -639,7 +639,7 @@ void ffLogoPrint(void) {
}
if (!ffStrbufEndsWithIgnCaseS(&options->source, ".txt")) {
#if !FF_MODULE_DISABLE_TERMINAL
#if !FF_MODULE_DISABLE_TERMINAL
const FFTerminalResult* terminal = ffDetectTerminal();
bool supportsIterm2 = ffStrbufEqualS(&terminal->prettyName, "iTerm");
@@ -655,15 +655,15 @@ void ffLogoPrint(void) {
ffStrbufIgnCaseEqualS(&terminal->processName, "wezterm") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "wayst") ||
ffStrbufIgnCaseEqualS(&terminal->processName, "ghostty") ||
#ifdef __APPLE__
#ifdef __APPLE__
ffStrbufIgnCaseEqualS(&terminal->processName, "WarpTerminal") ||
#else
#else
ffStrbufIgnCaseEqualS(&terminal->processName, "warp") ||
#endif
#endif
false;
#else
#else
bool supportsKitty = false;
#endif
#endif
// Try to load the logo as an image. If it succeeds, print it and return.
if (logoPrintImageIfExists(supportsKitty ? FF_LOGO_TYPE_IMAGE_KITTY : FF_LOGO_TYPE_IMAGE_CHAFA, false)) {
@@ -685,37 +685,39 @@ void ffLogoPrint(void) {
}
void ffLogoPrintLine(void) {
uint32_t printedLineWidth = 0;
FFLogoLineCacheState* cache = &instance.state.logoLineCache;
FFOptionsLogo* logo = &instance.config.logo;
if (cache->lines.length > 0 && (logo->position == FF_LOGO_POSITION_LEFT || logo->position == FF_LOGO_POSITION_RIGHT) && cache->nextLine < cache->lines.length) {
FFLogoCachedLine* line = FF_LIST_GET(FFLogoCachedLine, cache->lines, cache->nextLine);
if (cache->lines.length > 0) {
// Line cache is enabled. Always move cursor with whitespaces to make lolcat happy
if (cache->nextLine < cache->lines.length) {
// Print logo line and move cursor
FFLogoCachedLine* line = FF_LIST_GET(FFLogoCachedLine, cache->lines, cache->nextLine);
if (logo->position == FF_LOGO_POSITION_RIGHT && line->chars.length > 0) {
printf("\033[9999999C\033[%uD", cache->rightOffset);
ffStrbufWriteTo(&line->chars, stdout);
fputs("\033[G", stdout);
} else {
ffStrbufWriteTo(&line->chars, stdout);
printedLineWidth = line->width;
if (logo->position == FF_LOGO_POSITION_RIGHT) {
printf("\033[9999999C\033[%uD", cache->rightOffset);
ffStrbufWriteTo(&line->chars, stdout);
fputs("\033[G", stdout);
} else {
ffStrbufWriteTo(&line->chars, stdout);
uint32_t remaining = instance.state.logoWidth;
remaining = line->width < remaining ? remaining - line->width : 0;
ffPrintCharTimes(' ', remaining);
}
++cache->nextLine;
} else if (logo->position == FF_LOGO_POSITION_LEFT) {
// Move cursor to the start position
ffPrintCharTimes(' ', instance.state.logoWidth);
}
++cache->nextLine;
} else if (instance.state.logoWidth > 0) {
printf("\033[%uC", instance.state.logoWidth);
}
if (instance.state.logoWidth > 0) {
if (instance.config.logo.position == FF_LOGO_POSITION_LEFT) {
uint32_t remaining = instance.state.logoWidth;
remaining = printedLineWidth < remaining ? remaining - printedLineWidth : 0;
ffPrintCharTimes(' ', remaining);
} else {
printf("\033[%uC", instance.state.logoWidth);
}
}
if (instance.state.dynamicInterval > 0) {
if (instance.state.dynamicInterval > 0 && logo->position == FF_LOGO_POSITION_LEFT) {
fputs("\033[K", stdout); // Clear to the end of the line
// Note that we don't clear the line when the logo is on the right, as it will also clear the logo itself
}
++instance.state.keysHeight;