Handling Display Options in FastFetch: Architecture and Implementation

FastFetch centralizes all output customization in a global FFOptionsDisplay structure that is initialized with defaults, populated from command-line arguments or JSON configuration, and consumed as read-only state by every module to ensure consistent terminal formatting.

FastFetch implements a centralized architecture for handling display options in fastfetch, storing all formatting preferences in a single configuration object that serves as the single source of truth for colors, separators, and numeric formatting. The design separates parsing logic from rendering logic, allowing modules to query display settings without duplicating validation code.

Core Data Structure

The foundation of FastFetch's display handling is the FFOptionsDisplay struct defined in src/options/display.h. This structure aggregates every user-facing output customization into a single, flat configuration object.

typedef struct FFOptionsDisplay {
    /* colour handling */
    FFstrbuf colorKeys;          // “--color‑keys”
    FFstrbuf colorTitle;         // “--color‑title”
    FFstrbuf colorOutput;        // “--color‑output”
    FFstrbuf colorSeparator;     // “--color‑separator”
    bool brightColor;            // auto‑bright for dark terminals

    /* key/value layout */
    FFstrbuf keyValueSeparator; // “--separator”
    FFModuleKeyType keyType;     // “--key‑type”
    uint16_t keyWidth;           // “--key‑width”
    uint16_t keyPaddingLeft;     // “--key‑padding‑left”

    /* numeric formatting */
    int32_t stat;                // “--stat”
    bool pipe;                   // “--pipe”
    bool hideCursor;             // “--hide‑cursor”
    bool disableLinewrap;        // “--disable‑linewrap”
    bool durationAbbreviation;   // “--duration‑abbreviation”
    FFSpaceBeforeUnitType durationSpaceBeforeUnit;
    FFSizeBinaryPrefixType sizeBinaryPrefix;
    uint8_t sizeNdigits;
    uint8_t sizeMaxPrefix;
    FFSpaceBeforeUnitType sizeSpaceBeforeUnit;
    FFTemperatureUnit tempUnit;
    uint8_t tempNdigits;
    FFSpaceBeforeUnitType tempSpaceBeforeUnit;
    uint8_t barWidth;
    FFPercentageTypeFlags percentType;
    uint8_t percentNdigits;
    FFSpaceBeforeUnitType percentSpaceBeforeUnit;
    uint8_t percentWidth;
    int8_t freqNdigits;
    FFSpaceBeforeUnitType freqSpaceBeforeUnit;
    int8_t fractionNdigits;
    FFFractionTrailingZerosType fractionTrailingZeros;
    bool noBuffer;
    FFlist constants;            // user defined constants
} FFOptionsDisplay;

This struct lives inside the global configuration object FFconfig (declared in src/fastfetch.h), ensuring all display parameters are accessible throughout the codebase.

Global Instance Management

FastFetch uses a single global instance to host the display configuration. The FFinstance struct defined in src/fastfetch.h contains both configuration and runtime state:

typedef struct FFinstance {
    FFconfig config;
    FFstate state;
} FFinstance;

extern FFinstance instance;   // defined in `src/common/init.c`

All modules read instance.config.display directly, which guarantees a single source of truth for output style. This global pattern eliminates the need to pass configuration pointers through every function call while maintaining thread-safe read-only access during execution.

Configuration Initialization

During startup, FastFetch calls ffOptionsInitDisplay(&instance.config.display) to establish sane defaults. This function, implemented in src/options/display.c, allocates internal buffers and sets default values such as sizeBinaryPrefix = FF_SIZE_BINARY_PREFIX_TYPE_IEC, percentType = 9, and default color strings.

The initialization sequence ensures that every field contains a valid value before any user input is processed, preventing undefined behavior when modules query display settings.

Parsing User Input

FastFetch supports two input vectors for display options: command-line arguments and JSON configuration files. Both populate the same FFOptionsDisplay struct using dedicated parser functions.

Command-Line Parsing

The ffOptionsParseDisplayCommandLine function receives each --<option> flag and updates the matching field in the display struct. It is invoked from the generic option parser in src/fastfetch.c:

if (ffOptionsParseDisplayCommandLine(&instance.config.display, key, value)) …

This parser uses helper functions like ffOptionParseColor, ffOptionParseEnum, and ffOptionParseBoolean to translate raw strings into internal representations. For example, when processing --color-keys, the parser validates the color string and stores it in options->colorKeys.

JSON Configuration Parsing

When a config file is supplied, FastFetch reads it with yyjson and calls:

ffOptionsParseDisplayJsonConfig(&instance.config.display, root);

