How the Fastfetch Gamepad Detection Module Works: Cross-Platform Controller Discovery

Fastfetch's gamepad detection module uses platform-specific native APIs to enumerate connected controllers, extracting serial numbers, human-readable names, and battery levels through a two-layer architecture consisting of low-level detection drivers and a high-level presentation formatter.

The gamepad detection module in fastfetch-cli/fastfetch provides cross-platform controller discovery for Linux, Windows, macOS, and BSD systems. By abstracting OS-specific device enumeration APIs behind a unified FFGamepadDevice structure, the module delivers consistent gamepad information including battery status and hardware identifiers. This article examines the implementation details of the detection layer in src/detection/gamepad/ and the presentation logic in src/modules/gamepad/gamepad.c.

Architecture of the Gamepad Detection Module

The module follows a layered design that separates hardware detection from output formatting.

The detection layer consists of platform-specific implementations of ffDetectGamepad that populate an FFlist with FFGamepadDevice structures. Each device record contains three core fields: serial (hardware identifier string), name (human-readable label), and battery (percentage 0-100 or 0 if unknown).

The presentation layer in src/modules/gamepad/gamepad.c filters detected devices according to user preferences and formats the final output string, handling battery percentage display types and indexing for multiple controllers.

Platform-Specific Detection Implementations

Linux: Sysfs Input Interface

On Linux, the detection logic resides in src/detection/gamepad/gamepad_linux.c. The implementation scans /sys/class/input/ for joystick devices matching the jsX pattern.

