diff --git a/.codespellrc b/.codespellrc index 66e8f1098c..d07ab39fa9 100644 --- a/.codespellrc +++ b/.codespellrc @@ -2,4 +2,4 @@ check-filenames = builtin = clear,rare,usage,informal skip = */.git,*/cmake-build-*,*/.idea,*/completions,*/presets,*/screenshots,*/tests,*/3rdparty,*/logo/ascii,./src/detection/gpu/asahi_drm.h,./src/detection/cpu/cpu_x86.c,./src/detection/gpu/gpu_bsddrm.c,./src/detection/displayserver/linux/wayland/*-protocol.*,./build,./build-*,./build_*,./src/common/impl/genconfig.c -ignore-words-list = iterm,compiletime,unknwn,pengwin,siduction,master,slave,sur,doas,conexant,ags,bu,renderD,SME,Carmel,kinda +ignore-words-list = iterm,compiletime,unknwn,pengwin,siduction,master,slave,sur,doas,conexant,ags,bu,renderD,SME,Carmel,kinda,symbole,connexion,comando,processus,lokale,adres,titel,som,environnement,Sistem,Lokal,Lokal,Sistem,Paket,Hava diff --git a/.github/benchmark-index.html b/.github/benchmark-index.html new file mode 100644 index 0000000000..a2b9e309a5 --- /dev/null +++ b/.github/benchmark-index.html @@ -0,0 +1,405 @@ + + + + + + Fastfetch · Benchmark + + + + + +
+
+

Fastfetch Benchmark

+ +
+

Performance history grouped by CPU family. Each chart contains all modules and total process time for one operating system and architecture. Module curves are available from the legend and hidden by default.

+ +
+ +
+ + + diff --git a/.github/workflows/build-benchmark.yml b/.github/workflows/build-benchmark.yml new file mode 100644 index 0000000000..ebead8bf38 --- /dev/null +++ b/.github/workflows/build-benchmark.yml @@ -0,0 +1,55 @@ +name: Reusable Benchmark + +on: + workflow_call: + +permissions: + contents: write + +jobs: + benchmark: + runs-on: ubuntu-latest + concurrency: + group: benchmark + cancel-in-progress: false + steps: + - name: checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: download benchmark data + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: benchmark-* + path: benchmarks + + - name: merge benchmark data + run: jq -s 'add' benchmarks/*/benchmark.json > benchmark.json + + - name: save benchmark dashboard + run: cp .github/benchmark-index.html "$RUNNER_TEMP/benchmark-index.html" + + - name: store benchmark result + uses: benchmark-action/github-action-benchmark@52576c92bccf6ac60c8223ec7eb2565637cae9ba # v1.22.1 + with: + name: Fastfetch + tool: customSmallerIsBetter + output-file-path: benchmark.json + github-token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.sha }} + auto-push: true + summary-always: true + benchmark-data-dir-path: dev/bench + + - name: publish benchmark dashboard + run: | + git fetch origin gh-pages + git switch --force-create gh-pages FETCH_HEAD + mkdir -p dev/bench + cp "$RUNNER_TEMP/benchmark-index.html" dev/bench/index.html + git config user.name github-action-benchmark + git config user.email github@users.noreply.github.com + git add dev/bench/index.html + if ! git diff --cached --quiet; then + git commit -m "Update benchmark dashboard" + git push origin gh-pages + fi diff --git a/.github/workflows/build-dragonfly-amd64.yml b/.github/workflows/build-dragonfly-amd64.yml index c036d0d9d7..f7e406f54d 100644 --- a/.github/workflows/build-dragonfly-amd64.yml +++ b/.github/workflows/build-dragonfly-amd64.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Start VM - uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 + uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0 with: operating_system: dragonflybsd version: '6.4.2' diff --git a/.github/workflows/build-freebsd-amd64.yml b/.github/workflows/build-freebsd-amd64.yml index b75d67be89..1eb0c36c68 100644 --- a/.github/workflows/build-freebsd-amd64.yml +++ b/.github/workflows/build-freebsd-amd64.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Start VM - uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 + uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0 with: operating_system: freebsd version: '15.1' diff --git a/.github/workflows/build-haiku-amd64.yml b/.github/workflows/build-haiku-amd64.yml index b85fef26e5..c6642b9b16 100644 --- a/.github/workflows/build-haiku-amd64.yml +++ b/.github/workflows/build-haiku-amd64.yml @@ -18,10 +18,10 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Start VM - uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 + uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0 with: operating_system: haiku - version: 'r1beta5' + version: 'r1beta6' shell: bash cpu_count: 4 diff --git a/.github/workflows/build-linux-armv7l.yml b/.github/workflows/build-linux-armv7l.yml index 981ab48c47..16d239c5de 100644 --- a/.github/workflows/build-linux-armv7l.yml +++ b/.github/workflows/build-linux-armv7l.yml @@ -17,7 +17,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: run VM - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 + uses: uraimo/run-on-arch-action@460cb8e6d9f726a588fc9b5e681c8a6cab09ae41 # v3.2.0 id: runcmd with: arch: armv7 diff --git a/.github/workflows/build-linux-hosts.yml b/.github/workflows/build-linux-hosts.yml index bcd5069c42..51fe44c27a 100644 --- a/.github/workflows/build-linux-hosts.yml +++ b/.github/workflows/build-linux-hosts.yml @@ -43,7 +43,7 @@ jobs: run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - name: install required packages - run: sudo apt-get update && sudo apt-get install -y gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libefl-all-dev libunwind-dev liblua5.4-dev libvdpau-dev libva-dev rpm ninja-build + run: sudo apt-get update && sudo apt-get install -y gcc-13 jq libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libefl-all-dev libunwind-dev liblua5.4-dev libvdpau-dev libva-dev rpm ninja-build - name: install linuxbrew packages run: | @@ -52,7 +52,7 @@ jobs: - name: Initialize CodeQL if: inputs.arch == 'amd64' - uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 + uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 with: languages: c @@ -64,7 +64,7 @@ jobs: - name: perform CodeQL analysis if: inputs.arch == 'amd64' - uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 + uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8 - name: list features run: ./fastfetch --list-features @@ -73,7 +73,29 @@ jobs: run: time ./fastfetch -c presets/ci.jsonc --stat false - name: run fastfetch --format json - run: time ./fastfetch -c presets/ci.jsonc --format json + run: | + { time -p ./fastfetch -c presets/ci.jsonc --format json > fastfetch.raw.json 2> fastfetch.error; } 2> fastfetch.time + sed -n '/^[[:space:]]*\[/,$p' fastfetch.raw.json > fastfetch.json + cat fastfetch.json + cat fastfetch.error >&2 + cat fastfetch.time >&2 + + - name: prepare benchmark data + run: | + real_seconds=$(awk '$1 == "real" { print $2 }' fastfetch.time) + jq -e 'type == "array"' fastfetch.json > /dev/null + jq --arg platform "linux" --arg arch "${{ inputs.arch }}" --argjson real_seconds "$real_seconds" ' + map(select(.stat | type == "number") | { + name: ("fastfetch " + $platform + " " + $arch + " module " + .type), + unit: "ms", + value: .stat + }) + [{ + name: ("fastfetch " + $platform + " " + $arch + " total process time"), + unit: "ms", + value: ($real_seconds * 1000) + }] + ' fastfetch.json > benchmark.json + cat benchmark.json - name: run flashfetch run: time ./flashfetch @@ -105,3 +127,9 @@ jobs: with: name: fastfetch-linux-${{ inputs.arch }} path: ./fastfetch-*.* + + - name: upload benchmark data + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: benchmark-linux-${{ inputs.arch }} + path: benchmark.json diff --git a/.github/workflows/build-linux-vms.yml b/.github/workflows/build-linux-vms.yml index e61a2c52cb..73bfd9c667 100644 --- a/.github/workflows/build-linux-vms.yml +++ b/.github/workflows/build-linux-vms.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: run VM - uses: uraimo/run-on-arch-action@f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9 # v3.1.0 + uses: uraimo/run-on-arch-action@460cb8e6d9f726a588fc9b5e681c8a6cab09ae41 # v3.2.0 id: runcmd with: arch: ${{ inputs.arch }} diff --git a/.github/workflows/build-macos-hosts.yml b/.github/workflows/build-macos-hosts.yml index 66c1666b79..d805bf70dd 100644 --- a/.github/workflows/build-macos-hosts.yml +++ b/.github/workflows/build-macos-hosts.yml @@ -29,7 +29,7 @@ jobs: - name: install required packages run: | HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew update - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa lua quickjs-ng + HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa lua quickjs-ng jq - name: configure project run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On . @@ -41,10 +41,32 @@ jobs: run: ./fastfetch --list-features - name: run fastfetch - run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --stat false + run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan:bluetooth:bluetoothradio --stat false - name: run fastfetch --format json - run: time ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan --format json + run: | + { time -p ./fastfetch -c presets/ci.jsonc --structure-disabled vulkan:bluetooth:bluetoothradio --format json > fastfetch.raw.json 2> fastfetch.error; } 2> fastfetch.time + sed -n '/^[[:space:]]*\[/,$p' fastfetch.raw.json > fastfetch.json + cat fastfetch.json + cat fastfetch.error >&2 + cat fastfetch.time >&2 + + - name: prepare benchmark data + run: | + real_seconds=$(awk '$1 == "real" { print $2 }' fastfetch.time) + jq -e 'type == "array"' fastfetch.json > /dev/null + jq --arg platform "macos" --arg arch "${{ inputs.arch }}" --argjson real_seconds "$real_seconds" ' + map(select(.stat | type == "number") | { + name: ("fastfetch " + $platform + " " + $arch + " module " + .type), + unit: "ms", + value: .stat + }) + [{ + name: ("fastfetch " + $platform + " " + $arch + " total process time"), + unit: "ms", + value: ($real_seconds * 1000) + }] + ' fastfetch.json > benchmark.json + cat benchmark.json - name: run flashfetch run: time ./flashfetch @@ -60,3 +82,9 @@ jobs: with: name: fastfetch-macos-${{ inputs.arch }} path: ./fastfetch-*.* + + - name: upload benchmark data + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: benchmark-macos-${{ inputs.arch }} + path: benchmark.json diff --git a/.github/workflows/build-netbsd-amd64.yml b/.github/workflows/build-netbsd-amd64.yml index 9a6283a067..d55dbd0b50 100644 --- a/.github/workflows/build-netbsd-amd64.yml +++ b/.github/workflows/build-netbsd-amd64.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Start VM - uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 + uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0 with: operating_system: netbsd version: '10.1' diff --git a/.github/workflows/build-omnios-amd64.yml b/.github/workflows/build-omnios-amd64.yml index a473b0817a..f29c27dc46 100644 --- a/.github/workflows/build-omnios-amd64.yml +++ b/.github/workflows/build-omnios-amd64.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Start VM - uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 + uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0 with: operating_system: omnios version: 'r151058' diff --git a/.github/workflows/build-openbsd-amd64.yml b/.github/workflows/build-openbsd-amd64.yml index a22919f69d..7f7bd2102f 100644 --- a/.github/workflows/build-openbsd-amd64.yml +++ b/.github/workflows/build-openbsd-amd64.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Start VM - uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 + uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0 with: operating_system: openbsd version: '7.9' diff --git a/.github/workflows/build-windows-hosts.yml b/.github/workflows/build-windows-hosts.yml index fa1b3b3748..2518c25d6f 100644 --- a/.github/workflows/build-windows-hosts.yml +++ b/.github/workflows/build-windows-hosts.yml @@ -49,7 +49,7 @@ jobs: with: msystem: ${{ inputs.msystem }} update: true - install: git mingw-w64-clang-${{ inputs.msys-arch }}-7zip mingw-w64-clang-${{ inputs.msys-arch }}-cmake mingw-w64-clang-${{ inputs.msys-arch }}-clang mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-loader mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-headers mingw-w64-clang-${{ inputs.msys-arch }}-opencl-icd mingw-w64-clang-${{ inputs.msys-arch }}-opencl-headers mingw-w64-clang-${{ inputs.msys-arch }}-cppwinrt mingw-w64-clang-${{ inputs.msys-arch }}-imagemagick mingw-w64-clang-${{ inputs.msys-arch }}-chafa mingw-w64-clang-${{ inputs.msys-arch }}-lua mingw-w64-clang-${{ inputs.msys-arch }}-quickjs-ng mingw-w64-clang-${{ inputs.msys-arch }}-directx-headers + install: git mingw-w64-clang-${{ inputs.msys-arch }}-7zip mingw-w64-clang-${{ inputs.msys-arch }}-cmake mingw-w64-clang-${{ inputs.msys-arch }}-clang mingw-w64-clang-${{ inputs.msys-arch }}-jq mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-loader mingw-w64-clang-${{ inputs.msys-arch }}-vulkan-headers mingw-w64-clang-${{ inputs.msys-arch }}-opencl-icd mingw-w64-clang-${{ inputs.msys-arch }}-opencl-headers mingw-w64-clang-${{ inputs.msys-arch }}-cppwinrt mingw-w64-clang-${{ inputs.msys-arch }}-imagemagick mingw-w64-clang-${{ inputs.msys-arch }}-chafa mingw-w64-clang-${{ inputs.msys-arch }}-lua mingw-w64-clang-${{ inputs.msys-arch }}-quickjs-ng mingw-w64-clang-${{ inputs.msys-arch }}-directx-headers - name: print msys version run: uname -a @@ -70,7 +70,29 @@ jobs: run: time ./fastfetch -c presets/ci.jsonc --stat false - name: run fastfetch --format json - run: time ./fastfetch -c presets/ci.jsonc --format json + run: | + { time -p ./fastfetch -c presets/ci.jsonc --format json > fastfetch.raw.json 2> fastfetch.error; } 2> fastfetch.time + sed -n '/^[[:space:]]*\[/,$p' fastfetch.raw.json > fastfetch.json + cat fastfetch.json + cat fastfetch.error >&2 + cat fastfetch.time >&2 + + - name: prepare benchmark data + run: | + real_seconds=$(awk '$1 == "real" { print $2 }' fastfetch.time) + jq -e 'type == "array"' fastfetch.json > /dev/null + jq --arg platform "windows" --arg arch "${{ inputs.arch }}" --argjson real_seconds "$real_seconds" ' + map(select(.stat | type == "number") | { + name: ("fastfetch " + $platform + " " + $arch + " module " + .type), + unit: "ms", + value: .stat + }) + [{ + name: ("fastfetch " + $platform + " " + $arch + " total process time"), + unit: "ms", + value: ($real_seconds * 1000) + }] + ' fastfetch.json > benchmark.json + cat benchmark.json - name: run flashfetch run: time ./flashfetch @@ -94,7 +116,7 @@ jobs: - if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch' name: submit signing request - uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2.2 + uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627 # v2.3 with: api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' organization-id: '${{ vars.SIGNPATH_ORG_ID }}' @@ -116,3 +138,9 @@ jobs: name: fastfetch-windows-${{ inputs.arch }} path: ./fastfetch-windows-${{ inputs.arch }}.* overwrite: true + + - name: upload benchmark data + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: benchmark-windows-${{ inputs.arch }} + path: benchmark.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0081e62d88..16f6ce34c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -138,7 +138,6 @@ jobs: uses: ./.github/workflows/build-dragonfly-amd64.yml haiku-amd64: - if: false # Disabled because the Haiku build is currently broken needs: no-features-test name: Haiku-amd64 permissions: @@ -176,6 +175,17 @@ jobs: secrets: SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }} + benchmark: + if: github.event_name == 'push' && github.ref == 'refs/heads/dev' && github.repository == 'fastfetch-cli/fastfetch' + name: Benchmark + needs: + - linux-hosts + - macos-hosts + - windows-hosts + permissions: + contents: write + uses: ./.github/workflows/build-benchmark.yml + release: if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'fastfetch-cli/fastfetch' name: Release @@ -193,7 +203,7 @@ jobs: - dragonfly-amd64 - solaris-amd64 - omnios-amd64 - # - haiku-amd64 + - haiku-amd64 - windows-hosts permissions: contents: write diff --git a/.gitignore b/.gitignore index 1394fe9317..9fe9c7bfb4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ fastfetch.kdev4 *.user.* *.swp *.log +/*.zh.md +/.*-ai/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 598addb3a2..6e90211313 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,44 @@ +# 2.68.0 + +Changes: +* The DE / WM / LM modules now reports the full name "Desktop Environment" / "Window Manager" / "Login Manager" instead of the abbreviations. + +Features: +* Improved Login Manager (LM) module + * Added support on macOS, Windows and POSIX systems without systemd (by process list enumeration). + * Reports pretty name on Linux. +* Added Top module to print processes with the highest CPU, memory or disk I/O usage. (Top) +* Added module key localization support. (General) + * Added a new option `--key-language ` (`display.key.language: ""` in JSON config). When left empty, it defaults to the system locale. + * See `fastfetch -h key-language` for all supported languages. +* Improved Wi-Fi module + * Added Wifi channel width detection, exposed via `{channel-width}` in custom format (reports 0 when not supported). + * Improved Wifi channel frequency accuracy and fortified detection on Windows, macOS. + * Added Wifi detection support on SunOS, Haiku. +* Added Battery detection support on SunOS. (Battery, SunOS) +* Added terminal font size detection support on Termux. (TerminalFont, Android) +* Improved GTK theme/icons/font detection on standalone WMs such as Hyprland, sway, niri and i3. (Theme, Linux) +* Improved fish completion scripts to print enum descriptions. (Completion) +* Enabled Wayland support on Android. (DisplayServer, Android) +* Added DE detection support for ASUS Zenfone. (DE, Android) +* Added new ARM SoCs (CPU, Linux / Android) + +Bugfixes: +* Fixed I/O rate calculation precision in DiskIO and NetIO, and prevented division by zero. (DiskIO / NetIO) +* Fixed the fast path of ash version detection. (Shell) +* Fixed memory usage detection support on x86-32 FreeBSD (Memory, FreeBSD) + * Note: Although 32-bit systems are still supported, they are deprecated and barely tested. Users are encouraged to upgrade to 64-bit systems. +* Some internal cleanups and optimizations. + +Logos: +* Added macOS_old +* Added Omarchy, Omarchy2 + # 2.67.1 Bugfixes: -* Fixed a `Symbol not found` error when running on macOS 10.15. (Codec, macOS) -* Fixed Alacritty font size never being parsed from the TOML config. (TerminalFont) +* Fixed a `Symbol not found` error when running on macOS 10.15. (#2499, Codec, macOS) +* Fixed Alacritty font size never being parsed from the TOML config. (#2501, TerminalFont) * Fixed bright colors incorrectly being active when the terminal uses a light theme. (#2509, Display) * Fixed Ghostty terminal font detection (TerminalFont) * Now tries to load the config with the `.ghostty` extension. (#2500) @@ -3279,7 +3315,7 @@ Features: * Enhance `--percent-type` to allow hiding other texts (#387) * Add Wifi module support for Linux * Detect scaled resolutions (Windows, macOS) -* Optimise font module printing (Windows) +* Optimize font module printing (Windows) * Detect pacman package count inside MSYS2 environment (Windows) * Add Wifi / Battery module support for Android * Disk name support for Linux diff --git a/CMakeLists.txt b/CMakeLists.txt index 687e89b5e6..995c2049d1 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 +cmake_minimum_required(VERSION 3.21.0) # C_STANDARD project(fastfetch - VERSION 2.67.1 + VERSION 2.68.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -78,7 +78,7 @@ endif() include(CMakeDependentOption) cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS OR Haiku OR GNU" OFF) -cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD" OFF) +cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "ANDROID OR LINUX OR FreeBSD OR OpenBSD OR NetBSD" OFF) cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF) @@ -128,7 +128,7 @@ if(APPLE) option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF) endif() -file(GLOB FF_MODULE_DIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/*/") +file(GLOB FF_MODULE_DIRS CONFIGURE_DEPENDS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/*/") foreach(FF_MODULE_DIR ${FF_MODULE_DIRS}) if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/${FF_MODULE_DIR}") string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER) @@ -484,6 +484,7 @@ set(LIBFASTFETCH_SRC src/detection/physicalmemory/physicalmemory.c src/detection/publicip/publicip.c src/detection/terminaltheme/terminaltheme.c + src/detection/top/top.c src/detection/terminalfont/terminalfont.c src/detection/terminalshell/terminalshell.c src/detection/version/version.c @@ -559,6 +560,7 @@ set(LIBFASTFETCH_SRC src/modules/terminalsize/terminalsize.c src/modules/theme/theme.c src/modules/title/title.c + src/modules/top/top.c src/modules/tpm/tpm.c src/modules/uptime/uptime.c src/modules/users/users.c @@ -651,6 +653,7 @@ if(LINUX) src/detection/terminalshell/terminalshell_linux.c src/detection/terminalsize/terminalsize_linux.c src/detection/theme/theme_linux.c + src/detection/top/top_linux.c src/detection/tpm/tpm_linux.c src/detection/uptime/uptime_linux.c src/detection/users/users_linux.c @@ -691,6 +694,13 @@ elseif(ANDROID) src/detection/physicalmemory/physicalmemory_nosupport.c src/detection/diskio/diskio_linux.c src/detection/displayserver/displayserver_android.c + src/detection/displayserver/linux/wayland/wayland.c + src/detection/displayserver/linux/wayland/global-output.c + src/detection/displayserver/linux/wayland/kde-output.c + src/detection/displayserver/linux/wayland/wl-output-protocol.c + src/detection/displayserver/linux/wayland/wp-color-management-v1-protocol.c + src/detection/displayserver/linux/wayland/kde-output-device-v2-protocol.c + src/detection/displayserver/linux/wayland/xdg-output-unstable-v1-protocol.c src/detection/displayserver/linux/common.c src/detection/displayserver/linux/wmde.c src/detection/displayserver/linux/xcb.c @@ -726,6 +736,7 @@ elseif(ANDROID) src/detection/terminalshell/terminalshell_linux.c src/detection/terminalsize/terminalsize_linux.c src/detection/theme/theme_linux.c + src/detection/top/top_linux.c src/detection/tpm/tpm_nosupport.c src/detection/uptime/uptime_linux.c src/detection/users/users_linux.c @@ -802,7 +813,6 @@ elseif(FreeBSD) src/detection/os/os_linux.c src/detection/packages/packages_bsd.c src/detection/poweradapter/poweradapter_nosupport.c - src/detection/processes/processes_bsd.c src/detection/gtk_qt/qt.c src/detection/sound/sound_bsd.c src/detection/swap/swap_bsd.c @@ -822,11 +832,15 @@ elseif(FreeBSD) if(DragonFly) list(APPEND LIBFASTFETCH_SRC src/detection/bluetooth/bluetooth_nosupport.c + src/detection/processes/processes_dbsd.c + src/detection/top/top_dbsd.c src/detection/wifi/wifi_nosupport.c ) else() list(APPEND LIBFASTFETCH_SRC src/detection/bluetooth/bluetooth_bsd.c + src/detection/processes/processes_bsd.c + src/detection/top/top_bsd.c src/detection/wifi/wifi_bsd.c ) endif() @@ -904,6 +918,7 @@ elseif(NetBSD) src/detection/terminalsize/terminalsize_linux.c src/detection/theme/theme_linux.c src/detection/tpm/tpm_nosupport.c + src/detection/top/top_nbsd.c src/detection/uptime/uptime_bsd.c src/detection/users/users_linux.c src/detection/wallpaper/wallpaper_linux.c @@ -964,7 +979,7 @@ elseif(OpenBSD) src/detection/gpu/gpu_bsddrm.c src/detection/gtk_qt/gtk.c src/detection/host/host_obsd.c - src/detection/lm/lm_nosupport.c + src/detection/lm/lm_linux.c src/detection/icons/icons_linux.c src/detection/initsystem/initsystem_linux.c src/detection/keyboard/keyboard_nosupport.c @@ -990,6 +1005,7 @@ elseif(OpenBSD) src/detection/terminalsize/terminalsize_linux.c src/detection/theme/theme_linux.c src/detection/tpm/tpm_nosupport.c + src/detection/top/top_obsd.c src/detection/uptime/uptime_bsd.c src/detection/users/users_obsd.c src/detection/wallpaper/wallpaper_linux.c @@ -1035,7 +1051,7 @@ elseif(APPLE) src/detection/icons/icons_nosupport.c src/detection/initsystem/initsystem_linux.c src/detection/keyboard/keyboard_apple.c - src/detection/lm/lm_nosupport.c + src/detection/lm/lm_apple.m src/detection/loadavg/loadavg_bsd.c src/detection/libc/libc_apple.c src/detection/locale/locale_linux.c @@ -1050,13 +1066,14 @@ elseif(APPLE) src/detection/packages/packages_apple.c src/detection/packages/packages_nix.c src/detection/poweradapter/poweradapter_apple.c - src/detection/processes/processes_bsd.c + src/detection/processes/processes_apple.c src/detection/sound/sound_apple.c src/detection/swap/swap_apple.c src/detection/terminalfont/terminalfont_apple.m src/detection/terminalshell/terminalshell_linux.c src/detection/terminalsize/terminalsize_linux.c src/detection/theme/theme_apple.c + src/detection/top/top_apple.c src/detection/tpm/tpm_apple.c src/detection/uptime/uptime_bsd.c src/detection/users/users_linux.c @@ -1128,7 +1145,7 @@ elseif(WIN32) src/detection/initsystem/initsystem_windows.c src/detection/keyboard/keyboard_windows.c src/detection/libc/libc_windows.cpp - src/detection/lm/lm_nosupport.c + src/detection/lm/lm_windows.c src/detection/loadavg/loadavg_nosupport.c src/detection/locale/locale_windows.c src/detection/localip/localip_windows.c @@ -1149,6 +1166,7 @@ elseif(WIN32) src/detection/terminalshell/terminalshell_windows.c src/detection/terminalsize/terminalsize_windows.c src/detection/theme/theme_windows.c + src/detection/top/top_windows.c src/detection/tpm/tpm_windows.c src/detection/uptime/uptime_windows.c src/detection/users/users_windows.c @@ -1169,7 +1187,7 @@ elseif(SunOS) src/common/impl/FFPlatform_unix.c src/common/impl/binary_linux.c src/common/impl/kmod_sunos.c - src/detection/battery/battery_nosupport.c + src/detection/battery/battery_sunos.c src/detection/bios/bios_windows.c src/detection/board/board_windows.c src/detection/bootmgr/bootmgr_nosupport.c @@ -1210,7 +1228,7 @@ elseif(SunOS) src/detection/initsystem/initsystem_linux.c src/detection/keyboard/keyboard_nosupport.c src/detection/libc/libc_nosupport.c - src/detection/lm/lm_nosupport.c + src/detection/lm/lm_linux.c src/detection/loadavg/loadavg_sunos.c src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c @@ -1223,7 +1241,7 @@ elseif(SunOS) src/detection/os/os_sunos.c src/detection/packages/packages_sunos.c src/detection/poweradapter/poweradapter_nosupport.c - src/detection/processes/processes_linux.c + src/detection/processes/processes_sunos.c src/detection/gtk_qt/qt.c src/detection/sound/sound_sunos.c src/detection/swap/swap_sunos.c @@ -1231,11 +1249,12 @@ elseif(SunOS) src/detection/terminalshell/terminalshell_linux.c src/detection/terminalsize/terminalsize_linux.c src/detection/theme/theme_linux.c + src/detection/top/top_sunos.c src/detection/tpm/tpm_nosupport.c src/detection/uptime/uptime_sunos.c src/detection/users/users_linux.c src/detection/wallpaper/wallpaper_linux.c - src/detection/wifi/wifi_nosupport.c + src/detection/wifi/wifi_sunos.c src/detection/wm/wm_nosupport.c src/detection/de/de_linux.c src/detection/wmtheme/wmtheme_linux.c @@ -1301,11 +1320,12 @@ elseif(Haiku) src/detection/terminalshell/terminalshell_linux.c src/detection/terminalsize/terminalsize_linux.c src/detection/theme/theme_nosupport.c + src/detection/top/top_haiku.c src/detection/tpm/tpm_nosupport.c src/detection/uptime/uptime_haiku.c src/detection/users/users_linux.c src/detection/wallpaper/wallpaper_haiku.cpp - src/detection/wifi/wifi_nosupport.c + src/detection/wifi/wifi_haiku.cpp src/detection/wm/wm_nosupport.c src/detection/de/de_nosupport.c src/detection/wmtheme/wmtheme_haiku.cpp @@ -1377,7 +1397,7 @@ elseif(GNU) src/detection/packages/packages_linux.c src/detection/packages/packages_nix.c src/detection/poweradapter/poweradapter_nosupport.c - src/detection/processes/processes_linux.c + src/detection/processes/processes_gnu.c src/detection/gtk_qt/qt.c src/detection/sound/sound_linux.c src/detection/swap/swap_linux.c @@ -1385,6 +1405,7 @@ elseif(GNU) src/detection/terminalshell/terminalshell_linux.c src/detection/terminalsize/terminalsize_linux.c src/detection/theme/theme_linux.c + src/detection/top/top_gnu.c src/detection/tpm/tpm_nosupport.c src/detection/uptime/uptime_linux.c src/detection/users/users_linux.c @@ -1995,6 +2016,7 @@ elseif(SunOS) elseif(GNU) target_link_libraries(libfastfetch PRIVATE "m" + PRIVATE "ps" ) elseif(ANDROID) target_link_libraries(libfastfetch diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..0da08c892c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,724 @@ +# Contributing to fastfetch + +
+ + Note + + + This document is generated by AI, and mainly for AI use. It is not a substitute for human review, and may contain errors or omissions. Please verify any information before relying on it. +
+ +Thanks for your interest in fastfetch. This document covers building, architecture, how to add a module or a logo, code style, commit conventions, and the pull request workflow. + +fastfetch is a system information tool written in C23, supporting Linux, macOS, Windows, the BSDs, Solaris, Haiku and Android. The project is borderline obsessive about **startup time** and **optional dependencies** — many design decisions only make sense under that constraint, and this document keeps coming back to it. + +--- + +## Table of contents + +- [Contributor quick reference](#contributor-quick-reference) +- [Getting started](#getting-started) +- [Code map](#code-map) +- [Core architecture: modules vs. detection](#core-architecture-modules-vs-detection) +- [Module registration](#module-registration) +- [Walkthrough: adding a module](#walkthrough-adding-a-module) +- [Walkthrough: adding a logo](#walkthrough-adding-a-logo) +- [Platform conventions](#platform-conventions) +- [Code style](#code-style) +- [Commit messages](#commit-messages) +- [Changelog](#changelog) +- [Tests](#tests) +- [Pull requests](#pull-requests) +- [Gotchas](#gotchas) + +--- + +## Contributor quick reference + +| Task | Start here | Verify with | +|---|---|---| +| Build and run fastfetch | `run.sh`, `CMakeLists.txt` | `./run.sh` | +| Add a module | `src/modules//`, `src/detection//`, `src/modules/modules.c` | `cmake -B build && cmake --build build -j` | +| Add a platform implementation | `src/detection//_.c` | Build on the target platform or CI | +| Add an ASCII logo | `src/logo/ascii//.txt`, matching `.inc` | `./build/fastfetch --logo ` | +| Change formatting or JSON output | `src/modules//.c` | `./build/fastfetch -s --format json` | +| Change shared formatting or containers | `src/common/format.h`, `src/common/color.h`, `src/common/FFstrbuf.h` | Build and run the matching test | +| Run the test suite | `tests/`, `build/` | `cd build && ctest --output-on-failure` | + +For a normal code change, the shortest useful loop is: configure, build the `fastfetch` target, run the affected module, then run the tests. If you add a directory or a generated input, re-run `cmake -B build` so CMake refreshes its file globs. + +--- + +## Getting started + +### Building + +The quickest way is `run.sh` in the repository root — it creates `build/`, configures, compiles and runs the binary: + +```sh +./run.sh # build and run +./run.sh --format json # arguments are forwarded to fastfetch +``` + +Manual build: + +```sh +cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo +cmake --build build --target fastfetch -j$(nproc) +./build/fastfetch +``` + +The default build type is `RelWithDebInfo`. LTO is enabled whenever `ENABLE_LTO=ON` **and** `CMAKE_BUILD_TYPE != Debug` — so the default already has it, and only `Debug` builds lack it. This matters because LTO here is not merely an optimization: it is what strips the code of disabled modules. See [Gotchas](#gotchas). + +### Common build options + +| Option | Default | Notes | +|---|---|---| +| `CMAKE_BUILD_TYPE` | `RelWithDebInfo` | LTO is on for anything but `Debug` | +| `BUILD_TESTS` | `OFF` | Builds the unit tests in `tests/` | +| `BUILD_FLASHFETCH` | `ON` | Also builds the stripped-down `flashfetch` | +| `BINARY_LINK_TYPE` | `dlopen` | `dlopen` / `dynamic` / `static` | +| `ENABLE_ASAN` | `OFF` | Address Sanitizer | +| `ENABLE_LTO` | `ON` | Link-time optimization | +| `MODULE_DISABLE_` | `OFF` | Disable a module, e.g. `MODULE_DISABLE_GPU=ON` | +| `SET_TWEAK` | `ON` | Appends a tweak to the dev version; turned off for releases | + +`BINARY_LINK_TYPE=dlopen` (the default) is a deliberate design choice: optional dependencies (Vulkan, Wayland, DBus, ImageMagick, chafa, …) are loaded at runtime, so a missing library never prevents startup. When adding a third-party dependency, use the dlopen helpers in `common/library.h` — do not link it directly. + +For a minimal-dependency build, see `.github/workflows/build-no-features-test.yml`: + +```sh +cmake -DBUILD_TESTS=On -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_X11=OFF \ + -DENABLE_DBUS=OFF -DENABLE_ZLIB=OFF ... . +``` + +### Dependencies + +Required: CMake ≥ 3.21 and a C23 compiler (GCC, Clang or MSVC). Everything else is optional. + +Optional dependencies are auto-detected: libpci, libdrm, vulkan, wayland, xcb, xrandr, dbus, sqlite3, rpm, imagemagick{6,7}, chafa, zlib, egl, glx, opencl, freetype, pulse, ddcutil, elf, libzfs, and more. + +--- + +## Code map + +``` +src/ +├── fastfetch.c entry point: CLI parsing, module dispatch, main loop (36 KB) +├── flashfetch.c entry point for the stripped-down build +├── options/ global (non-module) config: general / display / logo +├── modules/ module layer — module formatting and output +├── detection/ detection layer — platform-specific data retrieval +├── common/ infrastructure: FFstrbuf, FFlist, formatting, printing, networking +├── logo/ 530 ASCII logos plus the image logo backends +└── 3rdparty/ yyjson, widecharwidth, display-library +``` + +Flow of control: + +``` +fastfetch.c → options/ (global configuration) + → modules/ (76 modules, formatting and output) + → detection/ (one platform implementation each, raw data) + → common/ (infrastructure) +``` + +The number of module directories and detection directories does not match, and that is expected. The following relationships are structural rather than a fixed inventory: + +- **13 modules have no detection directory** — they are either pure layout (`break`, `separator`, `colors`, `title`, `logo`) or reuse someone else's detection result (`display`, `monitor`, `kernel`, `shell`, `terminal`, `player`, `custom`, `datetime`). +- **4 detection directories serve modules with different names** — `displayserver` (→ `display`, `monitor`), `gtk_qt` (→ `theme`, `icons`, `font`, `cursor`), `terminalshell` (→ `terminal`, `shell`) and `libc`. + +So do not assume `modules//` always has a matching `detection//`. + +### Layer boundaries + +| Layer | Owns | Must not | +|---|---|---| +| `options/` | **Global** config (colors, logo, threading, timeouts) | hold per-module options | +| `modules/` | formatting, printing, JSON serialization | read `/proc`, call Win32 APIs, parse sysfs | +| `detection/` | cross-platform data retrieval, clean result structs | print anything, read display config | +| `common/` | general-purpose utilities, no business logic | depend on a specific module | + +The test is simple: **no file under `detection/` should contain `printf` or read `instance.config`.** Symmetrically, no file under `modules/` should contain `#ifdef __linux__`. + +--- + +## Core architecture: modules vs. detection + +This is the single most important convention in the codebase. Every feature is a fixed set of files — using CPU as the example: + +``` +modules/cpu/option.h module option struct +modules/cpu/cpu.c formatting and output +detection/cpu/cpu.h platform-independent interface +detection/cpu/cpu_linux.c ┐ +detection/cpu/cpu_apple.c │ +detection/cpu/cpu_windows.c ├─ platform implementations, one linked per build +detection/cpu/cpu_bsd.c │ +detection/cpu/cpu_nosupport.c ┘ +``` + +The interface is deliberately minimal — usually just two things: + +```c +typedef struct FFCPUResult { ... } FFCPUResult; // result struct +const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu); +``` + +The `const char*` return value is an **error string**; `nullptr` means success. This pattern is used throughout `detection/` — please keep it consistent. + +Helper functions are only exposed when they are genuinely shared between platform implementations (as in `cpu.h`: `ffCPUAppleCodeToName`, `ffCPUDetectByCpuid`). + +**The payoff:** adding a platform never touches `modules/`; fixing output formatting never touches `detection/`. + +--- + +## Module registration + +### The descriptor + +Every module exports one `FFModuleBaseInfo` (defined in `common/option.h:46`) — essentially a hand-written vtable: + +```c +typedef struct FFModuleBaseInfo { + const char* name; // lookup key, e.g. "cpu" + const char* description; // help text + FFModuleDisplayName displayName; // module name in 20 languages + + void (*initOptions)(void* options); + void (*destroyOptions)(void* options); + void (*parseJsonObject)(void* options, struct yyjson_val* module); + bool (*printModule)(void* options); + bool (*generateJsonResult)(void* options, yyjson_mut_doc*, yyjson_mut_val*); + void (*generateJsonConfig)(void* options, yyjson_mut_doc*, yyjson_mut_val*); + + FFModuleFormatArgList formatArgs; // format placeholder table + const uint8_t defaultOrder; // sort weight +} FFModuleBaseInfo; +``` + +The source comment openly admits this is UB — `void*` is not compatible with `FF*Options*`. It is a pragmatic compromise to get polymorphism in C. Don't try to "fix" it. + +### The registry: a first-letter hash bucket + +`src/modules/modules.c` defines 26 `static FFModuleBaseInfo*` arrays (`A[]` through `Z[]`), each terminated by `nullptr`, collected into `ffModuleInfos[26]`: + +```c +FFModuleBaseInfo** modules = ffModuleInfos[toupper(name[0]) - 'A']; // O(1) bucket lookup +for (; *modules; ++modules) { // linear scan in the bucket + if (ffStrEqualsIgnCase(name, (*modules)->name)) { ... } +} +``` + +The registry is small enough that a subtraction plus a few string comparisons is preferable to a general-purpose hash table. When adding a module, place its descriptor in the bucket matching the first letter of `.name`; keep the existing `nullptr` terminator at the end. + +### The calling convention: zero heap allocation + +Every dispatch site (`jsonconfig.c:98`, `commandoption.c:189`) follows the same pattern: + +```c +alignas(uint64_t) uint8_t optionBuf[FF_OPTION_MAX_SIZE]; // 256 bytes, on the stack +baseInfo->initOptions(optionBuf); +baseInfo->parseJsonObject(optionBuf, jsonVal); // JSONC path only +baseInfo->printModule(optionBuf); // or generateJsonResult +baseInfo->destroyOptions(optionBuf); +``` + +`FF_OPTION_MAX_SIZE = 1 << 8` (256 bytes). Every module's `option.h` must end with: + +```c +static_assert(sizeof(FFCPUOptions) <= FF_OPTION_MAX_SIZE, "FFCPUOptions size exceeds maximum allowed size"); +``` + +**Meaning: no module option struct may exceed 256 bytes.** Hard constraint, enforced at compile time. + +The three dispatch entry points: + +| Entry point | Location | Used for | +|---|---|---| +| `parseModuleJsonObject` | `common/impl/jsonconfig.c:90` | the `modules[]` array in a JSONC config | +| `parseStructureCommand` | `common/impl/commandoption.c:181` | the colon-separated structure string | +| `ffParseModuleOptions` | `common/impl/commandoption.c:13` | **deprecated** — see [Gotchas](#4-cli-module-options-are-removed) | + +### Build-time module discovery + +`CMakeLists.txt:131` globs `src/modules/*/` and generates a `MODULE_DISABLE_` option per directory: + +```cmake +file(GLOB FF_MODULE_DIRS RELATIVE "..." ".../src/modules/*/") +foreach(FF_MODULE_DIR ${FF_MODULE_DIRS}) + option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF) +endforeach() +``` + +**Adding a module requires no CMake change.** The same trick generates the package manager switches (`:146` regex-scans `src/modules/packages/option.h` for `FF_PACKAGES_FLAG_*_BIT`). + +--- + +## Walkthrough: adding a module + +Adding a `Foo` module, step by step. + +### 1. Define the option struct + +`src/modules/foo/option.h`: + +```c +#pragma once + +#include "common/option.h" + +typedef struct FFFooOptions { + FFModuleArgs moduleArgs; // must be the first field + bool showBar; +} FFFooOptions; + +static_assert(sizeof(FFFooOptions) <= FF_OPTION_MAX_SIZE, "FFFooOptions size exceeds maximum allowed size"); +``` + +`FFModuleArgs` must come first. It provides `key`, `format`, `outputColor`, `keyColor`, `keyIcon` and `keyWidth`; because it is the first field, `ffJsonConfigParseModuleArgs()` can handle these generically and you get them **for free — no parsing code to write**. + +Note these fields can only be set through the JSON config; CLI module options have been removed (see [Gotchas](#4-cli-module-options-are-removed)). + +### 2. Implement detection + +`src/detection/foo/foo.h`: + +```c +#pragma once +#include "fastfetch.h" + +typedef struct FFFooResult { + FFstrbuf name; + uint32_t count; +} FFFooResult; + +const char* ffDetectFoo(const FFFooOptions* options, FFFooResult* result); +``` + +`src/detection/foo/foo_linux.c`: + +```c +#include "foo.h" + +const char* ffDetectFoo(const FFFooOptions* options, FFFooResult* result) { + // read /sys, /proc, sysfs, dbus, ... + return nullptr; // success; return an error string on failure +} +``` + +**Write one file per target platform, and a `foo_nosupport.c` fallback for the rest.** There are already 45 `*_nosupport.c` files doing exactly this. + +### 3. Implement the module + +`src/modules/foo/foo.c` — implement the six functions, then define the descriptor: + +```c +FFModuleBaseInfo ffFooModuleInfo = { + .name = "Foo", + .description = "Print foo information", + .displayName = { + .en = "Foo", .ar = "Foo", .cs = "Foo", .de = "Foo", .es = "Foo", + .fr = "Foo", .gl = "Foo", .he = "Foo", .id = "Foo", .it = "Foo", + .ja = "Foo", .ko = "Foo", .pl = "Foo", .pt = "Foo", .ru = "Foo", + .tr = "Foo", .uk = "Foo", .vi = "Foo", .zh_CN = "Foo", .zh_TW = "Foo", + }, + .initOptions = (void*) ffInitFooOptions, + .destroyOptions = (void*) ffDestroyFooOptions, + .parseJsonObject = (void*) ffParseFooJsonObject, + .printModule = (void*) ffPrintFoo, + .generateJsonResult = (void*) ffGenerateFooJsonResult, + .generateJsonConfig = (void*) ffGenerateFooJsonConfig, + .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) { + { "Name", "name" }, + { "Count", "count" }, + })), + .defaultOrder = 73, +}; +``` + +Points to note: + +- **`displayName`** is a literal block of all 20 language fields — there is no shortcut macro, so copy the layout from a neighboring module: **`en`, `ar`, `cs`, `de`, `es`, `fr`, `gl`, `he`, `id`, `it`, `ja`, `ko`, `pl`, `pt`, `ru`, `tr`, `uk`, `vi`, `zh_CN`, `zh_TW`**. + **Fill in all 20.** The struct is read by byte offset (see [Gotchas](#6-localization-uses-byte-offsets-not-enums)) and there is **no fallback** — a missing field means the key prints empty in that language. +- **`formatArgs`** must be exhaustive. It drives the placeholder list printed by `fastfetch -h foo-format`; **anything you omit is invisible and unusable to the user.** +- The `moduleFormat` section in `doc/json_schema.json` is generated by `fastfetch -h format-json`. Its metadata comes from `FFModuleBaseInfo::formatArgs`. To keep the schema and module descriptors consistent, do not edit the `moduleFormat` section in `doc/json_schema.json` by hand; update the module metadata and regenerate it instead. +- **`defaultOrder`**: use the current maximum plus 1. Search the existing descriptors for `.defaultOrder =` before choosing a value; do not copy a hard-coded value from this document. Leaving it out, or setting it to `0`, makes the module **disappear** from the interactive `--gen-config` picker. Only `logo`, `command` and `custom` do this deliberately, because they need user arguments or are invoked directly by the display layer. + +### 4. Register it + +- Add `#include "modules/foo/foo.h"` to `src/modules/modules.h` +- Insert into the `F[]` array in `src/modules/modules.c`: + +```c +#if !FF_MODULE_DISABLE_FOO + &ffFooModuleInfo, +#endif +``` + +`FF_MODULE_DISABLE_FOO` is generated by CMake — you do not define it yourself. + +### 5. Modules that need warm-up + +If your module needs a sampling interval (CPU usage) or a network round-trip (public IP, weather), also implement `ffPrepareFoo()` and register it in the switch inside `ffPrepareCommandOption()` in `common/impl/commandoption.c`, under the right first-letter `case`. Six modules do this today: CPUUsage, DiskIO, NetIO, PublicIP, Top and Weather. + +### 6. Verify + +```sh +cmake -B build && cmake --build build -j +./build/fastfetch -s foo --format json # JSON output +./build/fastfetch -h foo-format # list formatArgs placeholders +./build/fastfetch --gen-config # confirm it appears in the picker +``` + +Note the `-format` suffix on the help flag: `fastfetch -h foo` does not work, only `fastfetch -h foo-format`. + +--- + +## Walkthrough: adding a logo + +A new logo **must** have a corresponding "Logo Request" issue, linked from the PR with `Closes #1234`. Logo PRs without a linked issue are not accepted. + +### 1. Drop in the ASCII file + +``` +src/logo/ascii//.txt +``` + +For example `src/logo/ascii/o/omarchy.txt`. Directories are already split by first letter (`a/` … `z/`, plus `_/`). + +The file is plain ASCII art with `$1` … `$9` as color placeholders: + +``` + $3 ,-^-___ +$3 /\\\/// +$2refined.$1 /\\\\// +``` + +- `$1` … `$9` map to palette slots 1–9; at most 9 (`FASTFETCH_LOGO_MAX_COLORS = 9`) +- `$$` is a literal `$` +- Characters without a placeholder inherit the current color. Of the 530 existing logos, 239 use no placeholders at all (monochrome) and 291 do — **new logos should use placeholders**; monochrome is a legacy style +- Tabs are expanded to 4 spaces +- Colors can be overridden with `--logo-color-1` … `--logo-color-9` + +### 2. Register it in the `.inc` + +CMake turns each `.txt` into a `FASTFETCH_DATATEXT_LOGO_` macro (`CMakeLists.txt:418`), but **the registry itself is maintained by hand**. Edit `src/logo/ascii/.inc`: + +```c +#ifdef FASTFETCH_DATATEXT_LOGO_OMARCHY +// Omarchy +{ + .names = { "omarchy", "Omarchy" }, + .lines = FASTFETCH_DATATEXT_LOGO_OMARCHY, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_WHITE, + FF_COLOR_FG_CYAN, + }, +}, +#endif +``` + +- `names` is matched case-insensitively. Do not add names that differ only by case, because they can never be distinguished. Every name is also shown by `--list-logos`, so use user-friendly spelling such as an initial capital where appropriate. +- For a new logo, use exactly one name unless there is a specific compatibility reason to add more. That name should first be the `ID` from `/etc/os-release`. If the ID cannot distinguish this logo from another logo for the same OS, use the `NAME` value instead. `logoGetBuiltinDetected` in `src/logo/logo.c` documents the detection order: `ID`, then `NAME`, then tokens from `ID_LIKE`, then the platform name fallback. Manual `logo-source` selection uses the name directly. +- `colors` is positional — entry 0 is `$1`, entry 1 is `$2`, and so on +- `colors[0]` also becomes the title color and `colors[1]` the key color, unless the user overrides them + +If the same OS has multiple logo variants, mark the variant explicitly with `.type`: + +```c +.type = FF_LOGO_LINE_TYPE_SMALL_BIT, +``` + +Use `FF_LOGO_LINE_TYPE_ALTER_BIT` for an alternate logo, `FF_LOGO_LINE_TYPE_SMALL_BIT` for a small logo, or combine the flags when both apply. This is also a lookup optimization: a logo marked `FF_LOGO_LINE_TYPE_SMALL_BIT` is considered only for `type = small`, while a logo marked `FF_LOGO_LINE_TYPE_ALTER_BIT` is never selected by automatic detection. Alternate logos are available only when explicitly requested through `--logo `. + +### 3. Verify + +```sh +./build/fastfetch --logo omarchy +./build/fastfetch --list-logos | grep -i omarchy +``` + +--- + +## Platform conventions + +The `detection/` layer picks its implementation by **filename suffix**; the choice is made at build time from `CMAKE_SYSTEM_NAME`: + +| Suffix | Platform | +|---|---| +| `_linux.c` | Linux | +| `_apple.c` / `_apple.m` | macOS / iOS (`.m` for Objective-C) | +| `_windows.c` / `_windows.cpp` | Windows (`.cpp` for WinRT / WMI) | +| `_bsd.c` | FreeBSD | +| `_nbsd.c` | NetBSD | +| `_obsd.c` | OpenBSD | +| `_sunos.c` | Solaris / illumos | +| `_haiku.c` / `.cpp` | Haiku | +| `_android.c` | Android (Termux) | +| `_gnu.c` | GNU/Hurd | +| `_nosupport.c` | Empty fallback | + +Current distribution of platform files in `detection/`: + +| Suffix | Files | Suffix | Files | +|---|---|---|---| +| `_linux` | 58 | `_obsd` | 16 | +| `_windows` | 47 | `_haiku` | 13 | +| `_apple` | 46 | `_android` | 12 | +| `_nosupport` | 45 | `_gnu` | 3 | +| `_bsd` | 29 | | | +| `_sunos` | 19 | | | +| `_nbsd` | 17 | | | + +(These are repository inventory figures, counting `.c`, `.m`, `.cpp`, and `.h`; they may change as platforms and modules are added.) + +**Don't pile `#ifdef __linux__` into one file.** When adding platform support, copy the closest existing implementation and change the suffix. + +`common/` uses the same idea: `common/impl/` contains `io_unix.c` / `io_windows.c`, `netif_linux.c` / `netif_apple.c` / `netif_bsd.c` and so on, with `common/apple/`, `common/windows/` and `common/haiku/` holding platform-specific helpers. + +When several platform suffixes could apply, use the most specific implementation supported by the build system (for example, `_nbsd.c` instead of the generic `_bsd.c` on NetBSD). Keep the generic file as the fallback for platforms that share its conventions. + +--- + +## Code style + +### Formatting + +The project uses clang-format with `BasedOnStyle: LLVM` plus these key overrides: + +```yaml +IndentWidth: 4 +UseTab: Never +ColumnLimit: 0 # never wrap +InsertBraces: true # braces even on single-statement ifs +PointerAlignment: Left # char* p, not char *p +SortIncludes: Never # include order is managed by hand +AlignAfterOpenBracket: DontAlign +BinPackParameters: false # all params on one line, or one per line +``` + +Before committing: + +```sh +clang-format -i src/modules/foo/*.c src/modules/foo/*.h +``` + +`src/3rdparty/**`, `build/**` and `src/logo/builtin.c` are listed in `.clang-format-ignore` — **do not reformat them.** + +`.editorconfig` adds LF line endings, 4-space indent, a final newline, and trailing whitespace trimmed (except in Markdown). + +### Naming + +| Kind | Convention | Example | +|---|---|---| +| Functions | `ff` + PascalCase | `ffDetectCPU`, `ffPrintCPU` | +| Types | `FF` + PascalCase | `FFCPUResult`, `FFModuleBaseInfo` | +| Variables / fields | camelCase | `coresPhysical`, `keyWidth` | +| Macros | SCREAMING_SNAKE_CASE | `FF_OPTION_MAX_SIZE` | +| Module options | `FFOptions` | `FFCPUOptions` | +| Detection results | `FFResult` | `FFCPUResult` | + +### Spelling + +CI runs codespell (`.codespellrc`). Known false positives live in `ignore-words-list` (`iterm`, `compiletime`, and various non-English distro words). Add new words there rather than changing the code. + +### Compiler warnings + +The build enables `-Wall -Wextra -Wconversion` plus several `-Werror`s: + +``` +-Werror=uninitialized -Werror=return-type -Werror=vla +-Werror=incompatible-pointer-types -Werror=implicit-function-declaration -Werror=int-conversion +``` + +**`-Wconversion` is strict**: every implicit narrowing conversion needs an explicit cast. + +--- + +## Commit messages + +Format: + +``` +[ (Platform)]: +``` + +Scope is the module or subsystem name; Platform is optional. Use the third-person singular present tense, capitalize the first letter, no trailing period. + +Real examples from recent history: + +``` +Processes (Haiku): honors `options->countKprocs` +WM (macOS): improves reliability of WM plugin detection +Memory (Windows): prefers `NQSI` +Top (Linux): improves performance of `stat` parsing +Logo (Builtin): adds omarchy +Global: introduces global macro `FF_PATH_PKG_BASE` to replace `_PATH_LOCALBASE` +LM (OpenBSD): adds support +CI: disables fail on alert +Doc: updates README +Presets: moves `top` to the bottom of running modules [ci skip] +``` + +Common verbs: `adds`, `removes`, `fixes`, `improves`, `updates`, `corrects`, `prefers`, `honors`, `disables`, `enables`, `introduces`, `detects`, `reports`, `skips`, `uses`. + +Scopes in use: `Top`, `Processes`, `Memory`, `Logo (Builtin)`, `CI`, `Doc`, `Presets`, `Global`, plus individual module names. + +Documentation-only changes get a `[ci skip]` suffix. + +--- + +## Changelog + +User-visible changes go into `CHANGELOG.md`, under the topmost version heading: + +```markdown +# Unreleased + +Changes: +* The DE / WM / LM modules now reports the full name ... + +Features: +* Added Top module to print processes with the highest CPU, memory or disk I/O usage. (Top) +* Improved Wi-Fi module + * Added Wifi channel width detection, exposed via `{channel-width}` in custom format. + * Improved Wifi channel frequency accuracy on Windows, macOS. +* Added Battery detection support on SunOS. (Battery, SunOS) + +Bugfixes: +* Fixed I/O rate calculation precision in DiskIO and NetIO. (DiskIO / NetIO) +``` + +Rules: + +- Three sections: `Changes:` (behavior changes), `Features:`, `Bugfixes:` +- Past tense: `Added` / `Fixed` / `Improved` +- End each entry with its scope: `(Module, Platform)` or `(ModuleA / ModuleB)` +- Sub-bullets are indented 4 spaces + +--- + +## Tests + +Tests are standalone executables in `tests/` using a `VERIFY` macro that exits non-zero on failure: + +```c +#define VERIFY(expression) \ + if (!(expression)) testFailed(&strbuf, #expression, __LINE__) +``` + +Current tests: `strbuf.c`, `list.c`, `format.c`, `color.c`, `duration.c`, `strutil.c`. + +```sh +cmake -B build -DBUILD_TESTS=On +cmake --build build +cd build && ctest --output-on-failure +``` + +Coverage focuses on the core data structures and the formatting engine in `common/`. The `detection/` layer has no automated tests (it depends too heavily on a real system) and is instead covered by the CI matrix — 20 workflows under `.github/workflows/` spanning Linux (including musl, loong64, armv7l, i686), macOS, Windows, FreeBSD, NetBSD, OpenBSD, DragonFly, Solaris, OmniOS and Haiku, plus spellcheck and benchmark jobs. + +**If you touch `common/FFstrbuf.h`, `common/format.h` or `common/color.h`, please extend the matching test.** + +--- + +## Pull requests + +1. **Open an issue first** (feature request / bug report / logo request) so you don't waste effort. Templates are in `.github/ISSUE_TEMPLATE/`. +2. Branch off `dev` — **`dev` is the main development branch**, not `master`. +3. Follow the [commit message convention](#commit-messages). +4. Update `CHANGELOG.md` for user-visible changes. +5. Open the PR against `dev` and fill in `.github/pull_request_template.md`: + - Summary + - Related issue (**required for new logos**, otherwise the PR won't be accepted) + - Changes + - Screenshots (required for visual changes) + - Checklist: confirm you tested locally + +### Pre-submit checklist + +```sh +clang-format -i # format +codespell # spelling +cmake -B build -DBUILD_TESTS=On && cmake --build build -j +cd build && ctest --output-on-failure # tests +./build/fastfetch --format json # verify JSON output is well-formed +./build/fastfetch -c presets/all.jsonc --stat false # smoke-test every module +``` + +--- + +## Gotchas + +Things that are easy to get wrong when reading this codebase. Most of them follow from the "obsessively fast startup" goal. + +### 1. Option structs must not exceed 256 bytes + +`FF_OPTION_MAX_SIZE = 1 << 8`. Exceeding it is caught at compile time by `static_assert`. Don't try to raise the value — it determines the stack cost of every module invocation. + +### 2. `FF_MODULE_DISABLE_*` controls registration, not compilation + +```c +// verbatim from the top of modules/modules.c: +// FF_MODULE_DISABLE_ only controls if the module is registered, +// the module code itself is still compiled. +// We rely `LTO` to remove the unused code (only enabled in Release mode) +``` + +**Disabling modules in a Debug build does not shrink the binary.** LTO is enabled whenever `CMAKE_BUILD_TYPE != Debug`, and the default `RelWithDebInfo` already satisfies that — so measure size with `RelWithDebInfo` or `Release`, never with `Debug`. (The "Release mode" wording in the comment above is imprecise.) + +### 3. CMake globs don't trigger reconfiguration + +Both the module directories and the logo files are discovered by glob. CMake will not notice new files on its own — you need to re-run `cmake -B build` (or delete `build/CMakeCache.txt` and start over). This is the classic CMake footgun. + +### 4. CLI module options are removed + +Per-module command-line flags such as `--cpu-temp` are **no longer supported**. The only job of `ffParseModuleOptions` now is to translate the flag into a JSON key, then `exit(477)` with a pointer to the config file: + +``` +Error: Unsupported module option: --cpu-temp + Support of module options has been removed. Please add the flag to the JSON config instead. + Example (demonstration only): `{ "modules": [ { "type": "cpu", "temp": true } ] }` +``` + +**JSONC is the first-class configuration interface; the command line is not.** When adding module options, implement `parseJsonObject` only — do not add a CLI branch. + +### 5. `defaultOrder = 0` hides a module from `--gen-config` + +`collectModuleInfos` (`genconfig.c:186`) skips any module whose `defaultOrder` is `0`. C zero-initializes the field, so **omitting it is the same as setting it to 0**. Only `logo`, `command` and `custom` do this on purpose. + +`defaultOrder` affects only the ordering in the interactive `--gen-config` picker; it has no effect on runtime output order. + +### 6. Localization uses byte offsets, not enums + +`instance.config.display.keyLanguage` does not hold a language enum — it holds a **byte offset** such as `offsetof(FFModuleDisplayName, zh_CN)`. The macro `FF_MODULE_GET_DISPLAY_NAME` (`common/option.h:123`) does plain pointer arithmetic with it: + +```c +#define FF_MODULE_GET_DISPLAY_NAME(moduleName) \ + (*(const char**) ((uint8_t*) &ff ## moduleName ## ModuleInfo.displayName + instance.config.display.keyLanguage)) +``` + +Consequence: **the field order of `FFModuleDisplayName` must never change** — reordering silently scrambles every language. + +### 7. The `multithreading` switch barely does anything + +The global `multithreading` option currently takes effect in exactly one place: `common/impl/networking_linux.c:339`. Modules are still printed sequentially. Modules that need concurrency go through the `ffPrepare*` warm-up hooks instead, which start sampling or fire off requests before the print loop begins. + +### 8. Don't print or read config inside `detection/` + +The `detection/` layer must stay pure: read system state, fill a struct, return an error string. Any `printf` or any read of `instance.config` there is a design error. + +### 9. `src/logo/builtin.c` and `3rdparty/` are formatting-exempt + +The former is a large generated/hand-maintained data table, the latter is upstream code. Both are excluded via `.clang-format-ignore` — leave them alone. + +--- + +## Reference + +- Configuration guide: +- JSON schema: `doc/json_schema.json` +- Example presets: `presets/` (`all.jsonc`, `neofetch.jsonc`, `ci.jsonc`, …) +- Man page source: `doc/fastfetch.1.in` +- Code generation scripts: `scripts/` (`gen-pciids.py`, `gen-amdgpuids.py`, `gen-man.py`) diff --git a/README.md b/README.md index 7f2481cbd2..b3e66cee84 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Fastfetch +[![Benchmark](https://img.shields.io/badge/GitHub%20Pages-live-blue?logo=github)](https://fastfetch-cli.github.io/fastfetch/dev/bench) [![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/fastfetch-cli/fastfetch/ci.yml)](https://github.com/fastfetch-cli/fastfetch/actions) [![GitHub license](https://img.shields.io/github/license/fastfetch-cli/fastfetch)](https://github.com/fastfetch-cli/fastfetch/blob/dev/LICENSE) [![GitHub contributors](https://img.shields.io/github/contributors/fastfetch-cli/fastfetch)](https://github.com/fastfetch-cli/fastfetch/graphs/contributors) @@ -99,7 +100,7 @@ See the Wiki: https://github.com/fastfetch-cli/fastfetch/wiki/Building * View all data that fastfetch detects: `fastfetch -s [:][:] --format json` * Display help messages: `fastfetch --help` * Generate a minimal config file: `fastfetch [-s [:]] --gen-config []` - * Use: `--gen-config-full` to generate a full config file with all optional options + * Use [The online configuration generator](https://fastfetch-cli.github.io/fastfetch-config/) to generate a full config file with all optional options ## Customization diff --git a/completions/fastfetch.fish b/completions/fastfetch.fish index f4804f5f50..45397a6b7e 100755 --- a/completions/fastfetch.fish +++ b/completions/fastfetch.fish @@ -63,46 +63,78 @@ echo ' import json, subprocess, sys +def escape_fish_dq(s: str) -> str: + """Escape special characters for Fish double quotes to prevent unexpected expansions. One slash in output needs four slashes in script""" + return str(s).replace("\\\\", "\\\\\\\\").replace("\\"", "\\\\\\"").replace("$", "\\\\$") + + def main(): data: dict[str, list[dict]] = json.loads(subprocess.check_output(["fastfetch", "--help-raw"])) + enum_functions = [] + complete_commands = [] + for key in data: for flag in data[key]: - if flag["long"] == "logo-color-[1-9]": + long_name = flag.get("long", "") + + # Handle pseudo flag logo-color-[1-9] + if long_name == "logo-color-[1-9]": + safe_desc_global = escape_fish_dq(flag.get("desc", "")) for i in range(1, 10): - print(f"""complete -c fastfetch -d "{flag["desc"]}" -l "logo-color-{i}" -x -a "(__fastfetch_complete_color)" """) + complete_commands.append(f"complete -c fastfetch -d \"{safe_desc_global}\" -l \"logo-color-{i}\" -x -a \"(__fastfetch_complete_color)\"") continue if flag.get("pseudo", False): continue - command_prefix = f"""complete -c fastfetch -d "{flag["desc"]}" -l "{flag["long"]}\"""" + safe_desc_global = escape_fish_dq(flag.get("desc", "")) + command_prefix = f"complete -c fastfetch -d \"{safe_desc_global}\" -l \"{long_name}\"" + if "short" in flag: - command_prefix += f""" -o {flag["short"]}""" + short_name = flag["short"] + command_prefix += f" -o {short_name}" if "arg" in flag: - type: str = flag["arg"]["type"] - if type == "bool": - print(f"{command_prefix} -x -a \"(__fastfetch_complete_bool)\"") - elif type == "color": - print(f"{command_prefix} -x -a \"(__fastfetch_complete_color)\"") - elif type == "command": - print(f"{command_prefix} -x -a \"(__fastfetch_complete_command)\"") - elif type == "config": - print(f"{command_prefix} -x -a \"(__fastfetch_complete_config)\"") - elif type == "enum": - temp: str = " ".join(flag["arg"]["enum"]) - print(f"{command_prefix} -x -a \"{temp}\"") - elif type == "logo": - print(f"{command_prefix} -x -a \"(__fastfetch_complete_logo)\"") - elif type == "structure": - print(f"{command_prefix} -x -a \"(__fish_complete_list : __fastfetch_complete_structure)\"") - elif type == "path": - print(f"{command_prefix} -r -F") + arg_type: str = flag["arg"]["type"] + if arg_type == "bool": + complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_bool)\"") + elif arg_type == "color": + complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_color)\"") + elif arg_type == "command": + complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_command)\"") + elif arg_type == "config": + complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_config)\"") + elif arg_type == "enum": + # Sanitize flag name to create a valid Fish function name + func_name = long_name.replace("-", "_").replace("[", "_").replace("]", "_") + func_name = f"__fastfetch_complete_enum_{func_name}" + + # Generate the enum function + func_lines = [f"function {func_name}"] + for enum_val, enum_desc in flag["arg"]["enum"].items(): + safe_val = escape_fish_dq(enum_val) + safe_desc = escape_fish_dq(enum_desc) + func_lines.append(f" echo -e \"{safe_val}\\t{safe_desc}\"") + func_lines.append("end\n") + enum_functions.append("\n".join(func_lines)) + + # Append complete command pointing to the new function + complete_commands.append(f"{command_prefix} -x -a \"({func_name})\"") + elif arg_type == "logo": + complete_commands.append(f"{command_prefix} -x -a \"(__fastfetch_complete_logo)\"") + elif arg_type == "structure": + complete_commands.append(f"{command_prefix} -x -a \"(__fish_complete_list : __fastfetch_complete_structure)\"") + elif arg_type == "path": + complete_commands.append(f"{command_prefix} -r -F") else: - print(f"{command_prefix} -x") + complete_commands.append(f"{command_prefix} -x") else: - print(f"{command_prefix} -f") + complete_commands.append(f"{command_prefix} -f") + + # Print generated enum functions first, then complete rules + print("\n".join(enum_functions)) + print("\n".join(complete_commands)) if __name__ == "__main__": diff --git a/doc/help.json b/doc/help.json index e24d294fc7..1d882d1330 100644 --- a/doc/help.json +++ b/doc/help.json @@ -585,6 +585,38 @@ "default": "string" } }, + { + "long": "key-language", + "desc": "Specify the language used for module keys", + "remark": "Leave empty for auto-detection based on the system locale. Unsupported locales fall back to English", + "arg": { + "type": "enum", + "default": "en", + "optional": true, + "enum": { + "en": "English", + "ar": "Arabic", + "cs": "Czech", + "de": "German", + "es": "Spanish", + "fr": "French", + "gl": "Galician", + "he": "Hebrew", + "id": "Indonesian", + "it": "Italian", + "ja": "Japanese", + "ko": "Korean", + "pl": "Polish", + "pt": "Portuguese", + "ru": "Russian", + "tr": "Turkish", + "uk": "Ukrainian", + "vi": "Vietnamese", + "zh_CN": "Simplified Chinese", + "zh_TW": "Traditional Chinese" + } + } + }, { "long": "bright-color", "desc": "Specify whether keys, title, and ASCII logo should be printed in bright color", diff --git a/doc/json_schema.json b/doc/json_schema.json index 797e03b4d9..0aeba6de29 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -398,7 +398,7 @@ "type": "string" }, "lmFormat": { - "description": "Output format for the `LM` module. See Wiki for formatting syntax\n 1. {service}: LM service\n 2. {type}: LM type\n 3. {version}: LM version", + "description": "Output format for the `LM` module. See Wiki for formatting syntax\n 1. {service}: LM service / process name\n 2. {pretty-name}: LM pretty name\n 3. {version}: LM version", "type": "string" }, "loadavgFormat": { @@ -505,6 +505,10 @@ "description": "Output format for the `Title` module. See Wiki for formatting syntax\n 1. {user-name}: User name\n 2. {host-name}: Host name\n 3. {home-dir}: Home directory\n 4. {exe-path}: Executable path of current process\n 5. {user-shell}: User's default shell\n 6. {user-name-colored}: User name (colored)\n 7. {at-symbol-colored}: @ symbol (colored)\n 8. {host-name-colored}: Host name (colored)\n 9. {full-user-name}: Full user name\n 10. {user-id}: UID (*nix) / SID (Windows)\n 11. {pid}: PID of current process\n 12. {cwd}: CWD with home dir replaced by `~`", "type": "string" }, + "topFormat": { + "description": "Output format for the `Top` module. See Wiki for formatting syntax\n 1. {name}: Process name\n 2. {pid}: Process ID\n 3. {cpu}: CPU usage\n 4. {mem}: Memory usage (RSS) in bytes\n 5. {disk-read}: Disk read bytes per second (0 if unsupported)\n 6. {disk-write}: Disk write bytes per second (0 if unsupported)\n 7. {threads}: Number of threads\n 8. {cpu-percentage}: CPU usage percentage\n 9. {mem-formatted}: Memory usage (RSS) formatted\n 10. {disk-read-formatted}: Disk read formatted\n 11. {disk-write-formatted}: Disk write formatted", + "type": "string" + }, "themeFormat": { "description": "Output format for the `Theme` module. See Wiki for formatting syntax\n 1. {theme1}: Theme part 1\n 2. {theme2}: Theme part 2", "type": "string" @@ -542,7 +546,7 @@ "type": "string" }, "wifiFormat": { - "description": "Output format for the `Wifi` module. See Wiki for formatting syntax\n 1. {inf-desc}: Interface description\n 2. {inf-status}: Interface status\n 3. {status}: Connection status\n 4. {ssid}: Connection SSID\n 5. {bssid}: Connection BSSID\n 6. {protocol}: Connection protocol\n 7. {signal-quality}: Connection signal quality (percentage num)\n 8. {rx-rate}: Connection RX rate\n 9. {tx-rate}: Connection TX rate\n 10. {security}: Connection Security algorithm\n 11. {signal-quality-bar}: Connection signal quality (percentage bar)\n 12. {channel}: Connection channel number\n 13. {band}: Connection channel band in GHz", + "description": "Output format for the `Wifi` module. See Wiki for formatting syntax\n 1. {inf-desc}: Interface description\n 2. {inf-status}: Interface status\n 3. {status}: Connection status\n 4. {ssid}: Connection SSID\n 5. {bssid}: Connection BSSID\n 6. {protocol}: Connection protocol\n 7. {signal-quality}: Connection signal quality (percentage num)\n 8. {rx-rate}: Connection RX rate\n 9. {tx-rate}: Connection TX rate\n 10. {security}: Connection Security algorithm\n 11. {signal-quality-bar}: Connection signal quality (percentage bar)\n 12. {channel}: Connection channel number\n 13. {channel-width}: Connection channel width in MHz\n 14. {band}: Connection channel band in GHz", "type": "string" }, "wmthemeFormat": { @@ -1057,6 +1061,116 @@ "description": "Left padding for keys", "minimum": 0, "default": 0 + }, + "language": { + "description": "Language used for module keys. `null` for auto detection based on system locale", + "oneOf": [ + { + "type": "null", + "description": "Auto detection based on system locale" + }, + { + "type": "string", + "const": "en", + "description": "English" + }, + { + "type": "string", + "const": "ar", + "description": "Arabic" + }, + { + "type": "string", + "const": "cs", + "description": "Czech" + }, + { + "type": "string", + "const": "de", + "description": "German" + }, + { + "type": "string", + "const": "es", + "description": "Spanish" + }, + { + "type": "string", + "const": "fr", + "description": "French" + }, + { + "type": "string", + "const": "gl", + "description": "Galician" + }, + { + "type": "string", + "const": "he", + "description": "Hebrew" + }, + { + "type": "string", + "const": "id", + "description": "Indonesian" + }, + { + "type": "string", + "const": "it", + "description": "Italian" + }, + { + "type": "string", + "const": "ja", + "description": "Japanese" + }, + { + "type": "string", + "const": "ko", + "description": "Korean" + }, + { + "type": "string", + "const": "pl", + "description": "Polish" + }, + { + "type": "string", + "const": "pt", + "description": "Portuguese" + }, + { + "type": "string", + "const": "ru", + "description": "Russian" + }, + { + "type": "string", + "const": "tr", + "description": "Turkish" + }, + { + "type": "string", + "const": "uk", + "description": "Ukrainian" + }, + { + "type": "string", + "const": "vi", + "description": "Vietnamese" + }, + { + "type": "string", + "const": "zh_CN", + "description": "Simplified Chinese" + }, + { + "type": "string", + "const": "zh_TW", + "description": "Traditional Chinese" + } + ], + "default": "en" } } }, @@ -1482,6 +1596,7 @@ "poweradapter", "processes", "publicip", + "top", "separator", "shell", "sound", @@ -2585,8 +2700,8 @@ }, "waitTime": { "type": "integer", - "description": "Wait time (in ms). Disk I/O = (totalBytesEnd - totalBytesStart) / waitTime", - "default": 200, + "description": "Wait time (in ms) used to measure the I/O rate (calculated in bytes/sec).\nAvoid setting this too low, as the kernel requires time to accurately update counters.\nIgnored if 'detectTotal' is true.", + "default": 500, "minimum": 1 }, "key": { @@ -3486,8 +3601,8 @@ }, "waitTime": { "type": "integer", - "description": "Wait time (in ms). Net I/O = (totalBytesEnd - totalBytesStart) / waitTime", - "default": 200, + "description": "Wait time (in ms) used to measure the I/O rate (calculated in bytes/sec).\nAvoid setting this too low, as the kernel requires time to accurately update counters.\nIgnored if 'detectTotal' is true.", + "default": 500, "minimum": 1 }, "key": { @@ -3869,7 +3984,12 @@ "properties": { "type": { "const": "processes", - "description": "Print the number of running processes" + "description": "Print the number of running processes and threads" + }, + "countKprocs": { + "description": "Whether to take kernel processes and threads into account", + "type": "boolean", + "default": false }, "key": { "$ref": "#/$defs/key" @@ -4314,6 +4434,103 @@ } } }, + { + "title": "Top", + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "top", + "description": "Print processes with the highest CPU, memory, disk I/O usage, latest start time or thread count" + }, + "sort": { + "type": "string", + "description": "Sort processes by CPU, memory, disk IO usage, start time or thread count", + "enum": [ + "cpu", + "memory", + "disk-read", + "disk-write", + "start-time", + "threads" + ], + "default": "cpu" + }, + "showTypes": { + "description": "Specify which resource usages are detected and displayed.\nIf memory or threads is enabled is enabled alone, only a single process snapshot is taken and `waitTime` sampling wait is skipped", + "default": [ + "cpu", + "memory", + "disk" + ], + "oneOf": [ + { + "type": "string", + "enum": [ + "cpu", + "memory", + "disk", + "threads" + ] + }, + { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "cpu", + "memory", + "disk", + "threads" + ] + } + } + ] + }, + "processes": { + "type": "integer", + "description": "Number of top processes to print", + "minimum": 1, + "default": 5 + }, + "waitTime": { + "type": "integer", + "description": "Wait time between process samples, in milliseconds. Ignored when showTypes contains \"memory\" only", + "minimum": 1, + "default": 500 + }, + "compact": { + "type": "boolean", + "description": "Print all process on a single line", + "default": false + }, + "percent": { + "$ref": "#/$defs/percent" + }, + "key": { + "$ref": "#/$defs/key" + }, + "keyColor": { + "$ref": "#/$defs/keyColor" + }, + "keyIcon": { + "$ref": "#/$defs/keyIcon" + }, + "keyWidth": { + "$ref": "#/$defs/keyWidth" + }, + "outputColor": { + "$ref": "#/$defs/outputColor" + }, + "format": { + "$ref": "#/$defs/topFormat" + }, + "condition": { + "$ref": "#/$defs/conditions" + } + } + }, { "title": "TPM", "type": "object", diff --git a/presets/all.jsonc b/presets/all.jsonc index c9f951659e..73901ca30b 100644 --- a/presets/all.jsonc +++ b/presets/all.jsonc @@ -99,16 +99,17 @@ "gamepad", "mouse", "keyboard", - { - "type": "weather", - "timeout": 1000 - }, + "top", "netio", "diskio", { "type": "physicaldisk", "temp": true }, + { + "type": "weather", + "timeout": 1000 + }, "tpm", "version", "break", diff --git a/presets/ci.jsonc b/presets/ci.jsonc index 52eb9b4427..c18994df06 100644 --- a/presets/ci.jsonc +++ b/presets/ci.jsonc @@ -94,23 +94,24 @@ "opengl", "opencl", "users", - // "bluetooth", // doesn't work on macOS because it requires bluetooth permissions - // "bluetoothradio", + "bluetooth", + "bluetoothradio", "sound", "camera", "gamepad", "mouse", "keyboard", - { - "type": "weather", - "timeout": 1000 - }, + "top", "netio", "diskio", { "type": "physicaldisk", "temp": true }, + { + "type": "weather", + "timeout": 1000 + }, "tpm", "version", "logo", diff --git a/presets/examples/14.jsonc b/presets/examples/14.jsonc index b20c544775..e85e7bfb49 100644 --- a/presets/examples/14.jsonc +++ b/presets/examples/14.jsonc @@ -12,7 +12,7 @@ }, "modules": [ { - // draw borders first to make colors of left and right border consistant + // draw borders first to make colors of left and right border consistent "key": " user", "type": "title", "format": "{user-name}", diff --git a/src/common/FFstrbuf.h b/src/common/FFstrbuf.h index e3722ad82d..0f4f405a6a 100644 --- a/src/common/FFstrbuf.h +++ b/src/common/FFstrbuf.h @@ -8,6 +8,7 @@ #include #include #include "common/memrchr.h" +#include "common/arrutil.h" #ifdef FF_USE_SYSTEM_YYJSON #include @@ -628,3 +629,4 @@ static inline void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file) { bool ffStrbufDecodeHexEscapeSequences(FFstrbuf* strbuf); #define FF_STRBUF_AUTO_DESTROY [[gnu::cleanup(ffStrbufDestroy)]] FFstrbuf +#define FF_STRBUF_STATIC(str) { .allocated = 0, .length = (uint32_t) sizeof(str) - 1, .chars = str } diff --git a/src/common/endian.h b/src/common/endian.h new file mode 100644 index 0000000000..2410911ee4 --- /dev/null +++ b/src/common/endian.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#if __BIG_ENDIAN__ + #define FF_READ_LE(x) _Generic((x), \ + uint16_t: __builtin_bswap16(x), \ + uint32_t: __builtin_bswap32(x), \ + uint64_t: __builtin_bswap64(x)) + #define FF_READ_BE(x) (x) +#else + #define FF_READ_LE(x) (x) + #define FF_READ_BE(x) _Generic((x), \ + uint16_t: __builtin_bswap16(x), \ + uint32_t: __builtin_bswap32(x), \ + uint64_t: __builtin_bswap64(x)) +#endif diff --git a/src/common/impl/FFPlatform_unix.c b/src/common/impl/FFPlatform_unix.c index ec2ef1699d..92f0e86349 100644 --- a/src/common/impl/FFPlatform_unix.c +++ b/src/common/impl/FFPlatform_unix.c @@ -3,7 +3,7 @@ #include "common/arrutil.h" #include "common/strutil.h" #include "common/io.h" -#include "fastfetch_config.h" +#include "common/path.h" #include #include @@ -238,8 +238,8 @@ static void getDataDirs(FFPlatform* platform) { ffPlatformPathAddHome(&platform->dataDirs, platform, ""); platformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS"); -#ifdef _PATH_LOCALBASE - ffPlatformPathAddAbsolute(&platform->dataDirs, _PATH_LOCALBASE "/share/"); +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + ffPlatformPathAddAbsolute(&platform->dataDirs, FF_PATH_PKG_BASE "/share/"); #endif ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/local/share/"); ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/share/"); diff --git a/src/common/impl/FFstrbuf.c b/src/common/impl/FFstrbuf.c index 2ed3e9de84..27cb4d66a6 100644 --- a/src/common/impl/FFstrbuf.c +++ b/src/common/impl/FFstrbuf.c @@ -83,6 +83,7 @@ void ffStrbufEnsureFreeNoCheck(FFstrbuf* strbuf, uint32_t free) { // Round up to the next power of 2. // If the value is already a power of 2, it will be rounded up to the next power of 2. + // allocate = __builtin_stdc_bit_ceil(allocate + 1); // Currently generates worse asm code allocate = 1U << (32 - __builtin_clz(allocate)); } diff --git a/src/common/impl/base64.c b/src/common/impl/base64.c index fa987c303c..3d743691b1 100644 --- a/src/common/impl/base64.c +++ b/src/common/impl/base64.c @@ -1,4 +1,5 @@ #include "common/base64.h" +#include "common/endian.h" // https://github.com/kostya/benchmarks/blob/master/base64/test-nolib.c#L145 void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output) { @@ -7,12 +8,8 @@ void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* const char* ends = str + (size - size % 3); while (str != ends) { uint32_t n = *(uint32_t*) str; - #if !__BIG_ENDIAN__ - // The 3 input bytes must be laid out big-endian (str[0] in the most - // significant position). On little-endian hosts swap; on big-endian - // hosts the word is already in the right order. - n = __builtin_bswap32(n); - #endif + // The 3 input bytes must be laid out big-endian (str[0] in the most significant position). + n = FF_READ_BE(n); *out++ = chars[(n >> 26) & 63]; *out++ = chars[(n >> 20) & 63]; *out++ = chars[(n >> 14) & 63]; diff --git a/src/common/impl/commandoption.c b/src/common/impl/commandoption.c index c4225a711b..fbbcc18db8 100644 --- a/src/common/impl/commandoption.c +++ b/src/common/impl/commandoption.c @@ -67,13 +67,13 @@ void ffPrepareCommandOption(FFdata* data) { char* moduleType = nullptr; size_t moduleLen = 0; while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) { -#define FF_IF_MODULE_MATCH(moduleNameConstant) if (moduleLen == strlen(moduleNameConstant) && ffStrEqualsIgnCase(moduleType, moduleNameConstant) && !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':')) +#define FF_IF_MODULE_MATCH(moduleNameConstant) if (ffStrEqualsIgnCase(moduleType, moduleNameConstant) && !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':')) switch (moduleType[0]) { #if !FF_MODULE_DISABLE_CPUUSAGE case 'C': case 'c': - FF_IF_MODULE_MATCH(FF_CPUUSAGE_MODULE_NAME) + FF_IF_MODULE_MATCH(ffCPUUsageModuleInfo.name) ffPrepareCPUUsage(); break; #endif @@ -81,7 +81,7 @@ void ffPrepareCommandOption(FFdata* data) { #if !FF_MODULE_DISABLE_DISKIO case 'D': case 'd': - FF_IF_MODULE_MATCH(FF_DISKIO_MODULE_NAME) { + FF_IF_MODULE_MATCH(ffDiskIOModuleInfo.name) { [[gnu::cleanup(ffDestroyDiskIOOptions)]] FFDiskIOOptions options; ffInitDiskIOOptions(&options); ffPrepareDiskIO(&options); @@ -92,7 +92,7 @@ void ffPrepareCommandOption(FFdata* data) { #if !FF_MODULE_DISABLE_NETIO case 'N': case 'n': - FF_IF_MODULE_MATCH(FF_NETIO_MODULE_NAME) { + FF_IF_MODULE_MATCH(ffNetIOModuleInfo.name) { [[gnu::cleanup(ffDestroyNetIOOptions)]] FFNetIOOptions options; ffInitNetIOOptions(&options); ffPrepareNetIO(&options); @@ -103,7 +103,7 @@ void ffPrepareCommandOption(FFdata* data) { #if !FF_MODULE_DISABLE_PUBLICIP case 'P': case 'p': - FF_IF_MODULE_MATCH(FF_PUBLICIP_MODULE_NAME) { + FF_IF_MODULE_MATCH(ffPublicIPModuleInfo.name) { [[gnu::cleanup(ffDestroyPublicIpOptions)]] FFPublicIPOptions options; ffInitPublicIpOptions(&options); ffPreparePublicIp(&options); @@ -111,10 +111,21 @@ void ffPrepareCommandOption(FFdata* data) { break; #endif + #if !FF_MODULE_DISABLE_TOP + case 'T': + case 't': + FF_IF_MODULE_MATCH(ffTopModuleInfo.name) { + [[gnu::cleanup(ffDestroyTopOptions)]] FFTopOptions options; + ffInitTopOptions(&options); + ffPrepareTopProcesses(options.showTypes); + } + break; + #endif + #if !FF_MODULE_DISABLE_WEATHER case 'W': case 'w': - FF_IF_MODULE_MATCH(FF_WEATHER_MODULE_NAME) { + FF_IF_MODULE_MATCH(ffWeatherModuleInfo.name) { [[gnu::cleanup(ffDestroyWeatherOptions)]] FFWeatherOptions options; ffInitWeatherOptions(&options); ffPrepareWeather(&options); diff --git a/src/common/impl/genconfig.c b/src/common/impl/genconfig.c index fbc10d874f..e819508166 100644 --- a/src/common/impl/genconfig.c +++ b/src/common/impl/genconfig.c @@ -996,7 +996,7 @@ bool ffGenConfigInteractive(FFdata* data) { success = true; } } else { - fputs("\nConfig generation cancelled.\n", stderr); + fputs("\nConfig generation canceled.\n", stderr); } ffListDestroy(&ui.items); diff --git a/src/common/impl/jsonconfig.c b/src/common/impl/jsonconfig.c index 5179d5b5d3..a7597ca408 100644 --- a/src/common/impl/jsonconfig.c +++ b/src/common/impl/jsonconfig.c @@ -136,9 +136,9 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) { #if !FF_MODULE_DISABLE_CPUUSAGE case 'c': case 'C': { - if (ffStrEqualsIgnCase(type, FF_CPUUSAGE_MODULE_NAME)) { + if (ffStrEqualsIgnCase(type, ffCPUUsageModuleInfo.name)) { ffPrepareCPUUsage(); - } else if (ffStrEqualsIgnCase(type, FF_COMMAND_MODULE_NAME)) { + } else if (ffStrEqualsIgnCase(type, ffCommandModuleInfo.name)) { [[gnu::cleanup(ffDestroyCommandOptions)]] FFCommandOptions options; ffInitCommandOptions(&options); if (module) { @@ -153,7 +153,7 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) { #if !FF_MODULE_DISABLE_DISKIO case 'd': case 'D': { - if (ffStrEqualsIgnCase(type, FF_DISKIO_MODULE_NAME)) { + if (ffStrEqualsIgnCase(type, ffDiskIOModuleInfo.name)) { [[gnu::cleanup(ffDestroyDiskIOOptions)]] FFDiskIOOptions options; ffInitDiskIOOptions(&options); if (module) { @@ -168,7 +168,7 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) { #if !FF_MODULE_DISABLE_NETIO case 'n': case 'N': { - if (ffStrEqualsIgnCase(type, FF_NETIO_MODULE_NAME)) { + if (ffStrEqualsIgnCase(type, ffNetIOModuleInfo.name)) { [[gnu::cleanup(ffDestroyNetIOOptions)]] FFNetIOOptions options; ffInitNetIOOptions(&options); if (module) { @@ -183,7 +183,7 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) { #if !FF_MODULE_DISABLE_PUBLICIP case 'p': case 'P': { - if (ffStrEqualsIgnCase(type, FF_PUBLICIP_MODULE_NAME)) { + if (ffStrEqualsIgnCase(type, ffPublicIPModuleInfo.name)) { [[gnu::cleanup(ffDestroyPublicIpOptions)]] FFPublicIPOptions options; ffInitPublicIpOptions(&options); if (module) { @@ -195,10 +195,25 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module) { } #endif + #if !FF_MODULE_DISABLE_TOP + case 't': + case 'T': { + if (ffStrEqualsIgnCase(type, ffTopModuleInfo.name)) { + [[gnu::cleanup(ffDestroyTopOptions)]] FFTopOptions options; + ffInitTopOptions(&options); + if (module) { + ffTopModuleInfo.parseJsonObject(&options, module); + } + ffPrepareTopProcesses(options.showTypes); + } + break; + } + #endif + #if !FF_MODULE_DISABLE_WEATHER case 'w': case 'W': { - if (ffStrEqualsIgnCase(type, FF_WEATHER_MODULE_NAME)) { + if (ffStrEqualsIgnCase(type, ffWeatherModuleInfo.name)) { [[gnu::cleanup(ffDestroyWeatherOptions)]] FFWeatherOptions options; ffInitWeatherOptions(&options); if (module) { diff --git a/src/common/impl/settings.c b/src/common/impl/settings.c index 0f00054df2..dcbd8893e2 100644 --- a/src/common/impl/settings.c +++ b/src/common/impl/settings.c @@ -450,12 +450,10 @@ bool ffSettingsGetSQLite3String(const char* dbPath, const char* query, FFstrbuf* return true; } #else // FF_HAVE_SQLITE3 -int ffSettingsGetSQLite3Int(const char* dbPath, const char* query) { - FF_UNUSED(dbPath, query) +int ffSettingsGetSQLite3Int(const char*, const char*) { return 0; } -bool ffSettingsGetSQLite3String(const char* dbPath, const char* query, FFstrbuf* result) { - FF_UNUSED(dbPath, query, result) +bool ffSettingsGetSQLite3String(const char*, const char*, FFstrbuf*) { return false; } #endif // FF_HAVE_SQLITE3 @@ -509,8 +507,8 @@ typedef struct E_Config { Eina_List* font_defaults; } E_Config; // Must be the same name as the top level struct in e.cfg - #define FF_EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(clas, type) \ - (ffeet_eina_file_data_descriptor_class_set(clas, sizeof(*(clas)), #type, sizeof(type))) + #define FF_EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(klass, type) \ + (ffeet_eina_file_data_descriptor_class_set(klass, sizeof(*(klass)), #type, sizeof(type))) #define FF_EET_DATA_DESCRIPTOR_ADD_BASIC(edd, struct_type, member, type) \ do { \ struct_type ___ett; \ diff --git a/src/common/impl/smbios.c b/src/common/impl/smbios.c index fc7df14b37..9a54003bc7 100644 --- a/src/common/impl/smbios.c +++ b/src/common/impl/smbios.c @@ -1,3 +1,4 @@ +#include "common/endian.h" #include "common/smbios.h" #include "common/io.h" #include "common/mallocHelper.h" @@ -81,9 +82,9 @@ static bool parseSmbiosTable(const uint8_t* data, uint32_t length) { break; } - if (header->Handle >= 0xFF00) { + if (FF_READ_LE(header->Handle) >= 0xFF00) { FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx", - header->Handle, + FF_READ_LE(header->Handle), (unsigned long) ((const uint8_t*) header - data)); break; } @@ -103,25 +104,25 @@ static bool parseSmbiosTable(const uint8_t* data, uint32_t length) { smbiosTable[header->Type] = header; FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u", header->Type, - header->Handle, + FF_READ_LE(header->Handle), header->Length); structureCount++; } else { FF_DEBUG("Duplicate SMBIOS structure type %u, handle 0x%04X, length %u", header->Type, - header->Handle, + FF_READ_LE(header->Handle), header->Length); } } else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) { FF_DEBUG("Reached SMBIOS end of type %u, handle 0x%04X, length %u", header->Type, - header->Handle, + FF_READ_LE(header->Handle), header->Length); break; } else { FF_DEBUG("Found custom SMBIOS structure type %u, handle 0x%04X, length %u; ignoring", header->Type, - header->Handle, + FF_READ_LE(header->Handle), header->Length); } } @@ -308,8 +309,8 @@ static bool fillTableBufferFallback(FFstrbuf* buffer) { sizeof(p->Smbios30)); return false; } - tableLength = p->Smbios30.StructureTableMaximumSize; - tableAddress = (off_t) p->Smbios30.StructureTableAddress; + tableLength = FF_READ_LE(p->Smbios30.StructureTableMaximumSize); + tableAddress = (off_t) FF_READ_LE(p->Smbios30.StructureTableAddress); FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u", tableLength, (unsigned long) tableAddress, @@ -325,8 +326,8 @@ static bool fillTableBufferFallback(FFstrbuf* buffer) { sizeof(p->Smbios20)); return false; } - tableLength = p->Smbios20.StructureTableLength; - tableAddress = (off_t) p->Smbios20.StructureTableAddress; + tableLength = FF_READ_LE(p->Smbios20.StructureTableLength); + tableAddress = (off_t) FF_READ_LE(p->Smbios20.StructureTableAddress); FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u", tableLength, (unsigned long) tableAddress, @@ -369,9 +370,9 @@ static bool detectSmbiosTableLength(const uint8_t* data, uint32_t bufferLength, return false; } - if (header->Handle >= 0xFF00) { + if (FF_READ_LE(header->Handle) >= 0xFF00) { FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx", - header->Handle, + FF_READ_LE(header->Handle), (unsigned long) (p - data)); return false; } @@ -614,8 +615,8 @@ static bool fillTableBufferPlatform(FFstrbuf* buffer) { sizeof(entryPoint.Smbios20)); return false; } - tableLength = entryPoint.Smbios20.StructureTableLength; - tableAddress = (off_t) entryPoint.Smbios20.StructureTableAddress; + tableLength = FF_READ_LE(entryPoint.Smbios20.StructureTableLength); + tableAddress = (off_t) FF_READ_LE(entryPoint.Smbios20.StructureTableAddress); FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u", tableLength, (unsigned long) tableAddress, @@ -629,8 +630,8 @@ static bool fillTableBufferPlatform(FFstrbuf* buffer) { sizeof(entryPoint.Smbios30)); return false; } - tableLength = entryPoint.Smbios30.StructureTableMaximumSize; - tableAddress = (off_t) entryPoint.Smbios30.StructureTableAddress; + tableLength = FF_READ_LE(entryPoint.Smbios30.StructureTableMaximumSize); + tableAddress = (off_t) FF_READ_LE(entryPoint.Smbios30.StructureTableAddress); FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u", tableLength, (unsigned long) tableAddress, diff --git a/src/common/impl/sysctl.c b/src/common/impl/sysctl.c index e17b5c348e..29d4a3afca 100644 --- a/src/common/impl/sysctl.c +++ b/src/common/impl/sysctl.c @@ -22,21 +22,41 @@ const char* ffSysctlGetString(int mib1, int mib2, FFstrbuf* result) { } int ffSysctlGetInt(int mib1, int mib2, int defaultValue) { - int result; + alignas(int) uint8_t result[sizeof(int)]; size_t neededLength = sizeof(result); if (sysctl((int[]) { mib1, mib2 }, 2, &result, &neededLength, nullptr, 0) != 0) { return defaultValue; } - return result; + switch (neededLength) { + case sizeof(int32_t): + return *(int32_t*)result; + case sizeof(int16_t): + return *(int16_t*)result; + case sizeof(int8_t): + return *(int8_t*)result; + default: + return defaultValue; + } } int64_t ffSysctlGetInt64(int mib1, int mib2, int64_t defaultValue) { - int64_t result; + alignas(int64_t) uint8_t result[sizeof(int64_t)]; size_t neededLength = sizeof(result); if (sysctl((int[]) { mib1, mib2 }, 2, &result, &neededLength, nullptr, 0) != 0) { return defaultValue; } - return result; + switch (neededLength) { + case sizeof(int64_t): + return *(int64_t*)result; + case sizeof(int32_t): + return *(int32_t*)result; + case sizeof(int16_t): + return *(int16_t*)result; + case sizeof(int8_t): + return *(int8_t*)result; + default: + return defaultValue; + } } #else const char* ffSysctlGetString(const char* propName, FFstrbuf* result) { @@ -59,35 +79,40 @@ const char* ffSysctlGetString(const char* propName, FFstrbuf* result) { } int ffSysctlGetInt(const char* propName, int defaultValue) { - int result; + alignas(int) uint8_t result[sizeof(int)]; size_t neededLength = sizeof(result); if (sysctlbyname(propName, &result, &neededLength, nullptr, 0) != 0) { return defaultValue; } - return result; + switch (neededLength) { + case sizeof(int32_t): + return *(int32_t*)result; + case sizeof(int16_t): + return *(int16_t*)result; + case sizeof(int8_t): + return *(int8_t*)result; + default: + return defaultValue; + } } int64_t ffSysctlGetInt64(const char* propName, int64_t defaultValue) { - int64_t result; + alignas(int64_t) uint8_t result[sizeof(int64_t)]; size_t neededLength = sizeof(result); if (sysctlbyname(propName, &result, &neededLength, nullptr, 0) != 0) { return defaultValue; } - return result; -} -#endif // OpenBSD - -void* ffSysctlGetData(int* request, u_int requestLength, size_t* resultLength) { - if (sysctl(request, requestLength, nullptr, resultLength, nullptr, 0) != 0) { - return nullptr; + switch (neededLength) { + case sizeof(int64_t): + return *(int64_t*)result; + case sizeof(int32_t): + return *(int32_t*)result; + case sizeof(int16_t): + return *(int16_t*)result; + case sizeof(int8_t): + return *(int8_t*)result; + default: + return defaultValue; } - - void* data = malloc(*resultLength); - - if (sysctl(request, requestLength, data, resultLength, nullptr, 0) != 0) { - free(data); - return nullptr; - } - - return data; } +#endif // OpenBSD diff --git a/src/common/option.h b/src/common/option.h index e2697042c6..0afaa449d1 100644 --- a/src/common/option.h +++ b/src/common/option.h @@ -18,10 +18,35 @@ typedef struct FFModuleFormatArgList { #define FF_FORMAT_ARG_LIST(list) { .args = list, .count = sizeof(list) / sizeof(FFModuleFormatArg) } +typedef struct FFModuleDisplayName { + const char* en; // English + + const char* ar; // Arabic + const char* cs; // Czech + const char* de; // German + const char* es; // Spanish + const char* fr; // French + const char* gl; // Galician + const char* he; // Hebrew + const char* id; // Indonesian + const char* it; // Italian + const char* ja; // Japanese + const char* ko; // Korean + const char* pl; // Polish + const char* pt; // (Brazilian) Portuguese + const char* ru; // Russian + const char* tr; // Turkish + const char* uk; // Ukrainian + const char* vi; // Vietnamese + const char* zh_CN; // Simplified Chinese + const char* zh_TW; // Traditional Chinese +} FFModuleDisplayName; + // Must be the first field of FFModuleOptions typedef struct FFModuleBaseInfo { const char* name; const char* description; + FFModuleDisplayName displayName; // 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 @@ -94,3 +119,5 @@ static inline void ffOptionDestroyModuleArg(FFModuleArgs* args) { } enum { FF_OPTION_MAX_SIZE = 1 << 8 }; // Maximum size of a single option value, used for static allocation + +#define FF_MODULE_GET_DISPLAY_NAME(moduleName) (*(const char**) ((uint8_t*) &ff ## moduleName ## ModuleInfo.displayName + instance.config.display.keyLanguage)) diff --git a/src/common/path.h b/src/common/path.h index 705b1c0948..0d292d8689 100644 --- a/src/common/path.h +++ b/src/common/path.h @@ -2,6 +2,7 @@ #include "common/FFstrbuf.h" #include "common/strutil.h" +#include "fastfetch_config.h" const char* ffFindExecutableInPath(const char* name, FFstrbuf* result); static inline bool ffIsAbsolutePath(const char* path) { @@ -19,3 +20,18 @@ char* realpath(const char* __restrict file_name, char* __restrict resolved_name ssize_t freadlink(void* hFile, char* buf, size_t bufsiz); ssize_t readlink(const char* path, char* buf, size_t bufsiz); #endif + +#ifdef __FreeBSD__ + #include + #ifdef _PATH_LOCALBASE + #define FF_PATH_PKG_BASE _PATH_LOCALBASE + #else + #define FF_PATH_PKG_BASE "/usr/local" + #endif +#elif __OpenBSD__ + #define FF_PATH_PKG_BASE "/usr/local" +#elif __NetBSD__ + #define FF_PATH_PKG_BASE "/usr/pkg" +#else + #define FF_PATH_PKG_BASE FASTFETCH_TARGET_DIR_USR +#endif \ No newline at end of file diff --git a/src/common/sysctl.h b/src/common/sysctl.h index 7e59ec663c..9d12df6fcb 100644 --- a/src/common/sysctl.h +++ b/src/common/sysctl.h @@ -14,4 +14,3 @@ const char* ffSysctlGetString(const char* propName, FFstrbuf* result); [[nodiscard]] int ffSysctlGetInt(const char* propName, int defaultValue); [[nodiscard]] int64_t ffSysctlGetInt64(const char* propName, int64_t defaultValue); #endif -[[nodiscard]] void* ffSysctlGetData(int* request, u_int requestLength, size_t* resultLength); diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h index 730a75ba81..ceecbf9c86 100644 --- a/src/common/windows/nt.h +++ b/src/common/windows/nt.h @@ -11,6 +11,7 @@ enum { SystemFirmwareTableInformation = 76, SystemBootEnvironmentInformation = 90, SystemLogicalProcessorAndGroupInformation = 107, + SystemBasicPerformanceInformation = 123, SystemSecureBootInformation = 146, }; @@ -169,7 +170,7 @@ NTSTATUS NTAPI NtQuerySystemEnvironmentValueEx( ); NTSTATUS NTAPI RtlGUIDFromString(IN PCUNICODE_STRING GuidString, OUT GUID* Guid); -NTSTATUS NTAPI RtlStringFromGUIDEx(IN GUID* Guid, OUT PCUNICODE_STRING GuidString, _In_ BOOLEAN AllocateGuidString); +NTSTATUS NTAPI RtlStringFromGUIDEx(IN const GUID* Guid, OUT PUNICODE_STRING GuidString, IN BOOLEAN AllocateGuidString); typedef struct _SYSTEM_SECUREBOOT_INFORMATION { BOOLEAN SecureBootEnabled; @@ -198,6 +199,13 @@ typedef struct _SYSTEM_FIRMWARE_TABLE_INFORMATION { _Field_size_bytes_(TableBufferLength) UCHAR TableBuffer[]; } SYSTEM_FIRMWARE_TABLE_INFORMATION, *PSYSTEM_FIRMWARE_TABLE_INFORMATION; +typedef struct _SYSTEM_BASIC_PERFORMANCE_INFORMATION { + SIZE_T AvailablePages; + SIZE_T CommittedPages; + SIZE_T CommitLimit; + SIZE_T PeakCommitment; +} SYSTEM_BASIC_PERFORMANCE_INFORMATION, *PSYSTEM_BASIC_PERFORMANCE_INFORMATION; + NTSYSAPI NTSTATUS NTAPI NtDelayExecution(_In_ BOOLEAN Alertable, _In_ PLARGE_INTEGER DelayInterval); /** @@ -599,6 +607,54 @@ typedef struct _KUSER_SHARED_DATA { }; }; + // + // Reserved padding field to preserve structure alignment and compatibility. + // + + ULONG DataFlagsPad[1]; + + // + // Depending on the processor, the code for fast system call will differ, + // Stub code is provided pointers below to access the appropriate code. + // + // N.B. The following field is only used on 32-bit systems. + // + + ULONGLONG TestRetInstruction; + + // + // Query-performance counter (QPC) frequency, in counts per second. + // + // N.B. This value represents the fixed frequency of the system's high-resolution + // performance counter. It is used by user-mode time routines to convert QPC + // ticks into elapsed time without requiring a system call. The frequency is + // constant for the lifetime of the system and reflects the hardware or + // virtualized timer source selected by the kernel. + // + + LONGLONG QpcFrequency; + + // + // On AMD64, this value is initialized to a nonzero value if the system + // operates with an altered view of the system service call mechanism. + // + + ULONG SystemCall; + + // + // Reserved field - do not use. Used to be UserCetAvailableEnvironments. + // + + ULONG Reserved2; + + // + // Full 64 bit version of the number of physical pages in the system. + // This can dynamically change as physical memory can be added or removed + // from a running system. + // + + ULONGLONG FullNumberOfPhysicalPages; + // ... more fields follow, but we don't need them } KUSER_SHARED_DATA, *PKUSER_SHARED_DATA; diff --git a/src/detection/battery/battery_sunos.c b/src/detection/battery/battery_sunos.c new file mode 100644 index 0000000000..e31b7bb9f4 --- /dev/null +++ b/src/detection/battery/battery_sunos.c @@ -0,0 +1,113 @@ +#include "battery.h" + +#include + +static inline void kstatFreeWrap(kstat_ctl_t** pkc) { + assert(pkc); + if (*pkc) { + kstat_close(*pkc); + } +} + +static bool getUint32(kstat_t* ksp, const char* name, uint32_t* value) { + kstat_named_t* stat = kstat_data_lookup(ksp, name); + if (!stat) { + return false; + } + + switch (stat->data_type) { + case KSTAT_DATA_UINT32: + *value = stat->value.ui32; + return true; + case KSTAT_DATA_INT32: + if (stat->value.i32 >= 0) { + *value = (uint32_t) stat->value.i32; + return true; + } + break; +#ifdef _INT64_TYPE + case KSTAT_DATA_UINT64: + if (stat->value.ui64 <= UINT32_MAX) { + *value = (uint32_t) stat->value.ui64; + return true; + } + break; + case KSTAT_DATA_INT64: + if (stat->value.i64 >= 0 && (uint64_t) stat->value.i64 <= UINT32_MAX) { + *value = (uint32_t) stat->value.i64; + return true; + } + break; +#endif + default: + break; + } + + return false; +} + +const char* ffDetectBattery([[maybe_unused]] FFBatteryOptions* options, FFlist* results) { + // https://github.com/illumos/illumos-gate/blob/master/usr/src/cmd/powertop/common/battery.c + [[gnu::cleanup(kstatFreeWrap)]] kstat_ctl_t* kc = kstat_open(); + if (!kc) { + return "kstat_open() failed"; + } + + // illumos exposes battery information through either of these modules. + const char* modules[] = { "battery", "acpi_drv" }; + kstat_t* bif = nullptr; + kstat_t* bst = nullptr; + for (size_t i = 0; i < ARRAY_SIZE(modules); ++i) { + bif = kstat_lookup(kc, modules[i], 0, "battery BIF0"); + bst = kstat_lookup(kc, modules[i], 0, "battery BST0"); + if (bif && bst) { + break; + } + bif = nullptr; + bst = nullptr; + } + + if (!bif || !bst || kstat_read(kc, bif, nullptr) < 0 || kstat_read(kc, bst, nullptr) < 0) { + return nullptr; + } + + uint32_t lastCapacity, remainingCapacity, rate, state; + if (!getUint32(bif, "bif_last_cap", &lastCapacity) || + !getUint32(bst, "bst_rem_cap", &remainingCapacity) || + !getUint32(bst, "bst_state", &state)) { + return "kstat_data_lookup() failed"; + } + + if (lastCapacity == 0 || remainingCapacity == UINT32_MAX) { + return nullptr; + } + + FFBatteryResult* battery = FF_LIST_ADD(FFBatteryResult, *results); + ffStrbufInit(&battery->manufacturer); + ffStrbufInit(&battery->manufactureDate); + ffStrbufInit(&battery->modelName); + ffStrbufInit(&battery->technology); + ffStrbufInit(&battery->serial); + battery->status = FF_BATTERY_STATUS_NONE; + battery->capacity = (double) remainingCapacity * 100.0 / (double) lastCapacity; + battery->temperature = FF_BATTERY_TEMP_UNSET; + battery->cycleCount = 0; + battery->timeRemaining = -1; + + // illumos BST state: bit 0 discharging, bit 1 charging, bit 2 critical. + if (state & (1u << 0)) { + battery->status |= FF_BATTERY_STATUS_DISCHARGING; + } + if (state & (1u << 1)) { + battery->status |= FF_BATTERY_STATUS_CHARGING; + } + if (state & (1u << 2)) { + battery->status |= FF_BATTERY_STATUS_CRITICAL; + } + + if ((state & (1u << 0)) && getUint32(bst, "bst_rate", &rate) && rate > 0 && rate != UINT32_MAX) { + battery->timeRemaining = (int32_t) (((uint64_t) remainingCapacity * 3600) / rate); + } + + return nullptr; +} diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c index 720d5cbd3b..cbede5d1ef 100644 --- a/src/detection/battery/battery_windows.c +++ b/src/detection/battery/battery_windows.c @@ -1,6 +1,7 @@ #define INITGUID #include "battery.h" +#include "common/endian.h" #include "common/debug.h" #include "common/mallocHelper.h" @@ -127,29 +128,24 @@ static void detectStaticData(FFlist* entries, FFlist* results) { FFBatteryWmiEntry* entry = getBatteryEntry(entries, results, data->Tag); FF_DEBUG("chemistry: %.4s", (const char*) &data->Chemistry); -#if __BIG_ENDIAN__ - #define htobe32(x) (x) // Should not happen on Windows, but just in case -#else - #define htobe32(x) __builtin_bswap32(x) -#endif switch (data->Chemistry) { - case htobe32('PbAc'): + case FF_READ_BE((uint32_t) 'PbAc'): ffStrbufSetStatic(&entry->result->technology, "Lead Acid"); break; - case htobe32('LION'): - case htobe32('Li-I'): + case FF_READ_BE((uint32_t) 'LION'): + case FF_READ_BE((uint32_t) 'Li-I'): ffStrbufSetStatic(&entry->result->technology, "Lithium Ion"); break; - case htobe32('NiCd'): + case FF_READ_BE((uint32_t) 'NiCd'): ffStrbufSetStatic(&entry->result->technology, "Nickel Cadmium"); break; - case htobe32('NiMH'): + case FF_READ_BE((uint32_t) 'NiMH'): ffStrbufSetStatic(&entry->result->technology, "Nickel Metal Hydride"); break; - case htobe32('NiZn'): + case FF_READ_BE((uint32_t) 'NiZn'): ffStrbufSetStatic(&entry->result->technology, "Nickel Zinc"); break; - case htobe32('RAM\0'): + case FF_READ_BE((uint32_t) 'RAM\0'): ffStrbufSetStatic(&entry->result->technology, "Rechargeable Alkaline-Manganese"); break; default: diff --git a/src/detection/chassis/chassis_linux.c b/src/detection/chassis/chassis_linux.c index 686fc67be9..e23f73463e 100644 --- a/src/detection/chassis/chassis_linux.c +++ b/src/detection/chassis/chassis_linux.c @@ -1,4 +1,5 @@ #include "chassis.h" +#include "common/endian.h" #include "common/io.h" #include "common/smbios.h" @@ -21,7 +22,7 @@ const char* ffDetectChassis(FFChassisResult* result) { uint32_t chassisType = 0; if (ffReadFileData("/sys/firmware/devicetree/base/smbios/smbios/chassis/chassis-type", sizeof(chassisType), &chassisType)) // big endian { - chassisType = __builtin_bswap32(chassisType); + chassisType = FF_READ_BE(chassisType); const char* typeStr = ffChassisTypeToString(chassisType); if (typeStr) { ffStrbufSetStatic(&result->type, typeStr); diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index e41aeabe8a..589d0ac01b 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -214,9 +214,15 @@ static void detectQualcomm(FFCPUResult* cpu) { const char* name = nullptr; switch (code) { + case 8975: + name = "8 Elite Extreme Gen 6"; + break; + case 8950: + name = "8 Elite Gen 6"; + break; case 8845: name = "8 Gen 5"; - break; // ? + break; case 8850: name = "8 Elite Gen 5"; break; @@ -238,6 +244,9 @@ static void detectQualcomm(FFCPUResult* cpu) { case 8475: name = "8+ Gen 1"; break; + case 8425: + name = "8+ Gen 1 4G"; + break; case 8450: name = "8 Gen 1"; break; @@ -271,12 +280,18 @@ static void detectQualcomm(FFCPUResult* cpu) { case 6475: name = "6 Gen 3"; break; + case 6225: + name = "6s 4G Gen 2"; + break; case 6115: - name = "6s Gen 1"; + name = "6s 4G Gen 1"; break; case 6450: name = "6 Gen 1"; break; + case 4850: + name = "4 Gen 5"; + break; case 4635: name = "4s Gen 2"; break; @@ -305,6 +320,9 @@ static void detectMediaTek(FFCPUResult* cpu) { switch (code) // The SOC code of MTK Dimensity series is full of mess { + case 6995: + name = "9600"; + break; case 6993: name = "9500"; break; @@ -357,6 +375,9 @@ static void detectExynos(FFCPUResult* cpu) { const char* name = nullptr; switch (code) { + case 9975: + name = "2700"; + break; case 9965: name = "2600"; break; @@ -374,6 +395,9 @@ static void detectExynos(FFCPUResult* cpu) { name = "2100"; break; + case 8865: + name = "1680"; + break; case 8855: name = "1580"; break; @@ -813,6 +837,46 @@ static const char* detectPhysicalCores(FFCPUResult* cpu) { return nullptr; } +[[maybe_unused]] static bool detectSnapdragonX(FFstrbuf* name, const char* model) { + // SoC models are enumerated in the form of `x[e|p]`, e.g. `x1e80100`, + // which is marketed as `X1E-80-100`, where `e` stands for Elite and `p` for Plus. + // https://en.wikipedia.org/wiki/List_of_Qualcomm_Snapdragon_systems_on_chips#Snapdragon_X_series + const char* prefix; + uint32_t prefixLen; + + if (ffStrStartsWith(model, "x1e")) { + prefix = "Qualcomm Snapdragon X Elite"; + prefixLen = 3; + } else if (ffStrStartsWith(model, "x1p")) { + prefix = "Qualcomm Snapdragon X Plus"; + prefixLen = 3; + } else if (ffStrStartsWith(model, "x2e")) { + // Only X2E-94-100 and X2E-96-100 are branded as Extreme + prefix = model[3] == '9' ? "Qualcomm Snapdragon X2 Elite Extreme" : "Qualcomm Snapdragon X2 Elite"; + prefixLen = 3; + } else if (ffStrStartsWith(model, "x2p")) { + prefix = "Qualcomm Snapdragon X2 Plus"; + prefixLen = 3; + } else if (ffStrStartsWith(model, "x1")) { + prefix = "Qualcomm Snapdragon X"; + prefixLen = 2; + } else { + return false; + } + + uint32_t length = (uint32_t) strlen(model); + bool splitCode = length - prefixLen == 5; + + ffStrbufSetF(name, "%s X", prefix); + for (uint32_t i = 1; i < length; ++i) { + if (i == prefixLen || (splitCode && i == length - 3)) { + ffStrbufAppendC(name, '-'); + } + ffStrbufAppendC(name, (char) toupper(model[i])); + } + return true; +} + [[maybe_unused]] static void parseIsa(FFstrbuf* cpuIsa) { // Always use the last part of the ISA string. Ref: #590 #1204 ffStrbufSubstrAfterLastC(cpuIsa, ' '); @@ -914,22 +978,19 @@ static const char* detectPhysicalCores(FFCPUResult* cpu) { } #endif else if (ffStrEquals(vendor, "qcom")) { - // https://elixir.bootlin.com/linux/v6.11/source/arch/arm64/boot/dts/qcom - if (ffStrStartsWith(model, "x")) { - ffStrbufSetS(&cpu->name, "Qualcomm Snapdragon X Elite "); - for (const char* p = model + 1; *p; ++p) { - ffStrbufAppendC(&cpu->name, (char) toupper(*p)); - } - } else if (ffStrStartsWith(model, "sc")) { - const char* code = model + 2; - uint32_t deviceId = (uint32_t) strtoul(code, nullptr, 10); - ffStrbufSetStatic(&cpu->name, ffCPUQualcommCodeToName(deviceId)); - if (!cpu->name.length) { - ffStrbufAppendS(&cpu->name, "Qualcomm Snapdragon SC"); - ffStrbufAppendS(&cpu->name, code); + // https://elixir.bootlin.com/linux/latest/source/arch/arm64/boot/dts/qcom + if (!detectSnapdragonX(&cpu->name, model)) { + if (ffStrStartsWith(model, "sc")) { + const char* code = model + 2; + uint32_t deviceId = (uint32_t) strtoul(code, nullptr, 10); + ffStrbufSetStatic(&cpu->name, ffCPUQualcommCodeToName(deviceId)); + if (!cpu->name.length) { + ffStrbufAppendS(&cpu->name, "Qualcomm Snapdragon SC"); + ffStrbufAppendS(&cpu->name, code); + } + } else { + ffStrbufSetS(&cpu->name, model); } - } else { - ffStrbufSetS(&cpu->name, model); } ffStrbufSetStatic(&cpu->vendor, "Qualcomm"); diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index 82b656e4f0..8975775d07 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -1,4 +1,5 @@ #include "cpu.h" +#include "common/endian.h" #include "common/windows/registry.h" #include "common/windows/nt.h" #include "common/mallocHelper.h" @@ -205,7 +206,7 @@ static const char* detectMaxSpeedBySmbios(FFCPUResult* cpu) { } } - uint32_t speed = data->MaxSpeed; + uint32_t speed = FF_READ_LE(data->MaxSpeed); // Sometimes SMBIOS reports invalid value. We assume that max speed is small than 2x of base if (speed < cpu->frequencyBase || speed > cpu->frequencyBase * 2) { return "Possible invalid CPU max speed in SMBIOS data. See #800"; diff --git a/src/detection/cpucache/cpucache_shared.c b/src/detection/cpucache/cpucache_shared.c index 58419a9632..f8888313ae 100644 --- a/src/detection/cpucache/cpucache_shared.c +++ b/src/detection/cpucache/cpucache_shared.c @@ -1,4 +1,5 @@ #include "cpucache.h" +#include "common/endian.h" #include "common/smbios.h" #include "common/strutil.h" @@ -43,24 +44,26 @@ const char* ffDetectCPUCache(FFCPUCacheResult* result) { continue; } - bool enabled = !!(data->CacheConfiguration & (1 << 7)); + uint16_t cacheConfiguration = FF_READ_LE(data->CacheConfiguration); + uint16_t installedSize = FF_READ_LE(data->InstalledSize); + bool enabled = !!(cacheConfiguration & (1 << 7)); if (!enabled) { continue; } - uint32_t size = data->InstalledSize; + uint32_t size = installedSize; if (size == 0) { continue; } - if (data->InstalledSize != 0xFFFF) { + if (installedSize != 0xFFFF) { size *= (size >> 15 ? 64 : 1) * 1024u; } else if (data->Header.Length > offsetof(FFSmbiosCacheInfo, InstalledCacheSize2)) { - size = data->InstalledCacheSize2; + size = FF_READ_LE(data->InstalledCacheSize2); size *= (size >> 31 ? 64 : 1) * 1024u; } - uint32_t level = (data->CacheConfiguration & 0b111u) + 1; + uint32_t level = (cacheConfiguration & 0b111u) + 1; FFCPUCacheType type; switch (data->SystemCacheType) { diff --git a/src/detection/de/de_linux.c b/src/detection/de/de_linux.c index e79c3f4183..719d81fe83 100644 --- a/src/detection/de/de_linux.c +++ b/src/detection/de/de_linux.c @@ -11,29 +11,12 @@ #include "detection/displayserver/displayserver.h" #include -#ifdef __FreeBSD__ - #include - #ifndef _PATH_LOCALBASE - #define _PATH_LOCALBASE "/usr/local" - #endif -#elif __OpenBSD__ - #define _PATH_LOCALBASE "/usr/local" -#elif __NetBSD__ - #define _PATH_LOCALBASE "/usr/pkg" -#endif static void getKDE(FFstrbuf* result, [[maybe_unused]] FFDEOptions* options) { -#ifdef _PATH_LOCALBASE - ffParsePropFile(_PATH_LOCALBASE "/share/wayland-sessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result); + ffParsePropFile(FF_PATH_PKG_BASE "/share/wayland-sessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result); if (result->length == 0) { - ffParsePropFile(_PATH_LOCALBASE "/share/xsessions/plasmax11.desktop", "X-KDE-PluginInfo-Version =", result); + ffParsePropFile(FF_PATH_PKG_BASE "/share/xsessions/plasmax11.desktop", "X-KDE-PluginInfo-Version =", result); } -#else - ffParsePropFile(FASTFETCH_TARGET_DIR_USR "/share/wayland-sessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result); - if (result->length == 0) { - ffParsePropFile(FASTFETCH_TARGET_DIR_USR "/share/xsessions/plasmax11.desktop", "X-KDE-PluginInfo-Version =", result); - } -#endif if (result->length == 0) { ffParsePropFileData("xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result); diff --git a/src/detection/diskio/diskio.c b/src/detection/diskio/diskio.c index 73f0075a75..a2f53439c1 100644 --- a/src/detection/diskio/diskio.c +++ b/src/detection/diskio/diskio.c @@ -72,8 +72,7 @@ const char* ffDetectDiskIO(FFlist* result, FFDiskIOOptions* options) { 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 */; + *currValue = (*currValue - *prevValue) * 1000 / (time2 - time1); // Calculate per second // For next function call *prevValue = temp; diff --git a/src/detection/displayserver/displayserver_android.c b/src/detection/displayserver/displayserver_android.c index 13f5fab0ef..4e666eb1d3 100644 --- a/src/detection/displayserver/displayserver_android.c +++ b/src/detection/displayserver/displayserver_android.c @@ -174,6 +174,11 @@ static bool detectDE(FFDisplayServerResult* ds) { ffStrbufPrependS(&ds->dePrettyName, "OxygenOS"); return true; } + if (ffSettingsGetAndroidProperty("ro.product.brand", &ds->dePrettyName) && ffStrbufEqualS(&ds->dePrettyName, "asus")) { + ffStrbufClear(&ds->dePrettyName); + ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName); + return true; + } if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName)) { if (ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS")) { ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' '); @@ -187,13 +192,23 @@ static bool detectDE(FFDisplayServerResult* ds) { } void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) { - const char* error = ffdsConnectXcbRandr(ds); - if (error) { - error = ffdsConnectXrandr(ds); - } - if (!error) { - ffdsDetectWMDE(ds); - return; + // Keep the same display-server preference as Linux: Wayland provides the + // richest output information, followed by XCB and XRandR. + if (instance.config.general.dsForceDrm == FF_DS_FORCE_DRM_TYPE_FALSE) { + ffdsConnectWayland(ds); + + if (ds->displays.length == 0) { + ffdsConnectXcbRandr(ds); + } + + if (ds->displays.length == 0) { + ffdsConnectXrandr(ds); + } + + if (ds->displays.length > 0) { + ffdsDetectWMDE(ds); + return; + } } // https://source.android.com/docs/core/graphics/surfaceflinger-windowmanager diff --git a/src/detection/displayserver/linux/wmde.c b/src/detection/displayserver/linux/wmde.c index 8044c4a95a..6984d1d0c3 100644 --- a/src/detection/displayserver/linux/wmde.c +++ b/src/detection/displayserver/linux/wmde.c @@ -7,6 +7,7 @@ #include #include #include +#include #if __FreeBSD__ #include @@ -376,48 +377,60 @@ static const char* getFromProcesses(FFDisplayServerResult* result) { } FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreateA(64); - ffStrbufAppendS(&procPath, "/proc/"); - - uint32_t procPathLength = procPath.length; - - FF_STRBUF_AUTO_DESTROY loginuid = ffStrbufCreate(); - FF_STRBUF_AUTO_DESTROY processName = ffStrbufCreateA(256); // Some processes have large command lines (looking at you chrome) + int procfd = dirfd(procdir); struct dirent* dirent; while ((dirent = readdir(procdir)) != nullptr) { // Match only folders starting with a number (the pid folders) - if (dirent->d_type != DT_DIR || !ffCharIsDigit(dirent->d_name[0])) { + if ((dirent->d_type != DT_DIR && dirent->d_type != DT_UNKNOWN) || !ffCharIsDigit(dirent->d_name[0])) { continue; } - ffStrbufAppendS(&procPath, dirent->d_name); + ffStrbufSetS(&procPath, dirent->d_name); uint32_t procFolderPathLength = procPath.length; // Don't check for processes not owned by the current user. + char loginuid[32]; ffStrbufAppendS(&procPath, "/loginuid"); - ffReadFileBuffer(procPath.chars, &loginuid); - if (ffStrbufToUInt(&loginuid, (uint64_t) -1) != userId) { - ffStrbufSubstrBefore(&procPath, procPathLength); + ssize_t bytesRead = ffReadFileDataRelative(procfd, procPath.chars, sizeof(loginuid) - 1, loginuid); + if (bytesRead <= 0) { + continue; + } + loginuid[bytesRead] = '\0'; + if (strtol(loginuid, nullptr, 10) != (long) userId) { continue; } ffStrbufSubstrBefore(&procPath, procFolderPathLength); - // We check the cmdline for the process name, because it is not trimmed. - ffStrbufAppendS(&procPath, "/cmdline"); - ffReadFileBuffer(procPath.chars, &processName); - ffStrbufTrimRightSpace(&processName); - ffStrbufSubstrBeforeFirstC(&processName, '\0'); // Trim the arguments - ffStrbufSubstrAfterLastC(&processName, '/'); - - ffStrbufSubstrBefore(&procPath, procPathLength); + // We check the realpath for the process name + ffStrbufAppendS(&procPath, "/exe"); + char exePath[PATH_MAX]; + ssize_t exePathLength = readlinkat(procfd, procPath.chars, exePath, sizeof(exePath) - 1); + const char* processName; + if (exePathLength >= 0) { + exePath[exePathLength] = '\0'; + processName = basename(exePath); + } else { + ffStrbufSubstrBefore(&procPath, procFolderPathLength); + ffStrbufAppendS(&procPath, "/comm"); + exePathLength = ffReadFileDataRelative(procfd, procPath.chars, sizeof(exePath) - 1, exePath); + if (exePathLength <= 0) { + continue; + } + if (exePath[exePathLength - 1] == '\n') { + --exePathLength; + } + exePath[exePathLength] = '\0'; + processName = exePath; + } if (result->dePrettyName.length == 0) { - applyPrettyNameIfDE(result, processName.chars); + applyPrettyNameIfDE(result, processName); } if (result->wmPrettyName.length == 0) { - applyNameIfWM(result, processName.chars); + applyNameIfWM(result, processName); } if (result->dePrettyName.length > 0 && result->wmPrettyName.length > 0) { diff --git a/src/detection/displayserver/linux/xcb.c b/src/detection/displayserver/linux/xcb.c index f3c3711370..32b20ab27e 100644 --- a/src/detection/displayserver/linux/xcb.c +++ b/src/detection/displayserver/linux/xcb.c @@ -331,7 +331,7 @@ static bool xcbRandrHandleMonitors(XcbRandrData* data, xcb_screen_t* screen) { } static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen) { - // With all the initialisation done, start the detection + // With all the initialization done, start the detection if (xcbRandrHandleMonitors(data, screen)) { return; } diff --git a/src/detection/gpu/gpu_apple.m b/src/detection/gpu/gpu_apple.m index 24ede6de2c..9596dbe76d 100644 --- a/src/detection/gpu/gpu_apple.m +++ b/src/detection/gpu/gpu_apple.m @@ -14,74 +14,68 @@ #define MTLFeatureSet_macOS_GPUFamily2_v1 ((MTLFeatureSet) 10005) #endif -const char* ffGpuDetectDriverVersion(FFlist* gpus) -{ - if (@available(macOS 10.7, *)) - { - NSMutableArray* arr = NSMutableArray.new; - FF_LIST_FOR_EACH(FFGPUResult, x, *gpus) - [arr addObject:@(x->driver.chars)]; +const char* ffGpuDetectDriverVersion(FFlist* gpus) { + NSMutableArray* arr = NSMutableArray.new; + FF_LIST_FOR_EACH (FFGPUResult, x, *gpus) { + [arr addObject:@(x->driver.chars)]; + } - NSDictionary* dict = CFBridgingRelease(KextManagerCopyLoadedKextInfo((__bridge CFArrayRef)arr, (__bridge CFArrayRef)@[@"CFBundleVersion"])); - FF_LIST_FOR_EACH(FFGPUResult, x, *gpus) - { - NSString* version = dict[@(x->driver.chars)][@"CFBundleVersion"]; - if (version) - { - ffStrbufAppendC(&x->driver, ' '); - ffStrbufAppendS(&x->driver, version.UTF8String); - } + NSDictionary* dict = CFBridgingRelease(KextManagerCopyLoadedKextInfo((__bridge CFArrayRef) arr, (__bridge CFArrayRef) @[ @"CFBundleVersion" ])); + FF_LIST_FOR_EACH (FFGPUResult, x, *gpus) { + NSString* version = dict[@(x->driver.chars)][@"CFBundleVersion"]; + if (version) { + ffStrbufAppendC(&x->driver, ' '); + ffStrbufAppendS(&x->driver, version.UTF8String); } - return nullptr; } - return "Unsupported macOS version"; + return nullptr; } -const char* ffGpuDetectMetal(FFlist* gpus) -{ - if (@available(macOS 10.13, *)) - { - for (id device in MTLCopyAllDevices()) - { - FFGPUResult* gpu = nullptr; - FF_LIST_FOR_EACH(FFGPUResult, x, *gpus) - { - if (x->deviceId == device.registryID) - { - gpu = x; - break; - } +const char* ffGpuDetectMetal(FFlist* gpus) { + // Metal.framework is statically linked into the application, + // so `MTLCopyAllDevices` is always available, or the application will not start. + for (id device in MTLCopyAllDevices()) { + FFGPUResult* gpu = nullptr; + FF_LIST_FOR_EACH (FFGPUResult, x, *gpus) { + if (x->deviceId == device.registryID) { + gpu = x; + break; } - if (!gpu) continue; + } + if (!gpu) { + continue; + } - #ifndef MAC_OS_X_VERSION_10_15 - if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily2_v1]) - ffStrbufSetStatic(&gpu->platformApi, "Metal Feature Set 2"); - else if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1]) - ffStrbufSetStatic(&gpu->platformApi, "Metal Feature Set 1"); - #else // MAC_OS_X_VERSION_10_15 - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wunguarded-availability-new" - if ([device supportsFamily:MTLGPUFamilyMetal4]) - ffStrbufSetStatic(&gpu->platformApi, "Metal 4"); - else if ([device supportsFamily:MTLGPUFamilyMetal3]) - ffStrbufSetStatic(&gpu->platformApi, "Metal 3"); - #pragma clang diagnostic pop - else if ([device supportsFamily:MTLGPUFamilyCommon3]) - ffStrbufSetStatic(&gpu->platformApi, "Metal Common 3"); - else if ([device supportsFamily:MTLGPUFamilyCommon2]) - ffStrbufSetStatic(&gpu->platformApi, "Metal Common 2"); - else if ([device supportsFamily:MTLGPUFamilyCommon1]) - ffStrbufSetStatic(&gpu->platformApi, "Metal Common 1"); +#ifndef MAC_OS_X_VERSION_10_15 + if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily2_v1]) { + ffStrbufSetStatic(&gpu->platformApi, "Metal Feature Set 2"); + } else if ([device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1]) { + ffStrbufSetStatic(&gpu->platformApi, "Metal Feature Set 1"); + } +#else // MAC_OS_X_VERSION_10_15 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunguarded-availability-new" + if ([device supportsFamily:MTLGPUFamilyMetal4]) { + ffStrbufSetStatic(&gpu->platformApi, "Metal 4"); + } else if ([device supportsFamily:MTLGPUFamilyMetal3]) { + ffStrbufSetStatic(&gpu->platformApi, "Metal 3"); + } + #pragma clang diagnostic pop + else if ([device supportsFamily:MTLGPUFamilyCommon3]) { + ffStrbufSetStatic(&gpu->platformApi, "Metal Common 3"); + } else if ([device supportsFamily:MTLGPUFamilyCommon2]) { + ffStrbufSetStatic(&gpu->platformApi, "Metal Common 2"); + } else if ([device supportsFamily:MTLGPUFamilyCommon1]) { + ffStrbufSetStatic(&gpu->platformApi, "Metal Common 1"); + } - gpu->type = device.hasUnifiedMemory ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; - gpu->index = (uint32_t) device.locationNumber; + gpu->type = device.hasUnifiedMemory ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; + gpu->index = (uint32_t) device.locationNumber; - if (device.hasUnifiedMemory && device.recommendedMaxWorkingSetSize > 0) - gpu->shared.total = device.recommendedMaxWorkingSetSize; - #endif + if (device.hasUnifiedMemory && device.recommendedMaxWorkingSetSize > 0) { + gpu->shared.total = device.recommendedMaxWorkingSetSize; } - return nullptr; +#endif } - return "Metal API is not supported by this macOS version"; + return nullptr; } diff --git a/src/detection/gpu/gpu_pci.c b/src/detection/gpu/gpu_pci.c index c3772fd911..b1bd08836d 100644 --- a/src/detection/gpu/gpu_pci.c +++ b/src/detection/gpu/gpu_pci.c @@ -1,21 +1,10 @@ #include "gpu.h" #include "common/io.h" +#include "common/path.h" #include "common/properties.h" #include "common/memrchr.h" #include "common/strutil.h" -#include -#ifdef __FreeBSD__ - #include - #ifndef _PATH_LOCALBASE - #define _PATH_LOCALBASE "/usr/local" - #endif -#elif __OpenBSD__ - #define _PATH_LOCALBASE "/usr/local" -#elif __NetBSD__ - #define _PATH_LOCALBASE "/usr/pkg" -#endif - #if FF_HAVE_EMBEDDED_PCIIDS #include "fastfetch_pciids.c.inc" #endif @@ -46,9 +35,9 @@ static const FFstrbuf* loadPciIds() { } } #elif __OpenBSD__ || __FreeBSD__ || __NetBSD__ - ffReadFileBuffer(_PATH_LOCALBASE "/share/hwdata/pci.ids", &pciids); + ffReadFileBuffer(FF_PATH_PKG_BASE "/share/hwdata/pci.ids", &pciids); if (pciids.length == 0) { - ffReadFileBuffer(_PATH_LOCALBASE "/share/pciids/pci.ids", &pciids); + ffReadFileBuffer(FF_PATH_PKG_BASE "/share/pciids/pci.ids", &pciids); } #elif __sun ffReadFileBuffer(FASTFETCH_TARGET_DIR_ROOT "/usr/share/hwdata/pci.ids", &pciids); diff --git a/src/detection/gtk_qt/gtk.c b/src/detection/gtk_qt/gtk.c index 12aaeac143..7c9638dbdf 100644 --- a/src/detection/gtk_qt/gtk.c +++ b/src/detection/gtk_qt/gtk.c @@ -62,50 +62,63 @@ static void detectGTKFromSettings(FFGTKResult* result) { const FFDisplayServerResult* wmde = ffConnectDisplayServer(); - if (ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_XFCE4)) { - themeName = ffSettingsGetXFConf("xsettings", "/Net/ThemeName", FF_VARIANT_TYPE_STRING).strValue; - iconsName = ffSettingsGetXFConf("xsettings", "/Net/IconThemeName", FF_VARIANT_TYPE_STRING).strValue; - fontName = ffSettingsGetXFConf("xsettings", "/Gtk/FontName", FF_VARIANT_TYPE_STRING).strValue; - cursorTheme = ffSettingsGetXFConf("xsettings", "/Gtk/CursorThemeName", FF_VARIANT_TYPE_STRING).strValue; - cursorSize = ffSettingsGetXFConf("xsettings", "/Gtk/CursorThemeSize", FF_VARIANT_TYPE_INT).intValue; - wallpaper = ffSettingsGetXFConfFirstMatch("xfce4-desktop", "/backdrop/screen0", FF_VARIANT_TYPE_STRING, nullptr, testXfconfWallpaperPropKey).strValue; - } else if (ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_CINNAMON)) { - themeName = ffSettingsGetGnome("/org/cinnamon/desktop/interface/gtk-theme", "org.cinnamon.desktop.interface", nullptr, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue; - iconsName = ffSettingsGetGnome("/org/cinnamon/desktop/interface/icon-theme", "org.cinnamon.desktop.interface", nullptr, "icon-theme", FF_VARIANT_TYPE_STRING).strValue; - fontName = ffSettingsGetGnome("/org/cinnamon/desktop/interface/font-name", "org.cinnamon.desktop.interface", nullptr, "font-name", FF_VARIANT_TYPE_STRING).strValue; - cursorTheme = ffSettingsGetGnome("/org/cinnamon/desktop/interface/cursor-theme", "org.cinnamon.desktop.interface", nullptr, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue; - cursorSize = ffSettingsGetGnome("/org/cinnamon/desktop/interface/cursor-size", "org.cinnamon.desktop.interface", nullptr, "cursor-size", FF_VARIANT_TYPE_INT).intValue; - wallpaper = ffSettingsGetGnome("/org/cinnamon/desktop/background/picture-uri", "org.cinnamon.desktop.background", nullptr, "picture-uri", FF_VARIANT_TYPE_STRING).strValue; - } else if (ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_MATE)) { - themeName = ffSettingsGetGnome("/org/mate/interface/gtk-theme", "org.mate.interface", nullptr, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue; - iconsName = ffSettingsGetGnome("/org/mate/interface/icon-theme", "org.mate.interface", nullptr, "icon-theme", FF_VARIANT_TYPE_STRING).strValue; - fontName = ffSettingsGetGnome("/org/mate/interface/font-name", "org.mate.interface", nullptr, "font-name", FF_VARIANT_TYPE_STRING).strValue; - cursorTheme = ffSettingsGetGnome("/org/mate/peripherals-mouse/cursor-theme", "org.mate.peripherals-mouse", nullptr, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue; - cursorSize = ffSettingsGetGnome("/org/mate/peripherals-mouse/cursor-size", "org.mate.peripherals-mouse", nullptr, "cursor-size", FF_VARIANT_TYPE_INT).intValue; - wallpaper = ffSettingsGetGnome("/org/mate/desktop/background", "org.mate.background", nullptr, "picture-filename", FF_VARIANT_TYPE_STRING).strValue; - } else if ( - ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_GNOME) || - ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_GNOME_CLASSIC) || - ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_UNITY) || - ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_BUDGIE) || - ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_NEBIDE)) { - themeName = ffSettingsGetGnome("/org/gnome/desktop/interface/gtk-theme", "org.gnome.desktop.interface", nullptr, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue; - iconsName = ffSettingsGetGnome("/org/gnome/desktop/interface/icon-theme", "org.gnome.desktop.interface", nullptr, "icon-theme", FF_VARIANT_TYPE_STRING).strValue; - fontName = ffSettingsGetGnome("/org/gnome/desktop/interface/font-name", "org.gnome.desktop.interface", nullptr, "font-name", FF_VARIANT_TYPE_STRING).strValue; - cursorTheme = ffSettingsGetGnome("/org/gnome/desktop/interface/cursor-theme", "org.gnome.desktop.interface", nullptr, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue; - cursorSize = ffSettingsGetGnome("/org/gnome/desktop/interface/cursor-size", "org.gnome.desktop.interface", nullptr, "cursor-size", FF_VARIANT_TYPE_INT).intValue; - wallpaper = ffSettingsGetGnome("/org/gnome/desktop/background/picture-uri", "org.gnome.desktop.background", nullptr, "picture-uri", FF_VARIANT_TYPE_STRING).strValue; - } else if ( - ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_ENLIGHTENMENT)) { - ffEnlightenmentSettings settings = {}; - if (ffSettingsGetEnlightenmentProperty(&settings)) { - themeName = settings.theme; - iconsName = settings.icon_theme; - fontName = settings.font; - cursorTheme = settings.use_e_cursor ? "Enlightenment" : "Application"; - cursorSize = settings.cursor_size; - wallpaper = settings.desktop_default_background; + if (wmde->dePrettyName.length > 0) { + if (ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_XFCE4)) { + themeName = ffSettingsGetXFConf("xsettings", "/Net/ThemeName", FF_VARIANT_TYPE_STRING).strValue; + iconsName = ffSettingsGetXFConf("xsettings", "/Net/IconThemeName", FF_VARIANT_TYPE_STRING).strValue; + fontName = ffSettingsGetXFConf("xsettings", "/Gtk/FontName", FF_VARIANT_TYPE_STRING).strValue; + cursorTheme = ffSettingsGetXFConf("xsettings", "/Gtk/CursorThemeName", FF_VARIANT_TYPE_STRING).strValue; + cursorSize = ffSettingsGetXFConf("xsettings", "/Gtk/CursorThemeSize", FF_VARIANT_TYPE_INT).intValue; + wallpaper = ffSettingsGetXFConfFirstMatch("xfce4-desktop", "/backdrop/screen0", FF_VARIANT_TYPE_STRING, nullptr, testXfconfWallpaperPropKey).strValue; + } else if (ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_CINNAMON)) { + themeName = ffSettingsGetGnome("/org/cinnamon/desktop/interface/gtk-theme", "org.cinnamon.desktop.interface", nullptr, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue; + iconsName = ffSettingsGetGnome("/org/cinnamon/desktop/interface/icon-theme", "org.cinnamon.desktop.interface", nullptr, "icon-theme", FF_VARIANT_TYPE_STRING).strValue; + fontName = ffSettingsGetGnome("/org/cinnamon/desktop/interface/font-name", "org.cinnamon.desktop.interface", nullptr, "font-name", FF_VARIANT_TYPE_STRING).strValue; + cursorTheme = ffSettingsGetGnome("/org/cinnamon/desktop/interface/cursor-theme", "org.cinnamon.desktop.interface", nullptr, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue; + cursorSize = ffSettingsGetGnome("/org/cinnamon/desktop/interface/cursor-size", "org.cinnamon.desktop.interface", nullptr, "cursor-size", FF_VARIANT_TYPE_INT).intValue; + wallpaper = ffSettingsGetGnome("/org/cinnamon/desktop/background/picture-uri", "org.cinnamon.desktop.background", nullptr, "picture-uri", FF_VARIANT_TYPE_STRING).strValue; + } else if (ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_MATE)) { + themeName = ffSettingsGetGnome("/org/mate/interface/gtk-theme", "org.mate.interface", nullptr, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue; + iconsName = ffSettingsGetGnome("/org/mate/interface/icon-theme", "org.mate.interface", nullptr, "icon-theme", FF_VARIANT_TYPE_STRING).strValue; + fontName = ffSettingsGetGnome("/org/mate/interface/font-name", "org.mate.interface", nullptr, "font-name", FF_VARIANT_TYPE_STRING).strValue; + cursorTheme = ffSettingsGetGnome("/org/mate/peripherals-mouse/cursor-theme", "org.mate.peripherals-mouse", nullptr, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue; + cursorSize = ffSettingsGetGnome("/org/mate/peripherals-mouse/cursor-size", "org.mate.peripherals-mouse", nullptr, "cursor-size", FF_VARIANT_TYPE_INT).intValue; + wallpaper = ffSettingsGetGnome("/org/mate/desktop/background", "org.mate.background", nullptr, "picture-filename", FF_VARIANT_TYPE_STRING).strValue; + } else if ( + ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_GNOME) || + ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_GNOME_CLASSIC) || + ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_UNITY) || + ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_BUDGIE) || + ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_NEBIDE)) { + themeName = ffSettingsGetGnome("/org/gnome/desktop/interface/gtk-theme", "org.gnome.desktop.interface", nullptr, "gtk-theme", FF_VARIANT_TYPE_STRING).strValue; + iconsName = ffSettingsGetGnome("/org/gnome/desktop/interface/icon-theme", "org.gnome.desktop.interface", nullptr, "icon-theme", FF_VARIANT_TYPE_STRING).strValue; + fontName = ffSettingsGetGnome("/org/gnome/desktop/interface/font-name", "org.gnome.desktop.interface", nullptr, "font-name", FF_VARIANT_TYPE_STRING).strValue; + cursorTheme = ffSettingsGetGnome("/org/gnome/desktop/interface/cursor-theme", "org.gnome.desktop.interface", nullptr, "cursor-theme", FF_VARIANT_TYPE_STRING).strValue; + cursorSize = ffSettingsGetGnome("/org/gnome/desktop/interface/cursor-size", "org.gnome.desktop.interface", nullptr, "cursor-size", FF_VARIANT_TYPE_INT).intValue; + wallpaper = ffSettingsGetGnome("/org/gnome/desktop/background/picture-uri", "org.gnome.desktop.background", nullptr, "picture-uri", FF_VARIANT_TYPE_STRING).strValue; + } else if ( + ffStrbufIgnCaseEqualS(&wmde->dePrettyName, FF_DE_PRETTY_ENLIGHTENMENT)) { + ffEnlightenmentSettings settings = {}; + if (ffSettingsGetEnlightenmentProperty(&settings)) { + themeName = settings.theme; + iconsName = settings.icon_theme; + fontName = settings.font; + cursorTheme = settings.use_e_cursor ? "Enlightenment" : "Application"; + cursorSize = settings.cursor_size; + wallpaper = settings.desktop_default_background; + } } + } else { + // Standalone WMs (Hyprland, sway, niri, i3, ...) report no DE and have no settings + // daemon, but GTK apps on them still read org.gnome.desktop.interface. + // Read via DConf, not GSettings: GSettings synthesizes the schema default for keys + // the user never set, DConf returns nothing. Runs last; applyGTKSettings() only + // fills still-empty fields, so config files always win. + themeName = ffSettingsGetDConf("/org/gnome/desktop/interface/gtk-theme", FF_VARIANT_TYPE_STRING).strValue; + iconsName = ffSettingsGetDConf("/org/gnome/desktop/interface/icon-theme", FF_VARIANT_TYPE_STRING).strValue; + fontName = ffSettingsGetDConf("/org/gnome/desktop/interface/font-name", FF_VARIANT_TYPE_STRING).strValue; + cursorTheme = ffSettingsGetDConf("/org/gnome/desktop/interface/cursor-theme", FF_VARIANT_TYPE_STRING).strValue; + cursorSize = ffSettingsGetDConf("/org/gnome/desktop/interface/cursor-size", FF_VARIANT_TYPE_INT).intValue; } applyGTKSettings(result, themeName, iconsName, fontName, cursorTheme, cursorSize, wallpaper); diff --git a/src/detection/host/host_windows.c b/src/detection/host/host_windows.c index 13bf56e88d..a39a3041ef 100644 --- a/src/detection/host/host_windows.c +++ b/src/detection/host/host_windows.c @@ -1,4 +1,5 @@ #include "host.h" +#include "common/endian.h" #include "common/smbios.h" typedef struct [[gnu::packed]] FFSmbiosSystemInfo { @@ -52,7 +53,14 @@ const char* ffDetectHost(FFHostResult* host) { static_assert(offsetof(FFSmbiosSystemInfo, UUID) == 0x08, "FFSmbiosSystemInfo.UUID offset is wrong"); if (data->Header.Length > offsetof(FFSmbiosSystemInfo, UUID)) { - ffStrbufSetF(&host->uuid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", data->UUID.TimeLow, data->UUID.TimeMid, data->UUID.TimeHighAndVersion, data->UUID.ClockSeqHiAndReserved, data->UUID.ClockSeqLow, data->UUID.Node[0], data->UUID.Node[1], data->UUID.Node[2], data->UUID.Node[3], data->UUID.Node[4], data->UUID.Node[5]); + ffStrbufSetF(&host->uuid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + FF_READ_LE(data->UUID.TimeLow), + FF_READ_LE(data->UUID.TimeMid), + FF_READ_LE(data->UUID.TimeHighAndVersion), + data->UUID.ClockSeqHiAndReserved, + data->UUID.ClockSeqLow, + data->UUID.Node[0], data->UUID.Node[1], data->UUID.Node[2], + data->UUID.Node[3], data->UUID.Node[4], data->UUID.Node[5]); } static_assert(offsetof(FFSmbiosSystemInfo, SKUNumber) == 0x19, "FFSmbiosSystemInfo.SKUNumber offset is wrong"); diff --git a/src/detection/lm/lm.h b/src/detection/lm/lm.h index d30229a79f..d2c3299f6d 100644 --- a/src/detection/lm/lm.h +++ b/src/detection/lm/lm.h @@ -5,7 +5,7 @@ typedef struct FFLMResult { FFstrbuf service; - FFstrbuf type; + FFstrbuf prettyName; FFstrbuf version; } FFLMResult; diff --git a/src/detection/lm/lm_apple.m b/src/detection/lm/lm_apple.m new file mode 100644 index 0000000000..d600058d47 --- /dev/null +++ b/src/detection/lm/lm_apple.m @@ -0,0 +1,18 @@ +#include "lm.h" + +#import + +const char* ffDetectLM(FFLMResult* result) { + ffStrbufSetStatic(&result->service, "loginwindow"); + ffStrbufSetStatic(&result->prettyName, "Login Window"); + + NSError* error; + NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@"/System/Library/CoreServices/loginwindow.app/Contents/Info.plist" isDirectory:NO] + error:&error]; + + if (dict) { + ffStrbufSetS(&result->version, ((NSString*) dict[@"CFBundleShortVersionString"]).UTF8String); + } + + return nullptr; +} diff --git a/src/detection/lm/lm_linux.c b/src/detection/lm/lm_linux.c index 1623c72775..e4f18c19e0 100644 --- a/src/detection/lm/lm_linux.c +++ b/src/detection/lm/lm_linux.c @@ -1,18 +1,35 @@ #include "lm.h" +#include "common/io.h" +#include "common/mallocHelper.h" #include "common/properties.h" -#include "common/dbus.h" #include "common/processing.h" -#include "detection/displayserver/displayserver.h" +#include "common/strutil.h" + +#ifdef FF_HAVE_DBUS + #include "common/dbus.h" +#endif #include -#define FF_SYSTEMD_SESSIONS_PATH "/run/systemd/sessions/" -#define FF_SYSTEMD_USERS_PATH "/run/systemd/users/" +#if __FreeBSD__ + #include + #include + #include +#elif __OpenBSD__ + #include + #include + #include +#elif __sun + #include +#elif __NetBSD__ + #include + #include +#endif static const char* getGdmVersion(FFstrbuf* version) { - const char* error = ffProcessAppendStdOut(version, (char* const[]) { "gdm", "--version", nullptr }); + const char* error = ffProcessAppendStdOut(version, (char* const[]) { "gdm3", "--version", nullptr }); if (error || version->length == 0) { - error = ffProcessAppendStdOut(version, (char* const[]) { "gdm3", "--version", nullptr }); + error = ffProcessAppendStdOut(version, (char* const[]) { "gdm", "--version", nullptr }); if (error || version->length == 0) { return "Failed to get GDM version"; } @@ -37,6 +54,8 @@ static const char* getSshdVersion(FFstrbuf* version) { #ifdef FF_HAVE_ZLIB #include "common/library.h" + #include "common/path.h" + #include #include @@ -49,7 +68,7 @@ static const char* getSddmVersion(FFstrbuf* version) { FF_LIBRARY_LOAD_SYMBOL_MESSAGE(zlib, gzrewind) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(zlib, gzclose) - gzFile file = ffgzopen(FASTFETCH_TARGET_DIR_USR "/share/man/man1/sddm.1.gz", "rb"); + gzFile file = ffgzopen(FF_PATH_PKG_BASE "/share/man/man1/sddm.1.gz", "rb"); if (file == Z_NULL) { return "ffgzopen(\"/usr/share/man/man1/sddm.1.gz\", \"rb\") failed"; } @@ -112,35 +131,360 @@ static const char* getLightdmVersion(FFstrbuf* version) { return nullptr; } -const char* ffDetectLM(FFLMResult* result) { +#if __linux__ && !__ANDROID__ +#ifdef FF_HAVE_DBUS +static const char* detectBySystemdDbus(FFLMResult* result) { + // This is the standard way to query the systemd session service name, + // and requires no $XDG_SESSION_ID being available + FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {}; + if (ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus) != nullptr) { + return "Failed to load system DBus"; + } + + if (!ffDBusGetPropertyString( + &dbus, + "org.freedesktop.login1", + "/org/freedesktop/login1/session/auto", + "org.freedesktop.login1.Session", + "Service", + &result->service)) { + return "Failed to get systemd session Service property"; + } + + return nullptr; +} +#endif + +#define FF_SYSTEMD_SESSIONS_PATH "/run/systemd/sessions/" +#define FF_SYSTEMD_USERS_PATH "/run/systemd/users/" + +static const char* detectBySystemdPrivate(FFLMResult* result) { FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY sessionId = ffStrbufCreateS(getenv("XDG_SESSION_ID")); if (sessionId.length == 0) { // On some incorrectly configured systems, $XDG_SESSION_ID is not set. Try finding it ourself // WARNING: This is private data. Do not parse - ffStrbufSetF(&path, FF_SYSTEMD_USERS_PATH "%d", instance.state.platform.uid); + ffStrbufAppendS(&path, FF_SYSTEMD_USERS_PATH); + ffStrbufAppendUInt(&path, instance.state.platform.uid); // This is actually buggy, and assumes current user is using DE // `sd_pid_get_session` can be a better option, but we need to find a pid to use - if (!ffParsePropFileValues(path.chars, 1, (FFpropquery[]) { - { "DISPLAY=", &sessionId }, - })) { + if (!ffParsePropFile(path.chars, "DISPLAY=", &sessionId)) { return "Failed to get $XDG_SESSION_ID"; } } - ffStrbufSetS(&path, FF_SYSTEMD_SESSIONS_PATH); + ffStrbufClear(&path); + ffStrbufAppendS(&path, FF_SYSTEMD_SESSIONS_PATH); ffStrbufAppend(&path, &sessionId); // WARNING: This is private data. Do not parse - if (!ffParsePropFileValues(path.chars, 2, (FFpropquery[]) { - { "SERVICE=", &result->service }, - { "TYPE=", &result->type }, - })) { + if (!ffParsePropFile(path.chars, "SERVICE=", &result->service)) { return "Failed to parse " FF_SYSTEMD_SESSIONS_PATH "$XDG_SESSION_ID"; } + return nullptr; +} +#endif + +static const char* testLms(const char* psName) { + static const char* A[] = { + "atrium", + }; + static const char* C[] = { + "cdm", + }; + static const char* E[] = { + "entrance", + }; + static const char* G[] = { + "gdm", + "gdm3", + "greetd", + }; + static const char* L[] = { + "lemurs", + "lightdm", + "lxdm", + "ly", + }; + static const char* P[] = { + "plasmalogin", + }; + static const char* S[] = { + "sddm", + "slim", + }; + static const char* T[] = { + "tbsm", + }; + static const char* X[] = { + "xdm", + "xenodm", + }; + + switch (psName[0]) { + #define PS_CASE(letter, array) \ + case letter: \ + for (uint32_t i = 0; i < ARRAY_SIZE(array); ++i) { \ + if (ffStrEqualsIgnCase(psName, array[i])) { \ + return array[i]; \ + } \ + } \ + break; + PS_CASE('a', A) + PS_CASE('c', C) + PS_CASE('e', E) + PS_CASE('g', G) + PS_CASE('l', L) + PS_CASE('p', P) + PS_CASE('s', S) + PS_CASE('t', T) + PS_CASE('x', X) + } + return nullptr; +} + +const char* detectByProcesses(FFLMResult* result) { +#if __FreeBSD__ + #ifdef __DragonFly__ + #define ki_comm kp_comm + #endif + + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, 0 }; + size_t length = 0; + + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, nullptr) failed"; + } + + FF_AUTO_FREE struct kinfo_proc* procs = (struct kinfo_proc*) malloc(length); + if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, procs) failed"; + } + + length /= sizeof(*procs); + + for (struct kinfo_proc* proc = procs; proc < procs + length; ++proc) { + const char* lm = testLms(proc->ki_comm); + if (lm) { + ffStrbufSetStatic(&result->service, lm); + break; + } + } +#elif __OpenBSD__ + kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr); + int count = 0; + const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_UID, 0, sizeof(*proc), &count); + if (proc) { + for (int i = 0; i < count; ++i) { + const char* lm = testLms(proc[i].p_comm); + if (lm) { + ffStrbufSetStatic(&result->service, lm); + break; + } + } + } + kvm_close(kd); +#elif __sun + FF_AUTO_CLOSE_DIR DIR* procdir = opendir("/proc"); + if (procdir == nullptr) { + return "opendir(\"/proc\") failed"; + } + + FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreateA(64); + ffStrbufAppendS(&procPath, "/proc/"); + + uint32_t procPathLength = procPath.length; + + struct dirent* dirent; + while ((dirent = readdir(procdir)) != nullptr) { + if (!ffCharIsDigit(dirent->d_name[0])) { + continue; + } + + ffStrbufAppendS(&procPath, dirent->d_name); + ffStrbufAppendS(&procPath, "/psinfo"); + psinfo_t proc; + if (ffReadFileData(procPath.chars, sizeof(proc), &proc) == sizeof(proc)) { + ffStrbufSubstrBefore(&procPath, procPathLength); + + if (proc.pr_uid != 0) { + continue; + } + + const char* lm = testLms(proc.pr_fname); + if (lm) { + ffStrbufSetS(&result->service, lm); + break; + } + } + } +#elif __linux__ || __GNU__ + FF_AUTO_CLOSE_DIR DIR* procdir = opendir("/proc"); + if (procdir == nullptr) { + return "opendir(\"/proc\") failed"; + } + + FF_STRBUF_AUTO_DESTROY procPath = ffStrbufCreateA(64); + int procfd = dirfd(procdir); + + struct dirent* dirent; + while ((dirent = readdir(procdir)) != nullptr) { + // Match only folders starting with a number (the pid folders) + if (dirent->d_type != DT_DIR || !ffCharIsDigit(dirent->d_name[0])) { + continue; + } + + ffStrbufSetS(&procPath, dirent->d_name); + uint32_t procFolderPathLength = procPath.length; + + // Don't check for processes not owned by root (login managers run as root). + char loginuid[32]; + ffStrbufAppendS(&procPath, "/loginuid"); + ssize_t bytesRead = ffReadFileDataRelative(procfd, procPath.chars, sizeof(loginuid) - 1, loginuid); + if (bytesRead <= 0) { + continue; + } + loginuid[bytesRead] = '\0'; + + if (strtol(loginuid, nullptr, 10) != (long) (uint32_t) -1 /* no loginuid */) { + continue; + } + + ffStrbufSubstrBefore(&procPath, procFolderPathLength); + + ffStrbufAppendS(&procPath, "/comm"); + char comm[256]; + bytesRead = ffReadFileDataRelative(procfd, procPath.chars, sizeof(comm) - 1, comm); + if (bytesRead <= 0) { + continue; + } + if (comm[bytesRead - 1] == '\n') { + --bytesRead; + } + comm[bytesRead] = '\0'; + + const char* lm = testLms(comm); + if (lm) { + ffStrbufSetS(&result->service, lm); + break; + } + } +#elif __NetBSD__ + int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_UID, 0, sizeof(struct kinfo_proc2), INT_MAX }; + + size_t size = 0; + if (sysctl(request, ARRAY_SIZE(request), nullptr, &size, nullptr, 0) != 0) { + return "sysctl(KERN_PROC_UID, nullptr) failed"; + } + + FF_AUTO_FREE struct kinfo_proc2* procs = malloc(size); + + if (sysctl(request, ARRAY_SIZE(request), procs, &size, nullptr, 0) != 0) { + return "sysctl(KERN_PROC_UID, procs) failed"; + } + + for (struct kinfo_proc2* proc = procs; proc < procs + (size / sizeof(struct kinfo_proc2)); proc++) { + const char* lm = testLms(proc->p_comm); + if (lm) { + ffStrbufSetStatic(&result->service, lm); + break; + } + } +#endif + + if (result->service.length == 0) { + return "Failed to detect login manager by processes"; + } + + return nullptr; +} + +const char* ffDetectLM(FFLMResult* result) { + const char* error = ""; +#if __linux__ + #ifdef FF_HAVE_DBUS + error = detectBySystemdDbus(result); + #endif + if (error != nullptr) { + error = detectBySystemdPrivate(result); + } +#endif + + if (error != nullptr) { + error = detectByProcesses(result); + } + + if (error != nullptr) { + return error; + } + + switch (result->service.chars[0]) { + case 'a': + if (ffStrEqualsIgnCase(result->service.chars, "atrium")) { + ffStrbufSetStatic(&result->prettyName, "Atrium"); + } + break; + case 'c': + if (ffStrEqualsIgnCase(result->service.chars, "cdm")) { + ffStrbufSetStatic(&result->prettyName, "CDM"); + } + break; + case 'e': + if (ffStrEqualsIgnCase(result->service.chars, "entrance")) { + ffStrbufSetStatic(&result->prettyName, "Entrance"); + } + break; + case 'g': + if (ffStrbufStartsWithIgnCaseS(&result->service, "gdm")) { + ffStrbufSetStatic(&result->prettyName, "GDM"); + } else if (ffStrEqualsIgnCase(result->service.chars, "greetd")) { + ffStrbufSetStatic(&result->prettyName, "greetd"); + } + break; + case 'l': + if (ffStrEqualsIgnCase(result->service.chars, "lemurs")) { + ffStrbufSetStatic(&result->prettyName, "Lemurs"); + } else if (ffStrEqualsIgnCase(result->service.chars, "lightdm")) { + ffStrbufSetStatic(&result->prettyName, "LightDM"); + } else if (ffStrEqualsIgnCase(result->service.chars, "lxdm")) { + ffStrbufSetStatic(&result->prettyName, "LXDM"); + } else if (ffStrEqualsIgnCase(result->service.chars, "ly")) { + ffStrbufSetStatic(&result->prettyName, "Ly"); + } + break; + case 'p': + if (ffStrEqualsIgnCase(result->service.chars, "plasmalogin")) { + ffStrbufSetStatic(&result->prettyName, "Plasma Login"); + } + break; + case 's': + if (ffStrEqualsIgnCase(result->service.chars, "sddm")) { + ffStrbufSetStatic(&result->prettyName, "SDDM"); + } else if (ffStrEqualsIgnCase(result->service.chars, "slim")) { + ffStrbufSetStatic(&result->prettyName, "SLiM"); + } + break; + case 't': + if (ffStrEqualsIgnCase(result->service.chars, "tbsm")) { + ffStrbufSetStatic(&result->prettyName, "TBSM"); + } + break; + case 'x': + if (ffStrEqualsIgnCase(result->service.chars, "xdm")) { + ffStrbufSetStatic(&result->prettyName, "XDM"); + } else if (ffStrEqualsIgnCase(result->service.chars, "xenodm")) { + ffStrbufSetStatic(&result->prettyName, "Xenodm"); + } + break; + } + + if (result->prettyName.length == 0) { + ffStrbufSet(&result->prettyName, &result->service); + } + if (instance.config.general.detectVersion) { if (ffStrbufStartsWithS(&result->service, "gdm")) { getGdmVersion(&result->version); @@ -155,14 +499,5 @@ const char* ffDetectLM(FFLMResult* result) { } } - // Correct char cases - if (ffStrbufIgnCaseEqualS(&result->type, FF_WM_PROTOCOL_WAYLAND)) { - ffStrbufSetS(&result->type, FF_WM_PROTOCOL_WAYLAND); - } else if (ffStrbufIgnCaseEqualS(&result->type, FF_WM_PROTOCOL_X11)) { - ffStrbufSetS(&result->type, FF_WM_PROTOCOL_X11); - } else if (ffStrbufIgnCaseEqualS(&result->type, FF_WM_PROTOCOL_TTY)) { - ffStrbufSetS(&result->type, FF_WM_PROTOCOL_TTY); - } - return nullptr; } diff --git a/src/detection/lm/lm_windows.c b/src/detection/lm/lm_windows.c new file mode 100644 index 0000000000..79cffba2b2 --- /dev/null +++ b/src/detection/lm/lm_windows.c @@ -0,0 +1,19 @@ +#include "lm.h" + +#include "common/windows/version.h" +#include "common/windows/nt.h" + +#include + +const char* ffDetectLM(FFLMResult* result) { + ffStrbufSetStatic(&result->service, "LogonUI"); + ffStrbufSetStatic(&result->prettyName, "Logon User Interface"); + + if (instance.config.general.detectVersion) { + wchar_t exePath[MAX_PATH]; + _snwprintf(exePath, ARRAY_SIZE(exePath), L"%ls\\system32\\%ls", (const wchar_t*) SharedUserData->NtSystemRoot, L"LogonUI.exe"); + ffGetFileVersion(exePath, nullptr, &result->version); + } + + return nullptr; +} diff --git a/src/detection/memory/memory_windows.c b/src/detection/memory/memory_windows.c index da689621d2..c86189ae65 100644 --- a/src/detection/memory/memory_windows.c +++ b/src/detection/memory/memory_windows.c @@ -1,19 +1,26 @@ #include "memory.h" -#include +#include "common/windows/nt.h" const char* ffDetectMemory(FFMemoryResult* ram) { - MEMORYSTATUSEX statex = { - .dwLength = sizeof(statex), - }; - // GlobalMemoryStatusEx() internally uses - // NtQuerySystemInformation(SystemBasicPerformanceInformation) in Win 7, and - // NtQuerySystemInformation(SystemMemoryUsageInformation) in Win 10 - if (!GlobalMemoryStatusEx(&statex)) { - return "GlobalMemoryStatusEx() failed"; + // Note: GlobalMemoryStatusEx() internally uses SystemMemoryUsageInformation in Win 10 + + SYSTEM_BASIC_PERFORMANCE_INFORMATION sbpi; + if (!NT_SUCCESS(NtQuerySystemInformation(SystemBasicPerformanceInformation, &sbpi, sizeof(sbpi), NULL))) + return "Failed to query memory information"; + + uint64_t phyPages = + #if _WIN64 + SharedUserData->FullNumberOfPhysicalPages; + if (__builtin_expect(phyPages == 0, false)) { // Not supported on Windows 8.1 + phyPages = SharedUserData->NumberOfPhysicalPages; // ULONG, maximum 16 TB } + #else + SharedUserData->NumberOfPhysicalPages; + #endif - ram->bytesTotal = statex.ullTotalPhys; - ram->bytesUsed = statex.ullTotalPhys - statex.ullAvailPhys; + uint64_t pageSize = instance.state.platform.sysinfo.pageSize; + ram->bytesTotal = phyPages * pageSize; + ram->bytesUsed = (phyPages - sbpi.AvailablePages) * pageSize; return nullptr; } diff --git a/src/detection/netio/netio.c b/src/detection/netio/netio.c index a6348265bb..3c986a8631 100644 --- a/src/detection/netio/netio.c +++ b/src/detection/netio/netio.c @@ -70,8 +70,7 @@ const char* ffDetectNetIO(FFlist* result, FFNetIOOptions* options) { 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 */; + *currValue = (*currValue - *prevValue) * 1000 / (time2 - time1); // Calculate per second *prevValue = temp; } } diff --git a/src/detection/physicaldisk/physicaldisk_bsd.c b/src/detection/physicaldisk/physicaldisk_bsd.c index cda7adda55..d17905388b 100644 --- a/src/detection/physicaldisk/physicaldisk_bsd.c +++ b/src/detection/physicaldisk/physicaldisk_bsd.c @@ -122,6 +122,7 @@ const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) } #else const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) { + FF_UNUSED(result, options); return "Fastfetch was compiled without libgeom support"; } #endif diff --git a/src/detection/physicalmemory/physicalmemory_linux.c b/src/detection/physicalmemory/physicalmemory_linux.c index fe248be0c5..c3e3fae960 100644 --- a/src/detection/physicalmemory/physicalmemory_linux.c +++ b/src/detection/physicalmemory/physicalmemory_linux.c @@ -1,4 +1,5 @@ #include "physicalmemory.h" +#include "common/endian.h" #include "common/smbios.h" // 7.18 @@ -82,7 +83,8 @@ const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* res } const char* strings = (const char*) data + data->Header.Length; - bool installed = data->Size != 0; + uint16_t size = FF_READ_LE(data->Size); + bool installed = size != 0; if (!installed && !options->showEmptySlots) { continue; @@ -101,19 +103,21 @@ const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* res device->installed = installed; device->ecc = false; - if (installed && data->TotalWidth != 0xFFFF && data->DataWidth != 0xFFFF) { - device->ecc = data->TotalWidth > data->DataWidth; + uint16_t totalWidth = FF_READ_LE(data->TotalWidth); + uint16_t dataWidth = FF_READ_LE(data->DataWidth); + if (installed && totalWidth != 0xFFFF && dataWidth != 0xFFFF) { + device->ecc = totalWidth > dataWidth; } - if (installed && data->Size != 0xFFFF) { - if (data->Size == 0x7FFF) { - device->size = (data->ExtendedSize & ~(1ULL << 31)) * 1024ULL * 1024ULL; - } else if (data->Size & (1 << 15)) { + if (installed && size != 0xFFFF) { + if (size == 0x7FFF) { + device->size = (FF_READ_LE(data->ExtendedSize) & ~(1ULL << 31)) * 1024ULL * 1024ULL; + } else if (size & (1 << 15)) { // in kB - device->size = (data->Size & ~(1ULL << 15)) * 1024ULL; + device->size = (size & ~(1ULL << 15)) * 1024ULL; } else { // in MB - device->size = data->Size * 1024ULL * 1024ULL; + device->size = size * 1024ULL * 1024ULL; } } @@ -206,8 +210,9 @@ const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* res if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, Speed)) // 2.3+ { - if (data->Speed) { - device->maxSpeed = data->Speed == 0xFFFF ? data->ExtendedSpeed : data->Speed; + uint16_t speed = FF_READ_LE(data->Speed); + if (speed) { + device->maxSpeed = speed == 0xFFFF ? FF_READ_LE(data->ExtendedSpeed) : speed; } ffStrbufSetStatic(&device->vendor, ffSmbiosLocateString(strings, data->Manufacturer)); @@ -223,10 +228,11 @@ const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* res if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, ConfiguredMemorySpeed)) // 2.7+ { - if (data->ConfiguredMemorySpeed) { - device->runningSpeed = data->ConfiguredMemorySpeed == 0xFFFF - ? data->ExtendedConfiguredSpeed - : data->ConfiguredMemorySpeed; + uint16_t configuredSpeed = FF_READ_LE(data->ConfiguredMemorySpeed); + if (configuredSpeed) { + device->runningSpeed = configuredSpeed == 0xFFFF + ? FF_READ_LE(data->ExtendedConfiguredSpeed) + : configuredSpeed; } } } diff --git a/src/detection/processes/processes.h b/src/detection/processes/processes.h index 69cf46381e..fdc7ceedb9 100644 --- a/src/detection/processes/processes.h +++ b/src/detection/processes/processes.h @@ -1,5 +1,12 @@ #pragma once #include "fastfetch.h" +#include "modules/processes/option.h" -const char* ffDetectProcesses(uint32_t* result); +typedef struct FFProcessesResult +{ + uint32_t processes; + uint32_t threads; +} FFProcessesResult; + +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result); diff --git a/src/detection/processes/processes_apple.c b/src/detection/processes/processes_apple.c new file mode 100644 index 0000000000..8ed1472b20 --- /dev/null +++ b/src/detection/processes/processes_apple.c @@ -0,0 +1,41 @@ +#include "processes.h" +#include "common/mallocHelper.h" + +#include +#import + +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + size_t length; + + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, nullptr}) failed"; + } + + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + FF_AUTO_FREE struct kinfo_proc* processes = malloc(length); + + if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, processes}) failed"; + } + + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &processes[i]; + if (!options->countKprocs && (proc->kp_proc.p_flag & P_SYSTEM)) { + continue; + } + result->processes++; + + pid_t pid = proc->kp_proc.p_pid; + struct proc_taskinfo taskInfo; + if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) != sizeof(taskInfo)) { + continue; + } + result->threads += taskInfo.pti_threadnum; + } + + return nullptr; +} diff --git a/src/detection/processes/processes_bsd.c b/src/detection/processes/processes_bsd.c index 4d16bc3af2..32ee53736c 100644 --- a/src/detection/processes/processes_bsd.c +++ b/src/detection/processes/processes_bsd.c @@ -1,23 +1,35 @@ #include "processes.h" +#include "common/mallocHelper.h" #include -#ifdef __FreeBSD__ - #include - #include -#endif +#include +#include -#ifndef KERN_PROC_PROC - #define KERN_PROC_PROC KERN_PROC_ALL // Apple -#endif - -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC }; size_t length; - if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_PROC}) failed"; } - *result = (uint32_t) (length / sizeof(struct kinfo_proc)); + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + FF_AUTO_FREE struct kinfo_proc* procs = malloc(length); + if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_PROC}) failed"; + } + + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &procs[i]; + + if (!options->countKprocs && (proc->ki_flag & P_KPROC)) { + continue; + } + + result->processes++; + result->threads += (uint32_t) proc->ki_numthreads; + } + return nullptr; } diff --git a/src/detection/processes/processes_dbsd.c b/src/detection/processes/processes_dbsd.c new file mode 100644 index 0000000000..c0fa246dea --- /dev/null +++ b/src/detection/processes/processes_dbsd.c @@ -0,0 +1,38 @@ +#include "processes.h" +#include "common/mallocHelper.h" + +#include +#include +#include +#include + +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL | (options->countKprocs ? KERN_PROC_FLAG_LWKT : 0) }; + size_t length; + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}) failed"; + } + + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + FF_AUTO_FREE struct kinfo_proc* procs = malloc(length); + if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}) failed"; + } + + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &procs[i]; + + if (proc->kp_pid == -1) { + // Kernel thread, only returned when countKprocs is set via KERN_PROC_FLAG_LWKT + result->threads++; + continue; + } + + result->processes++; + result->threads += (uint32_t) proc->kp_nthreads; + } + + return nullptr; +} diff --git a/src/detection/processes/processes_gnu.c b/src/detection/processes/processes_gnu.c new file mode 100644 index 0000000000..53c9781c65 --- /dev/null +++ b/src/detection/processes/processes_gnu.c @@ -0,0 +1,44 @@ +#include "processes.h" + +#include +#include + +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { + struct ps_context* context = nullptr; + if (ps_context_create(getproc(), &context) != 0) { + return "ps_context_create(getproc()) failed"; + } + + const char* error = nullptr; + struct proc_stat_list* list = nullptr; + if (proc_stat_list_create(context, &list) != 0) { + error = "proc_stat_list_create() failed"; + goto done; + } + + if (proc_stat_list_add_all(list, nullptr, nullptr) != 0) { + error = "proc_stat_list_add_all() failed"; + goto done; + } + + proc_stat_list_set_flags(list, PSTAT_NUM_THREADS); + + for (uint32_t i = 0; i < list->num_procs; i++) { + struct proc_stat* stat = list->proc_stats[i]; + if (!options->countKprocs && proc_stat_pid(stat) == 2) { // Kernel Task + continue; + } + + ++result->processes; + if (proc_stat_has(stat, PSTAT_NUM_THREADS)) { + result->threads += proc_stat_num_threads(stat); + } + } + +done: + if (list) { + proc_stat_list_free(list); + } + ps_context_free(context); + return error; +} diff --git a/src/detection/processes/processes_haiku.c b/src/detection/processes/processes_haiku.c index 0d960ab39e..feecc34e58 100644 --- a/src/detection/processes/processes_haiku.c +++ b/src/detection/processes/processes_haiku.c @@ -2,13 +2,22 @@ #include -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { system_info info; if (get_system_info(&info) != B_OK) { return "Error getting system info"; } - *result = info.used_teams; + result->processes = info.used_teams; + result->threads = info.used_threads; + + if (!options->countKprocs) { + team_info ti; + if (get_team_info(B_SYSTEM_TEAM, &ti) == B_OK) { + result->processes--; + result->threads -= (uint32_t) ti.thread_count; + } + } return nullptr; } diff --git a/src/detection/processes/processes_linux.c b/src/detection/processes/processes_linux.c index 78790c2ebc..68f819481b 100644 --- a/src/detection/processes/processes_linux.c +++ b/src/detection/processes/processes_linux.c @@ -1,27 +1,73 @@ #include "processes.h" #include "common/io.h" +#include "common/memrchr.h" #include "common/strutil.h" -const char* ffDetectProcesses(uint32_t* result) { +#define PF_KTHREAD 0x00200000 // defined in kernel include/linux/sched.h + +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { FF_AUTO_CLOSE_DIR DIR* dir = opendir("/proc"); if (dir == nullptr) { return "opendir(\"/proc\") failed"; } - uint32_t num = 0; + int procfd = dirfd(dir); struct dirent* entry; while ((entry = readdir(dir)) != nullptr) { - if ( -#ifdef _DIRENT_HAVE_D_TYPE - (entry->d_type == DT_DIR || entry->d_type == DT_UNKNOWN) && -#endif - ffCharIsDigit(entry->d_name[0])) - ++num; - } + if ((entry->d_type == DT_DIR || entry->d_type == DT_UNKNOWN) && ffCharIsDigit(entry->d_name[0])) { + char statPath[32]; + strcpy(ffStrCopy(statPath, entry->d_name, sizeof(statPath)), "/stat"); + + char statBuffer[512]; + ssize_t statLength = ffReadFileDataRelative(procfd, statPath, sizeof(statBuffer) - 1, statBuffer); + if (statLength < 0) { + continue; + } + statBuffer[statLength] = '\0'; + + const char* cursor = (const char*) memrchr(statBuffer, ')', (size_t) statLength); + if (!cursor) { + continue; + } + cursor += 2; // skip ") " + if (__builtin_expect(cursor >= statBuffer + statLength, false)) { + goto skip_process; + } + + char* p; + for (uint32_t field = 3; field <= 20 /*num_threads*/; ++field) { + switch (field) { + case 9: // flags + if ((strtoul(cursor, &p, 10) & PF_KTHREAD) && !options->countKprocs) { + goto skip_process; + } + cursor = p; + break; - *result = num; + case 20: // num_threads + result->threads += (uint32_t) strtoul(cursor, &p, 10); + cursor = p; + break; + + default: + while (*cursor != ' ' && __builtin_expect(*cursor != '\0', true)) { + ++cursor; + } + break; + } + + ++cursor; + if (__builtin_expect(cursor >= statBuffer + statLength, false)) { + goto skip_process; + } + } + + ++result->processes; + } + skip_process:; + } return nullptr; } diff --git a/src/detection/processes/processes_nbsd.c b/src/detection/processes/processes_nbsd.c index c3e7d047f1..cb91db096f 100644 --- a/src/detection/processes/processes_nbsd.c +++ b/src/detection/processes/processes_nbsd.c @@ -1,15 +1,37 @@ #include "processes.h" +#include "common/mallocHelper.h" #include -const char* ffDetectProcesses(uint32_t* result) { - int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, -1, sizeof(struct kinfo_proc2), 0 }; +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { + int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, -1, sizeof(struct kinfo_proc2), INT_MAX }; size_t length = 0; if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { - return "sysctl({CTL_KERN, KERN_PROC2, KERN_PROC_ALL}) failed"; + return "sysctl({CTL_KERN, KERN_PROC2, KERN_PROC_ALL, nullptr}) failed"; + } + + FF_AUTO_FREE struct kinfo_proc2* procs = malloc(length); + if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC2, KERN_PROC_ALL, procs}) failed"; + } + + uint32_t procCount = (uint32_t) (length / sizeof(struct kinfo_proc2)); + + for (uint32_t i = 0; i < procCount; ++i) { + const struct kinfo_proc2* proc = &procs[i]; + if (!options->countKprocs && (proc->p_flag & P_SYSTEM)) { + continue; + } + ++result->processes; + + int lwpRequest[] = { CTL_KERN, KERN_LWP, procs[i].p_pid, sizeof(struct kinfo_lwp), 0 }; + size_t lwpLength = 0; + + if (sysctl(lwpRequest, ARRAY_SIZE(lwpRequest), nullptr, &lwpLength, nullptr, 0) == 0) { + result->threads += (uint32_t) (lwpLength / sizeof(struct kinfo_lwp)); + } } - *result = (uint32_t) (length / sizeof(struct kinfo_proc2)); return nullptr; } diff --git a/src/detection/processes/processes_nosupport.c b/src/detection/processes/processes_nosupport.c index 7da208ec4c..04a5eafd83 100644 --- a/src/detection/processes/processes_nosupport.c +++ b/src/detection/processes/processes_nosupport.c @@ -1,5 +1,5 @@ #include "processes.h" -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses([[maybe_unused]] const FFProcessesOptions* options, [[maybe_unused]] FFProcessesResult* result) { return "Not supported on this platform"; } diff --git a/src/detection/processes/processes_obsd.c b/src/detection/processes/processes_obsd.c index 5a5ba31f46..8c50065a07 100644 --- a/src/detection/processes/processes_obsd.c +++ b/src/detection/processes/processes_obsd.c @@ -1,15 +1,37 @@ #include "processes.h" +#include "common/mallocHelper.h" + #include #include #include -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr); - const void* ret = kvm_getprocs(kd, KERN_PROC_ALL, 0, 1, result); - kvm_close(kd); - if (!ret) { + if (!kd) { + return "kvm_open() failed"; + } + + int count = 0; + // KERN_PROC_ALL returns all user-level processes + // KERN_PROC_KTHREAD returns all processes, including user-level processes (despite the name) + const struct kinfo_proc* procs = kvm_getprocs(kd, + (options->countKprocs ? KERN_PROC_KTHREAD : KERN_PROC_ALL) | KERN_PROC_SHOW_THREADS, + 0, sizeof(struct kinfo_proc), &count); + if (!procs) { + kvm_close(kd); return "kvm_getprocs() failed"; } + + for (int i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &procs[i]; + + ++result->threads; + if (proc->p_tid == -1) { + ++result->processes; + } + } + + kvm_close(kd); return nullptr; } diff --git a/src/detection/processes/processes_sunos.c b/src/detection/processes/processes_sunos.c new file mode 100644 index 0000000000..a815390bb5 --- /dev/null +++ b/src/detection/processes/processes_sunos.c @@ -0,0 +1,40 @@ +#include "processes.h" + +#include "common/io.h" +#include "common/strutil.h" + +#include + +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { + FF_AUTO_CLOSE_DIR DIR* dir = opendir("/proc"); + if (dir == nullptr) { + return "opendir(\"/proc\") failed"; + } + + int procfd = dirfd(dir); + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + if (ffCharIsDigit(entry->d_name[0])) { + char path[32]; + char* pidEnd = ffStrCopy(path, entry->d_name, sizeof(path)); + if (!options->countKprocs) { + pstatus_t status; + strcpy(pidEnd, "/status"); + if (ffReadFileDataRelative(procfd, path, sizeof(status), &status) != (ssize_t) sizeof(status) || + status.pr_flags & PR_ISSYS) { + continue; // The process may have exited, no permission or is a kernel process + } + } + + strcpy(pidEnd, "/psinfo"); + psinfo_t info; + if (ffReadFileDataRelative(procfd, path, sizeof(info), &info) == (ssize_t) sizeof(info)) { + ++result->processes; + result->threads += (uint32_t) info.pr_nlwp; + } + } + } + + return nullptr; +} diff --git a/src/detection/processes/processes_windows.c b/src/detection/processes/processes_windows.c index 78ed840b46..0a2ba2f41a 100644 --- a/src/detection/processes/processes_windows.c +++ b/src/detection/processes/processes_windows.c @@ -4,7 +4,7 @@ #include #include -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { FF_AUTO_FREE SYSTEM_PROCESS_INFORMATION* pstart = nullptr; // Multiple attempts in case processes change while @@ -25,9 +25,14 @@ const char* ffDetectProcesses(uint32_t* result) { } } - *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)) { - ++*result; + for (SYSTEM_PROCESS_INFORMATION* ptr = pstart; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) { + if (options->countKprocs || (uintptr_t) ptr->InheritedFromUniqueProcessId > 0) { + ++result->processes; + result->threads += ptr->NumberOfThreads; + } + if (ptr->NextEntryOffset == 0) { + break; + } } return nullptr; diff --git a/src/detection/terminalfont/terminalfont_android.c b/src/detection/terminalfont/terminalfont_android.c index 666cf3e2b2..494dcee1f7 100644 --- a/src/detection/terminalfont/terminalfont_android.c +++ b/src/detection/terminalfont/terminalfont_android.c @@ -2,6 +2,7 @@ #include "terminalfont.h" #include "detection/terminalshell/terminalshell.h" #include "common/io.h" +#include "common/properties.h" #ifdef FF_HAVE_FREETYPE #include "common/library.h" @@ -10,10 +11,18 @@ #endif #define FF_TERMUX_FONT_PATH FASTFETCH_TARGET_DIR_HOME "/.termux/font.ttf" +#define FF_TERMUX_PREF_PATH "/data/data/com.termux/shared_prefs/com.termux_preferences.xml" const char* detectTermux(FFTerminalFontResult* terminalFont) { + FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate(); + // SharedPreferences XML: 14, in px + ffParsePropFile(FF_TERMUX_PREF_PATH, "", &fontSize); + if (fontSize.length > 0) { + ffStrbufAppendS(&fontSize, "px"); + } + if (!ffPathExists(FF_TERMUX_FONT_PATH, FF_PATHTYPE_FILE)) { - ffFontInitCopy(&terminalFont->font, "monospace"); + ffFontInitValues(&terminalFont->font, "monospace", fontSize.length ? fontSize.chars : nullptr); return nullptr; } @@ -39,7 +48,7 @@ const char* detectTermux(FFTerminalFontResult* terminalFont) { goto exit; } - ffFontInitCopy(&terminalFont->font, face->family_name); + ffFontInitValues(&terminalFont->font, face->family_name, fontSize.length ? fontSize.chars : nullptr); exit: if (face) { @@ -53,7 +62,7 @@ const char* detectTermux(FFTerminalFontResult* terminalFont) { #else - FF_UNUSED(terminalFont); + ffFontInitValues(&terminalFont->font, "monospace", fontSize.length ? fontSize.chars : nullptr); return "Fastfetch was built without freetype2 support"; #endif diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index fa323aa9eb..1844d51637 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -8,17 +8,8 @@ #include #include -#ifdef __FreeBSD__ - #include - #ifndef _PATH_LOCALBASE - #define _PATH_LOCALBASE "/usr/local" - #endif -#elif __OpenBSD__ - #define _PATH_LOCALBASE "/usr/local" -#elif __NetBSD__ - #define _PATH_LOCALBASE "/usr/pkg" -#elif _WIN32 +#if _WIN32 #include "common/windows/registry.h" #include "common/windows/version.h" #include @@ -194,6 +185,9 @@ static bool extractBusyboxVersion(const char* line, uint32_t len, void* userdata static bool getShellVersionAsh(FFstrbuf* exe, FFstrbuf* version) { ffBinaryExtractStrings(exe->chars, extractBusyboxVersion, version, (uint32_t) strlen("BusyBox v0.0.0")); + if (version->length > 0) { + return true; + } const char* error = ffStrbufEndsWithS(exe, "busybox") ? ffProcessAppendStdErr(version, (char* const[]) { exe->chars, "ash", "--help", nullptr }) @@ -660,7 +654,7 @@ static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version) { ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib64/kitty/kitty/constants.py", ARRAY_SIZE(buffer) - 1, buffer) || ffReadFileData(FASTFETCH_TARGET_DIR_USR "/lib/kitty/kitty/constants.py", ARRAY_SIZE(buffer) - 1, buffer) #else - ffReadFileData(_PATH_LOCALBASE "/share/kitty/kitty/constants.py", ARRAY_SIZE(buffer) - 1, buffer) + ffReadFileData(FF_PATH_PKG_BASE "/share/kitty/kitty/constants.py", ARRAY_SIZE(buffer) - 1, buffer) #endif ) { // Starts from version 0.17.0 diff --git a/src/detection/top/top.c b/src/detection/top/top.c new file mode 100644 index 0000000000..dd4442a7ee --- /dev/null +++ b/src/detection/top/top.c @@ -0,0 +1,177 @@ +#include "top.h" + +#include "common/time.h" + +static FFlist first; +static double startTick; +static FFTopTypes preparedShowTypes = FF_TOP_TYPE_CPU | FF_TOP_TYPE_MEMORY | FF_TOP_TYPE_DISK; + +void ffPrepareTopProcesses(FFTopTypes showTypes) { + if ((showTypes & (FF_TOP_TYPE_CPU | FF_TOP_TYPE_DISK)) == 0) { + return; // Memory usage is instantaneous; no baseline snapshot is needed + } + + if (startTick != 0 && preparedShowTypes == showTypes) { + return; // Already prepared + } + + if (startTick != 0) { + // The set of requested types changed; discard the stale baseline + FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, first) { + ffStrbufDestroy(&item->name); + } + ffListDestroy(&first); + } + + ffListInit(&first); + startTick = ffTimeGetTick(); + preparedShowTypes = showTypes; + ffTopGetProcessSnapshot(&first, showTypes); +} + +// clang-format off +static int compareCpuResults(const FFTopProcessResult* a, const FFTopProcessResult* b) { + if (a->cpuPercent < b->cpuPercent) return 1; + if (a->cpuPercent > b->cpuPercent) return -1; + return (int) (a->pid - b->pid); +} + +static int compareMemoryResults(const FFTopProcessResult* a, const FFTopProcessResult* b) { + if (a->memBytes < b->memBytes) return 1; + if (a->memBytes > b->memBytes) return -1; + return (int) (a->pid - b->pid); +} + +static int compareDiskReadResults(const FFTopProcessResult* a, const FFTopProcessResult* b) { + if (a->bytesRead < b->bytesRead) return 1; + if (a->bytesRead > b->bytesRead) return -1; + return (int) (a->pid - b->pid); +} + +static int compareDiskWriteResults(const FFTopProcessResult* a, const FFTopProcessResult* b) { + if (a->bytesWritten < b->bytesWritten) return 1; + if (a->bytesWritten > b->bytesWritten) return -1; + return (int) (a->pid - b->pid); +} + +static int compareStartTimeResults(const FFTopProcessResult* a, const FFTopProcessResult* b) { + if (a->startTime < b->startTime) return 1; + if (a->startTime > b->startTime) return -1; + return (int) (a->pid - b->pid); +} + +static int compareThreadsResults(const FFTopProcessResult* a, const FFTopProcessResult* b) { + if (a->threads < b->threads) return 1; + if (a->threads > b->threads) return -1; + return (int) (a->pid - b->pid); +} +// clang-format on + +const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) { + ffListClear(result); + if (options->nProcesses == 0) { + return nullptr; + } + + // Memory usage and thread count are instantaneous; when neither CPU time nor disk IO + // counters are requested, a single snapshot suffices and no sampling wait is needed. + const bool sampleOnce = (options->showTypes & (FF_TOP_TYPE_CPU | FF_TOP_TYPE_DISK)) == 0; + + if (sampleOnce) { + FF_LIST_AUTO_DESTROY snapshots = ffListCreate(); + const char* error = ffTopGetProcessSnapshot(&snapshots, options->showTypes); + if (error) { + FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, snapshots) { + ffStrbufDestroy(&item->name); + } + return error; + } + if (snapshots.length == 0) { + return "No processes found"; + } + + FF_LIST_FOR_EACH (FFTopProcessSnapshot, snap, snapshots) { + FFTopProcessResult* item = FF_LIST_ADD(FFTopProcessResult, *result); + item->pid = snap->pid; + item->memBytes = snap->memBytes; + item->bytesRead = 0; + item->bytesWritten = 0; + item->cpuPercent = 0; + item->startTime = snap->startTime; + item->threads = snap->threads; + ffStrbufInitMove(&item->name, &snap->name); + } + } else { + if (startTick == 0 || preparedShowTypes != options->showTypes) { + ffPrepareTopProcesses(options->showTypes); + } + + double elapsedTime = ffTimeGetTick() - startTick; + if (elapsedTime < (double) options->waitTime) { + ffTimeSleep(options->waitTime - (uint32_t) elapsedTime); + } + + if (first.length == 0) { + return "No processes found"; + } + + ffListReserve(result, sizeof(FFTopProcessResult), first.length < options->nProcesses ? first.length : options->nProcesses); + + FF_LIST_AUTO_DESTROY second = ffListCreateA(sizeof(FFTopProcessSnapshot), first.length); + const char* error = ffTopGetProcessSnapshot(&second, options->showTypes); + const double elapsed = ffTimeGetTick() - startTick; + + if (error || elapsed <= 0) { + FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, second) { + ffStrbufDestroy(&item->name); + } + return error ?: "Invalid process sampling interval"; + } + + FF_LIST_FOR_EACH (FFTopProcessSnapshot, oldItem, first) { + FFTopProcessSnapshot* newItem = nullptr; + FF_LIST_FOR_EACH (FFTopProcessSnapshot, item, second) { + if (item->pid == oldItem->pid) { + newItem = item; + break; + } + } + if (!newItem || newItem->startTime != oldItem->startTime || newItem->cpuTime < oldItem->cpuTime || newItem->bytesRead < oldItem->bytesRead || newItem->bytesWritten < oldItem->bytesWritten) { + ffStrbufDestroy(&oldItem->name); + continue; + } + + FFTopProcessResult* item = FF_LIST_ADD(FFTopProcessResult, *result); + item->pid = newItem->pid; + item->memBytes = newItem->memBytes; + item->bytesRead = (newItem->bytesRead - oldItem->bytesRead) * 1000u / (uint64_t) elapsed; + item->bytesWritten = (newItem->bytesWritten - oldItem->bytesWritten) * 1000u / (uint64_t) elapsed; + item->cpuPercent = (double) (newItem->cpuTime - oldItem->cpuTime) / elapsed * 100.0; + item->startTime = newItem->startTime; + item->threads = newItem->threads; + ffStrbufInitMove(&item->name, &oldItem->name); + } + + // Reuse `second` as the baseline of the next call + ffListDestroy(&first); + ffListInitMove(&first, &second); + startTick = ffTimeGetTick(); + } + + const void* compare = options->sort == FF_TOP_TYPE_DISK_WRITE ? (void*) compareDiskWriteResults + : options->sort == FF_TOP_TYPE_DISK_READ ? (void*) compareDiskReadResults + : options->sort == FF_TOP_TYPE_MEMORY ? (void*) compareMemoryResults + : options->sort == FF_TOP_TYPE_START_TIME ? (void*) compareStartTimeResults + : options->sort == FF_TOP_TYPE_THREADS ? (void*) compareThreadsResults + : (void*) compareCpuResults; + ffListSort(result, sizeof(FFTopProcessResult), (void*) compare); + if (result->length > options->nProcesses) { + FFTopProcessResult* items = (FFTopProcessResult*) result->data; + for (uint32_t i = options->nProcesses; i < result->length; ++i) { + ffStrbufDestroy(&items[i].name); + } + result->length = options->nProcesses; + } + + return nullptr; +} diff --git a/src/detection/top/top.h b/src/detection/top/top.h new file mode 100644 index 0000000000..26f05d549b --- /dev/null +++ b/src/detection/top/top.h @@ -0,0 +1,29 @@ +#pragma once + +#include "fastfetch.h" +#include "modules/top/option.h" + +typedef struct FFTopProcessResult { + FFstrbuf name; + double cpuPercent; + uint64_t memBytes; + uint64_t bytesRead; + uint64_t bytesWritten; + uint64_t startTime; + uint32_t threads; + uint32_t pid; +} FFTopProcessResult; + +typedef struct FFTopProcessSnapshot { + FFstrbuf name; // comm + uint64_t cpuTime; // user_time + system_time + uint64_t memBytes; // RSS + uint64_t bytesRead; // Physical storage read + uint64_t bytesWritten; // Physical storage written + uint64_t startTime; // Differs between platforms + uint32_t threads; + uint32_t pid; +} FFTopProcessSnapshot; + +const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result); +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes); diff --git a/src/detection/top/top_apple.c b/src/detection/top/top_apple.c new file mode 100644 index 0000000000..6a1692b0e8 --- /dev/null +++ b/src/detection/top/top_apple.c @@ -0,0 +1,67 @@ +#include "top.h" +#include "common/mallocHelper.h" + +#include +#include +#include + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) { + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + size_t length; + + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, nullptr}) failed"; + } + + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + FF_AUTO_FREE struct kinfo_proc* processes = malloc(length); + + if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, processes}) failed"; + } + + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &processes[i]; + if (proc->kp_proc.p_flag & P_SYSTEM) { + continue; + } + pid_t pid = proc->kp_proc.p_pid; + + struct rusage_info_v2 rusage; + if (proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t*) &rusage) != 0) { + continue; // The process may have exited + } + + FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + ffStrbufInitS(&item->name, proc->kp_proc.p_comm); + item->pid = (uint32_t) pid; + // Note: Do NOT use proc->kp_proc.p_pctcpu for CPU usage. p_pctcpu is a + // decaying average (fixpt_t with FSCALE=2048) updated roughly once per + // second by the kernel. It is heavily smoothed, lags short bursts, has + // low resolution, and its multicore scaling (>100%) is inconsistent + // across XNU versions. It also breaks the unified model where + // FFTopProcessSnapshot.cpuTime is cumulative time and top.c computes + // (new - old) / elapsed * 100 for all platforms. proc_pid_rusage with + // RUSAGE_INFO_V2 provides precise cumulative ri_user_time + + // ri_system_time in nanoseconds and is the modern recommended API on + // Darwin, consistent with Linux/BSD differential sampling and accurate + // for the short waitTime interval (e.g. 100ms). + item->cpuTime = (rusage.ri_user_time + rusage.ri_system_time) / 1000000u; // ns -> ms + item->memBytes = rusage.ri_resident_size; + item->bytesRead = rusage.ri_diskio_bytesread; + item->bytesWritten = rusage.ri_diskio_byteswritten; + item->startTime = rusage.ri_proc_start_abstime; + item->threads = 0; + if (showTypes & FF_TOP_TYPE_THREADS) { + struct proc_taskinfo taskInfo; + if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) == sizeof(taskInfo)) { + item->threads = (uint32_t) taskInfo.pti_threadnum; + } + } + } + + return nullptr; +} diff --git a/src/detection/top/top_bsd.c b/src/detection/top/top_bsd.c new file mode 100644 index 0000000000..108e5e97e4 --- /dev/null +++ b/src/detection/top/top_bsd.c @@ -0,0 +1,58 @@ +#include "top.h" +#include "common/mallocHelper.h" + +#include +#include +#include +#include + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) { + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC }; + size_t length; + + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_PROC}) failed"; + } + + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + FF_AUTO_FREE struct kinfo_proc* processes = malloc(length); + if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_PROC}) failed"; + } + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + + uint32_t pageSize = instance.state.platform.sysinfo.pageSize; + + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &processes[i]; + + if (proc->ki_flag & P_KPROC) { + continue; + } + + uint32_t pid = (uint32_t) proc->ki_pid; + + FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + + ffStrbufInitS(&item->name, proc->ki_comm); + item->pid = pid; + + uint64_t userMs = (uint64_t) proc->ki_rusage.ru_utime.tv_sec * 1000 + + (uint64_t) proc->ki_rusage.ru_utime.tv_usec / 1000; + uint64_t sysMs = (uint64_t) proc->ki_rusage.ru_stime.tv_sec * 1000 + + (uint64_t) proc->ki_rusage.ru_stime.tv_usec / 1000; + item->cpuTime = userMs + sysMs; + + item->memBytes = (uint64_t) proc->ki_rssize * (uint64_t) pageSize; + + item->startTime = (uint64_t) proc->ki_start.tv_sec * 1000 + + (uint64_t) proc->ki_start.tv_usec / 1000; + + item->bytesRead = (uint64_t) proc->ki_rusage.ru_inblock * DEV_BSIZE; + item->bytesWritten = (uint64_t) proc->ki_rusage.ru_oublock * DEV_BSIZE; + item->threads = (uint32_t) proc->ki_numthreads; + } + + return NULL; +} diff --git a/src/detection/top/top_dbsd.c b/src/detection/top/top_dbsd.c new file mode 100644 index 0000000000..9896b6f54e --- /dev/null +++ b/src/detection/top/top_dbsd.c @@ -0,0 +1,59 @@ +#include "top.h" +#include "common/mallocHelper.h" + +#include +#include +#include +#include + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) { + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + size_t length; + + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, nullptr}) failed"; + } + + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + + FF_AUTO_FREE struct kinfo_proc* processes = malloc(length); + if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, processes}) failed"; + } + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + + uint32_t pageSize = instance.state.platform.sysinfo.pageSize; + + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &processes[i]; + + // KERN_PROC_ALL won't return kernel threads + if (proc->kp_stat == SZOMB) { // Ignore zombie processes + continue; + } + + FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + + ffStrbufInitS(&item->name, proc->kp_comm); + item->pid = (uint32_t) proc->kp_pid; + + // kp_ru only accounts for exited lwps; the cpu times of live lwps are + // kept in microseconds in kp_lwp.kl_uticks/kl_sticks (see calcru(9)). + uint64_t exitedMs = (uint64_t) (proc->kp_ru.ru_utime.tv_sec + proc->kp_ru.ru_stime.tv_sec) * 1000 + + (uint64_t) (proc->kp_ru.ru_utime.tv_usec + proc->kp_ru.ru_stime.tv_usec) / 1000; + uint64_t liveMs = (proc->kp_lwp.kl_uticks + proc->kp_lwp.kl_sticks) / 1000; + item->cpuTime = exitedMs + liveMs; + + item->memBytes = (uint64_t) proc->kp_vm_rssize * pageSize; + + item->startTime = (uint64_t) proc->kp_start.tv_sec * 1000 + + (uint64_t) proc->kp_start.tv_usec / 1000; + + item->bytesRead = (uint64_t) (proc->kp_ru.ru_inblock + proc->kp_lwp.kl_ru.ru_inblock) * DEV_BSIZE; + item->bytesWritten = (uint64_t) (proc->kp_ru.ru_oublock + proc->kp_lwp.kl_ru.ru_oublock) * DEV_BSIZE; + item->threads = (uint32_t) proc->kp_nthreads; + } + + return nullptr; +} diff --git a/src/detection/top/top_gnu.c b/src/detection/top/top_gnu.c new file mode 100644 index 0000000000..9f93422162 --- /dev/null +++ b/src/detection/top/top_gnu.c @@ -0,0 +1,93 @@ +#include "top.h" + +#include +#include +#include + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) { + struct ps_context* context = nullptr; + if (ps_context_create(getproc(), &context) != 0) { + return "ps_context_create(getproc()) failed"; + } + + const char* error = nullptr; + struct proc_stat_list* list = nullptr; + if (proc_stat_list_create(context, &list) != 0) { + error = "proc_stat_list_create() failed"; + goto done; + } + + if (proc_stat_list_add_all(list, nullptr, nullptr) != 0) { + error = "proc_stat_list_add_all() failed"; + goto done; + } + + // Flags that cannot be fetched for some process are simply left unset; + // each field must be checked before use. + if (proc_stat_list_set_flags(list, PSTAT_ARGS | PSTAT_STATE | PSTAT_TASK_BASIC | ((showTypes & FF_TOP_TYPE_THREADS) ? PSTAT_NUM_THREADS : 0)) != 0) { + error = "proc_stat_list_set_flags() failed"; + goto done; + } + + for (unsigned i = 0; i < list->num_procs; ++i) { + struct proc_stat* stat = list->proc_stats[i]; + + if (!proc_stat_has(stat, PSTAT_PID | PSTAT_STATE)) { + continue; + } + if (proc_stat_state(stat) & PSTAT_STATE_P_ZOMBIE) { + continue; + } + + uint32_t pid = (uint32_t) proc_stat_pid(stat); + if (pid == 2) { // Kernel Task + continue; + } + + FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + + // There is no kernel-side comm on the Hurd; use the first argument. + ffStrbufInit(&item->name); + if (proc_stat_has(stat, PSTAT_ARGS) && proc_stat_args(stat)) { + size_t len = strnlen(proc_stat_args(stat), proc_stat_args_len(stat)); + ffStrbufAppendNS(&item->name, (uint32_t) len, proc_stat_args(stat)); + } + if (item->name.length == 0) { + ffStrbufAppendS(&item->name, "(unknown)"); + } + + item->pid = pid; + item->cpuTime = 0; + item->memBytes = 0; + item->startTime = 0; + item->threads = 0; + + if (showTypes & FF_TOP_TYPE_THREADS) { + if (proc_stat_has(stat, PSTAT_NUM_THREADS)) { + item->threads = (uint32_t) proc_stat_num_threads(stat); + } + } + + if (proc_stat_has(stat, PSTAT_TASK_BASIC)) { + const task_basic_info_t info = proc_stat_task_basic_info(stat); + item->cpuTime = (uint64_t) info->user_time64.seconds * 1000 + + (uint64_t) info->user_time64.nanoseconds / 1000000 + + (uint64_t) info->system_time64.seconds * 1000 + + (uint64_t) info->system_time64.nanoseconds / 1000000; + item->memBytes = (uint64_t) info->resident_size; + item->startTime = (uint64_t) info->creation_time64.seconds * 1000 + + (uint64_t) info->creation_time64.nanoseconds / 1000000; + } + + // Storage io counters aren't exposed by kernel; leave them zeroed. + item->bytesRead = 0; + item->bytesWritten = 0; + } + +done: + if (list) { + proc_stat_list_free(list); + } + ps_context_free(context); + return error; +} diff --git a/src/detection/top/top_haiku.c b/src/detection/top/top_haiku.c new file mode 100644 index 0000000000..32f73d686b --- /dev/null +++ b/src/detection/top/top_haiku.c @@ -0,0 +1,48 @@ +#include "top.h" + +#include +#include + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) { + int32 cookie = 0; + team_info team; + while (get_next_team_info(&cookie, &team) == B_OK) { + if (team.team == B_SYSTEM_TEAM) { + continue; // The kernel team + } + + FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + + ffStrbufInitS(&item->name, team.name); + + item->pid = (uint32_t) team.team; + + // User and kernel time of all threads of the team combined in microseconds. + item->cpuTime = 0; + team_usage_info usage; + if (get_team_usage_info(team.team, B_TEAM_USAGE_SELF, &usage) == B_OK) { + item->cpuTime = ((uint64_t) usage.user_time + (uint64_t) usage.kernel_time) / 1000; + } + + // Sum up the physically resident memory of all areas of the team; + // ram_size is the amount of physical pages committed to an area, + // whereas size is just its virtual reservation. + uint64_t memBytes = 0; + ssize_t areaCookie = 0; + area_info area; + while (get_next_area_info(team.team, &areaCookie, &area) == B_OK) { + memBytes += area.ram_size; + } + item->memBytes = memBytes; + + // start_time is microseconds since boot. + item->startTime = (uint64_t) team.start_time; + + // Storage io counters are not exposed by Haiku; leave them zeroed. + item->bytesRead = 0; + item->bytesWritten = 0; + item->threads = (uint32_t) team.thread_count; + } + + return nullptr; +} diff --git a/src/detection/top/top_linux.c b/src/detection/top/top_linux.c new file mode 100644 index 0000000000..be71ab5a8f --- /dev/null +++ b/src/detection/top/top_linux.c @@ -0,0 +1,166 @@ +#include "top.h" + +#include "common/debug.h" +#include "common/io.h" +#include "common/memrchr.h" +#include "common/strutil.h" + +#include + +#define PF_KTHREAD 0x00200000 // defined in kernel include/linux/sched.h + +static bool parseStat(const char* buffer, size_t length, FFTopProcessSnapshot* result) { + const char* open = strchr(buffer, '('); + const char* close = (const char*) memrchr(buffer, ')', length); + if (!open || !close || close <= open || close + 2 >= buffer + length) { + FF_DEBUG("parseStat: malformed stat content"); + return false; + } + + ffStrbufSetNS(&result->name, (uint32_t) (close - open - 1), open + 1); + + const char* cursor = close + 2; // skip ") " + if (__builtin_expect(cursor >= buffer + length, false)) { + return false; + } + + char* p; + for (uint32_t field = 3; field <= 22 /*start time*/; ++field) { + switch (field) { + case 9: // flags + if ((strtoul(cursor, &p, 10) & PF_KTHREAD)) { + FF_DEBUG("parseStat: skipping kernel task %s", result->name.chars); + return false; + } + cursor = p; + break; + + case 14: // user + case 15: // system + result->cpuTime += (uint64_t) strtoull(cursor, &p, 10); + cursor = p; + break; + + case 20: // number of threads + result->threads = (uint32_t) strtoul(cursor, &p, 10); + cursor = p; + break; + + case 22: // start time + result->startTime = (uint64_t) strtoull(cursor, &p, 10); + cursor = p; + break; + + default: + while (*cursor != ' ' && __builtin_expect(*cursor != '\0', true)) { + ++cursor; + } + break; + } + + ++cursor; + if (__builtin_expect(cursor >= buffer + length, false)) { + return false; + } + } + + return true; +} + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) { + const long ticks = sysconf(_SC_CLK_TCK); + const long pageSize = instance.state.platform.sysinfo.pageSize; + FF_DEBUG("Scanning /proc: clk_tck=%ld, pageSize=%ld", ticks, pageSize); + if (ticks <= 0 || pageSize <= 0) { + return "sysconf(_SC_CLK_TCK or _SC_PAGESIZE) failed"; + } + + FF_AUTO_CLOSE_DIR DIR* dir = opendir("/proc"); + if (!dir) { + FF_DEBUG("opendir(\"/proc\") failed: %s", strerror(errno)); + return "opendir(\"/proc\") failed"; + } + + int procfd = dirfd(dir); + + struct dirent* entry; + char statBuffer[1024]; + char statmBuffer[256]; + while ((entry = readdir(dir))) { + if (!entry->d_name[0] || !ffCharIsDigit(entry->d_name[0])) { + continue; + } + + unsigned long pid = strtoul(entry->d_name, nullptr, 10); + if (pid == 0 || pid > UINT32_MAX) { + FF_DEBUG("Skip invalid pid directory: %s", entry->d_name); + continue; + } + + FF_AUTO_CLOSE_FD int subfd = openat(procfd, entry->d_name, O_RDONLY | O_DIRECTORY); + if (subfd < 0) { + FF_DEBUG("openat(/proc/%s) failed: %s", entry->d_name, strerror(errno)); + continue; + } + + ssize_t statLength; + if ((statLength = ffReadFileDataRelative(subfd, "stat", sizeof(statBuffer) - 1, statBuffer)) < 0) { + FF_DEBUG("Failed to read /proc/%s/stat: %s", entry->d_name, strerror(errno)); + continue; + } + statBuffer[statLength] = '\0'; + + ssize_t statmLength = 0; + if (showTypes & FF_TOP_TYPE_MEMORY) { + if ((statmLength = ffReadFileDataRelative(subfd, "statm", sizeof(statmBuffer) - 1, statmBuffer)) < 0) { + FF_DEBUG("Failed to read /proc/%s/statm: %s", entry->d_name, strerror(errno)); + statmLength = 0; + } + statmBuffer[statmLength] = '\0'; + } + + char ioBuffer[512]; + ssize_t ioLength = 0; + if (showTypes & FF_TOP_TYPE_DISK) { + if ((ioLength = ffReadFileDataRelative(subfd, "io", sizeof(ioBuffer) - 1, ioBuffer)) < 0) { + FF_DEBUG("Failed to read /proc/%s/io: %s", entry->d_name, strerror(errno)); + ioLength = 0; + } + ioBuffer[ioLength] = '\0'; + } + + auto snapshot = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + ffStrbufInit(&snapshot->name); + snapshot->threads = 0; + snapshot->cpuTime = 0; + snapshot->startTime = 0; + + if (!parseStat(statBuffer, (size_t) statLength, snapshot)) { + ffStrbufDestroy(&snapshot->name); + --snapshots->length; + continue; + } + + uint64_t rssPages = 0; + if (__builtin_expect((showTypes & FF_TOP_TYPE_MEMORY) && sscanf(statmBuffer, "%*u %" SCNu64, &rssPages) != 1, false)) { + FF_DEBUG("Failed to parse statm of /proc/%lu", pid); + ffStrbufDestroy(&snapshot->name); + --snapshots->length; + continue; + } + + snapshot->pid = (uint32_t) pid; + snapshot->memBytes = rssPages * (uint64_t) pageSize; + snapshot->bytesRead = 0; + snapshot->bytesWritten = 0; + if (ioLength > 0) { + sscanf(ioBuffer, "rchar: %*u\nwchar: %*u\nsyscr: %*u\nsyscw: %*u\nread_bytes: %" SCNu64 "\nwrite_bytes: %" SCNu64, &snapshot->bytesRead, &snapshot->bytesWritten); + } + snapshot->cpuTime = snapshot->cpuTime * 1000u / (uint64_t) ticks; + FF_DEBUG( + "Captured process %u (%s): cpuTime=%" PRIu64 "ms, mem=%" PRIu64 "B, diskRead=%" PRIu64 "B, diskWrite=%" PRIu64 "B", + snapshot->pid, snapshot->name.chars, snapshot->cpuTime, snapshot->memBytes, snapshot->bytesRead, snapshot->bytesWritten); + } + FF_DEBUG("Captured %u process snapshots in total", snapshots->length); + return nullptr; +} diff --git a/src/detection/top/top_nbsd.c b/src/detection/top/top_nbsd.c new file mode 100644 index 0000000000..8c9c4968de --- /dev/null +++ b/src/detection/top/top_nbsd.c @@ -0,0 +1,64 @@ +#include "top.h" +#include "common/mallocHelper.h" + +#include +#include +#include +#include + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) { + int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, -1, sizeof(struct kinfo_proc2), INT_MAX }; + size_t length; + + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC2, KERN_PROC_ALL, nullptr}) failed"; + } + + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc2); + + FF_AUTO_FREE struct kinfo_proc2* processes = malloc(length); + if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC2, KERN_PROC_ALL, processes}) failed"; + } + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc2)); + + uint32_t pageSize = instance.state.platform.sysinfo.pageSize; + + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc2* proc = &processes[i]; + + if ((proc->p_flag & P_SYSTEM) || !proc->p_uvalid) { // Kernel process or zombie + continue; + } + + FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + + ffStrbufInitS(&item->name, proc->p_comm); + item->pid = (uint32_t) proc->p_pid; + + uint64_t userMs = (uint64_t) proc->p_uutime_sec * 1000 + + (uint64_t) proc->p_uutime_usec / 1000; + uint64_t sysMs = (uint64_t) proc->p_ustime_sec * 1000 + + (uint64_t) proc->p_ustime_usec / 1000; + item->cpuTime = userMs + sysMs; + + item->memBytes = (uint64_t) proc->p_vm_rssize * (uint64_t) pageSize; + + item->startTime = (uint64_t) proc->p_ustart_sec * 1000 + + (uint64_t) proc->p_ustart_usec / 1000; + + item->bytesRead = (uint64_t) proc->p_uru_inblock * DEV_BSIZE; + item->bytesWritten = (uint64_t) proc->p_uru_oublock * DEV_BSIZE; + item->threads = 0; + if (showTypes & FF_TOP_TYPE_THREADS) { + int lwpRequest[] = { CTL_KERN, KERN_LWP, proc->p_pid, sizeof(struct kinfo_lwp), 0 }; + size_t lwpLength = 0; + if (sysctl(lwpRequest, ARRAY_SIZE(lwpRequest), nullptr, &lwpLength, nullptr, 0) == 0) { + item->threads = (uint32_t) (lwpLength / sizeof(struct kinfo_lwp)); + } + } + } + + return NULL; +} diff --git a/src/detection/top/top_obsd.c b/src/detection/top/top_obsd.c new file mode 100644 index 0000000000..365ecfe7f3 --- /dev/null +++ b/src/detection/top/top_obsd.c @@ -0,0 +1,61 @@ +#include "top.h" + +#include +#include // DEV_BSIZE +#include +#include + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) { + kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr); + if (!kd) { + return "kvm_open() failed"; + } + + int count = 0; + // KERN_PROC_ALL returns all user-level processes, excluding kernel processes and threads + const struct kinfo_proc* processes = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count); + if (!processes) { + kvm_close(kd); + return "kvm_getprocs() failed"; + } + + const uint32_t pageSize = instance.state.platform.sysinfo.pageSize; + + for (int i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &processes[i]; + + if (!proc->p_uvalid) { // Zombie process; p_u* members are invalid + continue; + } + + FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + ffStrbufInitS(&item->name, proc->p_comm); + item->pid = (uint32_t) proc->p_pid; + item->cpuTime = ((uint64_t) proc->p_uutime_sec * 1000 + (uint64_t) proc->p_uutime_usec / 1000) + + ((uint64_t) proc->p_ustime_sec * 1000 + (uint64_t) proc->p_ustime_usec / 1000); + item->memBytes = (uint64_t) proc->p_vm_rssize * pageSize; + item->startTime = (uint64_t) proc->p_ustart_sec * 1000 + (uint64_t) proc->p_ustart_usec / 1000; + item->bytesRead = (uint64_t) proc->p_uru_inblock * DEV_BSIZE; + item->bytesWritten = (uint64_t) proc->p_uru_oublock * DEV_BSIZE; + item->threads = 0; + } + + if (showTypes & FF_TOP_TYPE_THREADS) { + int threadCount = 0; + const struct kinfo_proc* threads = kvm_getprocs(kd, + KERN_PROC_ALL | KERN_PROC_SHOW_THREADS, 0, sizeof(struct kinfo_proc), &threadCount); + if (threads) { + for (uint32_t i = 0; i < snapshots->length; ++i) { + FFTopProcessSnapshot* item = FF_LIST_GET(FFTopProcessSnapshot, *snapshots, i); + for (int j = 0; j < threadCount; ++j) { + if (threads[j].p_pid == (pid_t) item->pid && threads[j].p_tid != -1) { + ++item->threads; + } + } + } + } + } + + kvm_close(kd); + return nullptr; +} diff --git a/src/detection/top/top_sunos.c b/src/detection/top/top_sunos.c new file mode 100644 index 0000000000..0054ae79a8 --- /dev/null +++ b/src/detection/top/top_sunos.c @@ -0,0 +1,74 @@ +#include "top.h" + +#include "common/io.h" +#include "common/strutil.h" + +#include +#include +#include // DEV_BSIZE + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) { + FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/proc"); + if (!dirp) { + return "opendir(\"/proc\") failed"; + } + + int procfd = dirfd(dirp); + + struct dirent* dp; + while ((dp = readdir(dirp))) { + if (!ffCharIsDigit(dp->d_name[0])) { // Skip "." and ".." + continue; + } + + FF_AUTO_CLOSE_FD int subfd = openat(procfd, dp->d_name, O_RDONLY | O_DIRECTORY); + if (subfd < 0) { + continue; + } + + pstatus_t status; + if (ffReadFileDataRelative(subfd, "status", sizeof(status), &status) != (ssize_t) sizeof(status) || + status.pr_flags & PR_ISSYS) { + continue; // The process may have exited, no permission or is a kernel process + } + + // The data model of the returned structure depends on the data model of + // the calling process, not of the observed process. + psinfo_t psinfo; + if (ffReadFileDataRelative(subfd, "psinfo", sizeof(psinfo), &psinfo) != (ssize_t) sizeof(psinfo)) { + continue; + } + + // zombies + if (psinfo.pr_lwp.pr_sname == 'Z') { + continue; + } + + FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + + ffStrbufInitS(&item->name, psinfo.pr_fname); + + item->pid = (uint32_t) psinfo.pr_pid; + + item->cpuTime = (uint64_t) psinfo.pr_time.tv_sec * 1000 + + (uint64_t) psinfo.pr_time.tv_nsec / 1000000; + + item->memBytes = (uint64_t) psinfo.pr_rssize * 1024; + + item->startTime = (uint64_t) psinfo.pr_start.tv_sec * 1000 + + (uint64_t) psinfo.pr_start.tv_nsec / 1000000; + + item->bytesRead = 0; + item->bytesWritten = 0; + item->threads = (uint32_t) psinfo.pr_nlwp; + if (showTypes & FF_TOP_TYPE_DISK) { + prusage_t usage; + if (ffReadFileDataRelative(subfd, "usage", sizeof(usage), &usage) == (ssize_t) sizeof(usage)) { + item->bytesRead = (uint64_t) usage.pr_inblk * DEV_BSIZE; + item->bytesWritten = (uint64_t) usage.pr_oublk * DEV_BSIZE; + } + } + } + + return nullptr; +} diff --git a/src/detection/top/top_windows.c b/src/detection/top/top_windows.c new file mode 100644 index 0000000000..33a430af73 --- /dev/null +++ b/src/detection/top/top_windows.c @@ -0,0 +1,49 @@ +#include "top.h" + +#include "common/mallocHelper.h" +#include "common/windows/unicode.h" + +#include +#include +#include + +const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) { + FF_AUTO_FREE SYSTEM_PROCESS_INFORMATION* pstart = nullptr; + + // 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"; + } + } + + for (SYSTEM_PROCESS_INFORMATION* info = pstart;; info = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) info + info->NextEntryOffset)) { + uintptr_t pidValue = (uintptr_t) info->UniqueProcessId; + if (pidValue <= UINT32_MAX && (uintptr_t) info->InheritedFromUniqueProcessId > 0) { + FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots); + item->pid = (uint32_t) pidValue; + item->startTime = (uint64_t) info->CreateTime.QuadPart; + item->cpuTime = ((uint64_t) info->UserTime.QuadPart + (uint64_t) info->KernelTime.QuadPart) / 10000u; + item->memBytes = (uint64_t) info->VirtualMemoryCounters.WorkingSetSize; + item->bytesRead = (uint64_t) info->IoCounters.ReadTransferCount; + item->bytesWritten = (uint64_t) info->IoCounters.WriteTransferCount; + item->threads = info->NumberOfThreads; + ffStrbufInitNWS(&item->name, info->ImageName.Length / sizeof(wchar_t), info->ImageName.Buffer); + } + if (info->NextEntryOffset == 0) { + break; + } + } + return nullptr; +} diff --git a/src/detection/wifi/wifi.h b/src/detection/wifi/wifi.h index e457295c36..faee5de46a 100644 --- a/src/detection/wifi/wifi.h +++ b/src/detection/wifi/wifi.h @@ -18,7 +18,8 @@ struct FFWifiConnection { double rxRate; double txRate; uint16_t channel; - uint16_t frequency; // MHz + uint16_t channelWidth; // MHz + uint16_t frequency; // MHz }; typedef struct FFWifiResult { @@ -60,3 +61,18 @@ static inline uint16_t ffWifiFreqToChannel(uint16_t frequency) { return 0; } + +static inline uint16_t ffWifiChannelToFreq(uint16_t channel) { + // Inverse of ffWifiFreqToChannel for the common 2.4 GHz and 5 GHz bands. + // 4.9 GHz and 6 GHz channels overlap numerically, so they are not handled here. + if (channel == 14) { + return 2484; + } + if (channel >= 1 && channel <= 13) { + return (uint16_t) (2407 + 5 * channel); + } + if (channel >= 36 && channel <= 177) { // 5 GHz: 5000 + 5*channel covers 36..177 + return (uint16_t) (5000 + 5 * channel); + } + return 0; +} diff --git a/src/detection/wifi/wifi_android.c b/src/detection/wifi/wifi_android.c index 4fbc4afb20..0fd5b3a9d5 100644 --- a/src/detection/wifi/wifi_android.c +++ b/src/detection/wifi/wifi_android.c @@ -42,6 +42,7 @@ const char* ffDetectWifi(FFlist* result) { item->conn.rxRate = -DBL_MAX; item->conn.txRate = -DBL_MAX; item->conn.channel = 0; + item->conn.channelWidth = 0; item->conn.frequency = 0; ffStrbufAppendJsonVal(&item->inf.status, yyjson_obj_get(root, "supplicant_state")); diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index f718179bd4..3807ef0e8a 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -1,26 +1,28 @@ #include "wifi.h" #include "common/processing.h" #include "common/strutil.h" +#include "common/apple/cf_helpers.h" #import +#import +#import -static inline double rssiToSignalQuality(int rssi) -{ - return (double) (rssi >= -50 ? 100 : rssi <= -100 ? 0 : (rssi + 100) * 2); +static inline double rssiToSignalQuality(int rssi) { + return (double) (rssi >= -50 ? 100 : rssi <= -100 ? 0 + : (rssi + 100) * 2); } -@interface CWNetworkProfile() -@property(readonly, retain, nullable) NSArray *bssidList; +@interface CWNetworkProfile () +@property (readonly, retain, nullable) NSArray* bssidList; @end -const char* ffDetectWifi(FFlist* result) -{ +const char* ffDetectWifi(FFlist* result) { NSArray* interfaces = CWWiFiClient.sharedWiFiClient.interfaces; - if (!interfaces) + if (!interfaces) { return "CWWiFiClient.sharedWiFiClient.interfaces is nil"; + } - for (CWInterface* inf in interfaces) - { + for (CWInterface* inf in interfaces) { FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result); ffStrbufInit(&item->inf.description); ffStrbufInit(&item->inf.status); @@ -33,37 +35,42 @@ @interface CWNetworkProfile() item->conn.rxRate = -DBL_MAX; item->conn.txRate = -DBL_MAX; item->conn.channel = 0; + item->conn.channelWidth = 0; item->conn.frequency = 0; - ffStrbufAppendS(&item->inf.description, inf.interfaceName.UTF8String); + ffStrbufSetS(&item->inf.description, inf.interfaceName.UTF8String); ffStrbufSetStatic(&item->inf.status, inf.powerOn ? "Power On" : "Power Off"); - if(!inf.powerOn) + + if (!inf.powerOn) { continue; + } ffStrbufSetStatic(&item->conn.status, inf.interfaceMode != kCWInterfaceModeNone ? "Active" : "Inactive"); - if(inf.interfaceMode == kCWInterfaceModeNone) + if (inf.interfaceMode == kCWInterfaceModeNone) { continue; + } FF_STRBUF_AUTO_DESTROY ipconfig = ffStrbufCreate(); CWNetworkProfile* networkProfile = inf.configuration.networkProfiles.firstObject; - if (inf.ssid) // https://developer.apple.com/forums/thread/732431 + if (inf.ssid) { // https://developer.apple.com/forums/thread/732431 ffStrbufAppendS(&item->conn.ssid, inf.ssid.UTF8String); - else if (networkProfile.ssid) + } else if (networkProfile.ssid) { ffStrbufSetStatic(&item->conn.ssid, inf.configuration.networkProfiles.firstObject.ssid.UTF8String); - else + } else { ffStrbufSetStatic(&item->conn.ssid, ""); // https://developer.apple.com/forums/thread/732431 + } - if (inf.bssid) + if (inf.bssid) { ffStrbufAppendS(&item->conn.bssid, inf.bssid.UTF8String); - else if (networkProfile.bssidList) + } else if (networkProfile.bssidList) { ffStrbufSetStatic(&item->conn.bssid, [networkProfile.bssidList.firstObject[@"BSSID"] UTF8String]); - else + } else { ffStrbufSetStatic(&item->conn.bssid, ""); + } - switch(inf.activePHYMode) - { + switch (inf.activePHYMode) { case kCWPHYModeNone: ffStrbufSetStatic(&item->conn.protocol, "none"); break; @@ -89,15 +96,15 @@ @interface CWNetworkProfile() ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)"); break; default: - if (inf.activePHYMode < 8) + if (inf.activePHYMode < 8) { ffStrbufAppendF(&item->conn.protocol, "Unknown (%ld)", inf.activePHYMode); + } break; } item->conn.signalQuality = rssiToSignalQuality((int) inf.rssiValue); item->conn.txRate = inf.transmitRate; - switch(inf.security) - { + switch (inf.security) { case kCWSecurityNone: ffStrbufSetStatic(&item->conn.security, "Insecure"); break; @@ -152,12 +159,33 @@ @interface CWNetworkProfile() } item->conn.channel = (uint16_t) inf.wlanChannel.channelNumber; - switch (inf.wlanChannel.channelBand) - { - case kCWChannelBand2GHz: item->conn.frequency = 2400; break; - case kCWChannelBand5GHz: item->conn.frequency = 5400; break; - case 3 /*kCWChannelBand6GHz*/: item->conn.frequency = 6400; break; - default: item->conn.frequency = 0; break; + switch (inf.wlanChannel.channelWidth) { + case kCWChannelWidth20MHz: item->conn.channelWidth = 20; break; + case kCWChannelWidth40MHz: item->conn.channelWidth = 40; break; + case kCWChannelWidth80MHz: item->conn.channelWidth = 80; break; + case kCWChannelWidth160MHz: item->conn.channelWidth = 160; break; + default: item->conn.channelWidth = 0; break; + } + + FF_IOOBJECT_AUTO_RELEASE io_object_t service = IOServiceGetMatchingService(MACH_PORT_NULL, IOBSDNameMatching(MACH_PORT_NULL, 0, item->inf.description.chars)); + FF_CFTYPE_AUTO_RELEASE CFNumberRef frequency; + // Note: IO80211ChannelFrequency is only available when the interface is connected + while (!(frequency = IORegistryEntryCreateCFProperty(service, CFSTR("IO80211ChannelFrequency"), nullptr, kNilOptions))) { + io_object_t parentService = IO_OBJECT_NULL; + if (IORegistryEntryGetParentEntry(service, kIOServicePlane, &parentService) == KERN_SUCCESS) { + IOObjectRelease(service); + service = parentService; + } else { + IOObjectRelease(service); + service = IO_OBJECT_NULL; + break; + } + } + if (frequency) { + int64_t value; + if (ffCfNumGetInt64(frequency, &value) == nil) { + item->conn.frequency = (uint16_t) value; + } } } diff --git a/src/detection/wifi/wifi_bsd.c b/src/detection/wifi/wifi_bsd.c index 7ef816ad5d..4b55d0257a 100644 --- a/src/detection/wifi/wifi_bsd.c +++ b/src/detection/wifi/wifi_bsd.c @@ -37,6 +37,7 @@ const char* ffDetectWifi(FFlist* result) { item->conn.rxRate = -DBL_MAX; item->conn.txRate = -DBL_MAX; item->conn.channel = 0; + item->conn.channelWidth = 0; item->conn.frequency = 0; char ssid[IEEE80211_NWID_LEN + 1] = {}; @@ -80,6 +81,16 @@ const char* ffDetectWifi(FFlist* result) { item->conn.channel = curchan.ic_ieee; item->conn.frequency = curchan.ic_freq; + // Determine channel width from channel flags + if (curchan.ic_flags & IEEE80211_CHAN_VHT160) + item->conn.channelWidth = 160; + else if (curchan.ic_flags & IEEE80211_CHAN_VHT80) + item->conn.channelWidth = 80; + else if (curchan.ic_flags & IEEE80211_CHAN_HT40) + item->conn.channelWidth = 40; + else + item->conn.channelWidth = 20; + #ifdef IEEE80211_IS_CHAN_HE // for future use if (IEEE80211_IS_CHAN_HE(&curchan)) { ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); diff --git a/src/detection/wifi/wifi_haiku.cpp b/src/detection/wifi/wifi_haiku.cpp new file mode 100644 index 0000000000..309cfcd5a1 --- /dev/null +++ b/src/detection/wifi/wifi_haiku.cpp @@ -0,0 +1,101 @@ +extern "C" { + #include "wifi.h" +} + +#include +#include +#include + +static void initWifiResult(FFWifiResult* item, const char* name) { + ffStrbufInitS(&item->inf.description, name); + ffStrbufInit(&item->inf.status); + ffStrbufInit(&item->conn.status); + ffStrbufInit(&item->conn.ssid); + ffStrbufInit(&item->conn.bssid); + ffStrbufInit(&item->conn.protocol); + ffStrbufInit(&item->conn.security); + item->conn.signalQuality = -DBL_MAX; + item->conn.rxRate = -DBL_MAX; + item->conn.txRate = -DBL_MAX; + item->conn.channel = 0; + item->conn.channelWidth = 0; + item->conn.frequency = 0; +} + +static void setWifiSecurity(FFstrbuf* security, const wireless_network& network) { + switch (network.authentication_mode) { + case B_NETWORK_AUTHENTICATION_NONE: + if (network.flags & B_NETWORK_IS_ENCRYPTED) { + ffStrbufSetStatic(security, "Encrypted"); + } else { + ffStrbufSetStatic(security, "Insecure"); + } + break; + case B_NETWORK_AUTHENTICATION_WEP: + ffStrbufSetStatic(security, "WEP"); + break; + case B_NETWORK_AUTHENTICATION_WPA: + ffStrbufSetStatic(security, "WPA"); + break; + case B_NETWORK_AUTHENTICATION_WPA2: + ffStrbufSetStatic(security, "WPA2"); + break; + case B_NETWORK_AUTHENTICATION_EAP: + ffStrbufSetStatic(security, "WPA Enterprise"); + break; + default: + ffStrbufSetF(security, "Unknown (%lu)", (unsigned long) network.authentication_mode); + break; + } +} + +static void setWifiBssid(FFstrbuf* bssid, const wireless_network& network) { + const uint8* address = network.address.LinkLevelAddress(); + if (address == nullptr || network.address.LinkLevelAddressLength() < 6) { + return; + } + + ffStrbufSetF(bssid, "%02X:%02X:%02X:%02X:%02X:%02X", + address[0], address[1], address[2], address[3], address[4], address[5]); +} + +const char* ffDetectWifi(FFlist* result) { + BNetworkRoster& roster = BNetworkRoster::Default(); + BNetworkInterface interface; + uint32 cookie = 0; + + while (roster.GetNextInterface(&cookie, interface) == B_OK) { + if (!interface.Exists()) { + continue; + } + + BNetworkDevice device(interface.Name()); + if (!device.Exists() || !device.IsWireless()) { + continue; + } + + FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result); + initWifiResult(item, interface.Name()); + + ffStrbufSetStatic(&item->inf.status, interface.HasLink() ? "Up" : "Down"); + if (!interface.HasLink() || !device.HasLink()) { + ffStrbufSetStatic(&item->conn.status, "Not connected"); + continue; + } + + ffStrbufSetStatic(&item->conn.status, "Connected"); + + uint32 networkCookie = 0; + wireless_network network = {}; + if (device.GetNextAssociatedNetwork(networkCookie, network) != B_OK) { + continue; + } + + ffStrbufSetNS(&item->conn.ssid, (uint32_t) strnlen(network.name, sizeof(network.name)), network.name); + setWifiBssid(&item->conn.bssid, network); + item->conn.signalQuality = network.signal_strength > 100 ? 100 : network.signal_strength; + setWifiSecurity(&item->conn.security, network); + } + + return nullptr; +} diff --git a/src/detection/wifi/wifi_linux.c b/src/detection/wifi/wifi_linux.c index c609c25c41..c0a56ede40 100644 --- a/src/detection/wifi/wifi_linux.c +++ b/src/detection/wifi/wifi_linux.c @@ -1,4 +1,5 @@ #include "wifi.h" +#include "common/endian.h" #include "common/io.h" #include "common/debug.h" #include "common/strutil.h" @@ -11,12 +12,11 @@ #include #include -#if !__BIG_ENDIAN__ - #include - #include +#include +#include - // Silence warning of `NLA_HDRLEN` and `NLA_ALIGN` - #pragma GCC diagnostic ignored "-Wsign-conversion" +// Silence warning of `NLA_HDRLEN` and `NLA_ALIGN` +#pragma GCC diagnostic ignored "-Wsign-conversion" typedef struct FFWifiNlContext { int sockFd; @@ -212,8 +212,9 @@ static bool ffWifiNlInit(FFWifiNlContext* ctx) { return true; } -static double ffWifiParseBitrateFromRateInfo(const struct nlattr* rateAttr, FFstrbuf* protocol) { +static double ffWifiParseBitrateFromRateInfo(const struct nlattr* rateAttr, FFstrbuf* protocol, uint16_t* channelWidth) { double rate = -DBL_MAX; + uint16_t width = 0; size_t remaining = ffWifiNlAttrPayload(rateAttr); for (const struct nlattr* info = (const struct nlattr*) ffWifiNlAttrData(rateAttr); @@ -223,6 +224,40 @@ static double ffWifiParseBitrateFromRateInfo(const struct nlattr* rateAttr, FFst size_t payload = ffWifiNlAttrPayload(info); switch (type) { + case NL80211_RATE_INFO_40_MHZ_WIDTH: + width = 40; + break; + case NL80211_RATE_INFO_80_MHZ_WIDTH: + case NL80211_RATE_INFO_80P80_MHZ_WIDTH: + width = 80; + break; + case NL80211_RATE_INFO_160_MHZ_WIDTH: + width = 160; + break; + case 18 /* NL80211_RATE_INFO_320_MHZ_WIDTH */: + width = 320; + break; + case NL80211_RATE_INFO_10_MHZ_WIDTH: + width = 10; + break; + case NL80211_RATE_INFO_5_MHZ_WIDTH: + width = 5; + break; + case 25 /* NL80211_RATE_INFO_1_MHZ_WIDTH */: + width = 1; + break; + case 26 /* NL80211_RATE_INFO_2_MHZ_WIDTH */: + width = 2; + break; + case 27 /* NL80211_RATE_INFO_4_MHZ_WIDTH */: + width = 4; + break; + case 28 /* NL80211_RATE_INFO_8_MHZ_WIDTH */: + width = 8; + break; + case 29 /* NL80211_RATE_INFO_16_MHZ_WIDTH */: + width = 16; + break; case 30 /* NL80211_RATE_INFO_UHR_MCS */: ffStrbufSetStatic(protocol, "802.11bn (Wi-Fi 8)"); break; @@ -254,9 +289,126 @@ static double ffWifiParseBitrateFromRateInfo(const struct nlattr* rateAttr, FFst } } + if (rate != -DBL_MAX && *channelWidth == 0) { + *channelWidth = width; + } + return rate; } +static uint16_t ffWifiChannelWidthToMhz(uint32_t width) { + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return 20; + case NL80211_CHAN_WIDTH_40: + return 40; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + return 80; + case NL80211_CHAN_WIDTH_160: + return 160; + case 6 /* NL80211_CHAN_WIDTH_5 */: + return 5; + case 7 /* NL80211_CHAN_WIDTH_10 */: + return 10; + case 8 /* NL80211_CHAN_WIDTH_1 */: + return 1; + case 9 /* NL80211_CHAN_WIDTH_2 */: + return 2; + case 10 /* NL80211_CHAN_WIDTH_4 */: + return 4; + case 11 /* NL80211_CHAN_WIDTH_8 */: + return 8; + case 12 /* NL80211_CHAN_WIDTH_16 */: + return 16; + case 13 /* NL80211_CHAN_WIDTH_320 */: + return 320; + default: + return 0; + } +} + +static bool ffWifiFetchInterfaceInfo(FFWifiNlContext* ctx, FFWifiResult* item, uint32_t ifIndex) { + struct { + struct nlmsghdr nlh; + struct genlmsghdr genl; + char attrs[32]; + } req = { + .nlh = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr)), + .nlmsg_type = ctx->nl80211FamilyId, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, + .nlmsg_seq = ++ctx->seq, + .nlmsg_pid = ctx->portId, + }, + .genl = { + .cmd = NL80211_CMD_GET_INTERFACE, + .version = 0, + }, + }; + + if (!ffWifiNlAppendAttr(&req.nlh, sizeof(req), NL80211_ATTR_IFINDEX, &ifIndex, sizeof(ifIndex))) { + FF_DEBUG("Failed to build nl80211 interface request"); + return false; + } + + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + }; + + ssize_t sent = sendto(ctx->sockFd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr*) &addr, sizeof(addr)); + if (sent != (ssize_t) req.nlh.nlmsg_len) { + FF_DEBUG("Failed to send nl80211 interface request"); + return false; + } + + uint8_t buffer[8192]; + while (true) { + ssize_t received = recvfrom(ctx->sockFd, buffer, sizeof(buffer), 0, nullptr, nullptr); + if (received < 0) { + FF_DEBUG("Failed to receive nl80211 interface reply: %s", strerror(errno)); + return false; + } + + for (const struct nlmsghdr* nlh = (const struct nlmsghdr*) buffer; + NLMSG_OK(nlh, received); + nlh = NLMSG_NEXT(nlh, received)) { + if (nlh->nlmsg_seq != req.nlh.nlmsg_seq) { + continue; + } + + if (nlh->nlmsg_type == NLMSG_ERROR) { + const struct nlmsgerr* err = (const struct nlmsgerr*) NLMSG_DATA(nlh); + if (err->error != 0) { + FF_DEBUG("nl80211 interface request failed: %s", strerror(-err->error)); + } + return false; + } + + if (nlh->nlmsg_type != ctx->nl80211FamilyId) { + continue; + } + + const struct genlmsghdr* genl = (const struct genlmsghdr*) NLMSG_DATA(nlh); + size_t attrRemaining = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; + for (const struct nlattr* attr = (const struct nlattr*) ((const char*) genl + GENL_HDRLEN); + ffWifiNlAttrOk(attr, attrRemaining); + attr = ffWifiNlAttrNext(attr, &attrRemaining)) { + if ((attr->nla_type & NLA_TYPE_MASK) != NL80211_ATTR_CHANNEL_WIDTH || + ffWifiNlAttrPayload(attr) < sizeof(uint32_t)) { + continue; + } + + item->conn.channelWidth = ffWifiChannelWidthToMhz(*(const uint32_t*) ffWifiNlAttrData(attr)); + return item->conn.channelWidth != 0; + } + + return false; + } + } +} + static void ffWifiApplySecurityFlags(FFWifiResult* item, const FFWifiSecurityFlags* sec) { ffStrbufClear(&item->conn.security); @@ -307,7 +459,7 @@ static void ffWifiParseRsnIe(const uint8_t* ie, size_t len, FFWifiSecurityFlags* if (pos + 2 > len) { // pairwise cipher suite count field length return; } - uint16_t pairwiseCount = *(uint16_t*) (ie + pos); + uint16_t pairwiseCount = FF_READ_LE(*(const uint16_t*) (ie + pos)); pos += 2; // skip pairwise cipher suite count field size_t pairwiseLen = (size_t) pairwiseCount * 4; // each suite selector is 4 bytes @@ -319,7 +471,7 @@ static void ffWifiParseRsnIe(const uint8_t* ie, size_t len, FFWifiSecurityFlags* if (pos + 2 > len) { // AKM suite count field length return; } - uint16_t akmCount = *(uint16_t*) (ie + pos); + uint16_t akmCount = FF_READ_LE(*(const uint16_t*) (ie + pos)); pos += 2; // skip AKM suite count field for (uint16_t i = 0; i < akmCount && pos + 4 <= len; ++i, pos += 4) { // each AKM suite selector is 4 bytes @@ -376,13 +528,13 @@ static void ffWifiParseWpaVendorIe(const uint8_t* ie, size_t len, FFWifiSecurity if (pos + 2 > len) { // unicast cipher suite count field length return; } - uint16_t pairwiseCount = *(uint16_t*) (ie + pos); + uint16_t pairwiseCount = FF_READ_LE(*(const uint16_t*) (ie + pos)); pos += 2 + (size_t) pairwiseCount * 4; // count field(2) + N unicast suite selectors(4 each) if (pos + 2 > len) { // AKM suite count field length return; } - uint16_t akmCount = *(uint16_t*) (ie + pos); + uint16_t akmCount = FF_READ_LE(*(const uint16_t*) (ie + pos)); pos += 2; // skip AKM suite count field for (uint16_t i = 0; i < akmCount && pos + 4 <= len; ++i, pos += 4) { // each AKM suite selector is 4 bytes @@ -566,13 +718,13 @@ static void ffWifiParseStationInfo(const struct nlattr* staInfoAttr, FFWifiResul if (type == NL80211_STA_INFO_SIGNAL && payload >= sizeof(uint8_t) && item->conn.signalQuality == -DBL_MAX) { int rssi = (int8_t) *(const uint8_t*) ffWifiNlAttrData(attr); item->conn.signalQuality = rssiToSignalQuality(rssi); - } else if (type == NL80211_STA_INFO_TX_BITRATE && item->conn.txRate == -DBL_MAX) { - double tx = ffWifiParseBitrateFromRateInfo(attr, &item->conn.protocol); + } else if (type == NL80211_STA_INFO_TX_BITRATE && (item->conn.txRate == -DBL_MAX || item->conn.channelWidth == 0)) { + double tx = ffWifiParseBitrateFromRateInfo(attr, &item->conn.protocol, &item->conn.channelWidth); if (tx != -DBL_MAX) { item->conn.txRate = tx; } - } else if (type == NL80211_STA_INFO_RX_BITRATE && item->conn.rxRate == -DBL_MAX) { - double rx = ffWifiParseBitrateFromRateInfo(attr, &item->conn.protocol); + } else if (type == NL80211_STA_INFO_RX_BITRATE && (item->conn.rxRate == -DBL_MAX || item->conn.channelWidth == 0)) { + double rx = ffWifiParseBitrateFromRateInfo(attr, &item->conn.protocol, &item->conn.channelWidth); if (rx != -DBL_MAX) { item->conn.rxRate = rx; } @@ -680,6 +832,7 @@ static const char* detectWithNetlink(FFWifiNlContext* ctx, FFWifiResult* item, u if (ffWifiFetchScanInfo(ctx, item, ifIndex)) { FF_DEBUG("found associated BSS: %s", item->conn.ssid.chars); ffStrbufSetStatic(&item->conn.status, "connected"); + ffWifiFetchInterfaceInfo(ctx, item, ifIndex); ffWifiFetchStationInfo(ctx, item, ifIndex); if (!item->conn.protocol.length && item->conn.txRate != -DBL_MAX) { FF_DEBUG("nl80211 station info did not include MCS family fields"); @@ -692,8 +845,6 @@ static const char* detectWithNetlink(FFWifiNlContext* ctx, FFWifiResult* item, u FF_DEBUG("Netlink wifi detection completed"); return nullptr; } -#endif - typedef struct FFWifiIcContext { int sockFd; } FFWifiIcContext; @@ -896,9 +1047,7 @@ const char* ffDetectWifi(FFlist* result) { return "if_nameindex() failed"; } -#if !__BIG_ENDIAN__ FFWifiNlContext nl = { .sockFd = -1 }; -#endif FFWifiIcContext ic = { .sockFd = -1 }; FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); @@ -923,6 +1072,7 @@ const char* ffDetectWifi(FFlist* result) { item->conn.rxRate = -DBL_MAX; item->conn.txRate = -DBL_MAX; item->conn.channel = 0; + item->conn.channelWidth = 0; item->conn.frequency = 0; char operstate; @@ -936,9 +1086,7 @@ const char* ffDetectWifi(FFlist* result) { if (operstate == 'u') { ffStrbufSetStatic(&item->inf.status, "up"); -#if !__BIG_ENDIAN__ detectWithNetlink(&nl, item, i->if_index); -#endif detectWithIoctl(&ic, item, i->if_name); } else { ffStrbufSetStatic(&item->conn.status, "disconnected"); @@ -962,11 +1110,9 @@ const char* ffDetectWifi(FFlist* result) { } if_freenameindex(infs); -#if !__BIG_ENDIAN__ if (nl.sockFd >= 0) { close(nl.sockFd); } -#endif if (ic.sockFd >= 0) { close(ic.sockFd); } diff --git a/src/detection/wifi/wifi_nbsd.c b/src/detection/wifi/wifi_nbsd.c index ea8c2ace76..e2b0836790 100644 --- a/src/detection/wifi/wifi_nbsd.c +++ b/src/detection/wifi/wifi_nbsd.c @@ -47,6 +47,7 @@ const char* ffDetectWifi(FFlist* result) { item->conn.rxRate = -DBL_MAX; item->conn.txRate = -DBL_MAX; item->conn.channel = 0; + item->conn.channelWidth = 0; item->conn.frequency = 0; char ssid[IEEE80211_NWID_LEN + 1] = {}; @@ -91,6 +92,16 @@ const char* ffDetectWifi(FFlist* result) { item->conn.channel = ffWifiFreqToChannel(curchan.ic_freq); item->conn.frequency = curchan.ic_freq; + // Determine channel width from channel flags + if (curchan.ic_flags & IEEE80211_CHAN_VHT160) + item->conn.channelWidth = 160; + else if (curchan.ic_flags & IEEE80211_CHAN_VHT80) + item->conn.channelWidth = 80; + else if (curchan.ic_flags & IEEE80211_CHAN_HT40) + item->conn.channelWidth = 40; + else + item->conn.channelWidth = 20; + #ifdef IEEE80211_IS_CHAN_HE // for future use if (IEEE80211_IS_CHAN_HE(&curchan)) { ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); diff --git a/src/detection/wifi/wifi_obsd.c b/src/detection/wifi/wifi_obsd.c index bcaec97fd9..a9817c0511 100644 --- a/src/detection/wifi/wifi_obsd.c +++ b/src/detection/wifi/wifi_obsd.c @@ -1,6 +1,5 @@ #include "wifi.h" #include "common/io.h" -#include "common/strutil.h" #include #include @@ -17,12 +16,20 @@ const char* ffDetectWifi(FFlist* result) { FF_AUTO_CLOSE_FD int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { + if_freenameindex(infs); return "socket() failed"; } for (struct if_nameindex* i = infs; !(i->if_index == 0 && i->if_name == nullptr); ++i) { - if (!ffStrStartsWith(i->if_name, "iwm")) { - continue; + struct ieee80211_nodereq nr = {}; + strlcpy(nr.nr_ifname, i->if_name, sizeof(nr.nr_ifname)); + + int err = 0; + if (ioctl(sock, SIOCG80211NODE, &nr) < 0) { + err = errno; + if (err == ENXIO || err == ENODEV || err == EINVAL || err == ENOTTY) { + continue; // Not a Wi-Fi interface + } } FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result); @@ -37,11 +44,9 @@ const char* ffDetectWifi(FFlist* result) { item->conn.rxRate = -DBL_MAX; item->conn.txRate = -DBL_MAX; item->conn.channel = 0; + item->conn.channelWidth = 0; // not exposed by ieee80211_nodereq item->conn.frequency = 0; - struct ieee80211_nodereq nr = {}; - strlcpy(nr.nr_ifname, i->if_name, sizeof(nr.nr_ifname)); - struct ifreq ifr = {}; strlcpy(ifr.ifr_name, i->if_name, sizeof(ifr.ifr_name)); if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { @@ -50,31 +55,34 @@ const char* ffDetectWifi(FFlist* result) { ffStrbufSetStatic(&item->inf.status, ifr.ifr_flags & IFF_UP ? "Up" : "Down"); } - if (ioctl(sock, SIOCG80211NODE, &nr) < 0) { - ffStrbufSetStatic(&item->conn.status, "Not associated"); - continue; - } - - if (nr.nr_nwid_len > 0) { - ffStrbufSetStatic(&item->conn.status, "Associated"); - ffStrbufAppendNS(&item->conn.ssid, nr.nr_nwid_len, (char*) nr.nr_nwid); - } else { + if (err != 0 || nr.nr_nwid_len == 0) { ffStrbufSetStatic(&item->conn.status, "Not associated"); continue; } + ffStrbufSetStatic(&item->conn.status, "Associated"); + ffStrbufAppendNS(&item->conn.ssid, nr.nr_nwid_len, (char*) nr.nr_nwid); ffStrbufSetF(&item->conn.bssid, "%02X:%02X:%02X:%02X:%02X:%02X", nr.nr_bssid[0], nr.nr_bssid[1], nr.nr_bssid[2], nr.nr_bssid[3], nr.nr_bssid[4], nr.nr_bssid[5]); item->conn.channel = nr.nr_channel; + if (item->conn.channel == 0) { + struct ieee80211chanreq chreq = {}; + strlcpy(chreq.i_name, i->if_name, sizeof(chreq.i_name)); + if (ioctl(sock, SIOCG80211CHANNEL, &chreq) >= 0) { + item->conn.channel = chreq.i_channel; + } + } + item->conn.frequency = ffWifiChannelToFreq(item->conn.channel); if (nr.nr_max_rssi) { - item->conn.signalQuality = ((float) nr.nr_rssi / nr.nr_max_rssi) * 100.0; + item->conn.signalQuality = ((double) nr.nr_rssi / (double) nr.nr_max_rssi) * 100.0; } - if (nr.nr_flags & IEEE80211_NODEREQ_HT) { - ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)"); - } else if (nr.nr_flags & IEEE80211_NODEREQ_VHT) { + // VHT implies HT, so test VHT first + if (nr.nr_flags & IEEE80211_NODEREQ_VHT) { ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); + } else if (nr.nr_flags & IEEE80211_NODEREQ_HT) { + ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)"); } else if (nr.nr_chan_flags & IEEE80211_CHANINFO_5GHZ) { ffStrbufSetStatic(&item->conn.protocol, "802.11a"); } else if (nr.nr_chan_flags & IEEE80211_CHANINFO_2GHZ) { @@ -85,15 +93,22 @@ const char* ffDetectWifi(FFlist* result) { strlcpy(wpa.i_name, i->if_name, sizeof(wpa.i_name)); if (ioctl(sock, SIOCG80211WPAPARMS, &wpa) >= 0 && wpa.i_enabled) { - if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA2) { + if (wpa.i_akms & IEEE80211_WPA_AKM_SAE) { + if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA2) { + ffStrbufSetStatic(&item->conn.security, "WPA2/WPA3"); + } else { + ffStrbufSetStatic(&item->conn.security, "WPA3"); + } + } else if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA2) { ffStrbufSetStatic(&item->conn.security, "WPA2"); } else if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA1) { ffStrbufSetStatic(&item->conn.security, "WPA"); + } else { + ffStrbufSetStatic(&item->conn.security, "Unknown"); } } else { struct ieee80211_nwkey nwkey = {}; strlcpy(nwkey.i_name, i->if_name, sizeof(nwkey.i_name)); - if (ioctl(sock, SIOCG80211NWKEY, &nwkey) >= 0) { if (nwkey.i_wepon) { ffStrbufSetStatic(&item->conn.security, "WEP"); diff --git a/src/detection/wifi/wifi_sunos.c b/src/detection/wifi/wifi_sunos.c new file mode 100644 index 0000000000..0b2e24ae21 --- /dev/null +++ b/src/detection/wifi/wifi_sunos.c @@ -0,0 +1,195 @@ +#include "wifi.h" +#include "common/io.h" +#include "common/mallocHelper.h" + +#include +#include +#include +#include +#include + +#include + +static bool getWifiValue(int fd, uint32_t id, void* value, size_t valueSize, wldp_t* buffer) { + memset(buffer, 0, MAX_BUF_LEN); + buffer->wldp_type = NET_802_11; + buffer->wldp_id = id; + + struct strioctl request = { + .ic_cmd = WLAN_GET_PARAM, + .ic_timout = 0, + .ic_len = MAX_BUF_LEN, + .ic_dp = (char*) buffer, + }; + + if (ioctl(fd, I_STR, &request) != 0 || buffer->wldp_result != WL_SUCCESS || buffer->wldp_length < WIFI_BUF_OFFSET + valueSize || buffer->wldp_length > MAX_BUF_LEN) { + return false; + } + + memcpy(value, buffer->wldp_buf, valueSize); + return true; +} + +static void setWifiProtocol(FFstrbuf* protocol, uint32_t subtype, bool htEnabled) { + switch (subtype) { + case WL_FHSS: + ffStrbufSetStatic(protocol, "802.11 (FHSS)"); + break; + case WL_DSSS: + ffStrbufSetStatic(protocol, "802.11 (DSSS)"); + break; + case WL_IRBASE: + ffStrbufSetStatic(protocol, "802.11 (IR)"); + break; + case WL_OFDM: + ffStrbufSetStatic(protocol, htEnabled ? "802.11n (Wi-Fi 4)" : "802.11a"); + break; + case WL_HRDS: + ffStrbufSetStatic(protocol, "802.11b"); + break; + case WL_ERP: + ffStrbufSetStatic(protocol, htEnabled ? "802.11n (Wi-Fi 4)" : "802.11g"); + break; + default: + break; + } +} + +static void setWifiSecurity(FFstrbuf* security, uint32_t encryption, bool haveAuthMode, uint32_t authMode) { + switch (encryption) { + case WL_NOENCRYPTION: + ffStrbufSetStatic(security, "Insecure"); + break; + case WL_ENC_WEP: + ffStrbufSetStatic(security, "WEP"); + break; + case WL_ENC_WPA: + ffStrbufSetStatic(security, haveAuthMode && authMode == WL_SHAREDKEY ? "WPA Shared Key" : "WPA"); + break; + default: + ffStrbufSetStatic(security, "Unknown"); + break; + } +} + +const char* ffDetectWifi(FFlist* result) { + // https://github.com/illumos/illumos-gate/blob/master/usr/src/cmd/cmd-inet/usr.sbin/wificonfig/wificonfig.c + + FF_AUTO_CLOSE_DIR DIR* directory = opendir("/dev/wifi"); + if (!directory) { + return nullptr; + } + + FF_AUTO_FREE void* bufferMemory = malloc(MAX_BUF_LEN); + if (!bufferMemory) { + return "malloc() failed"; + } + wldp_t* buffer = (wldp_t*) bufferMemory; + + struct dirent* entry; + while ((entry = readdir(directory)) != nullptr) { + size_t nameLength = strlen(entry->d_name); + if (nameLength == 0 || entry->d_name[nameLength - 1] < '0' || entry->d_name[nameLength - 1] > '9') { + continue; + } + + char devicePath[PATH_MAX]; + int pathLength = snprintf(devicePath, sizeof(devicePath), "/dev/wifi/%s", entry->d_name); + if (pathLength < 0 || (size_t) pathLength >= sizeof(devicePath)) { + continue; + } + + FF_AUTO_CLOSE_FD int fd = open(devicePath, O_RDWR | O_CLOEXEC); + if (fd < 0) { + continue; + } + if (!isastream(fd)) { + continue; + } + + FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result); + ffStrbufInitS(&item->inf.description, entry->d_name); + ffStrbufInit(&item->inf.status); + ffStrbufInit(&item->conn.status); + ffStrbufInit(&item->conn.ssid); + ffStrbufInit(&item->conn.bssid); + ffStrbufInit(&item->conn.protocol); + ffStrbufInit(&item->conn.security); + item->conn.signalQuality = -DBL_MAX; + item->conn.rxRate = -DBL_MAX; + item->conn.txRate = -DBL_MAX; + item->conn.channel = 0; + item->conn.channelWidth = 0; + item->conn.frequency = 0; + + wl_radio_t radio; + if (getWifiValue(fd, WL_RADIO, &radio, sizeof(radio), buffer)) { + ffStrbufSetStatic(&item->inf.status, radio ? "Up" : "Down"); + } else { + ffStrbufSetStatic(&item->inf.status, "Unknown"); + } + + wl_linkstatus_t linkStatus; + if (!getWifiValue(fd, WL_LINKSTATUS, &linkStatus, sizeof(linkStatus), buffer)) { + ffStrbufSetStatic(&item->conn.status, "Unknown"); + continue; + } + if (linkStatus != WL_CONNECTED) { + ffStrbufSetStatic(&item->conn.status, "Not connected"); + continue; + } + ffStrbufSetStatic(&item->conn.status, "Connected"); + + wl_essid_t essid; + if (getWifiValue(fd, WL_ESSID, &essid, sizeof(essid), buffer) && essid.wl_essid_length > 0 && essid.wl_essid_length < sizeof(essid.wl_essid_essid) && essid.wl_essid_length <= MAX_ESSID_LENGTH - 1) { + ffStrbufSetNS(&item->conn.ssid, essid.wl_essid_length, essid.wl_essid_essid); + } + + wl_bssid_t bssid; + if (getWifiValue(fd, WL_BSSID, bssid, sizeof(bssid), buffer)) { + ffStrbufSetF(&item->conn.bssid, "%02X:%02X:%02X:%02X:%02X:%02X", (uint8_t) bssid[0], (uint8_t) bssid[1], (uint8_t) bssid[2], (uint8_t) bssid[3], (uint8_t) bssid[4], (uint8_t) bssid[5]); + } + + wl_rssi_t rssi; + if (getWifiValue(fd, WL_RSSI, &rssi, sizeof(rssi), buffer)) { + item->conn.signalQuality = rssi >= MAX_RSSI ? 100.0 : ((double) rssi / MAX_RSSI) * 100.0; + } + + wl_phy_conf_t phy; + if (getWifiValue(fd, WL_PHY_CONFIG, &phy, sizeof(phy), buffer)) { + uint32_t subtype = phy.wl_phy_dsss_conf.wl_dsss_subtype; + switch (subtype) { + case WL_FHSS: + item->conn.channel = (uint16_t) phy.wl_phy_fhss_conf.wl_fhss_channel; + setWifiProtocol(&item->conn.protocol, subtype, false); + break; + case WL_DSSS: + item->conn.channel = (uint16_t) phy.wl_phy_dsss_conf.wl_dsss_channel; + setWifiProtocol(&item->conn.protocol, subtype, false); + break; + case WL_OFDM: + item->conn.frequency = (uint16_t) phy.wl_phy_ofdm_conf.wl_ofdm_frequency; + item->conn.channel = ffWifiFreqToChannel(item->conn.frequency); + setWifiProtocol(&item->conn.protocol, subtype, phy.wl_phy_ofdm_conf.wl_ofdm_ht_enabled); + break; + case WL_ERP: + item->conn.channel = (uint16_t) phy.wl_phy_erp_conf.wl_erp_channel; + setWifiProtocol(&item->conn.protocol, subtype, phy.wl_phy_erp_conf.wl_erp_ht_enabled); + break; + default: + setWifiProtocol(&item->conn.protocol, subtype, false); + break; + } + } + + wl_encryption_t encryption; + wl_authmode_t authMode; + bool haveEncryption = getWifiValue(fd, WL_ENCRYPTION, &encryption, sizeof(encryption), buffer); + bool haveAuthMode = getWifiValue(fd, WL_AUTH_MODE, &authMode, sizeof(authMode), buffer); + if (haveEncryption) { + setWifiSecurity(&item->conn.security, encryption, haveAuthMode, authMode); + } + } + + return nullptr; +} diff --git a/src/detection/wifi/wifi_windows.c b/src/detection/wifi/wifi_windows.c index 8e900ad42a..8382954840 100644 --- a/src/detection/wifi/wifi_windows.c +++ b/src/detection/wifi/wifi_windows.c @@ -5,41 +5,59 @@ #include #include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wswitch" - static void convertIfStateToString(WLAN_INTERFACE_STATE state, FFstrbuf* result) { switch (state) { case wlan_interface_state_not_ready: - ffStrbufAppendS(result, "Not ready"); + ffStrbufSetStatic(result, "Not ready"); break; case wlan_interface_state_connected: - ffStrbufAppendS(result, "Connected"); + ffStrbufSetStatic(result, "Connected"); break; case wlan_interface_state_ad_hoc_network_formed: - ffStrbufAppendS(result, "Ad hoc network formed"); + ffStrbufSetStatic(result, "Ad hoc network formed"); break; case wlan_interface_state_disconnecting: - ffStrbufAppendS(result, "Disconnecting"); + ffStrbufSetStatic(result, "Disconnecting"); break; case wlan_interface_state_disconnected: - ffStrbufAppendS(result, "Disconnected"); + ffStrbufSetStatic(result, "Disconnected"); break; case wlan_interface_state_associating: - ffStrbufAppendS(result, "Associating"); + ffStrbufSetStatic(result, "Associating"); break; case wlan_interface_state_discovering: - ffStrbufAppendS(result, "Discovering"); + ffStrbufSetStatic(result, "Discovering"); break; case wlan_interface_state_authenticating: - ffStrbufAppendS(result, "Authenticating"); + ffStrbufSetStatic(result, "Authenticating"); break; default: - ffStrbufAppendS(result, "Unknown"); + ffStrbufSetStatic(result, "Unknown"); break; } } +typedef struct _WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO { + UCHAR ucLinkID; + ULONG ulChannelCenterFrequencyMhz; + ULONG ulBandwidth; + LONG lRssi; + WLAN_RATE_SET wlanRateSet; +} WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO, *PWLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO; + +typedef struct _WLAN_REALTIME_CONNECTION_QUALITY { + DOT11_PHY_TYPE dot11PhyType; + ULONG ulLinkQuality; + ULONG ulRxRate; + ULONG ulTxRate; + BOOL bIsMLOConnection; + ULONG ulNumLinks; + // Array of size ulNumLinks + WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO linksInfo[]; +} WLAN_REALTIME_CONNECTION_QUALITY, *PWLAN_REALTIME_CONNECTION_QUALITY; + +enum { wlan_intf_opcode_realtime_connection_quality = 19 }; + const char* ffDetectWifi(FFlist* result) { FF_LIBRARY_LOAD_MESSAGE(wlanapi, "wlanapi" FF_LIBRARY_EXTENSION, 1) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanOpenHandle) @@ -54,7 +72,7 @@ const char* ffDetectWifi(FFlist* result) { WLAN_INTERFACE_INFO_LIST* ifList = nullptr; const char* error = nullptr; - if (ffWlanOpenHandle(2, nullptr, &curVersion, &hClient) != ERROR_SUCCESS) { + if (ffWlanOpenHandle(2 /*maxClientVersion*/, nullptr, &curVersion, &hClient) != ERROR_SUCCESS) { error = "WlanOpenHandle() failed"; goto exit; } @@ -79,6 +97,7 @@ const char* ffDetectWifi(FFlist* result) { item->conn.rxRate = -DBL_MAX; item->conn.txRate = -DBL_MAX; item->conn.channel = 0; + item->conn.channelWidth = 0; item->conn.frequency = 0; convertIfStateToString(ifInfo->isState, &item->inf.status); @@ -200,17 +219,44 @@ const char* ffDetectWifi(FFlist* result) { ffStrbufAppendS(&item->conn.security, "Insecure"); } - WLAN_BSS_LIST* bssList = nullptr; - if (ffWlanGetNetworkBssList(hClient, + WLAN_REALTIME_CONNECTION_QUALITY* connectionQuality = nullptr; + bufSize = 0; + if (ffWlanQueryInterface(hClient, &ifInfo->InterfaceGuid, - &connInfo->wlanAssociationAttributes.dot11Ssid, - connInfo->wlanAssociationAttributes.dot11BssType, - connInfo->wlanSecurityAttributes.bSecurityEnabled, + wlan_intf_opcode_realtime_connection_quality, nullptr, - &bssList) == ERROR_SUCCESS && - bssList->dwNumberOfItems > 0) { - item->conn.frequency = (uint16_t) (bssList->wlanBssEntries[0].ulChCenterFrequency / 1000); - ffWlanFreeMemory(bssList); + &bufSize, + (PVOID*) &connectionQuality, + &opCode) == ERROR_SUCCESS) { + const WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO* bestLink = nullptr; + for (ULONG linkIndex = 0; linkIndex < connectionQuality->ulNumLinks; ++linkIndex) { + const WLAN_REALTIME_CONNECTION_QUALITY_LINK_INFO* link = &connectionQuality->linksInfo[linkIndex]; + if (!bestLink || link->lRssi > bestLink->lRssi) { + bestLink = link; + } + } + if (bestLink) { + item->conn.channelWidth = (uint16_t) bestLink->ulBandwidth; + item->conn.frequency = (uint16_t) bestLink->ulChannelCenterFrequencyMhz; + } + } + if (connectionQuality) { + ffWlanFreeMemory(connectionQuality); + } + + if (item->conn.frequency == 0) { + WLAN_BSS_LIST* bssList = nullptr; + if (ffWlanGetNetworkBssList(hClient, + &ifInfo->InterfaceGuid, + &connInfo->wlanAssociationAttributes.dot11Ssid, + connInfo->wlanAssociationAttributes.dot11BssType, + connInfo->wlanSecurityAttributes.bSecurityEnabled, + nullptr, + &bssList) == ERROR_SUCCESS && + bssList->dwNumberOfItems > 0) { + item->conn.frequency = (uint16_t) (bssList->wlanBssEntries[0].ulChCenterFrequency / 1000); + ffWlanFreeMemory(bssList); + } } ffWlanFreeMemory(connInfo); @@ -238,5 +284,3 @@ const char* ffDetectWifi(FFlist* result) { } return error; } - -#pragma GCC diagnostic pop diff --git a/src/detection/wm/wm_apple.m b/src/detection/wm/wm_apple.m index c4bc780a21..e02c089bb7 100644 --- a/src/detection/wm/wm_apple.m +++ b/src/detection/wm/wm_apple.m @@ -11,16 +11,23 @@ const char* ffDetectWMPlugin(FFstrbuf* pluginName) { int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; - u_int requestLength = ARRAY_SIZE(request); + size_t length; - size_t length = 0; - FF_AUTO_FREE struct kinfo_proc* processes = ffSysctlGetData(request, requestLength, &length); - if (processes == nullptr) { - return "sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL) failed"; + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, nullptr}) failed"; } - assert(length % sizeof(struct kinfo_proc) == 0); - for (size_t i = 0; i < length / sizeof(struct kinfo_proc); i++) { + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + FF_AUTO_FREE struct kinfo_proc* processes = malloc(length); + + if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, processes}) failed"; + } + + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + + for (size_t i = 0; i < count; i++) { const struct kinfo_proc* proc = &processes[i]; if (proc->kp_eproc.e_ppid != 1) { continue; diff --git a/src/detection/wm/wm_linux.c b/src/detection/wm/wm_linux.c index 5e3fff085c..a22a513fb4 100644 --- a/src/detection/wm/wm_linux.c +++ b/src/detection/wm/wm_linux.c @@ -43,9 +43,8 @@ static const char* getHyprland(FFstrbuf* result) { FF_DEBUG("Detecting Hyprland version"); FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); - FF_DEBUG("Checking for " FASTFETCH_TARGET_DIR_USR "/include/hyprland/src/version.h" - " file"); - if (ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/include/hyprland/src/version.h", result)) { + FF_DEBUG("Checking for " FF_PATH_PKG_BASE "/include/hyprland/src/version.h file"); + if (ffReadFileBuffer(FF_PATH_PKG_BASE "/include/hyprland/src/version.h", result)) { FF_DEBUG("Found version.h file, extracting version"); if (ffStrbufSubstrAfterFirstS(result, "\n#define GIT_TAG ")) { ffStrbufSubstrAfterFirstC(result, '"'); diff --git a/src/fastfetch.c b/src/fastfetch.c index b6c2ea2018..3a936fb766 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -910,7 +910,7 @@ int main(int argc, char** argv) { ffStrbufSetS(&data.structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string } if (data.genConfigInteractive && !ffGenConfigInteractive(&data)) { - // User cancelled the interactive config generation + // User canceled the interactive config generation ffStrbufDestroy(&data.structure); ffStrbufDestroy(&data.structureDisabled); yyjson_doc_free(data.configDoc); diff --git a/src/logo/ascii/a.inc b/src/logo/ascii/a.inc index dfda555db6..38ebae9b33 100644 --- a/src/logo/ascii/a.inc +++ b/src/logo/ascii/a.inc @@ -633,6 +633,18 @@ static const FFlogo A[] = { .colorTitle = FF_COLOR_FG_CYAN, }, #endif + #ifdef FASTFETCH_DATATEXT_LOGO_ARTIX2 + // Artix2 + { + .names = { "artix2" }, + .lines = FASTFETCH_DATATEXT_LOGO_ARTIX2, + .colors = { + FF_COLOR_FG_CYAN, + }, + .colorKeys = FF_COLOR_FG_CYAN, + .colorTitle = FF_COLOR_FG_CYAN, + }, + #endif #ifdef FASTFETCH_DATATEXT_LOGO_ARTIX_SMALL // ArtixSmall { diff --git a/src/logo/ascii/a/artix2.txt b/src/logo/ascii/a/artix2.txt index 0e87beb2e4..68d8399522 100644 --- a/src/logo/ascii/a/artix2.txt +++ b/src/logo/ascii/a/artix2.txt @@ -1,20 +1,20 @@ - .: - .==: - .====: - .======: - .========: - .==========: - .============: - .-===========: - .:-========: - .-:. .:======: - .======:.. :-===. - .============:.. .:=. - .==================:.. - .=======================: - .====================::. . - ================::. :-=== - ============-:. .:======== - ========-:. :-========== - =====:. ..:-===== - =::. ..:: + .: + .==: + .====: + .======: + .========: + .==========: + .============: + .-===========: + .:-========: + .-:. .:======: + .======:.. :-===. + .============:.. .:=. + .==================:.. + .=======================: + .====================::. . + ================::. :-=== + ============-:. .:======== + ========-:. :-========== + =====:. ..:-===== + =::. ..:: \ No newline at end of file diff --git a/src/logo/ascii/m.inc b/src/logo/ascii/m.inc index 68faf3b9db..68bc8cc0eb 100644 --- a/src/logo/ascii/m.inc +++ b/src/logo/ascii/m.inc @@ -103,6 +103,19 @@ static const FFlogo M[] = { .colorTitle = FF_COLOR_FG_GREEN, }, #endif + #ifdef FASTFETCH_DATATEXT_LOGO_MACOS_OLD + // MacOS_old + { + .names = { "macOS_old" }, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .lines = FASTFETCH_DATATEXT_LOGO_MACOS_OLD, + .colors = { + FF_COLOR_FG_CYAN, + FF_COLOR_FG_BLUE, + FF_COLOR_FG_DEFAULT, + }, + }, + #endif #ifdef FASTFETCH_DATATEXT_LOGO_MAINSAILOS // MainsailOS { diff --git a/src/logo/ascii/m/macos_old.txt b/src/logo/ascii/m/macos_old.txt new file mode 100644 index 0000000000..c5cf0c70da --- /dev/null +++ b/src/logo/ascii/m/macos_old.txt @@ -0,0 +1,17 @@ + $3@@ + $3@@ +$2((((((((((((((((((((($3@@$1****************** +$2(((((((((((((((((((($3@@$1******************* +$2(((((((((($3%%$2((((((($3@@$1********$3@@$1********** +$2(((((((((($3%%$2(((((($3@@$1*********$3@@$1********** +$2((((((((((((((((($3@@$1********************** +$2(((((((((((((((($3@@$1*********************** +$2((((((((((((((($3@@$1************************ +$2(((((((((((((($3@@@@@@@@$1******************* +$2(((((((((((((((((((($3@@$1******************* +$2(((((((($3%%%%%%%%%%%@@@@@@@@@@@@@@$1******** +$2(((((((((((((((((((($3@@$1******************* +$2((((((((((((((((((((($3@@$1****************** + $3@@ + @@ + @@ \ No newline at end of file diff --git a/src/logo/ascii/o.inc b/src/logo/ascii/o.inc index b5b32a9025..54b9282f05 100644 --- a/src/logo/ascii/o.inc +++ b/src/logo/ascii/o.inc @@ -37,6 +37,31 @@ static const FFlogo O[] = { }, }, #endif + #ifdef FASTFETCH_DATATEXT_LOGO_OMARCHY + // Omarchy + { + .names = { "Omarchy" }, + .lines = FASTFETCH_DATATEXT_LOGO_OMARCHY, + .colors = { + FF_COLOR_FG_256 "149", + }, + .colorKeys = FF_COLOR_FG_256 "149", + .colorTitle = FF_COLOR_FG_256 "111", + }, + #endif + #ifdef FASTFETCH_DATATEXT_LOGO_OMARCHY2 + // Omarchy2 + { + .names = { "Omarchy2" }, + .lines = FASTFETCH_DATATEXT_LOGO_OMARCHY2, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, + .colors = { + FF_COLOR_FG_256 "149", + }, + .colorKeys = FF_COLOR_FG_256 "149", + .colorTitle = FF_COLOR_FG_256 "111", + }, + #endif #ifdef FASTFETCH_DATATEXT_LOGO_OMNIOS // OmniOS { diff --git a/src/logo/ascii/o/omarchy.txt b/src/logo/ascii/o/omarchy.txt new file mode 100644 index 0000000000..05998ae4df --- /dev/null +++ b/src/logo/ascii/o/omarchy.txt @@ -0,0 +1,15 @@ +██████████████████████████████████ +██ ██ ██ +██ █████████████ ████ ██ +██ ██ ██ ██ +██ ██ ██ ██ +██ ██ ██ ██ +██ ██ ██ ██ +███████ ██ ██ +██ ██ ██ ██ +██ ██ ██ ██ +██ ██ ██ ██ +██ ██ ██ ██ +██ ████████████████████████ ██ +██ ██ ██ +██████████████████ █████████████ \ No newline at end of file diff --git a/src/logo/ascii/o/omarchy2.txt b/src/logo/ascii/o/omarchy2.txt new file mode 100644 index 0000000000..c06e6802c6 --- /dev/null +++ b/src/logo/ascii/o/omarchy2.txt @@ -0,0 +1,10 @@ + ▄▄▄ + ▄█████▄ ▄███████████▄ ▄███████ ▄███████ ▄███████ ▄█ █▄ ▄█ █▄ +███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ +███ ███ ███ ███ ███ ███ ███ ███ ███ ███ █▀ ███ ███ ███ ███ +███ ███ ███ ███ ███ ▄███▄▄▄███ ▄███▄▄▄██▀ ███ ▄███▄▄▄███▄ ███▄▄▄███ +███ ███ ███ ███ ███ ▀███▀▀▀███ ▀███▀▀▀▀ ███ ▀▀███▀▀▀███ ▀▀▀▀▀▀███ +███ ███ ███ ███ ███ ███ ███ ██████████ ███ █▄ ███ ███ ▄██ ███ +███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ + ▀█████▀ ▀█ ███ █▀ ███ █▀ ███ ███ ███████▀ ███ █▀ ▀█████▀ + ███ █▀ \ No newline at end of file diff --git a/src/modules/battery/battery.c b/src/modules/battery/battery.c index 7b8405e71e..33e969436b 100644 --- a/src/modules/battery/battery.c +++ b/src/modules/battery/battery.c @@ -11,9 +11,9 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); if (options->moduleArgs.key.length == 0) { if (result->modelName.length > 0) { - ffStrbufSetF(&key, "%s (%s)", FF_BATTERY_MODULE_NAME, result->modelName.chars); + ffStrbufSetF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Battery), result->modelName.chars); } else { - ffStrbufSetS(&key, FF_BATTERY_MODULE_NAME); + ffStrbufSetS(&key, FF_MODULE_GET_DISPLAY_NAME(Battery)); } } else { ffStrbufClear(&key); @@ -170,11 +170,11 @@ bool ffPrintBattery(FFBatteryOptions* options) { const char* error = ffDetectBattery(options, &results); if (error) { - ffPrintError(FF_BATTERY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Battery), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (results.length == 0) { - ffPrintError(FF_BATTERY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "No batteries found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Battery), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "No batteries found"); return false; } @@ -209,7 +209,7 @@ void ffParseBatteryJsonObject(FFBatteryOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_BATTERY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Battery), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -297,8 +297,30 @@ void ffDestroyBatteryOptions(FFBatteryOptions* options) { } FFModuleBaseInfo ffBatteryModuleInfo = { - .name = FF_BATTERY_MODULE_NAME, + .name = "Battery", .description = "Print battery information", + .displayName = { + .en = "Battery", + .ar = "البطارية", + .cs = "Baterie", + .de = "Akku", + .es = "Batería", + .fr = "Batterie", + .gl = "Batería", + .he = "סוללה", + .id = "Baterai", + .it = "Batteria", + .ja = "バッテリー", + .ko = "배터리", + .pl = "Bateria", + .pt = "Bateria", + .ru = "Батарея", + .tr = "Pil", + .uk = "Батарея", + .vi = "Pin", + .zh_CN = "电池", + .zh_TW = "電池", + }, .initOptions = (void*) ffInitBatteryOptions, .destroyOptions = (void*) ffDestroyBatteryOptions, .parseJsonObject = (void*) ffParseBatteryJsonObject, diff --git a/src/modules/battery/battery.h b/src/modules/battery/battery.h index 16ad027d80..7157b51a72 100644 --- a/src/modules/battery/battery.h +++ b/src/modules/battery/battery.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_BATTERY_MODULE_NAME "Battery" - bool ffPrintBattery(FFBatteryOptions* options); void ffInitBatteryOptions(FFBatteryOptions* options); void ffDestroyBatteryOptions(FFBatteryOptions* options); diff --git a/src/modules/bios/bios.c b/src/modules/bios/bios.c index c000080f49..fc4f7ac738 100644 --- a/src/modules/bios/bios.c +++ b/src/modules/bios/bios.c @@ -18,12 +18,12 @@ bool ffPrintBios(FFBiosOptions* options) { FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); if (error) { - ffPrintError(FF_BIOS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Bios), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); goto exit; } if (bios.version.length == 0) { - ffPrintError(FF_BIOS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "bios_version is not set."); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Bios), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "bios_version is not set."); goto exit; } @@ -34,7 +34,7 @@ bool ffPrintBios(FFBiosOptions* options) { ffStrbufSetStatic(&bios.type, "Legacy"); } - ffStrbufSetF(&key, FF_BIOS_MODULE_NAME " (%s)", bios.type.chars); + ffStrbufSetF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Bios), bios.type.chars); } else { ffStrbufClear(&key); FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]) { @@ -80,7 +80,7 @@ void ffParseBiosJsonObject(FFBiosOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_BIOS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Bios), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -130,8 +130,30 @@ void ffDestroyBiosOptions(FFBiosOptions* options) { } FFModuleBaseInfo ffBiosModuleInfo = { - .name = FF_BIOS_MODULE_NAME, + .name = "BIOS", .description = "Print first-stage bootloader information (name, version, release date, etc.)", + .displayName = { + .en = "BIOS", + .ar = "BIOS", + .cs = "BIOS", + .de = "BIOS", + .es = "BIOS", + .fr = "BIOS", + .gl = "BIOS", + .he = "BIOS", + .id = "BIOS", + .it = "BIOS", + .ja = "BIOS", + .ko = "BIOS", + .pl = "BIOS", + .pt = "BIOS", + .ru = "BIOS", + .tr = "BIOS", + .uk = "BIOS", + .vi = "BIOS", + .zh_CN = "BIOS", + .zh_TW = "BIOS", + }, .initOptions = (void*) ffInitBiosOptions, .destroyOptions = (void*) ffDestroyBiosOptions, .parseJsonObject = (void*) ffParseBiosJsonObject, diff --git a/src/modules/bios/bios.h b/src/modules/bios/bios.h index d3b077e4d1..d34978d8da 100644 --- a/src/modules/bios/bios.h +++ b/src/modules/bios/bios.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_BIOS_MODULE_NAME "BIOS" - bool ffPrintBios(FFBiosOptions* options); void ffInitBiosOptions(FFBiosOptions* options); void ffDestroyBiosOptions(FFBiosOptions* options); diff --git a/src/modules/bluetooth/bluetooth.c b/src/modules/bluetooth/bluetooth.c index 42a251886a..9f69863920 100644 --- a/src/modules/bluetooth/bluetooth.c +++ b/src/modules/bluetooth/bluetooth.c @@ -8,7 +8,7 @@ static void printDevice(FFBluetoothOptions* options, const FFBluetoothResult* device, uint8_t index) { FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type; if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_BLUETOOTH_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Bluetooth), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); bool showBatteryLevel = device->battery > 0 && device->battery <= 100; @@ -44,7 +44,7 @@ static void printDevice(FFBluetoothOptions* options, const FFBluetoothResult* de ffPercentAppendBar(&percentageBar, device->battery, options->percent, &options->moduleArgs); } - FF_PRINT_FORMAT_CHECKED(FF_BLUETOOTH_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Bluetooth), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(device->name, "name"), FF_ARG(device->address, "address"), FF_ARG(device->type, "type"), @@ -60,12 +60,12 @@ bool ffPrintBluetooth(FFBluetoothOptions* options) { const char* error = ffDetectBluetooth(options, &devices); if (error) { - ffPrintError(FF_BLUETOOTH_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Bluetooth), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (devices.length == 0) { - ffPrintError(FF_BLUETOOTH_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No bluetooth devices found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Bluetooth), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No bluetooth devices found"); return false; } @@ -100,7 +100,7 @@ void ffParseBluetoothJsonObject(FFBluetoothOptions* options, yyjson_val* module) continue; } - ffPrintError(FF_BLUETOOTH_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Bluetooth), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -151,8 +151,30 @@ void ffDestroyBluetoothOptions(FFBluetoothOptions* options) { } FFModuleBaseInfo ffBluetoothModuleInfo = { - .name = FF_BLUETOOTH_MODULE_NAME, + .name = "Bluetooth", .description = "List connected Bluetooth devices", + .displayName = { + .en = "Bluetooth", + .ar = "بلوتوث", + .cs = "Bluetooth", + .de = "Bluetooth", + .es = "Bluetooth", + .fr = "Bluetooth", + .gl = "Bluetooth", + .he = "בלוטות'", + .id = "Bluetooth", + .it = "Bluetooth", + .ja = "ブルートゥース", + .ko = "블루투스", + .pl = "Bluetooth", + .pt = "Bluetooth", + .ru = "Bluetooth", + .tr = "Bluetooth", + .uk = "Bluetooth", + .vi = "Bluetooth", + .zh_CN = "蓝牙设备", + .zh_TW = "藍牙裝置", + }, .initOptions = (void*) ffInitBluetoothOptions, .destroyOptions = (void*) ffDestroyBluetoothOptions, .parseJsonObject = (void*) ffParseBluetoothJsonObject, diff --git a/src/modules/bluetooth/bluetooth.h b/src/modules/bluetooth/bluetooth.h index d9aa059e37..fc6fdd8ce4 100644 --- a/src/modules/bluetooth/bluetooth.h +++ b/src/modules/bluetooth/bluetooth.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_BLUETOOTH_MODULE_NAME "Bluetooth" - bool ffPrintBluetooth(FFBluetoothOptions* options); void ffInitBluetoothOptions(FFBluetoothOptions* options); void ffDestroyBluetoothOptions(FFBluetoothOptions* options); diff --git a/src/modules/bluetoothradio/bluetoothradio.c b/src/modules/bluetoothradio/bluetoothradio.c index fc113fc832..0596fb47eb 100644 --- a/src/modules/bluetoothradio/bluetoothradio.c +++ b/src/modules/bluetoothradio/bluetoothradio.c @@ -5,8 +5,6 @@ #include "detection/bluetoothradio/bluetoothradio.h" #include "modules/bluetoothradio/bluetoothradio.h" -#define FF_BLUETOOTHRADIO_DISPLAY_NAME "Bluetooth Radio" - static void destroyBluetoothRadios(FFlist* radios) { FF_LIST_FOR_EACH (FFBluetoothRadioResult, radio, *radios) { ffStrbufDestroy(&radio->name); @@ -18,7 +16,7 @@ static void destroyBluetoothRadios(FFlist* radios) { static void printDevice(FFBluetoothRadioOptions* options, const FFBluetoothRadioResult* radio, uint8_t index) { FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); if (options->moduleArgs.key.length == 0) { - ffStrbufAppendF(&key, "%s (%s)", FF_BLUETOOTHRADIO_DISPLAY_NAME, radio->name.chars); + ffStrbufAppendF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(BluetoothRadio), radio->name.chars); } else { FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]) { FF_ARG(index, "index"), @@ -101,7 +99,7 @@ bool ffPrintBluetoothRadio(FFBluetoothRadioOptions* options) { const char* error = ffDetectBluetoothRadio(&radios); if (error) { - ffPrintError(FF_BLUETOOTHRADIO_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(BluetoothRadio), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } @@ -117,9 +115,9 @@ bool ffPrintBluetoothRadio(FFBluetoothRadioOptions* options) { if (index == 0) { if (radios.length > 0) { - ffPrintError(FF_BLUETOOTHRADIO_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Bluetooth radios found but none enabled"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(BluetoothRadio), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Bluetooth radios found but none enabled"); } else { - ffPrintError(FF_BLUETOOTHRADIO_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No devices detected"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(BluetoothRadio), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No devices detected"); } destroyBluetoothRadios(&radios); return false; @@ -137,7 +135,7 @@ void ffParseBluetoothRadioJsonObject(FFBluetoothRadioOptions* options, yyjson_va continue; } - ffPrintError(FF_BLUETOOTHRADIO_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(BluetoothRadio), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -190,8 +188,30 @@ void ffDestroyBluetoothRadioOptions(FFBluetoothRadioOptions* options) { } FFModuleBaseInfo ffBluetoothRadioModuleInfo = { - .name = FF_BLUETOOTHRADIO_MODULE_NAME, + .name = "BluetoothRadio", .description = "List Bluetooth radios (supported versions, vendors, etc.)", + .displayName = { + .en = "Bluetooth Radio", + .ar = "راديو بلوتوث", + .cs = "Bluetooth rádio", + .de = "Bluetooth-Adapter", + .es = "Adaptador Bluetooth", + .fr = "Adaptateur Bluetooth", + .gl = "Adaptador Bluetooth", + .he = "רדיו בלוטות'", + .id = "Radio Bluetooth", + .it = "Adattatore Bluetooth", + .ja = "ブルートゥースアダプター", + .ko = "블루투스 어댑터", + .pl = "Adapter Bluetooth", + .pt = "Adaptador Bluetooth", + .ru = "Bluetooth-адаптер", + .tr = "Bluetooth Radyosu", + .uk = "Радіомодуль Bluetooth", + .vi = "Radio Bluetooth", + .zh_CN = "蓝牙适配器", + .zh_TW = "藍牙配接器", + }, .initOptions = (void*) ffInitBluetoothRadioOptions, .destroyOptions = (void*) ffDestroyBluetoothRadioOptions, .parseJsonObject = (void*) ffParseBluetoothRadioJsonObject, diff --git a/src/modules/bluetoothradio/bluetoothradio.h b/src/modules/bluetoothradio/bluetoothradio.h index 4707f1ba5e..391ea64a07 100644 --- a/src/modules/bluetoothradio/bluetoothradio.h +++ b/src/modules/bluetoothradio/bluetoothradio.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_BLUETOOTHRADIO_MODULE_NAME "BluetoothRadio" - bool ffPrintBluetoothRadio(FFBluetoothRadioOptions* options); void ffInitBluetoothRadioOptions(FFBluetoothRadioOptions* options); void ffDestroyBluetoothRadioOptions(FFBluetoothRadioOptions* options); diff --git a/src/modules/board/board.c b/src/modules/board/board.c index 1a85ea3930..c0f49baaab 100644 --- a/src/modules/board/board.c +++ b/src/modules/board/board.c @@ -14,24 +14,24 @@ bool ffPrintBoard(FFBoardOptions* options) { const char* error = ffDetectBoard(&result); if (error) { - ffPrintError(FF_BOARD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Board), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); goto exit; } if (result.name.length == 0) { - ffPrintError(FF_BOARD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "board_name is not set."); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Board), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "board_name is not set."); goto exit; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_BOARD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Board), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&result.name, stdout); if (result.version.length) { printf(" (%s)", result.version.chars); } putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_BOARD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Board), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.name, "name"), FF_ARG(result.vendor, "vendor"), FF_ARG(result.version, "version"), @@ -56,7 +56,7 @@ void ffParseBoardJsonObject(FFBoardOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_BOARD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Board), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -108,8 +108,30 @@ void ffDestroyBoardOptions(FFBoardOptions* options) { } FFModuleBaseInfo ffBoardModuleInfo = { - .name = FF_BOARD_MODULE_NAME, + .name = "Board", .description = "Print motherboard name and other information", + .displayName = { + .en = "Board", + .ar = "اللوحة الأم", + .cs = "Základní deska", + .de = "Mainboard", + .es = "Placa base", + .fr = "Carte mère", + .gl = "Placa base", + .he = "לוח אם", + .id = "Papan Induk", + .it = "Scheda madre", + .ja = "マザーボード", + .ko = "메인보드", + .pl = "Płyta główna", + .pt = "Placa-mãe", + .ru = "Материнская плата", + .tr = "Anakart", + .uk = "Материнська плата", + .vi = "Bo mạch chủ", + .zh_CN = "主板", + .zh_TW = "主機板", + }, .initOptions = (void*) ffInitBoardOptions, .destroyOptions = (void*) ffDestroyBoardOptions, .parseJsonObject = (void*) ffParseBoardJsonObject, diff --git a/src/modules/board/board.h b/src/modules/board/board.h index eae3d73c11..af819fbbaa 100644 --- a/src/modules/board/board.h +++ b/src/modules/board/board.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_BOARD_MODULE_NAME "Board" - bool ffPrintBoard(FFBoardOptions* options); void ffInitBoardOptions(FFBoardOptions* options); void ffDestroyBoardOptions(FFBoardOptions* options); diff --git a/src/modules/bootmgr/bootmgr.c b/src/modules/bootmgr/bootmgr.c index 494885fd6c..45e29bbbb3 100644 --- a/src/modules/bootmgr/bootmgr.c +++ b/src/modules/bootmgr/bootmgr.c @@ -14,7 +14,7 @@ bool ffPrintBootmgr(FFBootmgrOptions* options) { const char* error = ffDetectBootmgr(&bootmgr); if (error) { - ffPrintError(FF_BOOTMGR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Bootmgr), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); goto exit; } else { FF_STRBUF_AUTO_DESTROY firmwareName = ffStrbufCreateCopy(&bootmgr.firmware); @@ -25,7 +25,7 @@ bool ffPrintBootmgr(FFBootmgrOptions* options) { #endif if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_BOOTMGR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Bootmgr), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&bootmgr.name, stdout); if (firmwareName.length > 0) { printf(" - %s\n", firmwareName.chars); @@ -33,7 +33,7 @@ bool ffPrintBootmgr(FFBootmgrOptions* options) { putchar('\n'); } } else { - FF_PRINT_FORMAT_CHECKED(FF_BOOTMGR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Bootmgr), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(bootmgr.name, "name"), FF_ARG(bootmgr.firmware, "firmware-path"), FF_ARG(firmwareName, "firmware-name"), @@ -59,7 +59,7 @@ void ffParseBootmgrJsonObject(FFBootmgrOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_BOOTMGR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Bootmgr), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -103,8 +103,30 @@ void ffDestroyBootmgrOptions(FFBootmgrOptions* options) { } FFModuleBaseInfo ffBootmgrModuleInfo = { - .name = FF_BOOTMGR_MODULE_NAME, + .name = "Bootmgr", .description = "Print second-stage bootloader information (name, firmware, etc.)", + .displayName = { + .en = "Boot Manager", + .ar = "مدير الإقلاع", + .cs = "Správce spouštění", + .de = "Boot-Manager", + .es = "Gestor de arranque", + .fr = "Gestionnaire de démarrage", + .gl = "Xestor de arranque", + .he = "מנהל אתחול", + .id = "Manajer Boot", + .it = "Gestore di avvio", + .ja = "ブートマネージャー", + .ko = "부트 매니저", + .pl = "Menedżer rozruchu", + .pt = "Gerenciador de inicialização", + .ru = "Менеджер загрузки", + .tr = "Önyükleme Yöneticisi", + .uk = "Менеджер завантаження", + .vi = "Trình quản lý khởi động", + .zh_CN = "启动管理器", + .zh_TW = "啟動管理器", + }, .initOptions = (void*) ffInitBootmgrOptions, .destroyOptions = (void*) ffDestroyBootmgrOptions, .parseJsonObject = (void*) ffParseBootmgrJsonObject, diff --git a/src/modules/bootmgr/bootmgr.h b/src/modules/bootmgr/bootmgr.h index 4f6a2bac25..947163bd5a 100644 --- a/src/modules/bootmgr/bootmgr.h +++ b/src/modules/bootmgr/bootmgr.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_BOOTMGR_MODULE_NAME "Bootmgr" - bool ffPrintBootmgr(FFBootmgrOptions* options); void ffInitBootmgrOptions(FFBootmgrOptions* options); void ffDestroyBootmgrOptions(FFBootmgrOptions* options); diff --git a/src/modules/break/break.c b/src/modules/break/break.c index 011458a283..5a6f356732 100644 --- a/src/modules/break/break.c +++ b/src/modules/break/break.c @@ -16,7 +16,7 @@ void ffParseBreakJsonObject([[maybe_unused]] FFBreakOptions* options, [[maybe_un continue; } - ffPrintError(FF_BREAK_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Break), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -27,8 +27,30 @@ void ffDestroyBreakOptions([[maybe_unused]] FFBreakOptions* options) { } FFModuleBaseInfo ffBreakModuleInfo = { - .name = FF_BREAK_MODULE_NAME, + .name = "Break", .description = "Print an empty line", + .displayName = { + .en = "Break", + .ar = "فاصل", + .cs = "Konec řádku", + .de = "Umbruch", + .es = "Salto de línea", + .fr = "Saut de ligne", + .gl = "Salto de liña", + .he = "מעבר שורה", + .id = "Baris Baru", + .it = "Interruzione", + .ja = "改行", + .ko = "줄 바꿈", + .pl = "Przerwa", + .pt = "Quebra", + .ru = "Разрыв", + .tr = "Satır Sonu", + .uk = "Розрив рядка", + .vi = "Xuống dòng", + .zh_CN = "换行", + .zh_TW = "換行", + }, .initOptions = (void*) ffInitBreakOptions, .destroyOptions = (void*) ffDestroyBreakOptions, .parseJsonObject = (void*) ffParseBreakJsonObject, diff --git a/src/modules/break/break.h b/src/modules/break/break.h index f611b73790..9b11ebad89 100644 --- a/src/modules/break/break.h +++ b/src/modules/break/break.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_BREAK_MODULE_NAME "Break" - bool ffPrintBreak(FFBreakOptions* options); void ffInitBreakOptions(FFBreakOptions* options); void ffDestroyBreakOptions(FFBreakOptions* options); diff --git a/src/modules/brightness/brightness.c b/src/modules/brightness/brightness.c index 479a2ccb9b..5c6fc15449 100644 --- a/src/modules/brightness/brightness.c +++ b/src/modules/brightness/brightness.c @@ -11,12 +11,12 @@ bool ffPrintBrightness(FFBrightnessOptions* options) { const char* error = ffDetectBrightness(options, &result); if (error) { - ffPrintError(FF_BRIGHTNESS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Brightness), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (result.length == 0) { - ffPrintError(FF_BRIGHTNESS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No result is detected."); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Brightness), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No result is detected."); return false; } @@ -34,7 +34,7 @@ bool ffPrintBrightness(FFBrightnessOptions* options) { ffPercentAppendNum(&str, percent, options->percent, false, &options->moduleArgs); } - ffPrintLogoAndKey(FF_BRIGHTNESS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Brightness), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&str, stdout); return true; } @@ -44,7 +44,7 @@ bool ffPrintBrightness(FFBrightnessOptions* options) { uint32_t index = 0; FF_LIST_FOR_EACH (FFBrightnessResult, item, result) { if (options->moduleArgs.key.length == 0) { - ffStrbufAppendF(&key, "%s (%s)", FF_BRIGHTNESS_MODULE_NAME, item->name.chars); + ffStrbufAppendF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Brightness), item->name.chars); } else { uint32_t moduleIndex = result.length == 1 ? 0 : index + 1; FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]) { @@ -130,7 +130,7 @@ void ffParseBrightnessJsonObject(FFBrightnessOptions* options, yyjson_val* modul continue; } - ffPrintError(FF_BRIGHTNESS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Brightness), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -190,8 +190,30 @@ void ffDestroyBrightnessOptions(FFBrightnessOptions* options) { } FFModuleBaseInfo ffBrightnessModuleInfo = { - .name = FF_BRIGHTNESS_MODULE_NAME, + .name = "Brightness", .description = "Print the current brightness level of your monitors", + .displayName = { + .en = "Brightness", + .ar = "السطوع", + .cs = "Jas", + .de = "Helligkeit", + .es = "Brillo", + .fr = "Luminosité", + .gl = "Brillo", + .he = "בהירות", + .id = "Kecerahan", + .it = "Luminosità", + .ja = "明るさ", + .ko = "밝기", + .pl = "Jasność", + .pt = "Brilho", + .ru = "Яркость", + .tr = "Parlaklık", + .uk = "Яскравість", + .vi = "Độ sáng", + .zh_CN = "亮度", + .zh_TW = "亮度", + }, .initOptions = (void*) ffInitBrightnessOptions, .destroyOptions = (void*) ffDestroyBrightnessOptions, .parseJsonObject = (void*) ffParseBrightnessJsonObject, diff --git a/src/modules/brightness/brightness.h b/src/modules/brightness/brightness.h index 085366141f..0e03c1ce34 100644 --- a/src/modules/brightness/brightness.h +++ b/src/modules/brightness/brightness.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_BRIGHTNESS_MODULE_NAME "Brightness" - bool ffPrintBrightness(FFBrightnessOptions* options); void ffInitBrightnessOptions(FFBrightnessOptions* options); void ffDestroyBrightnessOptions(FFBrightnessOptions* options); diff --git a/src/modules/btrfs/btrfs.c b/src/modules/btrfs/btrfs.c index 21068e85db..052cf32dcb 100644 --- a/src/modules/btrfs/btrfs.c +++ b/src/modules/btrfs/btrfs.c @@ -10,9 +10,9 @@ static void printBtrfs(FFBtrfsOptions* options, FFBtrfsResult* result, uint8_t i FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); if (options->moduleArgs.key.length == 0) { if (result->name.length > 0) { - ffStrbufSetF(&buffer, "%s (%s)", FF_BTRFS_MODULE_NAME, result->name.chars); + ffStrbufSetF(&buffer, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Btrfs), result->name.chars); } else { - ffStrbufSetS(&buffer, FF_BTRFS_MODULE_NAME); + ffStrbufSetS(&buffer, FF_MODULE_GET_DISPLAY_NAME(Btrfs)); } } else { ffStrbufClear(&buffer); @@ -100,11 +100,11 @@ bool ffPrintBtrfs(FFBtrfsOptions* options) { const char* error = ffDetectBtrfs(&results); if (error) { - ffPrintError(FF_BTRFS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Btrfs), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (results.length == 0) { - ffPrintError(FF_BTRFS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "No btrfs drive found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Btrfs), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "No btrfs drive found"); return false; } @@ -136,7 +136,7 @@ void ffParseBtrfsJsonObject(FFBtrfsOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_BTRFS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Btrfs), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -198,8 +198,30 @@ void ffDestroyBtrfsOptions(FFBtrfsOptions* options) { } FFModuleBaseInfo ffBtrfsModuleInfo = { - .name = FF_BTRFS_MODULE_NAME, + .name = "Btrfs", .description = "Print Linux BTRFS volumes", + .displayName = { + .en = "BTRFS", + .ar = "BTRFS", + .cs = "BTRFS", + .de = "BTRFS", + .es = "BTRFS", + .fr = "BTRFS", + .gl = "BTRFS", + .he = "BTRFS", + .id = "BTRFS", + .it = "BTRFS", + .ja = "BTRFS", + .ko = "BTRFS", + .pl = "BTRFS", + .pt = "BTRFS", + .ru = "BTRFS", + .tr = "BTRFS", + .uk = "BTRFS", + .vi = "BTRFS", + .zh_CN = "BTRFS", + .zh_TW = "BTRFS", + }, .initOptions = (void*) ffInitBtrfsOptions, .destroyOptions = (void*) ffDestroyBtrfsOptions, .parseJsonObject = (void*) ffParseBtrfsJsonObject, diff --git a/src/modules/btrfs/btrfs.h b/src/modules/btrfs/btrfs.h index fc0c6abf4b..813ff8a2b5 100644 --- a/src/modules/btrfs/btrfs.h +++ b/src/modules/btrfs/btrfs.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_BTRFS_MODULE_NAME "Btrfs" - bool ffPrintBtrfs(FFBtrfsOptions* options); void ffInitBtrfsOptions(FFBtrfsOptions* options); void ffDestroyBtrfsOptions(FFBtrfsOptions* options); diff --git a/src/modules/camera/camera.c b/src/modules/camera/camera.c index 02c454bbbf..95101c23db 100644 --- a/src/modules/camera/camera.c +++ b/src/modules/camera/camera.c @@ -5,7 +5,7 @@ static void printDevice(FFCameraOptions* options, const FFCameraResult* device, uint8_t index) { if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_CAMERA_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Camera), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&device->name, stdout); if (device->colorspace.length > 0) { @@ -19,7 +19,7 @@ static void printDevice(FFCameraOptions* options, const FFCameraResult* device, putchar('\n'); } } else { - FF_PRINT_FORMAT_CHECKED(FF_CAMERA_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, (((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Camera), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, (((FFformatarg[]) { FF_ARG(device->name, "name"), FF_ARG(device->vendor, "vendor"), FF_ARG(device->colorspace, "colorspace"), @@ -35,12 +35,12 @@ bool ffPrintCamera(FFCameraOptions* options) { const char* error = ffDetectCamera(&result); if (error) { - ffPrintError(FF_CAMERA_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Camera), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (result.length == 0) { - ffPrintError(FF_CAMERA_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No camera found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Camera), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No camera found"); return false; } @@ -67,7 +67,7 @@ void ffParseCameraJsonObject(FFCameraOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_CAMERA_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Camera), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -115,8 +115,30 @@ void ffDestroyCameraOptions(FFCameraOptions* options) { } FFModuleBaseInfo ffCameraModuleInfo = { - .name = FF_CAMERA_MODULE_NAME, + .name = "Camera", .description = "Print available cameras", + .displayName = { + .en = "Camera", + .ar = "الكاميرا", + .cs = "Kamera", + .de = "Kamera", + .es = "Cámara", + .fr = "Caméra", + .gl = "Cámara", + .he = "מצלמה", + .id = "Kamera", + .it = "Fotocamera", + .ja = "カメラ", + .ko = "카메라", + .pl = "Kamera", + .pt = "Câmera", + .ru = "Камера", + .tr = "Kamera", + .uk = "Камера", + .vi = "Máy ảnh", + .zh_CN = "摄像头", + .zh_TW = "攝像頭", + }, .initOptions = (void*) ffInitCameraOptions, .destroyOptions = (void*) ffDestroyCameraOptions, .parseJsonObject = (void*) ffParseCameraJsonObject, diff --git a/src/modules/camera/camera.h b/src/modules/camera/camera.h index e8b3560400..f87a8595ec 100644 --- a/src/modules/camera/camera.h +++ b/src/modules/camera/camera.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_CAMERA_MODULE_NAME "Camera" - bool ffPrintCamera(FFCameraOptions* options); void ffInitCameraOptions(FFCameraOptions* options); void ffDestroyCameraOptions(FFCameraOptions* options); diff --git a/src/modules/chassis/chassis.c b/src/modules/chassis/chassis.c index 9b5b21e7a6..6049d533d5 100644 --- a/src/modules/chassis/chassis.c +++ b/src/modules/chassis/chassis.c @@ -16,24 +16,24 @@ bool ffPrintChassis(FFChassisOptions* options) { const char* error = ffDetectChassis(&result); if (error) { - ffPrintError(FF_CHASSIS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Chassis), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); goto exit; } if (result.type.length == 0) { - ffPrintError(FF_CHASSIS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "chassis_type is not set by O.E.M."); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Chassis), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "chassis_type is not set by O.E.M."); goto exit; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_CHASSIS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Chassis), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&result.type, stdout); if (result.version.length) { printf(" (%s)", result.version.chars); } putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_CHASSIS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Chassis), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.type, "type"), FF_ARG(result.vendor, "vendor"), FF_ARG(result.version, "version"), @@ -58,7 +58,7 @@ void ffParseChassisJsonObject(FFChassisOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_CHASSIS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Chassis), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -110,8 +110,30 @@ void ffDestroyChassisOptions(FFChassisOptions* options) { } FFModuleBaseInfo ffChassisModuleInfo = { - .name = FF_CHASSIS_MODULE_NAME, + .name = "Chassis", .description = "Print chassis type information (desktop, laptop, etc.)", + .displayName = { + .en = "Chassis", + .ar = "الهيكل", + .cs = "Skříň", + .de = "Gehäuse", + .es = "Chasis", + .fr = "Châssis", + .gl = "Chasis", + .he = "מארז", + .id = "Casing", + .it = "Telaio", + .ja = "シャーシ", + .ko = "섀시", + .pl = "Obudowa", + .pt = "Chassi", + .ru = "Корпус", + .tr = "Kasa", + .uk = "Корпус", + .vi = "Vỏ máy", + .zh_CN = "机箱", + .zh_TW = "機箱", + }, .initOptions = (void*) ffInitChassisOptions, .destroyOptions = (void*) ffDestroyChassisOptions, .parseJsonObject = (void*) ffParseChassisJsonObject, diff --git a/src/modules/chassis/chassis.h b/src/modules/chassis/chassis.h index d3ba4892cb..a57f7b876c 100644 --- a/src/modules/chassis/chassis.h +++ b/src/modules/chassis/chassis.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_CHASSIS_MODULE_NAME "Chassis" - bool ffPrintChassis(FFChassisOptions* options); void ffInitChassisOptions(FFChassisOptions* options); void ffDestroyChassisOptions(FFChassisOptions* options); diff --git a/src/modules/codec/codec.c b/src/modules/codec/codec.c index a9a465bf84..2e5b87983b 100644 --- a/src/modules/codec/codec.c +++ b/src/modules/codec/codec.c @@ -51,9 +51,9 @@ static void printCodecLine(const FFCodecOptions* options, uint8_t index, FFstrbu FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); if (options->moduleArgs.key.length == 0) { if (gpu->length > 0) { - ffStrbufSetF(&key, "%s (%s - %s)", FF_CODEC_MODULE_NAME, direction, gpu->chars); + ffStrbufSetF(&key, "%s (%s - %s)", FF_MODULE_GET_DISPLAY_NAME(Codec), direction, gpu->chars); } else { - ffStrbufSetF(&key, "%s (%s)", FF_CODEC_MODULE_NAME, direction); + ffStrbufSetF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Codec), direction); } } else { FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]){ @@ -100,12 +100,12 @@ bool ffPrintCodec(FFCodecOptions* options) { const char* error = ffDetectCodec(options, &result); if (error) { - ffPrintError(FF_CODEC_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Codec), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (result.length == 0) { - ffPrintError(FF_CODEC_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No hardware video acceleration found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Codec), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No hardware video acceleration found"); return false; } @@ -174,14 +174,14 @@ void ffParseCodecJsonObject(FFCodecOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_CODEC_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Codec), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->showType = (FFCodecShowType) value; } continue; } - ffPrintError(FF_CODEC_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Codec), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -251,8 +251,30 @@ void ffDestroyCodecOptions(FFCodecOptions* options) { } FFModuleBaseInfo ffCodecModuleInfo = { - .name = FF_CODEC_MODULE_NAME, + .name = "Codec", .description = "Print hardware video acceleration codec types (decode / encode)", + .displayName = { + .en = "Codec", + .ar = "الترميز", + .cs = "Kodek", + .de = "Codec", + .es = "Códec", + .fr = "Codec", + .gl = "Códec", + .he = "קודק", + .id = "Kodek", + .it = "Codec", + .ja = "コーデック", + .ko = "코덱", + .pl = "Kodek", + .pt = "Codec", + .ru = "Кодек", + .tr = "Kodek", + .uk = "Кодек", + .vi = "Codec", + .zh_CN = "编解码器", + .zh_TW = "編解碼器", + }, .initOptions = (void*) ffInitCodecOptions, .destroyOptions = (void*) ffDestroyCodecOptions, .parseJsonObject = (void*) ffParseCodecJsonObject, diff --git a/src/modules/codec/codec.h b/src/modules/codec/codec.h index efd3d7c018..20b9fbd35c 100644 --- a/src/modules/codec/codec.h +++ b/src/modules/codec/codec.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_CODEC_MODULE_NAME "Codec" - bool ffPrintCodec(FFCodecOptions* options); void ffInitCodecOptions(FFCodecOptions* options); void ffDestroyCodecOptions(FFCodecOptions* options); diff --git a/src/modules/colors/colors.c b/src/modules/colors/colors.c index 8d225d4bf1..b9fa813e72 100644 --- a/src/modules/colors/colors.c +++ b/src/modules/colors/colors.c @@ -37,7 +37,7 @@ bool ffPrintColors(FFColorsOptions* options) { } } if (result.length > 0) { - ffPrintLogoAndKey(FF_COLORS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); flag = true; if (options->paddingLeft > 0) { @@ -122,7 +122,7 @@ bool ffPrintColors(FFColorsOptions* options) { if (flag) { ffLogoPrintLine(); } else { - ffPrintLogoAndKey(FF_COLORS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); flag = true; } @@ -136,7 +136,7 @@ bool ffPrintColors(FFColorsOptions* options) { } if (!flag) { - ffPrintError(FF_COLORS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "Nothing to print"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "Nothing to print"); return false; } @@ -164,7 +164,7 @@ void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_COLORS_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->symbol = (FFColorsSymbol) value; } @@ -178,7 +178,7 @@ void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module) { if (unsafe_yyjson_equals_str(key, "block")) { if (!yyjson_is_obj(val)) { - ffPrintError(FF_COLORS_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: must be an object", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: must be an object", unsafe_yyjson_get_str(key)); } else { yyjson_val* width = yyjson_obj_get(val, "width"); if (width) { @@ -188,14 +188,14 @@ void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module) { yyjson_val* range = yyjson_obj_get(val, "range"); if (range) { if (!yyjson_is_arr(range) || yyjson_arr_size(range) != 2) { - ffPrintError(FF_COLORS_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s.range value: must be an array of 2 elements", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s.range value: must be an array of 2 elements", unsafe_yyjson_get_str(key)); } else { uint8_t start = (uint8_t) yyjson_get_uint(yyjson_arr_get(range, 0)); uint8_t end = (uint8_t) yyjson_get_uint(yyjson_arr_get(range, 1)); if (start > end) { - ffPrintError(FF_COLORS_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s.range value: range[0] > range[1]", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s.range value: range[0] > range[1]", unsafe_yyjson_get_str(key)); } else if (end > 15) { - ffPrintError(FF_COLORS_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s.range value: range[1] > 15", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s.range value: range[1] > 15", unsafe_yyjson_get_str(key)); } else { options->block.range[0] = start; options->block.range[1] = end; @@ -215,14 +215,14 @@ void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_COLORS_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->brightness = (FFColorsBrightness) value; } continue; } - ffPrintError(FF_COLORS_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Colors), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -295,8 +295,30 @@ void ffDestroyColorsOptions(FFColorsOptions* options) { } FFModuleBaseInfo ffColorsModuleInfo = { - .name = FF_COLORS_MODULE_NAME, + .name = "Colors", .description = "Display the terminal's 16-color palette", + .displayName = { + .en = "Colors", + .ar = "الألوان", + .cs = "Barvy", + .de = "Farben", + .es = "Colores", + .fr = "Couleurs", + .gl = "Cores", + .he = "צבעים", + .id = "Warna", + .it = "Colori", + .ja = "色", + .ko = "색상", + .pl = "Kolory", + .pt = "Cores", + .ru = "Цвета", + .tr = "Renkler", + .uk = "Кольори", + .vi = "Màu sắc", + .zh_CN = "颜色", + .zh_TW = "顏色", + }, .initOptions = (void*) ffInitColorsOptions, .destroyOptions = (void*) ffDestroyColorsOptions, .parseJsonObject = (void*) ffParseColorsJsonObject, diff --git a/src/modules/colors/colors.h b/src/modules/colors/colors.h index c88ccd7a50..d7558aff77 100644 --- a/src/modules/colors/colors.h +++ b/src/modules/colors/colors.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_COLORS_MODULE_NAME "Colors" - bool ffPrintColors(FFColorsOptions* options); void ffInitColorsOptions(FFColorsOptions* options); void ffDestroyColorsOptions(FFColorsOptions* options); diff --git a/src/modules/command/command.c b/src/modules/command/command.c index 4de947a792..21ac4378c5 100644 --- a/src/modules/command/command.c +++ b/src/modules/command/command.c @@ -8,12 +8,12 @@ bool ffPrintCommand(FFCommandOptions* options) { const char* error = ffDetectCommand(options, &result); if (error) { - ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Command), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (!result.length) { - ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No result generated"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Command), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No result generated"); return false; } @@ -23,18 +23,18 @@ bool ffPrintCommand(FFCommandOptions* options) { size_t len = 0; while (ffStrbufGetline(&line, &len, &result)) { if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_COMMAND_MODULE_NAME, ++index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Command), ++index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); puts(line); } else { - FF_PRINT_FORMAT_CHECKED(FF_COMMAND_MODULE_NAME, ++index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(line, "result") })); + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Command), ++index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(line, "result") })); } } } else { if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Command), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&result, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result, "result") })); + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Command), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result, "result") })); } } @@ -79,7 +79,7 @@ void ffParseCommandJsonObject(FFCommandOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Command), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -153,8 +153,30 @@ void ffDestroyCommandOptions(FFCommandOptions* options) { } FFModuleBaseInfo ffCommandModuleInfo = { - .name = FF_COMMAND_MODULE_NAME, + .name = "Command", .description = "Run custom shell scripts", + .displayName = { + .en = "Command", + .ar = "الأمر", + .cs = "Příkaz", + .de = "Befehl", + .es = "Comando", + .fr = "Commande", + .gl = "Comando", + .he = "פקודה", + .id = "Perintah", + .it = "Comando", + .ja = "コマンド", + .ko = "명령어", + .pl = "Polecenie", + .pt = "Comando", + .ru = "Команда", + .tr = "Komut", + .uk = "Команда", + .vi = "Lệnh", + .zh_CN = "命令", + .zh_TW = "命令", + }, .initOptions = (void*) ffInitCommandOptions, .destroyOptions = (void*) ffDestroyCommandOptions, .parseJsonObject = (void*) ffParseCommandJsonObject, diff --git a/src/modules/command/command.h b/src/modules/command/command.h index e0e143e016..bd55f3fbd9 100644 --- a/src/modules/command/command.h +++ b/src/modules/command/command.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_COMMAND_MODULE_NAME "Command" - bool ffPrepareCommand(FFCommandOptions* options); bool ffPrintCommand(FFCommandOptions* options); diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index 2716b2e037..9d52576873 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -23,9 +23,9 @@ bool ffPrintCPU(FFCPUOptions* options) { const char* error = ffDetectCPU(options, &cpu); if (error) { - ffPrintError(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(CPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); } else if (cpu.vendor.length == 0 && cpu.name.length == 0 && cpu.coresOnline <= 1) { - ffPrintError(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No CPU detected"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(CPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No CPU detected"); } else { FF_STRBUF_AUTO_DESTROY coreTypes = ffStrbufCreate(); if (options->showPeCoreCount) { @@ -43,7 +43,7 @@ bool ffPrintCPU(FFCPUOptions* options) { } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(CPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY str = ffStrbufCreate(); @@ -85,7 +85,7 @@ bool ffPrintCPU(FFCPUOptions* options) { FF_STRBUF_AUTO_DESTROY tempStr = ffStrbufCreate(); ffTempsAppendNum(cpu.temperature, &tempStr, options->tempConfig, &options->moduleArgs); - FF_PRINT_FORMAT_CHECKED(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(CPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(cpu.name, "name"), FF_ARG(cpu.vendor, "vendor"), FF_ARG(cpu.coresPhysical, "cores-physical"), @@ -131,7 +131,7 @@ void ffParseCPUJsonObject(FFCPUOptions* options, yyjson_val* module) { } if (unsafe_yyjson_equals_str(key, "freqNdigits")) { - ffPrintError(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "modules.CPU.freqNdigits has been moved to display.freq.ndigits"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(CPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "modules.CPU.freqNdigits has been moved to display.freq.ndigits"); continue; } @@ -140,7 +140,7 @@ void ffParseCPUJsonObject(FFCPUOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_CPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(CPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -251,8 +251,30 @@ void ffDestroyCPUOptions(FFCPUOptions* options) { } FFModuleBaseInfo ffCPUModuleInfo = { - .name = FF_CPU_MODULE_NAME, + .name = "CPU", .description = "Print CPU name, frequency, etc.", + .displayName = { + .en = "CPU", + .ar = "المعالج", + .cs = "CPU", + .de = "CPU", + .es = "CPU", + .fr = "CPU", + .gl = "CPU", + .he = "מעבד", + .id = "CPU", + .it = "CPU", + .ja = "CPU", + .ko = "CPU", + .pl = "CPU", + .pt = "CPU", + .ru = "Процессор", + .tr = "CPU", + .uk = "CPU", + .vi = "CPU", + .zh_CN = "CPU", + .zh_TW = "CPU", + }, .initOptions = (void*) ffInitCPUOptions, .destroyOptions = (void*) ffDestroyCPUOptions, .parseJsonObject = (void*) ffParseCPUJsonObject, diff --git a/src/modules/cpu/cpu.h b/src/modules/cpu/cpu.h index 33fff6bf5b..ce816a7725 100644 --- a/src/modules/cpu/cpu.h +++ b/src/modules/cpu/cpu.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_CPU_MODULE_NAME "CPU" - bool ffPrintCPU(FFCPUOptions* options); void ffInitCPUOptions(FFCPUOptions* options); void ffDestroyCPUOptions(FFCPUOptions* options); diff --git a/src/modules/cpucache/cpucache.c b/src/modules/cpucache/cpucache.c index 7dd39d1cb6..efb9407e45 100644 --- a/src/modules/cpucache/cpucache.c +++ b/src/modules/cpucache/cpucache.c @@ -5,8 +5,6 @@ #include "detection/cpucache/cpucache.h" #include "modules/cpucache/cpucache.h" -#define FF_CPUCACHE_DISPLAY_NAME "CPU Cache" - static void printCPUCacheNormal(const FFCPUCacheResult* result, FFCPUCacheOptions* options) { FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); @@ -16,7 +14,7 @@ static void printCPUCacheNormal(const FFCPUCacheResult* result, FFCPUCacheOption ffStrbufClear(&key); levelStr[1] = (char) ('1' + i); if (options->moduleArgs.key.length == 0) { - ffStrbufAppendF(&key, "%s (%s)", FF_CPUCACHE_DISPLAY_NAME, levelStr); + ffStrbufAppendF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(CPUCache), levelStr); } else { uint32_t index = i + 1; FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]) { @@ -88,12 +86,12 @@ static void printCPUCacheCompact(const FFCPUCacheResult* result, FFCPUCacheOptio } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_CPUCACHE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(CPUCache), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&buffer, stdout); } else { FF_STRBUF_AUTO_DESTROY buffer2 = ffStrbufCreate(); ffSizeAppendNum(sum, &buffer2); - FF_PRINT_FORMAT_CHECKED(FF_CPUCACHE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(CPUCache), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(buffer, "result"), FF_ARG(buffer2, "sum"), })); @@ -114,7 +112,7 @@ bool ffPrintCPUCache(FFCPUCacheOptions* options) { const char* error = ffDetectCPUCache(&result); if (error) { - ffPrintError(FF_CPUCACHE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(CPUCache), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); goto exit; } @@ -147,7 +145,7 @@ void ffParseCPUCacheJsonObject(FFCPUCacheOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_CPUCACHE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(CPUCache), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -223,8 +221,30 @@ void ffDestroyCPUCacheOptions(FFCPUCacheOptions* options) { } FFModuleBaseInfo ffCPUCacheModuleInfo = { - .name = FF_CPUCACHE_MODULE_NAME, + .name = "CPUCache", .description = "Print CPU cache sizes", + .displayName = { + .en = "CPU Cache", + .ar = "ذاكرة المعالج المؤقتة", + .cs = "Mezipaměť CPU", + .de = "CPU-Cache", + .es = "Caché de la CPU", + .fr = "Cache CPU", + .gl = "Caché da CPU", + .he = "מטמון מעבד", + .id = "Cache CPU", + .it = "Cache CPU", + .ja = "CPUキャッシュ", + .ko = "CPU 캐시", + .pl = "Pamięć podręczna CPU", + .pt = "Cache da CPU", + .ru = "Кэш процессора", + .tr = "CPU Önbelleği", + .uk = "Кеш ЦП", + .vi = "Bộ nhớ đệm CPU", + .zh_CN = "CPU缓存", + .zh_TW = "CPU快取記憶體", + }, .initOptions = (void*) ffInitCPUCacheOptions, .destroyOptions = (void*) ffDestroyCPUCacheOptions, .parseJsonObject = (void*) ffParseCPUCacheJsonObject, diff --git a/src/modules/cpucache/cpucache.h b/src/modules/cpucache/cpucache.h index 5061b44417..2bc16fe5f3 100644 --- a/src/modules/cpucache/cpucache.h +++ b/src/modules/cpucache/cpucache.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_CPUCACHE_MODULE_NAME "CPUCache" - bool ffPrintCPUCache(FFCPUCacheOptions* options); void ffInitCPUCacheOptions(FFCPUCacheOptions* options); void ffDestroyCPUCacheOptions(FFCPUCacheOptions* options); diff --git a/src/modules/cpuusage/cpuusage.c b/src/modules/cpuusage/cpuusage.c index ea592a2739..96c45c28d2 100644 --- a/src/modules/cpuusage/cpuusage.c +++ b/src/modules/cpuusage/cpuusage.c @@ -5,14 +5,12 @@ #include "detection/cpuusage/cpuusage.h" #include "modules/cpuusage/cpuusage.h" -#define FF_CPUUSAGE_DISPLAY_NAME "CPU Usage" - bool ffPrintCPUUsage(FFCPUUsageOptions* options) { FF_LIST_AUTO_DESTROY percentages = ffListCreate(); const char* error = ffGetCpuUsageResult(options, &percentages); if (error) { - ffPrintError(FF_CPUUSAGE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(CPUUsage), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } @@ -54,7 +52,7 @@ bool ffPrintCPUUsage(FFCPUUsageOptions* options) { FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type; if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_CPUUSAGE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(CPUUsage), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY str = ffStrbufCreate(); if (!options->separate) { @@ -102,7 +100,7 @@ bool ffPrintCPUUsage(FFCPUUsageOptions* options) { ffPercentAppendBar(&maxBar, maxValue, options->percent, &options->moduleArgs); } - FF_PRINT_FORMAT_CHECKED(FF_CPUUSAGE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(CPUUsage), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(avgNum, "avg"), FF_ARG(maxNum, "max"), FF_ARG(maxIndex, "max-index"), @@ -139,7 +137,7 @@ void ffParseCPUUsageJsonObject(FFCPUUsageOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_CPUUSAGE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(CPUUsage), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -181,8 +179,30 @@ void ffDestroyCPUUsageOptions(FFCPUUsageOptions* options) { } FFModuleBaseInfo ffCPUUsageModuleInfo = { - .name = FF_CPUUSAGE_MODULE_NAME, + .name = "CPUUsage", .description = "Print CPU usage. Collecting data takes some time", + .displayName = { + .en = "CPU Usage", + .ar = "استخدام المعالج", + .cs = "Využití CPU", + .de = "CPU-Auslastung", + .es = "Uso de la CPU", + .fr = "Utilisation du CPU", + .gl = "Uso da CPU", + .he = "שימוש במעבד", + .id = "Penggunaan CPU", + .it = "Utilizzo della CPU", + .ja = "CPU使用率", + .ko = "CPU 사용량", + .pl = "Użycie CPU", + .pt = "Uso da CPU", + .ru = "Использование процессора", + .tr = "CPU Kullanımı", + .uk = "Використання ЦП", + .vi = "Mức sử dụng CPU", + .zh_CN = "CPU使用率", + .zh_TW = "CPU使用率", + }, .initOptions = (void*) ffInitCPUUsageOptions, .destroyOptions = (void*) ffDestroyCPUUsageOptions, .parseJsonObject = (void*) ffParseCPUUsageJsonObject, diff --git a/src/modules/cpuusage/cpuusage.h b/src/modules/cpuusage/cpuusage.h index efa8b485cc..bd009a833f 100644 --- a/src/modules/cpuusage/cpuusage.h +++ b/src/modules/cpuusage/cpuusage.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_CPUUSAGE_MODULE_NAME "CPUUsage" - void ffPrepareCPUUsage(); bool ffPrintCPUUsage(FFCPUUsageOptions* options); diff --git a/src/modules/cursor/cursor.c b/src/modules/cursor/cursor.c index 1be3ac6e35..7bc274802c 100644 --- a/src/modules/cursor/cursor.c +++ b/src/modules/cursor/cursor.c @@ -14,7 +14,7 @@ bool ffPrintCursor(FFCursorOptions* options) { ffDetectCursor(&result); if (result.error.length) { - ffPrintError(FF_CURSOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", result.error.chars); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Cursor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", result.error.chars); } else { ffStrbufRemoveIgnCaseEndS(&result.theme, "cursors"); ffStrbufRemoveIgnCaseEndS(&result.theme, "cursor"); @@ -25,7 +25,7 @@ bool ffPrintCursor(FFCursorOptions* options) { } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_CURSOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Cursor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&result.theme, stdout); if (result.size.length > 0 && !ffStrbufEqualS(&result.size, "0")) { @@ -34,7 +34,7 @@ bool ffPrintCursor(FFCursorOptions* options) { putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_CURSOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Cursor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.theme, "theme"), FF_ARG(result.size, "size"), })); @@ -58,7 +58,7 @@ void ffParseCursorJsonObject(FFCursorOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_CURSOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Cursor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -100,8 +100,30 @@ void ffDestroyCursorOptions(FFCursorOptions* options) { } FFModuleBaseInfo ffCursorModuleInfo = { - .name = FF_CURSOR_MODULE_NAME, + .name = "Cursor", .description = "Print cursor style name", + .displayName = { + .en = "Cursor", + .ar = "المؤشر", + .cs = "Kurzor", + .de = "Mauszeiger", + .es = "Cursor", + .fr = "Curseur", + .gl = "Cursor", + .he = "סמן", + .id = "Kursor", + .it = "Cursore", + .ja = "カーソル", + .ko = "커서", + .pl = "Kursor", + .pt = "Cursor", + .ru = "Курсор", + .tr = "İmleç", + .uk = "Курсор", + .vi = "Con trỏ", + .zh_CN = "光标", + .zh_TW = "游標", + }, .initOptions = (void*) ffInitCursorOptions, .destroyOptions = (void*) ffDestroyCursorOptions, .parseJsonObject = (void*) ffParseCursorJsonObject, diff --git a/src/modules/cursor/cursor.h b/src/modules/cursor/cursor.h index e7695692cd..e94a259b57 100644 --- a/src/modules/cursor/cursor.h +++ b/src/modules/cursor/cursor.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_CURSOR_MODULE_NAME "Cursor" - bool ffPrintCursor(FFCursorOptions* options); void ffInitCursorOptions(FFCursorOptions* options); void ffDestroyCursorOptions(FFCursorOptions* options); diff --git a/src/modules/custom/custom.c b/src/modules/custom/custom.c index 054f3704ca..13a1fed0e6 100644 --- a/src/modules/custom/custom.c +++ b/src/modules/custom/custom.c @@ -5,7 +5,7 @@ #include "modules/custom/custom.h" bool ffPrintCustom(FFCustomOptions* options) { - ffPrintFormat(FF_CUSTOM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, 0, ((FFformatarg[]) {})); + ffPrintFormat(FF_MODULE_GET_DISPLAY_NAME(Custom), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, 0, ((FFformatarg[]) {})); return true; } @@ -21,7 +21,7 @@ void ffParseCustomJsonObject(FFCustomOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_CUSTOM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Custom), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -35,8 +35,30 @@ void ffDestroyCustomOptions(FFCustomOptions* options) { } FFModuleBaseInfo ffCustomModuleInfo = { - .name = FF_CUSTOM_MODULE_NAME, + .name = "Custom", .description = "Print a custom string, with or without key", + .displayName = { + .en = "Custom", + .ar = "مخصص", + .cs = "Vlastní", + .de = "Benutzerdefiniert", + .es = "Personalizado", + .fr = "Personnalisé", + .gl = "Personalizado", + .he = "מותאם אישית", + .id = "Kustom", + .it = "Personalizzato", + .ja = "カスタム", + .ko = "사용자 정의", + .pl = "Niestandardowy", + .pt = "Personalizado", + .ru = "Пользовательский", + .tr = "Özel", + .uk = "Власний", + .vi = "Tùy chỉnh", + .zh_CN = "自定义", + .zh_TW = "自訂", + }, .initOptions = (void*) ffInitCustomOptions, .destroyOptions = (void*) ffDestroyCustomOptions, .parseJsonObject = (void*) ffParseCustomJsonObject, diff --git a/src/modules/custom/custom.h b/src/modules/custom/custom.h index 628a621a32..f84dd1266d 100644 --- a/src/modules/custom/custom.h +++ b/src/modules/custom/custom.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_CUSTOM_MODULE_NAME "Custom" - bool ffPrintCustom(FFCustomOptions* options); void ffInitCustomOptions(FFCustomOptions* options); void ffDestroyCustomOptions(FFCustomOptions* options); diff --git a/src/modules/datetime/datetime.c b/src/modules/datetime/datetime.c index 14e3b8251f..1a7b12f253 100644 --- a/src/modules/datetime/datetime.c +++ b/src/modules/datetime/datetime.c @@ -8,8 +8,6 @@ #pragma GCC diagnostic ignored "-Wformat" // warning: unknown conversion type character 'F' in format -#define FF_DATETIME_DISPLAY_NAME "Date & Time" - typedef struct FFDateTimeResult { // Examples for 21.02.2022 - 15:18:37 uint16_t year; // 2022 @@ -67,7 +65,7 @@ static void printDateTimeFormat(struct tm* tm, const FFModuleArgs* moduleArgs) { strftime(result.amPm, sizeof(result.amPm), "%p", tm); FF_PRINT_FORMAT_CHECKED( - FF_DATETIME_DISPLAY_NAME, + FF_MODULE_GET_DISPLAY_NAME(DateTime), 0, moduleArgs, FF_PRINT_TYPE_DEFAULT, @@ -112,11 +110,11 @@ bool ffPrintDateTime(FFDateTimeOptions* options) { char buffer[32]; if (strftime(buffer, ARRAY_SIZE(buffer), "%F %T", tm) == 0) // yyyy-MM-dd HH:mm:ss { - ffPrintError(FF_DATETIME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "strftime() failed"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DateTime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "strftime() failed"); return false; } - ffPrintLogoAndKey(FF_DATETIME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(DateTime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); puts(buffer); return true; @@ -130,7 +128,7 @@ void ffParseDateTimeJsonObject(FFDateTimeOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_DATETIME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DateTime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -152,8 +150,30 @@ void ffDestroyDateTimeOptions(FFDateTimeOptions* options) { } FFModuleBaseInfo ffDateTimeModuleInfo = { - .name = FF_DATETIME_MODULE_NAME, + .name = "DateTime", .description = "Print the current date and time", + .displayName = { + .en = "Date & Time", + .ar = "التاريخ والوقت", + .cs = "Datum a čas", + .de = "Datum & Uhrzeit", + .es = "Fecha y hora", + .fr = "Date et heure", + .gl = "Data e hora", + .he = "תאריך ושעה", + .id = "Tanggal & Waktu", + .it = "Data e ora", + .ja = "日付と時刻", + .ko = "날짜/시간", + .pl = "Data i godzina", + .pt = "Data e hora", + .ru = "Дата и время", + .tr = "Tarih ve Saat", + .uk = "Дата й час", + .vi = "Ngày giờ", + .zh_CN = "日期时间", + .zh_TW = "日期時間", + }, .initOptions = (void*) ffInitDateTimeOptions, .destroyOptions = (void*) ffDestroyDateTimeOptions, .parseJsonObject = (void*) ffParseDateTimeJsonObject, diff --git a/src/modules/datetime/datetime.h b/src/modules/datetime/datetime.h index 29c6f54e41..44123ed1a6 100644 --- a/src/modules/datetime/datetime.h +++ b/src/modules/datetime/datetime.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_DATETIME_MODULE_NAME "DateTime" - bool ffPrintDateTime(FFDateTimeOptions* options); void ffInitDateTimeOptions(FFDateTimeOptions* options); void ffDestroyDateTimeOptions(FFDateTimeOptions* options); diff --git a/src/modules/de/de.c b/src/modules/de/de.c index 8e301fa32e..a7bb99c3a2 100644 --- a/src/modules/de/de.c +++ b/src/modules/de/de.c @@ -9,7 +9,7 @@ bool ffPrintDE(FFDEOptions* options) { const FFDisplayServerResult* result = ffConnectDisplayServer(); if (result->dePrettyName.length == 0) { - ffPrintError(FF_DE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No DE found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DE), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No DE found"); return false; } @@ -17,7 +17,7 @@ bool ffPrintDE(FFDEOptions* options) { ffDetectDEVersion(&result->dePrettyName, &version, options); if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_DE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(DE), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&result->dePrettyName, stdout); @@ -28,7 +28,7 @@ bool ffPrintDE(FFDEOptions* options) { putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_DE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result->deProcessName, "process-name"), FF_ARG(result->dePrettyName, "pretty-name"), FF_ARG(version, "version") })); + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(DE), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result->deProcessName, "process-name"), FF_ARG(result->dePrettyName, "pretty-name"), FF_ARG(version, "version") })); } return true; @@ -43,11 +43,11 @@ void ffParseDEJsonObject(FFDEOptions* options, yyjson_val* module) { } if (unsafe_yyjson_equals_str(key, "slowVersionDetection")) { - ffPrintError(FF_DE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Key `slowVersionDetection` is deprecated, it's always true"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DE), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Key `slowVersionDetection` is deprecated, it's always true"); continue; } - ffPrintError(FF_DE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DE), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -82,8 +82,30 @@ void ffDestroyDEOptions(FFDEOptions* options) { } FFModuleBaseInfo ffDEModuleInfo = { - .name = FF_DE_MODULE_NAME, + .name = "DE", .description = "Print desktop environment name", + .displayName = { + .en = "Desktop Environment", + .ar = "بيئة سطح المكتب", + .cs = "Pracovní prostředí", + .de = "Desktop-Umgebung", + .es = "Entorno de escritorio", + .fr = "Environnement de Bureau", + .gl = "Contorno do Escritorio", + .he = "סביבת שולחן עבודה", + .id = "Lingkungan Desktop", + .it = "Ambiente Desktop", + .ja = "デスクトップ環境", + .ko = "데스크탑 환경", + .pl = "Środowisko graficzne", + .pt = "Ambiente de desktop", + .ru = "Рабочее окружение", + .tr = "Masaüstü Ortamı", + .uk = "Стільниче середовище", + .vi = "Môi trường desktop", + .zh_CN = "桌面环境", + .zh_TW = "桌面環境", + }, .initOptions = (void*) ffInitDEOptions, .destroyOptions = (void*) ffDestroyDEOptions, .parseJsonObject = (void*) ffParseDEJsonObject, diff --git a/src/modules/de/de.h b/src/modules/de/de.h index 8aaf642d07..857524bd7c 100644 --- a/src/modules/de/de.h +++ b/src/modules/de/de.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_DE_MODULE_NAME "DE" - bool ffPrintDE(FFDEOptions* options); void ffInitDEOptions(FFDEOptions* options); void ffDestroyDEOptions(FFDEOptions* options); diff --git a/src/modules/disk/disk.c b/src/modules/disk/disk.c index 74b6488558..da6fe9faba 100644 --- a/src/modules/disk/disk.c +++ b/src/modules/disk/disk.c @@ -16,7 +16,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); if (options->moduleArgs.key.length == 0) { - ffStrbufSetF(&key, "%s (%s)", FF_DISK_MODULE_NAME, disk->mountpoint.chars); + ffStrbufSetF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Disk), disk->mountpoint.chars); } else { FF_STRBUF_AUTO_DESTROY mountpointLink = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY nameLink = ffStrbufCreate(); @@ -187,12 +187,12 @@ bool ffPrintDisk(FFDiskOptions* options) { const char* error = ffDetectDisks(options, &disks); if (error) { - ffPrintError(FF_DISK_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Disk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (disks.length == 0) { - ffPrintError(FF_DISK_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No disks found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Disk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No disks found"); return false; } @@ -327,7 +327,7 @@ void ffParseDiskJsonObject(FFDiskOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_DISK_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Disk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -451,8 +451,30 @@ void ffDestroyDiskOptions(FFDiskOptions* options) { } FFModuleBaseInfo ffDiskModuleInfo = { - .name = FF_DISK_MODULE_NAME, + .name = "Disk", .description = "Print partitions, space usage, file system, etc", + .displayName = { + .en = "Disk", + .ar = "القرص", + .cs = "Disk", + .de = "Festplatte", + .es = "Disco", + .fr = "Disque", + .gl = "Disco", + .he = "דיסק", + .id = "Disk", + .it = "Disco", + .ja = "ディスク", + .ko = "디스크", + .pl = "Dysk", + .pt = "Disco", + .ru = "Диск", + .tr = "Disk", + .uk = "Диск", + .vi = "Ổ đĩa", + .zh_CN = "磁盘分区", + .zh_TW = "硬碟分割", + }, .initOptions = (void*) ffInitDiskOptions, .destroyOptions = (void*) ffDestroyDiskOptions, .parseJsonObject = (void*) ffParseDiskJsonObject, diff --git a/src/modules/disk/disk.h b/src/modules/disk/disk.h index 5f7820ac7c..4d5bf76ad7 100644 --- a/src/modules/disk/disk.h +++ b/src/modules/disk/disk.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_DISK_MODULE_NAME "Disk" - bool ffPrintDisk(FFDiskOptions* options); void ffInitDiskOptions(FFDiskOptions* options); void ffDestroyDiskOptions(FFDiskOptions* options); diff --git a/src/modules/diskio/diskio.c b/src/modules/diskio/diskio.c index 372910d10a..61ed83001c 100644 --- a/src/modules/diskio/diskio.c +++ b/src/modules/diskio/diskio.c @@ -5,15 +5,13 @@ #include "detection/diskio/diskio.h" #include "modules/diskio/diskio.h" -#define FF_DISKIO_DISPLAY_NAME "Disk IO" - static int sortDevices(const FFDiskIOResult* left, const FFDiskIOResult* right) { return ffStrbufComp(&left->name, &right->name); } static void formatKey(const FFDiskIOOptions* options, FFDiskIOResult* dev, uint32_t index, FFstrbuf* key) { if (options->moduleArgs.key.length == 0) { - ffStrbufSetF(key, FF_DISKIO_DISPLAY_NAME " (%s)", dev->name.length ? dev->name.chars : dev->devPath.chars); + ffStrbufSetF(key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(DiskIO), dev->name.length ? dev->name.chars : dev->devPath.chars); } else { ffStrbufClear(key); FF_PARSE_FORMAT_STRING_CHECKED(key, &options->moduleArgs.key, ((FFformatarg[]) { @@ -30,7 +28,7 @@ bool ffPrintDiskIO(FFDiskIOOptions* options) { const char* error = ffDetectDiskIO(&result, options); if (error) { - ffPrintError(FF_DISKIO_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DiskIO), 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, "%s", error); return false; } @@ -113,10 +111,13 @@ void ffParseDiskIOJsonObject(FFDiskIOOptions* options, yyjson_val* module) { if (unsafe_yyjson_equals_str(key, "waitTime")) { options->waitTime = (uint32_t) yyjson_get_uint(val); + if (options->waitTime == 0) { + options->waitTime = 1; + } continue; } - ffPrintError(FF_DISKIO_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DiskIO), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -163,7 +164,7 @@ void ffInitDiskIOOptions(FFDiskIOOptions* options) { ffStrbufInit(&options->namePrefix); options->detectTotal = false; - options->waitTime = 1000; + options->waitTime = 500; } void ffDestroyDiskIOOptions(FFDiskIOOptions* options) { @@ -172,8 +173,30 @@ void ffDestroyDiskIOOptions(FFDiskIOOptions* options) { } FFModuleBaseInfo ffDiskIOModuleInfo = { - .name = FF_DISKIO_MODULE_NAME, + .name = "DiskIO", .description = "Print physical disk I/O throughput", + .displayName = { + .en = "Disk I/O", + .ar = "إدخال/إخراج القرص", + .cs = "Diskové I/O", + .de = "Festplatten I/O", + .es = "E/S del disco", + .fr = "Entrée/Sortie disque", + .gl = "E/S do disco", + .he = "קלט/פלט דיסק", + .id = "I/O Disk", + .it = "I/O del disco", + .ja = "ディスク I/O", + .ko = "디스크 I/O", + .pl = "Dysk I/O", + .pt = "Disco I/O", + .ru = "Дисковый I/O", + .tr = "Disk G/Ç", + .uk = "Диск В/В", + .vi = "Đĩa I/O", + .zh_CN = "磁盘 I/O", + .zh_TW = "磁碟 I/O", + }, .initOptions = (void*) ffInitDiskIOOptions, .destroyOptions = (void*) ffDestroyDiskIOOptions, .parseJsonObject = (void*) ffParseDiskIOJsonObject, diff --git a/src/modules/diskio/diskio.h b/src/modules/diskio/diskio.h index 7fba81c9de..4bbfc3492d 100644 --- a/src/modules/diskio/diskio.h +++ b/src/modules/diskio/diskio.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_DISKIO_MODULE_NAME "DiskIO" - void ffPrepareDiskIO(FFDiskIOOptions* options); bool ffPrintDiskIO(FFDiskIOOptions* options); diff --git a/src/modules/display/display.c b/src/modules/display/display.c index eceb9a2d13..8d169d7db2 100644 --- a/src/modules/display/display.c +++ b/src/modules/display/display.c @@ -19,7 +19,7 @@ bool ffPrintDisplay(FFDisplayOptions* options) { const FFDisplayServerResult* dsResult = ffConnectDisplayServer(); if (dsResult->displays.length == 0) { - ffPrintError(FF_DISPLAY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Couldn't detect display"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Couldn't detect display"); return false; } @@ -28,7 +28,7 @@ bool ffPrintDisplay(FFDisplayOptions* options) { } if (options->compactType != FF_DISPLAY_COMPACT_TYPE_NONE) { - ffPrintLogoAndKey(FF_DISPLAY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); FF_LIST_FOR_EACH (FFDisplayResult, result, dsResult->displays) { @@ -71,11 +71,11 @@ bool ffPrintDisplay(FFDisplayOptions* options) { ffStrbufClear(&key); if (options->moduleArgs.key.length == 0) { if (result->name.length) { - ffStrbufAppendF(&key, "%s (%s)", FF_DISPLAY_MODULE_NAME, result->name.chars); + ffStrbufAppendF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Display), result->name.chars); } else if (moduleIndex > 0) { - ffStrbufAppendF(&key, "%s (%d)", FF_DISPLAY_MODULE_NAME, moduleIndex); + ffStrbufAppendF(&key, "%s (%d)", FF_MODULE_GET_DISPLAY_NAME(Display), moduleIndex); } else { - ffStrbufAppendS(&key, FF_DISPLAY_MODULE_NAME); + ffStrbufAppendS(&key, FF_MODULE_GET_DISPLAY_NAME(Display)); } } else { FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]) { @@ -221,7 +221,7 @@ void ffParseDisplayJsonObject(FFDisplayOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_DISPLAY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->compactType = (FFDisplayCompactType) value; } @@ -246,7 +246,7 @@ void ffParseDisplayJsonObject(FFDisplayOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_DISPLAY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->order = (FFDisplayOrder) value; } @@ -254,7 +254,7 @@ void ffParseDisplayJsonObject(FFDisplayOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_DISPLAY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -411,8 +411,30 @@ void ffDestroyDisplayOptions(FFDisplayOptions* options) { } FFModuleBaseInfo ffDisplayModuleInfo = { - .name = FF_DISPLAY_MODULE_NAME, + .name = "Display", .description = "Print resolutions, refresh rates, etc", + .displayName = { + .en = "Display", + .ar = "الشاشة", + .cs = "Displej", + .de = "Anzeige", + .es = "Pantalla", + .fr = "Écran", + .gl = "Pantalla", + .he = "תצוגה", + .id = "Tampilan", + .it = "Display", + .ja = "ディスプレイ", + .ko = "디스플레이", + .pl = "Wyświetlacz", + .pt = "Tela", + .ru = "Дисплей", + .tr = "Ekran", + .uk = "Дисплей", + .vi = "Hiển thị", + .zh_CN = "显示器", + .zh_TW = "顯示器", + }, .initOptions = (void*) ffInitDisplayOptions, .destroyOptions = (void*) ffDestroyDisplayOptions, .parseJsonObject = (void*) ffParseDisplayJsonObject, diff --git a/src/modules/display/display.h b/src/modules/display/display.h index a0a4b30959..c74e734d5a 100644 --- a/src/modules/display/display.h +++ b/src/modules/display/display.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_DISPLAY_MODULE_NAME "Display" - bool ffPrintDisplay(FFDisplayOptions* options); void ffInitDisplayOptions(FFDisplayOptions* options); void ffDestroyDisplayOptions(FFDisplayOptions* options); diff --git a/src/modules/dns/dns.c b/src/modules/dns/dns.c index 396c3bd431..517deb4add 100644 --- a/src/modules/dns/dns.c +++ b/src/modules/dns/dns.c @@ -10,12 +10,12 @@ bool ffPrintDNS(FFDNSOptions* options) { const char* error = ffDetectDNS(options, &result); if (error) { - ffPrintError(FF_DNS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DNS), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (result.length == 0) { - ffPrintError(FF_DNS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "NO DNS servers detected"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DNS), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "NO DNS servers detected"); return false; } @@ -40,11 +40,11 @@ bool ffPrintDNS(FFDNSOptions* options) { } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_DNS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(DNS), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&buf, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_DNS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(DNS), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(buf, "result"), })); } @@ -73,14 +73,14 @@ void ffParseDNSJsonObject(FFDNSOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_DNS_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DNS), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->showType = (FFDNSShowType) value; } continue; } - ffPrintError(FF_DNS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(DNS), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -134,8 +134,30 @@ void ffDestroyDNSOptions(FFDNSOptions* options) { } FFModuleBaseInfo ffDNSModuleInfo = { - .name = FF_DNS_MODULE_NAME, + .name = "DNS", .description = "Print configured DNS servers", + .displayName = { + .en = "DNS", + .ar = "DNS", + .cs = "DNS", + .de = "DNS", + .es = "DNS", + .fr = "DNS", + .gl = "DNS", + .he = "DNS", + .id = "DNS", + .it = "DNS", + .ja = "DNS", + .ko = "DNS", + .pl = "DNS", + .pt = "DNS", + .ru = "DNS", + .tr = "DNS", + .uk = "DNS", + .vi = "DNS", + .zh_CN = "DNS", + .zh_TW = "DNS", + }, .initOptions = (void*) ffInitDNSOptions, .destroyOptions = (void*) ffDestroyDNSOptions, .parseJsonObject = (void*) ffParseDNSJsonObject, diff --git a/src/modules/dns/dns.h b/src/modules/dns/dns.h index aec4e85fc6..6fbffe33d8 100644 --- a/src/modules/dns/dns.h +++ b/src/modules/dns/dns.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_DNS_MODULE_NAME "DNS" - bool ffPrintDNS(FFDNSOptions* options); void ffInitDNSOptions(FFDNSOptions* options); void ffDestroyDNSOptions(FFDNSOptions* options); diff --git a/src/modules/editor/editor.c b/src/modules/editor/editor.c index 342b31812c..b55a24c69d 100644 --- a/src/modules/editor/editor.c +++ b/src/modules/editor/editor.c @@ -16,12 +16,12 @@ bool ffPrintEditor(FFEditorOptions* options) { const char* error = ffDetectEditor(&result); if (error) { - ffPrintError(FF_EDITOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Editor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_EDITOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Editor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (result.exe.length) { ffStrbufWriteTo(&result.exe, stdout); if (result.version.length) { @@ -32,7 +32,7 @@ bool ffPrintEditor(FFEditorOptions* options) { } putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_EDITOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Editor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.type, "type"), FF_ARG(result.name, "name"), FF_ARG(result.exe, "exe-name"), @@ -57,7 +57,7 @@ void ffParseEditorJsonObject(FFEditorOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_EDITOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Editor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -103,8 +103,30 @@ void ffDestroyEditorOptions(FFEditorOptions* options) { } FFModuleBaseInfo ffEditorModuleInfo = { - .name = FF_EDITOR_MODULE_NAME, + .name = "Editor", .description = "Print information about the default editor ($VISUAL or $EDITOR)", + .displayName = { + .en = "Editor", + .ar = "المحرر", + .cs = "Editor", + .de = "Editor", + .es = "Editor", + .fr = "Éditeur", + .gl = "Editor", + .he = "עורך", + .id = "Editor", + .it = "Editor", + .ja = "エディター", + .ko = "편집기", + .pl = "Edytor", + .pt = "Editor", + .ru = "Редактор", + .tr = "Düzenleyici", + .uk = "Редактор", + .vi = "Trình soạn thảo", + .zh_CN = "编辑器", + .zh_TW = "編輯器", + }, .initOptions = (void*) ffInitEditorOptions, .destroyOptions = (void*) ffDestroyEditorOptions, .parseJsonObject = (void*) ffParseEditorJsonObject, diff --git a/src/modules/editor/editor.h b/src/modules/editor/editor.h index 293d0a6e19..19d48b7563 100644 --- a/src/modules/editor/editor.h +++ b/src/modules/editor/editor.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_EDITOR_MODULE_NAME "Editor" - bool ffPrintEditor(FFEditorOptions* options); void ffInitEditorOptions(FFEditorOptions* options); void ffDestroyEditorOptions(FFEditorOptions* options); diff --git a/src/modules/font/font.c b/src/modules/font/font.c index be940b32d1..47b6d8f2d2 100644 --- a/src/modules/font/font.c +++ b/src/modules/font/font.c @@ -15,13 +15,13 @@ bool ffPrintFont(FFFontOptions* options) { const char* error = ffDetectFont(&font); if (error) { - ffPrintError(FF_FONT_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Font), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); } else { if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_FONT_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Font), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&font.display, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_FONT_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Font), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(font.fonts[0], "font1"), FF_ARG(font.fonts[1], "font2"), FF_ARG(font.fonts[2], "font3"), @@ -49,7 +49,7 @@ void ffParseFontJsonObject(FFFontOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_FONT_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Font), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -95,8 +95,30 @@ void ffDestroyFontOptions(FFFontOptions* options) { } FFModuleBaseInfo ffFontModuleInfo = { - .name = FF_FONT_MODULE_NAME, + .name = "Font", .description = "Print system font names", + .displayName = { + .en = "Font", + .ar = "الخط", + .cs = "Písmo", + .de = "Schriftart", + .es = "Fuente", + .fr = "Police", + .gl = "Fonte", + .he = "גופן", + .id = "Font", + .it = "Carattere", + .ja = "フォント", + .ko = "글꼴", + .pl = "Czcionka", + .pt = "Fonte", + .ru = "Шрифт", + .tr = "Yazı Tipi", + .uk = "Шрифт", + .vi = "Phông chữ", + .zh_CN = "字体", + .zh_TW = "字型", + }, .initOptions = (void*) ffInitFontOptions, .destroyOptions = (void*) ffDestroyFontOptions, .parseJsonObject = (void*) ffParseFontJsonObject, diff --git a/src/modules/font/font.h b/src/modules/font/font.h index b793b9fdd3..9af717487d 100644 --- a/src/modules/font/font.h +++ b/src/modules/font/font.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_FONT_MODULE_NAME "Font" - bool ffPrintFont(FFFontOptions* options); void ffInitFontOptions(FFFontOptions* options); void ffDestroyFontOptions(FFFontOptions* options); diff --git a/src/modules/gamepad/gamepad.c b/src/modules/gamepad/gamepad.c index f3c3bdfe40..470abd8e32 100644 --- a/src/modules/gamepad/gamepad.c +++ b/src/modules/gamepad/gamepad.c @@ -8,7 +8,7 @@ static void printDevice(FFGamepadOptions* options, const FFGamepadDevice* device, uint8_t index) { FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type; if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_GAMEPAD_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Gamepad), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); bool showBatteryLevel = device->battery > 0 && device->battery <= 100; @@ -39,7 +39,7 @@ static void printDevice(FFGamepadOptions* options, const FFGamepadDevice* device ffPercentAppendBar(&percentageBar, device->battery, options->percent, &options->moduleArgs); } - FF_PRINT_FORMAT_CHECKED(FF_GAMEPAD_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Gamepad), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(device->name, "name"), FF_ARG(device->serial, "serial"), FF_ARG(percentageNum, "battery-percentage"), @@ -54,12 +54,12 @@ bool ffPrintGamepad(FFGamepadOptions* options) { const char* error = ffDetectGamepad(&result); if (error) { - ffPrintError(FF_GAMEPAD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Gamepad), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (!result.length) { - ffPrintError(FF_GAMEPAD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No devices detected"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Gamepad), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No devices detected"); return false; } @@ -80,7 +80,7 @@ bool ffPrintGamepad(FFGamepadOptions* options) { bool ret = true; if (!filtered.length) { - ffPrintError(FF_GAMEPAD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "All devices are ignored"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Gamepad), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "All devices are ignored"); ret = false; } else { uint8_t index = 0; @@ -122,7 +122,7 @@ void ffParseGamepadJsonObject(FFGamepadOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_GAMEPAD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Gamepad), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -189,8 +189,30 @@ void ffDestroyGamepadOptions(FFGamepadOptions* options) { } FFModuleBaseInfo ffGamepadModuleInfo = { - .name = FF_GAMEPAD_MODULE_NAME, + .name = "Gamepad", .description = "List connected gamepads", + .displayName = { + .en = "Gamepad", + .ar = "يد التحكم", + .cs = "Gamepad", + .de = "Gamepad", + .es = "Gamepad", + .fr = "Manette", + .gl = "Gamepad", + .he = "משטח משחק", + .id = "Gamepad", + .it = "Gamepad", + .ja = "ゲームパッド", + .ko = "게임패드", + .pl = "Gamepad", + .pt = "Gamepad", + .ru = "Геймпад", + .tr = "Oyun Kumandası", + .uk = "Геймпад", + .vi = "Tay cầm chơi game", + .zh_CN = "游戏手柄", + .zh_TW = "遊戲手把", + }, .initOptions = (void*) ffInitGamepadOptions, .destroyOptions = (void*) ffDestroyGamepadOptions, .parseJsonObject = (void*) ffParseGamepadJsonObject, diff --git a/src/modules/gamepad/gamepad.h b/src/modules/gamepad/gamepad.h index 395c90b2e3..9855a7b72b 100644 --- a/src/modules/gamepad/gamepad.h +++ b/src/modules/gamepad/gamepad.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_GAMEPAD_MODULE_NAME "Gamepad" - bool ffPrintGamepad(FFGamepadOptions* options); void ffInitGamepadOptions(FFGamepadOptions* options); void ffDestroyGamepadOptions(FFGamepadOptions* options); diff --git a/src/modules/gpu/gpu.c b/src/modules/gpu/gpu.c index 19ba8c868b..159cbaf6b2 100644 --- a/src/modules/gpu/gpu.c +++ b/src/modules/gpu/gpu.c @@ -28,7 +28,7 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type; if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_GPU_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(GPU), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate(); @@ -145,7 +145,7 @@ static void printGPUResult(FFGPUOptions* options, uint8_t index, const FFGPUResu snprintf(currSpeed, sizeof(currSpeed), "%d x%d", gpu->psCurr.gen, gpu->psCurr.lanes); } - FF_PRINT_FORMAT_CHECKED(FF_GPU_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(GPU), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(gpu->vendor, "vendor"), FF_ARG(gpu->name, "name"), FF_ARG(gpu->driver, "driver"), @@ -176,7 +176,7 @@ bool ffPrintGPU(FFGPUOptions* options) { FF_LIST_AUTO_DESTROY gpus = ffListCreate(); const char* error = ffDetectGPU(options, &gpus); if (error) { - ffPrintError(FF_GPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(GPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } @@ -213,7 +213,7 @@ bool ffPrintGPU(FFGPUOptions* options) { } if (selectedGPUs.length == 0) { - ffPrintError(FF_GPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, (gpus.length > 0 ? "GPUs found but all hidden by hideType option" : "No GPUs detected")); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(GPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, (gpus.length > 0 ? "GPUs found but all hidden by hideType option" : "No GPUs detected")); return false; } @@ -249,7 +249,7 @@ void ffParseGPUJsonObject(FFGPUOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_GPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(GPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->detectionMethod = (FFGPUDetectionMethod) value; } @@ -269,7 +269,7 @@ void ffParseGPUJsonObject(FFGPUOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_GPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(GPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->hideType = (FFGPUType) value; } @@ -281,7 +281,7 @@ void ffParseGPUJsonObject(FFGPUOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_GPU_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(GPU), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -479,8 +479,30 @@ void ffDestroyGPUOptions(FFGPUOptions* options) { } FFModuleBaseInfo ffGPUModuleInfo = { - .name = FF_GPU_MODULE_NAME, + .name = "GPU", .description = "Print GPU names, memory sizes, types, etc", + .displayName = { + .en = "GPU", + .ar = "معالج الرسوميات", + .cs = "GPU", + .de = "GPU", + .es = "GPU", + .fr = "GPU", + .gl = "GPU", + .he = "מעבד גרפי", + .id = "GPU", + .it = "GPU", + .ja = "GPU", + .ko = "GPU", + .pl = "GPU", + .pt = "GPU", + .ru = "Видеокарта", + .tr = "GPU", + .uk = "GPU", + .vi = "GPU", + .zh_CN = "显卡", + .zh_TW = "顯卡", + }, .initOptions = (void*) ffInitGPUOptions, .destroyOptions = (void*) ffDestroyGPUOptions, .parseJsonObject = (void*) ffParseGPUJsonObject, diff --git a/src/modules/gpu/gpu.h b/src/modules/gpu/gpu.h index e2a6f14248..c2a3c7a417 100644 --- a/src/modules/gpu/gpu.h +++ b/src/modules/gpu/gpu.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_GPU_MODULE_NAME "GPU" - bool ffPrintGPU(FFGPUOptions* options); void ffInitGPUOptions(FFGPUOptions* options); void ffDestroyGPUOptions(FFGPUOptions* options); diff --git a/src/modules/host/host.c b/src/modules/host/host.c index 041ab712e6..ef72887766 100644 --- a/src/modules/host/host.c +++ b/src/modules/host/host.c @@ -17,17 +17,17 @@ bool ffPrintHost(FFHostOptions* options) { const char* error = ffDetectHost(&host); if (error) { - ffPrintError(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Host), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); goto exit; } if (host.name.length == 0 && host.family.length == 0) { - ffPrintError(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "neither product_family nor product_name is set by O.E.M."); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Host), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "neither product_family nor product_name is set by O.E.M."); goto exit; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Host), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate(); @@ -43,7 +43,7 @@ bool ffPrintHost(FFHostOptions* options) { ffStrbufPutTo(&output, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Host), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(host.family, "family"), FF_ARG(host.name, "name"), FF_ARG(host.version, "version"), @@ -75,7 +75,7 @@ void ffParseHostJsonObject(FFHostOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Host), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -136,8 +136,30 @@ void ffDestroyHostOptions(FFHostOptions* options) { } FFModuleBaseInfo ffHostModuleInfo = { - .name = FF_HOST_MODULE_NAME, + .name = "Host", .description = "Print your computer's product name", + .displayName = { + .en = "Host", + .ar = "المضيف", + .cs = "Hostitel", + .de = "Rechner", + .es = "Host", + .fr = "Hôte", + .gl = "Host", + .he = "מארח", + .id = "Host", + .it = "Host", + .ja = "ホスト", + .ko = "호스트", + .pl = "Host", + .pt = "Host", + .ru = "Модель", + .tr = "Ana Bilgisayar", + .uk = "Хост", + .vi = "Máy chủ", + .zh_CN = "主机", + .zh_TW = "主機", + }, .initOptions = (void*) ffInitHostOptions, .destroyOptions = (void*) ffDestroyHostOptions, .parseJsonObject = (void*) ffParseHostJsonObject, diff --git a/src/modules/host/host.h b/src/modules/host/host.h index 1f98fd406a..cf2ed1871d 100644 --- a/src/modules/host/host.h +++ b/src/modules/host/host.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_HOST_MODULE_NAME "Host" - bool ffPrintHost(FFHostOptions* options); void ffInitHostOptions(FFHostOptions* options); void ffDestroyHostOptions(FFHostOptions* options); diff --git a/src/modules/icons/icons.c b/src/modules/icons/icons.c index 15dea09287..358cc3dbaa 100644 --- a/src/modules/icons/icons.c +++ b/src/modules/icons/icons.c @@ -13,12 +13,12 @@ bool ffPrintIcons(FFIconsOptions* options) { const char* error = ffDetectIcons(&result); if (error) { - ffPrintError(FF_ICONS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Icons), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); goto exit; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_ICONS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Icons), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (result.icons1.length) { ffStrbufWriteTo(&result.icons1, stdout); } @@ -30,7 +30,7 @@ bool ffPrintIcons(FFIconsOptions* options) { } putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_ICONS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Icons), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.icons1, "icons1"), FF_ARG(result.icons2, "icons2"), })); @@ -52,7 +52,7 @@ void ffParseIconsJsonObject(FFIconsOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_ICONS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Icons), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -94,8 +94,30 @@ void ffDestroyIconsOptions(FFIconsOptions* options) { } FFModuleBaseInfo ffIconsModuleInfo = { - .name = FF_ICONS_MODULE_NAME, + .name = "Icons", .description = "Print icon style name", + .displayName = { + .en = "Icons", + .ar = "الأيقونات", + .cs = "Ikony", + .de = "Symbole", + .es = "Iconos", + .fr = "Icônes", + .gl = "Iconas", + .he = "סמלים", + .id = "Ikon", + .it = "Icone", + .ja = "アイコン", + .ko = "아이콘", + .pl = "Ikony", + .pt = "Ícones", + .ru = "Иконки", + .tr = "Simgeler", + .uk = "Піктограми", + .vi = "Biểu tượng", + .zh_CN = "图标", + .zh_TW = "圖示", + }, .initOptions = (void*) ffInitIconsOptions, .destroyOptions = (void*) ffDestroyIconsOptions, .parseJsonObject = (void*) ffParseIconsJsonObject, diff --git a/src/modules/icons/icons.h b/src/modules/icons/icons.h index a583a22571..ee1e74f047 100644 --- a/src/modules/icons/icons.h +++ b/src/modules/icons/icons.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_ICONS_MODULE_NAME "Icons" - bool ffPrintIcons(FFIconsOptions* options); void ffInitIconsOptions(FFIconsOptions* options); void ffDestroyIconsOptions(FFIconsOptions* options); diff --git a/src/modules/initsystem/initsystem.c b/src/modules/initsystem/initsystem.c index 7a02e0309b..3b5db0d8b1 100644 --- a/src/modules/initsystem/initsystem.c +++ b/src/modules/initsystem/initsystem.c @@ -4,8 +4,6 @@ #include "detection/initsystem/initsystem.h" #include "modules/initsystem/initsystem.h" -#define FF_INITSYSTEM_DISPLAY_NAME "Init System" - bool ffPrintInitSystem(FFInitSystemOptions* options) { bool success = false; FFInitSystemResult result = { @@ -18,12 +16,12 @@ bool ffPrintInitSystem(FFInitSystemOptions* options) { const char* error = ffDetectInitSystem(&result); if (error) { - ffPrintError(FF_INITSYSTEM_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(InitSystem), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); goto exit; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_INITSYSTEM_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(InitSystem), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&result.name, stdout); if (result.version.length) { printf(" %s\n", result.version.chars); @@ -31,7 +29,7 @@ bool ffPrintInitSystem(FFInitSystemOptions* options) { putchar('\n'); } } else { - FF_PRINT_FORMAT_CHECKED(FF_INITSYSTEM_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(InitSystem), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.name, "name"), FF_ARG(result.exe, "exe"), FF_ARG(result.version, "version"), @@ -56,7 +54,7 @@ void ffParseInitSystemJsonObject(FFInitSystemOptions* options, yyjson_val* modul continue; } - ffPrintError(FF_INITSYSTEM_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(InitSystem), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -103,8 +101,30 @@ void ffDestroyInitSystemOptions(FFInitSystemOptions* options) { } FFModuleBaseInfo ffInitSystemModuleInfo = { - .name = FF_INITSYSTEM_MODULE_NAME, + .name = "InitSystem", .description = "Print init system (pid 1) name and version", + .displayName = { + .en = "Init System", + .ar = "نظام الإقلاع", + .cs = "Init systém", + .de = "Init-System", + .es = "Sistema de inicio", + .fr = "Système d'init", + .gl = "Sistema de inicio", + .he = "מערכת אתחול", + .id = "Sistem Init", + .it = "Sistema di init", + .ja = "initシステム", + .ko = "초기화 시스템", + .pl = "System init", + .pt = "Sistema de inicialização", + .ru = "Система инициализации", + .tr = "Init Sistemi", + .uk = "Init-система", + .vi = "Hệ thống init", + .zh_CN = "Init 系统", + .zh_TW = "Init 系統", + }, .initOptions = (void*) ffInitInitSystemOptions, .destroyOptions = (void*) ffDestroyInitSystemOptions, .parseJsonObject = (void*) ffParseInitSystemJsonObject, diff --git a/src/modules/initsystem/initsystem.h b/src/modules/initsystem/initsystem.h index 381b955431..f6379f5956 100644 --- a/src/modules/initsystem/initsystem.h +++ b/src/modules/initsystem/initsystem.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_INITSYSTEM_MODULE_NAME "InitSystem" - bool ffPrintInitSystem(FFInitSystemOptions* options); void ffInitInitSystemOptions(FFInitSystemOptions* options); void ffDestroyInitSystemOptions(FFInitSystemOptions* options); diff --git a/src/modules/kernel/kernel.c b/src/modules/kernel/kernel.c index 9ae5878df4..7c3165ac38 100644 --- a/src/modules/kernel/kernel.c +++ b/src/modules/kernel/kernel.c @@ -7,12 +7,12 @@ bool ffPrintKernel(FFKernelOptions* options) { const FFPlatformSysinfo* info = &instance.state.platform.sysinfo; if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Kernel), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); printf("%s %s\n", info->name.chars, info->release.chars); } else { FF_STRBUF_AUTO_DESTROY str = ffStrbufCreate(); ffSizeAppendNum(info->pageSize, &str); - FF_PRINT_FORMAT_CHECKED(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Kernel), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(info->name, "sysname"), FF_ARG(info->release, "release"), FF_ARG(info->version, "version"), @@ -32,7 +32,7 @@ void ffParseKernelJsonObject(FFKernelOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_KERNEL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Kernel), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -62,8 +62,30 @@ void ffDestroyKernelOptions(FFKernelOptions* options) { } FFModuleBaseInfo ffKernelModuleInfo = { - .name = FF_KERNEL_MODULE_NAME, + .name = "Kernel", .description = "Print system kernel version", + .displayName = { + .en = "Kernel", + .ar = "النواة", + .cs = "Jádro", + .de = "Kernel", + .es = "Kernel", + .fr = "Kernel", + .gl = "Kernel", + .he = "ליבה", + .id = "Kernel", + .it = "Kernel", + .ja = "カーネル", + .ko = "커널", + .pl = "Jądro", + .pt = "Kernel", + .ru = "Ядро", + .tr = "Çekirdek", + .uk = "Ядро", + .vi = "Nhân", + .zh_CN = "内核", + .zh_TW = "核心", + }, .initOptions = (void*) ffInitKernelOptions, .destroyOptions = (void*) ffDestroyKernelOptions, .parseJsonObject = (void*) ffParseKernelJsonObject, diff --git a/src/modules/kernel/kernel.h b/src/modules/kernel/kernel.h index 010ec54c4d..faf6212689 100644 --- a/src/modules/kernel/kernel.h +++ b/src/modules/kernel/kernel.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_KERNEL_MODULE_NAME "Kernel" - bool ffPrintKernel(FFKernelOptions* options); void ffInitKernelOptions(FFKernelOptions* options); void ffDestroyKernelOptions(FFKernelOptions* options); diff --git a/src/modules/keyboard/keyboard.c b/src/modules/keyboard/keyboard.c index 76e6c36f95..b1ff758717 100644 --- a/src/modules/keyboard/keyboard.c +++ b/src/modules/keyboard/keyboard.c @@ -7,10 +7,10 @@ static void printDevice(FFKeyboardOptions* options, const FFKeyboardDevice* device, uint8_t index) { if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_KEYBOARD_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Keyboard), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&device->name, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_KEYBOARD_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Keyboard), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(device->name, "name"), FF_ARG(device->serial, "serial"), })); @@ -23,12 +23,12 @@ bool ffPrintKeyboard(FFKeyboardOptions* options) { const char* error = ffDetectKeyboard(&result); if (error) { - ffPrintError(FF_KEYBOARD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Keyboard), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (!result.length) { - ffPrintError(FF_KEYBOARD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No devices detected"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Keyboard), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No devices detected"); return false; } @@ -49,7 +49,7 @@ bool ffPrintKeyboard(FFKeyboardOptions* options) { bool ret = true; if (!filtered.length) { - ffPrintError(FF_KEYBOARD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "All devices are ignored"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Keyboard), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "All devices are ignored"); ret = false; } else { uint8_t index = 0; @@ -84,7 +84,7 @@ void ffParseKeyboardJsonObject(FFKeyboardOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_KEYBOARD_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Keyboard), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -149,8 +149,30 @@ void ffDestroyKeyboardOptions(FFKeyboardOptions* options) { } FFModuleBaseInfo ffKeyboardModuleInfo = { - .name = FF_KEYBOARD_MODULE_NAME, + .name = "Keyboard", .description = "List connected keyboards", + .displayName = { + .en = "Keyboard", + .ar = "لوحة المفاتيح", + .cs = "Klávesnice", + .de = "Tastatur", + .es = "Teclado", + .fr = "Clavier", + .gl = "Teclado", + .he = "מקלדת", + .id = "Papan Ketik", + .it = "Tastiera", + .ja = "キーボード", + .ko = "키보드", + .pl = "Klawiatura", + .pt = "Teclado", + .ru = "Клавиатура", + .tr = "Klavye", + .uk = "Клавіатура", + .vi = "Bàn phím", + .zh_CN = "键盘", + .zh_TW = "鍵盤", + }, .initOptions = (void*) ffInitKeyboardOptions, .destroyOptions = (void*) ffDestroyKeyboardOptions, .parseJsonObject = (void*) ffParseKeyboardJsonObject, diff --git a/src/modules/keyboard/keyboard.h b/src/modules/keyboard/keyboard.h index bd6e69c56b..d862173a1a 100644 --- a/src/modules/keyboard/keyboard.h +++ b/src/modules/keyboard/keyboard.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_KEYBOARD_MODULE_NAME "Keyboard" - bool ffPrintKeyboard(FFKeyboardOptions* options); void ffInitKeyboardOptions(FFKeyboardOptions* options); void ffDestroyKeyboardOptions(FFKeyboardOptions* options); diff --git a/src/modules/lm/lm.c b/src/modules/lm/lm.c index ed945bad46..ece31fb0ec 100644 --- a/src/modules/lm/lm.c +++ b/src/modules/lm/lm.c @@ -8,34 +8,31 @@ bool ffPrintLM(FFLMOptions* options) { bool success = false; FFLMResult result; ffStrbufInit(&result.service); - ffStrbufInit(&result.type); + ffStrbufInit(&result.prettyName); ffStrbufInit(&result.version); const char* error = ffDetectLM(&result); if (error) { - ffPrintError(FF_LM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(LM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); goto exit; } if (result.service.length == 0) { - ffPrintError(FF_LM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No LM service found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(LM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No LM service found"); goto exit; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_LM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - ffStrbufWriteTo(&result.service, stdout); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(LM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffStrbufWriteTo(&result.prettyName, stdout); if (result.version.length) { printf(" %s", result.version.chars); } - if (result.type.length) { - printf(" (%s)", result.type.chars); - } putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_LM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(LM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.service, "service"), - FF_ARG(result.type, "type"), + FF_ARG(result.prettyName, "pretty-name"), FF_ARG(result.version, "version"), })); } @@ -43,7 +40,7 @@ bool ffPrintLM(FFLMOptions* options) { exit: ffStrbufDestroy(&result.service); - ffStrbufDestroy(&result.type); + ffStrbufDestroy(&result.prettyName); ffStrbufDestroy(&result.version); return success; @@ -57,7 +54,7 @@ void ffParseLMJsonObject(FFLMOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_LM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(LM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -69,7 +66,7 @@ bool ffGenerateLMJsonResult([[maybe_unused]] FFLMOptions* options, yyjson_mut_do bool success = false; FFLMResult result; ffStrbufInit(&result.service); - ffStrbufInit(&result.type); + ffStrbufInit(&result.prettyName); ffStrbufInit(&result.version); const char* error = ffDetectLM(&result); @@ -85,13 +82,13 @@ bool ffGenerateLMJsonResult([[maybe_unused]] FFLMOptions* options, yyjson_mut_do 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, "prettyName", &result.prettyName); yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version); success = true; exit: ffStrbufDestroy(&result.service); - ffStrbufDestroy(&result.type); + ffStrbufDestroy(&result.prettyName); ffStrbufDestroy(&result.version); return success; @@ -106,8 +103,30 @@ void ffDestroyLMOptions(FFLMOptions* options) { } FFModuleBaseInfo ffLMModuleInfo = { - .name = FF_LM_MODULE_NAME, + .name = "LM", .description = "Print login manager (desktop manager) name and version", + .displayName = { + .en = "Login Manager", + .ar = "مدير تسجيل الدخول", + .cs = "Správce přihlášení", + .de = "Anmeldemanager", + .es = "Gestor de inicio de sesión", + .fr = "Gestionnaire de connexion", + .gl = "Xestor de inicio de sesión", + .he = "מנהל התחברות", + .id = "Manajer Login", + .it = "Gestore di accesso", + .ja = "ログインマネージャー", + .ko = "로그인 관리자", + .pl = "Menedżer logowania", + .pt = "Gerenciador de login", + .ru = "Менеджер входа", + .tr = "Oturum Açma Yöneticisi", + .uk = "Менеджер входу", + .vi = "Trình quản lý đăng nhập", + .zh_CN = "登录管理器", + .zh_TW = "登入管理器", + }, .initOptions = (void*) ffInitLMOptions, .destroyOptions = (void*) ffDestroyLMOptions, .parseJsonObject = (void*) ffParseLMJsonObject, @@ -115,8 +134,8 @@ FFModuleBaseInfo ffLMModuleInfo = { .generateJsonResult = (void*) ffGenerateLMJsonResult, .generateJsonConfig = (void*) ffGenerateLMJsonConfig, .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) { - { "LM service", "service" }, - { "LM type", "type" }, + { "LM service / process name", "service" }, + { "LM pretty name", "pretty-name" }, { "LM version", "version" }, })), .defaultOrder = 20, diff --git a/src/modules/lm/lm.h b/src/modules/lm/lm.h index 1ec33b6494..8329d9b776 100644 --- a/src/modules/lm/lm.h +++ b/src/modules/lm/lm.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_LM_MODULE_NAME "LM" - bool ffPrintLM(FFLMOptions* options); void ffInitLMOptions(FFLMOptions* options); void ffDestroyLMOptions(FFLMOptions* options); diff --git a/src/modules/loadavg/loadavg.c b/src/modules/loadavg/loadavg.c index a6fb6f800a..40aa8115bb 100644 --- a/src/modules/loadavg/loadavg.c +++ b/src/modules/loadavg/loadavg.c @@ -11,13 +11,13 @@ bool ffPrintLoadavg(FFLoadavgOptions* options) { const char* error = ffDetectLoadavg(result); if (error) { - ffPrintError(FF_LOADAVG_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (options->moduleArgs.outputFormat.length == 0) { if (options->compact) { - ffPrintLogoAndKey(FF_LOADAVG_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); printf("%.*f, %.*f, %.*f\n", options->ndigits, result[0], options->ndigits, result[1], options->ndigits, result[2]); } else { FFCPUResult cpu = { @@ -34,7 +34,7 @@ bool ffPrintLoadavg(FFLoadavgOptions* options) { uint32_t duration = index == 0 ? 1 : index == 1 ? 5 : 15; if (options->moduleArgs.key.length == 0) { - ffStrbufSetF(&buffer, "%s (%d min)", FF_LOADAVG_MODULE_NAME, duration); + ffStrbufSetF(&buffer, "%s (%d min)", FF_MODULE_GET_DISPLAY_NAME(Loadavg), duration); } else { ffStrbufClear(&buffer); FF_PARSE_FORMAT_STRING_CHECKED(&buffer, &options->moduleArgs.key, ((FFformatarg[]) { @@ -72,7 +72,7 @@ bool ffPrintLoadavg(FFLoadavgOptions* options) { } } } else { - FF_PRINT_FORMAT_CHECKED(FF_LOADAVG_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result[0], "loadavg1"), FF_ARG(result[1], "loadavg2"), FF_ARG(result[2], "loadavg3"), @@ -104,7 +104,7 @@ void ffParseLoadavgJsonObject(FFLoadavgOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_LOADAVG_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -148,8 +148,30 @@ void ffDestroyLoadavgOptions(FFLoadavgOptions* options) { } FFModuleBaseInfo ffLoadavgModuleInfo = { - .name = FF_LOADAVG_MODULE_NAME, + .name = "Loadavg", .description = "Print system load averages", + .displayName = { + .en = "Load Average", + .ar = "متوسط الحمل", + .cs = "Průměrná zátěž", + .de = "Systemlast", + .es = "Promedio de carga", + .fr = "Charge moyenne", + .gl = "Carga media", + .he = "עומס ממוצע", + .id = "Beban Rata-rata", + .it = "Carico medio", + .ja = "ロードアベレージ", + .ko = "시스템 부하 평균", + .pl = "Średnie obciążenie", + .pt = "Média de carga", + .ru = "Средняя нагрузка", + .tr = "Sistem Yükü", + .uk = "Середнє навантаження", + .vi = "Tải trung bình", + .zh_CN = "系统负载", + .zh_TW = "系統負載", + }, .initOptions = (void*) ffInitLoadavgOptions, .destroyOptions = (void*) ffDestroyLoadavgOptions, .parseJsonObject = (void*) ffParseLoadavgJsonObject, diff --git a/src/modules/loadavg/loadavg.h b/src/modules/loadavg/loadavg.h index 9af2786f90..8a2c6a8f5f 100644 --- a/src/modules/loadavg/loadavg.h +++ b/src/modules/loadavg/loadavg.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_LOADAVG_MODULE_NAME "Loadavg" - bool ffPrintLoadavg(FFLoadavgOptions* options); void ffInitLoadavgOptions(FFLoadavgOptions* options); void ffDestroyLoadavgOptions(FFLoadavgOptions* options); diff --git a/src/modules/locale/locale.c b/src/modules/locale/locale.c index cb9c8e7ec5..4c91a04f56 100644 --- a/src/modules/locale/locale.c +++ b/src/modules/locale/locale.c @@ -8,15 +8,15 @@ bool ffPrintLocale(FFLocaleOptions* options) { const char* error = ffDetectLocale(&locale); if (error) { - ffPrintError(FF_LOCALE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Locale), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_LOCALE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Locale), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&locale, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_LOCALE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(locale, "result") })); + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Locale), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(locale, "result") })); } return true; @@ -30,7 +30,7 @@ void ffParseLocaleJsonObject(FFLocaleOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_LOCALE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Locale), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -67,8 +67,30 @@ void ffDestroyLocaleOptions(FFLocaleOptions* options) { } FFModuleBaseInfo ffLocaleModuleInfo = { - .name = FF_LOCALE_MODULE_NAME, + .name = "Locale", .description = "Print system locale name", + .displayName = { + .en = "Locale", + .ar = "اللغة", + .cs = "Národní prostředí", + .de = "Sprachraum", + .es = "Configuración regional", + .fr = "Paramètres régionaux", + .gl = "Configuración rexional", + .he = "לוקאל", + .id = "Lokal", + .it = "Impostazioni locali", + .ja = "ロケール", + .ko = "로케일", + .pl = "Ustawienia regionalne", + .pt = "Configuração regional", + .ru = "Локаль", + .tr = "Yerel Ayar", + .uk = "Локаль", + .vi = "Bản địa", + .zh_CN = "语言环境", + .zh_TW = "語言環境", + }, .initOptions = (void*) ffInitLocaleOptions, .destroyOptions = (void*) ffDestroyLocaleOptions, .parseJsonObject = (void*) ffParseLocaleJsonObject, diff --git a/src/modules/locale/locale.h b/src/modules/locale/locale.h index fd86e42c36..a5b6a0981e 100644 --- a/src/modules/locale/locale.h +++ b/src/modules/locale/locale.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_LOCALE_MODULE_NAME "Locale" - bool ffPrintLocale(FFLocaleOptions* options); void ffInitLocaleOptions(FFLocaleOptions* options); void ffDestroyLocaleOptions(FFLocaleOptions* options); diff --git a/src/modules/localip/localip.c b/src/modules/localip/localip.c index eeb304e283..c1c53be23a 100644 --- a/src/modules/localip/localip.c +++ b/src/modules/localip/localip.c @@ -4,7 +4,6 @@ #include "detection/localip/localip.h" #include "modules/localip/localip.h" -#define FF_LOCALIP_DISPLAY_NAME "Local IP" #pragma GCC diagnostic ignored "-Wsign-conversion" static int sortIps(const FFLocalIpResult* left, const FFLocalIpResult* right) { @@ -17,7 +16,7 @@ static void formatKey(const FFLocalIpOptions* options, FFLocalIpResult* ip, uint ffStrbufSetF(&ip->name, "unknown %u", (unsigned) index); } - ffStrbufSetF(key, FF_LOCALIP_DISPLAY_NAME " (%s)", ip->name.chars); + ffStrbufSetF(key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(LocalIP), ip->name.chars); } else { ffStrbufClear(key); FF_PARSE_FORMAT_STRING_CHECKED(key, &options->moduleArgs.key, ((FFformatarg[]) { @@ -100,12 +99,12 @@ bool ffPrintLocalIp(FFLocalIpOptions* options) { const char* error = ffDetectLocalIps(options, &results); if (error) { - ffPrintError(FF_LOCALIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(LocalIP), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (results.length == 0) { - ffPrintError(FF_LOCALIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Failed to detect any IPs"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(LocalIP), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Failed to detect any IPs"); return false; } @@ -114,7 +113,7 @@ bool ffPrintLocalIp(FFLocalIpOptions* options) { FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); if (options->showType & FF_LOCALIP_TYPE_COMPACT_BIT) { - ffPrintLogoAndKey(FF_LOCALIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(LocalIP), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_LIST_FOR_EACH (FFLocalIpResult, ip, results) { if (buffer.length) { @@ -201,7 +200,7 @@ void ffParseLocalIpJsonObject(FFLocalIpOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_LOCALIP_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(LocalIP), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->showType |= FF_LOCALIP_TYPE_IPV6_BIT; options->ipv6Type = (FFLocalIpIpv6Type) value; @@ -296,7 +295,7 @@ void ffParseLocalIpJsonObject(FFLocalIpOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_LOCALIP_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(LocalIP), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -422,8 +421,30 @@ void ffDestroyLocalIpOptions(FFLocalIpOptions* options) { } FFModuleBaseInfo ffLocalIPModuleInfo = { - .name = FF_LOCALIP_MODULE_NAME, + .name = "LocalIp", .description = "List local IP addresses (IPv4 or IPv6), MAC addresses, etc", + .displayName = { + .en = "Local IP", + .ar = "عنوان IP المحلي", + .cs = "Místní IP", + .de = "Lokale IP", + .es = "IP local", + .fr = "IP locale", + .gl = "IP local", + .he = "IP מקומי", + .id = "IP Lokal", + .it = "IP locale", + .ja = "ローカル IP", + .ko = "로컬 IP", + .pl = "Lokalny adres IP", + .pt = "IP local", + .ru = "Локальный IP", + .tr = "Yerel IP", + .uk = "Локальна IP", + .vi = "IP cục bộ", + .zh_CN = "本地 IP", + .zh_TW = "區域 IP", + }, .initOptions = (void*) ffInitLocalIpOptions, .destroyOptions = (void*) ffDestroyLocalIpOptions, .parseJsonObject = (void*) ffParseLocalIpJsonObject, diff --git a/src/modules/localip/localip.h b/src/modules/localip/localip.h index f9e9c4e437..2580fac2ee 100644 --- a/src/modules/localip/localip.h +++ b/src/modules/localip/localip.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_LOCALIP_MODULE_NAME "LocalIp" - bool ffPrintLocalIp(FFLocalIpOptions* options); void ffInitLocalIpOptions(FFLocalIpOptions* options); void ffDestroyLocalIpOptions(FFLocalIpOptions* options); diff --git a/src/modules/logo/logo.c b/src/modules/logo/logo.c index a2fd7d2d1a..dfa92ce06d 100644 --- a/src/modules/logo/logo.c +++ b/src/modules/logo/logo.c @@ -4,7 +4,7 @@ #include "options/logo.h" bool ffPrintLogo([[maybe_unused]] FFLogoOptions* options) { - ffPrintError(FF_LOGO_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_DEFAULT, "Supported in JSON format only"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Logo), 0, nullptr, FF_PRINT_TYPE_DEFAULT, "Supported in JSON format only"); return false; } @@ -16,7 +16,7 @@ void ffParseLogoJsonObject([[maybe_unused]] FFLogoOptions* options, [[maybe_unus continue; } - ffPrintError(FF_LOGO_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Logo), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -77,8 +77,30 @@ void ffDestroyLogoOptions([[maybe_unused]] FFLogoOptions* options) { } FFModuleBaseInfo ffLogoModuleInfo = { - .name = FF_LOGO_MODULE_NAME, + .name = "Logo", .description = "Query built-in logo for JSON output", + .displayName = { + .en = "Logo", + .ar = "الشعار", + .cs = "Logo", + .de = "Logo", + .es = "Logo", + .fr = "Logo", + .gl = "Logo", + .he = "לוגו", + .id = "Logo", + .it = "Logo", + .ja = "ロゴ", + .ko = "로고", + .pl = "Logo", + .pt = "Logo", + .ru = "Логотип", + .tr = "Logo", + .uk = "Логотип", + .vi = "Logo", + .zh_CN = "徽标", + .zh_TW = "標誌", + }, .initOptions = (void*) ffInitLogoOptions, .destroyOptions = (void*) ffDestroyLogoOptions, .parseJsonObject = (void*) ffParseLogoJsonObject, diff --git a/src/modules/logo/logo.h b/src/modules/logo/logo.h index 7f48e2dd62..af8ca7a0e6 100644 --- a/src/modules/logo/logo.h +++ b/src/modules/logo/logo.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_LOGO_MODULE_NAME "Logo" - bool ffPrintLogo(FFLogoOptions* options); void ffInitLogoOptions(FFLogoOptions* options); void ffDestroyLogoOptions(FFLogoOptions* options); diff --git a/src/modules/media/media.c b/src/modules/media/media.c index 5f926d1156..6090e6eca2 100644 --- a/src/modules/media/media.c +++ b/src/modules/media/media.c @@ -56,7 +56,7 @@ bool ffPrintMedia(FFMediaOptions* options) { const FFMediaResult* media = ffDetectMedia(false); if (media->error.length > 0) { - ffPrintError(FF_MEDIA_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", media->error.chars); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Media), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", media->error.chars); return false; } @@ -110,7 +110,7 @@ bool ffPrintMedia(FFMediaOptions* options) { ffStrbufClear(&artistPretty); } - ffPrintLogoAndKey(FF_MEDIA_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Media), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (artistPretty.length > 0) { ffStrbufWriteTo(&artistPretty, stdout); @@ -171,7 +171,7 @@ bool ffPrintMedia(FFMediaOptions* options) { &options->moduleArgs); } } - FF_PRINT_FORMAT_CHECKED(FF_MEDIA_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){ + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Media), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){ FF_ARG(songPretty, "combined"), FF_ARG(media->song, "title"), FF_ARG(media->artist, "artist"), @@ -201,7 +201,7 @@ void ffParseMediaJsonObject(FFMediaOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_MEDIA_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Media), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -253,8 +253,30 @@ void ffDestroyMediaOptions(FFMediaOptions* options) { } FFModuleBaseInfo ffMediaModuleInfo = { - .name = FF_MEDIA_MODULE_NAME, + .name = "Media", .description = "Print the name of the currently playing song", + .displayName = { + .en = "Media", + .ar = "الوسائط", + .cs = "Média", + .de = "Medien", + .es = "Multimedia", + .fr = "Médias", + .gl = "Multimedia", + .he = "מדיה", + .id = "Media", + .it = "Media", + .ja = "メディア", + .ko = "미디어", + .pl = "Media", + .pt = "Mídia", + .ru = "Медиа", + .tr = "Medya", + .uk = "Медіа", + .vi = "Phương tiện", + .zh_CN = "媒体", + .zh_TW = "媒體", + }, .initOptions = (void*) ffInitMediaOptions, .destroyOptions = (void*) ffDestroyMediaOptions, .parseJsonObject = (void*) ffParseMediaJsonObject, diff --git a/src/modules/media/media.h b/src/modules/media/media.h index 8320227d8a..10f995eaac 100644 --- a/src/modules/media/media.h +++ b/src/modules/media/media.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_MEDIA_MODULE_NAME "Media" - bool ffPrintMedia(FFMediaOptions* options); void ffInitMediaOptions(FFMediaOptions* options); void ffDestroyMediaOptions(FFMediaOptions* options); diff --git a/src/modules/memory/memory.c b/src/modules/memory/memory.c index 73a8fc173b..6e4092d224 100644 --- a/src/modules/memory/memory.c +++ b/src/modules/memory/memory.c @@ -11,7 +11,7 @@ bool ffPrintMemory(FFMemoryOptions* options) { const char* error = ffDetectMemory(&storage); if (error) { - ffPrintError(FF_MEMORY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Memory), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } @@ -27,7 +27,7 @@ bool ffPrintMemory(FFMemoryOptions* options) { FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type; if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_MEMORY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Memory), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (storage.bytesTotal == 0) { puts("Disabled"); } else { @@ -59,7 +59,7 @@ bool ffPrintMemory(FFMemoryOptions* options) { ffPercentAppendBar(&percentageBar, percentage, options->percent, &options->moduleArgs); } - FF_PRINT_FORMAT_CHECKED(FF_MEMORY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Memory), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(usedPretty, "used"), FF_ARG(totalPretty, "total"), FF_ARG(percentageNum, "percentage"), @@ -82,7 +82,7 @@ void ffParseMemoryJsonObject(FFMemoryOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_MEMORY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Memory), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -118,8 +118,30 @@ void ffDestroyMemoryOptions(FFMemoryOptions* options) { } FFModuleBaseInfo ffMemoryModuleInfo = { - .name = FF_MEMORY_MODULE_NAME, + .name = "Memory", .description = "Print system memory usage information", + .displayName = { + .en = "Memory", + .ar = "الذاكرة", + .cs = "Paměť", + .de = "Speicher", + .es = "Memoria", + .fr = "Mémoire", + .gl = "Memoria", + .he = "זיכרון", + .id = "Memori", + .it = "Memoria", + .ja = "メモリ", + .ko = "메모리", + .pl = "Pamięć", + .pt = "Memória", + .ru = "Память", + .tr = "Bellek", + .uk = "Пам'ять", + .vi = "Bộ nhớ", + .zh_CN = "内存", + .zh_TW = "記憶體", + }, .initOptions = (void*) ffInitMemoryOptions, .destroyOptions = (void*) ffDestroyMemoryOptions, .parseJsonObject = (void*) ffParseMemoryJsonObject, diff --git a/src/modules/memory/memory.h b/src/modules/memory/memory.h index 7ed026fc3d..7883da2d84 100644 --- a/src/modules/memory/memory.h +++ b/src/modules/memory/memory.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_MEMORY_MODULE_NAME "Memory" - bool ffPrintMemory(FFMemoryOptions* options); void ffInitMemoryOptions(FFMemoryOptions* options); void ffDestroyMemoryOptions(FFMemoryOptions* options); diff --git a/src/modules/modules.c b/src/modules/modules.c index bcc4afdf45..ba4c95536f 100644 --- a/src/modules/modules.c +++ b/src/modules/modules.c @@ -271,6 +271,9 @@ static FFModuleBaseInfo* T[] = { #if !FF_MODULE_DISABLE_TITLE &ffTitleModuleInfo, #endif +#if !FF_MODULE_DISABLE_TOP + &ffTopModuleInfo, +#endif #if !FF_MODULE_DISABLE_THEME &ffThemeModuleInfo, #endif diff --git a/src/modules/modules.h b/src/modules/modules.h index 37ee242f0c..5d2cd99e88 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -64,6 +64,7 @@ #include "modules/terminaltheme/terminaltheme.h" #include "modules/theme/theme.h" #include "modules/title/title.h" +#include "modules/top/top.h" #include "modules/tpm/tpm.h" #include "modules/uptime/uptime.h" #include "modules/users/users.h" diff --git a/src/modules/monitor/monitor.c b/src/modules/monitor/monitor.c index 6c8bed1708..b5ac69f696 100644 --- a/src/modules/monitor/monitor.c +++ b/src/modules/monitor/monitor.c @@ -10,7 +10,7 @@ bool ffPrintMonitor(FFMonitorOptions* options) { const FFDisplayServerResult* result = ffConnectDisplayServer(); if (!result->displays.length) { - ffPrintError(FF_MONITOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No display detected"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Monitor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No display detected"); return false; } @@ -23,7 +23,7 @@ bool ffPrintMonitor(FFMonitorOptions* options) { ffStrbufClear(&key); if (options->moduleArgs.key.length == 0) { - ffStrbufAppendS(&key, FF_MONITOR_MODULE_NAME); + ffStrbufAppendS(&key, FF_MODULE_GET_DISPLAY_NAME(Monitor)); if (display->name.length > 0) { ffStrbufAppendF(&key, " (%s)", display->name.chars); } @@ -82,7 +82,7 @@ void ffParseMonitorJsonObject(FFMonitorOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_MONITOR_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Monitor), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -104,8 +104,30 @@ void ffDestroyMonitorOptions(FFMonitorOptions* options) { } FFModuleBaseInfo ffMonitorModuleInfo = { - .name = FF_MONITOR_MODULE_NAME, + .name = "Monitor", .description = "Same as Display module, but with a different default output format", + .displayName = { + .en = "Monitor", + .ar = "الشاشة", + .cs = "Monitor", + .de = "Monitor", + .es = "Monitor", + .fr = "Moniteur", + .gl = "Monitor", + .he = "צג", + .id = "Monitor", + .it = "Monitor", + .ja = "モニター", + .ko = "모니터", + .pl = "Monitor", + .pt = "Monitor", + .ru = "Монитор", + .tr = "Monitör", + .uk = "Монітор", + .vi = "Màn hình", + .zh_CN = "监视器", + .zh_TW = "監視器", + }, .initOptions = (void*) ffInitMonitorOptions, .destroyOptions = (void*) ffDestroyMonitorOptions, .parseJsonObject = (void*) ffParseMonitorJsonObject, diff --git a/src/modules/monitor/monitor.h b/src/modules/monitor/monitor.h index 82f9491fe6..598d226920 100644 --- a/src/modules/monitor/monitor.h +++ b/src/modules/monitor/monitor.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_MONITOR_MODULE_NAME "Monitor" - bool ffPrintMonitor(FFMonitorOptions* options); void ffInitMonitorOptions(FFMonitorOptions* options); void ffDestroyMonitorOptions(FFMonitorOptions* options); diff --git a/src/modules/mouse/mouse.c b/src/modules/mouse/mouse.c index 82dd6b053e..293f319312 100644 --- a/src/modules/mouse/mouse.c +++ b/src/modules/mouse/mouse.c @@ -7,10 +7,10 @@ static void printDevice(FFMouseOptions* options, const FFMouseDevice* device, uint8_t index) { if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_MOUSE_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Mouse), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&device->name, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_MOUSE_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Mouse), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(device->name, "name"), FF_ARG(device->serial, "serial"), })); @@ -23,12 +23,12 @@ bool ffPrintMouse(FFMouseOptions* options) { const char* error = ffDetectMouse(&result); if (error) { - ffPrintError(FF_MOUSE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Mouse), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (!result.length) { - ffPrintError(FF_MOUSE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No devices detected"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Mouse), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No devices detected"); return false; } @@ -49,7 +49,7 @@ bool ffPrintMouse(FFMouseOptions* options) { bool ret = true; if (!filtered.length) { - ffPrintError(FF_MOUSE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "All devices are ignored"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Mouse), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "All devices are ignored"); ret = false; } else { uint8_t index = 0; @@ -87,7 +87,7 @@ void ffParseMouseJsonObject(FFMouseOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_MOUSE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Mouse), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -152,8 +152,30 @@ void ffDestroyMouseOptions(FFMouseOptions* options) { } FFModuleBaseInfo ffMouseModuleInfo = { - .name = FF_MOUSE_MODULE_NAME, + .name = "Mouse", .description = "List connected mice", + .displayName = { + .en = "Mouse", + .ar = "الفأرة", + .cs = "Myš", + .de = "Maus", + .es = "Ratón", + .fr = "Souris", + .gl = "Rato", + .he = "עכבר", + .id = "Tetikus", + .it = "Mouse", + .ja = "マウス", + .ko = "마우스", + .pl = "Mysz", + .pt = "Mouse", + .ru = "Мышь", + .tr = "Fare", + .uk = "Миша", + .vi = "Chuột", + .zh_CN = "鼠标", + .zh_TW = "滑鼠", + }, .initOptions = (void*) ffInitMouseOptions, .destroyOptions = (void*) ffDestroyMouseOptions, .parseJsonObject = (void*) ffParseMouseJsonObject, diff --git a/src/modules/mouse/mouse.h b/src/modules/mouse/mouse.h index afbc20ff9e..f042cbfa45 100644 --- a/src/modules/mouse/mouse.h +++ b/src/modules/mouse/mouse.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_MOUSE_MODULE_NAME "Mouse" - bool ffPrintMouse(FFMouseOptions* options); void ffInitMouseOptions(FFMouseOptions* options); void ffDestroyMouseOptions(FFMouseOptions* options); diff --git a/src/modules/netio/netio.c b/src/modules/netio/netio.c index 8607a21af6..0ffe4f1d32 100644 --- a/src/modules/netio/netio.c +++ b/src/modules/netio/netio.c @@ -5,8 +5,6 @@ #include "detection/netio/netio.h" #include "modules/netio/netio.h" -#define FF_NETIO_DISPLAY_NAME "Network IO" - static int sortInfs(const FFNetIOResult* left, const FFNetIOResult* right) { return ffStrbufComp(&left->name, &right->name); } @@ -17,7 +15,7 @@ static void formatKey(const FFNetIOOptions* options, FFNetIOResult* inf, uint32_ ffStrbufSetF(&inf->name, "unknown %u", (unsigned) index); } - ffStrbufSetF(key, FF_NETIO_DISPLAY_NAME " (%s)", inf->name.chars); + ffStrbufSetF(key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(NetIO), inf->name.chars); } else { ffStrbufClear(key); FF_PARSE_FORMAT_STRING_CHECKED(key, &options->moduleArgs.key, ((FFformatarg[]) { @@ -33,7 +31,7 @@ bool ffPrintNetIO(FFNetIOOptions* options) { const char* error = ffDetectNetIO(&result, options); if (error) { - ffPrintError(FF_NETIO_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(NetIO), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } @@ -128,10 +126,13 @@ void ffParseNetIOJsonObject(FFNetIOOptions* options, yyjson_val* module) { if (unsafe_yyjson_equals_str(key, "waitTime")) { options->waitTime = (uint32_t) yyjson_get_uint(val); + if (options->waitTime == 0) { + options->waitTime = 1; + } continue; } - ffPrintError(FF_NETIO_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(NetIO), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -190,7 +191,7 @@ void ffInitNetIOOptions(FFNetIOOptions* options) { #endif ; options->detectTotal = false; - options->waitTime = 1000; + options->waitTime = 500; } void ffDestroyNetIOOptions(FFNetIOOptions* options) { @@ -199,8 +200,30 @@ void ffDestroyNetIOOptions(FFNetIOOptions* options) { } FFModuleBaseInfo ffNetIOModuleInfo = { - .name = FF_NETIO_MODULE_NAME, + .name = "NetIO", .description = "Print network I/O throughput", + .displayName = { + .en = "Network I/O", + .ar = "إدخال/إخراج الشبكة", + .cs = "Síťové I/O", + .de = "Netzwerk I/O", + .es = "E/S de la red", + .fr = "E/S réseau", + .gl = "E/S da rede", + .he = "קלט/פלט רשת", + .id = "I/O Jaringan", + .it = "I/O di rete", + .ja = "ネットワークI/O", + .ko = "네트워크 I/O", + .pl = "I/O sieciowe", + .pt = "E/S de rede", + .ru = "Сетевой I/O", + .tr = "Ağ G/Ç", + .uk = "Мережа В/В", + .vi = "Mạng I/O", + .zh_CN = "网络 I/O", + .zh_TW = "網路 I/O", + }, .initOptions = (void*) ffInitNetIOOptions, .destroyOptions = (void*) ffDestroyNetIOOptions, .parseJsonObject = (void*) ffParseNetIOJsonObject, diff --git a/src/modules/netio/netio.h b/src/modules/netio/netio.h index 653b7e97ce..2f048e5a36 100644 --- a/src/modules/netio/netio.h +++ b/src/modules/netio/netio.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_NETIO_MODULE_NAME "NetIO" - void ffPrepareNetIO(FFNetIOOptions* options); bool ffPrintNetIO(FFNetIOOptions* options); diff --git a/src/modules/opencl/opencl.c b/src/modules/opencl/opencl.c index 0a80d9d16b..c483c430d1 100644 --- a/src/modules/opencl/opencl.c +++ b/src/modules/opencl/opencl.c @@ -9,15 +9,15 @@ bool ffPrintOpenCL(FFOpenCLOptions* options) { FFOpenCLResult* result = ffDetectOpenCL(); if (result->error != nullptr) { - ffPrintError(FF_OPENCL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", result->error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(OpenCL), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", result->error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_OPENCL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(OpenCL), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&result->version, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_OPENCL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(OpenCL), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result->version, "version"), FF_ARG(result->name, "name"), FF_ARG(result->vendor, "vendor"), @@ -35,7 +35,7 @@ void ffParseOpenCLJsonObject(FFOpenCLOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_OPENCL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(OpenCL), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -120,8 +120,30 @@ void ffDestroyOpenCLOptions(FFOpenCLOptions* options) { } FFModuleBaseInfo ffOpenCLModuleInfo = { - .name = FF_OPENCL_MODULE_NAME, + .name = "OpenCL", .description = "Print the highest OpenCL version supported by the GPU", + .displayName = { + .en = "OpenCL", + .ar = "OpenCL", + .cs = "OpenCL", + .de = "OpenCL", + .es = "OpenCL", + .fr = "OpenCL", + .gl = "OpenCL", + .he = "OpenCL", + .id = "OpenCL", + .it = "OpenCL", + .ja = "OpenCL", + .ko = "OpenCL", + .pl = "OpenCL", + .pt = "OpenCL", + .ru = "OpenCL", + .tr = "OpenCL", + .uk = "OpenCL", + .vi = "OpenCL", + .zh_CN = "OpenCL", + .zh_TW = "OpenCL", + }, .initOptions = (void*) ffInitOpenCLOptions, .destroyOptions = (void*) ffDestroyOpenCLOptions, .parseJsonObject = (void*) ffParseOpenCLJsonObject, diff --git a/src/modules/opencl/opencl.h b/src/modules/opencl/opencl.h index aafab369f1..d7dbe3661e 100644 --- a/src/modules/opencl/opencl.h +++ b/src/modules/opencl/opencl.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_OPENCL_MODULE_NAME "OpenCL" - bool ffPrintOpenCL(FFOpenCLOptions* options); void ffInitOpenCLOptions(FFOpenCLOptions* options); void ffDestroyOpenCLOptions(FFOpenCLOptions* options); diff --git a/src/modules/opengl/opengl.c b/src/modules/opengl/opengl.c index 29adcf0f0d..0ef5472c88 100644 --- a/src/modules/opengl/opengl.c +++ b/src/modules/opengl/opengl.c @@ -15,13 +15,13 @@ bool ffPrintOpenGL(FFOpenGLOptions* options) { const char* error = ffDetectOpenGL(options, &result); if (error) { - ffPrintError(FF_OPENGL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(OpenGL), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); } else { if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_OPENGL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(OpenGL), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); puts(result.version.chars); } else { - FF_PRINT_FORMAT_CHECKED(FF_OPENGL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(OpenGL), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.version, "version"), FF_ARG(result.renderer, "renderer"), FF_ARG(result.vendor, "vendor"), @@ -58,14 +58,14 @@ void ffParseOpenGLJsonObject(FFOpenGLOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_OPENGL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(OpenGL), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->library = (FFOpenGLLibrary) value; } continue; } - ffPrintError(FF_OPENGL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(OpenGL), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -127,8 +127,30 @@ void ffDestroyOpenGLOptions(FFOpenGLOptions* options) { } FFModuleBaseInfo ffOpenGLModuleInfo = { - .name = FF_OPENGL_MODULE_NAME, + .name = "OpenGL", .description = "Print the highest OpenGL version supported by the GPU", + .displayName = { + .en = "OpenGL", + .ar = "OpenGL", + .cs = "OpenGL", + .de = "OpenGL", + .es = "OpenGL", + .fr = "OpenGL", + .gl = "OpenGL", + .he = "OpenGL", + .id = "OpenGL", + .it = "OpenGL", + .ja = "OpenGL", + .ko = "OpenGL", + .pl = "OpenGL", + .pt = "OpenGL", + .ru = "OpenGL", + .tr = "OpenGL", + .uk = "OpenGL", + .vi = "OpenGL", + .zh_CN = "OpenGL", + .zh_TW = "OpenGL", + }, .initOptions = (void*) ffInitOpenGLOptions, .destroyOptions = (void*) ffDestroyOpenGLOptions, .parseJsonObject = (void*) ffParseOpenGLJsonObject, diff --git a/src/modules/opengl/opengl.h b/src/modules/opengl/opengl.h index 8caffa5a5b..f7f4c05cc9 100644 --- a/src/modules/opengl/opengl.h +++ b/src/modules/opengl/opengl.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_OPENGL_MODULE_NAME "OpenGL" - bool ffPrintOpenGL(FFOpenGLOptions* options); void ffInitOpenGLOptions(FFOpenGLOptions* options); void ffDestroyOpenGLOptions(FFOpenGLOptions* options); diff --git a/src/modules/options.h b/src/modules/options.h index 7cf98c1274..3c0bd6ee80 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -64,6 +64,7 @@ #include "modules/terminaltheme/option.h" #include "modules/theme/option.h" #include "modules/title/option.h" +#include "modules/top/option.h" #include "modules/tpm/option.h" #include "modules/uptime/option.h" #include "modules/users/option.h" diff --git a/src/modules/os/os.c b/src/modules/os/os.c index d0dc5600b7..12dd2740aa 100644 --- a/src/modules/os/os.c +++ b/src/modules/os/os.c @@ -50,14 +50,14 @@ bool ffPrintOS(FFOSOptions* options) { const FFOSResult* os = ffDetectOS(); if (os->name.length == 0 && os->prettyName.length == 0 && os->id.length == 0) { - ffPrintError(FF_OS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Could not detect OS"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(OS), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Could not detect OS"); return false; } FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); if (options->moduleArgs.key.length == 0) { - ffStrbufSetStatic(&key, FF_OS_MODULE_NAME); + ffStrbufSetStatic(&key, FF_MODULE_GET_DISPLAY_NAME(OS)); } else { FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]) { FF_ARG(instance.state.platform.sysinfo.name, "sysname"), @@ -112,7 +112,7 @@ void ffParseOSJsonObject(FFOSOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_OS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(OS), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -172,8 +172,30 @@ void ffDestroyOSOptions(FFOSOptions* options) { } FFModuleBaseInfo ffOSModuleInfo = { - .name = FF_OS_MODULE_NAME, + .name = "OS", .description = "Print the OS or Linux distribution name and version", + .displayName = { + .en = "OS", + .ar = "نظام التشغيل", + .cs = "OS", + .de = "Betriebssystem", + .es = "Sistema operativo", + .fr = "Système d'exploitation", + .gl = "Sistema operativo", + .he = "מערכת הפעלה", + .id = "OS", + .it = "Sistema operativo", + .ja = "オペレーティングシステム", + .ko = "운영 체제", + .pl = "System operacyjny", + .pt = "Sistema operacional", + .ru = "Операционная система", + .tr = "İşletim Sistemi", + .uk = "ОС", + .vi = "Hệ điều hành", + .zh_CN = "操作系统", + .zh_TW = "操作系統", + }, .initOptions = (void*) ffInitOSOptions, .destroyOptions = (void*) ffDestroyOSOptions, .parseJsonObject = (void*) ffParseOSJsonObject, diff --git a/src/modules/os/os.h b/src/modules/os/os.h index a53d28516e..77312c6202 100644 --- a/src/modules/os/os.h +++ b/src/modules/os/os.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_OS_MODULE_NAME "OS" - bool ffPrintOS(FFOSOptions* options); void ffInitOSOptions(FFOSOptions* options); void ffDestroyOSOptions(FFOSOptions* options); diff --git a/src/modules/packages/packages.c b/src/modules/packages/packages.c index 1553893f1a..f31275a86a 100644 --- a/src/modules/packages/packages.c +++ b/src/modules/packages/packages.c @@ -11,12 +11,12 @@ bool ffPrintPackages(FFPackagesOptions* options) { const char* error = ffDetectPackages(&counts, options); if (error) { - ffPrintError(FF_PACKAGES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Packages), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (counts.all == 0) { - ffPrintError(FF_PACKAGES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No packages from known package managers found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Packages), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No packages from known package managers found"); return false; } @@ -29,7 +29,7 @@ bool ffPrintPackages(FFPackagesOptions* options) { uint32_t scoopAll = counts.scoopUser + counts.scoopGlobal; if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_PACKAGES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Packages), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate(); @@ -141,7 +141,7 @@ bool ffPrintPackages(FFPackagesOptions* options) { output.chars[output.length - 1] = '\n'; ffStrbufWriteTo(&output, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_PACKAGES_MODULE_NAME, + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Packages), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, @@ -219,7 +219,7 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) { #if !FF_PACKAGES_REMOVE_DISABLED if (unsafe_yyjson_equals_str(key, "disabled")) { if (!yyjson_is_null(val) && !yyjson_is_arr(val)) { - ffPrintError(FF_PACKAGES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid JSON value for %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Packages), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid JSON value for %s", unsafe_yyjson_get_str(key)); continue; } @@ -230,7 +230,7 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) { size_t flagIdx, flagMax; yyjson_arr_foreach (val, flagIdx, flagMax, flagObj) { if (!yyjson_is_str(flagObj)) { - ffPrintError(FF_PACKAGES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid JSON value for %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Packages), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid JSON value for %s", unsafe_yyjson_get_str(key)); continue; } const char* flag = unsafe_yyjson_get_str(flagObj); @@ -366,7 +366,7 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_PACKAGES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Packages), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -510,8 +510,30 @@ void ffDestroyPackagesOptions(FFPackagesOptions* options) { } FFModuleBaseInfo ffPackagesModuleInfo = { - .name = FF_PACKAGES_MODULE_NAME, + .name = "Packages", .description = "List installed package managers and count of installed packages", + .displayName = { + .en = "Packages", + .ar = "الحزم", + .cs = "Balíčky", + .de = "Pakete", + .es = "Paquetes", + .fr = "Paquets", + .gl = "Paquetes", + .he = "חבילות", + .id = "Paket", + .it = "Pacchetti", + .ja = "パッケージ", + .ko = "패키지", + .pl = "Pakiety", + .pt = "Pacotes", + .ru = "Пакеты", + .tr = "Paketler", + .uk = "Пакети", + .vi = "Gói phần mềm", + .zh_CN = "软件包", + .zh_TW = "軟體包", + }, .initOptions = (void*) ffInitPackagesOptions, .destroyOptions = (void*) ffDestroyPackagesOptions, .parseJsonObject = (void*) ffParsePackagesJsonObject, diff --git a/src/modules/packages/packages.h b/src/modules/packages/packages.h index 4e58768702..caf7832d82 100644 --- a/src/modules/packages/packages.h +++ b/src/modules/packages/packages.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_PACKAGES_MODULE_NAME "Packages" - bool ffPrintPackages(FFPackagesOptions* options); void ffInitPackagesOptions(FFPackagesOptions* options); void ffDestroyPackagesOptions(FFPackagesOptions* options); diff --git a/src/modules/physicaldisk/physicaldisk.c b/src/modules/physicaldisk/physicaldisk.c index 8935438840..3b396d1f74 100644 --- a/src/modules/physicaldisk/physicaldisk.c +++ b/src/modules/physicaldisk/physicaldisk.c @@ -5,15 +5,13 @@ #include "detection/physicaldisk/physicaldisk.h" #include "modules/physicaldisk/physicaldisk.h" -#define FF_PHYSICALDISK_DISPLAY_NAME "Physical Disk" - static int sortDevices(const FFPhysicalDiskResult* left, const FFPhysicalDiskResult* right) { return ffStrbufComp(&left->name, &right->name); } static void formatKey(const FFPhysicalDiskOptions* options, FFPhysicalDiskResult* dev, uint32_t index, FFstrbuf* key) { if (options->moduleArgs.key.length == 0) { - ffStrbufSetF(key, FF_PHYSICALDISK_DISPLAY_NAME " (%s)", dev->name.length ? dev->name.chars : dev->devPath.chars); + ffStrbufSetF(key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), dev->name.length ? dev->name.chars : dev->devPath.chars); } else { ffStrbufClear(key); FF_PARSE_FORMAT_STRING_CHECKED(key, &options->moduleArgs.key, ((FFformatarg[]) { @@ -30,7 +28,7 @@ bool ffPrintPhysicalDisk(FFPhysicalDiskOptions* options) { const char* error = ffDetectPhysicalDisk(&result, options); if (error) { - ffPrintError(FF_PHYSICALDISK_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } @@ -140,7 +138,7 @@ void ffParsePhysicalDiskJsonObject(FFPhysicalDiskOptions* options, yyjson_val* m if (unsafe_yyjson_equals_str(key, "hideVirtual")) { if (!yyjson_is_bool(val)) { - ffPrintError(FF_PHYSICALDISK_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "hideVirtual must be a boolean"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "hideVirtual must be a boolean"); } else { if (unsafe_yyjson_is_true(val)) { options->hideType |= FF_PHYSICALDISK_TYPE_VIRTUAL; @@ -153,7 +151,7 @@ void ffParsePhysicalDiskJsonObject(FFPhysicalDiskOptions* options, yyjson_val* m if (unsafe_yyjson_equals_str(key, "hideUnused")) { if (!yyjson_is_bool(val)) { - ffPrintError(FF_PHYSICALDISK_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "hideUnused must be a boolean"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "hideUnused must be a boolean"); } else { if (unsafe_yyjson_is_true(val)) { options->hideType |= FF_PHYSICALDISK_TYPE_UNUSED; @@ -168,7 +166,7 @@ void ffParsePhysicalDiskJsonObject(FFPhysicalDiskOptions* options, yyjson_val* m continue; } - ffPrintError(FF_PHYSICALDISK_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -265,8 +263,30 @@ void ffDestroyPhysicalDiskOptions(FFPhysicalDiskOptions* options) { } FFModuleBaseInfo ffPhysicalDiskModuleInfo = { - .name = FF_PHYSICALDISK_MODULE_NAME, + .name = "PhysicalDisk", .description = "Print physical disk information", + .displayName = { + .en = "Physical Disk", + .ar = "القرص الفعلي", + .cs = "Fyzický disk", + .de = "Physische Festplatte", + .es = "Disco físico", + .fr = "Disque physique", + .gl = "Disco físico", + .he = "דיסק פיזי", + .id = "Disk Fisik", + .it = "Disco fisico", + .ja = "物理ディスク", + .ko = "물리 디스크", + .pl = "Dysk fizyczny", + .pt = "Disco físico", + .ru = "Физический диск", + .tr = "Fiziksel Disk", + .uk = "Фізичний диск", + .vi = "Ổ đĩa vật lý", + .zh_CN = "物理磁盘", + .zh_TW = "物理磁碟", + }, .initOptions = (void*) ffInitPhysicalDiskOptions, .destroyOptions = (void*) ffDestroyPhysicalDiskOptions, .parseJsonObject = (void*) ffParsePhysicalDiskJsonObject, diff --git a/src/modules/physicaldisk/physicaldisk.h b/src/modules/physicaldisk/physicaldisk.h index aaad8883e6..41a8d9987d 100644 --- a/src/modules/physicaldisk/physicaldisk.h +++ b/src/modules/physicaldisk/physicaldisk.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_PHYSICALDISK_MODULE_NAME "PhysicalDisk" - bool ffPrintPhysicalDisk(FFPhysicalDiskOptions* options); void ffInitPhysicalDiskOptions(FFPhysicalDiskOptions* options); void ffDestroyPhysicalDiskOptions(FFPhysicalDiskOptions* options); diff --git a/src/modules/physicalmemory/physicalmemory.c b/src/modules/physicalmemory/physicalmemory.c index f044e59071..a374422e65 100644 --- a/src/modules/physicalmemory/physicalmemory.c +++ b/src/modules/physicalmemory/physicalmemory.c @@ -4,19 +4,17 @@ #include "detection/physicalmemory/physicalmemory.h" #include "modules/physicalmemory/physicalmemory.h" -#define FF_PHYSICALMEMORY_DISPLAY_NAME "Physical Memory" - bool ffPrintPhysicalMemory(FFPhysicalMemoryOptions* options) { FF_LIST_AUTO_DESTROY result = ffListCreate(); const char* error = ffDetectPhysicalMemory(options, &result); if (error) { - ffPrintError(FF_PHYSICALMEMORY_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalMemory), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (result.length == 0) { - ffPrintError(FF_PHYSICALMEMORY_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No physical memory devices detected"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalMemory), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No physical memory devices detected"); return false; } @@ -30,7 +28,7 @@ bool ffPrintPhysicalMemory(FFPhysicalMemoryOptions* options) { } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_PHYSICALMEMORY_DISPLAY_NAME, result.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(PhysicalMemory), result.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (device->installed) { fputs(prettySize.chars, stdout); @@ -59,7 +57,7 @@ bool ffPrintPhysicalMemory(FFPhysicalMemoryOptions* options) { } putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_PHYSICALMEMORY_DISPLAY_NAME, (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(PhysicalMemory), (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(device->size, "bytes"), FF_ARG(prettySize, "size"), FF_ARG(device->maxSpeed, "max-speed"), @@ -103,7 +101,7 @@ void ffParsePhysicalMemoryJsonObject(FFPhysicalMemoryOptions* options, yyjson_va continue; } - ffPrintError(FF_PHYSICALMEMORY_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PhysicalMemory), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -159,8 +157,30 @@ void ffDestroyPhysicalMemoryOptions(FFPhysicalMemoryOptions* options) { } FFModuleBaseInfo ffPhysicalMemoryModuleInfo = { - .name = FF_PHYSICALMEMORY_MODULE_NAME, + .name = "PhysicalMemory", .description = "Print system physical memory devices", + .displayName = { + .en = "Physical Memory", + .ar = "الذاكرة الفعلية", + .cs = "Fyzická paměť", + .de = "Physischer Speicher", + .es = "Memoria física", + .fr = "Mémoire physique", + .gl = "Memoria física", + .he = "זיכרון פיזי", + .id = "Memori Fisik", + .it = "Memoria fisica", + .ja = "物理メモリ", + .ko = "물리 메모리", + .pl = "Pamięć fizyczna", + .pt = "Memória física", + .ru = "Физическая память", + .tr = "Fiziksel Bellek", + .uk = "Фізична пам'ять", + .vi = "Bộ nhớ vật lý", + .zh_CN = "物理内存", + .zh_TW = "實體記憶體", + }, .initOptions = (void*) ffInitPhysicalMemoryOptions, .destroyOptions = (void*) ffDestroyPhysicalMemoryOptions, .parseJsonObject = (void*) ffParsePhysicalMemoryJsonObject, diff --git a/src/modules/physicalmemory/physicalmemory.h b/src/modules/physicalmemory/physicalmemory.h index feb3d1d293..ae4ae985ad 100644 --- a/src/modules/physicalmemory/physicalmemory.h +++ b/src/modules/physicalmemory/physicalmemory.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_PHYSICALMEMORY_MODULE_NAME "PhysicalMemory" - bool ffPrintPhysicalMemory(FFPhysicalMemoryOptions* options); void ffInitPhysicalMemoryOptions(FFPhysicalMemoryOptions* options); void ffDestroyPhysicalMemoryOptions(FFPhysicalMemoryOptions* options); diff --git a/src/modules/player/player.c b/src/modules/player/player.c index 0d1fd03b8f..1474494c2e 100644 --- a/src/modules/player/player.c +++ b/src/modules/player/player.c @@ -6,18 +6,16 @@ #include -#define FF_PLAYER_DISPLAY_NAME "Media Player" - bool ffPrintPlayer(FFPlayerOptions* options) { const FFMediaResult* media = ffDetectMedia(false); if (media->error.length > 0) { - ffPrintError(FF_PLAYER_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", media->error.chars); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Player), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", media->error.chars); return false; } if (media->player.length == 0) { - ffPrintError(FF_PLAYER_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No media player detected"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Player), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No media player detected"); return false; } @@ -65,10 +63,10 @@ bool ffPrintPlayer(FFPlayerOptions* options) { } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_PLAYER_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Player), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&playerPretty, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_PLAYER_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Player), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(playerPretty, "player"), FF_ARG(media->player, "name"), FF_ARG(media->playerId, "id"), @@ -87,7 +85,7 @@ void ffParsePlayerJsonObject(FFPlayerOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_PLAYER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Player), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -109,8 +107,30 @@ void ffDestroyPlayerOptions(FFPlayerOptions* options) { } FFModuleBaseInfo ffPlayerModuleInfo = { - .name = FF_PLAYER_MODULE_NAME, + .name = "Player", .description = "Print the music player name that is currently active", + .displayName = { + .en = "Media Player", + .ar = "مشغل الوسائط", + .cs = "Přehrávač médií", + .de = "Medienplayer", + .es = "Reproductor multimedia", + .fr = "Lecteur multimédia", + .gl = "Reprodutor multimedia", + .he = "נגן מדיה", + .id = "Pemutar Media", + .it = "Lettore multimediale", + .ja = "メディアプレイヤー", + .ko = "미디어 플레이어", + .pl = "Odtwarzacz multimediów", + .pt = "Reprodutor de mídia", + .ru = "Медиаплеер", + .tr = "Medya Oynatıcı", + .uk = "Медіаплеєр", + .vi = "Trình phát phương tiện", + .zh_CN = "媒体播放器", + .zh_TW = "媒體播放器", + }, .initOptions = (void*) ffInitPlayerOptions, .destroyOptions = (void*) ffDestroyPlayerOptions, .parseJsonObject = (void*) ffParsePlayerJsonObject, diff --git a/src/modules/player/player.h b/src/modules/player/player.h index 8b18d29828..001d81e373 100644 --- a/src/modules/player/player.h +++ b/src/modules/player/player.h @@ -3,8 +3,6 @@ #include "option.h" -#define FF_PLAYER_MODULE_NAME "Player" - bool ffPrintPlayer(FFPlayerOptions* options); void ffInitPlayerOptions(FFPlayerOptions* options); void ffDestroyPlayerOptions(FFPlayerOptions* options); diff --git a/src/modules/poweradapter/poweradapter.c b/src/modules/poweradapter/poweradapter.c index be20d2a6d7..2174a1b266 100644 --- a/src/modules/poweradapter/poweradapter.c +++ b/src/modules/poweradapter/poweradapter.c @@ -4,20 +4,18 @@ #include "detection/poweradapter/poweradapter.h" #include "modules/poweradapter/poweradapter.h" -#define FF_POWERADAPTER_DISPLAY_NAME "Power Adapter" - bool ffPrintPowerAdapter(FFPowerAdapterOptions* options) { FF_LIST_AUTO_DESTROY results = ffListCreate(); const char* error = ffDetectPowerAdapter(&results); if (error) { - ffPrintError(FF_POWERADAPTER_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PowerAdapter), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (results.length == 0) { - ffPrintError(FF_POWERADAPTER_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No power adapters found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PowerAdapter), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No power adapters found"); return false; } @@ -25,7 +23,7 @@ bool ffPrintPowerAdapter(FFPowerAdapterOptions* options) { FFPowerAdapterResult* result = FF_LIST_GET(FFPowerAdapterResult, results, i); if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_POWERADAPTER_DISPLAY_NAME, i, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(PowerAdapter), i, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (result->name.length > 0) { puts(result->name.chars); @@ -33,7 +31,7 @@ bool ffPrintPowerAdapter(FFPowerAdapterOptions* options) { printf("%dW\n", result->watts); } } else { - FF_PRINT_FORMAT_CHECKED(FF_POWERADAPTER_DISPLAY_NAME, i, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(PowerAdapter), i, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result->watts, "watts"), FF_ARG(result->name, "name"), FF_ARG(result->manufacturer, "manufacturer"), @@ -65,7 +63,7 @@ void ffParsePowerAdapterJsonObject(FFPowerAdapterOptions* options, yyjson_val* m continue; } - ffPrintError(FF_POWERADAPTER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PowerAdapter), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -110,8 +108,30 @@ void ffDestroyPowerAdapterOptions(FFPowerAdapterOptions* options) { } FFModuleBaseInfo ffPowerAdapterModuleInfo = { - .name = FF_POWERADAPTER_MODULE_NAME, + .name = "PowerAdapter", .description = "Print power adapter name and charging watts", + .displayName = { + .en = "Power Adapter", + .ar = "محول الطاقة", + .cs = "Napájecí adaptér", + .de = "Netzteil", + .es = "Adaptador de corriente", + .fr = "Adaptateur secteur", + .gl = "Adaptador de corrente", + .he = "מתאם מתח", + .id = "Adaptor Daya", + .it = "Alimentatore", + .ja = "電源アダプター", + .ko = "전원 어댑터", + .pl = "Zasilacz", + .pt = "Fonte de alimentação", + .ru = "Адаптер питания", + .tr = "Güç Adaptörü", + .uk = "Блок живлення", + .vi = "Bộ chuyển đổi nguồn", + .zh_CN = "电源适配器", + .zh_TW = "電源轉接器", + }, .initOptions = (void*) ffInitPowerAdapterOptions, .destroyOptions = (void*) ffDestroyPowerAdapterOptions, .parseJsonObject = (void*) ffParsePowerAdapterJsonObject, diff --git a/src/modules/poweradapter/poweradapter.h b/src/modules/poweradapter/poweradapter.h index 3e61025506..f4d1170c23 100644 --- a/src/modules/poweradapter/poweradapter.h +++ b/src/modules/poweradapter/poweradapter.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_POWERADAPTER_MODULE_NAME "PowerAdapter" - bool ffPrintPowerAdapter(FFPowerAdapterOptions* options); void ffInitPowerAdapterOptions(FFPowerAdapterOptions* options); void ffDestroyPowerAdapterOptions(FFPowerAdapterOptions* options); diff --git a/src/modules/processes/option.h b/src/modules/processes/option.h index 2ee25634df..9ef218135b 100644 --- a/src/modules/processes/option.h +++ b/src/modules/processes/option.h @@ -4,6 +4,8 @@ typedef struct FFProcessesOptions { FFModuleArgs moduleArgs; + + bool countKprocs; } FFProcessesOptions; static_assert(sizeof(FFProcessesOptions) <= FF_OPTION_MAX_SIZE, "FFProcessesOptions size exceeds maximum allowed size"); diff --git a/src/modules/processes/processes.c b/src/modules/processes/processes.c index daf7a23ed8..d6220aa03f 100644 --- a/src/modules/processes/processes.c +++ b/src/modules/processes/processes.c @@ -5,20 +5,20 @@ #include "modules/processes/processes.h" bool ffPrintProcesses(FFProcessesOptions* options) { - uint32_t numProcesses = 0; - const char* error = ffDetectProcesses(&numProcesses); + FFProcessesResult result = {}; + const char* error = ffDetectProcesses(options, &result); if (error) { - ffPrintError(FF_PROCESSES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_PROCESSES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - printf("%u\n", numProcesses); + printf("%u (%u threads)\n", result.processes, result.threads); } else { - FF_PRINT_FORMAT_CHECKED(FF_PROCESSES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(numProcesses, "result") })); + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.processes, "result") })); } return true; @@ -32,30 +32,40 @@ void ffParseProcessesJsonObject(FFProcessesOptions* options, yyjson_val* module) continue; } - ffPrintError(FF_PROCESSES_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + if (unsafe_yyjson_equals_str(key, "countKprocs")) { + options->countKprocs = yyjson_get_bool(val); + continue; + } + + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } void ffGenerateProcessesJsonConfig(FFProcessesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs); + yyjson_mut_obj_add_bool(doc, module, "countKprocs", options->countKprocs); } bool ffGenerateProcessesJsonResult([[maybe_unused]] FFProcessesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { - uint32_t result; - const char* error = ffDetectProcesses(&result); + FFProcessesResult result = {}; + const char* error = ffDetectProcesses(options, &result); if (error) { yyjson_mut_obj_add_str(doc, module, "error", error); return false; } - yyjson_mut_obj_add_uint(doc, module, "result", result); + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + yyjson_mut_obj_add_uint(doc, obj, "processes", result.processes); + yyjson_mut_obj_add_uint(doc, obj, "threads", result.threads); return true; } void ffInitProcessesOptions(FFProcessesOptions* options) { ffOptionInitModuleArg(&options->moduleArgs, ""); + + options->countKprocs = false; } void ffDestroyProcessesOptions(FFProcessesOptions* options) { @@ -63,8 +73,30 @@ void ffDestroyProcessesOptions(FFProcessesOptions* options) { } FFModuleBaseInfo ffProcessesModuleInfo = { - .name = FF_PROCESSES_MODULE_NAME, - .description = "Print number of running processes", + .name = "Processes", + .description = "Print number of running processes and threads", + .displayName = { + .en = "Processes", + .ar = "العمليات", + .cs = "Procesy", + .de = "Prozesse", + .es = "Procesos", + .fr = "Processus", + .gl = "Procesos", + .he = "תהליכים", + .id = "Proses", + .it = "Processi", + .ja = "プロセス", + .ko = "프로세스", + .pl = "Procesy", + .pt = "Processos", + .ru = "Процессы", + .tr = "Süreçler", + .uk = "Процеси", + .vi = "Tiến trình", + .zh_CN = "进程数", + .zh_TW = "進程數", + }, .initOptions = (void*) ffInitProcessesOptions, .destroyOptions = (void*) ffDestroyProcessesOptions, .parseJsonObject = (void*) ffParseProcessesJsonObject, diff --git a/src/modules/processes/processes.h b/src/modules/processes/processes.h index 0220c17dd0..6fe14cb87a 100644 --- a/src/modules/processes/processes.h +++ b/src/modules/processes/processes.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_PROCESSES_MODULE_NAME "Processes" - bool ffPrintProcesses(FFProcessesOptions* options); void ffInitProcessesOptions(FFProcessesOptions* options); void ffDestroyProcessesOptions(FFProcessesOptions* options); diff --git a/src/modules/publicip/publicip.c b/src/modules/publicip/publicip.c index 5d52e74a70..e93f34e091 100644 --- a/src/modules/publicip/publicip.c +++ b/src/modules/publicip/publicip.c @@ -4,8 +4,6 @@ #include "modules/publicip/publicip.h" #include "detection/publicip/publicip.h" -#define FF_PUBLICIP_DISPLAY_NAME "Public IP" - bool ffPrintPublicIp(FFPublicIPOptions* options) { FFPublicIpResult result; ffStrbufInit(&result.ip); @@ -13,19 +11,19 @@ bool ffPrintPublicIp(FFPublicIPOptions* options) { const char* error = ffDetectPublicIp(options, &result); if (error) { - ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PublicIP), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(PublicIP), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (result.location.length) { printf("%s (%s)\n", result.ip.chars, result.location.chars); } else { ffStrbufPutTo(&result.ip, stdout); } } else { - FF_PRINT_FORMAT_CHECKED(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(PublicIP), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.ip, "ip"), FF_ARG(result.location, "location"), })); @@ -60,7 +58,7 @@ void ffParsePublicIpJsonObject(FFPublicIPOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_PUBLICIP_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(PublicIP), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -110,8 +108,30 @@ void ffDestroyPublicIpOptions(FFPublicIPOptions* options) { } FFModuleBaseInfo ffPublicIPModuleInfo = { - .name = FF_PUBLICIP_MODULE_NAME, + .name = "PublicIp", .description = "Print your public IP address and related information", + .displayName = { + .en = "Public IP", + .ar = "عنوان IP العام", + .cs = "Veřejná IP", + .de = "Öffentliche IP", + .es = "IP pública", + .fr = "IP publique", + .gl = "IP pública", + .he = "IP ציבורי", + .id = "IP Publik", + .it = "IP pubblica", + .ja = "パブリックIP", + .ko = "공용 IP", + .pl = "Publiczne IP", + .pt = "IP público", + .ru = "Внешний IP", + .tr = "Genel IP", + .uk = "Публічна IP", + .vi = "IP công khai", + .zh_CN = "公网 IP", + .zh_TW = "公網 IP", + }, .initOptions = (void*) ffInitPublicIpOptions, .destroyOptions = (void*) ffDestroyPublicIpOptions, .parseJsonObject = (void*) ffParsePublicIpJsonObject, diff --git a/src/modules/publicip/publicip.h b/src/modules/publicip/publicip.h index 92768cbb6e..65b934bafc 100644 --- a/src/modules/publicip/publicip.h +++ b/src/modules/publicip/publicip.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_PUBLICIP_MODULE_NAME "PublicIp" - void ffPreparePublicIp(FFPublicIPOptions* options); bool ffPrintPublicIp(FFPublicIPOptions* options); diff --git a/src/modules/separator/separator.c b/src/modules/separator/separator.c index bd84d7a40d..60ab8e1cea 100644 --- a/src/modules/separator/separator.c +++ b/src/modules/separator/separator.c @@ -95,11 +95,11 @@ void ffParseSeparatorJsonObject(FFSeparatorOptions* options, yyjson_val* module) } if (unsafe_yyjson_equals_str(key, "length")) { - ffPrintError(FF_SEPARATOR_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "The option length has been renamed to times."); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Separator), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "The option length has been renamed to times."); continue; } - ffPrintError(FF_SEPARATOR_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Separator), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -121,8 +121,30 @@ void ffDestroySeparatorOptions(FFSeparatorOptions* options) { } FFModuleBaseInfo ffSeparatorModuleInfo = { - .name = FF_SEPARATOR_MODULE_NAME, + .name = "Separator", .description = "Print a separator line", + .displayName = { + .en = "Separator", + .ar = "الفاصل", + .cs = "Oddělovač", + .de = "Trennlinie", + .es = "Separador", + .fr = "Séparateur", + .gl = "Separador", + .he = "מפריד", + .id = "Pemisah", + .it = "Separatore", + .ja = "セパレーター", + .ko = "구분선", + .pl = "Separator", + .pt = "Separador", + .ru = "Разделитель", + .tr = "Ayraç", + .uk = "Роздільник", + .vi = "Dấu phân cách", + .zh_CN = "分隔线", + .zh_TW = "分隔線", + }, .initOptions = (void*) ffInitSeparatorOptions, .destroyOptions = (void*) ffDestroySeparatorOptions, .parseJsonObject = (void*) ffParseSeparatorJsonObject, diff --git a/src/modules/separator/separator.h b/src/modules/separator/separator.h index eebf39af36..2ad9c8fabf 100644 --- a/src/modules/separator/separator.h +++ b/src/modules/separator/separator.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_SEPARATOR_MODULE_NAME "Separator" - bool ffPrintSeparator(FFSeparatorOptions* options); void ffInitSeparatorOptions(FFSeparatorOptions* options); void ffDestroySeparatorOptions(FFSeparatorOptions* options); diff --git a/src/modules/shell/shell.c b/src/modules/shell/shell.c index 84db1baca0..849a905bec 100644 --- a/src/modules/shell/shell.c +++ b/src/modules/shell/shell.c @@ -8,12 +8,12 @@ bool ffPrintShell(FFShellOptions* options) { const FFShellResult* result = ffDetectShell(); if (result->processName.length == 0) { - ffPrintError(FF_SHELL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Couldn't detect shell"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Shell), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Couldn't detect shell"); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_SHELL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Shell), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&result->prettyName, stdout); if (result->version.length > 0) { @@ -23,7 +23,7 @@ bool ffPrintShell(FFShellOptions* options) { putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_SHELL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Shell), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result->processName, "process-name"), FF_ARG(result->exe, "exe"), FF_ARG(result->exeName, "exe-name"), @@ -46,7 +46,7 @@ void ffParseShellJsonObject(FFShellOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_SHELL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Shell), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -89,8 +89,30 @@ void ffDestroyShellOptions(FFShellOptions* options) { } FFModuleBaseInfo ffShellModuleInfo = { - .name = FF_SHELL_MODULE_NAME, + .name = "Shell", .description = "Print the current shell name and version", + .displayName = { + .en = "Shell", + .ar = "الصدفة", + .cs = "Shell", + .de = "Shell", + .es = "Shell", + .fr = "Shell", + .gl = "Shell", + .he = "מעטפת", + .id = "Shell", + .it = "Shell", + .ja = "シェル", + .ko = "셸", + .pl = "Powłoka", + .pt = "Shell", + .ru = "Оболочка", + .tr = "Kabuk", + .uk = "Оболонка", + .vi = "Shell", + .zh_CN = "命令行解释器", + .zh_TW = "命令列直譯器", + }, .initOptions = (void*) ffInitShellOptions, .destroyOptions = (void*) ffDestroyShellOptions, .parseJsonObject = (void*) ffParseShellJsonObject, diff --git a/src/modules/shell/shell.h b/src/modules/shell/shell.h index 50a094c80a..c8aec55742 100644 --- a/src/modules/shell/shell.h +++ b/src/modules/shell/shell.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_SHELL_MODULE_NAME "Shell" - bool ffPrintShell(FFShellOptions* options); void ffInitShellOptions(FFShellOptions* options); void ffDestroyShellOptions(FFShellOptions* options); diff --git a/src/modules/sound/sound.c b/src/modules/sound/sound.c index 6bc73cbe7b..dc506efb1d 100644 --- a/src/modules/sound/sound.c +++ b/src/modules/sound/sound.c @@ -8,7 +8,7 @@ static void printDevice(FFSoundOptions* options, const FFSoundDevice* device, uint8_t index) { FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type; if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_SOUND_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Sound), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY str = ffStrbufCreate(); if (!(percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT)) { @@ -54,7 +54,7 @@ static void printDevice(FFSoundOptions* options, const FFSoundDevice* device, ui bool isMain = !!(device->type & FF_SOUND_TYPE_MAIN); bool isActive = !!(device->type & FF_SOUND_TYPE_ACTIVE); - FF_PRINT_FORMAT_CHECKED(FF_SOUND_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Sound), index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(isMain, "is-main"), FF_ARG(isActive, "is-active"), FF_ARG(device->name, "name"), @@ -73,12 +73,12 @@ bool ffPrintSound(FFSoundOptions* options) { const char* error = ffDetectSound(options, &result); if (error) { - ffPrintError(FF_SOUND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Sound), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (result.length == 0) { - ffPrintError(FF_SOUND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No matched sound devices found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Sound), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No matched sound devices found"); return false; } @@ -113,7 +113,7 @@ void ffParseSoundJsonObject(FFSoundOptions* options, yyjson_val* module) { {}, }); if (error) { - ffPrintError(FF_SOUND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Sound), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->soundType = (FFSoundType) value; } @@ -124,7 +124,7 @@ void ffParseSoundJsonObject(FFSoundOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_SOUND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Sound), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -199,8 +199,30 @@ void ffDestroySoundOptions(FFSoundOptions* options) { } FFModuleBaseInfo ffSoundModuleInfo = { - .name = FF_SOUND_MODULE_NAME, + .name = "Sound", .description = "Print sound devices, volume levels, etc", + .displayName = { + .en = "Sound", + .ar = "الصوت", + .cs = "Zvuk", + .de = "Sound", + .es = "Sonido", + .fr = "Son", + .gl = "Son", + .he = "קול", + .id = "Suara", + .it = "Audio", + .ja = "サウンド", + .ko = "사운드", + .pl = "Dźwięk", + .pt = "Som", + .ru = "Звук", + .tr = "Ses", + .uk = "Звук", + .vi = "Âm thanh", + .zh_CN = "声音", + .zh_TW = "聲音", + }, .initOptions = (void*) ffInitSoundOptions, .destroyOptions = (void*) ffDestroySoundOptions, .parseJsonObject = (void*) ffParseSoundJsonObject, diff --git a/src/modules/sound/sound.h b/src/modules/sound/sound.h index a23f8d5e1b..651d640bfb 100644 --- a/src/modules/sound/sound.h +++ b/src/modules/sound/sound.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_SOUND_MODULE_NAME "Sound" - bool ffPrintSound(FFSoundOptions* options); void ffInitSoundOptions(FFSoundOptions* options); void ffDestroySoundOptions(FFSoundOptions* options); diff --git a/src/modules/swap/swap.c b/src/modules/swap/swap.c index cf9d107c60..7fe8b900b1 100644 --- a/src/modules/swap/swap.c +++ b/src/modules/swap/swap.c @@ -11,9 +11,9 @@ void printSwap(FFSwapOptions* options, uint8_t index, uint32_t totalCount, FFSwa if (options->moduleArgs.key.length == 0) { if (storage->name.length > 0) { - ffStrbufSetF(&key, "%s (%s)", FF_SWAP_MODULE_NAME, storage->name.chars); + ffStrbufSetF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Swap), storage->name.chars); } else { - ffStrbufSetS(&key, FF_SWAP_MODULE_NAME); + ffStrbufSetS(&key, FF_MODULE_GET_DISPLAY_NAME(Swap)); } } else { ffStrbufClear(&key); @@ -90,7 +90,7 @@ bool ffPrintSwap(FFSwapOptions* options) { const char* error = ffDetectSwap(&result); if (error) { - ffPrintError(FF_SWAP_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Swap), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } @@ -136,7 +136,7 @@ void ffParseSwapJsonObject(FFSwapOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_SWAP_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Swap), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -182,8 +182,30 @@ void ffDestroySwapOptions(FFSwapOptions* options) { } FFModuleBaseInfo ffSwapModuleInfo = { - .name = FF_SWAP_MODULE_NAME, + .name = "Swap", .description = "Print swap (paging file) space usage", + .displayName = { + .en = "Swap", + .ar = "الذاكرة التبادلية", + .cs = "Odkládací prostor", + .de = "Swap", + .es = "Swap", + .fr = "Swap", + .gl = "Swap", + .he = "זיכרון החלפה", + .id = "Swap", + .it = "Swap", + .ja = "スワップ", + .ko = "스왑", + .pl = "Swap", + .pt = "Swap", + .ru = "Подкачка", + .tr = "Takas", + .uk = "Своп", + .vi = "Swap", + .zh_CN = "交换空间", + .zh_TW = "交換空間", + }, .initOptions = (void*) ffInitSwapOptions, .destroyOptions = (void*) ffDestroySwapOptions, .parseJsonObject = (void*) ffParseSwapJsonObject, diff --git a/src/modules/swap/swap.h b/src/modules/swap/swap.h index e6b9a8b8ee..7c0bf13eb8 100644 --- a/src/modules/swap/swap.h +++ b/src/modules/swap/swap.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_SWAP_MODULE_NAME "Swap" - bool ffPrintSwap(FFSwapOptions* options); void ffInitSwapOptions(FFSwapOptions* options); void ffDestroySwapOptions(FFSwapOptions* options); diff --git a/src/modules/terminal/terminal.c b/src/modules/terminal/terminal.c index 5b2e9305fe..b13e32e9e2 100644 --- a/src/modules/terminal/terminal.c +++ b/src/modules/terminal/terminal.c @@ -8,12 +8,12 @@ bool ffPrintTerminal(FFTerminalOptions* options) { const FFTerminalResult* result = ffDetectTerminal(); if (result->processName.length == 0) { - ffPrintError(FF_TERMINAL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Couldn't detect terminal"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Terminal), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Couldn't detect terminal"); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_TERMINAL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Terminal), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (result->version.length) { printf("%s %s\n", result->prettyName.chars, result->version.chars); @@ -21,7 +21,7 @@ bool ffPrintTerminal(FFTerminalOptions* options) { ffStrbufPutTo(&result->prettyName, stdout); } } else { - FF_PRINT_FORMAT_CHECKED(FF_TERMINAL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Terminal), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result->processName, "process-name"), FF_ARG(result->exe, "exe"), FF_ARG(result->exeName, "exe-name"), @@ -44,7 +44,7 @@ void ffParseTerminalJsonObject(FFTerminalOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_TERMINAL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Terminal), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -83,8 +83,30 @@ void ffDestroyTerminalOptions(FFTerminalOptions* options) { } FFModuleBaseInfo ffTerminalModuleInfo = { - .name = FF_TERMINAL_MODULE_NAME, + .name = "Terminal", .description = "Print the current terminal name and version", + .displayName = { + .en = "Terminal", + .ar = "الطرفية", + .cs = "Terminál", + .de = "Terminal", + .es = "Terminal", + .fr = "Terminal", + .gl = "Terminal", + .he = "טרמינל", + .id = "Terminal", + .it = "Terminale", + .ja = "ターミナル", + .ko = "터미널", + .pl = "Terminal", + .pt = "Terminal", + .ru = "Терминал", + .tr = "Terminal", + .uk = "Термінал", + .vi = "Terminal", + .zh_CN = "终端", + .zh_TW = "終端機", + }, .initOptions = (void*) ffInitTerminalOptions, .destroyOptions = (void*) ffDestroyTerminalOptions, .parseJsonObject = (void*) ffParseTerminalJsonObject, diff --git a/src/modules/terminal/terminal.h b/src/modules/terminal/terminal.h index bb7c9a10d8..c8a2072465 100644 --- a/src/modules/terminal/terminal.h +++ b/src/modules/terminal/terminal.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_TERMINAL_MODULE_NAME "Terminal" - bool ffPrintTerminal(FFTerminalOptions* options); void ffInitTerminalOptions(FFTerminalOptions* options); void ffDestroyTerminalOptions(FFTerminalOptions* options); diff --git a/src/modules/terminalfont/terminalfont.c b/src/modules/terminalfont/terminalfont.c index 87a0837185..ecfb58548a 100644 --- a/src/modules/terminalfont/terminalfont.c +++ b/src/modules/terminalfont/terminalfont.c @@ -4,8 +4,6 @@ #include "detection/terminalfont/terminalfont.h" #include "modules/terminalfont/terminalfont.h" -#define FF_TERMINALFONT_DISPLAY_NAME "Terminal Font" - bool ffPrintTerminalFont(FFTerminalFontOptions* options) { bool success = false; FFTerminalFontResult terminalFont; @@ -14,10 +12,10 @@ bool ffPrintTerminalFont(FFTerminalFontOptions* options) { ffStrbufInit(&terminalFont.error); if (!ffDetectTerminalFont(&terminalFont)) { - ffPrintError(FF_TERMINALFONT_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", terminalFont.error.chars); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(TerminalFont), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", terminalFont.error.chars); } else { if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_TERMINALFONT_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(TerminalFont), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&terminalFont.font.pretty, stdout); if (terminalFont.fallback.pretty.length) { fputs(" / ", stdout); @@ -25,7 +23,7 @@ bool ffPrintTerminalFont(FFTerminalFontOptions* options) { } putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_TERMINALFONT_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(TerminalFont), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(terminalFont.font.pretty, "combined"), FF_ARG(terminalFont.font.name, "name"), FF_ARG(terminalFont.font.size, "size"), @@ -50,7 +48,7 @@ void ffParseTerminalFontJsonObject(FFTerminalFontOptions* options, yyjson_val* m continue; } - ffPrintError(FF_TERMINALFONT_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(TerminalFont), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -105,8 +103,30 @@ void ffDestroyTerminalFontOptions(FFTerminalFontOptions* options) { } FFModuleBaseInfo ffTerminalFontModuleInfo = { - .name = FF_TERMINALFONT_MODULE_NAME, + .name = "TerminalFont", .description = "Print the font name and size used by the current terminal", + .displayName = { + .en = "Terminal Font", + .ar = "خط الطرفية", + .cs = "Písmo terminálu", + .de = "Terminalschriftart", + .es = "Fuente del terminal", + .fr = "Police du terminal", + .gl = "Fonte do terminal", + .he = "גופן טרמינל", + .id = "Font Terminal", + .it = "Carattere del terminale", + .ja = "ターミナルフォント", + .ko = "터미널 글꼴", + .pl = "Czcionka terminala", + .pt = "Fonte do terminal", + .ru = "Шрифт терминала", + .tr = "Terminal Yazı Tipi", + .uk = "Шрифт терміналу", + .vi = "Phông chữ Terminal", + .zh_CN = "终端字体", + .zh_TW = "終端機字型", + }, .initOptions = (void*) ffInitTerminalFontOptions, .destroyOptions = (void*) ffDestroyTerminalFontOptions, .parseJsonObject = (void*) ffParseTerminalFontJsonObject, diff --git a/src/modules/terminalfont/terminalfont.h b/src/modules/terminalfont/terminalfont.h index 04987a61ea..a32e2e0b43 100644 --- a/src/modules/terminalfont/terminalfont.h +++ b/src/modules/terminalfont/terminalfont.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_TERMINALFONT_MODULE_NAME "TerminalFont" - bool ffPrintTerminalFont(FFTerminalFontOptions* options); void ffInitTerminalFontOptions(FFTerminalFontOptions* options); void ffDestroyTerminalFontOptions(FFTerminalFontOptions* options); diff --git a/src/modules/terminalsize/terminalsize.c b/src/modules/terminalsize/terminalsize.c index 088b97fcef..cfea3bd2bf 100644 --- a/src/modules/terminalsize/terminalsize.c +++ b/src/modules/terminalsize/terminalsize.c @@ -4,18 +4,16 @@ #include "detection/terminalsize/terminalsize.h" #include "modules/terminalsize/terminalsize.h" -#define FF_TERMINALSIZE_DISPLAY_NAME "Terminal Size" - bool ffPrintTerminalSize(FFTerminalSizeOptions* options) { FFTerminalSizeResult result = {}; if (!ffDetectTerminalSize(&result)) { - ffPrintError(FF_TERMINALSIZE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Failed to detect terminal size"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(TerminalSize), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Failed to detect terminal size"); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_TERMINALSIZE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(TerminalSize), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); printf("%u columns x %u rows", result.columns, result.rows); if (result.width != 0 && result.height != 0) { @@ -24,7 +22,7 @@ bool ffPrintTerminalSize(FFTerminalSizeOptions* options) { putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_TERMINALSIZE_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(TerminalSize), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.rows, "rows"), FF_ARG(result.columns, "columns"), FF_ARG(result.width, "width"), @@ -42,7 +40,7 @@ void ffParseTerminalSizeJsonObject(FFTerminalSizeOptions* options, yyjson_val* m continue; } - ffPrintError(FF_TERMINALSIZE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(TerminalSize), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -76,8 +74,30 @@ void ffDestroyTerminalSizeOptions(FFTerminalSizeOptions* options) { } FFModuleBaseInfo ffTerminalSizeModuleInfo = { - .name = FF_TERMINALSIZE_MODULE_NAME, + .name = "TerminalSize", .description = "Print the current terminal size", + .displayName = { + .en = "Terminal Size", + .ar = "حجم الطرفية", + .cs = "Velikost terminálu", + .de = "Terminalgröße", + .es = "Tamaño del terminal", + .fr = "Taille du terminal", + .gl = "Tamaño do terminal", + .he = "גודל טרמינל", + .id = "Ukuran Terminal", + .it = "Dimensioni terminale", + .ja = "ターミナルサイズ", + .ko = "터미널 크기", + .pl = "Rozmiar terminala", + .pt = "Tamanho do terminal", + .ru = "Размер терминала", + .tr = "Terminal Boyutu", + .uk = "Розмір терміналу", + .vi = "Kích thước Terminal", + .zh_CN = "终端大小", + .zh_TW = "終端機大小", + }, .initOptions = (void*) ffInitTerminalSizeOptions, .destroyOptions = (void*) ffDestroyTerminalSizeOptions, .parseJsonObject = (void*) ffParseTerminalSizeJsonObject, diff --git a/src/modules/terminalsize/terminalsize.h b/src/modules/terminalsize/terminalsize.h index 3cd05d5b23..0745b6a007 100644 --- a/src/modules/terminalsize/terminalsize.h +++ b/src/modules/terminalsize/terminalsize.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_TERMINALSIZE_MODULE_NAME "TerminalSize" - bool ffPrintTerminalSize(FFTerminalSizeOptions* options); void ffInitTerminalSizeOptions(FFTerminalSizeOptions* options); void ffDestroyTerminalSizeOptions(FFTerminalSizeOptions* options); diff --git a/src/modules/terminaltheme/terminaltheme.c b/src/modules/terminaltheme/terminaltheme.c index 3d5239b2f6..a3daf4f428 100644 --- a/src/modules/terminaltheme/terminaltheme.c +++ b/src/modules/terminaltheme/terminaltheme.c @@ -6,18 +6,16 @@ #include -#define FF_TERMINALTHEME_DISPLAY_NAME "Terminal Theme" - bool ffPrintTerminalTheme(FFTerminalThemeOptions* options) { FFTerminalThemeResult result = {}; if (!ffDetectTerminalTheme(&result, false)) { - ffPrintError(FF_TERMINALTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Failed to detect terminal theme"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(TerminalTheme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Failed to detect terminal theme"); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_TERMINALTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(TerminalTheme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); printf("#%02" PRIX16 "%02" PRIX16 "%02" PRIX16 " (FG) - #%02" PRIX16 "%02" PRIX16 "%02" PRIX16 " (BG) [%s]\n", result.fg.r, result.fg.g, @@ -32,7 +30,7 @@ bool ffPrintTerminalTheme(FFTerminalThemeOptions* options) { const char* bgType = result.bg.dark ? "Dark" : "Light"; snprintf(fg, ARRAY_SIZE(fg), "#%02" PRIX16 "%02" PRIX16 "%02" PRIX16, result.fg.r, result.fg.g, result.fg.b); snprintf(bg, ARRAY_SIZE(bg), "#%02" PRIX16 "%02" PRIX16 "%02" PRIX16, result.bg.r, result.bg.g, result.bg.b); - FF_PRINT_FORMAT_CHECKED(FF_TERMINALTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(TerminalTheme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(fg, "fg-color"), FF_ARG(fgType, "fg-type"), FF_ARG(bg, "bg-color"), @@ -51,7 +49,7 @@ void ffParseTerminalThemeJsonObject(FFTerminalThemeOptions* options, yyjson_val* continue; } - ffPrintError(FF_TERMINALTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(TerminalTheme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -93,8 +91,30 @@ void ffDestroyTerminalThemeOptions(FFTerminalThemeOptions* options) { } FFModuleBaseInfo ffTerminalThemeModuleInfo = { - .name = FF_TERMINALTHEME_MODULE_NAME, + .name = "TerminalTheme", .description = "Print the current terminal theme (foreground and background colors)", + .displayName = { + .en = "Terminal Theme", + .ar = "سمة الطرفية", + .cs = "Motiv terminálu", + .de = "Terminalthema", + .es = "Tema del terminal", + .fr = "Thème du terminal", + .gl = "Tema do terminal", + .he = "ערכת נושא של טרמינל", + .id = "Tema Terminal", + .it = "Tema del terminale", + .ja = "ターミナルテーマ", + .ko = "터미널 테마", + .pl = "Motyw terminala", + .pt = "Tema do terminal", + .ru = "Тема терминала", + .tr = "Terminal Teması", + .uk = "Тема терміналу", + .vi = "Chủ đề Terminal", + .zh_CN = "终端主题", + .zh_TW = "終端機主題", + }, .initOptions = (void*) ffInitTerminalThemeOptions, .destroyOptions = (void*) ffDestroyTerminalThemeOptions, .parseJsonObject = (void*) ffParseTerminalThemeJsonObject, diff --git a/src/modules/terminaltheme/terminaltheme.h b/src/modules/terminaltheme/terminaltheme.h index db120f74ce..e48bca0831 100644 --- a/src/modules/terminaltheme/terminaltheme.h +++ b/src/modules/terminaltheme/terminaltheme.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_TERMINALTHEME_MODULE_NAME "TerminalTheme" - bool ffPrintTerminalTheme(FFTerminalThemeOptions* options); void ffInitTerminalThemeOptions(FFTerminalThemeOptions* options); void ffDestroyTerminalThemeOptions(FFTerminalThemeOptions* options); diff --git a/src/modules/theme/theme.c b/src/modules/theme/theme.c index f51a4aac64..1f79179308 100644 --- a/src/modules/theme/theme.c +++ b/src/modules/theme/theme.c @@ -12,12 +12,12 @@ bool ffPrintTheme(FFThemeOptions* options) { const char* error = ffDetectTheme(&result); if (error) { - ffPrintError(FF_THEME_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Theme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_THEME_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Theme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (result.theme1.length) { ffStrbufWriteTo(&result.theme1, stdout); } @@ -29,7 +29,7 @@ bool ffPrintTheme(FFThemeOptions* options) { } putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_THEME_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Theme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.theme1, "theme1"), FF_ARG(result.theme2, "theme2"), })); @@ -48,7 +48,7 @@ void ffParseThemeJsonObject(FFThemeOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_THEME_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Theme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -87,8 +87,30 @@ void ffDestroyThemeOptions(FFThemeOptions* options) { } FFModuleBaseInfo ffThemeModuleInfo = { - .name = FF_THEME_MODULE_NAME, + .name = "Theme", .description = "Print the current desktop environment theme", + .displayName = { + .en = "Theme", + .ar = "السمة", + .cs = "Motiv", + .de = "Thema", + .es = "Tema", + .fr = "Thème", + .gl = "Tema", + .he = "ערכת נושא", + .id = "Tema", + .it = "Tema", + .ja = "テーマ", + .ko = "테마", + .pl = "Motyw", + .pt = "Tema", + .ru = "Тема", + .tr = "Tema", + .uk = "Тема", + .vi = "Chủ đề", + .zh_CN = "主题", + .zh_TW = "主題", + }, .initOptions = (void*) ffInitThemeOptions, .destroyOptions = (void*) ffDestroyThemeOptions, .parseJsonObject = (void*) ffParseThemeJsonObject, diff --git a/src/modules/theme/theme.h b/src/modules/theme/theme.h index 56ba49c812..83278d764d 100644 --- a/src/modules/theme/theme.h +++ b/src/modules/theme/theme.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_THEME_MODULE_NAME "Theme" - bool ffPrintTheme(FFThemeOptions* options); void ffInitThemeOptions(FFThemeOptions* options); void ffDestroyThemeOptions(FFThemeOptions* options); diff --git a/src/modules/title/title.c b/src/modules/title/title.c index ce877caed7..b5a7acacee 100644 --- a/src/modules/title/title.c +++ b/src/modules/title/title.c @@ -45,7 +45,7 @@ bool ffPrintTitle(FFTitleOptions* options) { } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_TITLE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Title), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&userNameColored, stdout); ffStrbufWriteTo(&atColored, stdout); @@ -68,7 +68,7 @@ bool ffPrintTitle(FFTitleOptions* options) { ffStrbufTrimRight(&cwdTilde, '/'); } - FF_PRINT_FORMAT_CHECKED(FF_TITLE_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Title), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(instance.state.platform.userName, "user-name"), FF_ARG(hostName, "host-name"), FF_ARG(instance.state.platform.homeDir, "home-dir"), @@ -124,7 +124,7 @@ void ffParseTitleJsonObject(FFTitleOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_TITLE_MODULE_NAME, 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Title), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -176,8 +176,30 @@ void ffDestroyTitleOptions(FFTitleOptions* options) { } FFModuleBaseInfo ffTitleModuleInfo = { - .name = FF_TITLE_MODULE_NAME, + .name = "Title", .description = "Print the title, including your username and hostname", + .displayName = { + .en = "Title", + .ar = "العنوان", + .cs = "Titulek", + .de = "Titel", + .es = "Título", + .fr = "Titre", + .gl = "Título", + .he = "כותרת", + .id = "Judul", + .it = "Titolo", + .ja = "タイトル", + .ko = "제목", + .pl = "Tytuł", + .pt = "Título", + .ru = "Заголовок", + .tr = "Başlık", + .uk = "Заголовок", + .vi = "Tiêu đề", + .zh_CN = "标题", + .zh_TW = "標題", + }, .initOptions = (void*) ffInitTitleOptions, .destroyOptions = (void*) ffDestroyTitleOptions, .parseJsonObject = (void*) ffParseTitleJsonObject, diff --git a/src/modules/title/title.h b/src/modules/title/title.h index 83c9496543..ce44461fa1 100644 --- a/src/modules/title/title.h +++ b/src/modules/title/title.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_TITLE_MODULE_NAME "Title" - bool ffPrintTitle(FFTitleOptions* options); void ffInitTitleOptions(FFTitleOptions* options); void ffDestroyTitleOptions(FFTitleOptions* options); diff --git a/src/modules/top/option.h b/src/modules/top/option.h new file mode 100644 index 0000000000..0c3ff406d1 --- /dev/null +++ b/src/modules/top/option.h @@ -0,0 +1,27 @@ +#pragma once + +#include "common/option.h" +#include "common/percent.h" + +typedef enum FFTopTypes: uint8_t { + FF_TOP_TYPE_CPU = 1 << 0, + FF_TOP_TYPE_MEMORY = 1 << 1, + FF_TOP_TYPE_DISK_READ = 1 << 2, + FF_TOP_TYPE_DISK_WRITE = 1 << 3, + FF_TOP_TYPE_START_TIME = 1 << 4, + FF_TOP_TYPE_THREADS = 1 << 5, + + FF_TOP_TYPE_DISK = FF_TOP_TYPE_DISK_READ | FF_TOP_TYPE_DISK_WRITE, +} FFTopTypes; + +typedef struct FFTopOptions { + FFModuleArgs moduleArgs; + FFTopTypes sort; + FFTopTypes showTypes; // bitfields + uint32_t nProcesses; + uint32_t waitTime; + FFPercentageModuleConfig percent; + bool compact; +} FFTopOptions; + +static_assert(sizeof(FFTopOptions) <= FF_OPTION_MAX_SIZE, "FFTopOptions size exceeds maximum allowed size"); diff --git a/src/modules/top/top.c b/src/modules/top/top.c new file mode 100644 index 0000000000..9edbc394d8 --- /dev/null +++ b/src/modules/top/top.c @@ -0,0 +1,309 @@ +#include "common/jsonconfig.h" +#include "common/percent.h" +#include "common/printing.h" +#include "common/size.h" +#include "detection/top/top.h" +#include "modules/top/top.h" + +static void printTopResult(FFTopOptions* options, uint32_t index, uint32_t total, FFTopProcessResult* process) { + FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type; + if (options->moduleArgs.outputFormat.length == 0) { + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Top), total == 1 ? 0 : (uint8_t) (index + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + + FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate(); + if (process->name.length == 0) { + ffStrbufAppendS(&output, ""); + } else { + ffStrbufAppend(&output, &process->name); + } + ffStrbufAppendF(&output, " (%u)", process->pid); + if ((options->showTypes & FF_TOP_TYPE_CPU) && (percentType & FF_PERCENTAGE_TYPE_NUM_BIT)) { + ffStrbufAppendS(&output, " - CPU "); + ffPercentAppendNum(&output, process->cpuPercent, options->percent, false, &options->moduleArgs); + } + if (!(percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT)) { + if (options->showTypes & FF_TOP_TYPE_MEMORY) { + ffStrbufAppendS(&output, " - MEM "); + ffSizeAppendNum(process->memBytes, &output); + } + if (options->showTypes & FF_TOP_TYPE_DISK) { + ffStrbufAppendS(&output, " - DSK "); + ffSizeAppendNum(process->bytesRead, &output); + ffStrbufAppendS(&output, "/s / "); + ffSizeAppendNum(process->bytesWritten, &output); + ffStrbufAppendS(&output, "/s"); + } + if (options->showTypes & FF_TOP_TYPE_THREADS) { + ffStrbufAppendF(&output, " - THR %u", process->threads); + } + } + ffStrbufPutTo(&output, stdout); + } else { + FF_STRBUF_AUTO_DESTROY cpuFormatted = ffStrbufCreate(); + if (percentType & FF_PERCENTAGE_TYPE_NUM_BIT) { + ffPercentAppendNum(&cpuFormatted, process->cpuPercent, options->percent, false, &options->moduleArgs); + } + FF_STRBUF_AUTO_DESTROY memFormatted = ffStrbufCreate(); + ffSizeAppendNum(process->memBytes, &memFormatted); + FF_STRBUF_AUTO_DESTROY diskReadFormatted = ffStrbufCreate(); + ffSizeAppendNum(process->bytesRead, &diskReadFormatted); + ffStrbufAppendS(&diskReadFormatted, "/s"); + FF_STRBUF_AUTO_DESTROY diskWriteFormatted = ffStrbufCreate(); + ffSizeAppendNum(process->bytesWritten, &diskWriteFormatted); + ffStrbufAppendS(&diskWriteFormatted, "/s"); + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Top), total == 1 ? 0 : (uint8_t) (index + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){ + FF_ARG(process->name, "name"), + FF_ARG(process->pid, "pid"), + FF_ARG(process->cpuPercent, "cpu"), + FF_ARG(process->memBytes, "mem"), + FF_ARG(process->bytesRead, "disk-read"), + FF_ARG(process->bytesWritten, "disk-write"), + FF_ARG(process->threads, "threads"), + FF_ARG(cpuFormatted, "cpu-percentage"), + FF_ARG(memFormatted, "mem-formatted"), + FF_ARG(diskReadFormatted, "disk-read-formatted"), + FF_ARG(diskWriteFormatted, "disk-write-formatted"), + })); + } +} + +bool ffPrintTop(FFTopOptions* options) { + FF_LIST_AUTO_DESTROY results = ffListCreate(); + const char* error = ffDetectTopProcesses(options, &results); + if (error) { + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + return false; + } + + if (!options->compact) { + FF_LIST_FOR_EACH (FFTopProcessResult, process, results) { + printTopResult(options, (uint32_t) (process - (FFTopProcessResult*) results.data), results.length, process); + } + } else { + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + FF_LIST_FOR_EACH (FFTopProcessResult, process, results) { + if ((void*) process != results.data) { + putchar(' '); + } + ffStrbufWriteTo(&process->name, stdout); + } + putchar('\n'); + } + + FF_LIST_FOR_EACH (FFTopProcessResult, item, results) { + ffStrbufDestroy(&item->name); + } + return true; +} + +void ffParseTopJsonObject(FFTopOptions* options, yyjson_val* module) { + yyjson_val *key, *val; + size_t idx, max; + yyjson_obj_foreach (module, idx, max, key, val) { + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) { + continue; + } + if (unsafe_yyjson_equals_str(key, "sort")) { + int value; + const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]){ + { "cpu", FF_TOP_TYPE_CPU }, + { "memory", FF_TOP_TYPE_MEMORY }, + { "disk-read", FF_TOP_TYPE_DISK_READ }, + { "disk-write", FF_TOP_TYPE_DISK_WRITE }, + { "start-time", FF_TOP_TYPE_START_TIME }, + {}, + }); + if (error) { + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + } else { + options->sort = (FFTopTypes) value; + } + continue; + } else if (unsafe_yyjson_equals_str(key, "showTypes")) { + if (yyjson_is_arr(val)) { + options->showTypes = 0; + yyjson_val* item; + size_t aidx, amax; + yyjson_arr_foreach (val, aidx, amax, item) { + int value; + const char* error = ffJsonConfigParseEnum(item, &value, (FFKeyValuePair[]){ + { "cpu", FF_TOP_TYPE_CPU }, + { "memory", FF_TOP_TYPE_MEMORY }, + { "disk", FF_TOP_TYPE_DISK }, + { "threads", FF_TOP_TYPE_THREADS }, + {}, + }); + if (error == nullptr) { + options->showTypes |= (FFTopTypes) value; + } + if (error) { + options->showTypes = FF_TOP_TYPE_CPU | FF_TOP_TYPE_MEMORY | FF_TOP_TYPE_DISK; + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + break; + } + } + } else { + int value; + const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]){ + { "cpu", FF_TOP_TYPE_CPU }, + { "memory", FF_TOP_TYPE_MEMORY }, + { "disk", FF_TOP_TYPE_DISK }, + { "threads", FF_TOP_TYPE_THREADS }, + {}, + }); + if (error == nullptr) { + options->showTypes = (FFTopTypes) value; + } + if (error) { + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + } + } + continue; + } else if (unsafe_yyjson_equals_str(key, "processes")) { + options->nProcesses = (uint32_t) yyjson_get_uint(val); + } else if (unsafe_yyjson_equals_str(key, "waitTime")) { + options->waitTime = (uint32_t) yyjson_get_uint(val); + } else if (unsafe_yyjson_equals_str(key, "compact")) { + options->compact = yyjson_get_bool(val); + } else if (ffPercentParseJsonObject(key, val, &options->percent)) { + continue; + } else { + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + } + } +} + +void ffGenerateTopJsonConfig(FFTopOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { + ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs); + yyjson_mut_obj_add_str(doc, module, "type", "top"); + yyjson_mut_val* showTypes = yyjson_mut_arr(doc); + if (options->showTypes & FF_TOP_TYPE_CPU) { + yyjson_mut_arr_add_str(doc, showTypes, "cpu"); + } + if (options->showTypes & FF_TOP_TYPE_MEMORY) { + yyjson_mut_arr_add_str(doc, showTypes, "memory"); + } + if (options->showTypes & FF_TOP_TYPE_DISK) { + yyjson_mut_arr_add_str(doc, showTypes, "disk"); + } + if (options->showTypes & FF_TOP_TYPE_THREADS) { + yyjson_mut_arr_add_str(doc, showTypes, "threads"); + } + yyjson_mut_obj_add_val(doc, module, "showTypes", showTypes); + if (options->sort == FF_TOP_TYPE_CPU) { + yyjson_mut_obj_add_str(doc, module, "sort", "cpu"); + } else if (options->sort == FF_TOP_TYPE_MEMORY) { + yyjson_mut_obj_add_str(doc, module, "sort", "memory"); + } else if (options->sort == FF_TOP_TYPE_DISK_READ) { + yyjson_mut_obj_add_str(doc, module, "sort", "disk-read"); + } else if (options->sort == FF_TOP_TYPE_DISK_WRITE) { + yyjson_mut_obj_add_str(doc, module, "sort", "disk-write"); + } else if (options->sort == FF_TOP_TYPE_START_TIME) { + yyjson_mut_obj_add_str(doc, module, "sort", "start-time"); + } else if (options->sort == FF_TOP_TYPE_THREADS) { + yyjson_mut_obj_add_str(doc, module, "sort", "threads"); + } + yyjson_mut_obj_add_uint(doc, module, "processes", options->nProcesses); + yyjson_mut_obj_add_uint(doc, module, "waitTime", options->waitTime); + yyjson_mut_obj_add_bool(doc, module, "compact", options->compact); + ffPercentGenerateJsonConfig(doc, module, options->percent); +} + +bool ffGenerateTopJsonResult(FFTopOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { + FF_LIST_AUTO_DESTROY results = ffListCreate(); + const char* error = ffDetectTopProcesses(options, &results); + if (error) { + yyjson_mut_obj_add_str(doc, module, "error", error); + return false; + } + yyjson_mut_val* array = yyjson_mut_obj_add_arr(doc, module, "result"); + FF_LIST_FOR_EACH (FFTopProcessResult, process, results) { + yyjson_mut_val* item = yyjson_mut_arr_add_obj(doc, array); + yyjson_mut_obj_add_strbuf(doc, item, "name", &process->name); + yyjson_mut_obj_add_uint(doc, item, "pid", process->pid); + if (options->showTypes & FF_TOP_TYPE_CPU) { + yyjson_mut_obj_add_real(doc, item, "cpuPercent", process->cpuPercent); + } + if (options->showTypes & FF_TOP_TYPE_MEMORY) { + yyjson_mut_obj_add_uint(doc, item, "mem", process->memBytes); + } + if (options->showTypes & FF_TOP_TYPE_DISK) { + yyjson_mut_obj_add_uint(doc, item, "bytesRead", process->bytesRead); + yyjson_mut_obj_add_uint(doc, item, "bytesWritten", process->bytesWritten); + } + if (options->showTypes & FF_TOP_TYPE_THREADS) { + yyjson_mut_obj_add_uint(doc, item, "threads", process->threads); + } + yyjson_mut_obj_add_uint(doc, item, "startTime", process->startTime); + } + + FF_LIST_FOR_EACH (FFTopProcessResult, item, results) { + ffStrbufDestroy(&item->name); + } + return true; +} + +void ffInitTopOptions(FFTopOptions* options) { + ffOptionInitModuleArg(&options->moduleArgs, "󰍛"); + options->sort = FF_TOP_TYPE_CPU; + options->showTypes = FF_TOP_TYPE_CPU | FF_TOP_TYPE_MEMORY +#if !__GNU__ && !__HAIKU__ + | FF_TOP_TYPE_DISK +#endif + ; + options->nProcesses = 5; + options->waitTime = 500; + options->compact = false; + options->percent = (FFPercentageModuleConfig){ 50, 80, 0 }; +} + +void ffDestroyTopOptions(FFTopOptions* options) { + ffOptionDestroyModuleArg(&options->moduleArgs); +} + +FFModuleBaseInfo ffTopModuleInfo = { + .name = "Top", + .description = "Print processes with the highest CPU, memory or disk I/O usage", + .displayName = { + .en = "Top Processes", + .ar = "أعلى العمليات", + .cs = "Nejvytíženější procesy", + .de = "Top-Prozesse", + .es = "Procesos principales", + .fr = "Processus principaux", + .gl = "Procesos principais", + .he = "התהליכים המובילים", + .id = "Proses teratas", + .it = "Processi principali", + .ja = "上位プロセス", + .ko = "상위 프로세스", + .pl = "Najaktywniejsze procesy", + .pt = "Processos principais", + .ru = "Топ процессов", + .tr = "En yoğun işlemler", + .uk = "Найактивніші процеси", + .vi = "Tiến trình hàng đầu", + .zh_CN = "进程排行", + .zh_TW = "進程排行", + }, + .initOptions = (void*) ffInitTopOptions, + .destroyOptions = (void*) ffDestroyTopOptions, + .parseJsonObject = (void*) ffParseTopJsonObject, + .printModule = (void*) ffPrintTop, + .generateJsonResult = (void*) ffGenerateTopJsonResult, + .generateJsonConfig = (void*) ffGenerateTopJsonConfig, + .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]){ + { "Process name", "name" }, + { "Process ID", "pid" }, + { "CPU usage", "cpu" }, + { "Memory usage (RSS) in bytes", "mem" }, + { "Disk read bytes per second (0 if unsupported)", "disk-read" }, + { "Disk write bytes per second (0 if unsupported)", "disk-write" }, + { "Number of threads", "threads" }, + { "CPU usage percentage", "cpu-percentage" }, + { "Memory usage (RSS) formatted", "mem-formatted" }, + { "Disk read formatted", "disk-read-formatted" }, + { "Disk write formatted", "disk-write-formatted" }, + })), + .defaultOrder = 36, +}; diff --git a/src/modules/top/top.h b/src/modules/top/top.h new file mode 100644 index 0000000000..48c13d6df3 --- /dev/null +++ b/src/modules/top/top.h @@ -0,0 +1,11 @@ +#pragma once + +#include "option.h" + +void ffPrepareTopProcesses(FFTopTypes showTypes); + +bool ffPrintTop(FFTopOptions* options); +void ffInitTopOptions(FFTopOptions* options); +void ffDestroyTopOptions(FFTopOptions* options); + +extern FFModuleBaseInfo ffTopModuleInfo; diff --git a/src/modules/tpm/tpm.c b/src/modules/tpm/tpm.c index 6596d6261c..8902efe70a 100644 --- a/src/modules/tpm/tpm.c +++ b/src/modules/tpm/tpm.c @@ -12,19 +12,19 @@ bool ffPrintTPM(FFTPMOptions* options) { const char* error = ffDetectTPM(&result); if (error) { - ffPrintError(FF_TPM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(TPM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_TPM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(TPM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (result.description.length > 0) { ffStrbufPutTo(&result.description, stdout); } else { ffStrbufPutTo(&result.version, stdout); } } else { - FF_PRINT_FORMAT_CHECKED(FF_TPM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(TPM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.version, "version"), FF_ARG(result.description, "description"), })); @@ -44,7 +44,7 @@ void ffParseTPMJsonObject(FFTPMOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_TPM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(TPM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -83,8 +83,30 @@ void ffDestroyTPMOptions(FFTPMOptions* options) { } FFModuleBaseInfo ffTPMModuleInfo = { - .name = FF_TPM_MODULE_NAME, + .name = "TPM", .description = "Print information about the Trusted Platform Module (TPM) security device", + .displayName = { + .en = "TPM", + .ar = "TPM", + .cs = "TPM", + .de = "TPM", + .es = "TPM", + .fr = "TPM", + .gl = "TPM", + .he = "TPM", + .id = "TPM", + .it = "TPM", + .ja = "TPM", + .ko = "TPM", + .pl = "TPM", + .pt = "TPM", + .ru = "TPM", + .tr = "TPM", + .uk = "TPM", + .vi = "TPM", + .zh_CN = "TPM", + .zh_TW = "TPM", + }, .initOptions = (void*) ffInitTPMOptions, .destroyOptions = (void*) ffDestroyTPMOptions, .parseJsonObject = (void*) ffParseTPMJsonObject, diff --git a/src/modules/tpm/tpm.h b/src/modules/tpm/tpm.h index 215f8c98aa..72ce6aac04 100644 --- a/src/modules/tpm/tpm.h +++ b/src/modules/tpm/tpm.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_TPM_MODULE_NAME "TPM" - bool ffPrintTPM(FFTPMOptions* options); void ffInitTPMOptions(FFTPMOptions* options); void ffDestroyTPMOptions(FFTPMOptions* options); diff --git a/src/modules/uptime/uptime.c b/src/modules/uptime/uptime.c index 41ac4526a1..25b8d1f45e 100644 --- a/src/modules/uptime/uptime.c +++ b/src/modules/uptime/uptime.c @@ -12,7 +12,7 @@ bool ffPrintUptime(FFUptimeOptions* options) { const char* error = ffDetectUptime(&result); if (error) { - ffPrintError(FF_UPTIME_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Uptime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } @@ -21,7 +21,7 @@ bool ffPrintUptime(FFUptimeOptions* options) { ffDurationAppendNum((uptime + 500) / 1000, &buffer); if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_UPTIME_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Uptime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&buffer, stdout); } else { uint32_t milliseconds = (uint32_t) (uptime % 1000); @@ -36,7 +36,7 @@ bool ffPrintUptime(FFUptimeOptions* options) { FFTimeGetAgeResult age = ffTimeGetAge(result.bootTime, ffTimeGetNow()); - FF_PRINT_FORMAT_CHECKED(FF_UPTIME_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(days, "days"), FF_ARG(hours, "hours"), FF_ARG(minutes, "minutes"), FF_ARG(seconds, "seconds"), FF_ARG(milliseconds, "milliseconds"), { FF_ARG_TYPE_STRING, ffTimeToShortStr(result.bootTime), "boot-time" }, FF_ARG(age.years, "years"), FF_ARG(age.daysOfYear, "days-of-year"), FF_ARG(age.yearsFraction, "years-fraction"), FF_ARG(buffer, "formatted") })); + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Uptime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(days, "days"), FF_ARG(hours, "hours"), FF_ARG(minutes, "minutes"), FF_ARG(seconds, "seconds"), FF_ARG(milliseconds, "milliseconds"), { FF_ARG_TYPE_STRING, ffTimeToShortStr(result.bootTime), "boot-time" }, FF_ARG(age.years, "years"), FF_ARG(age.daysOfYear, "days-of-year"), FF_ARG(age.yearsFraction, "years-fraction"), FF_ARG(buffer, "formatted") })); } return true; @@ -50,7 +50,7 @@ void ffParseUptimeJsonObject(FFUptimeOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_UPTIME_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Uptime), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -83,8 +83,30 @@ void ffDestroyUptimeOptions(FFUptimeOptions* options) { } FFModuleBaseInfo ffUptimeModuleInfo = { - .name = FF_UPTIME_MODULE_NAME, + .name = "Uptime", .description = "Print how long the system has been running", + .displayName = { + .en = "Uptime", + .ar = "مدة التشغيل", + .cs = "Doba běhu", + .de = "Betriebszeit", + .es = "Tiempo de actividad", + .fr = "Temps de fonctionnement", + .gl = "Tempo de actividade", + .he = "זמן פעילות", + .id = "Waktu Aktif", + .it = "Tempo di attività", + .ja = "稼働時間", + .ko = "업타임", + .pl = "Czas pracy", + .pt = "Tempo de atividade", + .ru = "Время работы", + .tr = "Çalışma Süresi", + .uk = "Час роботи", + .vi = "Thời gian hoạt động", + .zh_CN = "运行时间", + .zh_TW = "運行時間", + }, .initOptions = (void*) ffInitUptimeOptions, .destroyOptions = (void*) ffDestroyUptimeOptions, .parseJsonObject = (void*) ffParseUptimeJsonObject, diff --git a/src/modules/uptime/uptime.h b/src/modules/uptime/uptime.h index dec5f95e5c..c9db7dcd37 100644 --- a/src/modules/uptime/uptime.h +++ b/src/modules/uptime/uptime.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_UPTIME_MODULE_NAME "Uptime" - bool ffPrintUptime(FFUptimeOptions* options); void ffInitUptimeOptions(FFUptimeOptions* options); void ffDestroyUptimeOptions(FFUptimeOptions* options); diff --git a/src/modules/users/users.c b/src/modules/users/users.c index fbf1ffaa8e..6f24527ed2 100644 --- a/src/modules/users/users.c +++ b/src/modules/users/users.c @@ -13,18 +13,18 @@ bool ffPrintUsers(FFUsersOptions* options) { const char* error = ffDetectUsers(options, &users); if (error) { - ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Users), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (users.length == 0) { - ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "Unable to detect any users"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Users), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "Unable to detect any users"); return false; } if (options->moduleArgs.outputFormat.length == 0) { if (options->compact) { - ffPrintLogoAndKey(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Users), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); for (uint32_t i = 0; i < users.length; ++i) { @@ -39,7 +39,7 @@ bool ffPrintUsers(FFUsersOptions* options) { for (uint32_t i = 0; i < users.length; ++i) { FFUserResult* user = FF_LIST_GET(FFUserResult, users, i); - ffPrintLogoAndKey(FF_USERS_MODULE_NAME, users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Users), 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) { @@ -71,7 +71,7 @@ bool ffPrintUsers(FFUsersOptions* options) { FFTimeGetAgeResult age = ffTimeGetAge(user->loginTime, ffTimeGetNow()); - FF_PRINT_FORMAT_CHECKED(FF_USERS_MODULE_NAME, users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Users), users.length == 1 ? 0 : (uint8_t) (i + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(user->name, "name"), FF_ARG(user->hostName, "host-name"), FF_ARG(user->sessionName, "session-name"), @@ -121,7 +121,7 @@ void ffParseUsersJsonObject(FFUsersOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_USERS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Users), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -180,8 +180,30 @@ void ffDestroyUsersOptions(FFUsersOptions* options) { } FFModuleBaseInfo ffUsersModuleInfo = { - .name = FF_USERS_MODULE_NAME, + .name = "Users", .description = "Print users who are currently logged in", + .displayName = { + .en = "Users", + .ar = "المستخدمون", + .cs = "Uživatelé", + .de = "Benutzer", + .es = "Usuarios", + .fr = "Utilisateurs", + .gl = "Usuarios", + .he = "משתמשים", + .id = "Pengguna", + .it = "Utenti", + .ja = "ユーザー", + .ko = "사용자", + .pl = "Użytkownicy", + .pt = "Usuários", + .ru = "Пользователи", + .tr = "Kullanıcılar", + .uk = "Користувачі", + .vi = "Người dùng", + .zh_CN = "用户", + .zh_TW = "使用者", + }, .initOptions = (void*) ffInitUsersOptions, .destroyOptions = (void*) ffDestroyUsersOptions, .parseJsonObject = (void*) ffParseUsersJsonObject, diff --git a/src/modules/users/users.h b/src/modules/users/users.h index 44c8b01d7e..2567b395c4 100644 --- a/src/modules/users/users.h +++ b/src/modules/users/users.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_USERS_MODULE_NAME "Users" - bool ffPrintUsers(FFUsersOptions* options); void ffInitUsersOptions(FFUsersOptions* options); void ffDestroyUsersOptions(FFUsersOptions* options); diff --git a/src/modules/version/version.c b/src/modules/version/version.c index fa3b59fe7f..13e6f52b02 100644 --- a/src/modules/version/version.c +++ b/src/modules/version/version.c @@ -9,7 +9,7 @@ bool ffPrintVersion(FFVersionOptions* options) { FFVersionResult* result = &ffVersionResult; if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_VERSION_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Version), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); printf("%s %s%s%s (%s)\n", result->projectName, result->version, result->versionTweak, result->debugMode ? "-debug" : "", result->architecture); } else { FFLibcResult libcResult; @@ -23,7 +23,7 @@ bool ffPrintVersion(FFVersionOptions* options) { } const char* buildType = result->debugMode ? "debug" : "release"; - FF_PRINT_FORMAT_CHECKED(FF_VERSION_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Version), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result->projectName, "project-name"), FF_ARG(result->version, "version"), FF_ARG(result->versionTweak, "version-tweak"), @@ -48,7 +48,7 @@ void ffParseVersionJsonObject(FFVersionOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_VERSION_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Version), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -94,8 +94,30 @@ void ffDestroyVersionOptions(FFVersionOptions* options) { } FFModuleBaseInfo ffVersionModuleInfo = { - .name = FF_VERSION_MODULE_NAME, + .name = "Version", .description = "Print the Fastfetch version and build information", + .displayName = { + .en = "Version", + .ar = "الإصدار", + .cs = "Verze", + .de = "Version", + .es = "Versión", + .fr = "Version", + .gl = "Versión", + .he = "גרסה", + .id = "Versi", + .it = "Versione", + .ja = "バージョン", + .ko = "버전", + .pl = "Wersja", + .pt = "Versão", + .ru = "Версия", + .tr = "Sürüm", + .uk = "Версія", + .vi = "Phiên bản", + .zh_CN = "版本", + .zh_TW = "版本", + }, .initOptions = (void*) ffInitVersionOptions, .destroyOptions = (void*) ffDestroyVersionOptions, .parseJsonObject = (void*) ffParseVersionJsonObject, diff --git a/src/modules/version/version.h b/src/modules/version/version.h index add5e1198f..5cfaf1f14c 100644 --- a/src/modules/version/version.h +++ b/src/modules/version/version.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_VERSION_MODULE_NAME "Version" - bool ffPrintVersion(FFVersionOptions* options); void ffInitVersionOptions(FFVersionOptions* options); void ffDestroyVersionOptions(FFVersionOptions* options); diff --git a/src/modules/vulkan/vulkan.c b/src/modules/vulkan/vulkan.c index 03123b7ae6..938c67e23e 100644 --- a/src/modules/vulkan/vulkan.c +++ b/src/modules/vulkan/vulkan.c @@ -9,12 +9,12 @@ bool ffPrintVulkan(FFVulkanOptions* options) { const FFVulkanResult* vulkan = ffDetectVulkan(); if (vulkan->error) { - ffPrintError(FF_VULKAN_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", vulkan->error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Vulkan), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", vulkan->error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_VULKAN_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Vulkan), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); if (vulkan->apiVersion.length == 0 && vulkan->driver.length == 0) { ffStrbufWriteTo(&vulkan->instanceVersion, stdout); @@ -35,7 +35,7 @@ bool ffPrintVulkan(FFVulkanOptions* options) { putchar('\n'); } } else { - FF_PRINT_FORMAT_CHECKED(FF_VULKAN_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Vulkan), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(vulkan->driver, "driver"), FF_ARG(vulkan->apiVersion, "api-version"), FF_ARG(vulkan->conformanceVersion, "conformance-version"), @@ -54,7 +54,7 @@ void ffParseVulkanJsonObject(FFVulkanOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_VULKAN_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Vulkan), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -132,8 +132,30 @@ void ffDestroyVulkanOptions(FFVulkanOptions* options) { } FFModuleBaseInfo ffVulkanModuleInfo = { - .name = FF_VULKAN_MODULE_NAME, + .name = "Vulkan", .description = "Print the highest Vulkan version supported by the GPU", + .displayName = { + .en = "Vulkan", + .ar = "Vulkan", + .cs = "Vulkan", + .de = "Vulkan", + .es = "Vulkan", + .fr = "Vulkan", + .gl = "Vulkan", + .he = "Vulkan", + .id = "Vulkan", + .it = "Vulkan", + .ja = "Vulkan", + .ko = "Vulkan", + .pl = "Vulkan", + .pt = "Vulkan", + .ru = "Vulkan", + .tr = "Vulkan", + .uk = "Vulkan", + .vi = "Vulkan", + .zh_CN = "Vulkan", + .zh_TW = "Vulkan", + }, .initOptions = (void*) ffInitVulkanOptions, .destroyOptions = (void*) ffDestroyVulkanOptions, .parseJsonObject = (void*) ffParseVulkanJsonObject, diff --git a/src/modules/vulkan/vulkan.h b/src/modules/vulkan/vulkan.h index c838028c5d..141ad99334 100644 --- a/src/modules/vulkan/vulkan.h +++ b/src/modules/vulkan/vulkan.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_VULKAN_MODULE_NAME "Vulkan" - bool ffPrintVulkan(FFVulkanOptions* options); void ffInitVulkanOptions(FFVulkanOptions* options); void ffDestroyVulkanOptions(FFVulkanOptions* options); diff --git a/src/modules/wallpaper/wallpaper.c b/src/modules/wallpaper/wallpaper.c index b5fab48f50..38c7b204a5 100644 --- a/src/modules/wallpaper/wallpaper.c +++ b/src/modules/wallpaper/wallpaper.c @@ -21,15 +21,15 @@ bool ffPrintWallpaper(FFWallpaperOptions* options) { : fullpath.chars + index; if (error) { - ffPrintError(FF_WALLPAPER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Wallpaper), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_WALLPAPER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Wallpaper), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); puts(filename); } else { - FF_PRINT_FORMAT_CHECKED(FF_WALLPAPER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Wallpaper), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(filename, "file-name"), FF_ARG(fullpath, "full-path"), })); @@ -46,7 +46,7 @@ void ffParseWallpaperJsonObject(FFWallpaperOptions* options, yyjson_val* module) continue; } - ffPrintError(FF_WALLPAPER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Wallpaper), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -75,8 +75,30 @@ void ffDestroyWallpaperOptions(FFWallpaperOptions* options) { } FFModuleBaseInfo ffWallpaperModuleInfo = { - .name = FF_WALLPAPER_MODULE_NAME, + .name = "Wallpaper", .description = "Print the file path of the current wallpaper", + .displayName = { + .en = "Wallpaper", + .ar = "الخلفية", + .cs = "Tapeta", + .de = "Hintergrundbild", + .es = "Fondo de pantalla", + .fr = "Fond d'écran", + .gl = "Fondo de escritorio", + .he = "טפט", + .id = "Wallpaper", + .it = "Sfondo", + .ja = "壁紙", + .ko = "배경화면", + .pl = "Tapeta", + .pt = "Papel de parede", + .ru = "Обои", + .tr = "Duvar Kağıdı", + .uk = "Шпалери", + .vi = "Hình nền", + .zh_CN = "壁纸", + .zh_TW = "桌布", + }, .initOptions = (void*) ffInitWallpaperOptions, .destroyOptions = (void*) ffDestroyWallpaperOptions, .parseJsonObject = (void*) ffParseWallpaperJsonObject, diff --git a/src/modules/wallpaper/wallpaper.h b/src/modules/wallpaper/wallpaper.h index c3e72911be..eed2fa21c8 100644 --- a/src/modules/wallpaper/wallpaper.h +++ b/src/modules/wallpaper/wallpaper.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_WALLPAPER_MODULE_NAME "Wallpaper" - bool ffPrintWallpaper(FFWallpaperOptions* options); void ffInitWallpaperOptions(FFWallpaperOptions* options); void ffDestroyWallpaperOptions(FFWallpaperOptions* options); diff --git a/src/modules/weather/weather.c b/src/modules/weather/weather.c index 152cb057bd..17753251c1 100644 --- a/src/modules/weather/weather.c +++ b/src/modules/weather/weather.c @@ -9,15 +9,15 @@ bool ffPrintWeather(FFWeatherOptions* options) { const char* error = ffDetectWeather(options, &result); if (error) { - ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Weather), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Weather), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufPutTo(&result, stdout); } else { - FF_PRINT_FORMAT_CHECKED(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Weather), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result, "result"), })); } @@ -48,7 +48,7 @@ void ffParseWeatherJsonObject(FFWeatherOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_WEATHER_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Weather), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -92,8 +92,30 @@ void ffDestroyWeatherOptions(FFWeatherOptions* options) { } FFModuleBaseInfo ffWeatherModuleInfo = { - .name = FF_WEATHER_MODULE_NAME, + .name = "Weather", .description = "Print weather information", + .displayName = { + .en = "Weather", + .ar = "الطقس", + .cs = "Počasí", + .de = "Wetter", + .es = "Tiempo", + .fr = "Météo", + .gl = "Tempo", + .he = "מזג אוויר", + .id = "Cuaca", + .it = "Meteo", + .ja = "天気", + .ko = "날씨", + .pl = "Pogoda", + .pt = "Clima", + .ru = "Погода", + .tr = "Hava Durumu", + .uk = "Погода", + .vi = "Thời tiết", + .zh_CN = "天气", + .zh_TW = "天氣", + }, .initOptions = (void*) ffInitWeatherOptions, .destroyOptions = (void*) ffDestroyWeatherOptions, .parseJsonObject = (void*) ffParseWeatherJsonObject, diff --git a/src/modules/weather/weather.h b/src/modules/weather/weather.h index f28a4c2332..e2b8394eaf 100644 --- a/src/modules/weather/weather.h +++ b/src/modules/weather/weather.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_WEATHER_MODULE_NAME "Weather" - void ffPrepareWeather(FFWeatherOptions* options); bool ffPrintWeather(FFWeatherOptions* options); diff --git a/src/modules/wifi/wifi.c b/src/modules/wifi/wifi.c index 0e9df22c98..c48e46f517 100644 --- a/src/modules/wifi/wifi.c +++ b/src/modules/wifi/wifi.c @@ -9,11 +9,11 @@ bool ffPrintWifi(FFWifiOptions* options) { const char* error = ffDetectWifi(&result); if (error) { - ffPrintError(FF_WIFI_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Wifi), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (!result.length) { - ffPrintError(FF_WIFI_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No Wifi interfaces found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Wifi), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No Wifi interfaces found"); return false; } @@ -46,7 +46,7 @@ bool ffPrintWifi(FFWifiOptions* options) { } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_WIFI_MODULE_NAME, moduleIndex, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Wifi), moduleIndex, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); if (item->conn.ssid.length) { @@ -95,7 +95,7 @@ bool ffPrintWifi(FFWifiOptions* options) { ffPercentAppendBar(&percentBar, item->conn.signalQuality, options->percent, &options->moduleArgs); } - FF_PRINT_FORMAT_CHECKED(FF_WIFI_MODULE_NAME, moduleIndex, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Wifi), moduleIndex, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(item->inf.description, "inf-desc"), FF_ARG(item->inf.status, "inf-status"), FF_ARG(item->conn.status, "status"), @@ -108,6 +108,7 @@ bool ffPrintWifi(FFWifiOptions* options) { FF_ARG(item->conn.security, "security"), FF_ARG(percentBar, "signal-quality-bar"), FF_ARG(item->conn.channel, "channel"), + FF_ARG(item->conn.channelWidth, "channel-width"), FF_ARG(bandStr, "band"), })); } @@ -138,7 +139,7 @@ void ffParseWifiJsonObject(FFWifiOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_WIFI_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Wifi), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -186,6 +187,7 @@ bool ffGenerateWifiJsonResult([[maybe_unused]] FFWifiOptions* options, yyjson_mu yyjson_mut_obj_add_null(doc, conn, "txRate"); } yyjson_mut_obj_add_uint(doc, conn, "channel", wifi->conn.channel); + yyjson_mut_obj_add_uint(doc, conn, "channelWidth", wifi->conn.channelWidth); yyjson_mut_obj_add_uint(doc, conn, "frequency", wifi->conn.frequency); } @@ -213,8 +215,30 @@ void ffDestroyWifiOptions(FFWifiOptions* options) { } FFModuleBaseInfo ffWifiModuleInfo = { - .name = FF_WIFI_MODULE_NAME, + .name = "Wifi", .description = "Print connected Wi-Fi info (SSID, connection and security protocol)", + .displayName = { + .en = "Wi-Fi", + .ar = "واي فاي", + .cs = "Wi-Fi", + .de = "WLAN", + .es = "Wi-Fi", + .fr = "Wi-Fi", + .gl = "Wi-Fi", + .he = "Wi-Fi", + .id = "Wi-Fi", + .it = "Wi-Fi", + .ja = "Wi-Fi", + .ko = "와이파이", + .pl = "Wi-Fi", + .pt = "Wi-Fi", + .ru = "Wi-Fi", + .tr = "Wi-Fi", + .uk = "Wi-Fi", + .vi = "Wi-Fi", + .zh_CN = "无线网络", + .zh_TW = "無線網路", + }, .initOptions = (void*) ffInitWifiOptions, .destroyOptions = (void*) ffDestroyWifiOptions, .parseJsonObject = (void*) ffParseWifiJsonObject, @@ -234,6 +258,7 @@ FFModuleBaseInfo ffWifiModuleInfo = { { "Connection Security algorithm", "security" }, { "Connection signal quality (percentage bar)", "signal-quality-bar" }, { "Connection channel number", "channel" }, + { "Connection channel width in MHz", "channel-width" }, { "Connection channel band in GHz", "band" }, })), .defaultOrder = 51, diff --git a/src/modules/wifi/wifi.h b/src/modules/wifi/wifi.h index 506e1e22af..dd640815c8 100644 --- a/src/modules/wifi/wifi.h +++ b/src/modules/wifi/wifi.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_WIFI_MODULE_NAME "Wifi" - bool ffPrintWifi(FFWifiOptions* options); void ffInitWifiOptions(FFWifiOptions* options); void ffDestroyWifiOptions(FFWifiOptions* options); diff --git a/src/modules/wm/wm.c b/src/modules/wm/wm.c index 5e23826e1c..c094e45c6b 100644 --- a/src/modules/wm/wm.c +++ b/src/modules/wm/wm.c @@ -9,7 +9,7 @@ bool ffPrintWM(FFWMOptions* options) { const FFDisplayServerResult* result = ffConnectDisplayServer(); if (result->wmPrettyName.length == 0) { - ffPrintError(FF_WM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No WM found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(WM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No WM found"); return false; } @@ -24,7 +24,7 @@ bool ffPrintWM(FFWMOptions* options) { } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_WM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(WM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); ffStrbufWriteTo(&result->wmPrettyName, stdout); @@ -47,7 +47,7 @@ bool ffPrintWM(FFWMOptions* options) { putchar('\n'); } else { - FF_PRINT_FORMAT_CHECKED(FF_WM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(WM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result->wmProcessName, "process-name"), FF_ARG(result->wmPrettyName, "pretty-name"), FF_ARG(result->wmProtocolName, "protocol-name"), @@ -72,7 +72,7 @@ void ffParseWMJsonObject(FFWMOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_WM_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(WM), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -120,8 +120,30 @@ void ffDestroyWMOptions(FFWMOptions* options) { } FFModuleBaseInfo ffWMModuleInfo = { - .name = FF_WM_MODULE_NAME, + .name = "WM", .description = "Print the window manager name and version", + .displayName = { + .en = "Window Manager", + .ar = "مدير النوافذ", + .cs = "Správce oken", + .de = "Fenstermanager", + .es = "Gestor de ventanas", + .fr = "Gestionnaire de fenêtres", + .gl = "Xestor de xanelas", + .he = "מנהל חלונות", + .id = "Manajer Jendela", + .it = "Gestore finestre", + .ja = "ウィンドウマネージャー", + .ko = "윈도우 관리자", + .pl = "Menedżer okien", + .pt = "Gerenciador de janelas", + .ru = "Менеджер окон", + .tr = "Pencere Yöneticisi", + .uk = "Менеджер вікон", + .vi = "Trình quản lý cửa sổ", + .zh_CN = "窗口管理器", + .zh_TW = "視窗管理員", + }, .initOptions = (void*) ffInitWMOptions, .destroyOptions = (void*) ffDestroyWMOptions, .parseJsonObject = (void*) ffParseWMJsonObject, diff --git a/src/modules/wm/wm.h b/src/modules/wm/wm.h index 4819d73946..5e085ef015 100644 --- a/src/modules/wm/wm.h +++ b/src/modules/wm/wm.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_WM_MODULE_NAME "WM" - bool ffPrintWM(FFWMOptions* options); void ffInitWMOptions(FFWMOptions* options); void ffDestroyWMOptions(FFWMOptions* options); diff --git a/src/modules/wmtheme/wmtheme.c b/src/modules/wmtheme/wmtheme.c index f1ab4105f7..89caff3888 100644 --- a/src/modules/wmtheme/wmtheme.c +++ b/src/modules/wmtheme/wmtheme.c @@ -4,20 +4,18 @@ #include "detection/wmtheme/wmtheme.h" #include "modules/wmtheme/wmtheme.h" -#define FF_WMTHEME_DISPLAY_NAME "WM Theme" - bool ffPrintWMTheme(FFWMThemeOptions* options) { FF_STRBUF_AUTO_DESTROY themeOrError = ffStrbufCreate(); if (!ffDetectWmTheme(&themeOrError)) { - ffPrintError(FF_WMTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", themeOrError.chars); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(WMTheme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", themeOrError.chars); return false; } if (options->moduleArgs.outputFormat.length == 0) { - ffPrintLogoAndKey(FF_WMTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(WMTheme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); puts(themeOrError.chars); } else { - FF_PRINT_FORMAT_CHECKED(FF_WMTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(WMTheme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(themeOrError, "result"), })); } @@ -33,7 +31,7 @@ void ffParseWMThemeJsonObject(FFWMThemeOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_WMTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(WMTheme), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -61,8 +59,30 @@ void ffDestroyWMThemeOptions(FFWMThemeOptions* options) { } FFModuleBaseInfo ffWMThemeModuleInfo = { - .name = FF_WMTHEME_MODULE_NAME, + .name = "WMTheme", .description = "Print the current window manager theme", + .displayName = { + .en = "WM Theme", + .ar = "سمة مدير النوافذ", + .cs = "Motiv správce oken", + .de = "Fenstermanager-Thema", + .es = "Tema del gestor de ventanas", + .fr = "Thème du gestionnaire de fenêtres", + .gl = "Tema do xestor de xanelas", + .he = "ערכת נושא של מנהל חלונות", + .id = "Tema Manajer Jendela", + .it = "Tema del gestore finestre", + .ja = "ウィンドウマネージャのテーマ", + .ko = "윈도우 관리자 테마", + .pl = "Motyw menedżera okien", + .pt = "Tema do gerenciador de janelas", + .ru = "Тема менеджера окон", + .tr = "WM Teması", + .uk = "Тема менеджера вікон", + .vi = "Chủ đề trình quản lý cửa sổ", + .zh_CN = "窗口管理器主题", + .zh_TW = "視窗管理員主題", + }, .initOptions = (void*) ffInitWMThemeOptions, .destroyOptions = (void*) ffDestroyWMThemeOptions, .parseJsonObject = (void*) ffParseWMThemeJsonObject, diff --git a/src/modules/wmtheme/wmtheme.h b/src/modules/wmtheme/wmtheme.h index 5142ca42cc..46c8299c37 100644 --- a/src/modules/wmtheme/wmtheme.h +++ b/src/modules/wmtheme/wmtheme.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_WMTHEME_MODULE_NAME "WMTheme" - bool ffPrintWMTheme(FFWMThemeOptions* options); void ffInitWMThemeOptions(FFWMThemeOptions* options); void ffDestroyWMThemeOptions(FFWMThemeOptions* options); diff --git a/src/modules/zpool/zpool.c b/src/modules/zpool/zpool.c index 7e5f03a52d..c974f96306 100644 --- a/src/modules/zpool/zpool.c +++ b/src/modules/zpool/zpool.c @@ -11,9 +11,9 @@ static void printZpool(FFZpoolOptions* options, FFZpoolResult* result, uint8_t i FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); if (options->moduleArgs.key.length == 0) { if (result->name.length > 0) { - ffStrbufSetF(&buffer, "%s (%s)", FF_ZPOOL_MODULE_NAME, result->name.chars); + ffStrbufSetF(&buffer, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Zpool), result->name.chars); } else { - ffStrbufSetS(&buffer, FF_ZPOOL_MODULE_NAME); + ffStrbufSetS(&buffer, FF_MODULE_GET_DISPLAY_NAME(Zpool)); } } else { ffStrbufClear(&buffer); @@ -105,11 +105,11 @@ bool ffPrintZpool(FFZpoolOptions* options) { const char* error = ffDetectZpool(&results); if (error) { - ffPrintError(FF_ZPOOL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Zpool), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); return false; } if (results.length == 0) { - ffPrintError(FF_ZPOOL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "No zpool found"); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Zpool), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "No zpool found"); return false; } @@ -138,7 +138,7 @@ void ffParseZpoolJsonObject(FFZpoolOptions* options, yyjson_val* module) { continue; } - ffPrintError(FF_ZPOOL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Zpool), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } @@ -192,8 +192,30 @@ void ffDestroyZpoolOptions(FFZpoolOptions* options) { } FFModuleBaseInfo ffZpoolModuleInfo = { - .name = FF_ZPOOL_MODULE_NAME, + .name = "Zpool", .description = "Print ZFS storage pools", + .displayName = { + .en = "Zpool", + .ar = "Zpool", + .cs = "Zpool", + .de = "Zpool", + .es = "Zpool", + .fr = "Zpool", + .gl = "Zpool", + .he = "Zpool", + .id = "Zpool", + .it = "Zpool", + .ja = "Zpool", + .ko = "Zpool", + .pl = "Zpool", + .pt = "Zpool", + .ru = "Zpool", + .tr = "Zpool", + .uk = "Zpool", + .vi = "Zpool", + .zh_CN = "Zpool", + .zh_TW = "Zpool", + }, .initOptions = (void*) ffInitZpoolOptions, .destroyOptions = (void*) ffDestroyZpoolOptions, .parseJsonObject = (void*) ffParseZpoolJsonObject, diff --git a/src/modules/zpool/zpool.h b/src/modules/zpool/zpool.h index 35706bb356..3faed199fa 100644 --- a/src/modules/zpool/zpool.h +++ b/src/modules/zpool/zpool.h @@ -2,8 +2,6 @@ #include "option.h" -#define FF_ZPOOL_MODULE_NAME "Zpool" - bool ffPrintZpool(FFZpoolOptions* options); void ffInitZpoolOptions(FFZpoolOptions* options); void ffDestroyZpoolOptions(FFZpoolOptions* options); diff --git a/src/options/display.c b/src/options/display.c index d515e92c1b..3dbfce1ecf 100644 --- a/src/options/display.c +++ b/src/options/display.c @@ -8,9 +8,99 @@ #if !FF_MODULE_DISABLE_TERMINALTHEME #include "detection/terminaltheme/terminaltheme.h" #endif +#if !FF_MODULE_DISABLE_LOCALE + #include "detection/locale/locale.h" +#endif #include +// @returns offsetof(FFModuleDisplayName, ) +// @see src/common/option.h +static uint32_t optionParseLanguageString(FFstrbuf* language) { + const bool detected = language->length == 0; + + if (detected) { +#if !FF_MODULE_DISABLE_LOCALE + const char* error = ffDetectLocale(language); + if (__builtin_expect(error != nullptr, false)) { + fprintf(stderr, "ffDetectLocale: %s\n", error); + exit(477); + } +#else + fputs("Error: --key-language requires a value\n", stderr); + exit(477); +#endif + } + + ffStrbufSubstrBeforeFirstC(language, '.'); // Remove the encoding part of the locale + #if __linux__ + ffStrbufSubstrBeforeFirstC(language, '@'); // Remove the modifier part of the locale (Linux only) + #endif + ffStrbufLowerCase(language); + + if (language->length <= 1) { + if (language->chars[0] == 'c') { + return offsetof(FFModuleDisplayName, en); + } else { + goto error; + } + } else if (language->length > 2) { + if (language->chars[2] == '-') { + language->chars[2] = '_'; + } else if (__builtin_expect(language->chars[2] != '_', false)) { + goto error; + } + } + + // The language code is expected to be in the format of "en", "en_US", "de", "de_DE", etc. + // First try to match the full language code, then try to match just the first two characters (the language part). + // If no match is found, report a language not supported error, unless the language was auto-detected. + // en_US -> offsetof(FFModuleDisplayName, en) + // en -> offsetof(FFModuleDisplayName, en) + // zh_CN -> offsetof(FFModuleDisplayName, zh_CN) + // zh -> Error: Language 'zh' is not supported. This is an intentional design decision, as the language code 'zh' could refer to either Simplified or Traditional Chinese. + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmultichar" + switch (language->chars[0] << 8 | language->chars[1]) { + case 'en': return offsetof(FFModuleDisplayName, en); + case 'ar': return offsetof(FFModuleDisplayName, ar); + case 'cs': return offsetof(FFModuleDisplayName, cs); + case 'de': return offsetof(FFModuleDisplayName, de); + case 'es': return offsetof(FFModuleDisplayName, es); + case 'fr': return offsetof(FFModuleDisplayName, fr); + case 'gl': return offsetof(FFModuleDisplayName, gl); + case 'he': return offsetof(FFModuleDisplayName, he); + case 'id': return offsetof(FFModuleDisplayName, id); + case 'it': return offsetof(FFModuleDisplayName, it); + case 'ja': return offsetof(FFModuleDisplayName, ja); + case 'ko': return offsetof(FFModuleDisplayName, ko); + case 'pl': return offsetof(FFModuleDisplayName, pl); + case 'pt': return offsetof(FFModuleDisplayName, pt); + case 'ru': return offsetof(FFModuleDisplayName, ru); + case 'tr': return offsetof(FFModuleDisplayName, tr); + case 'uk': return offsetof(FFModuleDisplayName, uk); + case 'vi': return offsetof(FFModuleDisplayName, vi); + case 'zh': { + if (ffStrbufEqualS(language, "zh_cn")) { + return offsetof(FFModuleDisplayName, zh_CN); + } else if (ffStrbufEqualS(language, "zh_tw")) { + return offsetof(FFModuleDisplayName, zh_TW); + } + goto error; + } + } +#pragma GCC diagnostic pop + +error: + // The user asked for the system locale, not for this specific language. Most locales have no + // translation, so falling back to English is friendlier than aborting fastfetch entirely. + if (detected) return offsetof(FFModuleDisplayName, en); + + fprintf(stderr, "Error: Language '%s' is not supported\n", language->chars); + exit(477); +} + const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_val* root, yyjson_val** pkey) { yyjson_val* object = yyjson_obj_get(root, "display"); if (!object) { @@ -483,6 +573,17 @@ const char* ffOptionsParseDisplayJsonConfig(FFOptionsDisplay* options, yyjson_va if (paddingLeft) { options->keyPaddingLeft = (uint16_t) yyjson_get_uint(paddingLeft); } + + yyjson_val* language = yyjson_obj_get(val, "language"); + if (language) { + FF_STRBUF_AUTO_DESTROY languageBuffer = ffStrbufCreate(); + if (yyjson_is_str(language)) { + ffStrbufSetJsonVal(&languageBuffer, language); + } else if (!yyjson_is_null(language)) { + return "display.key.language must be a string or null"; + } + options->keyLanguage = optionParseLanguageString(&languageBuffer); + } } else { return "display.key must be an object"; } @@ -651,6 +752,9 @@ bool ffOptionsParseDisplayCommandLine(FFOptionsDisplay* options, const char* key options->keyType = (FFModuleKeyType) ffOptionParseEnum(key, value, (FFKeyValuePair[]) { { "none", FF_MODULE_KEY_TYPE_NONE }, { "string", FF_MODULE_KEY_TYPE_STRING }, { "icon", FF_MODULE_KEY_TYPE_ICON }, { "both", FF_MODULE_KEY_TYPE_BOTH }, { "both-0", FF_MODULE_KEY_TYPE_BOTH_0 }, { "both-1", FF_MODULE_KEY_TYPE_BOTH_1 }, { "both-2", FF_MODULE_KEY_TYPE_BOTH_2 }, { "both-3", FF_MODULE_KEY_TYPE_BOTH_3 }, { "both-4", FF_MODULE_KEY_TYPE_BOTH_4 }, {} }); } else if (ffStrEqualsIgnCase(subkey, "padding-left")) { options->keyPaddingLeft = (uint16_t) ffOptionParseUInt32(key, value); + } else if (ffStrEqualsIgnCase(subkey, "language")) { + FF_STRBUF_AUTO_DESTROY language = ffStrbufCreateS(value); + options->keyLanguage = optionParseLanguageString(&language); } else { return false; } @@ -867,6 +971,7 @@ void ffOptionsInitDisplay(FFOptionsDisplay* options) { options->keyWidth = 0; options->keyPaddingLeft = 0; options->keyType = FF_MODULE_KEY_TYPE_STRING; + options->keyLanguage = offsetof(FFModuleDisplayName, en); options->tempUnit = FF_TEMPERATURE_UNIT_DEFAULT; options->tempNdigits = 1; @@ -1168,6 +1273,69 @@ void ffOptionsGenerateDisplayJsonConfig(FFdata* data, FFOptionsDisplay* options) } yyjson_mut_obj_add_uint(doc, key, "paddingLeft", options->keyPaddingLeft); + + switch (options->keyLanguage) { + case offsetof(FFModuleDisplayName, en): + yyjson_mut_obj_add_str(doc, key, "language", "en"); + break; + case offsetof(FFModuleDisplayName, ar): + yyjson_mut_obj_add_str(doc, key, "language", "ar"); + break; + case offsetof(FFModuleDisplayName, cs): + yyjson_mut_obj_add_str(doc, key, "language", "cs"); + break; + case offsetof(FFModuleDisplayName, de): + yyjson_mut_obj_add_str(doc, key, "language", "de"); + break; + case offsetof(FFModuleDisplayName, es): + yyjson_mut_obj_add_str(doc, key, "language", "es"); + break; + case offsetof(FFModuleDisplayName, fr): + yyjson_mut_obj_add_str(doc, key, "language", "fr"); + break; + case offsetof(FFModuleDisplayName, gl): + yyjson_mut_obj_add_str(doc, key, "language", "gl"); + break; + case offsetof(FFModuleDisplayName, he): + yyjson_mut_obj_add_str(doc, key, "language", "he"); + break; + case offsetof(FFModuleDisplayName, id): + yyjson_mut_obj_add_str(doc, key, "language", "id"); + break; + case offsetof(FFModuleDisplayName, it): + yyjson_mut_obj_add_str(doc, key, "language", "it"); + break; + case offsetof(FFModuleDisplayName, ja): + yyjson_mut_obj_add_str(doc, key, "language", "ja"); + break; + case offsetof(FFModuleDisplayName, ko): + yyjson_mut_obj_add_str(doc, key, "language", "ko"); + break; + case offsetof(FFModuleDisplayName, pl): + yyjson_mut_obj_add_str(doc, key, "language", "pl"); + break; + case offsetof(FFModuleDisplayName, pt): + yyjson_mut_obj_add_str(doc, key, "language", "pt"); + break; + case offsetof(FFModuleDisplayName, ru): + yyjson_mut_obj_add_str(doc, key, "language", "ru"); + break; + case offsetof(FFModuleDisplayName, tr): + yyjson_mut_obj_add_str(doc, key, "language", "tr"); + break; + case offsetof(FFModuleDisplayName, uk): + yyjson_mut_obj_add_str(doc, key, "language", "uk"); + break; + case offsetof(FFModuleDisplayName, vi): + yyjson_mut_obj_add_str(doc, key, "language", "vi"); + break; + case offsetof(FFModuleDisplayName, zh_CN): + yyjson_mut_obj_add_str(doc, key, "language", "zh_CN"); + break; + case offsetof(FFModuleDisplayName, zh_TW): + yyjson_mut_obj_add_str(doc, key, "language", "zh_TW"); + break; + } } { diff --git a/src/options/display.h b/src/options/display.h index 8e234b08a0..2706a3e96a 100644 --- a/src/options/display.h +++ b/src/options/display.h @@ -80,6 +80,7 @@ typedef struct FFOptionsDisplay { uint8_t percentWidth; bool noBuffer; FFModuleKeyType keyType; + uint32_t keyLanguage; // Offset of FFModuleDisplayName member uint16_t keyWidth; uint16_t keyPaddingLeft; int8_t freqNdigits; diff --git a/tests/strbuf.c b/tests/strbuf.c index a8e698cafc..d1ca642658 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -23,6 +23,8 @@ int shouldNotBeCalled(int c) { exit(1); } +const FFstrbuf testCreateStatic = FF_STRBUF_STATIC("TEST"); + int main(void) { FFstrbuf strbuf;