mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
@@ -190,11 +190,14 @@ jobs:
|
||||
run: cmake --version
|
||||
|
||||
- name: configure project
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_DIRECTX_HEADERS=Off .
|
||||
run: CC=gcc-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -DCMAKE_C_FLAGS="-m32 -march=i686 -mtune=i686" -DCMAKE_SYSTEM_PROCESSOR_OVERRIDE=i686 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=i386 -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_DIRECTX_HEADERS=Off .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
|
||||
- name: check deb package
|
||||
run: dpkg -I fastfetch-*.deb
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
|
||||
|
||||
+36
-1
@@ -1,6 +1,41 @@
|
||||
# 2.55.0
|
||||
|
||||
Changes:
|
||||
* Commands are now executed in parallel by default to improve performance (#2045, Command)
|
||||
* This behavior can be disabled in the config file with `"parallel": false` if it causes problems with certain scripts
|
||||
* Folder/filesystem hiding is moved to the detection stage; hidden entries are no longer probed, improving performance (#2043, Disk)
|
||||
|
||||
Features:
|
||||
* Adds `command.parallel` and `command.useStdErr` config options (Command)
|
||||
* `parallel`: set to `false` to disable parallel execution (see Changes above)
|
||||
* `useStdErr`: set to `true` to use stderr output instead of stdout
|
||||
* Adds the command-line flag `--dynamic-interval <interval-in-ms>` to enable dynamic output auto-refresh (#2041)
|
||||
* Due to internal limitations, some modules do not support dynamic updates (notably Display and Media)
|
||||
* Adds support for using the current playing media's cover art as a logo source (Media / Logo)
|
||||
* Usage: `"logo": { "type": "<image-protocol>", "source": "media-cover" }` in JSON config; or `--<image-protocol> media-cover` in command line
|
||||
* Supports local sources only
|
||||
* Adds native GPU detection support on OpenBSD and NetBSD (instead of depending on `libpciaccess`) (GPU)
|
||||
* No functional changes
|
||||
* Root privileges are required to access PCI config space on OpenBSD (as always)
|
||||
* Adds GPU detection support on GNU/Hurd (GPU)
|
||||
* Requires building with `libpciaccess`
|
||||
* Shows Debian point release on Raspberry Pi OS (#2032, OS, Linux)
|
||||
* Adds `Brush` shell version detection (Shell)
|
||||
* Improves Mac family detection via prefix matching (Host)
|
||||
|
||||
Bugfixes:
|
||||
* Ignores `run-parts` during terminal/shell detection (#2048, Terminal / Shell, Linux)
|
||||
* Fixes fish version detection when `LC_ALL` is set (#2014, Shell, Linux)
|
||||
* Hides the module when no desktop icons are found (#2023, Icons, Windows)
|
||||
* Skips auxiliary display controllers to prevent the module from reporting duplicate entries (#2034, GPU, Linux)
|
||||
* Refines Apple rpath handling; fixes building for the Homebrew version on macOS (#1998, CMake)
|
||||
|
||||
Logos:
|
||||
* Adds Vincent OS and MacaroniOS
|
||||
|
||||
# 2.54.0
|
||||
|
||||
Windows binaries in Release page are now signed using SignPath.
|
||||
Windows binaries in Release page are now signed by SignPath.
|
||||
|
||||
Changes:
|
||||
* Moves macOS and Windows design language detection from the DE module to the Theme module
|
||||
|
||||
+19
-12
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
|
||||
|
||||
project(fastfetch
|
||||
VERSION 2.54.0
|
||||
VERSION 2.55.0
|
||||
LANGUAGES C
|
||||
DESCRIPTION "Fast neofetch-like system information tool"
|
||||
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
|
||||
@@ -93,7 +93,7 @@ cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" O
|
||||
cmake_dependent_option(ENABLE_ELF "Enable libelf" ON "LINUX OR ANDROID OR DragonFly OR Haiku OR GNU" OFF)
|
||||
cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF)
|
||||
cmake_dependent_option(ENABLE_LIBZFS "Enable libzfs" ON "LINUX OR FreeBSD OR SunOS" OFF)
|
||||
cmake_dependent_option(ENABLE_PCIACCESS "Enable libpciaccess" ON "NetBSD OR OpenBSD" OFF)
|
||||
cmake_dependent_option(ENABLE_PCIACCESS "Enable libpciaccess" ON "GNU" OFF)
|
||||
|
||||
option(ENABLE_ZLIB "Enable zlib" ON)
|
||||
option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embedded) yyjson library" OFF)
|
||||
@@ -389,6 +389,7 @@ set(LIBFASTFETCH_SRC
|
||||
src/detection/chassis/chassis.c
|
||||
src/detection/cpu/cpu.c
|
||||
src/detection/cpuusage/cpuusage.c
|
||||
src/detection/command/command.c
|
||||
src/detection/disk/disk.c
|
||||
src/detection/diskio/diskio.c
|
||||
src/detection/displayserver/displayserver.c
|
||||
@@ -794,7 +795,7 @@ elseif(NetBSD)
|
||||
src/detection/displayserver/linux/xcb.c
|
||||
src/detection/displayserver/linux/xlib.c
|
||||
src/detection/font/font_linux.c
|
||||
src/detection/gpu/gpu_general.c
|
||||
src/detection/gpu/gpu_nbsd.c
|
||||
src/detection/gpu/gpu_pci.c
|
||||
src/detection/gtk_qt/gtk.c
|
||||
src/detection/host/host_nbsd.c
|
||||
@@ -878,7 +879,7 @@ elseif(OpenBSD)
|
||||
src/detection/displayserver/linux/xlib.c
|
||||
src/detection/font/font_linux.c
|
||||
src/detection/gpu/gpu_pci.c
|
||||
src/detection/gpu/gpu_general.c
|
||||
src/detection/gpu/gpu_obsd.c
|
||||
src/detection/gtk_qt/gtk.c
|
||||
src/detection/host/host_obsd.c
|
||||
src/detection/lm/lm_nosupport.c
|
||||
@@ -1254,7 +1255,7 @@ elseif(GNU)
|
||||
src/detection/displayserver/linux/xcb.c
|
||||
src/detection/displayserver/linux/xlib.c
|
||||
src/detection/font/font_linux.c
|
||||
src/detection/gpu/gpu_nosupport.c
|
||||
src/detection/gpu/gpu_general.c
|
||||
src/detection/gpu/gpu_pci.c
|
||||
src/detection/gtk_qt/gtk.c
|
||||
src/detection/host/host_nosupport.c
|
||||
@@ -1340,15 +1341,15 @@ add_library(libfastfetch OBJECT
|
||||
)
|
||||
|
||||
if(yyjson_FOUND)
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_USE_SYSTEM_YYJSON)
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_USE_SYSTEM_YYJSON=1)
|
||||
target_link_libraries(libfastfetch PUBLIC yyjson::yyjson)
|
||||
# `target_link_libraries(yyjson::yyjson)` sets rpath implicitly
|
||||
else()
|
||||
# Used for dlopen finding dylibs installed by homebrew
|
||||
# `/opt/homebrew/lib` is not on in dlopen search path by default
|
||||
if(APPLE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Used for dlopen finding dylibs installed by homebrew
|
||||
# `/opt/homebrew/lib` is not on in dlopen search path by default
|
||||
if(APPLE AND NOT BINARY_LINK_TYPE STREQUAL "dlopen")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,/opt/homebrew/lib -Wl,-rpath,/usr/local/lib")
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
@@ -1823,8 +1824,14 @@ if(NOT WIN32)
|
||||
CHECK_INCLUDE_FILE("wordexp.h" HAVE_WORDEXP)
|
||||
if(HAVE_WORDEXP)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_WORDEXP=1)
|
||||
message(STATUS "wordexp.h found, wordexp support enabled")
|
||||
else()
|
||||
set(ENABLE_WORDEXP OFF)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT ENABLE_WORDEXP)
|
||||
message(STATUS "wordexp.h not found or disabled, glob used instead")
|
||||
endif()
|
||||
if(ENABLE_THREADS AND CMAKE_USE_PTHREADS_INIT)
|
||||
CHECK_INCLUDE_FILE("pthread_np.h" HAVE_PTHREAD_NP)
|
||||
if(HAVE_PTHREAD_NP)
|
||||
|
||||
@@ -322,6 +322,11 @@ If you find Fastfetch useful, please consider donating.
|
||||
* Current maintainer: [@CarterLi](https://paypal.me/zhangsongcui)
|
||||
* Original author: [@LinusDierheimer](https://github.com/sponsors/LinusDierheimer)
|
||||
|
||||
## Code signing
|
||||
|
||||
* Free code signing provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
|
||||
* This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it
|
||||
|
||||
## Star History
|
||||
|
||||
Give us a star to show your support!
|
||||
|
||||
+13
-1
@@ -1,3 +1,16 @@
|
||||
fastfetch (2.54.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Fix building on plucky
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 20 Oct 2025 14:27:02 +0800
|
||||
|
||||
fastfetch (2.54.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.54.0
|
||||
* Test independent changelog entries for different Ubuntu releases
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 20 Oct 2025 10:47:02 +0800
|
||||
|
||||
fastfetch (2.53.0) jammy; urgency=medium
|
||||
|
||||
* Update to 2.53.0
|
||||
@@ -435,4 +448,3 @@ fastfetch (2.7.1) jammy; urgency=medium
|
||||
* Initial release.
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Tue, 06 Feb 2024 15:01:11 +0800
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@ Source: fastfetch
|
||||
Section: universe/utils
|
||||
Priority: optional
|
||||
Maintainer: Carter Li <zhangsongcui@live.cn>
|
||||
Build-Depends: libelf-dev, 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, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build
|
||||
Build-Depends: libelf-dev, 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, libddcutil-dev, libchafa-dev, directx-headers-dev, pkgconf, cmake (>= 3.12), debhelper (>= 11.2), dh-cmake, dh-cmake-compat (= 1), dh-sequence-cmake, dh-sequence-ctest, ninja-build, python3-setuptools
|
||||
Standards-Version: 4.0.0
|
||||
Homepage: https://github.com/fastfetch-cli/fastfetch
|
||||
|
||||
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
fastfetch_2.53.0_source.buildinfo universe/utils optional
|
||||
Vendored
+74
-2
@@ -1,4 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# WARNING: DO NOT RUN THIS SCRIPT UNLESS YOU KNOW WHAT YOU ARE DOING.
|
||||
|
||||
debuild -S -i -I
|
||||
dput ppa:zhangsongcui3371/fastfetch ~/fastfetch_*.changes
|
||||
set -euo pipefail
|
||||
|
||||
command -v debuild >/dev/null 2>&1 || { echo "debuild not found. Install devscripts." >&2; exit 1; }
|
||||
command -v dput >/dev/null 2>&1 || { echo "dput not found." >&2; exit 1; }
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
||||
ROOT_DIR="$(cd -- "$SCRIPT_DIR/.." >/dev/null 2>&1 && pwd)"
|
||||
OUT_DIR="$(dirname "$ROOT_DIR")"
|
||||
PPA="ppa:zhangsongcui3371/fastfetch"
|
||||
CODENAMES=( jammy noble plucky questing resolute )
|
||||
DRY_RUN=0
|
||||
|
||||
if [[ -d "$OUT_DIR/build" ]]; then
|
||||
echo "Output directory exists: $OUT_DIR/build" >&2
|
||||
echo "Please move or delete it before proceeding." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TEMPLATE="$ROOT_DIR/debian/changelog.tpl"
|
||||
CHANGELOG_DEBIAN="$ROOT_DIR/debian/changelog"
|
||||
|
||||
if [[ ! -f "$TEMPLATE" ]]; then
|
||||
echo "Template not found: $TEMPLATE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q '#UBUNTU_CODENAME#' "$TEMPLATE"; then
|
||||
echo "Template missing placeholder: #UBUNTU_CODENAME#" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "IMPORTANT: Before proceeding, please ensure that 'debian/changelog.tpl' is up-to-date"
|
||||
echo " with the correct version number, release notes, and maintainer information."
|
||||
echo
|
||||
echo " The template must contain a placeholder like '#UBUNTU_CODENAME#' for the codename."
|
||||
echo
|
||||
read -p "Have you reviewed and updated 'debian/changelog.tpl'? (y/N): " -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
rm -f "$CHANGELOG_DEBIAN"
|
||||
rm -f "$ROOT_DIR/debian/files"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
shopt -s nullglob
|
||||
|
||||
for codename in "${CODENAMES[@]}"; do
|
||||
echo "==> Publishing distro: $codename"
|
||||
sed "s/#UBUNTU_CODENAME#/${codename}/g" "$TEMPLATE" > "$CHANGELOG_DEBIAN"
|
||||
|
||||
( cd "$ROOT_DIR" && debuild -S -i -I )
|
||||
|
||||
changes=( "$OUT_DIR"/fastfetch_*~${codename}_source.changes )
|
||||
if [[ ${#changes[@]} -ne 1 ]]; then
|
||||
echo "Unable to uniquely identify .changes for '$codename' in: $OUT_DIR" >&2
|
||||
printf 'Found:\n'; printf ' %s\n' "${changes[@]}" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
echo "[DRY-RUN] dput \"$PPA\" \"${changes[0]}\""
|
||||
else
|
||||
dput "$PPA" "${changes[0]}"
|
||||
fi
|
||||
|
||||
rm -f "$OUT_DIR"/fastfetch_*~${codename}_source.{changes,dsc,tar.*}
|
||||
|
||||
echo "<== Done: $codename"
|
||||
done
|
||||
|
||||
+12
-3
@@ -1008,7 +1008,7 @@
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Set whether to show icon before string keys",
|
||||
"description": "Set whether to show builtin icon before string keys",
|
||||
"oneOf": [
|
||||
{
|
||||
"const": "none",
|
||||
@@ -1020,7 +1020,7 @@
|
||||
},
|
||||
{
|
||||
"const": "icon",
|
||||
"description": "Show icon keys (requires newest nerd font)"
|
||||
"description": "Show builtin icon (requires newest nerd font)"
|
||||
},
|
||||
{
|
||||
"const": "both",
|
||||
@@ -1047,7 +1047,6 @@
|
||||
"description": "Show both icon and string with 4 spaces between them"
|
||||
}
|
||||
],
|
||||
],
|
||||
"default": "string"
|
||||
},
|
||||
"paddingLeft": {
|
||||
@@ -2102,6 +2101,16 @@
|
||||
"description": "Set the command text to be executed",
|
||||
"type": "string"
|
||||
},
|
||||
"useStdErr": {
|
||||
"description": "Set if stderr should be used instead of stdout for command output",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"parallel": {
|
||||
"description": "Set if the command should be executed in parallel with other commands\nImprove performance when using multiple commands, but may cause issues with some commands",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/$defs/key"
|
||||
},
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
},
|
||||
"keyIcon": "",
|
||||
"key": "│{#red}│ {icon} Clang │{$4}│{#keys}│{$2}",
|
||||
"text": "clang --version | head -1 | awk '{print $NF}'",
|
||||
"text": "clang --version | sed -n 's/.*version \\([0-9][0-9.]*\\).*/\\1/p'",
|
||||
"format": "clang {}"
|
||||
},
|
||||
{
|
||||
|
||||
+10
-3
@@ -30,6 +30,7 @@ static void initState(FFstate* state)
|
||||
ffPlatformInit(&state->platform);
|
||||
state->configDoc = NULL;
|
||||
state->resultDoc = NULL;
|
||||
state->dynamicInterval = 0;
|
||||
|
||||
{
|
||||
// don't enable bright color if the terminal is in light mode
|
||||
@@ -74,6 +75,9 @@ static void resetConsole(void)
|
||||
#if defined(_WIN32)
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
if(instance.state.dynamicInterval > 0)
|
||||
fputs("\033[?1049l", stdout); // Disable alternate buffer
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -130,14 +134,17 @@ void ffStart(void)
|
||||
if(ffDisableLinewrap)
|
||||
fputs("\033[?7l", stdout);
|
||||
|
||||
if(instance.state.dynamicInterval > 0)
|
||||
{
|
||||
fputs("\033[?1049h\033[H", stdout); // Enable alternate buffer
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
ffLogoPrint();
|
||||
}
|
||||
|
||||
void ffFinish(void)
|
||||
{
|
||||
if(instance.config.logo.printRemaining)
|
||||
ffLogoPrintRemaining();
|
||||
|
||||
resetConsole();
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@ static inline bool ffSearchUserConfigFile(const FFlist* configDirs, const char*
|
||||
}
|
||||
|
||||
FFNativeFD ffGetNullFD(void);
|
||||
bool ffRemoveFile(const char* fileName);
|
||||
|
||||
#ifdef _WIN32
|
||||
// Only O_RDONLY is supported
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#if FF_HAVE_WORDEXP
|
||||
#include <wordexp.h>
|
||||
#else
|
||||
#warning "<wordexp.h> is not available, use glob(3) instead"
|
||||
#include <glob.h>
|
||||
#endif
|
||||
|
||||
@@ -372,3 +371,8 @@ FFNativeFD ffGetNullFD(void)
|
||||
hNullFile = open("/dev/null", O_WRONLY | O_CLOEXEC);
|
||||
return hNullFile;
|
||||
}
|
||||
|
||||
bool ffRemoveFile(const char* fileName)
|
||||
{
|
||||
return unlink(fileName) == 0;
|
||||
}
|
||||
|
||||
@@ -371,3 +371,8 @@ FFNativeFD ffGetNullFD(void)
|
||||
});
|
||||
return hNullFile;
|
||||
}
|
||||
|
||||
bool ffRemoveFile(const char* fileName)
|
||||
{
|
||||
return DeleteFileA(fileName) != FALSE;
|
||||
}
|
||||
|
||||
@@ -158,6 +158,16 @@ static void prepareModuleJsonObject(const char* type, yyjson_val* module)
|
||||
ffPrepareCPUUsage();
|
||||
break;
|
||||
}
|
||||
case 'c': case 'C': {
|
||||
if (ffStrEqualsIgnCase(type, FF_COMMAND_MODULE_NAME))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyCommandOptions))) FFCommandOptions options;
|
||||
ffInitCommandOptions(&options);
|
||||
if (module) ffCommandModuleInfo.parseJsonObject(&options, module);
|
||||
ffPrepareCommand(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'd': case 'D': {
|
||||
if (ffStrEqualsIgnCase(type, FF_DISKIO_MODULE_NAME))
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
|
||||
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
|
||||
uint32_t minMetric = UINT32_MAX;
|
||||
int routeCount = 0;
|
||||
FF_MAYBE_UNUSED int routeCount = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -299,7 +299,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
|
||||
uint8_t buffer[1024 * 16]; // 16 KB buffer should be sufficient
|
||||
uint32_t minMetric = UINT32_MAX;
|
||||
int routeCount = 0;
|
||||
FF_MAYBE_UNUSED int routeCount = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
+25
-4
@@ -2,13 +2,30 @@
|
||||
|
||||
#include "util/FFstrbuf.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifndef _WIN32
|
||||
#include <sys/types.h> // pid_t
|
||||
#endif
|
||||
|
||||
const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool useStdErr);
|
||||
typedef struct FFProcessHandle {
|
||||
#if _WIN32
|
||||
void* pid; // HANDLE
|
||||
void* pipeRead; // HANDLE
|
||||
#else
|
||||
pid_t pid;
|
||||
int pipeRead;
|
||||
#endif
|
||||
} FFProcessHandle;
|
||||
|
||||
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle);
|
||||
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer); // Destroys handle internally
|
||||
|
||||
static inline const char* ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[])
|
||||
{
|
||||
const char* error = ffProcessAppendOutput(buffer, argv, false);
|
||||
FFProcessHandle handle;
|
||||
const char* error = ffProcessSpawn(argv, false, &handle);
|
||||
if (error) return error;
|
||||
|
||||
error = ffProcessReadOutput(&handle, buffer);
|
||||
if (!error)
|
||||
ffStrbufTrimRightSpace(buffer);
|
||||
return error;
|
||||
@@ -16,7 +33,11 @@ static inline const char* ffProcessAppendStdOut(FFstrbuf* buffer, char* const ar
|
||||
|
||||
static inline const char* ffProcessAppendStdErr(FFstrbuf* buffer, char* const argv[])
|
||||
{
|
||||
const char* error = ffProcessAppendOutput(buffer, argv, true);
|
||||
FFProcessHandle handle;
|
||||
const char* error = ffProcessSpawn(argv, true, &handle);
|
||||
if (error) return error;
|
||||
|
||||
error = ffProcessReadOutput(&handle, buffer);
|
||||
if (!error)
|
||||
ffStrbufTrimRightSpace(buffer);
|
||||
return error;
|
||||
|
||||
@@ -58,11 +58,9 @@ static inline int ffPipe2(int* fds, int flags)
|
||||
|
||||
|
||||
// Not thread-safe
|
||||
const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool useStdErr)
|
||||
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle)
|
||||
{
|
||||
int pipes[2];
|
||||
const int timeout = instance.config.general.processingTimeout;
|
||||
|
||||
if(ffPipe2(pipes, O_CLOEXEC) == -1)
|
||||
return "pipe() failed";
|
||||
|
||||
@@ -146,7 +144,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
|
||||
if(childPid == 0)
|
||||
{
|
||||
//Child
|
||||
// Child process
|
||||
dup2(pipes[1], useStdErr ? STDERR_FILENO : STDOUT_FILENO);
|
||||
dup2(nullFile, useStdErr ? STDOUT_FILENO : STDERR_FILENO);
|
||||
putenv("LANG=C.UTF-8");
|
||||
@@ -157,11 +155,24 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
#endif
|
||||
|
||||
close(pipes[1]);
|
||||
outHandle->pid = childPid;
|
||||
outHandle->pipeRead = pipes[0];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int FF_AUTO_CLOSE_FD childPipeFd = pipes[0];
|
||||
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
{
|
||||
assert(handle->pipeRead != -1);
|
||||
assert(handle->pid != -1);
|
||||
|
||||
const int32_t timeout = instance.config.general.processingTimeout;
|
||||
FF_AUTO_CLOSE_FD int childPipeFd = handle->pipeRead;
|
||||
pid_t childPid = handle->pid;
|
||||
handle->pipeRead = -1;
|
||||
handle->pid = -1;
|
||||
char str[FF_PIPE_BUFSIZ];
|
||||
|
||||
while(1)
|
||||
for (;;)
|
||||
{
|
||||
if (timeout >= 0)
|
||||
{
|
||||
@@ -189,14 +200,14 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
{
|
||||
kill(childPid, SIGTERM);
|
||||
waitpid(childPid, NULL, 0);
|
||||
return "poll(&pollfd, 1, timeout) error";
|
||||
return "poll(&pollfd, 1, timeout) error: not EINTR";
|
||||
}
|
||||
}
|
||||
else if (pollfd.revents & POLLERR)
|
||||
{
|
||||
kill(childPid, SIGTERM);
|
||||
waitpid(childPid, NULL, 0);
|
||||
return "poll(&pollfd, 1, timeout) error";
|
||||
return "poll(&pollfd, 1, timeout) error: POLLERR";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +222,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
if (!WIFEXITED(stat_loc))
|
||||
return "child process exited abnormally";
|
||||
if (WEXITSTATUS(stat_loc) == 127)
|
||||
return "command was not found";
|
||||
return "command not found";
|
||||
// We only handle 127 as an error. See `getTerminalVersionUrxvt` in `terminalshell.c`
|
||||
return NULL;
|
||||
}
|
||||
@@ -224,7 +235,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
else
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return "read(childPipeFd, str, FF_PIPE_BUFSIZ) failed";
|
||||
}
|
||||
|
||||
@@ -45,12 +45,13 @@ static void argvToCmdline(char* const argv[], FFstrbuf* result)
|
||||
}
|
||||
}
|
||||
|
||||
const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool useStdErr)
|
||||
const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle* outHandle)
|
||||
{
|
||||
int timeout = instance.config.general.processingTimeout;
|
||||
const int32_t timeout = instance.config.general.processingTimeout;
|
||||
|
||||
wchar_t pipeName[32];
|
||||
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u", GetCurrentProcessId());
|
||||
static unsigned pidCounter = 0;
|
||||
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u-%u", GetCurrentProcessId(), ++pidCounter);
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = CreateNamedPipeW(
|
||||
pipeName,
|
||||
@@ -82,52 +83,67 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
return "CreateFileW(L\"\\\\.\\pipe\\FASTFETCH-$(PID)\") failed";
|
||||
|
||||
PROCESS_INFORMATION piProcInfo = {};
|
||||
|
||||
BOOL success;
|
||||
|
||||
STARTUPINFOA siStartInfo = {
|
||||
.cb = sizeof(siStartInfo),
|
||||
.dwFlags = STARTF_USESTDHANDLES,
|
||||
};
|
||||
if (useStdErr)
|
||||
{
|
||||
STARTUPINFOA siStartInfo = {
|
||||
.cb = sizeof(siStartInfo),
|
||||
.dwFlags = STARTF_USESTDHANDLES,
|
||||
};
|
||||
if (useStdErr)
|
||||
{
|
||||
siStartInfo.hStdOutput = ffGetNullFD();
|
||||
siStartInfo.hStdError = hChildPipeWrite;
|
||||
}
|
||||
else
|
||||
{
|
||||
siStartInfo.hStdOutput = hChildPipeWrite;
|
||||
siStartInfo.hStdError = ffGetNullFD();
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY cmdline = ffStrbufCreate();
|
||||
argvToCmdline(argv, &cmdline);
|
||||
|
||||
success = CreateProcessA(
|
||||
NULL, // application name
|
||||
cmdline.chars, // command line
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
NULL, // use parent's environment
|
||||
NULL, // use parent's current directory
|
||||
&siStartInfo, // STARTUPINFO pointer
|
||||
&piProcInfo // receives PROCESS_INFORMATION
|
||||
);
|
||||
siStartInfo.hStdOutput = ffGetNullFD();
|
||||
siStartInfo.hStdError = hChildPipeWrite;
|
||||
}
|
||||
else
|
||||
{
|
||||
siStartInfo.hStdOutput = hChildPipeWrite;
|
||||
siStartInfo.hStdError = ffGetNullFD();
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY cmdline = ffStrbufCreate();
|
||||
argvToCmdline(argv, &cmdline);
|
||||
|
||||
BOOL success = CreateProcessA(
|
||||
NULL, // application name
|
||||
cmdline.chars, // command line
|
||||
NULL, // process security attributes
|
||||
NULL, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
NULL, // use parent's environment
|
||||
NULL, // use parent's current directory
|
||||
&siStartInfo, // STARTUPINFO pointer
|
||||
&piProcInfo // receives PROCESS_INFORMATION
|
||||
);
|
||||
|
||||
CloseHandle(hChildPipeWrite);
|
||||
if(!success)
|
||||
{
|
||||
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||
return "command not found";
|
||||
return "CreateProcessA() failed";
|
||||
}
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hProcess = piProcInfo.hProcess;
|
||||
FF_MAYBE_UNUSED FF_AUTO_CLOSE_FD HANDLE hThread = piProcInfo.hThread;
|
||||
CloseHandle(piProcInfo.hThread); // we don't need the thread handle
|
||||
outHandle->pid = piProcInfo.hProcess;
|
||||
outHandle->pipeRead = hChildPipeRead;
|
||||
hChildPipeRead = INVALID_HANDLE_VALUE; // ownership transferred, don't close it
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
{
|
||||
assert(handle->pipeRead != INVALID_HANDLE_VALUE);
|
||||
assert(handle->pid != INVALID_HANDLE_VALUE);
|
||||
|
||||
int32_t timeout = instance.config.general.processingTimeout;
|
||||
FF_AUTO_CLOSE_FD HANDLE hProcess = handle->pid;
|
||||
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = handle->pipeRead;
|
||||
handle->pid = INVALID_HANDLE_VALUE;
|
||||
handle->pipeRead = INVALID_HANDLE_VALUE;
|
||||
|
||||
char str[FF_PIPE_BUFSIZ];
|
||||
DWORD nRead = 0;
|
||||
OVERLAPPED overlapped = { };
|
||||
OVERLAPPED overlapped = {};
|
||||
// ReadFile always completes synchronously if the pipe is not created with FILE_FLAG_OVERLAPPED
|
||||
do
|
||||
{
|
||||
@@ -164,7 +180,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
break;
|
||||
|
||||
case ERROR_BROKEN_PIPE:
|
||||
return NULL;
|
||||
goto exit;
|
||||
|
||||
default:
|
||||
CancelIo(hChildPipeRead);
|
||||
@@ -175,6 +191,13 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
ffStrbufAppendNS(buffer, nRead, str);
|
||||
} while (nRead > 0);
|
||||
|
||||
exit:
|
||||
{
|
||||
DWORD exitCode = 0;
|
||||
if (GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE && exitCode != 0)
|
||||
return "Child process exited with an error";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,15 @@
|
||||
},
|
||||
"desc": "Enable or disable JSON output",
|
||||
"remark": "Shortcut for `--format json`"
|
||||
},
|
||||
{
|
||||
"long": "dynamic-interval",
|
||||
"desc": "Keep fastfetch open and update the output every <num> milliseconds",
|
||||
"remark": "0 (default) to disable the behavior; don't work with --json",
|
||||
"arg": {
|
||||
"type": "num",
|
||||
"default": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"Config": [
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "common/processing.h"
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "detection/command/command.h"
|
||||
|
||||
typedef struct FFCommandResultBundle
|
||||
{
|
||||
FFProcessHandle handle;
|
||||
const char* error;
|
||||
} FFCommandResultBundle;
|
||||
|
||||
// FIFO, non-thread-safe list of running commands
|
||||
static FFlist commandQueue = {
|
||||
.elementSize = sizeof(FFCommandResultBundle),
|
||||
};
|
||||
|
||||
static const char* spawnProcess(FFCommandOptions* options, FFProcessHandle* handle)
|
||||
{
|
||||
if (options->text.length == 0)
|
||||
return "No command text specified";
|
||||
|
||||
return ffProcessSpawn(options->param.length ? (char* const[]){
|
||||
options->shell.chars,
|
||||
options->param.chars,
|
||||
options->text.chars,
|
||||
NULL
|
||||
} : (char* const[]){
|
||||
options->shell.chars,
|
||||
options->text.chars,
|
||||
NULL
|
||||
}, options->useStdErr, handle);
|
||||
}
|
||||
|
||||
bool ffPrepareCommand(FFCommandOptions* options)
|
||||
{
|
||||
if (!options->parallel) return false;
|
||||
|
||||
FFCommandResultBundle* bundle = ffListAdd(&commandQueue);
|
||||
bundle->error = spawnProcess(options, &bundle->handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* ffDetectCommand(FFCommandOptions* options, FFstrbuf* result)
|
||||
{
|
||||
FFCommandResultBundle bundle = {};
|
||||
if (!options->parallel)
|
||||
bundle.error = spawnProcess(options, &bundle.handle);
|
||||
else if (!ffListShift(&commandQueue, &bundle))
|
||||
return "[BUG] command queue is empty";
|
||||
|
||||
if (bundle.error)
|
||||
return bundle.error;
|
||||
|
||||
bundle.error = ffProcessReadOutput(&bundle.handle, result);
|
||||
if (bundle.error)
|
||||
return bundle.error;
|
||||
|
||||
ffStrbufTrimRightSpace(result);
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "modules/command/option.h"
|
||||
|
||||
const char* ffDetectCommand(FFCommandOptions* options, FFstrbuf* result);
|
||||
@@ -10,7 +10,7 @@ const char* ffDetectDisks(FFDiskOptions* options, FFlist* disks)
|
||||
const char* error = ffDetectDisksImpl(options, disks);
|
||||
|
||||
if (error) return error;
|
||||
if (disks->length == 0) return "No disks found";
|
||||
if (disks->length == 0) return NULL;
|
||||
|
||||
//We need to sort the disks, so that we can detect, which disk a path resides on
|
||||
// For example for /boot/efi/bootmgr we need to check /boot/efi before /boot
|
||||
@@ -31,3 +31,27 @@ const char* ffDetectDisks(FFDiskOptions* options, FFlist* disks)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <fnmatch.h>
|
||||
|
||||
bool ffDiskMatchesFolderPatterns(FFstrbuf* folders, const char* path, char separator)
|
||||
{
|
||||
uint32_t startIndex = 0;
|
||||
while(startIndex < folders->length)
|
||||
{
|
||||
uint32_t sepIndex = ffStrbufNextIndexC(folders, startIndex, separator);
|
||||
|
||||
char savedSep = folders->chars[sepIndex]; // Can be '\0' if at end
|
||||
folders->chars[sepIndex] = '\0';
|
||||
|
||||
bool matched = fnmatch(&folders->chars[startIndex], path, 0) == 0;
|
||||
folders->chars[sepIndex] = savedSep;
|
||||
|
||||
if (matched) return true;
|
||||
|
||||
startIndex = sepIndex + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -35,3 +35,7 @@ typedef struct FFDisk
|
||||
const char* ffDetectDisks(FFDiskOptions* options, FFlist* disks /* list of FFDisk */);
|
||||
|
||||
const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks);
|
||||
|
||||
#ifndef _WIN32
|
||||
bool ffDiskMatchesFolderPatterns(FFstrbuf* folders, const char* path, char separator);
|
||||
#endif
|
||||
|
||||
@@ -151,6 +151,12 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
else if(!ffStrEquals(fs->f_mntonname, "/") && !ffStrStartsWith(fs->f_mntfromname, "/dev/") && !ffStrEquals(fs->f_fstypename, "zfs") && !ffStrEquals(fs->f_fstypename, "fusefs.sshfs"))
|
||||
continue;
|
||||
|
||||
if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, fs->f_mntonname, FF_DISK_FOLDER_SEPARATOR))
|
||||
continue;
|
||||
|
||||
if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, fs->f_fstypename, ':'))
|
||||
continue;
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
// f_bavail and f_ffree are signed on FreeBSD...
|
||||
if(fs->f_bavail < 0) fs->f_bavail = 0;
|
||||
|
||||
@@ -27,6 +27,12 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, path.Path(), FF_DISK_FOLDER_SEPARATOR))
|
||||
continue;
|
||||
|
||||
if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, fs.fsh_name, ':'))
|
||||
continue;
|
||||
|
||||
FFDisk* disk = (FFDisk*) ffListAdd(disks);
|
||||
|
||||
disk->bytesTotal = (uint64_t)fs.total_blocks * (uint64_t) fs.block_size;
|
||||
|
||||
@@ -297,6 +297,12 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
else if(!isPhysicalDevice(device))
|
||||
continue;
|
||||
|
||||
if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, device->mnt_dir, FF_DISK_FOLDER_SEPARATOR))
|
||||
continue;
|
||||
|
||||
if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, device->mnt_type, ':'))
|
||||
continue;
|
||||
|
||||
//We have a valid device, add it to the list
|
||||
FFDisk* disk = ffListAdd(disks);
|
||||
disk->type = FF_DISK_VOLUME_TYPE_NONE;
|
||||
|
||||
@@ -119,6 +119,12 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
else if(!isPhysicalDevice(&device))
|
||||
continue;
|
||||
|
||||
if (options->hideFolders.length && ffDiskMatchesFolderPatterns(&options->hideFolders, device.mnt_mountp, FF_DISK_FOLDER_SEPARATOR))
|
||||
continue;
|
||||
|
||||
if (options->hideFS.length && ffStrbufSeparatedContainS(&options->hideFS, device.mnt_fstype, ':'))
|
||||
continue;
|
||||
|
||||
//We have a valid device, add it to the list
|
||||
FFDisk* disk = ffListAdd(disks);
|
||||
disk->type = FF_DISK_VOLUME_TYPE_NONE;
|
||||
|
||||
@@ -53,6 +53,29 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
else if(driveType == DRIVE_NO_ROOT_DIR)
|
||||
continue;
|
||||
|
||||
if (options->hideFolders.length && ffStrbufSeparatedContain(&options->hideFolders, &buffer, FF_DISK_FOLDER_SEPARATOR))
|
||||
continue;
|
||||
|
||||
wchar_t diskName[MAX_PATH + 1], diskFileSystem[MAX_PATH + 1];
|
||||
|
||||
DWORD diskFlags;
|
||||
BOOL volumeInfoAvailable = GetVolumeInformationW(mountpoint,
|
||||
diskName, ARRAY_SIZE(diskName), //Volume name
|
||||
NULL, //Serial number
|
||||
NULL, //Max component length
|
||||
&diskFlags, //File system flags
|
||||
diskFileSystem, ARRAY_SIZE(diskFileSystem)
|
||||
);
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY diskFileSystemBuf = ffStrbufCreate();
|
||||
|
||||
if (volumeInfoAvailable)
|
||||
{
|
||||
ffStrbufSetWS(&diskFileSystemBuf, diskFileSystem);
|
||||
if (options->hideFS.length && ffStrbufSeparatedContain(&options->hideFS, &diskFileSystemBuf, ':'))
|
||||
continue;
|
||||
}
|
||||
|
||||
FFDisk* disk = ffListAdd(disks);
|
||||
|
||||
disk->filesUsed = 0;
|
||||
@@ -97,20 +120,9 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
(PULARGE_INTEGER)&disk->bytesFree
|
||||
);
|
||||
|
||||
wchar_t diskName[MAX_PATH + 1], diskFileSystem[MAX_PATH + 1];
|
||||
|
||||
DWORD diskFlags;
|
||||
BOOL result = GetVolumeInformationW(mountpoint,
|
||||
diskName, ARRAY_SIZE(diskName), //Volume name
|
||||
NULL, //Serial number
|
||||
NULL, //Max component length
|
||||
&diskFlags, //File system flags
|
||||
diskFileSystem, ARRAY_SIZE(diskFileSystem)
|
||||
);
|
||||
|
||||
if(result)
|
||||
if(volumeInfoAvailable)
|
||||
{
|
||||
ffStrbufSetWS(&disk->filesystem, diskFileSystem);
|
||||
ffStrbufInitMove(&disk->filesystem, &diskFileSystemBuf);
|
||||
ffStrbufSetWS(&disk->name, diskName);
|
||||
if(diskFlags & FILE_READ_ONLY_VOLUME)
|
||||
disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT;
|
||||
|
||||
+12
-1
@@ -58,7 +58,7 @@ typedef struct FFGpuDriverPciBusId
|
||||
uint32_t func;
|
||||
} FFGpuDriverPciBusId;
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) || defined(__GNU__)
|
||||
void ffGPUFillVendorAndName(uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu);
|
||||
void ffGPUQueryAmdGpuName(uint16_t deviceId, uint8_t revisionId, FFGPUResult* gpu);
|
||||
|
||||
@@ -73,3 +73,14 @@ const char* ffDrmDetectNouveau(FFGPUResult* gpu, int fd);
|
||||
|
||||
const char* ffGPUDetectDriverSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFGpuDriverPciBusId pciBusId);
|
||||
#endif // defined(XXX)
|
||||
|
||||
static inline uint64_t ffGPUPciAddr2Id(uint64_t domain, uint64_t bus, uint64_t device, uint64_t function)
|
||||
{
|
||||
return (domain << 16) | (bus << 8) | (device << 3) | function;
|
||||
}
|
||||
|
||||
static inline uint64_t ffGPUGeneral2Id(uint64_t originalId)
|
||||
{
|
||||
// Note: originalId may already have the MSB set
|
||||
return (1ULL << 63) | originalId;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ static void fillGPUTypeGeneric(FFGPUResult* gpu)
|
||||
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL)
|
||||
{
|
||||
// 0000:00:02.0 is reserved for Intel integrated graphics
|
||||
gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ static const char* detectByDrm(const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
case DRM_BUS_PCI:
|
||||
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(dev->deviceinfo.pci->vendor_id));
|
||||
gpu->deviceId = (dev->businfo.pci->domain * 100000ull) + (dev->businfo.pci->bus * 1000ull) + (dev->businfo.pci->dev * 10ull) + dev->businfo.pci->func;
|
||||
gpu->deviceId = ffGPUPciAddr2Id(dev->businfo.pci->domain, dev->businfo.pci->bus, dev->businfo.pci->dev, dev->businfo.pci->func);
|
||||
break;
|
||||
case DRM_BUS_HOST1X:
|
||||
ffStrbufSetS(&gpu->name, dev->deviceinfo.host1x->compatible[0]);
|
||||
@@ -179,6 +179,9 @@ static const char* detectByPci(const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
struct pci_conf* pc = &confs[i];
|
||||
|
||||
if (pc->pc_sel.pc_func > 0 && pc->pc_subclass == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/)
|
||||
continue; // Likely an auxiliary display controller (#2034)
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
|
||||
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(pc->pc_vendor));
|
||||
ffStrbufInit(&gpu->name);
|
||||
@@ -191,7 +194,7 @@ static const char* detectByPci(const FFGPUOptions* options, FFlist* gpus)
|
||||
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
|
||||
gpu->type = FF_GPU_TYPE_UNKNOWN;
|
||||
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
|
||||
gpu->deviceId = (pc->pc_sel.pc_domain * 100000ull) + (pc->pc_sel.pc_bus * 1000ull) + (pc->pc_sel.pc_dev * 10ull) + pc->pc_sel.pc_func;
|
||||
gpu->deviceId = ffGPUPciAddr2Id(pc->pc_sel.pc_domain, pc->pc_sel.pc_bus, pc->pc_sel.pc_dev, pc->pc_sel.pc_func);
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
|
||||
ffGPUDetectDriverSpecific(options, gpu, (FFGpuDriverPciBusId) {
|
||||
|
||||
@@ -313,7 +313,7 @@ const char* ffDrmDetectAsahi(FFGPUResult* gpu, int fd)
|
||||
// They removed `unstable_uabi_version` from the struct. Hopefully they won't introduce new ABI changes.
|
||||
gpu->coreCount = (int32_t) (paramsGlobal.num_clusters_total * paramsGlobal.num_cores_per_cluster);
|
||||
gpu->frequency = paramsGlobal.max_frequency_khz / 1000;
|
||||
gpu->deviceId = paramsGlobal.chip_id;
|
||||
gpu->deviceId = ffGPUGeneral2Id(paramsGlobal.chip_id);
|
||||
|
||||
if (!gpu->name.length)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_slot_match_iterator_create)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_device_next)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_system_cleanup)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_iterator_destroy)
|
||||
|
||||
{
|
||||
// Requires root access
|
||||
@@ -29,6 +30,11 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
if (dev->device_class >> 16 != 0x03 /*PCI_BASE_CLASS_DISPLAY*/)
|
||||
continue;
|
||||
|
||||
uint8_t subclass = (dev->device_class >> 8) & 0xFF;
|
||||
|
||||
if (dev->func > 0 && subclass == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/)
|
||||
continue; // Likely an auxiliary display controller (#2034)
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
|
||||
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(dev->vendor_id));
|
||||
ffStrbufInit(&gpu->name);
|
||||
@@ -40,15 +46,16 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
|
||||
gpu->type = FF_GPU_TYPE_UNKNOWN;
|
||||
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
|
||||
gpu->deviceId = ((uint64_t) dev->domain << 6) | ((uint64_t) dev->bus << 4) | ((uint64_t) dev->dev << 2) | (uint64_t) dev->func;
|
||||
gpu->deviceId = ffGPUPciAddr2Id(dev->domain, dev->bus, dev->dev, dev->func);
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
|
||||
ffGPUQueryAmdGpuName(dev->device_id, dev->revision, gpu);
|
||||
|
||||
if (gpu->name.length == 0)
|
||||
ffGPUFillVendorAndName((dev->device_class >> 8) & 0xFF, dev->vendor_id, dev->device_id, gpu);
|
||||
ffGPUFillVendorAndName(subclass, dev->vendor_id, dev->device_id, gpu);
|
||||
}
|
||||
ffpci_iterator_destroy(iter);
|
||||
|
||||
ffpci_system_cleanup();
|
||||
return NULL;
|
||||
|
||||
@@ -19,6 +19,9 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
if (dev.class_base != 0x03 /*PCI_BASE_CLASS_DISPLAY*/)
|
||||
continue;
|
||||
|
||||
if (dev.function > 0 && dev.class_sub == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/)
|
||||
continue; // Likely an auxiliary display controller (#2034)
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
|
||||
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(dev.vendor_id));
|
||||
ffStrbufInit(&gpu->name);
|
||||
@@ -30,7 +33,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
|
||||
gpu->type = FF_GPU_TYPE_UNKNOWN;
|
||||
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
|
||||
gpu->deviceId = ((uint64_t) dev.bus << 4) | ((uint64_t) dev.device << 2) | (uint64_t) dev.function;
|
||||
gpu->deviceId = ffGPUPciAddr2Id(0, dev.bus, dev.device, dev.function);
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
|
||||
|
||||
@@ -198,7 +198,7 @@ static void pciDetectIntelSpecific(const FFGPUOptions* options, FFGPUResult* gpu
|
||||
// https://patchwork.kernel.org/project/intel-gfx/patch/1422039866-11572-3-git-send-email-ville.syrjala@linux.intel.com/
|
||||
|
||||
// 0000:00:02.0 is reserved for Intel integrated graphics
|
||||
gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
|
||||
if (!drmKey) return;
|
||||
|
||||
@@ -342,6 +342,9 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
|
||||
if (sscanf(pPciPath, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4)
|
||||
return "Invalid PCI device path";
|
||||
|
||||
if (pciFunc > 0 && subclassId == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/)
|
||||
return "Likely an auxiliary display controller"; // #2034
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
|
||||
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString((uint16_t) vendorId));
|
||||
ffStrbufInit(&gpu->name);
|
||||
@@ -354,7 +357,7 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
|
||||
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
|
||||
gpu->type = FF_GPU_TYPE_UNKNOWN;
|
||||
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
|
||||
gpu->deviceId = (pciDomain * 100000ull) + (pciBus * 1000ull) + (pciDevice * 10ull) + pciFunc;
|
||||
gpu->deviceId = ffGPUPciAddr2Id(pciDomain, pciBus, pciDevice, pciFunc);
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
|
||||
char drmKeyBuffer[8];
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "gpu.h"
|
||||
#include "common/io/io.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcidevs.h>
|
||||
#include <dev/pci/pciio.h>
|
||||
|
||||
static inline int pciReadConf(int fd, uint32_t bus, uint32_t device, uint32_t func, uint32_t reg, uint32_t* result)
|
||||
{
|
||||
struct pciio_bdf_cfgreg bdfr = {
|
||||
.bus = bus,
|
||||
.device = device,
|
||||
.function = func,
|
||||
.cfgreg = {
|
||||
.reg = reg,
|
||||
},
|
||||
};
|
||||
|
||||
if (ioctl(fd, PCI_IOC_BDF_CFGREAD, &bdfr) == -1)
|
||||
return -1;
|
||||
|
||||
*result = bdfr.cfgreg.val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
char pciDevPath[] = "/dev/pciXXX";
|
||||
|
||||
for (uint32_t idev = 0; idev <= 255; idev++)
|
||||
{
|
||||
snprintf(pciDevPath + strlen("/dev/pci"), 4, "%u", idev);
|
||||
|
||||
FF_AUTO_CLOSE_FD int pcifd = open(pciDevPath, O_RDONLY | O_CLOEXEC);
|
||||
if (pcifd < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
break; // No more /dev/pciN devices
|
||||
return "open(\"/dev/pciN\", O_RDONLY | O_CLOEXEC) failed";
|
||||
}
|
||||
|
||||
struct pciio_businfo businfo;
|
||||
if (ioctl(pcifd, PCI_IOC_BUSINFO, &businfo) != 0)
|
||||
continue;
|
||||
|
||||
uint32_t bus = businfo.busno;
|
||||
for (uint32_t dev = 0; dev < businfo.maxdevs; dev++)
|
||||
{
|
||||
uint32_t maxfuncs = 0;
|
||||
for (uint32_t func = 0; func <= maxfuncs; func++)
|
||||
{
|
||||
uint32_t pciid, pciclass;
|
||||
if (pciReadConf(pcifd, bus, dev, func, PCI_ID_REG, &pciid) != 0)
|
||||
continue;
|
||||
|
||||
if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID || PCI_VENDOR(pciid) == 0)
|
||||
continue;
|
||||
|
||||
if (pciReadConf(pcifd, bus, dev, func, PCI_CLASS_REG, &pciclass) != 0)
|
||||
continue;
|
||||
|
||||
if (func == 0)
|
||||
{
|
||||
// For some reason, pciReadConf returns success even for non-existing devices.
|
||||
// So we need to check for `PCI_VENDOR(pciid) == PCI_VENDOR_INVALID` above to filter them out.
|
||||
uint32_t bhlcr;
|
||||
if (pciReadConf(pcifd, bus, dev, 0, PCI_BHLC_REG, &bhlcr) != 0)
|
||||
continue;
|
||||
|
||||
if (PCI_HDRTYPE_MULTIFN(bhlcr)) maxfuncs = 7;
|
||||
}
|
||||
|
||||
if (PCI_CLASS(pciclass) != PCI_CLASS_DISPLAY)
|
||||
continue;
|
||||
|
||||
if (func > 0 && PCI_SUBCLASS(pciclass) == PCI_SUBCLASS_DISPLAY_MISC)
|
||||
continue; // Likely an auxiliary display controller (#2034)
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
|
||||
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(PCI_VENDOR(pciid)));
|
||||
ffStrbufInit(&gpu->name);
|
||||
ffStrbufInit(&gpu->driver);
|
||||
ffStrbufInitS(&gpu->platformApi, pciDevPath);
|
||||
ffStrbufInit(&gpu->memoryType);
|
||||
gpu->index = FF_GPU_INDEX_UNSET;
|
||||
gpu->temperature = FF_GPU_TEMP_UNSET;
|
||||
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
|
||||
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
|
||||
gpu->type = FF_GPU_TYPE_UNKNOWN;
|
||||
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
|
||||
gpu->deviceId = ffGPUPciAddr2Id(0, bus, dev, func);
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
|
||||
ffGPUQueryAmdGpuName(PCI_PRODUCT(pciid), PCI_REVISION(pciid), gpu);
|
||||
if (gpu->name.length == 0)
|
||||
ffGPUFillVendorAndName(PCI_SUBCLASS(pciclass), PCI_VENDOR(pciid), PCI_PRODUCT(pciid), gpu);
|
||||
|
||||
struct pciio_drvname drvname = {
|
||||
.device = dev,
|
||||
.function = func,
|
||||
};
|
||||
if (ioctl(pcifd, PCI_IOC_DRVNAME, &drvname) == 0)
|
||||
ffStrbufInitS(&gpu->driver, drvname.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#include "gpu.h"
|
||||
#include "common/io/io.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcidevs.h>
|
||||
#include <sys/pciio.h>
|
||||
|
||||
static inline int pciReadConf(int fd, uint32_t bus, uint32_t device, uint32_t func, uint32_t reg, uint32_t* result)
|
||||
{
|
||||
struct pci_io bdfr = {
|
||||
.pi_sel = {
|
||||
.pc_bus = bus,
|
||||
.pc_dev = device,
|
||||
.pc_func = func,
|
||||
},
|
||||
.pi_reg = reg,
|
||||
.pi_width = 4,
|
||||
};
|
||||
|
||||
if (ioctl(fd, PCIOCREAD, &bdfr) == -1)
|
||||
return -1;
|
||||
|
||||
*result = bdfr.pi_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
char pciDevPath[] = "/dev/pci0";
|
||||
FF_AUTO_CLOSE_FD int pcifd = open(pciDevPath, O_RDONLY | O_CLOEXEC);
|
||||
if (pcifd < 0)
|
||||
return "open(\"/dev/pci0\", O_RDONLY | O_CLOEXEC) failed";
|
||||
|
||||
for (uint32_t bus = 0; bus <= 255; bus++)
|
||||
{
|
||||
for (uint32_t dev = 0; dev < 32; dev++)
|
||||
{
|
||||
uint32_t maxfuncs = 0;
|
||||
for (uint32_t func = 0; func <= maxfuncs; func++)
|
||||
{
|
||||
uint32_t pciid, pciclass;
|
||||
if (pciReadConf(pcifd, bus, dev, func, PCI_ID_REG, &pciid) != 0)
|
||||
continue;
|
||||
|
||||
if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID || PCI_VENDOR(pciid) == 0)
|
||||
continue;
|
||||
|
||||
if (pciReadConf(pcifd, bus, dev, func, PCI_CLASS_REG, &pciclass) != 0)
|
||||
continue;
|
||||
|
||||
if (func == 0)
|
||||
{
|
||||
// For some reason, pciReadConf returns success even for non-existing devices.
|
||||
// So we need to check for `PCI_VENDOR(pciid) == PCI_VENDOR_INVALID` above to filter them out.
|
||||
uint32_t bhlcr;
|
||||
if (pciReadConf(pcifd, bus, dev, 0, PCI_BHLC_REG, &bhlcr) != 0)
|
||||
continue;
|
||||
|
||||
if (PCI_HDRTYPE_MULTIFN(bhlcr)) maxfuncs = 7;
|
||||
}
|
||||
|
||||
if (PCI_CLASS(pciclass) != PCI_CLASS_DISPLAY)
|
||||
continue;
|
||||
|
||||
if (func > 0 && PCI_SUBCLASS(pciclass) == PCI_SUBCLASS_DISPLAY_MISC)
|
||||
continue; // Likely an auxiliary display controller (#2034)
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
|
||||
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(PCI_VENDOR(pciid)));
|
||||
ffStrbufInit(&gpu->name);
|
||||
ffStrbufInit(&gpu->driver);
|
||||
ffStrbufInitS(&gpu->platformApi, "/dev/pci0");
|
||||
ffStrbufInit(&gpu->memoryType);
|
||||
gpu->index = FF_GPU_INDEX_UNSET;
|
||||
gpu->temperature = FF_GPU_TEMP_UNSET;
|
||||
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
|
||||
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
|
||||
gpu->type = FF_GPU_TYPE_UNKNOWN;
|
||||
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
|
||||
gpu->deviceId = ffGPUPciAddr2Id(0, bus, dev, func);
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
|
||||
ffGPUQueryAmdGpuName(PCI_PRODUCT(pciid), PCI_REVISION(pciid), gpu);
|
||||
if (gpu->name.length == 0)
|
||||
ffGPUFillVendorAndName(PCI_SUBCLASS(pciclass), PCI_VENDOR(pciid), PCI_PRODUCT(pciid), gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -95,7 +95,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
{
|
||||
pciDev = (pciAddr >> 16) & 0xFFFF;
|
||||
pciFunc = pciAddr & 0xFFFF;
|
||||
gpu->deviceId = (pciBus * 1000ull) + (pciDev * 10ull) + pciFunc;
|
||||
gpu->deviceId = ffGPUPciAddr2Id(0, pciBus, pciDev, pciFunc);
|
||||
pciAddr = 1; // Set to 1 to indicate that the device is a PCI device
|
||||
FF_DEBUG("PCI location - Bus: %u, Device: %u, Function: %u, DeviceID: %llu", pciBus, pciDev, pciFunc, gpu->deviceId);
|
||||
}
|
||||
@@ -182,7 +182,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
if (ffRegReadUint64(hDirectxKey, L"AdapterLuid", &adapterLuid, NULL))
|
||||
{
|
||||
FF_DEBUG("Found adapter LUID: %llu", adapterLuid);
|
||||
if (!gpu->deviceId) gpu->deviceId = adapterLuid;
|
||||
if (!gpu->deviceId) gpu->deviceId = ffGPUGeneral2Id(adapterLuid);
|
||||
}
|
||||
|
||||
uint32_t featureLevel = 0;
|
||||
@@ -449,7 +449,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
FF_DEBUG("Using fallback GPU type detection");
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL)
|
||||
{
|
||||
gpu->type = gpu->deviceId == 20 ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
|
||||
FF_DEBUG("Intel GPU type determined: %s", gpu->type == FF_GPU_TYPE_INTEGRATED ? "Integrated" : "Discrete");
|
||||
}
|
||||
else if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET)
|
||||
|
||||
@@ -79,6 +79,13 @@ const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FF
|
||||
gpu->deviceId = 0;
|
||||
ffStrbufInitStatic(&gpu->platformApi, "DXCore");
|
||||
|
||||
LUID luid;
|
||||
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::InstanceLuid, sizeof(luid), &luid)))
|
||||
{
|
||||
static_assert(sizeof(luid) == sizeof(uint64_t), "LUID size mismatch");
|
||||
gpu->deviceId = ffGPUGeneral2Id(*(uint64_t*)&luid);
|
||||
}
|
||||
|
||||
ffStrbufInit(&gpu->driver);
|
||||
uint64_t value = 0;
|
||||
if (SUCCEEDED(adapter->GetProperty(DXCoreAdapterProperty::DriverVersion, sizeof(value), &value)))
|
||||
|
||||
@@ -181,7 +181,7 @@ const char* ffHostGetMacProductNameWithHwModel(const FFstrbuf* hwModel)
|
||||
#ifdef __x86_64__
|
||||
bool ffHostDetectMac(FFHostResult* host)
|
||||
{
|
||||
if (ffStrbufEqualS(&host->family, "Mac") && ffStrbufEqualS(&host->vendor, "Apple Inc."))
|
||||
if (ffStrbufStartsWithS(&host->family, "Mac") && ffStrbufEqualS(&host->vendor, "Apple Inc."))
|
||||
{
|
||||
const char* productName = ffHostGetMacProductNameWithHwModel(&host->name);
|
||||
if (productName)
|
||||
|
||||
@@ -16,6 +16,9 @@ const char* ffDetectIcons(FFIconsResult* result)
|
||||
ffRegReadUint(hKey, L"{645FF040-5081-101B-9F08-00AA002F954E}", &RecycleBin, NULL);
|
||||
ffRegReadUint(hKey, L"{5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0}", &ControlPanel, NULL);
|
||||
|
||||
if (ThisPC && UsersFiles && RemoteNetwork && RecycleBin && ControlPanel)
|
||||
return "All icons are hidden";
|
||||
|
||||
if (!ThisPC)
|
||||
ffStrbufAppendS(&result->icons1, "This PC, ");
|
||||
if (!UsersFiles)
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
#include "media.h"
|
||||
#include "common/io/io.h"
|
||||
|
||||
void ffDetectMediaImpl(FFMediaResult* media);
|
||||
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover);
|
||||
|
||||
const FFMediaResult* ffDetectMedia(void)
|
||||
static FFMediaResult result;
|
||||
|
||||
static void removeMediaCoverFile(void)
|
||||
{
|
||||
static FFMediaResult result;
|
||||
if (result.cover.length > 0)
|
||||
{
|
||||
ffRemoveFile(result.cover.chars);
|
||||
ffStrbufDestroy(&result.cover);
|
||||
}
|
||||
}
|
||||
|
||||
const FFMediaResult* ffDetectMedia(bool saveCover)
|
||||
{
|
||||
if (result.error.chars == NULL)
|
||||
{
|
||||
ffStrbufInit(&result.error);
|
||||
@@ -16,7 +26,9 @@ const FFMediaResult* ffDetectMedia(void)
|
||||
ffStrbufInit(&result.album);
|
||||
ffStrbufInit(&result.url);
|
||||
ffStrbufInit(&result.status);
|
||||
ffDetectMediaImpl(&result);
|
||||
ffStrbufInit(&result.cover);
|
||||
result.removeCoverAfterUse = false;
|
||||
ffDetectMediaImpl(&result, saveCover);
|
||||
|
||||
if(result.song.length == 0 && result.error.length == 0)
|
||||
ffStrbufAppendS(&result.error, "No media found");
|
||||
@@ -24,6 +36,9 @@ const FFMediaResult* ffDetectMedia(void)
|
||||
ffStrbufTrimRightSpace(&result.artist);
|
||||
ffStrbufTrimRightSpace(&result.album);
|
||||
ffStrbufTrimRightSpace(&result.player);
|
||||
|
||||
if (saveCover && result.removeCoverAfterUse)
|
||||
atexit(removeMediaCoverFile);
|
||||
}
|
||||
|
||||
return &result;
|
||||
|
||||
@@ -13,6 +13,8 @@ typedef struct FFMediaResult
|
||||
FFstrbuf album;
|
||||
FFstrbuf url;
|
||||
FFstrbuf status;
|
||||
FFstrbuf cover;
|
||||
bool removeCoverAfterUse;
|
||||
} FFMediaResult;
|
||||
|
||||
const FFMediaResult* ffDetectMedia();
|
||||
const FFMediaResult* ffDetectMedia(bool saveCover);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import <CoreServices/CoreServices.h>
|
||||
|
||||
// https://github.com/andrewwiik/iOS-Blocks/blob/master/Widgets/Music/MediaRemote.h
|
||||
extern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t dispatcher, void(^callback)(_Nullable CFDictionaryRef info)) __attribute__((weak_import));
|
||||
@@ -12,7 +13,7 @@ extern void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queu
|
||||
extern void MRMediaRemoteGetNowPlayingApplicationDisplayID(dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef displayID)) __attribute__((weak_import));
|
||||
extern void MRMediaRemoteGetNowPlayingApplicationDisplayName(int unknown, dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef name)) __attribute__((weak_import));
|
||||
|
||||
static const char* getMediaByMediaRemote(FFMediaResult* result)
|
||||
static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
|
||||
{
|
||||
#define FF_TEST_FN_EXISTANCE(fn) if (!fn) return "MediaRemote function " #fn " is not available"
|
||||
FF_TEST_FN_EXISTANCE(MRMediaRemoteGetNowPlayingInfo);
|
||||
@@ -30,6 +31,25 @@ static const char* getMediaByMediaRemote(FFMediaResult* result)
|
||||
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoTitle"), &result->song);
|
||||
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtist"), &result->artist);
|
||||
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoAlbum"), &result->album);
|
||||
|
||||
if (saveCover)
|
||||
{
|
||||
NSData* artworkData = (__bridge NSData*) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkData"));
|
||||
if (artworkData)
|
||||
{
|
||||
CFStringRef mime = (CFStringRef) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkMIMEType"));
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
FF_CFTYPE_AUTO_RELEASE CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mime, NULL);
|
||||
FF_CFTYPE_AUTO_RELEASE CFStringRef ext = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
|
||||
#pragma clang diagnostic pop
|
||||
NSString *tmpDir = NSTemporaryDirectory();
|
||||
NSString *uuid = NSUUID.UUID.UUIDString;
|
||||
NSString *path = [tmpDir stringByAppendingPathComponent:[NSString stringWithFormat:@"ff_%@.%@", uuid, ext ? (__bridge NSString *) ext : @"img"]];
|
||||
if ([artworkData writeToFile:path atomically:NO])
|
||||
ffStrbufSetS(&result->cover, path.UTF8String);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
error = "MRMediaRemoteGetNowPlayingInfo() failed";
|
||||
@@ -71,7 +91,7 @@ static const char* getMediaByMediaRemote(FFMediaResult* result)
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"), used))
|
||||
int ffPrintMediaByMediaRemote(void)
|
||||
int ffPrintMediaByMediaRemote(bool saveCover)
|
||||
{
|
||||
FFMediaResult media = {
|
||||
.status = ffStrbufCreate(),
|
||||
@@ -80,8 +100,9 @@ int ffPrintMediaByMediaRemote(void)
|
||||
.album = ffStrbufCreate(),
|
||||
.playerId = ffStrbufCreate(),
|
||||
.player = ffStrbufCreate(),
|
||||
.cover = ffStrbufCreate(),
|
||||
};
|
||||
if (getMediaByMediaRemote(&media) != NULL)
|
||||
if (getMediaByMediaRemote(&media, saveCover) != NULL)
|
||||
return 1;
|
||||
ffStrbufPutTo(&media.status, stdout);
|
||||
ffStrbufPutTo(&media.song, stdout);
|
||||
@@ -89,19 +110,21 @@ int ffPrintMediaByMediaRemote(void)
|
||||
ffStrbufPutTo(&media.album, stdout);
|
||||
ffStrbufPutTo(&media.playerId, stdout);
|
||||
ffStrbufPutTo(&media.player, stdout);
|
||||
ffStrbufWriteTo(&media.cover, stdout);
|
||||
ffStrbufDestroy(&media.status);
|
||||
ffStrbufDestroy(&media.song);
|
||||
ffStrbufDestroy(&media.artist);
|
||||
ffStrbufDestroy(&media.album);
|
||||
ffStrbufDestroy(&media.playerId);
|
||||
ffStrbufDestroy(&media.player);
|
||||
ffStrbufDestroy(&media.cover);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* getMediaByAuthorizedProcess(FFMediaResult* result)
|
||||
static const char* getMediaByAuthorizedProcess(FFMediaResult* result, bool saveCover)
|
||||
{
|
||||
// #1737
|
||||
FF_STRBUF_AUTO_DESTROY script = ffStrbufCreateF("import ctypes;ctypes.CDLL('%s').ffPrintMediaByMediaRemote()", instance.state.platform.exePath.chars);
|
||||
FF_STRBUF_AUTO_DESTROY script = ffStrbufCreateF("import ctypes;ctypes.CDLL('%s').ffPrintMediaByMediaRemote(%s)", instance.state.platform.exePath.chars, saveCover ? "True" : "False");
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
||||
|
||||
const char* error = ffProcessAppendStdOut(&buffer, (char* const[]) {
|
||||
@@ -114,7 +137,7 @@ static const char* getMediaByAuthorizedProcess(FFMediaResult* result)
|
||||
if (buffer.length == 0) return "No media found";
|
||||
|
||||
// status\ntitle\nartist\nalbum\nbundleName\nappName
|
||||
FFstrbuf* const varList[] = { &result->status, &result->song, &result->artist, &result->album, &result->playerId, &result->player };
|
||||
FFstrbuf* const varList[] = { &result->status, &result->song, &result->artist, &result->album, &result->playerId, &result->player, &result->cover };
|
||||
char* line = NULL;
|
||||
size_t len = 0;
|
||||
for (uint32_t i = 0; i < ARRAY_SIZE(varList) && ffStrbufGetline(&line, &len, &buffer); ++i)
|
||||
@@ -122,13 +145,13 @@ static const char* getMediaByAuthorizedProcess(FFMediaResult* result)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ffDetectMediaImpl(FFMediaResult* media)
|
||||
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
|
||||
{
|
||||
const char* error;
|
||||
if (@available(macOS 15.4, *))
|
||||
error = getMediaByAuthorizedProcess(media);
|
||||
error = getMediaByAuthorizedProcess(media, saveCover);
|
||||
else
|
||||
error = getMediaByMediaRemote(media);
|
||||
error = getMediaByMediaRemote(media, saveCover);
|
||||
if (error)
|
||||
ffStrbufAppendS(&media->error, error);
|
||||
else if (media->player.length == 0 && media->playerId.length > 0)
|
||||
@@ -137,5 +160,7 @@ void ffDetectMediaImpl(FFMediaResult* media)
|
||||
if (ffStrbufStartsWithIgnCaseS(&media->player, "com."))
|
||||
ffStrbufSubstrAfter(&media->player, strlen("com.") - 1);
|
||||
ffStrbufReplaceAllC(&media->player, '.', ' ');
|
||||
if (media->cover.length > 0)
|
||||
media->removeCoverAfterUse = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "common/io/io.h"
|
||||
#include "fastfetch.h"
|
||||
#include "detection/media/media.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "util/unused.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -52,21 +54,56 @@ static bool parseMprisMetadata(FFDBusData* data, DBusMessageIter* rootIterator,
|
||||
|
||||
data->lib->ffdbus_message_iter_next(&dictIterator);
|
||||
|
||||
if(!ffStrStartsWith(key, "xesam:"))
|
||||
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
|
||||
if(ffStrStartsWith(key, "xesam:"))
|
||||
{
|
||||
const char* xesam = key + strlen("xesam:");
|
||||
if(ffStrEquals(xesam, "title"))
|
||||
ffDBusGetString(data, &dictIterator, &result->song);
|
||||
else if(ffStrEquals(xesam, "album"))
|
||||
ffDBusGetString(data, &dictIterator, &result->album);
|
||||
else if(ffStrEquals(xesam, "artist"))
|
||||
ffDBusGetString(data, &dictIterator, &result->artist);
|
||||
else if(ffStrEquals(xesam, "url"))
|
||||
ffDBusGetString(data, &dictIterator, &result->url);
|
||||
|
||||
key += strlen("xesam:");
|
||||
if(ffStrEquals(key, "title"))
|
||||
ffDBusGetString(data, &dictIterator, &result->song);
|
||||
else if(ffStrEquals(key, "album"))
|
||||
ffDBusGetString(data, &dictIterator, &result->album);
|
||||
else if(ffStrEquals(key, "artist"))
|
||||
ffDBusGetString(data, &dictIterator, &result->artist);
|
||||
else if(ffStrEquals(key, "url"))
|
||||
ffDBusGetString(data, &dictIterator, &result->url);
|
||||
|
||||
if(result->song.length > 0 && result->artist.length > 0 && result->album.length > 0 && result->url.length > 0)
|
||||
break;
|
||||
if(result->song.length > 0 && result->artist.length > 0 && result->album.length > 0 && result->url.length > 0)
|
||||
break;
|
||||
}
|
||||
else if (ffStrStartsWith(key, "mpris:"))
|
||||
{
|
||||
const char* xesam = key + strlen("mpris:");
|
||||
if(ffStrEquals(xesam, "artUrl"))
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
|
||||
ffDBusGetString(data, &dictIterator, &path);
|
||||
if (ffStrbufStartsWithS(&path, "file:///"))
|
||||
{
|
||||
ffStrbufEnsureFree(&result->cover, path.length - (uint32_t) strlen("file://"));
|
||||
for (uint32_t i = (uint32_t) strlen("file://"); i < path.length; ++i)
|
||||
{
|
||||
if (path.chars[i] == '%')
|
||||
{
|
||||
if (i + 2 >= path.length)
|
||||
break;
|
||||
char str[] = { path.chars[i + 1], path.chars[i + 2], 0 };
|
||||
char* end = NULL;
|
||||
const char decodedChar = (char) strtoul(str, &end, 16);
|
||||
if (end == &str[2])
|
||||
{
|
||||
i += 2;
|
||||
ffStrbufAppendC(&result->cover, decodedChar);
|
||||
}
|
||||
else
|
||||
ffStrbufAppendC(&result->cover, '%');
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufAppendC(&result->cover, path.chars[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
|
||||
}
|
||||
@@ -241,8 +278,9 @@ static const char* getMedia(FFMediaResult* result)
|
||||
|
||||
#endif
|
||||
|
||||
void ffDetectMediaImpl(FFMediaResult* media)
|
||||
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
|
||||
{
|
||||
FF_UNUSED(saveCover); // We don't save the cover to a file for Mpris implementation
|
||||
#ifdef FF_HAVE_DBUS
|
||||
const char* error = getMedia(media);
|
||||
ffStrbufAppendS(&media->error, error);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "media.h"
|
||||
#include "media_windows.dll.h"
|
||||
|
||||
static const char* getMedia(FFMediaResult* media)
|
||||
static const char* getMedia(FFMediaResult* media, bool saveCover)
|
||||
{
|
||||
FF_LIBRARY_LOAD(libffwinrt, "dlopen libffwinrt" FF_LIBRARY_EXTENSION " failed", "libffwinrt" FF_LIBRARY_EXTENSION, 0)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libffwinrt, ffWinrtDetectMedia)
|
||||
@@ -11,7 +11,7 @@ static const char* getMedia(FFMediaResult* media)
|
||||
|
||||
FFWinrtMediaResult result = {};
|
||||
|
||||
const char* error = ffffWinrtDetectMedia(&result);
|
||||
const char* error = ffffWinrtDetectMedia(&result, saveCover);
|
||||
if (error)
|
||||
{
|
||||
ffStrbufSetStatic(&media->error, error);
|
||||
@@ -32,12 +32,15 @@ static const char* getMedia(FFMediaResult* media)
|
||||
ffStrbufSetWS(&media->song, result.song);
|
||||
ffStrbufSetWS(&media->artist, result.artist);
|
||||
ffStrbufSetWS(&media->album, result.album);
|
||||
ffStrbufSetWS(&media->cover, result.cover);
|
||||
ffStrbufSetStatic(&media->status, result.status);
|
||||
if (media->cover.length > 0)
|
||||
media->removeCoverAfterUse = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ffDetectMediaImpl(FFMediaResult* media)
|
||||
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
|
||||
{
|
||||
const char* error = getMedia(media);
|
||||
const char* error = getMedia(media, saveCover);
|
||||
ffStrbufAppendS(&media->error, error);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Media.Control.h>
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <wchar.h>
|
||||
#include <windows.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "media_windows.dll.h"
|
||||
|
||||
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result)
|
||||
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result, bool saveCover)
|
||||
{
|
||||
// C++/WinRT requires Windows 8.1+ and C++ runtime (std::string, exceptions and other stuff)
|
||||
// Make it a separate dll in order not to break Windows 7 support
|
||||
@@ -47,15 +50,55 @@ const char* ffWinrtDetectMedia(FFWinrtMediaResult* result)
|
||||
}
|
||||
|
||||
::wcsncpy(result->playerId, session.SourceAppUserModelId().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
|
||||
result->playerId[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
|
||||
::wcsncpy(result->song, mediaProps.Title().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
|
||||
result->song[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
|
||||
::wcsncpy(result->artist, mediaProps.Artist().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
|
||||
result->artist[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
|
||||
::wcsncpy(result->album, mediaProps.AlbumTitle().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
|
||||
result->album[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
|
||||
try
|
||||
{
|
||||
// Only works for UWP apps
|
||||
::wcsncpy(result->playerName, AppInfo::GetFromAppUserModelId(session.SourceAppUserModelId()).DisplayInfo().DisplayName().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
|
||||
result->playerName[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
|
||||
} catch (...) { }
|
||||
|
||||
if (saveCover)
|
||||
{
|
||||
using namespace winrt::Windows::Storage;
|
||||
using namespace winrt::Windows::Storage::Streams;
|
||||
if (auto thumbRef = mediaProps.Thumbnail())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (auto stream = thumbRef.OpenReadAsync().get())
|
||||
{
|
||||
if (stream.Size() > 0)
|
||||
{
|
||||
Buffer buffer(static_cast<uint32_t>(stream.Size()));
|
||||
stream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None).get();
|
||||
|
||||
wchar_t tempPath[MAX_PATH];
|
||||
if (GetTempPathW(MAX_PATH, tempPath) > 0)
|
||||
{
|
||||
auto tempFolder = StorageFolder::GetFolderFromPathAsync(tempPath).get();
|
||||
auto tempFile = tempFolder.CreateFileAsync(L"ff_thumb.img", CreationCollisionOption::GenerateUniqueName).get();
|
||||
FileIO::WriteBufferAsync(tempFile, buffer).get();
|
||||
|
||||
::wcsncpy(result->cover, tempFile.Path().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
|
||||
result->cover[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Ignore thumbnail errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
catch (...)
|
||||
|
||||
@@ -9,8 +9,9 @@ typedef struct FFWinrtMediaResult
|
||||
wchar_t song[FF_MEDIA_WIN_RESULT_BUFLEN];
|
||||
wchar_t artist[FF_MEDIA_WIN_RESULT_BUFLEN];
|
||||
wchar_t album[FF_MEDIA_WIN_RESULT_BUFLEN];
|
||||
wchar_t cover[FF_MEDIA_WIN_RESULT_BUFLEN];
|
||||
const char* status;
|
||||
} FFWinrtMediaResult;
|
||||
|
||||
__attribute__((__dllexport__))
|
||||
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result);
|
||||
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result, bool saveCover);
|
||||
|
||||
@@ -240,7 +240,7 @@ FF_MAYBE_UNUSED static bool detectDebianDerived(FFOSResult* result)
|
||||
ffStrbufSetStatic(&result->id, "raspbian");
|
||||
ffStrbufSetStatic(&result->idLike, "debian");
|
||||
ffStrbufSetStatic(&result->name, "Raspberry Pi OS");
|
||||
ffStrbufSetStatic(&result->prettyName, "Raspberry Pi OS");
|
||||
getDebianVersion(result);
|
||||
return true;
|
||||
}
|
||||
else if (ffPathExists("/boot/dietpi/.version", FF_PATHTYPE_FILE))
|
||||
|
||||
@@ -70,6 +70,10 @@ const char* ffDetectPublicIp(FFPublicIPOptions* options, FFPublicIpResult* resul
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY response = ffStrbufCreateA(4096);
|
||||
const char* error = ffNetworkingRecvHttpResponse(state, &response);
|
||||
|
||||
*state = (FFNetworkingState){};
|
||||
*status = FF_UNITIALIZED;
|
||||
|
||||
if (error == NULL)
|
||||
ffStrbufSubstrAfterFirstS(&response, "\r\n\r\n");
|
||||
else
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "util/binary.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#ifdef __FreeBSD__
|
||||
#include <paths.h>
|
||||
#ifndef _PATH_LOCALBASE
|
||||
@@ -85,8 +86,10 @@ static bool getShellVersionFish(FFstrbuf* exe, FFstrbuf* version)
|
||||
if(!getExeVersionRaw(exe, version))
|
||||
return false;
|
||||
|
||||
//fish, version 4.0.2-1 (Built by MSYS2 project)
|
||||
ffStrbufSubstrAfterFirstS(version, "version ");
|
||||
//fish, version 4.0.2-1 (Built by MSYS2 project) // version can be localized if LC_ALL is set
|
||||
if (version->length < strlen("fish, v") || !ffStrbufStartsWithS(version, "fish")) return false;
|
||||
uint32_t index = ffStrbufNextIndexC(version, strlen("fish, "), ' ');
|
||||
ffStrbufSubstrAfter(version, index);
|
||||
ffStrbufSubstrBeforeFirstC(version, ' ');
|
||||
return true;
|
||||
}
|
||||
@@ -291,6 +294,8 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath,
|
||||
return getShellVersionAsh(exe, version);
|
||||
if(ffStrEqualsIgnCase(exeName, "xonsh"))
|
||||
return getShellVersionXonsh(exe, version);
|
||||
if(ffStrEqualsIgnCase(exeName, "brush"))
|
||||
return getExeVersionGeneral(exe, version); // brush 0.2.23 (git:2835487)
|
||||
|
||||
#ifdef _WIN32
|
||||
if(ffStrEqualsIgnCase(exeName, "powershell") || ffStrEqualsIgnCase(exeName, "powershell_ise"))
|
||||
|
||||
@@ -57,6 +57,9 @@ static pid_t getShellInfo(FFShellResult* result, pid_t pid)
|
||||
ffStrbufEqualS(&result->processName, "flashfetch") ||
|
||||
ffStrbufEqualS(&result->processName, "proot") ||
|
||||
ffStrbufEqualS(&result->processName, "script") ||
|
||||
#ifdef __linux__
|
||||
ffStrbufEqualS(&result->processName, "run-parts") ||
|
||||
#endif
|
||||
ffStrbufContainS(&result->processName, "debug") ||
|
||||
ffStrbufContainS(&result->processName, "command-not-") ||
|
||||
ffStrbufEndsWithS(&result->processName, ".sh")
|
||||
@@ -113,6 +116,7 @@ static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid)
|
||||
#ifdef __linux__
|
||||
ffStrbufStartsWithS(&result->processName, "Relay(") || // Unknown process in WSL2
|
||||
ffStrbufStartsWithS(&result->processName, "flatpak-") || // #707
|
||||
ffStrbufEqualS(&result->processName, "run-parts") || // #2048
|
||||
#endif
|
||||
ffStrbufEndsWithS(&result->processName, ".sh")
|
||||
)
|
||||
|
||||
@@ -44,6 +44,10 @@ const char* ffDetectWeather(FFWeatherOptions* options, FFstrbuf* result)
|
||||
|
||||
ffStrbufEnsureFree(result, 4095);
|
||||
const char* error = ffNetworkingRecvHttpResponse(&state, result);
|
||||
|
||||
state = (FFNetworkingState){};
|
||||
status = FF_UNITIALIZED;
|
||||
|
||||
if (error == NULL)
|
||||
{
|
||||
ffStrbufSubstrAfterFirstS(result, "\r\n\r\n");
|
||||
|
||||
+29
-4
@@ -3,6 +3,7 @@
|
||||
#include "common/init.h"
|
||||
#include "common/io/io.h"
|
||||
#include "common/jsonconfig.h"
|
||||
#include "common/time.h"
|
||||
#include "detection/version/version.h"
|
||||
#include "logo/logo.h"
|
||||
#include "util/stringUtils.h"
|
||||
@@ -670,6 +671,8 @@ 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
|
||||
return;
|
||||
|
||||
@@ -768,15 +771,31 @@ static void run(FFdata* data)
|
||||
if (!instance.config.display.noBuffer) fflush(stdout);
|
||||
#endif
|
||||
|
||||
if (useJsonConfig)
|
||||
ffPrintJsonConfig(false, instance.state.resultDoc);
|
||||
else
|
||||
ffPrintCommandOption(data, instance.state.resultDoc);
|
||||
while (true)
|
||||
{
|
||||
if (useJsonConfig)
|
||||
ffPrintJsonConfig(false, instance.state.resultDoc);
|
||||
else
|
||||
ffPrintCommandOption(data, instance.state.resultDoc);
|
||||
|
||||
if (instance.state.dynamicInterval > 0)
|
||||
{
|
||||
fflush(stdout);
|
||||
ffTimeSleep(instance.state.dynamicInterval);
|
||||
fputs("\e[H", stdout);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (instance.state.resultDoc)
|
||||
yyjson_mut_write_fp(stdout, instance.state.resultDoc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES | YYJSON_WRITE_NEWLINE_AT_END, NULL, NULL);
|
||||
else
|
||||
{
|
||||
if (instance.config.logo.printRemaining)
|
||||
ffLogoPrintRemaining();
|
||||
ffFinish();
|
||||
}
|
||||
}
|
||||
|
||||
static void writeConfigFile(FFdata* data)
|
||||
@@ -835,6 +854,12 @@ int main(int argc, char** argv)
|
||||
};
|
||||
|
||||
parseArguments(&data, argc, argv, parseCommand);
|
||||
if(instance.state.dynamicInterval && instance.state.resultDoc)
|
||||
{
|
||||
fprintf(stderr, "Error: --dynamic-interval cannot be used with --json\n");
|
||||
exit(400);
|
||||
}
|
||||
|
||||
if(!data.configLoaded && !getenv("NO_CONFIG"))
|
||||
parseConfigFiles();
|
||||
parseArguments(&data, argc, argv, (void*) parseOption);
|
||||
|
||||
@@ -46,6 +46,7 @@ typedef struct FFstate
|
||||
yyjson_mut_doc* resultDoc;
|
||||
FFstrbuf genConfigPath;
|
||||
bool fullConfig;
|
||||
uint32_t dynamicInterval;
|
||||
} FFstate;
|
||||
|
||||
typedef struct FFinstance
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include "common/init.h"
|
||||
#include "logo/logo.h"
|
||||
#include "modules/modules.h"
|
||||
|
||||
// A dirty replicate of neofetch
|
||||
@@ -136,6 +137,7 @@ int main(void)
|
||||
ffPrintColors(&options);
|
||||
}
|
||||
|
||||
ffLogoPrintRemaining();
|
||||
ffFinish();
|
||||
ffDestroyInstance();
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.::-::::. :======---:.
|
||||
.-=++++++++==-:. .:-++===--=**+=:
|
||||
.=. ...:::::--=****+++=-:. --
|
||||
-: $2.-==-.$1 .::. ::
|
||||
. $2:*%@@@@@#. .:::.
|
||||
:#%%%%%@%#+. :*##%%%%#+.
|
||||
*%%%%%%*. =. +*#%%%%%%%#:
|
||||
*%%%%%%- +%%%%%%#@=
|
||||
:%%%%%%#-. .:+%%%%%%%%+
|
||||
:*%%%%%%+. .-+*###*=:
|
||||
:=+=-.
|
||||
@@ -0,0 +1,16 @@
|
||||
__agggg
|
||||
_g@@@@@@@P
|
||||
a@@@@@@@@F
|
||||
_@@@@@@@@F
|
||||
$2a@@@@@@_ _@@@@@@_$1`
|
||||
$2_a@@@~@@@@, @@@@~M@@&_
|
||||
_@@@~ `@@@@ @@@@~ `@@@_
|
||||
@@@" ~@@@@ @@@@~ "@@@
|
||||
@@@ ~@@@@ @@@@F @@@
|
||||
@@@ 4@@@@ @@@@F @@@
|
||||
@@@_ 4@@@b @@@@F _@@@
|
||||
"@@@_ 4@@@yy@@@F _@@@"
|
||||
"R@@g_ 5@@@@@@@ _g@@@"
|
||||
~@@@gy_ @@@@@@ _yg@@@~
|
||||
`T@@@@gyyy@@@@yyag@@@@F~
|
||||
~~FRR@@@@@@RPF~~
|
||||
@@ -2856,6 +2856,17 @@ static const FFlogo L[] = {
|
||||
};
|
||||
|
||||
static const FFlogo M[] = {
|
||||
// Macaroni
|
||||
{
|
||||
.names = {"Macaroni"},
|
||||
.lines = FASTFETCH_DATATEXT_LOGO_MACARONIOS,
|
||||
.colors = {
|
||||
FF_COLOR_FG_YELLOW,
|
||||
FF_COLOR_FG_WHITE,
|
||||
},
|
||||
.colorKeys = FF_COLOR_FG_YELLOW,
|
||||
.colorTitle = FF_COLOR_FG_GREEN,
|
||||
},
|
||||
// MacOS
|
||||
{
|
||||
.names = {"macos", "mac"},
|
||||
@@ -5200,6 +5211,15 @@ static const FFlogo V[] = {
|
||||
FF_COLOR_FG_BLUE,
|
||||
},
|
||||
},
|
||||
// VincentOS
|
||||
{
|
||||
.names = {"VincentOS"},
|
||||
.lines = FASTFETCH_DATATEXT_LOGO_VINCENTOS,
|
||||
.colors = {
|
||||
FF_COLOR_FG_GREEN,
|
||||
FF_COLOR_FG_DEFAULT,
|
||||
},
|
||||
},
|
||||
// Vnux
|
||||
{
|
||||
.names = {"Vnux"},
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
#include "common/io/io.h"
|
||||
#include "common/printing.h"
|
||||
#include "common/processing.h"
|
||||
#include "detection/media/media.h"
|
||||
#include "detection/os/os.h"
|
||||
#include "detection/terminalshell/terminalshell.h"
|
||||
#include "util/textModifier.h"
|
||||
#include "util/stringUtils.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFLogoSize
|
||||
@@ -483,6 +485,15 @@ static bool updateLogoPath(void)
|
||||
if (ffStrbufEqualS(&options->source, "-")) // stdin
|
||||
return true;
|
||||
|
||||
if (ffStrbufIgnCaseEqualS(&options->source, "media-cover"))
|
||||
{
|
||||
const FFMediaResult* media = ffDetectMedia(true);
|
||||
if (media->cover.length == 0)
|
||||
return false;
|
||||
ffStrbufSet(&options->source, &media->cover);
|
||||
return true;
|
||||
}
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreate();
|
||||
if (ffPathExpandEnv(options->source.chars, &fullPath) && ffPathExists(fullPath.chars, FF_PATHTYPE_FILE))
|
||||
{
|
||||
@@ -706,6 +717,9 @@ void ffLogoPrintLine(void)
|
||||
if(instance.state.logoWidth > 0)
|
||||
printf("\033[%uC", instance.state.logoWidth);
|
||||
|
||||
if (instance.state.dynamicInterval > 0)
|
||||
fputs("\033[K", stdout);
|
||||
|
||||
++instance.state.keysHeight;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,20 @@
|
||||
#include "common/printing.h"
|
||||
#include "common/jsonconfig.h"
|
||||
#include "common/processing.h"
|
||||
#include "modules/command/command.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "detection/command/command.h"
|
||||
|
||||
bool ffPrintCommand(FFCommandOptions* options)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
const char* error = ffProcessAppendStdOut(&result, options->param.length ? (char* const[]){
|
||||
options->shell.chars,
|
||||
options->param.chars,
|
||||
options->text.chars,
|
||||
NULL
|
||||
} : (char* const[]){
|
||||
options->shell.chars,
|
||||
options->text.chars,
|
||||
NULL
|
||||
});
|
||||
const char* error = ffDetectCommand(options, &result);
|
||||
|
||||
if(error)
|
||||
if (error)
|
||||
{
|
||||
ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!result.length)
|
||||
if (!result.length)
|
||||
{
|
||||
ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No result generated");
|
||||
return false;
|
||||
@@ -72,6 +62,18 @@ void ffParseCommandJsonObject(FFCommandOptions* options, yyjson_val* module)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unsafe_yyjson_equals_str(key, "useStdErr"))
|
||||
{
|
||||
options->useStdErr = yyjson_get_bool(val);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unsafe_yyjson_equals_str(key, "parallel"))
|
||||
{
|
||||
options->parallel = yyjson_get_bool(val);
|
||||
continue;
|
||||
}
|
||||
|
||||
ffPrintError(FF_COMMAND_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
|
||||
}
|
||||
}
|
||||
@@ -81,25 +83,16 @@ void ffGenerateCommandJsonConfig(FFCommandOptions* options, yyjson_mut_doc* doc,
|
||||
ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
|
||||
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "shell", &options->shell);
|
||||
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "param", &options->param);
|
||||
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "text", &options->text);
|
||||
yyjson_mut_obj_add_bool(doc, module, "useStdErr", options->useStdErr);
|
||||
yyjson_mut_obj_add_bool(doc, module, "parallel", options->parallel);
|
||||
}
|
||||
|
||||
bool ffGenerateCommandJsonResult(FF_MAYBE_UNUSED FFCommandOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
const char* error = ffProcessAppendStdOut(&result, options->param.length ? (char* const[]){
|
||||
options->shell.chars,
|
||||
options->param.chars,
|
||||
options->text.chars,
|
||||
NULL
|
||||
} : (char* const[]){
|
||||
options->shell.chars,
|
||||
options->text.chars,
|
||||
NULL
|
||||
});
|
||||
const char* error = ffDetectCommand(options, &result);
|
||||
|
||||
if(error)
|
||||
{
|
||||
@@ -137,6 +130,8 @@ void ffInitCommandOptions(FFCommandOptions* options)
|
||||
#endif
|
||||
);
|
||||
ffStrbufInit(&options->text);
|
||||
options->useStdErr = false;
|
||||
options->parallel = true;
|
||||
}
|
||||
|
||||
void ffDestroyCommandOptions(FFCommandOptions* options)
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#define FF_COMMAND_MODULE_NAME "Command"
|
||||
|
||||
bool ffPrepareCommand(FFCommandOptions* options);
|
||||
|
||||
bool ffPrintCommand(FFCommandOptions* options);
|
||||
void ffInitCommandOptions(FFCommandOptions* options);
|
||||
void ffDestroyCommandOptions(FFCommandOptions* options);
|
||||
|
||||
@@ -9,6 +9,8 @@ typedef struct FFCommandOptions
|
||||
FFstrbuf shell;
|
||||
FFstrbuf param;
|
||||
FFstrbuf text;
|
||||
bool useStdErr;
|
||||
bool parallel;
|
||||
} FFCommandOptions;
|
||||
|
||||
static_assert(sizeof(FFCommandOptions) <= FF_OPTION_MAX_SIZE, "FFCommandOptions size exceeds maximum allowed size");
|
||||
|
||||
+7
-31
@@ -189,30 +189,6 @@ static void printDisk(FFDiskOptions* options, const FFDisk* disk, uint32_t index
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool isMatchFolders(FFstrbuf* folders, const FFstrbuf* path, char separator)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
uint32_t startIndex = 0;
|
||||
while(startIndex < folders->length)
|
||||
{
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(folders, startIndex, separator);
|
||||
|
||||
char savedColon = folders->chars[colonIndex]; // Can be '\0' if at end
|
||||
folders->chars[colonIndex] = '\0';
|
||||
|
||||
bool matched = fnmatch(&folders->chars[startIndex], path->chars, 0) == 0;
|
||||
folders->chars[colonIndex] = savedColon;
|
||||
|
||||
if (matched) return true;
|
||||
|
||||
startIndex = colonIndex + 1;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return ffStrbufSeparatedContain(folders, path, separator);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ffPrintDisk(FFDiskOptions* options)
|
||||
{
|
||||
FF_LIST_AUTO_DESTROY disks = ffListCreate(sizeof (FFDisk));
|
||||
@@ -224,18 +200,18 @@ bool ffPrintDisk(FFDiskOptions* options)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(disks.length == 0)
|
||||
{
|
||||
ffPrintError(FF_DISK_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No disks found");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t index = 0;
|
||||
FF_LIST_FOR_EACH(FFDisk, disk, disks)
|
||||
{
|
||||
if(__builtin_expect(options->folders.length == 0, 1) && (disk->type & ~options->showTypes))
|
||||
continue;
|
||||
|
||||
if (options->hideFolders.length && isMatchFolders(&options->hideFolders, &disk->mountpoint, FF_DISK_FOLDER_SEPARATOR))
|
||||
continue;
|
||||
|
||||
if (options->hideFS.length && ffStrbufSeparatedContain(&options->hideFS, &disk->filesystem, ':'))
|
||||
continue;
|
||||
|
||||
printDisk(options, disk, ++index);
|
||||
}
|
||||
|
||||
@@ -407,7 +383,7 @@ bool ffGenerateDiskJsonResult(FFDiskOptions* options, yyjson_mut_doc* doc, yyjso
|
||||
|
||||
if(error)
|
||||
{
|
||||
yyjson_mut_obj_add_str(doc, module, "result", error);
|
||||
yyjson_mut_obj_add_str(doc, module, "error", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -386,9 +386,12 @@ bool ffGenerateGPUJsonResult(FFGPUOptions* options, yyjson_mut_doc* doc, yyjson_
|
||||
{
|
||||
case FF_GPU_TYPE_INTEGRATED: type = "Integrated"; break;
|
||||
case FF_GPU_TYPE_DISCRETE: type = "Discrete"; break;
|
||||
default: type = "Unknown"; break;
|
||||
default: type = NULL; break;
|
||||
}
|
||||
yyjson_mut_obj_add_str(doc, obj, "type", type);
|
||||
if (type)
|
||||
yyjson_mut_obj_add_str(doc, obj, "type", type);
|
||||
else
|
||||
yyjson_mut_obj_add_null(doc, obj, "type");
|
||||
|
||||
yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &gpu->vendor);
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ static bool artistInSongTitle(const FFstrbuf* song, const FFstrbuf* artist)
|
||||
|
||||
bool ffPrintMedia(FFMediaOptions* options)
|
||||
{
|
||||
const FFMediaResult* media = ffDetectMedia();
|
||||
const FFMediaResult* media = ffDetectMedia(false);
|
||||
|
||||
if(media->error.length > 0)
|
||||
{
|
||||
@@ -128,7 +128,7 @@ void ffGenerateMediaJsonConfig(FFMediaOptions* options, yyjson_mut_doc* doc, yyj
|
||||
|
||||
bool ffGenerateMediaJsonResult(FF_MAYBE_UNUSED FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module)
|
||||
{
|
||||
const FFMediaResult* media = ffDetectMedia();
|
||||
const FFMediaResult* media = ffDetectMedia(false);
|
||||
|
||||
if(media->error.length > 0)
|
||||
{
|
||||
@@ -143,6 +143,10 @@ bool ffGenerateMediaJsonResult(FF_MAYBE_UNUSED FFMediaOptions* options, yyjson_m
|
||||
yyjson_mut_obj_add_strbuf(doc, song, "artist", &media->artist);
|
||||
yyjson_mut_obj_add_strbuf(doc, song, "album", &media->album);
|
||||
yyjson_mut_obj_add_strbuf(doc, song, "status", &media->status);
|
||||
if (media->cover.length > 0)
|
||||
yyjson_mut_obj_add_strbuf(doc, song, "cover", &media->cover);
|
||||
else
|
||||
yyjson_mut_obj_add_null(doc, song, "cover");
|
||||
|
||||
yyjson_mut_val* player = yyjson_mut_obj_add_obj(doc, obj, "player");
|
||||
yyjson_mut_obj_add_strbuf(doc, player, "name", &media->player);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
bool ffPrintPlayer(FFPlayerOptions* options)
|
||||
{
|
||||
const FFMediaResult* media = ffDetectMedia();
|
||||
const FFMediaResult* media = ffDetectMedia(false);
|
||||
|
||||
if(media->error.length > 0)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "common/percent.h"
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "util/FFlist.h"
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFSizeBinaryPrefixType
|
||||
{
|
||||
|
||||
@@ -40,11 +40,9 @@ const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result)
|
||||
|
||||
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result)
|
||||
{
|
||||
ffStrbufClear(result);
|
||||
if (!cf)
|
||||
{
|
||||
ffStrbufClear(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (CFGetTypeID(cf) == CFStringGetTypeID())
|
||||
{
|
||||
@@ -56,6 +54,8 @@ const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result)
|
||||
else
|
||||
{
|
||||
uint32_t length = (uint32_t) CFStringGetLength(cfStr);
|
||||
if (length == 0)
|
||||
return NULL;
|
||||
ffStrbufEnsureFixedLengthFree(result, (uint32_t) CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8));
|
||||
if(!CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8))
|
||||
return "CFStringGetCString() failed";
|
||||
@@ -68,7 +68,9 @@ const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result)
|
||||
{
|
||||
CFDataRef cfData = (CFDataRef)cf;
|
||||
uint32_t length = (uint32_t)CFDataGetLength(cfData);
|
||||
ffStrbufEnsureFree(result, length + 1);
|
||||
if (length == 0)
|
||||
return NULL;
|
||||
ffStrbufEnsureFixedLengthFree(result, length + 1);
|
||||
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*)result->chars);
|
||||
result->length = (uint32_t)strnlen(result->chars, length);
|
||||
result->chars[result->length] = '\0';
|
||||
@@ -79,6 +81,29 @@ const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result)
|
||||
{
|
||||
ffStrbufClear(result);
|
||||
if (!cf)
|
||||
return NULL;
|
||||
|
||||
if (CFGetTypeID(cf) == CFDataGetTypeID())
|
||||
{
|
||||
CFDataRef cfData = (CFDataRef)cf;
|
||||
uint32_t length = (uint32_t)CFDataGetLength(cfData);
|
||||
if (length == 0)
|
||||
return NULL;
|
||||
ffStrbufEnsureFixedLengthFree(result, length + 1);
|
||||
CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*)result->chars);
|
||||
result->length = length;
|
||||
result->chars[result->length] = '\0';
|
||||
}
|
||||
else
|
||||
return "TypeID is not 'CFData'";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result)
|
||||
{
|
||||
CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key);
|
||||
@@ -88,6 +113,15 @@ const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* r
|
||||
return ffCfStrGetString(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result)
|
||||
{
|
||||
CFTypeRef cf = (CFTypeRef)CFDictionaryGetValue(dict, key);
|
||||
if(cf == NULL)
|
||||
return "CFDictionaryGetValue() failed";
|
||||
|
||||
return ffCfDataGetDataAsString(cf, result);
|
||||
}
|
||||
|
||||
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result)
|
||||
{
|
||||
CFBooleanRef cf = (CFBooleanRef)CFDictionaryGetValue(dict, key);
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result);
|
||||
const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result);
|
||||
const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result);
|
||||
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result);
|
||||
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
||||
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result);
|
||||
const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result);
|
||||
const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result);
|
||||
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length);
|
||||
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
||||
const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result);
|
||||
|
||||
static inline CFNumberRef ffCfCreateInt(int value)
|
||||
@@ -22,6 +24,7 @@ static inline CFNumberRef ffCfCreateInt(int value)
|
||||
|
||||
static inline void cfReleaseWrapper(void* type)
|
||||
{
|
||||
assert(type);
|
||||
if (*(CFTypeRef*) type)
|
||||
CFRelease(*(CFTypeRef*) type);
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable()
|
||||
#endif
|
||||
|
||||
FF_DEBUG("Parsing SMBIOS table structures");
|
||||
int structureCount = 0;
|
||||
FF_MAYBE_UNUSED int structureCount = 0;
|
||||
for (
|
||||
const FFSmbiosHeader* header = (const FFSmbiosHeader*) buffer.chars;
|
||||
(const uint8_t*) header < (const uint8_t*) buffer.chars + buffer.length;
|
||||
|
||||
Reference in New Issue
Block a user