Merge pull request #2490 from fastfetch-cli/dev

Release v2.67.0
This commit is contained in:
Carter Li
2026-08-04 20:05:50 -05:00
committed by GitHub
131 changed files with 2322 additions and 791 deletions
+32 -7
View File
@@ -6,16 +6,31 @@ body:
- type: markdown
attributes:
value: |
Tip: You can display a logo in fastfetch without adding it to fastfetch's official repo.
> [!WARNING]
> **NB:** If your logo request does not meet the basic requirements outlined in this template, the issue may be closed at any time without any further explanation.
**Tip:** You can display a logo in fastfetch without adding it to fastfetch's official repo.
For highly customized, personal logos, we recommend keeping them locally.
Please refer to https://github.com/fastfetch-cli/fastfetch/wiki/Migrate-Neofetch-Logo-To-Fastfetch
You can test your logo locally using:
`fastfetch -l /path/to/logo.txt --logo-color-1 white --logo-color-2 red`
Please refer to the [Migration Guide](https://github.com/fastfetch-cli/fastfetch/wiki/Migrate-Neofetch-Logo-To-Fastfetch) for details on color placeholders (`$1`, `$2`, etc.).
- type: textarea
attributes:
label: OS
description: Paste the contents of `/etc/os-release` and `/etc/lsb-release` here. If neither file exists, describe how to identify the distro.
description: Paste the contents of `/etc/os-release` (specifically the `ID` and `PRETTY_NAME` fields). If neither file exists, describe how to identify the distro.
placeholder: cat /etc/os-release
validations:
required: true
- type: input
attributes:
label: Public repository URL
description: A public repository is required to verify the request. Please provide a link to the repository where the distro is developed.
placeholder: https://github.com/example/distro
validations:
required: true
- type: input
attributes:
label: Distro Website
@@ -23,25 +38,35 @@ body:
placeholder: https://example.com
validations:
required: true
- type: textarea
attributes:
label: ASCII Art
description: The ASCII logo should not take up too much space (smaller than 50x20 characters, W x H). Please also include the color codes if they are not available in `os-release`.
placeholder: Paste ASCII art here
description: |
The ASCII logo should not take up too much space (smaller than 50x20 characters, W x H).
**Important:** Fastfetch uses `$1`, `$2`, `$3` etc. for color placeholders. Do NOT use hardcoded ANSI escape codes (like `\033[38;2...m`) or Neofetch's `${c1}` syntax.
Take <https://github.com/fastfetch-cli/fastfetch/tree/dev/src/logo/ascii> as examples.
placeholder: Paste ASCII art with $1, $2 placeholders here
validations:
required: true
- type: input
attributes:
label: Original Image URL
description: If the ASCII art is based on an image, please provide a link to the original image file.
placeholder: Image URL from the distro website mentioned above
- type: checkboxes
attributes:
label: Checklist
options:
- label: My distro has reached a stable state (out of beta, alpha, RC).
required: true
- label: The ASCII art is smaller than 50x20 characters (W x H).
required: true
- label: The ASCII art includes color codes, or the color codes are available in `os-release`.
- label: The ASCII art uses `$N` color placeholders (or colors are natively available in `os-release`).
required: true
- label: The ASCII art has no internal padding (spaces at the start and/or end of lines).
- label: The ASCII art has no unnecessary trailing spaces at the end of lines.
required: true
- label: I promise that I will send a removal request if the distro is discontinued.
required: true
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
- name: Update and install dependencies
run: |
sudo pkg update
sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa lua54 libva libvdpau
sudo pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3 egl opencl ocl-icd v4l_compat chafa lua54 libva libvdpau
- name: CMake configuration
run: cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_BUILD_TYPE=${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }} .
+57
View File
@@ -0,0 +1,57 @@
name: Reusable Linux loong64
on:
workflow_call:
env:
CMAKE_BUILD_TYPE: ${{ vars.CMAKE_BUILD_TYPE || 'RelWithDebInfo' }}
jobs:
build:
runs-on: ubuntu-24.04
steps:
- name: checkout repository
uses: actions/checkout@v7
- name: set up QEMU for loong64
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- name: build in loong64 container
run: |
docker run --rm --platform linux/loong64 \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
lcr.loongnix.cn/debian:14 \
bash -c '
set -e
uname -a
cat /etc/os-release || true
apt-get update
apt-get install -y --no-install-recommends \
cmake make gcc g++ \
libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev \
libdconf-dev libdbus-1-dev libmagickcore-dev \
libsqlite3-dev librpm-dev \
libegl-dev libglx-dev ocl-icd-opencl-dev \
libpulse-dev libdrm-dev \
libelf-dev libefl-all-dev \
liblua5.4-dev \
libvdpau-dev libva-dev \
rpm
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
cmake --build . --target package --verbose -j$(nproc)
./fastfetch --list-features
time ./fastfetch -c presets/ci.jsonc --stat false
time ./fastfetch -c presets/ci.jsonc --format json
time ./flashfetch
ldd fastfetch
ctest --output-on-failure
'
- name: upload artifacts
uses: actions/upload-artifact@v7
with:
name: fastfetch-linux-loong64
path: ./fastfetch-*.*
+12 -1
View File
@@ -54,6 +54,15 @@ jobs:
uses: ./.github/workflows/build-linux-armv7l.yml
secrets: inherit
linux-loong64:
needs: no-features-test
name: Linux-loong64
permissions:
security-events: write
contents: read
uses: ./.github/workflows/build-linux-loong64.yml
secrets: inherit
linux-vms:
needs: no-features-test
name: Linux-${{ matrix.arch }}
@@ -145,6 +154,7 @@ jobs:
secrets: inherit
haiku-amd64:
if: false # Disabled because the Haiku build is currently broken
needs: no-features-test
name: Haiku-amd64
permissions:
@@ -188,6 +198,7 @@ jobs:
- linux-hosts
- linux-i686
- linux-armv7l
- linux-loong64
- linux-vms
- musl-amd64
- macos-hosts
@@ -197,7 +208,7 @@ jobs:
- dragonfly-amd64
- solaris-amd64
- omnios-amd64
- haiku-amd64
# - haiku-amd64
- windows-hosts
permissions:
contents: write
+43
View File
@@ -1,3 +1,46 @@
# 2.67.0
Changes:
* The minimum supported compiler version has been raised to GCC 13 (released on 2023-04-26) and Clang 16 (released on 2023-03-17) to accommodate the new C23 syntax used throughout the codebase. (General)
* As a result, building from source in Debian 12 (which only ships GCC 12 by default) is no longer supported. Users must upgrade their compiler or use the prebuilt binaries from the GitHub releases page.
* The `general.preRun` option has been removed due to security concerns. (General)
* Users who relied on this option to run a command before printing output will need to move that logic to an external wrapper.
* The `{status}` placeholder in Battery custom format is now an array type instead of a single comma-separated string. (Battery)
* This is a breaking change for users who print `{status}` in Lua scripts. They can use `table.concat(...)` to join the array into a single string if needed.
* The `--gen-config-*` flags (`--gen-config-full`, `--gen-config-force`, `--gen-config-full-force`) have been removed and their functions have been merged into `--gen-config`. (Global)
* See features below.
Features:
* `--gen-config` now opens an interactive configuration TUI to make generating config files easier.
* It falls back to non-interactive generation when `$NO_COLOR` is set or when stdin/stdout is not a TTY. This behavior can also be used to disable the interactive mode.
* Added an experimental [WebUI](https://fastfetch-cli.github.io/fastfetch-config/) ([separate project](https://github.com/fastfetch-cli/fastfetch-config)) for generating and editing configs.
* Added `-w`/`--watch` as a seconds-based alias for `--dynamic-interval`, defaulting to 1 second when no value is provided. (#2478, Watch)
* Added InitSystem detection on Windows, reporting the first userland process (`smss.exe`). (InitSystem, Windows)
* Added CPU and GPU temperature detection support for Apple M5 series chips. (CPU / GPU, macOS)
* Reported `Basic` as the Windows theme when DWM is disabled. (Theme, Windows)
* Improved Ghostty terminal font detection performance. (#2122, TerminalFont)
* It now parses Ghostty directly instead of running `ghostty +show-config`.
* Added support for a wider range of TOML config syntaxes used by Alacritty. (#2456, TerminalFont)
Bugfixes:
* Fixed a potential fish version detection error. (Shell, Linux)
* Fixed incorrect resolution reporting when a monitor advertises multiple preferred modes. (#2481, Display, Linux)
* Fixed connected monitors being missed when Wayland output events arrive late. (#2451, Display, Linux)
* Fixed VP9 codec detection on macOS. (Codec, macOS)
* Corrected Base64 encoding on big-endian hosts. (#2470)
* Fixed invalid URL parsing in the PublicIP module. (PublicIP)
* Correctly reported virtual GPUs on Windows. (#2461, GPU, Windows)
* Added a size limit for network responses to prevent excessive memory usage and mitigate potential attacks. (PublicIP / Weather)
* Fixed Ubuntu Studio Core detection (OS, Linux)
* Relaxed the HTTP response check so that both `HTTP/1.0` and `HTTP/1.1` responses are accepted when fetching data over the network. (PublicIP / Weather)
* Various internal cleanups and optimizations:
* Fixed multiple memory leaks (Separator, Camera, Codec, Display)
* Added integer overflow checks to the string buffer implementation
* Various code cleanups and compiler warning fixes
Logos:
* Removed Hypros, MagpieOS, Furreto, EmperorOS and Magix
# 2.66.0
Changes:
+26 -2
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
project(fastfetch
VERSION 2.66.0
VERSION 2.67.0
LANGUAGES C
DESCRIPTION "Fast neofetch-like system information tool"
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
@@ -182,6 +182,29 @@ endif()
set(WARNING_FLAGS "-Wall -Wextra -Wconversion -Werror=uninitialized -Werror=return-type -Werror=vla")
if(LINUX)
if(NOT IS_MUSL) # `NOT DEFINED IS_MUSL` doesn't work
execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.so
OUTPUT_VARIABLE LIBC_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(LIBC_PATH MATCHES "-musl")
set(IS_MUSL ON CACHE BOOL "Build with musl libc" FORCE)
endif()
endif()
if(IS_MUSL)
message(STATUS "MUSL libc detected")
# Silence ioctl related warnings
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-sign-conversion")
endif()
endif()
# Minimal compiler version supported: GCC 13 (2023-04-26), Clang 16 (2023-03-17)
# Most C23 features are allowed with noticeable exceptions of `constexpr` and `#embed`
# See https://cppreference.com/c/compiler_support/23 for detail
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=incompatible-pointer-types -Werror=implicit-function-declaration -Werror=int-conversion")
@@ -411,6 +434,7 @@ set(LIBFASTFETCH_SRC
src/common/impl/font.c
src/common/impl/format.c
src/common/impl/frequency.c
src/common/impl/genconfig.c
src/common/impl/init.c
src/common/impl/jsonconfig.c
src/common/impl/library.c
@@ -1100,7 +1124,7 @@ elseif(WIN32)
src/detection/gpu/gpu_windows.cpp
src/detection/host/host_windows.c
src/detection/icons/icons_windows.c
src/detection/initsystem/initsystem_nosupport.c
src/detection/initsystem/initsystem_windows.c
src/detection/keyboard/keyboard_windows.c
src/detection/libc/libc_windows.cpp
src/detection/lm/lm_nosupport.c
+2 -1
View File
@@ -199,7 +199,8 @@ See [#1096](https://github.com/fastfetch-cli/fastfetch/issues/1096).
### Q: Fastfetch shows fewer dpkg packages than neofetch. Is it a bug?
Neofetch incorrectly counts `rc` packages (packages that have been removed but still have configuration files remaining). See bug: https://github.com/dylanaraps/neofetch/issues/2278
1. Neofetch incorrectly counts `rc` packages for apt (packages that have been removed but still have configuration files remaining). See bug: https://github.com/dylanaraps/neofetch/issues/2278
2. Neofetch incorrectly counts `gpg-pubkey` as packages for rpm. You may check the results of `dnf list --installed | wc -l` and `rpm -qa | wc -l` to see the difference.
### Q: I use Debian / Ubuntu / Debian-derived distro. My GPU is detected as `XXXX Device XXXX (VGA compatible)`. Is this a bug?
+12 -20
View File
@@ -83,6 +83,16 @@
"type": "num",
"default": 0
}
},
{
"long": "watch",
"short": "w",
"desc": "Alias of dynamic-interval, but the value is in seconds instead of milliseconds. If no value is provided, it defaults to 1 second",
"arg": {
"type": "num",
"optional": true,
"default": 1
}
}
],
"Config": [
@@ -97,26 +107,8 @@
},
{
"long": "gen-config",
"desc": "Generate a minimal config file at the specified path",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Prints the generated config if <path> is \"-\"",
"arg": {
"type": "path",
"optional": true
}
},
{
"long": "gen-config-full",
"desc": "Generate a full config file with all optional settings at the specified path",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\". Prints the generated config if <path> is \"-\"",
"arg": {
"type": "path",
"optional": true
}
},
{
"long": "gen-config-force",
"desc": "Generate a config file at the specified path, overwriting any existing file",
"remark": "Defaults to \"~/.config/fastfetch/config.jsonc\"",
"desc": "Interactively generate a config file at the specified path",
"remark": "Opens an interactive configuration UI when run in a terminal. Falls back to non-interactive generation when env-var `$NO_COLOR` is set or stdin/stdout is not a TTY. Defaults to \"~/.config/fastfetch/config.jsonc\". Prints the generated config if <path> is \"-\"",
"arg": {
"type": "path",
"optional": true
-5
View File
@@ -912,11 +912,6 @@
"description": "Set the timeout (ms) when waiting for child processes, `-1` for no timeout",
"default": 5000
},
"preRun": {
"type": "string",
"description": "Command to run before printing logos",
"default": ""
},
"detectVersion": {
"type": "boolean",
"description": "Whether to detect and display component versions. Mainly for benchmarking",
+22 -2
View File
@@ -4,6 +4,7 @@
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define FF_LIST_DEFAULT_ALLOC 16
@@ -110,6 +111,19 @@ static inline void* ffListAdd(FFlist* list, uint32_t elementSize) {
return ffListGet(list, elementSize, list->length - 1);
}
static inline void ffListRemoveAt(FFlist* list, uint32_t elementSize, uint32_t index) {
assert(list->length > index);
memmove(list->data + (index * elementSize), list->data + ((index + 1) * elementSize), (size_t) (list->length - index - 1) * elementSize);
--list->length;
}
static inline void ffListInsertAt(FFlist* list, uint32_t elementSize, uint32_t index, const void* element) {
assert(list->length >= index);
ffListAdd(list, elementSize);
memmove(list->data + ((index + 1) * elementSize), list->data + (index * elementSize), (size_t) (list->length - index - 1) * elementSize);
memcpy(list->data + (index * elementSize), element, elementSize);
}
#define FF_LIST_FOR_EACH(itemType, itemVarName, listVar) \
for (itemType* itemVarName = (itemType*) (listVar).data; \
itemVarName - (itemType*) (listVar).data < (intptr_t) (listVar).length; \
@@ -125,6 +139,12 @@ static inline void* ffListAdd(FFlist* list, uint32_t elementSize) {
#define FF_LIST_ADD(itemType, listVar) (itemType*) ffListAdd(&(listVar), (uint32_t) sizeof(itemType))
#define FF_LIST_REMOVE_AT(itemType, listVar, index) \
ffListRemoveAt(&(listVar), (uint32_t) sizeof(itemType), (index))
#define FF_LIST_INSERT_AT(itemType, listVar, index, pElement) \
ffListInsertAt(&(listVar), (uint32_t) sizeof(itemType), (index), (pElement))
#define FF_LIST_FIRST(itemType, listVar) FF_LIST_GET(itemType, listVar, 0)
#define FF_LIST_LAST(itemType, listVar) \
({ \
@@ -134,9 +154,9 @@ static inline void* ffListAdd(FFlist* list, uint32_t elementSize) {
#define FF_LIST_CONTAINS(listVar, pCompElement, compFunc) \
({ \
typedef typeof(*(pCompElement)) compElementType; \
typedef typeof(*(pCompElement)) compElementType; \
typedef bool compFuncType(const compElementType*, const compElementType*); \
static_assert(__builtin_types_compatible_p(typeof(compFunc), compFuncType), "Incompatible callback function"); \
static_assert(__builtin_types_compatible_p(typeof(compFunc), compFuncType), "Incompatible callback function"); \
ffListContains(&(listVar), (uint32_t) sizeof(*(pCompElement)), (pCompElement), (bool (*)(const void*, const void*)) compFunc); \
})
+3 -9
View File
@@ -221,9 +221,7 @@ static inline void ffStrbufClear(FFstrbuf* strbuf) {
}
static inline void ffStrbufAppendC(FFstrbuf* strbuf, char c) {
if (__builtin_expect(ffStrbufGetFree(strbuf) == 0, false)) {
ffStrbufEnsureFreeNoCheck(strbuf, 1);
}
ffStrbufEnsureFree(strbuf, 1);
strbuf->chars[strbuf->length++] = c;
strbuf->chars[strbuf->length] = '\0';
}
@@ -232,9 +230,7 @@ static inline void ffStrbufAppendNC(FFstrbuf* strbuf, uint32_t num, char c) {
if (__builtin_expect(num == 0, false)) {
return;
}
if (__builtin_expect(ffStrbufGetFree(strbuf) < num, false)) {
ffStrbufEnsureFreeNoCheck(strbuf, num);
}
ffStrbufEnsureFree(strbuf, num);
memset(&strbuf->chars[strbuf->length], c, num);
strbuf->length += num;
@@ -245,9 +241,7 @@ static inline void ffStrbufAppendNS(FFstrbuf* strbuf, uint32_t length, const cha
if (__builtin_expect(value == nullptr || length == 0, false)) {
return;
}
if (__builtin_expect(ffStrbufGetFree(strbuf) < length, false)) {
ffStrbufEnsureFreeNoCheck(strbuf, length);
}
ffStrbufEnsureFree(strbuf, length);
memcpy(&strbuf->chars[strbuf->length], value, length);
strbuf->length += length;
+35
View File
@@ -392,6 +392,27 @@ const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
count += detectTemp(conn, "Tp0e", result); // CPU performance core 8
break;
case FF_TEMP_CPU_M5X:
count += detectTemp(conn, "Tp00", result); // CPU super core 1
count += detectTemp(conn, "Tp04", result); // CPU super core 2
count += detectTemp(conn, "Tp08", result); // CPU super core 3
count += detectTemp(conn, "Tp0C", result); // CPU super core 4
count += detectTemp(conn, "Tp0G", result); // CPU super core 5
count += detectTemp(conn, "Tp0K", result); // CPU super core 6
count += detectTemp(conn, "Tp0O", result); // CPU performance core 1
count += detectTemp(conn, "Tp0R", result); // CPU performance core 2
count += detectTemp(conn, "Tp0U", result); // CPU performance core 3
count += detectTemp(conn, "Tp0X", result); // CPU performance core 4
count += detectTemp(conn, "Tp0a", result); // CPU performance core 5
count += detectTemp(conn, "Tp0d", result); // CPU performance core 6
count += detectTemp(conn, "Tp0g", result); // CPU performance core 7
count += detectTemp(conn, "Tp0j", result); // CPU performance core 8
count += detectTemp(conn, "Tp0m", result); // CPU performance core 9
count += detectTemp(conn, "Tp0p", result); // CPU performance core 10
count += detectTemp(conn, "Tp0u", result); // CPU performance core 11
count += detectTemp(conn, "Tp0y", result); // CPU performance core 12
break;
case FF_TEMP_GPU_INTEL:
count += detectTemp(conn, "TCGC", result); // GPU Intel Graphics
goto gpu_unknown;
@@ -442,6 +463,17 @@ const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
count += detectTemp(conn, "Tg0k", result); // GPU 8
break;
case FF_TEMP_GPU_M5X:
count += detectTemp(conn, "Tg0U", result); // GPU 1
count += detectTemp(conn, "Tg0X", result); // GPU 2
count += detectTemp(conn, "Tg0d", result); // GPU 3
count += detectTemp(conn, "Tg0g", result); // GPU 4
count += detectTemp(conn, "Tg0j", result); // GPU 5
count += detectTemp(conn, "Tg1Y", result); // GPU 6
count += detectTemp(conn, "Tg1c", result); // GPU 7
count += detectTemp(conn, "Tg1g", result); // GPU 8
break;
case FF_TEMP_BATTERY:
count += detectTemp(conn, "TB1T", result); // Battery
count += detectTemp(conn, "TB2T", result); // Battery
@@ -452,6 +484,9 @@ const char* ffDetectSmcTemps(enum FFTempType type, double* result) {
count += detectTemp(conn, "Tm06", result); // Memory 2
count += detectTemp(conn, "Tm08", result); // Memory 3
count += detectTemp(conn, "Tm09", result); // Memory 4
count += detectTemp(conn, "Tm0p", result); // Memory Proximity 1 (M4)
count += detectTemp(conn, "Tm1p", result); // Memory Proximity 2 (M4)
count += detectTemp(conn, "Tm2p", result); // Memory Proximity 3 (M4)
break;
}
+2
View File
@@ -14,6 +14,7 @@ enum FFTempType: uint8_t {
FF_TEMP_CPU_M2X,
FF_TEMP_CPU_M3X,
FF_TEMP_CPU_M4X,
FF_TEMP_CPU_M5X,
FF_TEMP_GPU_INTEL,
FF_TEMP_GPU_AMD,
@@ -22,6 +23,7 @@ enum FFTempType: uint8_t {
FF_TEMP_GPU_M2X,
FF_TEMP_GPU_M3X,
FF_TEMP_GPU_M4X,
FF_TEMP_GPU_M5X,
FF_TEMP_BATTERY,
+1 -1
View File
@@ -16,7 +16,7 @@ bool ffGetAppNameAndVersion(const char* exePath, FFstrbuf* retName, FFstrbuf* re
lastSlash -= strlen("MacOS");
char infoPlistPath[PATH_MAX];
memcpy(infoPlistPath, exePath, lastSlash - exePath);
memcpy(infoPlistPath, exePath, (size_t) (lastSlash - exePath));
memcpy(infoPlistPath + (lastSlash - exePath), "Info.plist", sizeof("Info.plist")); // X.app/Contents/Info.plist
NSError* error;
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@(infoPlistPath)]
+1
View File
@@ -19,4 +19,5 @@ typedef struct FFdata {
FFstrbuf genConfigPath; // Path to generate configuration file
FFDataResultDocType docType; // Type of result document
bool configLoaded;
bool genConfigInteractive; // `--gen-config` entered the interactive CUI
} FFdata;
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "common/ffdata.h"
// Runs the interactive config generation CUI.
//
// On success (the user saved the config), it sets `data->structure` to the user chosen
// module structure and `instance.config.logo.type` to the user chosen logo type, creates
// the result document and returns true. The caller is expected to call `writeConfigFile`.
// Returns false if the user quits without saving.
bool ffGenConfigInteractive(FFdata* data);
+32 -12
View File
@@ -1,6 +1,7 @@
#include "common/FFstrbuf.h"
#include "common/mallocHelper.h"
#include "common/strutil.h"
#include "common/debug.h"
#include <ctype.h>
#include <inttypes.h>
@@ -64,13 +65,24 @@ FFstrbuf ffStrbufCreateF(const char* format, ...) {
}
void ffStrbufEnsureFreeNoCheck(FFstrbuf* strbuf, uint32_t free) {
uint32_t allocate = strbuf->length + free;
uint32_t allocate;
if (__builtin_expect(__builtin_uadd_overflow(strbuf->length, free, &allocate), false)) {
FF_DEBUG("Error: Integer overflow when calculating allocation size. Aborting");
abort();
}
if (allocate < FASTFETCH_STRBUF_DEFAULT_ALLOC) { // `<` for null terminator
allocate = FASTFETCH_STRBUF_DEFAULT_ALLOC;
} else {
if (__builtin_expect(allocate > (UINT32_MAX >> 1), false)) {
// User tried to allocate more than 2GB of memory, which exceeds the maximum size supported by FFstrbuf.
// This is likely an error or an attempt to exploit the program. Abort to prevent potential issues.
FF_DEBUG("Error: Attempted to allocate %" PRIu32 " bytes more than 2GB of memory in FFstrbuf. Aborting", allocate);
abort();
}
// Round up to the next power of 2.
// If the value is already a power of 2, it will be rounded up to the next power of 2.
assert(allocate < (UINT32_MAX >> 1));
allocate = 1U << (32 - __builtin_clz(allocate));
}
@@ -91,16 +103,17 @@ void ffStrbufEnsureFreeNoCheck(FFstrbuf* strbuf, uint32_t free) {
// Ensure that at least `free` bytes are available in the buffer besides the current length
// for an empty buffer, free + 1 length memory will be allocated(+1 for the NUL)
// This function ensures a dynamic buffer is allocated even if free == 0
void ffStrbufEnsureFixedLengthFree(FFstrbuf* strbuf, uint32_t free) {
uint32_t oldFree = ffStrbufGetFree(strbuf);
if (oldFree >= free && !(strbuf->allocated == 0 && strbuf->length > 0)) {
return;
}
uint32_t newCap = strbuf->allocated + (free - oldFree);
uint32_t newCap;
if (strbuf->allocated == 0) {
newCap += strbuf->length + 1;
assert(strbuf->length < UINT32_MAX - 1); // We don't use static strings with length >= UINT32_MAX - 1, so this should never happen
if (__builtin_expect(__builtin_uadd_overflow(strbuf->length + 1, free, &newCap), false)) {
FF_DEBUG("Error: Integer overflow when calculating new capacity. Aborting");
abort();
}
char* newbuf = malloc(sizeof(*strbuf->chars) * newCap);
if (strbuf->length == 0) {
*newbuf = '\0';
@@ -109,6 +122,15 @@ void ffStrbufEnsureFixedLengthFree(FFstrbuf* strbuf, uint32_t free) {
}
strbuf->chars = newbuf;
} else {
uint32_t oldFree = ffStrbufGetFree(strbuf);
if (oldFree >= free) {
return;
}
if (__builtin_expect(__builtin_uadd_overflow(strbuf->allocated, free - oldFree, &newCap), false)) {
FF_DEBUG("Error: Integer overflow when calculating new capacity. Aborting");
abort();
}
strbuf->chars = realloc(strbuf->chars, sizeof(*strbuf->chars) * newCap);
}
@@ -410,9 +432,7 @@ bool ffStrbufSubstrBefore(FFstrbuf* strbuf, uint32_t index) {
if (strbuf->allocated == 0) {
// static string
if (index < strbuf->length) {
ffStrbufInitNS(strbuf, index, strbuf->chars);
}
ffStrbufInitNS(strbuf, index, strbuf->chars);
return true;
}
+7 -1
View File
@@ -6,7 +6,13 @@ void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char*
char* out = output;
const char* ends = str + (size - size % 3);
while (str != ends) {
uint32_t n = __builtin_bswap32(*(uint32_t*) str);
uint32_t n = *(uint32_t*) str;
#if !__BIG_ENDIAN__
// The 3 input bytes must be laid out big-endian (str[0] in the most
// significant position). On little-endian hosts swap; on big-endian
// hosts the word is already in the right order.
n = __builtin_bswap32(n);
#endif
*out++ = chars[(n >> 26) & 63];
*out++ = chars[(n >> 20) & 63];
*out++ = chars[(n >> 14) & 63];
+1 -6
View File
@@ -175,7 +175,7 @@ static bool parseStructureCommand(
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(line[0]) - 'A']; *modules; ++modules) {
FFModuleBaseInfo* baseInfo = *modules;
if (ffStrEqualsIgnCase(line, baseInfo->name)) {
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
alignas(uint64_t) uint8_t optionBuf[FF_OPTION_MAX_SIZE];
baseInfo->initOptions(optionBuf);
if (data->resultDoc != nullptr) {
fn(data, baseInfo, optionBuf);
@@ -244,11 +244,6 @@ void ffPrintCommandOption(FFdata* data) {
}
void ffMigrateCommandOptionToJsonc(FFdata* data) {
// If we don't have a custom structure, use the default one
if (data->structure.length == 0) {
ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
}
char* moduleType = nullptr;
size_t moduleLen = 0;
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure)) {
+15 -1
View File
@@ -109,7 +109,21 @@ static inline void appendInvalidPlaceholder(FFstrbuf* buffer, const char* start,
}
static inline bool formatArgSet(const FFformatarg* arg) {
return arg->value != nullptr && ((arg->type == FF_ARG_TYPE_DOUBLE && *(double*) arg->value > 0.0) || (arg->type == FF_ARG_TYPE_FLOAT && *(float*) arg->value > 0.0) || (arg->type == FF_ARG_TYPE_INT && *(int32_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*) arg->value)->length > 0) || (arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*) arg->value)) || (arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT && *(uint32_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_UINT64 && *(uint64_t*) arg->value > 0) || (arg->type == FF_ARG_TYPE_BOOL && *(bool*) arg->value) || (arg->type == FF_ARG_TYPE_LIST && ((FFlist*) arg->value)->length > 0));
// clang-format off
return arg->value != nullptr && (
(arg->type == FF_ARG_TYPE_DOUBLE && *(double*) arg->value > 0.0) ||
(arg->type == FF_ARG_TYPE_FLOAT && *(float*) arg->value > 0.0) ||
(arg->type == FF_ARG_TYPE_INT && *(int32_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_STRBUF && ((FFstrbuf*) arg->value)->length > 0) ||
(arg->type == FF_ARG_TYPE_STRING && ffStrSet((char*) arg->value)) ||
(arg->type == FF_ARG_TYPE_UINT8 && *(uint8_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_UINT16 && *(uint16_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_UINT && *(uint32_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_UINT64 && *(uint64_t*) arg->value > 0) ||
(arg->type == FF_ARG_TYPE_BOOL && *(bool*) arg->value) ||
(arg->type == FF_ARG_TYPE_LIST && ((FFlist*) arg->value)->length > 0)
);
// clang-format on
}
[[maybe_unused]] static inline void normalizeArgName(FFstrbuf* dst, const char* src) {
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -95,7 +95,7 @@ static bool parseModuleJsonObject(const char* type, yyjson_val* jsonVal, yyjson_
for (FFModuleBaseInfo** modules = ffModuleInfos[toupper(type[0]) - 'A']; *modules; ++modules) {
FFModuleBaseInfo* baseInfo = *modules;
if (ffStrEqualsIgnCase(type, baseInfo->name)) {
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
alignas(uint64_t) uint8_t optionBuf[FF_OPTION_MAX_SIZE];
baseInfo->initOptions(optionBuf);
if (jsonVal) {
baseInfo->parseJsonObject(optionBuf, jsonVal);
+1 -1
View File
@@ -6,7 +6,7 @@
#include <stdio.h>
bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result) {
FILE* FF_AUTO_CLOSE_FILE netRoute = fopen("/proc/route", "r");
FF_AUTO_CLOSE_FILE FILE* netRoute = fopen("/proc/route", "r");
if (!netRoute) {
return false;
+8 -1
View File
@@ -457,6 +457,13 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
if (clHeader) {
contentLength = (uint32_t) strtoul(clHeader + 15, nullptr, 10);
if (contentLength > 0) {
if (contentLength > 1024 * 1024) { // 1MB limit to prevent excessive memory allocation and potential attacks
FF_DEBUG("Content-Length is too large: %u bytes, aborting", contentLength);
close(state->sockfd);
state->sockfd = -1;
return "Content-Length too large";
}
FF_DEBUG("Detected Content-Length: %u, pre-allocating buffer", contentLength);
// Ensure buffer is large enough, adding header size and some margin
ffStrbufEnsureFree(buffer, contentLength + 16);
@@ -481,7 +488,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
return "No HTTP header end found";
}
if (!ffStrbufStartsWithS(buffer, "HTTP/1.0 200 OK\r\n")) {
if (!ffStrbufStartsWithS(buffer, "HTTP/1.0 200 OK\r\n") && !ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n")) {
FF_DEBUG("Invalid response: %.40s...", buffer->chars);
return "Invalid response";
}
+28 -33
View File
@@ -141,16 +141,14 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
}
// Initialize overlapped structure with WSA event for asynchronous I/O
state->overlapped = (OVERLAPPED) {
.hEvent = WSACreateEvent()
};
state->overlapped = (OVERLAPPED) {};
if (state->overlapped.hEvent == WSA_INVALID_EVENT) {
FF_DEBUG("WSACreateEvent() failed");
if (!NT_SUCCESS(NtCreateEvent(&state->overlapped.hEvent, EVENT_ALL_ACCESS, nullptr, NotificationEvent, FALSE))) {
FF_DEBUG("NtCreateEvent() failed");
closesocket(state->sockfd);
FreeAddrInfoW(addr);
state->sockfd = INVALID_SOCKET;
return "WSACreateEvent() failed";
return "NtCreateEvent() failed";
}
// Build HTTP command
@@ -194,7 +192,7 @@ const char* ffNetworkingSendHttpRequest(FFNetworkingState* state, const char* ho
if (!result) {
if (WSAGetLastError() != WSA_IO_PENDING) {
FF_DEBUG("ConnectEx() failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
WSACloseEvent(state->overlapped.hEvent);
NtClose(state->overlapped.hEvent);
closesocket(state->sockfd);
state->sockfd = INVALID_SOCKET;
ffStrbufDestroy(&state->command);
@@ -219,37 +217,27 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
return "ffNetworkingSendHttpRequest() failed";
}
DWORD transfer;
uint32_t timeout = state->timeout;
if (timeout > 0) {
FF_DEBUG("WSAWaitForMultipleEvents with timeout: %u ms", timeout);
DWORD result = WSAWaitForMultipleEvents(1, &state->overlapped.hEvent, TRUE, timeout, FALSE);
if (result != WSA_WAIT_EVENT_0) {
if (result == WSA_WAIT_TIMEOUT) {
FF_DEBUG("WSAWaitForMultipleEvents timed out");
} else {
FF_DEBUG("WSAWaitForMultipleEvents failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
}
if (CancelIoEx((HANDLE) state->sockfd, &state->overlapped)) {
WSAWaitForMultipleEvents(1, &state->overlapped.hEvent, TRUE, 10, TRUE);
}
WSACloseEvent(state->overlapped.hEvent);
closesocket(state->sockfd);
ffStrbufDestroy(&state->command);
return "WSAWaitForMultipleEvents() failed or timeout";
if (!GetOverlappedResultEx((HANDLE) state->sockfd, &state->overlapped, &transfer, timeout > 0 ? timeout : INFINITE, FALSE)) {
DWORD error = GetLastError();
if (error == WAIT_TIMEOUT) {
FF_DEBUG("GetOverlappedResultEx timed out");
} else {
FF_DEBUG("GetOverlappedResultEx failed: %s", ffDebugWin32Error(error));
}
}
DWORD transfer, flags;
if (!WSAGetOverlappedResult(state->sockfd, &state->overlapped, &transfer, TRUE, &flags)) {
FF_DEBUG("WSAGetOverlappedResult failed: %s", ffDebugWin32Error((DWORD) WSAGetLastError()));
IO_STATUS_BLOCK cancelIosb = {};
if (NT_SUCCESS(NtCancelIoFileEx((HANDLE) state->sockfd, (PIO_STATUS_BLOCK) &state->overlapped, &cancelIosb))) {
NtWaitForSingleObject(state->overlapped.hEvent, TRUE, &(LARGE_INTEGER) { .QuadPart = (int64_t) 10 * -10000 });
}
NtClose(state->overlapped.hEvent);
closesocket(state->sockfd);
WSACloseEvent(state->overlapped.hEvent);
ffStrbufDestroy(&state->command);
return "WSAGetOverlappedResult() failed";
return "GetOverlappedResultEx() failed or timeout";
}
FF_DEBUG("WSAGetOverlappedResult succeeded, %u bytes sent", (unsigned) transfer);
FF_DEBUG("GetOverlappedResultEx succeeded, %u bytes sent", (unsigned) transfer);
ffStrbufDestroy(&state->command);
WSACloseEvent(state->overlapped.hEvent);
NtClose(state->overlapped.hEvent);
state->overlapped.hEvent = nullptr;
if (setsockopt(state->sockfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, nullptr, 0) != 0) {
@@ -322,6 +310,13 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
if (clHeader) {
contentLength = (uint32_t) strtoul(clHeader + 15, nullptr, 10);
if (contentLength > 0) {
if (contentLength > 1024 * 1024) { // 1MB limit to prevent excessive memory allocation and potential attacks
FF_DEBUG("Content-Length is too large: %u bytes, aborting", contentLength);
closesocket(state->sockfd);
state->sockfd = INVALID_SOCKET;
return "Content-Length too large";
}
FF_DEBUG("Detected Content-Length: %u, pre-allocating buffer", contentLength);
// Ensure buffer is large enough, adding header size and some margin
ffStrbufEnsureFree(buffer, contentLength + 16);
@@ -346,7 +341,7 @@ const char* ffNetworkingRecvHttpResponse(FFNetworkingState* state, FFstrbuf* buf
return "No HTTP header end found";
}
if (!ffStrbufStartsWithS(buffer, "HTTP/1.0 200 OK\r\n")) {
if (!ffStrbufStartsWithS(buffer, "HTTP/1.0 200 OK\r\n") && !ffStrbufStartsWithS(buffer, "HTTP/1.1 200 OK\r\n")) {
FF_DEBUG("Invalid response: %.40s...", buffer->chars);
return "Invalid response";
}
+6
View File
@@ -57,6 +57,9 @@ static inline void ffLibraryUnload(void** handle) {
#define FF_LIBRARY_LOAD_SYMBOL_VAR(library, varName, symbolName, returnValue) \
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName).ff##symbolName, symbolName, returnValue);
#define FF_LIBRARY_LOAD_SYMBOL_VAR_LAZY(library, varName, symbolName) \
(varName).ff##symbolName = (typeof(&symbolName)) dlsym(library, #symbolName);
#define FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(library, varName, symbolName) \
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName).ff##symbolName, symbolName, "dlsym " #symbolName " failed");
@@ -94,6 +97,9 @@ void* ffLibraryLoadMulti(const char* path, int maxVersion, ...);
#define FF_LIBRARY_LOAD_SYMBOL_VAR(library, varName, symbolName, returnValue) \
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName).ff##symbolName, symbolName, returnValue);
#define FF_LIBRARY_LOAD_SYMBOL_VAR_LAZY(library, varName, symbolName) \
(varName).ff##symbolName = (typeof(&symbolName)) &symbolName;
#define FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(library, varName, symbolName) \
FF_LIBRARY_LOAD_SYMBOL_ADDRESS(library, (varName).ff##symbolName, symbolName, "dlsym " #symbolName " failed");
+1
View File
@@ -34,6 +34,7 @@ typedef struct FFModuleBaseInfo {
bool (*generateJsonResult)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* module); // true on success
void (*generateJsonConfig)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* obj);
FFModuleFormatArgList formatArgs;
const uint8_t defaultOrder;
} FFModuleBaseInfo;
typedef enum FFModuleKeyType: uint8_t {
+2 -4
View File
@@ -1,6 +1,4 @@
#pragma once
static inline void ffUnused(int dummy, ...) {
(void) dummy;
}
#define FF_UNUSED(...) ffUnused(0, __VA_ARGS__);
static inline void ffUnused(...) { /* no-op */ }
#define FF_UNUSED(...) ffUnused(__VA_ARGS__);
+8 -4
View File
@@ -41,7 +41,7 @@ static FFCodecType ffCodecCodecToType(CMVideoCodecType codec) {
}
static FFCodecType ffCodecDetectEncoders(void) {
CFArrayRef encoderList = nullptr;
FF_CFTYPE_AUTO_RELEASE CFArrayRef encoderList = nullptr;
if (VTCopyVideoEncoderList(nullptr, &encoderList) != noErr || !encoderList) {
return FF_CODEC_TYPE_NONE;
}
@@ -65,16 +65,20 @@ static FFCodecType ffCodecDetectEncoders(void) {
static FFCodecType ffCodecDetectDecoders() {
FFCodecType types = FF_CODEC_TYPE_NONE;
for (uint32_t i = 0; i < ARRAY_SIZE(FF_CODEC_CODECS); ++i) {
if (types & FF_CODEC_CODECS[i].type) {
auto codec = FF_CODEC_CODECS[i];
if (types & codec.type) {
continue;
}
bool supported = VTIsHardwareDecodeSupported(FF_CODEC_CODECS[i].codec);
if (__builtin_available(macOS 11.0, *)) {
VTRegisterSupplementalVideoDecoderIfAvailable(codec.codec);
}
bool supported = VTIsHardwareDecodeSupported(codec.codec);
if (!supported) {
continue;
}
types |= FF_CODEC_CODECS[i].type;
types |= codec.type;
}
return types;
+3
View File
@@ -25,6 +25,9 @@ static double detectCpuTemp(const FFCPUOptions* options, const FFstrbuf* cpuName
case 4:
error = ffDetectSmcTemps(FF_TEMP_CPU_M4X, &result);
break;
case 5:
error = ffDetectSmcTemps(FF_TEMP_CPU_M5X, &result);
break;
default:
error = "Unsupported Apple Silicon CPU";
}
@@ -25,6 +25,11 @@ static void waylandOutputScaleListener(void* data, [[maybe_unused]] struct wl_ou
display->dpi = 96 * (uint32_t) scale;
}
static void waylandOutputDoneListener(void* data, [[maybe_unused]] struct wl_output* output) {
WaylandDisplay* display = data;
display->done = true;
}
static void waylandOutputGeometryListener(void* data,
[[maybe_unused]] struct wl_output* output,
[[maybe_unused]] int32_t x,
@@ -49,24 +54,19 @@ static void handleXdgLogicalSize(void* data, [[maybe_unused]] struct zxdg_output
}
}
// Dirty hack for #477
// The order of these callbacks MUST follow `struct wl_output_listener`
static void* outputListener[] = {
waylandOutputGeometryListener, // geometry
waylandOutputModeListener, // mode
stubListener, // done
waylandOutputScaleListener, // scale
ffWaylandOutputNameListener, // name
ffWaylandOutputDescriptionListener, // description
static struct wl_output_listener outputListener = {
.geometry = waylandOutputGeometryListener,
.mode = waylandOutputModeListener,
.done = waylandOutputDoneListener,
.scale = waylandOutputScaleListener,
.name = (void*) ffWaylandOutputNameListener,
.description = (void*) ffWaylandOutputDescriptionListener,
};
static_assert(
sizeof(outputListener) >= sizeof(struct wl_output_listener),
"sizeof(outputListener) is too small. Please report it to fastfetch github issue");
static struct zxdg_output_v1_listener zxdgOutputListener = {
.logical_position = (void*) stubListener,
.logical_position = (void*) ffUnused,
.logical_size = handleXdgLogicalSize,
.done = (void*) stubListener,
.done = (void*) ffUnused,
.name = (void*) ffWaylandOutputNameListener,
.description = (void*) ffWaylandOutputDescriptionListener,
};
@@ -84,17 +84,17 @@ static void handleWpTfNamed(void *data, [[maybe_unused]] struct wp_image_descrip
}
static const struct wp_image_description_info_v1_listener wpImageDescInfoListener = {
.done = (void*) stubListener,
.icc_file = (void*) stubListener,
.primaries = (void*) stubListener,
.primaries_named = (void*) stubListener,
.tf_power = (void*) stubListener,
.done = (void*) ffUnused,
.icc_file = (void*) ffUnused,
.primaries = (void*) ffUnused,
.primaries_named = (void*) ffUnused,
.tf_power = (void*) ffUnused,
.tf_named = (void*) handleWpTfNamed,
.luminances = (void*) stubListener,
.target_primaries = (void*) stubListener,
.target_luminance = (void*) stubListener,
.target_max_cll = (void*) stubListener,
.target_max_fall = (void*) stubListener,
.luminances = (void*) ffUnused,
.target_primaries = (void*) ffUnused,
.target_luminance = (void*) ffUnused,
.target_max_cll = (void*) ffUnused,
.target_max_fall = (void*) ffUnused,
};
const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) {
@@ -122,6 +122,13 @@ const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry*
wldata->ffwl_proxy_destroy(output);
return "Failed to roundtrip wl_output";
}
if (bindVersion >= WL_OUTPUT_DONE_SINCE_VERSION && !display.done) {
const char* error = ffWaylandWaitForDone(&display);
if (error) {
wldata->ffwl_proxy_destroy(output);
return error;
}
}
if (wldata->zxdgOutputManager) {
uint32_t bindVersion = min(version, ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION);
@@ -33,8 +33,8 @@ static const struct kde_output_device_mode_v2_listener modeListener = {
.size = waylandKdeModeSizeListener,
.refresh = waylandKdeModeRefreshListener,
.preferred = waylandKdeModePreferredListener,
.removed = (void*) stubListener,
.flags = (void*) stubListener,
.removed = (void*) ffUnused,
.flags = (void*) ffUnused,
};
static void waylandKdeModeListener(void* data, [[maybe_unused]] struct kde_output_device_v2* _, struct kde_output_device_mode_v2* mode) {
@@ -57,23 +57,22 @@ static void waylandKdeCurrentModeListener(void* data, [[maybe_unused]] struct kd
return;
}
int set = 0;
bool foundCurrent = false, foundPreferred = false;
FF_LIST_FOR_EACH (WaylandKdeMode, m, *(FFlist*) wldata->internal) {
if (m->pMode == mode) {
if (!foundCurrent && m->pMode == mode) {
wldata->width = m->width;
wldata->height = m->height;
wldata->refreshRate = m->refreshRate;
if (++set == 2) {
break;
}
foundCurrent = true;
}
if (m->preferred) {
if (!foundPreferred && m->preferred) {
wldata->preferredWidth = m->width;
wldata->preferredHeight = m->height;
wldata->preferredRefreshRate = m->refreshRate;
if (++set == 2) {
break;
}
foundPreferred = true;
}
if (foundCurrent && foundPreferred) {
break;
}
}
}
@@ -147,47 +146,52 @@ static void waylandKdePriorityListener(void* data, [[maybe_unused]] struct kde_o
display->primary = priority == 1;
}
static void waylandKdeDoneListener(void* data, [[maybe_unused]] struct kde_output_device_v2* kde_output_device_v2) {
WaylandDisplay* display = data;
display->done = true;
}
static struct kde_output_device_v2_listener outputListener = {
.geometry = waylandKdeGeometryListener,
.current_mode = waylandKdeCurrentModeListener,
.mode = waylandKdeModeListener,
.done = (void*) stubListener,
.done = waylandKdeDoneListener,
.scale = waylandKdeScaleListener,
.edid = waylandKdeEdidListener,
.enabled = waylandKdeEnabledListener,
.uuid = (void*) stubListener,
.serial_number = (void*) stubListener,
.eisa_id = (void*) stubListener,
.capabilities = (void*) stubListener,
.overscan = (void*) stubListener,
.vrr_policy = (void*) stubListener,
.rgb_range = (void*) stubListener,
.uuid = (void*) ffUnused,
.serial_number = (void*) ffUnused,
.eisa_id = (void*) ffUnused,
.capabilities = (void*) ffUnused,
.overscan = (void*) ffUnused,
.vrr_policy = (void*) ffUnused,
.rgb_range = (void*) ffUnused,
.name = waylandKdeNameListener,
.high_dynamic_range = waylandKdeHdrListener,
.sdr_brightness = (void*) stubListener,
.wide_color_gamut = (void*) stubListener,
.auto_rotate_policy = (void*) stubListener,
.icc_profile_path = (void*) stubListener,
.brightness_metadata = (void*) stubListener,
.brightness_overrides = (void*) stubListener,
.sdr_gamut_wideness = (void*) stubListener,
.color_profile_source = (void*) stubListener,
.brightness = (void*) stubListener,
.color_power_tradeoff = (void*) stubListener,
.dimming = (void*) stubListener,
.replication_source = (void*) stubListener,
.ddc_ci_allowed = (void*) stubListener,
.sdr_brightness = (void*) ffUnused,
.wide_color_gamut = (void*) ffUnused,
.auto_rotate_policy = (void*) ffUnused,
.icc_profile_path = (void*) ffUnused,
.brightness_metadata = (void*) ffUnused,
.brightness_overrides = (void*) ffUnused,
.sdr_gamut_wideness = (void*) ffUnused,
.color_profile_source = (void*) ffUnused,
.brightness = (void*) ffUnused,
.color_power_tradeoff = (void*) ffUnused,
.dimming = (void*) ffUnused,
.replication_source = (void*) ffUnused,
.ddc_ci_allowed = (void*) ffUnused,
.max_bits_per_color = (void*) waylandKdeMaxBitsPerColorListener,
.max_bits_per_color_range = (void*) stubListener,
.automatic_max_bits_per_color_limit = (void*) stubListener,
.edr_policy = (void*) stubListener,
.sharpness = (void*) stubListener,
.max_bits_per_color_range = (void*) ffUnused,
.automatic_max_bits_per_color_limit = (void*) ffUnused,
.edr_policy = (void*) ffUnused,
.sharpness = (void*) ffUnused,
.priority = waylandKdePriorityListener,
.auto_brightness = (void*) stubListener,
.removed = (void*) stubListener,
.hdr_icc_profile_path = (void*) stubListener,
.hdr_color_profile_source = (void*) stubListener,
.abm_level = (void*) stubListener,
.auto_brightness = (void*) ffUnused,
.removed = (void*) ffUnused,
.hdr_icc_profile_path = (void*) ffUnused,
.hdr_color_profile_source = (void*) ffUnused,
.abm_level = (void*) ffUnused,
};
static const char* waylandKdeHandleOutput(WaylandData* wldata, struct wl_proxy* output) {
@@ -211,6 +215,13 @@ static const char* waylandKdeHandleOutput(WaylandData* wldata, struct wl_proxy*
wldata->ffwl_proxy_destroy(output);
return "Failed to roundtrip kde_output_device_v2";
}
if (!display.done) {
const char* error = ffWaylandWaitForDone(&display);
if (error) {
wldata->ffwl_proxy_destroy(output);
return error;
}
}
// Destroy any mode proxies that were created during the listeners.
// wl proxies created for modes are not automatically freed by destroying
// the parent output proxy, so destroy them explicitly to avoid leaks.
@@ -288,7 +299,7 @@ static void waylandKdeOutputListener(void* data, [[maybe_unused]] struct kde_out
static struct kde_output_device_registry_v2_listener registryListener = {
.output = waylandKdeOutputListener,
.finished = (void*) stubListener,
.finished = (void*) ffUnused,
};
const char* ffWaylandHandleKdeOutputRegistry(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version) {
@@ -217,6 +217,39 @@ uint32_t ffWaylandHandleRotation(WaylandDisplay* display) {
return rotation;
}
const char* ffWaylandWaitForDone(WaylandDisplay* display) {
int32_t timeout = instance.config.general.processingTimeout;
struct timespec ts;
if (timeout == 0) {
return "timeout is disabled";
} else if (timeout > 0) {
if (!display->parent->ffwl_display_dispatch_timeout) {
return "timeout is enabled, but the libwayland-client version is too old to support it";
}
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout % 1000) * 1000000;
}
WaylandData* wldata = display->parent;
// Some compositors emit the final output state asynchronously after the roundtrip callback (#2074)
while (!display->done) {
if (timeout > 0) {
if (wldata->ffwl_display_dispatch_timeout(wldata->display, &ts) < 0) {
return "wl_display_dispatch_timeout() failed";
}
} else if (timeout < 0) {
if (wldata->ffwl_display_dispatch(wldata->display) < 0) {
return "wl_display_dispatch() failed";
}
} else {
return "Timeout exceeded, but the compositor did not send a done event";
}
}
return nullptr;
}
const char* ffdsConnectWayland(FFDisplayServerResult* result) {
if (getenv("XDG_RUNTIME_DIR") == nullptr) {
return "Wayland requires $XDG_RUNTIME_DIR being set";
@@ -236,6 +269,12 @@ const char* ffdsConnectWayland(FFDisplayServerResult* result) {
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(wayland, data, wl_proxy_destroy)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(wayland, data, wl_display_roundtrip)
if (instance.config.general.processingTimeout < 0) {
FF_LIBRARY_LOAD_SYMBOL_VAR_LAZY(wayland, data, wl_display_dispatch)
} else if (instance.config.general.processingTimeout > 0) {
FF_LIBRARY_LOAD_SYMBOL_VAR_LAZY(wayland, data, wl_display_dispatch_timeout)
}
data.display = ffwl_display_connect(nullptr);
if (data.display == nullptr) {
return "wl_display_connect returned nullptr";
@@ -253,7 +292,7 @@ const char* ffdsConnectWayland(FFDisplayServerResult* result) {
struct wl_registry_listener registry_listener = {
.global = waylandGlobalAddListener,
.global_remove = (void*) stubListener
.global_remove = (void*) ffUnused
};
data.ffwl_proxy_add_listener(registry, (void (**)(void)) &registry_listener, &data);
@@ -20,12 +20,19 @@ typedef enum WaylandProtocolType: uint8_t {
FF_WAYLAND_PROTOCOL_TYPE_KDE_REGISTRY,
} WaylandProtocolType;
// In case users have an ancient version of libwayland-client
int wl_display_dispatch_timeout(struct wl_display *display, const struct timespec *timeout);
typedef struct WaylandData {
FFDisplayServerResult* result;
FF_LIBRARY_SYMBOL(wl_proxy_marshal_constructor_versioned)
FF_LIBRARY_SYMBOL(wl_proxy_add_listener)
FF_LIBRARY_SYMBOL(wl_proxy_destroy)
FF_LIBRARY_SYMBOL(wl_display_roundtrip)
union {
FF_LIBRARY_SYMBOL(wl_display_dispatch)
FF_LIBRARY_SYMBOL(wl_display_dispatch_timeout)
};
struct wl_display* display;
WaylandProtocolType protocolType;
struct wl_proxy* zxdgOutputManager;
@@ -58,12 +65,9 @@ typedef struct WaylandDisplay {
FFstrbuf serial;
uint8_t bitDepth;
bool primary;
bool done;
} WaylandDisplay;
inline static void stubListener(void* data, ...) {
(void) data;
}
inline static uint64_t ffWaylandGenerateIdFromName(const char* name) {
uint64_t id = 0;
size_t len = strlen(name);
@@ -79,6 +83,7 @@ void ffWaylandOutputNameListener(void* data, [[maybe_unused]] void* output, cons
void ffWaylandOutputDescriptionListener(void* data, [[maybe_unused]] void* output, const char* description);
// Modifies content of display. Don't call this function when calling ffdsAppendDisplay
uint32_t ffWaylandHandleRotation(WaylandDisplay* display);
const char* ffWaylandWaitForDone(WaylandDisplay* display);
const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
const char* ffWaylandHandleKdeOutputRegistry(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
+3
View File
@@ -28,6 +28,9 @@ static double detectGpuTemp(const FFstrbuf* gpuName) {
case 4:
error = ffDetectSmcTemps(FF_TEMP_GPU_M4X, &result);
break;
case 5:
error = ffDetectSmcTemps(FF_TEMP_GPU_M5X, &result);
break;
default:
error = "Unsupported Apple Silicon GPU";
break;
+283 -254
View File
@@ -17,12 +17,16 @@
#include <windows.h>
#include <cfgmgr32.h>
#include <devguid.h>
#include <devpkey.h>
#include <pciprop.h>
#define GUID_DEVCLASS_DISPLAY_STRING L"{4d36e968-e325-11ce-bfc1-08002be10318}" // Found in <devguid.h>
static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds, bool queryPcieGen) {
FF_DEBUG("Query PCI device info: %08llX", gpu->deviceId);
// https://wine-devel.winehq.narkive.com/vKO2Bkgj/patch-1-2-dxgi-tests-add-test-for-enumerating-display-adapters-using-setupapi
DEFINE_DEVPROPKEY(DEVPROPKEY_DISPLAY_ADAPTER_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
static bool queryDeviceInfoCM(FFGPUResult* gpu, uint64_t luid, D3DKMT_DEVICE_IDS* outDeviceIds, bool queryPcieGen) {
FF_DEBUG("Query PCI device info: %08llX with LUID: %08llX", gpu->deviceId, luid);
static FFlist deviceIdsCache;
static bool initialized;
@@ -32,7 +36,10 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
uint32_t maxLinkSpeed;
uint32_t maxLinkWidth;
D3DKMT_DEVICE_IDS deviceIds;
uint64_t adapterAddress;
uint64_t pciAddr;
uint64_t luid;
FFstrbuf vendor;
FFstrbuf name;
} CacheEntry;
if (!initialized) {
@@ -71,13 +78,36 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
}
if (wcsncmp(devId, L"PCI\\", 4) != 0) {
FF_DEBUG("Skipping non-PCI device ID: %ls", devId);
continue;
}
CacheEntry* entry = FF_LIST_ADD(CacheEntry, deviceIdsCache);
*entry = (CacheEntry) {};
*entry = (CacheEntry){};
ULONG bufLen = sizeof(entry->luid);
DEVPROPTYPE type;
CONFIGRET ret = CM_Get_DevNode_PropertyW(devInst, &DEVPROPKEY_DISPLAY_ADAPTER_LUID, &type, (PBYTE) &entry->luid, &bufLen, 0);
if (ret != CR_SUCCESS) { // Not available on Windows 8.1
FF_DEBUG("Failed to get device LUID: %s", ffDebugConfigRet(ret));
uint32_t pciBus = 0;
ULONG pciBufLen = sizeof(pciBus);
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_BUSNUMBER, nullptr, &pciBus, &pciBufLen, 0) == CR_SUCCESS) {
uint32_t pciAddr = 0;
pciBufLen = sizeof(pciAddr);
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_ADDRESS, nullptr, &pciAddr, &pciBufLen, 0) == CR_SUCCESS) {
entry->pciAddr = ffGPUPciAddr2Id(0, pciBus, (pciAddr >> 16) & 0xFFFF, pciAddr & 0xFFFF);
FF_DEBUG("Cached device IDs for PCI bus %u: vendor=0x%04x device=0x%04x", pciBus, entry->deviceIds.VendorID, entry->deviceIds.DeviceID);
} else {
FF_DEBUG("Failed to get PCI address");
}
} else {
FF_DEBUG("Failed to get PCI bus number");
}
if (entry->pciAddr == 0) {
FF_DEBUG("Skipping device ID: %ls due to missing LUID and PCI address", devId);
deviceIdsCache.length--;
continue;
}
}
// L"PCI\\VEN_10DE&DEV_2782&SUBSYS_513417AA&REV_A1\\4&3674a6b9&0&0008"
if (swscanf(devId + 4, L"VEN_%x&DEV_%x&SUBSYS_%4x%4x&REV_%x", &entry->deviceIds.VendorID, &entry->deviceIds.DeviceID, &entry->deviceIds.SubSystemID, &entry->deviceIds.SubVendorID, &entry->deviceIds.RevisionID) >= 2) {
@@ -87,32 +117,44 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
// And yeah, DXGKMDT_OPM_BUS_TYPE_PCIEXPRESS (3) exists
entry->deviceIds.BusType = 1;
} else {
FF_DEBUG("Failed to parse PCI IDs from device ID string");
deviceIdsCache.length--; // remove the cache entry since it's not valid
continue;
FF_DEBUG("Failed to parse PCI IDs from device ID: %ls", devId);
entry->deviceIds.VendorID = -1u;
}
uint32_t pciBus = 0;
ULONG pciBufLen = sizeof(pciBus);
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_BUSNUMBER, nullptr, &pciBus, &pciBufLen, 0) == CR_SUCCESS) {
uint32_t pciAddr = 0;
pciBufLen = sizeof(pciAddr);
if (CM_Get_DevNode_Registry_PropertyW(devInst, CM_DRP_ADDRESS, nullptr, &pciAddr, &pciBufLen, 0) == CR_SUCCESS) {
entry->adapterAddress = ffGPUPciAddr2Id(0, pciBus, (pciAddr >> 16) & 0xFFFF, pciAddr & 0xFFFF);
FF_DEBUG("Cached device IDs for PCI bus %u: vendor=0x%04x device=0x%04x", pciBus, entry->deviceIds.VendorID, entry->deviceIds.DeviceID);
} else {
FF_DEBUG("Failed to get PCI address");
}
wchar_t wstr[256];
bufLen = sizeof(wstr);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_FriendlyName, &type, (PBYTE) wstr, &bufLen, 0);
if (ret == CR_SUCCESS) {
ffStrbufSetNWS(&entry->name, (bufLen - 1) / sizeof(wchar_t), wstr);
FF_DEBUG("Device friendly name: %ls", wstr);
} else {
FF_DEBUG("Failed to get PCI bus number");
FF_DEBUG("Failed to get device friendly name: %s", ffDebugConfigRet(ret));
bufLen = sizeof(wstr);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_DeviceDesc, &type, (PBYTE) wstr, &bufLen, 0);
if (ret == CR_SUCCESS) {
ffStrbufSetNWS(&entry->name, (bufLen - 1) / sizeof(wchar_t), wstr);
FF_DEBUG("Device description: %ls", wstr);
} else {
FF_DEBUG("Failed to get device description: %s", ffDebugConfigRet(ret));
}
}
if (queryPcieGen) {
bufLen = sizeof(wstr);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Device_Manufacturer, &type, (PBYTE) wstr, &bufLen, 0);
if (ret == CR_SUCCESS) {
ffStrbufSetNWS(&entry->vendor, (bufLen - 1) / sizeof(wchar_t), wstr);
FF_DEBUG("Device vendor name: %ls", wstr);
} else {
FF_DEBUG("Failed to get device vendor name: %s", ffDebugConfigRet(ret));
}
if (queryPcieGen && entry->deviceIds.VendorID != -1u) {
DEVPROPTYPE propType;
pciBufLen = sizeof(entry->maxLinkSpeed);
bufLen = sizeof(entry->maxLinkSpeed);
// Reports PCIe gen despite the PKEY name
CONFIGRET ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkSpeed, &propType, (PBYTE) &entry->maxLinkSpeed, &pciBufLen, 0);
CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkSpeed, &propType, (PBYTE) &entry->maxLinkSpeed, &bufLen, 0);
if (ret == CR_SUCCESS) {
FF_DEBUG("PCIe max GEN: %u", entry->maxLinkSpeed);
} else {
@@ -120,8 +162,8 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
if (entry->maxLinkSpeed != FF_GPU_PCIE_SPEED_UNSET) {
pciBufLen = sizeof(entry->maxLinkWidth);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkWidth, &propType, (PBYTE) &entry->maxLinkWidth, &pciBufLen, 0);
bufLen = sizeof(entry->maxLinkWidth);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_MaxLinkWidth, &propType, (PBYTE) &entry->maxLinkWidth, &bufLen, 0);
if (ret == CR_SUCCESS) {
FF_DEBUG("PCIe max link width: %u", entry->maxLinkWidth);
} else {
@@ -129,8 +171,8 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
}
pciBufLen = sizeof(entry->currentLinkSpeed);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkSpeed, &propType, (PBYTE) &entry->currentLinkSpeed, &pciBufLen, 0);
bufLen = sizeof(entry->currentLinkSpeed);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkSpeed, &propType, (PBYTE) &entry->currentLinkSpeed, &bufLen, 0);
if (ret == CR_SUCCESS) {
FF_DEBUG("PCIe GEN: %u", entry->currentLinkSpeed);
} else {
@@ -138,8 +180,8 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
if (entry->currentLinkSpeed != FF_GPU_PCIE_SPEED_UNSET) {
pciBufLen = sizeof(entry->currentLinkWidth);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkWidth, &propType, (PBYTE) &entry->currentLinkWidth, &pciBufLen, 0);
bufLen = sizeof(entry->currentLinkWidth);
ret = CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_PciDevice_CurrentLinkWidth, &propType, (PBYTE) &entry->currentLinkWidth, &bufLen, 0);
if (ret == CR_SUCCESS) {
FF_DEBUG("PCIe current link width: %u", entry->currentLinkWidth);
} else {
@@ -151,12 +193,31 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
FF_LIST_FOR_EACH (CacheEntry, entry, deviceIdsCache) {
if (gpu->deviceId == entry->adapterAddress) {
FF_DEBUG("Cache hit for adapter address: %08llX", gpu->deviceId);
if (luid == entry->luid || entry->pciAddr == gpu->deviceId) {
FF_DEBUG("Cache hit for adapter LUID: %08llX", luid);
if (outDeviceIds->VendorID == -1u) {
*outDeviceIds = entry->deviceIds;
}
if (gpu->vendor.length == 0) {
if (entry->deviceIds.VendorID != -1u) {
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(entry->deviceIds.VendorID));
}
if (gpu->vendor.length == 0 && entry->vendor.length > 0) {
ffStrbufDestroy(&gpu->vendor);
ffStrbufInitMove(&gpu->vendor, &entry->vendor);
}
} else if (gpu->name.length == 0 && entry->deviceIds.VendorID == -1u) {
// Some Indirect display adapters reports fake Device IDs
ffStrbufDestroy(&gpu->vendor);
ffStrbufInitMove(&gpu->vendor, &entry->vendor);
}
if (gpu->name.length == 0) {
ffStrbufDestroy(&gpu->name);
ffStrbufInitMove(&gpu->name, &entry->name);
}
if (queryPcieGen) {
gpu->psMax.gen = (uint16_t) entry->maxLinkSpeed;
gpu->psMax.lanes = (uint16_t) entry->maxLinkWidth;
@@ -167,37 +228,10 @@ static bool queryPciDeviceInfo(FFGPUResult* gpu, D3DKMT_DEVICE_IDS* outDeviceIds
}
}
FF_DEBUG("Cache miss for adapter address: %08llX", gpu->deviceId);
FF_DEBUG("Cache miss for adapter LUID: %08llX", luid);
return false;
}
static bool queryVendorNameViaRegistry(FFstrbuf* vendor, D3DKMT_HANDLE hAdapter) {
// `KMTQAITYPE_QUERY_ADAPTER_UNIQUE_GUID` reports the GUID value used by the adapter's registry key (DirectX and Video)
GUID guid;
NTSTATUS status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = hAdapter,
.Type = KMTQAITYPE_QUERY_ADAPTER_UNIQUE_GUID,
.pPrivateDriverData = &guid,
.PrivateDriverDataSize = sizeof(guid),
});
if (!NT_SUCCESS(status)) {
FF_DEBUG("Failed to query adapter unique GUID: %s", ffDebugNtStatus(status));
return false;
}
wchar_t path[PATH_MAX];
swprintf(path, ARRAY_SIZE(path), L"SYSTEM\\CurrentControlSet\\Control\\Video\\{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\\0000", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
FF_DEBUG("Querying registry: HKEY_LOCAL_MACHINE\\%ls\\ProviderName", path);
FF_AUTO_CLOSE_FD HANDLE key = nullptr;
if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, path, &key, nullptr)) {
return false;
}
return ffRegReadStrbuf(key, L"ProviderName", vendor, nullptr);
}
#else
#include <unistd.h>
#include <fcntl.h>
@@ -301,6 +335,8 @@ ffGPUDetectWsl2
goto close_adapter;
}
uint64_t luid = ((uint64_t) adapter->AdapterLuid.HighPart << 32) | (uint64_t) adapter->AdapterLuid.LowPart;
FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
ffStrbufInit(&gpu->vendor);
ffStrbufInit(&gpu->name);
@@ -321,7 +357,7 @@ ffGPUDetectWsl2
: FF_GPU_TYPE_UNKNOWN;
gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
D3DKMT_DRIVERVERSION wddmVersion = KMT_DRIVERVERSION_WDDM_2_0;
D3DKMT_DRIVERVERSION wddmVersion;
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_DRIVERVERSION,
@@ -332,6 +368,7 @@ ffGPUDetectWsl2
ffStrbufSetF(&gpu->platformApi, "WDDM %u.%u", (uint32_t) wddmVersion / 1000, ((uint32_t) wddmVersion % 1000) / 100);
FF_DEBUG("Adapter #%u WDDM version: %u", i, (uint32_t) wddmVersion);
} else {
wddmVersion = KMT_DRIVERVERSION_WDDM_1_3; // Smallest supported WDDM version, used for fallback
ffStrbufSetStatic(&gpu->platformApi, "WDDM");
FF_DEBUG("KMTQAITYPE_DRIVERVERSION query failed for adapter #%u", i);
}
@@ -352,7 +389,7 @@ ffGPUDetectWsl2
adapterAddress.FunctionNumber);
} else {
adapterAddress.BusNumber = -1u;
gpu->deviceId = ffGPUGeneral2Id(((uint64_t) adapter->AdapterLuid.HighPart << 32) | (uint64_t) adapter->AdapterLuid.LowPart);
gpu->deviceId = ffGPUGeneral2Id(luid);
FF_DEBUG("KMTQAITYPE_ADAPTERADDRESS query failed for adapter #%u, fallback to LUID-based deviceId: %s",
i,
ffDebugNtStatus(status));
@@ -376,14 +413,6 @@ ffGPUDetectWsl2
FF_DEBUG("KMTQAITYPE_PHYSICALADAPTERDEVICEIDS query failed for adapter #%u: %s", i, ffDebugNtStatus(status));
}
#if _WIN32 && FF_WIN81_COMPAT
if (adapterAddress.BusNumber != -1u && (deviceIds.DeviceIds.VendorID == -1u || options->driverSpecific)) {
if (queryPciDeviceInfo(gpu, &deviceIds.DeviceIds, options->driverSpecific) && gpu->vendor.length == 0) {
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(deviceIds.DeviceIds.VendorID));
}
}
#endif
D3DKMT_UMD_DRIVER_VERSION umdDriverVersion;
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
@@ -403,63 +432,55 @@ ffGPUDetectWsl2
FF_DEBUG("KMTQAITYPE_UMD_DRIVER_VERSION query failed for adapter #%u: %s", i, ffDebugNtStatus(status));
}
typeof(&ffDetectNvidiaGpuInfo) detectFn;
const char* dllName;
if (options->driverSpecific && getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName)) {
FF_DEBUG("Calling driver-specific detection function for vendor: %s, DLL: %s", gpu->vendor.chars, dllName);
[[maybe_unused]] const char* error = detectFn(
&(FFGpuDriverCondition) {
.type = FF_GPU_DRIVER_CONDITION_TYPE_LUID |
(deviceIds.DeviceIds.VendorID != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID : 0) |
(adapterAddress.BusNumber != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID : 0),
.pciDeviceId = {
.deviceId = deviceIds.DeviceIds.DeviceID,
.vendorId = deviceIds.DeviceIds.VendorID,
.subSystemId = deviceIds.DeviceIds.SubSystemID,
.revId = deviceIds.DeviceIds.RevisionID,
},
.pciBusId = {
.domain = 0,
.bus = adapterAddress.BusNumber,
.device = adapterAddress.DeviceNumber,
.func = adapterAddress.FunctionNumber,
},
.luid = ((uint64_t) adapter->AdapterLuid.HighPart << 32) | (uint64_t) adapter->AdapterLuid.LowPart,
},
(FFGpuDriverResult) {
.index = &gpu->index,
.temp = options->temp ? &gpu->temperature : nullptr,
.memory = options->driverSpecific ? &gpu->dedicated : nullptr,
.sharedMemory = options->driverSpecific ? &gpu->shared : nullptr,
.memoryType = options->driverSpecific ? &gpu->memoryType : nullptr,
.coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : nullptr,
.coreUsage = options->driverSpecific ? &gpu->coreUsage : nullptr,
.type = &gpu->type,
.frequency = options->driverSpecific ? &gpu->frequency : nullptr,
.name = &gpu->name,
.psCurr = options->driverSpecific ? &gpu->psCurr : nullptr,
.psMax = options->driverSpecific ? &gpu->psMax : nullptr,
},
dllName);
FF_DEBUG("Driver-specific detection completed: %s", error ?: "Success");
} else if (options->driverSpecific) {
FF_DEBUG("No driver-specific detection function found for vendor: %s", gpu->vendor.chars);
bool isIndirectDisplayDevice = gpu->type == FF_GPU_TYPE_UNKNOWN && adapterType.IndirectDisplayDevice;
if (isIndirectDisplayDevice) {
FF_DEBUG("Adapter #%u is an indirect display device", i);
}
#if _WIN32
// Put this after the driver-specific detection, as `getDriverSpecificDetectionFn` never succeeds
if (gpu->vendor.length == 0 && wddmVersion >= KMT_DRIVERVERSION_WDDM_2_4) {
// For non-PCI devices
FF_DEBUG("Attempting to query vendor name via registry for adapter #%u", i);
queryVendorNameViaRegistry(&gpu->vendor, adapter->hAdapter);
}
#if !FF_WIN81_COMPAT
if (adapterAddress.BusNumber != -1u && options->driverSpecific && gpu->pcieSpeed == FF_GPU_PCIE_SPEED_UNSET) {
queryPciDeviceInfo(gpu, &deviceIds.DeviceIds, options->driverSpecific);
}
#endif
#endif
if (!isIndirectDisplayDevice) {
typeof(&ffDetectNvidiaGpuInfo) detectFn;
const char* dllName;
if (options->driverSpecific && getDriverSpecificDetectionFn(gpu->vendor.chars, &detectFn, &dllName)) {
FF_DEBUG("Calling driver-specific detection function for vendor: %s, DLL: %s", gpu->vendor.chars, dllName);
[[maybe_unused]] const char* error = detectFn(
&(FFGpuDriverCondition) {
.type = FF_GPU_DRIVER_CONDITION_TYPE_LUID |
(deviceIds.DeviceIds.VendorID != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID : 0) |
(adapterAddress.BusNumber != -1u ? FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID : 0),
.pciDeviceId = {
.deviceId = deviceIds.DeviceIds.DeviceID,
.vendorId = deviceIds.DeviceIds.VendorID,
.subSystemId = deviceIds.DeviceIds.SubSystemID,
.revId = deviceIds.DeviceIds.RevisionID,
},
.pciBusId = {
.domain = 0,
.bus = adapterAddress.BusNumber,
.device = adapterAddress.DeviceNumber,
.func = adapterAddress.FunctionNumber,
},
.luid = luid,
},
(FFGpuDriverResult) {
.index = &gpu->index,
.temp = options->temp ? &gpu->temperature : nullptr,
.memory = options->driverSpecific ? &gpu->dedicated : nullptr,
.sharedMemory = options->driverSpecific ? &gpu->shared : nullptr,
.memoryType = options->driverSpecific ? &gpu->memoryType : nullptr,
.coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : nullptr,
.coreUsage = options->driverSpecific ? &gpu->coreUsage : nullptr,
.type = &gpu->type,
.frequency = options->driverSpecific ? &gpu->frequency : nullptr,
.name = &gpu->name,
.psCurr = options->driverSpecific ? &gpu->psCurr : nullptr,
.psMax = options->driverSpecific ? &gpu->psMax : nullptr,
},
dllName);
FF_DEBUG("Driver-specific detection completed: %s", error ?: "Success");
} else if (options->driverSpecific) {
FF_DEBUG("No driver-specific detection function found for vendor: %s", gpu->vendor.chars);
}
};
if (gpu->name.length == 0) {
D3DKMT_ADAPTERREGISTRYINFO registryInfo;
@@ -477,148 +498,156 @@ ffGPUDetectWsl2
}
}
if (gpu->dedicated.total == FF_GPU_VMEM_SIZE_UNSET && gpu->shared.total == FF_GPU_VMEM_SIZE_UNSET) {
if (wddmVersion >= KMT_DRIVERVERSION_WDDM_3_1 && options->driverSpecific) {
// Supports memory usage query; requires Windows 11 (22H2) or later
D3DKMT_QUERYSTATISTICS queryStatistics = {
.Type = D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE,
.AdapterLuid = adapter->AdapterLuid,
.QuerySegmentGroupUsage = {
.PhysicalAdapterIndex = 0,
.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL,
},
};
status = D3DKMTQueryStatistics(&queryStatistics);
if (NT_SUCCESS(status)) {
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
gpu->dedicated.used = used;
gpu->dedicated.total = total;
FF_DEBUG("Adapter #%u local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
} else {
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (LOCAL) failed for adapter #%u: %s",
i,
ffDebugNtStatus(status));
}
queryStatistics.QuerySegmentGroupUsage.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL;
status = D3DKMTQueryStatistics(&queryStatistics);
if (NT_SUCCESS(status)) {
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
gpu->shared.used = used;
gpu->shared.total = total;
FF_DEBUG("Adapter #%u non-local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
} else {
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (NON_LOCAL) failed for adapter #%u: %s",
i,
ffDebugNtStatus(status));
}
} else {
// Supports basic segment (total) size query
D3DKMT_SEGMENTSIZEINFO segmentSizeInfo = {};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_GETSEGMENTSIZE,
.pPrivateDriverData = &segmentSizeInfo,
.PrivateDriverDataSize = sizeof(segmentSizeInfo),
});
if (NT_SUCCESS(status)) {
FF_DEBUG("Adapter #%u segment size - DedicatedVideoMemorySize: %" PRIu64
", DedicatedSystemMemorySize: %" PRIu64 ", SharedSystemMemorySize: %" PRIu64,
i,
(uint64_t) segmentSizeInfo.DedicatedVideoMemorySize,
(uint64_t) segmentSizeInfo.DedicatedSystemMemorySize,
(uint64_t) segmentSizeInfo.SharedSystemMemorySize);
gpu->dedicated.total = segmentSizeInfo.DedicatedVideoMemorySize;
gpu->shared.total = segmentSizeInfo.DedicatedSystemMemorySize + segmentSizeInfo.SharedSystemMemorySize;
} else {
FF_DEBUG("Failed to query segment size information for adapter #%u: %s", i, ffDebugNtStatus(status));
}
}
#if _WIN32
if (gpu->name.length == 0 || gpu->vendor.length == 0 || (options->driverSpecific && gpu->pcieSpeed == FF_GPU_PCIE_SPEED_UNSET)) {
queryDeviceInfoCM(gpu, luid, &deviceIds.DeviceIds, options->driverSpecific);
}
#endif
if (wddmVersion >= KMT_DRIVERVERSION_WDDM_2_4) {
if (gpu->frequency == FF_GPU_FREQUENCY_UNSET) {
for (uint32_t nodeIdx = 0;; nodeIdx++) {
D3DKMT_NODEMETADATA nodeMetadata = {
.NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx,
if (!isIndirectDisplayDevice) {
if (gpu->dedicated.total == FF_GPU_VMEM_SIZE_UNSET && gpu->shared.total == FF_GPU_VMEM_SIZE_UNSET) {
if (wddmVersion >= KMT_DRIVERVERSION_WDDM_3_1 && options->driverSpecific) {
// Supports memory usage query; requires Windows 11 (22H2) or later
D3DKMT_QUERYSTATISTICS queryStatistics = {
.Type = D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE,
.AdapterLuid = adapter->AdapterLuid,
.QuerySegmentGroupUsage = {
.PhysicalAdapterIndex = 0,
.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_LOCAL,
},
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_NODEMETADATA,
.pPrivateDriverData = &nodeMetadata,
.PrivateDriverDataSize = sizeof(nodeMetadata),
});
if (!NT_SUCCESS(status)) { break; }
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D) { continue; }
D3DKMT_NODE_PERFDATA nodePerfData = {
.NodeOrdinal = nodeIdx,
.PhysicalAdapterIndex = 0,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_NODEPERFDATA,
.pPrivateDriverData = &nodePerfData,
.PrivateDriverDataSize = sizeof(nodePerfData),
});
status = D3DKMTQueryStatistics(&queryStatistics);
if (NT_SUCCESS(status)) {
if (nodePerfData.MaxFrequency != 0) {
gpu->frequency = (uint32_t) (nodePerfData.MaxFrequency / 1000 / 1000);
FF_DEBUG("Adapter #%u max graphics frequency: %u MHz", i, gpu->frequency);
} else {
FF_DEBUG("Adapter #%u does not report max graphics frequency", i);
}
break;
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
gpu->dedicated.used = used;
gpu->dedicated.total = total;
FF_DEBUG("Adapter #%u local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
} else {
FF_DEBUG("Failed to query node performance data for adapter #%u node #%u: %s",
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (LOCAL) failed for adapter #%u: %s",
i,
nodeIdx,
ffDebugNtStatus(status));
}
}
}
if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET) {
D3DKMT_ADAPTER_PERFDATA adapterPerfData = {
.PhysicalAdapterIndex = 0,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_ADAPTERPERFDATA,
.pPrivateDriverData = &adapterPerfData,
.PrivateDriverDataSize = sizeof(adapterPerfData),
});
if (NT_SUCCESS(status)) {
if (adapterPerfData.Temperature != 0) {
gpu->temperature = adapterPerfData.Temperature / 10.0;
FF_DEBUG("Adapter #%u temperature: %.1f°C", i, gpu->temperature);
queryStatistics.QuerySegmentGroupUsage.SegmentGroup = D3DKMT_MEMORY_SEGMENT_GROUP_NON_LOCAL;
status = D3DKMTQueryStatistics(&queryStatistics);
if (NT_SUCCESS(status)) {
D3DKMT_QUERYSTATISTICS_MEMORY_USAGE* info = &queryStatistics.QueryResult.SegmentGroupUsageInformation;
uint64_t used = info->AllocatedBytes + info->ModifiedBytes + info->StandbyBytes;
uint64_t total = used + info->FreeBytes + info->ZeroBytes;
gpu->shared.used = used;
gpu->shared.total = total;
FF_DEBUG("Adapter #%u non-local memory usage: used=%" PRIu64 " total=%" PRIu64, i, used, total);
} else {
FF_DEBUG("Adapter #%u does not report temperature data", i);
FF_DEBUG("D3DKMT_QUERYSTATISTICS_SEGMENT_GROUP_USAGE (NON_LOCAL) failed for adapter #%u: %s",
i,
ffDebugNtStatus(status));
}
} else {
FF_DEBUG("Failed to query temperature for adapter #%u: %s", i, ffDebugNtStatus(status));
// Supports basic segment (total) size query
D3DKMT_SEGMENTSIZEINFO segmentSizeInfo = {};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_GETSEGMENTSIZE,
.pPrivateDriverData = &segmentSizeInfo,
.PrivateDriverDataSize = sizeof(segmentSizeInfo),
});
if (NT_SUCCESS(status)) {
FF_DEBUG("Adapter #%u segment size - DedicatedVideoMemorySize: %" PRIu64
", DedicatedSystemMemorySize: %" PRIu64 ", SharedSystemMemorySize: %" PRIu64,
i,
(uint64_t) segmentSizeInfo.DedicatedVideoMemorySize,
(uint64_t) segmentSizeInfo.DedicatedSystemMemorySize,
(uint64_t) segmentSizeInfo.SharedSystemMemorySize);
gpu->dedicated.total = segmentSizeInfo.DedicatedVideoMemorySize;
gpu->shared.total = segmentSizeInfo.DedicatedSystemMemorySize + segmentSizeInfo.SharedSystemMemorySize;
} else {
FF_DEBUG("Failed to query segment size information for adapter #%u: %s", i, ffDebugNtStatus(status));
}
}
}
}
if (gpu->type == FF_GPU_TYPE_UNKNOWN) {
if (ffGPUDetectTypeByVendorAndName(gpu)) {
// OK
if (wddmVersion >= KMT_DRIVERVERSION_WDDM_2_4) {
if (gpu->frequency == FF_GPU_FREQUENCY_UNSET) {
for (uint32_t nodeIdx = 0;; nodeIdx++) {
D3DKMT_NODEMETADATA nodeMetadata = {
.NodeOrdinalAndAdapterIndex = (0 << 16) | nodeIdx,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_NODEMETADATA,
.pPrivateDriverData = &nodeMetadata,
.PrivateDriverDataSize = sizeof(nodeMetadata),
});
if (!NT_SUCCESS(status)) { break; }
if (nodeMetadata.NodeData.EngineType != DXGK_ENGINE_TYPE_3D) { continue; }
D3DKMT_NODE_PERFDATA nodePerfData = {
.NodeOrdinal = nodeIdx,
.PhysicalAdapterIndex = 0,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_NODEPERFDATA,
.pPrivateDriverData = &nodePerfData,
.PrivateDriverDataSize = sizeof(nodePerfData),
});
if (NT_SUCCESS(status)) {
if (nodePerfData.MaxFrequency != 0) {
gpu->frequency = (uint32_t) (nodePerfData.MaxFrequency / 1000 / 1000);
FF_DEBUG("Adapter #%u max graphics frequency: %u MHz", i, gpu->frequency);
} else {
FF_DEBUG("Adapter #%u does not report max graphics frequency", i);
}
break;
} else {
FF_DEBUG("Failed to query node performance data for adapter #%u node #%u: %s",
i,
nodeIdx,
ffDebugNtStatus(status));
}
}
}
if (options->temp && gpu->temperature == FF_GPU_TEMP_UNSET) {
D3DKMT_ADAPTER_PERFDATA adapterPerfData = {
.PhysicalAdapterIndex = 0,
};
status = D3DKMTQueryAdapterInfo(&(D3DKMT_QUERYADAPTERINFO) {
.hAdapter = adapter->hAdapter,
.Type = KMTQAITYPE_ADAPTERPERFDATA,
.pPrivateDriverData = &adapterPerfData,
.PrivateDriverDataSize = sizeof(adapterPerfData),
});
if (NT_SUCCESS(status)) {
if (adapterPerfData.Temperature != 0) {
gpu->temperature = adapterPerfData.Temperature / 10.0;
FF_DEBUG("Adapter #%u temperature: %.1f°C", i, gpu->temperature);
} else {
FF_DEBUG("Adapter #%u does not report temperature data", i);
}
} else {
FF_DEBUG("Failed to query temperature for adapter #%u: %s", i, ffDebugNtStatus(status));
}
}
}
#if _WIN32
else if (ffIsWindows10OrGreater()) {
const char* ffGPUDetectTypeWithDXCore(LUID adapterLuid, FFGPUResult * gpu);
[[maybe_unused]] const char* error = ffGPUDetectTypeWithDXCore(adapter->AdapterLuid, gpu);
FF_DEBUG("DXCore GPU type detection result: %s", error ?: "Success");
}
#endif
else {
FF_DEBUG("Unable to determine GPU type by any method for this adapter");
if (gpu->type == FF_GPU_TYPE_UNKNOWN) {
if (ffGPUDetectTypeByVendorAndName(gpu)) {
// OK
}
#if _WIN32
else if (ffIsWindows10OrGreater()) {
const char* ffGPUDetectTypeWithDXCore(LUID adapterLuid, FFGPUResult * gpu);
[[maybe_unused]] const char* error = ffGPUDetectTypeWithDXCore(adapter->AdapterLuid, gpu);
FF_DEBUG("DXCore GPU type detection result: %s", error ?: "Success");
}
#endif
else {
FF_DEBUG("Unable to determine GPU type by any method for this adapter");
}
}
}
@@ -0,0 +1,38 @@
#include "initsystem.h"
#include "common/windows/unicode.h"
#include "common/windows/nt.h"
#include "common/windows/version.h"
#include <ntstatus.h>
#include <winternl.h>
#include <wchar.h>
const char* ffDetectInitSystem(FFInitSystemResult* result) {
// We only need to find the first user process, so 1024 entries should be enough
SYSTEM_PROCESS_INFORMATION buffer[1024] = {};
ULONG size = sizeof(buffer);
NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, buffer, size, &size);
if (status != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(status)) {
return "NtQuerySystemInformation(SystemProcessInformation) failed";
}
for (SYSTEM_PROCESS_INFORMATION* ptr = buffer; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
assert(ptr >= buffer && (uint8_t*) ptr < (uint8_t*) buffer + sizeof(buffer));
uint16_t len = ptr->ImageName.Length / sizeof(*ptr->ImageName.Buffer);
if (ptr->InheritedFromUniqueProcessId == (HANDLE)(uintptr_t) 4 /* System */ &&
len > 4 && _wcsnicmp(ptr->ImageName.Buffer + len - 4, L".exe", 4) == 0) { // smss.exe
result->pid = (uint32_t)(uintptr_t) ptr->UniqueProcessId;
// We have no permission to open the process for querying the full information
wchar_t exePath[MAX_PATH];
_snwprintf(exePath, ARRAY_SIZE(exePath), L"%ls\\system32\\%.*ls", (const wchar_t*) SharedUserData->NtSystemRoot, len, ptr->ImageName.Buffer);
ffGetFileVersion(exePath, NULL, &result->version);
ffStrbufSetWS(&result->exe, exePath);
ffStrbufSetNWS(&result->name, len - 4, ptr->ImageName.Buffer);
return nullptr;
}
// The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
if (!ptr->NextEntryOffset) {
return "Could not find init system process";
}
}
}
+19 -30
View File
@@ -7,48 +7,37 @@ extern "C" {
#endif
template <uint32_t Major, uint32_t Minor>
class version_t {
constexpr static auto buflen() noexcept {
unsigned int len = 2; // "."
if (Major == 0) {
struct version_t {
static constexpr uint32_t digits(uint32_t n) noexcept {
uint32_t len = 1;
for (auto temp = n / 10; temp; temp /= 10) {
len++;
} else {
for (auto n = Major; n; len++, n /= 10);
}
if (Minor == 0) {
len++;
} else {
for (auto n = Minor; n; len++, n /= 10);
}
return len;
}
char buf[buflen()] = {};
char buf[digits(Major) + digits(Minor) + 2];
public:
constexpr version_t() noexcept {
auto ptr = buf + buflen();
auto ptr = buf + sizeof(buf);
*--ptr = '\0';
if (Minor == 0) {
*--ptr = '0';
} else {
for (auto n = Minor; n; n /= 10) {
*--ptr = "0123456789"[n % 10];
auto append = [&](uint32_t n) noexcept {
if (n == 0) {
*--ptr = '0';
} else {
for (; n; n /= 10) {
*--ptr = static_cast<char>('0' + n % 10);
}
}
}
};
append(Minor);
*--ptr = '.';
if (Major == 0) {
*--ptr = '0';
} else {
for (auto n = Major; n; n /= 10) {
*--ptr = "0123456789"[n % 10];
}
}
append(Major);
}
constexpr operator const char*() const {
constexpr operator const char*() const noexcept {
return buf;
}
};
@@ -63,6 +52,6 @@ extern "C" const char* ffDetectLibc(FFLibcResult* result) {
result->name = "msvcrt";
#endif
result->version = version<(__MSVCRT_VERSION__ >> 8), (__MSVCRT_VERSION__ & 8)>;
result->version = version<(__MSVCRT_VERSION__ >> 8), (__MSVCRT_VERSION__ & 0xFF)>;
return nullptr;
}
+1 -1
View File
@@ -184,7 +184,7 @@ const char* ffOpenGLDetectByEGL(FFOpenGLResult* result) {
}
if (ffeglGetPlatformDisplay) {
FF_DEBUG("Trying eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA)");
display = ffeglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, nullptr);
display = ffeglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, nullptr /*EGL_DEFAULT_DISPLAY*/, nullptr);
FF_DEBUG("eglGetPlatformDisplay() %s", display == EGL_NO_DISPLAY ? "failed" : "succeeded");
} else {
FF_DEBUG("eglGetPlatformDisplay is unavailable, falling back to eglGetDisplay");
+2 -1
View File
@@ -92,7 +92,8 @@ static bool parseOsRelease(const char* fileName, FFOSResult* result) {
}
// xdgConfigDirs contains plasma only
if (ffPathExists("/var/lib/dpkg/info/ubuntustudio-desktop.list", FF_PATHTYPE_FILE)) {
// `ubuntustudio-desktop-core` is installed on both the full and core installations
if (ffPathExists("/var/lib/dpkg/info/ubuntustudio-desktop-core.list", FF_PATHTYPE_FILE)) {
ffStrbufSetStatic(&result->name, "Ubuntu Studio");
ffStrbufSetStatic(&result->id, "ubuntu-studio");
ffStrbufSetStatic(&result->idLike, "ubuntu");
+2 -3
View File
@@ -34,9 +34,8 @@ void ffPreparePublicIp(FFPublicIPOptions* options) {
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
if (pathStartIndex != host.length) {
ffStrbufAppendNS(&path, pathStartIndex, host.chars + (host.length - pathStartIndex));
host.length = pathStartIndex;
host.chars[pathStartIndex] = '\0';
ffStrbufAppendNS(&path, host.length - pathStartIndex, host.chars + pathStartIndex);
ffStrbufSubstrBefore(&host, pathStartIndex);
}
*status = ffNetworkingSendHttpRequest(state, host.chars, path.length == 0 ? "/" : path.chars, nullptr);
+75 -46
View File
@@ -3,7 +3,6 @@
#include "common/properties.h"
#include "common/processing.h"
#include "common/debug.h"
#include "common/strutil.h"
#include "detection/terminalshell/terminalshell.h"
static void detectAlacritty(FFTerminalFontResult* terminalFont) {
@@ -19,6 +18,7 @@ static void detectAlacritty(FFTerminalFontResult* terminalFont) {
do {
FFpropquery fontQueryToml[] = {
{ "normal =", &fontNormal },
{ "family =", &fontFamily },
{ "size =", &fontSize },
};
@@ -35,6 +35,8 @@ static void detectAlacritty(FFTerminalFontResult* terminalFont) {
} while (false);
if (fontNormal.length > 0) {
ffStrbufClear(&fontFamily); // If both `normal` and `family` are specified, `normal` takes precedence
// { family = "Fira Code", style = "Medium" }
ffStrbufTrimSpace(&fontNormal);
ffStrbufTrimRight(&fontNormal, '}');
@@ -67,14 +69,17 @@ static void detectAlacritty(FFTerminalFontResult* terminalFont) {
ffFontInitMoveValues(&terminalFont->font, &fontFamily, &fontSize, &fontStyle);
}
static bool parseGhosttyConfig(FFstrbuf* path, FFstrbuf* fontName, FFstrbuf* fontNameFallback, FFstrbuf* fontSize) {
static void parseGhosttyConfig(const FFstrbuf* path, FFstrbuf* fontName, FFstrbuf* fontNameFallback, FFstrbuf* fontSize, FFlist* configFiles /* list of FFstrbuf */) {
// Maximum number of `config-file` directives to follow, guarding against runaway includes
enum { FF_GHOSTTY_MAX_CONFIG_FILES = 16 };
FF_DEBUG("parsing config: %s", path->chars);
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY temp = ffStrbufCreate();
if (!ffAppendFileBuffer(path->chars, &buffer)) {
FF_DEBUG("cannot read config: %s", path->chars);
return false;
return;
}
char* line = nullptr;
@@ -82,70 +87,94 @@ static bool parseGhosttyConfig(FFstrbuf* path, FFstrbuf* fontName, FFstrbuf* fon
while (ffStrbufGetline(&line, &len, &buffer)) {
if (ffParsePropLine(line, "font-family =", &temp)) {
FF_DEBUG("found font-family='%s' in %s", temp.chars, path->chars);
// Latter overrides former; former becomes the fallback font
if (fontName->length > 0) {
ffStrbufDestroy(fontNameFallback);
ffStrbufInitMove(fontNameFallback, fontName);
}
ffStrbufDestroy(fontName);
ffStrbufInitMove(fontName, &temp);
} else if (ffParsePropLine(line, "font-size =", fontSize)) {
} else if (ffParsePropLine(line, "font-size =", &temp)) {
FF_DEBUG("found font-size='%s' in %s", temp.chars, path->chars);
// Latter overrides former
ffStrbufDestroy(fontSize);
ffStrbufInitMove(fontSize, &temp);
} else if (ffParsePropLine(line, "config-file =", &temp)) {
// Doc: https://ghostty.org/docs/config/reference#config-file
// A leading `?` suppresses errors if the file doesn't exist; missing files are skipped here either way
ffStrbufTrimLeft(&temp, '?');
ffStrbufTrim(&temp, '"');
if (temp.length == 0) {
continue;
}
if (!ffStrbufStartsWithC(&temp, '/')) {
// Relative paths are relative to the file containing the `config-file` directive
FF_STRBUF_AUTO_DESTROY absolutePath = ffStrbufCreateCopy(path);
ffStrbufSubstrBeforeLastC(&absolutePath, '/');
ffStrbufAppendC(&absolutePath, '/');
ffStrbufAppend(&absolutePath, &temp);
ffStrbufDestroy(&temp);
ffStrbufInitMove(&temp, &absolutePath);
}
// Each unique file is only loaded once, which also prevents include cycles
bool loaded = ffStrbufEqual(&temp, path);
if (!loaded) {
FF_LIST_FOR_EACH(FFstrbuf, it, *configFiles) {
if (ffStrbufEqual(it, &temp)) {
loaded = true;
break;
}
}
}
if (loaded) {
FF_DEBUG("config-file '%s' was already loaded, skipping to avoid cycles", temp.chars);
} else if (configFiles->length >= FF_GHOSTTY_MAX_CONFIG_FILES) {
FF_DEBUG("too many config-file directives, ignoring '%s'", temp.chars);
} else {
FF_DEBUG("found config-file='%s' in %s", temp.chars, path->chars);
ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *configFiles), &temp);
}
ffStrbufClear(&temp);
}
}
return true;
}
static void detectGhostty(const FFstrbuf* exe, FFTerminalFontResult* terminalFont, const char* configPathMac, const char* configPathUnix) {
static void detectGhostty(FFTerminalFontResult* terminalFont, [[maybe_unused]] const char* configPathMac, const char* configPathUnix) {
FF_DEBUG("detectGhostty: start");
FF_STRBUF_AUTO_DESTROY configPath = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontNameFallback = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate();
FF_LIST_AUTO_DESTROY configFiles = ffListCreate(); // list of FFstrbuf
// Ghostty loads the XDG config first, then (on macOS) the Application Support config,
// so values in the latter override the former
if (instance.state.platform.configDirs.length > 0) {
ffStrbufSet(&configPath, FF_LIST_FIRST(FFstrbuf, instance.state.platform.configDirs));
ffStrbufAppendS(&configPath, configPathUnix); // ghostty/config
parseGhosttyConfig(&configPath, &fontName, &fontNameFallback, &fontSize, &configFiles);
}
if (configPathMac && configPathUnix) {
#if __APPLE__
ffStrbufSet(&configPath, &instance.state.platform.homeDir);
ffStrbufAppendS(&configPath, "Library/Application Support/");
ffStrbufAppendS(&configPath, configPathMac); // com.mitchellh.ghostty/config
parseGhosttyConfig(&configPath, &fontName, &fontNameFallback, &fontSize);
ffStrbufSet(&configPath, &instance.state.platform.homeDir);
ffStrbufAppendS(&configPath, "Library/Application Support/");
ffStrbufAppendS(&configPath, configPathMac); // com.mitchellh.ghostty/config
parseGhosttyConfig(&configPath, &fontName, &fontNameFallback, &fontSize, &configFiles);
#endif
if (instance.state.platform.configDirs.length > 0) {
ffStrbufSet(&configPath, FF_LIST_FIRST(FFstrbuf, instance.state.platform.configDirs));
ffStrbufAppendS(&configPath, configPathUnix); // ghostty/config
parseGhosttyConfig(&configPath, &fontName, &fontNameFallback, &fontSize);
}
} else {
// Try ghostty +show-config first
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
const char* error = ffProcessAppendStdOut(&buffer, (char* const[]){
exe->chars,
"+show-config",
nullptr,
});
if (error == nullptr) {
char* line = nullptr;
size_t len = 0;
while (ffStrbufGetline(&line, &len, &buffer)) {
if (ffStrStartsWith(line, "font-family = ")) {
FF_DEBUG("found %s", line);
if (fontName.length > 0) {
ffStrbufDestroy(&fontNameFallback);
ffStrbufInitMove(&fontNameFallback, &fontName);
}
ffStrbufSetNS(&fontName, (uint32_t) (len - strlen("font-family = ")), line + strlen("font-family = "));
} else if (ffStrStartsWith(line, "font-size = ")) {
FF_DEBUG("found %s", line);
// `ghostty +show-config` reports only one font size even if the config has multiple font sizes
ffStrbufSetNS(&fontSize, (uint32_t) (len - strlen("font-size = ")), line + strlen("font-size = "));
}
}
} else {
FF_DEBUG("`ghostty +show-config` failed: %s", error);
}
// Files referenced by `config-file` don't take effect until the whole configuration is loaded,
// so they are parsed after all root config files, in the order they were found
for (uint32_t i = 0; i < configFiles.length; ++i) {
// Copy the path as parseGhosttyConfig may grow the list, invalidating pointers into it
ffStrbufSet(&configPath, FF_LIST_GET(FFstrbuf, configFiles, i));
parseGhosttyConfig(&configPath, &fontName, &fontNameFallback, &fontSize, &configFiles);
}
FF_LIST_FOR_EACH(FFstrbuf, it, configFiles) {
ffStrbufDestroy(it);
}
if (fontName.length == 0) {
@@ -365,9 +394,9 @@ static bool detectTerminalFontCommon(const FFTerminalResult* terminal, FFTermina
} else if (ffStrbufStartsWithIgnCaseS(&terminal->processName, "contour")) {
detectContour(&terminal->exe, terminalFont);
} else if (ffStrbufStartsWithIgnCaseS(&terminal->processName, "ghostty")) {
detectGhostty(&terminal->exe, terminalFont, nullptr, nullptr);
detectGhostty(terminalFont, "com.mitchellh.ghostty/config", "ghostty/config");
} else if (ffStrbufStartsWithIgnCaseS(&terminal->processName, "Muxy")) {
detectGhostty(&terminal->exe, terminalFont, "Muxy/ghostty.conf", "muxy/ghostty.conf");
detectGhostty(terminalFont, "Muxy/ghostty.conf", "muxy/ghostty.conf");
} else if (ffStrbufStartsWithIgnCaseS(&terminal->processName, "rio")) {
detectRio(terminalFont);
}
+7 -1
View File
@@ -90,7 +90,13 @@ static bool getShellVersionFish(FFstrbuf* exe, FFstrbuf* version) {
return false;
}
uint32_t index = ffStrbufFirstIndexC(version, ' '); // skip "fish,"
index = ffStrbufNextIndexC(version, index + 1, ' '); // skip "version"
while (index + 1 < version->length && !ffCharIsDigit(version->chars[index + 1])) {
index = ffStrbufNextIndexC(version, index + 1, ' '); // skip "version"
}
if (index + 1 >= version->length) {
return false;
}
ffStrbufSubstrAfter(version, index);
ffStrbufSubstrBeforeFirstC(version, ' ');
return true;
+22 -15
View File
@@ -1,27 +1,34 @@
#include "theme.h"
#include "detection/os/os.h"
#include "detection/displayserver/displayserver.h"
const char* ffDetectTheme(FFThemeResult* result) {
const FFOSResult* os = ffDetectOS();
const FFDisplayServerResult* displayServer = ffConnectDisplayServer();
if (ffStrbufEqualS(&displayServer->wmProcessName, "dwm.exe")) {
uint32_t ver = (uint32_t) ffStrbufToUInt(&os->version, 0);
if (ver > 1000) {
// Windows Server
if (ver >= 2016) {
ffStrbufSetStatic(&result->theme1, "Fluent");
} else if (ver >= 2012) {
ffStrbufSetStatic(&result->theme1, "Metro");
if (ver > 1000) {
// Windows Server
if (ver >= 2016) {
ffStrbufSetStatic(&result->theme1, "Fluent");
} else if (ver >= 2012) {
ffStrbufSetStatic(&result->theme1, "Metro");
} else {
ffStrbufSetStatic(&result->theme1, "Aero");
}
} else {
ffStrbufSetStatic(&result->theme1, "Aero");
}
} else {
if (ver >= 10) {
ffStrbufSetStatic(&result->theme1, "Fluent");
} else if (ver >= 8) {
ffStrbufSetStatic(&result->theme1, "Metro");
} else {
ffStrbufSetStatic(&result->theme1, "Aero");
if (ver >= 10) {
ffStrbufSetStatic(&result->theme1, "Fluent");
} else if (ver >= 8) {
ffStrbufSetStatic(&result->theme1, "Metro");
} else {
ffStrbufSetStatic(&result->theme1, "Aero");
}
}
} else if (ffStrbufEqualS(&displayServer->wmProcessName, "explorer.exe")) {
ffStrbufSetStatic(&result->theme1, "Basic");
}
return nullptr;
}
+62 -16
View File
@@ -3,6 +3,7 @@
#include "detection/version/version.h"
#include "logo/logo.h"
#include "common/commandoption.h"
#include "common/genconfig.h"
#include "common/init.h"
#include "common/io.h"
#include "common/jsonconfig.h"
@@ -15,6 +16,13 @@
#include <ctype.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#else
#include <windows.h>
#include "common/windows/nt.h"
#endif
[[gnu::cold]]
static void printCommandFormatHelpJson(void) {
yyjson_mut_doc* doc = yyjson_mut_doc_new(nullptr);
@@ -432,12 +440,7 @@ static bool parseJsoncFile(FFdata* data, const char* path, yyjson_read_flag flg)
}
[[gnu::cold]]
static void generateConfigFile(FFdata* data, bool force, const char* filePath, bool fullConfig) {
if (data->resultDoc) {
fprintf(stderr, "Error: duplicated `--gen-config` or `--format json` flags found\n");
exit(477);
}
static void setupGenConfigPath(FFdata* data, const char* filePath) {
if (!filePath) {
if (instance.state.platform.configDirs.length == 0) {
fprintf(stderr, "Error: No config directory found to generate config file in. Use --gen-config <path> to specify a path\n");
@@ -451,13 +454,23 @@ static void generateConfigFile(FFdata* data, bool force, const char* filePath, b
} else {
ffStrbufSetS(&data->genConfigPath, filePath);
}
}
if (!force && ffPathExists(data->genConfigPath.chars, FF_PATHTYPE_ANY)) {
fprintf(stderr, "Error: file `%s` exists. Use `--gen-config%s-force` to overwrite\n", data->genConfigPath.chars, fullConfig ? "-full" : "");
[[gnu::cold]]
static void generateConfigFile(FFdata* data, const char* filePath) {
if (data->resultDoc) {
fprintf(stderr, "Error: duplicated `--gen-config` or `--format json` flags found\n");
exit(477);
}
data->docType = fullConfig ? FF_RESULT_DOC_TYPE_CONFIG_FULL : FF_RESULT_DOC_TYPE_CONFIG;
setupGenConfigPath(data, filePath);
if (ffPathExists(data->genConfigPath.chars, FF_PATHTYPE_ANY)) {
fprintf(stderr, "Error: file `%s` exists. Please remove it before generating a new one\n", data->genConfigPath.chars);
exit(477);
}
data->docType = FF_RESULT_DOC_TYPE_CONFIG;
data->resultDoc = yyjson_mut_doc_new(nullptr);
}
@@ -586,6 +599,19 @@ static void enableJsonOutput(FFdata* data) {
yyjson_mut_doc_set_root(data->resultDoc, yyjson_mut_arr(data->resultDoc));
}
static void genConfigCommon(FFdata* data, const char* value) {
if (!getenv("NO_COLOR") && isatty(STDOUT_FILENO) && isatty(STDIN_FILENO)
#ifdef _WIN32
&& ffIsWindows10OrGreater()
#endif
) {
data->genConfigInteractive = true;
setupGenConfigPath(data, value);
} else {
generateConfigFile(data, value);
}
}
static void parseCommand(FFdata* data, char* key, char* value) {
if (ffStrEqualsIgnCase(key, "-h") || ffStrEqualsIgnCase(key, "--help")) {
printCommandHelp(value);
@@ -647,13 +673,7 @@ static void parseCommand(FFdata* data, char* key, char* value) {
exit(0);
} else if (ffStrEqualsIgnCase(key, "--gen-config")) {
generateConfigFile(data, false, value, false);
} else if (ffStrEqualsIgnCase(key, "--gen-config-force")) {
generateConfigFile(data, true, value, false);
} else if (ffStrEqualsIgnCase(key, "--gen-config-full")) {
generateConfigFile(data, false, value, true);
} else if (ffStrEqualsIgnCase(key, "--gen-config-full-force")) {
generateConfigFile(data, true, value, true);
genConfigCommon(data, value);
} else if (ffStrEqualsIgnCase(key, "-c") || ffStrEqualsIgnCase(key, "--config")) {
optionParseConfigFile(data, key, value);
} else if (ffStrEqualsIgnCase(key, "-j") || ffStrEqualsIgnCase(key, "--json")) {
@@ -670,6 +690,12 @@ static void parseCommand(FFdata* data, char* key, char* value) {
}
} else if (ffStrEqualsIgnCase(key, "--dynamic-interval")) {
instance.state.dynamicInterval = ffOptionParseUInt32(key, value); // seconds to milliseconds
} else if (ffStrEqualsIgnCase(key, "-w") || ffStrEqualsIgnCase(key, "--watch")) {
if (value == nullptr) {
instance.state.dynamicInterval = 1000; // default to 1 second if no value is provided
} else {
instance.state.dynamicInterval = ffOptionParseUInt32(key, value) * 1000; // seconds to milliseconds
}
} else {
return;
}
@@ -822,6 +848,14 @@ static void writeConfigFile(FFdata* data) {
ffOptionsGenerateLogoJsonConfig(data, &instance.config.logo);
ffOptionsGenerateDisplayJsonConfig(data, &instance.config.display);
ffOptionsGenerateGeneralJsonConfig(data, &instance.config.general);
} else if (data->genConfigInteractive) {
if (instance.config.logo.type == FF_LOGO_TYPE_NONE) {
yyjson_mut_obj_add_null(doc, root, "logo");
} else if (instance.config.logo.type == FF_LOGO_TYPE_SMALL) {
yyjson_mut_val* logo = yyjson_mut_obj(doc);
yyjson_mut_obj_add_str(doc, logo, "type", "small");
yyjson_mut_obj_add_val(doc, root, "logo", logo);
}
}
ffMigrateCommandOptionToJsonc(data);
@@ -871,6 +905,18 @@ int main(int argc, char** argv) {
if (__builtin_expect(data.genConfigPath.length == 0, true)) {
run(&data);
} else {
// If we don't have a custom structure, use the default one
if (data.structure.length == 0) {
ffStrbufSetS(&data.structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
}
if (data.genConfigInteractive && !ffGenConfigInteractive(&data)) {
// User cancelled the interactive config generation
ffStrbufDestroy(&data.structure);
ffStrbufDestroy(&data.structureDisabled);
yyjson_doc_free(data.configDoc);
ffStrbufDestroy(&data.genConfigPath);
return 0;
}
writeConfigFile(&data);
}
-11
View File
@@ -65,17 +65,6 @@ static const FFlogo E[] = {
},
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_EMPEROROS
// EmperorOS
{
.names = { "Emperor" },
.lines = FASTFETCH_DATATEXT_LOGO_EMPEROROS,
.colors = {
FF_COLOR_FG_YELLOW,
FF_COLOR_FG_DEFAULT,
},
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_ENOS
// EN-OS
{
-13
View File
@@ -1,13 +0,0 @@
!!
!!!!
llllll
llllll
IIIIIIIIII
IIIIIIIIIIIIIIIIIIII
;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;
:;;::;:;::
::::::
,,,,,,
,,,,
""
-13
View File
@@ -283,19 +283,6 @@ static const FFlogo F[] = {
.colorTitle = FF_COLOR_FG_DEFAULT,
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_FURRETO
// Furreto
{
.names = { "Furreto" },
.lines = FASTFETCH_DATATEXT_LOGO_FURRETO,
.colors = {
FF_COLOR_FG_WHITE,
FF_COLOR_FG_LIGHT_MAGENTA,
},
.colorKeys = FF_COLOR_FG_CYAN,
.colorTitle = FF_COLOR_FG_CYAN,
},
#endif
// LAST
{},
};
-22
View File
@@ -1,22 +0,0 @@
.$2xOOko $1.$2odd,
oX$1WW$2KOOOO. 'ON$1WW$20kkk.
$1.$2k0XKOOOOOOcOON$1W$2NOOOOO.
xOOOOOOOOOkkOOOOOOOOO;
$1.$2O0OkkocxO000000kcdk0OO0OOkx
k$1W$1M$2Xkkkkloxkkkx; :dxxxxddc...
'kO0OOOOOkc .cl:..kk0KK0Okc
;kOOO0000xd. dO00000Oo .xkO$1NMM$2XOOOO
.dddxkOOOkddc.kKN$1WW$2N000000l.ddk0000OOOO.
'dd:;ddddd;.dK$1MMMW$2K00KKK0O::ddxkO00Oko
.okxkOKK0kkOO00KKOxxlodddddddl
.00OOkkkkkkkkOOO00OOOO0O; .dddl
'kkkkkxxkkkkkkkOOkxdxkxxddd.
cddddddddxxkkkkkxddddddddddo
'ddddddodddddddddddddddddddc
$1.$2ddddddodddddddddodddddddc
.odddo.
$1.$2kOOkkk;
lkK$1WN$2kkkxc
kkxkkkkkkx.
,,..xxx.
-13
View File
@@ -168,19 +168,6 @@ static const FFlogo H[] = {
.colorTitle = FF_COLOR_FG_GREEN,
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_HYPROS
// HyprOS
{
.names = { "hypros" },
.lines = FASTFETCH_DATATEXT_LOGO_HYPROS,
.colors = {
FF_COLOR_FG_RED,
FF_COLOR_FG_YELLOW,
FF_COLOR_FG_CYAN,
FF_COLOR_FG_BLUE,
},
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_HYPERBOLA
// Hyperbola
{
-17
View File
@@ -1,17 +0,0 @@
___
,adZZEEEE#&$2>=x.
$1,zAP*~'$4_,-$2'~*VM$2N&x.
$1,%&P^`$4<$3,.$4<<$2,-===--.N>x
$1.%M7$4//$3,%^$2,x<3#$13EEbo$2<&>&b
$1&#/$4/$3.<^$4/$2x<>^$4-.`$1`+&WW$2<&N&;
$1/#/$4//$34$4//$2/W/ $4^+.`$1`###$2NM\
$1##'$4|$3.l$4|$2,&/ $4`.',$1I#I$2HI#
$1#I$4||$3`I$4|$2(#( $3)`'$1)##$2H~^
$1@\$4|||$3\$4\$2`X\ $3///$1,##%V$3'/
$4\\\\\\$3Y,$2*@b, $3.-+//$1/&#%#/$3,'
$4`\\\\$2,.$4\$3<$2`*$3^`x<$1,z<#&#x"$3,'
$3`x<<$2`Xx,$3`<_`$1+{##&@P^$4'>$3'
$3`<_<<$2^<\-.$3`*`>$1<^'$4,-'
$3`<_=-$2^\Xx$1XX\.$3+<.
$3`^<_-$2^<Xx$1#X\x$3+'-.
$3`"^^$2+*$1*``` $3```
-28
View File
@@ -151,34 +151,6 @@ static const FFlogo M[] = {
.colorTitle = FF_COLOR_FG_DEFAULT,
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_MAGIX
// Magix
{
.names = { "Magix", "MagixOS" },
.lines = FASTFETCH_DATATEXT_LOGO_MAGIX,
.colors = {
FF_COLOR_FG_LIGHT_MAGENTA,
FF_COLOR_FG_CYAN,
},
.colorKeys = FF_COLOR_FG_CYAN,
.colorTitle = FF_COLOR_FG_LIGHT_MAGENTA,
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_MAGPIEOS
// MagpieOS
{
.names = { "MagpieOS", "Magpie" },
.lines = FASTFETCH_DATATEXT_LOGO_MAGPIEOS,
.colors = {
FF_COLOR_FG_GREEN,
FF_COLOR_FG_RED,
FF_COLOR_FG_YELLOW,
FF_COLOR_FG_MAGENTA,
},
.colorKeys = FF_COLOR_FG_GREEN,
.colorTitle = FF_COLOR_FG_RED,
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_MANDRIVA
// Mandriva
{
-19
View File
@@ -1,19 +0,0 @@
$2@
@@--=====@@
@@--==@@ @@====+@
@-@@ @==@
@=@
@=@$1 @=@ @-==== @=@$2
@=@$1 @-===@==++@===+@$2
@=@$1 @--====@@=====+@$2
-=@$1 @--==========++@$2
==$1 @--==========++@$2 @=@
@==$1 @--=======@==++@$2 @=+@
@==$1 @-==========++$2 @=@
@==$1 @-=======@=%$2 @=@
@==$1 @@@@@@$2 @=@
@====@@@ @@===+%
@@=====@@==++++@@
=#@=@
@==@++@
@@@
-20
View File
@@ -1,20 +0,0 @@
;00000 :000Ol
.x00kk00: O0kk00k;
l00: :00. o0k :O0k.
.k0k. x$2d$dddd$1k' .d00;
k0k. $2.dddddl $1o00,
o00. $2':cc:. $1d0O
.00l ,00.
l00. d0x
k0O .:k0o
O0k ;dO0000d.
k0O .O0O$2xxxxk$100:
o00. k0O$2dddddd$1occ
'00l x0O$2dddddo$3;..$1
x00. .x00$2kxxd$3:..$1
.O0x .:oxxx$4Okl.$1
.x0d $4,xx,$1
.:o. $4.xd ckd$1
.. $4dxl .xx;
:xxolldxd'
;oxdl.
+11 -11
View File
@@ -121,36 +121,35 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin
ffDurationAppendNum((uint32_t) result->timeRemaining, &timeStr);
}
FF_STRBUF_AUTO_DESTROY statusStr = ffStrbufCreate();
FF_LIST_AUTO_DESTROY status = ffListCreate();
if (result->status & FF_BATTERY_STATUS_AC_CONNECTED) {
ffStrbufAppendS(&statusStr, "AC Connected, ");
ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, status), "AC Connected");
}
if (result->status & FF_BATTERY_STATUS_USB_CONNECTED) {
ffStrbufAppendS(&statusStr, "USB Connected, ");
ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, status), "USB Connected");
}
if (result->status & FF_BATTERY_STATUS_WIRELESS_CONNECTED) {
ffStrbufAppendS(&statusStr, "Wireless Connected, ");
ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, status), "Wireless Connected");
}
if (result->status & FF_BATTERY_STATUS_CHARGING) {
ffStrbufAppendS(&statusStr, "Charging, ");
ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, status), "Charging");
}
if (result->status & FF_BATTERY_STATUS_DISCHARGING) {
ffStrbufAppendS(&statusStr, "Discharging, ");
ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, status), "Discharging");
}
if (result->status & FF_BATTERY_STATUS_CRITICAL) {
ffStrbufAppendS(&statusStr, "Critical, ");
ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, status), "Critical");
}
if (result->status & FF_BATTERY_STATUS_UNKNOWN) {
ffStrbufAppendS(&statusStr, "Unknown, ");
ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, status), "Unknown");
}
ffStrbufSubstrBefore(&statusStr, statusStr.length - 2); // Remove last ", "
FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
FF_ARG(result->manufacturer, "manufacturer"),
FF_ARG(result->modelName, "model-name"),
FF_ARG(result->technology, "technology"),
FF_ARG(capacityNum, "capacity"),
FF_ARG(statusStr, "status"),
FF_ARG(status, "status"),
FF_ARG(tempStr, "temperature"),
FF_ARG(result->cycleCount, "cycle-count"),
FF_ARG(result->serial, "serial"),
@@ -322,5 +321,6 @@ FFModuleBaseInfo ffBatteryModuleInfo = {
{ "Battery time remaining minutes", "time-minutes" },
{ "Battery time remaining seconds", "time-seconds" },
{ "Battery time remaining (formatted)", "time-formatted" },
}))
})),
.defaultOrder = 44,
};
+2 -1
View File
@@ -144,5 +144,6 @@ FFModuleBaseInfo ffBiosModuleInfo = {
{ "BIOS vendor", "vendor" },
{ "BIOS version", "version" },
{ "Firmware type", "type" },
}))
})),
.defaultOrder = 5,
};
+2 -1
View File
@@ -166,5 +166,6 @@ FFModuleBaseInfo ffBluetoothModuleInfo = {
{ "Battery percentage number", "battery-percentage" },
{ "Is connected", "connected" },
{ "Battery percentage bar", "battery-percentage-bar" },
}))
})),
.defaultOrder = 58,
};
+2 -1
View File
@@ -207,5 +207,6 @@ FFModuleBaseInfo ffBluetoothRadioModuleInfo = {
{ "Vendor", "vendor" },
{ "Discoverable", "discoverable" },
{ "Connectable / Pairable", "connectable" },
}))
})),
.defaultOrder = 59,
};
+2 -1
View File
@@ -121,5 +121,6 @@ FFModuleBaseInfo ffBoardModuleInfo = {
{ "Board vendor", "vendor" },
{ "Board version", "version" },
{ "Board serial number", "serial" },
}))
})),
.defaultOrder = 7,
};
+2 -1
View File
@@ -117,5 +117,6 @@ FFModuleBaseInfo ffBootmgrModuleInfo = {
{ "Firmware file name", "firmware-name" },
{ "Is secure boot enabled", "secure-boot" },
{ "Boot order", "order" },
}))
})),
.defaultOrder = 6,
};
+1
View File
@@ -33,4 +33,5 @@ FFModuleBaseInfo ffBreakModuleInfo = {
.destroyOptions = (void*) ffDestroyBreakOptions,
.parseJsonObject = (void*) ffParseBreakJsonObject,
.printModule = (void*) ffPrintBreak,
.defaultOrder = 71,
};
+2 -1
View File
@@ -206,5 +206,6 @@ FFModuleBaseInfo ffBrightnessModuleInfo = {
{ "Current brightness value", "current" },
{ "Screen brightness (percentage bar)", "percentage-bar" },
{ "Is built-in screen", "is-builtin" },
}))
})),
.defaultOrder = 18,
};
+2 -1
View File
@@ -220,5 +220,6 @@ FFModuleBaseInfo ffBtrfsModuleInfo = {
{ "Allocated percentage bar", "allocated-percentage-bar" },
{ "Node size", "node-size" },
{ "Sector size", "sector-size" },
}))
})),
.defaultOrder = 42,
};
+4 -3
View File
@@ -1,7 +1,5 @@
#include "common/printing.h"
#include "common/jsonconfig.h"
#include "common/strutil.h"
#include "detection/libc/libc.h"
#include "detection/camera/camera.h"
#include "modules/camera/camera.h"
@@ -53,6 +51,7 @@ bool ffPrintCamera(FFCameraOptions* options) {
FF_LIST_FOR_EACH (FFCameraResult, dev, result) {
ffStrbufDestroy(&dev->name);
ffStrbufDestroy(&dev->vendor);
ffStrbufDestroy(&dev->id);
ffStrbufDestroy(&dev->colorspace);
}
@@ -99,6 +98,7 @@ bool ffGenerateCameraJsonResult([[maybe_unused]] FFCameraOptions* options, yyjso
FF_LIST_FOR_EACH (FFCameraResult, dev, result) {
ffStrbufDestroy(&dev->name);
ffStrbufDestroy(&dev->vendor);
ffStrbufDestroy(&dev->id);
ffStrbufDestroy(&dev->colorspace);
}
@@ -130,5 +130,6 @@ FFModuleBaseInfo ffCameraModuleInfo = {
{ "Identifier", "id" },
{ "Width (in px)", "width" },
{ "Height (in px)", "height" },
}))
})),
.defaultOrder = 61,
};
+1
View File
@@ -124,4 +124,5 @@ FFModuleBaseInfo ffChassisModuleInfo = {
{ "Chassis version", "version" },
{ "Chassis serial number", "serial" },
})),
.defaultOrder = 8,
};
+2 -1
View File
@@ -264,5 +264,6 @@ FFModuleBaseInfo ffCodecModuleInfo = {
{ "Decoder / Encoder", "direction" },
{ "Compatibility alias of codec types", "types" },
{ "Platform API used for detection", "platform-api" },
}))
})),
.defaultOrder = 37,
};
+1
View File
@@ -302,4 +302,5 @@ FFModuleBaseInfo ffColorsModuleInfo = {
.parseJsonObject = (void*) ffParseColorsJsonObject,
.printModule = (void*) ffPrintColors,
.generateJsonConfig = (void*) ffGenerateColorsJsonConfig,
.defaultOrder = 72,
};
+1 -1
View File
@@ -163,5 +163,5 @@ FFModuleBaseInfo ffCommandModuleInfo = {
.generateJsonConfig = (void*) ffGenerateCommandJsonConfig,
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "Command result", "result" },
}))
})),
};
+2 -1
View File
@@ -276,5 +276,6 @@ FFModuleBaseInfo ffCPUModuleInfo = {
{ "CPU code name", "code-name" },
{ "CPU technology", "technology" },
#endif
}))
})),
.defaultOrder = 33,
};
+2 -1
View File
@@ -234,5 +234,6 @@ FFModuleBaseInfo ffCPUCacheModuleInfo = {
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "Separate result", "result" },
{ "Sum result", "sum" },
}))
})),
.defaultOrder = 34,
};
+2 -1
View File
@@ -198,5 +198,6 @@ FFModuleBaseInfo ffCPUUsageModuleInfo = {
{ "CPU usage (percentage bar, average)", "avg-bar" },
{ "CPU usage (percentage bar, maximum)", "max-bar" },
{ "CPU usage (percentage bar, minimum)", "min-bar" },
}))
})),
.defaultOrder = 35,
};
+1
View File
@@ -112,4 +112,5 @@ FFModuleBaseInfo ffCursorModuleInfo = {
{ "Cursor theme", "theme" },
{ "Cursor size", "size" },
})),
.defaultOrder = 27,
};
+2 -1
View File
@@ -185,5 +185,6 @@ FFModuleBaseInfo ffDateTimeModuleInfo = {
{ "Locale-dependent timezone name or abbreviation", "timezone-name" },
{ "Day in month with leading zero", "day-pretty" },
{ "AM or PM", "am-pm" },
}))
})),
.defaultOrder = 52,
};
+2 -1
View File
@@ -94,5 +94,6 @@ FFModuleBaseInfo ffDEModuleInfo = {
{ "DE process name", "process-name" },
{ "DE pretty name", "pretty-name" },
{ "DE version", "version" },
}))
})),
.defaultOrder = 21,
};
+2 -1
View File
@@ -486,5 +486,6 @@ FFModuleBaseInfo ffDiskModuleInfo = {
{ "Years fraction after creation", "years-fraction" },
{ "Size free", "size-free" },
{ "Size available", "size-available" },
}))
})),
.defaultOrder = 41,
};
+2 -1
View File
@@ -189,5 +189,6 @@ FFModuleBaseInfo ffDiskIOModuleInfo = {
{ "Size of data written [per second] (in bytes)", "bytes-written" },
{ "Number of reads", "read-count" },
{ "Number of writes", "write-count" },
}))
})),
.defaultOrder = 67,
};
+2 -1
View File
@@ -445,5 +445,6 @@ FFModuleBaseInfo ffDisplayModuleInfo = {
{ "Screen preferred height (in pixels)", "preferred-height" },
{ "Screen preferred refresh rate (in Hz)", "preferred-refresh-rate" },
{ "DPI", "dpi" },
}))
})),
.defaultOrder = 17,
};
+2 -1
View File
@@ -144,5 +144,6 @@ FFModuleBaseInfo ffDNSModuleInfo = {
.generateJsonConfig = (void*) ffGenerateDNSJsonConfig,
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "DNS result", "result" },
}))
})),
.defaultOrder = 50,
};
+2 -1
View File
@@ -117,5 +117,6 @@ FFModuleBaseInfo ffEditorModuleInfo = {
{ "Exe name of real path", "exe-name" },
{ "Full path of real path", "path" },
{ "Version", "version" },
}))
})),
.defaultOrder = 16,
};
+2 -1
View File
@@ -109,5 +109,6 @@ FFModuleBaseInfo ffFontModuleInfo = {
{ "Font 3", "font3" },
{ "Font 4", "font4" },
{ "Combined fonts for display", "combined" },
}))
})),
.defaultOrder = 26,
};
+2 -1
View File
@@ -202,5 +202,6 @@ FFModuleBaseInfo ffGamepadModuleInfo = {
{ "Serial number", "serial" },
{ "Battery percentage num", "battery-percentage" },
{ "Battery percentage bar", "battery-percentage-bar" },
}))
})),
.defaultOrder = 62,
};
+1
View File
@@ -511,4 +511,5 @@ FFModuleBaseInfo ffGPUModuleInfo = {
{ "PCIe maximum speed in gen and lanes", "pcie-max-speed" },
{ "PCIe current speed in gen and lanes", "pcie-curr-speed" },
})),
.defaultOrder = 36,
};
+2 -1
View File
@@ -152,5 +152,6 @@ FFModuleBaseInfo ffHostModuleInfo = {
{ "Product vendor", "vendor" },
{ "Product serial number", "serial" },
{ "Product uuid", "uuid" },
}))
})),
.defaultOrder = 4,
};
+2 -1
View File
@@ -105,5 +105,6 @@ FFModuleBaseInfo ffIconsModuleInfo = {
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "Icons part 1", "icons1" },
{ "Icons part 2", "icons2" },
}))
})),
.defaultOrder = 25,
};
+2 -1
View File
@@ -116,5 +116,6 @@ FFModuleBaseInfo ffInitSystemModuleInfo = {
{ "Init system exe path", "exe" },
{ "Init system version path", "version" },
{ "Init system pid", "pid" },
}))
})),
.defaultOrder = 10,
};
+2 -1
View File
@@ -77,5 +77,6 @@ FFModuleBaseInfo ffKernelModuleInfo = {
{ "Architecture", "arch" },
{ "Display version", "display-version" },
{ "Page size", "page-size" },
}))
})),
.defaultOrder = 9,
};
+2 -1
View File
@@ -160,5 +160,6 @@ FFModuleBaseInfo ffKeyboardModuleInfo = {
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "Name", "name" },
{ "Serial number", "serial" },
}))
})),
.defaultOrder = 64,
};
+2 -1
View File
@@ -118,5 +118,6 @@ FFModuleBaseInfo ffLMModuleInfo = {
{ "LM service", "service" },
{ "LM type", "type" },
{ "LM version", "version" },
}))
})),
.defaultOrder = 20,
};
+2 -1
View File
@@ -160,5 +160,6 @@ FFModuleBaseInfo ffLoadavgModuleInfo = {
{ "Load average over 1min", "loadavg1" },
{ "Load average over 5min", "loadavg2" },
{ "Load average over 15min", "loadavg3" },
}))
})),
.defaultOrder = 12,
};
+2 -1
View File
@@ -77,5 +77,6 @@ FFModuleBaseInfo ffLocaleModuleInfo = {
.generateJsonConfig = (void*) ffGenerateLocaleJsonConfig,
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "Locale code", "result" },
}))
})),
.defaultOrder = 53,
};
+2 -1
View File
@@ -439,5 +439,6 @@ FFModuleBaseInfo ffLocalIPModuleInfo = {
{ "MTU size in bytes", "mtu" },
{ "Link speed (formatted)", "speed" },
{ "Interface flags", "flags" },
}))
})),
.defaultOrder = 49,
};
+2 -1
View File
@@ -273,5 +273,6 @@ FFModuleBaseInfo ffMediaModuleInfo = {
{ "Player name", "player-name" },
{ "Player ID", "player-id" },
{ "URL", "url" },
}))
})),
.defaultOrder = 47,
};
+2 -1
View File
@@ -131,5 +131,6 @@ FFModuleBaseInfo ffMemoryModuleInfo = {
{ "Total size", "total" },
{ "Percentage used (num)", "percentage" },
{ "Percentage used (bar)", "percentage-bar" },
}))
})),
.defaultOrder = 38,
};
+2 -1
View File
@@ -125,5 +125,6 @@ FFModuleBaseInfo ffMonitorModuleInfo = {
{ "Serial number", "serial" },
{ "Maximum refresh rate in Hz", "refresh-rate" },
{ "True if the display is HDR compatible", "hdr-compatible" },
}))
})),
.defaultOrder = 19,
};
+2 -1
View File
@@ -163,5 +163,6 @@ FFModuleBaseInfo ffMouseModuleInfo = {
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
{ "Mouse name", "name" },
{ "Mouse serial number", "serial" },
}))
})),
.defaultOrder = 63,
};
+2 -1
View File
@@ -220,5 +220,6 @@ FFModuleBaseInfo ffNetIOModuleInfo = {
{ "Number of errors sent [per second]", "tx-errors" },
{ "Number of packets dropped when receiving [per second]", "rx-drops" },
{ "Number of packets dropped when sending [per second]", "tx-drops" },
}))
})),
.defaultOrder = 66,
};
+2 -1
View File
@@ -132,5 +132,6 @@ FFModuleBaseInfo ffOpenCLModuleInfo = {
{ "Platform version", "version" },
{ "Platform name", "name" },
{ "Platform vendor", "vendor" },
}))
})),
.defaultOrder = 56,
};
+2 -1
View File
@@ -141,5 +141,6 @@ FFModuleBaseInfo ffOpenGLModuleInfo = {
{ "OpenGL vendor", "vendor" },
{ "OpenGL shading language version", "slv" },
{ "OpenGL library used", "library" },
}))
})),
.defaultOrder = 55,
};
+2 -1
View File
@@ -193,5 +193,6 @@ FFModuleBaseInfo ffOSModuleInfo = {
{ "Version codename of the OS", "codename" },
{ "Build ID of the OS", "build-id" },
{ "Architecture of the OS", "arch" },
}))
})),
.defaultOrder = 3,
};
+2 -1
View File
@@ -573,5 +573,6 @@ FFModuleBaseInfo ffPackagesModuleInfo = {
{ "Total number of all hpkg packages", "hpkg-all" },
{ "Total number of all nix packages", "nix-all" },
{ "Number of all packages", "all" },
}))
})),
.defaultOrder = 14,
};

Some files were not shown because too many files have changed in this diff Show More