The JSON parser walks each property inside the top-level "display" object, validates types, and copies values directly into the struct. This allows complex nested configurations like custom color schemes and unit preferences to be persisted across sessions.

Configuration Generation

When --gen-config is used, FastFetch serializes the current configuration back to JSON via ffOptionsGenerateDisplayJsonConfig(data, &instance.config.display). This ensures the generated file mirrors in-memory defaults and any runtime overrides, creating a perfect round-trip between CLI flags and configuration files.

Runtime Consumption Patterns

All modules that output data query the global instance.config.display in a read-only pattern. This design ensures consistent formatting across every system information module.

Color Handling

Modules wrap output with ANSI sequences only when !instance.config.display.pipe. For example, in src/modules/title/title.c:

if (!instance.config.display.pipe) {
    ffPrintColor(&instance.config.display.colorTitle);
}

The pipe flag acts as a master switch for all terminal control codes, ensuring that piped output never contains escape sequences that could corrupt downstream processing.

Percentage and Size Formatting

Modules respect global formatting defaults while allowing local overrides. In src/modules/cpuusage/cpuusage.c, the code falls back to global settings when module-specific options are unset:

FFPercentageTypeFlags pct = options->percent.type == 0
                            ? instance.config.display.percentType
                            : options->percent.type;

Helper functions in src/common/impl/size.c and src/common/impl/printing.c read display fields like sizeNdigits, freqSpaceBeforeUnit, and keyWidth to determine number formatting, spacing, and alignment.

Key-Value Rendering

The generic printer in src/common/impl/printing.c consults keyWidth, keyPaddingLeft, and keyValueSeparator to construct formatted output lines. This centralizes the logic for aligning keys and values across all modules, ensuring that --key-width 15 affects every module uniformly.

Configuration Lifecycle

The display options follow a strict lifecycle that maintains data integrity from startup to shutdown:

  1. Startup: ffOptionsInitDisplay allocates buffers and sets defaults
  2. CLI Parsing: ffOptionsParseDisplayCommandLine overwrites defaults per --<option> flag
  3. Config File: ffOptionsParseDisplayJsonConfig merges JSON values (overwriting CLI if processed later)
  4. Runtime: Modules and printers read instance.config.display (read-only access)
  5. Shutdown: ffOptionsDestroyDisplay frees allocated buffers and lists

This lifecycle ensures that display configuration is immutable during the actual information gathering and printing phases, preventing race conditions or inconsistent formatting.

Summary

  • Centralized Structure: All display options reside in FFOptionsDisplay (defined in src/options/display.h), accessed globally via instance.config.display.
  • Dual Input Support: The architecture handles both command-line arguments via ffOptionsParseDisplayCommandLine and JSON configuration via ffOptionsParseDisplayJsonConfig.
  • Read-Only Consumption: Modules query display settings at runtime but never modify them, ensuring consistent output formatting.
  • Pipe Safety: The pipe boolean acts as a global gate for ANSI escape sequences, automatically disabling colors and cursor manipulation when output is redirected.
  • Round-Trip Configuration: ffOptionsGenerateDisplayJsonConfig enables perfect serialization of runtime state back to JSON for configuration management.

Frequently Asked Questions

Where are the display option defaults defined in FastFetch?

The default values for all display options are established in ffOptionsInitDisplay within src/options/display.c. This function sets initial values such as IEC binary prefixes for sizes, specific percentage formatting flags, and default color buffers before any user configuration is loaded.

How does FastFetch handle the --pipe flag internally?

The pipe field in FFOptionsDisplay serves as a master switch for ANSI escape sequences. Every module checks instance.config.display.pipe before emitting color codes or cursor control sequences. When pipe mode is enabled, FastFetch outputs plain text suitable for redirection to files or other programs without terminal control characters.

Can display options be specified in both CLI arguments and JSON config simultaneously?

Yes. FastFetch processes display options from both sources, with later inputs overwriting earlier ones. Typically, CLI arguments are processed first, then JSON configuration is loaded. If the same option appears in both, the JSON value takes precedence unless the CLI flag was processed after the config file load. The ffOptionsGenerateDisplayJsonConfig function can export the final merged state to create a new config file reflecting all active settings.

Which modules consume the display configuration options?

All output modules consume the global display configuration. Key consumers include the title module (src/modules/title/title.c) for colors, the CPU usage module (src/modules/cpuusage/cpuusage.c) for percentage formatting, and the generic printing helpers in src/common/impl/printing.c and src/common/impl/size.c that handle key alignment, unit prefixes, and numeric precision across the entire codebase.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →