diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f70de31aa..6b4934da6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,13 +14,52 @@ jobs: - name: Install codespell shell: bash - run: sudo apt update && sudo apt install -y codespell + run: | + sudo apt-get update || true + sudo apt-get install -y codespell - name: Run Spellchecker run: codespell - Linux-old-amd64: - name: Linux-old-amd64 + no-features-test: + name: No-features-test + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - name: uname -a + run: uname -a + + - name: configure project + run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_XCB_RANDR=OFF -DENABLE_XCB=OFF -DENABLE_XRANDR=OFF -DENABLE_X11=OFF -DENABLE_DRM=OFF -DENABLE_GIO=OFF -DENABLE_DCONF=OFF -DENABLE_DBUS=OFF -DENABLE_XFCONF=OFF -DENABLE_SQLITE3=OFF -DENABLE_RPM=OFF -DENABLE_IMAGEMAGICK7=OFF -DENABLE_IMAGEMAGICK6=OFF -DENABLE_CHAFA=OFF -DENABLE_ZLIB=OFF -DENABLE_EGL=OFF -DENABLE_GLX=OFF -DENABLE_OSMESA=OFF -DENABLE_OPENCL=OFF -DENABLE_LIBNM=OFF -DENABLE_FREETYPE=OFF -DENABLE_PULSE=OFF -DENABLE_DDCUTIL=OFF -DENABLE_DIRECTX_HEADERS=OFF -DENABLE_THREADS=OFF + + - name: build project + run: cmake --build . --target package --verbose -j4 + + - name: list features + run: ./fastfetch --list-features + + - name: run fastfetch + run: time ./fastfetch -c presets/ci.jsonc + + - name: run fastfetch --format json + run: time ./fastfetch -c presets/ci.jsonc --format json + + - name: run flashfetch + run: time ./flashfetch + + - name: print dependencies + run: ldd fastfetch + + - name: run tests + run: ctest + + linux-amd64: + name: Linux-amd64 runs-on: ubuntu-20.04 permissions: security-events: write @@ -72,71 +111,6 @@ jobs: - name: run tests run: ctest - - name: upload artifacts - uses: actions/upload-artifact@v4 - with: - name: fastfetch-linux-old-amd64 - path: ./fastfetch-*.* - - linux-amd64: - name: Linux-amd64 - runs-on: ubuntu-22.04 # Ubuntu 22.04 uses Glibc 2.35. Should be fine with Debian 12, which uses Glibc 2.36 (https://packages.debian.org/source/bookworm/glibc) - permissions: - security-events: write - contents: read - outputs: - ffversion: ${{ steps.ffversion.outputs.ffversion }} - steps: - - name: checkout repository - uses: actions/checkout@v4 - - - name: uname -a - run: uname -a - - - name: install required packages - run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev libdrm-dev libddcutil-dev libchafa-dev directx-headers-dev - - - name: install linuxbrew packages - run: | - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - /home/linuxbrew/.linuxbrew/bin/brew install imagemagick --ignore-dependencies - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: c - - - name: configure project - run: PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . - - - name: build project - run: cmake --build . --target package --verbose -j4 - - - name: perform CodeQL analysis - uses: github/codeql-action/analyze@v3 - - - name: list features - run: ./fastfetch --list-features - - - name: run fastfetch - run: time ./fastfetch -c presets/ci.jsonc - - - name: run fastfetch --format json - run: time ./fastfetch -c presets/ci.jsonc --format json - - - name: run flashfetch - run: time ./flashfetch - - - name: print dependencies - run: ldd fastfetch - - - name: run tests - run: ctest - - - name: get fastfetch version - id: ffversion - run: echo "ffversion=$(./fastfetch --version-raw)" >> $GITHUB_OUTPUT - - name: upload artifacts uses: actions/upload-artifact@v4 with: @@ -258,7 +232,7 @@ jobs: macos-universal: name: macOS-universal - runs-on: macos-latest + runs-on: macos-12 permissions: security-events: write contents: read @@ -541,11 +515,6 @@ jobs: if: needs.linux-amd64.outputs.ffversion != steps.get_version_release.outputs.release uses: actions/download-artifact@v4 - - name: rm old artifacts - if: needs.linux-amd64.outputs.ffversion != steps.get_version_release.outputs.release - run: | - rm -rf fastfetch-*-old-* - - name: create release if: needs.linux-amd64.outputs.ffversion != steps.get_version_release.outputs.release uses: ncipollo/release-action@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 13ee141ef..17f58128a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +# 2.10.3 + +Changes: +* Default `hideCursor` to false. It doesn't make much difference but makes user's terminal unusable if fastfetch is not exited correctly. +* Linux amd64 binaries are built with Ubuntu 20.04 again (#808) + +Bugfixes: +* Fix swap usage detection in x86-32 build (Windows, Swap) +* Fix minimum cmake version support (#810) +* Fix wifi detection on platforms that don't use NetworkManager (#811, Wifi, Linux) +* Fix NixOS wrapped process name (#814, Terminal, Linux) +* Fix GPU type detection for AMD cards (#816, GPU, Linux) + +Features: +* Add basic support DE detection support for UKUI (DE, Linux) +* Support printing total number of nix / flatpak / brew packages (Packages) + * See `fastfetch -h packages-format` for detail +* Better max CPU frequency detection support with `CPUID / 16H` instruction (CPU, Windows) + * This requires Core I Gen 6 or newer, and with `Virtual Machine Platform` Windows feature disabled. X86 only. +* Improve performance of nix packages detection (Packages, Linux) + # 2.10.2 Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index c2ac9fcdd..3f2fff8dc 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.10.2 + VERSION 2.10.3 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -255,7 +255,7 @@ file(GLOB LOGO_FILES "src/logo/ascii/*.txt") set(LOGO_BUILTIN_H "#pragma once\n#pragma GCC diagnostic ignored \"-Wtrigraphs\"\n\n") foreach(file ${LOGO_FILES}) fastfetch_load_text("${file}" content) - get_filename_component(file "${file}" NAME_WLE) + get_filename_component(file "${file}" NAME_WE) string(TOUPPER "${file}" file) string(REGEX REPLACE "\\$\\{c([0-9]+)\\}" "$\\1" content "${content}") set(LOGO_BUILTIN_H "${LOGO_BUILTIN_H}#define FASTFETCH_DATATEXT_LOGO_${file} ${content}\n") @@ -933,10 +933,6 @@ if(ENABLE_THREADS) endif() endif() -if(ENABLE_PCI_MEMORY) - target_compile_definitions(libfastfetch PRIVATE FF_USE_PCI_MEMORY) -endif() - if(LINUX) target_link_libraries(libfastfetch PRIVATE "m" @@ -1147,16 +1143,20 @@ endif() if(LINUX) find_program(HAVE_DPKG "dpkg") - find_program(HAVE_RPMBUILD "rpmbuild") - if(HAVE_DPKG AND HAVE_RPMBUILD) - set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB;RPM") + if(HAVE_DPKG) + set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB") + + set(CPACK_DEBIAN_PACKAGE_SECTION, "utils") + set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") + set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6") endif() - set(CPACK_DEBIAN_PACKAGE_SECTION, "utils") - set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") - set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6") + find_program(HAVE_RPMBUILD "rpmbuild") + if(HAVE_RPMBUILD) + set(CPACK_GENERATOR "${CPACK_GENERATOR};RPM") - set(CPACK_RPM_PACKAGE_LICENSE "MIT") + set(CPACK_RPM_PACKAGE_LICENSE "MIT") + endif() endif() set(CPACK_SET_DESTDIR ON) diff --git a/README.md b/README.md index cc40b2ee6..9dc5c7a00 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ There are [screenshots on different platforms](https://github.com/fastfetch-cli/ Some distros packaged an outdated fastfetch version. Older version is not supported, please always ensure that the latest version is used. -* Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (for Ubuntu 22.04 or above) -* Debian / Ubuntu: Download `fastfetch--Linux.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and `dpkg -i fastfetch--Linux.deb` (for Ubuntu 22.04 or above and Debian 12 or above). +* Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (for Ubuntu 22.04 or newer) +* Debian / Ubuntu: Download `fastfetch--Linux.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and `dpkg -i fastfetch--Linux.deb` (for Ubuntu 20.04 or newer and Debian 11 or newer). * Arch Linux: `sudo pacman -S fastfetch` * Fedora: `sudo dnf install fastfetch` * Gentoo: `sudo emerge --ask app-misc/fastfetch` diff --git a/doc/json_schema.json b/doc/json_schema.json index 49c0596c7..5804b2292 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -679,6 +679,7 @@ "board", "break", "brightness", + "camera", "chassis", "cpu", "cpuusage", @@ -772,6 +773,10 @@ "const": "board", "description": "Print mather board name and other info" }, + { + "const": "camera", + "description": "Print available cameras" + }, { "const": "cursor", "description": "Print cursor style name" diff --git a/src/common/init.c b/src/common/init.c index c8ab6593e..853042fc8 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -238,9 +238,6 @@ void ffListFeatures(void) #ifdef FF_HAVE_DDCUTIL "libddcutil\n" #endif - #if __has_include() - "videodev2\n" - #endif #ifdef FF_HAVE_DIRECTX_HEADERS "Directx Headers\n" #endif @@ -250,8 +247,11 @@ void ffListFeatures(void) #ifdef FF_USE_SYSTEM_YYJSON "System yyjson\n" #endif - #ifdef FF_USE_PCI_MEMORY - "PCI memory\n" + #if __has_include() + "linux/videodev2\n" + #endif + #if __has_include() + "linux/wireless\n" #endif "" , stdout); diff --git a/src/common/library.c b/src/common/library.c index 7f858e966..e776516b5 100644 --- a/src/common/library.c +++ b/src/common/library.c @@ -4,7 +4,7 @@ #include //Clang doesn't define __SANITIZE_ADDRESS__ but defines __has_feature(address_sanitizer) -#if defined(__has_feature) +#if !defined(__SANITIZE_ADDRESS__) && defined(__has_feature) #if __has_feature(address_sanitizer) #define __SANITIZE_ADDRESS__ #endif diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index 2a8b69bfb..6a4d8195e 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -50,6 +50,31 @@ typedef struct FFSmbiosProcessorInfo uint16_t ThreadEnabled; // varies } FFSmbiosProcessorInfo; +#if defined(__x86_64__) || defined(__i386__) + +#include + +inline static const char* detectSpeedByCpuid(FFCPUResult* cpu) +{ + uint32_t base = 0, max = 0, bus = 0, unused = 0; + if (!__get_cpuid(0x16, &base, &max, &bus, &unused)) + return "Unsupported instruction"; + + // cpuid returns 0 MHz when hyper-v is enabled + if (base) cpu->frequencyBase = base / 1000.0; + if (max) cpu->frequencyMax = max / 1000.0; + return NULL; +} + +#else + +inline static const char* detectSpeedByCpuid(FFCPUResult* cpu) +{ + return "Unsupported platform"; +} + +#endif + static const char* detectMaxSpeedBySmbios(FFCPUResult* cpu) { const FFSmbiosProcessorInfo* data = (const FFSmbiosProcessorInfo*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_PROCESSOR_INFO]; @@ -114,6 +139,7 @@ static const char* detectByRegistry(FFCPUResult* cpu) if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &hKey, NULL)) return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\", &hKey, NULL) failed"; + if (detectSpeedByCpuid(cpu) != NULL || cpu->frequencyBase != cpu->frequencyBase) { uint32_t mhz; if(ffRegReadUint(hKey, L"~MHz", &mhz, NULL)) @@ -141,7 +167,8 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) if (error) return error; - detectMaxSpeedBySmbios(cpu); + if (cpu->frequencyMax != cpu->frequencyMax) + detectMaxSpeedBySmbios(cpu); if(options->temp) ffDetectSmbiosTemp(&cpu->temperature, NULL); diff --git a/src/detection/displayserver/displayserver.h b/src/detection/displayserver/displayserver.h index 9c6c2d96e..1497e786f 100644 --- a/src/detection/displayserver/displayserver.h +++ b/src/detection/displayserver/displayserver.h @@ -13,6 +13,7 @@ #define FF_DE_PRETTY_BUDGIE "Budgie" #define FF_DE_PRETTY_CDE "CDE" #define FF_DE_PRETTY_UNITY "Unity" +#define FF_DE_PRETTY_UKUI "UKUI" #define FF_WM_PRETTY_KWIN "KWin" #define FF_WM_PRETTY_MUTTER "Mutter" diff --git a/src/detection/displayserver/linux/wayland/global-output.c b/src/detection/displayserver/linux/wayland/global-output.c index c38ceee03..956afc376 100644 --- a/src/detection/displayserver/linux/wayland/global-output.c +++ b/src/detection/displayserver/linux/wayland/global-output.c @@ -118,7 +118,7 @@ void ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* regist rotation, display.edidName.length ? &display.edidName - : display.description.length + : display.description.length && !ffStrbufEndsWithS(&display.description, "-unknown") ? &display.description : &display.name, display.type, diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c index 5d21f1d6c..64f16db03 100644 --- a/src/detection/displayserver/linux/wmde.c +++ b/src/detection/displayserver/linux/wmde.c @@ -75,7 +75,9 @@ static void applyPrettyNameIfWM(FFDisplayServerResult* result, const char* name) ffStrEqualsIgnCase(name, "kwin_wayland_wrapper") || ffStrEqualsIgnCase(name, "kwin_x11") || ffStrEqualsIgnCase(name, "kwin_x11_wrapper") || - ffStrEqualsIgnCase(name, "kwin") + ffStrEqualsIgnCase(name, "kwin") || + ffStrEndsWithIgnCase(name, "-kwin_wayland") || + ffStrEndsWithIgnCase(name, "-kwin_x11") ) ffStrbufSetS(&result->wmPrettyName, FF_WM_PRETTY_KWIN); else if( ffStrEqualsIgnCase(name, "gnome-shell") || @@ -231,6 +233,13 @@ static void applyPrettyNameIfDE(FFDisplayServerResult* result, const char* name) ffStrbufSetS(&result->deProcessName, "dtsession"); ffStrbufSetS(&result->dePrettyName, FF_DE_PRETTY_CDE); } + + else if( + ffStrEqualsIgnCase(name, "ukui-session") + ) { + ffStrbufSetS(&result->deProcessName, "ukui-session"); + ffStrbufSetS(&result->dePrettyName, FF_DE_PRETTY_UKUI); + } } static void getWMProtocolNameFromEnv(FFDisplayServerResult* result) diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index b497a8a5a..7afe12ada 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -15,23 +15,6 @@ #include -FF_MAYBE_UNUSED static void pciDetectTemp(FFGPUResult* gpu, uint32_t deviceClass) -{ - const FFlist* tempsResult = ffDetectTemps(); - - FF_LIST_FOR_EACH(FFTempValue, tempValue, *tempsResult) - { - // https://www.kernel.org/doc/html/v5.10/gpu/amdgpu.html#hwmon-interfaces - // FIXME: this code doesn't take multiGPUs into count - // The kernel exposes the device class multiplied by 256 for some reason - if(tempValue->deviceClass == deviceClass * 256) - { - gpu->temperature = tempValue->value; - return; - } - } -} - static void pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) { ffStrbufAppendS(pciDir, "/driver"); @@ -56,33 +39,64 @@ static void pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer } } -static void pciDetectVmem(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) +static void pciDetectAmdSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) { - // Works for AMD GPUs // https://www.kernel.org/doc/html/v5.10/gpu/amdgpu.html#mem-info-vis-vram-total - ffStrbufAppendS(pciDir, "/mem_info_vis_vram_total"); - uint64_t size = 0; - if (ffReadFileBuffer(pciDir->chars, buffer) && (size = ffStrbufToUInt(buffer, 0))) - { - gpu->type = size > 1024UL * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; - if (gpu->type == FF_GPU_TYPE_DISCRETE) - gpu->dedicated.total = size; - else - gpu->shared.total = size; + const uint32_t pciDirLen = pciDir->length; - ffStrbufSubstrBefore(pciDir, pciDir->length - (uint32_t) strlen("/mem_info_vis_vram_total")); - ffStrbufAppendS(pciDir, "/mem_info_vram_used"); - if (ffReadFileBuffer(pciDir->chars, buffer) && (size = ffStrbufToUInt(buffer, 0))) + ffStrbufAppendS(pciDir, "/hwmon/"); + FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDir->chars); + struct dirent* entry; + while ((entry = readdir(dirp)) != NULL) + { + if (entry->d_name[0] == '.') continue; + break; + } + if (!entry) return; + ffStrbufAppendS(pciDir, entry->d_name); + ffStrbufAppendC(pciDir, '/'); + + const uint32_t hwmonLen = pciDir->length; + ffStrbufAppendS(pciDir, "in1_input"); // Northbridge voltage in millivolts (APUs only) + if (ffPathExists(pciDir->chars, FF_PATHTYPE_FILE)) + gpu->type = FF_GPU_TYPE_INTEGRATED; + else + gpu->type = FF_GPU_TYPE_DISCRETE; + + uint64_t value = 0; + if (options->temp) + { + ffStrbufSubstrBefore(pciDir, hwmonLen); + ffStrbufAppendS(pciDir, "temp1_input"); // The on die GPU temperature in millidegrees Celsius + if (ffReadFileBuffer(pciDir->chars, buffer) && (value = ffStrbufToUInt(buffer, 0))) + gpu->temperature = (double) value / 1000; + } + + ffStrbufSubstrBefore(pciDir, hwmonLen); + ffStrbufAppendS(pciDir, "freq1_input"); // The gfx/compute clock in hertz + if (ffReadFileBuffer(pciDir->chars, buffer) && (value = ffStrbufToUInt(buffer, 0))) + gpu->frequency = (double) value / (1000 * 1000 * 1000); + + if (options->driverSpecific) + { + ffStrbufSubstrBefore(pciDir, pciDirLen); + ffStrbufAppendS(pciDir, "/mem_info_vis_vram_total"); + if (ffReadFileBuffer(pciDir->chars, buffer) && (value = ffStrbufToUInt(buffer, 0))) { - if (gpu->type == FF_GPU_TYPE_DISCRETE) - gpu->dedicated.used = size; - else - gpu->shared.used = size; + ffStrbufSubstrBefore(pciDir, pciDir->length - (uint32_t) strlen("/mem_info_vis_vram_total")); + ffStrbufAppendS(pciDir, "/mem_info_vram_used"); + if (ffReadFileBuffer(pciDir->chars, buffer) && (value = ffStrbufToUInt(buffer, 0))) + { + if (gpu->type == FF_GPU_TYPE_DISCRETE) + gpu->dedicated.used = value; + else + gpu->shared.used = value; + } } } } -static void pciDetectVfreq(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) +static void pciDetectIntelSpecific(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) { if (!ffStrbufEndsWithS(pciDir, "/device")) // Must be in `/sys/class/drm/cardN/device` return; @@ -98,6 +112,10 @@ static void pciDetectVfreq(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) str[len] = '\0'; gpu->frequency = (double) strtoul(str, NULL, 10) / 1000.0; } + + if (ffStrbufStartsWithS(&gpu->name, "Intel ")) + ffStrbufSubstrAfter(&gpu->name, (uint32_t) strlen("Intel ")); + gpu->type = ffStrbufStartsWithIgnCaseS(&gpu->name, "Arc ") ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; } static bool loadPciIds(FFstrbuf* pciids) @@ -186,43 +204,50 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf ffGPUParsePciIds(&pciids, subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); } - pciDetectVmem(gpu, drmDir, buffer); - ffStrbufSubstrBefore(drmDir, drmDirPathLength); - - pciDetectVfreq(gpu, drmDir, buffer); - ffStrbufSubstrBefore(drmDir, drmDirPathLength); - pciDetectDriver(gpu, drmDir, buffer); ffStrbufSubstrBefore(drmDir, drmDirPathLength); - #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API - if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && (options->temp || options->driverSpecific)) + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) { - ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { - .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, - .pciBusId = { - .domain = pciDomain, - .bus = pciBus, - .device = pciDevice, - .func = pciFunc, - }, - }, (FFGpuDriverResult) { - .temp = options->temp ? &gpu->temperature : NULL, - .memory = options->driverSpecific ? &gpu->dedicated : NULL, - .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, - .type = &gpu->type, - .frequency = &gpu->frequency, - }, "libnvidia-ml.so"); - - if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) - gpu->type = gpu->dedicated.total > (uint64_t)1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + pciDetectAmdSpecific(options, gpu, drmDir, buffer); + ffStrbufSubstrBefore(drmDir, drmDirPathLength); } - #endif // FF_USE_PROPRIETARY_GPU_DRIVER_API + else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL) + { + pciDetectIntelSpecific(gpu, drmDir, buffer); + ffStrbufSubstrBefore(drmDir, drmDirPathLength); + } + else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA) + { + #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API + if (options->temp || options->driverSpecific) + { + ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { + .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, + .pciBusId = { + .domain = pciDomain, + .bus = pciBus, + .device = pciDevice, + .func = pciFunc, + }, + }, (FFGpuDriverResult) { + .temp = options->temp ? &gpu->temperature : NULL, + .memory = options->driverSpecific ? &gpu->dedicated : NULL, + .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, + .type = &gpu->type, + .frequency = &gpu->frequency, + }, "libnvidia-ml.so"); + } + #endif // FF_USE_PROPRIETARY_GPU_DRIVER_API - #ifdef __linux__ - if(options->temp && gpu->temperature != gpu->temperature) - pciDetectTemp(gpu, ((uint32_t) classId << 8) + subclassId); - #endif + if (gpu->type == FF_GPU_TYPE_UNKNOWN) + { + if (ffStrbufStartsWithIgnCaseS(&gpu->name, "GeForce") || + ffStrbufStartsWithIgnCaseS(&gpu->name, "Quadro") || + ffStrbufStartsWithIgnCaseS(&gpu->name, "Tesla")) + gpu->type = FF_GPU_TYPE_DISCRETE; + } + } return NULL; } diff --git a/src/detection/os/os_linux.c b/src/detection/os/os_linux.c index 5058033a2..74e9f1475 100644 --- a/src/detection/os/os_linux.c +++ b/src/detection/os/os_linux.c @@ -150,8 +150,11 @@ static void detectOS(FFOSResult* os) if(os->prettyName.length == 0) ffStrbufAppendS(&os->prettyName, "Bedrock Linux"); + + parseFile("/bedrock"FASTFETCH_TARGET_DIR_ETC"/os-release", os); - return; + if(allRelevantValuesSet(os)) + return; } parseFile(FASTFETCH_TARGET_DIR_ETC"/os-release", os); diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 9f5b3c307..aada79a74 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -118,28 +118,146 @@ static uint32_t countFilesRecursive(FFstrbuf* baseDir, const char* dirname, cons return sum; } +static bool isValidNixPkg(FFstrbuf* pkg) +{ + if (!ffPathExists(pkg->chars, FF_PATHTYPE_DIRECTORY)) + return false; + + ffStrbufSubstrAfterLastC(pkg, '/'); + if ( + ffStrbufStartsWithS(pkg, "nixos-system-nixos-") || + ffStrbufEndsWithS(pkg, "-doc") || + ffStrbufEndsWithS(pkg, "-man") || + ffStrbufEndsWithS(pkg, "-info") || + ffStrbufEndsWithS(pkg, "-dev") || + ffStrbufEndsWithS(pkg, "-bin") + ) return false; + + enum { START, DIGIT, DOT, MATCH } state = START; + + for (uint32_t i = 0; i < pkg->length; i++) + { + char c = pkg->chars[i]; + switch (state) + { + case START: + if (c >= '0' && c <= '9') + state = DIGIT; + break; + case DIGIT: + if (c >= '0' && c <= '9') + continue; + if (c == '.') + state = DOT; + else + state = START; + break; + case DOT: + if (c >= '0' && c <= '9') + state = MATCH; + else + state = START; + break; + case MATCH: + break; + } + } + + return state == MATCH; +} + +static bool checkNixCache(FFstrbuf* cacheDir, FFstrbuf* hash, uint32_t* count) +{ + if (!ffPathExists(cacheDir->chars, FF_PATHTYPE_FILE)) + return false; + + FF_STRBUF_AUTO_DESTROY cacheContent = ffStrbufCreate(); + if (!ffReadFileBuffer(cacheDir->chars, &cacheContent)) + return false; + + // Format: \n + uint32_t split = ffStrbufFirstIndexC(&cacheContent, '\n'); + if (split == cacheContent.length) + return false; + + ffStrbufSetNS(hash, split, cacheContent.chars); + *count = (uint32_t)atoi(cacheContent.chars + split + 1); + + return true; +} + +static bool writeNixCache(FFstrbuf* cacheDir, FFstrbuf* hash, uint32_t count) +{ + FF_STRBUF_AUTO_DESTROY cacheContent = ffStrbufCreateCopy(hash); + ffStrbufAppendF(&cacheContent, "\n%u", count); + return ffWriteFileBuffer(cacheDir->chars, &cacheContent); +} + static uint32_t getNixPackagesImpl(char* path) { //Nix detection is kinda slow, so we only do it if the dir exists if(!ffPathExists(path, FF_PATHTYPE_DIRECTORY)) return 0; - FF_STRBUF_AUTO_DESTROY output = ffStrbufCreateA(128); + FF_STRBUF_AUTO_DESTROY cacheDir = ffStrbufCreateCopy(&instance.state.platform.cacheDir); + ffStrbufEnsureEndsWithC(&cacheDir, '/'); + ffStrbufAppendS(&cacheDir, "fastfetch/packages/nix"); + ffStrbufAppendS(&cacheDir, path); - //https://github.com/fastfetch-cli/fastfetch/issues/195#issuecomment-1191748222 - FF_STRBUF_AUTO_DESTROY command = ffStrbufCreateA(255); - ffStrbufAppendS(&command, "for x in $(nix-store --query --requisites "); - ffStrbufAppendS(&command, path); - ffStrbufAppendS(&command, "); do if [ -d $x ]; then echo $x ; fi ; done | cut -d- -f2- | egrep '([0-9]{1,}\\.)+[0-9]{1,}' | egrep -v '\\-doc$|\\-man$|\\-info$|\\-dev$|\\-bin$|^nixos-system-nixos-' | uniq | wc -l"); + //Check the hash first to determine if we need to recompute the count + FF_STRBUF_AUTO_DESTROY hash = ffStrbufCreateA(64); + FF_STRBUF_AUTO_DESTROY cacheHash = ffStrbufCreateA(64); + uint32_t count = 0; - ffProcessAppendStdOut(&output, (char* const[]) { - "sh", - "-c", - command.chars, + ffProcessAppendStdOut(&hash, (char* const[]) { + "nix-store", + "--query", + "--hash", + path, NULL }); - return (uint32_t) strtol(output.chars, NULL, 10); + if (checkNixCache(&cacheDir, &cacheHash, &count) && ffStrbufEqual(&hash, &cacheHash)) + return count; + + //Cache is invalid, recompute the count + count = 0; + + //Implementation based on bash script from here: + //https://github.com/fastfetch-cli/fastfetch/issues/195#issuecomment-1191748222 + + FF_STRBUF_AUTO_DESTROY output = ffStrbufCreateA(1024); + + ffProcessAppendStdOut(&output, (char* const[]) { + "nix-store", + "--query", + "--requisites", + path, + NULL + }); + + uint32_t lineLength = 0; + for (uint32_t i = 0; i < output.length; i++) + { + if (output.chars[i] != '\n') + { + lineLength++; + continue; + } + + output.chars[i] = '\0'; + FFstrbuf line = { + .allocated = 0, + .length = lineLength, + .chars = output.chars + i - lineLength + }; + if (isValidNixPkg(&line)) + count++; + lineLength = 0; + } + + writeNixCache(&cacheDir, &hash, count); + return count; } static uint32_t getNixPackages(FFstrbuf* baseDir, const char* dirname) diff --git a/src/detection/swap/swap_windows.c b/src/detection/swap/swap_windows.c index a5788a358..5b3a15457 100644 --- a/src/detection/swap/swap_windows.c +++ b/src/detection/swap/swap_windows.c @@ -7,20 +7,11 @@ const char* ffDetectSwap(FFSwapResult* swap) { - ULONG size = sizeof(SYSTEM_PAGEFILE_INFORMATION); - SYSTEM_PAGEFILE_INFORMATION* FF_AUTO_FREE pstart = (SYSTEM_PAGEFILE_INFORMATION*)malloc(size); - while(true) - { - NTSTATUS status = NtQuerySystemInformation(SystemPagefileInformation, pstart, size, &size); - if(status == STATUS_INFO_LENGTH_MISMATCH) - { - if(!(pstart = (SYSTEM_PAGEFILE_INFORMATION*)realloc(pstart, size))) - return "realloc(pstart, size) failed"; - } - else if(!NT_SUCCESS(status)) - return "NtQuerySystemInformation(SystemPagefileInformation, size) failed"; - break; - } + uint8_t buffer[1024]; + ULONG size = sizeof(buffer); + SYSTEM_PAGEFILE_INFORMATION* pstart = (SYSTEM_PAGEFILE_INFORMATION*) buffer; + if(!NT_SUCCESS(NtQuerySystemInformation(SystemPagefileInformation, pstart, size, &size))) + return "NtQuerySystemInformation(SystemPagefileInformation, size) failed"; uint32_t pageSize = instance.state.platform.pageSize; swap->bytesUsed = (uint64_t)pstart->TotalUsed * pageSize; diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index e6e28794c..a12bb0930 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -482,14 +482,12 @@ static void setShellInfoDetails(FFShellResult* result) static void setTerminalInfoDetails(FFTerminalResult* result) { - if(result->exeName[0] == '.' && ffStrEndsWith(result->exeName, "-wrapped")) + if(ffStrbufStartsWithC(&result->processName, '.') && ffStrbufEndsWithS(&result->processName, "-wrapped")) { // For NixOS. Ref: #510 and https://github.com/NixOS/nixpkgs/pull/249428 // We use processName when detecting version and font, overriding it for simplification - ffStrbufSetNS( - &result->processName, - (uint32_t) (strlen(result->exeName) - strlen(".-wrapped")), - result->exeName + 1); + ffStrbufSubstrBefore(&result->processName, result->processName.length - (uint32_t) strlen("-wrapped")); + ffStrbufSubstrAfter(&result->processName, 0); } if(ffStrbufEqualS(&result->processName, "wezterm-gui")) diff --git a/src/detection/wifi/wifi_linux.c b/src/detection/wifi/wifi_linux.c index d9788c994..fffadc9a2 100644 --- a/src/detection/wifi/wifi_linux.c +++ b/src/detection/wifi/wifi_linux.c @@ -337,7 +337,8 @@ static const char* detectWifiWithIoctls(FFlist* result) const char* ffDetectWifi(FFlist* result) { #ifdef FF_HAVE_LIBNM - if(!detectWifiWithLibnm(result)) + detectWifiWithLibnm(result); + if(result->length) // NetworkManager not enabled? #811 return NULL; #endif diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 843f620c6..7f411d023 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -4,7 +4,7 @@ #include "modules/packages/packages.h" #include "util/stringUtils.h" -#define FF_PACKAGES_NUM_FORMAT_ARGS 26 +#define FF_PACKAGES_NUM_FORMAT_ARGS 29 void ffPrintPackages(FFPackagesOptions* options) { @@ -70,6 +70,9 @@ void ffPrintPackages(FFPackagesOptions* options) } else { + uint32_t nixAll = counts.nixDefault + counts.nixSystem + counts.nixUser; + uint32_t flatpakAll = counts.flatpakSystem + counts.flatpakUser; + uint32_t brewAll = counts.brew + counts.brewCask; FF_PRINT_FORMAT_CHECKED(FF_PACKAGES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, FF_PACKAGES_NUM_FORMAT_ARGS, ((FFformatarg[]){ {FF_FORMAT_ARG_TYPE_UINT, &counts.all}, {FF_FORMAT_ARG_TYPE_UINT, &counts.pacman}, @@ -97,6 +100,9 @@ void ffPrintPackages(FFPackagesOptions* options) {FF_FORMAT_ARG_TYPE_UINT, &counts.winget}, {FF_FORMAT_ARG_TYPE_UINT, &counts.opkg}, {FF_FORMAT_ARG_TYPE_UINT, &counts.am}, + {FF_FORMAT_ARG_TYPE_UINT, &nixAll}, + {FF_FORMAT_ARG_TYPE_UINT, &flatpakAll}, + {FF_FORMAT_ARG_TYPE_UINT, &brewAll}, })); } @@ -367,7 +373,7 @@ void ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy void ffPrintPackagesHelpFormat(void) { - FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_PACKAGES_MODULE_NAME, "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (MacPorts), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis), {24} (winget), {25} (opkg)", FF_PACKAGES_NUM_FORMAT_ARGS, ((const char* []) { + FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_PACKAGES_MODULE_NAME, "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (MacPorts), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis), {24} (winget), {25} (opkg), {26} (am)", FF_PACKAGES_NUM_FORMAT_ARGS, ((const char* []) { "Number of all packages", "Number of pacman packages", "Pacman branch on manjaro", @@ -394,6 +400,9 @@ void ffPrintPackagesHelpFormat(void) "Number of winget packages", "Number of opkg packages", "Number of am packages", + "Total number of all nix packages", + "Total number of all flatpak packages", + "Total number of all brew packages", })); } diff --git a/src/modules/separator/separator.c b/src/modules/separator/separator.c index 538b651f4..7f8ac41d3 100644 --- a/src/modules/separator/separator.c +++ b/src/modules/separator/separator.c @@ -12,9 +12,20 @@ static inline uint32_t max(uint32_t a, uint32_t b) static inline uint32_t getWcsWidth(const FFstrbuf* mbstr, wchar_t* wstr, mbstate_t* state) { + int result = 1; + for (uint32_t i = 0; i < mbstr->length; i++) + { + if (!isascii(mbstr->chars[i])) + { + result = 0; + break; + } + } + if (__builtin_expect(result, 1)) return mbstr->length; + const char* str = mbstr->chars; uint32_t wstrLength = (uint32_t) mbsrtowcs(wstr, &str, mbstr->length, state); - int result = mk_wcswidth(wstr, wstrLength); + result = mk_wcswidth(wstr, wstrLength); return result > 0 ? (uint32_t) result : mbstr->length; } @@ -32,9 +43,9 @@ void ffPrintSeparator(FFSeparatorOptions* options) + (fqdn ? platform->hostName.length : ffStrbufFirstIndexC(&platform->hostName, '.')); // host name ffLogoPrintLine(); - if(options->string.length == 0) + if(__builtin_expect(options->string.length == 1, 1)) { - ffPrintCharTimes('-', titleLength); + ffPrintCharTimes(options->string.chars[0], titleLength); } else { @@ -131,7 +142,7 @@ void ffInitSeparatorOptions(FFSeparatorOptions* options) NULL, ffGenerateSeparatorJsonConfig ); - ffStrbufInit(&options->string); + ffStrbufInitStatic(&options->string, "-"); } void ffDestroySeparatorOptions(FFSeparatorOptions* options) diff --git a/src/options/display.c b/src/options/display.c index 423177286..f5683fc21 100644 --- a/src/options/display.c +++ b/src/options/display.c @@ -354,12 +354,11 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) #ifdef NDEBUG options->disableLinewrap = !options->pipe; - options->hideCursor = !options->pipe; #else options->disableLinewrap = false; - options->hideCursor = false; #endif + options->hideCursor = false; options->binaryPrefixType = FF_BINARY_PREFIX_TYPE_IEC; options->sizeNdigits = 2; options->sizeMaxPrefix = UINT8_MAX;