Compare commits

..

1 Commits

Author SHA1 Message Date
Carter Li ab0f69731e Revert "CMAKE: bumps minimum CMake version to 3.21.0 (#2517)"
This reverts commit 233c481f7a.
2026-09-01 18:42:00 +08:00
63 changed files with 1482 additions and 2421 deletions
+2 -2
View File
@@ -52,7 +52,7 @@ jobs:
- name: Initialize CodeQL
if: inputs.arch == 'amd64'
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
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@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
- name: list features
run: ./fastfetch --list-features
+1 -1
View File
@@ -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_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 .
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 .
- name: build project
run: cmake --build . --target package --verbose -j4
+2 -19
View File
@@ -26,30 +26,13 @@ jobs:
- name: uname -a
run: uname -a
- 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'
- name: install required packages
run: |
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew update
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --overwrite vulkan-loader vulkan-headers molten-vk imagemagick chafa lua quickjs-ng jq
- name: configure project
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 .
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
- name: build project
run: cmake --build . --target package --verbose -j4
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: run VM
uses: vmactions/solaris-vm@96d8d976f9e67d82ec6c7e8ce9c1060731f9e21c # v1.3.9
uses: vmactions/solaris-vm@315163f088b66e55bbcc45928bd224d4973b2312 # v1.3.8
with:
usesh: true
envs: 'CMAKE_BUILD_TYPE'
+1 -9
View File
@@ -1,9 +1,4 @@
# Unreleased
Features:
* Added CPU name and frequency detection support on SPARC. (CPU, Linux)
# 2.68.1
# 2.68.0
Changes:
* The DE / WM / LM modules now reports the full name "Desktop Environment" / "Window Manager" / "Login Manager" instead of the abbreviations.
@@ -16,7 +11,6 @@ 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.
@@ -35,8 +29,6 @@ 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
+95 -31
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.21.0) # C_STANDARD C17 and C23
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
project(fastfetch
VERSION 2.68.1
VERSION 2.68.0
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -9,6 +9,9 @@ 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")
@@ -125,23 +128,12 @@ if(APPLE)
option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF)
endif()
# 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)
file(GLOB FF_MODULE_DIRS CONFIGURE_DEPENDS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/*/")
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
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)
endif()
endforeach()
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
@@ -504,18 +496,88 @@ 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
@@ -1423,9 +1485,11 @@ add_library(libfastfetch OBJECT
)
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
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)
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()
endif()
endforeach()
@@ -1458,11 +1522,10 @@ if(yyjson_FOUND)
# `target_link_libraries(yyjson::yyjson)` sets rpath implicitly
endif()
# 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
# Used for dlopen finding dylibs installed by homebrew
# `/opt/homebrew/lib` is not on in 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 -Wl,-rpath,/opt/local/lib")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib")
endif()
if(ANDROID)
@@ -1931,6 +1994,7 @@ elseif(FreeBSD)
elseif(OpenBSD)
target_link_libraries(libfastfetch
PRIVATE "m"
PRIVATE "kvm"
PRIVATE "sndio"
PRIVATE "util"
)
+71 -144
View File
@@ -2,15 +2,15 @@
<details>
<summary>
Disclaimer
Note
</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.
This document is generated by AI, and mainly for AI use. It is not a substitute for human review, and may contain errors or omissions. Please verify any information before relying on it.
</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.
Thanks for your interest in fastfetch. This document covers building, architecture, how to add a module or a logo, code style, commit conventions, and the pull request workflow.
fastfetch is a system information tool written in C23, supporting Linux, macOS, Windows, the BSDs, Solaris, Haiku and Android. The project 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.
fastfetch is a system information tool written in C23, supporting Linux, macOS, Windows, the BSDs, Solaris, Haiku and Android. The project is borderline obsessive about **startup time** and **optional dependencies** many design decisions only make sense under that constraint, and this document keeps coming back to it.
---
@@ -29,7 +29,7 @@ fastfetch is a system information tool written in C23, supporting Linux, macOS,
- [Changelog](#changelog)
- [Tests](#tests)
- [Pull requests](#pull-requests)
- [Pitfalls](#pitfalls)
- [Gotchas](#gotchas)
---
@@ -38,14 +38,14 @@ fastfetch is a system information tool written in C23, supporting Linux, macOS,
| 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 a module | `src/modules/<name>/`, `src/detection/<name>/`, `src/modules/modules.c` | `cmake -B build && cmake --build build -j` |
| Add a platform implementation | `src/detection/<name>/<name>_<platform>.c` | 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.
For a normal code change, the shortest useful loop is: configure, build the `fastfetch` target, run the affected module, then run the tests. If you add a directory or a generated input, re-run `cmake -B build` so CMake refreshes its file globs.
---
@@ -53,7 +53,7 @@ For a typical code change, the shortest useful iteration is: configure, build th
### Building
The quickest way is `run.sh` in the repository root: it creates `build/`, configures, compiles and runs the binary.
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
@@ -68,7 +68,7 @@ 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).
The default build type is `RelWithDebInfo`. LTO is enabled whenever `ENABLE_LTO=ON` **and** `CMAKE_BUILD_TYPE != Debug` so the default already has it, and only `Debug` builds lack it. This matters because LTO here is not merely an optimization: it is what strips the code of disabled modules. See [Gotchas](#gotchas).
### Common build options
@@ -123,12 +123,12 @@ fastfetch.c → options/ (global configuration)
→ 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:
The number of module directories and detection directories does not match, and that is expected. The following relationships are structural rather than a fixed inventory:
- **13 modules have no detection directory** — they are either pure layout (`break`, `separator`, `colors`, `title`, `logo`) or reuse another module's detection result (`display`, `monitor`, `kernel`, `shell`, `terminal`, `player`, `custom`, `datetime`).
- **13 modules have no detection directory** — they are either pure layout (`break`, `separator`, `colors`, `title`, `logo`) or reuse someone else's detection result (`display`, `monitor`, `kernel`, `shell`, `terminal`, `player`, `custom`, `datetime`).
- **4 detection directories serve modules with different names** — `displayserver` (→ `display`, `monitor`), `gtk_qt` (→ `theme`, `icons`, `font`, `cursor`), `terminalshell` (→ `terminal`, `shell`) and `libc`.
Therefore, do not assume that `modules/<x>/` always has a matching `detection/<x>/`.
So do not assume `modules/<x>/` always has a matching `detection/<x>/`.
### Layer boundaries
@@ -139,13 +139,13 @@ Therefore, do not assume that `modules/<x>/` always has a matching `detection/<x
| `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__`.
The test is simple: **no file under `detection/` should contain `printf` or read `instance.config`.** Symmetrically, no file under `modules/` should contain `#ifdef __linux__`.
---
## Core architecture: modules vs. detection
This is the most important convention in the codebase. Every feature consists of a fixed set of files; using CPU as the example:
This is the single most important convention in the codebase. Every feature is a fixed set of files using CPU as the example:
```
modules/cpu/option.h module option struct
@@ -158,7 +158,7 @@ detection/cpu/cpu_bsd.c │
detection/cpu/cpu_nosupport.c ┘
```
The interface is deliberately minimal, typically consisting of only two declarations:
The interface is deliberately minimal — usually just two things:
```c
typedef struct FFCPUResult { ... } FFCPUResult; // result struct
@@ -169,7 +169,7 @@ The `const char*` return value is an **error string**; `nullptr` means success.
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/`.
**The payoff:** adding a platform never touches `modules/`; fixing output formatting never touches `detection/`.
---
@@ -197,7 +197,7 @@ typedef struct FFModuleBaseInfo {
} 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 source comment openly admits this is UB — `void*` is not compatible with `FF*Options*`. It is a pragmatic compromise to get polymorphism in C. Don't try to "fix" it.
### The registry: a first-letter hash bucket
@@ -210,7 +210,7 @@ for (; *modules; ++modules) { // linear s
}
```
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 registry is small enough that a subtraction plus a few string comparisons is preferable to a general-purpose hash table. When adding a module, place its descriptor in the bucket matching the first letter of `.name`; keep the existing `nullptr` terminator at the end.
### The calling convention: zero heap allocation
@@ -230,7 +230,7 @@ baseInfo->destroyOptions(optionBuf);
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.
**Meaning: no module option struct may exceed 256 bytes.** Hard constraint, enforced at compile time.
The three dispatch entry points:
@@ -238,49 +238,20 @@ The three dispatch entry points:
|---|---|---|
| `parseModuleJsonObject` | `common/impl/jsonconfig.c:90` | the `modules[]` array in a JSONC config |
| `parseStructureCommand` | `common/impl/commandoption.c:181` | the colon-separated structure string |
| `ffParseModuleOptions` | `common/impl/commandoption.c:13` | **deprecated** — see [Gotchas](#4-cli-module-options-are-removed) |
### Build-time module discovery
`CMakeLists.txt: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:
`CMakeLists.txt:131` globs `src/modules/*/` and generates a `MODULE_DISABLE_<UPPER>` option per directory:
```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()
file(GLOB FF_MODULE_DIRS RELATIVE "..." ".../src/modules/*/")
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).
**Adding a module requires no CMake change.** The same trick generates the package manager switches (`:146` regex-scans `src/modules/packages/option.h` for `FF_PACKAGES_FLAG_*_BIT`).
---
@@ -305,7 +276,9 @@ typedef struct 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**.
`FFModuleArgs` must come first. It provides `key`, `format`, `outputColor`, `keyColor`, `keyIcon` and `keyWidth`; because it is the first field, `ffJsonConfigParseModuleArgs()` can handle these generically and you get them **for free — no parsing code to write**.
Note these fields can only be set through the JSON config; CLI module options have been removed (see [Gotchas](#4-cli-module-options-are-removed)).
### 2. Implement detection
@@ -334,7 +307,7 @@ const char* ffDetectFoo(const FFFooOptions* options, FFFooResult* result) {
}
```
**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.
**Write one file per target platform, and a `foo_nosupport.c` fallback for the rest.** There are already 45 `*_nosupport.c` files doing exactly this.
### 3. Implement the module
@@ -366,11 +339,11 @@ FFModuleBaseInfo ffFooModuleInfo = {
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.**
- **`displayName`** is a literal block of all 20 language fields there is no shortcut macro, so copy the layout from a neighboring module: **`en`, `ar`, `cs`, `de`, `es`, `fr`, `gl`, `he`, `id`, `it`, `ja`, `ko`, `pl`, `pt`, `ru`, `tr`, `uk`, `vi`, `zh_CN`, `zh_TW`**.
**Fill in all 20.** The struct is read by byte offset (see [Gotchas](#6-localization-uses-byte-offsets-not-enums)) and there is **no fallback** — a missing field means the key prints empty in that language.
- **`formatArgs`** must be exhaustive. It drives the placeholder list printed by `fastfetch -h foo-format`; **anything you omit is invisible and unusable to the user.**
- The `moduleFormat` section in `doc/json_schema.json` is generated by `fastfetch -h format-json`. Its metadata comes from `FFModuleBaseInfo::formatArgs`. To keep the schema and module descriptors consistent, do not edit the `moduleFormat` section in `doc/json_schema.json` by hand; update the module metadata and regenerate it instead.
- **`defaultOrder`**: use the current maximum plus 1. Search the existing descriptors for `.defaultOrder =` before choosing a value; do not copy a hard-coded value from this document. Leaving it out, or setting it to `0`, 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.
- **`defaultOrder`**: use the current maximum plus 1. Search the existing descriptors for `.defaultOrder =` before choosing a value; do not copy a hard-coded value from this document. Leaving it out, or setting it to `0`, makes the module **disappear** from the interactive `--gen-config` picker. Only `logo`, `command` and `custom` do this deliberately, because they need user arguments or are invoked directly by the display layer.
### 4. Register it
@@ -385,46 +358,11 @@ Points to note:
`FF_MODULE_DISABLE_FOO` is generated by CMake — you do not define it yourself.
### 5. Add the platform sources to `CMakeLists.txt`
### 5. Modules that need warm-up
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:
If your module needs a sampling interval (CPU usage) or a network round-trip (public IP, weather), also implement `ffPrepareFoo()` and register it in the switch inside `ffPrepareCommandOption()` in `common/impl/commandoption.c`, under the right first-letter `case`. Six modules do this today: CPUUsage, DiskIO, NetIO, PublicIP, Top and Weather.
| 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
### 6. Verify
```sh
cmake -B build && cmake --build build -j
@@ -433,9 +371,7 @@ cmake -B build && cmake --build build -j
./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.
Note the `-format` suffix on the help flag: `fastfetch -h foo` does not work, only `fastfetch -h foo-format`.
---
@@ -443,13 +379,13 @@ If you added detection sources, re-check [step 5](#5-add-the-platform-sources-to
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
### 1. Drop in 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 `_/`).
For example `src/logo/ascii/o/omarchy.txt`. Directories are already split by first letter (`a/``z/`, plus `_/`).
The file is plain ASCII art with `$1``$9` as color placeholders:
@@ -459,29 +395,26 @@ $3 /\\\///
$2refined.$1 /\\\\//
```
- `$1` … `$9` map to palette slots 19; at most 9 are supported (`FASTFETCH_LOGO_MAX_COLORS = 9`)
- `$1``$9` map to palette slots 19; at most 9 (`FASTFETCH_LOGO_MAX_COLORS = 9`)
- `$$` is a literal `$`
- Characters without a placeholder inherit the current color. Of the 530 existing logos, 239 use no placeholders at all (monochrome) and 291 do; **new logos should use placeholders**, as monochrome is a legacy style
- Characters without a placeholder inherit the current color. Of the 530 existing logos, 239 use no placeholders at all (monochrome) and 291 do **new logos should use placeholders**; monochrome is a legacy style
- Tabs are expanded to 4 spaces
- Colors can be overridden with `--logo-color-1``--logo-color-9`
### 2. Register it in the `.inc`
CMake turns each `.txt` 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`:
CMake turns each `.txt` 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
#ifdef FASTFETCH_DATATEXT_LOGO_OMARCHY
// Omarchy
{
.names = { "Distro" }, // ID (preferred) or NAME from /etc/os-release, do NOT add both
.lines = FASTFETCH_DATATEXT_LOGO_DISTRO,
.names = { "omarchy", "Omarchy" },
.lines = FASTFETCH_DATATEXT_LOGO_OMARCHY,
.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
FF_COLOR_FG_BLUE,
FF_COLOR_FG_WHITE,
FF_COLOR_FG_CYAN,
},
},
#endif
@@ -498,13 +431,13 @@ If the same OS has multiple logo variants, mark the variant explicitly with `.ty
.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>`.
Use `FF_LOGO_LINE_TYPE_ALTER_BIT` for an alternate logo, `FF_LOGO_LINE_TYPE_SMALL_BIT` for a small logo, or combine the flags when both apply. This is also a lookup optimization: a logo marked `FF_LOGO_LINE_TYPE_SMALL_BIT` is considered only for `type = small`, while a logo marked `FF_LOGO_LINE_TYPE_ALTER_BIT` is never selected by automatic detection. Alternate logos are available only when explicitly requested through `--logo <source>`.
### 3. Verify
```sh
./build/fastfetch -l distro
./build/fastfetch --list-logos | grep -i distro
./build/fastfetch --logo omarchy
./build/fastfetch --list-logos | grep -i omarchy
```
---
@@ -541,9 +474,9 @@ Current distribution of platform files in `detection/`:
(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.
**Don't pile `#ifdef __linux__` into one 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.
`common/` uses the same idea: `common/impl/` contains `io_unix.c` / `io_windows.c`, `netif_linux.c` / `netif_apple.c` / `netif_bsd.c` and so on, with `common/apple/`, `common/windows/` and `common/haiku/` holding platform-specific helpers.
When several platform suffixes could apply, use the most specific implementation supported by the build system (for example, `_nbsd.c` instead of the generic `_bsd.c` on NetBSD). Keep the generic file as the fallback for platforms that share its conventions.
@@ -574,7 +507,7 @@ 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).
`.editorconfig` adds LF line endings, 4-space indent, a final newline, and trailing whitespace trimmed (except in Markdown).
### Naming
@@ -589,7 +522,7 @@ clang-format -i src/modules/foo/*.c src/modules/foo/*.h
### 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.
CI runs codespell (`.codespellrc`). Known false positives live in `ignore-words-list` (`iterm`, `compiletime`, and various non-English distro words). Add new words there rather than changing the code.
### Compiler warnings
@@ -633,7 +566,7 @@ Common verbs: `adds`, `removes`, `fixes`, `improves`, `updates`, `corrects`, `pr
Scopes in use: `Top`, `Processes`, `Memory`, `Logo (Builtin)`, `CI`, `Doc`, `Presets`, `Global`, plus individual module names.
Documentation-only changes use a `[ci skip]` suffix.
Documentation-only changes get a `[ci skip]` suffix.
---
@@ -684,21 +617,21 @@ 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.
Coverage focuses on the core data structures and the formatting engine in `common/`. The `detection/` layer has no automated tests (it depends too heavily on a real system) and is instead covered by the CI matrix — 20 workflows under `.github/workflows/` spanning Linux (including musl, loong64, armv7l, i686), macOS, Windows, FreeBSD, NetBSD, OpenBSD, DragonFly, Solaris, OmniOS and Haiku, plus spellcheck and benchmark jobs.
**If you modify `common/FFstrbuf.h`, `common/format.h` or `common/color.h`, extend the corresponding test.**
**If you touch `common/FFstrbuf.h`, `common/format.h` or `common/color.h`, please extend the matching test.**
---
## Pull requests
1. **Open an issue first** (feature request / bug report / logo request) to confirm that the change is wanted before investing effort. Templates are in `.github/ISSUE_TEMPLATE/`.
1. **Open an issue first** (feature request / bug report / logo request) so you don't waste effort. Templates are in `.github/ISSUE_TEMPLATE/`.
2. Branch off `dev`**`dev` is the main development branch**, not `master`.
3. Follow the [commit message convention](#commit-messages).
4. Update `CHANGELOG.md` for user-visible changes.
5. Open the PR against `dev` and fill in `.github/pull_request_template.md`:
- Summary
- Related issue (**required for new logos**; otherwise the PR is not accepted)
- Related issue (**required for new logos**, otherwise the PR won't be accepted)
- Changes
- Screenshots (required for visual changes)
- Checklist: confirm you tested locally
@@ -716,13 +649,13 @@ cd build && ctest --output-on-failure # tests
---
## Pitfalls
## Gotchas
The following points are easy to misinterpret when reading this codebase. Most of them follow from the startup-time objective described above.
Things that are easy to get wrong when reading this codebase. Most of them follow from the "obsessively fast startup" goal.
### 1. Option structs must not exceed 256 bytes
`FF_OPTION_MAX_SIZE = 1 << 8`. Exceeding it is caught at compile time by `static_assert`. Do not attempt to increase the value: it determines the stack cost of every module invocation.
`FF_OPTION_MAX_SIZE = 1 << 8`. Exceeding it is caught at compile time by `static_assert`. Don't try to raise the value it determines the stack cost of every module invocation.
### 2. `FF_MODULE_DISABLE_*` controls registration, not compilation
@@ -735,11 +668,9 @@ The following points are easy to misinterpret when reading this codebase. Most o
**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
### 3. CMake globs don't trigger reconfiguration
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).
Both the module directories and the logo files are discovered by glob. CMake will not notice new files on its own — you need to re-run `cmake -B build` (or delete `build/CMakeCache.txt` and start over). This is the classic CMake footgun.
### 4. CLI module options are removed
@@ -755,9 +686,9 @@ Error: Unsupported module option: --cpu-temp
### 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.
`collectModuleInfos` (`genconfig.c:186`) skips any module whose `defaultOrder` is `0`. C zero-initializes the field, so **omitting it is the same as setting it to 0**. Only `logo`, `command` and `custom` do this on purpose.
`defaultOrder` affects only the ordering in the interactive `--gen-config` picker; it has no effect on the runtime output order.
`defaultOrder` affects only the ordering in the interactive `--gen-config` picker; it has no effect on runtime output order.
### 6. Localization uses byte offsets, not enums
@@ -768,23 +699,19 @@ Error: Unsupported module option: --cpu-temp
(*(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.
Consequence: **the field order of `FFModuleDisplayName` must never change** — reordering silently scrambles every language.
### 7. The `multithreading` option has limited effect
### 7. The `multithreading` switch barely does anything
The global `multithreading` option currently takes effect in exactly one place: `common/impl/networking_linux.c:339`. Modules are still printed sequentially. Modules that need concurrency go through the `ffPrepare*` warm-up hooks instead, which start sampling or fire off requests before the print loop begins.
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/`
### 8. Don't print or read config inside `detection/`
The `detection/` layer must stay pure: read system state, fill a struct, return an error string. Any `printf` or any read of `instance.config` there is a design error.
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.
The former is a large generated/hand-maintained data table, the latter is upstream code. Both are excluded via `.clang-format-ignore` — leave them alone.
---
-6
View File
@@ -1,9 +1,3 @@
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
+1 -2
View File
@@ -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. {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",
"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",
"type": "string"
},
"physicaldiskFormat": {
@@ -3763,7 +3763,6 @@
"apk",
"brew",
"choco",
"crux",
"dpkg",
"emerge",
"eopkg",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"home": "https://github.com/ibireme/yyjson",
"license": "MIT ( embed in source )",
"version": "0.13.0",
"version": "0.12.0",
"author": "ibireme"
}
+285 -700
View File
File diff suppressed because it is too large Load Diff
+674 -911
View File
File diff suppressed because it is too large Load Diff
+25 -38
View File
@@ -4,7 +4,6 @@
#include "common/strutil.h"
#include "common/io.h"
#include "common/path.h"
#include "common/mallocHelper.h"
#include <unistd.h>
#include <pwd.h>
@@ -20,6 +19,7 @@
#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,14 +68,13 @@ static void getExePath(FFPlatform* platform) {
// Current implementation uses argv[0], which can be easily spoofed.
// See #2195
size_t exePathLen = 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) {
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* arg0 = argv[0];
if (arg0[0]) {
if (strchr(arg0, '/') != nullptr) // likely a path (absolute or relative)
@@ -98,38 +97,25 @@ static void getExePath(FFPlatform* platform) {
if (exePathLen > 0) {
struct stat st;
if (stat(exePath, &st) == 0 && S_ISREG(st.st_mode)) {
// 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;
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;
}
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;
@@ -138,6 +124,7 @@ static void getExePath(FFPlatform* platform) {
}
}
}
kvm_close(kd);
}
#elif defined(__sun)
ssize_t exePathLen = readlink("/proc/self/path/a.out", exePath, sizeof(exePath) - 1);
+13 -47
View File
@@ -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,34 +178,6 @@ 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,
@@ -219,13 +191,7 @@ static bool parseStructureCommand(
if (data->resultDoc != nullptr) {
fn(data, baseInfo, optionBuf);
} else {
yyjson_val* configModule = findStructureModuleConfig(data, baseInfo->name);
if (configModule != nullptr) {
baseInfo->parseJsonObject(optionBuf, configModule);
baseInfo->printModule(optionBuf);
} else {
baseInfo->printModule(optionBuf);
}
baseInfo->printModule(optionBuf);
}
baseInfo->destroyOptions(optionBuf);
return true;
-1
View File
@@ -47,7 +47,6 @@ 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);
}
+19 -16
View File
@@ -28,6 +28,7 @@
#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>
@@ -422,13 +423,12 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
#elif defined(__OpenBSD__)
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) {
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) {
const char* arg0 = argv[0];
if (arg0[0] == '-') {
arg0++;
@@ -436,6 +436,7 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
ffStrbufSetS(exe, arg0);
}
}
kvm_close(kd);
#elif defined(__HAIKU__)
@@ -632,19 +633,21 @@ const char* ffProcessGetBasicInfoLinux(pid_t pid, FFstrbuf* name, pid_t* ppid, i
#elif defined(__OpenBSD__)
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);
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);
if (ppid) {
*ppid = proc.p_ppid;
*ppid = proc->p_ppid;
}
if (tty) {
*tty = (int) proc.p_tdev;
*tty = (int) proc->p_tdev;
}
} else {
return "sysctl(KERN_PROC_PID) failed";
}
kvm_close(kd);
if (!proc) {
return "kvm_getprocs() failed";
}
#elif defined(__HAIKU__)
+3 -23
View File
@@ -13,8 +13,6 @@ enum {
SystemLogicalProcessorAndGroupInformation = 107,
SystemBasicPerformanceInformation = 123,
SystemSecureBootInformation = 146,
SystemBasicProcessInformation = 252,
SystemHandleCountInformation = 253,
};
typedef struct _PROCESSOR_POWER_INFORMATION {
@@ -208,20 +206,6 @@ 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);
/**
@@ -707,19 +691,15 @@ static inline uint64_t ffKSystemTimeToUInt64(const volatile KSYSTEM_TIME* pTime)
static inline bool ffIsWindows10OrGreater() {
#if FF_WIN81_COMPAT
return SharedUserData->NtBuildNumber >= 10240;
return SharedUserData->NtMajorVersion >= 10;
#else
return true;
#endif
}
static inline bool ffIsWindows11OrGreater() {
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
return SharedUserData->NtMajorVersion > 10 ||
(SharedUserData->NtMajorVersion == 10 && SharedUserData->NtBuildNumber >= 22000);
}
NTSYSAPI NTSTATUS NTAPI NtOpenProcessToken(
-4
View File
@@ -62,10 +62,6 @@ 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;
}
+1 -8
View File
@@ -619,9 +619,6 @@ 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)) ||
@@ -715,7 +712,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 id = strtoul(p, &pend, 10);
unsigned long long id = strtoul(p, &pend, 10);
if (__builtin_expect(id > 64, false)) { // Do 129-socket boards exist?
high |= 1ULL << (id - 64);
} else {
@@ -1104,10 +1101,6 @@ 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
}
+16 -23
View File
@@ -16,6 +16,7 @@
#elif __OpenBSD__
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
#elif __sun
#include <procfs.h>
#elif __NetBSD__
@@ -310,33 +311,25 @@ static const char* getFromProcesses(FFDisplayServerResult* result) {
}
}
#elif __OpenBSD__
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, (int) userId, (int) sizeof(struct kinfo_proc), 0 };
size_t length = 0;
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);
}
if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_UID}, nullptr) failed";
}
if (result->wmPrettyName.length == 0) {
applyNameIfWM(result, proc[i].p_comm);
}
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;
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) {
+1 -1
View File
@@ -426,7 +426,7 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
ffStrbufAppendS(deviceDir, "/revision");
if (ffReadFileBuffer(deviceDir->chars, buffer)) {
char* pend;
uint32_t revision = (uint32_t) strtoul(buffer->chars, &pend, 16);
uint64_t revision = strtoul(buffer->chars, &pend, 16);
if (pend != buffer->chars) {
ffGPUQueryAmdGpuName((uint16_t) deviceId, (uint8_t) revision, gpu);
}
+1 -1
View File
@@ -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 = (uint64_t) strtoull(di_bus_addr(node), nullptr, 16);
gpu->deviceId = strtoul(di_bus_addr(node), nullptr, 16);
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
+22 -55
View File
@@ -7,65 +7,32 @@
#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) {
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";
}
}
// 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";
}
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";
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;
}
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";
}
// 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";
}
}
}
+13 -20
View File
@@ -18,6 +18,7 @@
#elif __OpenBSD__
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
#elif __sun
#include <procfs.h>
#elif __NetBSD__
@@ -54,7 +55,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>
@@ -273,27 +274,19 @@ const char* detectByProcesses(FFLMResult* result) {
}
}
#elif __OpenBSD__
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_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
int count = 0;
const struct kinfo_proc* proc = kvm_getprocs(kd, KERN_PROC_UID, 0, sizeof(*proc), &count);
if (proc) {
for (int i = 0; i < count; ++i) {
const char* lm = testLms(proc[i].p_comm);
if (lm) {
ffStrbufSetStatic(&result->service, lm);
break;
}
}
}
kvm_close(kd);
#elif __sun
FF_AUTO_CLOSE_DIR DIR* procdir = opendir("/proc");
if (procdir == nullptr) {
-1
View File
@@ -12,7 +12,6 @@ typedef struct FFPackagesResult {
uint32_t brewCask;
uint32_t cards;
uint32_t choco;
uint32_t crux;
uint32_t dpkg;
uint32_t emerge;
uint32_t eopkg;
-3
View File
@@ -487,9 +487,6 @@ 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) strtoull(blkSize, nullptr, 10) * 512;
size = (uint64_t) strtoul(blkSize, nullptr, 10) * 512;
}
}
@@ -38,7 +38,7 @@ static void appendDevice(
if (size)
{
char* unit = nullptr;
device->size = (uint64_t) strtoull(size.UTF8String, &unit, 10);
device->size = strtoul(size.UTF8String, &unit, 10);
if (*unit == ' ') ++unit;
switch (*unit)
+3 -3
View File
@@ -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)) {
// NOTE: This fails for system processes
result->threads += (uint32_t) taskInfo.pti_threadnum;
if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) != sizeof(taskInfo)) {
continue;
}
result->threads += taskInfo.pti_threadnum;
}
return nullptr;
+14 -10
View File
@@ -4,22 +4,25 @@
#include <sys/param.h>
#include <sys/sysctl.h>
#include <kvm.h>
const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) {
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";
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
if (!kd) {
return "kvm_open() 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 = 0;
// KERN_PROC_ALL returns all user-level processes
// KERN_PROC_KTHREAD returns all processes, including user-level processes (despite the name)
const struct kinfo_proc* procs = kvm_getprocs(kd,
(options->countKprocs ? KERN_PROC_KTHREAD : KERN_PROC_ALL) | KERN_PROC_SHOW_THREADS,
0, sizeof(struct kinfo_proc), &count);
if (!procs) {
kvm_close(kd);
return "kvm_getprocs() failed";
}
int count = (int) (length / sizeof(struct kinfo_proc));
for (int i = 0; i < count; ++i) {
const struct kinfo_proc* proc = &procs[i];
@@ -29,5 +32,6 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
}
}
kvm_close(kd);
return nullptr;
}
@@ -1,23 +1,10 @@
#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
+5
View File
@@ -0,0 +1,5 @@
#include "sound.h"
const char* ffDetectSound([[maybe_unused]] FFlist* devices /* List of FFSoundDevice */) {
return "Not supported on this platform";
}
+2 -2
View File
@@ -19,11 +19,11 @@ static const char* detectByProcMeminfo(FFlist* result) {
char* token = nullptr;
if ((token = strstr(buf, "SwapTotal:")) != nullptr) {
swapTotal = (uint64_t) strtoull(token + strlen("SwapTotal:"), nullptr, 10);
swapTotal = strtoul(token + strlen("SwapTotal:"), nullptr, 10);
}
if ((token = strstr(buf, "SwapFree:")) != nullptr) {
swapFree = (uint64_t) strtoull(token + strlen("SwapFree:"), nullptr, 10);
swapFree = strtoul(token + strlen("SwapFree:"), nullptr, 10);
}
FFSwapResult* swap = FF_LIST_ADD(FFSwapResult, *result);
+42 -29
View File
@@ -6,46 +6,59 @@
#include <libproc.h>
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";
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";
}
uint32_t count = (uint32_t) npids;
// The process table may change between the two sysctl calls; retry with a larger buffer.
length += length / 8 + sizeof(struct kinfo_proc);
FF_AUTO_FREE struct kinfo_proc* processes = malloc(length);
if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) {
return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, processes}) failed";
}
uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc));
for (uint32_t i = 0; i < count; ++i) {
pid_t pid = pids[i];
struct proc_taskallinfo proc;
if (proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &proc, sizeof(proc)) != sizeof(proc)) {
const struct kinfo_proc* proc = &processes[i];
if (proc->kp_proc.p_flag & P_SYSTEM) {
continue;
}
pid_t pid = proc->kp_proc.p_pid;
if (proc.pbsd.pbi_flags & PROC_FLAG_SYSTEM) {
continue;
struct rusage_info_v2 rusage;
if (proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t*) &rusage) != 0) {
continue; // The process may have exited
}
FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots);
ffStrbufInitS(&item->name, proc.pbsd.pbi_name);
if (item->name.length == 0) {
ffStrbufInitS(&item->name, proc.pbsd.pbi_comm);
}
ffStrbufInitS(&item->name, proc->kp_proc.p_comm);
item->pid = (uint32_t) pid;
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;
// Note: Do NOT use proc->kp_proc.p_pctcpu for CPU usage. p_pctcpu is a
// decaying average (fixpt_t with FSCALE=2048) updated roughly once per
// second by the kernel. It is heavily smoothed, lags short bursts, has
// low resolution, and its multicore scaling (>100%) is inconsistent
// across XNU versions. It also breaks the unified model where
// FFTopProcessSnapshot.cpuTime is cumulative time and top.c computes
// (new - old) / elapsed * 100 for all platforms. proc_pid_rusage with
// RUSAGE_INFO_V2 provides precise cumulative ri_user_time +
// ri_system_time in nanoseconds and is the modern recommended API on
// Darwin, consistent with Linux/BSD differential sampling and accurate
// for the short waitTime interval (e.g. 100ms).
item->cpuTime = (rusage.ri_user_time + rusage.ri_system_time) / 1000000u; // ns -> ms
item->memBytes = rusage.ri_resident_size;
item->bytesRead = rusage.ri_diskio_bytesread;
item->bytesWritten = rusage.ri_diskio_byteswritten;
item->startTime = rusage.ri_proc_start_abstime;
item->threads = 0;
if (showTypes & FF_TOP_TYPE_THREADS) {
struct proc_taskinfo taskInfo;
if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) == sizeof(taskInfo)) {
item->threads = (uint32_t) taskInfo.pti_threadnum;
}
}
}
+20 -25
View File
@@ -1,26 +1,24 @@
#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 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";
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
if (!kd) {
return "kvm_open() 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 = 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";
}
int count = (int) (length / sizeof(struct kinfo_proc));
const uint32_t pageSize = instance.state.platform.sysinfo.pageSize;
for (int i = 0; i < count; ++i) {
@@ -43,24 +41,21 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
}
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;
}
int threadCount = 0;
const struct kinfo_proc* threads = kvm_getprocs(kd,
KERN_PROC_ALL | KERN_PROC_SHOW_THREADS, 0, sizeof(struct kinfo_proc), &threadCount);
if (threads) {
for (uint32_t i = 0; i < snapshots->length; ++i) {
FFTopProcessSnapshot* item = FF_LIST_GET(FFTopProcessSnapshot, *snapshots, i);
for (int j = 0; j < threadCount; ++j) {
if (threads[j].p_pid == (pid_t) item->pid && threads[j].p_tid != -1) {
++item->threads;
}
}
}
}
}
kvm_close(kd);
return nullptr;
}
+61 -107
View File
@@ -5,125 +5,79 @@
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
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";
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;
}
}
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);
{
// 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);
}
}
if (result->length > 0)
return nullptr;
}
}
if (result->length == 0) {
return "Failed to detect wallpaper from plist";
}
return nullptr;
}
#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,
#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,
"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)) {
"WHERE display_id=1 AND space_id=1 AND key=1", result)
)
return nullptr;
}
#endif
if (ffOsascript("tell application \"Finder\" to get POSIX path of (get desktop picture as alias)", 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";
}
NSURL* url = [NSWorkspace.sharedWorkspace desktopImageURLForScreen:mainScreen];
if (url.fileURL) {
ffStrbufSetS(result, url.path.UTF8String);
return nullptr;
}
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;
return "All detection methods failed";
}
+53 -93
View File
@@ -114,110 +114,70 @@ static bool isProcessTrusted(DWORD processId, FFProcessType processType, UNICODE
#define ffStrEqualNWS(str, compareTo) (_wcsnicmp(str, L##compareTo, sizeof(compareTo) - 1) == 0)
// 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) {
const char* ffDetectWMPlugin(FFstrbuf* pluginName) {
alignas(UNICODE_STRING) uint8_t buffer[4096];
UNICODE_STRING* filePath = (UNICODE_STRING*) buffer;
FF_AUTO_FREE SYSTEM_PROCESS_INFORMATION* pstart = nullptr;
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 ");
// 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 {
ffStrbufSetStatic(pluginName, "FancyWM");
return "NtQuerySystemInformation(SystemProcessInformation) failed";
}
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;
}
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;
}
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;
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 {
return "NtQuerySystemInformation(SystemProcessInformation) failed";
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;
}
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;
}
if (ptr->NextEntryOffset == 0) {
break;
}
}
-20
View File
@@ -181,26 +181,6 @@ 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
{
-20
View File
@@ -1,20 +0,0 @@
$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:.
+12
View File
@@ -3,6 +3,18 @@
#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
{
+18
View File
@@ -0,0 +1,18 @@
MMM
MMMM
MMMMM
MMMMMM
MMMMMMa
MMMMMMMa
MMMMMMM
MMMMMM
MMMM
M
oxdo aMa
aMd aMMMMMMMMM
aMMMMM MMMMMMMMMMMMMMa
MMMMMMMa MMMMMMMMMMMMMMMa
MMMMMMMMa
MMMMMMMMa
MMMMMMM
MMMM
+1 -2
View File
@@ -15,8 +15,7 @@ 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" FF_LIBRARY_EXTENSION, 8
"libMagickCore-6.Q16" FF_LIBRARY_EXTENSION, 8
)
// clang-format on
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(imageMagick, ffResizeImage, ResizeImage, FF_LOGO_IMAGE_RESULT_INIT_ERROR)
+1 -2
View File
@@ -20,8 +20,7 @@ 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" FF_LIBRARY_EXTENSION, 11
"libMagickCore-7.Q16" FF_LIBRARY_EXTENSION, 11
)
#endif
// clang-format on
-1
View File
@@ -21,7 +21,6 @@ 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"),
}));
}
-1
View File
@@ -40,7 +40,6 @@ 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,7 +22,6 @@ 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"),
}));
}
-1
View File
@@ -51,7 +51,6 @@ 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"),
}));
}
-1
View File
@@ -20,7 +20,6 @@ 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"),
}));
}
-1
View File
@@ -61,7 +61,6 @@ 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"),
}));
}
-1
View File
@@ -21,7 +21,6 @@ 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"),
}));
}
-1
View File
@@ -45,7 +45,6 @@ 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"),
}));
}
-1
View File
@@ -19,7 +19,6 @@ 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"),
}));
}
}
-1
View File
@@ -83,7 +83,6 @@ 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"),
}));
}
-1
View File
@@ -41,7 +41,6 @@ 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"),
}));
}
-1
View File
@@ -24,7 +24,6 @@ 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"),
}));
}
}
-1
View File
@@ -33,7 +33,6 @@ 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"),
}));
}
-1
View File
@@ -22,7 +22,6 @@ 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"),
}));
}
}
-1
View File
@@ -63,7 +63,6 @@ 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"),
}));
}
-1
View File
@@ -41,7 +41,6 @@ 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), "");
-6
View File
@@ -63,7 +63,6 @@ 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)
@@ -155,7 +154,6 @@ 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"),
@@ -259,7 +257,6 @@ 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)
@@ -393,7 +390,6 @@ 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)
@@ -455,7 +451,6 @@ 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)
@@ -554,7 +549,6 @@ 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" },
-1
View File
@@ -19,7 +19,6 @@ 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"),
}));
}
}
-1
View File
@@ -21,7 +21,6 @@ void printSwap(FFSwapOptions* options, uint8_t index, uint32_t totalCount, FFSwa
FF_ARG(index, "index"),
FF_ARG(storage->name, "name"),
FF_ARG(options->moduleArgs.keyIcon, "icon"),
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Swap), "module-name"),
}));
}
-1
View File
@@ -22,7 +22,6 @@ static void printZpool(FFZpoolOptions* options, FFZpoolResult* result, uint8_t i
FF_ARG(result->name, "name"),
FF_ARG(result->guid, "guid"),
FF_ARG(options->moduleArgs.keyIcon, "icon"),
FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Zpool), "module-name"),
}));
}