diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 715ebbcdc4..c6832bdcfb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,24 +13,29 @@ jobs: fail-fast: false matrix: os: [ubuntu-22.04, ubuntu-20.04] - compiler: [gcc, clang, "musl-gcc"] + compiler: [ + { cc: gcc, cxx: "g++" }, + { cc: clang, cxx: "clang++" }, + { cc: "musl-gcc", cxx: "musl-g++" } + ] enableFeatures: [ON, OFF] exclude: # The feature libraries are all build against glibc, so they can't be used with musl - - compiler: "musl-gcc" + - compiler: { cc: "musl-gcc", cxx: "musl-g++" } enableFeatures: ON runs-on: ${{ matrix.os }} steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install required packages - run: sudo apt-get update && sudo apt-get install -y musl-dev musl-tools linux-headers-generic libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev + run: sudo apt-get update && sudo apt-get install -y musl-dev musl-tools linux-headers-generic libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev directx-headers-dev - name: configure project env: - CC: ${{ matrix.compiler }} - run: cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_LIBPCI=${{ matrix.enableFeatures }} -DENABLE_VULKAN=${{ matrix.enableFeatures }} -DENABLE_WAYLAND=${{ matrix.enableFeatures }} -DENABLE_XCB_RANDR=${{ matrix.enableFeatures }} -DENABLE_XCB=${{ matrix.enableFeatures }} -DENABLE_XRANDR=${{ matrix.enableFeatures }} -DENABLE_X11=${{ matrix.enableFeatures }} -DENABLE_GIO=${{ matrix.enableFeatures }} -DENABLE_DCONF=${{ matrix.enableFeatures }} -DENABLE_DBUS=${{ matrix.enableFeatures }} -DENABLE_XFCONF=${{ matrix.enableFeatures }} -DENABLE_SQLITE3=${{ matrix.enableFeatures }} -DENABLE_RPM=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK7=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK6=${{ matrix.enableFeatures }} -DENABLE_CHAFA=${{ matrix.enableFeatures }} -DENABLE_ZLIB=${{ matrix.enableFeatures }} -DENABLE_EGL=${{ matrix.enableFeatures }} -DENABLE_GLX=${{ matrix.enableFeatures }} -DENABLE_OSMESA=${{ matrix.enableFeatures }} -DENABLE_OPENCL=${{ matrix.enableFeatures }} -DENABLE_LIBNM=${{ matrix.enableFeatures }} -DENABLE_PULSE=${{ matrix.enableFeatures }} -DENABLE_JSONC=${{ matrix.enableFeatures }} . + CC: ${{ matrix.compiler.cc }} + CXX: ${{ matrix.compiler.cxx }} + run: cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_LIBPCI=${{ matrix.enableFeatures }} -DENABLE_VULKAN=${{ matrix.enableFeatures }} -DENABLE_WAYLAND=${{ matrix.enableFeatures }} -DENABLE_XCB_RANDR=${{ matrix.enableFeatures }} -DENABLE_XCB=${{ matrix.enableFeatures }} -DENABLE_XRANDR=${{ matrix.enableFeatures }} -DENABLE_X11=${{ matrix.enableFeatures }} -DENABLE_GIO=${{ matrix.enableFeatures }} -DENABLE_DCONF=${{ matrix.enableFeatures }} -DENABLE_DBUS=${{ matrix.enableFeatures }} -DENABLE_XFCONF=${{ matrix.enableFeatures }} -DENABLE_SQLITE3=${{ matrix.enableFeatures }} -DENABLE_RPM=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK7=${{ matrix.enableFeatures }} -DENABLE_IMAGEMAGICK6=${{ matrix.enableFeatures }} -DENABLE_CHAFA=${{ matrix.enableFeatures }} -DENABLE_ZLIB=${{ matrix.enableFeatures }} -DENABLE_EGL=${{ matrix.enableFeatures }} -DENABLE_GLX=${{ matrix.enableFeatures }} -DENABLE_OSMESA=${{ matrix.enableFeatures }} -DENABLE_OPENCL=${{ matrix.enableFeatures }} -DENABLE_LIBNM=${{ matrix.enableFeatures }} -DENABLE_PULSE=${{ matrix.enableFeatures }} -DENABLE_JSONC=${{ matrix.enableFeatures }} -DENABLE_DIRECTX_HEADERS=${{ matrix.enableFeatures }} . - name: build project run: cmake --build . @@ -39,7 +44,7 @@ jobs: run: ctest - name: run fastfetch - run: ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: ./fastfetch --stat --show-errors --no-buffer --load-config presets/all - name: run flashfetch run: ./flashfetch @@ -54,10 +59,10 @@ jobs: ffversion: ${{ steps.ffversion.outputs.ffversion }} steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install required packages - run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev + run: sudo apt-get update && sudo apt-get install -y libpci-dev libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libxfconf-0-dev libsqlite3-dev rpm librpm-dev libzstd-dev libegl-dev libglx-dev libosmesa6-dev ocl-icd-opencl-dev libnm-dev libpulse-dev directx-headers-dev - name: Initialize CodeQL uses: github/codeql-action/init@v2 @@ -77,11 +82,14 @@ jobs: run: ./fastfetch --list-features - name: run fastfetch - run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: time ./fastfetch -c presets/ci - name: run flashfetch run: time ./flashfetch + - name: print dependencies + run: ldd fastfetch + - name: run tests run: ctest @@ -103,7 +111,7 @@ jobs: contents: read steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install required packages run: brew install vulkan-loader molten-vk imagemagick chafa @@ -126,11 +134,14 @@ jobs: run: ./fastfetch --list-features - name: run fastfetch - run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: time ./fastfetch -c presets/ci - name: run flashfetch run: time ./flashfetch + - name: print dependencies + run: otool -L fastfetch + - name: run tests run: ctest @@ -148,7 +159,7 @@ jobs: contents: read steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: run VM uses: vmactions/freebsd-vm@v0 @@ -159,8 +170,9 @@ jobs: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On . cmake --build . --target package ./fastfetch --list-features - time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + time ./fastfetch -c presets/ci time ./flashfetch + ldd fastfetch ctest - name: upload artifacts @@ -180,7 +192,7 @@ jobs: shell: msys2 {0} steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: setup-msys2 uses: msys2/setup-msys2@v2 @@ -213,11 +225,14 @@ jobs: run: ./fastfetch --list-features - name: run fastfetch - run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: time ./fastfetch -c presets/ci - name: run flashfetch run: time ./flashfetch + - name: print dependencies + run: ldd fastfetch + - name: run tests run: ctest @@ -244,7 +259,7 @@ jobs: shell: msys2 {0} steps: - name: checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: setup-msys2 uses: msys2/setup-msys2@v2 @@ -274,11 +289,14 @@ jobs: run: cp /clang32/bin/{OpenCL,vulkan-1}.dll . - name: run fastfetch - run: time ./fastfetch --disable-linewrap false --hide-cursor false --show-errors --no-buffer --load-config presets/all + run: time ./fastfetch -c presets/ci - name: run flashfetch run: time ./flashfetch + - name: print dependencies + run: ldd fastfetch + - name: run tests run: ctest diff --git a/CHANGELOG.md b/CHANGELOG.md index 9defef35eb..feeaeb4723 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,57 @@ +# 2.1.0 + +This release introduces a new output format: JSON result + +Changes: +* Users module detects and prints user login time by default. Specifying `--users-compact` to disable it +* Fastfetch now requires yyjson 0.8.0 or later, which is embeded in fastfetch source tree. If you build fastfetch with `-DENABLE_SYSTEM_YYJSON` cmake option, you must upgrade your yyjson package +* Reduced information supported by `--terminal-format`, `--shell-format` +* Some config presets (`devinfo` and `verbose`) are obseleted and removed. They are barely maintained and can be replaced with `--format json` now. +* Custom strings in `--module-key` and `--module-format` are no longer trimmed. +* `/boot` is hidden by default (FreeBSD, Disk) + +Features: +* Add `--format json`, which prints system information as JSON format +* Add fast path for xfce4 version detection (DE, FreeBSD) +* Support contour terminal version and font detection (Terminal / TerminalFont) +* Support `kitty-direct` / `iterm` without specifying logo width / height. Note: in this case, the entre screen will be cleared. +* Support new flag `--logo-separate`. If true, print modules at bottom of the logo +* Support Apple Silicon CPU frequency detection (CPU, macOS) +* Support user login time detection (Users) +* Support winget package manager detection, guarded behind `--allow-slow-operations` (Packages, Windows) +* Print monitor type (built-in or external) (Display) +* Support full GPU detection in WSL (Linux, GPU) +* Add `--module-key " "` as a special case for hiding keys +* Support `--title-format`. See `fastfetch --help title-format` for detail +* Support `--colors-key` (Colors) +* Add `-c` as a shortcut of `--load-config`. Note it was used as the shortcut of `--color` before 2.0.5 +* Support Windows Service Pack version detection (Kernel, Windows) +* Support Debian point releases detection (OS, Linux) +* Add new module `NetIO` which prints network throughput (usage) of specified interface. Note this module costs about 1 second to finish. +* Use `lscpu` to detect CPU name for ARM CPUs (CPU, Linux) + +Bugfixes: +* Fix fastfetch hanging in specific environment (#561) +* Fix short read when reading from stdin (Logo) +* Fix `poll() timeout or failed` error when image is very large (Logo) +* Fix Termux Monet terminal version detection (Terminal) +* Fix zpool volumes detection (Disk, Linux) +* Fix external volumes detection (Disk, Linux) +* Fix snap package number detection on systems other than Ubuntu (Packages, Linux) +* Fix dpkg / apt package number detection (Packages, Linux) +* Fix bluetooth mac address detection (Bluetooth, Windows) + +Logo: +* Add Afterglow +* Add Elbrus +* Update EvolutionOS +* Update AOSC OS +* Update Ubuntu_old +* Update Windows 11_small +* Add Amazon Linux +* Add LainOS +* Fix colors of Slackware + # 2.0.5 Bugfixes: @@ -11,7 +65,7 @@ Features: # 2.0.4 Bugfixes: -* Fix building on 32-bit FreeBSD (Memory, BSD) +* Fix building on 32-bit FreeBSD (Memory, FreeBSD) * Fix `--file-raw` doesn't work (Logo) Features: diff --git a/CMakeLists.txt b/CMakeLists.txt index a799086bac..04eebf6797 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.0.5 + VERSION 2.1.0 LANGUAGES C DESCRIPTION "Fast system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -67,6 +67,7 @@ cmake_dependent_option(ENABLE_LIBNM "Enable libnm" ON "LINUX" OFF) cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF) cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF) +cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" ON "LINUX" OFF) cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF) cmake_dependent_option(ENABLE_PCI_MEMORY "Enable detecting GPU memory size with libpci" OFF "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embeded) yyjson library" OFF "LINUX OR APPLE OR BSD OR WIN32 OR ANDROID" OFF) @@ -98,10 +99,13 @@ set(WARNING_FLAGS "-Wall -Wextra -Wconversion -Werror=uninitialized -Werror=retu set(CMAKE_C_STANDARD 11) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=incompatible-pointer-types -Werror=implicit-function-declaration") -if(WIN32) +if(WIN32 OR ENABLE_DIRECTX_HEADERS) enable_language(CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -fno-exceptions -fno-rtti") +endif() + +if(WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--tsaware -Wl,--build-id -Wl,--subsystem,console:6.1,--major-os-version,6,--minor-os-version,1") endif() @@ -112,8 +116,8 @@ if(APPLE AND DEFINED ENV{HOMEBREW_PREFIX}) endif() set(FASTFETCH_FLAGS_DEBUG "-fno-omit-frame-pointer") -if(NOT WIN32) - set(FASTFETCH_FLAGS_DEBUG "${FASTFETCH_FLAGS_DEBUG} -fsanitize=address -fsanitize=undefined") +if(NOT WIN32 AND NOT APPLE) + set(FASTFETCH_FLAGS_DEBUG "${FASTFETCH_FLAGS_DEBUG} -fsanitize=address") endif() set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FASTFETCH_FLAGS_DEBUG}") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FASTFETCH_FLAGS_DEBUG}") @@ -121,8 +125,8 @@ if(NOT WIN32) set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -rdynamic") endif() - if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(STATUS "Enabling LTO") include(CheckIPOSupported) check_ipo_supported(RESULT IPO_SUPPORTED) if(IPO_SUPPORTED) @@ -259,6 +263,7 @@ set(LIBFASTFETCH_SRC src/common/jsonconfig.c src/common/library.c src/common/modules.c + src/common/netif/netif.c src/common/option.c src/common/parsing.c src/common/printing.c @@ -267,20 +272,22 @@ set(LIBFASTFETCH_SRC src/detection/chassis/chassis.c src/detection/cpu/cpu.c src/detection/cpuusage/cpuusage.c - src/detection/datetime/datetime.c src/detection/disk/disk.c src/detection/displayserver/displayserver.c src/detection/font/font.c src/detection/gpu/gpu.c src/detection/locale/locale.c src/detection/media/media.c + src/detection/netio/netio.c src/detection/opencl/opencl.c src/detection/os/os.c src/detection/packages/packages.c + src/detection/publicip/publicip.c src/detection/terminalfont/terminalfont.c src/detection/terminalshell/terminalshell.c src/detection/version/version.c src/detection/vulkan/vulkan.c + src/detection/weather/weather.c src/logo/builtin.c src/logo/image/im6.c src/logo/image/im7.c @@ -314,6 +321,7 @@ set(LIBFASTFETCH_SRC src/modules/localip/localip.c src/modules/memory/memory.c src/modules/monitor/monitor.c + src/modules/netio/netio.c src/modules/opencl/opencl.c src/modules/opengl/opengl.c src/modules/os/os.c @@ -354,6 +362,7 @@ if(LINUX) list(APPEND LIBFASTFETCH_SRC src/common/dbus.c src/common/io/io_unix.c + src/common/netif/netif_linux.c src/common/networking_linux.c src/common/processing_linux.c src/detection/battery/battery_linux.c @@ -382,6 +391,7 @@ if(LINUX) src/detection/media/media_linux.c src/detection/memory/memory_linux.c src/detection/monitor/monitor_linux.c + src/detection/netio/netio_linux.c src/detection/opengl/opengl_linux.c src/detection/os/os_linux.c src/detection/packages/packages_linux.c @@ -405,6 +415,7 @@ if(LINUX) elseif(ANDROID) list(APPEND LIBFASTFETCH_SRC src/common/io/io_unix.c + src/common/netif/netif_linux.c src/common/networking_linux.c src/common/processing_linux.c src/detection/battery/battery_android.c @@ -428,6 +439,7 @@ elseif(ANDROID) src/detection/media/media_nosupport.c src/detection/memory/memory_linux.c src/detection/monitor/monitor_nosupport.c + src/detection/netio/netio_linux.c src/detection/opengl/opengl_linux.c src/detection/os/os_android.c src/detection/packages/packages_linux.c @@ -451,6 +463,7 @@ elseif(BSD) list(APPEND LIBFASTFETCH_SRC src/common/dbus.c src/common/io/io_unix.c + src/common/netif/netif_bsd.c src/common/networking_linux.c src/common/processing_linux.c src/common/sysctl.c @@ -480,6 +493,7 @@ elseif(BSD) src/detection/media/media_linux.c src/detection/memory/memory_bsd.c src/detection/monitor/monitor_nosupport.c + src/detection/netio/netio_bsd.c src/detection/opengl/opengl_linux.c src/detection/os/os_linux.c src/detection/packages/packages_linux.c @@ -503,6 +517,7 @@ elseif(BSD) elseif(APPLE) list(APPEND LIBFASTFETCH_SRC src/common/io/io_unix.c + src/common/netif/netif_bsd.c src/common/networking_linux.c src/common/processing_linux.c src/common/sysctl.c @@ -515,7 +530,6 @@ elseif(APPLE) src/detection/cpu/cpu_apple.c src/detection/cpuusage/cpuusage_apple.c src/detection/cursor/cursor_apple.m - src/detection/disk/disk_apple.m src/detection/disk/disk_bsd.c src/detection/displayserver/displayserver_apple.c src/detection/font/font_apple.m @@ -528,6 +542,7 @@ elseif(APPLE) src/detection/media/media_apple.m src/detection/memory/memory_apple.c src/detection/monitor/monitor_apple.m + src/detection/netio/netio_bsd.c src/detection/opengl/opengl_apple.c src/detection/os/os_apple.m src/detection/packages/packages_apple.c @@ -552,6 +567,7 @@ elseif(APPLE) elseif(WIN32) list(APPEND LIBFASTFETCH_SRC src/common/io/io_windows.c + src/common/netif/netif_windows.c src/common/networking_windows.c src/common/processing_windows.c src/detection/battery/battery_windows.c @@ -575,6 +591,7 @@ elseif(WIN32) src/detection/media/media_nosupport.c src/detection/memory/memory_windows.c src/detection/monitor/monitor_windows.c + src/detection/netio/netio_windows.c src/detection/opengl/opengl_windows.c src/detection/os/os_windows.cpp src/detection/packages/packages_windows.c @@ -601,6 +618,11 @@ elseif(WIN32) ) endif() +if(ENABLE_DIRECTX_HEADERS) + message(STATUS "Enabling DirectX headers for WSL") + list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_wsl.cpp) +endif() + include(CheckFunctionExists) check_function_exists(wcwidth HAVE_WCWIDTH) if(NOT HAVE_WCWIDTH) @@ -623,14 +645,17 @@ endif() add_library(libfastfetch OBJECT ${LIBFASTFETCH_SRC} ) + if(yyjson_FOUND) target_compile_definitions(libfastfetch PRIVATE FF_USE_SYSTEM_YYJSON) target_link_libraries(libfastfetch PRIVATE yyjson) endif() -target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE) +target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE __STDC_WANT_LIB_EXT1__=1) if(WIN32) target_compile_definitions(libfastfetch PUBLIC WIN32_LEAN_AND_MEAN=1) +elseif(APPLE) + target_compile_definitions(libfastfetch PUBLIC _DARWIN_C_SOURCE) endif() if(HAVE_WCWIDTH) @@ -773,6 +798,10 @@ ff_lib_enable(DDCUTIL "ddcutil" "Ddcutil" ) +ff_lib_enable(DIRECTX_HEADERS + "DirectX-Headers" + "DirectX-Headers" +) if(ENABLE_THREADS) target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS) @@ -877,6 +906,12 @@ target_link_libraries(flashfetch PRIVATE libfastfetch ) +# Prevent fastfetch from linking to libstdc++ +set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "") +set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "") +set_target_properties(fastfetch PROPERTIES LINKER_LANGUAGE C) +set_target_properties(flashfetch PROPERTIES LINKER_LANGUAGE C) + if(WIN32) set(TARGET_NAME fastfetch) target_sources(fastfetch diff --git a/README.md b/README.md index 4f1a1ebc31..3148465d55 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ The following libraries are used if present at runtime: * [`libnm`](https://networkmanager.dev/docs/libnm/latest/): Used for Wifi detection. * [`libpulse`](https://freedesktop.org/software/pulseaudio/doxygen/): Used for Sound detection. * [`libddcutil`](https://github.com/rockowitz/ddcutil): Used for brightness detection of external displays +* [`DirectX-Headers`](https://github.com/microsoft/DirectX-Headers): Used for GPU detection in WSL ### macOS @@ -91,7 +92,7 @@ For the image logo, iTerm with iterm image protocol should work. Apple Terminal Note: In Windows 7, 8 and 8.1, [ConEmu](https://conemu.github.io/en/AnsiEscapeCodes.html) is required to run fastfetch due to [the lack of ASCII escape code native support](https://en.wikipedia.org/wiki/ANSI_escape_code#DOS,_OS/2,_and_Windows). In addition, as fastfetch for Windows targets [UCRT](https://learn.microsoft.com/en-us/cpp/windows/universal-crt-deployment) C runtime library, [it must be installed manually](https://support.microsoft.com/en-us/topic/update-for-universal-c-runtime-in-windows-c0514201-7fe6-95a3-b0a5-287930f3560c) as UCRT is only pre-installed in Windows 10 and later. -For the image logo, only chafa is supported due to [a design flaw of ConPTY](https://github.com/microsoft/terminal/issues/1173). In addition, chafa support is not included by default due to the massive dependencies of imagemagick. You must built it yourself. +For the image logo, WezTerm with iterm image protocol is known to work, surprisingly. ### Android @@ -100,19 +101,21 @@ For the image logo, only chafa is supported due to [a design flaw of ConPTY](htt * [`termux-api`](https://github.com/termux/termux-api-package): Used for Wifi / Battery detection. Read [the official doc](https://wiki.termux.com/wiki/Termux:API) for detail or if you hang on these modules (IMPORTANT). [`termux-api`](https://github.com/termux/termux-packages/tree/master/packages/termux-api) * [`libandroid-wordexp-static`](https://github.com/termux/termux-packages/tree/master/packages/libandroid-wordexp): `wordexp.h` support for Android. +For the image logo, [Termux Monet](https://github.com/HardcodedCat/termux-monet) supports iterm image protocol. + ## Support status All categories not listed here should work without needing a specific implementation. ##### Available Modules ``` -Battery, Bios, Bluetooth, Board, Break, Brightness, Colors, Command, CPU, CPUUsage, Cursor, Custom, Date, DateTime, DE, Disk, Display, Font, Gamepad, GPU, Host, Icons, Kernel, LM, Locale, LocalIP, Media, Memory, Monitor, OpenCL, OpenGL, OS, Packages, Player, Power Adapter, Processes, PublicIP, Separator, Shell, Sound, Swap, Terminal, Terminal Font, Terminal Size, Theme, Time, Title, Uptime, Version, Vulkan, Wallpaper, Weather, Wifi, WM, WMTheme +Battery, Bios, Bluetooth, Board, Break, Brightness, Colors, Command, CPU, CPUUsage, Cursor, Custom, Date, DateTime, DE, Disk, Display, Font, Gamepad, GPU, Host, Icons, Kernel, LM, Locale, LocalIP, Media, Memory, Monitor, NetIO, OpenCL, OpenGL, OS, Packages, Player, Power Adapter, Processes, PublicIP, Separator, Shell, Sound, Swap, Terminal, Terminal Font, Terminal Size, Theme, Time, Title, Uptime, Version, Vulkan, Wallpaper, Weather, Wifi, WM, WMTheme ``` ##### Builtin logos ``` -AerOS, AIX, AlmaLinux, Alpine, Alpine2Small, AlpineSmall, Alter, Amazon, AmogOS, Anarchy, Android, AndroidSmall, Antergos, Antix, AoscOS, AoscOsRetro, AoscOsRetro_small, Aperture, Apple, AppleSmall, Apricity, Arch, Arch2, ArchBox, Archcraft, Archcraft2, Archlabs, ArchSmall, ArchStrike, ArcoLinux, ArcoLinuxSmall, ArseLinux, Artix, Artix2Small, ArtixSmall, Arya, Asahi, Aster, AsteroidOS, AstOS, Astra, Ataraxia, Athena, Bedrock, BigLinux, Bitrig, BlackArch, BlackPanther, BLAG, BlankOn, BlueLight, Bodhi, Bonsai, BSD, BunsenLabs, CachyOS, CachyOSSmall, Calculate, CalinixOS, CalinixOSSmall, Carbs, CBL-Mariner, CelOS, Center, CentOS, CentOSSmall, Chakra, ChaletOS, Chapeau, ChonkySealOS, Chrom, Cleanjaro, CleanjaroSmall, ClearLinux, ClearOS, Clover, Cobalt, Condres, ContainerLinux, CRUX, CRUXSmall, CrystalLinux, Cucumber, CutefishOS, CuteOS, CyberOS, Dahlia, DarkOS, Debian, DebianSmall, Deepin, DesaOS, Devuan, DevuanSmall, DietPi, DracOS, DragonFly, DragonFlyOld, DragonFlySmall, Drauger, Droidian, Elementary, ElementarySmall, Elive, EncryptOS, Endeavour, Endless, Enso, EuroLinux, EvolutionOS, Exherbo, ExodiaPredator, Fedora, FedoraOld, FedoraSmall, FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeBSDSmall, FreeMiNT, Frugalware, Funtoo, GalliumOS, Garuda, GarudaDragon, GarudaSmall, Gentoo, GentooSmall, GhostBSD, Glaucus, GNewSense, Gnome, GNU, GoboLinux, GrapheneOS, Grombyang, Guix, GuixSmall, Haiku, HaikuSmall, HamoniKR, HarDClanZ, HardenedBSD, Hash, Huayra, Hybrid, HydroOS, Hyperbola, HyperbolaSmall, Iglunix, InstantOS, IRIX, Itc, Januslinux, Kaisen, Kali, KaliSmall, KaOS, KDENeon, Kibojoe, KISSLinux, Kogaion, Korora, KrassOS, KSLinux, Kubuntu, LangitKetujuh, Laxeros, LEDE, LibreELEC, Linspire, Linux, LinuxLight, LinuxLightSmall, LinuxMint, LinuxMintOld, LinuxMintSmall, LinuxSmall, Live_Raizo, LMDE, Lunar, MacOS, MacOS2, MacOS2Small, MacOSSmall, Mageia, MageiaSmall, MagpieOS, Mandriva, Manjaro, ManjaroSmall, MassOS, MatuusOS, MaUI, Meowix, Mer, Minix, Mint, MintOld, MintSmall, MiracleLinux, MOS, Msys2, MX, MXSmall, Namib, Nekos, Neptune, NetBSD, NetRunner, Nitrux, NixOS, NixOS_small, NixOsOld, NixOsSmall, Nobara, NomadBSD, Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, OpenBSD, OpenBSDSmall, OpenEuler, OpenIndiana, OpenKylin, OpenMamba, OpenMandriva, OpenStage, OpenSuse, OpenSuseLeap, OpenSuseSmall, OpenSuseTumbleweed, OpenWrt, OPNsense, Oracle, Orchid, OrchidSmall, OS_Elbrus, OSMC, OSX, OSXSmall, PacBSD, Panwah, Parabola, ParabolaSmall, Parch, Pardus, Parrot, Parsix, PCBSD, PCLinuxOS, PearOS, Pengwin, Pentoo, Peppermint, PhyOS, Pisi, PNMLinux, Pop, PopSmall, Porteus, PostMarketOS, PostMarketOSSmall, Proxmox, PuffOS, Puppy, PureOS, PureOSSmall, Q4OS, Qubes, Qubyt, Quibian, Radix, Raspbian, RaspbianSmall, RavynOS, Reborn, RebornSmall, RedCore, RedHatEnterpriseLinux, RedHatEnterpriseLinux_old, RedstarOS, Refracted Devuan, Regata, Regolith, RhaymOS, RockyLinux, RockyLinuxSmall, RosaLinux, Sabayon, Sabotage, Sailfish, SalentOS, SalientOS, Salix, SambaBOX, Sasanqua, Scientific, Semc, Septor, Serene, SharkLinux, ShastraOS, Siduction, SkiffOS, Slackel, Slackware, SlackwareSmall, Slitaz, SmartOS, Soda, Solaris, SolarisSmall, Solus, SourceMage, Sparky, Star, SteamOS, StockLinux, Sulin, Suse, SuseSmall, Swagarch, T2, Tails, TeArch, TorizonCore, Trisquel, TuxedoOS, Twister, Ubuntu, Ubuntu2Old, Ubuntu2Small, UbuntuBudgie, UbuntuCinnamon, UbuntuGnome, UbuntuKde, UbuntuKylin, UbuntuMate, UbuntuOld, UbuntuSmall, UbuntuStudio, UbuntuSway, UbuntuTouch, UbuntuUnity, Ultramarine, Univalent, Univention, UOS, UrukOS, Uwuntu, Vanilla, Venom, Vnux, Void, VoidSmall, Vzlinux, WiiLinuxNgx, Windows, Windows11, Windows11Small, Windows8, Windows95, Xferience, YiffOS, Zorin +AerOS, Afterglow, AIX, AlmaLinux, Alpine, Alpine2Small, AlpineSmall, Alter, Amazon, AmazonLinux, AmogOS, Anarchy, Android, AndroidSmall, Antergos, Antix, AoscOS, AoscOS_old, AoscOsRetro, AoscOsRetro_small, Aperture, Apple, AppleSmall, Apricity, Arch, Arch2, ArchBox, Archcraft, Archcraft2, Archlabs, ArchSmall, ArchStrike, ArcoLinux, ArcoLinuxSmall, ArseLinux, Artix, Artix2Small, ArtixSmall, Arya, Asahi, Aster, AsteroidOS, AstOS, Astra, Ataraxia, Athena, Bedrock, BigLinux, Bitrig, BlackArch, BlackPanther, BLAG, BlankOn, BlueLight, Bodhi, Bonsai, BSD, BunsenLabs, CachyOS, CachyOSSmall, Calculate, CalinixOS, CalinixOSSmall, Carbs, CBL-Mariner, CelOS, Center, CentOS, CentOSSmall, Chakra, ChaletOS, Chapeau, ChonkySealOS, Chrom, Cleanjaro, CleanjaroSmall, ClearLinux, ClearOS, Clover, Cobalt, Condres, ContainerLinux, CRUX, CRUXSmall, CrystalLinux, Cucumber, CutefishOS, CuteOS, CyberOS, Dahlia, DarkOS, Debian, DebianSmall, Deepin, DesaOS, Devuan, DevuanSmall, DietPi, DracOS, DragonFly, DragonFlyOld, DragonFlySmall, Drauger, Droidian, Elbrus, Elementary, ElementarySmall, Elive, EncryptOS, Endeavour, Endless, Enso, EuroLinux, EvolutionOS, EvolutionOS_old, EvolutionOSSmall, Exherbo, ExodiaPredator, Fedora, FedoraOld, FedoraSmall, FemboyOS, Feren, Finnix, Floflis, FreeBSD, FreeBSDSmall, FreeMiNT, Frugalware, Funtoo, GalliumOS, Garuda, GarudaDragon, GarudaSmall, Gentoo, GentooSmall, GhostBSD, Glaucus, GNewSense, Gnome, GNU, GoboLinux, GrapheneOS, Grombyang, Guix, GuixSmall, Haiku, HaikuSmall, HamoniKR, HarDClanZ, HardenedBSD, Hash, Huayra, Hybrid, HydroOS, Hyperbola, HyperbolaSmall, Iglunix, InstantOS, IRIX, Itc, Januslinux, Kaisen, Kali, KaliSmall, KaOS, KDENeon, Kibojoe, KISSLinux, Kogaion, Korora, KrassOS, KSLinux, Kubuntu, LangitKetujuh, Laxeros, LEDE, LibreELEC, Linspire, Linux, LinuxLight, LinuxLightSmall, LinuxMint, LinuxMintOld, LinuxMintSmall, LinuxSmall, Live_Raizo, LMDE, Lunar, MacOS, MacOS2, MacOS2Small, MacOSSmall, Mageia, MageiaSmall, MagpieOS, Mandriva, Manjaro, ManjaroSmall, MassOS, MatuusOS, MaUI, Meowix, Mer, Minix, Mint, MintOld, MintSmall, MiracleLinux, MOS, Msys2, MX, MXSmall, Namib, Nekos, Neptune, NetBSD, NetRunner, Nitrux, NixOS, NixOS_small, NixOsOld, NixOsSmall, Nobara, NomadBSD, Nurunner, NuTyX, Obarun, OBRevenge, OmniOS, OpenBSD, OpenBSDSmall, OpenEuler, OpenIndiana, OpenKylin, OpenMamba, OpenMandriva, OpenStage, OpenSuse, OpenSuseLeap, OpenSuseSmall, OpenSuseTumbleweed, OpenWrt, OPNsense, Oracle, Orchid, OrchidSmall, OS_Elbrus, OSMC, OSX, OSXSmall, PacBSD, Panwah, Parabola, ParabolaSmall, Parch, Pardus, Parrot, Parsix, PCBSD, PCLinuxOS, PearOS, Pengwin, Pentoo, Peppermint, PhyOS, Pisi, PNMLinux, Pop, PopSmall, Porteus, PostMarketOS, PostMarketOSSmall, Proxmox, PuffOS, Puppy, PureOS, PureOSSmall, Q4OS, Qubes, Qubyt, Quibian, Radix, Raspbian, RaspbianSmall, RavynOS, Reborn, RebornSmall, RedCore, RedHatEnterpriseLinux, RedHatEnterpriseLinux_old, RedstarOS, Refracted Devuan, Regata, Regolith, RhaymOS, RockyLinux, RockyLinuxSmall, RosaLinux, Sabayon, Sabotage, Sailfish, SalentOS, SalientOS, Salix, SambaBOX, Sasanqua, Scientific, Semc, Septor, Serene, SharkLinux, ShastraOS, Siduction, SkiffOS, Slackel, Slackware, SlackwareSmall, Slitaz, SmartOS, Soda, Solaris, SolarisSmall, Solus, SourceMage, Sparky, Star, SteamOS, StockLinux, Sulin, Suse, SuseSmall, Swagarch, T2, Tails, TeArch, TorizonCore, Trisquel, TuxedoOS, Twister, Ubuntu, Ubuntu2Old, Ubuntu2Small, UbuntuBudgie, UbuntuCinnamon, UbuntuGnome, UbuntuKde, UbuntuKylin, UbuntuMate, UbuntuOld, UbuntuSmall, UbuntuStudio, UbuntuSway, UbuntuTouch, UbuntuUnity, Ultramarine, Univalent, Univention, UOS, UrukOS, Uwuntu, Vanilla, Venom, VenomSmall, Vnux, Void, VoidSmall, Vzlinux, WiiLinuxNgx, Windows, Windows11, Windows11Small, Windows8, Windows95, Xferience, YiffOS, Zorin ``` Run `fastfetch --print-logos` to print them diff --git a/completions/bash b/completions/bash index a8db1c6000..9a59d71163 100644 --- a/completions/bash +++ b/completions/bash @@ -24,7 +24,7 @@ __fastfetch_complete_help() "terminal-format" "terminalfont-format" "cpu-format" - "cpu-usage-format" + "cpuusage-format" "gpu-format" "memory-format" "swap-format" diff --git a/doc/json_schema.json b/doc/json_schema.json index 60c3c52592..d7e681566c 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -166,6 +166,11 @@ "title": "If true, regenerate image logo cache", "default": false }, + "separate": { + "type": "boolean", + "title": "If true, print modules at bottom of the logo", + "default": false + }, "chafa": { "type": "object", "title": "Chafa configuration. See chafa document for details", @@ -269,6 +274,11 @@ "type": "integer", "title": "Set the timeout (ms) for WMI queries, `-1` for no timeout. Windows only", "default": 5000 + }, + "processingTimeout": { + "type": "integer", + "title": "Set the timeout (ms) when waiting for child processes, `-1` for no timeout", + "default": 1000 } }, "additionalProperties": false @@ -548,6 +558,7 @@ "media", "memory", "monitor", + "netio", "opencl", "opengl", "os", @@ -989,7 +1000,37 @@ "title": "Show IPs with given name prefix only", "type": "string" }, - "showDefaultRouteOnly": { + "defaultRouteOnly": { + "title": "Show ips that are used for default routing only", + "type": "boolean", + "default": false + }, + "key": { + "$ref": "#/$defs/key" + }, + "keyColor": { + "$ref": "#/$defs/keyColor" + }, + "keyWidth": { + "$ref": "#/$defs/keyWidth" + }, + "format": { + "$ref": "#/$defs/format" + } + }, + "additionalProperties": false + }, + { + "title": "Network throughput (usage)", + "properties": { + "type": { + "const": "netio" + }, + "namePrefix": { + "title": "Show IPs with given name prefix only", + "type": "string" + }, + "defaultRouteOnly": { "title": "Show ips that are used for default routing only", "type": "boolean", "default": false diff --git a/presets/all b/presets/all index 1841c47ca6..b0ed3cab52 100644 --- a/presets/all +++ b/presets/all @@ -1 +1 @@ ---structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Display:Brightness:Monitor:LM:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Wallpaper:Terminal:TerminalFont:TerminalSize:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Bluetooth:Sound:Gamepad:Weather:Version:Break:Colors +--structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Display:Brightness:Monitor:LM:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Wallpaper:Terminal:TerminalFont:TerminalSize:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Bluetooth:Sound:Gamepad:Weather:NetIO:Version:Break:Colors diff --git a/presets/all.jsonc b/presets/all.jsonc index b3e9c30d95..f8d4cc82ed 100644 --- a/presets/all.jsonc +++ b/presets/all.jsonc @@ -51,6 +51,7 @@ "sound", "gamepad", "weather", + "netio", "version", "break", "colors" diff --git a/presets/ci b/presets/ci new file mode 100644 index 0000000000..d8327be7ac --- /dev/null +++ b/presets/ci @@ -0,0 +1,7 @@ +-c all +--pipe +--show-errors +--no-buffer +--publicip-timeout 1000 +--weather-timeout 1000 +--stat diff --git a/presets/devinfo b/presets/devinfo deleted file mode 100644 index 2ddf030b58..0000000000 --- a/presets/devinfo +++ /dev/null @@ -1,3 +0,0 @@ ---disable-linewrap false ---multithreading false ---show-errors diff --git a/presets/devinfo-verbose b/presets/devinfo-verbose deleted file mode 100644 index 6f79f97e78..0000000000 --- a/presets/devinfo-verbose +++ /dev/null @@ -1,3 +0,0 @@ ---load-config devinfo ---load-config all ---load-config verbose diff --git a/presets/devinfo.jsonc b/presets/devinfo.jsonc deleted file mode 100644 index 450adb9262..0000000000 --- a/presets/devinfo.jsonc +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", - "logo": { - "type": "builtin", - "source": "arch" - }, - "display": { - "disableLinewrap": false, - "showErrors": true - }, - "general": { - "multithreading": false - }, - "modules": [ - "title", - "separator", - { - "type": "os", - "format": "Arch Linux ({12})" - }, - "host", - "kernel", - "uptime", - { - "type": "packages", - "format": "{} (pacman)" - }, - "shell", - "display", - "de", - "wm", - "wmtheme", - "theme", - "icons", - "font", - "cursor", - "terminal", - "terminalfont", - "cpu", - "gpu", - "memory", - "disk", - "battery", - "poweradapter", - "locale", - "break", - "colors" - ] -} diff --git a/presets/examples/10.jsonc b/presets/examples/10.jsonc new file mode 100644 index 0000000000..96ec3bb062 --- /dev/null +++ b/presets/examples/10.jsonc @@ -0,0 +1,150 @@ +// Load with --load-config examples/2.jsonc +// Note that you must replace the image path to an existing image to display it. + +{ + "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", + "logo": { + "padding": { + "top": 2 + } + }, + "display": { + "separator": " -> " + }, + "modules": [ + { + "type": "custom", + "format": "\u001b[90m┌────────────────────────────────────────────────────────────┐" + }, + { + "type": "title", + "keyWidth": 10 + }, + { + "type": "custom", + "format": "\u001b[90m└────────────────────────────────────────────────────────────┘" + }, + { + "type": "custom", + "format": " \u001b[90m \u001b[31m \u001b[32m \u001b[33m \u001b[34m \u001b[35m \u001b[36m \u001b[37m \u001b[38m \u001b[39m  \u001b[38m \u001b[37m \u001b[36m \u001b[35m \u001b[34m \u001b[33m \u001b[32m \u001b[31m \u001b[90m" + }, + { + "type": "custom", + "format": "\u001b[90m┌────────────────────────────────────────────────────────────┐" + }, + { + "type": "os", + "key": " OS", + "keyColor": "yellow" + }, + { + "type": "kernel", + "key": "│ ├", + "keyColor": "yellow" + }, + { + "type": "packages", + "key": "│ ├󰏖", + "keyColor": "yellow" + }, + { + "type": "shell", + "key": "│ └", + "keyColor": "yellow" + }, + { + "type": "wm", + "key": " DE/WM", + "keyColor": "blue" + }, + { + "type": "lm", + "key": "│ ├󰧨", + "keyColor": "blue" + }, + { + "type": "wmtheme", + "key": "│ ├󰉼", + "keyColor": "blue" + }, + { + "type": "icons", + "key": "│ ├󰀻", + "keyColor": "blue" + }, + { + "type": "terminal", + "key": "│ ├", + "keyColor": "blue" + }, + { + "type": "wallpaper", + "key": "│ └󰸉", + "keyColor": "blue" + }, + { + "type": "host", + "key": " PC", + "keyColor": "green" + }, + { + "type": "cpu", + "key": "│ ├", + "keyColor": "green" + }, + { + "type": "gpu", + "key": "│ ├﬙", + "keyColor": "green" + }, + { + "type": "disk", + "key": "│ ├", + "keyColor": "green" + }, + { + "type": "memory", + "key": "│ ├󰑭", + "keyColor": "green" + }, + { + "type": "swap", + "key": "│ ├󰓡", + "keyColor": "green" + }, + { + "type": "uptime", + "key": "│ ├󰅐", + "keyColor": "green" + }, + { + "type": "display", + "key": "│ └󰍹", + "keyColor": "green" + }, + { + "type": "sound", + "key": " SND", + "keyColor": "cyan" + }, + { + "type": "player", + "key": "│ ├󰥠", + "keyColor": "cyan" + }, + { + "type": "media", + "key": "│ └󰝚", + "keyColor": "cyan" + }, + { + "type": "custom", + "format": "\u001b[90m└────────────────────────────────────────────────────────────┘" + }, + "break", + { + "type": "custom", + "format": " \u001b[90m \u001b[31m \u001b[32m \u001b[33m \u001b[34m \u001b[35m \u001b[36m \u001b[37m \u001b[38m \u001b[39m  \u001b[38m \u001b[37m \u001b[36m \u001b[35m \u001b[34m \u001b[33m \u001b[32m \u001b[31m \u001b[90m" + } + ] +} diff --git a/presets/examples/11.jsonc b/presets/examples/11.jsonc new file mode 100644 index 0000000000..13215add19 --- /dev/null +++ b/presets/examples/11.jsonc @@ -0,0 +1,38 @@ +{ + "$schema": "file:///C:/msys64/home/zhang/fastfetch/doc/json_schema.json", + "logo": { + "type": "small" + }, + "display": { + "percentType": 9, + "separator": "\u001b[31m-> " + }, + "modules": [ + { + "key": "Distro ", + "type": "os" + }, + { + "key": "Shell ", + "type": "shell" + }, + { + "key": "Terminal ", + "type": "terminal" + }, + { + "key": "Display ", + "type": "display" + }, + { + "key": "Backlight ", + "type": "brightness" + }, + "break", + { + "type": "colors", + "paddingLeft": 6, + "symbol": "circle" + } + ] +} diff --git a/presets/verbose b/presets/verbose deleted file mode 100644 index 3ed020f193..0000000000 --- a/presets/verbose +++ /dev/null @@ -1,31 +0,0 @@ ---os-format System: {}; Name: {}; Pretty name: {}; ID: {}; ID like: {}; Variant: {}; Variant ID: {}; Version: {}; Version ID: {}; Version codename: {}; Build ID: {}; Architecture: {} ---host-format Family: product_family: {}; product_name: {}; product_version: {}; product_sku: {}; sys_vendor: {} ---kernel-format Sysname: {}; Release: {}; Version: {} ---uptime-format Days: {}; Hours: {}; Minutes: {}; Seconds: {} ---processes-format Count: {} ---packages-format All: {}; pacman: {}; pacman branch: {}; dpkg: {}; rpm: {}; emerge: {}; eopkg: {}; xbps: {}; apk: {}; flatpak-system: {}; flatpak-user: {}; snap: {}; brew: {}; brew-cask: {}; port: {}; scoop: {}; choco: {}; pkgtool: {}; paludis {} ---shell-format Process name: {}; Process path: {}; Process exe: {}; Process version: {}; User path: {}; User exe: {}; User version: {} ---display-format Width: {}; Height: {}; Refresh rate: {}; ScaledWith: {}; ScaledHeight: {} ---de-format Process name: {}; Pretty name: {}; Version: {} ---wm-format Process name: {}; Pretty name: {}; Protocol: {} ---wmtheme-format Name: {} ---theme-format Plasma: {}; Plasma colors: {}; Plasma colors pretty: {}; GTK2: {}; GTK3: {}; GTK4: {}; GTK: {} ---icons-format Plasma: {}; GTK2: {}; GTK3: {}; GTK4: {}; GTK: {} ---font-format Font1: {}; Font2: {}; Font3: {}; Font4: {} ---cursor-format Theme: {}; Size: {} ---terminal-format Process: {}; Path: {}; Exe: {} ---terminalfont-format Pretty: {}; Name: {}; Size: {}; Styles: {} ---cpu-format Name: {}, Vendor: {}, CoresPhysical: {}, CoresLogical: {}, CoresOnline: {}, FrequencyMin: {}, FrequencyMax: {}, Temperature: {} ---cpu-usage-format Percentage: {} ---gpu-format Vendor: {}; Name: {}; Driver: {}; Temperature: {}; CoreCount: {}; Type: {} ---memory-format Used: {}; Total: {}; Percentage: {} ---disk-format SizeUsed: {}; SizeTotal: {}; SizePercentage: {}; FilesUsed: {}; FilesTotal: {}; FilesPercentage: {}; External: {}; Hidden: {}; Filesystem: {}; Name: {} ---battery-format Manufactor: {}; Model: {}; Technology: {}; Capacity: {}; Status: {} ---poweradapter-format Watts: {}; Name: {}; Manufactor: {}; Model: {}; Description: {} ---player-format Pretty: {}; Name: {}; Bus: {}; Url: {} ---media-format Pretty: {}; Name: {}; Artist: {}; Album: {} ---datetime-format year: {}; yearShort: {}; month: {}; monthPretty: {}; monthName: {}; monthNameShort: {}; weekNumber: {}; weekday: {}; weekdayShort: {}; dayInYear: {}; dayInMonth: {}; dayInWeek: {}; hour: {}; hourPretty: {}; hour12: {}; hour12Pretty: {}; minute: {}; minutePretty: {}; second: {}; secondPretty: {} ---vulkan-format driver: {}; Api Version: {}; Conformance Version: {} ---opengl-format version: {}; renderer: {}; vendor: {} ---opencl-format version: {}; device: {}; vendor: {} ---bluetooth-format Name: {}; Address: {}; Type: {}; Battery: {} diff --git a/src/3rdparty/yyjson/repo.json b/src/3rdparty/yyjson/repo.json index d9edbde014..4e8e1106d0 100644 --- a/src/3rdparty/yyjson/repo.json +++ b/src/3rdparty/yyjson/repo.json @@ -1,6 +1,6 @@ { "home": "https://github.com/ibireme/yyjson", "license": "MIT ( embed in source )", - "version": "5e3b26d2659287d31e2f8e10f95f95feb7e5ab3a", + "version": "0.8.0", "author": "ibireme" } diff --git a/src/3rdparty/yyjson/yyjson.c b/src/3rdparty/yyjson/yyjson.c index c669bab9bc..3e74166c19 100644 --- a/src/3rdparty/yyjson/yyjson.c +++ b/src/3rdparty/yyjson/yyjson.c @@ -307,6 +307,9 @@ uint32_t yyjson_version(void) { #define YYJSON_MUT_DOC_VAL_POOL_INIT_SIZE (0x10 * sizeof(yyjson_mut_val)) #define YYJSON_MUT_DOC_VAL_POOL_MAX_SIZE (0x1000000 * sizeof(yyjson_mut_val)) +/* The minimum size of the dynamic allocator's chunk. */ +#define YYJSON_ALC_DYN_MIN_SIZE 0x1000 + /* Default value for compile-time options. */ #ifndef YYJSON_DISABLE_READER #define YYJSON_DISABLE_READER 0 @@ -966,6 +969,15 @@ static const yyjson_alc YYJSON_DEFAULT_ALC = { NULL }; + + +/*============================================================================== + * Null Memory Allocator + * + * This allocator is just a placeholder to ensure that the internal + * malloc/realloc/free function pointers are not null. + *============================================================================*/ + static void *null_malloc(void *ctx, usize size) { return NULL; } @@ -989,29 +1001,37 @@ static const yyjson_alc YYJSON_NULL_ALC = { /*============================================================================== * Pool Memory Allocator - * This is a simple memory allocator that uses linked list memory chunk. - * The following code will be executed only when the library user creates - * this allocator manually. + * + * This allocator is initialized with a fixed-size buffer. + * The buffer is split into multiple memory chunks for memory allocation. *============================================================================*/ -/** chunk header */ +/** memory chunk header */ typedef struct pool_chunk { - usize size; /* chunk memory size (include chunk header) */ - struct pool_chunk *next; + usize size; /* chunk memory size, include chunk header */ + struct pool_chunk *next; /* linked list, nullable */ + /* char mem[]; flexible array member */ } pool_chunk; -/** ctx header */ +/** allocator ctx header */ typedef struct pool_ctx { - usize size; /* total memory size (include ctx header) */ - pool_chunk *free_list; + usize size; /* total memory size, include ctx header */ + pool_chunk *free_list; /* linked list, nullable */ + /* pool_chunk chunks[]; flexible array member */ } pool_ctx; +/** align up the input size to chunk size */ +static_inline void pool_size_align(usize *size) { + *size = size_align_up(*size, sizeof(pool_chunk)) + sizeof(pool_chunk); +} + static void *pool_malloc(void *ctx_ptr, usize size) { + /* assert(size != 0) */ pool_ctx *ctx = (pool_ctx *)ctx_ptr; pool_chunk *next, *prev = NULL, *cur = ctx->free_list; - if (unlikely(size == 0 || size >= ctx->size)) return NULL; - size = size_align_up(size, sizeof(pool_chunk)) + sizeof(pool_chunk); + if (unlikely(size >= ctx->size)) return NULL; + pool_size_align(&size); while (cur) { if (cur->size < size) { @@ -1038,6 +1058,7 @@ static void *pool_malloc(void *ctx_ptr, usize size) { } static void pool_free(void *ctx_ptr, void *ptr) { + /* assert(ptr != NULL) */ pool_ctx *ctx = (pool_ctx *)ctx_ptr; pool_chunk *cur = ((pool_chunk *)ptr) - 1; pool_chunk *prev = NULL, *next = ctx->free_list; @@ -1064,25 +1085,15 @@ static void pool_free(void *ctx_ptr, void *ptr) { static void *pool_realloc(void *ctx_ptr, void *ptr, usize old_size, usize size) { + /* assert(ptr != NULL && size != 0 && old_size < size) */ pool_ctx *ctx = (pool_ctx *)ctx_ptr; pool_chunk *cur = ((pool_chunk *)ptr) - 1, *prev, *next, *tmp; - usize free_size; - void *new_ptr; - - if (unlikely(size == 0 || size >= ctx->size)) return NULL; - size = size_align_up(size, sizeof(pool_chunk)) + sizeof(pool_chunk); - /* reduce size */ - if (unlikely(size <= cur->size)) { - free_size = cur->size - size; - if (free_size >= sizeof(pool_chunk) * 2) { - tmp = (pool_chunk *)(void *)((u8 *)cur + cur->size - free_size); - tmp->size = free_size; - pool_free(ctx_ptr, (void *)(tmp + 1)); - cur->size -= free_size; - } - return ptr; - } + /* check size */ + if (unlikely(size >= ctx->size)) return NULL; + pool_size_align(&old_size); + pool_size_align(&size); + if (unlikely(old_size == size)) return ptr; /* find next and prev chunk */ prev = NULL; @@ -1092,10 +1103,9 @@ static void *pool_realloc(void *ctx_ptr, void *ptr, next = next->next; } - /* merge to higher chunk if they are contiguous */ - if ((u8 *)cur + cur->size == (u8 *)next && - cur->size + next->size >= size) { - free_size = cur->size + next->size - size; + if ((u8 *)cur + cur->size == (u8 *)next && cur->size + next->size >= size) { + /* merge to higher chunk if they are contiguous */ + usize free_size = cur->size + next->size - size; if (free_size > sizeof(pool_chunk) * 2) { tmp = (pool_chunk *)(void *)((u8 *)cur + size); if (prev) prev->next = tmp; @@ -1109,15 +1119,15 @@ static void *pool_realloc(void *ctx_ptr, void *ptr, cur->size += next->size; } return ptr; + } else { + /* fallback to malloc and memcpy */ + void *new_ptr = pool_malloc(ctx_ptr, size - sizeof(pool_chunk)); + if (new_ptr) { + memcpy(new_ptr, ptr, cur->size - sizeof(pool_chunk)); + pool_free(ctx_ptr, ptr); + } + return new_ptr; } - - /* fallback to malloc and memcpy */ - new_ptr = pool_malloc(ctx_ptr, size - sizeof(pool_chunk)); - if (new_ptr) { - memcpy(new_ptr, ptr, cur->size - sizeof(pool_chunk)); - pool_free(ctx_ptr, ptr); - } - return new_ptr; } bool yyjson_alc_pool_init(yyjson_alc *alc, void *buf, usize size) { @@ -1147,6 +1157,161 @@ bool yyjson_alc_pool_init(yyjson_alc *alc, void *buf, usize size) { +/*============================================================================== + * Dynamic Memory Allocator + * + * This allocator allocates memory on demand and does not immediately release + * unused memory. Instead, it places the unused memory into a freelist for + * potential reuse in the future. It is only when the entire allocator is + * destroyed that all previously allocated memory is released at once. + *============================================================================*/ + +/** memory chunk header */ +typedef struct dyn_chunk { + usize size; /* chunk size, include header */ + struct dyn_chunk *next; + /* char mem[]; flexible array member */ +} dyn_chunk; + +/** allocator ctx header */ +typedef struct { + dyn_chunk free_list; /* dummy header, sorted from small to large */ + dyn_chunk used_list; /* dummy header */ +} dyn_ctx; + +/** align up the input size to chunk size */ +static_inline bool dyn_size_align(usize *size) { + usize alc_size = *size + sizeof(dyn_chunk); + alc_size = size_align_up(alc_size, YYJSON_ALC_DYN_MIN_SIZE); + if (unlikely(alc_size < *size)) return false; /* overflow */ + *size = alc_size; + return true; +} + +/** remove a chunk from list (the chunk must already be in the list) */ +static_inline void dyn_chunk_list_remove(dyn_chunk *list, dyn_chunk *chunk) { + dyn_chunk *prev = list, *cur; + for (cur = prev->next; cur; cur = cur->next) { + if (cur == chunk) { + prev->next = cur->next; + cur->next = NULL; + return; + } + prev = cur; + } +} + +/** add a chunk to list header (the chunk must not be in the list) */ +static_inline void dyn_chunk_list_add(dyn_chunk *list, dyn_chunk *chunk) { + chunk->next = list->next; + list->next = chunk; +} + +static void *dyn_malloc(void *ctx_ptr, usize size) { + /* assert(size != 0) */ + const yyjson_alc def = YYJSON_DEFAULT_ALC; + dyn_ctx *ctx = (dyn_ctx *)ctx_ptr; + dyn_chunk *chunk, *prev, *next; + if (unlikely(!dyn_size_align(&size))) return NULL; + + /* freelist is empty, create new chunk */ + if (!ctx->free_list.next) { + chunk = (dyn_chunk *)def.malloc(def.ctx, size); + if (unlikely(!chunk)) return NULL; + chunk->size = size; + chunk->next = NULL; + dyn_chunk_list_add(&ctx->used_list, chunk); + return (void *)(chunk + 1); + } + + /* find a large enough chunk, or resize the largest chunk */ + prev = &ctx->free_list; + while (true) { + chunk = prev->next; + if (chunk->size >= size) { /* enough size, reuse this chunk */ + prev->next = chunk->next; + dyn_chunk_list_add(&ctx->used_list, chunk); + return (void *)(chunk + 1); + } + if (!chunk->next) { /* resize the largest chunk */ + chunk = (dyn_chunk *)def.realloc(def.ctx, chunk, chunk->size, size); + if (unlikely(!chunk)) return NULL; + prev->next = NULL; + chunk->size = size; + dyn_chunk_list_add(&ctx->used_list, chunk); + return (void *)(chunk + 1); + } + prev = chunk; + } +} + +static void *dyn_realloc(void *ctx_ptr, void *ptr, + usize old_size, usize size) { + /* assert(ptr != NULL && size != 0 && old_size < size) */ + const yyjson_alc def = YYJSON_DEFAULT_ALC; + dyn_ctx *ctx = (dyn_ctx *)ctx_ptr; + dyn_chunk *prev, *next, *new_chunk; + dyn_chunk *chunk = (dyn_chunk *)ptr - 1; + if (unlikely(!dyn_size_align(&size))) return NULL; + if (chunk->size >= size) return ptr; + + dyn_chunk_list_remove(&ctx->used_list, chunk); + new_chunk = (dyn_chunk *)def.realloc(def.ctx, chunk, chunk->size, size); + if (likely(new_chunk)) { + new_chunk->size = size; + chunk = new_chunk; + } + dyn_chunk_list_add(&ctx->used_list, chunk); + return new_chunk ? (void *)(new_chunk + 1) : NULL; +} + +static void dyn_free(void *ctx_ptr, void *ptr) { + /* assert(ptr != NULL) */ + dyn_ctx *ctx = (dyn_ctx *)ctx_ptr; + dyn_chunk *chunk = (dyn_chunk *)ptr - 1, *prev; + + dyn_chunk_list_remove(&ctx->used_list, chunk); + for (prev = &ctx->free_list; prev; prev = prev->next) { + if (!prev->next || prev->next->size >= chunk->size) { + chunk->next = prev->next; + prev->next = chunk; + break; + } + } +} + +yyjson_alc *yyjson_alc_dyn_new(void) { + const yyjson_alc def = YYJSON_DEFAULT_ALC; + usize hdr_len = sizeof(yyjson_alc) + sizeof(dyn_ctx); + yyjson_alc *alc = (yyjson_alc *)def.malloc(def.ctx, hdr_len); + dyn_ctx *ctx = (dyn_ctx *)(void *)(alc + 1); + if (unlikely(!alc)) return NULL; + alc->malloc = dyn_malloc; + alc->realloc = dyn_realloc; + alc->free = dyn_free; + alc->ctx = alc + 1; + memset(ctx, 0, sizeof(*ctx)); + return alc; +} + +void yyjson_alc_dyn_free(yyjson_alc *alc) { + const yyjson_alc def = YYJSON_DEFAULT_ALC; + dyn_ctx *ctx = (dyn_ctx *)(void *)(alc + 1); + dyn_chunk *chunk, *next; + if (unlikely(!alc)) return; + for (chunk = ctx->free_list.next; chunk; chunk = next) { + next = chunk->next; + def.free(def.ctx, chunk); + } + for (chunk = ctx->used_list.next; chunk; chunk = next) { + next = chunk->next; + def.free(def.ctx, chunk); + } + def.free(def.ctx, alc); +} + + + /*============================================================================== * JSON document and value *============================================================================*/ @@ -1305,7 +1470,6 @@ yyjson_mut_val *yyjson_val_mut_copy(yyjson_mut_doc *m_doc, We copy them to another contiguous memory as mutable values, then reconnect the mutable values with the original relationship. */ - usize i_vals_len; yyjson_mut_val *m_vals, *m_val; yyjson_val *i_val, *i_end; @@ -1375,7 +1539,6 @@ static yyjson_mut_val *unsafe_yyjson_mut_val_mut_copy(yyjson_mut_doc *m_doc, second to last item, which needs to be linked to the last item to close the circle. */ - yyjson_mut_val *m_val = unsafe_yyjson_mut_val(m_doc, 1); if (unlikely(!m_val)) return NULL; m_val->tag = m_vals->tag; @@ -1540,12 +1703,13 @@ static_inline bool unsafe_yyjson_num_equals(void *lhs, void *rhs) { yyjson_val_uni *runi = &((yyjson_val *)rhs)->uni; yyjson_subtype lt = unsafe_yyjson_get_subtype(lhs); yyjson_subtype rt = unsafe_yyjson_get_subtype(rhs); - if (lt == rt) - return luni->u64 == runi->u64; - if (lt == YYJSON_SUBTYPE_SINT && rt == YYJSON_SUBTYPE_UINT) + if (lt == rt) return luni->u64 == runi->u64; + if (lt == YYJSON_SUBTYPE_SINT && rt == YYJSON_SUBTYPE_UINT) { return luni->i64 >= 0 && luni->u64 == runi->u64; - if (lt == YYJSON_SUBTYPE_UINT && rt == YYJSON_SUBTYPE_SINT) + } + if (lt == YYJSON_SUBTYPE_UINT && rt == YYJSON_SUBTYPE_SINT) { return runi->i64 >= 0 && luni->u64 == runi->u64; + } return false; } @@ -1571,8 +1735,8 @@ bool unsafe_yyjson_equals(yyjson_val *lhs, yyjson_val *rhs) { while (len-- > 0) { rhs = yyjson_obj_iter_getn(&iter, lhs->uni.str, unsafe_yyjson_get_len(lhs)); - if (!rhs || !unsafe_yyjson_equals(lhs + 1, rhs)) - return false; + if (!rhs) return false; + if (!unsafe_yyjson_equals(lhs + 1, rhs)) return false; lhs = unsafe_yyjson_get_next(lhs + 1); } } @@ -1626,8 +1790,8 @@ bool unsafe_yyjson_mut_equals(yyjson_mut_val *lhs, yyjson_mut_val *rhs) { while (len-- > 0) { rhs = yyjson_mut_obj_iter_getn(&iter, lhs->uni.str, unsafe_yyjson_get_len(lhs)); - if (!rhs || !unsafe_yyjson_mut_equals(lhs->next, rhs)) - return false; + if (!rhs) return false; + if (!unsafe_yyjson_mut_equals(lhs->next, rhs)) return false; lhs = lhs->next->next; } } @@ -2506,6 +2670,7 @@ yyjson_mut_val *yyjson_merge_patch(yyjson_mut_doc *doc, builder = yyjson_mut_obj(doc); if (unlikely(!builder)) return NULL; + memset(&local_orig, 0, sizeof(local_orig)); if (!yyjson_is_obj(orig)) { orig = &local_orig; orig->tag = builder->tag; @@ -2557,6 +2722,7 @@ yyjson_mut_val *yyjson_mut_merge_patch(yyjson_mut_doc *doc, builder = yyjson_mut_obj(doc); if (unlikely(!builder)) return NULL; + memset(&local_orig, 0, sizeof(local_orig)); if (!yyjson_mut_is_obj(orig)) { orig = &local_orig; orig->tag = builder->tag; @@ -3318,8 +3484,6 @@ static_inline void pow10_table_get_exp(i32 exp10, i32 *exp2) { -#if !YYJSON_DISABLE_READER - /*============================================================================== * JSON Character Matcher *============================================================================*/ @@ -3513,6 +3677,8 @@ static_inline bool digi_is_digit_or_fp(u8 d) { +#if !YYJSON_DISABLE_READER + /*============================================================================== * Hex Character Reader * This function is used by JSON reader to read escaped characters. diff --git a/src/3rdparty/yyjson/yyjson.h b/src/3rdparty/yyjson/yyjson.h index a9740045ad..97b86fb96b 100644 --- a/src/3rdparty/yyjson/yyjson.h +++ b/src/3rdparty/yyjson/yyjson.h @@ -527,16 +527,16 @@ extern "C" { #define YYJSON_VERSION_MAJOR 0 /** The minor version of yyjson. */ -#define YYJSON_VERSION_MINOR 7 +#define YYJSON_VERSION_MINOR 8 /** The patch version of yyjson. */ #define YYJSON_VERSION_PATCH 0 /** The version of yyjson in hex: `(major << 16) | (minor << 8) | (patch)`. */ -#define YYJSON_VERSION_HEX 0x000700 +#define YYJSON_VERSION_HEX 0x000800 /** The version string of yyjson. */ -#define YYJSON_VERSION_STRING "0.7.0" +#define YYJSON_VERSION_STRING "0.8.0" /** The version of yyjson in hex, same as `YYJSON_VERSION_HEX`. */ yyjson_api uint32_t yyjson_version(void); @@ -635,11 +635,11 @@ typedef struct yyjson_alc { function, but the amount of memory required to write a JSON cannot be directly calculated. - This is not a general-purpose allocator. If used to read multiple JSON - documents and only some of them are released, it may cause memory - fragmentation, leading to performance degradation and memory waste. Therefore, - it is recommended to use this allocator only for reading or writing a single - JSON document. + This is not a general-purpose allocator. It is designed to handle a single JSON + data at a time. If it is used for overly complex memory tasks, such as parsing + multiple JSON documents using the same allocator but releasing only a few of + them, it may cause memory fragmentation, resulting in performance degradation + and memory waste. @param alc The allocator to be initialized. If this parameter is NULL, the function will fail and return false. @@ -662,9 +662,31 @@ typedef struct yyjson_alc { yyjson_doc *doc = yyjson_read_opts(json, strlen(json), 0, &alc, NULL); // the memory of `doc` is on the stack @endcode + + @warning This Allocator is not thread-safe. */ yyjson_api bool yyjson_alc_pool_init(yyjson_alc *alc, void *buf, size_t size); +/** + A dynamic allocator. + + This allocator has a similar usage to the pool allocator above. However, when + there is not enough memory, this allocator will dynamically request more memory + using libc's `malloc` function, and frees it all at once when it is destroyed. + + @return A new dynamic allocator, or NULL if memory allocation failed. + @note The returned value should be freed with `yyjson_alc_dyn_free()`. + + @warning This Allocator is not thread-safe. + */ +yyjson_api yyjson_alc *yyjson_alc_dyn_new(void); + +/** + Free a dynamic allocator which is created by `yyjson_alc_dyn_new()`. + @param alc The dynamic allocator to be destroyed. + */ +yyjson_api void yyjson_alc_dyn_free(yyjson_alc *alc); + /*============================================================================== @@ -3556,7 +3578,7 @@ yyjson_api_inline bool yyjson_mut_obj_rotate(yyjson_mut_val *obj, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_null(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3566,7 +3588,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_null(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_true(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3576,7 +3598,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_true(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_false(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3586,7 +3608,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_false(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3596,7 +3618,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_uint(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3606,7 +3628,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_uint(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_sint(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3616,7 +3638,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_sint(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3626,7 +3648,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_real(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3636,7 +3658,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_real(yyjson_mut_doc *doc, The `key` and `val` should be null-terminated UTF-8 strings. This function allows duplicated key in one object. - @warning The key/value string are not copied, you should keep these strings + @warning The key/value strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_str(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3648,7 +3670,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_str(yyjson_mut_doc *doc, The `len` should be the length of the `val`, in bytes. This function allows duplicated key in one object. - @warning The key/value string are not copied, you should keep these strings + @warning The key/value strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_strn(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3660,7 +3682,7 @@ yyjson_api_inline bool yyjson_mut_obj_add_strn(yyjson_mut_doc *doc, The value string is copied. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -3673,18 +3695,44 @@ yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, The `len` should be the length of the `val`, in bytes. This function allows duplicated key in one object. - @warning The key/value string are not copied, you should keep these strings + @warning The key/value strings are not copied, you should keep these strings unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_strncpy(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *key, const char *val, size_t len); +/** + Creates and adds a new array to the target object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep these strings + unmodified for the lifetime of this JSON document. + @return The new array, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_arr(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key); + +/** + Creates and adds a new object to the target object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep these strings + unmodified for the lifetime of this JSON document. + @return The new object, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_obj(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key); + /** Adds a JSON value at the end of the object. The `key` should be a null-terminated UTF-8 string. This function allows duplicated key in one object. - @warning The key string are not copied, you should keep the string + @warning The key string is not copied, you should keep the string unmodified for the lifetime of this JSON document. */ yyjson_api_inline bool yyjson_mut_obj_add_val(yyjson_mut_doc *doc, yyjson_mut_val *obj, @@ -6920,6 +6968,22 @@ yyjson_api_inline bool yyjson_mut_obj_add_strncpy(yyjson_mut_doc *doc, }); } +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_arr(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key) { + yyjson_mut_val *key = yyjson_mut_str(doc, _key); + yyjson_mut_val *val = yyjson_mut_arr(doc); + return yyjson_mut_obj_add(obj, key, val) ? val : NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_obj(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key) { + yyjson_mut_val *key = yyjson_mut_str(doc, _key); + yyjson_mut_val *val = yyjson_mut_obj(doc); + return yyjson_mut_obj_add(obj, key, val) ? val : NULL; +} + yyjson_api_inline bool yyjson_mut_obj_add_val(yyjson_mut_doc *doc, yyjson_mut_val *obj, const char *_key, diff --git a/src/common/commandoption.c b/src/common/commandoption.c index 510ac61a9e..e8a7e8001d 100644 --- a/src/common/commandoption.c +++ b/src/common/commandoption.c @@ -1,7 +1,26 @@ #include "commandoption.h" +#include "common/printing.h" +#include "common/time.h" +#include "fastfetch_datatext.h" +#include "modules/modules.h" #include "util/stringUtils.h" #include +#include + +static inline yyjson_mut_val* genJson(FFModuleBaseInfo* baseInfo) +{ + yyjson_mut_doc* doc = instance.state.resultDoc; + if (__builtin_expect(!doc, true)) return NULL; + + yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root); + yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name); + if (baseInfo->generateJson) + baseInfo->generateJson(baseInfo, doc, module); + else + yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format"); + return module; +} bool ffParseModuleCommand(const char* type) { @@ -12,7 +31,8 @@ bool ffParseModuleCommand(const char* type) FFModuleBaseInfo* baseInfo = *modules; if (ffStrEqualsIgnCase(type, baseInfo->name)) { - baseInfo->printModule(baseInfo); + if (!genJson(baseInfo)) + baseInfo->printModule(baseInfo); return true; } } @@ -30,3 +50,91 @@ bool ffParseModuleOptions(const char* key, const char* value) } return false; } + +void ffPrepareCommandOption(FFdata* data) +{ + //If we don't have a custom structure, use the default one + if(data->structure.length == 0) + ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); + + if(ffStrbufContainIgnCaseS(&data->structure, FF_CPUUSAGE_MODULE_NAME)) + ffPrepareCPUUsage(); + + if(ffStrbufContainIgnCaseS(&data->structure, FF_NETIO_MODULE_NAME)) + ffPrepareNetIO(&instance.config.netIo); + + if(instance.config.multithreading) + { + if(ffStrbufContainIgnCaseS(&data->structure, FF_PUBLICIP_MODULE_NAME)) + ffPreparePublicIp(&instance.config.publicIP); + + if(ffStrbufContainIgnCaseS(&data->structure, FF_WEATHER_MODULE_NAME)) + ffPrepareWeather(&instance.config.weather); + } +} + +static void parseStructureCommand(const char* line, FFlist* customValues) +{ + // handle `--set` and `--set-keyless` + FF_LIST_FOR_EACH(FFCustomValue, customValue, *customValues) + { + if (ffStrbufEqualS(&customValue->key, line)) + { + __attribute__((__cleanup__(ffDestroyCustomOptions))) FFCustomOptions options; + ffInitCustomOptions(&options); + if (customValue->printKey) + ffStrbufAppend(&options.moduleArgs.key, &customValue->key); + ffStrbufAppend(&options.moduleArgs.outputFormat, &customValue->value); + ffPrintCustom(&options); + return; + } + } + + if(!ffParseModuleCommand(line)) + ffPrintErrorString(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, ""); +} + +void ffPrintCommandOption(FFdata* data) +{ + yyjson_mut_doc* resultDoc = instance.state.resultDoc; + + //Parse the structure and call the modules + uint32_t startIndex = 0; + while (startIndex < data->structure.length) + { + uint32_t colonIndex = ffStrbufNextIndexC(&data->structure, startIndex, ':'); + data->structure.chars[colonIndex] = '\0'; + + uint64_t ms = 0; + if(instance.config.stat) + ms = ffTimeGetTick(); + + parseStructureCommand(data->structure.chars + startIndex, &data->customValues); + + if(instance.config.stat) + { + ms = ffTimeGetTick() - ms; + + if (resultDoc) + { + yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(resultDoc->root); + yyjson_mut_obj_add_uint(resultDoc, moduleJson, "stat", ms); + } + else + { + char str[32]; + int len = snprintf(str, sizeof str, "%" PRIu64 "ms", ms); + if(instance.config.pipe) + puts(str); + else + printf("\033[s\033[1A\033[9999999C\033[%dD%s\033[u", len, str); // Save; Up 1; Right 9999999; Left ; Print ; Load + } + } + + #if defined(_WIN32) + if (!instance.config.noBuffer) fflush(stdout); + #endif + + startIndex = colonIndex + 1; + } +} diff --git a/src/common/commandoption.h b/src/common/commandoption.h index 3ae2a83111..c938fe2a81 100644 --- a/src/common/commandoption.h +++ b/src/common/commandoption.h @@ -5,7 +5,24 @@ #include "fastfetch.h" +typedef struct FFCustomValue +{ + bool printKey; + FFstrbuf key; + FFstrbuf value; +} FFCustomValue; + +// Things only needed by fastfetch +typedef struct FFdata +{ + FFstrbuf structure; + FFlist customValues; // List of FFCustomValue + bool loadUserConfig; +} FFdata; + bool ffParseModuleCommand(const char* type); bool ffParseModuleOptions(const char* key, const char* value); +void ffPrepareCommandOption(FFdata* data); +void ffPrintCommandOption(FFdata* data); #endif diff --git a/src/common/format.c b/src/common/format.c index 4ed096dc1c..b5092c6c7f 100644 --- a/src/common/format.c +++ b/src/common/format.c @@ -12,6 +12,8 @@ void ffFormatAppendFormatArg(FFstrbuf* buffer, const FFformatarg* formatarg) ffStrbufAppendF(buffer, "%i", *(int*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT) ffStrbufAppendF(buffer, "%" PRIu32, *(uint32_t*)formatarg->value); + else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT64) + ffStrbufAppendF(buffer, "%" PRIu64, *(uint64_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT16) ffStrbufAppendF(buffer, "%" PRIu16, *(uint16_t*)formatarg->value); else if(formatarg->type == FF_FORMAT_ARG_TYPE_UINT8) @@ -254,7 +256,5 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n ffFormatAppendFormatArg(buffer, &arguments[index - 1]); } - ffStrbufTrimRight(buffer, ' '); - ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET); } diff --git a/src/common/format.h b/src/common/format.h index 57c8c680fe..3e7c751f23 100644 --- a/src/common/format.h +++ b/src/common/format.h @@ -7,6 +7,7 @@ typedef enum FFformatargtype { FF_FORMAT_ARG_TYPE_NULL = 0, FF_FORMAT_ARG_TYPE_UINT, + FF_FORMAT_ARG_TYPE_UINT64, FF_FORMAT_ARG_TYPE_UINT16, FF_FORMAT_ARG_TYPE_UINT8, FF_FORMAT_ARG_TYPE_INT, diff --git a/src/common/init.c b/src/common/init.c index dfe8b4d19d..35f1467fc4 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -27,6 +27,7 @@ static void initState(FFstate* state) ffPlatformInit(&state->platform); state->configDoc = NULL; + state->resultDoc = NULL; } static void defaultConfig(void) @@ -105,6 +106,7 @@ static void defaultConfig(void) ffInitMediaOptions(&instance.config.media); ffInitMemoryOptions(&instance.config.memory); ffInitMonitorOptions(&instance.config.monitor); + ffInitNetIOOptions(&instance.config.netIo); ffInitOSOptions(&instance.config.os); ffInitOpenCLOptions(&instance.config.openCL); ffInitOpenGLOptions(&instance.config.openGL); @@ -234,11 +236,11 @@ void ffStart(void) startDetectionThreads(); #endif - ffDisableLinewrap = instance.config.disableLinewrap && !instance.config.pipe; - ffHideCursor = instance.config.hideCursor && !instance.config.pipe; + ffDisableLinewrap = instance.config.disableLinewrap && !instance.config.pipe && !instance.state.resultDoc; + ffHideCursor = instance.config.hideCursor && !instance.config.pipe && !instance.state.resultDoc; #ifdef _WIN32 - if (!instance.config.noBuffer) setvbuf(stdout, NULL, _IOFBF, 4096); + setvbuf(stdout, NULL, _IOFBF, instance.config.noBuffer ? 0 : 4096); SetConsoleCtrlHandler(consoleHandler, TRUE); HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); DWORD mode = 0; @@ -254,7 +256,7 @@ void ffStart(void) #endif //reset everything to default before we start printing - if(!instance.config.pipe) + if(!instance.config.pipe && !instance.state.resultDoc) fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); if(ffHideCursor) @@ -318,6 +320,7 @@ static void destroyConfig(void) ffDestroyMediaOptions(&instance.config.media); ffDestroyMemoryOptions(&instance.config.memory); ffDestroyMonitorOptions(&instance.config.monitor); + ffDestroyNetIOOptions(&instance.config.netIo); ffDestroyOSOptions(&instance.config.os); ffDestroyOpenCLOptions(&instance.config.openCL); ffDestroyOpenGLOptions(&instance.config.openGL); @@ -375,6 +378,7 @@ static void destroyState(void) { ffPlatformDestroy(&instance.state.platform); yyjson_doc_free(instance.state.configDoc); + yyjson_mut_doc_free(instance.state.resultDoc); } void ffDestroyInstance(void) diff --git a/src/common/io/io.h b/src/common/io/io.h index 8cca6e1475..78c94ad9f8 100644 --- a/src/common/io/io.h +++ b/src/common/io/io.h @@ -12,6 +12,7 @@ typedef HANDLE FFNativeFD; #else #include + #include typedef int FFNativeFD; #endif @@ -84,6 +85,8 @@ typedef enum FFPathType bool ffPathExists(const char* path, FFPathType pathType); bool ffPathExpandEnv(const char* in, FFstrbuf* out); +#define FF_IO_TERM_RESP_WAIT_MS 100 // #554 + FF_C_SCANF(2, 3) const char* ffGetTerminalResponse(const char* request, const char* format, ...); @@ -130,4 +133,25 @@ static inline bool wrapFclose(FILE** pfile) } #define FF_AUTO_CLOSE_FILE __attribute__((__cleanup__(wrapFclose))) +#ifndef _WIN32 +static inline bool wrapClosedir(DIR** pdir) +{ + assert(pdir); + if (!*pdir) + return false; + closedir(*pdir); + return true; +} +#else +static inline bool wrapClosedir(HANDLE* pdir) +{ + assert(pdir); + if (!*pdir) + return false; + FindClose(*pdir); + return true; +} +#endif +#define FF_AUTO_CLOSE_DIR __attribute__((__cleanup__(wrapClosedir))) + #endif // FF_INCLUDED_common_io_io diff --git a/src/common/io/io_unix.c b/src/common/io/io_unix.c index 1941e6637a..db5f3c65b7 100644 --- a/src/common/io/io_unix.c +++ b/src/common/io/io_unix.c @@ -54,24 +54,20 @@ bool ffAppendFDBuffer(int fd, FFstrbuf* buffer) uint32_t free = ffStrbufGetFree(buffer); while( - (readed = read(fd, buffer->chars + buffer->length, free)) > 0 && - (uint32_t) readed == free + (readed = read(fd, buffer->chars + buffer->length, free)) > 0 ) { buffer->length += (uint32_t) readed; - ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte. + if((uint32_t) readed == free) + ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte. free = ffStrbufGetFree(buffer); } - // In case of failure, read returns -1. We don't want to substract the length of the buffer. - if(readed > 0) - buffer->length += (uint32_t) readed; - buffer->chars[buffer->length] = '\0'; ffStrbufTrimRight(buffer, '\n'); ffStrbufTrimRight(buffer, ' '); - return readed >= 0; + return buffer->length > 0; } ssize_t ffReadFileData(const char* fileName, size_t dataSize, void* data) @@ -150,7 +146,7 @@ const char* ffGetTerminalResponse(const char* request, const char* format, ...) fflush(stdout); //Give the terminal 35ms to respond - if(poll(&(struct pollfd) { .fd = STDIN_FILENO, .events = POLLIN }, 1, 35) <= 0) + if(poll(&(struct pollfd) { .fd = STDIN_FILENO, .events = POLLIN }, 1, FF_IO_TERM_RESP_WAIT_MS) <= 0) { tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm); return "poll() timeout or failed"; diff --git a/src/common/io/io_windows.c b/src/common/io/io_windows.c index 2cd916bd8d..c4c26145b4 100644 --- a/src/common/io/io_windows.c +++ b/src/common/io/io_windows.c @@ -44,16 +44,14 @@ bool ffAppendFDBuffer(HANDLE handle, FFstrbuf* buffer) bool success; while( (success = !!ReadFile(handle, buffer->chars + buffer->length, free, &readed, NULL)) && - (uint32_t) readed == free + readed > 0 ) { buffer->length += (uint32_t) readed; - ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte. + if((uint32_t) readed == free) + ffStrbufEnsureFree(buffer, buffer->allocated - 1); // Doubles capacity every round. -1 for the null byte. free = ffStrbufGetFree(buffer); } - if(readed > 0) - buffer->length += (uint32_t) readed; - buffer->chars[buffer->length] = '\0'; ffStrbufTrimRight(buffer, '\n'); @@ -173,7 +171,7 @@ const char* ffGetTerminalResponse(const char* request, const char* format, ...) while (true) { - if (WaitForSingleObjectEx(hInput, 35, TRUE) != WAIT_OBJECT_0) + if (WaitForSingleObjectEx(hInput, FF_IO_TERM_RESP_WAIT_MS, TRUE) != WAIT_OBJECT_0) { SetConsoleMode(hInput, prev_mode); return "WaitForSingleObject() failed or timeout"; diff --git a/src/common/jsonconfig.c b/src/common/jsonconfig.c index 71bc3cf8f8..6d8fee3291 100644 --- a/src/common/jsonconfig.c +++ b/src/common/jsonconfig.c @@ -70,6 +70,20 @@ const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair p return "Invalid enum value type; must be a string or integer"; } +static inline yyjson_mut_val* genJson(FFModuleBaseInfo* baseInfo) +{ + yyjson_mut_doc* doc = instance.state.resultDoc; + if (__builtin_expect(!doc, true)) return NULL; + + yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root); + yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name); + if (baseInfo->generateJson) + baseInfo->generateJson(baseInfo, doc, module); + else + yyjson_mut_obj_add_str(doc, module, "error", "Unsupported for JSON format"); + return module; +} + static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal) { if(!isalpha(type[0])) return false; @@ -80,7 +94,8 @@ static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal) if (ffStrEqualsIgnCase(type, baseInfo->name)) { if (jsonVal) baseInfo->parseJsonObject(baseInfo, jsonVal); - baseInfo->printModule(baseInfo); + if (!genJson(baseInfo)) + baseInfo->printModule(baseInfo); return true; } } @@ -97,6 +112,14 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) ffPrepareCPUUsage(); break; } + case 'n': case 'N': { + if (ffStrEqualsIgnCase(type, FF_NETIO_MODULE_NAME)) + { + if (module) ffParseNetIOJsonObject(&cfg->netIo, module); + ffPrepareNetIO(&cfg->netIo); + } + break; + } case 'p': case 'P': { if (ffStrEqualsIgnCase(type, FF_PUBLICIP_MODULE_NAME)) { @@ -128,6 +151,8 @@ static const char* printJsonConfig(bool prepare) if (!modules) return NULL; if (!yyjson_is_arr(modules)) return "Property 'modules' must be an array of strings or objects"; + yyjson_mut_doc* resultDoc = instance.state.resultDoc; + yyjson_val* item; size_t idx, max; yyjson_arr_foreach(modules, idx, max, item) @@ -157,16 +182,25 @@ static const char* printJsonConfig(bool prepare) if(!prepare && instance.config.stat) { - char str[32]; - int len = snprintf(str, sizeof str, "%" PRIu64 "ms", ffTimeGetTick() - ms); - if(instance.config.pipe) - puts(str); + ms = ffTimeGetTick() - ms; + if (resultDoc) + { + yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(resultDoc->root); + yyjson_mut_obj_add_uint(resultDoc, moduleJson, "stat", ms); + } else - printf("\033[s\033[1A\033[9999999C\033[%dD%s\033[u", len, str); // Save; Up 1; Right 9999999; Left ; Print ; Load + { + char str[32]; + int len = snprintf(str, sizeof str, "%" PRIu64 "ms", ms); + if(instance.config.pipe) + puts(str); + else + printf("\033[s\033[1A\033[9999999C\033[%dD%s\033[u", len, str); // Save; Up 1; Right 9999999; Left ; Print ; Load + } } #if defined(_WIN32) - if (!instance.config.noBuffer) fflush(stdout); + if (!instance.config.noBuffer && !resultDoc) fflush(stdout); #endif } diff --git a/src/common/jsonconfig.h b/src/common/jsonconfig.h index e701cae864..afb0fd8657 100644 --- a/src/common/jsonconfig.h +++ b/src/common/jsonconfig.h @@ -8,3 +8,16 @@ void ffPrintJsonConfig(bool prepare); const char* ffParseGeneralJsonConfig(void); const char* ffParseDisplayJsonConfig(void); const char* ffParseLibraryJsonConfig(void); + +yyjson_api_inline bool yyjson_mut_obj_add_strbuf(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + const FFstrbuf* buf) { + return yyjson_mut_obj_add_strncpy(doc, obj, _key, buf->chars, buf->length); +} + +yyjson_api_inline bool yyjson_mut_arr_add_strbuf(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const FFstrbuf* buf) { + return yyjson_mut_arr_add_strncpy(doc, obj, buf->chars, buf->length); +} diff --git a/src/common/modules.c b/src/common/modules.c index 1c4cd108aa..420903c8ee 100644 --- a/src/common/modules.c +++ b/src/common/modules.c @@ -82,6 +82,7 @@ static FFModuleBaseInfo* M[] = { }; static FFModuleBaseInfo* N[] = { + (void*) &instance.config.netIo, NULL, }; diff --git a/src/common/netif/netif.c b/src/common/netif/netif.c new file mode 100644 index 0000000000..ec317454b8 --- /dev/null +++ b/src/common/netif/netif.c @@ -0,0 +1,30 @@ +#include "netif.h" + +#ifndef _WIN32 + #include +#else + #define IF_NAMESIZE 16 +#endif + +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex); +enum { IF_INDEX_UNINITIALIZED = (uint32_t) -1, IF_INDEX_INVALID = (uint32_t) -2 }; +static uint32_t ifIndex = IF_INDEX_UNINITIALIZED; +static char ifName[IF_NAMESIZE + 1]; + +static inline void init() +{ + if (ifIndex == (uint32_t) IF_INDEX_UNINITIALIZED && !ffNetifGetDefaultRouteImpl(ifName, &ifIndex)) + ifIndex = (uint32_t) IF_INDEX_INVALID; +} + +const char* ffNetifGetDefaultRouteIfName() +{ + init(); + return ifName; +} + +uint32_t ffNetifGetDefaultRouteIfIndex() +{ + init(); + return ifIndex; +} diff --git a/src/common/netif/netif.h b/src/common/netif/netif.h new file mode 100644 index 0000000000..23ca5ef607 --- /dev/null +++ b/src/common/netif/netif.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#ifndef _WIN32 +const char* ffNetifGetDefaultRouteIfName(); +#endif + +uint32_t ffNetifGetDefaultRouteIfIndex(); diff --git a/src/common/netif/netif_bsd.c b/src/common/netif/netif_bsd.c new file mode 100644 index 0000000000..f288d7d356 --- /dev/null +++ b/src/common/netif/netif_bsd.c @@ -0,0 +1,98 @@ +#include "netif.h" + +#include "common/io/io.h" + +#include +#include +#include +#include +#include + +#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n)) + +#if defined(__APPLE__) +# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) +#elif defined(__NetBSD__) +# define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t)) +#elif defined(__FreeBSD__) +# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) +#elif defined(__OpenBSD__) +# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) +#else +# error unknown platform +#endif + +static struct sockaddr * +get_rt_address(struct rt_msghdr *rtm, int desired) +{ + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + + for (int i = 0; i < RTAX_MAX; i++) + { + if (rtm->rtm_addrs & (1 << i)) + { + if ((1 <sa_len) + (char *)sa); + } + } + return NULL; +} + +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) +{ + //https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104 + + FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET); + if (pfRoute < 0) + return false; + + { + struct timeval timeout = {1, 0}; + setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); + setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); + } + + int pid = getpid(); + + struct { + struct rt_msghdr hdr; + struct sockaddr_in dst; + uint8_t data[512]; + } rtmsg = { + .hdr = { + .rtm_type = RTM_GET, + .rtm_flags = RTF_UP | RTF_GATEWAY, + .rtm_version = RTM_VERSION, + .rtm_addrs = RTA_DST | RTA_IFP, + .rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst), + .rtm_pid = pid, + .rtm_seq = 1, + }, + .dst = { + .sin_family = AF_INET, + .sin_len = sizeof(rtmsg.dst), + }, + }; + + if (write(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen) != rtmsg.hdr.rtm_msglen) + return false; + + while (read(pfRoute, &rtmsg, sizeof(rtmsg)) > 0) + { + if (rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid) + { + struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP); + if (sdl) + { + assert(sdl->sdl_nlen <= IF_NAMESIZE); + memcpy(iface, sdl->sdl_data, sdl->sdl_nlen); + iface[sdl->sdl_nlen] = '\0'; + *ifIndex = sdl->sdl_index; + return true; + } + return false; + } + } + return false; +} diff --git a/src/common/netif/netif_linux.c b/src/common/netif/netif_linux.c new file mode 100644 index 0000000000..f645b0a0b7 --- /dev/null +++ b/src/common/netif/netif_linux.c @@ -0,0 +1,28 @@ +#include "netif.h" +#include "common/io/io.h" + +#include +#include + +#define FF_STR_INDIR(x) #x +#define FF_STR(x) FF_STR_INDIR(x) + +bool ffNetifGetDefaultRouteImpl(char iface[IF_NAMESIZE + 1], uint32_t* ifIndex) +{ + FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/net/route", "r"); + if (!netRoute) return false; + + // skip first line + flockfile(netRoute); + while (getc_unlocked(netRoute) != '\n'); + funlockfile(netRoute); + unsigned long long destination; //, gateway, flags, refCount, use, metric, mask, mtu, + + while (fscanf(netRoute, "%" FF_STR(IF_NAMESIZE) "s%llx%*[^\n]", iface, &destination) == 2) + { + if (destination != 0) continue; + *ifIndex = if_nametoindex(iface); + return true; + } + return false; +} diff --git a/src/common/netif/netif_windows.c b/src/common/netif/netif_windows.c new file mode 100644 index 0000000000..8607ed4866 --- /dev/null +++ b/src/common/netif/netif_windows.c @@ -0,0 +1,27 @@ +#include "netif.h" +#include "util/mallocHelper.h" + +#include + +bool ffNetifGetDefaultRouteImpl(FF_MAYBE_UNUSED char* iface/* unsupported */, uint32_t* ifIndex) +{ + ULONG size = 0; + if (GetIpForwardTable(NULL, &size, TRUE) != ERROR_INSUFFICIENT_BUFFER) + return false; + + FF_AUTO_FREE MIB_IPFORWARDTABLE* pIpForwardTable = (MIB_IPFORWARDTABLE*) malloc(size); + if (GetIpForwardTable(pIpForwardTable, &size, TRUE) != ERROR_SUCCESS) + return false; + + for (uint32_t i = 0; i < pIpForwardTable->dwNumEntries; ++i) + { + MIB_IPFORWARDROW* ipForwardRow = &pIpForwardTable->table[i]; + if (ipForwardRow->dwForwardDest == 0 && ipForwardRow->dwForwardMask == 0) + { + *ifIndex = ipForwardRow->dwForwardIfIndex; + break; + } + } + + return true; +} diff --git a/src/common/networking_linux.c b/src/common/networking_linux.c index 7fb6fade55..37c796276f 100644 --- a/src/common/networking_linux.c +++ b/src/common/networking_linux.c @@ -77,7 +77,8 @@ bool ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* host, con bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, uint32_t timeout) { #ifdef FF_HAVE_THREADS - ffThreadJoin(state->thread); + if (!ffThreadJoin(state->thread, timeout)) + return false; #endif if(state->sockfd == -1) return false; @@ -85,8 +86,8 @@ bool ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buffer, ui if(timeout > 0) { struct timeval timev; - timev.tv_sec = 0; - timev.tv_usec = (__typeof__(timev.tv_usec)) (timeout * 1000); //milliseconds to microseconds + timev.tv_sec = timeout / 1000; + timev.tv_usec = (__typeof__(timev.tv_usec)) ((timeout % 1000) * 1000); //milliseconds to microseconds setsockopt(state->sockfd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)); } diff --git a/src/common/option.h b/src/common/option.h index 342b48083e..3fef4995a0 100644 --- a/src/common/option.h +++ b/src/common/option.h @@ -3,14 +3,21 @@ #include "util/FFstrbuf.h" struct yyjson_val; +struct yyjson_mut_doc; +struct yyjson_mut_val; // Must be the first field of FFModuleOptions typedef struct FFModuleBaseInfo { const char* name; + // A dirty polymorphic implementation in C. + // This is UB, because `void*` is not compatible with `FF*Options*`. + // However we can't do it better unless we move to C++, so that `option` becomes a `this` pointer + // https://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type bool (*parseCommandOptions)(void* options, const char* key, const char* value); void (*parseJsonObject)(void* options, struct yyjson_val *module); void (*printModule)(void* options); + void (*generateJson)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* module); } FFModuleBaseInfo; static inline void ffOptionInitModuleBaseInfo( @@ -18,13 +25,15 @@ static inline void ffOptionInitModuleBaseInfo( const char* name, void* parseCommandOptions, // bool (*const parseCommandOptions)(void* options, const char* key, const char* value) void* parseJsonObject, // void (*const parseJsonObject)(void* options, yyjson_val *module) - void* printModule // void (*const printModule)(void* options) + void* printModule, // void (*const printModule)(void* options) + void* generateJson // void (*const generateJson)(void* options, yyjson_mut_doc* doc, yyjson_mut_val* obj) ) { baseInfo->name = name; baseInfo->parseCommandOptions = (__typeof__(baseInfo->parseCommandOptions)) parseCommandOptions; baseInfo->parseJsonObject = (__typeof__(baseInfo->parseJsonObject)) parseJsonObject; baseInfo->printModule = (__typeof__(baseInfo->printModule)) printModule; + baseInfo->generateJson = (__typeof__(baseInfo->generateJson)) generateJson; } typedef struct FFModuleArgs diff --git a/src/common/printing.c b/src/common/printing.c index 063127081c..0d8635f2f0 100644 --- a/src/common/printing.c +++ b/src/common/printing.c @@ -10,6 +10,10 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu if(moduleName == NULL) return; + //This is used as a magic value for hiding keys + if(moduleArgs && ffStrbufEqualS(&moduleArgs->key, " ")) + return; + if(!instance.config.pipe) { fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); @@ -112,9 +116,7 @@ void ffPrintColor(const FFstrbuf* colorValue) if(colorValue->length == 0) return; - fputs("\033[", stdout); - ffStrbufWriteTo(colorValue, stdout); - fputc('m', stdout); + printf("\e[%sm", colorValue->chars); } void ffPrintCharTimes(char c, uint32_t times) diff --git a/src/common/processing_linux.c b/src/common/processing_linux.c index 815ed1fc88..84bc79cece 100644 --- a/src/common/processing_linux.c +++ b/src/common/processing_linux.c @@ -38,6 +38,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use close(pipes[0]); close(pipes[1]); close(useStdErr ? STDOUT_FILENO : STDERR_FILENO); + setenv("LANG", "C", 1); execvp(argv[0], argv); exit(901); } diff --git a/src/common/thread.h b/src/common/thread.h index 0b8989c4e3..55b4c5be70 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -10,6 +10,7 @@ #include #include #include + #include #define FF_THREAD_MUTEX_INITIALIZER SRWLOCK_INIT typedef SRWLOCK FFThreadMutex; typedef HANDLE FFThreadType; @@ -21,9 +22,23 @@ #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static __stdcall unsigned fn ## ThreadMain (void* data) { fn((paramType)data); return 0; } #define FF_THREAD_ENTRY_DECL_WRAPPER_NOPARAM(fn) static __stdcall unsigned fn ## ThreadMain () { fn(); return 0; } static inline void ffThreadDetach(FFThreadType thread) { CloseHandle(thread); } - static inline void ffThreadJoin(FFThreadType thread) { WaitForSingleObject(thread, 0xffffffff /*INFINITE*/); } + static inline bool ffThreadJoin(FFThreadType thread, uint32_t timeout) + { + if (WaitForSingleObject(thread, timeout == 0 ? (DWORD) -1 : timeout) != 0 /*WAIT_OBJECT_0*/) + { + TerminateThread(thread, (DWORD) -1); + CloseHandle(thread); + return false; + } + CloseHandle(thread); + return true; + } #else #include + #include + #if __has_include() + #include + #endif #define FF_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER typedef pthread_mutex_t FFThreadMutex; typedef pthread_t FFThreadType; @@ -37,7 +52,28 @@ #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) static void* fn ## ThreadMain (void* data) { fn((paramType)data); return NULL; } #define FF_THREAD_ENTRY_DECL_WRAPPER_NOPARAM(fn) static void* fn ## ThreadMain () { fn(); return NULL; } static inline void ffThreadDetach(FFThreadType thread) { pthread_detach(thread); } - static inline void ffThreadJoin(FFThreadType thread) { pthread_join(thread, NULL); } + static inline bool ffThreadJoin(FFThreadType thread, FF_MAYBE_UNUSED uint32_t timeout) + { + #if (defined(__linux__) && !defined(__ANDROID__)) || __has_include() + if (timeout > 0) + { + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) + { + ts.tv_sec += ts.tv_sec / 1000; + ts.tv_nsec += (ts.tv_nsec % 1000) * 1000000; + if (pthread_timedjoin_np(thread, NULL, &ts) != 0) + { + pthread_kill(thread, SIGTERM); + return false; + } + return true; + } + } + #endif + pthread_join(thread, NULL); + return true; + } #endif #else //FF_HAVE_THREADS #define FF_THREAD_MUTEX_INITIALIZER 0 diff --git a/src/common/time.h b/src/common/time.h index 797461cbd1..8da2494489 100644 --- a/src/common/time.h +++ b/src/common/time.h @@ -7,6 +7,7 @@ #ifdef _WIN32 #include #include + #include #else #include #endif @@ -26,6 +27,19 @@ static inline uint64_t ffTimeGetTick() //In msec #endif } +static inline uint64_t ffTimeGetNow() +{ + #ifdef _WIN32 + uint64_t timeNow; + GetSystemTimeAsFileTime((FILETIME*) &timeNow); + return (timeNow - 116444736000000000ull) / 10000ull; + #else + struct timespec timeNow; + clock_gettime(CLOCK_REALTIME, &timeNow); + return (uint64_t)((timeNow.tv_sec * 1000) + (timeNow.tv_nsec / 1000000)); + #endif +} + static inline void ffTimeSleep(uint32_t msec) { #ifdef _WIN32 diff --git a/src/data/config_user.txt b/src/data/config_user.txt index 7bd07a49e0..c8a257ec53 100644 --- a/src/data/config_user.txt +++ b/src/data/config_user.txt @@ -360,7 +360,7 @@ #--terminal-key Terminal #--terminalfont-key Terminal Font #--cpu-key CPU -#--cpu-usage-key CPU Usage +#--cpuusage-key CPU Usage #--gpu-key GPU {1} #--memory-key Memory #--swap-key Swap @@ -370,6 +370,7 @@ #--lm-key LM #--locale-key Locale #--localip-key Local IP ({1}) +#--netio-key Network IO ({1}) #--publicip-key Public IP #--wifi-key Wifi #--weather-key Weather @@ -391,6 +392,7 @@ # For information on format strings, see "fastfetch --help format". # To see the parameter they take and their default value, see "fastfetch --help *-format", e.g. "fastfetch --help os-format". # An empty format string (As they are currently below) will behave as if it was not set. +#--title-format #--os-format #--host-format #--chassis-format @@ -411,7 +413,7 @@ #--terminal-format #--terminalfont-format #--cpu-format -#--cpu-usage-format +#--cpuusage-format #--gpu-format #--memory-format #--swap-format @@ -421,6 +423,7 @@ #--lm-format #--locale-format #--localip-format +#--netio-format #--publicip-format #--weather-format #--monitor-format @@ -439,6 +442,7 @@ # Key color options: # Overrides the global `--color-keys` for one specified module +#--title-key-color #--os-key-color #--host-key-color #--chassis-key-color @@ -459,7 +463,7 @@ #--terminal-key-color #--terminalfont-key-color #--cpu-key-color -#--cpu-usage-key-color +#--cpuusage-key-color #--gpu-key-color #--memory-key-color #--swap-key-color @@ -469,6 +473,7 @@ #--lm-key-color #--locale-key-color #--localip-key-color +#--netio-key-color #--publicip-key-color #--weather-key-color #--monitor-key-color diff --git a/src/data/help.txt b/src/data/help.txt index eb975f7865..94ba7f6175 100644 --- a/src/data/help.txt +++ b/src/data/help.txt @@ -147,9 +147,11 @@ Module specific options: --localip-show-ipv6 : Show IPv6 addresses in local ip module. Default is false --localip-show-mac : Show mac addresses in local ip module. Default is false --localip-show-loop : Show loop back addresses (127.0.0.1) in local ip module. Default is false - --localip-name-prefix : Show IPs with given name prefix only. Default is empty - --localip-default-route-only : Show ips that are used for default routing only. Default is false + --localip-name-prefix : Show interfaces with given interface name prefix only. Default is empty + --localip-default-route-only : Show the interface that is used for default routing only. Default is false --localip-compact : Show all IPs in one line. Default is false + --netio-name-prefix : Show interfaces with given name prefix only. Default is empty + --netio-default-route-only : Show the interfac that is used for default routing only. Default is false --publicip-timeout: Time in milliseconds to wait for the public ip server to respond. Default is disabled (0) --publicip-url: The URL of public IP detection server to be used. --weather-location: Set the location to be used. It must be URI encoded (eg a whitespace must be encoded as `+`). diff --git a/src/detection/battery/battery.h b/src/detection/battery/battery.h index 5667af76cf..7df622ea2e 100644 --- a/src/detection/battery/battery.h +++ b/src/detection/battery/battery.h @@ -7,7 +7,7 @@ #define FF_BATTERY_TEMP_UNSET (0/0.0) -typedef struct BatteryResult +typedef struct FFBatteryResult { FFstrbuf manufacturer; FFstrbuf modelName; @@ -15,7 +15,7 @@ typedef struct BatteryResult double capacity; FFstrbuf status; double temperature; -} BatteryResult; +} FFBatteryResult; const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results); diff --git a/src/detection/battery/battery_android.c b/src/detection/battery/battery_android.c index b05cce111d..16dce83d0c 100644 --- a/src/detection/battery/battery_android.c +++ b/src/detection/battery/battery_android.c @@ -33,7 +33,7 @@ static const char* parseTermuxApi(FFBatteryOptions* options, FFlist* results) if (!yyjson_is_obj(root)) return "Battery info result is not a JSON object"; - BatteryResult* battery = ffListAdd(results); + FFBatteryResult* battery = ffListAdd(results); battery->temperature = FF_BATTERY_TEMP_UNSET; ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->modelName); @@ -68,7 +68,7 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results) return NULL; ffStrbufClear(&temp); - BatteryResult* battery = ffListAdd(results); + FFBatteryResult* battery = ffListAdd(results); battery->temperature = FF_BATTERY_TEMP_UNSET; ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->modelName); @@ -78,7 +78,7 @@ static const char* parseDumpsys(FFBatteryOptions* options, FFlist* results) if (ffParsePropLines(start, "AC powered: ", &temp) && ffStrbufEqualS(&temp, "true")) ffStrbufAppendS(&battery->status, "AC powered"); ffStrbufClear(&temp); - + if (ffParsePropLines(start, "USB powered: ", &temp) && ffStrbufEqualS(&temp, "true")) { if (battery->status.length) ffStrbufAppendS(&battery->status, ", "); diff --git a/src/detection/battery/battery_apple.c b/src/detection/battery/battery_apple.c index dc1281ddab..52f1797803 100644 --- a/src/detection/battery/battery_apple.c +++ b/src/detection/battery/battery_apple.c @@ -38,7 +38,7 @@ const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) bool boolValue; const char* error; - BatteryResult* battery = ffListAdd(results); + FFBatteryResult* battery = ffListAdd(results); battery->capacity = 0.0/0.0; int currentCapacity, maxCapacity; diff --git a/src/detection/battery/battery_bsd.c b/src/detection/battery/battery_bsd.c index 287f004a13..8675ab0995 100644 --- a/src/detection/battery/battery_bsd.c +++ b/src/detection/battery/battery_bsd.c @@ -32,7 +32,7 @@ const char* ffDetectBattery(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* r if(ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &battio) < 0 || (battio.battinfo.state == ACPI_BATT_STAT_NOT_PRESENT)) continue; - BatteryResult* battery = ffListAdd(results); + FFBatteryResult* battery = ffListAdd(results); battery->temperature = FF_BATTERY_TEMP_UNSET; ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->modelName); diff --git a/src/detection/battery/battery_linux.c b/src/detection/battery/battery_linux.c index 41d49db625..a0bbf85f63 100644 --- a/src/detection/battery/battery_linux.c +++ b/src/detection/battery/battery_linux.c @@ -27,7 +27,7 @@ static void parseBattery(FFstrbuf* dir, FFlist* results) if(ffStrbufIgnCaseCompS(&testBatteryBuffer, "Device") == 0) return; - BatteryResult* result = ffListAdd(results); + FFBatteryResult* result = ffListAdd(results); //capacity must exist and be not empty ffStrbufAppendS(dir, "/capacity"); diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c index 66df36cf38..056bfd06ab 100644 --- a/src/detection/battery/battery_windows.c +++ b/src/detection/battery/battery_windows.c @@ -74,7 +74,7 @@ const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) if(!(bi.Capabilities & BATTERY_SYSTEM_BATTERY)) continue; - BatteryResult* battery = (BatteryResult*)ffListAdd(results); + FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results); if(memcmp(bi.Chemistry, "PbAc", 4) == 0) ffStrbufInitS(&battery->technology, "Lead Acid"); @@ -143,7 +143,7 @@ const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) SYSTEM_BATTERY_STATE info; if (NT_SUCCESS(NtPowerInformation(SystemBatteryState, NULL, 0, &info, sizeof(info))) && info.BatteryPresent) { - BatteryResult* battery = (BatteryResult*)ffListAdd(results); + FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results); ffStrbufInit(&battery->modelName); ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->technology); diff --git a/src/detection/bluetooth/bluetooth.h b/src/detection/bluetooth/bluetooth.h index 4aecdfc6f1..a9c07b161b 100644 --- a/src/detection/bluetooth/bluetooth.h +++ b/src/detection/bluetooth/bluetooth.h @@ -5,15 +5,15 @@ #include "fastfetch.h" -typedef struct FFBluetoothDevice +typedef struct FFBluetoothResult { FFstrbuf name; FFstrbuf address; FFstrbuf type; uint8_t battery; // 0-100% bool connected; -} FFBluetoothDevice; +} FFBluetoothResult; -const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */); +const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */); #endif diff --git a/src/detection/bluetooth/bluetooth_apple.m b/src/detection/bluetooth/bluetooth_apple.m index d6f3e98784..abd31a0c24 100644 --- a/src/detection/bluetooth/bluetooth_apple.m +++ b/src/detection/bluetooth/bluetooth_apple.m @@ -2,7 +2,7 @@ #import -const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */) +const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */) { NSArray* ioDevices = IOBluetoothDevice.pairedDevices; if(!ioDevices) @@ -10,7 +10,7 @@ for(IOBluetoothDevice* ioDevice in ioDevices) { - FFBluetoothDevice* device = ffListAdd(devices); + FFBluetoothResult* device = ffListAdd(devices); ffStrbufInitS(&device->name, ioDevice.name.UTF8String); ffStrbufInitS(&device->address, ioDevice.addressString.UTF8String); ffStrbufInit(&device->type); diff --git a/src/detection/bluetooth/bluetooth_linux.c b/src/detection/bluetooth/bluetooth_linux.c index 79f61d5fbd..1ed712c90a 100644 --- a/src/detection/bluetooth/bluetooth_linux.c +++ b/src/detection/bluetooth/bluetooth_linux.c @@ -44,7 +44,7 @@ array [ //root ] */ -static void detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothDevice* device) +static void detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothResult* device) { if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) return; @@ -72,7 +72,7 @@ static void detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBlue ffDBusGetBool(dbus, &dictIter, &device->connected); } -static void detectBluetoothProperty(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothDevice* device) +static void detectBluetoothProperty(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothResult* device) { if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) return; @@ -130,7 +130,7 @@ static void detectBluetoothObject(FFlist* devices, FFDBusData* dbus, DBusMessage DBusMessageIter arrayIter; dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter); - FFBluetoothDevice* device = ffListAdd(devices); + FFBluetoothResult* device = ffListAdd(devices); ffStrbufInit(&device->name); ffStrbufInit(&device->address); ffStrbufInit(&device->type); @@ -193,7 +193,7 @@ static const char* detectBluetooth(FFlist* devices) #endif -const char* ffDetectBluetooth(FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothDevice */) +const char* ffDetectBluetooth(FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothResult */) { #ifdef FF_HAVE_DBUS return detectBluetooth(devices); diff --git a/src/detection/bluetooth/bluetooth_nosupport.c b/src/detection/bluetooth/bluetooth_nosupport.c index 4ae74ac3e8..8db5a465cb 100644 --- a/src/detection/bluetooth/bluetooth_nosupport.c +++ b/src/detection/bluetooth/bluetooth_nosupport.c @@ -1,6 +1,6 @@ #include "bluetooth.h" -const char* ffDetectBluetooth(FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothDevice */) +const char* ffDetectBluetooth(FF_MAYBE_UNUSED FFlist* devices /* FFBluetoothResult */) { return "Not supported on this platform"; } diff --git a/src/detection/bluetooth/bluetooth_windows.c b/src/detection/bluetooth/bluetooth_windows.c index 0066cebb3a..bcd9da33eb 100644 --- a/src/detection/bluetooth/bluetooth_windows.c +++ b/src/detection/bluetooth/bluetooth_windows.c @@ -7,7 +7,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpointer-sign" -const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */) +const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */) { BLUETOOTH_DEVICE_SEARCH_PARAMS btsp = { .fReturnConnected = TRUE, @@ -25,7 +25,7 @@ const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */) return "BluetoothFindFirstDevice() failed or no devices found"; do { - FFBluetoothDevice* device = ffListAdd(devices); + FFBluetoothResult* device = ffListAdd(devices); ffStrbufInit(&device->name); ffStrbufInit(&device->address); ffStrbufInit(&device->type); @@ -33,9 +33,14 @@ const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothDevice */) device->connected = !!btdi.fConnected; ffStrbufSetWS(&device->name, btdi.szName); - for (uint32_t i = 0; i < sizeof(btdi.Address.rgBytes) / sizeof(btdi.Address.rgBytes); ++i) - ffStrbufAppendF(&device->address, "%X:", btdi.Address.rgBytes[i]); - ffStrbufTrimRight(&device->name, ':'); + + ffStrbufAppendF(&device->address, "%02x:%02x:%02x:%02x:%02x:%02x", + btdi.Address.rgBytes[0], + btdi.Address.rgBytes[1], + btdi.Address.rgBytes[2], + btdi.Address.rgBytes[3], + btdi.Address.rgBytes[4], + btdi.Address.rgBytes[5]); //https://btprodspecificationrefs.blob.core.windows.net/assigned-numbers/Assigned%20Number%20Types/Assigned%20Numbers.pdf if(BitTest(&btdi.ulClassofDevice, 13)) diff --git a/src/detection/brightness/brightness.h b/src/detection/brightness/brightness.h index d65843947f..58aeed20a2 100644 --- a/src/detection/brightness/brightness.h +++ b/src/detection/brightness/brightness.h @@ -9,7 +9,7 @@ typedef struct FFBrightnessResult { FFstrbuf name; - float value; + double min, max, current; } FFBrightnessResult; const char* ffDetectBrightness(FFlist* result); // list of FFBrightnessResult diff --git a/src/detection/brightness/brightness_apple.c b/src/detection/brightness/brightness_apple.c index ecbe604787..a3b1d3c8f2 100644 --- a/src/detection/brightness/brightness_apple.c +++ b/src/detection/brightness/brightness_apple.c @@ -20,7 +20,9 @@ static const char* detectWithDisplayServices(const FFDisplayServerResult* displa if(DisplayServicesGetBrightness((CGDirectDisplayID) display->id, &value) == kCGErrorSuccess) { FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); - brightness->value = value * 100; + brightness->current = value; + brightness->max = 1; + brightness->min = 0; ffStrbufInitCopy(&brightness->name, &display->name); } } @@ -90,7 +92,9 @@ FF_MAYBE_UNUSED static const char* detectWithDdcci(FFlist* result) uint32_t max = ((uint32_t) i2cOut[6] << 8u) + (uint32_t) i2cOut[7]; FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); - brightness->value = (float) current * 100.f / max; + brightness->max = max; + brightness->min = 0; + brightness->current = current; ffStrbufInit(&brightness->name); uint8_t edid[128] = {}; diff --git a/src/detection/brightness/brightness_bsd.c b/src/detection/brightness/brightness_bsd.c index 8c6a89e2f8..72f1e54762 100644 --- a/src/detection/brightness/brightness_bsd.c +++ b/src/detection/brightness/brightness_bsd.c @@ -26,13 +26,16 @@ const char* ffDetectBrightness(FFlist* result) if(ioctl(blfd, BACKLIGHTGETSTATUS, &status) < 0) continue; - FFBrightnessResult* display = (FFBrightnessResult*) ffListAdd(result); - ffStrbufInit(&display->name); - display->value = (float) status.brightness / BACKLIGHTMAXLEVELS; + FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); + ffStrbufInit(&brightness->name); + + brightness->max = BACKLIGHTMAXLEVELS; + brightness->min = 0; + brightness->current = status.brightness; struct backlight_info info; if(ioctl(blfd, BACKLIGHTGETINFO, &info) < 0) - ffStrbufAppendS(&display->name, info.name); + ffStrbufAppendS(&brightness->name, info.name); } return NULL; } @@ -44,4 +47,4 @@ const char* ffDetectBrightness(FF_MAYBE_UNUSED FFlist* result) return "Backlight is supported only on FreeBSD 13 and newer"; } -#endif \ No newline at end of file +#endif diff --git a/src/detection/brightness/brightness_linux.c b/src/detection/brightness/brightness_linux.c index 8ab4e5f860..b3e75b2347 100644 --- a/src/detection/brightness/brightness_linux.c +++ b/src/detection/brightness/brightness_linux.c @@ -72,8 +72,9 @@ static const char* detectWithBacklight(FFlist* result) } else ffStrbufInitS(&brightness->name, entry->d_name); - double maxBrightness = ffStrbufToDouble(&buffer); - brightness->value = (float) (actualBrightness * 100 / maxBrightness); + brightness->max = ffStrbufToDouble(&buffer); + brightness->min = 0; + brightness->current = actualBrightness; } } ffStrbufSubstrBefore(&backlightDir, backlightDirLength); @@ -123,7 +124,9 @@ static const char* detectWithDdcci(FFlist* result) ffddca_free_any_vcp_value(vcpValue); FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); - brightness->value = (float) current * 100.f / (float) max; + brightness->max = max; + brightness->min = 0; + brightness->current = current; ffStrbufInitS(&brightness->name, display->model_name); } ffddca_close_display(handle); diff --git a/src/detection/brightness/brightness_windows.cpp b/src/detection/brightness/brightness_windows.cpp index 28f489b966..06c1690178 100644 --- a/src/detection/brightness/brightness_windows.cpp +++ b/src/detection/brightness/brightness_windows.cpp @@ -20,7 +20,9 @@ static const char* detectWithWmi(FFlist* result) if(FFWmiVariant vtValue = record.get(L"CurrentBrightness")) { FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result); - brightness->value = vtValue.get(); + brightness->max = 100; + brightness->min = 0; + brightness->current = vtValue.get(); ffStrbufInit(&brightness->name); if (FFWmiVariant vtName = record.get(L"InstanceName")) @@ -51,7 +53,9 @@ static char* detectWithDdcci(const FFDisplayServerResult* displayServer, FFlist* else ffStrbufInitWS(&brightness->name, physicalMonitor.szPhysicalMonitorDescription); - brightness->value = (float) (curr - min) * 100.f / (float) (max - min); + brightness->max = max; + brightness->min = min; + brightness->current = curr; } } } diff --git a/src/detection/chassis/chassis_linux.c b/src/detection/chassis/chassis_linux.c index deace35d91..074753eb64 100644 --- a/src/detection/chassis/chassis_linux.c +++ b/src/detection/chassis/chassis_linux.c @@ -25,7 +25,7 @@ const char* ffDetectChassis(FFChassisResult* result) if(result->type.length) { - const char* typeStr = ffChassisTypeToString(ffStrbufToUInt16(&result->type, 9999)); + const char* typeStr = ffChassisTypeToString((uint32_t) ffStrbufToUInt(&result->type, 9999)); if(typeStr) ffStrbufSetS(&result->type, typeStr); } diff --git a/src/detection/cpu/cpu_apple.c b/src/detection/cpu/cpu_apple.c index 9dcb07edd1..ded5e0d899 100644 --- a/src/detection/cpu/cpu_apple.c +++ b/src/detection/cpu/cpu_apple.c @@ -2,17 +2,6 @@ #include "common/sysctl.h" #include "detection/temps/temps_apple.h" -static double getFrequency(const char* propName) -{ - double herz = (double) ffSysctlGetInt64(propName, 0); - if(herz <= 0.0) - return herz; - - herz /= 1000.0; //to KHz - herz /= 1000.0; //to MHz - return herz / 1000.0; //to GHz -} - static double detectCpuTemp(const FFstrbuf* cpuName) { double result = 0; @@ -31,11 +20,85 @@ static double detectCpuTemp(const FFstrbuf* cpuName) return result; } +#ifdef __aarch64__ +#include "util/apple/cf_helpers.h" + +#include + +static const char* detectFrequency(FFCPUResult* cpu) +{ + // https://github.com/giampaolo/psutil/pull/2222/files + + CFMutableDictionaryRef matchDict = IOServiceMatching("AppleARMIODevice"); + if (matchDict == NULL) + return "IOServiceMatching(\"AppleARMIODevice\") failed"; + + io_iterator_t iterator; + if(IOServiceGetMatchingServices(MACH_PORT_NULL, matchDict, &iterator) != kIOReturnSuccess) + return "IOServiceGetMatchingServices() failed"; + + io_registry_entry_t registryEntry; + while((registryEntry = IOIteratorNext(iterator)) != 0) + { + CFMutableDictionaryRef properties; + if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) + { + IOObjectRelease(registryEntry); + continue; + } + + io_name_t name; + if (IORegistryEntryGetName(registryEntry, name) != KERN_SUCCESS) + continue; + if (strcmp(name, "pmgr") != 0) + continue; + + uint32_t pMin, eMin, aMax, pCoreLength; + ffCfDictGetData(properties, CFSTR("voltage-states5-sram"), 0, 4, (uint8_t*) &pMin, &pCoreLength); // pCore + ffCfDictGetData(properties, CFSTR("voltage-states1-sram"), 0, 4, (uint8_t*) &eMin, NULL); // eCore + cpu->frequencyMin = (pMin < eMin ? pMin : eMin) / (1000.0 * 1000 * 1000); + + if (pCoreLength >= 8) + { + ffCfDictGetData(properties, CFSTR("voltage-states5-sram"), pCoreLength - 8, 4, (uint8_t*) &aMax, NULL); + cpu->frequencyMax = aMax / (1000.0 * 1000 * 1000); + } + else + cpu->frequencyMax = 0.0; + + CFRelease(properties); + IOObjectRelease(registryEntry); + } + + IOObjectRelease(iterator); + return NULL; +} +#else +static const char* detectFrequency(FFCPUResult* cpu) +{ + cpu->frequencyMin = ffSysctlGetInt64("hw.cpufrequency_min", 0) / 1000.0 / 1000.0 / 1000.0; + cpu->frequencyMax = ffSysctlGetInt64("hw.cpufrequency_max", 0); + if(cpu->frequencyMax > 0.0) + cpu->frequencyMax /= 1000.0 * 1000.0 * 1000.0; + else + { + unsigned current = 0; + size_t size = sizeof(current); + if (sysctl((int[]){ CTL_HW, HW_CPU_FREQ }, 2, ¤t, &size, NULL, 0) == 0) + cpu->frequencyMax = (double) current / 1000.0 / 1000.0 / 1000.0; + } + return NULL; +} +#endif + const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { if (ffSysctlGetString("machdep.cpu.brand_string", &cpu->name) != NULL) return "sysctlbyname(machdep.cpu.brand_string) failed"; + ffSysctlGetString("machdep.cpu.vendor", &cpu->vendor); + if (cpu->vendor.length == 0 && ffStrbufStartsWithS(&cpu->name, "Apple ")) + ffStrbufAppendS(&cpu->vendor, "Apple"); cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu_max", 1); if(cpu->coresPhysical == 1) @@ -49,10 +112,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) if(cpu->coresOnline == 1) cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.activecpu", 1); - cpu->frequencyMin = getFrequency("hw.cpufrequency_min"); - cpu->frequencyMax = getFrequency("hw.cpufrequency_max"); - if(cpu->frequencyMax == 0.0) - cpu->frequencyMax = getFrequency("hw.cpufrequency"); + detectFrequency(cpu); cpu->temperature = options->temp ? detectCpuTemp(&cpu->name) : FF_CPU_TEMP_UNSET; diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index bdffc755ff..1241ef8891 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -1,5 +1,6 @@ #include "cpu.h" #include "common/io/io.h" +#include "common/processing.h" #include "common/properties.h" #include "detection/temps/temps_linux.h" #include "util/mallocHelper.h" @@ -128,7 +129,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) const char* error = parseCpuInfo(cpu, &physicalCoresBuffer, &cpuMHz, &cpuIsa, &cpuUarch); if (error) return error; - cpu->coresPhysical = ffStrbufToUInt16(&physicalCoresBuffer, 1); + cpu->coresPhysical = (uint16_t) ffStrbufToUInt(&physicalCoresBuffer, 1); cpu->coresLogical = (uint16_t) get_nprocs_conf(); cpu->coresOnline = (uint16_t) get_nprocs(); @@ -163,5 +164,17 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) detectAndroid(cpu); #endif + #ifdef __linux__ + if (cpu->name.length == 0) + { + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (!ffProcessAppendStdOut(&buffer, (char *const[]) { "lscpu", NULL })) + { + ffParsePropLines(buffer.chars, "Model name:", &cpu->name); + if (ffStrbufEqualS(&cpu->name, "-")) ffStrbufClear(&cpu->name); + } + } + #endif + return NULL; } diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index 231ea342e4..59f07ab7ad 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -35,7 +35,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) return "GetLogicalProcessorInformationEx(RelationAll, pProcessorInfo, &length) failed"; } - FF_HKEY_AUTO_DESTROY hKey; + FF_HKEY_AUTO_DESTROY hKey = NULL; if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &hKey, NULL)) return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\", &hKey, NULL) failed"; diff --git a/src/detection/datetime/datetime.c b/src/detection/datetime/datetime.c deleted file mode 100644 index 60cc1f6582..0000000000 --- a/src/detection/datetime/datetime.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "fastfetch.h" -#include "detection/datetime/datetime.h" -#include "common/thread.h" - -#include - -const FFDateTimeResult* ffDetectDateTime(void) -{ - static FFDateTimeResult result; - static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER; - static bool init = false; - - ffThreadMutexLock(&mutex); - if (init) - { - ffThreadMutexUnlock(&mutex); - return &result; - } - init = true; - - const time_t t = time(NULL); - struct tm* tm = localtime(&t); - - result.year = (uint16_t) (tm->tm_year + 1900); - result.yearShort = (uint8_t) (result.year % 100); - result.month = (uint8_t) (tm->tm_mon + 1); - - ffStrbufInitA(&result.monthPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.monthPretty.length = (uint32_t) strftime(result.monthPretty.chars, ffStrbufGetFree(&result.monthPretty), "%m", tm); - - ffStrbufInitA(&result.monthName, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.monthName.length = (uint32_t) strftime(result.monthName.chars, ffStrbufGetFree(&result.monthName), "%B", tm); - - ffStrbufInitA(&result.monthNameShort, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.monthNameShort.length = (uint32_t) strftime(result.monthNameShort.chars, ffStrbufGetFree(&result.monthNameShort), "%b", tm); - - result.week = (uint8_t) (tm->tm_yday / 7 + 1); - - ffStrbufInitA(&result.weekday, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.weekday.length = (uint32_t) strftime(result.weekday.chars, ffStrbufGetFree(&result.weekday), "%A", tm); - - ffStrbufInitA(&result.weekdayShort, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.weekdayShort.length = (uint32_t) strftime(result.weekdayShort.chars, ffStrbufGetFree(&result.weekdayShort), "%a", tm); - - result.dayInYear = (uint8_t) (tm->tm_yday + 1); - result.dayInMonth = (uint8_t) tm->tm_mday; - result.dayInWeek = tm->tm_wday == 0 ? 7 : (uint8_t) tm->tm_wday; - - result.hour = (uint8_t) tm->tm_hour; - - ffStrbufInitA(&result.hourPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.hourPretty.length = (uint32_t) strftime(result.hourPretty.chars, ffStrbufGetFree(&result.hourPretty), "%H", tm); - - result.hour12 = (uint8_t) (result.hour % 12); - - ffStrbufInitA(&result.hour12Pretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.hour12Pretty.length = (uint32_t) strftime(result.hour12Pretty.chars, ffStrbufGetFree(&result.hour12Pretty), "%I", tm); - - result.minute = (uint8_t) tm->tm_min; - - ffStrbufInitA(&result.minutePretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.minutePretty.length = (uint32_t) strftime(result.minutePretty.chars, ffStrbufGetFree(&result.minutePretty), "%M", tm); - - result.second = (uint8_t) tm->tm_sec; - - ffStrbufInitA(&result.secondPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); - result.secondPretty.length = (uint32_t) strftime(result.secondPretty.chars, ffStrbufGetFree(&result.secondPretty), "%S", tm); - - ffThreadMutexUnlock(&mutex); - return &result; -} diff --git a/src/detection/datetime/datetime.h b/src/detection/datetime/datetime.h deleted file mode 100644 index fd47ba03bd..0000000000 --- a/src/detection/datetime/datetime.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#ifndef FF_INCLUDED_detection_datetime_datetime -#define FF_INCLUDED_detection_datetime_datetime - -#include "fastfetch.h" - -typedef struct FFDateTimeResult -{ - //Examples for 21.02.2022 - 15:18:37 - uint16_t year; //2022 - uint8_t yearShort; //22 - uint8_t month; //2 - FFstrbuf monthPretty; //02 - FFstrbuf monthName; //February - FFstrbuf monthNameShort; //Feb - uint8_t week; //8 - FFstrbuf weekday; //Monday - FFstrbuf weekdayShort; //Mon - uint16_t dayInYear; //52 - uint8_t dayInMonth; //21 - uint8_t dayInWeek; //1 - uint8_t hour; //15 - FFstrbuf hourPretty; //15 - uint8_t hour12; //3 - FFstrbuf hour12Pretty; //03 - uint8_t minute; //18 - FFstrbuf minutePretty; //18 - uint8_t second; //37 - FFstrbuf secondPretty; //37 -} FFDateTimeResult; - -const FFDateTimeResult* ffDetectDateTime(); - -#endif diff --git a/src/detection/disk/disk.h b/src/detection/disk/disk.h index fced34a92f..06611f85c2 100644 --- a/src/detection/disk/disk.h +++ b/src/detection/disk/disk.h @@ -5,12 +5,21 @@ #include "fastfetch.h" +typedef enum FFDiskPhysicalType +{ + FF_DISK_PHYSICAL_TYPE_UNKNOWN, + FF_DISK_PHYSICAL_TYPE_HDD, + FF_DISK_PHYSICAL_TYPE_SSD, +} FFDiskPhysicalType; + typedef struct FFDisk { + FFstrbuf mountFrom; FFstrbuf mountpoint; FFstrbuf filesystem; FFstrbuf name; FFDiskVolumeType type; + FFDiskPhysicalType physicalType; uint64_t bytesUsed; uint64_t bytesFree; diff --git a/src/detection/disk/disk_apple.m b/src/detection/disk/disk_apple.m deleted file mode 100644 index 81bcdeb482..0000000000 --- a/src/detection/disk/disk_apple.m +++ /dev/null @@ -1,17 +0,0 @@ -#include "disk.h" - -#include -#import - -void detectFsInfo(struct statfs* fs, FFDisk* disk) -{ - // FreeBSD doesn't support these flags - if(fs->f_flags & MNT_DONTBROWSE) - disk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT; - else if(fs->f_flags & MNT_REMOVABLE) - disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; - else - disk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT; - - ffStrbufInitS(&disk->name, [NSFileManager.defaultManager displayNameAtPath:@(fs->f_mntonname)].UTF8String); -} diff --git a/src/detection/disk/disk_bsd.c b/src/detection/disk/disk_bsd.c index b6c656e8bd..55fc94a2e6 100644 --- a/src/detection/disk/disk_bsd.c +++ b/src/detection/disk/disk_bsd.c @@ -1,42 +1,68 @@ #include "disk.h" +#include "util/mallocHelper.h" +#include "util/stringUtils.h" #include #ifdef __FreeBSD__ -#include "util/stringUtils.h" - static void detectFsInfo(struct statfs* fs, FFDisk* disk) { if(ffStrbufEqualS(&disk->filesystem, "zfs")) { - disk->type = !ffStrStartsWith(fs->f_mntfromname, "zroot/") || ffStrStartsWith(fs->f_mntfromname, "zroot/ROOT/") + disk->type = !ffStrbufStartsWithS(&disk->mountFrom, "zroot/") || ffStrbufStartsWithS(&disk->mountFrom, "zroot/ROOT/") ? FF_DISK_VOLUME_TYPE_REGULAR_BIT : FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT; } - else if(!ffStrStartsWith(fs->f_mntfromname, "/dev/")) + else if(ffStrbufStartsWithS(&disk->mountpoint, "/boot") || ffStrbufStartsWithS(&disk->mountpoint, "/efi")) disk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT; else if(!(fs->f_flags & MNT_LOCAL)) disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; else disk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT; +} +#elif __APPLE__ +#include +#include - ffStrbufInit(&disk->name); +struct VolAttrBuf { + uint32_t length; + attrreference_t volNameRef; + char volNameSpace[MAXPATHLEN]; +} __attribute__((aligned(4), packed)); + +void detectFsInfo(struct statfs* fs, FFDisk* disk) +{ + if(fs->f_flags & MNT_DONTBROWSE) + disk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT; + else if(fs->f_flags & MNT_REMOVABLE || !(fs->f_flags & MNT_LOCAL)) + disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; + else + disk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT; + + struct VolAttrBuf attrBuf; + if (getattrlist(disk->mountpoint.chars, &(struct attrlist) { + .bitmapcount = ATTR_BIT_MAP_COUNT, + .volattr = ATTR_VOL_INFO | ATTR_VOL_NAME, + }, &attrBuf, sizeof(attrBuf), 0) == 0) + ffStrbufInitNS(&disk->name, attrBuf.volNameRef.attr_length - 1 /* excluding '\0' */, attrBuf.volNameSpace); } -#else -void detectFsInfo(struct statfs* fs, FFDisk* disk); #endif const char* ffDetectDisksImpl(FFlist* disks) { - struct statfs* buf; + int size = getfsstat(NULL, 0, MNT_WAIT); - int size = getmntinfo(&buf, MNT_WAIT); if(size <= 0) - return "getmntinfo(&buf, MNT_WAIT) failed"; + return "getfsstat(NULL, 0, MNT_WAIT) failed"; + + FF_AUTO_FREE struct statfs* buf = malloc(sizeof(*buf) * (unsigned) size); + if(getfsstat(buf, (int) (sizeof(*buf) * (unsigned) size), MNT_NOWAIT) <= 0) + return "getfsstat(buf, size, MNT_NOWAIT) failed"; for(struct statfs* fs = buf; fs < buf + size; ++fs) { - FFDisk* disk = ffListAdd(disks); + if(!ffStrStartsWith(fs->f_mntfromname, "/dev/") && !ffStrEquals(fs->f_fstypename, "zfs")) + continue; #ifdef __FreeBSD__ // f_bavail and f_ffree are signed on FreeBSD... @@ -44,6 +70,9 @@ const char* ffDetectDisksImpl(FFlist* disks) if(fs->f_ffree < 0) fs->f_ffree = 0; #endif + FFDisk* disk = ffListAdd(disks); + disk->physicalType = FF_DISK_PHYSICAL_TYPE_UNKNOWN; + disk->bytesTotal = fs->f_blocks * fs->f_bsize; disk->bytesFree = (uint64_t)fs->f_bfree * fs->f_bsize; disk->bytesAvailable = (uint64_t)fs->f_bavail * fs->f_bsize; @@ -52,8 +81,11 @@ const char* ffDetectDisksImpl(FFlist* disks) disk->filesTotal = (uint32_t) fs->f_files; disk->filesUsed = (uint32_t) (disk->filesTotal - (uint64_t)fs->f_ffree); + ffStrbufInitS(&disk->mountFrom, fs->f_mntfromname); ffStrbufInitS(&disk->mountpoint, fs->f_mntonname); ffStrbufInitS(&disk->filesystem, fs->f_fstypename); + ffStrbufInit(&disk->name); + disk->type = 0; detectFsInfo(fs, disk); if(fs->f_flags & MNT_RDONLY) diff --git a/src/detection/disk/disk_linux.c b/src/detection/disk/disk_linux.c index fb5597baa3..bc53eb7834 100644 --- a/src/detection/disk/disk_linux.c +++ b/src/detection/disk/disk_linux.c @@ -1,5 +1,6 @@ #include "disk.h" +#include "common/io/io.h" #include "util/stringUtils.h" #include @@ -7,6 +8,8 @@ #include #include #include +#include +#include #ifdef __USE_LARGEFILE64 #define stat stat64 @@ -15,77 +18,60 @@ #define readdir readdir64 #endif -static bool isPhysicalDevice(FFstrbuf* device) +static bool isPhysicalDevice(const struct mntent* device) { #ifndef __ANDROID__ //On Android, `/dev` is not accessable, so that the following checks always fail - //https://askubuntu.com/a/1289123 - if(ffStrbufEqualS(device, "/cow")) + //Always show the root path + if(ffStrEquals(device->mnt_dir, "/")) return true; //DrvFs is a filesystem plugin to WSL that was designed to support interop between WSL and the Windows filesystem. - if(ffStrbufEqualS(device, "drvfs")) + if(ffStrEquals(device->mnt_fsname, "drvfs")) return true; - //ZFS root pool. The format is rpool/// - if(ffStrbufStartsWithS(device, "rpool/")) + //ZFS pool + if(ffStrEquals(device->mnt_type, "zfs")) return true; + //Pseudo filesystems don't have a device in /dev + if(!ffStrStartsWith(device->mnt_fsname, "/dev/")) + return false; + + if( + ffStrStartsWith(device->mnt_fsname + 5, "loop") || //Ignore loop devices + ffStrStartsWith(device->mnt_fsname + 5, "ram") || //Ignore ram devices + ffStrStartsWith(device->mnt_fsname + 5, "fd") //Ignore fd devices + ) return false; + struct stat deviceStat; - if(stat(device->chars, &deviceStat) != 0) + if(stat(device->mnt_fsname, &deviceStat) != 0) return false; //Ignore all devices that are not block devices if(!S_ISBLK(deviceStat.st_mode)) return false; + #else + //Pseudo filesystems don't have a device in /dev - if(!ffStrbufStartsWithS(device, "/dev/")) + if(!ffStrStartsWith(device->mnt_fsname, "/dev/")) return false; - #endif // __ANDROID__ - if( - ffStrbufStartsWithS(device, "/dev/loop") || //Ignore loop devices - ffStrbufStartsWithS(device, "/dev/ram") || //Ignore ram devices - ffStrbufStartsWithS(device, "/dev/fd") //Ignore fd devices + ffStrStartsWith(device->mnt_fsname + 5, "loop") || //Ignore loop devices + ffStrStartsWith(device->mnt_fsname + 5, "ram") || //Ignore ram devices + ffStrStartsWith(device->mnt_fsname + 5, "fd") //Ignore fd devices ) return false; - return true; -} - -static void appendNextEntry(FFstrbuf* buffer, char** source) -{ - //Read the current entry into the buffer - while(**source != '\0' && !isspace(**source)) - { - //After a backslash the next 3 characters are octal ascii codes - if(**source == '\\' && strnlen(*source, 4) == 4) - { - char octal[4] = {0}; - strncpy(octal, *source + 1, 3); - - long value = strtol(octal, NULL, 8); //Returns 0 on error, so no need to check endptr - if(value > 0 && value < CHAR_MAX) - { - ffStrbufAppendC(buffer, (char) value); - *source += 4; - continue; - } - } - - ffStrbufAppendC(buffer, **source); - ++*source; - } + #endif // __ANDROID__ - //Skip whitespace - while(isspace(**source)) - ++*source; + return true; } static void detectNameFromPath(FFDisk* disk, const struct stat* deviceStat, FFstrbuf* basePath) { - DIR* dir = opendir(basePath->chars); + FF_AUTO_CLOSE_DIR DIR* dir = opendir(basePath->chars); if(dir == NULL) return; @@ -110,18 +96,24 @@ static void detectNameFromPath(FFDisk* disk, const struct stat* deviceStat, FFst ffStrbufAppendS(&disk->name, entry->d_name); break; } - - closedir(dir); } -static void detectName(FFDisk* disk, const FFstrbuf* device) +static void detectName(FFDisk* disk) { struct stat deviceStat; - if(stat(device->chars, &deviceStat) != 0) + if(stat(disk->mountFrom.chars, &deviceStat) != 0) return; FF_STRBUF_AUTO_DESTROY basePath = ffStrbufCreate(); + //Detect USB devices. Code is put here to reuse deviceStat + //https://stackoverflow.com/a/73302717 + ffStrbufAppendS(&basePath, "/dev/disk/by-id/"); + detectNameFromPath(disk, &deviceStat, &basePath); + if (ffStrbufStartsWithS(&disk->name, "usb-")) + disk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; + ffStrbufClear(&disk->name); + //Try label first ffStrbufSetS(&basePath, "/dev/disk/by-label/"); detectNameFromPath(disk, &deviceStat, &basePath); @@ -133,6 +125,8 @@ static void detectName(FFDisk* disk, const FFstrbuf* device) detectNameFromPath(disk, &deviceStat, &basePath); } + if (disk->name.length == 0) return; + // Basic\x20data\x20partition for (uint32_t i = ffStrbufFirstIndexS(&disk->name, "\\x"); i != disk->name.length; @@ -152,7 +146,7 @@ static void detectName(FFDisk* disk, const FFstrbuf* device) #ifdef __ANDROID__ -static void detectType(FF_MAYBE_UNUSED const FFlist* devices, FFDisk* currentDisk, FF_MAYBE_UNUSED const char* options) +static void detectType(FF_MAYBE_UNUSED const FFlist* disks, FFDisk* currentDisk) { if(ffStrbufEqualS(¤tDisk->mountpoint, "/") || ffStrbufEqualS(¤tDisk->mountpoint, "/storage/emulated")) currentDisk->type = FF_DISK_VOLUME_TYPE_REGULAR_BIT; @@ -164,36 +158,95 @@ static void detectType(FF_MAYBE_UNUSED const FFlist* devices, FFDisk* currentDis #else -static bool isSubvolume(const FFlist* devices) +static bool isSubvolume(const FFlist* disks, FFDisk* currentDisk) { - const FFstrbuf* current = ffListGet(devices, devices->length - 1); - - if(ffStrbufEqualS(current, "drvfs")) // WSL Windows drives + if(ffStrbufEqualS(¤tDisk->mountFrom, "drvfs")) // WSL Windows drives return false; - //Filter all disks which device was already found. This catches BTRFS subvolumes. - for(uint32_t i = 0; i < devices->length - 1; i++) + if(ffStrbufEqualS(¤tDisk->filesystem, "zfs")) { - const FFstrbuf* otherDevice = ffListGet(devices, i); + //ZFS subvolumes + uint32_t index = ffStrbufFirstIndexC(¤tDisk->mountFrom, '/'); + if (index == currentDisk->mountFrom.length) + return false; - if(ffStrbufEqual(current, otherDevice)) - return true; + FF_STRBUF_AUTO_DESTROY zpoolName = ffStrbufCreateNS(index, currentDisk->mountFrom.chars); + for(uint32_t i = 0; i < disks->length - 1; i++) + { + const FFDisk* otherDevice = ffListGet(disks, i); + if(ffStrbufEqualS(&otherDevice->filesystem, "zfs") && ffStrbufStartsWith(&otherDevice->mountFrom, &zpoolName)) + return true; + } + + return false; } + else + { + //Filter all disks which device was already found. This catches BTRFS subvolumes. + for(uint32_t i = 0; i < disks->length - 1; i++) + { + const FFDisk* otherDevice = ffListGet(disks, i); - //ZFS subvolumes: rpool///. - //Test if the third slash is present. - if(ffStrbufStartsWithS(current, "rpool/") && ffStrHasNChars(current->chars, '/', 3)) - return true; + if(ffStrbufEqual(¤tDisk->mountFrom, &otherDevice->mountFrom)) + return true; + } + } return false; } -static void detectType(const FFlist* devices, FFDisk* currentDisk, const char* options) +static bool detectPhysicalTypeAndReturnRemovable(FFDisk* currentDisk) { - if(isSubvolume(devices)) - currentDisk->type = FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT; - else if(strstr(options, "nosuid") != NULL || strstr(options, "nodev") != NULL) + // https://stackoverflow.com/a/73302025 + // Note my USB mobile hard disk isn't detected as removable, but my USB flash disk does. + // USB devices should be handled in detectName + FF_STRBUF_AUTO_DESTROY basePath = ffStrbufCreateS("/sys/block/"); + + FF_AUTO_CLOSE_DIR DIR* dir = opendir(basePath.chars); + if(dir == NULL) + return false; + + uint32_t index = ffStrbufLastIndexC(¤tDisk->mountFrom, '/'); + const char* partitionName = index == currentDisk->mountFrom.length ? NULL : currentDisk->mountFrom.chars + index + 1; + if (!partitionName || !*partitionName) return false; + + struct dirent* entry; + while((entry = readdir(dir)) != NULL) + { + if(entry->d_name[0] == '.') + continue; + + if (!ffStrStartsWith(partitionName, entry->d_name)) continue; + + ffStrbufAppendS(&basePath, entry->d_name); + index = basePath.length; + // /sys/block/sdx/queue/rotational + ffStrbufAppendS(&basePath, "/queue/rotational"); + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffReadFileBuffer(basePath.chars, &buffer)) + currentDisk->physicalType = ffStrbufEqualS(&buffer, "1") ? FF_DISK_PHYSICAL_TYPE_HDD : FF_DISK_PHYSICAL_TYPE_SSD; + else + currentDisk->physicalType = FF_DISK_PHYSICAL_TYPE_UNKNOWN; + ffStrbufSubstrBefore(&basePath, index); + + // /sys/block/sdx/removable + ffStrbufAppendS(&basePath, "/removable"); + + return ffReadFileBuffer(basePath.chars, &buffer) && ffStrbufEqualS(&buffer, "1"); + } + + return false; +} + +static void detectType(const FFlist* disks, FFDisk* currentDisk) +{ + bool isRemovable = detectPhysicalTypeAndReturnRemovable(currentDisk); + + if(currentDisk->type != FF_DISK_VOLUME_TYPE_NONE) return; + if(isRemovable) currentDisk->type = FF_DISK_VOLUME_TYPE_EXTERNAL_BIT; + else if(isSubvolume(disks, currentDisk)) + currentDisk->type = FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT; else if(ffStrbufStartsWithS(¤tDisk->mountpoint, "/boot") || ffStrbufStartsWithS(¤tDisk->mountpoint, "/efi")) currentDisk->type = FF_DISK_VOLUME_TYPE_HIDDEN_BIT; else @@ -222,61 +275,43 @@ static void detectStats(FFDisk* disk) const char* ffDetectDisksImpl(FFlist* disks) { - FILE* mountsFile = fopen("/proc/mounts", "r"); + FILE* mountsFile = setmntent("/proc/mounts", "r"); if(mountsFile == NULL) - return "fopen(\"/proc/mounts\", \"r\") == NULL"; + return "setmntent(\"/proc/mounts\", \"r\") == NULL"; - FF_LIST_AUTO_DESTROY devices = ffListCreate(sizeof(FFstrbuf)); + struct mntent* device; - char* line = NULL; - size_t len = 0; - - while(getline(&line, &len, mountsFile) != EOF) + while((device = getmntent(mountsFile))) { - //Format of the file: " ..." (Same as fstab) - char* currentPos = line; - - //detect device - FFstrbuf* device = ffListAdd(&devices); - ffStrbufInit(device); - appendNextEntry(device, ¤tPos); - if(!isPhysicalDevice(device)) - { - ffStrbufDestroy(device); - devices.length--; continue; - } //We have a valid device, add it to the list FFDisk* disk = ffListAdd(disks); + disk->type = FF_DISK_VOLUME_TYPE_NONE; + disk->physicalType = FF_DISK_PHYSICAL_TYPE_UNKNOWN; + + //detect mountFrom + ffStrbufInitS(&disk->mountFrom, device->mnt_fsname); //detect mountpoint - ffStrbufInit(&disk->mountpoint); - appendNextEntry(&disk->mountpoint, ¤tPos); + ffStrbufInitS(&disk->mountpoint, device->mnt_dir); //detect filesystem - ffStrbufInit(&disk->filesystem); - appendNextEntry(&disk->filesystem, ¤tPos); + ffStrbufInitS(&disk->filesystem, device->mnt_type); //detect name ffStrbufInit(&disk->name); - detectName(disk, device); + detectName(disk); // Also detects external devices //detect type - detectType(&devices, disk, currentPos); + detectType(disks, disk); //Detects stats detectStats(disk); } - if(line != NULL) - free(line); - - FF_LIST_FOR_EACH(FFstrbuf, device, devices) - ffStrbufDestroy(device); - - fclose(mountsFile); + endmntent(mountsFile); return NULL; } diff --git a/src/detection/disk/disk_windows.c b/src/detection/disk/disk_windows.c index 5b3b39232e..dadb7d3aec 100644 --- a/src/detection/disk/disk_windows.c +++ b/src/detection/disk/disk_windows.c @@ -1,7 +1,9 @@ #include "disk.h" +#include "common/io/io.h" #include "util/windows/unicode.h" #include +#include #include const char* ffDetectDisksImpl(FFlist* disks) @@ -25,6 +27,41 @@ const char* ffDetectDisksImpl(FFlist* disks) FFDisk* disk = ffListAdd(disks); ffStrbufInitWS(&disk->mountpoint, mountpoint); + wchar_t volumeName[64]; + + disk->physicalType = FF_DISK_PHYSICAL_TYPE_UNKNOWN; + if(mountpoint[1] == ':') + { + memcpy(volumeName, L"\\\\.\\ :", sizeof(L"\\\\.\\ :")); + volumeName[4] = mountpoint[0]; + FF_AUTO_CLOSE_FD HANDLE handle = CreateFileW(volumeName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (handle != INVALID_HANDLE_VALUE) + { + DEVICE_SEEK_PENALTY_DESCRIPTOR dspd = {}; + if(DeviceIoControl( + handle, + IOCTL_STORAGE_QUERY_PROPERTY, + &(STORAGE_PROPERTY_QUERY) { + .PropertyId = StorageDeviceSeekPenaltyProperty, + .QueryType = PropertyStandardQuery, + }, + sizeof(STORAGE_PROPERTY_QUERY), + &dspd, + sizeof(dspd), + NULL, + NULL + )) + disk->physicalType = dspd.IncursSeekPenalty ? FF_DISK_PHYSICAL_TYPE_HDD : FF_DISK_PHYSICAL_TYPE_SSD; + } + } + + if(GetVolumeNameForVolumeMountPointW(mountpoint, volumeName, sizeof(volumeName) / sizeof(*volumeName))) + { + ffStrbufInitWS(&disk->mountFrom, volumeName); + } + else + ffStrbufInit(&disk->mountFrom); + if(!GetDiskFreeSpaceExW( mountpoint, (PULARGE_INTEGER)&disk->bytesAvailable, diff --git a/src/detection/displayserver/displayserver_android.c b/src/detection/displayserver/displayserver_android.c index af57a06283..d1e342503f 100644 --- a/src/detection/displayserver/displayserver_android.c +++ b/src/detection/displayserver/displayserver_android.c @@ -80,9 +80,9 @@ static bool detectWithGetprop(FFDisplayServerResult* ds) ffStrbufContainC(&buffer, ',')) { // 1440,3200,560 => width,height,ppi - uint32_t width = ffStrbufToUInt16(&buffer, 0); + uint32_t width = (uint32_t) ffStrbufToUInt(&buffer, 0); ffStrbufSubstrAfterFirstC(&buffer, ','); - uint32_t height = ffStrbufToUInt16(&buffer, 0); + uint32_t height = (uint32_t) ffStrbufToUInt(&buffer, 0); return ffdsAppendDisplay(ds, width, height, diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c index f1bcea4eee..b34c429f28 100644 --- a/src/detection/displayserver/linux/wmde.c +++ b/src/detection/displayserver/linux/wmde.c @@ -233,6 +233,23 @@ static void getXFCE4(FFDisplayServerResult* result) ffStrbufSetS(&result->dePrettyName, FF_DE_PRETTY_XFCE4); ffParsePropFileData("gtk-doc/html/libxfce4ui/index.html", "

Version", &result->deVersion); + #ifdef __FreeBSD__ + if(result->deVersion.length == 0) + { + FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/usr/local/share/licenses/"); + if (dirp) + { + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) + { + if(!ffStrStartsWith(entry->d_name, "xfce-") || !isdigit(entry->d_name[5])) + continue; + ffStrbufAppendS(&result->deVersion, &entry->d_name[5]); + } + } + } + #endif + if(result->deVersion.length == 0 && instance.config.allowSlowOperations) { //This is somewhat slow diff --git a/src/detection/gamepad/gamepad_windows.c b/src/detection/gamepad/gamepad_windows.c index e115782aa9..8a5799717d 100644 --- a/src/detection/gamepad/gamepad_windows.c +++ b/src/detection/gamepad/gamepad_windows.c @@ -130,7 +130,7 @@ const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */) if (hHidFile && HidD_GetProductString(hHidFile, displayName, sizeof(wchar_t) * 126)) ffStrbufSetWS(&device->name, displayName); else - ffStrbufSetF(&device->name, "Unknown gamepad %4X-%4X", rdi.hid.dwVendorId, rdi.hid.dwProductId); + ffStrbufSetF(&device->name, "Unknown gamepad %4X-%4X", (unsigned) rdi.hid.dwVendorId, (unsigned) rdi.hid.dwProductId); } } diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 749072f467..72a6530083 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -311,6 +311,12 @@ static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) { + #ifdef FF_HAVE_DIRECTX_HEADERS + const char* ffGPUDetectByDirectX(const FFGPUOptions* options, FFlist* gpus); + if (!ffGPUDetectByDirectX(options, gpus)) + return NULL; + #endif + #ifdef FF_HAVE_LIBPCI return pciDetectGPUs(options, gpus); #else diff --git a/src/detection/gpu/gpu_wsl.cpp b/src/detection/gpu/gpu_wsl.cpp new file mode 100644 index 0000000000..fa77a4be0d --- /dev/null +++ b/src/detection/gpu/gpu_wsl.cpp @@ -0,0 +1,102 @@ +#ifdef FF_HAVE_DIRECTX_HEADERS + +#define INITGUID +extern "C" { +#include "common/library.h" +#include "detection/gpu/gpu.h" +} + +#include +#include +#include +#include +#include +#include + +template +struct on_scope_exit { + on_scope_exit(Fn &&fn): _fn(std::move(fn)) {} + ~on_scope_exit() { this->_fn(); } + +private: + Fn _fn; +}; + +extern "C" +const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus) +{ + FF_LIBRARY_LOAD(libdxcore, nullptr, "dlopen libdxcore.so failed. Make sure WSLg is enabled", "/usr/lib/wsl/lib/libdxcore" FF_LIBRARY_EXTENSION, 4) + // DXCoreCreateAdapterFactory is a reloaded function, so we can't use FF_LIBRARY_LOAD_SYMBOL_MESSAGE here + typedef HRESULT (*DXCoreCreateAdapterFactory_t)(REFIID riid, void** ppvFactory); + auto ffDXCoreCreateAdapterFactory = (DXCoreCreateAdapterFactory_t) dlsym(libdxcore, "DXCoreCreateAdapterFactory"); + if(ffDXCoreCreateAdapterFactory == nullptr) return "dlsym DXCoreCreateAdapterFactory failed"; + + IDXCoreAdapterFactory *factory = nullptr; + + if (FAILED(ffDXCoreCreateAdapterFactory(IID_PPV_ARGS(&factory)))) + return "DXCoreCreateAdapterFactory(IID_PPV_ARGS(&factory)) failed"; + on_scope_exit destroyFactory([&] { factory->Release(); }); + + IDXCoreAdapterList *list = NULL; + if (FAILED(factory->CreateAdapterList(1, &DXCORE_ADAPTER_ATTRIBUTE_D3D11_GRAPHICS, &list))) + return "factory->CreateAdapterList(1, &DXCORE_ADAPTER_ATTRIBUTE_D3D11_GRAPHICS, &list) failed"; + on_scope_exit destroyList([&] { list->Release(); }); + + for (uint32_t i = 0, count = list->GetAdapterCount(); i < count; i++) + { + IDXCoreAdapter *adapter = nullptr; + if (FAILED(list->GetAdapter(i, &adapter))) + continue; + on_scope_exit destroyAdapter([&] { adapter->Release(); }); + + // https://learn.microsoft.com/en-us/windows/win32/api/dxcore_interface/ne-dxcore_interface-dxcoreadapterproperty + { + bool value = 0; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::IsHardware, sizeof(value), &value)) && !value) + continue; + } + + char desc[256]; + if (FAILED(adapter->GetProperty(DXCoreAdapterProperty::DriverDescription, sizeof(desc), desc))) + continue; + + FFGPUResult* gpu = (FFGPUResult*) ffListAdd(gpus); + ffStrbufInitS(&gpu->name, desc); + gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->temperature = FF_GPU_TEMP_UNSET; + + ffStrbufInit(&gpu->driver); + uint64_t value = 0; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::DriverVersion, sizeof(value), &value))) + { + ffStrbufSetF(&gpu->driver, "%" PRIu64 ".%" PRIu64 ".%" PRIu64 ".%" PRIu64, + (value >> 48) & 0xFFFF, + (value >> 32) & 0xFFFF, + (value >> 16) & 0xFFFF, + (value >> 0) & 0xFFFF); + } + + gpu->dedicated.used = gpu->shared.used = gpu->dedicated.total = gpu->shared.total = FF_GPU_VMEM_SIZE_UNSET; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::DedicatedAdapterMemory, sizeof(value), &value))) + gpu->dedicated.total = value; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::SharedSystemMemory, sizeof(value), &value))) + gpu->shared.total = value; + + gpu->type = FF_GPU_TYPE_UNKNOWN; + bool integrated; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::IsIntegrated, sizeof(integrated), &integrated))) + gpu->type = integrated ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; + + ffStrbufInit(&gpu->vendor); + DXCoreHardwareID hardwareId; + if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::HardwareID, sizeof(hardwareId), &hardwareId))) + { + const char* vendorStr = ffGetGPUVendorString((unsigned) hardwareId.vendorID); + ffStrbufAppendS(&gpu->vendor, vendorStr); + } + } + + return NULL; +} + +#endif diff --git a/src/detection/localip/localip.h b/src/detection/localip/localip.h index 6fea95a79d..8d0fe84bf4 100644 --- a/src/detection/localip/localip.h +++ b/src/detection/localip/localip.h @@ -12,10 +12,6 @@ typedef struct FFLocalIpResult FFstrbuf ipv6; FFstrbuf mac; bool defaultRoute; - - #ifdef _WIN32 - uint32_t ifIndex; - #endif } FFLocalIpResult; const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results); diff --git a/src/detection/localip/localip_linux.c b/src/detection/localip/localip_linux.c index e702347357..1f319ad34d 100644 --- a/src/detection/localip/localip_linux.c +++ b/src/detection/localip/localip_linux.c @@ -1,5 +1,7 @@ #include "localip.h" #include "common/io/io.h" +#include "common/netif/netif.h" +#include "util/stringUtils.h" #include #include @@ -11,124 +13,11 @@ #if defined(__FreeBSD__) || defined(__APPLE__) #include -#include -#include #else #include #endif -#if defined(__linux__) -static bool getDefaultRoute(char iface[IF_NAMESIZE + 1]) -{ - FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/net/route", "r"); - if (!netRoute) return false; - - // skip first line - flockfile(netRoute); - while (getc_unlocked(netRoute) != '\n'); - funlockfile(netRoute); - unsigned long long destination; //, gateway, flags, refCount, use, metric, mask, mtu, - - static_assert(IF_NAMESIZE >= 16, "IF_NAMESIZE is too small"); - while (fscanf(netRoute, "%16s%llx%*[^\n]", iface, &destination) == 2) - { - if (destination == 0) - return true; - } - return false; -} -#elif defined(__FreeBSD__) || defined(__APPLE__) - -#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n)) - -#if defined(__APPLE__) -# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) -#elif defined(__NetBSD__) -# define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t)) -#elif defined(__FreeBSD__) -# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) -#elif defined(__OpenBSD__) -# define ROUNDUP(a) ROUNDUP2((a), sizeof(int)) -#else -# error unknown platform -#endif - -static struct sockaddr * -get_rt_address(struct rt_msghdr *rtm, int desired) -{ - struct sockaddr *sa = (struct sockaddr *)(rtm + 1); - - for (int i = 0; i < RTAX_MAX; i++) - { - if (rtm->rtm_addrs & (1 << i)) - { - if ((1 <sa_len) + (char *)sa); - } - } - return NULL; -} - -static bool getDefaultRoute(char iface[IF_NAMESIZE + 1]) -{ - //https://github.com/hashPirate/copenheimer-masscan-fork/blob/36f1ed9f7b751a7dccd5ed27874e2e703db7d481/src/rawsock-getif.c#L104 - - FF_AUTO_CLOSE_FD int pfRoute = socket(PF_ROUTE, SOCK_RAW, AF_INET); - if (pfRoute < 0) - return false; - - { - struct timeval timeout = {1, 0}; - setsockopt(pfRoute, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); - setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)); - } - - int pid = getpid(); - - struct { - struct rt_msghdr hdr; - struct sockaddr_in dst; - uint8_t data[512]; - } rtmsg = { - .hdr = { - .rtm_type = RTM_GET, - .rtm_flags = RTF_UP | RTF_GATEWAY, - .rtm_version = RTM_VERSION, - .rtm_addrs = RTA_DST | RTA_IFP, - .rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst), - .rtm_pid = pid, - .rtm_seq = 1, - }, - .dst = { - .sin_family = AF_INET, - .sin_len = sizeof(rtmsg.dst), - }, - }; - - if (write(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen) != rtmsg.hdr.rtm_msglen) - return false; - - while (read(pfRoute, &rtmsg, sizeof(rtmsg)) > 0) - { - if (rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid) - { - struct sockaddr_dl* sdl = (struct sockaddr_dl *)get_rt_address(&rtmsg.hdr, RTA_IFP); - if (sdl) - { - assert(sdl->sdl_nlen <= IF_NAMESIZE); - memcpy(iface, sdl->sdl_data, sdl->sdl_nlen); - iface[sdl->sdl_nlen] = 0; - return true; - } - return false; - } - } - return false; -} -#endif - -static void addNewIp(FFlist* list, const char* name, const char* addr, int type) +static void addNewIp(FFlist* list, const char* name, const char* addr, int type, bool defaultRoute) { FFLocalIpResult* ip = NULL; @@ -145,7 +34,7 @@ static void addNewIp(FFlist* list, const char* name, const char* addr, int type) ffStrbufInit(&ip->ipv4); ffStrbufInit(&ip->ipv6); ffStrbufInit(&ip->mac); - ip->defaultRoute = false; + ip->defaultRoute = defaultRoute; } switch (type) @@ -168,11 +57,17 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) if(getifaddrs(&ifAddrStruct) < 0) return "getifaddrs(&ifAddrStruct) failed"; + const char* defaultRouteIfName = ffNetifGetDefaultRouteIfName(); + for (struct ifaddrs* ifa = ifAddrStruct; ifa; ifa = ifa->ifa_next) { if (!ifa->ifa_addr || !(ifa->ifa_flags & IFF_RUNNING)) continue; + bool isDefaultRoute = ffStrEquals(defaultRouteIfName, ifa->ifa_name); + if (options->defaultRouteOnly && !isDefaultRoute) + continue; + if ((ifa->ifa_flags & IFF_LOOPBACK) && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) continue; @@ -187,7 +82,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) struct sockaddr_in* ipv4 = (struct sockaddr_in*) ifa->ifa_addr; char addressBuffer[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN); - addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET); + addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET, isDefaultRoute); } else if (ifa->ifa_addr->sa_family == AF_INET6) { @@ -197,7 +92,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) struct sockaddr_in6* ipv6 = (struct sockaddr_in6 *)ifa->ifa_addr; char addressBuffer[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN); - addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET6); + addNewIp(results, ifa->ifa_name, addressBuffer, AF_INET6, isDefaultRoute); } #if defined(__FreeBSD__) || defined(__APPLE__) else if (ifa->ifa_addr->sa_family == AF_LINK) @@ -209,7 +104,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = (uint8_t*) LLADDR((struct sockaddr_dl *)ifa->ifa_addr); snprintf(addressBuffer, sizeof(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, ifa->ifa_name, addressBuffer, -1); + addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute); } #else else if (ifa->ifa_addr->sa_family == AF_PACKET) @@ -221,18 +116,12 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = ((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr; snprintf(addressBuffer, sizeof(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, ifa->ifa_name, addressBuffer, -1); + addNewIp(results, ifa->ifa_name, addressBuffer, -1, isDefaultRoute); } #endif } if (ifAddrStruct) freeifaddrs(ifAddrStruct); - char iface[16 /*IF_NAMESIZE*/ + 1]; - if (getDefaultRoute(iface)) - { - FF_LIST_FOR_EACH(FFLocalIpResult, ip, *results) - ip->defaultRoute = ffStrbufEqualS(&ip->name, iface); - } return NULL; } diff --git a/src/detection/localip/localip_windows.c b/src/detection/localip/localip_windows.c index c986120baf..bb39b43254 100644 --- a/src/detection/localip/localip_windows.c +++ b/src/detection/localip/localip_windows.c @@ -1,39 +1,12 @@ -#include #include #include +#include "common/netif/netif.h" #include "util/mallocHelper.h" #include "util/windows/unicode.h" #include "localip.h" -static void setDefaultRoute(FFlist* ips) -{ - ULONG size = 0; - if (GetIpForwardTable(NULL, &size, TRUE) != ERROR_INSUFFICIENT_BUFFER) - return; - - FF_AUTO_FREE MIB_IPFORWARDTABLE* pIpForwardTable = (MIB_IPFORWARDTABLE*) malloc(size); - if (GetIpForwardTable(pIpForwardTable, &size, TRUE) != ERROR_SUCCESS) - return; - - for (uint32_t i = 0; i < pIpForwardTable->dwNumEntries; ++i) - { - MIB_IPFORWARDROW* ipForwardRow = &pIpForwardTable->table[i]; - if (ipForwardRow->dwForwardDest == 0 && ipForwardRow->dwForwardMask == 0) - { - FF_LIST_FOR_EACH(FFLocalIpResult, ip, *ips) - { - if (ip->ifIndex == ipForwardRow->dwForwardIfIndex) - { - ip->defaultRoute = true; - break; - } - } - } - } -} - -static void addNewIp(FFlist* list, const char* name, const char* value, int type, bool newIp, uint32_t ifIndex) +static void addNewIp(FFlist* list, const char* name, const char* value, int type, bool newIp, bool defaultRoute) { FFLocalIpResult* ip = NULL; @@ -44,8 +17,7 @@ static void addNewIp(FFlist* list, const char* name, const char* value, int type ffStrbufInit(&ip->ipv4); ffStrbufInit(&ip->ipv6); ffStrbufInit(&ip->mac); - ip->defaultRoute = false; - ip->ifIndex = ifIndex; + ip->defaultRoute = defaultRoute; } else { @@ -70,14 +42,16 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) { IP_ADAPTER_ADDRESSES* FF_AUTO_FREE adapter_addresses = NULL; - // Start with a 16 KB buffer and resize if needed - - // multiple attempts in case interfaces change while + // Multiple attempts in case interfaces change while // we are in the middle of querying them. - DWORD adapter_addresses_buffer_size = 16 * 1024; - for (int attempts = 0; attempts != 3; ++attempts) + DWORD adapter_addresses_buffer_size = 0; + for (int attempts = 0;; ++attempts) { - adapter_addresses = (IP_ADAPTER_ADDRESSES*)realloc(adapter_addresses, adapter_addresses_buffer_size); - assert(adapter_addresses); + if (adapter_addresses_buffer_size) + { + adapter_addresses = (IP_ADAPTER_ADDRESSES*)realloc(adapter_addresses, adapter_addresses_buffer_size); + assert(adapter_addresses); + } DWORD error = GetAdaptersAddresses( options->showType & FF_LOCALIP_TYPE_IPV4_BIT @@ -90,15 +64,21 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) if (error == ERROR_SUCCESS) break; - else if (ERROR_BUFFER_OVERFLOW == error) + else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) continue; else return "GetAdaptersAddresses() failed"; } + uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteIfIndex(); + // Iterate through all of the adapters for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) { + bool isDefaultRoute = adapter->IfIndex == defaultRouteIfIndex; + if (options->defaultRouteOnly && !isDefaultRoute) + continue; + bool isLoop = adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK; if (isLoop && !(options->showType & FF_LOCALIP_TYPE_LOOP_BIT)) continue; @@ -116,7 +96,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) uint8_t* ptr = adapter->PhysicalAddress; snprintf(addressBuffer, sizeof(addressBuffer), "%02x:%02x:%02x:%02x:%02x:%02x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); - addNewIp(results, name, addressBuffer, -1, newIp, adapter->IfIndex); + addNewIp(results, name, addressBuffer, -1, newIp, isDefaultRoute); newIp = false; } @@ -127,7 +107,7 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr; char addressBuffer[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN); - addNewIp(results, name, addressBuffer, AF_INET, newIp, adapter->IfIndex); + addNewIp(results, name, addressBuffer, AF_INET, newIp, isDefaultRoute); newIp = false; } else if (ifa->Address.lpSockaddr->sa_family == AF_INET6) @@ -135,13 +115,11 @@ const char* ffDetectLocalIps(const FFLocalIpOptions* options, FFlist* results) SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr; char addressBuffer[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN); - addNewIp(results, name, addressBuffer, AF_INET6, newIp, adapter->IfIndex); + addNewIp(results, name, addressBuffer, AF_INET6, newIp, isDefaultRoute); newIp = false; } } } - setDefaultRoute(results); - return NULL; } diff --git a/src/detection/netio/netio.c b/src/detection/netio/netio.c new file mode 100644 index 0000000000..5a07af8e2a --- /dev/null +++ b/src/detection/netio/netio.c @@ -0,0 +1,67 @@ +#include "netio.h" + +#include "common/time.h" + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options); + +static FFlist ioCounters1; +static uint64_t time1; + +void ffPrepareNetIO(FFNetIOOptions* options) +{ + ffListInit(&ioCounters1, sizeof(FFNetIOResult)); + ffNetIOGetIoCounters(&ioCounters1, options); + time1 = ffTimeGetNow(); +} + +const char* ffDetectNetIO(FFlist* result, FFNetIOOptions* options) +{ + const char* error = NULL; + if (time1 == 0) + { + ffListInit(&ioCounters1, sizeof(FFNetIOResult)); + error = ffNetIOGetIoCounters(&ioCounters1, options); + if (error) + return error; + time1 = ffTimeGetNow(); + } + + if (ioCounters1.length == 0) + return "No network interfaces found"; + + uint64_t time2 = ffTimeGetNow(); + while (time2 - time1 < 1000) + { + ffTimeSleep((uint32_t) (1000 - (time2 - time1))); + time2 = ffTimeGetNow(); + } + + error = ffNetIOGetIoCounters(result, options); + if (error) + return error; + + if (result->length != ioCounters1.length) + return "Different number of network interfaces. Network change?"; + + for (uint32_t i = 0; i < result->length; ++i) + { + FFNetIOResult* icPrev = (FFNetIOResult*)ffListGet(&ioCounters1, i); + FFNetIOResult* icCurr = (FFNetIOResult*)ffListGet(result, i); + if (!ffStrbufEqual(&icPrev->name, &icCurr->name)) + return "Network interface name changed"; + + static_assert(sizeof(FFNetIOResult) - offsetof(FFNetIOResult, txBytes) == sizeof(uint64_t) * 8, "Unexpected struct FFNetIOResult layout"); + for (size_t off = offsetof(FFNetIOResult, txBytes); off < sizeof(FFNetIOResult); off += sizeof(uint64_t)) + { + uint64_t* prevValue = (uint64_t*) ((uint8_t*) icPrev + off); + uint64_t* currValue = (uint64_t*) ((uint8_t*) icCurr + off); + uint64_t temp = *currValue; + *currValue -= *prevValue; + *currValue /= (time2 - time1) / 1000 /* seconds */; + *prevValue = temp; + } + } + time1 = time2; + + return NULL; +} diff --git a/src/detection/netio/netio.h b/src/detection/netio/netio.h new file mode 100644 index 0000000000..295c4478ed --- /dev/null +++ b/src/detection/netio/netio.h @@ -0,0 +1,17 @@ +#include "fastfetch.h" + +typedef struct FFNetIOResult +{ + FFstrbuf name; + bool defaultRoute; + uint64_t txBytes; + uint64_t rxBytes; + uint64_t txPackets; + uint64_t rxPackets; + uint64_t rxErrors; + uint64_t txErrors; + uint64_t rxDrops; + uint64_t txDrops; +} FFNetIOResult; + +const char* ffDetectNetIO(FFlist* result, FFNetIOOptions* options); diff --git a/src/detection/netio/netio_bsd.c b/src/detection/netio/netio_bsd.c new file mode 100644 index 0000000000..6bf9a8ce6e --- /dev/null +++ b/src/detection/netio/netio_bsd.c @@ -0,0 +1,58 @@ +#include "netio.h" + +#include "common/io/io.h" +#include "common/netif/netif.h" +#include "util/stringUtils.h" +#include "util/mallocHelper.h" + +#include +#include +#include +#include +#include +#include + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) +{ + uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteIfIndex(); + + size_t bufSize = 0; + if (sysctl((int[]) { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, (options->defaultRouteOnly ? (int) defaultRouteIfIndex : 0) }, 6, NULL, &bufSize, 0, 0) < 0) + return "sysctl({ CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, ifIndex }, 6, NULL, &bufSize, 0, 0) failed"; + + FF_AUTO_FREE struct if_msghdr* buf = (struct if_msghdr*) malloc(bufSize); + if (sysctl((int[]) { CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, (options->defaultRouteOnly ? (int) defaultRouteIfIndex : 0) }, 6, buf, &bufSize, 0, 0) < 0) + return "sysctl({ CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, ifIndex }, 6, buf, &bufSize, 0, 0) failed"; + + for (struct if_msghdr* ifm = buf; + ifm < (struct if_msghdr*) ((uint8_t*) buf + bufSize); + ifm = (struct if_msghdr*) ((uint8_t*) ifm + ifm->ifm_msglen)) + { + if (ifm->ifm_type != RTM_IFINFO || !(ifm->ifm_flags & IFF_RUNNING) || (ifm->ifm_flags & IFF_NOARP)) continue; + + struct sockaddr_dl* sdl = (struct sockaddr_dl*) (ifm + 1); + assert(sdl->sdl_family == AF_LINK); + if (sdl->sdl_type != IFT_ETHER && !(ifm->ifm_flags & IFF_LOOPBACK)) continue; + + sdl->sdl_data[sdl->sdl_nlen] = 0; + + if (options->namePrefix.length && strncmp(sdl->sdl_data, options->namePrefix.chars, options->namePrefix.length) != 0) + continue; + + FFNetIOResult* counters = (FFNetIOResult*) ffListAdd(result); + *counters = (FFNetIOResult) { + .name = ffStrbufCreateNS(sdl->sdl_nlen, sdl->sdl_data), + .txBytes = ifm->ifm_data.ifi_obytes, + .rxBytes = ifm->ifm_data.ifi_ibytes, + .txPackets = ifm->ifm_data.ifi_opackets, + .rxPackets = ifm->ifm_data.ifi_ipackets, + .txErrors = ifm->ifm_data.ifi_oerrors, + .rxErrors = ifm->ifm_data.ifi_ierrors, + .txDrops = 0, // unsupported + .rxDrops = ifm->ifm_data.ifi_iqdrops, + .defaultRoute = sdl->sdl_index == defaultRouteIfIndex, + }; + } + + return NULL; +} diff --git a/src/detection/netio/netio_linux.c b/src/detection/netio/netio_linux.c new file mode 100644 index 0000000000..2b1f244789 --- /dev/null +++ b/src/detection/netio/netio_linux.c @@ -0,0 +1,97 @@ +#include "netio.h" + +#include "common/io/io.h" +#include "common/netif/netif.h" +#include "util/stringUtils.h" + +#include + +static void getData(FFstrbuf* buffer, const char* ifName, bool isDefaultRoute, FFstrbuf* path, FFlist* result) +{ + ffStrbufSetF(path, "/sys/class/net/%s/operstate", ifName); + if(!ffReadFileBuffer(path->chars, buffer) || !ffStrbufEqualS(buffer, "up")) + return; + + FFNetIOResult* counters = (FFNetIOResult*) ffListAdd(result); + ffStrbufInitS(&counters->name, ifName); + counters->defaultRoute = isDefaultRoute; + + ffStrbufSetF(path, "/sys/class/net/%s/statistics/", ifName); + uint32_t statLen = path->length; + + ffStrbufAppendS(path, "rx_bytes"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->rxBytes = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "tx_bytes"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->txBytes = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "rx_packets"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->rxPackets = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "tx_packets"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->txPackets = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "rx_errors"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->rxErrors = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "tx_errors"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->txErrors = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "rx_dropped"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->rxDrops = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); + + ffStrbufAppendS(path, "tx_dropped"); + if (ffReadFileBuffer(path->chars, buffer)) + counters->txDrops = ffStrbufToUInt(buffer, 0); + ffStrbufSubstrBefore(path, statLen); +} + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) +{ + FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/net"); + if (!dirp) return "opendir(\"/sys/class/net\") == NULL"; + + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateA(64); + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + + const char* defaultRouteIfName = ffNetifGetDefaultRouteIfName(); + + if (options->defaultRouteOnly) + { + if (options->namePrefix.length && strncmp(defaultRouteIfName, options->namePrefix.chars, options->namePrefix.length) != 0) + return NULL; + + getData(&buffer, defaultRouteIfName, true, &path, result); + } + else + { + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) + { + const char* ifName = entry->d_name; + if(ifName[0] == '.') + continue; + + if (options->namePrefix.length && strncmp(ifName, options->namePrefix.chars, options->namePrefix.length) != 0) + continue; + + getData(&buffer, ifName, ffStrEquals(ifName, defaultRouteIfName), &path, result); + } + } + + return NULL; +} diff --git a/src/detection/netio/netio_windows.c b/src/detection/netio/netio_windows.c new file mode 100644 index 0000000000..4291df3d46 --- /dev/null +++ b/src/detection/netio/netio_windows.c @@ -0,0 +1,74 @@ +#include "netio.h" + +#include "common/netif/netif.h" +#include "util/mallocHelper.h" +#include "util/windows/unicode.h" + +#include +#include + +const char* ffNetIOGetIoCounters(FFlist* result, FFNetIOOptions* options) +{ + IP_ADAPTER_ADDRESSES* FF_AUTO_FREE adapter_addresses = NULL; + + // Multiple attempts in case interfaces change while + // we are in the middle of querying them. + DWORD adapter_addresses_buffer_size = 0; + for (int attempts = 0;; ++attempts) + { + if (adapter_addresses_buffer_size) + { + adapter_addresses = (IP_ADAPTER_ADDRESSES*)realloc(adapter_addresses, adapter_addresses_buffer_size); + assert(adapter_addresses); + } + + DWORD error = GetAdaptersAddresses( + AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER, + NULL, + adapter_addresses, + &adapter_addresses_buffer_size); + + if (error == ERROR_SUCCESS) + break; + else if (ERROR_BUFFER_OVERFLOW == error && attempts < 4) + continue; + else + return "GetAdaptersAddresses() failed"; + } + + uint32_t defaultRouteIfIndex = ffNetifGetDefaultRouteIfIndex(); + + // Iterate through all of the adapters + for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) + { + bool isDefaultRoute = adapter->IfIndex == defaultRouteIfIndex; + if (options->defaultRouteOnly && !isDefaultRoute) + continue; + + char name[128]; + WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName, -1, name, sizeof(name), NULL, NULL); + if (options->namePrefix.length && strncmp(name, options->namePrefix.chars, options->namePrefix.length) != 0) + continue; + + MIB_IF_ROW2 ifRow = { .InterfaceIndex = adapter->IfIndex }; + if (GetIfEntry2(&ifRow) == NO_ERROR) + { + FFNetIOResult* counters = (FFNetIOResult*) ffListAdd(result); + *counters = (FFNetIOResult) { + .name = ffStrbufCreateS(name), + .txBytes = ifRow.OutOctets, + .rxBytes = ifRow.InOctets, + .txPackets = (ifRow.OutUcastPkts + ifRow.OutNUcastPkts), + .rxPackets = (ifRow.InUcastPkts + ifRow.InNUcastPkts), + .rxErrors = ifRow.InErrors, + .txErrors = ifRow.OutErrors, + .rxDrops = ifRow.InDiscards, + .txDrops = ifRow.OutDiscards, + .defaultRoute = isDefaultRoute, + }; + } + } + + return NULL; +} diff --git a/src/detection/opencl/opencl.c b/src/detection/opencl/opencl.c index d8cb4a66b4..0526de2e22 100644 --- a/src/detection/opencl/opencl.c +++ b/src/detection/opencl/opencl.c @@ -49,14 +49,17 @@ static const char* openCLHandleData(OpenCLData* data, FFOpenCLResult* result) if(ffStrStartsWithIgnCase(version, "OpenCL ")) versionPretty = version + strlen("OpenCL "); ffStrbufSetS(&result->version, versionPretty); + ffStrbufTrim(&result->version, ' '); ffStrbufEnsureFree(&result->device, 128); data->ffclGetDeviceInfo(deviceID, CL_DEVICE_NAME, result->device.allocated, result->device.chars, NULL); ffStrbufRecalculateLength(&result->device); + ffStrbufTrim(&result->device, ' '); ffStrbufEnsureFree(&result->vendor, 32); data->ffclGetDeviceInfo(deviceID, CL_DEVICE_VENDOR, result->vendor.allocated, result->vendor.chars, NULL); ffStrbufRecalculateLength(&result->vendor); + ffStrbufTrim(&result->vendor, ' '); return NULL; } diff --git a/src/detection/opengl/opengl_linux.c b/src/detection/opengl/opengl_linux.c index 8da49361b8..8ea44355d9 100644 --- a/src/detection/opengl/opengl_linux.c +++ b/src/detection/opengl/opengl_linux.c @@ -363,7 +363,7 @@ const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) #else - FF_UNUSED(result); + FF_UNUSED(options, result); return "Fastfetch was built without gl support."; #endif //FF_HAVE_GL diff --git a/src/detection/os/os_linux.c b/src/detection/os/os_linux.c index 338a1fcfb1..1b99c174c8 100644 --- a/src/detection/os/os_linux.c +++ b/src/detection/os/os_linux.c @@ -1,6 +1,7 @@ #include "os.h" #include "common/properties.h" #include "common/parsing.h" +#include "common/io/io.h" #include "util/stringUtils.h" #include @@ -121,6 +122,15 @@ static void getUbuntuFlavour(FFOSResult* result) } } +static void getDebianVersion(FFOSResult* result) +{ + FF_STRBUF_AUTO_DESTROY debianVersion = ffStrbufCreate(); + ffAppendFileBuffer("/etc/debian_version", &debianVersion); + if (!debianVersion.length) return; + ffStrbufSet(&result->version, &debianVersion); + ffStrbufSet(&result->versionID, &debianVersion); +} + static void detectOS(FFOSResult* os) { if(instance.config.osFile.length > 0) @@ -169,6 +179,8 @@ void ffDetectOSImpl(FFOSResult* os) detectOS(os); - if(ffStrbufIgnCaseCompS(&os->id, "ubuntu") == 0) + if(ffStrbufIgnCaseEqualS(&os->id, "ubuntu")) getUbuntuFlavour(os); + else if(ffStrbufIgnCaseEqualS(&os->id, "debian")) + getDebianVersion(os); } diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h index 77f8e68121..ae36b5078e 100644 --- a/src/detection/packages/packages.h +++ b/src/detection/packages/packages.h @@ -27,6 +27,7 @@ typedef struct FFPackagesResult uint32_t rpm; uint32_t scoop; uint32_t snap; + uint32_t winget; uint32_t xbps; uint32_t all; //Make sure this goes last diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c index 7dd4061b4a..1aa2c2433a 100644 --- a/src/detection/packages/packages_linux.c +++ b/src/detection/packages/packages_linux.c @@ -197,6 +197,9 @@ static uint32_t getSnap(FFstrbuf* baseDir) { uint32_t result = getNumElements(baseDir, "/snap", DT_DIR); + if (result == 0) + result = getNumElements(baseDir, "/var/lib/snapd/snap", DT_DIR); + //Accounting for the /snap/bin folder return result > 0 ? result - 1 : 0; } @@ -262,7 +265,7 @@ static uint32_t getRpmFromLibrpm(void) static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts) { packageCounts->apk += getNumStrings(baseDir, "/lib/apk/db/installed", "C:Q"); - packageCounts->dpkg += getNumStrings(baseDir, "/var/lib/dpkg/status", "Status: "); + packageCounts->dpkg += getNumStrings(baseDir, "/var/lib/dpkg/status", "Status: install ok installed"); packageCounts->emerge += countFilesRecursive(baseDir, "/var/db/pkg", "SIZE"); packageCounts->eopkg += getNumElements(baseDir, "/var/lib/eopkg/package", DT_DIR); packageCounts->flatpakSystem += getFlatpak(baseDir, "/var/lib/flatpak"); diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c index 5b5343f793..bfd6a4c793 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -1,4 +1,5 @@ #include "packages.h" +#include "common/processing.h" #include "util/stringUtils.h" #include @@ -62,7 +63,7 @@ static void detectChoco(FF_MAYBE_UNUSED FFPackagesResult* result) result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY, "choco"); } -static void detectPacman(FF_MAYBE_UNUSED FFPackagesResult* result) +static void detectPacman(FFPackagesResult* result) { const char* msystemPrefix = getenv("MSYSTEM_PREFIX"); if(!msystemPrefix) @@ -75,9 +76,40 @@ static void detectPacman(FF_MAYBE_UNUSED FFPackagesResult* result) result->pacman = getNumElements(pacmanPath, FILE_ATTRIBUTE_DIRECTORY, NULL); } +static void detectWinget(FFPackagesResult* result) +{ + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffProcessAppendStdOut(&buffer, (char* []) { + "winget.exe", + "list", + "--disable-interactivity", + NULL, + })) + return; + + uint32_t index = ffStrbufFirstIndexS(&buffer, "--\r\n"); // Ignore garbage and table headers + if (index == buffer.length) + return; + + uint32_t count = 0; + for ( + index += strlen("--\r\n"); + (index = ffStrbufNextIndexC(&buffer, index, '\n')) < buffer.length; + ++index + ) + ++count; + + if (buffer.chars[buffer.length - 1] != '\n') // count last line + ++count; + + result->winget = count; +} + void ffDetectPackagesImpl(FFPackagesResult* result) { detectScoop(result); detectChoco(result); detectPacman(result); + if (instance.config.allowSlowOperations) + detectWinget(result); } diff --git a/src/detection/poweradapter/poweradapter.h b/src/detection/poweradapter/poweradapter.h index 9abdd48ecf..0c60d4d9fe 100644 --- a/src/detection/poweradapter/poweradapter.h +++ b/src/detection/poweradapter/poweradapter.h @@ -5,14 +5,14 @@ #include "fastfetch.h" -typedef struct PowerAdapterResult +typedef struct FFPowerAdapterResult { FFstrbuf description; FFstrbuf name; FFstrbuf modelName; FFstrbuf manufacturer; int watts; -} PowerAdapterResult; +} FFPowerAdapterResult; const char* ffDetectPowerAdapterImpl(FFlist* results); diff --git a/src/detection/poweradapter/poweradapter_apple.c b/src/detection/poweradapter/poweradapter_apple.c index de3025a40b..06d8a8df6b 100644 --- a/src/detection/poweradapter/poweradapter_apple.c +++ b/src/detection/poweradapter/poweradapter_apple.c @@ -24,7 +24,7 @@ const char* ffDetectPowerAdapterImpl(FFlist* results) continue; } - PowerAdapterResult* adapter = ffListAdd(results); + FFPowerAdapterResult* adapter = ffListAdd(results); ffStrbufInit(&adapter->name); ffStrbufInit(&adapter->description); diff --git a/src/detection/processes/processes_windows.c b/src/detection/processes/processes_windows.c index dc784c5db8..fb29cd2109 100644 --- a/src/detection/processes/processes_windows.c +++ b/src/detection/processes/processes_windows.c @@ -6,18 +6,26 @@ const char* ffDetectProcesses(uint32_t* result) { - ULONG size = 0; - if(NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &size) != STATUS_INFO_LENGTH_MISMATCH) - return "NtQuerySystemInformation(SystemProcessInformation, NULL) failed"; - - size += sizeof(SystemProcessInformation) * 5; //What if new processes are created during two syscalls? + SYSTEM_PROCESS_INFORMATION* FF_AUTO_FREE pstart = NULL; - SYSTEM_PROCESS_INFORMATION* FF_AUTO_FREE pstart = (SYSTEM_PROCESS_INFORMATION*)malloc(size); - if(!pstart) - return "malloc(size) failed"; - - if(!NT_SUCCESS(NtQuerySystemInformation(SystemProcessInformation, pstart, size, NULL))) - return "NtQuerySystemInformation(SystemProcessInformation, pstart) failed"; + // Multiple attempts in case processes change while + // we are in the middle of querying them. + ULONG size = 0; + for (int attempts = 0;; ++attempts) + { + if (size) + { + pstart = (SYSTEM_PROCESS_INFORMATION*)realloc(pstart, size); + assert(pstart); + } + NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, pstart, size, &size); + if(NT_SUCCESS(status)) + break; + else if(status == STATUS_INFO_LENGTH_MISMATCH && attempts < 4) + size += sizeof(SYSTEM_PROCESS_INFORMATION) * 5; + else + return "NtQuerySystemInformation(SystemProcessInformation) failed"; + } *result = 1; //Init with 1 because we test for ptr->NextEntryOffset for (SYSTEM_PROCESS_INFORMATION* ptr = pstart; ptr->NextEntryOffset; ptr = (SYSTEM_PROCESS_INFORMATION*)((uint8_t*)ptr + ptr->NextEntryOffset)) diff --git a/src/detection/publicip/publicip.c b/src/detection/publicip/publicip.c new file mode 100644 index 0000000000..4c1cdc42f7 --- /dev/null +++ b/src/detection/publicip/publicip.c @@ -0,0 +1,73 @@ +#include "publicip.h" +#include "common/networking.h" + +static FFNetworkingState state; +static int status = -1; + +void ffPreparePublicIp(FFPublicIpOptions* options) +{ + if (status != -1) + { + fputs("Error: this module can only be used once due to internal limitations\n", stderr); + exit(1); + } + + if (options->url.length == 0) + status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/json", NULL); + else + { + FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url); + ffStrbufSubstrAfterFirstS(&host, "://"); + uint32_t pathStartIndex = ffStrbufFirstIndexC(&host, '/'); + + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + if(pathStartIndex != host.length) + { + ffStrbufAppendNS(&path, pathStartIndex, host.chars + (host.length - pathStartIndex)); + host.length = pathStartIndex; + host.chars[pathStartIndex] = '\0'; + } + + status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL); + } +} + +static inline void wrapYyjsonFree(yyjson_doc** doc) +{ + assert(doc); + if (*doc) + yyjson_doc_free(*doc); +} + +const char* ffDetectPublicIp(FFPublicIpOptions* options, FFPublicIpResult* result) +{ + if (status == -1) + ffPreparePublicIp(options); + + if (status == 0) + return "Failed to connect to an IP detection server"; + + FF_STRBUF_AUTO_DESTROY response = ffStrbufCreateA(4096); + bool success = ffNetworkingRecvHttpResponse(&state, &response, options->timeout); + if (success) ffStrbufSubstrAfterFirstS(&response, "\r\n\r\n"); + + if (!success || response.length == 0) + return "Failed to receive the server response"; + + if (options->url.length == 0) + { + yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(response.chars, response.length, 0, NULL, NULL); + if (doc) + { + yyjson_val* root = yyjson_doc_get_root(doc); + ffStrbufAppendS(&result->ip, yyjson_get_str(yyjson_obj_get(root, "ip"))); + ffStrbufDestroy(&result->location); + ffStrbufInitF(&result->location, "%s, %s", yyjson_get_str(yyjson_obj_get(root, "city")), yyjson_get_str(yyjson_obj_get(root, "country"))); + return NULL; + } + } + + ffStrbufDestroy(&result->ip); + ffStrbufInitMove(&result->ip, &response); + return NULL; +} diff --git a/src/detection/publicip/publicip.h b/src/detection/publicip/publicip.h new file mode 100644 index 0000000000..1313cc46a0 --- /dev/null +++ b/src/detection/publicip/publicip.h @@ -0,0 +1,17 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_publicip_publicip +#define FF_INCLUDED_detection_publicip_publicip + +#include "fastfetch.h" + +typedef struct FFPublicIpResult +{ + FFstrbuf ip; + FFstrbuf location; +} FFPublicIpResult; + +void ffPreparePublicIp(FFPublicIpOptions* options); +const char* ffDetectPublicIp(FFPublicIpOptions* options, FFPublicIpResult* result); + +#endif diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index 8fcb7db6c7..1ebca55d9a 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -237,7 +237,7 @@ static void detectFromWindowsTeriminal(const FFstrbuf* terminalExe, FFTerminalFo #endif //defined(_WIN32) || defined(__linux__) -FF_MAYBE_UNUSED static bool detectKitty(FFTerminalFontResult* result) +FF_MAYBE_UNUSED static bool detectKitty(const FFstrbuf* exe, FFTerminalFontResult* result) { FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate(); @@ -247,6 +247,23 @@ FF_MAYBE_UNUSED static bool detectKitty(FFTerminalFontResult* result) {"font_size ", &fontSize}, }; + if(instance.config.allowSlowOperations) + { + FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); + if(!ffProcessAppendStdOut(&buf, (char* const[]){ + exe->chars, + "+kitten", + "query-terminal", + NULL, + })) + { + ffParsePropLines(buf.chars, "font_family: ", &fontName); + ffParsePropLines(buf.chars, "font_size: ", &fontSize); + ffFontInitValues(&result->font, fontName.chars, fontSize.chars); + return true; + } + } + if(!ffParsePropFileConfigValues("kitty/kitty.conf", 2, fontQuery)) return false; @@ -338,6 +355,35 @@ static bool detectTabby(FFTerminalFontResult* result) return true; } +static bool detectContour(const FFstrbuf* exe, FFTerminalFontResult* result) +{ + FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); + if(ffProcessAppendStdOut(&buf, (char* const[]){ + exe->chars, + "font-locator", + NULL + })) + { + ffStrbufAppendS(&result->error, "`contour font-locator` failed"); + return false; + } + + //[error] Missing key .logging.enabled. Using default: false. + //[error] ... + //Matching fonts using : Fontconfig + //Font description : (family=Sarasa Term SC Nerd weight=Regular slant=Roman spacing=Monospace, strict_spacing=yes) + //Number of fonts found : 49 + // path /usr/share/fonts/google-noto/NotoSansMono-Regular.ttf Regular Roman + // path ... + + uint32_t index = ffStrbufFirstIndexS(&buf, "Font description : (family="); + if(index >= buf.length) return false; + index += (uint32_t) strlen("Font description : (family="); + ffStrbufSubstrBefore(&buf, ffStrbufNextIndexS(&buf, index, " weight=")); + ffFontInitCopy(&result->font, buf.chars + index); + return true; +} + void ffDetectTerminalFontPlatform(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont); static bool detectTerminalFontCommon(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) @@ -350,12 +396,14 @@ static bool detectTerminalFontCommon(const FFTerminalShellResult* terminalShell, detectWezterm(terminalFont); else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "tabby")) detectTabby(terminalFont); + else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "contour")) + detectContour(&terminalShell->terminalExe, terminalFont); #ifndef _WIN32 else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalExe, "/dev/pts/")) ffStrbufAppendS(&terminalFont->error, "Terminal font detection is not supported on PTS"); else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "kitty")) - detectKitty(terminalFont); + detectKitty(&terminalShell->terminalExe, terminalFont); else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalExe, "/dev/tty")) detectTTY(terminalFont); #endif diff --git a/src/detection/terminalfont/terminalfont_android.c b/src/detection/terminalfont/terminalfont_android.c index ae79a4517a..bced44147c 100644 --- a/src/detection/terminalfont/terminalfont_android.c +++ b/src/detection/terminalfont/terminalfont_android.c @@ -55,7 +55,7 @@ const char* detectTermux(FFTerminalFontResult* terminalFont) void ffDetectTerminalFontPlatform(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) { - if(ffStrbufCompS(&terminalShell->terminalProcessName, "Termux") != 0) + if(ffStrbufCompS(&terminalShell->terminalProcessName, "com.termux") != 0) { ffStrbufSetS(&terminalFont->error, "Unsupported terminal"); return; diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 18a9b79c8b..cad8c5cbcc 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -128,6 +128,13 @@ static bool getShellVersionOksh(FFstrbuf* exe, FFstrbuf* version) return true; } +static bool getShellVersionNushell(FFstrbuf* exe, FFstrbuf* version) +{ + ffStrbufSetS(version, getenv("NU_VERSION")); + if (version->length) return true; + return getExeVersionRaw(exe, version); //0.73.0 +} + #ifdef _WIN32 static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) { @@ -158,7 +165,7 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) if(strcasecmp(exeName, "csh") == 0 || strcasecmp(exeName, "tcsh") == 0) return getExeVersionGeneral(exe, version); //tcsh 6.24.07 (Astron) 2022-12-21 (aarch64-apple-darwin) options wide,nls,dl,al,kan,sm,rh,color,filec if(strcasecmp(exeName, "nu") == 0) - return getExeVersionRaw(exe, version); //0.73.0 + return getShellVersionNushell(exe, version); if(strcasecmp(exeName, "ksh") == 0) return getShellVersionKsh(exe, version); if(strcasecmp(exeName, "oksh") == 0) @@ -263,6 +270,20 @@ FF_MAYBE_UNUSED static bool getTerminalVersionXterm(FFstrbuf* exe, FFstrbuf* ver return version->length > 0; } +static bool getTerminalVersionContour(FFstrbuf* exe, FFstrbuf* version) +{ + const char* env = getenv("TERMINAL_VERSION_STRING"); + if (env) + { + ffStrbufAppendS(version, env); + return true; + } + if(!getExeVersionRaw(exe, version)) return false; + // Contour Terminal Emulator 0.3.12.262 + ffStrbufSubstrAfterFirstC(version, ' '); + return version->length > 0; +} + #ifdef _WIN32 static bool getTerminalVersionWindowsTerminal(FFstrbuf* exe, FFstrbuf* version) @@ -296,7 +317,7 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe { #ifdef __ANDROID__ - if(ffStrbufEqualS(processName, "Termux")) + if(ffStrbufEqualS(processName, "com.termux")) return getTerminalVersionTermux(version); #endif @@ -361,6 +382,9 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe if(ffStrbufStartsWithIgnCaseS(processName, "alacritty")) return getExeVersionGeneral(exe, version); + if(ffStrbufStartsWithIgnCaseS(processName, "contour")) + return getTerminalVersionContour(exe, version); + const char* termProgramVersion = getenv("TERM_PROGRAM_VERSION"); if(termProgramVersion) { diff --git a/src/detection/terminalshell/terminalshell.h b/src/detection/terminalshell/terminalshell.h index c727b5fdf4..41e31700f2 100644 --- a/src/detection/terminalshell/terminalshell.h +++ b/src/detection/terminalshell/terminalshell.h @@ -20,10 +20,6 @@ typedef struct FFTerminalShellResult const char* terminalExeName; //pointer to a char in terminalExe FFstrbuf terminalVersion; uint32_t terminalPid; - - FFstrbuf userShellExe; - const char* userShellExeName; //pointer to a char in userShellExe - FFstrbuf userShellVersion; } FFTerminalShellResult; const FFTerminalShellResult* ffDetectTerminalShell(); diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 58e29f1f8e..957a0b304d 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -56,7 +56,7 @@ static void getProcessInformation(pid_t pid, FFstrbuf* processName, FFstrbuf* ex exe->chars, &size, NULL, 0 )) - exe->length = (uint32_t)size; + exe->length = (uint32_t)size - 1; #endif @@ -237,7 +237,7 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) getenv("TERMUX_VERSION") != NULL || getenv("TERMUX_MAIN_PACKAGE_FORMAT") != NULL || getenv("TMUX_TMPDIR") != NULL - )) term = "Termux"; + )) term = "com.termux"; #endif #ifdef __linux__ @@ -269,18 +269,12 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) static void getUserShellFromEnv(FFTerminalShellResult* result) { - ffStrbufSet(&result->userShellExe, &instance.state.platform.userShell); - if(result->userShellExe.length == 0) - return; - - setExeName(&result->userShellExe, &result->userShellExeName); - //If shell detection via processes failed - if(result->shellProcessName.length == 0 && result->userShellExe.length > 0) + if(result->shellProcessName.length == 0 && instance.state.platform.userShell.length > 0) { - ffStrbufAppendS(&result->shellProcessName, result->userShellExeName); - ffStrbufSet(&result->shellExe, &result->userShellExe); + ffStrbufSet(&result->shellExe, &instance.state.platform.userShell); setExeName(&result->shellExe, &result->shellExeName); + ffStrbufAppendS(&result->shellProcessName, result->shellExeName); } } @@ -331,6 +325,8 @@ const FFTerminalShellResult* ffDetectTerminalShell() const uint32_t exePathLen = PROC_PIDPATHINFO_MAXSIZE; #elif defined(MAXPATH) const uint32_t exePathLen = MAXPATH; + #elif defined(PATH_MAX) + const uint32_t exePathLen = PATH_MAX; #else const uint32_t exePathLen = 260; #endif @@ -346,10 +342,6 @@ const FFTerminalShellResult* ffDetectTerminalShell() result.terminalExeName = result.terminalExe.chars; result.terminalPid = 0; - ffStrbufInit(&result.userShellExe); - result.userShellExeName = result.userShellExe.chars; - ffStrbufInit(&result.userShellVersion); - getTerminalShell(&result, getppid()); getTerminalFromEnv(&result); @@ -358,11 +350,6 @@ const FFTerminalShellResult* ffDetectTerminalShell() ffStrbufClear(&result.shellVersion); getShellVersion(&result.shellExe, result.shellExeName, &result.shellVersion); - if(strcasecmp(result.shellExeName, result.userShellExeName) != 0) - getShellVersion(&result.userShellExe, result.userShellExeName, &result.userShellVersion); - else - ffStrbufSet(&result.userShellVersion, &result.shellVersion); - if(ffStrbufEqualS(&result.shellProcessName, "pwsh")) ffStrbufInitStatic(&result.shellPrettyName, "PowerShell"); else if(ffStrbufEqualS(&result.shellProcessName, "nu")) @@ -388,7 +375,13 @@ const FFTerminalShellResult* ffDetectTerminalShell() if(ffStrbufEqualS(&result.terminalProcessName, "wezterm-gui")) ffStrbufInitStatic(&result.terminalPrettyName, "WezTerm"); - #if defined(__linux__) || defined(__FreeBSD__) + + #if defined(__ANDROID__) + + else if(ffStrbufEqualS(&result.terminalProcessName, "com.termux")) + ffStrbufInitStatic(&result.terminalPrettyName, "Termux"); + + #elif defined(__linux__) || defined(__FreeBSD__) else if(ffStrbufStartsWithS(&result.terminalProcessName, "gnome-terminal-")) ffStrbufInitStatic(&result.terminalPrettyName, "gnome-terminal"); diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index 0bd65e5784..ebc062b337 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -379,10 +379,6 @@ const FFTerminalShellResult* ffDetectTerminalShell(void) ffStrbufInit(&result.terminalPrettyName); result.terminalPid = 0; - ffStrbufInit(&result.userShellExe); - result.userShellExeName = ""; - ffStrbufInit(&result.userShellVersion); - uint32_t ppid; if(!getProcessInfo(0, &ppid, NULL, NULL, NULL)) goto exit; diff --git a/src/detection/uptime/uptime.h b/src/detection/uptime/uptime.h index 447b13d5b6..105e1bfa5c 100644 --- a/src/detection/uptime/uptime.h +++ b/src/detection/uptime/uptime.h @@ -5,6 +5,12 @@ #include "fastfetch.h" -const char* ffDetectUptime(uint64_t* result); +typedef struct FFUptimeResult +{ + uint64_t bootTime; + uint64_t uptime; +} FFUptimeResult; + +const char* ffDetectUptime(FFUptimeResult* result); #endif diff --git a/src/detection/uptime/uptime_bsd.c b/src/detection/uptime/uptime_bsd.c index 4e3a707c6e..47202544ae 100644 --- a/src/detection/uptime/uptime_bsd.c +++ b/src/detection/uptime/uptime_bsd.c @@ -1,10 +1,10 @@ #include "uptime.h" +#include "common/time.h" -#include #include #include -const char* ffDetectUptime(uint64_t* result) +const char* ffDetectUptime(FFUptimeResult* result) { struct timeval bootTime; size_t bootTimeSize = sizeof(bootTime); @@ -15,7 +15,8 @@ const char* ffDetectUptime(uint64_t* result) ) != 0) return "sysctl({CTL_KERN, KERN_BOOTTIME}) failed"; - *result = (uint64_t) difftime(time(NULL), bootTime.tv_sec); + result->bootTime = (uint64_t) bootTime.tv_sec * 1000 + (uint64_t) bootTime.tv_usec / 1000; + result->uptime = ffTimeGetNow() - result->bootTime; return NULL; } diff --git a/src/detection/uptime/uptime_linux.c b/src/detection/uptime/uptime_linux.c index 0339bc7e65..bfa67215b7 100644 --- a/src/detection/uptime/uptime_linux.c +++ b/src/detection/uptime/uptime_linux.c @@ -1,12 +1,15 @@ #include "uptime.h" +#include "common/time.h" -#include - -const char* ffDetectUptime(uint64_t* result) +const char* ffDetectUptime(FFUptimeResult* result) { - struct sysinfo info; - if(sysinfo(&info) != 0) - return "sysinfo() failed"; - *result = (uint64_t) info.uptime; + struct timespec uptime; + if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) + return "clock_gettime(CLOCK_BOOTTIME) failed"; + + result->uptime = (uint64_t) uptime.tv_sec * 1000 + (uint64_t) uptime.tv_nsec / 1000000; + + result->bootTime = ffTimeGetNow() + result->uptime; + return NULL; } diff --git a/src/detection/uptime/uptime_windows.c b/src/detection/uptime/uptime_windows.c index 55053ab246..6978a1163d 100644 --- a/src/detection/uptime/uptime_windows.c +++ b/src/detection/uptime/uptime_windows.c @@ -1,9 +1,11 @@ #include "uptime.h" +#include "common/time.h" #include -const char* ffDetectUptime(uint64_t* result) +const char* ffDetectUptime(FFUptimeResult* result) { - *result = GetTickCount64() / 1000; + result->uptime = GetTickCount64(); + result->bootTime = ffTimeGetNow() - result->uptime; return NULL; } diff --git a/src/detection/users/users.h b/src/detection/users/users.h index 8842e16a2e..8ddc2a0cf4 100644 --- a/src/detection/users/users.h +++ b/src/detection/users/users.h @@ -5,6 +5,15 @@ #include "fastfetch.h" -void ffDetectUsers(FFlist* users /* List of FFstrbuf */, FFstrbuf* error); +typedef struct FFUserResult +{ + FFstrbuf name; + FFstrbuf hostName; + FFstrbuf clientIp; + FFstrbuf tty; + uint64_t loginTime; // ms +} FFUserResult; + +const char* ffDetectUsers(FFlist* users); #endif diff --git a/src/detection/users/users_linux.c b/src/detection/users/users_linux.c index c17bf139c7..294c69b3e1 100644 --- a/src/detection/users/users_linux.c +++ b/src/detection/users/users_linux.c @@ -11,7 +11,7 @@ #define getutxent getutent #endif -void ffDetectUsers(FFlist* users, FFstrbuf* error) +const char* ffDetectUsers(FFlist* users) { struct utmpx* n = NULL; setutxent(); @@ -22,15 +22,24 @@ void ffDetectUsers(FFlist* users, FFstrbuf* error) if(n->ut_type != USER_PROCESS) continue; - FF_LIST_FOR_EACH(FFstrbuf, user, *users) + FF_LIST_FOR_EACH(FFUserResult, user, *users) { - if(ffStrbufEqualS(user, n->ut_user)) + if(ffStrbufEqualS(&user->name, n->ut_user)) goto next; } - ffStrbufInitS((FFstrbuf*)ffListAdd(users), n->ut_user); + FFUserResult* user = (FFUserResult*) ffListAdd(users); + ffStrbufInitS(&user->name, n->ut_user); + ffStrbufInitS(&user->hostName, n->ut_host); + ffStrbufInitS(&user->tty, n->ut_line); + #ifdef __linux__ + if(n->ut_addr_v6[0] || n->ut_addr_v6[1] || n->ut_addr_v6[2] || n->ut_addr_v6[3]) + ffStrbufInitF(&user->clientIp, "%u.%u.%u.%u", n->ut_addr_v6[0], n->ut_addr_v6[1], n->ut_addr_v6[2], n->ut_addr_v6[3]); + else + #endif + ffStrbufInit(&user->clientIp); + user->loginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000; } - if(users->length == 0) - ffStrbufAppendS(error, "Unable to detect users"); + return NULL; } diff --git a/src/detection/users/users_windows.c b/src/detection/users/users_windows.c index 01fab40a77..a891520b11 100644 --- a/src/detection/users/users_windows.c +++ b/src/detection/users/users_windows.c @@ -4,17 +4,20 @@ #include #include -void ffDetectUsers(FFlist* users, FFstrbuf* error) +static inline uint64_t to_ms(uint64_t ret) +{ + ret -= 116444736000000000ull; + return ret / 10000ull; +} + +const char* ffDetectUsers(FFlist* users) { WTS_SESSION_INFO_1W* sessionInfo; DWORD sessionCount; DWORD level = 1; if(!WTSEnumerateSessionsExW(WTS_CURRENT_SERVER_HANDLE, &level, 0, &sessionInfo, &sessionCount)) - { - ffStrbufAppendS(error, "WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE) failed"); - return; - } + return "WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE) failed"; for (DWORD i = 0; i < sessionCount; i++) { @@ -22,14 +25,33 @@ void ffDetectUsers(FFlist* users, FFstrbuf* error) if(session->State != WTSActive) continue; - FF_STRBUF_AUTO_DESTROY domainName = ffStrbufCreateWS(session->pDomainName); - FF_STRBUF_AUTO_DESTROY userName = ffStrbufCreateWS(session->pUserName); - - ffStrbufInitF((FFstrbuf*)ffListAdd(users), "%s\\%s", domainName.chars, userName.chars); + FFUserResult* user = (FFUserResult*) ffListAdd(users); + ffStrbufInitWS(&user->name, session->pUserName); + ffStrbufInitWS(&user->hostName, session->pHostName); + + DWORD bytes = 0; + PWTS_CLIENT_ADDRESS address = NULL; + if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSClientAddress, (LPWSTR *) &address, &bytes)) + { + if (address->AddressFamily == 2 /*AF_INET*/) + ffStrbufInitF(&user->clientIp, "%u.%u.%u.%u", address->Address[2], address->Address[3], address->Address[4], address->Address[5]); + WTSFreeMemory(address); + } + else + ffStrbufInitS(&user->clientIp, "0.0.0.0"); + + bytes = 0; + PWTSINFOW wtsInfo = NULL; + if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session->SessionId, WTSSessionInfo, (LPWSTR *) &wtsInfo, &bytes)) + { + user->loginTime = to_ms(*(uint64_t*) &wtsInfo->LogonTime); + WTSFreeMemory(wtsInfo); + } + else + user->loginTime = 0; } WTSFreeMemoryExW(WTSTypeSessionInfoLevel1, sessionInfo, 1); - if(users->length == 0) - ffStrbufAppendS(error, "Unable to detect users"); + return NULL; } diff --git a/src/detection/version/version.c b/src/detection/version/version.c index 4a8eb420c4..202c10ca20 100644 --- a/src/detection/version/version.c +++ b/src/detection/version/version.c @@ -20,6 +20,9 @@ #define FF_ARCHITECTURE "unknown" #endif +#define FF_STR_INDIR(x) #x +#define FF_STR(x) FF_STR_INDIR(x) + void ffDetectVersion(FFVersionResult* version) { version->projectName = FASTFETCH_PROJECT_NAME; @@ -27,6 +30,16 @@ void ffDetectVersion(FFVersionResult* version) version->version = FASTFETCH_PROJECT_VERSION; version->versionTweak = FASTFETCH_PROJECT_VERSION_TWEAK; version->cmakeBuiltType = FASTFETCH_PROJECT_CMAKE_BUILD_TYPE; + version->compileTime = __DATE__ ", " __TIME__; + #ifdef __clang__ + version->compiler = "clang " FF_STR(__clang_major__) "." FF_STR(__clang_minor__) "." FF_STR(__clang_patchlevel__); + #elif defined(__GNUC__) + version->compiler = "gcc " FF_STR(__GNUC__) "." FF_STR(__GNUC_MINOR__) "." FF_STR(__GNUC_PATCHLEVEL__); + #elif defined(_MSC_VER) + version->compiler = "msvc " FF_STR(_MSC_VER); + #else + version->compiler = "unknown"; + #endif #ifndef NDEBUG version->debugMode = true; #else diff --git a/src/detection/version/version.h b/src/detection/version/version.h index bdb11aa3a1..4cb7b9ac6c 100644 --- a/src/detection/version/version.h +++ b/src/detection/version/version.h @@ -12,6 +12,8 @@ typedef struct FFVersionResult const char* version; const char* versionTweak; const char* cmakeBuiltType; + const char* compileTime; + const char* compiler; bool debugMode; } FFVersionResult; diff --git a/src/detection/weather/weather.c b/src/detection/weather/weather.c new file mode 100644 index 0000000000..94bd6b5b68 --- /dev/null +++ b/src/detection/weather/weather.c @@ -0,0 +1,38 @@ +#include "weather.h" + +static FFNetworkingState state; +static int status = -1; + +void ffPrepareWeather(FFWeatherOptions* options) +{ + if (status != -1) + { + fputs("Error: this module can only be used once due to internal limitations\n", stderr); + exit(1); + } + + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateS("/"); + if (options->location.length) + ffStrbufAppend(&path, &options->location); + ffStrbufAppendS(&path, "?format="); + ffStrbufAppend(&path, &options->outputFormat); + status = ffNetworkingSendHttpRequest(&state, "wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n"); +} + +const char* ffDetectWeather(FFWeatherOptions* options, FFstrbuf* result) +{ + if(status == -1) + ffPrepareWeather(options); + + if(status == 0) + return "Failed to connect to 'wttr.in'"; + + ffStrbufEnsureFree(result, 4095); + bool success = ffNetworkingRecvHttpResponse(&state, result, options->timeout); + if (success) ffStrbufSubstrAfterFirstS(result, "\r\n\r\n"); + + if(!success || result->length == 0) + return "Failed to receive the server response"; + + return NULL; +} diff --git a/src/detection/weather/weather.h b/src/detection/weather/weather.h new file mode 100644 index 0000000000..c15f1ea3ac --- /dev/null +++ b/src/detection/weather/weather.h @@ -0,0 +1,11 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_weather_weather +#define FF_INCLUDED_detection_weather_weather + +#include "common/networking.h" + +void ffPrepareWeather(FFWeatherOptions* options); +const char* ffDetectWeather(FFWeatherOptions* options, FFstrbuf* result); + +#endif diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index c6b572012f..775856eec8 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -63,7 +63,7 @@ ffStrbufAppendF(&item->conn.protocol, "Unknown (%ld)", inf.activePHYMode); break; } - item->conn.signalQuality = inf.rssiValue >= -50 ? 100 : inf.rssiValue <= -100 ? 0 : (inf.rssiValue + 100) * 2; + item->conn.signalQuality = (double) (inf.rssiValue >= -50 ? 100 : inf.rssiValue <= -100 ? 0 : (inf.rssiValue + 100) * 2); item->conn.txRate = inf.transmitRate; switch(inf.security) diff --git a/src/fastfetch.c b/src/fastfetch.c index 4c947d5628..c956357ebe 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -21,21 +21,6 @@ #include "modules/modules.h" -typedef struct FFCustomValue -{ - bool printKey; - FFstrbuf key; - FFstrbuf value; -} FFCustomValue; - -// Things only needed by fastfetch -typedef struct FFdata -{ - FFstrbuf structure; - FFlist customValues; // List of FFCustomValue - bool loadUserConfig; -} FFdata; - static void constructAndPrintCommandHelpFormat(const char* name, const char* def, uint32_t numArgs, ...) { va_list argp; @@ -64,6 +49,19 @@ static inline void printCommandHelp(const char* command) puts(FASTFETCH_DATATEXT_HELP_FORMAT); else if(ffStrEqualsIgnCase(command, "load-config") || ffStrEqualsIgnCase(command, "config")) puts(FASTFETCH_DATATEXT_HELP_CONFIG); + else if(ffStrEqualsIgnCase(command, "title-format")) + { + constructAndPrintCommandHelpFormat("title", "{6}{7}{8}", 8, + "User name", + "Host name", + "Home directory", + "Executable path of current process", + "User's default shell", + "User name (colored)", + "@ symbol (colored)", + "Host name (colored)" + ); + } else if(ffStrEqualsIgnCase(command, "os-format")) { constructAndPrintCommandHelpFormat("os", "{3} {12}", 12, @@ -144,7 +142,7 @@ static inline void printCommandHelp(const char* command) } else if(ffStrEqualsIgnCase(command, "packages-format")) { - constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (port), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis)", 23, + constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (eopkg), {8} (xbps), {9} (nix-system), {10} (nix-user), {11} (nix-default), {12} (apk), {13} (pkg), {14} (flatpak-system), {15} (flatpack-user), {16} (snap), {17} (brew), {18} (brew-cask), {19} (port), {20} (scoop), {21} (choco), {22} (pkgtool), {23} (paludis), {24} (winget)", 24, "Number of all packages", "Number of pacman packages", "Pacman branch on manjaro", @@ -168,18 +166,18 @@ static inline void printCommandHelp(const char* command) "Number of choco packages", "Number of pkgtool packages", "Number of paludis packages" + "Number of winget packages" ); } else if(ffStrEqualsIgnCase(command, "shell-format")) { - constructAndPrintCommandHelpFormat("shell", "{3} {4}", 7, + constructAndPrintCommandHelpFormat("shell", "{3} {4}", 6, "Shell process name", "Shell path with exe name", "Shell exe name", "Shell version", - "User shell path with exe name", - "User shell exe name", - "User shell version" + "Shell pid", + "Shell pretty name" ); } else if(ffStrEqualsIgnCase(command, "display-format")) @@ -276,13 +274,9 @@ static inline void printCommandHelp(const char* command) "Terminal process name", "Terminal path with exe name", "Terminal exe name", - "Shell process name", - "Shell path with exe name", - "Shell exe name", - "Shell version", - "User shell path with exe name", - "User shell exe name", - "User shell version" + "Terminal pid", + "Terminal pretty name", + "Terminal version" ); } else if(ffStrEqualsIgnCase(command, "terminalfont-format")) @@ -307,9 +301,9 @@ static inline void printCommandHelp(const char* command) "Temperature" ); } - else if(ffStrEqualsIgnCase(command, "cpu-usage-format")) + else if(ffStrEqualsIgnCase(command, "cpuusage-format")) { - constructAndPrintCommandHelpFormat("cpu-usage", "{0}%", 1, + constructAndPrintCommandHelpFormat("cpuusage", "{0}%", 1, "CPU usage without percent mark" ); } @@ -387,18 +381,31 @@ static inline void printCommandHelp(const char* command) "Locale code" ); } - else if(ffStrEqualsIgnCase(command, "local-ip-format")) + else if(ffStrEqualsIgnCase(command, "localip-format")) { - constructAndPrintCommandHelpFormat("local-ip", "{}", 1, - "Local IP address" + constructAndPrintCommandHelpFormat("localip", "{}", 5, + "Local IPv4 address", + "Local IPv6 address", + "Physical (MAC) address", + "Interface name", + "Is default route" ); } - else if(ffStrEqualsIgnCase(command, "public-ip-format")) + else if(ffStrEqualsIgnCase(command, "publicip-format")) { - constructAndPrintCommandHelpFormat("public-ip", "{}", 1, + constructAndPrintCommandHelpFormat("publicip", "{}", 1, "Public IP address" ); } + else if(ffStrEqualsIgnCase(command, "netio-format")) + { + constructAndPrintCommandHelpFormat("netio", "{}", 3, + "Size of data received per second (formatted)", + "Size of data sent per second (formatted)", + "Interface name", + "Is default route" + ); + } else if(ffStrEqualsIgnCase(command, "wifi-format")) { constructAndPrintCommandHelpFormat("wifi", "{4} - {6}", 3, @@ -465,7 +472,9 @@ static inline void printCommandHelp(const char* command) "Version tweak", "Build type (debug or release)", "Architecture", - "CMake build type (Debug, Release, RelWithDebInfo, MinSizeRel)" + "CMake build type (Debug, Release, RelWithDebInfo, MinSizeRel)", + "Date time when compiling", + "Compiler used" ); } else if(ffStrEqualsIgnCase(command, "vulkan-format")) @@ -836,25 +845,25 @@ static void parseOption(FFdata* data, const char* key, const char* value) puts(FASTFETCH_PROJECT_VERSION); exit(0); } - else if(ffStrStartsWithIgnCase(key, "--print")) + else if(ffStrStartsWithIgnCase(key, "--print-")) { - const char* subkey = key + strlen("--print"); - if(ffStrEqualsIgnCase(subkey, "-config-system")) + const char* subkey = key + strlen("--print-"); + if(ffStrEqualsIgnCase(subkey, "config-system")) { puts(FASTFETCH_DATATEXT_CONFIG_SYSTEM); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-config-user")) + else if(ffStrEqualsIgnCase(subkey, "config-user")) { puts(FASTFETCH_DATATEXT_CONFIG_USER); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-structure")) + else if(ffStrEqualsIgnCase(subkey, "structure")) { puts(FASTFETCH_DATATEXT_STRUCTURE); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-logos")) + else if(ffStrEqualsIgnCase(subkey, "logos")) { ffLogoBuiltinPrint(); exit(0); @@ -862,35 +871,35 @@ static void parseOption(FFdata* data, const char* key, const char* value) else goto error; } - else if(ffStrStartsWithIgnCase(key, "--list")) + else if(ffStrStartsWithIgnCase(key, "--list-")) { - const char* subkey = key + strlen("--list"); - if(ffStrEqualsIgnCase(subkey, "-modules")) + const char* subkey = key + strlen("--list-"); + if(ffStrEqualsIgnCase(subkey, "modules")) { listModules(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-presets")) + else if(ffStrEqualsIgnCase(subkey, "presets")) { listAvailablePresets(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-config-paths")) + else if(ffStrEqualsIgnCase(subkey, "config-paths")) { listConfigPaths(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-data-paths")) + else if(ffStrEqualsIgnCase(subkey, "data-paths")) { listDataPaths(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-features")) + else if(ffStrEqualsIgnCase(subkey, "features")) { ffListFeatures(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-logos")) + else if(ffStrEqualsIgnCase(subkey, "logos")) { puts("Builtin logos:"); ffLogoBuiltinList(); @@ -898,7 +907,7 @@ static void parseOption(FFdata* data, const char* key, const char* value) listAvailableLogos(); exit(0); } - else if(ffStrEqualsIgnCase(subkey, "-logos-autocompletion")) + else if(ffStrEqualsIgnCase(subkey, "logos-autocompletion")) { ffLogoBuiltinListAutocompletion(); exit(0); @@ -906,12 +915,8 @@ static void parseOption(FFdata* data, const char* key, const char* value) else goto error; } - else if(ffStrStartsWithIgnCase(key, "--set")) + else if(ffStrEqualsIgnCase(key, "--set") || ffStrEqualsIgnCase(key, "--set-keyless")) { - const char* subkey = key + strlen("--set"); - if(*subkey != '\0' && !ffStrEqualsIgnCase(subkey, "-keyless")) - goto error; - FF_STRBUF_AUTO_DESTROY customValueStr = ffStrbufCreate(); ffOptionParseString(key, value, &customValueStr); uint32_t index = ffStrbufFirstIndexC(&customValueStr, '='); @@ -938,14 +943,14 @@ static void parseOption(FFdata* data, const char* key, const char* value) ffStrbufInitMove(&customValue->key, &customKey); ffStrbufSubstrAfter(&customValueStr, index); ffStrbufInitMove(&customValue->value, &customValueStr); - customValue->printKey = *subkey == '\0'; + customValue->printKey = key[5] == '\0'; } /////////////////// //General options// /////////////////// - else if(ffStrEqualsIgnCase(key, "--load-config") || ffStrEqualsIgnCase(key, "--config")) + else if(ffStrEqualsIgnCase(key, "-c") || ffStrEqualsIgnCase(key, "--load-config") || ffStrEqualsIgnCase(key, "--config")) optionParseConfigFile(data, key, value); else if(ffStrEqualsIgnCase(key, "--gen-config")) generateConfigFile(false, value); @@ -968,6 +973,30 @@ static void parseOption(FFdata* data, const char* key, const char* value) data->loadUserConfig = ffOptionParseBoolean(value); else if(ffStrEqualsIgnCase(key, "--processing-timeout")) instance.config.processingTimeout = ffOptionParseInt32(key, value); + else if(ffStrEqualsIgnCase(key, "--format")) + { + switch (ffOptionParseEnum(key, value, (FFKeyValuePair[]) { + { "default", 0}, + { "json", 1 }, + {}, + })) + { + case 0: + if (instance.state.resultDoc) + { + yyjson_mut_doc_free(instance.state.resultDoc); + instance.state.resultDoc = NULL; + } + break; + case 1: + if (!instance.state.resultDoc) + { + instance.state.resultDoc = yyjson_mut_doc_new(NULL); + yyjson_mut_doc_set_root(instance.state.resultDoc, yyjson_mut_arr(instance.state.resultDoc)); + } + break; + } + } #if defined(__linux__) || defined(__FreeBSD__) else if(ffStrEqualsIgnCase(key, "--player-name")) @@ -1001,21 +1030,21 @@ static void parseOption(FFdata* data, const char* key, const char* value) ffOptionParseString(key, value, &data->structure); else if(ffStrEqualsIgnCase(key, "--separator")) ffOptionParseString(key, value, &instance.config.keyValueSeparator); - else if(ffStrStartsWith(key, "--color")) + else if(ffStrEqualsIgnCase(key, "--color")) { - const char* subkey = key + strlen("--color"); - if(*subkey == '\0') - { - optionCheckString(key, value, &instance.config.colorKeys); - ffOptionParseColor(value, &instance.config.colorKeys); - ffStrbufSet(&instance.config.colorTitle, &instance.config.colorKeys); - } - else if(ffStrEqualsIgnCase(subkey, "-keys")) + optionCheckString(key, value, &instance.config.colorKeys); + ffOptionParseColor(value, &instance.config.colorKeys); + ffStrbufSet(&instance.config.colorTitle, &instance.config.colorKeys); + } + else if(ffStrStartsWithIgnCase(key, "--color-")) + { + const char* subkey = key + strlen("--color-"); + if(ffStrEqualsIgnCase(subkey, "keys")) { optionCheckString(key, value, &instance.config.colorKeys); ffOptionParseColor(value, &instance.config.colorKeys); } - else if(ffStrEqualsIgnCase(subkey, "-title")) + else if(ffStrEqualsIgnCase(subkey, "title")) { optionCheckString(key, value, &instance.config.colorTitle); ffOptionParseColor(value, &instance.config.colorTitle); @@ -1071,16 +1100,16 @@ static void parseOption(FFdata* data, const char* key, const char* value) instance.config.percentNdigits = (uint8_t) ffOptionParseUInt32(key, value); else if(ffStrEqualsIgnCase(key, "--no-buffer")) instance.config.noBuffer = ffOptionParseBoolean(value); - else if(ffStrStartsWithIgnCase(key, "--bar")) + else if(ffStrStartsWithIgnCase(key, "--bar-")) { - const char* subkey = key + strlen("--bar"); - if(ffStrEqualsIgnCase(subkey, "-char-elapsed")) + const char* subkey = key + strlen("--bar-"); + if(ffStrEqualsIgnCase(subkey, "char-elapsed")) ffOptionParseString(key, value, &instance.config.barCharElapsed); - else if(ffStrEqualsIgnCase(subkey, "-char-total")) + else if(ffStrEqualsIgnCase(subkey, "char-total")) ffOptionParseString(key, value, &instance.config.barCharTotal); - else if(ffStrEqualsIgnCase(subkey, "-width")) + else if(ffStrEqualsIgnCase(subkey, "width")) instance.config.barWidth = (uint8_t) ffOptionParseUInt32(key, value); - else if(ffStrEqualsIgnCase(subkey, "-border")) + else if(ffStrEqualsIgnCase(subkey, "border")) instance.config.barBorder = ffOptionParseBoolean(value); else goto error; @@ -1090,56 +1119,56 @@ static void parseOption(FFdata* data, const char* key, const char* value) //Library options// /////////////////// - else if(ffStrStartsWithIgnCase(key, "--lib")) + else if(ffStrStartsWithIgnCase(key, "--lib-")) { - const char* subkey = key + strlen("--lib"); - if(ffStrEqualsIgnCase(subkey, "-PCI")) + const char* subkey = key + strlen("--lib-"); + if(ffStrEqualsIgnCase(subkey, "PCI")) ffOptionParseString(key, value, &instance.config.libPCI); - else if(ffStrEqualsIgnCase(subkey, "-vulkan")) + else if(ffStrEqualsIgnCase(subkey, "vulkan")) ffOptionParseString(key, value, &instance.config.libVulkan); - else if(ffStrEqualsIgnCase(subkey, "-freetype")) + else if(ffStrEqualsIgnCase(subkey, "freetype")) ffOptionParseString(key, value, &instance.config.libfreetype); - else if(ffStrEqualsIgnCase(subkey, "-wayland")) + else if(ffStrEqualsIgnCase(subkey, "wayland")) ffOptionParseString(key, value, &instance.config.libWayland); - else if(ffStrEqualsIgnCase(subkey, "-xcb-randr")) + else if(ffStrEqualsIgnCase(subkey, "xcb-randr")) ffOptionParseString(key, value, &instance.config.libXcbRandr); - else if(ffStrEqualsIgnCase(subkey, "-xcb")) + else if(ffStrEqualsIgnCase(subkey, "xcb")) ffOptionParseString(key, value, &instance.config.libXcb); - else if(ffStrEqualsIgnCase(subkey, "-Xrandr")) + else if(ffStrEqualsIgnCase(subkey, "Xrandr")) ffOptionParseString(key, value, &instance.config.libXrandr); - else if(ffStrEqualsIgnCase(subkey, "-X11")) + else if(ffStrEqualsIgnCase(subkey, "X11")) ffOptionParseString(key, value, &instance.config.libX11); - else if(ffStrEqualsIgnCase(subkey, "-gio")) + else if(ffStrEqualsIgnCase(subkey, "gio")) ffOptionParseString(key, value, &instance.config.libGIO); - else if(ffStrEqualsIgnCase(subkey, "-DConf")) + else if(ffStrEqualsIgnCase(subkey, "DConf")) ffOptionParseString(key, value, &instance.config.libDConf); - else if(ffStrEqualsIgnCase(subkey, "-dbus")) + else if(ffStrEqualsIgnCase(subkey, "dbus")) ffOptionParseString(key, value, &instance.config.libDBus); - else if(ffStrEqualsIgnCase(subkey, "-XFConf")) + else if(ffStrEqualsIgnCase(subkey, "XFConf")) ffOptionParseString(key, value, &instance.config.libXFConf); - else if(ffStrEqualsIgnCase(subkey, "-sqlite") || ffStrEqualsIgnCase(subkey, "-sqlite3")) + else if(ffStrEqualsIgnCase(subkey, "sqlite") || ffStrEqualsIgnCase(subkey, "sqlite3")) ffOptionParseString(key, value, &instance.config.libSQLite3); - else if(ffStrEqualsIgnCase(subkey, "-rpm")) + else if(ffStrEqualsIgnCase(subkey, "rpm")) ffOptionParseString(key, value, &instance.config.librpm); - else if(ffStrEqualsIgnCase(subkey, "-imagemagick")) + else if(ffStrEqualsIgnCase(subkey, "imagemagick")) ffOptionParseString(key, value, &instance.config.libImageMagick); - else if(ffStrEqualsIgnCase(subkey, "-z")) + else if(ffStrEqualsIgnCase(subkey, "z")) ffOptionParseString(key, value, &instance.config.libZ); - else if(ffStrEqualsIgnCase(subkey, "-chafa")) + else if(ffStrEqualsIgnCase(subkey, "chafa")) ffOptionParseString(key, value, &instance.config.libChafa); - else if(ffStrEqualsIgnCase(subkey, "-egl")) + else if(ffStrEqualsIgnCase(subkey, "egl")) ffOptionParseString(key, value, &instance.config.libEGL); - else if(ffStrEqualsIgnCase(subkey, "-glx")) + else if(ffStrEqualsIgnCase(subkey, "glx")) ffOptionParseString(key, value, &instance.config.libGLX); - else if(ffStrEqualsIgnCase(subkey, "-osmesa")) + else if(ffStrEqualsIgnCase(subkey, "osmesa")) ffOptionParseString(key, value, &instance.config.libOSMesa); - else if(ffStrEqualsIgnCase(subkey, "-opencl")) + else if(ffStrEqualsIgnCase(subkey, "opencl")) ffOptionParseString(key, value, &instance.config.libOpenCL); - else if(ffStrEqualsIgnCase(key, "-pulse")) + else if(ffStrEqualsIgnCase(subkey, "pulse")) ffOptionParseString(key, value, &instance.config.libPulse); - else if(ffStrEqualsIgnCase(subkey, "-nm")) + else if(ffStrEqualsIgnCase(subkey, "nm")) ffOptionParseString(key, value, &instance.config.libnm); - else if(ffStrEqualsIgnCase(subkey, "-ddcutil")) + else if(ffStrEqualsIgnCase(subkey, "ddcutil")) ffOptionParseString(key, value, &instance.config.libDdcutil); else goto error; @@ -1219,27 +1248,6 @@ static void parseArguments(FFdata* data, int argc, const char** argv) } } -static void parseStructureCommand(const char* line, FFlist* customValues) -{ - // handle `--set` and `--set-keyless` - FF_LIST_FOR_EACH(FFCustomValue, customValue, *customValues) - { - if (ffStrbufEqualS(&customValue->key, line)) - { - __attribute__((__cleanup__(ffDestroyCustomOptions))) FFCustomOptions options; - ffInitCustomOptions(&options); - if (customValue->printKey) - ffStrbufAppend(&options.moduleArgs.key, &customValue->key); - ffStrbufAppend(&options.moduleArgs.outputFormat, &customValue->value); - ffPrintCustom(&options); - return; - } - } - - if(!ffParseModuleCommand(line)) - ffPrintErrorString(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, ""); -} - int main(int argc, const char** argv) { ffInitInstance(); @@ -1273,25 +1281,9 @@ int main(int argc, const char** argv) const bool useJsonConfig = data.structure.length == 0 && instance.state.configDoc; if(useJsonConfig) - ffPrintJsonConfig(true); + ffPrintJsonConfig(true /* prepare */); else - { - //If we don't have a custom structure, use the default one - if(data.structure.length == 0) - ffStrbufAppendS(&data.structure, FASTFETCH_DATATEXT_STRUCTURE); - - if(ffStrbufContainIgnCaseS(&data.structure, FF_CPUUSAGE_MODULE_NAME)) - ffPrepareCPUUsage(); - - if(instance.config.multithreading) - { - if(ffStrbufContainIgnCaseS(&data.structure, FF_PUBLICIP_MODULE_NAME)) - ffPreparePublicIp(&instance.config.publicIP); - - if(ffStrbufContainIgnCaseS(&data.structure, FF_WEATHER_MODULE_NAME)) - ffPrepareWeather(&instance.config.weather); - } - } + ffPrepareCommandOption(&data); ffStart(); @@ -1300,40 +1292,13 @@ int main(int argc, const char** argv) #endif if (useJsonConfig) - { ffPrintJsonConfig(false); - } else - { - //Parse the structure and call the modules - uint32_t startIndex = 0; - while (startIndex < data.structure.length) - { - uint32_t colonIndex = ffStrbufNextIndexC(&data.structure, startIndex, ':'); - data.structure.chars[colonIndex] = '\0'; - - uint64_t ms = 0; - if(__builtin_expect(instance.config.stat, false)) - ms = ffTimeGetTick(); + ffPrintCommandOption(&data); - parseStructureCommand(data.structure.chars + startIndex, &data.customValues); - - if(__builtin_expect(instance.config.stat, false)) - { - char str[32]; - int len = snprintf(str, sizeof str, "%" PRIu64 "ms", ffTimeGetTick() - ms); - if(instance.config.pipe) - puts(str); - else - printf("\033[s\033[1A\033[9999999C\033[%dD%s\033[u", len, str); // Save; Up 1; Right 9999999; Left ; Print ; Load - } - - #if defined(_WIN32) - if (!instance.config.noBuffer) fflush(stdout); - #endif - - startIndex = colonIndex + 1; - } + if (instance.state.resultDoc) + { + yyjson_mut_write_fp(stdout, instance.state.resultDoc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES, NULL, NULL); } ffFinish(); diff --git a/src/fastfetch.h b/src/fastfetch.h index a5f990912c..0877c1088a 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -14,6 +14,10 @@ #include "3rdparty/yyjson/yyjson.h" #endif +#ifdef _MSC_VER + #define __attribute__(x) +#endif + #include "util/FFstrbuf.h" #include "util/FFlist.h" #include "util/platform/FFPlatform.h" @@ -108,6 +112,7 @@ typedef struct FFconfig FFMediaOptions media; FFMemoryOptions memory; FFMonitorOptions monitor; + FFNetIOOptions netIo; FFOSOptions os; FFOpenCLOptions openCL; FFOpenGLOptions openGL; @@ -169,6 +174,7 @@ typedef struct FFstate FFPlatform platform; yyjson_doc* configDoc; + yyjson_mut_doc* resultDoc; } FFstate; typedef struct FFinstance diff --git a/src/logo/ascii/afterglow.txt b/src/logo/ascii/afterglow.txt new file mode 100644 index 0000000000..3b86a44d49 --- /dev/null +++ b/src/logo/ascii/afterglow.txt @@ -0,0 +1,15 @@ + ${c2}. + ${c1}. ${c2}.{! + ${c1}.L! ${c2}J@||* + ${c1}gJJJJL` ${c2}g@FFS" + ${c1},@FFFJF`${c2}_g@@LLP` + ${c1}_@FFFFF`${c2}_@@@@@P` ${c4}. + ${c1}J@@@LLF ${c2}_@@@@@P` ${c4}.J! + ${c1}g@@@@@" ${c2}_@@@@@P`${c3}. ${c4}.L|||* +${c1}g@@@@M" ${c2}"VP`${c3}.L! ${c4}<@JJJJ` + ${c1}"@N" ${c3}:||||! ${c4}JFFFFS" + ${c3}.{JJ||F`${c4}_gFFFF@' + ${c3}.@FJJJF`${c4},@LFFFF` + ${c3}_@FFFFF ${c4}VLLLP` + ${c3}J@@LL@" ${c4}`" + ${c3}V@@" diff --git a/src/logo/ascii/amazon_linux.txt b/src/logo/ascii/amazon_linux.txt new file mode 100644 index 0000000000..9152678683 --- /dev/null +++ b/src/logo/ascii/amazon_linux.txt @@ -0,0 +1,10 @@ + , $2#_$1 + ~\_ $2####_$1 + ~~ \_$2#####\$1 + ~~ \$2###|$1 + ~~ \$2#/$1 ___ + ~~ V~' '-> + ~~~ / + ~~._. _/ + _/ _/ + _/m/' diff --git a/src/logo/ascii/aoscos.txt b/src/logo/ascii/aoscos.txt index 2886c092dd..8681218b45 100644 --- a/src/logo/ascii/aoscos.txt +++ b/src/logo/ascii/aoscos.txt @@ -1,20 +1,17 @@ - .:+syhhhhys+:. - .ohNMMMMMMMMMMMMMMNho. - `+mMMMMMMMMMMmdmNMMMMMMMMm+` - +NMMMMMMMMMMMM/ `./smMMMMMN+ - .mMMMMMMMMMMMMMMo -yMMMMMm. - :NMMMMMMMMMMMMMMMs .hMMMMN: - .NMMMMhmMMMMMMMMMMm+/- oMMMMN. - dMMMMs ./ymMMMMMMMMMMNy. sMMMMd --MMMMN` oMMMMMMMMMMMN: `NMMMM- -/MMMMh NMMMMMMMMMMMMm hMMMM/ -/MMMMh NMMMMMMMMMMMMm hMMMM/ --MMMMN` :MMMMMMMMMMMMy. `NMMMM- - dMMMMs .yNMMMMMMMMMMMNy/. sMMMMd - .NMMMMo -/+sMMMMMMMMMMMmMMMMN. - :NMMMMh. .MMMMMMMMMMMMMMMN: - .mMMMMMy- NMMMMMMMMMMMMMm. - +NMMMMMms/.` mMMMMMMMMMMMN+ - `+mMMMMMMMMNmddMMMMMMMMMMm+` - .ohNMMMMMMMMMMMMMMNho. - .:+syhhhhys+:. + ${c2}__ + ${c2}gpBBBBBBBBBP + ${c2}_gBBBBBBBBBRP + ${c2}4BBBBBBBBRP ${c4},_____ + ${c2}`"" ${c4}_g@@@@@@@@@@@@@%g> + ${c4}__@@@@@@@@@@@@@@@@P" ${c1}___ + ${c4}_g@@@@@@@@@@@@@@@N"` ${c1}_gN@@@@@N^ + ${c4}_w@@@@@@@@@@@@@@@@P" ${c1}_g@@@@@@@P" + ${c4}_g@@@@@@@@@@@@@@@N"` ${c1}VMNN@NNNM^` +${c4}^MMM@@@@@@@@@@@MP" ${c3},ggppww__ + ${c4}`""""" ${c3}_wNNNNNNNNNNNNNNNNNNN + ${c3}_gBNNNNNNNNNNNNNNNNNP" + ${c3}_wNNNNNNNNNNNNNNNNNNMP` + ${c3}_gBNNNNNNNNNNNNNNNNNP" + ${c3}_wNNNNNNNNNNNNNNNNNNNM^ + ${c3}""Y^^MNNNNNNNNNNNNP` + ${c3}`""""""" diff --git a/src/logo/ascii/aoscos_old.txt b/src/logo/ascii/aoscos_old.txt new file mode 100644 index 0000000000..2886c092dd --- /dev/null +++ b/src/logo/ascii/aoscos_old.txt @@ -0,0 +1,20 @@ + .:+syhhhhys+:. + .ohNMMMMMMMMMMMMMMNho. + `+mMMMMMMMMMMmdmNMMMMMMMMm+` + +NMMMMMMMMMMMM/ `./smMMMMMN+ + .mMMMMMMMMMMMMMMo -yMMMMMm. + :NMMMMMMMMMMMMMMMs .hMMMMN: + .NMMMMhmMMMMMMMMMMm+/- oMMMMN. + dMMMMs ./ymMMMMMMMMMMNy. sMMMMd +-MMMMN` oMMMMMMMMMMMN: `NMMMM- +/MMMMh NMMMMMMMMMMMMm hMMMM/ +/MMMMh NMMMMMMMMMMMMm hMMMM/ +-MMMMN` :MMMMMMMMMMMMy. `NMMMM- + dMMMMs .yNMMMMMMMMMMMNy/. sMMMMd + .NMMMMo -/+sMMMMMMMMMMMmMMMMN. + :NMMMMh. .MMMMMMMMMMMMMMMN: + .mMMMMMy- NMMMMMMMMMMMMMm. + +NMMMMMms/.` mMMMMMMMMMMMN+ + `+mMMMMMMMMNmddMMMMMMMMMMm+` + .ohNMMMMMMMMMMMMMMNho. + .:+syhhhhys+:. diff --git a/src/logo/ascii/elbrus.txt b/src/logo/ascii/elbrus.txt new file mode 100644 index 0000000000..b70cac89c6 --- /dev/null +++ b/src/logo/ascii/elbrus.txt @@ -0,0 +1,14 @@ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +██▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀██ +██ ██ +██ ███████ ███████ ██ +██ ██ ██ ██ ██ ██ +██ ██ ██ ██ ██ ██ +██ ██ ██ ██ ██ ██ +██ ██ ██ ██ ██ ██ +██ ██ ███████ ███████ +██ ██ ██ +██ ██▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄██ +██ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀██ +██ ██ +███████████████████████████ diff --git a/src/logo/ascii/evolutionos.txt b/src/logo/ascii/evolutionos.txt index 7a6622d4a9..18ca20e235 100644 --- a/src/logo/ascii/evolutionos.txt +++ b/src/logo/ascii/evolutionos.txt @@ -1,18 +1,21 @@ - dddddddddddddddddddddddd -.dddd''''''''''''''''''''''dddd. -dd: dddddddddddddddddddd; dd: -dd: ldl:'''''''''''''''' dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: ddddddd; ddddd; dd: -dd: ldl: ''''''' ''''' dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: dd: -dd: ldl: ddddddddddddddd; dd: -dddd:.''' ''''''''''''''' dddd: - dddddddddddddddddddddddddd;;' - ''''''''''''''''''''''''' + $2.':ldxxxxdo:,. + .lXMMMMMMMMMMMMMMMMNo' + dWMMMMMMMMMMMMMMMMMMMMMMWk + .OMMMMWWWWWWWWWWWWWWWWWWWMMMMM0; + kMMMMM$1XxxxxkkkkkkkkkkkkkkkK$2WMMMMMK. + .kMMMMMM$1Xddd0KKKKKKKKKKKKKKKN$2MMMMMMMN. + KMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMMX. + cMMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMMMo + KMMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMMMN + XMMMMMMMM$1XdddX$2WO$1kkkkkkkK$2WK$1OOOX$2MMMMMMMMMW + XMMMMMMMM$1XdddX$2WO$1kkkkkkkK$2WK$1OOOX$2MMMMMMMMMW + 0MMMMMMMM$1XdddX$2MWWWWWWWWWMWWWWWMMMMMMMMMN + cMMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMMMd + .kMMMMMMM$1XdddX$2MMMMMMMMMMMMMMMMMMMMMMMM0. + .kMMMMMM$1XxxxN$2W0$1OOOOOOOOOOOOOK$2MMMMMMMK. + oMMMMM$1XxxxN$2W0$1OOOOOOOOOOOOOK$2WMMMMMk. + '0MMMWNNNWMWWWWWWWWWWWWWWWMMMMX; + cWMMMMMMMMMMMMMMMMMMMMMMWd + :KWMMMMMMMMMMMMMMWXo. + .cdO0KK00xl' + .. diff --git a/src/logo/ascii/evolutionos_old.txt b/src/logo/ascii/evolutionos_old.txt new file mode 100644 index 0000000000..7a6622d4a9 --- /dev/null +++ b/src/logo/ascii/evolutionos_old.txt @@ -0,0 +1,18 @@ + dddddddddddddddddddddddd +.dddd''''''''''''''''''''''dddd. +dd: dddddddddddddddddddd; dd: +dd: ldl:'''''''''''''''' dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: ddddddd; ddddd; dd: +dd: ldl: ''''''' ''''' dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: dd: +dd: ldl: ddddddddddddddd; dd: +dddd:.''' ''''''''''''''' dddd: + dddddddddddddddddddddddddd;;' + ''''''''''''''''''''''''' diff --git a/src/logo/ascii/evolutionos_small.txt b/src/logo/ascii/evolutionos_small.txt new file mode 100644 index 0000000000..8692019a9e --- /dev/null +++ b/src/logo/ascii/evolutionos_small.txt @@ -0,0 +1,9 @@ + $2,coddoc' + 'cddddddddddc' + 'ddd$1OWWXXXXXXK$2ddo. +.dddd$1OMX$2ddddddddddd. +odddd$1OMX$2k$100O$2k$1OO$2ddddo +.dddd$1OMX$2kOOOxOkdddd. + .ddd$1OWW$2X$1XXXXXK$2ddd' + 'dddddddddddd' + 'cddddd, diff --git a/src/logo/ascii/lainos.txt b/src/logo/ascii/lainos.txt new file mode 100644 index 0000000000..9bd6c55305 --- /dev/null +++ b/src/logo/ascii/lainos.txt @@ -0,0 +1,20 @@ +${c2} /==\\ + \\==/ +${c1} · · · · · · · + · · · · · · · · · · + · · · ${c2}.-======-.${c1}· · · · +${c2} .::.${c1} ·${c2}.-============-.${c1}· ${c2}.::. + .:==:${c1}· ${c2}.:===:'${c1}. ·· .${c2}':===:.${c1} ·${c2}:==:. + .:===:${c1} · ${c2}:===.${c1} · ${c3}.--.${c1} · ${c2}.===:${c1} · ${c2}:===:. + :===:${c1}· · ${c2}:===.${c1} · ${c3}.:====:.${c1} · ${c2}.===:${c1} · ·${c2}:===: +(===:${c1}· · ${c2}:===-${c1} · ${c3}:======:${c1} · ${c2}-===:${c1} · ·${c2}:===) + :===:${c1}· · ${c2}:===.${c1} · ${c3}':====:'${c1} · ${c2}.===:${c1} · ·${c2}:===: + ':===:${c1} · ${c2}:===.${c1} · ${c3}'--'${c1} · ${c2}.===:${c1} · ${c2}:===:' + ':==:${c1}· ${c2}':===:.${c1}' ·· '${c2}.:===:'${c1} ·${c2}:==:' + '::'${c1} · ${c2}'===-. .-==='${c1} · ${c2}'::' + ${c2}/==\\${c1} · · · ${c2}:=== ===:${c1} · · · ${c2}/==\\ + \\==/${c1} · · ·${c2}:===${c1} ·${c2}===:${c1}· · · ${c2}\\==/${c2} + .-. ${c1}· ${c2}:===${c1}· ${c2}===:${c1} ·${c2} ${c2}.-. + .===. .=== ===. .===. + .======== ========. + ''''' ''''' diff --git a/src/logo/ascii/ubuntu_old.txt b/src/logo/ascii/ubuntu_old.txt index 5a816363b1..2b158dd4c0 100644 --- a/src/logo/ascii/ubuntu_old.txt +++ b/src/logo/ascii/ubuntu_old.txt @@ -1,5 +1,5 @@ - .-/+oossssoo+/-. - `:+ssssssssssssssssss+:` + --+oossssssoo+-- + .:+ssssssssssssssssss+:. -+ssssssssssssssssssyyssss+- .ossssssssssssssssssd$2MMMNy$1sssso. /sssssssssss$2hdmmNNmmyNMMMMh$1ssssss/ @@ -16,5 +16,5 @@ oss$2yNMMMNyMMh$1ssssssssssssss$2hmmmh$1ssssssso /sssssssssss$2hdmNNNNmyNMMMMh$1ssssss/ .ossssssssssssssssss$2dMMMNy$1sssso. -+sssssssssssssssss$2yyy$1ssss+- - `:+ssssssssssssssssss+:` - .-/+oossssoo+/-. \ No newline at end of file + `:+ssssssssssssssssss+:´ + --+oossssssoo+-- diff --git a/src/logo/ascii/windows_11_small.txt b/src/logo/ascii/windows_11_small.txt index c04184f246..ee78eb35ab 100644 --- a/src/logo/ascii/windows_11_small.txt +++ b/src/logo/ascii/windows_11_small.txt @@ -1,9 +1,7 @@ -$1lllllllll $2lllllllll -$1lllllllll $2lllllllll -$1lllllllll $2lllllllll -$1lllllllll $2lllllllll +$1lllllll $2lllllll +$1lllllll $2lllllll +$1lllllll $2lllllll -$3lllllllll $4lllllllll -$3lllllllll $4lllllllll -$3lllllllll $4lllllllll -$3lllllllll $4lllllllll +$3lllllll $4lllllll +$3lllllll $4lllllll +$3lllllll $4lllllll diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 8793280626..2063635713 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -22,6 +22,17 @@ static const FFlogo A[] = { FF_COLOR_FG_CYAN, }, }, + // Afterglow + { + .names = {"Afterglow"}, + .lines = FASTFETCH_DATATEXT_LOGO_AFTERGLOW, + .colors = { + FF_COLOR_FG_MAGENTA, + FF_COLOR_FG_RED, + FF_COLOR_FG_YELLOW, + FF_COLOR_FG_BLUE, + }, + }, // AIX { .names = {"aix"}, @@ -98,9 +109,16 @@ static const FFlogo A[] = { .colors = { FF_COLOR_FG_YELLOW, FF_COLOR_FG_WHITE, - }, - .colorKeys = FF_COLOR_FG_YELLOW, - .colorTitle = FF_COLOR_FG_WHITE, + } + }, + // AmazonLinux + { + .names = {"Amazon Linux", "amzn"}, + .lines = FASTFETCH_DATATEXT_LOGO_AMAZON_LINUX, + .colors = { + FF_COLOR_FG_WHITE, + FF_COLOR_FG_256 "178", + } }, // AmogOS { @@ -195,6 +213,17 @@ static const FFlogo A[] = { .lines = FASTFETCH_DATATEXT_LOGO_AOSCOS, .colors = { FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLACK, + FF_COLOR_FG_GREEN, + FF_COLOR_FG_YELLOW, + }, + }, + // AoscOS_old + { + .names = {"Aosc OS_old", "aoscos_old"}, + .lines = FASTFETCH_DATATEXT_LOGO_AOSCOS_OLD, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .colors = { FF_COLOR_FG_WHITE, }, .colorKeys = FF_COLOR_FG_BLUE, @@ -1170,6 +1199,16 @@ static const FFlogo D[] = { }; static const FFlogo E[] = { + // Elbrus + { + .names = {"elbrus"}, + .lines = FASTFETCH_DATATEXT_LOGO_ELBRUS, + .colors = { + FF_COLOR_FG_BLUE, + }, + .colorKeys = FF_COLOR_FG_BLUE, + .colorTitle = FF_COLOR_FG_BLUE, + }, // Elementary { .names = {"Elementary"}, @@ -1261,6 +1300,26 @@ static const FFlogo E[] = { { .names = {"EvolutionOS"}, .lines = FASTFETCH_DATATEXT_LOGO_EVOLUTIONOS, + .colors = { + FF_COLOR_FG_GREEN, + FF_COLOR_FG_WHITE, + }, + }, + // EvolutionOSSmall + { + .names = {"EvolutionOS_small"}, + .type = FF_LOGO_LINE_TYPE_SMALL_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_EVOLUTIONOS_SMALL, + .colors = { + FF_COLOR_FG_GREEN, + FF_COLOR_FG_WHITE, + }, + }, + // EvolutionOS_old + { + .names = {"EvolutionOS_old"}, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_EVOLUTIONOS_OLD, .colors = { FF_COLOR_FG_LIGHT_BLUE, FF_COLOR_FG_WHITE, @@ -2087,6 +2146,16 @@ static const FFlogo L[] = { .colorKeys = FF_COLOR_FG_GREEN, .colorTitle = FF_COLOR_FG_WHITE, }, + // LainOS + { + .names = {"LainOS"}, + .lines = FASTFETCH_DATATEXT_LOGO_LAINOS, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_256 "14", + FF_COLOR_FG_WHITE, + }, + }, // Lunar { .names = {"Lunar"}, @@ -2705,7 +2774,7 @@ static const FFlogo O[] = { }, // Oracle { - .names = {"oracle", "oracle linux"}, + .names = {"oracle", "oracle linux", "oracle linux server"}, .lines = FASTFETCH_DATATEXT_LOGO_ORACLE, .colors = { FF_COLOR_FG_RED, @@ -3453,7 +3522,7 @@ static const FFlogo S[] = { .lines = FASTFETCH_DATATEXT_LOGO_SLACKWARE, .colors = { FF_COLOR_FG_BLUE, - FF_COLOR_FG_BLUE, + FF_COLOR_FG_WHITE, }, .colorKeys = FF_COLOR_FG_BLUE, .colorTitle = FF_COLOR_FG_BLUE, @@ -4014,6 +4083,9 @@ static const FFlogo W[] = { .lines = FASTFETCH_DATATEXT_LOGO_WINDOWS_11_SMALL, .colors = { FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLUE, }, .colorKeys = FF_COLOR_FG_YELLOW, .colorTitle = FF_COLOR_FG_CYAN, diff --git a/src/logo/image/image.c b/src/logo/image/image.c index 9277ee3a53..953d12f8db 100644 --- a/src/logo/image/image.c +++ b/src/logo/image/image.c @@ -11,7 +11,7 @@ #include #endif -static FFstrbuf base64Encode(FFstrbuf* in) +static FFstrbuf base64Encode(const FFstrbuf* in) { const char* base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -37,55 +37,148 @@ static FFstrbuf base64Encode(FFstrbuf* in) static bool printImageIterm(void) { - if(instance.config.logo.width == 0 || instance.config.logo.height == 0) - { - fputs("Logo: `iterm` protocol only works when both `--logo-width` and `--logo-height` being specified\n", stderr); - return false; - } - + const FFLogoOptions* options = &instance.config.logo; FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); - if(!ffAppendFileBuffer(instance.config.logo.source.chars, &buf)) + if(!ffAppendFileBuffer(options->source.chars, &buf)) { fputs("Logo: Failed to load image file\n", stderr); return false; } - ffPrintCharTimes('\n', instance.config.logo.paddingTop); - ffPrintCharTimes(' ', instance.config.logo.paddingLeft); + fflush(stdout); FF_STRBUF_AUTO_DESTROY base64 = base64Encode(&buf); - instance.state.logoWidth = instance.config.logo.width + instance.config.logo.paddingLeft + instance.config.logo.paddingRight; - instance.state.logoHeight = instance.config.logo.paddingTop + instance.config.logo.height; - printf("\033]1337;File=inline=1;width=%u;height=%u;preserveAspectRatio=%u:%s\a\033[9999999D\n\033[%uA", - (unsigned) instance.config.logo.width, - (unsigned) instance.config.logo.height, - (unsigned) instance.config.logo.preserveAspectRadio, - base64.chars, - (unsigned) instance.state.logoHeight - ); + ffStrbufClear(&buf); + + if (!options->width || !options->height) + { + if (!options->separate) + { + ffStrbufAppendF(&buf, "\e[2J\e[3J\e[%u;%uH", + (unsigned) options->paddingTop, + (unsigned) options->paddingLeft + ); + } + else + { + ffStrbufAppendNC(&buf, options->paddingTop, '\n'); + ffStrbufAppendNC(&buf, options->paddingLeft, ' '); + } + ffStrbufAppendF(&buf, "\e]1337;File=inline=1:%s\a", + base64.chars + ); + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); + + if (!options->separate) + { + uint16_t X = 0, Y = 0; + const char* error = ffGetTerminalResponse("\e[6n", "\e[%hu;%huR", &Y, &X); + if (error) + { + fprintf(stderr, "\nLogo (iterm): fail to query cursor position: %s\n", error); + return true; // We already printed image logo, don't print ascii logo then + } + instance.state.logoWidth = X + options->paddingRight; + instance.state.logoHeight = Y; + fputs("\e[H", stdout); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffPrintCharTimes('\n', options->paddingRight); + } + } + else + { + ffStrbufAppendNC(&buf, options->paddingTop, '\n'); + ffStrbufAppendNC(&buf, options->paddingLeft, ' '); + ffStrbufAppendF(&buf, "\e]1337;File=inline=1;width=%u;height=%u;preserveAspectRatio=%u:%s\a\n", + (unsigned) options->width, + (unsigned) options->height, + (unsigned) options->preserveAspectRadio, + base64.chars + ); + + if (!options->separate) + { + instance.state.logoWidth = options->width + options->paddingLeft + options->paddingRight; + instance.state.logoHeight = options->paddingTop + options->height; + ffStrbufAppendF(&buf, "\e[%uA", (unsigned) instance.state.logoHeight); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffStrbufAppendNC(&buf, options->paddingRight, '\n'); + } + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); + } return true; } static bool printImageKittyDirect(void) { - if (!instance.config.logo.width || !instance.config.logo.height) + const FFLogoOptions* options = &instance.config.logo; + FF_STRBUF_AUTO_DESTROY base64 = base64Encode(&options->source); + + if (!options->width || !options->height) { - fputs("Logo: `kitty-direct` protocol only works when both `--logo-width` and `--logo-height` being specified\n", stderr); - return false; + if (!options->separate) + { + // We must clear the entre screen to make sure that terminal buffer won't scroll up + printf("\e[2J\e[3J\e[%u;%uH", + (unsigned) options->paddingTop, + (unsigned) options->paddingLeft + ); + } + else + { + ffPrintCharTimes('\n', options->paddingTop); + ffPrintCharTimes(' ', options->paddingLeft); + } + printf("\e_Ga=T,f=100,t=f;%s\e\\", base64.chars); + fflush(stdout); + if (!options->separate) + { + uint16_t X = 0, Y = 0; + const char* error = ffGetTerminalResponse("\e[6n", "\e[%hu;%huR", &Y, &X); + if (error) + { + fprintf(stderr, "\nLogo (kitty-direct): fail to query cursor position: %s\n", error); + return true; // We already printed image logo, don't print ascii logo then + } + instance.state.logoWidth = X + options->paddingRight; + instance.state.logoHeight = Y; + fputs("\e[H", stdout); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffPrintCharTimes('\n', options->paddingRight); + } + } + else + { + ffPrintCharTimes('\n', options->paddingTop); + ffPrintCharTimes(' ', options->paddingLeft); + + printf("\e_Ga=T,f=100,t=f,c=%u,r=%u;%s\e\\\n", + (unsigned) options->width, + (unsigned) options->height, + base64.chars + ); + if (!options->separate) + { + instance.state.logoWidth = options->width + options->paddingLeft + options->paddingRight; + instance.state.logoHeight = options->paddingTop + options->height; + printf("\e[%uA", (unsigned) instance.state.logoHeight); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffPrintCharTimes('\n', options->paddingRight); + } } - ffPrintCharTimes('\n', instance.config.logo.paddingTop); - ffPrintCharTimes(' ', instance.config.logo.paddingLeft); - - FF_STRBUF_AUTO_DESTROY base64 = base64Encode(&instance.config.logo.source); - instance.state.logoWidth = instance.config.logo.width + instance.config.logo.paddingLeft + instance.config.logo.paddingRight; - instance.state.logoHeight = instance.config.logo.paddingTop + instance.config.logo.height; - printf("\033_Ga=T,f=100,t=f,c=%u,r=%u;%s\033\\\033[9999999D\n\033[%uA", - (unsigned) instance.config.logo.width, - (unsigned) instance.config.logo.height, - base64.chars, - (unsigned) instance.state.logoHeight - ); return true; } @@ -203,31 +296,28 @@ static void writeCacheUint32(FFLogoRequestData* requestData, uint32_t value, con static void printImagePixels(FFLogoRequestData* requestData, const FFstrbuf* result, const char* cacheFileName) { + const FFLogoOptions* options = &instance.config.logo; //Calculate character dimensions - instance.state.logoWidth = requestData->logoCharacterWidth + instance.config.logo.paddingLeft + instance.config.logo.paddingRight; - - instance.state.logoHeight = requestData->logoCharacterHeight + instance.config.logo.paddingTop; - if(requestData->type == FF_LOGO_TYPE_IMAGE_KITTY) - instance.state.logoHeight -= 1; + instance.state.logoWidth = requestData->logoCharacterWidth + options->paddingLeft + options->paddingRight; + instance.state.logoHeight = requestData->logoCharacterHeight + options->paddingTop - 1; //Write cache files writeCacheStrbuf(requestData, result, cacheFileName); - if(instance.config.logo.width == 0) - writeCacheUint32(requestData, instance.state.logoWidth, FF_CACHE_FILE_WIDTH); + if(options->width == 0) + writeCacheUint32(requestData, requestData->logoCharacterWidth, FF_CACHE_FILE_WIDTH); - if(instance.config.logo.height == 0) - writeCacheUint32(requestData, instance.state.logoHeight, FF_CACHE_FILE_HEIGHT); + if(options->height == 0) + writeCacheUint32(requestData, requestData->logoCharacterHeight, FF_CACHE_FILE_HEIGHT); //Write result to stdout - ffPrintCharTimes('\n', instance.config.logo.paddingTop); - ffPrintCharTimes(' ', instance.config.logo.paddingLeft); + ffPrintCharTimes('\n', options->paddingTop); + ffPrintCharTimes(' ', options->paddingLeft); fflush(stdout); ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), result); //Go to upper left corner - fputs("\033[9999999D", stdout); - printf("\033[%uA", instance.state.logoHeight); + printf("\e[1G\e[%uA", instance.state.logoHeight); } static bool printImageSixel(FFLogoRequestData* requestData, const ImageData* imageData) @@ -533,10 +623,7 @@ static uint32_t readCachedUint32(FFLogoRequestData* requestData, const char* cac uint32_t result = 0; if(content.length != sizeof(result)) - { - ffStrbufDestroy(&content); return 0; - } memcpy(&result, content.chars, sizeof(result)); @@ -603,7 +690,7 @@ static bool printCachedPixel(FFLogoRequestData* requestData) instance.state.logoHeight = requestData->logoCharacterHeight + instance.config.logo.paddingTop; //Go to upper left corner - printf("\033[9999999D\033[%uA", instance.state.logoHeight); + printf("\e[1G\e[%uA", instance.state.logoHeight); return true; } diff --git a/src/logo/logo.c b/src/logo/logo.c index 9206a5ab17..e03ba72219 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -19,16 +19,38 @@ typedef enum FFLogoSize static void ffLogoPrintCharsRaw(const char* data, size_t length) { FFLogoOptions* options = &instance.config.logo; + FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); - if (instance.config.brightColor) - fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout); - - ffPrintCharTimes('\n', options->paddingTop); - ffPrintCharTimes(' ', options->paddingLeft); - fwrite(data, length, 1, stdout); - instance.state.logoHeight = options->paddingTop + options->height; - instance.state.logoWidth = options->paddingLeft + options->width + options->paddingRight; - printf("\033[9999999D\n\033[%uA", instance.state.logoHeight); + if (!options->width || !options->height) + { + ffStrbufAppendF(&buf, "\e[2J\e[3J\e[%u;%uH", + (unsigned) options->paddingTop, + (unsigned) options->paddingLeft + ); + ffStrbufAppendNS(&buf, (uint32_t) length, data); + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); + + uint16_t X = 0, Y = 0; + const char* error = ffGetTerminalResponse("\e[6n", "\e[%hu;%huR", &Y, &X); + if (error) + { + fprintf(stderr, "\nLogo (image-raw): fail to query cursor position: %s\n", error); + return; + } + instance.state.logoWidth = X + instance.config.logo.paddingRight; + instance.state.logoHeight = Y; + fputs("\e[H", stdout); + } + else + { + ffStrbufAppendNC(&buf, options->paddingTop, '\n'); + ffStrbufAppendNC(&buf, options->paddingLeft, ' '); + ffStrbufAppendNS(&buf, (uint32_t) length, data); + instance.state.logoHeight = options->paddingTop + options->height; + instance.state.logoWidth = options->paddingLeft + options->width + options->paddingRight; + ffStrbufAppendF(&buf, "\n\e[%uA", instance.state.logoHeight); + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf); + } } void ffLogoPrintChars(const char* data, bool doColorReplacement) @@ -37,33 +59,35 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) uint32_t currentlineLength = 0; + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateA(2048); + if (instance.config.brightColor) - fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout); + ffStrbufAppendS(&result, FASTFETCH_TEXT_MODIFIER_BOLT); - ffPrintCharTimes('\n', options->paddingTop); - ffPrintCharTimes(' ', options->paddingLeft); + ffStrbufAppendNC(&result, options->paddingTop, '\n'); + ffStrbufAppendNC(&result, options->paddingLeft, ' '); instance.state.logoHeight = options->paddingTop; //Use logoColor[0] as the default color if(doColorReplacement) - ffPrintColor(&options->colors[0]); + ffStrbufAppendF(&result, "\e[%sm", options->colors[0].chars); while(*data != '\0') { //We are at the end of a line. Print paddings and update max line length if(*data == '\n' || (*data == '\r' && *(data + 1) == '\n')) { - ffPrintCharTimes(' ', options->paddingRight); + ffStrbufAppendNC(&result, options->paddingRight, ' '); //We have \r\n, skip the \r if(*data == '\r') ++data; - putchar('\n'); + ffStrbufAppendC(&result, '\n'); ++data; - ffPrintCharTimes(' ', options->paddingLeft); + ffStrbufAppendNC(&result, options->paddingLeft, ' '); if(currentlineLength > instance.state.logoWidth) instance.state.logoWidth = currentlineLength; @@ -76,26 +100,26 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) //Always print tabs as 4 spaces, to have consistent spacing if(*data == '\t') { - ffPrintCharTimes(' ', 4); + ffStrbufAppendNC(&result, 4, ' '); ++data; continue; } //We have an escape sequence direclty as bytes. We print it, but don't increase the line length - if(*data == '\033' && *(data + 1) == '[') + if(*data == '\e' && *(data + 1) == '[') { const char* start = data; - fputs("\033[", stdout); + ffStrbufAppendS(&result, "\e["); data += 2; while(isdigit(*data) || *data == ';') - putchar(*data++); // number + ffStrbufAppendC(&result, *data++); // number //We have a valid control sequence, print it and continue with next char if(isascii(*data)) { - putchar(*data++); // single letter, end of control sequence + ffStrbufAppendC(&result, *data++); // single letter, end of control sequence continue; } @@ -113,7 +137,7 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) //If we have $$, or $\0, print it as single $ if(*data == '$' || *data == '\0') { - putchar('$'); + ffStrbufAppendC(&result, '$'); ++currentlineLength; ++data; continue; @@ -125,13 +149,13 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) //If the index is valid, print the color. Otherwise continue as normal if(index < 0 || index >= FASTFETCH_LOGO_MAX_COLORS) { - putchar('$'); + ffStrbufAppendC(&result, '$'); ++currentlineLength; //Don't continue here, we want to print the current char as unicode } else { - ffPrintColor(&options->colors[index]); + ffStrbufAppendF(&result, "\e[%sm", options->colors[index].chars); ++data; continue; } @@ -160,34 +184,39 @@ void ffLogoPrintChars(const char* data, bool doColorReplacement) if(*data == '\0') break; - putchar(*data++); + ffStrbufAppendC(&result, *data++); } } - ffPrintCharTimes(' ', options->paddingRight); - fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); + ffStrbufAppendS(&result, FASTFETCH_TEXT_MODIFIER_RESET); - //Happens if the last line is the longest - if(currentlineLength > instance.state.logoWidth) - instance.state.logoWidth = currentlineLength; + if(!options->separate) + { + //Happens if the last line is the longest + if(currentlineLength > instance.state.logoWidth) + instance.state.logoWidth = currentlineLength; - instance.state.logoWidth += options->paddingLeft + options->paddingRight; + instance.state.logoWidth += options->paddingLeft + options->paddingRight; - //Go to the leftmost position - fputs("\033[9999999D", stdout); + //Go to the leftmost position and go up the height + ffStrbufAppendF(&result, "\e[1G\e[%uA", instance.state.logoHeight); + } + else + { + instance.state.logoWidth = instance.state.logoHeight = 0; + ffStrbufAppendNC(&result, options->paddingRight, '\n'); + } - //If the logo height is > 1, go up the height - if(instance.state.logoHeight > 0) - printf("\033[%uA", instance.state.logoHeight); + ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &result); } static void logoApplyColors(const FFlogo* logo) { - if(instance.config.colorKeys.length == 0) - ffStrbufAppendS(&instance.config.colorKeys, logo->colorKeys ? logo->colorKeys : logo->colors[0]); - if(instance.config.colorTitle.length == 0) - ffStrbufAppendS(&instance.config.colorTitle, logo->colorTitle ? logo->colorTitle : logo->colors[1]); + ffStrbufAppendS(&instance.config.colorTitle, logo->colorTitle ? logo->colorTitle : logo->colors[0]); + + if(instance.config.colorKeys.length == 0) + ffStrbufAppendS(&instance.config.colorKeys, logo->colorKeys ? logo->colorKeys : logo->colors[1]); } static bool logoHasName(const FFlogo* logo, const FFstrbuf* name, bool small) @@ -417,15 +446,7 @@ static bool logoTryKnownType(void) return logoPrintFileIfExists(false, false); if(options->type == FF_LOGO_TYPE_IMAGE_RAW) - { - if(options->width == 0 || options->height == 0) - { - fputs("both `--logo-width` and `--logo-height` must be specified\n", stderr); - return false; - } - return logoPrintFileIfExists(false, true); - } return logoPrintImageIfExists(options->type, true); } @@ -441,7 +462,7 @@ void ffLogoPrint(void) //In pipe mode, we don't have a logo or padding. //We also don't need to set main color, because it won't be printed anyway. //So we can return quickly here. - if(instance.config.pipe) + if(instance.config.pipe || instance.state.resultDoc) { instance.state.logoHeight = 0; instance.state.logoWidth = 0; diff --git a/src/logo/option.c b/src/logo/option.c index 59474a2d32..32114de227 100644 --- a/src/logo/option.c +++ b/src/logo/option.c @@ -17,6 +17,7 @@ void ffInitLogoOptions(FFLogoOptions* options) options->printRemaining = true; options->preserveAspectRadio = false; options->recache = false; + options->separate = false; options->chafaFgOnly = false; ffStrbufInitStatic(&options->chafaSymbols, "block+border+space-wide-inverted"); // Chafa default @@ -116,6 +117,8 @@ bool ffParseLogoCommandOptions(FFLogoOptions* options, const char* key, const ch options->preserveAspectRadio = ffOptionParseBoolean(value); else if(strcasecmp(subKey, "recache") == 0) options->recache = ffOptionParseBoolean(value); + else if(strcasecmp(subKey, "separate") == 0) + options->separate = ffOptionParseBoolean(value); else return false; } @@ -342,6 +345,11 @@ const char* ffParseLogoJsonConfig(void) options->recache = yyjson_get_bool(val); continue; } + else if (strcasecmp(key, "separate") == 0) + { + options->separate = yyjson_get_bool(val); + continue; + } else if (strcasecmp(key, "chafa") == 0) { if (!yyjson_is_obj(val)) diff --git a/src/logo/option.h b/src/logo/option.h index 1eb41c1e90..9273e2a3f2 100644 --- a/src/logo/option.h +++ b/src/logo/option.h @@ -36,6 +36,7 @@ typedef struct FFLogoOptions bool printRemaining; bool preserveAspectRadio; bool recache; + bool separate; bool chafaFgOnly; FFstrbuf chafaSymbols; diff --git a/src/modules/battery/battery.c b/src/modules/battery/battery.c index 4f239650ac..85103b5c8b 100644 --- a/src/modules/battery/battery.c +++ b/src/modules/battery/battery.c @@ -8,7 +8,7 @@ #define FF_BATTERY_NUM_FORMAT_ARGS 5 -static void printBattery(FFBatteryOptions* options, BatteryResult* result, uint8_t index) +static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uint8_t index) { if(options->moduleArgs.outputFormat.length == 0) { @@ -76,8 +76,7 @@ static void printBattery(FFBatteryOptions* options, BatteryResult* result, uint8 void ffPrintBattery(FFBatteryOptions* options) { - FFlist results; - ffListInitA(&results, sizeof(BatteryResult), 0); + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFBatteryResult)); const char* error = ffDetectBattery(options, &results); @@ -89,7 +88,7 @@ void ffPrintBattery(FFBatteryOptions* options) { for(uint8_t i = 0; i < (uint8_t) results.length; i++) { - BatteryResult* result = ffListGet(&results, i); + FFBatteryResult* result = ffListGet(&results, i); printBattery(options, result, i); ffStrbufDestroy(&result->manufacturer); @@ -100,13 +99,11 @@ void ffPrintBattery(FFBatteryOptions* options) if(results.length == 0) ffPrintError(FF_BATTERY_MODULE_NAME, 0, &options->moduleArgs, "No batteries found"); } - - ffListDestroy(&results); } void ffInitBatteryOptions(FFBatteryOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BATTERY_MODULE_NAME, ffParseBatteryCommandOptions, ffParseBatteryJsonObject, ffPrintBattery); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BATTERY_MODULE_NAME, ffParseBatteryCommandOptions, ffParseBatteryJsonObject, ffPrintBattery, ffGenerateBatteryJson); ffOptionInitModuleArg(&options->moduleArgs); options->temp = false; @@ -178,3 +175,36 @@ void ffParseBatteryJsonObject(FFBatteryOptions* options, yyjson_val* module) ffPrintError(FF_BATTERY_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBatteryJson(FFBatteryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFBatteryResult)); + + const char* error = ffDetectBattery(options, &results); + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + + FF_LIST_FOR_EACH(FFBatteryResult, battery, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_real(doc, obj, "capacity", battery->capacity); + yyjson_mut_obj_add_strbuf(doc, obj, "manufacturer", &battery->manufacturer); + yyjson_mut_obj_add_strbuf(doc, obj, "modelName", &battery->modelName); + yyjson_mut_obj_add_strbuf(doc, obj, "status", &battery->status); + yyjson_mut_obj_add_strbuf(doc, obj, "technology", &battery->technology); + yyjson_mut_obj_add_real(doc, obj, "temperature", battery->temperature); + } + + FF_LIST_FOR_EACH(FFBatteryResult, battery, results) + { + ffStrbufDestroy(&battery->manufacturer); + ffStrbufDestroy(&battery->modelName); + ffStrbufDestroy(&battery->technology); + ffStrbufDestroy(&battery->status); + } +} diff --git a/src/modules/battery/battery.h b/src/modules/battery/battery.h index 26312bca7d..adefbdeddf 100644 --- a/src/modules/battery/battery.h +++ b/src/modules/battery/battery.h @@ -10,3 +10,4 @@ void ffInitBatteryOptions(FFBatteryOptions* options); bool ffParseBatteryCommandOptions(FFBatteryOptions* options, const char* key, const char* value); void ffDestroyBatteryOptions(FFBatteryOptions* options); void ffParseBatteryJsonObject(FFBatteryOptions* options, yyjson_val* module); +void ffGenerateBatteryJson(FFBatteryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/bios/bios.c b/src/modules/bios/bios.c index ee42017955..b3f3e598ce 100644 --- a/src/modules/bios/bios.c +++ b/src/modules/bios/bios.c @@ -55,7 +55,7 @@ void ffPrintBios(FFBiosOptions* options) void ffInitBiosOptions(FFBiosOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BIOS_MODULE_NAME, ffParseBiosCommandOptions, ffParseBiosJsonObject, ffPrintBios); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BIOS_MODULE_NAME, ffParseBiosCommandOptions, ffParseBiosJsonObject, ffPrintBios, ffGenerateBiosJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -90,3 +90,38 @@ void ffParseBiosJsonObject(FFBiosOptions* options, yyjson_val* module) ffPrintError(FF_BIOS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBiosJson(FF_MAYBE_UNUSED FFBiosOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFBiosResult bios; + ffStrbufInit(&bios.date); + ffStrbufInit(&bios.release); + ffStrbufInit(&bios.vendor); + ffStrbufInit(&bios.version); + + const char* error = ffDetectBios(&bios); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if (bios.version.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "bios_version is not set."); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "date", &bios.date); + yyjson_mut_obj_add_strbuf(doc, obj, "release", &bios.release); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &bios.vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &bios.version); + +exit: + ffStrbufDestroy(&bios.date); + ffStrbufDestroy(&bios.release); + ffStrbufDestroy(&bios.vendor); + ffStrbufDestroy(&bios.version); +} diff --git a/src/modules/bios/bios.h b/src/modules/bios/bios.h index aaaf15d650..a3bb81d8d1 100644 --- a/src/modules/bios/bios.h +++ b/src/modules/bios/bios.h @@ -9,3 +9,4 @@ void ffInitBiosOptions(FFBiosOptions* options); bool ffParseBiosCommandOptions(FFBiosOptions* options, const char* key, const char* value); void ffDestroyBiosOptions(FFBiosOptions* options); void ffParseBiosJsonObject(FFBiosOptions* options, yyjson_val* module); +void ffGenerateBiosJson(FFBiosOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/bluetooth/bluetooth.c b/src/modules/bluetooth/bluetooth.c index fa8b65d51b..ade503bc1c 100644 --- a/src/modules/bluetooth/bluetooth.c +++ b/src/modules/bluetooth/bluetooth.c @@ -6,7 +6,7 @@ #define FF_BLUETOOTH_NUM_FORMAT_ARGS 4 -static void printDevice(FFBluetoothOptions* options, const FFBluetoothDevice* device, uint8_t index) +static void printDevice(FFBluetoothOptions* options, const FFBluetoothResult* device, uint8_t index) { if(options->moduleArgs.outputFormat.length == 0) { @@ -34,7 +34,7 @@ static void printDevice(FFBluetoothOptions* options, const FFBluetoothDevice* de void ffPrintBluetooth(FFBluetoothOptions* options) { - FF_LIST_AUTO_DESTROY devices = ffListCreate(sizeof (FFBluetoothDevice)); + FF_LIST_AUTO_DESTROY devices = ffListCreate(sizeof (FFBluetoothResult)); const char* error = ffDetectBluetooth(&devices); if(error) @@ -43,14 +43,14 @@ void ffPrintBluetooth(FFBluetoothOptions* options) } else { - FF_LIST_AUTO_DESTROY filtered = ffListCreate(sizeof(FFBluetoothDevice*)); + FF_LIST_AUTO_DESTROY filtered = ffListCreate(sizeof(FFBluetoothResult*)); - FF_LIST_FOR_EACH(FFBluetoothDevice, device, devices) + FF_LIST_FOR_EACH(FFBluetoothResult, device, devices) { if(!device->connected && !options->showDisconnected) continue; - *(FFBluetoothDevice**)ffListAdd(&filtered) = device; + *(FFBluetoothResult**)ffListAdd(&filtered) = device; } if(filtered.length == 0) @@ -61,11 +61,11 @@ void ffPrintBluetooth(FFBluetoothOptions* options) for(uint32_t i = 0; i < filtered.length; i++) { uint8_t index = (uint8_t) (filtered.length == 1 ? 0 : i + 1); - printDevice(options, *(FFBluetoothDevice**)ffListGet(&filtered, i), index); + printDevice(options, *(FFBluetoothResult**)ffListGet(&filtered, i), index); } } - FF_LIST_FOR_EACH(FFBluetoothDevice, device, devices) + FF_LIST_FOR_EACH(FFBluetoothResult, device, devices) { ffStrbufDestroy(&device->name); ffStrbufDestroy(&device->type); @@ -75,7 +75,7 @@ void ffPrintBluetooth(FFBluetoothOptions* options) void ffInitBluetoothOptions(FFBluetoothOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BLUETOOTH_MODULE_NAME, ffParseBluetoothCommandOptions, ffParseBluetoothJsonObject, ffPrintBluetooth); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BLUETOOTH_MODULE_NAME, ffParseBluetoothCommandOptions, ffParseBluetoothJsonObject, ffPrintBluetooth, ffGenerateBluetoothJson); ffOptionInitModuleArg(&options->moduleArgs); options->showDisconnected = false; } @@ -123,3 +123,36 @@ void ffParseBluetoothJsonObject(FFBluetoothOptions* options, yyjson_val* module) ffPrintError(FF_BLUETOOTH_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBluetoothJson(FF_MAYBE_UNUSED FFBluetoothOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFBluetoothResult)); + + const char* error = ffDetectBluetooth(&results); + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + else + { + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + + FF_LIST_FOR_EACH(FFBluetoothResult, item, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "address", &item->address); + yyjson_mut_obj_add_uint(doc, obj, "battery", item->battery); + yyjson_mut_obj_add_bool(doc, obj, "connected", item->connected); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_strbuf(doc, obj, "type", &item->type); + } + } + + FF_LIST_FOR_EACH(FFBluetoothResult, device, results) + { + ffStrbufDestroy(&device->name); + ffStrbufDestroy(&device->type); + ffStrbufDestroy(&device->address); + } +} diff --git a/src/modules/bluetooth/bluetooth.h b/src/modules/bluetooth/bluetooth.h index 411bb39651..b4d1f0a89f 100644 --- a/src/modules/bluetooth/bluetooth.h +++ b/src/modules/bluetooth/bluetooth.h @@ -9,3 +9,4 @@ void ffInitBluetoothOptions(FFBluetoothOptions* options); bool ffParseBluetoothCommandOptions(FFBluetoothOptions* options, const char* key, const char* value); void ffDestroyBluetoothOptions(FFBluetoothOptions* options); void ffParseBluetoothJsonObject(FFBluetoothOptions* options, yyjson_val* module); +void ffGenerateBluetoothJson(FFBluetoothOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/board/board.c b/src/modules/board/board.c index 29a676ca83..17e187adee 100644 --- a/src/modules/board/board.c +++ b/src/modules/board/board.c @@ -51,7 +51,7 @@ void ffPrintBoard(FFBoardOptions* options) void ffInitBoardOptions(FFBoardOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BOARD_MODULE_NAME, ffParseBoardCommandOptions, ffParseBoardJsonObject, ffPrintBoard); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BOARD_MODULE_NAME, ffParseBoardCommandOptions, ffParseBoardJsonObject, ffPrintBoard, NULL); ffOptionInitModuleArg(&options->moduleArgs); } @@ -86,3 +86,35 @@ void ffParseBoardJsonObject(FFBoardOptions* options, yyjson_val* module) ffPrintError(FF_BOARD_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBoardJson(FF_MAYBE_UNUSED FFBoardOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFBoardResult board; + ffStrbufInit(&board.name); + ffStrbufInit(&board.vendor); + ffStrbufInit(&board.version); + + const char* error = ffDetectBoard(&board); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if (board.name.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "board_name is not set."); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &board.name); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &board.vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &board.version); + +exit: + ffStrbufDestroy(&board.name); + ffStrbufDestroy(&board.vendor); + ffStrbufDestroy(&board.version); +} diff --git a/src/modules/board/board.h b/src/modules/board/board.h index f5531368e7..0e1dabbaba 100644 --- a/src/modules/board/board.h +++ b/src/modules/board/board.h @@ -9,3 +9,4 @@ void ffInitBoardOptions(FFBoardOptions* options); bool ffParseBoardCommandOptions(FFBoardOptions* options, const char* key, const char* value); void ffDestroyBoardOptions(FFBoardOptions* options); void ffParseBoardJsonObject(FFBoardOptions* options, yyjson_val* module); +void ffGenerateBoardJson(FFBoardOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/break/break.c b/src/modules/break/break.c index 87821ee001..59c39c1d72 100644 --- a/src/modules/break/break.c +++ b/src/modules/break/break.c @@ -9,7 +9,7 @@ void ffPrintBreak(FF_MAYBE_UNUSED FFBreakOptions* options) void ffInitBreakOptions(FF_MAYBE_UNUSED FFBreakOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BREAK_MODULE_NAME, ffParseBreakCommandOptions, ffParseBreakJsonObject, ffPrintBreak); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BREAK_MODULE_NAME, ffParseBreakCommandOptions, ffParseBreakJsonObject, ffPrintBreak, NULL); } bool ffParseBreakCommandOptions(FF_MAYBE_UNUSED FFBreakOptions* options, FF_MAYBE_UNUSED const char* key, FF_MAYBE_UNUSED const char* value) diff --git a/src/modules/brightness/brightness.c b/src/modules/brightness/brightness.c index 560353749f..261122bb06 100644 --- a/src/modules/brightness/brightness.c +++ b/src/modules/brightness/brightness.c @@ -5,7 +5,7 @@ #include "modules/brightness/brightness.h" #include "util/stringUtils.h" -#define FF_BRIGHTNESS_NUM_FORMAT_ARGS 2 +#define FF_BRIGHTNESS_NUM_FORMAT_ARGS 5 void ffPrintBrightness(FFBrightnessOptions* options) { @@ -43,6 +43,8 @@ void ffPrintBrightness(FFBrightnessOptions* options) }); } + const double percent = (item->current - item->min) / (item->max - item->min) * 100; + if(options->moduleArgs.outputFormat.length == 0) { FF_STRBUF_AUTO_DESTROY str = ffStrbufCreate(); @@ -50,7 +52,7 @@ void ffPrintBrightness(FFBrightnessOptions* options) if (instance.config.percentType & FF_PERCENTAGE_TYPE_BAR_BIT) { - ffAppendPercentBar(&str, item->value, 0, 100, 100); + ffAppendPercentBar(&str, percent, 0, 100, 100); } if(instance.config.percentType & FF_PERCENTAGE_TYPE_NUM_BIT) @@ -58,7 +60,7 @@ void ffPrintBrightness(FFBrightnessOptions* options) if(str.length > 0) ffStrbufAppendC(&str, ' '); - ffAppendPercentNum(&str, item->value, 10, 10, str.length > 0); + ffAppendPercentNum(&str, percent, 10, 10, str.length > 0); } ffStrbufPutTo(&str, stdout); @@ -66,10 +68,13 @@ void ffPrintBrightness(FFBrightnessOptions* options) else { FF_STRBUF_AUTO_DESTROY valueStr = ffStrbufCreate(); - ffAppendPercentNum(&valueStr, item->value, 10, 10, false); + ffAppendPercentNum(&valueStr, percent, 10, 10, false); ffPrintFormatString(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_BRIGHTNESS_NUM_FORMAT_ARGS, (FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &valueStr}, {FF_FORMAT_ARG_TYPE_STRBUF, &item->name}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &item->max}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &item->min}, + {FF_FORMAT_ARG_TYPE_DOUBLE, &item->current}, }); } @@ -81,7 +86,7 @@ void ffPrintBrightness(FFBrightnessOptions* options) void ffInitBrightnessOptions(FFBrightnessOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BRIGHTNESS_MODULE_NAME, ffParseBrightnessCommandOptions, ffParseBrightnessJsonObject, ffPrintBrightness); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_BRIGHTNESS_MODULE_NAME, ffParseBrightnessCommandOptions, ffParseBrightnessJsonObject, ffPrintBrightness, ffGenerateBrightnessJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -116,3 +121,39 @@ void ffParseBrightnessJsonObject(FFBrightnessOptions* options, yyjson_val* modul ffPrintError(FF_BRIGHTNESS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateBrightnessJson(FF_MAYBE_UNUSED FFBrightnessOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFBrightnessResult)); + + const char* error = ffDetectBrightness(&result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + if(result.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No result is detected."); + return; + } + + yyjson_mut_val* arr = yyjson_mut_arr(doc); + yyjson_mut_obj_add_val(doc, module, "result", arr); + + FF_LIST_FOR_EACH(FFBrightnessResult, item, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_real(doc, obj, "max", item->max); + yyjson_mut_obj_add_real(doc, obj, "min", item->min); + yyjson_mut_obj_add_real(doc, obj, "current", item->current); + } + + FF_LIST_FOR_EACH(FFBrightnessResult, item, result) + { + ffStrbufDestroy(&item->name); + } +} diff --git a/src/modules/brightness/brightness.h b/src/modules/brightness/brightness.h index 1272e5d0f4..6933c65009 100644 --- a/src/modules/brightness/brightness.h +++ b/src/modules/brightness/brightness.h @@ -9,3 +9,4 @@ void ffInitBrightnessOptions(FFBrightnessOptions* options); bool ffParseBrightnessCommandOptions(FFBrightnessOptions* options, const char* key, const char* value); void ffDestroyBrightnessOptions(FFBrightnessOptions* options); void ffParseBrightnessJsonObject(FFBrightnessOptions* options, yyjson_val* module); +void ffGenerateBrightnessJson(FFBrightnessOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/chassis/chassis.c b/src/modules/chassis/chassis.c index 02cd3e97d9..28a0f557dc 100644 --- a/src/modules/chassis/chassis.c +++ b/src/modules/chassis/chassis.c @@ -52,7 +52,7 @@ void ffPrintChassis(FFChassisOptions* options) void ffInitChassisOptions(FFChassisOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CHASSIS_MODULE_NAME, ffParseChassisCommandOptions, ffParseChassisJsonObject, ffPrintChassis); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CHASSIS_MODULE_NAME, ffParseChassisCommandOptions, ffParseChassisJsonObject, ffPrintChassis, ffGenerateChassisJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -87,3 +87,35 @@ void ffParseChassisJsonObject(FFChassisOptions* options, yyjson_val* module) ffPrintError(FF_CHASSIS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateChassisJson(FF_MAYBE_UNUSED FFChassisOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFChassisResult result; + ffStrbufInit(&result.type); + ffStrbufInit(&result.vendor); + ffStrbufInit(&result.version); + + const char* error = ffDetectChassis(&result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if(result.type.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "chassis_type is not set by O.E.M."); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "type", &result.type); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &result.vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version); + +exit: + ffStrbufDestroy(&result.type); + ffStrbufDestroy(&result.vendor); + ffStrbufDestroy(&result.version); +} diff --git a/src/modules/chassis/chassis.h b/src/modules/chassis/chassis.h index 5fe4bb62fd..c5553f7722 100644 --- a/src/modules/chassis/chassis.h +++ b/src/modules/chassis/chassis.h @@ -9,3 +9,4 @@ void ffInitChassisOptions(FFChassisOptions* options); bool ffParseChassisCommandOptions(FFChassisOptions* options, const char* key, const char* value); void ffDestroyChassisOptions(FFChassisOptions* options); void ffParseChassisJsonObject(FFChassisOptions* options, yyjson_val* module); +void ffGenerateChassisJson(FFChassisOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/colors/colors.c b/src/modules/colors/colors.c index 488413f214..4bcf9bafd2 100644 --- a/src/modules/colors/colors.c +++ b/src/modules/colors/colors.c @@ -9,7 +9,7 @@ void ffPrintColors(FFColorsOptions* options) if(instance.config.pipe) return; - ffLogoPrintLine(); + ffPrintLogoAndKey(FF_COLORS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if(options->paddingLeft > 0) ffPrintCharTimes(' ', options->paddingLeft); @@ -57,7 +57,9 @@ void ffPrintColors(FFColorsOptions* options) void ffInitColorsOptions(FFColorsOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_COLORS_MODULE_NAME, ffParseColorsCommandOptions, ffParseColorsJsonObject, ffPrintColors); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_COLORS_MODULE_NAME, ffParseColorsCommandOptions, ffParseColorsJsonObject, ffPrintColors, NULL); + ffOptionInitModuleArg(&options->moduleArgs); + ffStrbufSetStatic(&options->moduleArgs.key, " "); options->symbol = FF_COLORS_SYMBOL_BLOCK; options->paddingLeft = 0; } @@ -66,6 +68,8 @@ bool ffParseColorsCommandOptions(FFColorsOptions* options, const char* key, cons { const char* subKey = ffOptionTestPrefix(key, FF_COLORS_MODULE_NAME); if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; if (ffStrEqualsIgnCase(subKey, "symbol")) { @@ -92,6 +96,7 @@ bool ffParseColorsCommandOptions(FFColorsOptions* options, const char* key, cons void ffDestroyColorsOptions(FF_MAYBE_UNUSED FFColorsOptions* options) { + ffOptionDestroyModuleArg(&options->moduleArgs); } void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module) @@ -104,6 +109,9 @@ void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module) if(ffStrEqualsIgnCase(key, "type")) continue; + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + if (ffStrEqualsIgnCase(key, "symbol")) { int value; diff --git a/src/modules/colors/option.h b/src/modules/colors/option.h index a17029bd84..e51cea6f12 100644 --- a/src/modules/colors/option.h +++ b/src/modules/colors/option.h @@ -17,6 +17,8 @@ typedef enum FFColorsSymbol typedef struct FFColorsOptions { FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; + FFColorsSymbol symbol; uint32_t paddingLeft; } FFColorsOptions; diff --git a/src/modules/command/command.c b/src/modules/command/command.c index f19f1f27d9..393aab97b7 100644 --- a/src/modules/command/command.c +++ b/src/modules/command/command.c @@ -36,7 +36,7 @@ void ffPrintCommand(FFCommandOptions* options) void ffInitCommandOptions(FFCommandOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_COMMAND_MODULE_NAME, ffParseCommandCommandOptions, ffParseCommandJsonObject, ffPrintCommand); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_COMMAND_MODULE_NAME, ffParseCommandCommandOptions, ffParseCommandJsonObject, ffPrintCommand, ffGenerateCommandJson); ffOptionInitModuleArg(&options->moduleArgs); ffStrbufInitStatic(&options->shell, @@ -107,3 +107,32 @@ void ffParseCommandJsonObject(FFCommandOptions* options, yyjson_val* module) ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateCommandJson(FF_MAYBE_UNUSED FFCommandOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + const char* error = ffProcessAppendStdOut(&result, (char* const[]){ + options->shell.chars, + #ifdef _WIN32 + "/c", + #else + "-c", + #endif + options->text.chars, + NULL + }); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + if(!result.length) + { + yyjson_mut_obj_add_str(doc, module, "error", "No result printed"); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &result); +} diff --git a/src/modules/command/command.h b/src/modules/command/command.h index 8ee5cc82c1..ec8e2bab37 100644 --- a/src/modules/command/command.h +++ b/src/modules/command/command.h @@ -9,3 +9,4 @@ void ffInitCommandOptions(FFCommandOptions* options); bool ffParseCommandCommandOptions(FFCommandOptions* options, const char* key, const char* value); void ffDestroyCommandOptions(FFCommandOptions* options); void ffParseCommandJsonObject(FFCommandOptions* options, yyjson_val* module); +void ffGenerateCommandJson(FFCommandOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index 00aec6ba84..3cd5a6cf7a 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -79,7 +79,7 @@ void ffPrintCPU(FFCPUOptions* options) void ffInitCPUOptions(FFCPUOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CPU_MODULE_NAME, ffParseCPUCommandOptions, ffParseCPUJsonObject, ffPrintCPU); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CPU_MODULE_NAME, ffParseCPUCommandOptions, ffParseCPUJsonObject, ffPrintCPU, ffGenerateCPUJson); ffOptionInitModuleArg(&options->moduleArgs); options->temp = false; } @@ -127,3 +127,44 @@ void ffParseCPUJsonObject(FFCPUOptions* options, yyjson_val* module) ffPrintError(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateCPUJson(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFCPUResult cpu; + cpu.temperature = FF_CPU_TEMP_UNSET; + cpu.coresPhysical = cpu.coresLogical = cpu.coresOnline = 0; + cpu.frequencyMax = cpu.frequencyMin = 0; + ffStrbufInit(&cpu.name); + ffStrbufInit(&cpu.vendor); + + const char* error = ffDetectCPU(options, &cpu); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else if(cpu.vendor.length == 0 && cpu.name.length == 0 && cpu.coresOnline <= 1) + { + yyjson_mut_obj_add_str(doc, module, "error", "No CPU detected"); + } + else + { + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "cpu", &cpu.name); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &cpu.vendor); + + yyjson_mut_val* cores = yyjson_mut_obj_add_obj(doc, obj, "cores"); + yyjson_mut_obj_add_uint(doc, cores, "physical", cpu.coresPhysical); + yyjson_mut_obj_add_uint(doc, cores, "logical", cpu.coresLogical); + yyjson_mut_obj_add_uint(doc, cores, "online", cpu.coresOnline); + + yyjson_mut_val* frequency = yyjson_mut_obj_add_obj(doc, obj, "frequency"); + yyjson_mut_obj_add_real(doc, frequency, "min", cpu.frequencyMin); + yyjson_mut_obj_add_real(doc, frequency, "max", cpu.frequencyMax); + + yyjson_mut_obj_add_real(doc, obj, "temperature", cpu.temperature); + } + + ffStrbufDestroy(&cpu.name); + ffStrbufDestroy(&cpu.vendor); +} diff --git a/src/modules/cpu/cpu.h b/src/modules/cpu/cpu.h index acff0a13ce..0a3e7c4c62 100644 --- a/src/modules/cpu/cpu.h +++ b/src/modules/cpu/cpu.h @@ -9,3 +9,4 @@ void ffInitCPUOptions(FFCPUOptions* options); bool ffParseCPUCommandOptions(FFCPUOptions* options, const char* key, const char* value); void ffDestroyCPUOptions(FFCPUOptions* options); void ffParseCPUJsonObject(FFCPUOptions* options, yyjson_val* module); +void ffGenerateCPUJson(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/cpuusage/cpuusage.c b/src/modules/cpuusage/cpuusage.c index 23b29e7918..ca0909377a 100644 --- a/src/modules/cpuusage/cpuusage.c +++ b/src/modules/cpuusage/cpuusage.c @@ -46,7 +46,7 @@ void ffPrintCPUUsage(FFCPUUsageOptions* options) void ffInitCPUUsageOptions(FFCPUUsageOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CPUUSAGE_MODULE_NAME, ffParseCPUUsageCommandOptions, ffParseCPUUsageJsonObject, ffPrintCPUUsage); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CPUUSAGE_MODULE_NAME, ffParseCPUUsageCommandOptions, ffParseCPUUsageJsonObject, ffPrintCPUUsage, ffGenerateCPUUsageJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -81,3 +81,16 @@ void ffParseCPUUsageJsonObject(FFCPUUsageOptions* options, yyjson_val* module) ffPrintError(FF_CPUUSAGE_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateCPUUsageJson(FF_MAYBE_UNUSED FFCPUUsageOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + double percentage = 0.0/0.0; + const char* error = ffGetCpuUsageResult(&percentage); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + yyjson_mut_obj_add_real(doc, module, "result", percentage); +} diff --git a/src/modules/cpuusage/cpuusage.h b/src/modules/cpuusage/cpuusage.h index 5a221f554c..e31bc14c78 100644 --- a/src/modules/cpuusage/cpuusage.h +++ b/src/modules/cpuusage/cpuusage.h @@ -11,3 +11,4 @@ void ffInitCPUUsageOptions(FFCPUUsageOptions* options); bool ffParseCPUUsageCommandOptions(FFCPUUsageOptions* options, const char* key, const char* value); void ffDestroyCPUUsageOptions(FFCPUUsageOptions* options); void ffParseCPUUsageJsonObject(FFCPUUsageOptions* options, yyjson_val* module); +void ffGenerateCPUUsageJson(FFCPUUsageOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/cursor/cursor.c b/src/modules/cursor/cursor.c index c33e13d266..555ec0374c 100644 --- a/src/modules/cursor/cursor.c +++ b/src/modules/cursor/cursor.c @@ -52,7 +52,7 @@ void ffPrintCursor(FFCursorOptions* options) void ffInitCursorOptions(FFCursorOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CURSOR_MODULE_NAME, ffParseCursorCommandOptions, ffParseCursorJsonObject, ffPrintCursor); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CURSOR_MODULE_NAME, ffParseCursorCommandOptions, ffParseCursorJsonObject, ffPrintCursor, ffGenerateCursorJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -87,3 +87,27 @@ void ffParseCursorJsonObject(FFCursorOptions* options, yyjson_val* module) ffPrintError(FF_CURSOR_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateCursorJson(FF_MAYBE_UNUSED FFCursorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFCursorResult result; + ffStrbufInit(&result.error); + ffStrbufInit(&result.theme); + ffStrbufInit(&result.size); + + ffDetectCursor(&result); + + if (result.error.length) + { + yyjson_mut_obj_add_str(doc, module, "error", result.error.chars); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "theme", &result.theme); + yyjson_mut_obj_add_strbuf(doc, obj, "size", &result.size); + + ffStrbufDestroy(&result.error); + ffStrbufDestroy(&result.theme); + ffStrbufDestroy(&result.size); +} diff --git a/src/modules/cursor/cursor.h b/src/modules/cursor/cursor.h index ff849b5476..02ed47e8fd 100644 --- a/src/modules/cursor/cursor.h +++ b/src/modules/cursor/cursor.h @@ -9,3 +9,4 @@ void ffInitCursorOptions(FFCursorOptions* options); bool ffParseCursorCommandOptions(FFCursorOptions* options, const char* key, const char* value); void ffDestroyCursorOptions(FFCursorOptions* options); void ffParseCursorJsonObject(FFCursorOptions* options, yyjson_val* module); +void ffGenerateCursorJson(FFCursorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/custom/custom.c b/src/modules/custom/custom.c index b9406dcd36..6576cd3c5e 100644 --- a/src/modules/custom/custom.c +++ b/src/modules/custom/custom.c @@ -12,15 +12,16 @@ void ffPrintCustom(FFCustomOptions* options) return; } - ffPrintLogoAndKey(options->moduleArgs.key.length == 0 ? NULL : FF_CUSTOM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_CUSTOM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&options->moduleArgs.outputFormat, stdout); puts(FASTFETCH_TEXT_MODIFIER_RESET); } void ffInitCustomOptions(FFCustomOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CUSTOM_MODULE_NAME, ffParseCustomCommandOptions, ffParseCustomJsonObject, ffPrintCustom); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_CUSTOM_MODULE_NAME, ffParseCustomCommandOptions, ffParseCustomJsonObject, ffPrintCustom, NULL); ffOptionInitModuleArg(&options->moduleArgs); + ffStrbufSetStatic(&options->moduleArgs.key, " "); } bool ffParseCustomCommandOptions(FFCustomOptions* options, const char* key, const char* value) diff --git a/src/modules/datetime/datetime.c b/src/modules/datetime/datetime.c index 9d6307a132..adc7c50cf9 100644 --- a/src/modules/datetime/datetime.c +++ b/src/modules/datetime/datetime.c @@ -1,57 +1,149 @@ +#include "common/time.h" #include "common/printing.h" #include "common/jsonconfig.h" -#include "detection/datetime/datetime.h" #include "modules/datetime/datetime.h" #include "util/stringUtils.h" +#include + #define FF_DATETIME_DISPLAY_NAME "Date & Time" #define FF_DATETIME_NUM_FORMAT_ARGS 20 -void ffPrintDateTimeFormat(const char* moduleName, const FFModuleArgs* moduleArgs) +typedef struct FFDateTimeResult +{ + //Examples for 21.02.2022 - 15:18:37 + uint16_t year; //2022 + uint8_t yearShort; //22 + uint8_t month; //2 + FFstrbuf monthPretty; //02 + FFstrbuf monthName; //February + FFstrbuf monthNameShort; //Feb + uint8_t week; //8 + FFstrbuf weekday; //Monday + FFstrbuf weekdayShort; //Mon + uint16_t dayInYear; //52 + uint8_t dayInMonth; //21 + uint8_t dayInWeek; //1 + uint8_t hour; //15 + FFstrbuf hourPretty; //15 + uint8_t hour12; //3 + FFstrbuf hour12Pretty; //03 + uint8_t minute; //18 + FFstrbuf minutePretty; //18 + uint8_t second; //37 + FFstrbuf secondPretty; //37 +} FFDateTimeResult; + +void ffPrintDateTimeFormat(struct tm* tm, const FFModuleArgs* moduleArgs) { - const FFDateTimeResult* result = ffDetectDateTime(); - ffPrintFormat(moduleName, 0, moduleArgs, FF_DATETIME_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_UINT16, &result->year}, // 1 - {FF_FORMAT_ARG_TYPE_UINT8, &result->yearShort}, // 2 - {FF_FORMAT_ARG_TYPE_UINT8, &result->month}, // 3 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->monthPretty}, // 4 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->monthName}, // 5 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->monthNameShort}, // 6 - {FF_FORMAT_ARG_TYPE_UINT8, &result->week}, // 7 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->weekday}, // 8 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->weekdayShort}, // 9 - {FF_FORMAT_ARG_TYPE_UINT16, &result->dayInYear}, // 10 - {FF_FORMAT_ARG_TYPE_UINT8, &result->dayInMonth}, // 11 - {FF_FORMAT_ARG_TYPE_UINT8, &result->dayInWeek}, // 12 - {FF_FORMAT_ARG_TYPE_UINT8, &result->hour}, // 13 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->hourPretty}, // 14 - {FF_FORMAT_ARG_TYPE_UINT8, &result->hour12}, // 15 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->hour12Pretty}, // 16 - {FF_FORMAT_ARG_TYPE_UINT8, &result->minute}, // 17 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->minutePretty}, // 18 - {FF_FORMAT_ARG_TYPE_UINT8, &result->second}, // 19 - {FF_FORMAT_ARG_TYPE_STRBUF, &result->secondPretty} // 20 + FFDateTimeResult result; + + result.year = (uint16_t) (tm->tm_year + 1900); + result.yearShort = (uint8_t) (result.year % 100); + result.month = (uint8_t) (tm->tm_mon + 1); + + ffStrbufInitA(&result.monthPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.monthPretty.length = (uint32_t) strftime(result.monthPretty.chars, ffStrbufGetFree(&result.monthPretty), "%m", tm); + + ffStrbufInitA(&result.monthName, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.monthName.length = (uint32_t) strftime(result.monthName.chars, ffStrbufGetFree(&result.monthName), "%B", tm); + + ffStrbufInitA(&result.monthNameShort, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.monthNameShort.length = (uint32_t) strftime(result.monthNameShort.chars, ffStrbufGetFree(&result.monthNameShort), "%b", tm); + + result.week = (uint8_t) (tm->tm_yday / 7 + 1); + + ffStrbufInitA(&result.weekday, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.weekday.length = (uint32_t) strftime(result.weekday.chars, ffStrbufGetFree(&result.weekday), "%A", tm); + + ffStrbufInitA(&result.weekdayShort, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.weekdayShort.length = (uint32_t) strftime(result.weekdayShort.chars, ffStrbufGetFree(&result.weekdayShort), "%a", tm); + + result.dayInYear = (uint8_t) (tm->tm_yday + 1); + result.dayInMonth = (uint8_t) tm->tm_mday; + result.dayInWeek = tm->tm_wday == 0 ? 7 : (uint8_t) tm->tm_wday; + + result.hour = (uint8_t) tm->tm_hour; + + ffStrbufInitA(&result.hourPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.hourPretty.length = (uint32_t) strftime(result.hourPretty.chars, ffStrbufGetFree(&result.hourPretty), "%H", tm); + + result.hour12 = (uint8_t) (result.hour % 12); + + ffStrbufInitA(&result.hour12Pretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.hour12Pretty.length = (uint32_t) strftime(result.hour12Pretty.chars, ffStrbufGetFree(&result.hour12Pretty), "%I", tm); + + result.minute = (uint8_t) tm->tm_min; + + ffStrbufInitA(&result.minutePretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.minutePretty.length = (uint32_t) strftime(result.minutePretty.chars, ffStrbufGetFree(&result.minutePretty), "%M", tm); + + result.second = (uint8_t) tm->tm_sec; + + ffStrbufInitA(&result.secondPretty, FASTFETCH_STRBUF_DEFAULT_ALLOC); + result.secondPretty.length = (uint32_t) strftime(result.secondPretty.chars, ffStrbufGetFree(&result.secondPretty), "%S", tm); + + ffPrintFormat(FF_DATETIME_DISPLAY_NAME, 0, moduleArgs, FF_DATETIME_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_UINT16, &result.year}, // 1 + {FF_FORMAT_ARG_TYPE_UINT8, &result.yearShort}, // 2 + {FF_FORMAT_ARG_TYPE_UINT8, &result.month}, // 3 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.monthPretty}, // 4 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.monthName}, // 5 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.monthNameShort}, // 6 + {FF_FORMAT_ARG_TYPE_UINT8, &result.week}, // 7 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.weekday}, // 8 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.weekdayShort}, // 9 + {FF_FORMAT_ARG_TYPE_UINT16, &result.dayInYear}, // 10 + {FF_FORMAT_ARG_TYPE_UINT8, &result.dayInMonth}, // 11 + {FF_FORMAT_ARG_TYPE_UINT8, &result.dayInWeek}, // 12 + {FF_FORMAT_ARG_TYPE_UINT8, &result.hour}, // 13 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.hourPretty}, // 14 + {FF_FORMAT_ARG_TYPE_UINT8, &result.hour12}, // 15 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.hour12Pretty}, // 16 + {FF_FORMAT_ARG_TYPE_UINT8, &result.minute}, // 17 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.minutePretty}, // 18 + {FF_FORMAT_ARG_TYPE_UINT8, &result.second}, // 19 + {FF_FORMAT_ARG_TYPE_STRBUF, &result.secondPretty} // 20 }); + + ffStrbufDestroy(&result.hour12Pretty); + ffStrbufDestroy(&result.hourPretty); + ffStrbufDestroy(&result.minutePretty); + ffStrbufDestroy(&result.monthName); + ffStrbufDestroy(&result.monthNameShort); + ffStrbufDestroy(&result.monthPretty); + ffStrbufDestroy(&result.secondPretty); + ffStrbufDestroy(&result.weekday); + ffStrbufDestroy(&result.weekdayShort); } void ffPrintDateTime(FFDateTimeOptions* options) { + uint64_t msNow = ffTimeGetNow(); + time_t sNow = (time_t) (msNow / 1000); + struct tm* tm = localtime(&sNow); + if(options->moduleArgs.outputFormat.length > 0) { - ffPrintDateTimeFormat(FF_DATETIME_DISPLAY_NAME, &options->moduleArgs); + ffPrintDateTimeFormat(tm, &options->moduleArgs); + return; + } + + char buffer[32]; + if (strftime(buffer, sizeof(buffer), "%F %T", tm) == 0) //yyyy-MM-dd HH:mm:ss + { + ffPrintError(FF_DATETIME_DISPLAY_NAME, 0, &options->moduleArgs, "strftime() failed"); return; } - const FFDateTimeResult* datetime = ffDetectDateTime(); ffPrintLogoAndKey(FF_DATETIME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - //yyyy-MM-dd HH:mm:ss - printf("%u-%s-%02u %s:%s:%s\n", datetime->year, datetime->monthPretty.chars, datetime->dayInMonth, datetime->hourPretty.chars, datetime->minutePretty.chars, datetime->secondPretty.chars); + puts(buffer); } void ffInitDateTimeOptions(FFDateTimeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DATETIME_MODULE_NAME, ffParseDateTimeCommandOptions, ffParseDateTimeJsonObject, ffPrintDateTime); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DATETIME_MODULE_NAME, ffParseDateTimeCommandOptions, ffParseDateTimeJsonObject, ffPrintDateTime, ffGenerateDateTimeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -86,3 +178,8 @@ void ffParseDateTimeJsonObject(FFDateTimeOptions* options, yyjson_val* module) ffPrintError(FF_DATETIME_DISPLAY_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateDateTimeJson(FF_MAYBE_UNUSED FFDateTimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + yyjson_mut_obj_add_uint(doc, module, "result", ffTimeGetNow()); +} diff --git a/src/modules/datetime/datetime.h b/src/modules/datetime/datetime.h index 6ccb30b278..8f4ceb6908 100644 --- a/src/modules/datetime/datetime.h +++ b/src/modules/datetime/datetime.h @@ -9,3 +9,4 @@ void ffInitDateTimeOptions(FFDateTimeOptions* options); bool ffParseDateTimeCommandOptions(FFDateTimeOptions* options, const char* key, const char* value); void ffDestroyDateTimeOptions(FFDateTimeOptions* options); void ffParseDateTimeJsonObject(FFDateTimeOptions* options, yyjson_val* module); +void ffGenerateDateTimeJson(FFDateTimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/de/de.c b/src/modules/de/de.c index a44f437d7a..a6d896d7ce 100644 --- a/src/modules/de/de.c +++ b/src/modules/de/de.c @@ -42,7 +42,7 @@ void ffPrintDE(FFDEOptions* options) void ffInitDEOptions(FFDEOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DE_MODULE_NAME, ffParseDECommandOptions, ffParseDEJsonObject, ffPrintDE); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DE_MODULE_NAME, ffParseDECommandOptions, ffParseDEJsonObject, ffPrintDE, ffGenerateDEJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -77,3 +77,19 @@ void ffParseDEJsonObject(FFDEOptions* options, yyjson_val* module) ffPrintError(FF_DE_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateDEJson(FF_MAYBE_UNUSED FFDEOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFDisplayServerResult* result = ffConnectDisplayServer(); + + if(result->dePrettyName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No DE found"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->deProcessName); + yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &result->dePrettyName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->deVersion); +} diff --git a/src/modules/de/de.h b/src/modules/de/de.h index 0a4e145411..b8d122184f 100644 --- a/src/modules/de/de.h +++ b/src/modules/de/de.h @@ -9,3 +9,4 @@ void ffInitDEOptions(FFDEOptions* options); bool ffParseDECommandOptions(FFDEOptions* options, const char* key, const char* value); void ffDestroyDEOptions(FFDEOptions* options); void ffParseDEJsonObject(FFDEOptions* options, yyjson_val* module); +void ffGenerateDEJson(FFDEOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/disk/disk.c b/src/modules/disk/disk.c index 202d8e1a88..e6c976f0eb 100644 --- a/src/modules/disk/disk.c +++ b/src/modules/disk/disk.c @@ -6,7 +6,7 @@ #include "modules/disk/disk.h" #include "util/stringUtils.h" -#define FF_DISK_NUM_FORMAT_ARGS 10 +#define FF_DISK_NUM_FORMAT_ARGS 11 #pragma GCC diagnostic ignored "-Wsign-conversion" static void printDisk(FFDiskOptions* options, const FFDisk* disk) @@ -111,6 +111,19 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk) bool isExternal = !!(disk->type & FF_DISK_VOLUME_TYPE_EXTERNAL_BIT); bool isHidden = !!(disk->type & FF_DISK_VOLUME_TYPE_HIDDEN_BIT); bool isReadOnly = !!(disk->type & FF_DISK_VOLUME_TYPE_READONLY_BIT); + const char* physicalType; + switch(disk->physicalType) + { + case FF_DISK_PHYSICAL_TYPE_HDD: + physicalType = "HDD"; + break; + case FF_DISK_PHYSICAL_TYPE_SSD: + physicalType = "SSD"; + break; + default: + physicalType = "Unknown"; + break; + } ffPrintFormatString(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_DISK_NUM_FORMAT_ARGS, (FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &usedPretty}, {FF_FORMAT_ARG_TYPE_STRBUF, &totalPretty}, @@ -123,6 +136,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk) {FF_FORMAT_ARG_TYPE_STRBUF, &disk->filesystem}, {FF_FORMAT_ARG_TYPE_STRBUF, &disk->name}, {FF_FORMAT_ARG_TYPE_BOOL, &isReadOnly}, + {FF_FORMAT_ARG_TYPE_STRING, physicalType} }); } } @@ -194,6 +208,7 @@ void ffPrintDisk(FFDiskOptions* options) FF_LIST_FOR_EACH(FFDisk, disk, disks) { + ffStrbufDestroy(&disk->mountFrom); ffStrbufDestroy(&disk->mountpoint); ffStrbufDestroy(&disk->filesystem); ffStrbufDestroy(&disk->name); @@ -202,7 +217,7 @@ void ffPrintDisk(FFDiskOptions* options) void ffInitDiskOptions(FFDiskOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DISK_MODULE_NAME, ffParseDiskCommandOptions, ffParseDiskJsonObject, ffPrintDisk); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DISK_MODULE_NAME, ffParseDiskCommandOptions, ffParseDiskJsonObject, ffPrintDisk, ffGenerateDiskJson); ffOptionInitModuleArg(&options->moduleArgs); ffStrbufInit(&options->folders); @@ -370,3 +385,69 @@ void ffParseDiskJsonObject(FFDiskOptions* options, yyjson_val* module) ffPrintError(FF_DISK_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateDiskJson(FFDiskOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY disks = ffListCreate(sizeof (FFDisk)); + const char* error = ffDetectDisks(options, &disks); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "result", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + + FF_LIST_FOR_EACH(FFDisk, item, disks) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + + yyjson_mut_val* bytes = yyjson_mut_obj_add_obj(doc, obj, "bytes"); + yyjson_mut_obj_add_uint(doc, bytes, "available", item->bytesAvailable); + yyjson_mut_obj_add_uint(doc, bytes, "free", item->bytesFree); + yyjson_mut_obj_add_uint(doc, bytes, "total", item->bytesTotal); + yyjson_mut_obj_add_uint(doc, bytes, "used", item->bytesUsed); + + yyjson_mut_val* files = yyjson_mut_obj_add_obj(doc, obj, "files"); + yyjson_mut_obj_add_uint(doc, files, "total", item->filesTotal); + yyjson_mut_obj_add_uint(doc, files, "used", item->filesUsed); + + yyjson_mut_obj_add_strbuf(doc, obj, "filesystem", &item->filesystem); + yyjson_mut_obj_add_strbuf(doc, obj, "mountpoint", &item->mountpoint); + yyjson_mut_obj_add_strbuf(doc, obj, "mountFrom", &item->mountFrom); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_val* typeArr = yyjson_mut_obj_add_arr(doc, obj, "volumeType"); + if(item->type & FF_DISK_VOLUME_TYPE_REGULAR_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "Regular"); + if(item->type & FF_DISK_VOLUME_TYPE_EXTERNAL_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "External"); + if(item->type & FF_DISK_VOLUME_TYPE_SUBVOLUME_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "Subvolume"); + if(item->type & FF_DISK_VOLUME_TYPE_HIDDEN_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "Hidden"); + if(item->type & FF_DISK_VOLUME_TYPE_READONLY_BIT) + yyjson_mut_arr_add_str(doc, typeArr, "Read-only"); + + switch(item->physicalType) + { + case FF_DISK_PHYSICAL_TYPE_HDD: + yyjson_mut_obj_add_str(doc, obj, "physicalType", "HDD"); + break; + case FF_DISK_PHYSICAL_TYPE_SSD: + yyjson_mut_obj_add_str(doc, obj, "physicalType", "SSD"); + break; + default: + yyjson_mut_obj_add_null(doc, obj, "physicalType"); + break; + } + } + + FF_LIST_FOR_EACH(FFDisk, item, disks) + { + ffStrbufDestroy(&item->mountpoint); + ffStrbufDestroy(&item->mountFrom); + ffStrbufDestroy(&item->filesystem); + ffStrbufDestroy(&item->name); + } +} diff --git a/src/modules/disk/disk.h b/src/modules/disk/disk.h index 1f71f766be..eb4520b351 100644 --- a/src/modules/disk/disk.h +++ b/src/modules/disk/disk.h @@ -9,3 +9,4 @@ void ffInitDiskOptions(FFDiskOptions* options); bool ffParseDiskCommandOptions(FFDiskOptions* options, const char* key, const char* value); void ffDestroyDiskOptions(FFDiskOptions* options); void ffParseDiskJsonObject(FFDiskOptions* options, yyjson_val* module); +void ffGenerateDiskJson(FFDiskOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/display/display.c b/src/modules/display/display.c index 31371de42a..f623bc5f4f 100644 --- a/src/modules/display/display.c +++ b/src/modules/display/display.c @@ -85,6 +85,9 @@ void ffPrintDisplay(FFDisplayOptions* options) result->scaledHeight > 0 && result->scaledHeight != result->height) printf(" (as %ix%i)", result->scaledWidth, result->scaledHeight); + if(result->type != FF_DISPLAY_TYPE_UNKNOWN) + fputs(result->type == FF_DISPLAY_TYPE_BUILTIN ? " [Built-in]" : " [External]", stdout); + if(moduleIndex > 0 && result->primary) printf(" *"); @@ -109,7 +112,7 @@ void ffPrintDisplay(FFDisplayOptions* options) void ffInitDisplayOptions(FFDisplayOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DISPLAY_MODULE_NAME, ffParseDisplayCommandOptions, ffParseDisplayJsonObject, ffPrintDisplay); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_DISPLAY_MODULE_NAME, ffParseDisplayCommandOptions, ffParseDisplayJsonObject, ffPrintDisplay, ffGenerateDisplayJson); ffOptionInitModuleArg(&options->moduleArgs); options->compactType = FF_DISPLAY_COMPACT_TYPE_NONE; options->preciseRefreshRate = false; @@ -185,3 +188,40 @@ void ffParseDisplayJsonObject(FFDisplayOptions* options, yyjson_val* module) ffPrintError(FF_DISPLAY_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateDisplayJson(FF_MAYBE_UNUSED FFDisplayOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFDisplayServerResult* dsResult = ffConnectDisplayServer(); + + if(dsResult->displays.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Couldn't detect display"); + return; + } + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFDisplayResult, item, dsResult->displays) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_uint(doc, obj, "width", item->width); + yyjson_mut_obj_add_uint(doc, obj, "height", item->height); + yyjson_mut_obj_add_uint(doc, obj, "id", item->id); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_bool(doc, obj, "primary", item->primary); + yyjson_mut_obj_add_real(doc, obj, "refreshRate", item->refreshRate); + yyjson_mut_obj_add_uint(doc, obj, "rotation", item->rotation); + yyjson_mut_obj_add_uint(doc, obj, "scaledHeight", item->scaledHeight); + yyjson_mut_obj_add_uint(doc, obj, "scaledWidth", item->scaledWidth); + switch (item->type) + { + case FF_DISPLAY_TYPE_BUILTIN: + yyjson_mut_obj_add_str(doc, obj, "type", "Builtin"); + break; + case FF_DISPLAY_TYPE_EXTERNAL: + yyjson_mut_obj_add_str(doc, obj, "type", "External"); + break; + default: + yyjson_mut_obj_add_str(doc, obj, "type", "Unknown"); + break; + } + } +} diff --git a/src/modules/display/display.h b/src/modules/display/display.h index b2f7570564..6c46dfb18c 100644 --- a/src/modules/display/display.h +++ b/src/modules/display/display.h @@ -9,3 +9,4 @@ void ffInitDisplayOptions(FFDisplayOptions* options); bool ffParseDisplayCommandOptions(FFDisplayOptions* options, const char* key, const char* value); void ffDestroyDisplayOptions(FFDisplayOptions* options); void ffParseDisplayJsonObject(FFDisplayOptions* options, yyjson_val* module); +void ffGenerateDisplayJson(FFDisplayOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/font/font.c b/src/modules/font/font.c index 97c77359bd..a737c615e9 100644 --- a/src/modules/font/font.c +++ b/src/modules/font/font.c @@ -45,7 +45,7 @@ void ffPrintFont(FFFontOptions* options) void ffInitFontOptions(FFFontOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_FONT_MODULE_NAME, ffParseFontCommandOptions, ffParseFontJsonObject, ffPrintFont); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_FONT_MODULE_NAME, ffParseFontCommandOptions, ffParseFontJsonObject, ffPrintFont, ffGenerateFontJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -80,3 +80,29 @@ void ffParseFontJsonObject(FFFontOptions* options, yyjson_val* module) ffPrintError(FF_FONT_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateFontJson(FF_MAYBE_UNUSED FFFontOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFFontResult font; + for(uint32_t i = 0; i < FF_DETECT_FONT_NUM_FONTS; ++i) + ffStrbufInit(&font.fonts[i]); + ffStrbufInit(&font.display); + + const char* error = ffDetectFont(&font); + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else + { + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "display", &font.display); + yyjson_mut_val* fontsArr = yyjson_mut_obj_add_arr(doc, obj, "fonts"); + for (uint32_t i = 0; i < FF_DETECT_FONT_NUM_FONTS; ++i) + yyjson_mut_arr_add_strbuf(doc, fontsArr, &font.fonts[i]); + } + + ffStrbufDestroy(&font.display); + for (uint32_t i = 0; i < FF_DETECT_FONT_NUM_FONTS; ++i) + ffStrbufDestroy(&font.fonts[i]); +} diff --git a/src/modules/font/font.h b/src/modules/font/font.h index a51f9d4471..dedb1dfb95 100644 --- a/src/modules/font/font.h +++ b/src/modules/font/font.h @@ -9,3 +9,4 @@ void ffInitFontOptions(FFFontOptions* options); bool ffParseFontCommandOptions(FFFontOptions* options, const char* key, const char* value); void ffDestroyFontOptions(FFFontOptions* options); void ffParseFontJsonObject(FFFontOptions* options, yyjson_val* module); +void ffGenerateFontJson(FFFontOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/gamepad/gamepad.c b/src/modules/gamepad/gamepad.c index 3691b5ac87..43818aa2fa 100644 --- a/src/modules/gamepad/gamepad.c +++ b/src/modules/gamepad/gamepad.c @@ -51,7 +51,7 @@ void ffPrintGamepad(FFGamepadOptions* options) void ffInitGamepadOptions(FFGamepadOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_GAMEPAD_MODULE_NAME, ffParseGamepadCommandOptions, ffParseGamepadJsonObject, ffPrintGamepad); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_GAMEPAD_MODULE_NAME, ffParseGamepadCommandOptions, ffParseGamepadJsonObject, ffPrintGamepad, ffGenerateGamepadJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -86,3 +86,36 @@ void ffParseGamepadJsonObject(FFGamepadOptions* options, yyjson_val* module) ffPrintError(FF_GAMEPAD_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateGamepadJson(FF_MAYBE_UNUSED FFGamepadOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFGamepadDevice)); + + const char* error = ffDetectGamepad(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + if(!result.length) + { + yyjson_mut_obj_add_str(doc, module, "error", "No devices detected"); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFGamepadDevice, device, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "identifier", &device->identifier); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &device->name); + } + + FF_LIST_FOR_EACH(FFGamepadDevice, device, result) + { + ffStrbufDestroy(&device->identifier); + ffStrbufDestroy(&device->name); + } +} diff --git a/src/modules/gamepad/gamepad.h b/src/modules/gamepad/gamepad.h index eae8a8a175..eff98e9925 100644 --- a/src/modules/gamepad/gamepad.h +++ b/src/modules/gamepad/gamepad.h @@ -9,3 +9,4 @@ void ffInitGamepadOptions(FFGamepadOptions* options); bool ffParseGamepadCommandOptions(FFGamepadOptions* options, const char* key, const char* value); void ffDestroyGamepadOptions(FFGamepadOptions* options); void ffParseGamepadJsonObject(FFGamepadOptions* options, yyjson_val* module); +void ffGenerateGamepadJson(FFGamepadOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/gpu/gpu.c b/src/modules/gpu/gpu.c index 5f71779a7d..95a25df3b1 100644 --- a/src/modules/gpu/gpu.c +++ b/src/modules/gpu/gpu.c @@ -121,7 +121,7 @@ void ffPrintGPU(FFGPUOptions* options) void ffInitGPUOptions(FFGPUOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_GPU_MODULE_NAME, ffParseGPUCommandOptions, ffParseGPUJsonObject, ffPrintGPU); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_GPU_MODULE_NAME, ffParseGPUCommandOptions, ffParseGPUJsonObject, ffPrintGPU, ffGenerateGPUJson); ffOptionInitModuleArg(&options->moduleArgs); options->forceVulkan = false; @@ -210,3 +210,70 @@ void ffParseGPUJsonObject(FFGPUOptions* options, yyjson_val* module) ffPrintError(FF_GPU_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateGPUJson(FFGPUOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY gpus = ffListCreate(sizeof (FFGPUResult)); + const char* error = ffDetectGPU(options, &gpus); + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFGPUResult, gpu, gpus) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + if (gpu->coreCount != FF_GPU_CORE_COUNT_UNSET) + yyjson_mut_obj_add_int(doc, obj, "coreCount", gpu->coreCount); + else + yyjson_mut_obj_add_null(doc, obj, "coreCount"); + + yyjson_mut_val* memoryObj = yyjson_mut_obj_add_obj(doc, obj, "memory"); + + yyjson_mut_val* dedicatedObj = yyjson_mut_obj_add_obj(doc, memoryObj, "dedicated"); + if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, dedicatedObj, "total", gpu->dedicated.total); + else + yyjson_mut_obj_add_null(doc, dedicatedObj, "total"); + + if (gpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, dedicatedObj, "used", gpu->dedicated.used); + else + yyjson_mut_obj_add_null(doc, dedicatedObj, "used"); + + yyjson_mut_obj_add_strbuf(doc, obj, "driver", &gpu->driver); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &gpu->name); + + yyjson_mut_val* sharedObj = yyjson_mut_obj_add_obj(doc, memoryObj, "shared"); + if (gpu->shared.total != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, sharedObj, "total", gpu->shared.total); + else + yyjson_mut_obj_add_null(doc, sharedObj, "total"); + if (gpu->shared.used != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, sharedObj, "used", gpu->shared.used); + else + yyjson_mut_obj_add_null(doc, sharedObj, "used"); + + yyjson_mut_obj_add_null(doc, obj, "temperature"); + + const char* type; + switch (gpu->type) + { + case FF_GPU_TYPE_INTEGRATED: type = "Integrated"; break; + case FF_GPU_TYPE_DISCRETE: type = "Discrete"; break; + default: type = "Unknown"; break; + } + yyjson_mut_obj_add_str(doc, obj, "type", type); + + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &gpu->vendor); + } + + FF_LIST_FOR_EACH(FFGPUResult, gpu, gpus) + { + ffStrbufDestroy(&gpu->vendor); + ffStrbufDestroy(&gpu->name); + ffStrbufDestroy(&gpu->driver); + } +} diff --git a/src/modules/gpu/gpu.h b/src/modules/gpu/gpu.h index b5334730a0..73bd7fad51 100644 --- a/src/modules/gpu/gpu.h +++ b/src/modules/gpu/gpu.h @@ -9,3 +9,4 @@ void ffInitGPUOptions(FFGPUOptions* options); bool ffParseGPUCommandOptions(FFGPUOptions* options, const char* key, const char* value); void ffDestroyGPUOptions(FFGPUOptions* options); void ffParseGPUJsonObject(FFGPUOptions* options, yyjson_val* module); +void ffGenerateGPUJson(FFGPUOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/host/host.c b/src/modules/host/host.c index d281ece1b7..30df24f127 100644 --- a/src/modules/host/host.c +++ b/src/modules/host/host.c @@ -67,7 +67,7 @@ void ffPrintHost(FFHostOptions* options) void ffInitHostOptions(FFHostOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_HOST_MODULE_NAME, ffParseHostCommandOptions, ffParseHostJsonObject, ffPrintHost); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_HOST_MODULE_NAME, ffParseHostCommandOptions, ffParseHostJsonObject, ffPrintHost, ffGenerateHostJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -102,3 +102,40 @@ void ffParseHostJsonObject(FFHostOptions* options, yyjson_val* module) ffPrintError(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateHostJson(FF_MAYBE_UNUSED FFHostOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFHostResult host; + ffStrbufInit(&host.productFamily); + ffStrbufInit(&host.productName); + ffStrbufInit(&host.productVersion); + ffStrbufInit(&host.productSku); + ffStrbufInit(&host.sysVendor); + const char* error = ffDetectHost(&host); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if (host.productFamily.length == 0 && host.productName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "neither product_family nor product_name is set by O.E.M."); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "family", &host.productFamily); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &host.productName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &host.productVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "sku", &host.productSku); + yyjson_mut_obj_add_strbuf(doc, obj, "sysVender", &host.sysVendor); + +exit: + ffStrbufDestroy(&host.productFamily); + ffStrbufDestroy(&host.productName); + ffStrbufDestroy(&host.productVersion); + ffStrbufDestroy(&host.productSku); + ffStrbufDestroy(&host.sysVendor); +} diff --git a/src/modules/host/host.h b/src/modules/host/host.h index 5a689288af..4ed4575a1c 100644 --- a/src/modules/host/host.h +++ b/src/modules/host/host.h @@ -9,3 +9,4 @@ void ffInitHostOptions(FFHostOptions* options); bool ffParseHostCommandOptions(FFHostOptions* options, const char* key, const char* value); void ffDestroyHostOptions(FFHostOptions* options); void ffParseHostJsonObject(FFHostOptions* options, yyjson_val* module); +void ffGenerateHostJson(FFHostOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/icons/icons.c b/src/modules/icons/icons.c index aa1827a5fe..720432b70a 100644 --- a/src/modules/icons/icons.c +++ b/src/modules/icons/icons.c @@ -32,7 +32,7 @@ void ffPrintIcons(FFIconsOptions* options) void ffInitIconsOptions(FFIconsOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_ICONS_MODULE_NAME, ffParseIconsCommandOptions, ffParseIconsJsonObject, ffPrintIcons); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_ICONS_MODULE_NAME, ffParseIconsCommandOptions, ffParseIconsJsonObject, ffPrintIcons, ffGenerateIconsJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,17 @@ void ffParseIconsJsonObject(FFIconsOptions* options, yyjson_val* module) ffPrintError(FF_ICONS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateIconsJson(FF_MAYBE_UNUSED FFIconsOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY icons = ffStrbufCreate(); + const char* error = ffDetectIcons(&icons); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &icons); +} diff --git a/src/modules/icons/icons.h b/src/modules/icons/icons.h index 97533b096a..dab251e25e 100644 --- a/src/modules/icons/icons.h +++ b/src/modules/icons/icons.h @@ -9,3 +9,4 @@ void ffInitIconsOptions(FFIconsOptions* options); bool ffParseIconsCommandOptions(FFIconsOptions* options, const char* key, const char* value); void ffDestroyIconsOptions(FFIconsOptions* options); void ffParseIconsJsonObject(FFIconsOptions* options, yyjson_val* module); +void ffGenerateIconsJson(FFIconsOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/kernel/kernel.c b/src/modules/kernel/kernel.c index bc1bd21a8f..89aab0af12 100644 --- a/src/modules/kernel/kernel.c +++ b/src/modules/kernel/kernel.c @@ -7,32 +7,32 @@ void ffPrintKernel(FFKernelOptions* options) { + const FFPlatform* platform = &instance.state.platform; if(options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - ffStrbufWriteTo(&instance.state.platform.systemRelease, stdout); + ffStrbufWriteTo(&platform->systemRelease, stdout); - #ifdef _WIN32 - if(instance.state.platform.systemVersion.length > 0) - printf(" (%s)", instance.state.platform.systemVersion.chars); - #endif - - putchar('\n'); + if(platform->systemDisplayVersion.length > 0) + printf(" (%s)\n", platform->systemDisplayVersion.chars); + else + putchar('\n'); } else { ffPrintFormat(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, FF_KERNEL_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.systemName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.systemRelease}, - {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.systemVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.systemArchitecture} + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemRelease}, + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemVersion}, + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemArchitecture}, + {FF_FORMAT_ARG_TYPE_STRBUF, &platform->systemDisplayVersion} }); } } void ffInitKernelOptions(FFKernelOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_KERNEL_MODULE_NAME, ffParseKernelCommandOptions, ffParseKernelJsonObject, ffPrintKernel); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_KERNEL_MODULE_NAME, ffParseKernelCommandOptions, ffParseKernelJsonObject, ffPrintKernel, ffGenerateKernelJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,13 @@ void ffParseKernelJsonObject(FFKernelOptions* options, yyjson_val* module) ffPrintError(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateKernelJson(FF_MAYBE_UNUSED FFKernelOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "architecture", &instance.state.platform.systemArchitecture); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &instance.state.platform.systemName); + yyjson_mut_obj_add_strbuf(doc, obj, "release", &instance.state.platform.systemRelease); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &instance.state.platform.systemVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "displayVersion", &instance.state.platform.systemDisplayVersion); +} diff --git a/src/modules/kernel/kernel.h b/src/modules/kernel/kernel.h index 943641d602..419f2544a3 100644 --- a/src/modules/kernel/kernel.h +++ b/src/modules/kernel/kernel.h @@ -9,3 +9,4 @@ void ffInitKernelOptions(FFKernelOptions* options); bool ffParseKernelCommandOptions(FFKernelOptions* options, const char* key, const char* value); void ffDestroyKernelOptions(FFKernelOptions* options); void ffParseKernelJsonObject(FFKernelOptions* options, yyjson_val* module); +void ffGenerateKernelJson(FFKernelOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/lm/lm.c b/src/modules/lm/lm.c index afef101a03..f84a422a34 100644 --- a/src/modules/lm/lm.c +++ b/src/modules/lm/lm.c @@ -51,7 +51,7 @@ void ffPrintLM(FFLMOptions* options) void ffInitLMOptions(FFLMOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LM_MODULE_NAME, ffParseLMCommandOptions, ffParseLMJsonObject, ffPrintLM); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LM_MODULE_NAME, ffParseLMCommandOptions, ffParseLMJsonObject, ffPrintLM, NULL); ffOptionInitModuleArg(&options->moduleArgs); } @@ -86,3 +86,34 @@ void ffParseLMJsonObject(FFLMOptions* options, yyjson_val* module) ffPrintError(FF_LM_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateLMJson(FF_MAYBE_UNUSED FFLMOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFLMResult result; + ffStrbufInit(&result.service); + ffStrbufInit(&result.type); + ffStrbufInit(&result.version); + const char* error = ffDetectLM(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if(result.service.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No LM service found"); + goto exit; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "service", &result.service); + yyjson_mut_obj_add_strbuf(doc, obj, "type", &result.type); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version); + +exit: + ffStrbufDestroy(&result.service); + ffStrbufDestroy(&result.type); + ffStrbufDestroy(&result.version); +} diff --git a/src/modules/lm/lm.h b/src/modules/lm/lm.h index 079f90e5f6..72509adcbd 100644 --- a/src/modules/lm/lm.h +++ b/src/modules/lm/lm.h @@ -9,3 +9,4 @@ void ffInitLMOptions(FFLMOptions* options); bool ffParseLMCommandOptions(FFLMOptions* options, const char* key, const char* value); void ffDestroyLMOptions(FFLMOptions* options); void ffParseLMJsonObject(FFLMOptions* options, yyjson_val* module); +void ffGenerateLMJson(FFLMOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/locale/locale.c b/src/modules/locale/locale.c index 4fa037b15d..10c53ba3db 100644 --- a/src/modules/locale/locale.c +++ b/src/modules/locale/locale.c @@ -32,7 +32,7 @@ void ffPrintLocale(FFLocaleOptions* options) void ffInitLocaleOptions(FFLocaleOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LOCALE_MODULE_NAME, ffParseLocaleCommandOptions, ffParseLocaleJsonObject, ffPrintLocale); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LOCALE_MODULE_NAME, ffParseLocaleCommandOptions, ffParseLocaleJsonObject, ffPrintLocale, ffGenerateLocaleJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,17 @@ void ffParseLocaleJsonObject(FFLocaleOptions* options, yyjson_val* module) ffPrintError(FF_LOCALE_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateLocaleJson(FF_MAYBE_UNUSED FFLocaleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY locale = ffStrbufCreate(); + + ffDetectLocale(&locale); + if(locale.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No locale found"); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &locale); +} diff --git a/src/modules/locale/locale.h b/src/modules/locale/locale.h index 405f697c9d..8570a3c8de 100644 --- a/src/modules/locale/locale.h +++ b/src/modules/locale/locale.h @@ -9,3 +9,4 @@ void ffInitLocaleOptions(FFLocaleOptions* options); bool ffParseLocaleCommandOptions(FFLocaleOptions* options, const char* key, const char* value); void ffDestroyLocaleOptions(FFLocaleOptions* options); void ffParseLocaleJsonObject(FFLocaleOptions* options, yyjson_val* module); +void ffGenerateLocaleJson(FFLocaleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/localip/localip.c b/src/modules/localip/localip.c index 74b61c5803..36013d5058 100644 --- a/src/modules/localip/localip.c +++ b/src/modules/localip/localip.c @@ -5,7 +5,7 @@ #include "util/stringUtils.h" #define FF_LOCALIP_DISPLAY_NAME "Local IP" -#define FF_LOCALIP_NUM_FORMAT_ARGS 2 +#define FF_LOCALIP_NUM_FORMAT_ARGS 5 #pragma GCC diagnostic ignored "-Wsign-conversion" static int sortIps(const FFLocalIpResult* left, const FFLocalIpResult* right) @@ -13,14 +13,14 @@ static int sortIps(const FFLocalIpResult* left, const FFLocalIpResult* right) return ffStrbufComp(&left->name, &right->name); } -static void formatKey(const FFLocalIpOptions* options, const FFLocalIpResult* ip, uint32_t index, FFstrbuf* key) +static void formatKey(const FFLocalIpOptions* options, FFLocalIpResult* ip, uint32_t index, FFstrbuf* key) { if(options->moduleArgs.key.length == 0) { - if(ip->name.length) - ffStrbufSetF(key, FF_LOCALIP_DISPLAY_NAME " (%s)", ip->name.chars); - else - ffStrbufSetS(key, FF_LOCALIP_DISPLAY_NAME); + if(!ip->name.length) + ffStrbufSetF(&ip->name, "unknown %u", (unsigned) index); + + ffStrbufSetF(key, FF_LOCALIP_DISPLAY_NAME " (%s)", ip->name.chars); } else { @@ -86,9 +86,6 @@ void ffPrintLocalIp(FFLocalIpOptions* options) FF_LIST_FOR_EACH(FFLocalIpResult, ip, results) { - if (options->defaultRouteOnly && !ip->defaultRoute) - continue; - if (flag) fputs(" - ", stdout); else @@ -104,9 +101,6 @@ void ffPrintLocalIp(FFLocalIpOptions* options) FF_LIST_FOR_EACH(FFLocalIpResult, ip, results) { - if (options->defaultRouteOnly && !ip->defaultRoute) - continue; - formatKey(options, ip, results.length == 1 ? 0 : index + 1, &key); if(options->moduleArgs.outputFormat.length == 0) { @@ -139,7 +133,7 @@ void ffPrintLocalIp(FFLocalIpOptions* options) void ffInitLocalIpOptions(FFLocalIpOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LOCALIP_MODULE_NAME, ffParseLocalIpCommandOptions, ffParseLocalIpJsonObject, ffPrintLocalIp); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_LOCALIP_MODULE_NAME, ffParseLocalIpCommandOptions, ffParseLocalIpJsonObject, ffPrintLocalIp, ffGenerateLocalIpJson); ffOptionInitModuleArg(&options->moduleArgs); options->showType = FF_LOCALIP_TYPE_IPV4_BIT; @@ -293,3 +287,42 @@ void ffParseLocalIpJsonObject(FFLocalIpOptions* options, yyjson_val* module) ffPrintError(FF_LOCALIP_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateLocalIpJson(FF_MAYBE_UNUSED FFLocalIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFLocalIpResult)); + + const char* error = ffDetectLocalIps(options, &results); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + if(results.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Failed to detect any IPs"); + goto exit; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFLocalIpResult, ip, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_bool(doc, obj, "defaultRoute", ip->defaultRoute); + yyjson_mut_obj_add_strbuf(doc, obj, "ipv4", &ip->ipv4); + yyjson_mut_obj_add_strbuf(doc, obj, "ipv6", &ip->ipv6); + yyjson_mut_obj_add_strbuf(doc, obj, "mac", &ip->mac); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &ip->name); + } + +exit: + FF_LIST_FOR_EACH(FFLocalIpResult, ip, results) + { + ffStrbufDestroy(&ip->name); + ffStrbufDestroy(&ip->ipv4); + ffStrbufDestroy(&ip->ipv6); + ffStrbufDestroy(&ip->mac); + } +} diff --git a/src/modules/localip/localip.h b/src/modules/localip/localip.h index 114459c6f5..799860a429 100644 --- a/src/modules/localip/localip.h +++ b/src/modules/localip/localip.h @@ -9,3 +9,4 @@ void ffInitLocalIpOptions(FFLocalIpOptions* options); bool ffParseLocalIpCommandOptions(FFLocalIpOptions* options, const char* key, const char* value); void ffDestroyLocalIpOptions(FFLocalIpOptions* options); void ffParseLocalIpJsonObject(FFLocalIpOptions* options, yyjson_val* module); +void ffGenerateLocalIpJson(FFLocalIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/media/media.c b/src/modules/media/media.c index 87b9b7f12c..7a0e01e770 100644 --- a/src/modules/media/media.c +++ b/src/modules/media/media.c @@ -107,7 +107,7 @@ void ffPrintMedia(FFMediaOptions* options) void ffInitMediaOptions(FFMediaOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MEDIA_MODULE_NAME, ffParseMediaCommandOptions, ffParseMediaJsonObject, ffPrintMedia); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MEDIA_MODULE_NAME, ffParseMediaCommandOptions, ffParseMediaJsonObject, ffPrintMedia, ffGenerateMediaJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -142,3 +142,20 @@ void ffParseMediaJsonObject(FFMediaOptions* options, yyjson_val* module) ffPrintError(FF_MEDIA_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateMediaJson(FF_MAYBE_UNUSED FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFMediaResult* media = ffDetectMedia(); + + if(media->error.length > 0) + { + yyjson_mut_obj_add_strbuf(doc, module, "error", &media->error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "song", &media->song); + yyjson_mut_obj_add_strbuf(doc, obj, "artist", &media->artist); + yyjson_mut_obj_add_strbuf(doc, obj, "album", &media->album); + yyjson_mut_obj_add_strbuf(doc, obj, "status", &media->status); +} diff --git a/src/modules/media/media.h b/src/modules/media/media.h index f5267e5794..853814d7e5 100644 --- a/src/modules/media/media.h +++ b/src/modules/media/media.h @@ -9,3 +9,4 @@ void ffInitMediaOptions(FFMediaOptions* options); bool ffParseMediaCommandOptions(FFMediaOptions* options, const char* key, const char* value); void ffDestroyMediaOptions(FFMediaOptions* options); void ffParseMediaJsonObject(FFMediaOptions* options, yyjson_val* module); +void ffGenerateMediaJson(FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/memory/memory.c b/src/modules/memory/memory.c index 410fb97f2b..1d2bba3913 100644 --- a/src/modules/memory/memory.c +++ b/src/modules/memory/memory.c @@ -68,7 +68,7 @@ void ffPrintMemory(FFMemoryOptions* options) void ffInitMemoryOptions(FFMemoryOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MEMORY_MODULE_NAME, ffParseMemoryCommandOptions, ffParseMemoryJsonObject, ffPrintMemory); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MEMORY_MODULE_NAME, ffParseMemoryCommandOptions, ffParseMemoryJsonObject, ffPrintMemory, ffGenerateMemoryJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -103,3 +103,19 @@ void ffParseMemoryJsonObject(FFMemoryOptions* options, yyjson_val* module) ffPrintError(FF_MEMORY_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateMemoryJson(FF_MAYBE_UNUSED FFMemoryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFMemoryResult storage; + const char* error = ffDetectMemory(&storage); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_uint(doc, obj, "total", storage.bytesTotal); + yyjson_mut_obj_add_uint(doc, obj, "used", storage.bytesUsed); +} diff --git a/src/modules/memory/memory.h b/src/modules/memory/memory.h index aec76b21f7..b4fefff5dc 100644 --- a/src/modules/memory/memory.h +++ b/src/modules/memory/memory.h @@ -9,3 +9,4 @@ void ffInitMemoryOptions(FFMemoryOptions* options); bool ffParseMemoryCommandOptions(FFMemoryOptions* options, const char* key, const char* value); void ffDestroyMemoryOptions(FFMemoryOptions* options); void ffParseMemoryJsonObject(FFMemoryOptions* options, yyjson_val* module); +void ffGenerateMemoryJson(FFMemoryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/modules.h b/src/modules/modules.h index 1f7669c102..64ab6ea213 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -31,6 +31,7 @@ #include "modules/media/media.h" #include "modules/memory/memory.h" #include "modules/monitor/monitor.h" +#include "modules/netio/netio.h" #include "modules/opengl/opengl.h" #include "modules/opencl/opencl.h" #include "modules/os/os.h" diff --git a/src/modules/monitor/monitor.c b/src/modules/monitor/monitor.c index 688c62413f..be0e22830c 100644 --- a/src/modules/monitor/monitor.c +++ b/src/modules/monitor/monitor.c @@ -77,7 +77,7 @@ void ffPrintMonitor(FFMonitorOptions* options) void ffInitMonitorOptions(FFMonitorOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MONITOR_MODULE_NAME, ffParseMonitorCommandOptions, ffParseMonitorJsonObject, ffPrintMonitor); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_MONITOR_MODULE_NAME, ffParseMonitorCommandOptions, ffParseMonitorJsonObject, ffPrintMonitor, ffGenerateMonitorJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -112,3 +112,38 @@ void ffParseMonitorJsonObject(FFMonitorOptions* options, yyjson_val* module) ffPrintError(FF_MONITOR_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateMonitorJson(FF_MAYBE_UNUSED FFMonitorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFMonitorResult)); + + const char* error = ffDetectMonitor(&results); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else if(results.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No monitors found"); + } + else + { + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFMonitorResult, item, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_bool(doc, obj, "hdrCompatible", item->hdrCompatible); + yyjson_mut_obj_add_uint(doc, obj, "width", item->width); + yyjson_mut_obj_add_uint(doc, obj, "height", item->height); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_uint(doc, obj, "physicalHeight", item->physicalHeight); + yyjson_mut_obj_add_uint(doc, obj, "physicalWidth", item->physicalWidth); + } + } + + FF_LIST_FOR_EACH(FFMonitorResult, item, results) + { + ffStrbufDestroy(&item->name); + } +} diff --git a/src/modules/monitor/monitor.h b/src/modules/monitor/monitor.h index ff03d26e96..8511442040 100644 --- a/src/modules/monitor/monitor.h +++ b/src/modules/monitor/monitor.h @@ -9,3 +9,4 @@ void ffInitMonitorOptions(FFMonitorOptions* options); bool ffParseMonitorCommandOptions(FFMonitorOptions* options, const char* key, const char* value); void ffDestroyMonitorOptions(FFMonitorOptions* options); void ffParseMonitorJsonObject(FFMonitorOptions* options, yyjson_val* module); +void ffGenerateMonitorJson(FFMonitorOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/netio/netio.c b/src/modules/netio/netio.c new file mode 100644 index 0000000000..3efef948fa --- /dev/null +++ b/src/modules/netio/netio.c @@ -0,0 +1,199 @@ +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "common/parsing.h" +#include "detection/netio/netio.h" +#include "modules/netio/netio.h" +#include "util/stringUtils.h" + +#define FF_NETIO_DISPLAY_NAME "Network IO" +#define FF_NETIO_NUM_FORMAT_ARGS 12 + +static int sortInfs(const FFNetIOResult* left, const FFNetIOResult* right) +{ + return ffStrbufComp(&left->name, &right->name); +} + +static void formatKey(const FFNetIOOptions* options, FFNetIOResult* inf, uint32_t index, FFstrbuf* key) +{ + if(options->moduleArgs.key.length == 0) + { + if(!inf->name.length) + ffStrbufSetF(&inf->name, "unknown %u", (unsigned) index); + + ffStrbufSetF(key, FF_NETIO_DISPLAY_NAME " (%s)", inf->name.chars); + } + else + { + ffStrbufClear(key); + ffParseFormatString(key, &options->moduleArgs.key, 2, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_UINT, &index}, + {FF_FORMAT_ARG_TYPE_STRBUF, &inf->name}, + }); + } +} + +void ffPrintNetIO(FFNetIOOptions* options) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFNetIOResult)); + const char* error = ffDetectNetIO(&result, options); + + if(error) + { + ffPrintError(FF_NETIO_DISPLAY_NAME, 0, &options->moduleArgs, "%s", error); + return; + } + + ffListSort(&result, (const void*) sortInfs); + + uint32_t index = 0; + FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY buffer2 = ffStrbufCreate(); + + FF_LIST_FOR_EACH(FFNetIOResult, inf, result) + { + formatKey(options, inf, result.length == 1 ? 0 : index + 1, &key); + ffStrbufClear(&buffer); + + if(options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + ffParseSize(inf->rxBytes, &buffer); + ffStrbufAppendS(&buffer, "/s (IN) - "); + ffParseSize(inf->txBytes, &buffer); + ffStrbufAppendS(&buffer, "/s (OUT)"); + if (!options->defaultRouteOnly && inf->defaultRoute) + ffStrbufAppendS(&buffer, " *"); + ffStrbufPutTo(&buffer, stdout); + } + else + { + ffStrbufClear(&buffer2); + ffParseSize(inf->rxBytes, &buffer); + ffStrbufAppendS(&buffer, "/s"); + ffParseSize(inf->txBytes, &buffer2); + ffStrbufAppendS(&buffer2, "/s"); + + ffPrintFormatString(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_NETIO_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &buffer}, + {FF_FORMAT_ARG_TYPE_STRBUF, &buffer2}, + {FF_FORMAT_ARG_TYPE_STRBUF, &inf->name}, + {FF_FORMAT_ARG_TYPE_BOOL, &inf->defaultRoute}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->txBytes}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->rxBytes}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->txPackets}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->rxPackets}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->rxErrors}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->txErrors}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->rxDrops}, + {FF_FORMAT_ARG_TYPE_UINT64, &inf->txDrops}, + }); + } + ++index; + } + + FF_LIST_FOR_EACH(FFNetIOResult, inf, result) + { + ffStrbufDestroy(&inf->name); + } +} + +void ffInitNetIOOptions(FFNetIOOptions* options) +{ + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_NETIO_MODULE_NAME, ffParseNetIOCommandOptions, ffParseNetIOJsonObject, ffPrintNetIO, ffGenerateNetIOJson); + ffOptionInitModuleArg(&options->moduleArgs); + + ffStrbufInit(&options->namePrefix); + options->defaultRouteOnly = false; +} + +bool ffParseNetIOCommandOptions(FFNetIOOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_NETIO_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + if (ffStrEqualsIgnCase(subKey, "name-prefix")) + { + ffOptionParseString(key, value, &options->namePrefix); + return true; + } + + if (ffStrEqualsIgnCase(subKey, "default-route-only")) + { + options->defaultRouteOnly = ffOptionParseBoolean(value); + return true; + } + + return false; +} + +void ffDestroyNetIOOptions(FFNetIOOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); + ffStrbufDestroy(&options->namePrefix); +} + +void ffParseNetIOJsonObject(FFNetIOOptions* options, yyjson_val* module) +{ + yyjson_val *key_, *val; + size_t idx, max; + yyjson_obj_foreach(module, idx, max, key_, val) + { + const char* key = yyjson_get_str(key_); + if(ffStrEqualsIgnCase(key, "type")) + continue; + + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + + if (ffStrEqualsIgnCase(key, "namePrefix")) + { + ffStrbufSetS(&options->namePrefix, yyjson_get_str(val)); + continue; + } + + if (ffStrEqualsIgnCase(key, "defaultRouteOnly")) + { + options->defaultRouteOnly = yyjson_get_bool(val); + continue; + } + + ffPrintError(FF_NETIO_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); + } +} + +void ffGenerateNetIOJson(FFNetIOOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFNetIOResult)); + const char* error = ffDetectNetIO(&result, options); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFNetIOResult, counter, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &counter->name); + yyjson_mut_obj_add_bool(doc, obj, "defaultRoute", counter->defaultRoute); + yyjson_mut_obj_add_uint(doc, obj, "txBytes", counter->txBytes); + yyjson_mut_obj_add_uint(doc, obj, "rxBytes", counter->rxBytes); + yyjson_mut_obj_add_uint(doc, obj, "txPackets", counter->txPackets); + yyjson_mut_obj_add_uint(doc, obj, "rxPackets", counter->rxPackets); + yyjson_mut_obj_add_uint(doc, obj, "rxErrors", counter->rxErrors); + yyjson_mut_obj_add_uint(doc, obj, "txErrors", counter->txErrors); + yyjson_mut_obj_add_uint(doc, obj, "rxDrops", counter->rxDrops); + yyjson_mut_obj_add_uint(doc, obj, "txDrops", counter->txDrops); + } + + FF_LIST_FOR_EACH(FFNetIOResult, inf, result) + { + ffStrbufDestroy(&inf->name); + } +} diff --git a/src/modules/netio/netio.h b/src/modules/netio/netio.h new file mode 100644 index 0000000000..3562d0ccd7 --- /dev/null +++ b/src/modules/netio/netio.h @@ -0,0 +1,14 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_NETIO_MODULE_NAME "NetIO" + +void ffPrepareNetIO(FFNetIOOptions* options); + +void ffPrintNetIO(FFNetIOOptions* options); +void ffInitNetIOOptions(FFNetIOOptions* options); +bool ffParseNetIOCommandOptions(FFNetIOOptions* options, const char* key, const char* value); +void ffDestroyNetIOOptions(FFNetIOOptions* options); +void ffParseNetIOJsonObject(FFNetIOOptions* options, yyjson_val* module); +void ffGenerateNetIOJson(FFNetIOOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/netio/option.h b/src/modules/netio/option.h new file mode 100644 index 0000000000..62b493d292 --- /dev/null +++ b/src/modules/netio/option.h @@ -0,0 +1,14 @@ +#pragma once + +// This file will be included in "fastfetch.h", do NOT put unnecessary things here + +#include "common/option.h" + +typedef struct FFNetIOOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; + + FFstrbuf namePrefix; + bool defaultRouteOnly; +} FFNetIOOptions; diff --git a/src/modules/opencl/opencl.c b/src/modules/opencl/opencl.c index 6194b7a0b5..afe3efa244 100644 --- a/src/modules/opencl/opencl.c +++ b/src/modules/opencl/opencl.c @@ -41,7 +41,7 @@ void ffPrintOpenCL(FFOpenCLOptions* options) void ffInitOpenCLOptions(FFOpenCLOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OPENCL_MODULE_NAME, ffParseOpenCLCommandOptions, ffParseOpenCLJsonObject, ffPrintOpenCL); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OPENCL_MODULE_NAME, ffParseOpenCLCommandOptions, ffParseOpenCLJsonObject, ffPrintOpenCL, ffGenerateOpenCLJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -76,3 +76,29 @@ void ffParseOpenCLJsonObject(FFOpenCLOptions* options, yyjson_val* module) ffPrintError(FF_OPENCL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateOpenCLJson(FF_MAYBE_UNUSED FFOpenCLOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFOpenCLResult opencl; + ffStrbufInit(&opencl.version); + ffStrbufInit(&opencl.device); + ffStrbufInit(&opencl.vendor); + + const char* error = ffDetectOpenCL(&opencl); + + if(error != NULL) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else + { + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &opencl.version); + yyjson_mut_obj_add_strbuf(doc, obj, "device", &opencl.device); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &opencl.vendor); + } + + ffStrbufDestroy(&opencl.version); + ffStrbufDestroy(&opencl.device); + ffStrbufDestroy(&opencl.vendor); +} diff --git a/src/modules/opencl/opencl.h b/src/modules/opencl/opencl.h index f5e2a32c2c..592f453bf1 100644 --- a/src/modules/opencl/opencl.h +++ b/src/modules/opencl/opencl.h @@ -9,3 +9,4 @@ void ffInitOpenCLOptions(FFOpenCLOptions* options); bool ffParseOpenCLCommandOptions(FFOpenCLOptions* options, const char* key, const char* value); void ffDestroyOpenCLOptions(FFOpenCLOptions* options); void ffParseOpenCLJsonObject(FFOpenCLOptions* options, yyjson_val* module); +void ffGenerateOpenCLJson(FFOpenCLOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/opengl/opengl.c b/src/modules/opengl/opengl.c index d94849cf50..78bbf18428 100644 --- a/src/modules/opengl/opengl.c +++ b/src/modules/opengl/opengl.c @@ -44,7 +44,7 @@ void ffPrintOpenGL(FFOpenGLOptions* options) void ffInitOpenGLOptions(FFOpenGLOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OPENGL_MODULE_NAME, ffParseOpenGLCommandOptions, ffParseOpenGLJsonObject, ffPrintOpenGL); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OPENGL_MODULE_NAME, ffParseOpenGLCommandOptions, ffParseOpenGLJsonObject, ffPrintOpenGL, ffGenerateOpenGLJson); ffOptionInitModuleArg(&options->moduleArgs); #if defined(__linux__) || defined(__FreeBSD__) @@ -115,3 +115,31 @@ void ffParseOpenGLJsonObject(FFOpenGLOptions* options, yyjson_val* module) ffPrintError(FF_OPENGL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateOpenGLJson(FF_MAYBE_UNUSED FFOpenGLOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFOpenGLResult result; + ffStrbufInit(&result.version); + ffStrbufInit(&result.renderer); + ffStrbufInit(&result.vendor); + ffStrbufInit(&result.slv); + + const char* error = ffDetectOpenGL(options, &result); + if(error != NULL) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else + { + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version); + yyjson_mut_obj_add_strbuf(doc, obj, "renderer", &result.renderer); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &result.vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "slv", &result.slv); + } + + ffStrbufDestroy(&result.version); + ffStrbufDestroy(&result.renderer); + ffStrbufDestroy(&result.vendor); + ffStrbufDestroy(&result.slv); +} diff --git a/src/modules/opengl/opengl.h b/src/modules/opengl/opengl.h index 5d0b1937bc..6c35ede54e 100644 --- a/src/modules/opengl/opengl.h +++ b/src/modules/opengl/opengl.h @@ -9,3 +9,4 @@ void ffInitOpenGLOptions(FFOpenGLOptions* options); bool ffParseOpenGLCommandOptions(FFOpenGLOptions* options, const char* key, const char* value); void ffDestroyOpenGLOptions(FFOpenGLOptions* options); void ffParseOpenGLJsonObject(FFOpenGLOptions* options, yyjson_val* module); +void ffGenerateOpenGLJson(FFOpenGLOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/options.h b/src/modules/options.h index 12041ce5b6..1be2e88e0c 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -31,6 +31,7 @@ #include "modules/media/option.h" #include "modules/memory/option.h" #include "modules/monitor/option.h" +#include "modules/netio/option.h" #include "modules/opengl/option.h" #include "modules/opencl/option.h" #include "modules/os/option.h" diff --git a/src/modules/os/os.c b/src/modules/os/os.c index e5e5d58ee3..120f769fc1 100644 --- a/src/modules/os/os.c +++ b/src/modules/os/os.c @@ -21,21 +21,20 @@ static void buildOutputDefault(const FFOSResult* os, FFstrbuf* result) else ffStrbufAppend(result, &instance.state.platform.systemName); - #ifdef __APPLE__ - if(os->codename.length > 0) + //Append code name if it is missing + if(os->codename.length > 0 && !ffStrbufContain(result, &os->versionID)) { ffStrbufAppendC(result, ' '); ffStrbufAppend(result, &os->codename); } - #endif //Append version if it is missing - if(os->versionID.length > 0 && ffStrbufFirstIndex(result, &os->versionID) == result->length) + if(os->versionID.length > 0 && !ffStrbufContain(result, &os->versionID)) { ffStrbufAppendC(result, ' '); ffStrbufAppend(result, &os->versionID); } - else if(os->versionID.length == 0 && os->version.length > 0 && ffStrbufFirstIndex(result, &os->version) == result->length) + else if(os->versionID.length == 0 && os->version.length > 0 && !ffStrbufContain(result, &os->version)) { ffStrbufAppendC(result, ' '); ffStrbufAppend(result, &os->version); @@ -139,7 +138,7 @@ void ffPrintOS(FFOSOptions* options) void ffInitOSOptions(FFOSOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OS_MODULE_NAME, ffParseOSCommandOptions, ffParseOSJsonObject, ffPrintOS); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_OS_MODULE_NAME, ffParseOSCommandOptions, ffParseOSJsonObject, ffPrintOS, ffGenerateOSJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -174,3 +173,26 @@ void ffParseOSJsonObject(FFOSOptions* options, yyjson_val* module) ffPrintError(FF_OS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateOSJson(FF_MAYBE_UNUSED FFOSOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFOSResult* os = ffDetectOS(); + + if(os->name.length == 0 && os->prettyName.length == 0 && os->id.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Could not detect OS"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "buildID", &os->buildID); + yyjson_mut_obj_add_strbuf(doc, obj, "codename", &os->codename); + yyjson_mut_obj_add_strbuf(doc, obj, "id", &os->id); + yyjson_mut_obj_add_strbuf(doc, obj, "idLike", &os->idLike); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &os->name); + yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &os->prettyName); + yyjson_mut_obj_add_strbuf(doc, obj, "variant", &os->variant); + yyjson_mut_obj_add_strbuf(doc, obj, "variantID", &os->variantID); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &os->version); + yyjson_mut_obj_add_strbuf(doc, obj, "versionID", &os->versionID); +} diff --git a/src/modules/os/os.h b/src/modules/os/os.h index 07320194d3..b7ed39d0d4 100644 --- a/src/modules/os/os.h +++ b/src/modules/os/os.h @@ -9,3 +9,4 @@ void ffInitOSOptions(FFOSOptions* options); bool ffParseOSCommandOptions(FFOSOptions* options, const char* key, const char* value); void ffDestroyOSOptions(FFOSOptions* options); void ffParseOSJsonObject(FFOSOptions* options, yyjson_val* module); +void ffGenerateOSJson(FFOSOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 21599e07a0..1d6ba534ab 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -4,7 +4,7 @@ #include "modules/packages/packages.h" #include "util/stringUtils.h" -#define FF_PACKAGES_NUM_FORMAT_ARGS 22 +#define FF_PACKAGES_NUM_FORMAT_ARGS 24 void ffPrintPackages(FFPackagesOptions* options) { @@ -63,6 +63,7 @@ void ffPrintPackages(FFPackagesOptions* options) FF_PRINT_PACKAGE(choco) FF_PRINT_PACKAGE(pkgtool) FF_PRINT_PACKAGE(paludis) + FF_PRINT_PACKAGE(winget) putchar('\n'); } @@ -91,6 +92,8 @@ void ffPrintPackages(FFPackagesOptions* options) {FF_FORMAT_ARG_TYPE_UINT, &counts.scoop}, {FF_FORMAT_ARG_TYPE_UINT, &counts.choco}, {FF_FORMAT_ARG_TYPE_UINT, &counts.pkgtool}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.paludis}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.winget}, }); } @@ -99,7 +102,7 @@ void ffPrintPackages(FFPackagesOptions* options) void ffInitPackagesOptions(FFPackagesOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PACKAGES_MODULE_NAME, ffParsePackagesCommandOptions, ffParsePackagesJsonObject, ffPrintPackages); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PACKAGES_MODULE_NAME, ffParsePackagesCommandOptions, ffParsePackagesJsonObject, ffPrintPackages, ffGeneratePackagesJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -134,3 +137,46 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) ffPrintError(FF_PACKAGES_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGeneratePackagesJson(FF_MAYBE_UNUSED FFPackagesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFPackagesResult counts = {}; + ffStrbufInit(&counts.pacmanBranch); + + const char* error = ffDetectPackages(&counts); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + + #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(apk) + FF_APPEND_PACKAGE_COUNT(brew) + FF_APPEND_PACKAGE_COUNT(brewCask) + FF_APPEND_PACKAGE_COUNT(choco) + FF_APPEND_PACKAGE_COUNT(dpkg) + FF_APPEND_PACKAGE_COUNT(emerge) + FF_APPEND_PACKAGE_COUNT(eopkg) + FF_APPEND_PACKAGE_COUNT(flatpakSystem) + FF_APPEND_PACKAGE_COUNT(flatpakUser) + FF_APPEND_PACKAGE_COUNT(nixDefault) + FF_APPEND_PACKAGE_COUNT(nixSystem) + FF_APPEND_PACKAGE_COUNT(nixUser) + FF_APPEND_PACKAGE_COUNT(pacman) + FF_APPEND_PACKAGE_COUNT(paludis) + FF_APPEND_PACKAGE_COUNT(pkg) + FF_APPEND_PACKAGE_COUNT(pkgtool) + FF_APPEND_PACKAGE_COUNT(port) + FF_APPEND_PACKAGE_COUNT(rpm) + FF_APPEND_PACKAGE_COUNT(scoop) + FF_APPEND_PACKAGE_COUNT(snap) + FF_APPEND_PACKAGE_COUNT(winget) + FF_APPEND_PACKAGE_COUNT(xbps) + yyjson_mut_obj_add_strbuf(doc, obj, "pacmanBranch", &counts.pacmanBranch); +} diff --git a/src/modules/packages/packages.h b/src/modules/packages/packages.h index 14fd7301d9..97b4ce239f 100644 --- a/src/modules/packages/packages.h +++ b/src/modules/packages/packages.h @@ -9,3 +9,4 @@ void ffInitPackagesOptions(FFPackagesOptions* options); bool ffParsePackagesCommandOptions(FFPackagesOptions* options, const char* key, const char* value); void ffDestroyPackagesOptions(FFPackagesOptions* options); void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module); +void ffGeneratePackagesJson(FFPackagesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/player/player.c b/src/modules/player/player.c index 4dca659169..5ca58ba279 100644 --- a/src/modules/player/player.c +++ b/src/modules/player/player.c @@ -75,7 +75,7 @@ void ffPrintPlayer(FFPlayerOptions* options) void ffInitPlayerOptions(FFPlayerOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PLAYER_MODULE_NAME, ffParsePlayerCommandOptions, ffParsePlayerJsonObject, ffPrintPlayer); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PLAYER_MODULE_NAME, ffParsePlayerCommandOptions, ffParsePlayerJsonObject, ffPrintPlayer, ffGeneratePlayerJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -110,3 +110,19 @@ void ffParsePlayerJsonObject(FFPlayerOptions* options, yyjson_val* module) ffPrintError(FF_PLAYER_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGeneratePlayerJson(FF_MAYBE_UNUSED FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFMediaResult* media = ffDetectMedia(); + + if(media->error.length > 0) + { + yyjson_mut_obj_add_strbuf(doc, module, "error", &media->error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "player", &media->player); + yyjson_mut_obj_add_strbuf(doc, obj, "playerId", &media->playerId); + yyjson_mut_obj_add_strbuf(doc, obj, "url", &media->url); +} diff --git a/src/modules/player/player.h b/src/modules/player/player.h index f63e12250d..829b7857a4 100644 --- a/src/modules/player/player.h +++ b/src/modules/player/player.h @@ -10,3 +10,4 @@ void ffInitPlayerOptions(FFPlayerOptions* options); bool ffParsePlayerCommandOptions(FFPlayerOptions* options, const char* key, const char* value); void ffDestroyPlayerOptions(FFPlayerOptions* options); void ffParsePlayerJsonObject(FFPlayerOptions* options, yyjson_val* module); +void ffGeneratePlayerJson(FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/poweradapter/poweradapter.c b/src/modules/poweradapter/poweradapter.c index 7fcd772af4..a5ce64002c 100644 --- a/src/modules/poweradapter/poweradapter.c +++ b/src/modules/poweradapter/poweradapter.c @@ -9,8 +9,7 @@ void ffPrintPowerAdapter(FFPowerAdapterOptions* options) { - FFlist results; - ffListInit(&results, sizeof(PowerAdapterResult)); + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFPowerAdapterResult)); const char* error = ffDetectPowerAdapterImpl(&results); @@ -26,7 +25,7 @@ void ffPrintPowerAdapter(FFPowerAdapterOptions* options) { for(uint8_t i = 0; i < (uint8_t) results.length; i++) { - PowerAdapterResult* result = ffListGet(&results, i); + FFPowerAdapterResult* result = ffListGet(&results, i); if(result->watts != FF_POWERADAPTER_UNSET) { @@ -59,13 +58,11 @@ void ffPrintPowerAdapter(FFPowerAdapterOptions* options) ffStrbufDestroy(&result->name); } } - - ffListDestroy(&results); } void ffInitPowerAdapterOptions(FFPowerAdapterOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_POWERADAPTER_MODULE_NAME, ffParsePowerAdapterCommandOptions, ffParsePowerAdapterJsonObject, ffPrintPowerAdapter); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_POWERADAPTER_MODULE_NAME, ffParsePowerAdapterCommandOptions, ffParsePowerAdapterJsonObject, ffPrintPowerAdapter, ffGeneratePowerAdapterJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -100,3 +97,32 @@ void ffParsePowerAdapterJsonObject(FFPowerAdapterOptions* options, yyjson_val* m ffPrintError(FF_POWERADAPTER_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGeneratePowerAdapterJson(FF_MAYBE_UNUSED FFPowerAdapterOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFPowerAdapterResult)); + + const char* error = ffDetectPowerAdapterImpl(&results); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + } + else if(results.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No power adapters found"); + } + else + { + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFPowerAdapterResult, item, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "description", &item->description); + yyjson_mut_obj_add_strbuf(doc, obj, "manufacturer", &item->manufacturer); + yyjson_mut_obj_add_strbuf(doc, obj, "modelName", &item->modelName); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_int(doc, obj, "watts", item->watts); + } + } +} diff --git a/src/modules/poweradapter/poweradapter.h b/src/modules/poweradapter/poweradapter.h index e9490c8323..a99d6acabb 100644 --- a/src/modules/poweradapter/poweradapter.h +++ b/src/modules/poweradapter/poweradapter.h @@ -9,3 +9,4 @@ void ffInitPowerAdapterOptions(FFPowerAdapterOptions* options); bool ffParsePowerAdapterCommandOptions(FFPowerAdapterOptions* options, const char* key, const char* value); void ffDestroyPowerAdapterOptions(FFPowerAdapterOptions* options); void ffParsePowerAdapterJsonObject(FFPowerAdapterOptions* options, yyjson_val* module); +void ffGeneratePowerAdapterJson(FFPowerAdapterOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/processes/processes.c b/src/modules/processes/processes.c index fa2fc89bd3..02a8bb53f1 100644 --- a/src/modules/processes/processes.c +++ b/src/modules/processes/processes.c @@ -33,7 +33,7 @@ void ffPrintProcesses(FFProcessesOptions* options) void ffInitProcessesOptions(FFProcessesOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PROCESSES_MODULE_NAME, ffParseProcessesCommandOptions, ffParseProcessesJsonObject, ffPrintProcesses); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PROCESSES_MODULE_NAME, ffParseProcessesCommandOptions, ffParseProcessesJsonObject, ffPrintProcesses, ffGenerateProcessesJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -68,3 +68,17 @@ void ffParseProcessesJsonObject(FFProcessesOptions* options, yyjson_val* module) ffPrintError(FF_PROCESSES_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateProcessesJson(FF_MAYBE_UNUSED FFProcessesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + uint32_t result; + const char* error = ffDetectProcesses(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_obj_add_uint(doc, module, "result", result); +} diff --git a/src/modules/processes/processes.h b/src/modules/processes/processes.h index 724faf7fb0..8f14e00e44 100644 --- a/src/modules/processes/processes.h +++ b/src/modules/processes/processes.h @@ -9,3 +9,4 @@ void ffInitProcessesOptions(FFProcessesOptions* options); bool ffParseProcessesCommandOptions(FFProcessesOptions* options, const char* key, const char* value); void ffDestroyProcessesOptions(FFProcessesOptions* options); void ffParseProcessesJsonObject(FFProcessesOptions* options, yyjson_val* module); +void ffGenerateProcessesJson(FFProcessesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/publicip/publicip.c b/src/modules/publicip/publicip.c index 6097b54a38..e7c277e21e 100644 --- a/src/modules/publicip/publicip.c +++ b/src/modules/publicip/publicip.c @@ -1,103 +1,48 @@ #include "common/printing.h" #include "common/jsonconfig.h" -#include "common/networking.h" #include "modules/publicip/publicip.h" +#include "detection/publicip/publicip.h" #include "util/stringUtils.h" #define FF_PUBLICIP_DISPLAY_NAME "Public IP" #define FF_PUBLICIP_NUM_FORMAT_ARGS 1 -static FFNetworkingState state; -static int status = -1; - -static inline void wrapYyjsonFree(yyjson_doc** doc) -{ - assert(doc); - if (*doc) - yyjson_doc_free(*doc); -} - -void ffPreparePublicIp(FFPublicIpOptions* options) -{ - if (status != -1) - { - fputs("Error: " FF_PUBLICIP_DISPLAY_NAME " can only be used once due to internal limitations\n", stderr); - exit(1); - } - - if (options->url.length == 0) - status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/json", NULL); - else - { - FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url); - ffStrbufSubstrAfterFirstS(&host, "://"); - uint32_t pathStartIndex = ffStrbufFirstIndexC(&host, '/'); - - FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); - if(pathStartIndex != host.length) - { - ffStrbufAppendNS(&path, pathStartIndex, host.chars + (host.length - pathStartIndex)); - host.length = pathStartIndex; - host.chars[pathStartIndex] = '\0'; - } - - status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL); - } -} - void ffPrintPublicIp(FFPublicIpOptions* options) { - if (status == -1) - ffPreparePublicIp(options); - - if (status == 0) - { - ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, "Failed to connect to an IP detection server"); - return; - } + FFPublicIpResult result; + ffStrbufInit(&result.ip); + ffStrbufInit(&result.location); + const char* error = ffDetectPublicIp(options, &result); - FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateA(4096); - bool success = ffNetworkingRecvHttpResponse(&state, &result, options->timeout); - if (success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n"); - - if (!success || result.length == 0) + if (error) { - ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, "Failed to receive the server response"); + ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, "%s", error); return; } if (options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - - if (options->url.length == 0) - { - yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(result.chars, result.length, 0, NULL, NULL); - if (doc) - { - yyjson_val* root = yyjson_doc_get_root(doc); - printf("%s (%s, %s)\n", - yyjson_get_str(yyjson_obj_get(root, "ip")), - yyjson_get_str(yyjson_obj_get(root, "city")), - yyjson_get_str(yyjson_obj_get(root, "country")) - ); - return; - } - } - - ffStrbufPutTo(&result, stdout); + if (result.location.length) + printf("%s (%s)\n", result.ip.chars, result.location.chars); + else + ffStrbufPutTo(&result.ip, stdout); } else { ffPrintFormat(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PUBLICIP_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_STRBUF, &result} + {FF_FORMAT_ARG_TYPE_STRBUF, &result.ip}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.location}, }); } + + ffStrbufDestroy(&result.ip); + ffStrbufDestroy(&result.location); } void ffInitPublicIpOptions(FFPublicIpOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PUBLICIP_MODULE_NAME, ffParsePublicIpCommandOptions, ffParsePublicIpJsonObject, ffPrintPublicIp); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_PUBLICIP_MODULE_NAME, ffParsePublicIpCommandOptions, ffParsePublicIpJsonObject, ffPrintPublicIp, ffGeneratePublicIpJson); ffOptionInitModuleArg(&options->moduleArgs); ffStrbufInit(&options->url); @@ -161,3 +106,24 @@ void ffParsePublicIpJsonObject(FFPublicIpOptions* options, yyjson_val* module) ffPrintError(FF_PUBLICIP_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGeneratePublicIpJson(FFPublicIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFPublicIpResult result; + ffStrbufInit(&result.ip); + ffStrbufInit(&result.location); + const char* error = ffDetectPublicIp(options, &result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "ip", &result.ip); + yyjson_mut_obj_add_strbuf(doc, obj, "location", &result.location); + + ffStrbufDestroy(&result.ip); + ffStrbufDestroy(&result.location); +} diff --git a/src/modules/publicip/publicip.h b/src/modules/publicip/publicip.h index 6a7c739d2e..870591eb74 100644 --- a/src/modules/publicip/publicip.h +++ b/src/modules/publicip/publicip.h @@ -11,3 +11,4 @@ void ffInitPublicIpOptions(FFPublicIpOptions* options); bool ffParsePublicIpCommandOptions(FFPublicIpOptions* options, const char* key, const char* value); void ffDestroyPublicIpOptions(FFPublicIpOptions* options); void ffParsePublicIpJsonObject(FFPublicIpOptions* options, yyjson_val* module); +void ffGeneratePublicIpJson(FFPublicIpOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/separator/separator.c b/src/modules/separator/separator.c index 0eaceff6a8..555bb2a04c 100644 --- a/src/modules/separator/separator.c +++ b/src/modules/separator/separator.c @@ -29,7 +29,7 @@ void ffPrintSeparator(FFSeparatorOptions* options) uint32_t titleLength = 1 // @ + getWcsWidth(&platform->userName, wstr, &state) // user name - + (fqdn ? platform->domainName.length : platform->hostName.length); // host name + + (fqdn ? platform->hostName.length : ffStrbufFirstIndexC(&platform->hostName, '.')); // host name ffLogoPrintLine(); if(options->string.length == 0) @@ -77,7 +77,7 @@ void ffPrintSeparator(FFSeparatorOptions* options) void ffInitSeparatorOptions(FFSeparatorOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SEPARATOR_MODULE_NAME, ffParseSeparatorCommandOptions, ffParseSeparatorJsonObject, ffPrintSeparator); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SEPARATOR_MODULE_NAME, ffParseSeparatorCommandOptions, ffParseSeparatorJsonObject, ffPrintSeparator, NULL); ffStrbufInit(&options->string); } diff --git a/src/modules/shell/shell.c b/src/modules/shell/shell.c index d7e2cfa6dd..f7668c5882 100644 --- a/src/modules/shell/shell.c +++ b/src/modules/shell/shell.c @@ -4,7 +4,7 @@ #include "modules/shell/shell.h" #include "util/stringUtils.h" -#define FF_SHELL_NUM_FORMAT_ARGS 7 +#define FF_SHELL_NUM_FORMAT_ARGS 6 void ffPrintShell(FFShellOptions* options) { @@ -36,16 +36,15 @@ void ffPrintShell(FFShellOptions* options) {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellExe}, {FF_FORMAT_ARG_TYPE_STRING, result->shellExeName}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellExe}, - {FF_FORMAT_ARG_TYPE_STRING, result->userShellExeName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellVersion} + {FF_FORMAT_ARG_TYPE_UINT, &result->shellPid}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellPrettyName}, }); } } void ffInitShellOptions(FFShellOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SHELL_MODULE_NAME, ffParseShellCommandOptions, ffParseShellJsonObject, ffPrintShell); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SHELL_MODULE_NAME, ffParseShellCommandOptions, ffParseShellJsonObject, ffPrintShell, ffGenerateShellJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -80,3 +79,21 @@ void ffParseShellJsonObject(FFShellOptions* options, yyjson_val* module) ffPrintError(FF_SHELL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateShellJson(FF_MAYBE_UNUSED FFShellOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFTerminalShellResult* result = ffDetectTerminalShell(); + + if(result->shellProcessName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Couldn't detect shell"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "exe", &result->shellExe); + yyjson_mut_obj_add_strcpy(doc, obj, "exeName", result->shellExeName); + yyjson_mut_obj_add_uint(doc, obj, "pid", result->shellPid); + yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->shellProcessName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->shellVersion); +} diff --git a/src/modules/shell/shell.h b/src/modules/shell/shell.h index 4e4f976bfe..354b512290 100644 --- a/src/modules/shell/shell.h +++ b/src/modules/shell/shell.h @@ -9,3 +9,4 @@ void ffInitShellOptions(FFShellOptions* options); bool ffParseShellCommandOptions(FFShellOptions* options, const char* key, const char* value); void ffDestroyShellOptions(FFShellOptions* options); void ffParseShellJsonObject(FFShellOptions* options, yyjson_val* module); +void ffGenerateShellJson(FFShellOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/sound/sound.c b/src/modules/sound/sound.c index 707556360c..3796a7365a 100644 --- a/src/modules/sound/sound.c +++ b/src/modules/sound/sound.c @@ -85,7 +85,7 @@ void ffPrintSound(FFSoundOptions* options) void ffInitSoundOptions(FFSoundOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SOUND_MODULE_NAME, ffParseSoundCommandOptions, ffParseSoundJsonObject, ffPrintSound); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SOUND_MODULE_NAME, ffParseSoundCommandOptions, ffParseSoundJsonObject, ffPrintSound, ffGenerateSoundJson); ffOptionInitModuleArg(&options->moduleArgs); options->soundType = FF_SOUND_TYPE_MAIN; @@ -149,3 +149,38 @@ void ffParseSoundJsonObject(FFSoundOptions* options, yyjson_val* module) ffPrintError(FF_SOUND_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateSoundJson(FF_MAYBE_UNUSED FFSoundOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFSoundDevice)); + const char* error = ffDetectSound(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + if(result.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No active sound devices found"); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFSoundDevice, item, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_bool(doc, obj, "active", item->active); + yyjson_mut_obj_add_bool(doc, obj, "main", item->main); + yyjson_mut_obj_add_uint(doc, obj, "volume", item->volume); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_strbuf(doc, obj, "identifier", &item->identifier); + } + + FF_LIST_FOR_EACH(FFSoundDevice, device, result) + { + ffStrbufDestroy(&device->identifier); + ffStrbufDestroy(&device->name); + } +} diff --git a/src/modules/sound/sound.h b/src/modules/sound/sound.h index c35c168012..03ec8fe680 100644 --- a/src/modules/sound/sound.h +++ b/src/modules/sound/sound.h @@ -9,3 +9,4 @@ void ffInitSoundOptions(FFSoundOptions* options); bool ffParseSoundCommandOptions(FFSoundOptions* options, const char* key, const char* value); void ffDestroySoundOptions(FFSoundOptions* options); void ffParseSoundJsonObject(FFSoundOptions* options, yyjson_val* module); +void ffGenerateSoundJson(FFSoundOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/swap/swap.c b/src/modules/swap/swap.c index d4b08031df..331cbf26dd 100644 --- a/src/modules/swap/swap.c +++ b/src/modules/swap/swap.c @@ -75,7 +75,7 @@ void ffPrintSwap(FFSwapOptions* options) void ffInitSwapOptions(FFSwapOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SWAP_MODULE_NAME, ffParseSwapCommandOptions, ffParseSwapJsonObject, ffPrintSwap); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_SWAP_MODULE_NAME, ffParseSwapCommandOptions, ffParseSwapJsonObject, ffPrintSwap, ffGenerateSwapJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -110,3 +110,19 @@ void ffParseSwapJsonObject(FFSwapOptions* options, yyjson_val* module) ffPrintError(FF_SWAP_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateSwapJson(FF_MAYBE_UNUSED FFSwapOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFSwapResult storage; + const char* error = ffDetectSwap(&storage); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_uint(doc, obj, "total", storage.bytesTotal); + yyjson_mut_obj_add_uint(doc, obj, "used", storage.bytesUsed); +} diff --git a/src/modules/swap/swap.h b/src/modules/swap/swap.h index 5921ad9c54..fbb125a449 100644 --- a/src/modules/swap/swap.h +++ b/src/modules/swap/swap.h @@ -9,3 +9,4 @@ void ffInitSwapOptions(FFSwapOptions* options); bool ffParseSwapCommandOptions(FFSwapOptions* options, const char* key, const char* value); void ffDestroySwapOptions(FFSwapOptions* options); void ffParseSwapJsonObject(FFSwapOptions* options, yyjson_val* module); +void ffGenerateSwapJson(FFSwapOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/terminal/terminal.c b/src/modules/terminal/terminal.c index 5afa31c190..ac68652f04 100644 --- a/src/modules/terminal/terminal.c +++ b/src/modules/terminal/terminal.c @@ -6,7 +6,7 @@ #include -#define FF_TERMINAL_NUM_FORMAT_ARGS 10 +#define FF_TERMINAL_NUM_FORMAT_ARGS 6 void ffPrintTerminal(FFTerminalOptions* options) { @@ -33,20 +33,16 @@ void ffPrintTerminal(FFTerminalOptions* options) {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalProcessName}, {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalExe}, {FF_FORMAT_ARG_TYPE_STRING, result->terminalExeName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellProcessName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellExe}, - {FF_FORMAT_ARG_TYPE_STRING, result->shellExeName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellExe}, - {FF_FORMAT_ARG_TYPE_STRING, result->userShellExeName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->userShellVersion} + {FF_FORMAT_ARG_TYPE_UINT, &result->terminalPid}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalPrettyName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalVersion}, }); } } void ffInitTerminalOptions(FFTerminalOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINAL_MODULE_NAME, ffParseTerminalCommandOptions, ffParseTerminalJsonObject, ffPrintTerminal); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINAL_MODULE_NAME, ffParseTerminalCommandOptions, ffParseTerminalJsonObject, ffPrintTerminal, ffGenerateTerminalJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -81,3 +77,22 @@ void ffParseTerminalJsonObject(FFTerminalOptions* options, yyjson_val* module) ffPrintError(FF_TERMINAL_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateTerminalJson(FF_MAYBE_UNUSED FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFTerminalShellResult* result = ffDetectTerminalShell(); + + if(result->terminalProcessName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "Couldn't detect terminal"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->terminalProcessName); + yyjson_mut_obj_add_strbuf(doc, obj, "exe", &result->terminalExe); + yyjson_mut_obj_add_strcpy(doc, obj, "exeName", result->terminalExeName); + yyjson_mut_obj_add_uint(doc, obj, "pid", result->terminalPid); + yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &result->terminalPrettyName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->terminalVersion); +} diff --git a/src/modules/terminal/terminal.h b/src/modules/terminal/terminal.h index 09949b00d0..709b02e568 100644 --- a/src/modules/terminal/terminal.h +++ b/src/modules/terminal/terminal.h @@ -9,3 +9,4 @@ void ffInitTerminalOptions(FFTerminalOptions* options); bool ffParseTerminalCommandOptions(FFTerminalOptions* options, const char* key, const char* value); void ffDestroyTerminalOptions(FFTerminalOptions* options); void ffParseTerminalJsonObject(FFTerminalOptions* options, yyjson_val* module); +void ffGenerateTerminalJson(FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/terminalfont/terminalfont.c b/src/modules/terminalfont/terminalfont.c index 755fc302dd..ca28576c92 100644 --- a/src/modules/terminalfont/terminalfont.c +++ b/src/modules/terminalfont/terminalfont.c @@ -49,7 +49,7 @@ void ffPrintTerminalFont(FFTerminalFontOptions* options) void ffInitTerminalFontOptions(FFTerminalFontOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINALFONT_MODULE_NAME, ffParseTerminalFontCommandOptions, ffParseTerminalFontJsonObject, ffPrintTerminalFont); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINALFONT_MODULE_NAME, ffParseTerminalFontCommandOptions, ffParseTerminalFontJsonObject, ffPrintTerminalFont, ffGenerateTerminalFontJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -84,3 +84,43 @@ void ffParseTerminalFontJsonObject(FFTerminalFontOptions* options, yyjson_val* m ffPrintError(FF_TERMINALFONT_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateTerminalFontJson(FF_MAYBE_UNUSED FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFTerminalFontResult result; + ffFontInit(&result.font); + ffFontInit(&result.fallback); + ffStrbufInit(&result.error); + + if(!ffDetectTerminalFont(&result)) + { + yyjson_mut_obj_add_strbuf(doc, module, "error", &result.error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + + yyjson_mut_val* font = yyjson_mut_obj_add_obj(doc, obj, "font"); + yyjson_mut_obj_add_strbuf(doc, font, "name", &result.font.name); + yyjson_mut_obj_add_strbuf(doc, font, "size", &result.font.size); + yyjson_mut_val* fontStyles = yyjson_mut_obj_add_arr(doc, font, "styles"); + FF_LIST_FOR_EACH(FFstrbuf, style, result.font.styles) + { + yyjson_mut_arr_add_strbuf(doc, fontStyles, style); + } + yyjson_mut_obj_add_strbuf(doc, font, "pretty", &result.font.pretty); + + yyjson_mut_val* fallback = yyjson_mut_obj_add_obj(doc, obj, "fallback"); + yyjson_mut_obj_add_strbuf(doc, fallback, "name", &result.fallback.name); + yyjson_mut_obj_add_strbuf(doc, fallback, "size", &result.fallback.size); + yyjson_mut_val* fallbackStyles = yyjson_mut_obj_add_arr(doc, fallback, "styles"); + FF_LIST_FOR_EACH(FFstrbuf, style, result.fallback.styles) + { + yyjson_mut_arr_add_strbuf(doc, fallbackStyles, style); + } + yyjson_mut_obj_add_strbuf(doc, fallback, "pretty", &result.fallback.pretty); + + ffStrbufDestroy(&result.error); + ffFontDestroy(&result.font); + ffFontDestroy(&result.fallback); +} diff --git a/src/modules/terminalfont/terminalfont.h b/src/modules/terminalfont/terminalfont.h index ac5953a367..434eb1a1f7 100644 --- a/src/modules/terminalfont/terminalfont.h +++ b/src/modules/terminalfont/terminalfont.h @@ -9,3 +9,4 @@ void ffInitTerminalFontOptions(FFTerminalFontOptions* options); bool ffParseTerminalFontCommandOptions(FFTerminalFontOptions* options, const char* key, const char* value); void ffDestroyTerminalFontOptions(FFTerminalFontOptions* options); void ffParseTerminalFontJsonObject(FFTerminalFontOptions* options, yyjson_val* module); +void ffGenerateTerminalFontJson(FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/terminalsize/terminalsize.c b/src/modules/terminalsize/terminalsize.c index f1ad620bc2..22ef849efe 100644 --- a/src/modules/terminalsize/terminalsize.c +++ b/src/modules/terminalsize/terminalsize.c @@ -41,7 +41,7 @@ void ffPrintTerminalSize(FFTerminalSizeOptions* options) void ffInitTerminalSizeOptions(FFTerminalSizeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINALSIZE_MODULE_NAME, ffParseTerminalSizeCommandOptions, ffParseTerminalSizeJsonObject, ffPrintTerminalSize); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TERMINALSIZE_MODULE_NAME, ffParseTerminalSizeCommandOptions, ffParseTerminalSizeJsonObject, ffPrintTerminalSize, ffGenerateTerminalSizeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -76,3 +76,20 @@ void ffParseTerminalSizeJsonObject(FFTerminalSizeOptions* options, yyjson_val* m ffPrintError(FF_TERMINALSIZE_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateTerminalSizeJson(FF_MAYBE_UNUSED FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFTerminalSizeResult result; + + if(!ffDetectTerminalSize(&result)) + { + yyjson_mut_obj_add_str(doc, module, "error", "Failed to detect terminal size"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_uint(doc, obj, "columns", result.columns); + yyjson_mut_obj_add_uint(doc, obj, "rows", result.rows); + yyjson_mut_obj_add_uint(doc, obj, "width", result.width); + yyjson_mut_obj_add_uint(doc, obj, "height", result.height); +} diff --git a/src/modules/terminalsize/terminalsize.h b/src/modules/terminalsize/terminalsize.h index 1703c5944a..33699057d5 100644 --- a/src/modules/terminalsize/terminalsize.h +++ b/src/modules/terminalsize/terminalsize.h @@ -9,3 +9,4 @@ void ffInitTerminalSizeOptions(FFTerminalSizeOptions* options); bool ffParseTerminalSizeCommandOptions(FFTerminalSizeOptions* options, const char* key, const char* value); void ffDestroyTerminalSizeOptions(FFTerminalSizeOptions* options); void ffParseTerminalSizeJsonObject(FFTerminalSizeOptions* options, yyjson_val* module); +void ffGenerateTerminalSizeJson(FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/theme/theme.c b/src/modules/theme/theme.c index 025e2c78f6..2fc89549bd 100644 --- a/src/modules/theme/theme.c +++ b/src/modules/theme/theme.c @@ -32,7 +32,7 @@ void ffPrintTheme(FFThemeOptions* options) void ffInitThemeOptions(FFThemeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_THEME_MODULE_NAME, ffParseThemeCommandOptions, ffParseThemeJsonObject, ffPrintTheme); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_THEME_MODULE_NAME, ffParseThemeCommandOptions, ffParseThemeJsonObject, ffPrintTheme, ffGenerateThemeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,17 @@ void ffParseThemeJsonObject(FFThemeOptions* options, yyjson_val* module) ffPrintError(FF_THEME_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateThemeJson(FF_MAYBE_UNUSED FFThemeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY theme = ffStrbufCreate(); + const char* error = ffDetectTheme(&theme); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &theme); +} diff --git a/src/modules/theme/theme.h b/src/modules/theme/theme.h index ef8f18a796..c0cee8584b 100644 --- a/src/modules/theme/theme.h +++ b/src/modules/theme/theme.h @@ -9,3 +9,4 @@ void ffInitThemeOptions(FFThemeOptions* options); bool ffParseThemeCommandOptions(FFThemeOptions* options, const char* key, const char* value); void ffDestroyThemeOptions(FFThemeOptions* options); void ffParseThemeJsonObject(FFThemeOptions* options, yyjson_val* module); +void ffGenerateThemeJson(FFThemeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/title/title.c b/src/modules/title/title.c index 8374cd10b5..a0174322a5 100644 --- a/src/modules/title/title.c +++ b/src/modules/title/title.c @@ -4,55 +4,77 @@ #include "util/textModifier.h" #include "util/stringUtils.h" -#define FF_TITLE_NUM_FORMAT_ARGS 2 +#define FF_TITLE_NUM_FORMAT_ARGS 8 -static inline void printTitlePart(const FFstrbuf* content, const FFstrbuf* color) +static void appendText(FFstrbuf* output, const FFstrbuf* text, const FFstrbuf* color) { - if(!instance.config.pipe) + if (!instance.config.pipe) { if (instance.config.brightColor) - fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout); - ffPrintColor(color->length > 0 ? color : &instance.config.colorTitle); + ffStrbufAppendS(output, FASTFETCH_TEXT_MODIFIER_BOLT); + if (color->length > 0) + ffStrbufAppendF(output, "\e[%sm", color->chars); + else if (instance.config.colorTitle.length > 0) + ffStrbufAppendF(output, "\e[%sm", instance.config.colorTitle.chars); } - ffStrbufWriteTo(content, stdout); + ffStrbufAppend(output, text); if(!instance.config.pipe) - fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); + ffStrbufAppendS(output, FASTFETCH_TEXT_MODIFIER_RESET); } void ffPrintTitle(FFTitleOptions* options) { - FFstrbuf* host = options->fqdn ? - &instance.state.platform.domainName : - &instance.state.platform.hostName; + FF_STRBUF_AUTO_DESTROY userNameColored = ffStrbufCreate(); + appendText(&userNameColored, &instance.state.platform.userName, &options->colorUser); - if (options->moduleArgs.outputFormat.length == 0) - { - ffPrintLogoAndKey(options->moduleArgs.key.length == 0 ? NULL : FF_TITLE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + FF_STRBUF_AUTO_DESTROY hostName = ffStrbufCreateCopy(&instance.state.platform.hostName); + if (!options->fqdn) + ffStrbufSubstrBeforeFirstC(&hostName, '.'); - printTitlePart(&instance.state.platform.userName, &options->colorUser); + FF_STRBUF_AUTO_DESTROY hostNameColored = ffStrbufCreate(); + appendText(&hostNameColored, &hostName, &options->colorHost); - if (!instance.config.pipe && options->colorAt.length > 0) - ffPrintColor(&options->colorAt); - putchar('@'); + FF_STRBUF_AUTO_DESTROY atColored = ffStrbufCreate(); + if (!instance.config.pipe && options->colorAt.length > 0) + { + ffStrbufAppendF(&atColored, "\e[%sm", options->colorAt.chars); + ffStrbufAppendC(&atColored, '@'); + ffStrbufAppendS(&atColored, FASTFETCH_TEXT_MODIFIER_RESET); + } + else + ffStrbufAppendC(&atColored, '@'); - printTitlePart(host, &options->colorHost); - putchar('\n'); + if (options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(FF_TITLE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + ffStrbufWriteTo(&userNameColored, stdout); + ffStrbufWriteTo(&atColored, stdout); + ffStrbufPutTo(&hostNameColored, stdout); } else { ffPrintFormat(FF_TITLE_MODULE_NAME, 0, &options->moduleArgs, FF_TITLE_NUM_FORMAT_ARGS, (FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.userName}, - {FF_FORMAT_ARG_TYPE_STRBUF, host}, + {FF_FORMAT_ARG_TYPE_STRBUF, &hostName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.homeDir}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.exePath}, + {FF_FORMAT_ARG_TYPE_STRBUF, &instance.state.platform.userShell}, + {FF_FORMAT_ARG_TYPE_STRBUF, &userNameColored}, + {FF_FORMAT_ARG_TYPE_STRBUF, &atColored}, + {FF_FORMAT_ARG_TYPE_STRBUF, &hostNameColored}, }); } } void ffInitTitleOptions(FFTitleOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TITLE_MODULE_NAME, ffParseTitleCommandOptions, ffParseTitleJsonObject, ffPrintTitle); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_TITLE_MODULE_NAME, ffParseTitleCommandOptions, ffParseTitleJsonObject, ffPrintTitle, ffGenerateTitleJson); ffOptionInitModuleArg(&options->moduleArgs); + ffStrbufSetStatic(&options->moduleArgs.key, " "); + options->fqdn = false; ffStrbufInit(&options->colorUser); ffStrbufInit(&options->colorAt); @@ -140,3 +162,13 @@ void ffParseTitleJsonObject(FFTitleOptions* options, yyjson_val* module) ffPrintErrorString(FF_TITLE_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", key); } } + +void ffGenerateTitleJson(FF_MAYBE_UNUSED FFTitleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "userName", &instance.state.platform.userName); + yyjson_mut_obj_add_strbuf(doc, obj, "hostName", &instance.state.platform.hostName); + yyjson_mut_obj_add_strbuf(doc, obj, "homeDir", &instance.state.platform.homeDir); + yyjson_mut_obj_add_strbuf(doc, obj, "exePath", &instance.state.platform.exePath); + yyjson_mut_obj_add_strbuf(doc, obj, "userShell", &instance.state.platform.userShell); +} diff --git a/src/modules/title/title.h b/src/modules/title/title.h index fd7ae5e689..76bec364d9 100644 --- a/src/modules/title/title.h +++ b/src/modules/title/title.h @@ -9,3 +9,4 @@ void ffInitTitleOptions(FFTitleOptions* options); bool ffParseTitleCommandOptions(FFTitleOptions* options, const char* key, const char* value); void ffDestroyTitleOptions(FFTitleOptions* options); void ffParseTitleJsonObject(FFTitleOptions* options, yyjson_val* module); +void ffGenerateTitleJson(FFTitleOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/uptime/uptime.c b/src/modules/uptime/uptime.c index 1574209e0a..96be154563 100644 --- a/src/modules/uptime/uptime.c +++ b/src/modules/uptime/uptime.c @@ -4,13 +4,13 @@ #include "modules/uptime/uptime.h" #include "util/stringUtils.h" -#define FF_UPTIME_NUM_FORMAT_ARGS 4 +#define FF_UPTIME_NUM_FORMAT_ARGS 7 void ffPrintUptime(FFUptimeOptions* options) { - uint64_t uptime; + FFUptimeResult result; - const char* error = ffDetectUptime(&uptime); + const char* error = ffDetectUptime(&result); if(error) { @@ -18,10 +18,17 @@ void ffPrintUptime(FFUptimeOptions* options) return; } - uint32_t days = (uint32_t) uptime / 86400; - uint32_t hours = (uint32_t) (uptime - (days * 86400)) / 3600; - uint32_t minutes = (uint32_t) (uptime - (days * 86400) - (hours * 3600)) / 60; - uint32_t seconds = (uint32_t) uptime - (days * 86400) - (hours * 3600) - (minutes * 60); + uint64_t uptime = result.uptime; + + uint32_t milliseconds = (uint32_t) (uptime % 1000); + uptime /= 1000; + uint32_t seconds = (uint32_t) (uptime % 60); + uptime /= 60; + uint32_t minutes = (uint32_t) (uptime % 60); + uptime /= 60; + uint32_t hours = (uint32_t) (uptime % 24); + uptime /= 24; + uint32_t days = (uint32_t) uptime; if(options->moduleArgs.outputFormat.length == 0) { @@ -74,14 +81,17 @@ void ffPrintUptime(FFUptimeOptions* options) {FF_FORMAT_ARG_TYPE_UINT, &days}, {FF_FORMAT_ARG_TYPE_UINT, &hours}, {FF_FORMAT_ARG_TYPE_UINT, &minutes}, - {FF_FORMAT_ARG_TYPE_UINT, &seconds} + {FF_FORMAT_ARG_TYPE_UINT, &seconds}, + {FF_FORMAT_ARG_TYPE_UINT, &milliseconds}, + {FF_FORMAT_ARG_TYPE_UINT64, &result.uptime}, + {FF_FORMAT_ARG_TYPE_UINT64, &result.bootTime}, }); } } void ffInitUptimeOptions(FFUptimeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_UPTIME_MODULE_NAME, ffParseUptimeCommandOptions, ffParseUptimeJsonObject, ffPrintUptime); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_UPTIME_MODULE_NAME, ffParseUptimeCommandOptions, ffParseUptimeJsonObject, ffPrintUptime, ffGenerateUptimeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -116,3 +126,19 @@ void ffParseUptimeJsonObject(FFUptimeOptions* options, yyjson_val* module) ffPrintError(FF_UPTIME_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateUptimeJson(FF_MAYBE_UNUSED FFUptimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFUptimeResult result; + const char* error = ffDetectUptime(&result); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_uint(doc, obj, "uptime", result.uptime); + yyjson_mut_obj_add_uint(doc, obj, "bootTime", result.bootTime); +} diff --git a/src/modules/uptime/uptime.h b/src/modules/uptime/uptime.h index 3bc1fd55f8..2fa12e9638 100644 --- a/src/modules/uptime/uptime.h +++ b/src/modules/uptime/uptime.h @@ -9,3 +9,4 @@ void ffInitUptimeOptions(FFUptimeOptions* options); bool ffParseUptimeCommandOptions(FFUptimeOptions* options, const char* key, const char* value); void ffDestroyUptimeOptions(FFUptimeOptions* options); void ffParseUptimeJsonObject(FFUptimeOptions* options, yyjson_val* module); +void ffGenerateUptimeJson(FFUptimeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/users/option.h b/src/modules/users/option.h index 80bf876985..6883e8dd08 100644 --- a/src/modules/users/option.h +++ b/src/modules/users/option.h @@ -8,4 +8,6 @@ typedef struct FFUsersOptions { FFModuleBaseInfo moduleInfo; FFModuleArgs moduleArgs; + + bool compact; } FFUsersOptions; diff --git a/src/modules/users/users.c b/src/modules/users/users.c index 0c12636208..ab58440c2e 100644 --- a/src/modules/users/users.c +++ b/src/modules/users/users.c @@ -4,49 +4,99 @@ #include "modules/users/users.h" #include "util/stringUtils.h" +#include + #define FF_USERS_NUM_FORMAT_ARGS 1 void ffPrintUsers(FFUsersOptions* options) { - FF_LIST_AUTO_DESTROY users = ffListCreate(sizeof(FFstrbuf)); - - FF_STRBUF_AUTO_DESTROY error = ffStrbufCreate(); + FF_LIST_AUTO_DESTROY users = ffListCreate(sizeof(FFUserResult)); - ffDetectUsers(&users, &error); + const char* error = ffDetectUsers(&users); - if(error.length > 0) + if(error) { - ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "%*s", error.length, error.chars); + ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "%s", error); return; } - FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); - for(uint32_t i = 0; i < users.length; ++i) + if(users.length == 0) { - if(i > 0) - ffStrbufAppendS(&result, ", "); - FFstrbuf* user = (FFstrbuf*)ffListGet(&users, i); - ffStrbufAppend(&result, user); - ffStrbufDestroy(user); + ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "%s", "Unable to detect any users"); + return; } if(options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - puts(result.chars); + if(options->compact) + { + ffPrintLogoAndKey(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + for(uint32_t i = 0; i < users.length; ++i) + { + if(i > 0) + ffStrbufAppendS(&result, ", "); + FFUserResult* user = (FFUserResult*)ffListGet(&users, i); + ffStrbufAppend(&result, &user->name); + } + ffStrbufPutTo(&result, stdout); + } + else + { + for(uint32_t i = 0; i < users.length; ++i) + { + FFUserResult* user = (FFUserResult*)ffListGet(&users, i); + + ffPrintLogoAndKey(FF_USERS_MODULE_NAME, users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateCopy(&user->name); + if(user->hostName.length) + ffStrbufAppendF(&result, "@%s", user->hostName.chars); + + if(user->loginTime) + { + char buf[64]; + time_t time = (time_t) (user->loginTime / 1000); + strftime(buf, sizeof(buf), "%FT%T%z", localtime(&time)); + ffStrbufAppendF(&result, " - login time %s", buf); + } + + ffStrbufPutTo(&result, stdout); + } + } } else { - ffPrintFormat(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_USERS_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, &result}, - }); + for(uint32_t i = 0; i < users.length; ++i) + { + FFUserResult* user = (FFUserResult*)ffListGet(&users, i); + + ffPrintFormat(FF_USERS_MODULE_NAME, users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_USERS_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &user->name}, + {FF_FORMAT_ARG_TYPE_STRBUF, &user->hostName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &user->tty}, + {FF_FORMAT_ARG_TYPE_STRBUF, &user->clientIp}, + {FF_FORMAT_ARG_TYPE_UINT64, &user->loginTime}, + }); + } + } + + FF_LIST_FOR_EACH(FFUserResult, user, users) + { + ffStrbufDestroy(&user->clientIp); + ffStrbufDestroy(&user->hostName); + ffStrbufDestroy(&user->tty); + ffStrbufDestroy(&user->name); } } void ffInitUsersOptions(FFUsersOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_USERS_MODULE_NAME, ffParseUsersCommandOptions, ffParseUsersJsonObject, ffPrintUsers); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_USERS_MODULE_NAME, ffParseUsersCommandOptions, ffParseUsersJsonObject, ffPrintUsers, ffGenerateUsersJson); ffOptionInitModuleArg(&options->moduleArgs); + + options->compact = false; } bool ffParseUsersCommandOptions(FFUsersOptions* options, const char* key, const char* value) @@ -56,6 +106,12 @@ bool ffParseUsersCommandOptions(FFUsersOptions* options, const char* key, const if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) return true; + if(ffStrEqualsIgnCase(subKey, "compact")) + { + options->compact = ffOptionParseBoolean(value); + return true; + } + return false; } @@ -77,6 +133,45 @@ void ffParseUsersJsonObject(FFUsersOptions* options, yyjson_val* module) if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) continue; + if (ffStrEqualsIgnCase(key, "compact")) + { + options->compact = yyjson_get_bool(val); + continue; + } + ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateUsersJson(FF_MAYBE_UNUSED FFUsersOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFUserResult)); + + const char* error = ffDetectUsers(&results); + + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + goto exit; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFUserResult, user, results) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + yyjson_mut_obj_add_strbuf(doc, obj, "clientIp", &user->clientIp); + yyjson_mut_obj_add_strbuf(doc, obj, "hostName", &user->hostName); + yyjson_mut_obj_add_uint(doc, obj, "loginTime", user->loginTime); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &user->name); + yyjson_mut_obj_add_strbuf(doc, obj, "tty", &user->tty); + } + +exit: + FF_LIST_FOR_EACH(FFUserResult, user, results) + { + ffStrbufDestroy(&user->clientIp); + ffStrbufDestroy(&user->hostName); + ffStrbufDestroy(&user->tty); + ffStrbufDestroy(&user->name); + } +} diff --git a/src/modules/users/users.h b/src/modules/users/users.h index 86d5f66f48..3ef57a1cf7 100644 --- a/src/modules/users/users.h +++ b/src/modules/users/users.h @@ -9,3 +9,4 @@ void ffInitUsersOptions(FFUsersOptions* options); bool ffParseUsersCommandOptions(FFUsersOptions* options, const char* key, const char* value); void ffDestroyUsersOptions(FFUsersOptions* options); void ffParseUsersJsonObject(FFUsersOptions* options, yyjson_val* module); +void ffGenerateUsersJson(FFUsersOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/version/version.c b/src/modules/version/version.c index 9cef899d24..54781eb622 100644 --- a/src/modules/version/version.c +++ b/src/modules/version/version.c @@ -4,11 +4,11 @@ #include "modules/version/version.h" #include "util/stringUtils.h" -#define FF_VERSION_NUM_FORMAT_ARGS 6 +#define FF_VERSION_NUM_FORMAT_ARGS 8 void ffPrintVersion(FFVersionOptions* options) { - FFVersionResult result = {}; + FFVersionResult result; ffDetectVersion(&result); if(options->moduleArgs.outputFormat.length == 0) @@ -25,13 +25,15 @@ void ffPrintVersion(FFVersionOptions* options) {FF_FORMAT_ARG_TYPE_STRING, result.debugMode ? "debug" : "release"}, {FF_FORMAT_ARG_TYPE_STRING, result.architecture}, {FF_FORMAT_ARG_TYPE_STRING, result.cmakeBuiltType}, + {FF_FORMAT_ARG_TYPE_STRING, result.compileTime}, + {FF_FORMAT_ARG_TYPE_STRING, result.compiler}, }); } } void ffInitVersionOptions(FFVersionOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_VERSION_MODULE_NAME, ffParseVersionCommandOptions, ffParseVersionJsonObject, ffPrintVersion); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_VERSION_MODULE_NAME, ffParseVersionCommandOptions, ffParseVersionJsonObject, ffPrintVersion, ffGenerateVersionJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -66,3 +68,19 @@ void ffParseVersionJsonObject(FFVersionOptions* options, yyjson_val* module) ffPrintError(FF_VERSION_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateVersionJson(FF_MAYBE_UNUSED FFVersionOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFVersionResult result; + ffDetectVersion(&result); + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_str(doc, obj, "projectName", result.projectName); + yyjson_mut_obj_add_str(doc, obj, "architecture", result.architecture); + yyjson_mut_obj_add_str(doc, obj, "version", result.version); + yyjson_mut_obj_add_str(doc, obj, "versionTweak", result.versionTweak); + yyjson_mut_obj_add_str(doc, obj, "cmakeBuiltType", result.cmakeBuiltType); + yyjson_mut_obj_add_str(doc, obj, "compileTime", result.compileTime); + yyjson_mut_obj_add_str(doc, obj, "compiler", result.compiler); + yyjson_mut_obj_add_bool(doc, obj, "debugMode", result.debugMode); +} diff --git a/src/modules/version/version.h b/src/modules/version/version.h index ef423b36ed..bb1e4c15f7 100644 --- a/src/modules/version/version.h +++ b/src/modules/version/version.h @@ -9,3 +9,4 @@ void ffInitVersionOptions(FFVersionOptions* options); bool ffParseVersionCommandOptions(FFVersionOptions* options, const char* key, const char* value); void ffDestroyVersionOptions(FFVersionOptions* options); void ffParseVersionJsonObject(FFVersionOptions* options, yyjson_val* module); +void ffGenerateVersionJson(FFVersionOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/vulkan/vulkan.c b/src/modules/vulkan/vulkan.c index bb66ae6044..14c37153ac 100644 --- a/src/modules/vulkan/vulkan.c +++ b/src/modules/vulkan/vulkan.c @@ -1,5 +1,6 @@ #include "common/printing.h" #include "common/jsonconfig.h" +#include "detection/gpu/gpu.h" #include "detection/vulkan/vulkan.h" #include "modules/vulkan/vulkan.h" #include "util/stringUtils.h" @@ -45,7 +46,7 @@ void ffPrintVulkan(FFVulkanOptions* options) void ffInitVulkanOptions(FFVulkanOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_VULKAN_MODULE_NAME, ffParseVulkanCommandOptions, ffParseVulkanJsonObject, ffPrintVulkan); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_VULKAN_MODULE_NAME, ffParseVulkanCommandOptions, ffParseVulkanJsonObject, ffPrintVulkan, ffGenerateVulkanJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -80,3 +81,58 @@ void ffParseVulkanJsonObject(FFVulkanOptions* options, yyjson_val* module) ffPrintError(FF_VULKAN_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateVulkanJson(FF_MAYBE_UNUSED FFVulkanOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFVulkanResult* result = ffDetectVulkan(); + + if(result->error) + { + yyjson_mut_obj_add_str(doc, module, "error", result->error); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_strbuf(doc, obj, "apiVersion", &result->apiVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "conformanceVersion", &result->conformanceVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "driver", &result->driver); + yyjson_mut_val* gpus = yyjson_mut_obj_add_arr(doc, obj, "gpus"); + FF_LIST_FOR_EACH(FFGPUResult, vulkanGpu, result->gpus) + { + yyjson_mut_val* gpuObj = yyjson_mut_arr_add_obj(doc, gpus); + yyjson_mut_obj_add_str(doc, gpuObj, "type", vulkanGpu->type == FF_GPU_TYPE_UNKNOWN ? "Unknown" : vulkanGpu->type == FF_GPU_TYPE_INTEGRATED ? "Integrated" : "Discrete"); + yyjson_mut_obj_add_strbuf(doc, gpuObj, "vendor", &vulkanGpu->vendor); + yyjson_mut_obj_add_strbuf(doc, gpuObj, "name", &vulkanGpu->name); + yyjson_mut_obj_add_strbuf(doc, gpuObj, "driver", &vulkanGpu->driver); + + yyjson_mut_val* memoryObj = yyjson_mut_obj_add_obj(doc, gpuObj, "memory"); + + { + yyjson_mut_val* dedicatedMemory = yyjson_mut_obj_add_obj(doc, memoryObj, "dedicated"); + if (vulkanGpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, dedicatedMemory, "total", vulkanGpu->dedicated.total); + else + yyjson_mut_obj_add_null(doc, dedicatedMemory, "total"); + + if (vulkanGpu->dedicated.used != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, dedicatedMemory, "used", vulkanGpu->dedicated.total); + else + yyjson_mut_obj_add_null(doc, dedicatedMemory, "used"); + } + + { + yyjson_mut_val* sharedMemory = yyjson_mut_obj_add_obj(doc, memoryObj, "shared"); + if (vulkanGpu->shared.total != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, sharedMemory, "total", vulkanGpu->shared.total); + else + yyjson_mut_obj_add_null(doc, sharedMemory, "total"); + + if (vulkanGpu->shared.used != FF_GPU_VMEM_SIZE_UNSET) + yyjson_mut_obj_add_uint(doc, sharedMemory, "used", vulkanGpu->shared.used); + else + yyjson_mut_obj_add_null(doc, sharedMemory, "used"); + } + + yyjson_mut_obj_add_uint(doc, gpuObj, "deviceId", vulkanGpu->vulkanDeviceId); + } +} diff --git a/src/modules/vulkan/vulkan.h b/src/modules/vulkan/vulkan.h index e9b7d3954e..a0b378394e 100644 --- a/src/modules/vulkan/vulkan.h +++ b/src/modules/vulkan/vulkan.h @@ -9,3 +9,4 @@ void ffInitVulkanOptions(FFVulkanOptions* options); bool ffParseVulkanCommandOptions(FFVulkanOptions* options, const char* key, const char* value); void ffDestroyVulkanOptions(FFVulkanOptions* options); void ffParseVulkanJsonObject(FFVulkanOptions* options, yyjson_val* module); +void ffGenerateVulkanJson(FFVulkanOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/wallpaper/wallpaper.c b/src/modules/wallpaper/wallpaper.c index e51ba590a0..947a1031e5 100644 --- a/src/modules/wallpaper/wallpaper.c +++ b/src/modules/wallpaper/wallpaper.c @@ -44,7 +44,7 @@ void ffPrintWallpaper(FFWallpaperOptions* options) void ffInitWallpaperOptions(FFWallpaperOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WALLPAPER_MODULE_NAME, ffParseWallpaperCommandOptions, ffParseWallpaperJsonObject, ffPrintWallpaper); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WALLPAPER_MODULE_NAME, ffParseWallpaperCommandOptions, ffParseWallpaperJsonObject, ffPrintWallpaper, ffGenerateWallpaperJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -79,3 +79,15 @@ void ffParseWallpaperJsonObject(FFWallpaperOptions* options, yyjson_val* module) ffPrintError(FF_WALLPAPER_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWallpaperJson(FF_MAYBE_UNUSED FFWallpaperOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY fullpath = ffStrbufCreate(); + const char* error = ffDetectWallpaper(&fullpath); + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + yyjson_mut_obj_add_strbuf(doc, module, "result", &fullpath); +} diff --git a/src/modules/wallpaper/wallpaper.h b/src/modules/wallpaper/wallpaper.h index 244684fc4c..3925784b27 100644 --- a/src/modules/wallpaper/wallpaper.h +++ b/src/modules/wallpaper/wallpaper.h @@ -9,3 +9,4 @@ void ffInitWallpaperOptions(FFWallpaperOptions* options); bool ffParseWallpaperCommandOptions(FFWallpaperOptions* options, const char* key, const char* value); void ffDestroyWallpaperOptions(FFWallpaperOptions* options); void ffParseWallpaperJsonObject(FFWallpaperOptions* options, yyjson_val* module); +void ffGenerateWallpaperJson(FFWallpaperOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/weather/weather.c b/src/modules/weather/weather.c index 68d18d5507..ec25374b5d 100644 --- a/src/modules/weather/weather.c +++ b/src/modules/weather/weather.c @@ -1,50 +1,22 @@ #include "common/printing.h" #include "common/jsonconfig.h" -#include "common/networking.h" +#include "detection/weather/weather.h" #include "modules/weather/weather.h" #include "util/stringUtils.h" #define FF_WEATHER_NUM_FORMAT_ARGS 1 -static FFNetworkingState state; -static int status = -1; - -void ffPrepareWeather(FFWeatherOptions* options) -{ - if (status != -1) - { - fputs("Error: " FF_WEATHER_MODULE_NAME " can only be used once due to internal limitations\n", stderr); - exit(1); - } - - FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateS("/"); - if (options->location.length) - ffStrbufAppend(&path, &options->location); - ffStrbufAppendS(&path, "?format="); - ffStrbufAppend(&path, &options->outputFormat); - status = ffNetworkingSendHttpRequest(&state, "wttr.in", path.chars, "User-Agent: curl/0.0.0\r\n"); -} - void ffPrintWeather(FFWeatherOptions* options) { - if(status == -1) - ffPrepareWeather(options); + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + const char* error = ffDetectWeather(options, &result); - if(status == 0) + if(error) { - ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, "Failed to connect to 'wttr.in'"); + ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, "%s", error); return; } - FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateA(4096); - bool success = ffNetworkingRecvHttpResponse(&state, &result, options->timeout); - if (success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n"); - - if(!success || result.length == 0) - { - ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, "Failed to receive the server response"); - return; - } if(options->moduleArgs.outputFormat.length == 0) { @@ -61,7 +33,7 @@ void ffPrintWeather(FFWeatherOptions* options) void ffInitWeatherOptions(FFWeatherOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WEATHER_MODULE_NAME, ffParseWeatherCommandOptions, ffParseWeatherJsonObject, ffPrintWeather); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WEATHER_MODULE_NAME, ffParseWeatherCommandOptions, ffParseWeatherJsonObject, ffPrintWeather, ffGenerateWeatherJson); ffOptionInitModuleArg(&options->moduleArgs); ffStrbufInit(&options->location); @@ -138,3 +110,17 @@ void ffParseWeatherJsonObject(FFWeatherOptions* options, yyjson_val* module) ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWeatherJson(FFWeatherOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + const char* error = ffDetectWeather(options, &result); + + if (error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &result); +} diff --git a/src/modules/weather/weather.h b/src/modules/weather/weather.h index 8886069577..a769402448 100644 --- a/src/modules/weather/weather.h +++ b/src/modules/weather/weather.h @@ -11,3 +11,4 @@ void ffInitWeatherOptions(FFWeatherOptions* options); bool ffParseWeatherCommandOptions(FFWeatherOptions* options, const char* key, const char* value); void ffDestroyWeatherOptions(FFWeatherOptions* options); void ffParseWeatherJsonObject(FFWeatherOptions* options, yyjson_val* module); +void ffGenerateWeatherJson(FFWeatherOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/wifi/wifi.c b/src/modules/wifi/wifi.c index afa0269172..449b82e67a 100644 --- a/src/modules/wifi/wifi.c +++ b/src/modules/wifi/wifi.c @@ -72,7 +72,7 @@ void ffPrintWifi(FFWifiOptions* options) void ffInitWifiOptions(FFWifiOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WIFI_MODULE_NAME, ffParseWifiCommandOptions, ffParseWifiJsonObject, ffPrintWifi); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WIFI_MODULE_NAME, ffParseWifiCommandOptions, ffParseWifiJsonObject, ffPrintWifi, ffGenerateWifiJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -107,3 +107,49 @@ void ffParseWifiJsonObject(FFWifiOptions* options, yyjson_val* module) ffPrintError(FF_WIFI_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWifiJson(FF_MAYBE_UNUSED FFWifiOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFWifiResult)); + const char* error = ffDetectWifi(&result); + if(error) + { + yyjson_mut_obj_add_str(doc, module, "error", error); + return; + } + if(!result.length) + { + yyjson_mut_obj_add_str(doc, module, "error", "No Wifi interfaces found"); + return; + } + + yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH(FFWifiResult, wifi, result) + { + yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); + + yyjson_mut_val* inf = yyjson_mut_obj_add_obj(doc, obj, "inf"); + yyjson_mut_obj_add_strbuf(doc, inf, "description", &wifi->inf.description); + yyjson_mut_obj_add_strbuf(doc, inf, "status", &wifi->inf.status); + + yyjson_mut_val* conn = yyjson_mut_obj_add_obj(doc, obj, "conn"); + yyjson_mut_obj_add_strbuf(doc, conn, "status", &wifi->conn.status); + yyjson_mut_obj_add_strbuf(doc, conn, "ssid", &wifi->conn.ssid); + yyjson_mut_obj_add_strbuf(doc, conn, "macAddress", &wifi->conn.macAddress); + yyjson_mut_obj_add_strbuf(doc, conn, "protocol", &wifi->conn.protocol); + yyjson_mut_obj_add_real(doc, conn, "signalQuality", wifi->conn.signalQuality); + yyjson_mut_obj_add_real(doc, conn, "rxRate", wifi->conn.rxRate); + yyjson_mut_obj_add_real(doc, conn, "txRate", wifi->conn.txRate); + } + + FF_LIST_FOR_EACH(FFWifiResult, item, result) + { + ffStrbufDestroy(&item->inf.description); + ffStrbufDestroy(&item->inf.status); + ffStrbufDestroy(&item->conn.status); + ffStrbufDestroy(&item->conn.ssid); + ffStrbufDestroy(&item->conn.macAddress); + ffStrbufDestroy(&item->conn.protocol); + ffStrbufDestroy(&item->conn.security); + } +} diff --git a/src/modules/wifi/wifi.h b/src/modules/wifi/wifi.h index 0a7b78e2d0..2957f876d8 100644 --- a/src/modules/wifi/wifi.h +++ b/src/modules/wifi/wifi.h @@ -9,3 +9,4 @@ void ffInitWifiOptions(FFWifiOptions* options); bool ffParseWifiCommandOptions(FFWifiOptions* options, const char* key, const char* value); void ffDestroyWifiOptions(FFWifiOptions* options); void ffParseWifiJsonObject(FFWifiOptions* options, yyjson_val* module); +void ffGenerateWifiJson(FFWifiOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/wm/wm.c b/src/modules/wm/wm.c index f02efbf4e1..371b48deb8 100644 --- a/src/modules/wm/wm.c +++ b/src/modules/wm/wm.c @@ -43,7 +43,7 @@ void ffPrintWM(FFWMOptions* options) void ffInitWMOptions(FFWMOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WM_MODULE_NAME, ffParseWMCommandOptions, ffParseWMJsonObject, ffPrintWM); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WM_MODULE_NAME, ffParseWMCommandOptions, ffParseWMJsonObject, ffPrintWM, ffGenerateWMJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -78,3 +78,18 @@ void ffParseWMJsonObject(FFWMOptions* options, yyjson_val* module) ffPrintError(FF_WM_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWMJson(FF_MAYBE_UNUSED FFWMOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + const FFDisplayServerResult* result = ffConnectDisplayServer(); + + if(result->wmPrettyName.length == 0) + { + yyjson_mut_obj_add_str(doc, module, "error", "No WM found"); + return; + } + 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); +} diff --git a/src/modules/wm/wm.h b/src/modules/wm/wm.h index 607732d720..fe960b6885 100644 --- a/src/modules/wm/wm.h +++ b/src/modules/wm/wm.h @@ -9,3 +9,4 @@ void ffInitWMOptions(FFWMOptions* options); bool ffParseWMCommandOptions(FFWMOptions* options, const char* key, const char* value); void ffDestroyWMOptions(FFWMOptions* options); void ffParseWMJsonObject(FFWMOptions* options, yyjson_val* module); +void ffGenerateWMJson(FFWMOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/modules/wmtheme/wmtheme.c b/src/modules/wmtheme/wmtheme.c index 438e7f8326..3560769223 100644 --- a/src/modules/wmtheme/wmtheme.c +++ b/src/modules/wmtheme/wmtheme.c @@ -32,7 +32,7 @@ void ffPrintWMTheme(FFWMThemeOptions* options) void ffInitWMThemeOptions(FFWMThemeOptions* options) { - ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WMTHEME_MODULE_NAME, ffParseWMThemeCommandOptions, ffParseWMThemeJsonObject, ffPrintWMTheme); + ffOptionInitModuleBaseInfo(&options->moduleInfo, FF_WMTHEME_MODULE_NAME, ffParseWMThemeCommandOptions, ffParseWMThemeJsonObject, ffPrintWMTheme, ffGenerateWMThemeJson); ffOptionInitModuleArg(&options->moduleArgs); } @@ -67,3 +67,15 @@ void ffParseWMThemeJsonObject(FFWMThemeOptions* options, yyjson_val* module) ffPrintError(FF_WMTHEME_DISPLAY_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } + +void ffGenerateWMThemeJson(FF_MAYBE_UNUSED FFWMThemeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FF_STRBUF_AUTO_DESTROY themeOrError = ffStrbufCreate(); + if(!ffDetectWmTheme(&themeOrError)) + { + yyjson_mut_obj_add_strbuf(doc, module, "error", &themeOrError); + return; + } + + yyjson_mut_obj_add_strbuf(doc, module, "result", &themeOrError); +} diff --git a/src/modules/wmtheme/wmtheme.h b/src/modules/wmtheme/wmtheme.h index 216fdc7233..e90f9e2530 100644 --- a/src/modules/wmtheme/wmtheme.h +++ b/src/modules/wmtheme/wmtheme.h @@ -9,3 +9,4 @@ void ffInitWMThemeOptions(FFWMThemeOptions* options); bool ffParseWMThemeCommandOptions(FFWMThemeOptions* options, const char* key, const char* value); void ffDestroyWMThemeOptions(FFWMThemeOptions* options); void ffParseWMThemeJsonObject(FFWMThemeOptions* options, yyjson_val* module); +void ffGenerateWMThemeJson(FFWMThemeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module); diff --git a/src/util/FFcheckmacros.h b/src/util/FFcheckmacros.h index 1ed61cf82d..9eb45f78cf 100644 --- a/src/util/FFcheckmacros.h +++ b/src/util/FFcheckmacros.h @@ -3,8 +3,14 @@ #ifndef FASTFETCH_INCLUDED_FFCHECKMACROS #define FASTFETCH_INCLUDED_FFCHECKMACROS +#ifdef _MSC_VER + #include +#endif + #if defined(__GNUC__) || defined(__clang__) #define FF_C_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) + #define FF_C_NODISCARD _Check_return_ #else #define FF_C_NODISCARD #endif diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 70a2a029df..221a98fc30 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -73,6 +73,14 @@ void ffStrbufAppendC(FFstrbuf* strbuf, char c) strbuf->chars[strbuf->length] = '\0'; } +void ffStrbufAppendNC(FFstrbuf* strbuf, uint32_t num, char c) +{ + ffStrbufEnsureFree(strbuf, num); + memset(&strbuf->chars[strbuf->length], c, num); + strbuf->length += num; + strbuf->chars[strbuf->length] = '\0'; +} + void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value) { if(value == NULL || length == 0) @@ -423,11 +431,11 @@ double ffStrbufToDouble(const FFstrbuf* strbuf) return str_end == strbuf->chars ? 0.0/0.0 : result; } -uint16_t ffStrbufToUInt16(const FFstrbuf* strbuf, uint16_t defaultValue) +uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue) { char* str_end; - unsigned long result = strtoul(strbuf->chars, &str_end, 10); - return str_end == strbuf->chars || result > UINT16_MAX ? defaultValue : (uint16_t)result; + unsigned long long result = strtoull(strbuf->chars, &str_end, 10); + return str_end == strbuf->chars ? defaultValue : (uint64_t)result; } void ffStrbufUpperCase(FFstrbuf* strbuf) diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 9e9bce198d..9a28bafb4e 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -38,6 +38,7 @@ void ffStrbufClear(FFstrbuf* strbuf); static inline void ffStrbufAppend(FFstrbuf* __restrict strbuf, const FFstrbuf* __restrict value); void ffStrbufAppendC(FFstrbuf* strbuf, char c); +void ffStrbufAppendNC(FFstrbuf* strbuf, uint32_t num, char c); void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const char* value); void ffStrbufAppendNSExludingC(FFstrbuf* strbuf, uint32_t length, const char* value, char exclude); void ffStrbufAppendTransformS(FFstrbuf* strbuf, const char* value, int(*transformFunc)(int)); @@ -49,7 +50,7 @@ void ffStrbufPrependNS(FFstrbuf* strbuf, uint32_t length, const char* value); void ffStrbufSetNS(FFstrbuf* strbuf, uint32_t length, const char* value); void ffStrbufSet(FFstrbuf* strbuf, const FFstrbuf* value); -void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...); +FF_C_PRINTF(2, 3) void ffStrbufSetF(FFstrbuf* strbuf, const char* format, ...); void ffStrbufTrimLeft(FFstrbuf* strbuf, char c); void ffStrbufTrimRight(FFstrbuf* strbuf, char c); @@ -81,7 +82,7 @@ void ffStrbufWriteTo(const FFstrbuf* strbuf, FILE* file); void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file); FF_C_NODISCARD double ffStrbufToDouble(const FFstrbuf* strbuf); -FF_C_NODISCARD uint16_t ffStrbufToUInt16(const FFstrbuf* strbuf, uint16_t defaultValue); +FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue); void ffStrbufUpperCase(FFstrbuf* strbuf); void ffStrbufLowerCase(FFstrbuf* strbuf); diff --git a/src/util/apple/cf_helpers.c b/src/util/apple/cf_helpers.c index 797b51b591..2ce563d5ed 100644 --- a/src/util/apple/cf_helpers.c +++ b/src/util/apple/cf_helpers.c @@ -83,6 +83,27 @@ const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) return "TypeID is neither 'CFNumber' nor 'CFData'"; } +const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length) +{ + CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key); + if(cf == NULL) + return "CFDictionaryGetValue() failed"; + + if(CFGetTypeID(cf) != CFDataGetTypeID()) + return "TypeID is not 'CFData'"; + + CFIndex trueLength = CFDataGetLength((CFDataRef)cf); + + if(trueLength < offset + size) + return "Data length is less than offset + size"; + + if(length) + *length = (uint32_t) trueLength; + + CFDataGetBytes((CFDataRef)cf, CFRangeMake(offset, size), result); + return NULL; +} + const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result) { CFDictionaryRef cf = (CFDictionaryRef)CFDictionaryGetValue(dict, key); diff --git a/src/util/apple/cf_helpers.h b/src/util/apple/cf_helpers.h index 52755bb8bc..a8d9804a3f 100644 --- a/src/util/apple/cf_helpers.h +++ b/src/util/apple/cf_helpers.h @@ -11,6 +11,7 @@ const char* ffCfStrGetString(CFStringRef str, FFstrbuf* result); const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result); const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result); const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result); +const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length); const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result); static inline CFNumberRef ffCfCreateInt(int value) diff --git a/src/util/platform/FFPlatform.c b/src/util/platform/FFPlatform.c index ad0a179da8..b81adc3c6a 100644 --- a/src/util/platform/FFPlatform.c +++ b/src/util/platform/FFPlatform.c @@ -12,7 +12,6 @@ void ffPlatformInit(FFPlatform* platform) ffStrbufInit(&platform->userName); ffStrbufInit(&platform->hostName); - ffStrbufInit(&platform->domainName); ffStrbufInit(&platform->userShell); ffStrbufInit(&platform->systemName); @@ -22,9 +21,6 @@ void ffPlatformInit(FFPlatform* platform) ffPlatformInitImpl(platform); - if(platform->domainName.length == 0) - ffStrbufAppend(&platform->domainName, &platform->hostName); - if(platform->systemName.length == 0) { #if defined(__linux__) @@ -59,13 +55,13 @@ void ffPlatformDestroy(FFPlatform* platform) ffStrbufDestroy(&platform->userName); ffStrbufDestroy(&platform->hostName); - ffStrbufDestroy(&platform->domainName); ffStrbufDestroy(&platform->userShell); ffStrbufDestroy(&platform->systemArchitecture); ffStrbufDestroy(&platform->systemName); ffStrbufDestroy(&platform->systemRelease); ffStrbufDestroy(&platform->systemVersion); + ffStrbufDestroy(&platform->systemDisplayVersion); } void ffPlatformPathAddAbsolute(FFlist* dirs, const char* path) diff --git a/src/util/platform/FFPlatform.h b/src/util/platform/FFPlatform.h index fa646f9deb..48c72513ef 100644 --- a/src/util/platform/FFPlatform.h +++ b/src/util/platform/FFPlatform.h @@ -15,13 +15,13 @@ typedef struct FFPlatform { FFstrbuf userName; FFstrbuf hostName; - FFstrbuf domainName; FFstrbuf userShell; FFstrbuf systemName; FFstrbuf systemRelease; FFstrbuf systemVersion; FFstrbuf systemArchitecture; + FFstrbuf systemDisplayVersion; } FFPlatform; void ffPlatformInit(FFPlatform* platform); diff --git a/src/util/platform/FFPlatform_unix.c b/src/util/platform/FFPlatform_unix.c index 3f7aada12d..33c76bc694 100644 --- a/src/util/platform/FFPlatform_unix.c +++ b/src/util/platform/FFPlatform_unix.c @@ -141,37 +141,8 @@ static void getUserName(FFPlatform* platform, const struct passwd* pwd) static void getHostName(FFPlatform* platform, const struct utsname* uts) { - char hostname[256]; - if(gethostname(hostname, sizeof(hostname)) == 0) - ffStrbufAppendS(&platform->hostName, hostname); - - if(platform->hostName.length == 0) - ffStrbufAppendS(&platform->hostName, uts->nodename); -} - -#ifdef __linux__ -#include -static void getDomainName(FFPlatform* platform) -{ - struct addrinfo hints = {0}; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - - struct addrinfo* info = NULL; - - if(getaddrinfo(platform->hostName.chars, "80", &hints, &info) != 0) - return; - - struct addrinfo* current = info; - while(platform->domainName.length == 0 && current != NULL) - { - ffStrbufAppendS(&platform->domainName, current->ai_canonname); - current = current->ai_next; - } - - freeaddrinfo(info); + ffStrbufAppendS(&platform->hostName, uts->nodename); } -#endif static void getUserShell(FFPlatform* platform, const struct passwd* pwd) { @@ -198,15 +169,11 @@ void ffPlatformInitImpl(FFPlatform* platform) getUserName(platform, pwd); getHostName(platform, &uts); - - #ifdef __linux__ - getDomainName(platform); - #endif - getUserShell(platform, pwd); ffStrbufAppendS(&platform->systemName, uts.sysname); ffStrbufAppendS(&platform->systemRelease, uts.release); ffStrbufAppendS(&platform->systemVersion, uts.version); ffStrbufAppendS(&platform->systemArchitecture, uts.machine); + ffStrbufInit(&platform->systemDisplayVersion); } diff --git a/src/util/platform/FFPlatform_windows.c b/src/util/platform/FFPlatform_windows.c index 0009dd10e7..0774c81c39 100644 --- a/src/util/platform/FFPlatform_windows.c +++ b/src/util/platform/FFPlatform_windows.c @@ -2,10 +2,16 @@ #include "common/io/io.h" #include "util/stringUtils.h" #include "util/windows/unicode.h" +#include "util/windows/registry.h" +#include #include #include +NTSTATUS NTAPI RtlGetVersion( + _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation +); + static void getExePath(FFPlatform* platform) { wchar_t exePathW[MAX_PATH]; @@ -130,63 +136,51 @@ static void getHostName(FFPlatform* platform) ffStrbufSetWS(&platform->hostName, buffer); } -static void getDomainName(FFPlatform* platform) +static void getUserShell(FFPlatform* platform) { - wchar_t buffer[128]; - DWORD len = sizeof(buffer) / sizeof(*buffer); - if(GetComputerNameExW(ComputerNameDnsDomain, buffer, &len)) - ffStrbufSetWS(&platform->domainName, buffer); -} - -static void getSystemName(FFPlatform* platform) -{ - ffStrbufAppendS(&platform->systemName, getenv("OS")); + // Works in MSYS2 + ffStrbufAppendS(&platform->userShell, getenv("SHELL")); + ffStrbufReplaceAllC(&platform->userShell, '\\', '/'); } static void getSystemReleaseAndVersion(FFPlatform* platform) { - HKEY hKey; - if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) + RTL_OSVERSIONINFOW osVersion = { .dwOSVersionInfoSize = sizeof(osVersion) }; + if (!NT_SUCCESS(RtlGetVersion(&osVersion))) return; - DWORD bufSize; - - char currentVersion[32]; - - { - DWORD currentMajorVersionNumber; - DWORD currentMinorVersionNumber; - bufSize = sizeof(currentMajorVersionNumber); - if(RegGetValueW(hKey, NULL, L"CurrentMajorVersionNumber", RRF_RT_REG_DWORD, NULL, ¤tMajorVersionNumber, &bufSize) == ERROR_SUCCESS && - RegGetValueW(hKey, NULL, L"CurrentMinorVersionNumber", RRF_RT_REG_DWORD, NULL, ¤tMinorVersionNumber, &bufSize) == ERROR_SUCCESS - ) - snprintf(currentVersion, sizeof(currentVersion), "%u.%u", (unsigned)currentMajorVersionNumber, (unsigned)currentMinorVersionNumber); - else - { - bufSize = sizeof(currentVersion); - if(RegGetValueA(hKey, NULL, "CurrentVersion", RRF_RT_REG_SZ, NULL, currentVersion, &bufSize) != ERROR_SUCCESS) - strcpy(currentVersion, "0.0"); - } - } + FF_HKEY_AUTO_DESTROY hKey = NULL; + if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL)) + return; - char currentBuildNumber[32]; - bufSize = sizeof(currentBuildNumber); - if(RegGetValueA(hKey, NULL, "CurrentBuildNumber", RRF_RT_REG_SZ, NULL, currentBuildNumber, &bufSize) != ERROR_SUCCESS) - strcpy(currentBuildNumber, "0"); + uint32_t ubr = 0; + ffRegReadUint(hKey, L"UBR", &ubr, NULL); - DWORD ubr; - bufSize = sizeof(ubr); - if(RegGetValueW(hKey, NULL, L"UBR", RRF_RT_REG_DWORD, NULL, &ubr, &bufSize) != ERROR_SUCCESS || bufSize != sizeof(ubr)) - ubr = 0; + ffStrbufAppendF(&platform->systemRelease, + "%u.%u.%u.%u", + (unsigned) osVersion.dwMajorVersion, + (unsigned) osVersion.dwMinorVersion, + (unsigned) osVersion.dwBuildNumber, + (unsigned) ubr); - ffStrbufAppendF(&platform->systemRelease, "%s.%s.%u", currentVersion, currentBuildNumber, (unsigned)ubr); + ffStrbufInit(&platform->systemDisplayVersion); + if(!ffRegReadStrbuf(hKey, L"DisplayVersion", &platform->systemDisplayVersion, NULL) && osVersion.szCSDVersion[0]) + ffStrbufSetWS(&platform->systemDisplayVersion, osVersion.szCSDVersion); - ffStrbufEnsureFree(&platform->systemVersion, 256); - bufSize = (DWORD) ffStrbufGetFree(&platform->systemVersion); - if(RegGetValueA(hKey, NULL, "DisplayVersion", RRF_RT_REG_SZ, NULL, platform->systemVersion.chars, &bufSize) == ERROR_SUCCESS) - platform->systemVersion.length = (uint32_t) bufSize; + ffRegReadStrbuf(hKey, L"BuildLabEx", &platform->systemVersion, NULL); - RegCloseKey(hKey); + switch (osVersion.dwPlatformId) + { + case VER_PLATFORM_WIN32s: + ffStrbufAppendS(&platform->systemName, "WIN32s"); + break; + case VER_PLATFORM_WIN32_WINDOWS: + ffStrbufAppendS(&platform->systemName, "WIN32_WINDOWS"); + break; + case VER_PLATFORM_WIN32_NT: + ffStrbufAppendS(&platform->systemName, "WIN32_NT"); + break; + } } static void getSystemArchitecture(FFPlatform* platform) @@ -233,9 +227,8 @@ void ffPlatformInitImpl(FFPlatform* platform) getUserName(platform); getHostName(platform); - getDomainName(platform); + getUserShell(platform); - getSystemName(platform); getSystemReleaseAndVersion(platform); getSystemArchitecture(platform); } diff --git a/src/util/unused.h b/src/util/unused.h index fbc0e93e37..faef70d09e 100644 --- a/src/util/unused.h +++ b/src/util/unused.h @@ -5,6 +5,10 @@ static inline void ffUnused(int dummy, ...) { (void) dummy; } #define FF_UNUSED(...) ffUnused(0, __VA_ARGS__); -#define FF_MAYBE_UNUSED __attribute__ ((__unused__)) +#if defined(__GNUC__) || defined(__clang__) + #define FF_MAYBE_UNUSED __attribute__ ((__unused__)) +#else + #define FF_MAYBE_UNUSED +#endif #endif diff --git a/src/util/windows/unicode.h b/src/util/windows/unicode.h index 6892134dc2..a25d4a7b48 100644 --- a/src/util/windows/unicode.h +++ b/src/util/windows/unicode.h @@ -10,6 +10,7 @@ void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); static inline void ffStrbufSetWS(FFstrbuf* result, const wchar_t* source) { + if (!source) return ffStrbufClear(result); return ffStrbufSetNWS(result, (uint32_t)wcslen(source), source); } @@ -17,6 +18,7 @@ void ffStrbufInitNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); static inline void ffStrbufInitWS(FFstrbuf* result, const wchar_t* source) { + if (!source) return ffStrbufInit(result); return ffStrbufInitNWS(result, (uint32_t)wcslen(source), source); } @@ -29,6 +31,7 @@ static inline FFstrbuf ffStrbufCreateNWS(uint32_t length, const wchar_t* source) static inline FFstrbuf ffStrbufCreateWS(const wchar_t* source) { + if (!source) return ffStrbufCreate(); return ffStrbufCreateNWS((uint32_t)wcslen(source), source); } diff --git a/tests/strbuf.c b/tests/strbuf.c index d35c93e2f6..702f405392 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -56,6 +56,16 @@ int main(void) VERIFY(strbuf.allocated == 0); VERIFY(strbuf.length == 0); + //append(N)C + + ffStrbufAppendC(&strbuf, '1'); + VERIFY(ffStrbufEqualS(&strbuf, "1")); + VERIFY(strbuf.allocated >= 1); + ffStrbufAppendNC(&strbuf, 5, '2'); + VERIFY(ffStrbufEqualS(&strbuf, "122222")); + VERIFY(strbuf.allocated >= 6); + ffStrbufClear(&strbuf); + //appendS ffStrbufAppendS(&strbuf, "12345"); @@ -113,7 +123,7 @@ int main(void) //toNumber VERIFY(ffStrbufToDouble(&strbuf) == 123456789.0); - VERIFY(ffStrbufToUInt16(&strbuf, 999) == 999); //overflow + VERIFY(ffStrbufToUInt(&strbuf, 999) == 123456789); //countC