Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot] b27503a385 CI: Bump the github-actions group with 3 updates
Bumps the github-actions group with 3 updates: [uraimo/run-on-arch-action](https://github.com/uraimo/run-on-arch-action), [github/codeql-action/init](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `uraimo/run-on-arch-action` from 3.1.0 to 3.2.0
- [Release notes](https://github.com/uraimo/run-on-arch-action/releases)
- [Commits](https://github.com/uraimo/run-on-arch-action/compare/f9b26e3a1a408d5fd530d20c17b9f3f4428ff8d9...460cb8e6d9f726a588fc9b5e681c8a6cab09ae41)

Updates `github/codeql-action/init` from 4.37.7 to 4.37.8
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd...db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28)

Updates `github/codeql-action/analyze` from 4.37.7 to 4.37.8
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd...db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28)

---
updated-dependencies:
- dependency-name: uraimo/run-on-arch-action
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-30 18:07:08 +00:00
71 changed files with 168 additions and 1115 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
with:
operating_system: dragonflybsd
version: '6.4.2'
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
with:
operating_system: freebsd
version: '15.1'
+2 -2
View File
@@ -18,10 +18,10 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
with:
operating_system: haiku
version: 'r1beta6'
version: 'r1beta5'
shell: bash
cpu_count: 4
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
with:
operating_system: netbsd
version: '10.1'
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
with:
operating_system: omnios
version: 'r151058'
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Start VM
uses: cross-platform-actions/action@faa0c6197e94aacf1c5956460152c8380d3560a5 # v1.5.0
uses: cross-platform-actions/action@24ef01df165c76df1ed2b9f9e9212e78dc2fc963 # v1.4.0
with:
operating_system: openbsd
version: '7.9'
+2 -1
View File
@@ -138,6 +138,7 @@ jobs:
uses: ./.github/workflows/build-dragonfly-amd64.yml
haiku-amd64:
if: false # Disabled because the Haiku build is currently broken
needs: no-features-test
name: Haiku-amd64
permissions:
@@ -203,7 +204,7 @@ jobs:
- dragonfly-amd64
- solaris-amd64
- omnios-amd64
- haiku-amd64
# - haiku-amd64
- windows-hosts
permissions:
contents: write
-2
View File
@@ -13,5 +13,3 @@ fastfetch.kdev4
*.user.*
*.swp
*.log
/*.zh.md
/.*-ai/
+2 -6
View File
@@ -1,4 +1,4 @@
# 2.68.1
# Unreleased
Changes:
* The DE / WM / LM modules now reports the full name "Desktop Environment" / "Window Manager" / "Login Manager" instead of the abbreviations.
@@ -11,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.
@@ -22,7 +21,6 @@ Features:
* Improved fish completion scripts to print enum descriptions. (Completion)
* Enabled Wayland support on Android. (DisplayServer, Android)
* Added DE detection support for ASUS Zenfone. (DE, Android)
* Added new ARM SoCs (CPU, Linux / Android)
Bugfixes:
* Fixed I/O rate calculation precision in DiskIO and NetIO, and prevented division by zero. (DiskIO / NetIO)
@@ -30,8 +28,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
@@ -3318,7 +3314,7 @@ Features:
* Enhance `--percent-type` to allow hiding other texts (#387)
* Add Wifi module support for Linux
* Detect scaled resolutions (Windows, macOS)
* Optimize font module printing (Windows)
* Optimise font module printing (Windows)
* Detect pacman package count inside MSYS2 environment (Windows)
* Add Wifi / Battery module support for Android
* Disk name support for Linux
+3 -3
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.12.0) # Don't change before we investigate why #2553 happens
cmake_minimum_required(VERSION 3.21.0) # C_STANDARD
project(fastfetch
VERSION 2.68.1
VERSION 2.67.1
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -128,7 +128,7 @@ if(APPLE)
option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF)
endif()
file(GLOB FF_MODULE_DIRS CONFIGURE_DEPENDS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/*/")
file(GLOB FF_MODULE_DIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/*/")
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src/modules/${FF_MODULE_DIR}")
string(TOUPPER "${FF_MODULE_DIR}" FF_MODULE_UPPER)
-724
View File
@@ -1,724 +0,0 @@
# Contributing to fastfetch
<details>
<summary>
Note
</summary>
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>
Thanks for your interest in fastfetch. This document covers building, architecture, how to add a module or a logo, code style, commit conventions, and the pull request workflow.
fastfetch is a system information tool written in C23, supporting Linux, macOS, Windows, the BSDs, Solaris, Haiku and Android. The project is borderline obsessive about **startup time** and **optional dependencies** — many design decisions only make sense under that constraint, and this document keeps coming back to it.
---
## Table of contents
- [Contributor quick reference](#contributor-quick-reference)
- [Getting started](#getting-started)
- [Code map](#code-map)
- [Core architecture: modules vs. detection](#core-architecture-modules-vs-detection)
- [Module registration](#module-registration)
- [Walkthrough: adding a module](#walkthrough-adding-a-module)
- [Walkthrough: adding a logo](#walkthrough-adding-a-logo)
- [Platform conventions](#platform-conventions)
- [Code style](#code-style)
- [Commit messages](#commit-messages)
- [Changelog](#changelog)
- [Tests](#tests)
- [Pull requests](#pull-requests)
- [Gotchas](#gotchas)
---
## Contributor quick reference
| Task | Start here | Verify with |
|---|---|---|
| Build and run fastfetch | `run.sh`, `CMakeLists.txt` | `./run.sh` |
| Add a module | `src/modules/<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 normal code change, the shortest useful loop is: configure, build the `fastfetch` target, run the affected module, then run the tests. If you add a directory or a generated input, re-run `cmake -B build` so CMake refreshes its file globs.
---
## Getting started
### Building
The quickest way is `run.sh` in the repository root — it creates `build/`, configures, compiles and runs the binary:
```sh
./run.sh # build and run
./run.sh --format json # arguments are forwarded to fastfetch
```
Manual build:
```sh
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build --target fastfetch -j$(nproc)
./build/fastfetch
```
The default build type is `RelWithDebInfo`. LTO is enabled whenever `ENABLE_LTO=ON` **and** `CMAKE_BUILD_TYPE != Debug` — so the default already has it, and only `Debug` builds lack it. This matters because LTO here is not merely an optimization: it is what strips the code of disabled modules. See [Gotchas](#gotchas).
### Common build options
| Option | Default | Notes |
|---|---|---|
| `CMAKE_BUILD_TYPE` | `RelWithDebInfo` | LTO is on for anything but `Debug` |
| `BUILD_TESTS` | `OFF` | Builds the unit tests in `tests/` |
| `BUILD_FLASHFETCH` | `ON` | Also builds the stripped-down `flashfetch` |
| `BINARY_LINK_TYPE` | `dlopen` | `dlopen` / `dynamic` / `static` |
| `ENABLE_ASAN` | `OFF` | Address Sanitizer |
| `ENABLE_LTO` | `ON` | Link-time optimization |
| `MODULE_DISABLE_<NAME>` | `OFF` | Disable a module, e.g. `MODULE_DISABLE_GPU=ON` |
| `SET_TWEAK` | `ON` | Appends a tweak to the dev version; turned off for releases |
`BINARY_LINK_TYPE=dlopen` (the default) is a deliberate design choice: optional dependencies (Vulkan, Wayland, DBus, ImageMagick, chafa, …) are loaded at runtime, so a missing library never prevents startup. When adding a third-party dependency, use the dlopen helpers in `common/library.h` — do not link it directly.
For a minimal-dependency build, see `.github/workflows/build-no-features-test.yml`:
```sh
cmake -DBUILD_TESTS=On -DENABLE_VULKAN=OFF -DENABLE_WAYLAND=OFF -DENABLE_X11=OFF \
-DENABLE_DBUS=OFF -DENABLE_ZLIB=OFF ... .
```
### Dependencies
Required: CMake ≥ 3.21 and a C23 compiler (GCC, Clang or MSVC). Everything else is optional.
Optional dependencies are auto-detected: libpci, libdrm, vulkan, wayland, xcb, xrandr, dbus, sqlite3, rpm, imagemagick{6,7}, chafa, zlib, egl, glx, opencl, freetype, pulse, ddcutil, elf, libzfs, and more.
---
## Code map
```
src/
├── fastfetch.c entry point: CLI parsing, module dispatch, main loop (36 KB)
├── flashfetch.c entry point for the stripped-down build
├── options/ global (non-module) config: general / display / logo
├── modules/ module layer — module formatting and output
├── detection/ detection layer — platform-specific data retrieval
├── common/ infrastructure: FFstrbuf, FFlist, formatting, printing, networking
├── logo/ 530 ASCII logos plus the image logo backends
└── 3rdparty/ yyjson, widecharwidth, display-library
```
Flow of control:
```
fastfetch.c → options/ (global configuration)
→ modules/ (76 modules, formatting and output)
→ detection/ (one platform implementation each, raw data)
→ common/ (infrastructure)
```
The number of module directories and detection directories does not match, and that is expected. The following relationships are structural rather than a fixed inventory:
- **13 modules have no detection directory** — they are either pure layout (`break`, `separator`, `colors`, `title`, `logo`) or reuse someone else's detection result (`display`, `monitor`, `kernel`, `shell`, `terminal`, `player`, `custom`, `datetime`).
- **4 detection directories serve modules with different names** — `displayserver` (→ `display`, `monitor`), `gtk_qt` (→ `theme`, `icons`, `font`, `cursor`), `terminalshell` (→ `terminal`, `shell`) and `libc`.
So do not assume `modules/<x>/` always has a matching `detection/<x>/`.
### Layer boundaries
| Layer | Owns | Must not |
|---|---|---|
| `options/` | **Global** config (colors, logo, threading, timeouts) | hold per-module options |
| `modules/` | formatting, printing, JSON serialization | read `/proc`, call Win32 APIs, parse sysfs |
| `detection/` | cross-platform data retrieval, clean result structs | print anything, read display config |
| `common/` | general-purpose utilities, no business logic | depend on a specific module |
The test is simple: **no file under `detection/` should contain `printf` or read `instance.config`.** Symmetrically, no file under `modules/` should contain `#ifdef __linux__`.
---
## Core architecture: modules vs. detection
This is the single most important convention in the codebase. Every feature is a fixed set of files — using CPU as the example:
```
modules/cpu/option.h module option struct
modules/cpu/cpu.c formatting and output
detection/cpu/cpu.h platform-independent interface
detection/cpu/cpu_linux.c ┐
detection/cpu/cpu_apple.c │
detection/cpu/cpu_windows.c ├─ platform implementations, one linked per build
detection/cpu/cpu_bsd.c │
detection/cpu/cpu_nosupport.c ┘
```
The interface is deliberately minimal — usually just two things:
```c
typedef struct FFCPUResult { ... } FFCPUResult; // result struct
const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu);
```
The `const char*` return value is an **error string**; `nullptr` means success. This pattern is used throughout `detection/` — please keep it consistent.
Helper functions are only exposed when they are genuinely shared between platform implementations (as in `cpu.h`: `ffCPUAppleCodeToName`, `ffCPUDetectByCpuid`).
**The payoff:** adding a platform never touches `modules/`; fixing output formatting never touches `detection/`.
---
## Module registration
### The descriptor
Every module exports one `FFModuleBaseInfo` (defined in `common/option.h:46`) — essentially a hand-written vtable:
```c
typedef struct FFModuleBaseInfo {
const char* name; // lookup key, e.g. "cpu"
const char* description; // help text
FFModuleDisplayName displayName; // module name in 20 languages
void (*initOptions)(void* options);
void (*destroyOptions)(void* options);
void (*parseJsonObject)(void* options, struct yyjson_val* module);
bool (*printModule)(void* options);
bool (*generateJsonResult)(void* options, yyjson_mut_doc*, yyjson_mut_val*);
void (*generateJsonConfig)(void* options, yyjson_mut_doc*, yyjson_mut_val*);
FFModuleFormatArgList formatArgs; // format placeholder table
const uint8_t defaultOrder; // sort weight
} FFModuleBaseInfo;
```
The source comment openly admits this is UB — `void*` is not compatible with `FF*Options*`. It is a pragmatic compromise to get polymorphism in C. Don't try to "fix" it.
### The registry: a first-letter hash bucket
`src/modules/modules.c` defines 26 `static FFModuleBaseInfo*` arrays (`A[]` through `Z[]`), each terminated by `nullptr`, collected into `ffModuleInfos[26]`:
```c
FFModuleBaseInfo** modules = ffModuleInfos[toupper(name[0]) - 'A']; // O(1) bucket lookup
for (; *modules; ++modules) { // linear scan in the bucket
if (ffStrEqualsIgnCase(name, (*modules)->name)) { ... }
}
```
The registry is small enough that a subtraction plus a few string comparisons is preferable to a general-purpose hash table. When adding a module, place its descriptor in the bucket matching the first letter of `.name`; keep the existing `nullptr` terminator at the end.
### The calling convention: zero heap allocation
Every dispatch site (`jsonconfig.c:98`, `commandoption.c:189`) follows the same pattern:
```c
alignas(uint64_t) uint8_t optionBuf[FF_OPTION_MAX_SIZE]; // 256 bytes, on the stack
baseInfo->initOptions(optionBuf);
baseInfo->parseJsonObject(optionBuf, jsonVal); // JSONC path only
baseInfo->printModule(optionBuf); // or generateJsonResult
baseInfo->destroyOptions(optionBuf);
```
`FF_OPTION_MAX_SIZE = 1 << 8` (256 bytes). Every module's `option.h` must end with:
```c
static_assert(sizeof(FFCPUOptions) <= FF_OPTION_MAX_SIZE, "FFCPUOptions size exceeds maximum allowed size");
```
**Meaning: no module option struct may exceed 256 bytes.** Hard constraint, enforced at compile time.
The three dispatch entry points:
| Entry point | Location | Used for |
|---|---|---|
| `parseModuleJsonObject` | `common/impl/jsonconfig.c:90` | the `modules[]` array in a JSONC config |
| `parseStructureCommand` | `common/impl/commandoption.c:181` | the colon-separated structure string |
| `ffParseModuleOptions` | `common/impl/commandoption.c:13` | **deprecated** — see [Gotchas](#4-cli-module-options-are-removed) |
### Build-time module discovery
`CMakeLists.txt:131` globs `src/modules/*/` and generates a `MODULE_DISABLE_<UPPER>` option per directory:
```cmake
file(GLOB FF_MODULE_DIRS RELATIVE "..." ".../src/modules/*/")
foreach(FF_MODULE_DIR ${FF_MODULE_DIRS})
option(MODULE_DISABLE_${FF_MODULE_UPPER} "Disable module ${FF_MODULE_DIR}" OFF)
endforeach()
```
**Adding a module requires no CMake change.** The same trick generates the package manager switches (`:146` regex-scans `src/modules/packages/option.h` for `FF_PACKAGES_FLAG_*_BIT`).
---
## Walkthrough: adding a module
Adding a `Foo` module, step by step.
### 1. Define the option struct
`src/modules/foo/option.h`:
```c
#pragma once
#include "common/option.h"
typedef struct FFFooOptions {
FFModuleArgs moduleArgs; // must be the first field
bool showBar;
} FFFooOptions;
static_assert(sizeof(FFFooOptions) <= FF_OPTION_MAX_SIZE, "FFFooOptions size exceeds maximum allowed size");
```
`FFModuleArgs` must come first. It provides `key`, `format`, `outputColor`, `keyColor`, `keyIcon` and `keyWidth`; because it is the first field, `ffJsonConfigParseModuleArgs()` can handle these generically and you get them **for free — no parsing code to write**.
Note these fields can only be set through the JSON config; CLI module options have been removed (see [Gotchas](#4-cli-module-options-are-removed)).
### 2. Implement detection
`src/detection/foo/foo.h`:
```c
#pragma once
#include "fastfetch.h"
typedef struct FFFooResult {
FFstrbuf name;
uint32_t count;
} FFFooResult;
const char* ffDetectFoo(const FFFooOptions* options, FFFooResult* result);
```
`src/detection/foo/foo_linux.c`:
```c
#include "foo.h"
const char* ffDetectFoo(const FFFooOptions* options, FFFooResult* result) {
// read /sys, /proc, sysfs, dbus, ...
return nullptr; // success; return an error string on failure
}
```
**Write one file per target platform, and a `foo_nosupport.c` fallback for the rest.** There are already 45 `*_nosupport.c` files doing exactly this.
### 3. Implement the module
`src/modules/foo/foo.c` — implement the six functions, then define the descriptor:
```c
FFModuleBaseInfo ffFooModuleInfo = {
.name = "Foo",
.description = "Print foo information",
.displayName = {
.en = "Foo", .ar = "Foo", .cs = "Foo", .de = "Foo", .es = "Foo",
.fr = "Foo", .gl = "Foo", .he = "Foo", .id = "Foo", .it = "Foo",
.ja = "Foo", .ko = "Foo", .pl = "Foo", .pt = "Foo", .ru = "Foo",
.tr = "Foo", .uk = "Foo", .vi = "Foo", .zh_CN = "Foo", .zh_TW = "Foo",
},
.initOptions = (void*) ffInitFooOptions,
.destroyOptions = (void*) ffDestroyFooOptions,
.parseJsonObject = (void*) ffParseFooJsonObject,
.printModule = (void*) ffPrintFoo,
.generateJsonResult = (void*) ffGenerateFooJsonResult,
.generateJsonConfig = (void*) ffGenerateFooJsonConfig,
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "Name", "name" },
{ "Count", "count" },
})),
.defaultOrder = 73,
};
```
Points to note:
- **`displayName`** is a literal block of all 20 language fields — there is no shortcut macro, so copy the layout from a neighboring module: **`en`, `ar`, `cs`, `de`, `es`, `fr`, `gl`, `he`, `id`, `it`, `ja`, `ko`, `pl`, `pt`, `ru`, `tr`, `uk`, `vi`, `zh_CN`, `zh_TW`**.
**Fill in all 20.** The struct is read by byte offset (see [Gotchas](#6-localization-uses-byte-offsets-not-enums)) and there is **no fallback** — a missing field means the key prints empty in that language.
- **`formatArgs`** must be exhaustive. It drives the placeholder list printed by `fastfetch -h foo-format`; **anything you omit is invisible and unusable to the user.**
- The `moduleFormat` section in `doc/json_schema.json` is generated by `fastfetch -h format-json`. Its metadata comes from `FFModuleBaseInfo::formatArgs`. To keep the schema and module descriptors consistent, do not edit the `moduleFormat` section in `doc/json_schema.json` by hand; update the module metadata and regenerate it instead.
- **`defaultOrder`**: use the current maximum plus 1. Search the existing descriptors for `.defaultOrder =` before choosing a value; do not copy a hard-coded value from this document. Leaving it out, or setting it to `0`, makes the module **disappear** from the interactive `--gen-config` picker. Only `logo`, `command` and `custom` do this deliberately, because they need user arguments or are invoked directly by the display layer.
### 4. Register it
- Add `#include "modules/foo/foo.h"` to `src/modules/modules.h`
- Insert into the `F[]` array in `src/modules/modules.c`:
```c
#if !FF_MODULE_DISABLE_FOO
&ffFooModuleInfo,
#endif
```
`FF_MODULE_DISABLE_FOO` is generated by CMake — you do not define it yourself.
### 5. Modules that need warm-up
If your module needs a sampling interval (CPU usage) or a network round-trip (public IP, weather), also implement `ffPrepareFoo()` and register it in the switch inside `ffPrepareCommandOption()` in `common/impl/commandoption.c`, under the right first-letter `case`. Six modules do this today: CPUUsage, DiskIO, NetIO, PublicIP, Top and Weather.
### 6. Verify
```sh
cmake -B build && cmake --build build -j
./build/fastfetch -s foo --format json # JSON output
./build/fastfetch -h foo-format # list formatArgs placeholders
./build/fastfetch --gen-config # confirm it appears in the picker
```
Note the `-format` suffix on the help flag: `fastfetch -h foo` does not work, only `fastfetch -h foo-format`.
---
## Walkthrough: adding a logo
A new logo **must** have a corresponding "Logo Request" issue, linked from the PR with `Closes #1234`. Logo PRs without a linked issue are not accepted.
### 1. Drop in the ASCII file
```
src/logo/ascii/<first-letter>/<distro>.txt
```
For example `src/logo/ascii/o/omarchy.txt`. Directories are already split by first letter (`a/``z/`, plus `_/`).
The file is plain ASCII art with `$1``$9` as color placeholders:
```
$3 ,-^-___
$3 /\\\///
$2refined.$1 /\\\\//
```
- `$1``$9` map to palette slots 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**; monochrome is a legacy style
- Tabs are expanded to 4 spaces
- Colors can be overridden with `--logo-color-1``--logo-color-9`
### 2. Register it in the `.inc`
CMake turns each `.txt` into a `FASTFETCH_DATATEXT_LOGO_<UPPERCASE>` macro (`CMakeLists.txt:418`), but **the registry itself is maintained by hand**. Edit `src/logo/ascii/<first-letter>.inc`:
```c
#ifdef FASTFETCH_DATATEXT_LOGO_OMARCHY
// Omarchy
{
.names = { "omarchy", "Omarchy" },
.lines = FASTFETCH_DATATEXT_LOGO_OMARCHY,
.colors = {
FF_COLOR_FG_BLUE,
FF_COLOR_FG_WHITE,
FF_COLOR_FG_CYAN,
},
},
#endif
```
- `names` is matched case-insensitively. Do not add names that differ only by case, because they can never be distinguished. Every name is also shown by `--list-logos`, so use user-friendly spelling such as an initial capital where appropriate.
- For a new logo, use exactly one name unless there is a specific compatibility reason to add more. That name should first be the `ID` from `/etc/os-release`. If the ID cannot distinguish this logo from another logo for the same OS, use the `NAME` value instead. `logoGetBuiltinDetected` in `src/logo/logo.c` documents the detection order: `ID`, then `NAME`, then tokens from `ID_LIKE`, then the platform name fallback. Manual `logo-source` selection uses the name directly.
- `colors` is positional — entry 0 is `$1`, entry 1 is `$2`, and so on
- `colors[0]` also becomes the title color and `colors[1]` the key color, unless the user overrides them
If the same OS has multiple logo variants, mark the variant explicitly with `.type`:
```c
.type = FF_LOGO_LINE_TYPE_SMALL_BIT,
```
Use `FF_LOGO_LINE_TYPE_ALTER_BIT` for an alternate logo, `FF_LOGO_LINE_TYPE_SMALL_BIT` for a small logo, or combine the flags when both apply. This is also a lookup optimization: a logo marked `FF_LOGO_LINE_TYPE_SMALL_BIT` is considered only for `type = small`, while a logo marked `FF_LOGO_LINE_TYPE_ALTER_BIT` is never selected by automatic detection. Alternate logos are available only when explicitly requested through `--logo <source>`.
### 3. Verify
```sh
./build/fastfetch --logo omarchy
./build/fastfetch --list-logos | grep -i omarchy
```
---
## Platform conventions
The `detection/` layer picks its implementation by **filename suffix**; the choice is made at build time from `CMAKE_SYSTEM_NAME`:
| Suffix | Platform |
|---|---|
| `_linux.c` | Linux |
| `_apple.c` / `_apple.m` | macOS / iOS (`.m` for Objective-C) |
| `_windows.c` / `_windows.cpp` | Windows (`.cpp` for WinRT / WMI) |
| `_bsd.c` | FreeBSD |
| `_nbsd.c` | NetBSD |
| `_obsd.c` | OpenBSD |
| `_sunos.c` | Solaris / illumos |
| `_haiku.c` / `.cpp` | Haiku |
| `_android.c` | Android (Termux) |
| `_gnu.c` | GNU/Hurd |
| `_nosupport.c` | Empty fallback |
Current distribution of platform files in `detection/`:
| Suffix | Files | Suffix | Files |
|---|---|---|---|
| `_linux` | 58 | `_obsd` | 16 |
| `_windows` | 47 | `_haiku` | 13 |
| `_apple` | 46 | `_android` | 12 |
| `_nosupport` | 45 | `_gnu` | 3 |
| `_bsd` | 29 | | |
| `_sunos` | 19 | | |
| `_nbsd` | 17 | | |
(These are repository inventory figures, counting `.c`, `.m`, `.cpp`, and `.h`; they may change as platforms and modules are added.)
**Don't pile `#ifdef __linux__` into one file.** When adding platform support, copy the closest existing implementation and change the suffix.
`common/` uses the same idea: `common/impl/` contains `io_unix.c` / `io_windows.c`, `netif_linux.c` / `netif_apple.c` / `netif_bsd.c` and so on, with `common/apple/`, `common/windows/` and `common/haiku/` holding platform-specific helpers.
When several platform suffixes could apply, use the most specific implementation supported by the build system (for example, `_nbsd.c` instead of the generic `_bsd.c` on NetBSD). Keep the generic file as the fallback for platforms that share its conventions.
---
## Code style
### Formatting
The project uses clang-format with `BasedOnStyle: LLVM` plus these key overrides:
```yaml
IndentWidth: 4
UseTab: Never
ColumnLimit: 0 # never wrap
InsertBraces: true # braces even on single-statement ifs
PointerAlignment: Left # char* p, not char *p
SortIncludes: Never # include order is managed by hand
AlignAfterOpenBracket: DontAlign
BinPackParameters: false # all params on one line, or one per line
```
Before committing:
```sh
clang-format -i src/modules/foo/*.c src/modules/foo/*.h
```
`src/3rdparty/**`, `build/**` and `src/logo/builtin.c` are listed in `.clang-format-ignore`**do not reformat them.**
`.editorconfig` adds LF line endings, 4-space indent, a final newline, and trailing whitespace trimmed (except in Markdown).
### Naming
| Kind | Convention | Example |
|---|---|---|
| Functions | `ff` + PascalCase | `ffDetectCPU`, `ffPrintCPU` |
| Types | `FF` + PascalCase | `FFCPUResult`, `FFModuleBaseInfo` |
| Variables / fields | camelCase | `coresPhysical`, `keyWidth` |
| Macros | SCREAMING_SNAKE_CASE | `FF_OPTION_MAX_SIZE` |
| Module options | `FF<Name>Options` | `FFCPUOptions` |
| Detection results | `FF<Name>Result` | `FFCPUResult` |
### Spelling
CI runs codespell (`.codespellrc`). Known false positives live in `ignore-words-list` (`iterm`, `compiletime`, and various non-English distro words). Add new words there rather than changing the code.
### Compiler warnings
The build enables `-Wall -Wextra -Wconversion` plus several `-Werror`s:
```
-Werror=uninitialized -Werror=return-type -Werror=vla
-Werror=incompatible-pointer-types -Werror=implicit-function-declaration -Werror=int-conversion
```
**`-Wconversion` is strict**: every implicit narrowing conversion needs an explicit cast.
---
## Commit messages
Format:
```
<Scope>[ (Platform)]: <third-person singular verb> <object>
```
Scope is the module or subsystem name; Platform is optional. Use the third-person singular present tense, capitalize the first letter, no trailing period.
Real examples from recent history:
```
Processes (Haiku): honors `options->countKprocs`
WM (macOS): improves reliability of WM plugin detection
Memory (Windows): prefers `NQSI`
Top (Linux): improves performance of `stat` parsing
Logo (Builtin): adds omarchy
Global: introduces global macro `FF_PATH_PKG_BASE` to replace `_PATH_LOCALBASE`
LM (OpenBSD): adds support
CI: disables fail on alert
Doc: updates README
Presets: moves `top` to the bottom of running modules [ci skip]
```
Common verbs: `adds`, `removes`, `fixes`, `improves`, `updates`, `corrects`, `prefers`, `honors`, `disables`, `enables`, `introduces`, `detects`, `reports`, `skips`, `uses`.
Scopes in use: `Top`, `Processes`, `Memory`, `Logo (Builtin)`, `CI`, `Doc`, `Presets`, `Global`, plus individual module names.
Documentation-only changes get a `[ci skip]` suffix.
---
## Changelog
User-visible changes go into `CHANGELOG.md`, under the topmost version heading:
```markdown
# Unreleased
Changes:
* The DE / WM / LM modules now reports the full name ...
Features:
* Added Top module to print processes with the highest CPU, memory or disk I/O usage. (Top)
* Improved Wi-Fi module
* Added Wifi channel width detection, exposed via `{channel-width}` in custom format.
* Improved Wifi channel frequency accuracy on Windows, macOS.
* Added Battery detection support on SunOS. (Battery, SunOS)
Bugfixes:
* Fixed I/O rate calculation precision in DiskIO and NetIO. (DiskIO / NetIO)
```
Rules:
- Three sections: `Changes:` (behavior changes), `Features:`, `Bugfixes:`
- Past tense: `Added` / `Fixed` / `Improved`
- End each entry with its scope: `(Module, Platform)` or `(ModuleA / ModuleB)`
- Sub-bullets are indented 4 spaces
---
## Tests
Tests are standalone executables in `tests/` using a `VERIFY` macro that exits non-zero on failure:
```c
#define VERIFY(expression) \
if (!(expression)) testFailed(&strbuf, #expression, __LINE__)
```
Current tests: `strbuf.c`, `list.c`, `format.c`, `color.c`, `duration.c`, `strutil.c`.
```sh
cmake -B build -DBUILD_TESTS=On
cmake --build build
cd build && ctest --output-on-failure
```
Coverage focuses on the core data structures and the formatting engine in `common/`. The `detection/` layer has no automated tests (it depends too heavily on a real system) and is instead covered by the CI matrix — 20 workflows under `.github/workflows/` spanning Linux (including musl, loong64, armv7l, i686), macOS, Windows, FreeBSD, NetBSD, OpenBSD, DragonFly, Solaris, OmniOS and Haiku, plus spellcheck and benchmark jobs.
**If you touch `common/FFstrbuf.h`, `common/format.h` or `common/color.h`, please extend the matching test.**
---
## Pull requests
1. **Open an issue first** (feature request / bug report / logo request) so you don't waste effort. Templates are in `.github/ISSUE_TEMPLATE/`.
2. Branch off `dev`**`dev` is the main development branch**, not `master`.
3. Follow the [commit message convention](#commit-messages).
4. Update `CHANGELOG.md` for user-visible changes.
5. Open the PR against `dev` and fill in `.github/pull_request_template.md`:
- Summary
- Related issue (**required for new logos**, otherwise the PR won't be accepted)
- Changes
- Screenshots (required for visual changes)
- Checklist: confirm you tested locally
### Pre-submit checklist
```sh
clang-format -i <changed files> # format
codespell # spelling
cmake -B build -DBUILD_TESTS=On && cmake --build build -j
cd build && ctest --output-on-failure # tests
./build/fastfetch --format json # verify JSON output is well-formed
./build/fastfetch -c presets/all.jsonc --stat false # smoke-test every module
```
---
## Gotchas
Things that are easy to get wrong when reading this codebase. Most of them follow from the "obsessively fast startup" goal.
### 1. Option structs must not exceed 256 bytes
`FF_OPTION_MAX_SIZE = 1 << 8`. Exceeding it is caught at compile time by `static_assert`. Don't try to raise the value — it determines the stack cost of every module invocation.
### 2. `FF_MODULE_DISABLE_*` controls registration, not compilation
```c
// verbatim from the top of modules/modules.c:
// FF_MODULE_DISABLE_<module> only controls if the module is registered,
// the module code itself is still compiled.
// We rely `LTO` to remove the unused code (only enabled in Release mode)
```
**Disabling modules in a Debug build does not shrink the binary.** LTO is enabled whenever `CMAKE_BUILD_TYPE != Debug`, and the default `RelWithDebInfo` already satisfies that — so measure size with `RelWithDebInfo` or `Release`, never with `Debug`. (The "Release mode" wording in the comment above is imprecise.)
### 3. CMake globs don't trigger reconfiguration
Both the module directories and the logo files are discovered by glob. CMake will not notice new files on its own — you need to re-run `cmake -B build` (or delete `build/CMakeCache.txt` and start over). This is the classic CMake footgun.
### 4. CLI module options are removed
Per-module command-line flags such as `--cpu-temp` are **no longer supported**. The only job of `ffParseModuleOptions` now is to translate the flag into a JSON key, then `exit(477)` with a pointer to the config file:
```
Error: Unsupported module option: --cpu-temp
Support of module options has been removed. Please add the flag to the JSON config instead.
Example (demonstration only): `{ "modules": [ { "type": "cpu", "temp": true } ] }`
```
**JSONC is the first-class configuration interface; the command line is not.** When adding module options, implement `parseJsonObject` only — do not add a CLI branch.
### 5. `defaultOrder = 0` hides a module from `--gen-config`
`collectModuleInfos` (`genconfig.c:186`) skips any module whose `defaultOrder` is `0`. C zero-initializes the field, so **omitting it is the same as setting it to 0**. Only `logo`, `command` and `custom` do this on purpose.
`defaultOrder` affects only the ordering in the interactive `--gen-config` picker; it has no effect on runtime output order.
### 6. Localization uses byte offsets, not enums
`instance.config.display.keyLanguage` does not hold a language enum — it holds a **byte offset** such as `offsetof(FFModuleDisplayName, zh_CN)`. The macro `FF_MODULE_GET_DISPLAY_NAME` (`common/option.h:123`) does plain pointer arithmetic with it:
```c
#define FF_MODULE_GET_DISPLAY_NAME(moduleName) \
(*(const char**) ((uint8_t*) &ff ## moduleName ## ModuleInfo.displayName + instance.config.display.keyLanguage))
```
Consequence: **the field order of `FFModuleDisplayName` must never change** — reordering silently scrambles every language.
### 7. The `multithreading` switch barely does anything
The global `multithreading` option currently takes effect in exactly one place: `common/impl/networking_linux.c:339`. Modules are still printed sequentially. Modules that need concurrency go through the `ffPrepare*` warm-up hooks instead, which start sampling or fire off requests before the print loop begins.
### 8. Don't print or read config inside `detection/`
The `detection/` layer must stay pure: read system state, fill a struct, return an error string. Any `printf` or any read of `instance.config` there is a design error.
### 9. `src/logo/builtin.c` and `3rdparty/` are formatting-exempt
The former is a large generated/hand-maintained data table, the latter is upstream code. Both are excluded via `.clang-format-ignore` — leave them alone.
---
## Reference
- Configuration guide: <https://github.com/fastfetch-cli/fastfetch/wiki/Configuration>
- JSON schema: `doc/json_schema.json`
- Example presets: `presets/` (`all.jsonc`, `neofetch.jsonc`, `ci.jsonc`, …)
- Man page source: `doc/fastfetch.1.in`
- Code generation scripts: `scripts/` (`gen-pciids.py`, `gen-amdgpuids.py`, `gen-man.py`)
-6
View File
@@ -1,9 +1,3 @@
fastfetch (2.68.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.68.0
-- Carter Li <zhangsongcui@live.cn> Tue, 01 Sep 2026 16:34:36 +0800
fastfetch (2.67.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
* Update to 2.67.0
+9 -13
View File
@@ -322,7 +322,7 @@
"type": "string"
},
"cpuFormat": {
"description": "Output format for the `CPU` module. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: CPU microarchitecture\n 12. {numa-nodes}: NUMA node count\n 13. {code-name}: CPU code name\n 14. {technology}: CPU technology",
"description": "Output format for the `CPU` module. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: CPU microarchitecture\n 12. {numa-nodes}: NUMA node count",
"type": "string"
},
"cpucacheFormat": {
@@ -398,7 +398,7 @@
"type": "string"
},
"lmFormat": {
"description": "Output format for the `LM` module. See Wiki for formatting syntax\n 1. {service}: LM service / process name\n 2. {pretty-name}: LM pretty name\n 3. {version}: LM version",
"description": "Output format for the `LM` module. See Wiki for formatting syntax\n 1. {service}: LM service\n 2. {type}: LM type\n 3. {version}: LM version",
"type": "string"
},
"loadavgFormat": {
@@ -506,7 +506,7 @@
"type": "string"
},
"topFormat": {
"description": "Output format for the `Top` module. See Wiki for formatting syntax\n 1. {name}: Process name\n 2. {pid}: Process ID\n 3. {cpu}: CPU usage\n 4. {mem}: Memory usage (RSS) in bytes\n 5. {disk-read}: Disk read bytes per second (0 if unsupported)\n 6. {disk-write}: Disk write bytes per second (0 if unsupported)\n 7. {threads}: Number of threads\n 8. {cpu-percentage}: CPU usage percentage\n 9. {mem-formatted}: Memory usage (RSS) formatted\n 10. {disk-read-formatted}: Disk read formatted\n 11. {disk-write-formatted}: Disk write formatted",
"description": "Output format for the `Top` module. See Wiki for formatting syntax\n 1. {name}: Process name\n 2. {path}: Executable path\n 3. {pid}: Process ID\n 4. {cpu}: CPU usage\n 5. {mem}: Memory usage (RSS) in bytes\n 6. {disk-read}: Disk read bytes per second\n 7. {disk-write}: Disk write bytes per second\n 8. {cpu-percentage}: CPU usage percentage\n 9. {mem-formatted}: Memory usage (RSS) formatted\n 10. {disk-read-formatted}: Disk read formatted\n 11. {disk-write-formatted}: Disk write formatted",
"type": "string"
},
"themeFormat": {
@@ -4441,23 +4441,21 @@
"properties": {
"type": {
"const": "top",
"description": "Print processes with the highest CPU, memory, disk I/O usage, latest start time or thread count"
"description": "Print processes with the highest CPU or memory usage"
},
"sort": {
"type": "string",
"description": "Sort processes by CPU, memory, disk IO usage, start time or thread count",
"description": "Sort processes by CPU, memory or disk IO usage",
"enum": [
"cpu",
"memory",
"disk-read",
"disk-write",
"start-time",
"threads"
"disk-write"
],
"default": "cpu"
},
"showTypes": {
"description": "Specify which resource usages are detected and displayed.\nIf memory or threads is enabled is enabled alone, only a single process snapshot is taken and `waitTime` sampling wait is skipped",
"description": "Specify which resource usages are detected and displayed.\nIf memory is enabled is enabled alone, only a single process snapshot is taken and `waitTime` sampling wait is skipped",
"default": [
"cpu",
"memory",
@@ -4469,8 +4467,7 @@
"enum": [
"cpu",
"memory",
"disk",
"threads"
"disk"
]
},
{
@@ -4481,8 +4478,7 @@
"enum": [
"cpu",
"memory",
"disk",
"threads"
"disk"
]
}
}
+1 -1
View File
@@ -12,7 +12,7 @@
},
"modules": [
{
// draw borders first to make colors of left and right border consistent
// draw borders first to make colors of left and right border consistant
"key": " user",
"type": "title",
"format": "{user-name}",
-17
View File
@@ -1,17 +0,0 @@
#pragma once
#include <stdint.h>
#if __BIG_ENDIAN__
#define FF_READ_LE(x) _Generic((x), \
uint16_t: __builtin_bswap16(x), \
uint32_t: __builtin_bswap32(x), \
uint64_t: __builtin_bswap64(x))
#define FF_READ_BE(x) (x)
#else
#define FF_READ_LE(x) (x)
#define FF_READ_BE(x) _Generic((x), \
uint16_t: __builtin_bswap16(x), \
uint32_t: __builtin_bswap32(x), \
uint64_t: __builtin_bswap64(x))
#endif
-2
View File
@@ -238,9 +238,7 @@ static void getDataDirs(FFPlatform* platform) {
ffPlatformPathAddHome(&platform->dataDirs, platform, "");
platformPathAddEnv(&platform->dataDirs, "XDG_DATA_DIRS");
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
ffPlatformPathAddAbsolute(&platform->dataDirs, FF_PATH_PKG_BASE "/share/");
#endif
ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/local/share/");
ffPlatformPathAddAbsolute(&platform->dataDirs, FASTFETCH_TARGET_DIR_USR "/share/");
}
+6 -3
View File
@@ -1,5 +1,4 @@
#include "common/base64.h"
#include "common/endian.h"
// https://github.com/kostya/benchmarks/blob/master/base64/test-nolib.c#L145
void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output) {
@@ -8,8 +7,12 @@ void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char*
const char* ends = str + (size - size % 3);
while (str != ends) {
uint32_t n = *(uint32_t*) str;
// The 3 input bytes must be laid out big-endian (str[0] in the most significant position).
n = FF_READ_BE(n);
#if !__BIG_ENDIAN__
// The 3 input bytes must be laid out big-endian (str[0] in the most
// significant position). On little-endian hosts swap; on big-endian
// hosts the word is already in the right order.
n = __builtin_bswap32(n);
#endif
*out++ = chars[(n >> 26) & 63];
*out++ = chars[(n >> 20) & 63];
*out++ = chars[(n >> 14) & 63];
+1 -1
View File
@@ -996,7 +996,7 @@ bool ffGenConfigInteractive(FFdata* data) {
success = true;
}
} else {
fputs("\nConfig generation canceled.\n", stderr);
fputs("\nConfig generation cancelled.\n", stderr);
}
ffListDestroy(&ui.items);
-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);
}
+2 -2
View File
@@ -507,8 +507,8 @@ typedef struct E_Config {
Eina_List* font_defaults;
} E_Config; // Must be the same name as the top level struct in e.cfg
#define FF_EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(klass, type) \
(ffeet_eina_file_data_descriptor_class_set(klass, sizeof(*(klass)), #type, sizeof(type)))
#define FF_EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(clas, type) \
(ffeet_eina_file_data_descriptor_class_set(clas, sizeof(*(clas)), #type, sizeof(type)))
#define FF_EET_DATA_DESCRIPTOR_ADD_BASIC(edd, struct_type, member, type) \
do { \
struct_type ___ett; \
+16 -17
View File
@@ -1,4 +1,3 @@
#include "common/endian.h"
#include "common/smbios.h"
#include "common/io.h"
#include "common/mallocHelper.h"
@@ -82,9 +81,9 @@ static bool parseSmbiosTable(const uint8_t* data, uint32_t length) {
break;
}
if (FF_READ_LE(header->Handle) >= 0xFF00) {
if (header->Handle >= 0xFF00) {
FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx",
FF_READ_LE(header->Handle),
header->Handle,
(unsigned long) ((const uint8_t*) header - data));
break;
}
@@ -104,25 +103,25 @@ static bool parseSmbiosTable(const uint8_t* data, uint32_t length) {
smbiosTable[header->Type] = header;
FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u",
header->Type,
FF_READ_LE(header->Handle),
header->Handle,
header->Length);
structureCount++;
} else {
FF_DEBUG("Duplicate SMBIOS structure type %u, handle 0x%04X, length %u",
header->Type,
FF_READ_LE(header->Handle),
header->Handle,
header->Length);
}
} else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) {
FF_DEBUG("Reached SMBIOS end of type %u, handle 0x%04X, length %u",
header->Type,
FF_READ_LE(header->Handle),
header->Handle,
header->Length);
break;
} else {
FF_DEBUG("Found custom SMBIOS structure type %u, handle 0x%04X, length %u; ignoring",
header->Type,
FF_READ_LE(header->Handle),
header->Handle,
header->Length);
}
}
@@ -309,8 +308,8 @@ static bool fillTableBufferFallback(FFstrbuf* buffer) {
sizeof(p->Smbios30));
return false;
}
tableLength = FF_READ_LE(p->Smbios30.StructureTableMaximumSize);
tableAddress = (off_t) FF_READ_LE(p->Smbios30.StructureTableAddress);
tableLength = p->Smbios30.StructureTableMaximumSize;
tableAddress = (off_t) p->Smbios30.StructureTableAddress;
FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u",
tableLength,
(unsigned long) tableAddress,
@@ -326,8 +325,8 @@ static bool fillTableBufferFallback(FFstrbuf* buffer) {
sizeof(p->Smbios20));
return false;
}
tableLength = FF_READ_LE(p->Smbios20.StructureTableLength);
tableAddress = (off_t) FF_READ_LE(p->Smbios20.StructureTableAddress);
tableLength = p->Smbios20.StructureTableLength;
tableAddress = (off_t) p->Smbios20.StructureTableAddress;
FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u",
tableLength,
(unsigned long) tableAddress,
@@ -370,9 +369,9 @@ static bool detectSmbiosTableLength(const uint8_t* data, uint32_t bufferLength,
return false;
}
if (FF_READ_LE(header->Handle) >= 0xFF00) {
if (header->Handle >= 0xFF00) {
FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx",
FF_READ_LE(header->Handle),
header->Handle,
(unsigned long) (p - data));
return false;
}
@@ -615,8 +614,8 @@ static bool fillTableBufferPlatform(FFstrbuf* buffer) {
sizeof(entryPoint.Smbios20));
return false;
}
tableLength = FF_READ_LE(entryPoint.Smbios20.StructureTableLength);
tableAddress = (off_t) FF_READ_LE(entryPoint.Smbios20.StructureTableAddress);
tableLength = entryPoint.Smbios20.StructureTableLength;
tableAddress = (off_t) entryPoint.Smbios20.StructureTableAddress;
FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u",
tableLength,
(unsigned long) tableAddress,
@@ -630,8 +629,8 @@ static bool fillTableBufferPlatform(FFstrbuf* buffer) {
sizeof(entryPoint.Smbios30));
return false;
}
tableLength = FF_READ_LE(entryPoint.Smbios30.StructureTableMaximumSize);
tableAddress = (off_t) FF_READ_LE(entryPoint.Smbios30.StructureTableAddress);
tableLength = entryPoint.Smbios30.StructureTableMaximumSize;
tableAddress = (off_t) entryPoint.Smbios30.StructureTableAddress;
FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u",
tableLength,
(unsigned long) tableAddress,
+12 -8
View File
@@ -1,7 +1,6 @@
#define INITGUID
#include "battery.h"
#include "common/endian.h"
#include "common/debug.h"
#include "common/mallocHelper.h"
@@ -128,24 +127,29 @@ static void detectStaticData(FFlist* entries, FFlist* results) {
FFBatteryWmiEntry* entry = getBatteryEntry(entries, results, data->Tag);
FF_DEBUG("chemistry: %.4s", (const char*) &data->Chemistry);
#if __BIG_ENDIAN__
#define htobe32(x) (x) // Should not happen on Windows, but just in case
#else
#define htobe32(x) __builtin_bswap32(x)
#endif
switch (data->Chemistry) {
case FF_READ_BE((uint32_t) 'PbAc'):
case htobe32('PbAc'):
ffStrbufSetStatic(&entry->result->technology, "Lead Acid");
break;
case FF_READ_BE((uint32_t) 'LION'):
case FF_READ_BE((uint32_t) 'Li-I'):
case htobe32('LION'):
case htobe32('Li-I'):
ffStrbufSetStatic(&entry->result->technology, "Lithium Ion");
break;
case FF_READ_BE((uint32_t) 'NiCd'):
case htobe32('NiCd'):
ffStrbufSetStatic(&entry->result->technology, "Nickel Cadmium");
break;
case FF_READ_BE((uint32_t) 'NiMH'):
case htobe32('NiMH'):
ffStrbufSetStatic(&entry->result->technology, "Nickel Metal Hydride");
break;
case FF_READ_BE((uint32_t) 'NiZn'):
case htobe32('NiZn'):
ffStrbufSetStatic(&entry->result->technology, "Nickel Zinc");
break;
case FF_READ_BE((uint32_t) 'RAM\0'):
case htobe32('RAM\0'):
ffStrbufSetStatic(&entry->result->technology, "Rechargeable Alkaline-Manganese");
break;
default:
+1 -2
View File
@@ -1,5 +1,4 @@
#include "chassis.h"
#include "common/endian.h"
#include "common/io.h"
#include "common/smbios.h"
@@ -22,7 +21,7 @@ const char* ffDetectChassis(FFChassisResult* result) {
uint32_t chassisType = 0;
if (ffReadFileData("/sys/firmware/devicetree/base/smbios/smbios/chassis/chassis-type", sizeof(chassisType), &chassisType)) // big endian
{
chassisType = FF_READ_BE(chassisType);
chassisType = __builtin_bswap32(chassisType);
const char* typeStr = ffChassisTypeToString(chassisType);
if (typeStr) {
ffStrbufSetStatic(&result->type, typeStr);
+17 -78
View File
@@ -214,15 +214,9 @@ static void detectQualcomm(FFCPUResult* cpu) {
const char* name = nullptr;
switch (code) {
case 8975:
name = "8 Elite Extreme Gen 6";
break;
case 8950:
name = "8 Elite Gen 6";
break;
case 8845:
name = "8 Gen 5";
break;
break; // ?
case 8850:
name = "8 Elite Gen 5";
break;
@@ -244,9 +238,6 @@ static void detectQualcomm(FFCPUResult* cpu) {
case 8475:
name = "8+ Gen 1";
break;
case 8425:
name = "8+ Gen 1 4G";
break;
case 8450:
name = "8 Gen 1";
break;
@@ -280,18 +271,12 @@ static void detectQualcomm(FFCPUResult* cpu) {
case 6475:
name = "6 Gen 3";
break;
case 6225:
name = "6s 4G Gen 2";
break;
case 6115:
name = "6s 4G Gen 1";
name = "6s Gen 1";
break;
case 6450:
name = "6 Gen 1";
break;
case 4850:
name = "4 Gen 5";
break;
case 4635:
name = "4s Gen 2";
break;
@@ -320,9 +305,6 @@ static void detectMediaTek(FFCPUResult* cpu) {
switch (code) // The SOC code of MTK Dimensity series is full of mess
{
case 6995:
name = "9600";
break;
case 6993:
name = "9500";
break;
@@ -375,9 +357,6 @@ static void detectExynos(FFCPUResult* cpu) {
const char* name = nullptr;
switch (code) {
case 9975:
name = "2700";
break;
case 9965:
name = "2600";
break;
@@ -395,9 +374,6 @@ static void detectExynos(FFCPUResult* cpu) {
name = "2100";
break;
case 8865:
name = "1680";
break;
case 8855:
name = "1580";
break;
@@ -837,46 +813,6 @@ static const char* detectPhysicalCores(FFCPUResult* cpu) {
return nullptr;
}
[[maybe_unused]] static bool detectSnapdragonX(FFstrbuf* name, const char* model) {
// SoC models are enumerated in the form of `x<gen>[e|p]<part><rev>`, e.g. `x1e80100`,
// which is marketed as `X1E-80-100`, where `e` stands for Elite and `p` for Plus.
// https://en.wikipedia.org/wiki/List_of_Qualcomm_Snapdragon_systems_on_chips#Snapdragon_X_series
const char* prefix;
uint32_t prefixLen;
if (ffStrStartsWith(model, "x1e")) {
prefix = "Qualcomm Snapdragon X Elite";
prefixLen = 3;
} else if (ffStrStartsWith(model, "x1p")) {
prefix = "Qualcomm Snapdragon X Plus";
prefixLen = 3;
} else if (ffStrStartsWith(model, "x2e")) {
// Only X2E-94-100 and X2E-96-100 are branded as Extreme
prefix = model[3] == '9' ? "Qualcomm Snapdragon X2 Elite Extreme" : "Qualcomm Snapdragon X2 Elite";
prefixLen = 3;
} else if (ffStrStartsWith(model, "x2p")) {
prefix = "Qualcomm Snapdragon X2 Plus";
prefixLen = 3;
} else if (ffStrStartsWith(model, "x1")) {
prefix = "Qualcomm Snapdragon X";
prefixLen = 2;
} else {
return false;
}
uint32_t length = (uint32_t) strlen(model);
bool splitCode = length - prefixLen == 5;
ffStrbufSetF(name, "%s X", prefix);
for (uint32_t i = 1; i < length; ++i) {
if (i == prefixLen || (splitCode && i == length - 3)) {
ffStrbufAppendC(name, '-');
}
ffStrbufAppendC(name, (char) toupper(model[i]));
}
return true;
}
[[maybe_unused]] static void parseIsa(FFstrbuf* cpuIsa) {
// Always use the last part of the ISA string. Ref: #590 #1204
ffStrbufSubstrAfterLastC(cpuIsa, ' ');
@@ -978,19 +914,22 @@ static const char* detectPhysicalCores(FFCPUResult* cpu) {
}
#endif
else if (ffStrEquals(vendor, "qcom")) {
// https://elixir.bootlin.com/linux/latest/source/arch/arm64/boot/dts/qcom
if (!detectSnapdragonX(&cpu->name, model)) {
if (ffStrStartsWith(model, "sc")) {
const char* code = model + 2;
uint32_t deviceId = (uint32_t) strtoul(code, nullptr, 10);
ffStrbufSetStatic(&cpu->name, ffCPUQualcommCodeToName(deviceId));
if (!cpu->name.length) {
ffStrbufAppendS(&cpu->name, "Qualcomm Snapdragon SC");
ffStrbufAppendS(&cpu->name, code);
}
} else {
ffStrbufSetS(&cpu->name, model);
// https://elixir.bootlin.com/linux/v6.11/source/arch/arm64/boot/dts/qcom
if (ffStrStartsWith(model, "x")) {
ffStrbufSetS(&cpu->name, "Qualcomm Snapdragon X Elite ");
for (const char* p = model + 1; *p; ++p) {
ffStrbufAppendC(&cpu->name, (char) toupper(*p));
}
} else if (ffStrStartsWith(model, "sc")) {
const char* code = model + 2;
uint32_t deviceId = (uint32_t) strtoul(code, nullptr, 10);
ffStrbufSetStatic(&cpu->name, ffCPUQualcommCodeToName(deviceId));
if (!cpu->name.length) {
ffStrbufAppendS(&cpu->name, "Qualcomm Snapdragon SC");
ffStrbufAppendS(&cpu->name, code);
}
} else {
ffStrbufSetS(&cpu->name, model);
}
ffStrbufSetStatic(&cpu->vendor, "Qualcomm");
+1 -2
View File
@@ -1,5 +1,4 @@
#include "cpu.h"
#include "common/endian.h"
#include "common/windows/registry.h"
#include "common/windows/nt.h"
#include "common/mallocHelper.h"
@@ -206,7 +205,7 @@ static const char* detectMaxSpeedBySmbios(FFCPUResult* cpu) {
}
}
uint32_t speed = FF_READ_LE(data->MaxSpeed);
uint32_t speed = data->MaxSpeed;
// Sometimes SMBIOS reports invalid value. We assume that max speed is small than 2x of base
if (speed < cpu->frequencyBase || speed > cpu->frequencyBase * 2) {
return "Possible invalid CPU max speed in SMBIOS data. See #800";
+5 -8
View File
@@ -1,5 +1,4 @@
#include "cpucache.h"
#include "common/endian.h"
#include "common/smbios.h"
#include "common/strutil.h"
@@ -44,26 +43,24 @@ const char* ffDetectCPUCache(FFCPUCacheResult* result) {
continue;
}
uint16_t cacheConfiguration = FF_READ_LE(data->CacheConfiguration);
uint16_t installedSize = FF_READ_LE(data->InstalledSize);
bool enabled = !!(cacheConfiguration & (1 << 7));
bool enabled = !!(data->CacheConfiguration & (1 << 7));
if (!enabled) {
continue;
}
uint32_t size = installedSize;
uint32_t size = data->InstalledSize;
if (size == 0) {
continue;
}
if (installedSize != 0xFFFF) {
if (data->InstalledSize != 0xFFFF) {
size *= (size >> 15 ? 64 : 1) * 1024u;
} else if (data->Header.Length > offsetof(FFSmbiosCacheInfo, InstalledCacheSize2)) {
size = FF_READ_LE(data->InstalledCacheSize2);
size = data->InstalledCacheSize2;
size *= (size >> 31 ? 64 : 1) * 1024u;
}
uint32_t level = (cacheConfiguration & 0b111u) + 1;
uint32_t level = (data->CacheConfiguration & 0b111u) + 1;
FFCPUCacheType type;
switch (data->SystemCacheType) {
+1 -1
View File
@@ -331,7 +331,7 @@ static bool xcbRandrHandleMonitors(XcbRandrData* data, xcb_screen_t* screen) {
}
static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen) {
// With all the initialization done, start the detection
// With all the initialisation done, start the detection
if (xcbRandrHandleMonitors(data, screen)) {
return;
}
+1 -1
View File
@@ -111,7 +111,7 @@ static void detectGTKFromSettings(FFGTKResult* result) {
} else {
// Standalone WMs (Hyprland, sway, niri, i3, ...) report no DE and have no settings
// daemon, but GTK apps on them still read org.gnome.desktop.interface.
// Read via DConf, not GSettings: GSettings synthesizes the schema default for keys
// Read via DConf, not GSettings: GSettings synthesises the schema default for keys
// the user never set, DConf returns nothing. Runs last; applyGTKSettings() only
// fills still-empty fields, so config files always win.
themeName = ffSettingsGetDConf("/org/gnome/desktop/interface/gtk-theme", FF_VARIANT_TYPE_STRING).strValue;
+1 -9
View File
@@ -1,5 +1,4 @@
#include "host.h"
#include "common/endian.h"
#include "common/smbios.h"
typedef struct [[gnu::packed]] FFSmbiosSystemInfo {
@@ -53,14 +52,7 @@ const char* ffDetectHost(FFHostResult* host) {
static_assert(offsetof(FFSmbiosSystemInfo, UUID) == 0x08, "FFSmbiosSystemInfo.UUID offset is wrong");
if (data->Header.Length > offsetof(FFSmbiosSystemInfo, UUID)) {
ffStrbufSetF(&host->uuid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
FF_READ_LE(data->UUID.TimeLow),
FF_READ_LE(data->UUID.TimeMid),
FF_READ_LE(data->UUID.TimeHighAndVersion),
data->UUID.ClockSeqHiAndReserved,
data->UUID.ClockSeqLow,
data->UUID.Node[0], data->UUID.Node[1], data->UUID.Node[2],
data->UUID.Node[3], data->UUID.Node[4], data->UUID.Node[5]);
ffStrbufSetF(&host->uuid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", data->UUID.TimeLow, data->UUID.TimeMid, data->UUID.TimeHighAndVersion, data->UUID.ClockSeqHiAndReserved, data->UUID.ClockSeqLow, data->UUID.Node[0], data->UUID.Node[1], data->UUID.Node[2], data->UUID.Node[3], data->UUID.Node[4], data->UUID.Node[5]);
}
static_assert(offsetof(FFSmbiosSystemInfo, SKUNumber) == 0x19, "FFSmbiosSystemInfo.SKUNumber offset is wrong");
@@ -1,5 +1,4 @@
#include "physicalmemory.h"
#include "common/endian.h"
#include "common/smbios.h"
// 7.18
@@ -83,8 +82,7 @@ const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* res
}
const char* strings = (const char*) data + data->Header.Length;
uint16_t size = FF_READ_LE(data->Size);
bool installed = size != 0;
bool installed = data->Size != 0;
if (!installed && !options->showEmptySlots) {
continue;
@@ -103,21 +101,19 @@ const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* res
device->installed = installed;
device->ecc = false;
uint16_t totalWidth = FF_READ_LE(data->TotalWidth);
uint16_t dataWidth = FF_READ_LE(data->DataWidth);
if (installed && totalWidth != 0xFFFF && dataWidth != 0xFFFF) {
device->ecc = totalWidth > dataWidth;
if (installed && data->TotalWidth != 0xFFFF && data->DataWidth != 0xFFFF) {
device->ecc = data->TotalWidth > data->DataWidth;
}
if (installed && size != 0xFFFF) {
if (size == 0x7FFF) {
device->size = (FF_READ_LE(data->ExtendedSize) & ~(1ULL << 31)) * 1024ULL * 1024ULL;
} else if (size & (1 << 15)) {
if (installed && data->Size != 0xFFFF) {
if (data->Size == 0x7FFF) {
device->size = (data->ExtendedSize & ~(1ULL << 31)) * 1024ULL * 1024ULL;
} else if (data->Size & (1 << 15)) {
// in kB
device->size = (size & ~(1ULL << 15)) * 1024ULL;
device->size = (data->Size & ~(1ULL << 15)) * 1024ULL;
} else {
// in MB
device->size = size * 1024ULL * 1024ULL;
device->size = data->Size * 1024ULL * 1024ULL;
}
}
@@ -210,9 +206,8 @@ const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* res
if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, Speed)) // 2.3+
{
uint16_t speed = FF_READ_LE(data->Speed);
if (speed) {
device->maxSpeed = speed == 0xFFFF ? FF_READ_LE(data->ExtendedSpeed) : speed;
if (data->Speed) {
device->maxSpeed = data->Speed == 0xFFFF ? data->ExtendedSpeed : data->Speed;
}
ffStrbufSetStatic(&device->vendor, ffSmbiosLocateString(strings, data->Manufacturer));
@@ -228,11 +223,10 @@ const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* res
if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, ConfiguredMemorySpeed)) // 2.7+
{
uint16_t configuredSpeed = FF_READ_LE(data->ConfiguredMemorySpeed);
if (configuredSpeed) {
device->runningSpeed = configuredSpeed == 0xFFFF
? FF_READ_LE(data->ExtendedConfiguredSpeed)
: configuredSpeed;
if (data->ConfiguredMemorySpeed) {
device->runningSpeed = data->ConfiguredMemorySpeed == 0xFFFF
? data->ExtendedConfiguredSpeed
: data->ConfiguredMemorySpeed;
}
}
}
+2 -1
View File
@@ -29,12 +29,13 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
continue;
}
++result->processes;
if (proc_stat_has(stat, PSTAT_NUM_THREADS)) {
result->threads += proc_stat_num_threads(stat);
}
}
result->processes = (uint32_t) list->num_procs;
done:
if (list) {
proc_stat_list_free(list);
@@ -1,5 +1,5 @@
#include "processes.h"
const char* ffDetectProcesses([[maybe_unused]] const FFProcessesOptions* options, [[maybe_unused]] FFProcessesResult* result) {
const char* ffDetectProcesses([[maybe_unused]] FFProcessesResult* result) {
return "Not supported on this platform";
}
+1 -2
View File
@@ -13,8 +13,7 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
}
int count = 0;
// KERN_PROC_ALL returns all user-level processes
// KERN_PROC_KTHREAD returns all processes, including user-level processes (despite the name)
// KERN_PROC_ALL returns all user-level processes, excluding kernel processes and threads
const struct kinfo_proc* procs = kvm_getprocs(kd,
(options->countKprocs ? KERN_PROC_KTHREAD : KERN_PROC_ALL) | KERN_PROC_SHOW_THREADS,
0, sizeof(struct kinfo_proc), &count);
+4 -3
View File
@@ -26,10 +26,11 @@ const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResu
}
for (SYSTEM_PROCESS_INFORMATION* ptr = pstart; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
if (options->countKprocs || (uintptr_t) ptr->InheritedFromUniqueProcessId > 0) {
++result->processes;
result->threads += ptr->NumberOfThreads;
if (!options->countKprocs && ptr->CreateTime.QuadPart == 0) {
continue;
}
++result->processes;
result->threads += ptr->NumberOfThreads;
if (ptr->NextEntryOffset == 0) {
break;
}
@@ -16,8 +16,7 @@
const char* detectTermux(FFTerminalFontResult* terminalFont) {
FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate();
// SharedPreferences XML: <string name="fontsize">14</string>, in px
ffParsePropFile(FF_TERMUX_PREF_PATH, "<string name=\"fontsize\">", &fontSize);
if (fontSize.length > 0) {
if (ffParsePropFile(FF_TERMUX_PREF_PATH, "<string name=\"fontsize\">", &fontSize)) {
ffStrbufAppendS(&fontSize, "px");
}
+2 -18
View File
@@ -53,18 +53,6 @@ static int compareDiskWriteResults(const FFTopProcessResult* a, const FFTopProce
if (a->bytesWritten > b->bytesWritten) return -1;
return (int) (a->pid - b->pid);
}
static int compareStartTimeResults(const FFTopProcessResult* a, const FFTopProcessResult* b) {
if (a->startTime < b->startTime) return 1;
if (a->startTime > b->startTime) return -1;
return (int) (a->pid - b->pid);
}
static int compareThreadsResults(const FFTopProcessResult* a, const FFTopProcessResult* b) {
if (a->threads < b->threads) return 1;
if (a->threads > b->threads) return -1;
return (int) (a->pid - b->pid);
}
// clang-format on
const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
@@ -73,8 +61,8 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
return nullptr;
}
// Memory usage and thread count are instantaneous; when neither CPU time nor disk IO
// counters are requested, a single snapshot suffices and no sampling wait is needed.
// Memory usage is instantaneous; when neither CPU time nor disk IO counters
// are requested, a single snapshot suffices and no sampling wait is needed.
const bool sampleOnce = (options->showTypes & (FF_TOP_TYPE_CPU | FF_TOP_TYPE_DISK)) == 0;
if (sampleOnce) {
@@ -98,7 +86,6 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
item->bytesWritten = 0;
item->cpuPercent = 0;
item->startTime = snap->startTime;
item->threads = snap->threads;
ffStrbufInitMove(&item->name, &snap->name);
}
} else {
@@ -148,7 +135,6 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
item->bytesWritten = (newItem->bytesWritten - oldItem->bytesWritten) * 1000u / (uint64_t) elapsed;
item->cpuPercent = (double) (newItem->cpuTime - oldItem->cpuTime) / elapsed * 100.0;
item->startTime = newItem->startTime;
item->threads = newItem->threads;
ffStrbufInitMove(&item->name, &oldItem->name);
}
@@ -161,8 +147,6 @@ const char* ffDetectTopProcesses(FFTopOptions* options, FFlist* result) {
const void* compare = options->sort == FF_TOP_TYPE_DISK_WRITE ? (void*) compareDiskWriteResults
: options->sort == FF_TOP_TYPE_DISK_READ ? (void*) compareDiskReadResults
: options->sort == FF_TOP_TYPE_MEMORY ? (void*) compareMemoryResults
: options->sort == FF_TOP_TYPE_START_TIME ? (void*) compareStartTimeResults
: options->sort == FF_TOP_TYPE_THREADS ? (void*) compareThreadsResults
: (void*) compareCpuResults;
ffListSort(result, sizeof(FFTopProcessResult), (void*) compare);
if (result->length > options->nProcesses) {
-2
View File
@@ -10,7 +10,6 @@ typedef struct FFTopProcessResult {
uint64_t bytesRead;
uint64_t bytesWritten;
uint64_t startTime;
uint32_t threads;
uint32_t pid;
} FFTopProcessResult;
@@ -21,7 +20,6 @@ typedef struct FFTopProcessSnapshot {
uint64_t bytesRead; // Physical storage read
uint64_t bytesWritten; // Physical storage written
uint64_t startTime; // Differs between platforms
uint32_t threads;
uint32_t pid;
} FFTopProcessSnapshot;
+1 -8
View File
@@ -5,7 +5,7 @@
#include <sys/sysctl.h>
#include <libproc.h>
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
size_t length;
@@ -54,13 +54,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
item->bytesRead = rusage.ri_diskio_bytesread;
item->bytesWritten = rusage.ri_diskio_byteswritten;
item->startTime = rusage.ri_proc_start_abstime;
item->threads = 0;
if (showTypes & FF_TOP_TYPE_THREADS) {
struct proc_taskinfo taskInfo;
if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) == sizeof(taskInfo)) {
item->threads = (uint32_t) taskInfo.pti_threadnum;
}
}
}
return nullptr;
+1 -2
View File
@@ -6,7 +6,7 @@
#include <sys/sysctl.h>
#include <sys/user.h>
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC };
size_t length;
@@ -51,7 +51,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
item->bytesRead = (uint64_t) proc->ki_rusage.ru_inblock * DEV_BSIZE;
item->bytesWritten = (uint64_t) proc->ki_rusage.ru_oublock * DEV_BSIZE;
item->threads = (uint32_t) proc->ki_numthreads;
}
return NULL;
+1 -2
View File
@@ -6,7 +6,7 @@
#include <sys/sysctl.h>
#include <sys/user.h>
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
size_t length;
@@ -52,7 +52,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
item->bytesRead = (uint64_t) (proc->kp_ru.ru_inblock + proc->kp_lwp.kl_ru.ru_inblock) * DEV_BSIZE;
item->bytesWritten = (uint64_t) (proc->kp_ru.ru_oublock + proc->kp_lwp.kl_ru.ru_oublock) * DEV_BSIZE;
item->threads = (uint32_t) proc->kp_nthreads;
}
return nullptr;
+2 -12
View File
@@ -4,7 +4,7 @@
#include <string.h>
#include <ps.h>
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
struct ps_context* context = nullptr;
if (ps_context_create(getproc(), &context) != 0) {
return "ps_context_create(getproc()) failed";
@@ -24,7 +24,7 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
// Flags that cannot be fetched for some process are simply left unset;
// each field must be checked before use.
if (proc_stat_list_set_flags(list, PSTAT_ARGS | PSTAT_STATE | PSTAT_TASK_BASIC | ((showTypes & FF_TOP_TYPE_THREADS) ? PSTAT_NUM_THREADS : 0)) != 0) {
if (proc_stat_list_set_flags(list, PSTAT_ARGS | PSTAT_STATE | PSTAT_TASK_BASIC) != 0) {
error = "proc_stat_list_set_flags() failed";
goto done;
}
@@ -57,16 +57,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
}
item->pid = pid;
item->cpuTime = 0;
item->memBytes = 0;
item->startTime = 0;
item->threads = 0;
if (showTypes & FF_TOP_TYPE_THREADS) {
if (proc_stat_has(stat, PSTAT_NUM_THREADS)) {
item->threads = (uint32_t) proc_stat_num_threads(stat);
}
}
if (proc_stat_has(stat, PSTAT_TASK_BASIC)) {
const task_basic_info_t info = proc_stat_task_basic_info(stat);
+1 -2
View File
@@ -3,7 +3,7 @@
#include <OS.h>
#include <string.h>
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
int32 cookie = 0;
team_info team;
while (get_next_team_info(&cookie, &team) == B_OK) {
@@ -41,7 +41,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
// Storage io counters are not exposed by Haiku; leave them zeroed.
item->bytesRead = 0;
item->bytesWritten = 0;
item->threads = (uint32_t) team.thread_count;
}
return nullptr;
+2 -8
View File
@@ -24,6 +24,8 @@ static bool parseStat(const char* buffer, size_t length, FFTopProcessSnapshot* r
return false;
}
result->cpuTime = 0;
char* p;
for (uint32_t field = 3; field <= 22 /*start time*/; ++field) {
switch (field) {
@@ -41,11 +43,6 @@ static bool parseStat(const char* buffer, size_t length, FFTopProcessSnapshot* r
cursor = p;
break;
case 20: // number of threads
result->threads = (uint32_t) strtoul(cursor, &p, 10);
cursor = p;
break;
case 22: // start time
result->startTime = (uint64_t) strtoull(cursor, &p, 10);
cursor = p;
@@ -131,9 +128,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
auto snapshot = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots);
ffStrbufInit(&snapshot->name);
snapshot->threads = 0;
snapshot->cpuTime = 0;
snapshot->startTime = 0;
if (!parseStat(statBuffer, (size_t) statLength, snapshot)) {
ffStrbufDestroy(&snapshot->name);
+1 -9
View File
@@ -6,7 +6,7 @@
#include <sys/sysctl.h>
#include <errno.h>
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, -1, sizeof(struct kinfo_proc2), INT_MAX };
size_t length;
@@ -50,14 +50,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
item->bytesRead = (uint64_t) proc->p_uru_inblock * DEV_BSIZE;
item->bytesWritten = (uint64_t) proc->p_uru_oublock * DEV_BSIZE;
item->threads = 0;
if (showTypes & FF_TOP_TYPE_THREADS) {
int lwpRequest[] = { CTL_KERN, KERN_LWP, proc->p_pid, sizeof(struct kinfo_lwp), 0 };
size_t lwpLength = 0;
if (sysctl(lwpRequest, ARRAY_SIZE(lwpRequest), nullptr, &lwpLength, nullptr, 0) == 0) {
item->threads = (uint32_t) (lwpLength / sizeof(struct kinfo_lwp));
}
}
}
return NULL;
+1 -18
View File
@@ -5,7 +5,7 @@
#include <sys/sysctl.h>
#include <kvm.h>
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr);
if (!kd) {
return "kvm_open() failed";
@@ -37,23 +37,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
item->startTime = (uint64_t) proc->p_ustart_sec * 1000 + (uint64_t) proc->p_ustart_usec / 1000;
item->bytesRead = (uint64_t) proc->p_uru_inblock * DEV_BSIZE;
item->bytesWritten = (uint64_t) proc->p_uru_oublock * DEV_BSIZE;
item->threads = 0;
}
if (showTypes & FF_TOP_TYPE_THREADS) {
int threadCount = 0;
const struct kinfo_proc* threads = kvm_getprocs(kd,
KERN_PROC_ALL | KERN_PROC_SHOW_THREADS, 0, sizeof(struct kinfo_proc), &threadCount);
if (threads) {
for (uint32_t i = 0; i < snapshots->length; ++i) {
FFTopProcessSnapshot* item = FF_LIST_GET(FFTopProcessSnapshot, *snapshots, i);
for (int j = 0; j < threadCount; ++j) {
if (threads[j].p_pid == (pid_t) item->pid && threads[j].p_tid != -1) {
++item->threads;
}
}
}
}
}
kvm_close(kd);
-1
View File
@@ -60,7 +60,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
item->bytesRead = 0;
item->bytesWritten = 0;
item->threads = (uint32_t) psinfo.pr_nlwp;
if (showTypes & FF_TOP_TYPE_DISK) {
prusage_t usage;
if (ffReadFileDataRelative(subfd, "usage", sizeof(usage), &usage) == (ssize_t) sizeof(usage)) {
+1 -2
View File
@@ -30,7 +30,7 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
for (SYSTEM_PROCESS_INFORMATION* info = pstart;; info = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) info + info->NextEntryOffset)) {
uintptr_t pidValue = (uintptr_t) info->UniqueProcessId;
if (pidValue <= UINT32_MAX && (uintptr_t) info->InheritedFromUniqueProcessId > 0) {
if (pidValue <= UINT32_MAX && info->CreateTime.QuadPart > 0) {
FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots);
item->pid = (uint32_t) pidValue;
item->startTime = (uint64_t) info->CreateTime.QuadPart;
@@ -38,7 +38,6 @@ const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes) {
item->memBytes = (uint64_t) info->VirtualMemoryCounters.WorkingSetSize;
item->bytesRead = (uint64_t) info->IoCounters.ReadTransferCount;
item->bytesWritten = (uint64_t) info->IoCounters.WriteTransferCount;
item->threads = info->NumberOfThreads;
ffStrbufInitNWS(&item->name, info->ImageName.Length / sizeof(wchar_t), info->ImageName.Buffer);
}
if (info->NextEntryOffset == 0) {
+24 -16
View File
@@ -1,5 +1,4 @@
#include "wifi.h"
#include "common/endian.h"
#include "common/io.h"
#include "common/debug.h"
#include "common/strutil.h"
@@ -12,11 +11,12 @@
#include <linux/wireless.h>
#include <unistd.h>
#include <linux/genetlink.h>
#include <linux/nl80211.h>
#if !__BIG_ENDIAN__
#include <linux/genetlink.h>
#include <linux/nl80211.h>
// Silence warning of `NLA_HDRLEN` and `NLA_ALIGN`
#pragma GCC diagnostic ignored "-Wsign-conversion"
// Silence warning of `NLA_HDRLEN` and `NLA_ALIGN`
#pragma GCC diagnostic ignored "-Wsign-conversion"
typedef struct FFWifiNlContext {
int sockFd;
@@ -308,19 +308,19 @@ static uint16_t ffWifiChannelWidthToMhz(uint32_t width) {
return 80;
case NL80211_CHAN_WIDTH_160:
return 160;
case 6 /* NL80211_CHAN_WIDTH_5 */:
case NL80211_CHAN_WIDTH_5:
return 5;
case 7 /* NL80211_CHAN_WIDTH_10 */:
case NL80211_CHAN_WIDTH_10:
return 10;
case 8 /* NL80211_CHAN_WIDTH_1 */:
case NL80211_CHAN_WIDTH_1:
return 1;
case 9 /* NL80211_CHAN_WIDTH_2 */:
case NL80211_CHAN_WIDTH_2:
return 2;
case 10 /* NL80211_CHAN_WIDTH_4 */:
case NL80211_CHAN_WIDTH_4:
return 4;
case 11 /* NL80211_CHAN_WIDTH_8 */:
case NL80211_CHAN_WIDTH_8:
return 8;
case 12 /* NL80211_CHAN_WIDTH_16 */:
case NL80211_CHAN_WIDTH_16:
return 16;
case 13 /* NL80211_CHAN_WIDTH_320 */:
return 320;
@@ -459,7 +459,7 @@ static void ffWifiParseRsnIe(const uint8_t* ie, size_t len, FFWifiSecurityFlags*
if (pos + 2 > len) { // pairwise cipher suite count field length
return;
}
uint16_t pairwiseCount = FF_READ_LE(*(const uint16_t*) (ie + pos));
uint16_t pairwiseCount = *(uint16_t*) (ie + pos);
pos += 2; // skip pairwise cipher suite count field
size_t pairwiseLen = (size_t) pairwiseCount * 4; // each suite selector is 4 bytes
@@ -471,7 +471,7 @@ static void ffWifiParseRsnIe(const uint8_t* ie, size_t len, FFWifiSecurityFlags*
if (pos + 2 > len) { // AKM suite count field length
return;
}
uint16_t akmCount = FF_READ_LE(*(const uint16_t*) (ie + pos));
uint16_t akmCount = *(uint16_t*) (ie + pos);
pos += 2; // skip AKM suite count field
for (uint16_t i = 0; i < akmCount && pos + 4 <= len; ++i, pos += 4) { // each AKM suite selector is 4 bytes
@@ -528,13 +528,13 @@ static void ffWifiParseWpaVendorIe(const uint8_t* ie, size_t len, FFWifiSecurity
if (pos + 2 > len) { // unicast cipher suite count field length
return;
}
uint16_t pairwiseCount = FF_READ_LE(*(const uint16_t*) (ie + pos));
uint16_t pairwiseCount = *(uint16_t*) (ie + pos);
pos += 2 + (size_t) pairwiseCount * 4; // count field(2) + N unicast suite selectors(4 each)
if (pos + 2 > len) { // AKM suite count field length
return;
}
uint16_t akmCount = FF_READ_LE(*(const uint16_t*) (ie + pos));
uint16_t akmCount = *(uint16_t*) (ie + pos);
pos += 2; // skip AKM suite count field
for (uint16_t i = 0; i < akmCount && pos + 4 <= len; ++i, pos += 4) { // each AKM suite selector is 4 bytes
@@ -845,6 +845,8 @@ static const char* detectWithNetlink(FFWifiNlContext* ctx, FFWifiResult* item, u
FF_DEBUG("Netlink wifi detection completed");
return nullptr;
}
#endif
typedef struct FFWifiIcContext {
int sockFd;
} FFWifiIcContext;
@@ -1047,7 +1049,9 @@ const char* ffDetectWifi(FFlist* result) {
return "if_nameindex() failed";
}
#if !__BIG_ENDIAN__
FFWifiNlContext nl = { .sockFd = -1 };
#endif
FFWifiIcContext ic = { .sockFd = -1 };
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
@@ -1086,7 +1090,9 @@ const char* ffDetectWifi(FFlist* result) {
if (operstate == 'u') {
ffStrbufSetStatic(&item->inf.status, "up");
#if !__BIG_ENDIAN__
detectWithNetlink(&nl, item, i->if_index);
#endif
detectWithIoctl(&ic, item, i->if_name);
} else {
ffStrbufSetStatic(&item->conn.status, "disconnected");
@@ -1110,9 +1116,11 @@ const char* ffDetectWifi(FFlist* result) {
}
if_freenameindex(infs);
#if !__BIG_ENDIAN__
if (nl.sockFd >= 0) {
close(nl.sockFd);
}
#endif
if (ic.sockFd >= 0) {
close(ic.sockFd);
}
+1 -1
View File
@@ -910,7 +910,7 @@ int main(int argc, char** argv) {
ffStrbufSetS(&data.structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
}
if (data.genConfigInteractive && !ffGenConfigInteractive(&data)) {
// User canceled the interactive config generation
// User cancelled the interactive config generation
ffStrbufDestroy(&data.structure);
ffStrbufDestroy(&data.structureDisabled);
yyjson_doc_free(data.configDoc);
-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
@@ -44,7 +44,6 @@ FFModuleBaseInfo ffCustomModuleInfo = {
.de = "Benutzerdefiniert",
.es = "Personalizado",
.fr = "Personnalisé",
.gl = "Personalizado",
.he = "מותאם אישית",
.id = "Kustom",
.it = "Personalizzato",
-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"),
}));
}
-4
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"),
}));
}
}
@@ -112,9 +111,6 @@ void ffParseDiskIOJsonObject(FFDiskIOOptions* options, yyjson_val* module) {
if (unsafe_yyjson_equals_str(key, "waitTime")) {
options->waitTime = (uint32_t) yyjson_get_uint(val);
if (options->waitTime == 0) {
options->waitTime = 1;
}
continue;
}
-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"),
}));
}
-4
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"),
}));
}
}
@@ -127,9 +126,6 @@ void ffParseNetIOJsonObject(FFNetIOOptions* options, yyjson_val* module) {
if (unsafe_yyjson_equals_str(key, "waitTime")) {
options->waitTime = (uint32_t) yyjson_get_uint(val);
if (options->waitTime == 0) {
options->waitTime = 1;
}
continue;
}
-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
@@ -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
@@ -83,7 +83,6 @@ FFModuleBaseInfo ffTerminalSizeModuleInfo = {
.de = "Terminalgröße",
.es = "Tamaño del terminal",
.fr = "Taille du terminal",
.gl = "Tamaño do terminal",
.he = "גודל טרמינל",
.id = "Ukuran Terminal",
.it = "Dimensioni terminale",
-2
View File
@@ -8,8 +8,6 @@ typedef enum FFTopTypes: uint8_t {
FF_TOP_TYPE_MEMORY = 1 << 1,
FF_TOP_TYPE_DISK_READ = 1 << 2,
FF_TOP_TYPE_DISK_WRITE = 1 << 3,
FF_TOP_TYPE_START_TIME = 1 << 4,
FF_TOP_TYPE_THREADS = 1 << 5,
FF_TOP_TYPE_DISK = FF_TOP_TYPE_DISK_READ | FF_TOP_TYPE_DISK_WRITE,
} FFTopTypes;
+18 -41
View File
@@ -11,12 +11,7 @@ static void printTopResult(FFTopOptions* options, uint32_t index, uint32_t total
ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Top), total == 1 ? 0 : (uint8_t) (index + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate();
if (process->name.length == 0) {
ffStrbufAppendS(&output, "<unknown>");
} else {
ffStrbufAppend(&output, &process->name);
}
ffStrbufAppendF(&output, " (%u)", process->pid);
ffStrbufAppendF(&output, "%s (%u)", process->name.chars, process->pid);
if ((options->showTypes & FF_TOP_TYPE_CPU) && (percentType & FF_PERCENTAGE_TYPE_NUM_BIT)) {
ffStrbufAppendS(&output, " - CPU ");
ffPercentAppendNum(&output, process->cpuPercent, options->percent, false, &options->moduleArgs);
@@ -33,9 +28,6 @@ static void printTopResult(FFTopOptions* options, uint32_t index, uint32_t total
ffSizeAppendNum(process->bytesWritten, &output);
ffStrbufAppendS(&output, "/s");
}
if (options->showTypes & FF_TOP_TYPE_THREADS) {
ffStrbufAppendF(&output, " - THR %u", process->threads);
}
}
ffStrbufPutTo(&output, stdout);
} else {
@@ -51,19 +43,18 @@ static void printTopResult(FFTopOptions* options, uint32_t index, uint32_t total
FF_STRBUF_AUTO_DESTROY diskWriteFormatted = ffStrbufCreate();
ffSizeAppendNum(process->bytesWritten, &diskWriteFormatted);
ffStrbufAppendS(&diskWriteFormatted, "/s");
FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Top), total == 1 ? 0 : (uint8_t) (index + 1), &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){
FF_ARG(process->name, "name"),
FF_ARG(process->pid, "pid"),
FF_ARG(process->cpuPercent, "cpu"),
FF_ARG(process->memBytes, "mem"),
FF_ARG(process->bytesRead, "disk-read"),
FF_ARG(process->bytesWritten, "disk-write"),
FF_ARG(process->threads, "threads"),
FF_ARG(cpuFormatted, "cpu-percentage"),
FF_ARG(memFormatted, "mem-formatted"),
FF_ARG(diskReadFormatted, "disk-read-formatted"),
FF_ARG(diskWriteFormatted, "disk-write-formatted"),
}));
FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Top), (uint8_t) index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]){
FF_ARG(process->name, "name"),
FF_ARG(process->pid, "pid"),
FF_ARG(process->cpuPercent, "cpu"),
FF_ARG(process->memBytes, "mem"),
FF_ARG(process->bytesRead, "disk-read"),
FF_ARG(process->bytesWritten, "disk-write"),
FF_ARG(cpuFormatted, "cpu-percentage"),
FF_ARG(memFormatted, "mem-formatted"),
FF_ARG(diskReadFormatted, "disk-read-formatted"),
FF_ARG(diskWriteFormatted, "disk-write-formatted"),
}));
}
}
@@ -82,12 +73,11 @@ bool ffPrintTop(FFTopOptions* options) {
} else {
ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Top), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
FF_LIST_FOR_EACH (FFTopProcessResult, process, results) {
ffStrbufWriteTo(&process->name, stdout);
if ((void*) process != results.data) {
putchar(' ');
}
ffStrbufWriteTo(&process->name, stdout);
}
putchar('\n');
}
FF_LIST_FOR_EACH (FFTopProcessResult, item, results) {
@@ -110,7 +100,6 @@ void ffParseTopJsonObject(FFTopOptions* options, yyjson_val* module) {
{ "memory", FF_TOP_TYPE_MEMORY },
{ "disk-read", FF_TOP_TYPE_DISK_READ },
{ "disk-write", FF_TOP_TYPE_DISK_WRITE },
{ "start-time", FF_TOP_TYPE_START_TIME },
{},
});
if (error) {
@@ -130,7 +119,6 @@ void ffParseTopJsonObject(FFTopOptions* options, yyjson_val* module) {
{ "cpu", FF_TOP_TYPE_CPU },
{ "memory", FF_TOP_TYPE_MEMORY },
{ "disk", FF_TOP_TYPE_DISK },
{ "threads", FF_TOP_TYPE_THREADS },
{},
});
if (error == nullptr) {
@@ -148,7 +136,6 @@ void ffParseTopJsonObject(FFTopOptions* options, yyjson_val* module) {
{ "cpu", FF_TOP_TYPE_CPU },
{ "memory", FF_TOP_TYPE_MEMORY },
{ "disk", FF_TOP_TYPE_DISK },
{ "threads", FF_TOP_TYPE_THREADS },
{},
});
if (error == nullptr) {
@@ -186,9 +173,6 @@ void ffGenerateTopJsonConfig(FFTopOptions* options, yyjson_mut_doc* doc, yyjson_
if (options->showTypes & FF_TOP_TYPE_DISK) {
yyjson_mut_arr_add_str(doc, showTypes, "disk");
}
if (options->showTypes & FF_TOP_TYPE_THREADS) {
yyjson_mut_arr_add_str(doc, showTypes, "threads");
}
yyjson_mut_obj_add_val(doc, module, "showTypes", showTypes);
if (options->sort == FF_TOP_TYPE_CPU) {
yyjson_mut_obj_add_str(doc, module, "sort", "cpu");
@@ -198,10 +182,6 @@ void ffGenerateTopJsonConfig(FFTopOptions* options, yyjson_mut_doc* doc, yyjson_
yyjson_mut_obj_add_str(doc, module, "sort", "disk-read");
} else if (options->sort == FF_TOP_TYPE_DISK_WRITE) {
yyjson_mut_obj_add_str(doc, module, "sort", "disk-write");
} else if (options->sort == FF_TOP_TYPE_START_TIME) {
yyjson_mut_obj_add_str(doc, module, "sort", "start-time");
} else if (options->sort == FF_TOP_TYPE_THREADS) {
yyjson_mut_obj_add_str(doc, module, "sort", "threads");
}
yyjson_mut_obj_add_uint(doc, module, "processes", options->nProcesses);
yyjson_mut_obj_add_uint(doc, module, "waitTime", options->waitTime);
@@ -231,9 +211,6 @@ bool ffGenerateTopJsonResult(FFTopOptions* options, yyjson_mut_doc* doc, yyjson_
yyjson_mut_obj_add_uint(doc, item, "bytesRead", process->bytesRead);
yyjson_mut_obj_add_uint(doc, item, "bytesWritten", process->bytesWritten);
}
if (options->showTypes & FF_TOP_TYPE_THREADS) {
yyjson_mut_obj_add_uint(doc, item, "threads", process->threads);
}
yyjson_mut_obj_add_uint(doc, item, "startTime", process->startTime);
}
@@ -247,10 +224,10 @@ void ffInitTopOptions(FFTopOptions* options) {
ffOptionInitModuleArg(&options->moduleArgs, "󰍛");
options->sort = FF_TOP_TYPE_CPU;
options->showTypes = FF_TOP_TYPE_CPU | FF_TOP_TYPE_MEMORY
#if !__GNU__ && !__HAIKU__
#if !__GNU__ && !__HAIKU__
| FF_TOP_TYPE_DISK
#endif
;
#endif
;
options->nProcesses = 5;
options->waitTime = 500;
options->compact = false;
@@ -294,12 +271,12 @@ FFModuleBaseInfo ffTopModuleInfo = {
.generateJsonConfig = (void*) ffGenerateTopJsonConfig,
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]){
{ "Process name", "name" },
{ "Executable path", "path" },
{ "Process ID", "pid" },
{ "CPU usage", "cpu" },
{ "Memory usage (RSS) in bytes", "mem" },
{ "Disk read bytes per second (0 if unsupported)", "disk-read" },
{ "Disk write bytes per second (0 if unsupported)", "disk-write" },
{ "Number of threads", "threads" },
{ "CPU usage percentage", "cpu-percentage" },
{ "Memory usage (RSS) formatted", "mem-formatted" },
{ "Disk read formatted", "disk-read-formatted" },
-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"),
}));
}