diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index c51da8fed5..c242ca522c 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@v3 - name: install required packages - run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev + run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libcjson-dev - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 361f820169..f157d0b0f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_OSMESA "Enable osmesa" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_LIBPLIST "Enable libplist" ON "APPLE" OFF) +cmake_dependent_option(ENABLE_LIBCJSON "Enable libcjson" ON "LINUX" OFF) option(BUILD_TESTS "Build tests" OFF) # Also create test executables option(SET_TWEAK "Add tweak to project version" ON) # This is set to off by github actions for release builds @@ -375,6 +376,7 @@ ff_lib_enable(GLX glx) ff_lib_enable(OSMESA osmesa) ff_lib_enable(OPENCL OpenCL) ff_lib_enable(LIBPLIST libplist-2.0) +ff_lib_enable(LIBCJSON libcjson) if(APPLE) target_link_libraries(libfastfetch diff --git a/README.md b/README.md index f83bfe7346..1e3b233b90 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ The following libraries are used if present at runtime: * [`libsqlite3`](https://www.sqlite.org/index.html): Needed for pkg & rpm package count. * [`librpm`](http://rpm.org/): Slower fallback for rpm package count. Needed on openSUSE. * [`libplist`](https://github.com/libimobiledevice/libplist): Binary `plist` file parser ( macOS ). Needed for iTerm2 Terminal font and WM Theme. +* [`libcJSON`](https://github.com/DaveGamble/cJSON): Needed for Windows Terminal font ( WSL ). ## Support status All categories not listed here should work without needing a specific implementation. @@ -81,7 +82,7 @@ KDE Plasma, Gnome, Cinnamon, Mate, XFCE4, LXQt ##### Terminal fonts ``` -Konsole, Gnome Terminal, Tilix, XFCE4 Terminal, Alacritty, LXTerminal, iTerm2, Apple Terminal, TTY +Konsole, Gnome Terminal, Tilix, XFCE4 Terminal, Alacritty, LXTerminal, iTerm2, Apple Terminal, TTY, Windows Terminal ``` ## Building diff --git a/src/common/init.c b/src/common/init.c index 1037eb3635..19771a505c 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -218,6 +218,7 @@ static void defaultConfig(FFinstance* instance) ffStrbufInitA(&instance->config.libOSMesa, 0); ffStrbufInitA(&instance->config.libOpenCL, 0); ffStrbufInitA(&instance->config.libplist, 0); + ffStrbufInitA(&instance->config.libcJSON, 0); instance->config.titleFQDN = false; diff --git a/src/data/config_user.txt b/src/data/config_user.txt index c41027baf7..1ded95bfcb 100644 --- a/src/data/config_user.txt +++ b/src/data/config_user.txt @@ -324,3 +324,4 @@ #--lib-osmesa /usr/lib/libOSMesa.so #--lib-opencl /usr/lib/libOpenCL.so #--lib-plist /usr/local/bin/libplist-2.0.dylib +#--lib-cjson /usr/lib/libcjson.so diff --git a/src/data/help.txt b/src/data/help.txt index 75f4fde5e8..eb02e6df52 100644 --- a/src/data/help.txt +++ b/src/data/help.txt @@ -85,6 +85,7 @@ Library options: Set the path of a library to load --lib-osmesa --lib-opencl --lib-plist + --lib-cjson Module specific options: --title-fqdn : Sets if the title should use fully qualified domain name. Default is false. diff --git a/src/detection/terminalShell.c b/src/detection/terminalShell.c index fbd898f60a..cd3785a0f9 100644 --- a/src/detection/terminalShell.c +++ b/src/detection/terminalShell.c @@ -153,6 +153,14 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) if(!ffStrSet(term)) term = getenv("TERM_PROGRAM"); + //We are in WSL but not in Windows Terminal + if(!ffStrSet(term) && ( + getenv("WSLENV") != NULL || + getenv("WSL_DISTRO") != NULL || + getenv("WSL_INTEROP") != NULL + )) + term = "conhost"; + //Normal Terminal if(!ffStrSet(term)) term = getenv("TERM"); diff --git a/src/detection/terminalfont/terminalfont_apple.c b/src/detection/terminalfont/terminalfont_apple.c index f14ae04c02..5c4f896b00 100644 --- a/src/detection/terminalfont/terminalfont_apple.c +++ b/src/detection/terminalfont/terminalfont_apple.c @@ -32,8 +32,13 @@ static const char* iterm2ParsePList(const FFinstance* instance, const FFstrbuf* else ffplist_from_xml(content->chars, content->length, &root_node); + const char* error = NULL; + if(root_node == NULL) - return "parsing iTerm preference file failed"; + { + error = "parsing iTerm preference file failed"; + goto exit; + } plist_t bookmarks = ffplist_access_path(root_node, 1, "New Bookmarks"); @@ -50,20 +55,22 @@ static const char* iterm2ParsePList(const FFinstance* instance, const FFstrbuf* if(item == NULL) { - ffplist_free(root_node); - return "find profile bookmark failed"; + error = "find profile bookmark failed"; + goto exit; } item = ffplist_dict_get_item(item, "Normal Font"); if (item == NULL) { - ffplist_free(root_node); - return "find Normal Font key failed"; + error = "find Normal Font key failed"; + goto exit; } ffFontInitWithSpace(&terminalFont->font, ffplist_get_string_ptr(item, NULL)); +exit: ffplist_free(root_node); + dlclose(libplist); return NULL; } diff --git a/src/detection/terminalfont/terminalfont_linux.c b/src/detection/terminalfont/terminalfont_linux.c index 79dc0521f8..f68d457445 100644 --- a/src/detection/terminalfont/terminalfont_linux.c +++ b/src/detection/terminalfont/terminalfont_linux.c @@ -140,6 +140,178 @@ static void detectXFCETerminal(const FFinstance* instance, FFTerminalFontResult* ffStrbufDestroy(&useSysFont); } +#ifdef FF_HAVE_LIBCJSON + +#include "common/library.h" +#include "common/processing.h" + +#include +#include + +typedef struct CJSONData +{ + FF_LIBRARY_SYMBOL(cJSON_Parse) + FF_LIBRARY_SYMBOL(cJSON_IsObject) + FF_LIBRARY_SYMBOL(cJSON_GetObjectItemCaseSensitive) + FF_LIBRARY_SYMBOL(cJSON_IsString) + FF_LIBRARY_SYMBOL(cJSON_GetStringValue) + FF_LIBRARY_SYMBOL(cJSON_IsNumber) + FF_LIBRARY_SYMBOL(cJSON_GetNumberValue) + FF_LIBRARY_SYMBOL(cJSON_IsArray) + FF_LIBRARY_SYMBOL(cJSON_Delete) +} CJSONData; + +static const char* detectWTProfile(CJSONData* cjsonData, cJSON* profile, FFstrbuf* name, int* size) +{ + if(!cjsonData->ffcJSON_IsObject(profile)) + return "cJSON_IsObject(profile) returns false"; + + cJSON* font = cjsonData->ffcJSON_GetObjectItemCaseSensitive(profile, "font"); + if(!cjsonData->ffcJSON_IsObject(font)) + return "cJSON_IsObject(font) returns false"; + + if(name->length == 0) + { + cJSON* pface = cjsonData->ffcJSON_GetObjectItemCaseSensitive(font, "face"); + if(cjsonData->ffcJSON_IsString(pface)) + ffStrbufAppendS(name, cjsonData->ffcJSON_GetStringValue(pface)); + } + if(*size < 0) + { + cJSON* psize = cjsonData->ffcJSON_GetObjectItemCaseSensitive(font, "size"); + if(cjsonData->ffcJSON_IsNumber(psize)) + *size = (int)cjsonData->ffcJSON_GetNumberValue(psize); + } + return NULL; +} + +static const char* detectFromWTImpl(const FFinstance* instance, FFstrbuf* content, FFstrbuf* name, int* size) +{ + CJSONData cjsonData; + + FF_LIBRARY_LOAD(libcjson, &instance->config.libcJSON, "dlopen libcjson failed", "libcjson"FF_LIBRARY_EXTENSION, 1) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_Parse) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsObject) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetObjectItemCaseSensitive) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsString) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetStringValue) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsNumber) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetNumberValue) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsArray) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_Delete) + + const char* error = NULL; + + cJSON* root = cjsonData.ffcJSON_Parse(content->chars); + if(!cjsonData.ffcJSON_IsObject(root)) + { + error = "cJSON_Parse() failed"; + goto exit; + } + + cJSON* profiles = cjsonData.ffcJSON_GetObjectItemCaseSensitive(root, "profiles"); + if(!cjsonData.ffcJSON_IsObject(profiles)) + { + error = "cJSON_GetObjectItemCaseSensitive(root, \"profiles\") failed"; + goto exit; + } + + FFstrbuf wtProfileId; + ffStrbufInitS(&wtProfileId, getenv("WT_PROFILE_ID")); + ffStrbufTrim(&wtProfileId, '\''); + if(wtProfileId.length > 0) + { + cJSON* list = cjsonData.ffcJSON_GetObjectItemCaseSensitive(profiles, "list"); + if(cjsonData.ffcJSON_IsArray(list)) + { + cJSON* profile; + cJSON_ArrayForEach(profile, list) + { + if(!cjsonData.ffcJSON_IsObject(profile)) + continue; + cJSON* guid = cjsonData.ffcJSON_GetObjectItemCaseSensitive(profile, "guid"); + if(!cjsonData.ffcJSON_IsString(guid)) + continue; + if(ffStrbufCompS(&wtProfileId, cjsonData.ffcJSON_GetStringValue(guid)) == 0) + { + detectWTProfile(&cjsonData, profile, name, size); + break; + } + } + } + } + ffStrbufDestroy(&wtProfileId); + + cJSON* defaults = cjsonData.ffcJSON_GetObjectItemCaseSensitive(profiles, "defaults"); + detectWTProfile(&cjsonData, defaults, name, size); + + if(name->length == 0) + ffStrbufSetS(name, "Cascadia Mono"); + if(*size < 0) + *size = 12; + +exit: + cjsonData.ffcJSON_Delete(root); + dlclose(libcjson); + return error; +} + +static void detectFromWindowsTeriminal(const FFinstance* instance, FFTerminalFontResult* terminalFont) +{ + //https://learn.microsoft.com/en-us/windows/terminal/install#settings-json-file + FFstrbuf json; + ffStrbufInit(&json); + const char* error; + error = ffProcessAppendStdOut(&json, (char* const[]) { + "cmd.exe", + "/c", + //print the file content directly, so we don't need to handle the difference of Windows and POSIX path + "if exist %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json " + "( type %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminal_8wekyb3d8bbwe\\LocalState\\settings.json ) " + "else if exist %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\\LocalState\\settings.json " + "( type %LOCALAPPDATA%\\Packages\\Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\\LocalState\\settings.json ) " + "else if exist \"%LOCALAPPDATA%\\Microsoft\\Windows Terminal\\settings.json\" " + "( type %LOCALAPPDATA%\\Microsoft\\Windows Terminal\\settings.json ) " + "else ( call )", + NULL + }); + if(error) + { + ffStrbufAppendS(&terminalFont->error, error); + ffStrbufDestroy(&json); + return; + } + ffStrbufTrimRight(&json, '\n'); + if(json.length == 0) + { + ffStrbufAppendS(&terminalFont->error, "Cannot find file \"settings.json\""); + ffStrbufDestroy(&json); + return; + } + + FFstrbuf name; + ffStrbufInit(&name); + int size = -1; + detectFromWTImpl(instance, &json, &name, &size); + ffStrbufDestroy(&json); + + char sizeStr[16]; + snprintf(sizeStr, sizeof(sizeStr), "%d", size); + ffFontInitValues(&terminalFont->font, name.chars, sizeStr); + + ffStrbufDestroy(&name); +} + +#else + +static void detectFromWindowsTeriminal(const FFinstance* instance, FFTerminalFontResult* terminalFont) +{ + FF_UNUSED(instance, terminalFont); + ffStrbufAppendS(&terminalFont->error, "fastfetch is built without libcjson support"); +} + +#endif + void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) { if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "konsole") == 0) @@ -152,4 +324,6 @@ void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalSh detectFromGSettings(instance, "/com/gexperts/Tilix/profiles/", "com.gexperts.Tilix.ProfilesList", "com.gexperts.Tilix.Profile", terminalFont); else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "gnome-terminal-") == 0) detectFromGSettings(instance, "/org/gnome/terminal/legacy/profiles:/:", "org.gnome.Terminal.ProfilesList", "org.gnome.Terminal.Legacy.Profile", terminalFont); + else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "Windows Terminal") == 0) + detectFromWindowsTeriminal(instance, terminalFont); } diff --git a/src/fastfetch.c b/src/fastfetch.c index 359b0449eb..090e849362 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -1246,6 +1246,8 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con optionParseString(key, value, &instance->config.libOpenCL); else if(strcasecmp(key, "--lib-plist") == 0) optionParseString(key, value, &instance->config.libplist); + else if(strcasecmp(key, "--lib-cjson") == 0) + optionParseString(key, value, &instance->config.libcJSON); ////////////////// //Module options// diff --git a/src/fastfetch.h b/src/fastfetch.h index 8d4447bf9a..0eb435ec20 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -150,6 +150,7 @@ typedef struct FFconfig FFstrbuf libOSMesa; FFstrbuf libOpenCL; FFstrbuf libplist; + FFstrbuf libcJSON; bool titleFQDN;