mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 09:58:03 +02:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eff8514536 | |||
| f37a5a2543 | |||
| 9a9f82b59e | |||
| 29818d2dca | |||
| 222fe7e3f9 | |||
| 7d0256fb85 | |||
| a70c37fdaf | |||
| 66e9e38f28 | |||
| 5d290cc3d5 | |||
| 2e542bea64 | |||
| c708b6f5cd | |||
| 6a275da8b1 | |||
| ecee5694fb | |||
| 4e78f1b703 | |||
| 62f78ca60c | |||
| 00af2d1df2 | |||
| 1128a75d8c | |||
| ce61783c4b | |||
| 20597bff00 | |||
| caf64f5e4d | |||
| 90a6e90b19 | |||
| e9f719f37a | |||
| 8c98bf1953 | |||
| 1c1136ebd1 | |||
| 2787d97973 | |||
| 2e06a73102 | |||
| 37c198d997 | |||
| 37afe5a79d | |||
| 0ebf09754b | |||
| 0b1bc5b527 | |||
| 3d107764fa | |||
| 4863a173e4 | |||
| 8ea4468eec | |||
| b0ba084b5e | |||
| 43d01f26ea | |||
| b5ffc32b80 | |||
| 4ff931c06f | |||
| 6eef800ea2 | |||
| b9e4d04d65 | |||
| 9c791b2c12 | |||
| 76b6768141 | |||
| 42002a6240 | |||
| ef7f5aeac3 | |||
| d5214af189 | |||
| a33d6326f1 | |||
| df99241424 | |||
| 5b1ad1008d |
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
|
||||
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
|
||||
with:
|
||||
operating_system: dragonflybsd
|
||||
version: '6.4.2'
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
|
||||
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
|
||||
with:
|
||||
operating_system: freebsd
|
||||
version: '15.1'
|
||||
|
||||
@@ -18,10 +18,10 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
|
||||
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
|
||||
with:
|
||||
operating_system: haiku
|
||||
version: 'r1beta5'
|
||||
version: 'r1beta6'
|
||||
shell: bash
|
||||
cpu_count: 4
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: inputs.arch == 'amd64'
|
||||
uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
|
||||
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@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
|
||||
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
run: cmake --version
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 CMAKE_PREFIX_PATH=/home/linuxbrew/.linuxbrew PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
run: CC=gcc-13 CMAKE_PREFIX_PATH=/home/linuxbrew/.linuxbrew PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_VERSION=5.10.0 -DCMAKE_SYSTEM_PROCESSOR=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -26,13 +26,30 @@ jobs:
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: install required packages
|
||||
- name: install required packages with MacPorts # Homebrew dropped Intel support
|
||||
if: inputs.arch == 'amd64'
|
||||
run: |
|
||||
curl -fsSL https://github.com/macports/macports-base/releases/download/v2.12.6/MacPorts-2.12.6-15-Sequoia.pkg -o "$RUNNER_TEMP/MacPorts.pkg"
|
||||
sudo installer -pkg "$RUNNER_TEMP/MacPorts.pkg" -target /
|
||||
echo /opt/local/bin >> "$GITHUB_PATH"
|
||||
echo /opt/local/sbin >> "$GITHUB_PATH"
|
||||
export PATH=/opt/local/bin:/opt/local/sbin:$PATH
|
||||
sudo port selfupdate
|
||||
sudo port install vulkan-loader vulkan-headers MoltenVK ImageMagick7 chafa lua quickjs-ng jq
|
||||
|
||||
- name: install required packages with Homebrew
|
||||
if: inputs.arch == 'aarch64'
|
||||
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 jq
|
||||
|
||||
- name: configure project
|
||||
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
run: |
|
||||
if [ "${{ inputs.arch }}" = "amd64" ]; then
|
||||
export CMAKE_PREFIX_PATH=/opt/local
|
||||
export PKG_CONFIG_PATH=/opt/local/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}
|
||||
fi
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
|
||||
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
|
||||
with:
|
||||
operating_system: netbsd
|
||||
version: '10.1'
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
|
||||
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
|
||||
with:
|
||||
operating_system: omnios
|
||||
version: 'r151058'
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Start VM
|
||||
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
|
||||
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
|
||||
with:
|
||||
operating_system: openbsd
|
||||
version: '7.9'
|
||||
|
||||
@@ -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:
|
||||
@@ -204,7 +203,7 @@ jobs:
|
||||
- dragonfly-amd64
|
||||
- solaris-amd64
|
||||
- omnios-amd64
|
||||
# - haiku-amd64
|
||||
- haiku-amd64
|
||||
- windows-hosts
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
@@ -13,3 +13,5 @@ fastfetch.kdev4
|
||||
*.user.*
|
||||
*.swp
|
||||
*.log
|
||||
/*.zh.md
|
||||
/.*-ai/
|
||||
|
||||
+10
-1
@@ -1,5 +1,10 @@
|
||||
# Unreleased
|
||||
|
||||
Features:
|
||||
* Added CPU name and frequency detection support on SPARC. (CPU, Linux)
|
||||
|
||||
# 2.68.1
|
||||
|
||||
Changes:
|
||||
* The DE / WM / LM modules now reports the full name "Desktop Environment" / "Window Manager" / "Login Manager" instead of the abbreviations.
|
||||
|
||||
@@ -11,6 +16,7 @@ Features:
|
||||
* Added module key localization support. (General)
|
||||
* Added a new option `--key-language <?lang>` (`display.key.language: "<?lang>"` in JSON config). When left empty, it defaults to the system locale.
|
||||
* See `fastfetch -h key-language` for all supported languages.
|
||||
* Exposed in custom key format as `{module-name}`.
|
||||
* 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.
|
||||
@@ -21,6 +27,7 @@ Features:
|
||||
* 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)
|
||||
@@ -28,6 +35,8 @@ Bugfixes:
|
||||
* 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.
|
||||
* Attempted to fix potential issues on big-endian systems
|
||||
* Not tested due to lack of access to big-endian hardware.
|
||||
|
||||
Logos:
|
||||
* Added macOS_old
|
||||
@@ -3314,7 +3323,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
|
||||
|
||||
+31
-95
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.21.0) # C_STANDARD
|
||||
cmake_minimum_required(VERSION 3.21.0) # C_STANDARD C17 and C23
|
||||
|
||||
project(fastfetch
|
||||
VERSION 2.67.1
|
||||
VERSION 2.68.1
|
||||
LANGUAGES C
|
||||
DESCRIPTION "Fast neofetch-like system information tool"
|
||||
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
|
||||
@@ -9,9 +9,6 @@ project(fastfetch
|
||||
|
||||
set(PROJECT_LICENSE "MIT license")
|
||||
|
||||
if(DEFINED CMAKE_SYSTEM_PROCESSOR_OVERRIDE) # Used by github actions for i686 build
|
||||
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR_OVERRIDE} CACHE INTERNAL "")
|
||||
endif()
|
||||
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" CMAKE_SYSTEM_PROCESSOR)
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||
set(CMAKE_SYSTEM_PROCESSOR "amd64")
|
||||
@@ -128,13 +125,24 @@ 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/*/")
|
||||
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)
|
||||
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
|
||||
# A module is a directory `src/modules/<module>/` that contains `<module>.c`.
|
||||
# Globbing the sources rather than the directories skips empty directories and
|
||||
# other leftovers, and makes `CONFIGURE_DEPENDS` react to added or removed sources.
|
||||
file(GLOB FF_MODULE_SRCS CONFIGURE_DEPENDS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/*/*.c")
|
||||
set(FF_MODULE_DIRS "")
|
||||
foreach(FF_MODULE_SRC ${FF_MODULE_SRCS})
|
||||
get_filename_component(FF_MODULE_DIR "${FF_MODULE_SRC}" DIRECTORY)
|
||||
get_filename_component(FF_MODULE_NAME "${FF_MODULE_SRC}" NAME_WE)
|
||||
if("${FF_MODULE_DIR}" STREQUAL "${FF_MODULE_NAME}")
|
||||
list(APPEND FF_MODULE_DIRS "${FF_MODULE_DIR}")
|
||||
endif()
|
||||
endforeach()
|
||||
unset(FF_MODULE_SRCS)
|
||||
|
||||
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
|
||||
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
|
||||
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
|
||||
endforeach()
|
||||
|
||||
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
|
||||
set(BINARY_LINK_TYPE dlopen CACHE STRING "How to link fastfetch")
|
||||
@@ -496,88 +504,18 @@ set(LIBFASTFETCH_SRC
|
||||
src/logo/image/im7.c
|
||||
src/logo/image/image.c
|
||||
src/logo/logo.c
|
||||
src/modules/battery/battery.c
|
||||
src/modules/bios/bios.c
|
||||
src/modules/bluetooth/bluetooth.c
|
||||
src/modules/bluetoothradio/bluetoothradio.c
|
||||
src/modules/board/board.c
|
||||
src/modules/bootmgr/bootmgr.c
|
||||
src/modules/brightness/brightness.c
|
||||
src/modules/break/break.c
|
||||
src/modules/btrfs/btrfs.c
|
||||
src/modules/camera/camera.c
|
||||
src/modules/chassis/chassis.c
|
||||
src/modules/codec/codec.c
|
||||
src/modules/colors/colors.c
|
||||
src/modules/cpu/cpu.c
|
||||
src/modules/cpucache/cpucache.c
|
||||
src/modules/cpuusage/cpuusage.c
|
||||
src/modules/cursor/cursor.c
|
||||
src/modules/custom/custom.c
|
||||
src/modules/command/command.c
|
||||
src/modules/datetime/datetime.c
|
||||
src/modules/de/de.c
|
||||
src/modules/disk/disk.c
|
||||
src/modules/diskio/diskio.c
|
||||
src/modules/dns/dns.c
|
||||
src/modules/editor/editor.c
|
||||
src/modules/font/font.c
|
||||
src/modules/gpu/gpu.c
|
||||
src/modules/host/host.c
|
||||
src/modules/icons/icons.c
|
||||
src/modules/initsystem/initsystem.c
|
||||
src/modules/gamepad/gamepad.c
|
||||
src/modules/kernel/kernel.c
|
||||
src/modules/keyboard/keyboard.c
|
||||
src/modules/lm/lm.c
|
||||
src/modules/loadavg/loadavg.c
|
||||
src/modules/locale/locale.c
|
||||
src/modules/localip/localip.c
|
||||
src/modules/logo/logo.c
|
||||
src/modules/memory/memory.c
|
||||
src/modules/monitor/monitor.c
|
||||
src/modules/netio/netio.c
|
||||
src/modules/opencl/opencl.c
|
||||
src/modules/opengl/opengl.c
|
||||
src/modules/os/os.c
|
||||
src/modules/packages/packages.c
|
||||
src/modules/physicaldisk/physicaldisk.c
|
||||
src/modules/physicalmemory/physicalmemory.c
|
||||
src/modules/processes/processes.c
|
||||
src/modules/player/player.c
|
||||
src/modules/poweradapter/poweradapter.c
|
||||
src/modules/publicip/publicip.c
|
||||
src/modules/display/display.c
|
||||
src/modules/separator/separator.c
|
||||
src/modules/shell/shell.c
|
||||
src/modules/sound/sound.c
|
||||
src/modules/swap/swap.c
|
||||
src/modules/media/media.c
|
||||
src/modules/mouse/mouse.c
|
||||
src/modules/terminal/terminal.c
|
||||
src/modules/terminaltheme/terminaltheme.c
|
||||
src/modules/terminalfont/terminalfont.c
|
||||
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
|
||||
src/modules/version/version.c
|
||||
src/modules/vulkan/vulkan.c
|
||||
src/modules/wallpaper/wallpaper.c
|
||||
src/modules/weather/weather.c
|
||||
src/modules/wifi/wifi.c
|
||||
src/modules/wm/wm.c
|
||||
src/modules/wmtheme/wmtheme.c
|
||||
src/modules/zpool/zpool.c
|
||||
src/modules/modules.c
|
||||
src/options/display.c
|
||||
src/options/logo.c
|
||||
src/options/general.c
|
||||
)
|
||||
|
||||
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
src/modules/${FF_MODULE_DIR}/${FF_MODULE_DIR}.c
|
||||
)
|
||||
endforeach()
|
||||
|
||||
if(LINUX)
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
src/common/impl/dbus.c
|
||||
@@ -1485,11 +1423,9 @@ add_library(libfastfetch OBJECT
|
||||
)
|
||||
|
||||
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)
|
||||
if(MODULE_DISABLE_${FF_MODULE_UPPER})
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_MODULE_DISABLE_${FF_MODULE_UPPER}=1)
|
||||
endif()
|
||||
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
|
||||
if(MODULE_DISABLE_${FF_MODULE_UPPER})
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_MODULE_DISABLE_${FF_MODULE_UPPER}=1)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -1522,10 +1458,11 @@ if(yyjson_FOUND)
|
||||
# `target_link_libraries(yyjson::yyjson)` sets rpath implicitly
|
||||
endif()
|
||||
|
||||
# Used for dlopen finding dylibs installed by homebrew
|
||||
# `/opt/homebrew/lib` is not on in dlopen search path by default
|
||||
# Used for dlopen finding dylibs installed by package managers
|
||||
# `/opt/homebrew/lib`, `/usr/local/lib` and `/opt/local/lib` are not on the
|
||||
# dlopen search path by default
|
||||
if(APPLE AND BINARY_LINK_TYPE STREQUAL "dlopen")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib -Wl,-rpath,/opt/local/lib")
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
@@ -1994,7 +1931,6 @@ elseif(FreeBSD)
|
||||
elseif(OpenBSD)
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE "m"
|
||||
PRIVATE "kvm"
|
||||
PRIVATE "sndio"
|
||||
PRIVATE "util"
|
||||
)
|
||||
|
||||
+797
@@ -0,0 +1,797 @@
|
||||
# Contributing to fastfetch
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
Disclaimer
|
||||
</summary>
|
||||
|
||||
This document is generated by AI and reviewed by humans. It is primarily intended for AI-assisted development, while remaining useful to human developers.
|
||||
</details>
|
||||
|
||||
Thank you 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 places strong emphasis on **startup time** and on keeping **dependencies optional**; many design decisions only make sense under that constraint, and this document returns to it repeatedly.
|
||||
|
||||
---
|
||||
|
||||
## 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)
|
||||
- [Pitfalls](#pitfalls)
|
||||
|
||||
---
|
||||
|
||||
## Contributor quick reference
|
||||
|
||||
| Task | Start here | Verify with |
|
||||
|---|---|---|
|
||||
| Build and run fastfetch | `run.sh`, `CMakeLists.txt` | `./run.sh` |
|
||||
| Add a module | `src/modules/<name>/`, `src/detection/<name>/`, `src/modules/modules.c`, `CMakeLists.txt` | `cmake -B build && cmake --build build -j` |
|
||||
| Add a platform implementation | `src/detection/<name>/<name>_<platform>.c`, the matching platform block in `CMakeLists.txt` | Build on the target platform or CI |
|
||||
| Add an ASCII logo | `src/logo/ascii/<letter>/<name>.txt`, matching `<letter>.inc` | `./build/fastfetch --logo <name>` |
|
||||
| Change formatting or JSON output | `src/modules/<name>/<name>.c` | `./build/fastfetch -s <name> --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 typical code change, the shortest useful iteration 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 that 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 configuration enables it and only `Debug` builds omit it. This is significant because LTO here is not merely an optimization: it is what removes the code of disabled modules. See [Pitfalls](#pitfalls).
|
||||
|
||||
### 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_<NAME>` | `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 does not match the number of detection directories, which 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 another module'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`.
|
||||
|
||||
Therefore, do not assume that `modules/<x>/` always has a matching `detection/<x>/`.
|
||||
|
||||
### 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 |
|
||||
|
||||
A simple check: **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 most important convention in the codebase. Every feature consists of 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, typically consisting of only two declarations:
|
||||
|
||||
```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 benefit of this separation:** 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 acknowledges that this is undefined behavior, since `void*` is not compatible with `FF*Options*`. It is a pragmatic compromise to obtain polymorphism in C; do not attempt 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)) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
Because the registry is small, a single arithmetic bucket lookup followed by 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`, and 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");
|
||||
```
|
||||
|
||||
**In other words, no module option struct may exceed 256 bytes.** This is a 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 |
|
||||
|
||||
### Build-time module discovery
|
||||
|
||||
`CMakeLists.txt:134` globs `src/modules/*/*.c`, keeps only the entries whose file name matches their directory name, and generates a `MODULE_DISABLE_<UPPER>` option for each:
|
||||
|
||||
```cmake
|
||||
file(GLOB FF_MODULE_SRCS CONFIGURE_DEPENDS RELATIVE "..." ".../src/modules/*/*.c")
|
||||
set(FF_MODULE_DIRS "")
|
||||
foreach(FF_MODULE_SRC ${FF_MODULE_SRCS})
|
||||
get_filename_component(FF_MODULE_DIR "${FF_MODULE_SRC}" DIRECTORY)
|
||||
get_filename_component(FF_MODULE_NAME "${FF_MODULE_SRC}" NAME_WE)
|
||||
if("${FF_MODULE_DIR}" STREQUAL "${FF_MODULE_NAME}")
|
||||
list(APPEND FF_MODULE_DIRS "${FF_MODULE_DIR}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
|
||||
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
|
||||
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
|
||||
endforeach()
|
||||
```
|
||||
|
||||
Because the glob matches sources rather than directories, a directory counts as a module only when `src/modules/<name>/<name>.c` exists. Empty directories — which git does not track, so they are easy to leave behind — and stray files are ignored instead of breaking the configure step with `Cannot find source file`.
|
||||
|
||||
### Module sources are discovered automatically
|
||||
|
||||
`FF_MODULE_DIRS` also drives the source list (`CMakeLists.txt:516`):
|
||||
|
||||
```cmake
|
||||
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
src/modules/${FF_MODULE_DIR}/${FF_MODULE_DIR}.c
|
||||
)
|
||||
endforeach()
|
||||
```
|
||||
|
||||
Consequences:
|
||||
|
||||
- `src/modules/<name>/<name>.c` is compiled as soon as the file exists — **there is no source list to edit for the module layer**.
|
||||
- The file name must match the directory name. A directory whose `.c` file is named differently, or has none at all, is silently not a module: a typo therefore shows up as a module missing from `fastfetch --list-modules`, not as a build error.
|
||||
- The glob uses `CONFIGURE_DEPENDS`, so with the Makefile and Ninja generators the build re-evaluates it and re-runs CMake when a module source is added or removed. With other generators, or to be safe, re-run `cmake -B build` explicitly.
|
||||
|
||||
Only the module layer is automated. Sources under `src/detection/` and `src/common/impl/` are **not** globbed and must still be listed by hand — see [step 5](#5-add-the-platform-sources-to-cmakeliststxt).
|
||||
|
||||
---
|
||||
|
||||
## 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()` handles these generically, **so no parsing code is required**.
|
||||
|
||||
### 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, plus a `foo_nosupport.c` fallback for the remaining platforms.** The repository currently contains 45 `*_nosupport.c` files serving this purpose.
|
||||
|
||||
### 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 an existing 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 [Pitfalls](#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`; **any placeholder omitted here is neither listed nor usable by 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`, excludes the module from the interactive `--gen-config` picker. Only `logo`, `command` and `custom` rely on this behavior, because they require 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. Add the platform sources to `CMakeLists.txt`
|
||||
|
||||
The module layer is discovered by glob (see [Build-time module discovery](#module-sources-are-discovered-automatically)), **but `src/detection/**` and `src/common/impl/**` are not**. `CMakeLists.txt` lists them explicitly, inside one mutually exclusive chain of platform blocks:
|
||||
|
||||
| Block | Line | Covers |
|
||||
|---|---|---|
|
||||
| `if(LINUX)` | `CMakeLists.txt:522` | Linux |
|
||||
| `elseif(ANDROID)` | `CMakeLists.txt:608` | Android (Termux) |
|
||||
| `elseif(FreeBSD)` | `CMakeLists.txt:691` | FreeBSD, MidnightBSD, DragonFly |
|
||||
| `elseif(NetBSD)` | `CMakeLists.txt:788` | NetBSD |
|
||||
| `elseif(OpenBSD)` | `CMakeLists.txt:872` | OpenBSD |
|
||||
| `elseif(APPLE)` | `CMakeLists.txt:959` | macOS / iOS |
|
||||
| `elseif(WIN32)` | `CMakeLists.txt:1046` | Windows |
|
||||
| `elseif(SunOS)` | `CMakeLists.txt:1121` | Solaris / illumos |
|
||||
| `elseif(Haiku)` | `CMakeLists.txt:1204` | Haiku |
|
||||
| `elseif(GNU)` | `CMakeLists.txt:1282` | GNU/Hurd |
|
||||
|
||||
Add the platform implementation to every block whose platform it supports, and `foo_nosupport.c` to every remaining block, so that all ten platforms still link:
|
||||
|
||||
```cmake
|
||||
elseif(FreeBSD)
|
||||
list(APPEND LIBFASTFETCH_SRC
|
||||
...
|
||||
src/detection/foo/foo_bsd.c
|
||||
)
|
||||
```
|
||||
|
||||
Points to note:
|
||||
|
||||
- Exactly one block is compiled per build, so a file omitted from a block does not exist for that platform, and the link fails with an undefined reference to `ffDetectFoo()` — **on that platform only**. A missing entry therefore builds fine locally and fails in CI; this is why every block must be covered.
|
||||
- A platform may reuse another platform's implementation instead of a stub. `src/common/impl/networking_linux.c`, for example, is listed in nine of the ten blocks (all but `WIN32`). Check where the closest sibling module points before adding a new file.
|
||||
- `DragonFly` is handled by the inner `if(DragonFly)` sub-block inside the FreeBSD block (`CMakeLists.txt:773`); add the variant there, as `processes`, `top` and `wifi` do.
|
||||
- New helpers under `src/common/impl/` follow the same rule: they are not globbed, and each block that needs one must list it.
|
||||
- A few files are appended outside the platform chain because they depend on an option or on a specific feature — for example the proprietary GPU backends (`CMakeLists.txt:1379`) and `src/common/impl/wcwidth.c` (`CMakeLists.txt:1408`). Those are written by hand as well.
|
||||
|
||||
### 6. 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 matching first-letter `case`. Six modules currently do this: CPUUsage, DiskIO, NetIO, PublicIP, Top and Weather.
|
||||
|
||||
### 7. 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` is not supported; only `fastfetch -h foo-format` works.
|
||||
|
||||
If you added detection sources, re-check [step 5](#5-add-the-platform-sources-to-cmakeliststxt) before pushing: a platform block you missed compiles fine locally and fails only when that platform is built.
|
||||
|
||||
---
|
||||
|
||||
## 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. Add the ASCII file
|
||||
|
||||
```
|
||||
src/logo/ascii/<first-letter>/<distro>.txt
|
||||
```
|
||||
|
||||
For example `src/logo/ascii/d/distro.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 are supported (`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**, as 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` file into a `FASTFETCH_DATATEXT_LOGO_<UPPERCASE>` macro (`CMakeLists.txt:418`), but **the registry itself is maintained by hand**. Edit `src/logo/ascii/<first-letter>.inc`:
|
||||
|
||||
```c
|
||||
// src/logo/ascii/d.inc
|
||||
|
||||
#ifdef FASTFETCH_DATATEXT_LOGO_DISTRO
|
||||
// Distro
|
||||
{
|
||||
.names = { "Distro" }, // ID (preferred) or NAME from /etc/os-release, do NOT add both
|
||||
.lines = FASTFETCH_DATATEXT_LOGO_DISTRO,
|
||||
.colors = {
|
||||
FF_COLOR_FG_PRIMARY, // recommended for using as WHITE replacement, light-theme terminal friendly
|
||||
FF_COLOR_FG_BLUE, // preferred
|
||||
FF_COLOR_FG_256 "34",
|
||||
FF_COLOR_FG_RGB "0;255;0", // not recommended because of bad compatibility with raw TTY
|
||||
},
|
||||
},
|
||||
#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 `-l <source>`.
|
||||
|
||||
### 3. Verify
|
||||
|
||||
```sh
|
||||
./build/fastfetch -l distro
|
||||
./build/fastfetch --list-logos | grep -i distro
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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.)
|
||||
|
||||
**Do not accumulate `#ifdef __linux__` blocks in a single file.** When adding platform support, copy the closest existing implementation and change the suffix.
|
||||
|
||||
`common/` follows the same convention: `common/impl/` contains `io_unix.c` / `io_windows.c`, `netif_linux.c` / `netif_apple.c` / `netif_bsd.c` and similar files, 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` specifies LF line endings, a 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 | `FF<Name>Options` | `FFCPUOptions` |
|
||||
| Detection results | `FF<Name>Result` | `FFCPUResult` |
|
||||
|
||||
### Spelling
|
||||
|
||||
CI runs codespell (`.codespellrc`). Known false positives are listed 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:
|
||||
|
||||
```
|
||||
<Scope>[ (Platform)]: <third-person singular verb> <object>
|
||||
```
|
||||
|
||||
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 use 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, because it depends on the state of a running system; it 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 modify `common/FFstrbuf.h`, `common/format.h` or `common/color.h`, extend the corresponding test.**
|
||||
|
||||
---
|
||||
|
||||
## Pull requests
|
||||
|
||||
1. **Open an issue first** (feature request / bug report / logo request) to confirm that the change is wanted before investing 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 is not accepted)
|
||||
- Changes
|
||||
- Screenshots (required for visual changes)
|
||||
- Checklist: confirm you tested locally
|
||||
|
||||
### Pre-submit checklist
|
||||
|
||||
```sh
|
||||
clang-format -i <changed files> # 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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pitfalls
|
||||
|
||||
The following points are easy to misinterpret when reading this codebase. Most of them follow from the startup-time objective described above.
|
||||
|
||||
### 1. Option structs must not exceed 256 bytes
|
||||
|
||||
`FF_OPTION_MAX_SIZE = 1 << 8`. Exceeding it is caught at compile time by `static_assert`. Do not attempt to increase 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_<module> 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. Only two things are discovered by glob
|
||||
|
||||
The module sources (`CMakeLists.txt:134`) and the logo `.txt` files (`CMakeLists.txt:429`). Both use `CONFIGURE_DEPENDS`, so with the Makefile and Ninja generators the build re-checks the glob and re-runs CMake when the result changes; other generators (Visual Studio and Xcode in particular) do not track it as reliably. Re-run `cmake -B build` after adding a module source or a logo file rather than relying on that behavior.
|
||||
|
||||
Everything else — `src/detection/**` and `src/common/impl/**`, and any source outside the platform chain — must be listed in `CMakeLists.txt` by hand; see [step 5](#5-add-the-platform-sources-to-cmakeliststxt).
|
||||
|
||||
### 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` rely on this intentionally.
|
||||
|
||||
`defaultOrder` affects only the ordering in the interactive `--gen-config` picker; it has no effect on the 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 the fields silently corrupts the output for every language.
|
||||
|
||||
### 7. The `multithreading` option has limited effect
|
||||
|
||||
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.
|
||||
|
||||
This may change in the future. The main blocking issue is that there are dependencies between different modules.
|
||||
|
||||
### 8. Do not print or read configuration 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.
|
||||
|
||||
Use `FF_DEBUG` (`src/common/debug.h`) for logging.
|
||||
|
||||
### 9. `src/logo/builtin.c` and `3rdparty/` are formatting-exempt
|
||||
|
||||
The former is a large generated and hand-maintained data table; the latter is upstream code. Both are excluded via `.clang-format-ignore`; do not reformat either.
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
- Configuration guide: <https://github.com/fastfetch-cli/fastfetch/wiki/Configuration>
|
||||
- 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`)
|
||||
Vendored
+12
@@ -1,3 +1,15 @@
|
||||
fastfetch (2.68.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.68.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 02 Sep 2026 10:47:51 +0800
|
||||
|
||||
fastfetch (2.68.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.68.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Tue, 01 Sep 2026 16:34:36 +0800
|
||||
|
||||
fastfetch (2.67.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.67.0
|
||||
|
||||
+15
-10
@@ -322,7 +322,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"cpuFormat": {
|
||||
"description": "Output format for the `CPU` module. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: CPU microarchitecture\n 12. {numa-nodes}: NUMA node count",
|
||||
"description": "Output format for the `CPU` module. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: CPU microarchitecture\n 12. {numa-nodes}: NUMA node count\n 13. {code-name}: CPU code name\n 14. {technology}: CPU technology",
|
||||
"type": "string"
|
||||
},
|
||||
"cpucacheFormat": {
|
||||
@@ -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": {
|
||||
@@ -446,7 +446,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"packagesFormat": {
|
||||
"description": "Output format for the `Packages` module. See Wiki for formatting syntax\n 1. {am-system}: Number of am-system packages\n 2. {am-user}: Number of am-user (aka appman) packages\n 3. {appimage}: Number of appimage packages\n 4. {apk}: Number of apk packages\n 5. {brew}: Number of brew packages\n 6. {brew-cask}: Number of brew-cask packages\n 7. {cards}: Number of cards packages\n 8. {choco}: Number of choco packages\n 9. {dpkg}: Number of dpkg packages\n 10. {emerge}: Number of emerge packages\n 11. {eopkg}: Number of eopkg packages\n 12. {flatpak-system}: Number of flatpak-system app packages\n 13. {flatpak-user}: Number of flatpak-user app packages\n 14. {guix-home}: Number of guix-home packages\n 15. {guix-system}: Number of guix-system packages\n 16. {guix-user}: Number of guix-user packages\n 17. {hpkg-system}: Number of hpkg-system packages\n 18. {hpkg-user}: Number of hpkg-user packages\n 19. {install-release}: Number of install-release packages\n 20. {kiss}: Number of kiss packages\n 21. {linglong}: Number of linglong packages\n 22. {lpkg}: Number of lpkg packages\n 23. {lpkgbuild}: Number of lpkgbuild packages\n 24. {macports}: Number of macports packages\n 25. {mport}: Number of mport packages\n 26. {moss}: Number of moss packages\n 27. {nix-default}: Number of nix-default packages\n 28. {nix-system}: Number of nix-system packages\n 29. {nix-user}: Number of nix-user packages\n 30. {opkg}: Number of opkg packages\n 31. {pacman}: Number of pacman packages\n 32. {pacman-branch}: Pacman branch on manjaro\n 33. {pacstall}: Number of pacstall packages\n 34. {paludis}: Number of paludis packages\n 35. {pisi}: Number of pisi packages\n 36. {pkg}: Number of pkg packages\n 37. {pkgsrc}: Number of pkgsrc packages\n 38. {pkgtool}: Number of pkgtool packages\n 39. {porg}: Number of porg packages\n 40. {rpm}: Number of rpm packages\n 41. {scoop-global}: Number of scoop-global packages\n 42. {scoop-user}: Number of scoop-user packages\n 43. {snap}: Number of snap packages\n 44. {soar}: Number of soar packages\n 45. {sorcery}: Number of sorcery packages\n 46. {winget}: Number of winget packages\n 47. {xbps}: Number of xbps packages\n 48. {brew-all}: Total number of all brew packages\n 49. {flatpak-all}: Total number of all flatpak app packages\n 50. {guix-all}: Total number of all guix packages\n 51. {hpkg-all}: Total number of all hpkg packages\n 52. {nix-all}: Total number of all nix packages\n 53. {all}: Number of all packages",
|
||||
"description": "Output format for the `Packages` module. See Wiki for formatting syntax\n 1. {am-system}: Number of am-system packages\n 2. {am-user}: Number of am-user (aka appman) packages\n 3. {appimage}: Number of appimage packages\n 4. {apk}: Number of apk packages\n 5. {brew}: Number of brew packages\n 6. {brew-cask}: Number of brew-cask packages\n 7. {cards}: Number of cards packages\n 8. {choco}: Number of choco packages\n 9. {crux}: Number of crux packages\n 10. {dpkg}: Number of dpkg packages\n 11. {emerge}: Number of emerge packages\n 12. {eopkg}: Number of eopkg packages\n 13. {flatpak-system}: Number of flatpak-system app packages\n 14. {flatpak-user}: Number of flatpak-user app packages\n 15. {guix-home}: Number of guix-home packages\n 16. {guix-system}: Number of guix-system packages\n 17. {guix-user}: Number of guix-user packages\n 18. {hpkg-system}: Number of hpkg-system packages\n 19. {hpkg-user}: Number of hpkg-user packages\n 20. {install-release}: Number of install-release packages\n 21. {kiss}: Number of kiss packages\n 22. {linglong}: Number of linglong packages\n 23. {lpkg}: Number of lpkg packages\n 24. {lpkgbuild}: Number of lpkgbuild packages\n 25. {macports}: Number of macports packages\n 26. {mport}: Number of mport packages\n 27. {moss}: Number of moss packages\n 28. {nix-default}: Number of nix-default packages\n 29. {nix-system}: Number of nix-system packages\n 30. {nix-user}: Number of nix-user packages\n 31. {opkg}: Number of opkg packages\n 32. {pacman}: Number of pacman packages\n 33. {pacman-branch}: Pacman branch on manjaro\n 34. {pacstall}: Number of pacstall packages\n 35. {paludis}: Number of paludis packages\n 36. {pisi}: Number of pisi packages\n 37. {pkg}: Number of pkg packages\n 38. {pkgsrc}: Number of pkgsrc packages\n 39. {pkgtool}: Number of pkgtool packages\n 40. {porg}: Number of porg packages\n 41. {rpm}: Number of rpm packages\n 42. {scoop-global}: Number of scoop-global packages\n 43. {scoop-user}: Number of scoop-user packages\n 44. {snap}: Number of snap packages\n 45. {soar}: Number of soar packages\n 46. {sorcery}: Number of sorcery packages\n 47. {winget}: Number of winget packages\n 48. {xbps}: Number of xbps packages\n 49. {brew-all}: Total number of all brew packages\n 50. {flatpak-all}: Total number of all flatpak app packages\n 51. {guix-all}: Total number of all guix packages\n 52. {hpkg-all}: Total number of all hpkg packages\n 53. {nix-all}: Total number of all nix packages\n 54. {all}: Number of all packages",
|
||||
"type": "string"
|
||||
},
|
||||
"physicaldiskFormat": {
|
||||
@@ -506,7 +506,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"topFormat": {
|
||||
"description": "Output format for the `Top` module. See Wiki for formatting syntax\n 1. {name}: Process name\n 2. {path}: Executable path\n 3. {pid}: Process ID\n 4. {cpu}: CPU usage\n 5. {mem}: Memory usage (RSS) in bytes\n 6. {disk-read}: Disk read bytes per second\n 7. {disk-write}: Disk write bytes per second\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",
|
||||
"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": {
|
||||
@@ -3763,6 +3763,7 @@
|
||||
"apk",
|
||||
"brew",
|
||||
"choco",
|
||||
"crux",
|
||||
"dpkg",
|
||||
"emerge",
|
||||
"eopkg",
|
||||
@@ -4441,21 +4442,23 @@
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "top",
|
||||
"description": "Print processes with the highest CPU or memory usage"
|
||||
"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 or disk IO usage",
|
||||
"description": "Sort processes by CPU, memory, disk IO usage, start time or thread count",
|
||||
"enum": [
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk-read",
|
||||
"disk-write"
|
||||
"disk-write",
|
||||
"start-time",
|
||||
"threads"
|
||||
],
|
||||
"default": "cpu"
|
||||
},
|
||||
"showTypes": {
|
||||
"description": "Specify which resource usages are detected and displayed.\nIf memory is enabled is enabled alone, only a single process snapshot is taken and `waitTime` sampling wait is skipped",
|
||||
"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",
|
||||
@@ -4467,7 +4470,8 @@
|
||||
"enum": [
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk"
|
||||
"disk",
|
||||
"threads"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -4478,7 +4482,8 @@
|
||||
"enum": [
|
||||
"cpu",
|
||||
"memory",
|
||||
"disk"
|
||||
"disk",
|
||||
"threads"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}",
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"home": "https://github.com/ibireme/yyjson",
|
||||
"license": "MIT ( embed in source )",
|
||||
"version": "0.12.0",
|
||||
"version": "0.13.0",
|
||||
"author": "ibireme"
|
||||
}
|
||||
|
||||
Vendored
+700
-285
File diff suppressed because it is too large
Load Diff
Vendored
+911
-674
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "common/strutil.h"
|
||||
#include "common/io.h"
|
||||
#include "common/path.h"
|
||||
#include "common/mallocHelper.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
@@ -19,7 +20,6 @@
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <kvm.h>
|
||||
#include "common/path.h"
|
||||
#elif defined(__HAIKU__)
|
||||
#include <image.h>
|
||||
@@ -68,13 +68,14 @@ static void getExePath(FFPlatform* platform) {
|
||||
// Current implementation uses argv[0], which can be easily spoofed.
|
||||
// See #2195
|
||||
size_t exePathLen = 0;
|
||||
kvm_t* kd = kvm_openfiles(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
|
||||
if (kd) {
|
||||
int kpCount;
|
||||
struct kinfo_proc* kp = kvm_getprocs(kd, KERN_PROC_PID, (pid_t) platform->pid, sizeof(*kp), &kpCount);
|
||||
if (kp && kpCount == 1) {
|
||||
char** argv = kvm_getargv(kd, kp, 0);
|
||||
if (argv && argv[0]) {
|
||||
{
|
||||
char argvBuf[ARG_MAX];
|
||||
size_t argvSize = sizeof(argvBuf);
|
||||
int argvMib[] = { CTL_KERN, KERN_PROC_ARGS, (pid_t) platform->pid, KERN_PROC_ARGV };
|
||||
if (sysctl(argvMib, ARRAY_SIZE(argvMib), argvBuf, &argvSize, nullptr, 0) == 0) {
|
||||
// The buffer is filled with an array of char pointers followed by the strings themselves
|
||||
char** argv = (char**) argvBuf;
|
||||
if (argv[0] && (char*) argv[0] >= argvBuf && (char*) argv[0] < argvBuf + argvSize) {
|
||||
char* arg0 = argv[0];
|
||||
if (arg0[0]) {
|
||||
if (strchr(arg0, '/') != nullptr) // likely a path (absolute or relative)
|
||||
@@ -97,25 +98,38 @@ static void getExePath(FFPlatform* platform) {
|
||||
if (exePathLen > 0) {
|
||||
struct stat st;
|
||||
if (stat(exePath, &st) == 0 && S_ISREG(st.st_mode)) {
|
||||
int cntp;
|
||||
struct kinfo_file* kf = kvm_getfiles(kd, KERN_FILE_BYPID, (pid_t) platform->pid, sizeof(*kf), &cntp);
|
||||
if (kf) {
|
||||
int i;
|
||||
for (i = 0; i < cntp; i++) {
|
||||
if (kf[i].fd_fd == KERN_FILE_TEXT) {
|
||||
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
|
||||
if (st.st_dev != (dev_t) kf[i].va_fsid || st.st_ino != (ino_t) kf[i].va_fileid) {
|
||||
i = -1;
|
||||
// Replicate kvm_getfiles()'s live path: {CTL_KERN, KERN_FILE, KERN_FILE_BYPID, pid, esize, count}
|
||||
int fileMib[6] = { CTL_KERN, KERN_FILE, KERN_FILE_BYPID, (pid_t) platform->pid, (int) sizeof(struct kinfo_file), 0 };
|
||||
size_t fileSize = 0;
|
||||
if (sysctl(fileMib, ARRAY_SIZE(fileMib), nullptr, &fileSize, nullptr, 0) == 0) {
|
||||
fileSize += fileSize / 8; // add ~10%
|
||||
FF_AUTO_FREE struct kinfo_file* kf = (struct kinfo_file*) malloc(fileSize);
|
||||
if (kf) {
|
||||
int rv;
|
||||
do {
|
||||
fileMib[5] = (int) (fileSize / sizeof(struct kinfo_file));
|
||||
rv = sysctl(fileMib, ARRAY_SIZE(fileMib), kf, &fileSize, nullptr, 0);
|
||||
} while (rv == -1 && errno == ENOMEM);
|
||||
|
||||
if (rv == 0) {
|
||||
int cntp = (int) (fileSize / sizeof(struct kinfo_file));
|
||||
int i;
|
||||
for (i = 0; i < cntp; i++) {
|
||||
if (kf[i].fd_fd == KERN_FILE_TEXT) {
|
||||
// KERN_FILE_TEXT is the executable file, not a shared library, and should be unique in the list.
|
||||
if (st.st_dev != (dev_t) kf[i].va_fsid || st.st_ino != (ino_t) kf[i].va_fileid) {
|
||||
i = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < 0) {
|
||||
exePathLen = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// If we can't get the list of open files, we can't verify that the file is actually the executable
|
||||
// Assume it is
|
||||
}
|
||||
if (i < 0) {
|
||||
exePathLen = 0;
|
||||
}
|
||||
} else {
|
||||
// If we can't get the list of open files, we can't verify that the file is actually the executable
|
||||
// Assume it is
|
||||
}
|
||||
} else {
|
||||
exePathLen = 0;
|
||||
@@ -124,7 +138,6 @@ static void getExePath(FFPlatform* platform) {
|
||||
}
|
||||
}
|
||||
}
|
||||
kvm_close(kd);
|
||||
}
|
||||
#elif defined(__sun)
|
||||
ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1);
|
||||
@@ -238,7 +251,9 @@ static void getDataDirs(FFPlatform* platform) {
|
||||
|
||||
ffPlatformPathAddHome(&platform->dataDirs, platform, "");
|
||||
platformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS");
|
||||
#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/");
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -70,15 +70,15 @@ void ffPrepareCommandOption(FFdata* data) {
|
||||
#define FF_IF_MODULE_MATCH(moduleNameConstant) if (ffStrEqualsIgnCase(moduleType, moduleNameConstant) && !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':'))
|
||||
|
||||
switch (moduleType[0]) {
|
||||
#if !FF_MODULE_DISABLE_CPUUSAGE
|
||||
#if !FF_MODULE_DISABLE_CPUUSAGE
|
||||
case 'C':
|
||||
case 'c':
|
||||
FF_IF_MODULE_MATCH(ffCPUUsageModuleInfo.name)
|
||||
ffPrepareCPUUsage();
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !FF_MODULE_DISABLE_DISKIO
|
||||
#if !FF_MODULE_DISABLE_DISKIO
|
||||
case 'D':
|
||||
case 'd':
|
||||
FF_IF_MODULE_MATCH(ffDiskIOModuleInfo.name) {
|
||||
@@ -87,9 +87,9 @@ void ffPrepareCommandOption(FFdata* data) {
|
||||
ffPrepareDiskIO(&options);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !FF_MODULE_DISABLE_NETIO
|
||||
#if !FF_MODULE_DISABLE_NETIO
|
||||
case 'N':
|
||||
case 'n':
|
||||
FF_IF_MODULE_MATCH(ffNetIOModuleInfo.name) {
|
||||
@@ -98,9 +98,9 @@ void ffPrepareCommandOption(FFdata* data) {
|
||||
ffPrepareNetIO(&options);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !FF_MODULE_DISABLE_PUBLICIP
|
||||
#if !FF_MODULE_DISABLE_PUBLICIP
|
||||
case 'P':
|
||||
case 'p':
|
||||
FF_IF_MODULE_MATCH(ffPublicIPModuleInfo.name) {
|
||||
@@ -109,9 +109,9 @@ void ffPrepareCommandOption(FFdata* data) {
|
||||
ffPreparePublicIp(&options);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !FF_MODULE_DISABLE_TOP
|
||||
#if !FF_MODULE_DISABLE_TOP
|
||||
case 'T':
|
||||
case 't':
|
||||
FF_IF_MODULE_MATCH(ffTopModuleInfo.name) {
|
||||
@@ -120,9 +120,9 @@ void ffPrepareCommandOption(FFdata* data) {
|
||||
ffPrepareTopProcesses(options.showTypes);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !FF_MODULE_DISABLE_WEATHER
|
||||
#if !FF_MODULE_DISABLE_WEATHER
|
||||
case 'W':
|
||||
case 'w':
|
||||
FF_IF_MODULE_MATCH(ffWeatherModuleInfo.name) {
|
||||
@@ -131,7 +131,7 @@ void ffPrepareCommandOption(FFdata* data) {
|
||||
ffPrepareWeather(&options);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef FF_IF_MODULE_MATCH
|
||||
@@ -178,6 +178,34 @@ static void genJsonResult(FFdata* data, FFModuleBaseInfo* baseInfo, void* option
|
||||
}
|
||||
}
|
||||
|
||||
static yyjson_val* findStructureModuleConfig(FFdata* data, const char* moduleType) {
|
||||
if (data->configDoc == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
yyjson_val* root = yyjson_doc_get_root(data->configDoc);
|
||||
if (root == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
yyjson_val* modules = yyjson_obj_get(root, "modules");
|
||||
if (!yyjson_is_arr(modules)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
yyjson_val* item;
|
||||
size_t idx, max;
|
||||
yyjson_arr_foreach (modules, idx, max, item) {
|
||||
if (yyjson_is_obj(item)) {
|
||||
const char* type = yyjson_get_str(yyjson_obj_get(item, "type"));
|
||||
if (type != nullptr && ffStrEqualsIgnCase(type, moduleType)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool parseStructureCommand(
|
||||
FFdata* data,
|
||||
const char* line,
|
||||
@@ -191,7 +219,13 @@ static bool parseStructureCommand(
|
||||
if (data->resultDoc != nullptr) {
|
||||
fn(data, baseInfo, optionBuf);
|
||||
} else {
|
||||
baseInfo->printModule(optionBuf);
|
||||
yyjson_val* configModule = findStructureModuleConfig(data, baseInfo->name);
|
||||
if (configModule != nullptr) {
|
||||
baseInfo->parseJsonObject(optionBuf, configModule);
|
||||
baseInfo->printModule(optionBuf);
|
||||
} else {
|
||||
baseInfo->printModule(optionBuf);
|
||||
}
|
||||
}
|
||||
baseInfo->destroyOptions(optionBuf);
|
||||
return true;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -47,6 +47,7 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
|
||||
FF_PARSE_FORMAT_STRING_CHECKED(&key, &moduleArgs->key, ((FFformatarg[]) {
|
||||
FF_ARG(moduleIndex, "index"),
|
||||
FF_ARG(moduleArgs->keyIcon, "icon"),
|
||||
FF_ARG(moduleName, "module-name"),
|
||||
}));
|
||||
ffStrbufWriteTo(&key, stdout);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <kvm.h>
|
||||
#elif defined(__NetBSD__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
@@ -423,12 +422,13 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
|
||||
|
||||
#elif defined(__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_PID, pid, sizeof(struct kinfo_proc), &count);
|
||||
if (proc) {
|
||||
char** argv = kvm_getargv(kd, proc, 0);
|
||||
if (argv) {
|
||||
char argvBuf[ARG_MAX];
|
||||
size_t argvSize = sizeof(argvBuf);
|
||||
int argvMib[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV };
|
||||
if (sysctl(argvMib, ARRAY_SIZE(argvMib), argvBuf, &argvSize, nullptr, 0) == 0) {
|
||||
// The buffer is filled with an array of char pointers followed by the strings themselves
|
||||
char** argv = (char**) argvBuf;
|
||||
if (argv[0] && (char*) argv[0] >= argvBuf && (char*) argv[0] < argvBuf + argvSize) {
|
||||
const char* arg0 = argv[0];
|
||||
if (arg0[0] == '-') {
|
||||
arg0++;
|
||||
@@ -436,7 +436,6 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
|
||||
ffStrbufSetS(exe, arg0);
|
||||
}
|
||||
}
|
||||
kvm_close(kd);
|
||||
|
||||
#elif defined(__HAIKU__)
|
||||
|
||||
@@ -633,21 +632,19 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
|
||||
|
||||
#elif defined(__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_PID, pid, sizeof(struct kinfo_proc), &count);
|
||||
if (proc) {
|
||||
ffStrbufSetS(name, proc->p_comm);
|
||||
struct kinfo_proc proc;
|
||||
size_t size = sizeof(proc);
|
||||
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, (int) sizeof(struct kinfo_proc), 1 };
|
||||
if (sysctl(mib, ARRAY_SIZE(mib), &proc, &size, nullptr, 0) == 0) {
|
||||
ffStrbufSetS(name, proc.p_comm);
|
||||
if (ppid) {
|
||||
*ppid = proc->p_ppid;
|
||||
*ppid = proc.p_ppid;
|
||||
}
|
||||
if (tty) {
|
||||
*tty = (int) proc->p_tdev;
|
||||
*tty = (int) proc.p_tdev;
|
||||
}
|
||||
}
|
||||
kvm_close(kd);
|
||||
if (!proc) {
|
||||
return "kvm_getprocs() failed";
|
||||
} else {
|
||||
return "sysctl(KERN_PROC_PID) failed";
|
||||
}
|
||||
|
||||
#elif defined(__HAIKU__)
|
||||
|
||||
@@ -507,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; \
|
||||
|
||||
+17
-16
@@ -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,
|
||||
|
||||
+23
-3
@@ -13,6 +13,8 @@ enum {
|
||||
SystemLogicalProcessorAndGroupInformation = 107,
|
||||
SystemBasicPerformanceInformation = 123,
|
||||
SystemSecureBootInformation = 146,
|
||||
SystemBasicProcessInformation = 252,
|
||||
SystemHandleCountInformation = 253,
|
||||
};
|
||||
|
||||
typedef struct _PROCESSOR_POWER_INFORMATION {
|
||||
@@ -206,6 +208,20 @@ typedef struct _SYSTEM_BASIC_PERFORMANCE_INFORMATION {
|
||||
SIZE_T PeakCommitment;
|
||||
} SYSTEM_BASIC_PERFORMANCE_INFORMATION, *PSYSTEM_BASIC_PERFORMANCE_INFORMATION;
|
||||
|
||||
typedef struct _SYSTEM_BASICPROCESS_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
HANDLE UniqueProcessId;
|
||||
HANDLE InheritedFromUniqueProcessId;
|
||||
ULONG64 SequenceNumber;
|
||||
UNICODE_STRING ImageName;
|
||||
} SYSTEM_BASICPROCESS_INFORMATION, *PSYSTEM_BASICPROCESS_INFORMATION;
|
||||
|
||||
typedef struct _SYSTEM_HANDLECOUNT_INFORMATION {
|
||||
ULONG ProcessCount;
|
||||
ULONG ThreadCount;
|
||||
ULONG HandleCount;
|
||||
} SYSTEM_HANDLECOUNT_INFORMATION, *PSYSTEM_HANDLECOUNT_INFORMATION;
|
||||
|
||||
NTSYSAPI NTSTATUS NTAPI NtDelayExecution(_In_ BOOLEAN Alertable, _In_ PLARGE_INTEGER DelayInterval);
|
||||
|
||||
/**
|
||||
@@ -691,15 +707,19 @@ static inline uint64_t ffKSystemTimeToUInt64(const volatile KSYSTEM_TIME* pTime)
|
||||
|
||||
static inline bool ffIsWindows10OrGreater() {
|
||||
#if FF_WIN81_COMPAT
|
||||
return SharedUserData->NtMajorVersion >= 10;
|
||||
return SharedUserData->NtBuildNumber >= 10240;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool ffIsWindows11OrGreater() {
|
||||
return SharedUserData->NtMajorVersion > 10 ||
|
||||
(SharedUserData->NtMajorVersion == 10 && SharedUserData->NtBuildNumber >= 22000);
|
||||
return SharedUserData->NtBuildNumber >= 22000;
|
||||
}
|
||||
|
||||
static inline bool ffIsSystemBasicProcessInfoAvailable() { // Includes HandleCountInformation, which was added together
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation#systembasicprocessinformation
|
||||
return SharedUserData->NtBuildNumber >= 26200; // MSDN says it was added in 26100.4770; ntdoc says 25H2
|
||||
}
|
||||
|
||||
NTSYSAPI NTSTATUS NTAPI NtOpenProcessToken(
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -62,6 +62,10 @@ const char* ffCPUAppleCodeToName(uint32_t code) {
|
||||
return "Apple M4 Pro";
|
||||
case 6041:
|
||||
return "Apple M4 Max";
|
||||
case 8142:
|
||||
return "Apple M5";
|
||||
case 6050:
|
||||
return "Apple M5 Pro / Max";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -595,6 +619,9 @@ static const char* parseCpuInfo(
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "cpu :", &cpu->name)) ||
|
||||
#elif __sh__
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "cpu type :", &cpu->name)) ||
|
||||
#elif __sparc__ || __sparc
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "cpu :", &cpu->name)) ||
|
||||
(cpuMHz->length == 0 && ffParsePropLine(line, "Cpu0ClkTck :", cpuMHz)) ||
|
||||
#else
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) ||
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "model :", &cpu->name)) ||
|
||||
@@ -688,7 +715,7 @@ static bool detectFrequency(FFCPUResult* cpu, const FFCPUOptions* options) {
|
||||
while ((p = memmem(p, cpuinfo->length - (uint32_t) (p - cpuinfo->chars), "\nphysical id\t:", strlen("\nphysical id\t:")))) {
|
||||
p += strlen("\nphysical id\t:");
|
||||
char* pend;
|
||||
unsigned long long id = strtoul(p, &pend, 10);
|
||||
unsigned long id = strtoul(p, &pend, 10);
|
||||
if (__builtin_expect(id > 64, false)) { // Do 129-socket boards exist?
|
||||
high |= 1ULL << (id - 64);
|
||||
} else {
|
||||
@@ -813,6 +840,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<gen>[e|p]<part><rev>`, 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 +981,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));
|
||||
// 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 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);
|
||||
}
|
||||
|
||||
ffStrbufSetStatic(&cpu->vendor, "Qualcomm");
|
||||
@@ -1040,6 +1104,10 @@ static const char* detectPhysicalCores(FFCPUResult* cpu) {
|
||||
if (cpu->name.length) {
|
||||
ffStrbufPrependS(&cpu->name, "Machine ");
|
||||
}
|
||||
#elif __sparc__ || __sparc
|
||||
// Cpu0ClkTck is in Hz, printed as "%016lx" by sparc64 and "%ld" by sparc32. A 32-bit userland can run
|
||||
// on a 64-bit kernel, so take the base from the width of the value rather than from our own bitness.
|
||||
cpu->frequencyBase = (uint32_t) (strtoull(cpuMHz.chars, nullptr, cpuMHz.length == 16 ? 16 : 10) / 1000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#elif __OpenBSD__
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <kvm.h>
|
||||
#elif __sun
|
||||
#include <procfs.h>
|
||||
#elif __NetBSD__
|
||||
@@ -311,25 +310,33 @@ static const char* getFromProcesses(FFDisplayServerResult* result) {
|
||||
}
|
||||
}
|
||||
#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, (int) userId, sizeof(*proc), &count);
|
||||
if (proc) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (result->dePrettyName.length == 0) {
|
||||
applyPrettyNameIfDE(result, proc[i].p_comm);
|
||||
}
|
||||
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, (int) userId, (int) sizeof(struct kinfo_proc), 0 };
|
||||
size_t length = 0;
|
||||
|
||||
if (result->wmPrettyName.length == 0) {
|
||||
applyNameIfWM(result, proc[i].p_comm);
|
||||
}
|
||||
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
|
||||
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, nullptr) failed";
|
||||
}
|
||||
|
||||
if (result->dePrettyName.length > 0 && result->wmPrettyName.length > 0) {
|
||||
break;
|
||||
}
|
||||
FF_AUTO_FREE struct kinfo_proc* procs = (struct kinfo_proc*) malloc(length);
|
||||
request[5] = (int) (length / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
|
||||
if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) {
|
||||
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, procs) failed";
|
||||
}
|
||||
|
||||
int count = (int) (length / sizeof(struct kinfo_proc));
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (result->dePrettyName.length == 0) {
|
||||
applyPrettyNameIfDE(result, procs[i].p_comm);
|
||||
}
|
||||
|
||||
if (result->wmPrettyName.length == 0) {
|
||||
applyNameIfWM(result, procs[i].p_comm);
|
||||
}
|
||||
|
||||
if (result->dePrettyName.length > 0 && result->wmPrettyName.length > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
kvm_close(kd);
|
||||
#elif __sun
|
||||
FF_AUTO_CLOSE_DIR DIR* procdir = opendir("/proc");
|
||||
if (procdir == nullptr) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
|
||||
ffStrbufAppendS(deviceDir, "/revision");
|
||||
if (ffReadFileBuffer(deviceDir->chars, buffer)) {
|
||||
char* pend;
|
||||
uint64_t revision = strtoul(buffer->chars, &pend, 16);
|
||||
uint32_t revision = (uint32_t) strtoul(buffer->chars, &pend, 16);
|
||||
if (pend != buffer->chars) {
|
||||
ffGPUQueryAmdGpuName((uint16_t) deviceId, (uint8_t) revision, gpu);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ static int walkDevTree(di_node_t node, [[maybe_unused]] di_minor_t minor, FFlist
|
||||
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
|
||||
gpu->type = FF_GPU_TYPE_UNKNOWN;
|
||||
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
|
||||
gpu->deviceId = strtoul(di_bus_addr(node), nullptr, 16);
|
||||
gpu->deviceId = (uint64_t) strtoull(di_bus_addr(node), nullptr, 16);
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ static void detectGTKFromSettings(FFGTKResult* result) {
|
||||
} 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 synthesises the schema default for keys
|
||||
// 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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -7,32 +7,65 @@
|
||||
#include <winternl.h>
|
||||
#include <wchar.h>
|
||||
|
||||
static bool fillResult(FFInitSystemResult* result, uint32_t ppid, uint32_t pid, uint16_t len, PCWSTR name) {
|
||||
if (ppid != 4 /* System */ || len <= 4 || _wcsnicmp(name + len - 4, L".exe", 4) != 0) { // smss.exe
|
||||
return false;
|
||||
}
|
||||
result->pid = pid;
|
||||
// We have no permission to open the process for querying the full information
|
||||
wchar_t exePath[MAX_PATH];
|
||||
_snwprintf(exePath, ARRAY_SIZE(exePath), L"%ls\\system32\\%.*ls", (const wchar_t*) SharedUserData->NtSystemRoot, len, name);
|
||||
ffGetFileVersion(exePath, NULL, &result->version);
|
||||
ffStrbufSetWS(&result->exe, exePath);
|
||||
ffStrbufSetNWS(&result->name, len - 4, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* ffDetectInitSystem(FFInitSystemResult* result) {
|
||||
// We only need to find the first user process, so 1024 entries should be enough
|
||||
SYSTEM_PROCESS_INFORMATION buffer[1024] = {};
|
||||
ULONG size = sizeof(buffer);
|
||||
NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, buffer, size, &size);
|
||||
if (status != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(status)) {
|
||||
return "NtQuerySystemInformation(SystemProcessInformation) failed";
|
||||
if (ffIsSystemBasicProcessInfoAvailable()) {
|
||||
// SYSTEM_BASICPROCESS_INFORMATION entries are much smaller than SYSTEM_PROCESS_INFORMATION ones,
|
||||
// so a modest buffer should be enough to contain all processes
|
||||
SYSTEM_BASICPROCESS_INFORMATION buffer[1024];
|
||||
NTSTATUS status = NtQuerySystemInformation(SystemBasicProcessInformation, buffer, sizeof(buffer), NULL);
|
||||
if (status != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(status)) {
|
||||
goto fallback;
|
||||
}
|
||||
for (auto ptr = buffer; ;ptr = (PSYSTEM_BASICPROCESS_INFORMATION) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
|
||||
assert(ptr >= buffer && (uint8_t*) ptr < (uint8_t*) buffer + sizeof(buffer));
|
||||
if (fillResult(result, (uint32_t)(uintptr_t) ptr->InheritedFromUniqueProcessId,
|
||||
(uint32_t)(uintptr_t) ptr->UniqueProcessId,
|
||||
ptr->ImageName.Length / sizeof(*ptr->ImageName.Buffer),
|
||||
ptr->ImageName.Buffer)) {
|
||||
return nullptr;
|
||||
}
|
||||
// The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
|
||||
if (!ptr->NextEntryOffset) {
|
||||
return "Could not find init system process";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (SYSTEM_PROCESS_INFORMATION* ptr = buffer; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
|
||||
assert(ptr >= buffer && (uint8_t*) ptr < (uint8_t*) buffer + sizeof(buffer));
|
||||
uint16_t len = ptr->ImageName.Length / sizeof(*ptr->ImageName.Buffer);
|
||||
if (ptr->InheritedFromUniqueProcessId == (HANDLE)(uintptr_t) 4 /* System */ &&
|
||||
len > 4 && _wcsnicmp(ptr->ImageName.Buffer + len - 4, L".exe", 4) == 0) { // smss.exe
|
||||
result->pid = (uint32_t)(uintptr_t) ptr->UniqueProcessId;
|
||||
// We have no permission to open the process for querying the full information
|
||||
wchar_t exePath[MAX_PATH];
|
||||
_snwprintf(exePath, ARRAY_SIZE(exePath), L"%ls\\system32\\%.*ls", (const wchar_t*) SharedUserData->NtSystemRoot, len, ptr->ImageName.Buffer);
|
||||
ffGetFileVersion(exePath, NULL, &result->version);
|
||||
ffStrbufSetWS(&result->exe, exePath);
|
||||
ffStrbufSetNWS(&result->name, len - 4, ptr->ImageName.Buffer);
|
||||
return nullptr;
|
||||
fallback:
|
||||
{
|
||||
// We only need to find the first user process, so 1024 entries should be enough
|
||||
SYSTEM_PROCESS_INFORMATION buffer[1024] = {};
|
||||
ULONG size = sizeof(buffer);
|
||||
NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, buffer, size, &size);
|
||||
if (status != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(status)) {
|
||||
return "NtQuerySystemInformation(SystemProcessInformation) failed";
|
||||
}
|
||||
// The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
|
||||
if (!ptr->NextEntryOffset) {
|
||||
return "Could not find init system process";
|
||||
|
||||
for (auto ptr = buffer; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
|
||||
assert(ptr >= buffer && (uint8_t*) ptr < (uint8_t*) buffer + sizeof(buffer));
|
||||
uint16_t len = ptr->ImageName.Length / sizeof(*ptr->ImageName.Buffer);
|
||||
if (fillResult(result, (uint32_t)(uintptr_t) ptr->InheritedFromUniqueProcessId,
|
||||
(uint32_t)(uintptr_t) ptr->UniqueProcessId, len, ptr->ImageName.Buffer)) {
|
||||
return nullptr;
|
||||
}
|
||||
// The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
|
||||
if (!ptr->NextEntryOffset) {
|
||||
return "Could not find init system process";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-13
@@ -18,7 +18,6 @@
|
||||
#elif __OpenBSD__
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <kvm.h>
|
||||
#elif __sun
|
||||
#include <procfs.h>
|
||||
#elif __NetBSD__
|
||||
@@ -55,7 +54,7 @@ static const char* getSshdVersion(FFstrbuf* version) {
|
||||
#ifdef FF_HAVE_ZLIB
|
||||
#include "common/library.h"
|
||||
#include "common/path.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <zlib.h>
|
||||
|
||||
@@ -274,19 +273,27 @@ const char* detectByProcesses(FFLMResult* result) {
|
||||
}
|
||||
}
|
||||
#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;
|
||||
}
|
||||
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, 0, (int) sizeof(struct kinfo_proc), 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);
|
||||
request[5] = (int) (length / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
|
||||
if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) {
|
||||
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, procs) failed";
|
||||
}
|
||||
|
||||
int count = (int) (length / sizeof(struct kinfo_proc));
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const char* lm = testLms(procs[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) {
|
||||
|
||||
@@ -12,6 +12,7 @@ typedef struct FFPackagesResult {
|
||||
uint32_t brewCask;
|
||||
uint32_t cards;
|
||||
uint32_t choco;
|
||||
uint32_t crux;
|
||||
uint32_t dpkg;
|
||||
uint32_t emerge;
|
||||
uint32_t eopkg;
|
||||
|
||||
@@ -487,6 +487,9 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts,
|
||||
if (FF_PACKAGES_IS_ENABLED(options, APK)) {
|
||||
packageCounts->apk += getNumStrings(baseDir, "/lib/apk/db/installed", "C:Q", "apk");
|
||||
}
|
||||
if (FF_PACKAGES_IS_ENABLED(options, CRUX)) {
|
||||
packageCounts->crux += getNumStrings(baseDir, "/var/lib/pkg/db", "\n\n", "crux");
|
||||
}
|
||||
if (FF_PACKAGES_IS_ENABLED(options, DPKG)) {
|
||||
packageCounts->dpkg += getNumStrings(baseDir, "/var/lib/dpkg/status", "Status: install ok installed", "dpkg");
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ static void parsePhysicalDisk(int dfd, const char* devName, FFPhysicalDiskOption
|
||||
ssize_t fileSize = ffReadFileDataRelative(dfd, "size", ARRAY_SIZE(blkSize) - 1, blkSize);
|
||||
if (fileSize > 0) {
|
||||
blkSize[fileSize] = 0;
|
||||
size = (uint64_t) strtoul(blkSize, nullptr, 10) * 512;
|
||||
size = (uint64_t) strtoull(blkSize, nullptr, 10) * 512;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ static void appendDevice(
|
||||
if (size)
|
||||
{
|
||||
char* unit = nullptr;
|
||||
device->size = strtoul(size.UTF8String, &unit, 10);
|
||||
device->size = (uint64_t) strtoull(size.UTF8String, &unit, 10);
|
||||
if (*unit == ' ') ++unit;
|
||||
|
||||
switch (*unit)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
|
||||
|
||||
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;
|
||||
if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) == sizeof(taskInfo)) {
|
||||
// NOTE: This fails for system processes
|
||||
result->threads += (uint32_t) taskInfo.pti_threadnum;
|
||||
}
|
||||
result->threads += taskInfo.pti_threadnum;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -29,13 +29,12 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
|
||||
continue;
|
||||
}
|
||||
|
||||
++result->processes;
|
||||
if (proc_stat_has(stat, PSTAT_NUM_THREADS)) {
|
||||
result->threads += proc_stat_num_threads(stat);
|
||||
}
|
||||
}
|
||||
|
||||
result->processes = (uint32_t) list->num_procs;
|
||||
|
||||
done:
|
||||
if (list) {
|
||||
proc_stat_list_free(list);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "processes.h"
|
||||
|
||||
const char* ffDetectProcesses([[maybe_unused]] FFProcessesResult* result) {
|
||||
const char* ffDetectProcesses([[maybe_unused]] const FFProcessesOptions* options, [[maybe_unused]] FFProcessesResult* result) {
|
||||
return "Not supported on this platform";
|
||||
}
|
||||
|
||||
@@ -4,24 +4,22 @@
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <kvm.h>
|
||||
|
||||
const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) {
|
||||
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
|
||||
if (!kd) {
|
||||
return "kvm_open() failed";
|
||||
int request[] = { CTL_KERN, KERN_PROC, (options->countKprocs ? KERN_PROC_KTHREAD : KERN_PROC_ALL) | KERN_PROC_SHOW_THREADS, 0, (int) sizeof(struct kinfo_proc), 0 };
|
||||
size_t length = 0;
|
||||
|
||||
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
|
||||
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}, nullptr) failed";
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
// KERN_PROC_ALL returns all user-level processes, excluding kernel processes and threads
|
||||
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";
|
||||
FF_AUTO_FREE struct kinfo_proc* procs = (struct kinfo_proc*) malloc(length);
|
||||
request[5] = (int) (length / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
|
||||
if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) {
|
||||
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}, procs) failed";
|
||||
}
|
||||
|
||||
int count = (int) (length / sizeof(struct kinfo_proc));
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const struct kinfo_proc* proc = &procs[i];
|
||||
|
||||
@@ -31,6 +29,5 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
|
||||
}
|
||||
}
|
||||
|
||||
kvm_close(kd);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
#include "processes.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <ntstatus.h>
|
||||
#include <winternl.h>
|
||||
|
||||
const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) {
|
||||
if (options->countKprocs && ffIsSystemBasicProcessInfoAvailable()) {
|
||||
// SystemHandleCountInformation reports the total process/thread counts directly,
|
||||
// so we don't need to walk the whole process table.
|
||||
SYSTEM_HANDLECOUNT_INFORMATION info = {}; // Seems that kernel only fills the lower 32 bits of the counts, leave the upper 32 bits untouched.
|
||||
if (NT_SUCCESS(NtQuerySystemInformation(SystemHandleCountInformation, &info, sizeof(info), NULL))) {
|
||||
result->processes = info.ProcessCount;
|
||||
result->threads = info.ThreadCount;
|
||||
return nullptr;
|
||||
}
|
||||
// Otherwise fall back to walking the process table
|
||||
}
|
||||
|
||||
FF_AUTO_FREE SYSTEM_PROCESS_INFORMATION* pstart = nullptr;
|
||||
|
||||
// Multiple attempts in case processes change while
|
||||
@@ -26,11 +39,10 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
|
||||
}
|
||||
|
||||
for (SYSTEM_PROCESS_INFORMATION* ptr = pstart; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
|
||||
if (!options->countKprocs && ptr->CreateTime.QuadPart == 0) {
|
||||
continue;
|
||||
if (options->countKprocs || (uintptr_t) ptr->InheritedFromUniqueProcessId > 0) {
|
||||
++result->processes;
|
||||
result->threads += ptr->NumberOfThreads;
|
||||
}
|
||||
++result->processes;
|
||||
result->threads += ptr->NumberOfThreads;
|
||||
if (ptr->NextEntryOffset == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#include "sound.h"
|
||||
|
||||
const char* ffDetectSound([[maybe_unused]] FFlist* devices /* List of FFSoundDevice */) {
|
||||
return "Not supported on this platform";
|
||||
}
|
||||
@@ -19,11 +19,11 @@ static const char* detectByProcMeminfo(FFlist* result) {
|
||||
|
||||
char* token = nullptr;
|
||||
if ((token = strstr(buf, "SwapTotal:")) != nullptr) {
|
||||
swapTotal = strtoul(token + strlen("SwapTotal:"), nullptr, 10);
|
||||
swapTotal = (uint64_t) strtoull(token + strlen("SwapTotal:"), nullptr, 10);
|
||||
}
|
||||
|
||||
if ((token = strstr(buf, "SwapFree:")) != nullptr) {
|
||||
swapFree = strtoul(token + strlen("SwapFree:"), nullptr, 10);
|
||||
swapFree = (uint64_t) strtoull(token + strlen("SwapFree:"), nullptr, 10);
|
||||
}
|
||||
|
||||
FFSwapResult* swap = FF_LIST_ADD(FFSwapResult, *result);
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
const char* detectTermux(FFTerminalFontResult* terminalFont) {
|
||||
FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate();
|
||||
// SharedPreferences XML: <string name="fontsize">14</string>, in px
|
||||
if (ffParsePropFile(FF_TERMUX_PREF_PATH, "<string name=\"fontsize\">", &fontSize)) {
|
||||
ffParsePropFile(FF_TERMUX_PREF_PATH, "<string name=\"fontsize\">", &fontSize);
|
||||
if (fontSize.length > 0) {
|
||||
ffStrbufAppendS(&fontSize, "px");
|
||||
}
|
||||
|
||||
|
||||
+18
-2
@@ -53,6 +53,18 @@ static int compareDiskWriteResults(const FFTopProcessResult* a, const FFTopProce
|
||||
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) {
|
||||
@@ -61,8 +73,8 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Memory usage is instantaneous; when neither CPU time nor disk IO counters
|
||||
// are requested, a single snapshot suffices and no sampling wait is needed.
|
||||
// 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) {
|
||||
@@ -86,6 +98,7 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
|
||||
item->bytesWritten = 0;
|
||||
item->cpuPercent = 0;
|
||||
item->startTime = snap->startTime;
|
||||
item->threads = snap->threads;
|
||||
ffStrbufInitMove(&item->name, &snap->name);
|
||||
}
|
||||
} else {
|
||||
@@ -135,6 +148,7 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -147,6 +161,8 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
|
||||
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) {
|
||||
|
||||
@@ -10,6 +10,7 @@ typedef struct FFTopProcessResult {
|
||||
uint64_t bytesRead;
|
||||
uint64_t bytesWritten;
|
||||
uint64_t startTime;
|
||||
uint32_t threads;
|
||||
uint32_t pid;
|
||||
} FFTopProcessResult;
|
||||
|
||||
@@ -20,6 +21,7 @@ typedef struct FFTopProcessSnapshot {
|
||||
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;
|
||||
|
||||
|
||||
@@ -5,55 +5,49 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <libproc.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
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";
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
|
||||
int npids = proc_listallpids(nullptr, 0);
|
||||
if (npids <= 0) {
|
||||
return "proc_listallpids(nullptr, 0) failed";
|
||||
}
|
||||
FF_AUTO_FREE pid_t* pids = malloc((uint32_t) (npids + npids / 8 + 1) * sizeof(pid_t));
|
||||
npids = proc_listallpids(pids, npids);
|
||||
if (npids <= 0) {
|
||||
return "proc_listallpids(pids, bufferSize) 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 count = (uint32_t) npids;
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const struct kinfo_proc* proc = &processes[i];
|
||||
if (proc->kp_proc.p_flag & P_SYSTEM) {
|
||||
pid_t pid = pids[i];
|
||||
|
||||
struct proc_taskallinfo proc;
|
||||
if (proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &proc, sizeof(proc)) != sizeof(proc)) {
|
||||
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
|
||||
if (proc.pbsd.pbi_flags & PROC_FLAG_SYSTEM) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots);
|
||||
ffStrbufInitS(&item->name, proc->kp_proc.p_comm);
|
||||
ffStrbufInitS(&item->name, proc.pbsd.pbi_name);
|
||||
if (item->name.length == 0) {
|
||||
ffStrbufInitS(&item->name, proc.pbsd.pbi_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->cpuTime = (proc.ptinfo.pti_total_user + proc.ptinfo.pti_total_system) / 1000000u; // ns -> ms
|
||||
item->memBytes = proc.ptinfo.pti_resident_size;
|
||||
item->startTime = proc.pbsd.pbi_start_tvsec * 1000u + proc.pbsd.pbi_start_tvusec / 1000u; // convert to ms
|
||||
item->threads = (uint32_t) proc.ptinfo.pti_threadnum;
|
||||
|
||||
if (showTypes & FF_TOP_TYPE_DISK) {
|
||||
struct rusage_info_v2 rusage;
|
||||
if (proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t*) &rusage) == 0) {
|
||||
item->bytesRead = rusage.ri_diskio_bytesread;
|
||||
item->bytesWritten = rusage.ri_diskio_byteswritten;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
|
||||
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC };
|
||||
size_t length;
|
||||
|
||||
@@ -51,6 +51,7 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
|
||||
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;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
|
||||
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
|
||||
size_t length;
|
||||
|
||||
@@ -52,6 +52,7 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
|
||||
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;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <string.h>
|
||||
#include <ps.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
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";
|
||||
@@ -24,7 +24,7 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
|
||||
// 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) != 0) {
|
||||
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;
|
||||
}
|
||||
@@ -57,6 +57,16 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <OS.h>
|
||||
#include <string.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
|
||||
int32 cookie = 0;
|
||||
team_info team;
|
||||
while (get_next_team_info(&cookie, &team) == B_OK) {
|
||||
@@ -41,6 +41,7 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
// 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;
|
||||
|
||||
@@ -24,8 +24,6 @@ static bool parseStat(const char* buffer, size_t length, FFTopProcessSnapshot* r
|
||||
return false;
|
||||
}
|
||||
|
||||
result->cpuTime = 0;
|
||||
|
||||
char* p;
|
||||
for (uint32_t field = 3; field <= 22 /*start time*/; ++field) {
|
||||
switch (field) {
|
||||
@@ -43,6 +41,11 @@ static bool parseStat(const char* buffer, size_t length, FFTopProcessSnapshot* r
|
||||
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;
|
||||
@@ -128,6 +131,9 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
|
||||
|
||||
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);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <errno.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
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;
|
||||
|
||||
@@ -50,6 +50,14 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
|
||||
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;
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
#include "top.h"
|
||||
|
||||
#include "common/mallocHelper.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h> // DEV_BSIZE
|
||||
#include <sys/sysctl.h>
|
||||
#include <kvm.h>
|
||||
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
|
||||
if (!kd) {
|
||||
return "kvm_open() failed";
|
||||
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
|
||||
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0, (int) sizeof(struct kinfo_proc), 0 };
|
||||
size_t length = 0;
|
||||
|
||||
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
|
||||
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}, nullptr) 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";
|
||||
FF_AUTO_FREE struct kinfo_proc* processes = (struct kinfo_proc*) malloc(length);
|
||||
request[5] = (int) (length / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
|
||||
if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) {
|
||||
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}, processes) failed";
|
||||
}
|
||||
|
||||
int count = (int) (length / sizeof(struct kinfo_proc));
|
||||
const uint32_t pageSize = instance.state.platform.sysinfo.pageSize;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
@@ -37,8 +39,28 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
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 threadRequest[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL | KERN_PROC_SHOW_THREADS, 0, (int) sizeof(struct kinfo_proc), 0 };
|
||||
size_t threadLength = 0;
|
||||
if (sysctl(threadRequest, ARRAY_SIZE(threadRequest), nullptr, &threadLength, nullptr, 0) == 0 && threadLength > 0) {
|
||||
FF_AUTO_FREE struct kinfo_proc* threads = (struct kinfo_proc*) malloc(threadLength);
|
||||
threadRequest[5] = (int) (threadLength / sizeof(struct kinfo_proc)); // count must be non-zero for data fetch
|
||||
if (sysctl(threadRequest, ARRAY_SIZE(threadRequest), threads, &threadLength, nullptr, 0) == 0) {
|
||||
int threadCount = (int) (threadLength / sizeof(struct kinfo_proc));
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
|
||||
|
||||
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)) {
|
||||
|
||||
@@ -30,7 +30,7 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
|
||||
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 && info->CreateTime.QuadPart > 0) {
|
||||
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;
|
||||
@@ -38,6 +38,7 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
|
||||
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) {
|
||||
|
||||
@@ -5,79 +5,125 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
const char* ffDetectWallpaper(FFstrbuf* result)
|
||||
{
|
||||
{
|
||||
// Reliable for user-picked static images.
|
||||
NSURL* url = [NSWorkspace.sharedWorkspace desktopImageURLForScreen:NSScreen.mainScreen];
|
||||
if (url.fileURL && ![url.path isEqualToString:@"/System/Library/CoreServices/DefaultDesktop.heic"] /* dynamic wallpapers */)
|
||||
{
|
||||
ffStrbufSetS(result, url.path.UTF8String);
|
||||
return nullptr;
|
||||
}
|
||||
const char* detectFromPlist(FFstrbuf* result) {
|
||||
// For Sonoma and later (macOS 14.0+)
|
||||
// https://github.com/JohnCoates/Aerial/issues/1332
|
||||
NSError* error;
|
||||
NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Application Support/com.apple.wallpaper/Store/Index.plist", instance.state.platform.homeDir.chars];
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName]
|
||||
error:&error];
|
||||
|
||||
if (error) {
|
||||
return "Failed to read wallpaper plist file";
|
||||
}
|
||||
|
||||
{
|
||||
// For Sonoma
|
||||
// https://github.com/JohnCoates/Aerial/issues/1332
|
||||
NSError* error;
|
||||
NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Application Support/com.apple.wallpaper/Store/Index.plist", instance.state.platform.homeDir.chars];
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName]
|
||||
error:&error];
|
||||
if (!error)
|
||||
{
|
||||
NSArray* choices = [dict valueForKeyPath:@"SystemDefault.Desktop.Content.Choices"];
|
||||
if (choices.count > 0)
|
||||
{
|
||||
NSDictionary* choice = choices[0];
|
||||
NSArray* files = choice[@"Files"];
|
||||
if (files.count > 0)
|
||||
{
|
||||
NSString* file = files[0][@"relative"];
|
||||
ffStrbufAppendS(result, [NSURL URLWithString:file].path.UTF8String);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* provider = choice[@"Provider"];
|
||||
NSString* builtinPrefix = @"com.apple.wallpaper.choice.";
|
||||
if ([provider hasPrefix:builtinPrefix])
|
||||
provider = [provider substringFromIndex:builtinPrefix.length];
|
||||
if ([provider isEqualToString:@"sonoma"])
|
||||
ffStrbufSetStatic(result, "macOS Sonoma");
|
||||
else if ([provider isEqualToString:@"aerials"]) // Most builtin aerial wallpapers are private
|
||||
ffStrbufSetStatic(result, "Built-in aerial photography");
|
||||
else
|
||||
ffStrbufAppendF(result, "Built-in %s wallpaper", provider.UTF8String);
|
||||
NSArray* choices = [dict valueForKeyPath:@"SystemDefault.Desktop.Content.Choices"];
|
||||
if (choices.count > 0) {
|
||||
NSDictionary* choice = choices[0];
|
||||
|
||||
NSArray* files = choice[@"Files"];
|
||||
if (files.count > 0) {
|
||||
NSString* file = files[0][@"relative"];
|
||||
ffStrbufSetS(result, [NSURL URLWithString:file].path.UTF8String);
|
||||
}
|
||||
|
||||
if (result->length == 0) {
|
||||
NSData* configData = choice[@"Configuration"];
|
||||
if (configData && configData.length > 0) {
|
||||
NSError* plistError = nil;
|
||||
NSDictionary* configPlist = [NSPropertyListSerialization propertyListWithData:configData
|
||||
options:NSPropertyListImmutable
|
||||
format:NULL
|
||||
error:&plistError];
|
||||
|
||||
if (!plistError && [configPlist isKindOfClass:NSDictionary.class]) {
|
||||
NSDictionary* urlDict = configPlist[@"url"];
|
||||
if ([urlDict isKindOfClass:NSDictionary.class]) {
|
||||
NSString* relativeUrlString = urlDict[@"relative"];
|
||||
if ([relativeUrlString isKindOfClass:[NSString class]]) {
|
||||
NSURL* fileUrl = [NSURL URLWithString:relativeUrlString];
|
||||
if (fileUrl.fileURL) {
|
||||
ffStrbufSetS(result, fileUrl.path.UTF8String);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result->length == 0) {
|
||||
NSString* provider = choice[@"Provider"];
|
||||
if ([provider isKindOfClass:NSString.class]) {
|
||||
NSString* builtinPrefix = @"com.apple.wallpaper.choice.";
|
||||
if ([provider hasPrefix:builtinPrefix]) {
|
||||
provider = [provider substringFromIndex:builtinPrefix.length];
|
||||
}
|
||||
|
||||
// macOS internal wallpapers
|
||||
if ([provider isEqualToString:@"aerials"]) { // Most builtin aerial wallpapers are private
|
||||
ffStrbufSetStatic(result, "Built-in aerial photography");
|
||||
} else if ([provider isEqualToString:@"default"]) {
|
||||
ffStrbufSetStatic(result, "macOS Default Wallpaper");
|
||||
} else {
|
||||
ffStrbufSetF(result, "Built-in %s wallpaper", provider.UTF8String);
|
||||
}
|
||||
}
|
||||
if (result->length > 0)
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (result->length == 0) {
|
||||
return "Failed to detect wallpaper from plist";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef FF_HAVE_SQLITE3
|
||||
|
||||
{
|
||||
// For Ventura
|
||||
// https://stackoverflow.com/questions/301215/getting-desktop-background-on-mac
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(&instance.state.platform.homeDir);
|
||||
ffStrbufAppendS(&path, "Library/Application Support/Dock/desktoppicture.db");
|
||||
if (ffSettingsGetSQLite3String(path.chars,
|
||||
#ifdef FF_HAVE_SQLITE3
|
||||
const char* detectFromSQLite(FFstrbuf* result) {
|
||||
// For Ventura
|
||||
// https://stackoverflow.com/questions/301215/getting-desktop-background-on-mac
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(&instance.state.platform.homeDir);
|
||||
ffStrbufAppendS(&path, "Library/Application Support/Dock/desktoppicture.db");
|
||||
if (ffSettingsGetSQLite3String(path.chars,
|
||||
"SELECT value\n"
|
||||
"FROM preferences\n"
|
||||
"JOIN data ON preferences.data_id=data.ROWID\n"
|
||||
"JOIN pictures ON preferences.picture_id=pictures.ROWID\n"
|
||||
"JOIN displays ON pictures.display_id=displays.ROWID\n"
|
||||
"JOIN spaces ON pictures.space_id=spaces.ROWID\n"
|
||||
"WHERE display_id=1 AND space_id=1 AND key=1", result)
|
||||
)
|
||||
return nullptr;
|
||||
"WHERE display_id=1 AND space_id=1 AND key=1",
|
||||
result)) {
|
||||
return nullptr;
|
||||
}
|
||||
return "Failed to detect wallpaper from SQLite database";
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* detectFromNSWorkspace(FFstrbuf* result) {
|
||||
// Reliable for user-picked static images.
|
||||
NSScreen* mainScreen = NSScreen.mainScreen;
|
||||
if (!mainScreen) {
|
||||
return "Failed to detect wallpaper from NSWorkspace: No main screen found";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (ffOsascript("tell application \"Finder\" to get POSIX path of (get desktop picture as alias)", result))
|
||||
NSURL* url = [NSWorkspace.sharedWorkspace desktopImageURLForScreen:mainScreen];
|
||||
if (url.fileURL) {
|
||||
ffStrbufSetS(result, url.path.UTF8String);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return "All detection methods failed";
|
||||
return "Failed to detect wallpaper from NSWorkspace";
|
||||
}
|
||||
|
||||
const char* ffDetectWallpaper(FFstrbuf* result) {
|
||||
const char* error;
|
||||
|
||||
if (@available(macOS 14.0, *)) {
|
||||
error = detectFromPlist(result);
|
||||
} else {
|
||||
#ifdef FF_HAVE_SQLITE3
|
||||
error = detectFromSQLite(result);
|
||||
#else
|
||||
error = detectFromNSWorkspace(result);
|
||||
#endif
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -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 <linux/wireless.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !__BIG_ENDIAN__
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/nl80211.h>
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/nl80211.h>
|
||||
|
||||
// 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;
|
||||
@@ -308,19 +308,19 @@ static uint16_t ffWifiChannelWidthToMhz(uint32_t width) {
|
||||
return 80;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
return 160;
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case 6 /* NL80211_CHAN_WIDTH_5 */:
|
||||
return 5;
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
case 7 /* NL80211_CHAN_WIDTH_10 */:
|
||||
return 10;
|
||||
case NL80211_CHAN_WIDTH_1:
|
||||
case 8 /* NL80211_CHAN_WIDTH_1 */:
|
||||
return 1;
|
||||
case NL80211_CHAN_WIDTH_2:
|
||||
case 9 /* NL80211_CHAN_WIDTH_2 */:
|
||||
return 2;
|
||||
case NL80211_CHAN_WIDTH_4:
|
||||
case 10 /* NL80211_CHAN_WIDTH_4 */:
|
||||
return 4;
|
||||
case NL80211_CHAN_WIDTH_8:
|
||||
case 11 /* NL80211_CHAN_WIDTH_8 */:
|
||||
return 8;
|
||||
case NL80211_CHAN_WIDTH_16:
|
||||
case 12 /* NL80211_CHAN_WIDTH_16 */:
|
||||
return 16;
|
||||
case 13 /* NL80211_CHAN_WIDTH_320 */:
|
||||
return 320;
|
||||
@@ -459,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
|
||||
@@ -471,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
|
||||
@@ -528,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
|
||||
@@ -845,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;
|
||||
@@ -1049,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();
|
||||
@@ -1090,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");
|
||||
@@ -1116,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);
|
||||
}
|
||||
|
||||
@@ -114,70 +114,110 @@ static bool isProcessTrusted(DWORD processId, FFProcessType processType, UNICODE
|
||||
|
||||
#define ffStrEqualNWS(str, compareTo) (_wcsnicmp(str, L##compareTo, sizeof(compareTo) - 1) == 0)
|
||||
|
||||
const char* ffDetectWMPlugin(FFstrbuf* pluginName) {
|
||||
// Returns true if the process was recognized as a WM plugin and pluginName was set.
|
||||
static bool handleProcess(FFstrbuf* pluginName, uint32_t pid, const UNICODE_STRING* imageName) {
|
||||
alignas(UNICODE_STRING) uint8_t buffer[4096];
|
||||
UNICODE_STRING* filePath = (UNICODE_STRING*) buffer;
|
||||
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;
|
||||
if (imageName->Length == strlen("FancyWM-GUI.exe") * sizeof(wchar_t) &&
|
||||
ffStrEqualNWS(imageName->Buffer, "FancyWM-GUI.exe") &&
|
||||
isProcessTrusted(pid, FF_PROCESS_TYPE_WINDOWS_STORE | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) {
|
||||
if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) {
|
||||
ffStrbufPrependS(pluginName, "FancyWM ");
|
||||
} else {
|
||||
return "NtQuerySystemInformation(SystemProcessInformation) failed";
|
||||
ffStrbufSetStatic(pluginName, "FancyWM");
|
||||
}
|
||||
return true;
|
||||
} else if (imageName->Length == strlen("glazewm-watcher.exe") * sizeof(wchar_t) &&
|
||||
ffStrEqualNWS(imageName->Buffer, "glazewm-watcher.exe") &&
|
||||
isProcessTrusted(pid, FF_PROCESS_TYPE_SIGNED | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) {
|
||||
if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) {
|
||||
ffStrbufPrependS(pluginName, "GlazeWM ");
|
||||
} else {
|
||||
ffStrbufSetStatic(pluginName, "GlazeWM");
|
||||
}
|
||||
return true;
|
||||
} else if (imageName->Length == strlen("komorebi.exe") * sizeof(wchar_t) &&
|
||||
ffStrEqualNWS(imageName->Buffer, "komorebi.exe") &&
|
||||
isProcessTrusted(pid, FF_PROCESS_TYPE_CUI, filePath, sizeof(buffer))) {
|
||||
if (instance.config.general.detectVersion) {
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateNWS(filePath->Length / sizeof(wchar_t), filePath->Buffer);
|
||||
if (ffProcessAppendStdOut(pluginName, (char* const[]) {
|
||||
path.chars,
|
||||
"--version",
|
||||
nullptr,
|
||||
}) == nullptr) {
|
||||
ffStrbufSubstrBeforeFirstC(pluginName, '\n');
|
||||
}
|
||||
}
|
||||
if (pluginName->length == 0) {
|
||||
ffStrbufSetStatic(pluginName, "Komorebi");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
for (SYSTEM_PROCESS_INFORMATION* ptr = pstart;; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
|
||||
assert(ptr->ImageName.Length == 0 || ptr->ImageName.MaximumLength >= ptr->ImageName.Length + 2); // nullptr terminated
|
||||
if (ptr->ImageName.Length == strlen("FancyWM-GUI.exe") * sizeof(wchar_t) &&
|
||||
ffStrEqualNWS(ptr->ImageName.Buffer, "FancyWM-GUI.exe") &&
|
||||
isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_WINDOWS_STORE | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) {
|
||||
if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) {
|
||||
ffStrbufPrependS(pluginName, "FancyWM ");
|
||||
} else {
|
||||
ffStrbufSetStatic(pluginName, "FancyWM");
|
||||
}
|
||||
break;
|
||||
} else if (ptr->ImageName.Length == strlen("glazewm-watcher.exe") * sizeof(wchar_t) &&
|
||||
ffStrEqualNWS(ptr->ImageName.Buffer, "glazewm-watcher.exe") &&
|
||||
isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_SIGNED | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) {
|
||||
if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) {
|
||||
ffStrbufPrependS(pluginName, "GlazeWM ");
|
||||
} else {
|
||||
ffStrbufSetStatic(pluginName, "GlazeWM");
|
||||
}
|
||||
break;
|
||||
} else if (ptr->ImageName.Length == strlen("komorebi.exe") * sizeof(wchar_t) &&
|
||||
ffStrEqualNWS(ptr->ImageName.Buffer, "komorebi.exe") &&
|
||||
isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_CUI, filePath, sizeof(buffer))) {
|
||||
if (instance.config.general.detectVersion) {
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateNWS(filePath->Length / sizeof(wchar_t), filePath->Buffer);
|
||||
if (ffProcessAppendStdOut(pluginName, (char* const[]) {
|
||||
path.chars,
|
||||
"--version",
|
||||
nullptr,
|
||||
}) == nullptr) {
|
||||
ffStrbufSubstrBeforeFirstC(pluginName, '\n');
|
||||
}
|
||||
}
|
||||
if (pluginName->length == 0) {
|
||||
ffStrbufSetStatic(pluginName, "Komorebi");
|
||||
}
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* ffDetectWMPlugin(FFstrbuf* pluginName) {
|
||||
if (ffIsSystemBasicProcessInfoAvailable()) {
|
||||
// Unlike SystemProcessInformation, SystemBasicProcessInformation only returns processes
|
||||
// (no threads), so a single query with a modest buffer is enough.
|
||||
ULONG size = 0;
|
||||
if (NtQuerySystemInformation(SystemBasicProcessInformation, nullptr, 0, &size) != STATUS_INFO_LENGTH_MISMATCH) {
|
||||
goto fallback;
|
||||
}
|
||||
// The process table may change between the two calls; retry with a larger buffer.
|
||||
size += size / 8 + sizeof(SYSTEM_BASICPROCESS_INFORMATION);
|
||||
FF_AUTO_FREE SYSTEM_BASICPROCESS_INFORMATION* pstart = malloc(size);
|
||||
|
||||
if (!NT_SUCCESS(NtQuerySystemInformation(SystemBasicProcessInformation, pstart, size, &size))) {
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
if (ptr->NextEntryOffset == 0) {
|
||||
break;
|
||||
for (auto ptr = pstart;; ptr = (SYSTEM_BASICPROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
|
||||
assert(ptr >= pstart && (uint8_t*) ptr < (uint8_t*) pstart + size);
|
||||
if (handleProcess(pluginName, (uint32_t)(uintptr_t) ptr->UniqueProcessId, &ptr->ImageName)) {
|
||||
return nullptr;
|
||||
}
|
||||
// The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
|
||||
if (!ptr->NextEntryOffset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fallback:
|
||||
{
|
||||
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 (auto ptr = pstart;; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
|
||||
assert(ptr->ImageName.Length == 0 || ptr->ImageName.MaximumLength >= ptr->ImageName.Length + 2); // nullptr terminated
|
||||
if (handleProcess(pluginName, (uint32_t)(uintptr_t) ptr->UniqueProcessId, &ptr->ImageName)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (ptr->NextEntryOffset == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
@@ -181,6 +181,26 @@ static const FFlogo A[] = {
|
||||
.colorTitle = FF_COLOR_FG_CYAN,
|
||||
},
|
||||
#endif
|
||||
#ifdef FASTFETCH_DATATEXT_LOGO_ALT_ATOMIC
|
||||
// ALT Atomic
|
||||
{
|
||||
.names = { "alt-atomic" },
|
||||
.lines = FASTFETCH_DATATEXT_LOGO_ALT_ATOMIC,
|
||||
.colors = {
|
||||
FF_COLOR_FG_RGB "79;157;207",
|
||||
FF_COLOR_FG_RGB "113;174;213",
|
||||
FF_COLOR_FG_RGB "61;120;158",
|
||||
FF_COLOR_FG_RGB "244;250;252",
|
||||
FF_COLOR_FG_RGB "207;230;243",
|
||||
FF_COLOR_FG_RGB "29;58;76",
|
||||
FF_COLOR_FG_RGB "213;201;181",
|
||||
FF_COLOR_FG_RGB "150;180;193",
|
||||
FF_COLOR_FG_RGB "161;204;230",
|
||||
},
|
||||
.colorKeys = FF_COLOR_FG_RGB "80;157;207",
|
||||
.colorTitle = FF_COLOR_FG_RGB "255;209;164",
|
||||
},
|
||||
#endif
|
||||
#ifdef FASTFETCH_DATATEXT_LOGO_ALTLINUX
|
||||
// ALTLinux
|
||||
{
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
$6.:$3-==$1++++++$3==-$6:.
|
||||
$6.:$3=$1++++++++++++++++++$3=$6:.
|
||||
$6:$3=$1+++++++++++$2*$1++++++++++++$3=$6:
|
||||
$6.$3=$1+++++++++++$9%$4@@@$5@$9#$1+++++++++++$3=$6.
|
||||
$3-$1+++++++++++$2+$4@@$9%$1+$2*$5@$4@$9#$1++++++++++++$3-
|
||||
$3=$1++++++++++++$5%$4@$9%$1++++$5@$4@$2*$1++++++++++++$3=
|
||||
$3-$1+++++$8*******$9#$4@@$2*$1++++$9#$4@$5%$2*****$1++++++++$3-
|
||||
$6:$1+++++$7#$8*$1++++++$5%$4@$5%$9#$5%$4@@@@@@@$5@%%%%$9%$2*$1++++++$6:
|
||||
$3=$1+++++$8*$7%$8*$1+++$2*$9%$4@@@@@$5%$9###$5%$9%$8#*$2*$1+++$2**$1++++++$3=
|
||||
$1++++++++$7#%$5%$4@@$5@@%$9#$1+$2*$7#$8#$1++$5%@$2*+$8*#*$2***$1+++++++
|
||||
$1+++++++$9#$4@@$5%$7%%#$5%@$2*$1++$8#*$1+$2*$5@%$2*$1+$2**$8#$7##$1++++++++
|
||||
$3=$1+++++$5%$4@$2*$1+=+$2+$8*$4@@$7%%#$8**$2*$8*$5@%$2**$1++++$7#%$1++++++$3=
|
||||
$6:$1+++++$9##$2****$1++$5%$4@$5%$2*$8*$7###%%%#######$8*$1++++++$6:
|
||||
$3-$1++++++$2****$1++$9#$4@@$2*$1++++$5%$4@$5%$2*$8***$2**$1+++++++$3-
|
||||
$3=$1++++++++++++$5@$4@$5%$1++++$2*$9#$2*$1++++++++++++$3=
|
||||
$3-$1+++++++++++$9#$4@@$9#$1+++++++++++++++++$3-
|
||||
$6.$3=$1++++++++++$9#$5@$4@$5@$9#$1+++++++++++++$3=$6.
|
||||
$6:$3=$1+++++++++$2*$9###$1+++++++++++$3=$6:
|
||||
$6.:$3=$1++++++++++++++++++$3=$6:.
|
||||
$6.:$3-==$1++++++$3==-$6:.
|
||||
@@ -3,18 +3,6 @@
|
||||
#include "common/color.h"
|
||||
|
||||
static const FFlogo Z[] = {
|
||||
#ifdef FASTFETCH_DATATEXT_LOGO_ZERENE
|
||||
// Zerene
|
||||
{
|
||||
.names = { "Zerene" },
|
||||
.lines = FASTFETCH_DATATEXT_LOGO_ZERENE,
|
||||
.colors = {
|
||||
FF_COLOR_FG_BLUE,
|
||||
},
|
||||
.colorKeys = FF_COLOR_FG_BLUE,
|
||||
.colorTitle = FF_COLOR_FG_BLUE,
|
||||
},
|
||||
#endif
|
||||
#ifdef FASTFETCH_DATATEXT_LOGO_ZORIN
|
||||
// Zorin
|
||||
{
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
MMM
|
||||
MMMM
|
||||
MMMMM
|
||||
MMMMMM
|
||||
MMMMMMa
|
||||
MMMMMMMa
|
||||
MMMMMMM
|
||||
MMMMMM
|
||||
MMMM
|
||||
M
|
||||
oxdo aMa
|
||||
aMd aMMMMMMMMM
|
||||
aMMMMM MMMMMMMMMMMMMMa
|
||||
MMMMMMMa MMMMMMMMMMMMMMMa
|
||||
MMMMMMMMa
|
||||
MMMMMMMMa
|
||||
MMMMMMM
|
||||
MMMM
|
||||
@@ -15,7 +15,8 @@ FFLogoImageResult ffLogoPrintImageIM6(FFLogoRequestData* requestData) {
|
||||
// clang-format off
|
||||
FF_LIBRARY_LOAD(imageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR,
|
||||
"libMagickCore-6.Q16HDRI" FF_LIBRARY_EXTENSION, 8,
|
||||
"libMagickCore-6.Q16" FF_LIBRARY_EXTENSION, 8
|
||||
"libMagickCore-6.Q16" FF_LIBRARY_EXTENSION, 8,
|
||||
"libMagickCore-6" FF_LIBRARY_EXTENSION, 8
|
||||
)
|
||||
// clang-format on
|
||||
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(imageMagick, ffResizeImage, ResizeImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
|
||||
|
||||
@@ -20,7 +20,8 @@ FFLogoImageResult ffLogoPrintImageIM7(FFLogoRequestData* requestData) {
|
||||
#else
|
||||
FF_LIBRARY_LOAD(imageMagick, FF_LOGO_IMAGE_RESULT_INIT_ERROR,
|
||||
"libMagickCore-7.Q16HDRI" FF_LIBRARY_EXTENSION, 11,
|
||||
"libMagickCore-7.Q16" FF_LIBRARY_EXTENSION, 11
|
||||
"libMagickCore-7.Q16" FF_LIBRARY_EXTENSION, 11,
|
||||
"libMagickCore-7" FF_LIBRARY_EXTENSION, 11
|
||||
)
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
@@ -21,6 +21,7 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin
|
||||
FF_ARG(index, "index"),
|
||||
FF_ARG(result->modelName, "name"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Battery), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ bool ffPrintBios(FFBiosOptions* options) {
|
||||
FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]) {
|
||||
FF_ARG(bios.type, "type"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Bios), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ static void printDevice(FFBluetoothRadioOptions* options, const FFBluetoothRadio
|
||||
FF_ARG(index, "index"),
|
||||
FF_ARG(radio->name, "name"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(BluetoothRadio), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ bool ffPrintBrightness(FFBrightnessOptions* options) {
|
||||
FF_ARG(moduleIndex, "index"),
|
||||
FF_ARG(item->name, "name"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Brightness), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ static void printBtrfs(FFBtrfsOptions* options, FFBtrfsResult* result, uint8_t i
|
||||
FF_ARG(index, "index"),
|
||||
FF_ARG(result->name, "name"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Btrfs), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ static void printCodecLine(const FFCodecOptions* options, uint8_t index, FFstrbu
|
||||
FF_ARG(*gpu, "gpu"),
|
||||
FF_ARG(direction, "direction"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Codec), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ static void printCPUCacheNormal(const FFCPUCacheResult* result, FFCPUCacheOption
|
||||
FF_ARG(index, "index"),
|
||||
FF_ARG(levelStr, "level"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(CPUCache), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ FFModuleBaseInfo ffCustomModuleInfo = {
|
||||
.de = "Benutzerdefiniert",
|
||||
.es = "Personalizado",
|
||||
.fr = "Personnalisé",
|
||||
.gl = "Personalizado",
|
||||
.he = "מותאם אישית",
|
||||
.id = "Kustom",
|
||||
.it = "Personalizzato",
|
||||
|
||||
@@ -45,6 +45,7 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index
|
||||
FF_ARG(disk->filesystem, "filesystem"),
|
||||
FF_ARG(mountpointLink, "mountpoint-link"),
|
||||
FF_ARG(nameLink, "name-link"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Disk), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ static void formatKey(const FFDiskIOOptions* options, FFDiskIOResult* dev, uint3
|
||||
FF_ARG(dev->name, "name"),
|
||||
FF_ARG(dev->devPath, "dev-path"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(DiskIO), "module-name"),
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -111,6 +112,9 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ bool ffPrintDisplay(FFDisplayOptions* options) {
|
||||
FF_ARG(result->name, "name"),
|
||||
FF_ARG(displayType, "type"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Display), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ bool ffPrintLoadavg(FFLoadavgOptions* options) {
|
||||
FF_ARG(index, "index"),
|
||||
FF_ARG(duration, "duration"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Loadavg), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ static void formatKey(const FFLocalIpOptions* options, FFLocalIpResult* ip, uint
|
||||
FF_ARG(ip->name, "ifname"),
|
||||
FF_ARG(ip->mac, "mac"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(LocalIP), "module-name"),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ bool ffPrintMonitor(FFMonitorOptions* options) {
|
||||
FF_ARG(moduleIndex, "index"),
|
||||
FF_ARG(display->name, "name"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Monitor), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ static void formatKey(const FFNetIOOptions* options, FFNetIOResult* inf, uint32_
|
||||
FF_ARG(index, "index"),
|
||||
FF_ARG(inf->name, "name"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(NetIO), "module-name"),
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -126,6 +127,9 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ bool ffPrintOS(FFOSOptions* options) {
|
||||
FF_ARG(instance.state.platform.sysinfo.name, "sysname"),
|
||||
FF_ARG(os->name, "name"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(OS), "module-name"),
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ typedef enum FFPackagesFlags: uint64_t {
|
||||
FF_PACKAGES_FLAG_CARDS_BIT = UINT64_C(1) << 34U,
|
||||
FF_PACKAGES_FLAG_PORG_BIT = UINT64_C(1) << 35U,
|
||||
FF_PACKAGES_FLAG_INSTALLRELEASE_BIT = UINT64_C(1) << 36U,
|
||||
FF_PACKAGES_FLAG_CRUX_BIT = UINT64_C(1) << 37U,
|
||||
} FFPackagesFlags;
|
||||
static_assert(sizeof(FFPackagesFlags) == sizeof(uint64_t), "");
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ bool ffPrintPackages(FFPackagesOptions* options) {
|
||||
}
|
||||
FF_PRINT_PACKAGE(cards)
|
||||
FF_PRINT_PACKAGE(choco)
|
||||
FF_PRINT_PACKAGE(crux)
|
||||
FF_PRINT_PACKAGE(dpkg)
|
||||
FF_PRINT_PACKAGE(emerge)
|
||||
FF_PRINT_PACKAGE(eopkg)
|
||||
@@ -154,6 +155,7 @@ bool ffPrintPackages(FFPackagesOptions* options) {
|
||||
FF_ARG(counts.brewCask, "brew-cask"),
|
||||
FF_ARG(counts.cards, "cards"),
|
||||
FF_ARG(counts.choco, "choco"),
|
||||
FF_ARG(counts.choco, "crux"),
|
||||
FF_ARG(counts.dpkg, "dpkg"),
|
||||
FF_ARG(counts.emerge, "emerge"),
|
||||
FF_ARG(counts.eopkg, "eopkg"),
|
||||
@@ -257,6 +259,7 @@ void ffParsePackagesJsonObject(FFPackagesOptions* options, yyjson_val* module) {
|
||||
;
|
||||
FF_TEST_PACKAGE_NAME(CARDS)
|
||||
FF_TEST_PACKAGE_NAME(CHOCO)
|
||||
FF_TEST_PACKAGE_NAME(CRUX)
|
||||
break;
|
||||
case 'D':
|
||||
if (false)
|
||||
@@ -390,6 +393,7 @@ void ffGeneratePackagesJsonConfig(FFPackagesOptions* options, yyjson_mut_doc* do
|
||||
FF_TEST_PACKAGE_NAME(BREW)
|
||||
FF_TEST_PACKAGE_NAME(CARDS)
|
||||
FF_TEST_PACKAGE_NAME(CHOCO)
|
||||
FF_TEST_PACKAGE_NAME(CRUX)
|
||||
FF_TEST_PACKAGE_NAME(DPKG)
|
||||
FF_TEST_PACKAGE_NAME(EMERGE)
|
||||
FF_TEST_PACKAGE_NAME(EOPKG)
|
||||
@@ -451,6 +455,7 @@ bool ffGeneratePackagesJsonResult(FFPackagesOptions* options, yyjson_mut_doc* do
|
||||
FF_APPEND_PACKAGE_COUNT(brewCask)
|
||||
FF_APPEND_PACKAGE_COUNT(cards)
|
||||
FF_APPEND_PACKAGE_COUNT(choco)
|
||||
FF_APPEND_PACKAGE_COUNT(crux)
|
||||
FF_APPEND_PACKAGE_COUNT(dpkg)
|
||||
FF_APPEND_PACKAGE_COUNT(emerge)
|
||||
FF_APPEND_PACKAGE_COUNT(eopkg)
|
||||
@@ -549,6 +554,7 @@ FFModuleBaseInfo ffPackagesModuleInfo = {
|
||||
{ "Number of brew-cask packages", "brew-cask" },
|
||||
{ "Number of cards packages", "cards" },
|
||||
{ "Number of choco packages", "choco" },
|
||||
{ "Number of crux packages", "crux" },
|
||||
{ "Number of dpkg packages", "dpkg" },
|
||||
{ "Number of emerge packages", "emerge" },
|
||||
{ "Number of eopkg packages", "eopkg" },
|
||||
|
||||
@@ -19,6 +19,7 @@ static void formatKey(const FFPhysicalDiskOptions* options, FFPhysicalDiskResult
|
||||
FF_ARG(dev->name, "name"),
|
||||
FF_ARG(dev->devPath, "dev-path"),
|
||||
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
||||
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(PhysicalDisk), "module-name"),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user