diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a183d5f92..9373fc4855 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -232,6 +232,41 @@ jobs: name: fastfetch-linux-riscv64 path: ./fastfetch-*.* + linux-ppc64le: + name: Linux-ppc64le + runs-on: ubuntu-22.04 + permissions: + security-events: write + contents: read + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - name: run VM + uses: uraimo/run-on-arch-action@v2 + id: runcmd + with: + arch: ppc64le + distro: ubuntu20.04 + githubToken: ${{ github.token }} + run: | + uname -a + apt-get update && apt-get install -y cmake make g++ 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 libpulse-dev libdrm-dev libchafa-dev libelf-dev directx-headers-dev rpm + cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr . + cmake --build . --target package --verbose -j4 + ./fastfetch --list-features + time ./fastfetch -c presets/ci.jsonc --stat false + time ./fastfetch -c presets/ci.jsonc --format json + time ./flashfetch + ldd fastfetch + ctest + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: fastfetch-linux-ppc64le + path: ./fastfetch-*.* + musl-amd64: name: Musl-amd64 runs-on: ubuntu-latest @@ -377,7 +412,7 @@ jobs: architecture: x86-64 cpu_count: 4 shell: bash - version: '14.1' + version: '14.2' run: | uname -a sudo pkg update @@ -449,7 +484,7 @@ jobs: architecture: x86-64 cpu_count: 4 shell: bash - version: '7.5' + version: '7.6' run: | uname -a sudo pkg_add -r cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 xfconf imagemagick chafa pulseaudio hwdata py3-requests @@ -578,6 +613,7 @@ jobs: - linux-aarch64 - linux-armv7 - linux-riscv64 + - linux-ppc64le - musl-amd64 - macos-universal - freebsd-amd64 diff --git a/CHANGELOG.md b/CHANGELOG.md index ab2341eebd..5436497641 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +# 2.34.0 + +Changes: +* We now print distro pretty name if available (OS) + * This is a long requested feature. However, it may break some distros. File a bug with the content of `/etc/os-release` if it breaks your distro. + +Bugfixes: +* Fix thunderbolt version of new MBPs (#1465, Host, macOS) +* Fix backlight name detection on FreeBSD (Brightness, FreeBSD) +* Fix Terminal detection when running fastfetch in `pk-command-not-found` (#1467, Terminal, Linux) +* Relax detection of terminals in NixOS (#1479, Terminal, Linux) + * Should fix konsole, ghostty and maybe others +* Fix core count output in multi-package platforms (CPU) +* Don't suppress the output of `preRun` (#1489) +* Fix battery percentage detection (Battery, NetBSD) + +Features: +* Support ghostty terminal font detection (TerminalFont, Linux / macOS) +* Support `kitty-icat` image protocol, which uses `kitten icat` to generate image data + * Pros: support tmux; support gif animations; good performance + * Cons: due to the limitation of `kitten icat`, we need to clear the screen before displaying the image logo +* Support WM version detection (WM) + * In Linux, Hyprland & sway are supported currently +* Improve performance when stdout is redirected (TerminalSize) +* Report thermal zone temp if CPU temp is not available (CPU, Linux) +* Report sound server (Pipewire or PulseAudio) if available (#1454, Sound, Linux) +* Enable OpenGL & OpenCL detection on Android (OpenGL / OpenCL, Android) +* Detect & report MediaTek Dimensity 9000+ SOC name (CPU, Android) +* Support appman (am-user) package manager detection (Packages, Linux) + +Logo: +* Add Lubuntu +* Update Xray_os +* Add SnigdhaOS +* Add Rhino Linux + # 2.33.0 Changes: diff --git a/CMakeLists.txt b/CMakeLists.txt index 71c0e886ea..d524a7ada2 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.33.0 + VERSION 2.34.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -70,8 +70,8 @@ cmake_dependent_option(ENABLE_IMAGEMAGICK7 "Enable imagemagick 7" ON "LINUX OR F cmake_dependent_option(ENABLE_IMAGEMAGICK6 "Enable imagemagick 6" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR APPLE OR SunOS" OFF) cmake_dependent_option(ENABLE_CHAFA "Enable chafa" ON "ENABLE_IMAGEMAGICK6 OR ENABLE_IMAGEMAGICK7" OFF) cmake_dependent_option(ENABLE_ZLIB "Enable zlib" ON "ENABLE_IMAGEMAGICK6 OR ENABLE_IMAGEMAGICK7" OFF) -cmake_dependent_option(ENABLE_EGL "Enable egl" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR SunOS" OFF) -cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF) +cmake_dependent_option(ENABLE_EGL "Enable egl" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR WIN32 OR SunOS" OFF) +cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS" OFF) cmake_dependent_option(ENABLE_OSMESA "Enable osmesa" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF) cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS" OFF) cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF) @@ -534,7 +534,7 @@ if(LINUX) src/detection/users/users_linux.c src/detection/wallpaper/wallpaper_linux.c src/detection/wifi/wifi_linux.c - src/detection/wm/wm_nosupport.c + src/detection/wm/wm_linux.c src/detection/de/de_linux.c src/detection/wmtheme/wmtheme_linux.c src/detection/camera/camera_linux.c @@ -617,7 +617,6 @@ elseif(FreeBSD) src/common/sysctl.c src/detection/battery/battery_bsd.c src/detection/bios/bios_bsd.c - src/detection/bluetooth/bluetooth_nosupport.c src/detection/bluetoothradio/bluetoothradio_nosupport.c src/detection/board/board_bsd.c src/detection/bootmgr/bootmgr_bsd.c @@ -690,6 +689,15 @@ elseif(FreeBSD) src/util/platform/FFPlatform_unix.c src/util/binary_linux.c ) + if(DragonFly) + list(APPEND LIBFASTFETCH_SRC + src/detection/bluetooth/bluetooth_nosupport.c + ) + else() + list(APPEND LIBFASTFETCH_SRC + src/detection/bluetooth/bluetooth_bsd.c + ) + endif() elseif(NetBSD) list(APPEND LIBFASTFETCH_SRC src/common/dbus.c @@ -914,7 +922,7 @@ elseif(APPLE) src/detection/users/users_linux.c src/detection/wallpaper/wallpaper_apple.m src/detection/wifi/wifi_apple.m - src/detection/wm/wm_apple.c + src/detection/wm/wm_apple.m src/detection/de/de_nosupport.c src/detection/wmtheme/wmtheme_apple.m src/detection/camera/camera_apple.m @@ -995,6 +1003,7 @@ elseif(WIN32) src/util/windows/registry.c src/util/windows/unicode.c src/util/windows/wmi.cpp + src/util/windows/version.c src/util/platform/FFPlatform_windows.c src/util/binary_windows.c ) @@ -1133,6 +1142,14 @@ else() endif() endif() +if(ANDROID) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/vendor/lib64 -Wl,-rpath,/system/lib64") + else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/vendor/lib -Wl,-rpath,/system/lib") + endif() +endif() + if(LINUX AND EXISTS "/lib/ld-musl-${CMAKE_HOST_SYSTEM_PROCESSOR}.so.1") execute_process(COMMAND "/lib/ld-musl-${CMAKE_HOST_SYSTEM_PROCESSOR}.so.1" ERROR_VARIABLE LD_MUSL_RESULT) @@ -1468,6 +1485,7 @@ elseif(FreeBSD) target_link_libraries(libfastfetch PRIVATE "m" PRIVATE "usbhid" + PRIVATE "bluetooth" ) if(NOT DragonFly) target_link_libraries(libfastfetch diff --git a/README.md b/README.md index 3563d39f8f..ad7f423a00 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,35 @@ Try `export XAUTHORITY=$HOME/.Xauthority` Try `fastfetch --wm-detect-plugin`. See also [#984](https://github.com/fastfetch-cli/fastfetch/issues/984) +### Q: How can I change the colors of my ASCII logo? + +Try `fastfetch --logo-color-[1-9] `. `[1-9]` is the index of color placeholders. + +For example: `fastfetch --logo-color-1 red --logo-color-2 green`. + +In JSONC, you can use: + +```jsonc +{ + "logo": { + "color": { + "1": "red", + "2": "green" + } + } +} +``` + +### Q: How to hide a key? + +Set the key to a white space. + +```jsonc +{ + "key": " " +} +``` + ### Q: I want feature A / B / C. Will fastfetch support it? Fastfetch is a system information tool. We only accept hardware or system level software feature requests. For most personal uses, I recommend using `Command` module to detect it yourself, which can be used to grab output from a custom shell script: diff --git a/debian/changelog b/debian/changelog index 0bbb4ec4d6..3b280a6733 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +fastfetch (2.33.0) jammy; urgency=medium + + * Update to 2.33.0 + + -- Carter Li Thu, 26 Dec 2024 09:42:27 +0800 + fastfetch (2.32.0) jammy; urgency=medium * Update to 2.32.0 diff --git a/debian/files b/debian/files index 59c42bc9b5..185e493196 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -fastfetch_2.32.0_source.buildinfo universe/utils optional +fastfetch_2.33.0_source.buildinfo universe/utils optional diff --git a/doc/json_schema.json b/doc/json_schema.json index efb530e7dd..d6d034b73a 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -275,11 +275,11 @@ "type": "string" }, "osFormat": { - "description": "Output format of the module `OS`. See `-h format` for formatting syntax\n 1. {sysname}: Name of the kernel\n 2. {name}: Name of the OS\n 3. {pretty-name}: Pretty name of the OS\n 4. {id}: ID of the OS\n 5. {id-like}: ID like of the OS\n 6. {variant}: Variant of the OS\n 7. {variant-id}: Variant ID of the OS\n 8. {version}: Version of the OS\n 9. {version-id}: Version ID of the OS\n 10. {codename}: Version codename of the OS\n 11. {build-id}: Build ID of the OS\n 12. {arch}: Architecture of the OS", + "description": "Output format of the module `OS`. See `-h format` for formatting syntax\n 1. {sysname}: Name of the kernel\n 2. {name}: Name of the OS\n 3. {pretty-name}: Pretty name of the OS, if available\n 4. {id}: ID of the OS\n 5. {id-like}: ID like of the OS\n 6. {variant}: Variant of the OS\n 7. {variant-id}: Variant ID of the OS\n 8. {version}: Version of the OS\n 9. {version-id}: Version ID of the OS\n 10. {codename}: Version codename of the OS\n 11. {build-id}: Build ID of the OS\n 12. {arch}: Architecture of the OS", "type": "string" }, "packagesFormat": { - "description": "Output format of the module `Packages`. See `-h format` for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of emerge packages\n 7. {eopkg}: Number of eopkg packages\n 8. {xbps}: Number of xbps packages\n 9. {nix-system}: Number of nix-system packages\n 10. {nix-user}: Number of nix-user packages\n 11. {nix-default}: Number of nix-default packages\n 12. {apk}: Number of apk packages\n 13. {pkg}: Number of pkg packages\n 14. {flatpak-system}: Number of flatpak-system app packages\n 15. {flatpak-user}: Number of flatpak-user app packages\n 16. {snap}: Number of snap packages\n 17. {brew}: Number of brew packages\n 18. {brew-cask}: Number of brew-cask packages\n 19. {macports}: Number of macports packages\n 20. {scoop}: Number of scoop packages\n 21. {choco}: Number of choco packages\n 22. {pkgtool}: Number of pkgtool packages\n 23. {paludis}: Number of paludis packages\n 24. {winget}: Number of winget packages\n 25. {opkg}: Number of opkg packages\n 26. {am}: Number of am packages\n 27. {sorcery}: Number of sorcery packages\n 28. {lpkg}: Number of lpkg packages\n 29. {lpkgbuild}: Number of lpkgbuild packages\n 30. {guix-system}: Number of guix-system packages\n 31. {guix-user}: Number of guix-user packages\n 32. {guix-home}: Number of guix-home packages\n 33. {linglong}: Number of linglong packages\n 34. {pacstall}: Number of pacstall packages\n 35. {mport}: Number of mport packages\n 36. {qi}: Number of qi packages\n 37. {nix-all}: Total number of all nix packages\n 38. {flatpak-all}: Total number of all flatpak app packages\n 39. {brew-all}: Total number of all brew packages\n 40. {guix-all}: Total number of all guix packages", + "description": "Output format of the module `Packages`. See `-h format` for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of emerge packages\n 7. {eopkg}: Number of eopkg packages\n 8. {xbps}: Number of xbps packages\n 9. {nix-system}: Number of nix-system packages\n 10. {nix-user}: Number of nix-user packages\n 11. {nix-default}: Number of nix-default packages\n 12. {apk}: Number of apk packages\n 13. {pkg}: Number of pkg packages\n 14. {flatpak-system}: Number of flatpak-system app packages\n 15. {flatpak-user}: Number of flatpak-user app packages\n 16. {snap}: Number of snap packages\n 17. {brew}: Number of brew packages\n 18. {brew-cask}: Number of brew-cask packages\n 19. {macports}: Number of macports packages\n 20. {scoop}: Number of scoop packages\n 21. {choco}: Number of choco packages\n 22. {pkgtool}: Number of pkgtool packages\n 23. {paludis}: Number of paludis packages\n 24. {winget}: Number of winget packages\n 25. {opkg}: Number of opkg packages\n 26. {am-system}: Number of am-system packages\n 27. {sorcery}: Number of sorcery packages\n 28. {lpkg}: Number of lpkg packages\n 29. {lpkgbuild}: Number of lpkgbuild packages\n 30. {guix-system}: Number of guix-system packages\n 31. {guix-user}: Number of guix-user packages\n 32. {guix-home}: Number of guix-home packages\n 33. {linglong}: Number of linglong packages\n 34. {pacstall}: Number of pacstall packages\n 35. {mport}: Number of mport packages\n 36. {qi}: Number of qi packages\n 37. {am-user}: Number of am-user (aka appman) packages\n 38. {nix-all}: Total number of all nix packages\n 39. {flatpak-all}: Total number of all flatpak app packages\n 40. {brew-all}: Total number of all brew packages\n 41. {guix-all}: Total number of all guix packages", "type": "string" }, "physicaldiskFormat": { @@ -315,7 +315,7 @@ "type": "string" }, "soundFormat": { - "description": "Output format of the module `Sound`. See `-h format` for formatting syntax\n 1. {is-main}: Is main sound device\n 2. {name}: Device name\n 3. {volume-percentage}: Volume (in percentage num)\n 4. {identifier}: Identifier\n 5. {volume-percentage-bar}: Volume (in percentage bar)", + "description": "Output format of the module `Sound`. See `-h format` for formatting syntax\n 1. {is-main}: Is main sound device\n 2. {name}: Device name\n 3. {volume-percentage}: Volume (in percentage num)\n 4. {identifier}: Identifier\n 5. {volume-percentage-bar}: Volume (in percentage bar)\n 6. {platform-api}: Platform API used", "type": "string" }, "swapFormat": { @@ -375,7 +375,7 @@ "type": "string" }, "wmFormat": { - "description": "Output format of the module `WM`. See `-h format` for formatting syntax\n 1. {process-name}: WM process name\n 2. {pretty-name}: WM pretty name\n 3. {protocol-name}: WM protocol name\n 4. {plugin-name}: WM plugin name", + "description": "Output format of the module `WM`. See `-h format` for formatting syntax\n 1. {process-name}: WM process name\n 2. {pretty-name}: WM pretty name\n 3. {protocol-name}: WM protocol name\n 4. {plugin-name}: WM plugin name\n 5. {version}: WM version", "type": "string" }, "wifiFormat": { @@ -432,6 +432,7 @@ "sixel", "kitty", "kitty-direct", + "kitty-icat", "iterm", "chafa", "raw", diff --git a/src/common/processing_windows.c b/src/common/processing_windows.c index e5f7086d3e..60b3ec2711 100644 --- a/src/common/processing_windows.c +++ b/src/common/processing_windows.c @@ -169,6 +169,9 @@ bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFst ? GetCurrentProcess() : OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + if (hProcess == NULL) + return false; + if (gui) *gui = GetGuiResources(hProcess, GR_GDIOBJECTS) > 0; diff --git a/src/data/help.json b/src/data/help.json index 481b3a3e2b..6acbb17bfa 100644 --- a/src/data/help.json +++ b/src/data/help.json @@ -186,7 +186,8 @@ "data-raw": "Text data, printed as is", "sixel": "Image file, printed as sixel codes", "kitty": "Image file, printed as kitty graphics protocol", - "kitty_direct": "Image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm)", + "kitty-direct": "Image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm)", + "kitty-icat": "Image file, use `kitten icat` to display the image. Requires binary `kitten` to be installed", "iterm": "Image file, printed as iterm graphics protocol", "chafa": "Image file, printed as ascii art using libchafa", "raw": "Image file, printed as raw binary string", @@ -351,6 +352,14 @@ "type": "path" } }, + { + "long": "kitty-icat", + "desc": "Short for --logo-type kitty-icat --logo ", + "remark": "See \"--help logo-type\" for more info", + "arg": { + "type": "path" + } + }, { "long": "iterm", "desc": "Short for --logo-type iterm --logo ", diff --git a/src/detection/battery/battery_nbsd.c b/src/detection/battery/battery_nbsd.c index 682bfbfd28..0938533366 100644 --- a/src/detection/battery/battery_nbsd.c +++ b/src/detection/battery/battery_nbsd.c @@ -46,7 +46,7 @@ const char* ffDetectBattery(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* r { if (prop_object_type(dict) != PROP_TYPE_DICTIONARY) continue; - + const char* desc = NULL; if (!prop_dictionary_get_string(dict, "description", &desc)) continue; @@ -88,7 +88,7 @@ const char* ffDetectBattery(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* r ffStrbufInit(&battery->manufactureDate); battery->timeRemaining = -1; - battery->capacity = (double) curr / max; + battery->capacity = (double) curr / (double) max * 100.; if (charging) ffStrbufAppendS(&battery->status, "Charging, "); else if (dischargeRate) @@ -102,6 +102,6 @@ const char* ffDetectBattery(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* r } } prop_object_iterator_release(itKey); - + return NULL; } diff --git a/src/detection/bluetooth/bluetooth_bsd.c b/src/detection/bluetooth/bluetooth_bsd.c new file mode 100644 index 0000000000..117675dadf --- /dev/null +++ b/src/detection/bluetooth/bluetooth_bsd.c @@ -0,0 +1,25 @@ +#include "bluetooth.h" + +#define L2CAP_SOCKET_CHECKED +#include + +static int enumDev(FF_MAYBE_UNUSED int sockfd, struct bt_devinfo const* dev, FFlist* devices) +{ + FFBluetoothResult* device = ffListAdd(devices); + ffStrbufInitS(&device->name, bt_devremote_name_gen(dev->devname, &dev->bdaddr)); + ffStrbufInitS(&device->address, bt_ntoa(&dev->bdaddr, NULL)); + ffStrbufUpperCase(&device->address); + ffStrbufInit(&device->type); + device->battery = 0; + device->connected = true; + return 0; +} + +const char* ffDetectBluetooth(FF_MAYBE_UNUSED FFBluetoothOptions* options, FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothResult */) +{ + // struct hostent* ent = bt_gethostent(); + if (bt_devenum((void*) enumDev, devices) < 0) + return "bt_devenum() failed"; + + return 0; +} diff --git a/src/detection/brightness/brightness_bsd.c b/src/detection/brightness/brightness_bsd.c index b71f1074f9..c77c378fea 100644 --- a/src/detection/brightness/brightness_bsd.c +++ b/src/detection/brightness/brightness_bsd.c @@ -34,7 +34,7 @@ const char* ffDetectBrightness(FF_MAYBE_UNUSED FFBrightnessOptions* options, FFl brightness->current = status.brightness; struct backlight_info info; - if(ioctl(blfd, BACKLIGHTGETINFO, &info) < 0) + if(ioctl(blfd, BACKLIGHTGETINFO, &info) == 0) ffStrbufAppendS(&brightness->name, info.name); else ffStrbufAppendS(&brightness->name, path + strlen("/dev/backlight/")); diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 3fc9c8557b..48e88a5432 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -44,12 +44,20 @@ static double parseHwmonDir(FFstrbuf* dir, FFstrbuf* buffer) if( ffStrbufContainS(buffer, "cpu") || ffStrbufEqualS(buffer, "k10temp") || // AMD + ffStrbufEqualS(buffer, "fam15h_power") || // AMD ffStrbufEqualS(buffer, "coretemp") // Intel ) return value / 1000.; return 0.0/0.0; } +static double detectTZTemp(FFstrbuf* buffer) +{ + if (ffReadFileBuffer("/sys/class/thermal/thermal_zone0/temp", buffer)) + return ffStrbufToDouble(buffer) / 1000.; + return 0.0/0.0; +} + static double detectCPUTemp(void) { FF_STRBUF_AUTO_DESTROY baseDir = ffStrbufCreateA(64); @@ -79,7 +87,7 @@ static double detectCPUTemp(void) ffStrbufSubstrBefore(&baseDir, baseDirLength); } - return 0.0/0.0; + return detectTZTemp(&buffer); } #ifdef __ANDROID__ @@ -136,6 +144,36 @@ static void detectQualcomm(FFCPUResult* cpu) ffStrbufSetStatic(&cpu->name, "Qualcomm Snapdragon 4 Gen 1 [SM4375]"); } +static void detectMediaTek(FFCPUResult* cpu) +{ + // https://en.wikipedia.org/wiki/List_of_MediaTek_systems_on_chips + + if (ffStrbufEqualS(&cpu->name, "MT6991")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9400 [MT6991]"); + else if (ffStrbufEqualS(&cpu->name, "MT6991Z")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9400 [MT6991Z]"); + else if (ffStrbufEqualS(&cpu->name, "MT6989Z")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9300+ [MT6989Z]"); + else if (ffStrbufEqualS(&cpu->name, "MT8796Z")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9300+ [MT8796Z]"); + else if (ffStrbufEqualS(&cpu->name, "MT6989")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9300 [MT6989]"); + else if (ffStrbufEqualS(&cpu->name, "MT8796")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9300 [MT8796]"); + else if (ffStrbufEqualS(&cpu->name, "MT6985W")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9200+ [MT6985W]"); + else if (ffStrbufEqualS(&cpu->name, "MT6985")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9200 [MT6985]"); + else if (ffStrbufEqualS(&cpu->name, "MT6983W")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9000+ [MT6983W]"); + else if (ffStrbufEqualS(&cpu->name, "MT8798Z/T")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9000+ [MT8798Z/T]"); + else if (ffStrbufEqualS(&cpu->name, "MT6983Z")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9000 [MT6983Z]"); + else if (ffStrbufEqualS(&cpu->name, "MT8798Z/C")) + ffStrbufSetStatic(&cpu->name, "MediaTek Dimensity 9000 [MT8798Z/C]"); +} + static void detectAndroid(FFCPUResult* cpu) { if (cpu->name.length == 0) @@ -153,6 +191,8 @@ static void detectAndroid(FFCPUResult* cpu) if (ffStrbufEqualS(&cpu->vendor, "QTI") && ffStrbufStartsWithS(&cpu->name, "SM")) detectQualcomm(cpu); + else if (ffStrbufEqualS(&cpu->vendor, "MTK") && ffStrbufStartsWithS(&cpu->name, "MT")) + detectMediaTek(cpu); } #endif @@ -188,6 +228,7 @@ static void detectArmName(FFstrbuf* cpuinfo, FFCPUResult* cpu, uint32_t implId) { // https://github.com/Dr-Noob/cpufetch/issues/213#issuecomment-1927782105 ffStrbufSetStatic(&cpu->name, "Virtualized Apple Silicon"); + ffStrbufGetlineRestore(&line, &len, cpuinfo); return; } name = applePartId2name(partId); @@ -243,7 +284,10 @@ static const char* parseCpuInfo( && cpu->name.length > 0 // #1202 #1204 #endif ) + { + ffStrbufGetlineRestore(&line, &len, cpuinfo); break; + } (void)( // arm64 doesn't have "model name"; arm32 does have "model name" but its value is not useful. @@ -512,6 +556,8 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) cpu->coresPhysical = (uint16_t) ffStrbufToUInt(&physicalCoresBuffer, cpu->coresLogical); #if __x86_64__ || __i386__ cpu->packages = getPackageCount(&cpuinfo); + if (cpu->packages > 1) + cpu->coresPhysical *= cpu->packages; #endif // Ref https://github.com/fastfetch-cli/fastfetch/issues/1194#issuecomment-2295058252 diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c index 557be31aae..c8324f7897 100644 --- a/src/detection/displayserver/linux/wmde.c +++ b/src/detection/displayserver/linux/wmde.c @@ -62,6 +62,9 @@ static const char* parseEnv(void) if(getenv("HYPRLAND_CMD") != NULL) return "Hyprland"; + if(getenv("SWAYSOCK") != NULL) + return "Sway"; + #ifdef __linux__ if( getenv("WAYLAND_DISPLAY") != NULL && diff --git a/src/detection/gpu/gpu.c b/src/detection/gpu/gpu.c index a863760cd1..86e16716d2 100644 --- a/src/detection/gpu/gpu.c +++ b/src/detection/gpu/gpu.c @@ -16,7 +16,7 @@ const char* FF_GPU_VENDOR_NAME_MICROSOFT = "Microsoft"; const char* FF_GPU_VENDOR_NAME_REDHAT = "RedHat"; const char* FF_GPU_VENDOR_NAME_ORACLE = "Oracle"; -const char* ffGetGPUVendorString(unsigned vendorId) +const char* ffGPUGetVendorString(unsigned vendorId) { // https://devicehunt.com/all-pci-vendors switch (vendorId) diff --git a/src/detection/gpu/gpu.h b/src/detection/gpu/gpu.h index 5ccd33dd8e..0d5f3dc928 100644 --- a/src/detection/gpu/gpu.h +++ b/src/detection/gpu/gpu.h @@ -46,7 +46,7 @@ typedef struct FFGPUResult const char* ffDetectGPU(const FFGPUOptions* options, FFlist* result); const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus); -const char* ffGetGPUVendorString(unsigned vendorId); +const char* ffGPUGetVendorString(unsigned vendorId); #if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__NetBSD__) || defined(__OpenBSD__) void ffGPUFillVendorAndName(uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu); diff --git a/src/detection/gpu/gpu_apple.c b/src/detection/gpu/gpu_apple.c index cc35ef0ee2..551b8bffe7 100644 --- a/src/detection/gpu/gpu_apple.c +++ b/src/detection/gpu/gpu_apple.c @@ -161,7 +161,7 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) int vendorId; if(ffCfDictGetInt(properties, CFSTR("vendor-id"), &vendorId) == NULL) { - const char* vendorStr = ffGetGPUVendorString((unsigned) vendorId); + const char* vendorStr = ffGPUGetVendorString((unsigned) vendorId); ffStrbufAppendS(&gpu->vendor, vendorStr); if (vendorStr == FF_GPU_VENDOR_NAME_APPLE || vendorStr == FF_GPU_VENDOR_NAME_INTEL) gpu->type = FF_GPU_TYPE_INTEGRATED; diff --git a/src/detection/gpu/gpu_bsd.c b/src/detection/gpu/gpu_bsd.c index 49218e13f4..40821c9b84 100644 --- a/src/detection/gpu/gpu_bsd.c +++ b/src/detection/gpu/gpu_bsd.c @@ -38,7 +38,7 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) struct pci_conf* pc = &confs[i]; FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); - ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString(pc->pc_vendor)); + ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(pc->pc_vendor)); ffStrbufInit(&gpu->name); ffStrbufInitS(&gpu->driver, pc->pd_name); ffStrbufInit(&gpu->platformApi); diff --git a/src/detection/gpu/gpu_general.c b/src/detection/gpu/gpu_general.c index 31d7bac3ec..464cccbef9 100644 --- a/src/detection/gpu/gpu_general.c +++ b/src/detection/gpu/gpu_general.c @@ -15,7 +15,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_slot_match_iterator_create) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_device_next) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_system_cleanup) - + { // Requires root access // Same behavior can be observed with `cp $(which scanpci) /tmp/ && /tmp/scanpci` @@ -31,7 +31,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* continue; FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); - ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString(dev->vendor_id)); + ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(dev->vendor_id)); ffStrbufInit(&gpu->name); ffStrbufInit(&gpu->driver); ffStrbufInit(&gpu->platformApi); diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index f67a9cf60e..cc655e31ed 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -435,7 +435,7 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf return "Invalid PCI device path"; FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); - ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString((uint16_t) vendorId)); + ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString((uint16_t) vendorId)); ffStrbufInit(&gpu->name); ffStrbufInit(&gpu->driver); ffStrbufInit(&gpu->platformApi); diff --git a/src/detection/gpu/gpu_sunos.c b/src/detection/gpu/gpu_sunos.c index 4edfb7fb07..d79f430e29 100644 --- a/src/detection/gpu/gpu_sunos.c +++ b/src/detection/gpu/gpu_sunos.c @@ -51,7 +51,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* uint32_t revision = (uint32_t) strtoul(pclass, NULL, 16); FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); - ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString(vendorId)); + ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(vendorId)); ffStrbufInit(&gpu->name); ffStrbufInit(&gpu->driver); ffStrbufInit(&gpu->platformApi); diff --git a/src/detection/gpu/gpu_windows.c b/src/detection/gpu/gpu_windows.c index 2a29d83a97..7543392d93 100644 --- a/src/detection/gpu/gpu_windows.c +++ b/src/detection/gpu/gpu_windows.c @@ -72,7 +72,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* { uint32_t vendorId = 0; if(ffRegReadUint(hDirectxKey, L"VendorId", &vendorId, NULL) && vendorId) - ffStrbufSetStatic(&gpu->vendor, ffGetGPUVendorString(vendorId)); + ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId)); if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL) gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; @@ -147,7 +147,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* if (SetupDiGetDeviceRegistryPropertyW(hdev, &did, SPDRP_HARDWAREID, NULL, (PBYTE) buffer, sizeof(buffer), NULL)) { swscanf(buffer, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId); - ffStrbufSetStatic(&gpu->vendor, ffGetGPUVendorString(vendorId)); + ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId)); } detectFn( diff --git a/src/detection/gpu/gpu_wsl.cpp b/src/detection/gpu/gpu_wsl.cpp index 5448bb6b95..d8d7700b0a 100644 --- a/src/detection/gpu/gpu_wsl.cpp +++ b/src/detection/gpu/gpu_wsl.cpp @@ -104,7 +104,7 @@ const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FF DXCoreHardwareID hardwareId; if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::HardwareID, sizeof(hardwareId), &hardwareId))) { - const char* vendorStr = ffGetGPUVendorString((unsigned) hardwareId.vendorID); + const char* vendorStr = ffGPUGetVendorString((unsigned) hardwareId.vendorID); ffStrbufSetStatic(&gpu->vendor, vendorStr); if (vendorStr == FF_GPU_VENDOR_NAME_NVIDIA && (options->driverSpecific || options->temp)) diff --git a/src/detection/host/host_mac.c b/src/detection/host/host_mac.c index 8f8551cfc7..20460b4d4c 100644 --- a/src/detection/host/host_mac.c +++ b/src/detection/host/host_mac.c @@ -109,11 +109,11 @@ const char* ffHostGetMacProductNameWithHwModel(const FFstrbuf* hwModel) const char* version = hwModel->chars + strlen("Mac"); if(ffStrEquals(version, "16,3")) return "iMac (24-inch, 2024, Four Thunderbolt / USB 4 ports)"; if(ffStrEquals(version, "16,2")) return "iMac (24-inch, 2024, Two Thunderbolt / USB 4 ports)"; - if(ffStrEquals(version, "16,1") || - ffStrEquals(version, "16,6") || - ffStrEquals(version, "16,8")) return "MacBook Pro (14-inch, 2024, Three Thunderbolt 4 ports)"; + if(ffStrEquals(version, "16,1")) return "MacBook Pro (14-inch, 2024, Three Thunderbolt 4 ports)"; + if(ffStrEquals(version, "16,6") || + ffStrEquals(version, "16,8")) return "MacBook Pro (14-inch, 2024, Three Thunderbolt 5 ports)"; if(ffStrEquals(version, "16,7") || - ffStrEquals(version, "16,5")) return "MacBook Pro (16-inch, 2024, Three Thunderbolt 4 ports)"; + ffStrEquals(version, "16,5")) return "MacBook Pro (16-inch, 2024, Three Thunderbolt 5 ports)"; if(ffStrEquals(version, "16,15") || ffStrEquals(version, "16,10")) return "Mac mini (2024)"; if(ffStrEquals(version, "15,13")) return "MacBook Air (15-inch, M3, 2024)"; diff --git a/src/detection/media/media_linux.c b/src/detection/media/media_linux.c index 4434f317eb..479fa431d6 100644 --- a/src/detection/media/media_linux.c +++ b/src/detection/media/media_linux.c @@ -93,8 +93,6 @@ static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResul DBusMessageIter arrayIterator; data->lib->ffdbus_message_iter_recurse(&rootIterator, &arrayIterator); - FF_STRBUF_AUTO_DESTROY desktopIdentity = ffStrbufCreate(); - while(true) { if(data->lib->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY) diff --git a/src/detection/opencl/opencl.c b/src/detection/opencl/opencl.c index 56f86215f6..0c37000236 100644 --- a/src/detection/opencl/opencl.c +++ b/src/detection/opencl/opencl.c @@ -112,7 +112,7 @@ static const char* openCLHandleData(OpenCLData* data, FFOpenCLResult* result) { cl_uint vendorId; if (data->ffclGetDeviceInfo(deviceID, CL_DEVICE_VENDOR_ID, sizeof(vendorId), &vendorId, NULL) == CL_SUCCESS) - ffStrbufSetStatic(&gpu->vendor, ffGetGPUVendorString(vendorId)); + ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId)); if (gpu->vendor.length == 0 && data->ffclGetDeviceInfo(deviceID, CL_DEVICE_VENDOR, sizeof(buffer), buffer, NULL) == CL_SUCCESS) ffStrbufSetS(&gpu->vendor, buffer); } diff --git a/src/detection/opengl/opengl_linux.c b/src/detection/opengl/opengl_linux.c index 1aaf49f229..01491ef666 100644 --- a/src/detection/opengl/opengl_linux.c +++ b/src/detection/opengl/opengl_linux.c @@ -4,6 +4,13 @@ #include +#if __ANDROID__ && !defined(FF_HAVE_EGL) + // On Android, installing OpenGL headers is enough (mesa-dev) + #if __has_include() + #define FF_HAVE_EGL 1 + #endif +#endif + #if defined(FF_HAVE_EGL) || defined(FF_HAVE_GLX) || defined(FF_HAVE_OSMESA) #define FF_HAVE_GL 1 diff --git a/src/detection/opengl/opengl_shared.c b/src/detection/opengl/opengl_shared.c index f13916e74e..e32b681b8b 100644 --- a/src/detection/opengl/opengl_shared.c +++ b/src/detection/opengl/opengl_shared.c @@ -3,7 +3,7 @@ #if __has_include() #include -#elif __has_include() +#elif __APPLE__ #define GL_SILENCE_DEPRECATION 1 #include #else @@ -64,9 +64,14 @@ static const char* eglHandleContext(FFOpenGLResult* result, EGLData* data) return NULL; } -static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data) +static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data, bool gles) { - data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){EGL_NONE}); + data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){ + EGL_CONTEXT_CLIENT_VERSION, gles ? 2 : 1, // Try GLES 2.0+ first + EGL_NONE + }); + if(data->context == EGL_NO_CONTEXT && gles) // Some ANGLE builds support GLES 1.1 only + data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){EGL_NONE}); if(data->context == EGL_NO_CONTEXT) return "eglCreateContext returned EGL_NO_CONTEXT"; @@ -78,10 +83,11 @@ static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data) static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data) { // try use OpenGL API. If failed, use the default API (usually OpenGL ES) - data->ffeglBindAPI(EGL_OPENGL_API); + bool gles = !data->ffeglBindAPI(EGL_OPENGL_API); EGLint eglConfigCount; data->ffeglGetConfigs(data->display, &data->config, 1, &eglConfigCount); + if(eglConfigCount == 0) return "eglGetConfigs returned 0 configs"; @@ -94,7 +100,7 @@ static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data) if(data->surface == EGL_NO_SURFACE) return "eglCreatePbufferSurface returned EGL_NO_SURFACE"; - const char* error = eglHandleSurface(result, data); + const char* error = eglHandleSurface(result, data, gles); data->ffeglDestroySurface(data->display, data->surface); return error; } @@ -148,7 +154,7 @@ const char* ffOpenGLDetectByEGL(FFOpenGLResult* result) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroySurface); FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglTerminate); - FF_SUPPRESS_IO(); + // FF_SUPPRESS_IO(); return eglHandleData(result, &eglData); } diff --git a/src/detection/os/os_android.c b/src/detection/os/os_android.c index 17752a27b6..7d39954d6b 100644 --- a/src/detection/os/os_android.c +++ b/src/detection/os/os_android.c @@ -5,8 +5,6 @@ void ffDetectOSImpl(FFOSResult* os) { ffStrbufSetStatic(&os->name, "Android"); - ffStrbufSetStatic(&os->prettyName, "Android"); - ffStrbufSetStatic(&os->id, "android"); ffSettingsGetAndroidProperty("ro.build.version.release", &os->version); diff --git a/src/detection/os/os_apple.m b/src/detection/os/os_apple.m index b86202b1e4..dc8e30a126 100644 --- a/src/detection/os/os_apple.m +++ b/src/detection/os/os_apple.m @@ -54,18 +54,18 @@ static bool detectOSCodeName(FFOSResult* os) case 14: ffStrbufSetStatic(&os->codename, "Mojave"); return true; case 13: ffStrbufSetStatic(&os->codename, "High Sierra"); return true; case 12: ffStrbufSetStatic(&os->codename, "Sierra"); return true; - case 11: ffStrbufSetStatic(&os->codename, "El Capitan"); ffStrbufSetStatic(&os->prettyName, "OS X"); return true; - case 10: ffStrbufSetStatic(&os->codename, "Yosemite"); ffStrbufSetStatic(&os->prettyName, "OS X"); return true; - case 9: ffStrbufSetStatic(&os->codename, "Mavericks"); ffStrbufSetStatic(&os->prettyName, "OS X"); return true; - case 8: ffStrbufSetStatic(&os->codename, "Mountain Lion"); ffStrbufSetStatic(&os->prettyName, "OS X"); return true; - case 7: ffStrbufSetStatic(&os->codename, "Lion"); ffStrbufSetStatic(&os->prettyName, "Mac OS X"); return true; - case 6: ffStrbufSetStatic(&os->codename, "Snow Leopard"); ffStrbufSetStatic(&os->prettyName, "Mac OS X"); return true; - case 5: ffStrbufSetStatic(&os->codename, "Leopard"); ffStrbufSetStatic(&os->prettyName, "Mac OS X"); return true; - case 4: ffStrbufSetStatic(&os->codename, "Tiger"); ffStrbufSetStatic(&os->prettyName, "Mac OS X"); return true; + case 11: ffStrbufSetStatic(&os->codename, "El Capitan"); return true; + case 10: ffStrbufSetStatic(&os->codename, "Yosemite"); return true; + case 9: ffStrbufSetStatic(&os->codename, "Mavericks"); return true; + case 8: ffStrbufSetStatic(&os->codename, "Mountain Lion"); return true; + case 7: ffStrbufSetStatic(&os->codename, "Lion"); return true; + case 6: ffStrbufSetStatic(&os->codename, "Snow Leopard"); return true; + case 5: ffStrbufSetStatic(&os->codename, "Leopard"); return true; + case 4: ffStrbufSetStatic(&os->codename, "Tiger"); return true; case 3: ffStrbufSetStatic(&os->codename, "Panther"); ffStrbufSetStatic(&os->prettyName, "Mac OS X"); return true; - case 2: ffStrbufSetStatic(&os->codename, "Jaguar"); ffStrbufSetStatic(&os->prettyName, "Mac OS X"); return true; - case 1: ffStrbufSetStatic(&os->codename, "Puma"); ffStrbufSetStatic(&os->prettyName, "Mac OS X"); return true; - case 0: ffStrbufSetStatic(&os->codename, "Cheetah"); ffStrbufSetStatic(&os->prettyName, "Mac OS X"); return true; + case 2: ffStrbufSetStatic(&os->codename, "Jaguar"); return true; + case 1: ffStrbufSetStatic(&os->codename, "Puma"); return true; + case 0: ffStrbufSetStatic(&os->codename, "Cheetah"); return true; } } } @@ -106,8 +106,6 @@ void ffDetectOSImpl(FFOSResult* os) if(os->buildID.length == 0) ffSysctlGetString("kern.osversion", &os->buildID); - if(os->prettyName.length == 0) - ffStrbufSetStatic(&os->prettyName, "macOS"); ffStrbufAppend(&os->versionID, &os->version); if(!detectOSCodeName(os)) diff --git a/src/detection/os/os_linux.c b/src/detection/os/os_linux.c index f293d5f0ef..7774847ca5 100644 --- a/src/detection/os/os_linux.c +++ b/src/detection/os/os_linux.c @@ -80,6 +80,14 @@ FF_MAYBE_UNUSED static void getUbuntuFlavour(FFOSResult* result) ffStrbufSetS(&result->versionID, result->prettyName.chars + strlen("Linux Lite ")); return; } + else if(ffStrbufStartsWithS(&result->prettyName, "Rhino Linux ")) + { + ffStrbufSetS(&result->name, "Rhino Linux"); + ffStrbufSetS(&result->id, "rhinolinux"); + ffStrbufSetS(&result->idLike, "ubuntu"); + ffStrbufSetS(&result->versionID, result->prettyName.chars + strlen("Rhino Linux ")); + return; + } if(ffStrContains(xdgConfigDirs, "kde") || ffStrContains(xdgConfigDirs, "plasma") || ffStrContains(xdgConfigDirs, "kubuntu")) { @@ -99,7 +107,7 @@ FF_MAYBE_UNUSED static void getUbuntuFlavour(FFOSResult* result) return; } - if(ffStrContains(xdgConfigDirs, "lxde") || ffStrContains(xdgConfigDirs, "lubuntu")) + if(ffStrContains(xdgConfigDirs, "lxqt") || ffStrContains(xdgConfigDirs, "lubuntu")) { ffStrbufSetS(&result->name, "Lubuntu"); ffStrbufSetS(&result->prettyName, "Lubuntu"); diff --git a/src/detection/os/os_windows.c b/src/detection/os/os_windows.c index dda2004c51..ae3ff50832 100644 --- a/src/detection/os/os_windows.c +++ b/src/detection/os/os_windows.c @@ -13,6 +13,7 @@ void ffDetectOSImpl(FFOSResult* os) const wchar_t* rawName = BrandingFormatString(L"%WINDOWS_LONG%"); ffStrbufSetWS(&os->variant, rawName); GlobalFree((HGLOBAL)rawName); + ffStrbufSet(&os->prettyName, &os->variant); ffStrbufTrimRight(&os->variant, ' '); //WMI returns the "Microsoft" prefix while BrandingFormatString doesn't. Make them consistent. @@ -22,14 +23,12 @@ void ffDetectOSImpl(FFOSResult* os) if(ffStrbufStartsWithS(&os->variant, "Windows ")) { ffStrbufAppendS(&os->name, "Windows"); - ffStrbufAppendS(&os->prettyName, "Windows"); ffStrbufSubstrAfter(&os->variant, strlen("Windows ") - 1); if(ffStrbufStartsWithS(&os->variant, "Server ")) { ffStrbufAppendS(&os->name, " Server"); - ffStrbufAppendS(&os->prettyName, " Server"); ffStrbufSubstrAfter(&os->variant, strlen(" Server") - 1); } @@ -38,7 +37,7 @@ void ffDetectOSImpl(FFOSResult* os) ffStrbufSubstrAfter(&os->variant, index); // Windows Server 20xx Rx - if(ffStrbufEndsWithC(&os->prettyName, 'r')) + if(ffStrbufEndsWithC(&os->name, 'r')) { if(os->variant.chars[0] == 'R' && ffCharIsDigit(os->variant.chars[1]) && @@ -56,6 +55,6 @@ void ffDetectOSImpl(FFOSResult* os) ffStrbufClear(&os->variant); } - ffStrbufAppendF(&os->id, "%*s %*s", os->prettyName.length, os->prettyName.chars, os->version.length, os->version.chars); + ffStrbufAppendF(&os->id, "%s %s", os->name.chars, os->version.chars); ffStrbufSetStatic(&os->idLike, "Windows"); } diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index 261c8acb08..24ce9748c3 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -4,7 +4,8 @@ typedef struct FFPackagesResult { - uint32_t am; + uint32_t amSystem; + uint32_t amUser; uint32_t apk; uint32_t brew; uint32_t brewCask; diff --git a/src/detection/packages/packages_apple.c b/src/detection/packages/packages_apple.c index e708eee255..350426deaf 100644 --- a/src/detection/packages/packages_apple.c +++ b/src/detection/packages/packages_apple.c @@ -52,7 +52,7 @@ static void getBrewPackages(FFPackagesResult* result) static uint32_t countMacPortsPackages(const char* dirname) { FF_STRBUF_AUTO_DESTROY baseDir = ffStrbufCreateS(dirname); - ffStrbufAppendS(&baseDir, FASTFETCH_TARGET_DIR_ROOT "/var/macports/software"); + ffStrbufAppendS(&baseDir, "/var/macports/software"); return getNumElements(baseDir.chars, DT_DIR); } diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 6d5eff0794..1242f12387 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -386,7 +386,29 @@ static uint32_t getRpmFromLibrpm(void) #endif //FF_HAVE_RPM -static uint32_t getAM(FFstrbuf* baseDir) +static uint32_t getAMPackages(FFstrbuf* baseDir) +{ + uint32_t baseLength = baseDir->length; + FF_AUTO_CLOSE_DIR DIR* dirp = opendir(baseDir->chars); + if (!dirp) return 0; + + uint32_t result = 0; + struct dirent *entry; + while ((entry = readdir(dirp)) != NULL) + { + if (entry->d_name[0] == '.') continue; + if (entry->d_type == DT_DIR) + { + ffStrbufAppendF(baseDir, "/%s/remove", entry->d_name); + if (ffPathExists(baseDir->chars, FF_PATHTYPE_FILE)) + ++result; + ffStrbufSubstrBefore(baseDir, baseLength); + } + } + return result; +} + +static uint32_t getAMSystem(FFstrbuf* baseDir) { // #771 uint32_t baseDirLength = baseDir->length; @@ -401,28 +423,27 @@ static uint32_t getAM(FFstrbuf* baseDir) { ++result; // `am` itself is counted as a package too ffStrbufSubstrBefore(baseDir, optDirLength); - FF_AUTO_CLOSE_DIR DIR* dirp = opendir(baseDir->chars); - if(dirp) - { - struct dirent *entry; - while ((entry = readdir(dirp)) != NULL) - { - if (entry->d_name[0] == '.') continue; - if (entry->d_type == DT_DIR) - { - ffStrbufAppendF(baseDir, "/%s/AM-updater", entry->d_name); - if (ffPathExists(baseDir->chars, FF_PATHTYPE_FILE)) - ++result; - ffStrbufSubstrBefore(baseDir, optDirLength); - } - } - } + result = getAMPackages(baseDir); } ffStrbufSubstrBefore(baseDir, baseDirLength); return result; } +static uint32_t getAMUser(void) +{ + // check if $XDG_CONFIG_HOME/appman/appman-config exists + FFstrbuf* baseDir = FF_LIST_GET(FFstrbuf, instance.state.platform.configDirs, 0); + uint32_t baseLen = baseDir->length; + ffStrbufAppendS(baseDir, "appman/appman-config"); + FF_STRBUF_AUTO_DESTROY packagesPath = ffStrbufCreate(); + if (ffReadFileBuffer(baseDir->chars, &packagesPath)) + ffStrbufTrimRightSpace(&packagesPath); + ffStrbufSubstrBefore(baseDir, baseLen); + + return packagesPath.length > 0 ? getAMPackages(&packagesPath) : 0; +} + static int compareHash(const void* a, const void* b) { return memcmp(a, b, 32); @@ -565,7 +586,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, } if (!(options->disabled & FF_PACKAGES_FLAG_PALUDIS_BIT)) packageCounts->paludis += countFilesRecursive(baseDir, "/var/db/paludis/repositories", "environment.bz2"); if (!(options->disabled & FF_PACKAGES_FLAG_OPKG_BIT)) packageCounts->opkg += getNumStrings(baseDir, "/usr/lib/opkg/status", "Package:", "opkg"); // openwrt - if (!(options->disabled & FF_PACKAGES_FLAG_AM_BIT)) packageCounts->am = getAM(baseDir); + if (!(options->disabled & FF_PACKAGES_FLAG_AM_BIT)) packageCounts->amSystem = getAMSystem(baseDir); if (!(options->disabled & FF_PACKAGES_FLAG_SORCERY_BIT)) packageCounts->sorcery += getNumStrings(baseDir, "/var/state/sorcery/packages", ":installed:", "sorcery"); if (!(options->disabled & FF_PACKAGES_FLAG_GUIX_BIT)) { @@ -678,4 +699,7 @@ void ffDetectPackagesImpl(FFPackagesResult* result, FFPackagesOptions* options) if (!(options->disabled & FF_PACKAGES_FLAG_FLATPAK_BIT)) result->flatpakUser = getFlatpakPackages(&baseDir, "/.local/share"); + + if (!(options->disabled & FF_PACKAGES_FLAG_AM_BIT)) + result->amUser = getAMUser(); } diff --git a/src/detection/sound/sound.h b/src/detection/sound/sound.h index 6ad8110440..fb8d05f7e5 100644 --- a/src/detection/sound/sound.h +++ b/src/detection/sound/sound.h @@ -8,6 +8,7 @@ typedef struct FFSoundDevice { FFstrbuf identifier; FFstrbuf name; + FFstrbuf platformApi; uint8_t volume; // 0-100% bool main; bool active; diff --git a/src/detection/sound/sound_apple.c b/src/detection/sound/sound_apple.c index 22ecec2acf..1c9c3f630f 100644 --- a/src/detection/sound/sound_apple.c +++ b/src/detection/sound/sound_apple.c @@ -73,6 +73,7 @@ const char* ffDetectSound(FFlist* devices /* List of FFSoundDevice */) device->volume = FF_SOUND_VOLUME_UNKNOWN; ffStrbufInitF(&device->identifier, "%u", (unsigned) deviceId); ffStrbufInit(&device->name); + ffStrbufInitStatic(&device->platformApi, "Core Audio"); ffCfStrGetString(name, &device->name); uint32_t muted; diff --git a/src/detection/sound/sound_bsd.c b/src/detection/sound/sound_bsd.c index 4487b42a4b..fcc2730ad5 100644 --- a/src/detection/sound/sound_bsd.c +++ b/src/detection/sound/sound_bsd.c @@ -39,7 +39,8 @@ const char* ffDetectSound(FFlist* devices) FFSoundDevice* device = ffListAdd(devices); ffStrbufInitS(&device->identifier, path); ffStrbufInitF(&device->name, "%s %s", ci.longname, ci.hw_info); - ffStrbufTrimRightSpace(&device->name); + ffStrbufTrimRightSpace(&device->name); + ffStrbufInitStatic(&device->platformApi, "OSS"); device->volume = mutemask & SOUND_MASK_VOLUME ? 0 : ((uint8_t) volume /*left*/ + (uint8_t) (volume >> 8) /*right*/) / 2; diff --git a/src/detection/sound/sound_linux.c b/src/detection/sound/sound_linux.c index a66fb0a596..bd309aca0d 100644 --- a/src/detection/sound/sound_linux.c +++ b/src/detection/sound/sound_linux.c @@ -13,6 +13,7 @@ static void paSinkInfoCallback(pa_context *c, const pa_sink_info *i, int eol, vo FFSoundDevice* device = ffListAdd(userdata); ffStrbufInitS(&device->identifier, i->name); + ffStrbufInitS(&device->platformApi, "PulseAudio"); ffStrbufTrimRightSpace(&device->identifier); ffStrbufInitS(&device->name, i->description); ffStrbufTrimRightSpace(&device->name); @@ -22,20 +23,24 @@ static void paSinkInfoCallback(pa_context *c, const pa_sink_info *i, int eol, vo device->main = false; } -static void paServerInfoCallback(pa_context *c, const pa_server_info *i, void *userdata) +static void paServerInfoCallback(FF_MAYBE_UNUSED pa_context *c, const pa_server_info *i, void *userdata) { - FF_UNUSED(c); + if(!i) return; - if(!i) - return; + FF_STRBUF_AUTO_DESTROY api = ffStrbufCreate(); + const char* realServer = strstr(i->server_name, "(on "); + if (realServer) + { + ffStrbufSetS(&api, realServer + strlen("(on ")); + ffStrbufTrimRight(&api, ')'); + } + else + ffStrbufSetF(&api, "%s %s", i->server_name, i->server_version); FF_LIST_FOR_EACH(FFSoundDevice, device, *(FFlist*)userdata) { - if(ffStrbufEqualS(&device->identifier, i->default_sink_name)) - { - device->main = true; - break; - } + device->main = ffStrbufEqualS(&device->identifier, i->default_sink_name); + ffStrbufSet(&device->platformApi, &api); } } diff --git a/src/detection/sound/sound_windows.cpp b/src/detection/sound/sound_windows.cpp index 55e6e99b10..fece395f03 100644 --- a/src/detection/sound/sound_windows.cpp +++ b/src/detection/sound/sound_windows.cpp @@ -65,6 +65,7 @@ const char* ffDetectSound(FFlist* devices /* List of FFSoundDevice */) device->volume = FF_SOUND_VOLUME_UNKNOWN; ffStrbufInitWS(&device->identifier, immDeviceId); ffStrbufInit(&device->name); + ffStrbufInitStatic(&device->platformApi, "Core Audio APIs"); { PROPVARIANT __attribute__((__cleanup__(PropVariantClear))) friendlyName; diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index c933937bf9..005c3f32b9 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -48,6 +48,35 @@ static void detectAlacritty(FFTerminalFontResult* terminalFont) ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); } +static void detectGhostty(FFTerminalFontResult* terminalFont) +{ + FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate(); + + FFpropquery fontQueryToml[] = { + {"font-family =", &fontName}, + {"font-size =", &fontSize}, + }; + + if ( + #if __APPLE__ + !ffParsePropFileConfigValues("com.mitchellh.ghostty/config", 2, fontQueryToml) && + #endif + !ffParsePropFileConfigValues("ghostty/config", 2, fontQueryToml) + ) { + ffStrbufAppendS(&terminalFont->error, "Couldn't find file `ghostty/config`"); + return; + } + + if(fontName.length == 0) + ffStrbufAppendS(&fontName, "JetBrains Mono"); + + if(fontSize.length == 0) + ffStrbufAppendS(&fontSize, "13"); + + ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); +} + FF_MAYBE_UNUSED static void detectTTY(FFTerminalFontResult* terminalFont) { FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreate(); @@ -230,6 +259,8 @@ static bool detectTerminalFontCommon(const FFTerminalResult* terminal, FFTermina detectTabby(terminalFont); else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "contour")) detectContour(&terminal->exe, terminalFont); + else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "ghostty")) + detectGhostty(terminalFont); #ifndef _WIN32 else if(ffStrbufStartsWithIgnCaseS(&terminal->exe, "/dev/pts/")) diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index a591829a2a..f29902727d 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -15,41 +15,18 @@ #define _PATH_LOCALBASE "/usr/local" #elif __NetBSD__ #define _PATH_LOCALBASE "/usr/pkg" -#endif - -#ifdef _WIN32 +#elif _WIN32 -#include "util/mallocHelper.h" +#include "util/windows/version.h" +#include -#include - -static bool getFileVersion(const char* exePath, FFstrbuf* version) +static bool getFileVersion(const FFstrbuf* exePath, FFstrbuf* version) { - DWORD handle; - DWORD size = GetFileVersionInfoSizeA(exePath, &handle); - if(size > 0) - { - FF_AUTO_FREE void* versionData = malloc(size); - if(GetFileVersionInfoA(exePath, handle, size, versionData)) - { - VS_FIXEDFILEINFO* verInfo; - UINT len; - if(VerQueryValueW(versionData, L"\\", (void**)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD) - { - ffStrbufAppendF(version, "%u.%u.%u.%u", - (unsigned)(( verInfo->dwFileVersionMS >> 16 ) & 0xffff), - (unsigned)(( verInfo->dwFileVersionMS >> 0 ) & 0xffff), - (unsigned)(( verInfo->dwFileVersionLS >> 16 ) & 0xffff), - (unsigned)(( verInfo->dwFileVersionLS >> 0 ) & 0xffff) - ); - return true; - } - } - } - - return false; + wchar_t exePathW[PATH_MAX]; + int len = MultiByteToWideChar(CP_UTF8, 0, exePath->chars, (int)exePath->length, exePathW, ARRAY_SIZE(exePathW)); + if (len <= 0) return false; + return ffGetFileVersion(exePathW, version); } - #endif static bool getExeVersionRaw(FFstrbuf* exe, FFstrbuf* version) @@ -119,7 +96,7 @@ static bool getShellVersionPwsh(FFstrbuf* exe, FFstrbuf* version) } #ifdef _WIN32 - if(getFileVersion(exe->chars, version)) + if(getFileVersion(exe, version)) { ffStrbufSubstrBeforeLastC(version, '.'); return true; @@ -312,7 +289,7 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, if(ffStrEqualsIgnCase(exeName, "powershell") || ffStrEqualsIgnCase(exeName, "powershell_ise")) return getShellVersionWinPowerShell(exe, version); - return getFileVersion(exe->chars, version); + return getFileVersion(exe, version); #endif return false; @@ -621,6 +598,38 @@ static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) } } } + #elif __APPLE__ + if (ffStrbufEndsWithS(exe, "/kitty.app/Contents/MacOS/kitty")) + { + ffStrbufSet(version, exe); + ffStrbufSubstrBeforeLastC(version, '/'); + ffStrbufSubstrBeforeLastC(version, '/'); + ffStrbufAppendS(version, "/Info.plist"); + char buf[4096]; + ssize_t size = ffReadFileData(version->chars, ARRAY_SIZE(buf) - 1, buf); + if (size > 0) + { + buf[size] = '\0'; + + const char* p = strstr(buf, "CFBundleShortVersionString"); + if (p) + { + p += strlen("CFBundleShortVersionString"); + p = strchr(p, '>'); + if (p) + { + p++; + const char* end = strchr(p, '<'); + if (end) + { + ffStrbufSetNS(version, (uint32_t) (end - p), p); + return true; + } + } + } + } + ffStrbufClear(version); + } #endif char versionHex[64]; @@ -706,7 +715,7 @@ static bool getTerminalVersionWindowsTerminal(FFstrbuf* exe, FFstrbuf* version) return true; } - return getFileVersion(exe->chars, version); + return getFileVersion(exe, version); } static bool getTerminalVersionConEmu(FFstrbuf* exe, FFstrbuf* version) @@ -716,7 +725,7 @@ static bool getTerminalVersionConEmu(FFstrbuf* exe, FFstrbuf* version) if(version->length) return true; - return getFileVersion(exe->chars, version); + return getFileVersion(exe, version); } #endif @@ -875,7 +884,7 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe #ifdef _WIN32 - return getFileVersion(exe->chars, version); + return getFileVersion(exe, version); #else diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 91160f3d39..b0fe080856 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -56,7 +56,7 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid) ffStrbufEqualS(&result->processName, "fastfetch") || //994 ffStrbufEqualS(&result->processName, "flashfetch") || ffStrbufContainS(&result->processName, "debug") || - ffStrbufContainS(&result->processName, "not-found") || + ffStrbufContainS(&result->processName, "command-not-") || #ifdef __ANDROID__ ffStrbufEqualS(&result->processName, "proot") || #endif @@ -313,11 +313,11 @@ static void setShellInfoDetails(FFShellResult* result) static void setTerminalInfoDetails(FFTerminalResult* result) { - if(ffStrbufStartsWithC(&result->processName, '.') && ffStrbufEndsWithS(&result->processName, "-wrapped")) + if(ffStrbufStartsWithC(&result->processName, '.') && ffStrbufContainS(&result->processName, "-wrap")) { // For NixOS. Ref: #510 and https://github.com/NixOS/nixpkgs/pull/249428 // We use processName when detecting version and font, overriding it for simplification - ffStrbufSubstrBefore(&result->processName, result->processName.length - (uint32_t) strlen("-wrapped")); + ffStrbufSubstrBeforeLastC(&result->processName, '-'); ffStrbufSubstrAfter(&result->processName, 0); } diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index ca567bcc03..97bfe1820b 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -5,39 +5,13 @@ #include "util/mallocHelper.h" #include "util/windows/registry.h" #include "util/windows/unicode.h" +#include "util/windows/version.h" #include "util/stringUtils.h" #include #include #include -static bool getProductVersion(const wchar_t* filePath, FFstrbuf* version) -{ - DWORD handle; - DWORD size = GetFileVersionInfoSizeW(filePath, &handle); - if(size > 0) - { - FF_AUTO_FREE void* versionData = malloc(size); - if(GetFileVersionInfoW(filePath, handle, size, versionData)) - { - VS_FIXEDFILEINFO* verInfo; - UINT len; - if(VerQueryValueW(versionData, L"\\", (void**)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD) - { - ffStrbufAppendF(version, "%u.%u.%u.%u", - (unsigned)(( verInfo->dwProductVersionMS >> 16 ) & 0xffff), - (unsigned)(( verInfo->dwProductVersionMS >> 0 ) & 0xffff), - (unsigned)(( verInfo->dwProductVersionLS >> 16 ) & 0xffff), - (unsigned)(( verInfo->dwProductVersionLS >> 0 ) & 0xffff) - ); - return true; - } - } - } - - return false; -} - bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, const FFstrbuf* exePath, FFstrbuf* version); static uint32_t getShellInfo(FFShellResult* result, uint32_t pid) @@ -113,7 +87,7 @@ static void setShellInfoDetails(FFShellResult* result) if(wcsncmp(module.szModule, L"clink_dll_", strlen("clink_dll_")) == 0) { ffStrbufAppendS(&result->prettyName, " (with Clink "); - getProductVersion(module.szExePath, &result->prettyName); + ffGetFileVersion(module.szExePath, &result->prettyName); ffStrbufAppendC(&result->prettyName, ')'); break; } diff --git a/src/detection/terminalsize/terminalsize_linux.c b/src/detection/terminalsize/terminalsize_linux.c index 70c29d558e..cb6880f8d4 100644 --- a/src/detection/terminalsize/terminalsize_linux.c +++ b/src/detection/terminalsize/terminalsize_linux.c @@ -2,6 +2,7 @@ #include "common/io/io.h" #include +#include #include #ifdef __sun @@ -11,7 +12,11 @@ bool ffDetectTerminalSize(FFTerminalSizeResult* result) { struct winsize winsize = {}; - ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); + static int ttyfd = STDOUT_FILENO; + if (!isatty(ttyfd)) + ttyfd = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC); + + ioctl(ttyfd, TIOCGWINSZ, &winsize); if (winsize.ws_row == 0 || winsize.ws_col == 0) ffGetTerminalResponse("\e[18t", 2, "\e[8;%hu;%hut", &winsize.ws_row, &winsize.ws_col); diff --git a/src/detection/vulkan/vulkan.c b/src/detection/vulkan/vulkan.c index 571ab50d41..3a613ee719 100644 --- a/src/detection/vulkan/vulkan.c +++ b/src/detection/vulkan/vulkan.c @@ -40,14 +40,7 @@ static void applyDriverName(VkPhysicalDeviceDriverPropertiesKHR* properties, FFs static const char* detectVulkan(FFVulkanResult* result) { FF_LIBRARY_LOAD(vulkan, "dlopen libvulkan" FF_LIBRARY_EXTENSION " failed", - #if __ANDROID__ - #if UINTPTR_MAX == UINT32_MAX - "/system/lib/" - #else - "/system/lib64/" - #endif - "libvulkan" FF_LIBRARY_EXTENSION, -1 - #elif __APPLE__ + #if __APPLE__ "libMoltenVK" FF_LIBRARY_EXTENSION, -1 #elif _WIN32 "vulkan-1" FF_LIBRARY_EXTENSION, -1 @@ -225,7 +218,7 @@ static const char* detectVulkan(FFVulkanResult* result) ffStrbufInitS(&gpu->name, physicalDeviceProperties.properties.deviceName); gpu->type = physicalDeviceProperties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; - ffStrbufInitS(&gpu->vendor, ffGetGPUVendorString(physicalDeviceProperties.properties.vendorID)); + ffStrbufInitS(&gpu->vendor, ffGPUGetVendorString(physicalDeviceProperties.properties.vendorID)); ffStrbufInitS(&gpu->driver, driverProperties.driverInfo); VkPhysicalDeviceMemoryProperties memoryProperties = {}; diff --git a/src/detection/wm/wm.h b/src/detection/wm/wm.h index 5301cff94e..5f5fa4adfe 100644 --- a/src/detection/wm/wm.h +++ b/src/detection/wm/wm.h @@ -3,3 +3,4 @@ #include "fastfetch.h" const char* ffDetectWMPlugin(FFstrbuf* pluginName); +const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FFWMOptions* options); diff --git a/src/detection/wm/wm_apple.c b/src/detection/wm/wm_apple.m similarity index 65% rename from src/detection/wm/wm_apple.c rename to src/detection/wm/wm_apple.m index 29b5497f8a..c2395d811b 100644 --- a/src/detection/wm/wm_apple.c +++ b/src/detection/wm/wm_apple.m @@ -5,6 +5,7 @@ #include "util/stringUtils.h" #include +#import const char* ffDetectWMPlugin(FFstrbuf* pluginName) { @@ -38,3 +39,19 @@ const char* ffDetectWMPlugin(FFstrbuf* pluginName) return NULL; } + +const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE_UNUSED FFWMOptions* options) +{ + if (!wmName) + return "No WM detected"; + + if (ffStrbufEqualS(wmName, "WindowServer")) + { + NSError* error; + NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"file:///System/Library/CoreServices/WindowManager.app/Contents/version.plist"] + error:&error]; + ffStrbufInitS(result, ((NSString*) dict[@"CFBundleVersion"]).UTF8String); + } + + return NULL; +} diff --git a/src/detection/wm/wm_linux.c b/src/detection/wm/wm_linux.c new file mode 100644 index 0000000000..2c249484ae --- /dev/null +++ b/src/detection/wm/wm_linux.c @@ -0,0 +1,113 @@ +#include "wm.h" + +#include "common/processing.h" +#include "common/io/io.h" +#include "detection/displayserver/displayserver.h" +#include "util/binary.h" +#include "util/path.h" +#include "util/stringUtils.h" + +const char* ffDetectWMPlugin(FF_MAYBE_UNUSED FFstrbuf* pluginName) +{ + return "Not supported on this platform"; +} + +static bool extractHyprlandVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, void *userdata) +{ + if (line[0] != 'v') return true; + int count = 0; + sscanf(line + 1, "%*d.%*d.%*d%n", &count); + if (count == 0) return true; + + ffStrbufSetNS((FFstrbuf*) userdata, len - 1, line + 1); + return false; +} + +static const char* getHyprland(FFstrbuf* result) +{ + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + const char* error = ffFindExecutableInPath("Hyprland", &path); + if (error) return "Failed to find Hyprland executable path"; + + if (ffBinaryExtractStrings(path.chars, extractHyprlandVersion, result, (uint32_t) strlen("v0.0.0")) == NULL) + return NULL; + + if (ffProcessAppendStdOut(result, (char* const[]){ + path.chars, + "--version", + NULL + }) == NULL) + { // Hyprland 0.46.2 built from branch v0.46.2-b at... long and multi line + ffStrbufSubstrAfterFirstC(result, ' '); + ffStrbufSubstrBeforeFirstC(result, ' '); + return NULL; + } + + return "Failed to run command `Hyprland --version`"; +} + +static bool extractSwayVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, void *userdata) +{ + if (!ffStrStartsWith(line, "sway version ")) return true; + + ffStrbufSetNS((FFstrbuf*) userdata, len - (uint32_t) strlen("sway version "), line + strlen("sway version ")); + return false; +} + +static const char* getSway(FFstrbuf* result) +{ + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + const char* error = ffFindExecutableInPath("sway", &path); + if (error) return "Failed to find sway executable path"; + + if (ffBinaryExtractStrings(path.chars, extractSwayVersion, result, (uint32_t) strlen("v0.0.0")) == NULL) + { + ffStrbufTrimRightSpace(result); + return NULL; + } + + if (ffProcessAppendStdOut(result, (char* const[]){ + path.chars, + "--version", + NULL + }) == NULL) + { // sway version 1.10 + ffStrbufSubstrAfterLastC(result, ' '); + ffStrbufTrimRightSpace(result); + return NULL; + } + + return "Failed to run command `sway --version`"; +} + +static const char* getWslg(FFstrbuf* result) +{ + if (!ffAppendFileBuffer("/mnt/wslg/versions.txt", result)) + return "Failed to read /mnt/wslg/versions.txt"; + + if (!ffStrbufStartsWithS(result, "WSLg ")) + return "Failed to find WSLg version"; + + ffStrbufSubstrBeforeFirstC(result, '\n'); + ffStrbufSubstrBeforeFirstC(result, '+'); + ffStrbufSubstrAfterFirstC(result, ':'); + ffStrbufTrimLeft(result, ' '); + return NULL; +} + +const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE_UNUSED FFWMOptions* options) +{ + if (!wmName) + return "No WM detected"; + + if (ffStrbufEqualS(wmName, "Hyprland")) + return getHyprland(result); + + if (ffStrbufEqualS(wmName, "sway")) + return getSway(result); + + if (ffStrbufEqualS(wmName, "WSLg")) + return getWslg(result); + + return "Unsupported WM"; +} diff --git a/src/detection/wm/wm_nosupport.c b/src/detection/wm/wm_nosupport.c index 37c0105d71..595cdd9468 100644 --- a/src/detection/wm/wm_nosupport.c +++ b/src/detection/wm/wm_nosupport.c @@ -4,3 +4,8 @@ const char* ffDetectWMPlugin(FF_MAYBE_UNUSED FFstrbuf* pluginName) { return "Not supported on this platform"; } + +const char* ffDetectWMVersion(FF_MAYBE_UNUSED const FFstrbuf* wmName, FF_MAYBE_UNUSED FFstrbuf* result, FF_MAYBE_UNUSED FFWMOptions* options) +{ + return "Not supported on this platform"; +} diff --git a/src/detection/wm/wm_windows.cpp b/src/detection/wm/wm_windows.cpp index 583cf21491..e111968050 100644 --- a/src/detection/wm/wm_windows.cpp +++ b/src/detection/wm/wm_windows.cpp @@ -3,6 +3,7 @@ extern "C" #include "wm.h" #include "common/io/io.h" #include "detection/terminalshell/terminalshell.h" +#include "util/windows/version.h" } #include "util/windows/com.hpp" @@ -64,3 +65,24 @@ const char* ffDetectWMPlugin(FFstrbuf* pluginName) } return NULL; } + +const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE_UNUSED FFWMOptions* options) +{ + if (!wmName) + return "No WM detected"; + + if (ffStrbufEqualS(wmName, "dwm.exe")) + { + PWSTR pPath = NULL; + if(SUCCEEDED(SHGetKnownFolderPath(FOLDERID_System, KF_FLAG_DEFAULT, NULL, &pPath))) + { + wchar_t fullPath[MAX_PATH]; + wcscpy(fullPath, pPath); + wcscat(fullPath, L"\\dwm.exe"); + ffGetFileVersion(fullPath, result); + } + CoTaskMemFree(pPath); + return NULL; + } + return "Not supported on this platform"; +} diff --git a/src/logo/ascii/lubuntu.txt b/src/logo/ascii/lubuntu.txt new file mode 100644 index 0000000000..2843553b97 --- /dev/null +++ b/src/logo/ascii/lubuntu.txt @@ -0,0 +1,20 @@ + `.:/ossyyyysso/:. + `.:yyyyyyyyyyyyyyyyyy:.` + .:yyyyyyyyyyyyyyyyyyyyyyyy:. + .:yyyyyyyyyyyyyyyyyyyyyyyyyyyy:. + -yyyyyyyyyyyyyy${c2}+hNMMMNh+${c1}yyyyyyyyy- + :yy${c2}mNy+${c1}yyyyyyyy${c2}+Nmso++smMdhyysoo+${c1}yy: + -yy${c2}+MMMmmy${c1}yyyyyy${c2}hh${c1}yyyyyyyyyyyyyyyyyyy- +.yyyy${c2}NMN${c1}yy${c2}shhs${c1}yyy${c2}+o${c1}yyyyyyyyyyyyyyyyyyyy. +:yyyy${c2}oNM+${c1}yyyy${c2}+sso${c1}yyyyyyy${c2}ss${c1}yyyyyyyyyyyyy: +:yyyyy${c2}+dNs${c1}yyyyyyy${c2}++${c1}yyyyy${c2}oN+${c1}yyyyyyyyyyyy: +:yyyyy${c2}oMMmhysso${c1}yyyyyyyyyy${c2}mN+${c1}yyyyyyyyyyy: +:yyyyyy${c2}hMm${c1}yyyyy${c2}+++${c1}yyyyyyy${c2}+MN${c1}yyyyyyyyyyy: +.yyyyyyy${c2}ohmy+${c1}yyyyyyyyyyyyy${c2}NMh${c1}yyyyyyyyyy. + -yyyyyyyyyy${c2}++${c1}yyyyyyyyyyyy${c2}MMh${c1}yyyyyyyyy- + :yyyyyyyyyyyyyyyyyyyyy${c2}+mMN+${c1}yyyyyyyy: + -yyyyyyyyyyyyyyyyy${c2}+sdMMd+${c1}yyyyyyyy- + .:yyyyyyyyy${c2}hmdmmNMNdy+${c1}yyyyyyyy:. + .:yyyyyyy${c2}my${c1}yyyyyyyyyyyyyyy:. + `.:yyyy${c2}s${c1}yyyyyyyyyyyyy:.` + `.:/oosyyyysso/:.` diff --git a/src/logo/ascii/rhino.txt b/src/logo/ascii/rhino.txt new file mode 100644 index 0000000000..8d4012f269 --- /dev/null +++ b/src/logo/ascii/rhino.txt @@ -0,0 +1,12 @@ +${c1} .;:;,. .: +${c1} 'coooooooo:oo.';. +${c1} ,oooooooooooooooo ; +${c1} clllcccllloooooooo;c:'o +${c1}.${c4};${c3}';:::::::::${c1}cclooooooo' +${c4}''',${c3}::::::::::::::${c1}ccclc. +${c4}.'''${c3};::::::::::${c2}l${c3}::::::: +${c4} ''''${c3},:::::::::${c2}kd${c3}. +${c4} .'''''${c3},;::${c2}ck:${c2}oW${c3}; +${c4} ''''''''${c2}kXOM. +${c4} .,,:${c2}dXMK +${c4} ${c2}:k diff --git a/src/logo/ascii/snigdhaos.txt b/src/logo/ascii/snigdhaos.txt new file mode 100644 index 0000000000..94f2201160 --- /dev/null +++ b/src/logo/ascii/snigdhaos.txt @@ -0,0 +1,20 @@ + WK0OO0X + WKOxk0XWNXkxN + WXOxk0N kk + WKkxOXW NxO + NX0ddk00OOOOkkkkkkkkk0 + WKkxO0dOXXNNNNWWW WXKK +NxkN kxW XOxxdN +KoX WkxX WkxK Xd0 +WxkW XkkX kkW NkxX + W0dK XxOWkkW XxkN + NkxX XoKWkxK Kd0 + XkkN WOdN NkxX NxO + OxNKxOW XdO kk + kdk0W XoK WXkxN + WNNXKXXXXKKKK000OOdxkk0X + KdOO000KKKKXXXXX0xx0W + XoK N0kx0N + Nd0 NKOxOKW + Nkk0K0kxOKN + WXXXN diff --git a/src/logo/builtin.c b/src/logo/builtin.c index b062be144f..a940abb34b 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -2582,6 +2582,15 @@ static const FFlogo L[] = { .colorKeys = FF_COLOR_FG_YELLOW, .colorTitle = FF_COLOR_FG_RED, }, + // Lubuntu + { + .names = {"lubuntu"}, + .lines = FASTFETCH_DATATEXT_LOGO_LUBUNTU, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_WHITE, + }, + }, // Lunar { .names = {"Lunar"}, @@ -3980,6 +3989,19 @@ static const FFlogo R[] = { .colorKeys = FF_COLOR_FG_BLUE, .colorTitle = FF_COLOR_FG_BLUE, }, + // RhinoLinux + { + .names = {"Rhino Linux"}, + .lines = FASTFETCH_DATATEXT_LOGO_RHINO, + .colors = { + FF_COLOR_FG_MAGENTA, + FF_COLOR_FG_LIGHT_BLUE, + FF_COLOR_FG_LIGHT_MAGENTA, + FF_COLOR_FG_MAGENTA, + }, + .colorKeys = FF_COLOR_FG_MAGENTA, + .colorTitle = FF_COLOR_FG_MAGENTA, + }, // LAST {}, }; @@ -4220,6 +4242,15 @@ static const FFlogo S[] = { FF_COLOR_FG_WHITE, }, }, + // SnigdhaOS + { + .names = {"SnigdhaOS", "Snigdha"}, + .lines = FASTFETCH_DATATEXT_LOGO_SMARTOS, + .colors = { + FF_COLOR_FG_CYAN, + FF_COLOR_FG_WHITE, + }, + }, // Soda { .names = {"Soda"}, @@ -4991,9 +5022,10 @@ static const FFlogo X[] = { .names = {"Xray_OS"}, .lines = FASTFETCH_DATATEXT_LOGO_XRAY_OS, .colors = { - FF_COLOR_FG_256 "16", + FF_COLOR_FG_256 "15", FF_COLOR_FG_256 "14", FF_COLOR_FG_256 "16", + FF_COLOR_FG_256 "24", } }, // LAST diff --git a/src/logo/image/image.c b/src/logo/image/image.c index 25cca39f2b..5454605212 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -1,6 +1,7 @@ #include "image.h" #include "common/io/io.h" #include "common/printing.h" +#include "common/processing.h" #include "util/stringUtils.h" #include "util/base64.h" #include "detection/terminalsize/terminalsize.h" @@ -138,6 +139,127 @@ static bool printImageIterm(bool printError) return true; } +static bool printImageKittyIcat(bool printError) +{ + const FFOptionsLogo* options = &instance.config.logo; + + if (!ffPathExists(options->source.chars, FF_PATHTYPE_FILE)) + { + if (printError) + fputs("Logo (kitty-icat): Failed to load image file\n", stderr); + return false; + } + + fflush(stdout); + + FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); + + if (options->position == FF_LOGO_POSITION_LEFT) + { + ffStrbufAppendF(&buf, "\e[2J\e[3J\e[%u;%uH", + (unsigned) options->paddingTop + 1, + (unsigned) options->paddingLeft + 1 + ); + } + else if (options->position == FF_LOGO_POSITION_TOP) + { + if (!options->width) + { + ffStrbufAppendNC(&buf, options->paddingTop, '\n'); + ffStrbufAppendNC(&buf, options->paddingLeft, ' '); + } + else + { + if (printError) + fputs("Logo (kitty-icat): position top is not supported when logo width is set\n", stderr); + return false; + } + } + else if (options->position == FF_LOGO_POSITION_RIGHT) + { + if (printError) + fputs("Logo (kitty-icat): position right is not supported\n", stderr); + return false; + } + + uint32_t prevLength = buf.length; + + const char* error = NULL; + + if (options->width) + { + char place[64]; + snprintf(place, + ARRAY_SIZE(place), + "--place=%ux%u@%ux%u", + options->width, + options->height == 0 ? 9999 : options->height, + options->paddingLeft + 1, + options->paddingTop + 1); + + error = ffProcessAppendStdOut(&buf, (char* []) { + "kitten", + "icat", + "-n", + "--align=center", + place, + "--scale-up", + options->source.chars, + NULL, + }); + } + else + { + error = ffProcessAppendStdOut(&buf, (char* []) { + "kitten", + "icat", + "-n", + "--align=left", + options->source.chars, + NULL, + }); + } + if (error) + { + if (printError) + fprintf(stderr, "Logo (kitty-icat): running `kitten icat` failed %s\n", error); + return false; + } + + if (buf.length == prevLength) + { + if (printError) + fputs("Logo (kitty-icat): `kitten icat` returned empty output\n", stderr); + return false; + } + + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); + + if (options->position == FF_LOGO_POSITION_LEFT || options->position == FF_LOGO_POSITION_RIGHT) + { + uint16_t X = 0, Y = 0; + const char* error = ffGetTerminalResponse("\e[6n", 2, "%*[^0-9]%hu;%huR", &Y, &X); + if (error) + { + fprintf(stderr, "\nLogo (kitty-icat): fail to query cursor position: %s\n", error); + return true; // We already printed image logo, don't print ascii logo then + } + if (X < options->paddingLeft + options->width) + X = (uint16_t) (options->paddingLeft + options->width); + if (options->position == FF_LOGO_POSITION_LEFT) + instance.state.logoWidth = X + options->paddingRight - 1; + instance.state.logoHeight = Y; + fputs("\e[H", stdout); + } + else if (options->position == FF_LOGO_POSITION_TOP) + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffPrintCharTimes('\n', options->paddingRight); + } + + return true; +} + static bool printImageKittyDirect(bool printError) { const FFOptionsLogo* options = &instance.config.logo; @@ -957,6 +1079,9 @@ bool ffLogoPrintImageIfExists(FFLogoType type, bool printError) if(type == FF_LOGO_TYPE_IMAGE_KITTY_DIRECT) return printImageKittyDirect(printError); + if(type == FF_LOGO_TYPE_IMAGE_KITTY_ICAT) + return printImageKittyIcat(printError); + #if !defined(FF_HAVE_CHAFA) if(type == FF_LOGO_TYPE_IMAGE_CHAFA) { diff --git a/src/logo/logo.c b/src/logo/logo.c index 09dfd86962..c1c2b38d68 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -605,7 +605,8 @@ void ffLogoPrint(void) ffStrbufIgnCaseEqualS(&terminal->processName, "kitty") || ffStrbufIgnCaseEqualS(&terminal->processName, "konsole") || ffStrbufIgnCaseEqualS(&terminal->processName, "wezterm") || - ffStrbufIgnCaseEqualS(&terminal->processName, "wayst"); + ffStrbufIgnCaseEqualS(&terminal->processName, "wayst") || + ffStrbufIgnCaseEqualS(&terminal->processName, "ghostty"); //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)) diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index f059883a3b..7acec96f42 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -69,12 +69,7 @@ void ffPrintCPU(FFCPUOptions* options) if(coreTypes.length > 0) ffStrbufAppendF(&str, " (%s)", coreTypes.chars); else if(cpu.coresOnline > 1) - { - if(cpu.packages > 1) - ffStrbufAppendF(&str, " (%u)", cpu.coresOnline / 2); - else - ffStrbufAppendF(&str, " (%u)", cpu.coresOnline); - } + ffStrbufAppendF(&str, " (%u)", cpu.coresOnline); uint32_t freq = cpu.frequencyMax; if(freq == 0) diff --git a/src/modules/os/os.c b/src/modules/os/os.c index 10df93b0fc..b26ef8600f 100644 --- a/src/modules/os/os.c +++ b/src/modules/os/os.c @@ -51,38 +51,6 @@ static void buildOutputDefault(const FFOSResult* os, FFstrbuf* result) ffStrbufAppend(result, &os->variantID); ffStrbufAppendC(result, ')'); } - - //Append architecture if it is missing - if(!ffStrbufContainIgnCase(result, &instance.state.platform.sysinfo.architecture)) - { - ffStrbufAppendC(result, ' '); - ffStrbufAppend(result, &instance.state.platform.sysinfo.architecture); - } -} - -static void buildOutputNixOS(const FFOSResult* os, FFstrbuf* result) -{ - ffStrbufAppendS(result, "NixOS"); - - if(os->buildID.length > 0) - { - ffStrbufAppendC(result, ' '); - ffStrbufAppend(result, &os->buildID); - } - - if(os->codename.length > 0) - { - ffStrbufAppendS(result, " ("); - ffStrbufAppendC(result, (char) toupper(os->codename.chars[0])); - ffStrbufAppendS(result, os->codename.chars + 1); - ffStrbufAppendC(result, ')'); - } - - if(instance.state.platform.sysinfo.architecture.length > 0) - { - ffStrbufAppendC(result, ' '); - ffStrbufAppend(result, &instance.state.platform.sysinfo.architecture); - } } void ffPrintOS(FFOSOptions* options) @@ -99,11 +67,18 @@ void ffPrintOS(FFOSOptions* options) { FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); - if(ffStrbufIgnCaseCompS(&os->id, "nixos") == 0) - buildOutputNixOS(os, &result); + if(os->prettyName.length > 0) + ffStrbufAppend(&result, &os->prettyName); else buildOutputDefault(os, &result); + //Append architecture if it is missing + if(!ffStrbufContainIgnCase(&result, &instance.state.platform.sysinfo.architecture)) + { + ffStrbufAppendC(&result, ' '); + ffStrbufAppend(&result, &instance.state.platform.sysinfo.architecture); + } + ffPrintLogoAndKey(FF_OS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&result, stdout); } @@ -195,7 +170,7 @@ static FFModuleBaseInfo ffModuleInfo = { .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) { {"Name of the kernel", "sysname"}, {"Name of the OS", "name"}, - {"Pretty name of the OS", "pretty-name"}, + {"Pretty name of the OS, if available", "pretty-name"}, {"ID of the OS", "id"}, {"ID like of the OS", "id-like"}, {"Variant of the OS", "variant"}, diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 480bf193a6..6a9e3af736 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -62,7 +62,8 @@ void ffPrintPackages(FFPackagesOptions* options) FF_PRINT_PACKAGE(paludis) FF_PRINT_PACKAGE(winget) FF_PRINT_PACKAGE(opkg) - FF_PRINT_PACKAGE(am) + FF_PRINT_PACKAGE_NAME(amSystem, "am") + FF_PRINT_PACKAGE_NAME(amUser, "appman") FF_PRINT_PACKAGE(sorcery) FF_PRINT_PACKAGE(lpkg) FF_PRINT_PACKAGE(lpkgbuild) @@ -108,7 +109,7 @@ void ffPrintPackages(FFPackagesOptions* options) FF_FORMAT_ARG(counts.paludis, "paludis"), FF_FORMAT_ARG(counts.winget, "winget"), FF_FORMAT_ARG(counts.opkg, "opkg"), - FF_FORMAT_ARG(counts.am, "am"), + FF_FORMAT_ARG(counts.amSystem, "am-system"), FF_FORMAT_ARG(counts.sorcery, "sorcery"), FF_FORMAT_ARG(counts.lpkg, "lpkg"), FF_FORMAT_ARG(counts.lpkgbuild, "lpkgbuild"), @@ -119,6 +120,7 @@ void ffPrintPackages(FFPackagesOptions* options) FF_FORMAT_ARG(counts.pacstall, "pacstall"), FF_FORMAT_ARG(counts.mport, "mport"), FF_FORMAT_ARG(counts.qi, "qi"), + FF_FORMAT_ARG(counts.amUser, "am-user"), FF_FORMAT_ARG(nixAll, "nix-all"), FF_FORMAT_ARG(flatpakAll, "flatpak-all"), FF_FORMAT_ARG(brewAll, "brew-all"), @@ -400,7 +402,8 @@ void ffGeneratePackagesJsonResult(FF_MAYBE_UNUSED FFPackagesOptions* options, yy #define FF_APPEND_PACKAGE_COUNT(name) yyjson_mut_obj_add_uint(doc, obj, #name, counts.name); FF_APPEND_PACKAGE_COUNT(all) - FF_APPEND_PACKAGE_COUNT(am) + FF_APPEND_PACKAGE_COUNT(amSystem) + FF_APPEND_PACKAGE_COUNT(amUser) FF_APPEND_PACKAGE_COUNT(apk) FF_APPEND_PACKAGE_COUNT(brew) FF_APPEND_PACKAGE_COUNT(brewCask) @@ -469,7 +472,7 @@ static FFModuleBaseInfo ffModuleInfo = { {"Number of paludis packages", "paludis"}, {"Number of winget packages", "winget"}, {"Number of opkg packages", "opkg"}, - {"Number of am packages", "am"}, + {"Number of am-system packages", "am-system"}, {"Number of sorcery packages", "sorcery"}, {"Number of lpkg packages", "lpkg"}, {"Number of lpkgbuild packages", "lpkgbuild"}, @@ -480,6 +483,7 @@ static FFModuleBaseInfo ffModuleInfo = { {"Number of pacstall packages", "pacstall"}, {"Number of mport packages", "mport"}, {"Number of qi packages", "qi"}, + {"Number of am-user (aka appman) packages", "am-user"}, {"Total number of all nix packages", "nix-all"}, {"Total number of all flatpak app packages", "flatpak-all"}, {"Total number of all brew packages", "brew-all"}, diff --git a/src/modules/sound/sound.c b/src/modules/sound/sound.c index 756d47716e..e91b378f47 100644 --- a/src/modules/sound/sound.c +++ b/src/modules/sound/sound.c @@ -58,6 +58,7 @@ static void printDevice(FFSoundOptions* options, const FFSoundDevice* device, ui FF_FORMAT_ARG(percentageNum, "volume-percentage"), FF_FORMAT_ARG(device->identifier, "identifier"), FF_FORMAT_ARG(percentageBar, "volume-percentage-bar"), + FF_FORMAT_ARG(device->platformApi, "platform-api"), })); } } @@ -218,6 +219,7 @@ void ffGenerateSoundJsonResult(FF_MAYBE_UNUSED FFSoundOptions* options, yyjson_m yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); yyjson_mut_obj_add_strbuf(doc, obj, "identifier", &item->identifier); + yyjson_mut_obj_add_strbuf(doc, obj, "platformApi", &item->platformApi); } FF_LIST_FOR_EACH(FFSoundDevice, device, result) @@ -241,6 +243,7 @@ static FFModuleBaseInfo ffModuleInfo = { {"Volume (in percentage num)", "volume-percentage"}, {"Identifier", "identifier"}, {"Volume (in percentage bar)", "volume-percentage-bar"}, + {"Platform API used", "platform-api"}, })) }; diff --git a/src/modules/wm/wm.c b/src/modules/wm/wm.c index dc02ba4c0d..f0f0f31b48 100644 --- a/src/modules/wm/wm.c +++ b/src/modules/wm/wm.c @@ -19,12 +19,22 @@ void ffPrintWM(FFWMOptions* options) if(options->detectPlugin) ffDetectWMPlugin(&pluginName); + FF_STRBUF_AUTO_DESTROY version = ffStrbufCreate(); + if (instance.config.general.detectVersion) + ffDetectWMVersion(&result->wmProcessName, &version, options); + if(options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_WM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&result->wmPrettyName, stdout); + if(version.length > 0) + { + putchar(' '); + ffStrbufWriteTo(&version, stdout); + } + if(result->wmProtocolName.length > 0) { fputs(" (", stdout); @@ -48,6 +58,7 @@ void ffPrintWM(FFWMOptions* options) FF_FORMAT_ARG(result->wmPrettyName, "pretty-name"), FF_FORMAT_ARG(result->wmProtocolName, "protocol-name"), FF_FORMAT_ARG(pluginName, "plugin-name"), + FF_FORMAT_ARG(version, "version"), })); } } @@ -116,11 +127,16 @@ void ffGenerateWMJsonResult(FF_MAYBE_UNUSED FFWMOptions* options, yyjson_mut_doc if(options->detectPlugin) ffDetectWMPlugin(&pluginName); + FF_STRBUF_AUTO_DESTROY version = ffStrbufCreate(); + if (instance.config.general.detectVersion) + ffDetectWMVersion(&result->wmProcessName, &version, options); + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->wmProcessName); yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &result->wmPrettyName); yyjson_mut_obj_add_strbuf(doc, obj, "protocolName", &result->wmProtocolName); yyjson_mut_obj_add_strbuf(doc, obj, "pluginName", &pluginName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &version); } static FFModuleBaseInfo ffModuleInfo = { @@ -136,6 +152,7 @@ static FFModuleBaseInfo ffModuleInfo = { {"WM pretty name", "pretty-name"}, {"WM protocol name", "protocol-name"}, {"WM plugin name", "plugin-name"}, + {"WM version", "version"}, })) }; diff --git a/src/options/general.c b/src/options/general.c index 6f088256fb..cff518f401 100644 --- a/src/options/general.c +++ b/src/options/general.c @@ -24,16 +24,7 @@ const char* ffOptionsParseGeneralJsonConfig(FFOptionsGeneral* options, yyjson_va options->processingTimeout = (int32_t) yyjson_get_int(val); else if (ffStrEqualsIgnCase(key, "preRun")) { - FF_STRBUF_AUTO_DESTROY _ = ffStrbufCreate(); - const char* error = ffProcessAppendStdOut(&_, (char* const[]) { - #ifdef _WIN32 - "cmd.exe", "/C", - #else - "/bin/sh", "-c", - #endif - (char*) yyjson_get_str(val), NULL - }); - if (error) + if (system(yyjson_get_str(val)) < 0) return "Failed to execute preRun command"; } else if (ffStrEqualsIgnCase(key, "detectVersion")) diff --git a/src/options/logo.c b/src/options/logo.c index 582644c7f0..25fc829f27 100644 --- a/src/options/logo.c +++ b/src/options/logo.c @@ -63,6 +63,7 @@ bool ffOptionsParseLogoCommandLine(FFOptionsLogo* options, const char* key, cons { "sixel", FF_LOGO_TYPE_IMAGE_SIXEL }, { "kitty", FF_LOGO_TYPE_IMAGE_KITTY }, { "kitty-direct", FF_LOGO_TYPE_IMAGE_KITTY_DIRECT }, + { "kitty-icat", FF_LOGO_TYPE_IMAGE_KITTY_ICAT }, { "iterm", FF_LOGO_TYPE_IMAGE_ITERM }, { "chafa", FF_LOGO_TYPE_IMAGE_CHAFA }, { "raw", FF_LOGO_TYPE_IMAGE_RAW }, @@ -172,6 +173,11 @@ bool ffOptionsParseLogoCommandLine(FFOptionsLogo* options, const char* key, cons ffOptionParseString(key, value, &options->source); options->type = FF_LOGO_TYPE_IMAGE_KITTY_DIRECT; } + else if(ffStrEqualsIgnCase(key, "--kitty-icat")) + { + ffOptionParseString(key, value, &options->source); + options->type = FF_LOGO_TYPE_IMAGE_KITTY_ICAT; + } else if(ffStrEqualsIgnCase(key, "--iterm")) { ffOptionParseString(key, value, &options->source); @@ -283,6 +289,7 @@ const char* ffOptionsParseLogoJsonConfig(FFOptionsLogo* options, yyjson_val* roo { "sixel", FF_LOGO_TYPE_IMAGE_SIXEL }, { "kitty", FF_LOGO_TYPE_IMAGE_KITTY }, { "kitty-direct", FF_LOGO_TYPE_IMAGE_KITTY_DIRECT }, + { "kitty-icat", FF_LOGO_TYPE_IMAGE_KITTY_ICAT }, { "iterm", FF_LOGO_TYPE_IMAGE_ITERM }, { "chafa", FF_LOGO_TYPE_IMAGE_CHAFA }, { "raw", FF_LOGO_TYPE_IMAGE_RAW }, diff --git a/src/options/logo.h b/src/options/logo.h index a4a031d5f2..c2a180b7b6 100644 --- a/src/options/logo.h +++ b/src/options/logo.h @@ -17,6 +17,7 @@ typedef enum __attribute__((__packed__)) FFLogoType FF_LOGO_TYPE_IMAGE_SIXEL, //image file, printed as sixel codes FF_LOGO_TYPE_IMAGE_KITTY, //image file, printed as kitty graphics protocol FF_LOGO_TYPE_IMAGE_KITTY_DIRECT, //image file, tell the terminal emulator to read image data from the specified file (Supported by kitty and wezterm) + FF_LOGO_TYPE_IMAGE_KITTY_ICAT, //image file, use `kitten icat` to display the image. Requires binary `kitten` to be installed" FF_LOGO_TYPE_IMAGE_ITERM, //image file, printed as iterm graphics protocol FF_LOGO_TYPE_IMAGE_CHAFA, //image file, printed as ascii art using libchafa FF_LOGO_TYPE_IMAGE_RAW, //image file, printed as raw binary string diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 31ec637d62..32692907aa 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -225,12 +225,6 @@ void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value) ffStrbufAppendNS(strbuf, length, value); } -void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value) -{ - ffStrbufClear(strbuf); - ffStrbufAppendNS(strbuf, value->length, value->chars); -} - void ffStrbufTrimLeft(FFstrbuf* strbuf, char c) { if(strbuf->length == 0) @@ -576,6 +570,22 @@ bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer) return true; } +/// @brief Restore the end of a line that was modified by ffStrbufGetline. +/// @warning This function should be called before breaking an ffStrbufGetline loop. +void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer) +{ + assert(buffer && lineptr && n); + assert(buffer->allocated > 0 || (buffer->allocated == 0 && buffer->length == 0)); + assert(!*lineptr || (*lineptr >= buffer->chars && *lineptr <= buffer->chars + buffer->length)); + + if (!*lineptr) + return; + + *lineptr += *n; + if (*lineptr < buffer->chars + buffer->length) + **lineptr = '\n'; +} + bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf) { if (strbuf->allocated == 0) return false; // Doesn't work with static strings diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 1c754b10a6..a3bce4ca64 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -49,7 +49,6 @@ void ffStrbufPrependC(FFstrbuf* strbuf, char c); void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c); void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value); -void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value); FF_C_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...); void ffStrbufTrimLeft(FFstrbuf* strbuf, char c); @@ -91,6 +90,7 @@ void ffStrbufUpperCase(FFstrbuf* strbuf); void ffStrbufLowerCase(FFstrbuf* strbuf); bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer); +void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer); bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf); FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) @@ -102,8 +102,13 @@ FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) static inline void ffStrbufInitCopy(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict src) { - ffStrbufInitA(strbuf, src->allocated); - ffStrbufAppend(strbuf, src); + if (src->allocated == 0) // static string + memcpy(strbuf, src, sizeof(FFstrbuf)); + else + { + ffStrbufInitA(strbuf, src->allocated); + ffStrbufAppend(strbuf, src); + } } FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateCopy(const FFstrbuf* src) @@ -209,6 +214,17 @@ static inline void ffStrbufSetS(FFstrbuf* strbuf, const char* value) ffStrbufAppendNS(strbuf, (uint32_t) strlen(value), value); } +static inline void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value) +{ + assert(value && value != strbuf); + if (strbuf->allocated == 0 && value->allocated == 0) + { + memcpy(strbuf, value, sizeof(FFstrbuf)); + return; + } + ffStrbufSetNS(strbuf, value->length, value->chars); +} + static inline void ffStrbufInit(FFstrbuf* strbuf) { extern char* CHAR_NULL_PTR; diff --git a/src/util/platform/FFPlatform_unix.c b/src/util/platform/FFPlatform_unix.c index b0d0ac1bda..c8b5c7c65a 100644 --- a/src/util/platform/FFPlatform_unix.c +++ b/src/util/platform/FFPlatform_unix.c @@ -21,7 +21,8 @@ static void getExePath(FFPlatform* platform) char exePath[PATH_MAX + 1]; #ifdef __linux__ ssize_t exePathLen = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); - exePath[exePathLen] = '\0'; + if (exePathLen >= 0) + exePath[exePathLen] = '\0'; #elif defined(__APPLE__) int exePathLen = proc_pidpath((int) getpid(), exePath, sizeof(exePath)); #elif defined(__FreeBSD__) || defined(__NetBSD__) @@ -44,7 +45,8 @@ static void getExePath(FFPlatform* platform) size_t exePathLen = 0; #elif defined(__sun) ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1); - exePath[exePathLen] = '\0'; + if (exePathLen >= 0) + exePath[exePathLen] = '\0'; #endif if (exePathLen > 0) { @@ -107,6 +109,7 @@ static void getCacheDir(FFPlatform* platform) static void getConfigDirs(FFPlatform* platform) { + // Always make sure `${XDG_CONFIG_HOME:-$HOME/.config}` is the first entry platformPathAddEnv(&platform->configDirs, "XDG_CONFIG_HOME"); ffPlatformPathAddHome(&platform->configDirs, platform, ".config/"); diff --git a/src/util/platform/FFPlatform_windows.c b/src/util/platform/FFPlatform_windows.c index 5357bde3d5..71feae3f30 100644 --- a/src/util/platform/FFPlatform_windows.c +++ b/src/util/platform/FFPlatform_windows.c @@ -31,13 +31,12 @@ static void getExePath(FFPlatform* platform) static void getHomeDir(FFPlatform* platform) { - PWSTR pPath; + PWSTR pPath = NULL; if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pPath))) { ffStrbufSetWS(&platform->homeDir, pPath); ffStrbufReplaceAllC(&platform->homeDir, '\\', '/'); ffStrbufEnsureEndsWithC(&platform->homeDir, '/'); - CoTaskMemFree(pPath); } else { @@ -45,28 +44,29 @@ static void getHomeDir(FFPlatform* platform) ffStrbufReplaceAllC(&platform->homeDir, '\\', '/'); ffStrbufEnsureEndsWithC(&platform->homeDir, '/'); } + CoTaskMemFree(pPath); } static void getCacheDir(FFPlatform* platform) { - PWSTR pPath; + PWSTR pPath = NULL; if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_LocalAppData, KF_FLAG_DEFAULT, NULL, &pPath))) { ffStrbufSetWS(&platform->cacheDir, pPath); ffStrbufReplaceAllC(&platform->cacheDir, '\\', '/'); ffStrbufEnsureEndsWithC(&platform->cacheDir, '/'); - CoTaskMemFree(pPath); } else { ffStrbufAppend(&platform->cacheDir, &platform->homeDir); ffStrbufAppendS(&platform->cacheDir, "AppData/Local/"); } + CoTaskMemFree(pPath); } static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId) { - PWSTR pPath; + PWSTR pPath = NULL; if(SUCCEEDED(SHGetKnownFolderPath(folderId, 0, NULL, &pPath))) { FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateWS(pPath); @@ -74,8 +74,8 @@ static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId) ffStrbufEnsureEndsWithC(&buffer, '/'); if (!ffListContains(dirs, &buffer, (void*) ffStrbufEqual)) ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer); - CoTaskMemFree(pPath); } + CoTaskMemFree(pPath); } static void platformPathAddEnvSuffix(FFlist* dirs, const char* env, const char* suffix) diff --git a/src/util/windows/version.c b/src/util/windows/version.c new file mode 100644 index 0000000000..e01fe27a14 --- /dev/null +++ b/src/util/windows/version.c @@ -0,0 +1,31 @@ +#include "util/windows/version.h" +#include "util/mallocHelper.h" + +#include + +bool ffGetFileVersion(const wchar_t* filePath, FFstrbuf* version) +{ + DWORD handle; + DWORD size = GetFileVersionInfoSizeW(filePath, &handle); + if(size > 0) + { + FF_AUTO_FREE void* versionData = malloc(size); + if(GetFileVersionInfoW(filePath, handle, size, versionData)) + { + VS_FIXEDFILEINFO* verInfo; + UINT len; + if(VerQueryValueW(versionData, L"\\", (void**)&verInfo, &len) && len && verInfo->dwSignature == 0xFEEF04BD) + { + ffStrbufAppendF(version, "%u.%u.%u.%u", + (unsigned)(( verInfo->dwProductVersionMS >> 16 ) & 0xffff), + (unsigned)(( verInfo->dwProductVersionMS >> 0 ) & 0xffff), + (unsigned)(( verInfo->dwProductVersionLS >> 16 ) & 0xffff), + (unsigned)(( verInfo->dwProductVersionLS >> 0 ) & 0xffff) + ); + return true; + } + } + } + + return false; +} diff --git a/src/util/windows/version.h b/src/util/windows/version.h new file mode 100644 index 0000000000..c6206fa5b4 --- /dev/null +++ b/src/util/windows/version.h @@ -0,0 +1,3 @@ +#include "fastfetch.h" + +bool ffGetFileVersion(const wchar_t* filePath, FFstrbuf* version); diff --git a/tests/strbuf.c b/tests/strbuf.c index 005a12fa5b..d3f9116ca9 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -593,6 +593,48 @@ int main(void) VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == false); VERIFY(strcmp(strbuf.chars, " ") == 0); + { + ffStrbufSetStatic(&strbuf, "abcdef"); + FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateCopy(&strbuf); + VERIFY(newStr.allocated == 0); + VERIFY(newStr.chars == strbuf.chars); + } + + { + ffStrbufSetStatic(&strbuf, "abcdef"); + FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456"); + ffStrbufSet(&newStr, &strbuf); + VERIFY(newStr.allocated > 0); + VERIFY(newStr.chars != strbuf.chars); + VERIFY(ffStrbufEqualS(&newStr, "abcdef")); + } + + { + ffStrbufSetStatic(&strbuf, "abcdefghijkl"); + FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456"); + ffStrbufSet(&newStr, &strbuf); + VERIFY(newStr.allocated > 0); + VERIFY(newStr.chars != strbuf.chars); + VERIFY(ffStrbufEqualS(&newStr, "abcdefghijkl")); + } + + { + ffStrbufClear(&strbuf); + FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateCopy(&strbuf); + VERIFY(newStr.allocated == 0); + VERIFY(newStr.chars == strbuf.chars); + VERIFY(newStr.chars[0] == '\0'); + } + + { + ffStrbufClear(&strbuf); + FF_STRBUF_AUTO_DESTROY newStr = ffStrbufCreateS("123456"); + ffStrbufSet(&newStr, &strbuf); + VERIFY(newStr.allocated > 0); + VERIFY(newStr.chars != strbuf.chars); + VERIFY(ffStrbufEqualS(&newStr, "")); + } + //Success puts("\e[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET); }