mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 18:06:11 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b27503a385 |
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,5 +13,3 @@ fastfetch.kdev4
|
||||
*.user.*
|
||||
*.swp
|
||||
*.log
|
||||
/*.zh.md
|
||||
/.*-ai/
|
||||
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
# 2.68.0
|
||||
# Unreleased
|
||||
|
||||
Changes:
|
||||
* The DE / WM / LM modules now reports the full name "Desktop Environment" / "Window Manager" / "Login Manager" instead of the abbreviations.
|
||||
@@ -21,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)
|
||||
@@ -3315,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
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.21.0) # C_STANDARD
|
||||
|
||||
project(fastfetch
|
||||
VERSION 2.68.0
|
||||
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
@@ -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 1–9; at most 9 (`FASTFETCH_LOGO_MAX_COLORS = 9`)
|
||||
- `$$` is a literal `$`
|
||||
- Characters without a placeholder inherit the current color. Of the 530 existing logos, 239 use no placeholders at all (monochrome) and 291 do — **new logos should use placeholders**; monochrome is a legacy style
|
||||
- Tabs are expanded to 4 spaces
|
||||
- Colors can be overridden with `--logo-color-1` … `--logo-color-9`
|
||||
|
||||
### 2. Register it in the `.inc`
|
||||
|
||||
CMake turns each `.txt` into a `FASTFETCH_DATATEXT_LOGO_<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`)
|
||||
+9
-13
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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
|
||||
@@ -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/");
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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,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";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -44,7 +44,6 @@ FFModuleBaseInfo ffCustomModuleInfo = {
|
||||
.de = "Benutzerdefiniert",
|
||||
.es = "Personalizado",
|
||||
.fr = "Personnalisé",
|
||||
.gl = "Personalizado",
|
||||
.he = "מותאם אישית",
|
||||
.id = "Kustom",
|
||||
.it = "Personalizzato",
|
||||
|
||||
@@ -111,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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" },
|
||||
|
||||
Reference in New Issue
Block a user