For each discovered device, the code constructs paths to read the controller name from device/name and attempts to locate battery information via device/power_supply/*/capacity or _level files. The uniq attribute provides the serial number.

const char* ffDetectGamepad(FFlist* devices) {
    DIR* dirp = opendir("/sys/class/input/");               // [gamepad_linux.c#L66-L70]

    while ((entry = readdir(dirp)) != NULL) {               // [gamepad_linux.c#L75-L81]
        if (!ffStrStartsWith(entry->d_name, "js")) continue;

        ffStrbufAppendS(&path, entry->d_name);
        ffStrbufAppendS(&path, "/device/name");             // [gamepad_linux.c#L83-L85]

        detectGamepad(devices, &name, &path);               // [gamepad_linux.c#L90-L92]
    }
    return NULL;
}

The helper function detectGamepad (defined at lines 5-38) handles the actual population of the FFGamepadDevice structure, parsing sysfs attributes and power supply subdirectories to complete the device profile.

Windows: Raw Input and HID APIs

The Windows implementation in src/detection/gamepad/gamepad_windows.c utilizes the Raw Input API to enumerate Human Interface Devices (HID). It filters for devices with usage page 1 and usage values 4 (joystick) or 5 (gamepad).

After opening each candidate device with CreateFileW, the code queries HID strings for manufacturer, product name, and serial number. A vendor mapping function detectKnownDeviceName translates Nintendo, Sony, and Logitech product IDs into friendly display names. Battery levels are extracted from HID input reports for supported controllers like DualShock and DualSense.

const char* ffDetectGamepad(FFlist* devices) {
    UINT nDevices = 0;
    GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)); // [gamepad_windows.c#L80-L84]

    for (UINT i = 0; i < nDevices; ++i) {
        if (pRawInputDeviceList[i].dwType != RIM_TYPEHID) continue;

        if (rdi.hid.usUsagePage != 1 ||
           (rdi.hid.usUsage != 4 && rdi.hid.usUsage != 5)) continue; // [gamepad_windows.c#L106-L108]

        FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd(devices);

    }
    return NULL;
}

macOS: IOKit HID Manager

For macOS systems, src/detection/gamepad/gamepad_apple.c leverages the IOKit framework's IOHIDManager API. The implementation creates an HID manager instance, configures it to match devices with "Generic Desktop" usage page and "Joystick" or "GamePad" usage types, then copies the matching device set.

The enumeration callback extracts manufacturer, product, and serial properties using IOHIDDeviceGetProperty, storing the CFString values in the standard FFGamepadDevice structure.

const char* ffDetectGamepad(FFlist* devices) {
    IOHIDManagerRef manager = IOHIDManagerCreate(...);

    CFSetRef set = IOHIDManagerCopyDevices(manager);
    if (set) CFSetApplyFunction(set, (CFSetApplierFunction) &enumSet, devices); // [gamepad_apple.c#L41-L44]
    return NULL;
}

BSD and Other Platforms

BSD systems use a sysfs-style approach similar to Linux in src/detection/gamepad/gamepad_bsd.c, scanning /dev/input for controller devices. Haiku platforms implement Haiku-specific HID APIs in gamepad_haiku.cpp. Unsupported platforms compile gamepad_nosupport.c, which returns a "Not supported on this platform" error string while maintaining API compatibility.

The Presentation Layer

The module frontend in src/modules/gamepad/gamepad.c orchestrates the detection process and output generation. The ffPrintGamepad function initializes a device list, invokes ffDetectGamepad, applies user-configured ignore patterns, and iterates through results.

bool ffPrintGamepad(FFgamepadOptions* options) {
    FF_LIST_AUTO_DESTROY result = ffListCreate(sizeof(FFGamepadDevice));
    const char* error = ffDetectGamepad(&result);                     // [gamepad.c#L54-L55]

    FF_LIST_FOR_EACH (FFGamepadDevice*, pdevice, filtered) {
        printDevice(options, *pdevice, filtered.length > 1 ? ++index : 0); // [gamepad.c#L86-L90]
    }
}

The printDevice helper handles formatting logic, including battery bar visualization and percentage display based on the --percent-type configuration.

Data Structures and Public API

The detection interface is defined in src/detection/gamepad/gamepad.h, which declares the FFGamepadDevice structure and the ffDetectGamepad function prototype.

Key structure fields:

  • serial: FFstrbuf containing the hardware serial number
  • name: FFstrbuf with the device display name
  • battery: uint8_t representing battery percentage (0-100)

Practical Usage Examples

Command-Line Interface

Display all connected gamepads with battery information:

fastfetch --module gamepad

Configure battery display format:


# Show only numeric percentage

fastfetch --module gamepad --percent-type num

# Hide specific controllers using prefix matching

fastfetch --module gamepad --ignore "Xbox"

Programmatic Integration

Fastfetch exposes its detection API for library usage:

#include "fastfetch.h"
#include "detection/gamepad/gamepad.h"

int main(void) {
    FFlist devices;
    ffListInit(&devices, sizeof(FFGamepadDevice));

    const char *err = ffDetectGamepad(&devices);
    if (err) {
        fprintf(stderr, "Gamepad detection failed: %s\n", err);
        return 1;
    }

    FF_LIST_FOR_EACH (FFGamepadDevice, dev, devices) {
        printf("Name: %s\n", dev.name.chars);
        printf("Serial: %s\n", dev.serial.chars);
        printf("Battery: %u%%\n", dev.battery);
    }

    ffListDestroy(&devices);
    return 0;
}

Summary

  • The fastfetch gamepad detection module implements platform-specific discovery in src/detection/gamepad/ using native APIs: sysfs on Linux, Raw Input on Windows, and IOKit on macOS.
  • Each detection source populates a standardized FFGamepadDevice structure containing serial, name, and battery fields.
  • The presentation layer in src/modules/gamepad/gamepad.c filters results and formats output according to user preferences.
  • The module supports battery level reporting where hardware allows, retrieving data from power supply sysfs entries on Linux and HID input reports on Windows.
  • Programmatic access is available through the public ffDetectGamepad API defined in gamepad.h.

Frequently Asked Questions

Which gamepads are supported by Fastfetch's detection module?

Fastfetch detects any controller exposing standard HID interfaces or sysfs entries. On Windows, known vendors like Sony (DualShock/DualSense), Nintendo, and Logitech receive friendly name mappings. Linux supports any controller creating /sys/class/input/jsX nodes. Battery reporting works only on controllers exposing battery status through standard OS APIs.

How does Fastfetch read gamepad battery levels on Linux?

The Linux implementation in src/detection/gamepad/gamepad_linux.c searches for power supply subdirectories within the device's sysfs entry, reading capacity or _level files to determine battery percentage. If no power supply information exists, the battery field returns 0.

Can I use the gamepad detection code in my own project?

Yes. The detection layer is designed as a reusable library. Include src/detection/gamepad/gamepad.h, link against the Fastfetch detection sources, and call ffDetectGamepad with an initialized FFlist. The function returns NULL on success or an error string on failure, populating the list with FFGamepadDevice structures.

Why doesn't my controller show up in Fastfetch?

Ensure your controller uses standard HID protocols and is recognized by the operating system. On Linux, verify /sys/class/input/js0 or similar nodes exist. On Windows, check that the device appears in Device Manager as a game controller. The --ignore flag may also filter out devices if a pattern matches your controller name.

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 →