mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 09:58:03 +02:00
@@ -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
|
||||
|
||||
@@ -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' }} .
|
||||
|
||||
@@ -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-*.*
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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); \
|
||||
})
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
@@ -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__);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) ®istry_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);
|
||||
|
||||
@@ -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
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
!!
|
||||
!!!!
|
||||
llllll
|
||||
llllll
|
||||
IIIIIIIIII
|
||||
IIIIIIIIIIIIIIIIIIII
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;
|
||||
:;;::;:;::
|
||||
::::::
|
||||
,,,,,,
|
||||
,,,,
|
||||
""
|
||||
@@ -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
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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```
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
$2@
|
||||
@@--=====@@
|
||||
@@--==@@ @@====+@
|
||||
@-@@ @==@
|
||||
@=@
|
||||
@=@$1 @=@ @-==== @=@$2
|
||||
@=@$1 @-===@==++@===+@$2
|
||||
@=@$1 @--====@@=====+@$2
|
||||
-=@$1 @--==========++@$2
|
||||
==$1 @--==========++@$2 @=@
|
||||
@==$1 @--=======@==++@$2 @=+@
|
||||
@==$1 @-==========++$2 @=@
|
||||
@==$1 @-=======@=%$2 @=@
|
||||
@==$1 @@@@@@$2 @=@
|
||||
@====@@@ @@===+%
|
||||
@@=====@@==++++@@
|
||||
=#@=@
|
||||
@==@++@
|
||||
@@@
|
||||
@@ -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.
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -144,5 +144,6 @@ FFModuleBaseInfo ffBiosModuleInfo = {
|
||||
{ "BIOS vendor", "vendor" },
|
||||
{ "BIOS version", "version" },
|
||||
{ "Firmware type", "type" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 5,
|
||||
};
|
||||
|
||||
@@ -166,5 +166,6 @@ FFModuleBaseInfo ffBluetoothModuleInfo = {
|
||||
{ "Battery percentage number", "battery-percentage" },
|
||||
{ "Is connected", "connected" },
|
||||
{ "Battery percentage bar", "battery-percentage-bar" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 58,
|
||||
};
|
||||
|
||||
@@ -207,5 +207,6 @@ FFModuleBaseInfo ffBluetoothRadioModuleInfo = {
|
||||
{ "Vendor", "vendor" },
|
||||
{ "Discoverable", "discoverable" },
|
||||
{ "Connectable / Pairable", "connectable" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 59,
|
||||
};
|
||||
|
||||
@@ -121,5 +121,6 @@ FFModuleBaseInfo ffBoardModuleInfo = {
|
||||
{ "Board vendor", "vendor" },
|
||||
{ "Board version", "version" },
|
||||
{ "Board serial number", "serial" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 7,
|
||||
};
|
||||
|
||||
@@ -117,5 +117,6 @@ FFModuleBaseInfo ffBootmgrModuleInfo = {
|
||||
{ "Firmware file name", "firmware-name" },
|
||||
{ "Is secure boot enabled", "secure-boot" },
|
||||
{ "Boot order", "order" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 6,
|
||||
};
|
||||
|
||||
@@ -33,4 +33,5 @@ FFModuleBaseInfo ffBreakModuleInfo = {
|
||||
.destroyOptions = (void*) ffDestroyBreakOptions,
|
||||
.parseJsonObject = (void*) ffParseBreakJsonObject,
|
||||
.printModule = (void*) ffPrintBreak,
|
||||
.defaultOrder = 71,
|
||||
};
|
||||
|
||||
@@ -206,5 +206,6 @@ FFModuleBaseInfo ffBrightnessModuleInfo = {
|
||||
{ "Current brightness value", "current" },
|
||||
{ "Screen brightness (percentage bar)", "percentage-bar" },
|
||||
{ "Is built-in screen", "is-builtin" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 18,
|
||||
};
|
||||
|
||||
@@ -220,5 +220,6 @@ FFModuleBaseInfo ffBtrfsModuleInfo = {
|
||||
{ "Allocated percentage bar", "allocated-percentage-bar" },
|
||||
{ "Node size", "node-size" },
|
||||
{ "Sector size", "sector-size" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 42,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -124,4 +124,5 @@ FFModuleBaseInfo ffChassisModuleInfo = {
|
||||
{ "Chassis version", "version" },
|
||||
{ "Chassis serial number", "serial" },
|
||||
})),
|
||||
.defaultOrder = 8,
|
||||
};
|
||||
|
||||
@@ -264,5 +264,6 @@ FFModuleBaseInfo ffCodecModuleInfo = {
|
||||
{ "Decoder / Encoder", "direction" },
|
||||
{ "Compatibility alias of codec types", "types" },
|
||||
{ "Platform API used for detection", "platform-api" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 37,
|
||||
};
|
||||
|
||||
@@ -302,4 +302,5 @@ FFModuleBaseInfo ffColorsModuleInfo = {
|
||||
.parseJsonObject = (void*) ffParseColorsJsonObject,
|
||||
.printModule = (void*) ffPrintColors,
|
||||
.generateJsonConfig = (void*) ffGenerateColorsJsonConfig,
|
||||
.defaultOrder = 72,
|
||||
};
|
||||
|
||||
@@ -163,5 +163,5 @@ FFModuleBaseInfo ffCommandModuleInfo = {
|
||||
.generateJsonConfig = (void*) ffGenerateCommandJsonConfig,
|
||||
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
|
||||
{ "Command result", "result" },
|
||||
}))
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -276,5 +276,6 @@ FFModuleBaseInfo ffCPUModuleInfo = {
|
||||
{ "CPU code name", "code-name" },
|
||||
{ "CPU technology", "technology" },
|
||||
#endif
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 33,
|
||||
};
|
||||
|
||||
@@ -234,5 +234,6 @@ FFModuleBaseInfo ffCPUCacheModuleInfo = {
|
||||
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
|
||||
{ "Separate result", "result" },
|
||||
{ "Sum result", "sum" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 34,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -112,4 +112,5 @@ FFModuleBaseInfo ffCursorModuleInfo = {
|
||||
{ "Cursor theme", "theme" },
|
||||
{ "Cursor size", "size" },
|
||||
})),
|
||||
.defaultOrder = 27,
|
||||
};
|
||||
|
||||
@@ -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
@@ -94,5 +94,6 @@ FFModuleBaseInfo ffDEModuleInfo = {
|
||||
{ "DE process name", "process-name" },
|
||||
{ "DE pretty name", "pretty-name" },
|
||||
{ "DE version", "version" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 21,
|
||||
};
|
||||
|
||||
@@ -486,5 +486,6 @@ FFModuleBaseInfo ffDiskModuleInfo = {
|
||||
{ "Years fraction after creation", "years-fraction" },
|
||||
{ "Size free", "size-free" },
|
||||
{ "Size available", "size-available" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 41,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -144,5 +144,6 @@ FFModuleBaseInfo ffDNSModuleInfo = {
|
||||
.generateJsonConfig = (void*) ffGenerateDNSJsonConfig,
|
||||
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
|
||||
{ "DNS result", "result" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 50,
|
||||
};
|
||||
|
||||
@@ -117,5 +117,6 @@ FFModuleBaseInfo ffEditorModuleInfo = {
|
||||
{ "Exe name of real path", "exe-name" },
|
||||
{ "Full path of real path", "path" },
|
||||
{ "Version", "version" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 16,
|
||||
};
|
||||
|
||||
@@ -109,5 +109,6 @@ FFModuleBaseInfo ffFontModuleInfo = {
|
||||
{ "Font 3", "font3" },
|
||||
{ "Font 4", "font4" },
|
||||
{ "Combined fonts for display", "combined" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 26,
|
||||
};
|
||||
|
||||
@@ -202,5 +202,6 @@ FFModuleBaseInfo ffGamepadModuleInfo = {
|
||||
{ "Serial number", "serial" },
|
||||
{ "Battery percentage num", "battery-percentage" },
|
||||
{ "Battery percentage bar", "battery-percentage-bar" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 62,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -152,5 +152,6 @@ FFModuleBaseInfo ffHostModuleInfo = {
|
||||
{ "Product vendor", "vendor" },
|
||||
{ "Product serial number", "serial" },
|
||||
{ "Product uuid", "uuid" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 4,
|
||||
};
|
||||
|
||||
@@ -105,5 +105,6 @@ FFModuleBaseInfo ffIconsModuleInfo = {
|
||||
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
|
||||
{ "Icons part 1", "icons1" },
|
||||
{ "Icons part 2", "icons2" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 25,
|
||||
};
|
||||
|
||||
@@ -116,5 +116,6 @@ FFModuleBaseInfo ffInitSystemModuleInfo = {
|
||||
{ "Init system exe path", "exe" },
|
||||
{ "Init system version path", "version" },
|
||||
{ "Init system pid", "pid" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 10,
|
||||
};
|
||||
|
||||
@@ -77,5 +77,6 @@ FFModuleBaseInfo ffKernelModuleInfo = {
|
||||
{ "Architecture", "arch" },
|
||||
{ "Display version", "display-version" },
|
||||
{ "Page size", "page-size" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 9,
|
||||
};
|
||||
|
||||
@@ -160,5 +160,6 @@ FFModuleBaseInfo ffKeyboardModuleInfo = {
|
||||
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
|
||||
{ "Name", "name" },
|
||||
{ "Serial number", "serial" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 64,
|
||||
};
|
||||
|
||||
+2
-1
@@ -118,5 +118,6 @@ FFModuleBaseInfo ffLMModuleInfo = {
|
||||
{ "LM service", "service" },
|
||||
{ "LM type", "type" },
|
||||
{ "LM version", "version" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 20,
|
||||
};
|
||||
|
||||
@@ -160,5 +160,6 @@ FFModuleBaseInfo ffLoadavgModuleInfo = {
|
||||
{ "Load average over 1min", "loadavg1" },
|
||||
{ "Load average over 5min", "loadavg2" },
|
||||
{ "Load average over 15min", "loadavg3" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 12,
|
||||
};
|
||||
|
||||
@@ -77,5 +77,6 @@ FFModuleBaseInfo ffLocaleModuleInfo = {
|
||||
.generateJsonConfig = (void*) ffGenerateLocaleJsonConfig,
|
||||
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
|
||||
{ "Locale code", "result" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 53,
|
||||
};
|
||||
|
||||
@@ -439,5 +439,6 @@ FFModuleBaseInfo ffLocalIPModuleInfo = {
|
||||
{ "MTU size in bytes", "mtu" },
|
||||
{ "Link speed (formatted)", "speed" },
|
||||
{ "Interface flags", "flags" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 49,
|
||||
};
|
||||
|
||||
@@ -273,5 +273,6 @@ FFModuleBaseInfo ffMediaModuleInfo = {
|
||||
{ "Player name", "player-name" },
|
||||
{ "Player ID", "player-id" },
|
||||
{ "URL", "url" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 47,
|
||||
};
|
||||
|
||||
@@ -131,5 +131,6 @@ FFModuleBaseInfo ffMemoryModuleInfo = {
|
||||
{ "Total size", "total" },
|
||||
{ "Percentage used (num)", "percentage" },
|
||||
{ "Percentage used (bar)", "percentage-bar" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 38,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -163,5 +163,6 @@ FFModuleBaseInfo ffMouseModuleInfo = {
|
||||
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
|
||||
{ "Mouse name", "name" },
|
||||
{ "Mouse serial number", "serial" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 63,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -132,5 +132,6 @@ FFModuleBaseInfo ffOpenCLModuleInfo = {
|
||||
{ "Platform version", "version" },
|
||||
{ "Platform name", "name" },
|
||||
{ "Platform vendor", "vendor" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 56,
|
||||
};
|
||||
|
||||
@@ -141,5 +141,6 @@ FFModuleBaseInfo ffOpenGLModuleInfo = {
|
||||
{ "OpenGL vendor", "vendor" },
|
||||
{ "OpenGL shading language version", "slv" },
|
||||
{ "OpenGL library used", "library" },
|
||||
}))
|
||||
})),
|
||||
.defaultOrder = 55,
|
||||
};
|
||||
|
||||
+2
-1
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user