mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 18:06:11 +02:00
Compare commits
162 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e50421a6ce | |||
| 417b944e1c | |||
| cb8b4b335d | |||
| c0160537b0 | |||
| 4cb7a6424a | |||
| 7d6c79d73e | |||
| 8652737f64 | |||
| d6ac89ea8d | |||
| 2b620cf487 | |||
| f644df481c | |||
| e99ecacb52 | |||
| 3e22dd4b0e | |||
| 19f30ec687 | |||
| 76bee78200 | |||
| ce7455d8f0 | |||
| 5e27ee0f57 | |||
| 84a5c6ebe8 | |||
| ad7e23f64c | |||
| 2738f20eea | |||
| 09362b236c | |||
| 3cfef81877 | |||
| 3a04ae2542 | |||
| dbaf8a319d | |||
| 2fe2a21bfe | |||
| 37639c3582 | |||
| 9a74e6d7bc | |||
| 5d13389827 | |||
| 1c2c6644d5 | |||
| ffcddf30e3 | |||
| 22c92c7b05 | |||
| b569048010 | |||
| f703d9ac89 | |||
| ed6abfd626 | |||
| 50388efc78 | |||
| 7e8fb0fa14 | |||
| 9d70469665 | |||
| a94f0efb5b | |||
| da3a9be7c6 | |||
| cd8ab8e55a | |||
| 1b5b32afad | |||
| 917f784438 | |||
| c2d5d5cca1 | |||
| b435738c34 | |||
| 934905e63a | |||
| d15bb8a232 | |||
| c22bca1f87 | |||
| e04cc9c9d5 | |||
| 7a2bf4e0ac | |||
| 662f5127fa | |||
| fb9117553b | |||
| 9f24e4363f | |||
| 084f9294f2 | |||
| a9f1f9a647 | |||
| 0aed89036b | |||
| df15bbc5f2 | |||
| e6b0bd3817 | |||
| 11cd7153a2 | |||
| f8bc1cb605 | |||
| 8b8eb6f290 | |||
| 056fe21ee2 | |||
| cb0a88df22 | |||
| 298c0431c3 | |||
| 448de9afa5 | |||
| 9401a4b9ad | |||
| ea92ad3785 | |||
| 7aaad5e03e | |||
| 645bf5c839 | |||
| 7638e0e4a1 | |||
| 302b8a7b1b | |||
| dbb9d8a660 | |||
| c51a164042 | |||
| 6b8a62da6e | |||
| 578ca23e75 | |||
| 2e73a90835 | |||
| cc6273fecc | |||
| e63b777916 | |||
| 907567f511 | |||
| e0fd7308be | |||
| b761034aae | |||
| 04d6b1453d | |||
| 11d1754686 | |||
| 25e84decff | |||
| 7d665b203f | |||
| ac62442798 | |||
| 758f96de60 | |||
| db623b9c86 | |||
| c353bf3c96 | |||
| 338b552499 | |||
| 5d73cc1232 | |||
| faddef7a3b | |||
| 2d9bbc8049 | |||
| 64afaa07e7 | |||
| 5cf3ab23b0 | |||
| 3fe926d3a0 | |||
| f71bb682de | |||
| 904f860f3c | |||
| 8f15c9604c | |||
| ff28f5807b | |||
| 6a317366d7 | |||
| dc3e4a2d18 | |||
| 46c4a71ba3 | |||
| f0759acd11 | |||
| 96c7c2966d | |||
| 554e7bce4a | |||
| 70377a8afd | |||
| 51bc6d421a | |||
| be0756f8ba | |||
| 71a2cb4e1b | |||
| 2eba761bed | |||
| 4b5e1a816b | |||
| a92e4b2d67 | |||
| a32404ba98 | |||
| 0a792e4935 | |||
| 05108977e8 | |||
| 25353261d0 | |||
| 4b517263d0 | |||
| 98a65966e7 | |||
| 32dcf9487f | |||
| e3a698f7b1 | |||
| 1631f74700 | |||
| 78c6d71049 | |||
| 0b9ecdeade | |||
| 185a9211f1 | |||
| 24459b2fbf | |||
| 88c8dcb3e8 | |||
| f3a8fedf0a | |||
| 6b8b7c1a62 | |||
| af26828f91 | |||
| f4c43abbf7 | |||
| e84739bff0 | |||
| 66aad0e4f1 | |||
| 77c43c0214 | |||
| 31ae7a642a | |||
| 1bbb21486c | |||
| 316d8ddf9b | |||
| 5d8c21a5ca | |||
| 240ba5c00c | |||
| 554184ec72 | |||
| 7a055a2254 | |||
| 0a807ac1a3 | |||
| 4d831c8c73 | |||
| e6e78a7aae | |||
| 09bef5f41d | |||
| e394efdd85 | |||
| c10b652a2e | |||
| 1594a69d6c | |||
| 9b1e318bd1 | |||
| e292bf1dee | |||
| 55a0c6e97d | |||
| 8b8e4a40a4 | |||
| 8be070998f | |||
| 093f56a03d | |||
| bd9744c27c | |||
| adc25f4420 | |||
| 229fe7c7bf | |||
| 5d571752ae | |||
| 512ddfae6b | |||
| 7b689f94df | |||
| 9eed149b09 | |||
| 0f5d321df8 | |||
| 4e84094fd8 | |||
| 7b9fce0189 |
@@ -0,0 +1,3 @@
|
||||
github: LinusDierheimer
|
||||
ko_fi: carterli
|
||||
custom: https://paypal.me/zhangsongcui
|
||||
@@ -36,3 +36,5 @@ body:
|
||||
required: true
|
||||
- label: The ASCII art contains color codes, or the color codes are available in `os-release`
|
||||
required: true
|
||||
- label: The ASCII art contains no internal paddings (spaces at the start and the end of lines)
|
||||
required: true
|
||||
|
||||
+119
-19
@@ -89,8 +89,11 @@ jobs:
|
||||
- name: cat /proc/cpuinfo
|
||||
run: cat /proc/cpuinfo
|
||||
|
||||
- name: add gcc-13 repo
|
||||
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libchafa-dev directx-headers-dev rpm ninja-build
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 g++-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev libchafa-dev directx-headers-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
@@ -104,7 +107,7 @@ jobs:
|
||||
languages: c
|
||||
|
||||
- name: configure project
|
||||
run: PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
run: CC=gcc-13 CXX=g++-13 PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig:$PKG_CONFIG_PATH cmake -GNinja -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
|
||||
- name: build project
|
||||
run: cmake --build . --target package --verbose -j4
|
||||
@@ -142,7 +145,10 @@ jobs:
|
||||
strip fastfetch && ./polyfill-glibc fastfetch --target-glibc=2.17
|
||||
strip flashfetch && ./polyfill-glibc flashfetch --target-glibc=2.17
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-polyfilled")' >> CPackConfig.cmake
|
||||
cpack
|
||||
echo 'set(CPACK_PACKAGE_RELOCATABLE OFF)' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.17)")' >> CPackConfig.cmake
|
||||
echo 'set(CPACK_RPM_SPEC_MORE_DEFINE "%global __os_install_post %{nil}")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -150,6 +156,72 @@ jobs:
|
||||
name: fastfetch-linux-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
linux-i686:
|
||||
name: Linux-i686
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
|
||||
- name: cat /etc/os-release
|
||||
run: cat /etc/os-release
|
||||
|
||||
- name: cat /proc/cpuinfo
|
||||
run: cat /proc/cpuinfo
|
||||
|
||||
- name: add gcc-13 repo
|
||||
run: sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
|
||||
- name: install required packages
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-13 gcc-13-multilib libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev libddcutil-dev rpm ninja-build
|
||||
|
||||
- name: install linuxbrew packages
|
||||
run: |
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
/home/linuxbrew/.linuxbrew/bin/brew install imagemagick chafa --ignore-dependencies
|
||||
|
||||
- name: cmake version
|
||||
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 -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
|
||||
|
||||
- name: run fastfetch
|
||||
run: time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
|
||||
- name: run fastfetch --format json
|
||||
run: time ./fastfetch -c presets/ci.jsonc --format json
|
||||
|
||||
- name: run flashfetch
|
||||
run: time ./flashfetch
|
||||
|
||||
- name: print dependencies
|
||||
run: ldd fastfetch
|
||||
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-linux-i686
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
linux-armv7l:
|
||||
name: Linux-armv7l
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -169,12 +241,13 @@ jobs:
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y ca-certificates gpg curl
|
||||
apt-get update && apt-get install -y software-properties-common ca-certificates gpg curl
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
curl -L https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ focal main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
|
||||
echo -e 'Acquire::https::Verify-Peer "false";\nAcquire::https::Verify-Host "false";' >> /etc/apt/apt.conf.d/99ignore-certificates
|
||||
apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
apt-get update && apt-get install -y cmake make gcc-13 libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
@@ -209,8 +282,8 @@ jobs:
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y wget
|
||||
apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
apt-get install -y cmake make gcc libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libelf-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
@@ -246,12 +319,14 @@ jobs:
|
||||
id: runcmd
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
distro: ubuntu20.04
|
||||
distro: ubuntu22.04
|
||||
githubToken: ${{ github.token }}
|
||||
run: |
|
||||
uname -a
|
||||
apt-get update && apt-get install -y cmake make g++ libvulkan-dev libwayland-dev libxrandr-dev libxcb-randr0-dev libdconf-dev libdbus-1-dev libmagickcore-dev libsqlite3-dev librpm-dev libegl-dev libglx-dev ocl-icd-opencl-dev libpulse-dev libdrm-dev libchafa-dev libelf-dev rpm
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
apt-get update && apt-get install -y software-properties-common
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
apt-get update && apt-get install -y cmake make gcc-13 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 libchafa-dev libelf-dev rpm
|
||||
CC=gcc-13 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_DIRECTX_HEADERS=Off -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
@@ -371,7 +446,6 @@ jobs:
|
||||
usesh: true
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg install pkg:/package/pkg
|
||||
pkg update --accept
|
||||
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick
|
||||
|
||||
@@ -449,8 +523,9 @@ jobs:
|
||||
version: '7.7'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg_add -r cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 imagemagick chafa
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
sudo pkg_add -u
|
||||
sudo pkg_add -r llvm-19.1.7p3 cmake git pkgconf wayland vulkan-headers vulkan-loader glib2 dconf dbus sqlite3 imagemagick chafa
|
||||
CC=clang-19 cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
@@ -485,8 +560,8 @@ jobs:
|
||||
version: '10.1'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkgin -y install cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
sudo pkgin -y install clang cmake git pkgconf wayland vulkan-headers dconf dbus sqlite3 ImageMagick
|
||||
CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
@@ -518,10 +593,10 @@ jobs:
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg update
|
||||
pkg install -y cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa libelf
|
||||
pkg install -y llvm cmake git pkgconf binutils wayland vulkan-headers vulkan-loader libxcb libXrandr libX11 libdrm glib dconf dbus sqlite3-tcl egl opencl ocl-icd v4l_compat chafa libelf
|
||||
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
env CC=clang cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -DENABLE_EMBEDDED_PCIIDS=On -DENABLE_EMBEDDED_AMDGPUIDS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
@@ -634,17 +709,41 @@ jobs:
|
||||
- name: run tests
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
id: upload-unsigned-artifact
|
||||
name: upload artifacts for signing
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}
|
||||
path: |
|
||||
*.dll
|
||||
fastfetch.exe
|
||||
flashfetch.exe
|
||||
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
name: submit signing request
|
||||
uses: signpath/github-action-submit-signing-request@v1
|
||||
with:
|
||||
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
|
||||
organization-id: '${{ vars.SIGNPATH_ORG_ID }}'
|
||||
project-slug: 'fastfetch'
|
||||
signing-policy-slug: ${{ github.ref == 'refs/heads/master' && 'release-signing' || 'test-signing' }}
|
||||
github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}'
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: '.'
|
||||
|
||||
- name: create zip archive
|
||||
run: 7z a -tzip -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.zip LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: create 7z archive
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: upload artifacts
|
||||
- name: upload true artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}
|
||||
path: ./fastfetch-windows-${{ matrix.arch }}.*
|
||||
overwrite: true
|
||||
|
||||
release:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
@@ -652,6 +751,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- linux-hosts
|
||||
- linux-i686
|
||||
- linux-armv7l
|
||||
- linux-armv6l
|
||||
- linux-vms
|
||||
|
||||
+138
@@ -1,3 +1,141 @@
|
||||
# 2.56.0
|
||||
|
||||
Features:
|
||||
* Enhances config file loading. `--config` and `-c` with relative path now also searches paths defined in `fastfetch --list-config-paths` (typically `~/.config/fastfetch/`)
|
||||
* This allows users to use `fastfetch -c my-config` without needing to specify the full path.
|
||||
* Adds NUMA node count detection (CPU)
|
||||
* Exposed via `{numa-nodes}` in custom format
|
||||
* Supported on Linux, FreeBSD and Windows
|
||||
* Supports the newest Alacritty config format (#2070, TerminalFont)
|
||||
* Detects driver specific info for Zhaoxin GPUs (GPU, Linux)
|
||||
* Detects Android OEM UI for certain OSes (DE, Android)
|
||||
* Improves users detection on Linux (#2064, Users, Linux)
|
||||
* Adds systemd fallback when utmp is unavailable
|
||||
* Fixes resource leaks
|
||||
* Always reports the newest session info
|
||||
* Adds kiss package manager support (#2072, Packages, Linux)
|
||||
* Reports `sshd` if `$SSH_TTY` is not available (Terminal)
|
||||
* Zpool module rewrite (#2051, Zpool)
|
||||
* Adds new Zpool properties: allocated, guid, readOnly
|
||||
* Zpool module now uses runtime lookup for properties to ensure portability
|
||||
* Adds NetBSD (requires `sudo`) and macOS support
|
||||
* Adds `splitLines` option for Command module, which splits the output into sub modules, each containing one line of the output (Command)
|
||||
```
|
||||
* Command output:
|
||||
Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
|
||||
* Old behavior:
|
||||
Command: Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
|
||||
* With `"splitLines": true`:
|
||||
Command 1: Line 1
|
||||
Command 2: Line 2
|
||||
Command 3: Line 3
|
||||
```
|
||||
|
||||
Bugfixes:
|
||||
* Fixes {m,o}ksh version detection on Linux (Shell)
|
||||
* Fixes Alacritty config parsing for TOML format (#2070, TerminalFont)
|
||||
* Improves builtin logo printing for piping and buffering (#2065, Logo)
|
||||
* Uses absolute path when detecting shell and terminal version if available (#2067, TerminalShell)
|
||||
|
||||
Logos:
|
||||
* Updates Codex Linux logo (#2071)
|
||||
* Adds OS/2 Warp logo (#2062)
|
||||
* Adds Amiga logo (#2061)
|
||||
|
||||
# 2.55.1
|
||||
|
||||
Bugfixes:
|
||||
* Fix parallel command execution breaks randomly (#2056 / #2058, Command)
|
||||
* Regression from v2.55.0
|
||||
* Fix `dylib` searching path on macOS (macOS)
|
||||
* Regression from v2.55.0
|
||||
* Fix an uninitialized field (#2057, Display)
|
||||
|
||||
# 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 by SignPath.
|
||||
|
||||
Changes:
|
||||
* Moves macOS and Windows design language detection from the DE module to the Theme module
|
||||
|
||||
Features:
|
||||
* Adds `--json` and `-j` command line flags as a shortcut for `--format json`
|
||||
* Various improvements to the OS module (OS)
|
||||
* Displays point releases for Debian
|
||||
* Displays code names for Ubuntu
|
||||
* Displays build ID for macOS
|
||||
* Displays code names for Windows (previously shown in the Kernel module)
|
||||
* Adds basic support for Wine (Windows)
|
||||
* Adds basic support for hppa and sh architectures (CPU, Linux)
|
||||
* Improves T-Head SoC name detection from the device tree (#1997, CPU, Linux)
|
||||
* Supports glob patterns in `Disk.hideFolders` (Disk)
|
||||
* For example, `/boot/*` will match both `/boot/efi` and `/boot/firmware`
|
||||
* Adds brightness-level detection for external monitor support on Intel macOS (Brightness, macOS)
|
||||
* Adds configurable spacing between icon and text in keys
|
||||
* `display.key.type: "both-N"` where N is `0-4`
|
||||
* Useful for non-monospaced Nerd Fonts
|
||||
* Adds detection support for modern Samsung Exynos SoCs (CPU, Android)
|
||||
* Adds a new CMake option `-DENABLE_WORDEXP=<ON|OFF>` to enable or disable using `wordexp(3)` for acquiring logo file paths (`logo.source`)
|
||||
* Enabled by default for compatibility
|
||||
* Disabling this option reverts to using `glob(3)`, which is less functional but more secure
|
||||
|
||||
Bugfixes:
|
||||
* Avoids integer overflow when calculating swap size (#1988, Swap, Windows)
|
||||
* Trims whitespace from full user name (Title, macOS)
|
||||
* Fixes default font size for Ghostty (#1986, TerminalFont, Linux)
|
||||
* Works around an issue that could report impossibly high memory usage in rare cases (#1988, Memory, Linux)
|
||||
* Fixes incorrect glibc dependency in polyfilled DEB packages (#1983, Linux)
|
||||
* Fixes corrupted binaries in polyfilled RPM packages (#1990, Linux)
|
||||
* Fixes crashes on ancient Android kernels (#1993, Disk, Android)
|
||||
* Fixes incorrect usage of `glob(3)` (OpenBSD)
|
||||
* Prefers resolutions reported by RandR mode info, fixing incorrect resolutions on XFCE when DPI scaling is enabled (Display, Linux)
|
||||
* Various code cleanups and minor fixes
|
||||
|
||||
Logos:
|
||||
* Adds secureblue, PrismLinux, EmperorOS and Zraxyl
|
||||
* Updates T2
|
||||
|
||||
# 2.53.0
|
||||
|
||||
Changes:
|
||||
|
||||
+63
-69
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
|
||||
|
||||
project(fastfetch
|
||||
VERSION 2.53.0
|
||||
VERSION 2.56.0
|
||||
LANGUAGES C
|
||||
DESCRIPTION "Fast neofetch-like system information tool"
|
||||
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
|
||||
@@ -9,6 +9,17 @@ project(fastfetch
|
||||
|
||||
set(PROJECT_LICENSE "MIT license")
|
||||
|
||||
if(DEFINED CMAKE_SYSTEM_PROCESSOR_OVERRIDE) # Used by github actions for i686 build
|
||||
set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR_OVERRIDE} CACHE INTERNAL "")
|
||||
endif()
|
||||
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" CMAKE_SYSTEM_PROCESSOR)
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||
set(CMAKE_SYSTEM_PROCESSOR "amd64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(CMAKE_SYSTEM_PROCESSOR "aarch64")
|
||||
endif()
|
||||
message(STATUS "Build for system processor: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
|
||||
###################
|
||||
# Target Platform #
|
||||
###################
|
||||
@@ -81,8 +92,7 @@ cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF)
|
||||
cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" ON "LINUX" OFF)
|
||||
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)
|
||||
@@ -95,6 +105,8 @@ option(IS_MUSL "Build with musl libc" OFF) # Used by Github Actions
|
||||
option(INSTALL_LICENSE "Install license into /usr/share/licenses" ON)
|
||||
option(ENABLE_EMBEDDED_PCIIDS "Embed pci.ids into fastfetch, requires `python`" OFF)
|
||||
option(ENABLE_EMBEDDED_AMDGPUIDS "Embed amdgpu.ids into fastfetch, requires `python`" OFF)
|
||||
option(ENABLE_WORDEXP "Enable using of wordexp(3) if available, instead of glob(3)" ON)
|
||||
option(ENABLE_LIBZFS "Enable libzfs" ON)
|
||||
|
||||
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
|
||||
set(BINARY_LINK_TYPE dlopen CACHE STRING "How to link fastfetch")
|
||||
@@ -377,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
|
||||
@@ -397,6 +410,7 @@ set(LIBFASTFETCH_SRC
|
||||
src/detection/version/version.c
|
||||
src/detection/vulkan/vulkan.c
|
||||
src/detection/weather/weather.c
|
||||
src/detection/zpool/zpool.c
|
||||
src/logo/builtin.c
|
||||
src/logo/image/im6.c
|
||||
src/logo/image/im7.c
|
||||
@@ -569,7 +583,6 @@ if(LINUX)
|
||||
src/detection/de/de_linux.c
|
||||
src/detection/wmtheme/wmtheme_linux.c
|
||||
src/detection/camera/camera_linux.c
|
||||
src/detection/zpool/zpool_linux.c
|
||||
src/util/platform/FFPlatform_unix.c
|
||||
src/util/binary_linux.c
|
||||
)
|
||||
@@ -643,7 +656,6 @@ elseif(ANDROID)
|
||||
src/detection/de/de_linux.c
|
||||
src/detection/wmtheme/wmtheme_linux.c
|
||||
src/detection/camera/camera_android.c
|
||||
src/detection/zpool/zpool_nosupport.c
|
||||
src/util/platform/FFPlatform_unix.c
|
||||
src/util/binary_linux.c
|
||||
)
|
||||
@@ -726,7 +738,6 @@ elseif(FreeBSD)
|
||||
src/detection/de/de_linux.c
|
||||
src/detection/wmtheme/wmtheme_linux.c
|
||||
src/detection/camera/camera_linux.c
|
||||
src/detection/zpool/zpool_linux.c
|
||||
src/util/platform/FFPlatform_unix.c
|
||||
src/util/binary_linux.c
|
||||
)
|
||||
@@ -782,7 +793,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
|
||||
@@ -820,7 +831,6 @@ elseif(NetBSD)
|
||||
src/detection/de/de_linux.c
|
||||
src/detection/wmtheme/wmtheme_linux.c
|
||||
src/detection/camera/camera_linux.c
|
||||
src/detection/zpool/zpool_linux.c
|
||||
src/util/platform/FFPlatform_unix.c
|
||||
src/util/binary_linux.c
|
||||
)
|
||||
@@ -866,7 +876,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
|
||||
@@ -903,7 +913,6 @@ elseif(OpenBSD)
|
||||
src/detection/de/de_linux.c
|
||||
src/detection/wmtheme/wmtheme_linux.c
|
||||
src/detection/camera/camera_linux.c
|
||||
src/detection/zpool/zpool_nosupport.c
|
||||
src/util/platform/FFPlatform_unix.c
|
||||
src/util/binary_linux.c
|
||||
src/util/smbiosHelper.c
|
||||
@@ -963,7 +972,7 @@ elseif(APPLE)
|
||||
src/detection/terminalfont/terminalfont_apple.m
|
||||
src/detection/terminalshell/terminalshell_linux.c
|
||||
src/detection/terminalsize/terminalsize_linux.c
|
||||
src/detection/theme/theme_nosupport.c
|
||||
src/detection/theme/theme_apple.c
|
||||
src/detection/tpm/tpm_apple.c
|
||||
src/detection/uptime/uptime_bsd.c
|
||||
src/detection/users/users_linux.c
|
||||
@@ -973,7 +982,6 @@ elseif(APPLE)
|
||||
src/detection/de/de_nosupport.c
|
||||
src/detection/wmtheme/wmtheme_apple.m
|
||||
src/detection/camera/camera_apple.m
|
||||
src/detection/zpool/zpool_nosupport.c
|
||||
src/util/apple/cf_helpers.c
|
||||
src/util/apple/osascript.m
|
||||
src/util/apple/smc_temps.c
|
||||
@@ -1024,7 +1032,7 @@ elseif(WIN32)
|
||||
src/detection/physicalmemory/physicalmemory_linux.c
|
||||
src/detection/netio/netio_windows.c
|
||||
src/detection/opengl/opengl_windows.c
|
||||
src/detection/os/os_windows.c
|
||||
src/detection/os/os_windows.cpp
|
||||
src/detection/packages/packages_windows.c
|
||||
src/detection/poweradapter/poweradapter_nosupport.c
|
||||
src/detection/processes/processes_windows.c
|
||||
@@ -1033,7 +1041,7 @@ elseif(WIN32)
|
||||
src/detection/terminalfont/terminalfont_windows.c
|
||||
src/detection/terminalshell/terminalshell_windows.c
|
||||
src/detection/terminalsize/terminalsize_windows.c
|
||||
src/detection/theme/theme_nosupport.c
|
||||
src/detection/theme/theme_windows.c
|
||||
src/detection/tpm/tpm_windows.c
|
||||
src/detection/uptime/uptime_windows.c
|
||||
src/detection/users/users_windows.c
|
||||
@@ -1043,7 +1051,6 @@ elseif(WIN32)
|
||||
src/detection/de/de_nosupport.c
|
||||
src/detection/wmtheme/wmtheme_windows.c
|
||||
src/detection/camera/camera_windows.cpp
|
||||
src/detection/zpool/zpool_nosupport.c
|
||||
src/util/windows/getline.c
|
||||
src/util/windows/com.cpp
|
||||
src/util/windows/registry.c
|
||||
@@ -1132,7 +1139,6 @@ elseif(SunOS)
|
||||
src/detection/de/de_linux.c
|
||||
src/detection/wmtheme/wmtheme_linux.c
|
||||
src/detection/camera/camera_nosupport.c
|
||||
src/detection/zpool/zpool_linux.c
|
||||
src/util/platform/FFPlatform_unix.c
|
||||
src/util/binary_linux.c
|
||||
)
|
||||
@@ -1201,7 +1207,6 @@ elseif(Haiku)
|
||||
src/detection/de/de_nosupport.c
|
||||
src/detection/wmtheme/wmtheme_nosupport.c
|
||||
src/detection/camera/camera_nosupport.c
|
||||
src/detection/zpool/zpool_nosupport.c
|
||||
src/util/platform/FFPlatform_unix.c
|
||||
src/util/binary_linux.c
|
||||
src/util/haiku/version.cpp
|
||||
@@ -1242,7 +1247,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
|
||||
@@ -1281,7 +1286,6 @@ elseif(GNU)
|
||||
src/detection/de/de_linux.c
|
||||
src/detection/wmtheme/wmtheme_linux.c
|
||||
src/detection/camera/camera_nosupport.c
|
||||
src/detection/zpool/zpool_nosupport.c
|
||||
src/util/platform/FFPlatform_unix.c
|
||||
src/util/binary_linux.c
|
||||
)
|
||||
@@ -1328,15 +1332,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 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)
|
||||
@@ -1598,24 +1602,6 @@ ff_lib_enable(PCIACCESS
|
||||
"pciaccess"
|
||||
"pciaccess"
|
||||
)
|
||||
# The system <libzfs.h> is only usable on SunOS. We provide our local copy of it so it's always available.
|
||||
if(ENABLE_LIBZFS)
|
||||
if(BINARY_LINK_TYPE STREQUAL "dlopen")
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBZFS=1)
|
||||
else()
|
||||
set(CMAKE_REQUIRED_LIBRARIES_BACKUP ${CMAKE_REQUIRED_LIBRARIES})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} zfs)
|
||||
check_function_exists("libzfs_init" LIBZFS_FOUND)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES_BACKUP})
|
||||
if(NOT LIBZFS_FOUND)
|
||||
message(STATUS "Library: missing: LIBZFS")
|
||||
else()
|
||||
message(STATUS "Library: found LIBZFS")
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBZFS=1)
|
||||
target_link_libraries(libfastfetch PRIVATE zfs)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_THREADS)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS=1)
|
||||
@@ -1630,6 +1616,15 @@ endif()
|
||||
if(ENABLE_EMBEDDED_AMDGPUIDS)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_EMBEDDED_AMDGPUIDS=1)
|
||||
endif()
|
||||
if(ENABLE_LIBZFS)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_LIBZFS=1)
|
||||
|
||||
if(NOT BINARY_LINK_TYPE STREQUAL "dlopen")
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE "zfs"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LINUX)
|
||||
target_link_libraries(libfastfetch
|
||||
@@ -1678,11 +1673,10 @@ elseif(WIN32)
|
||||
PRIVATE "wtsapi32"
|
||||
PRIVATE "imagehlp"
|
||||
PRIVATE "cfgmgr32"
|
||||
PRIVATE "winbrand"
|
||||
PRIVATE "propsys"
|
||||
PRIVATE "secur32"
|
||||
)
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
# WoA only works on Windows 10 or higher
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE "mincore"
|
||||
@@ -1721,8 +1715,6 @@ elseif(SunOS)
|
||||
PRIVATE "socket"
|
||||
PRIVATE "kstat"
|
||||
PRIVATE "proc"
|
||||
PRIVATE "zfs"
|
||||
PRIVATE "nvpair"
|
||||
PRIVATE "devinfo"
|
||||
)
|
||||
elseif(GNU)
|
||||
@@ -1733,17 +1725,20 @@ elseif(ANDROID)
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE "m"
|
||||
)
|
||||
CHECK_LIBRARY_EXISTS(-l:libandroid-wordexp.a wordexp "" HAVE_LIBANDROID_WORDEXP_STATIC)
|
||||
if(HAVE_LIBANDROID_WORDEXP_STATIC)
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE -l:libandroid-wordexp.a
|
||||
)
|
||||
else()
|
||||
CHECK_LIBRARY_EXISTS(android-wordexp wordexp "" HAVE_LIBANDROID_WORDEXP)
|
||||
if(HAVE_LIBANDROID_WORDEXP)
|
||||
if(ENABLE_WORDEXP)
|
||||
# https://github.com/termux/termux-packages/pull/7056
|
||||
CHECK_LIBRARY_EXISTS(-l:libandroid-wordexp.a wordexp "" HAVE_LIBANDROID_WORDEXP_STATIC)
|
||||
if(HAVE_LIBANDROID_WORDEXP_STATIC)
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE android-wordexp
|
||||
PRIVATE -l:libandroid-wordexp.a
|
||||
)
|
||||
else()
|
||||
CHECK_LIBRARY_EXISTS(android-wordexp wordexp "" HAVE_LIBANDROID_WORDEXP)
|
||||
if(HAVE_LIBANDROID_WORDEXP)
|
||||
target_link_libraries(libfastfetch
|
||||
PRIVATE android-wordexp
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
elseif(Haiku)
|
||||
@@ -1805,13 +1800,17 @@ if(NOT WIN32)
|
||||
if(HAVE_UTMPX)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_UTMPX=1)
|
||||
endif()
|
||||
CHECK_INCLUDE_FILE("wordexp.h" HAVE_WORDEXP)
|
||||
if(HAVE_WORDEXP)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_WORDEXP=1)
|
||||
if(ENABLE_WORDEXP)
|
||||
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()
|
||||
CHECK_INCLUDE_FILE("glob.h" HAVE_GLOB)
|
||||
if(HAVE_GLOB)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_GLOB=1)
|
||||
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)
|
||||
@@ -2020,11 +2019,6 @@ install(
|
||||
##################
|
||||
|
||||
set(CPACK_GENERATOR "TGZ;ZIP")
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||
set(CMAKE_SYSTEM_PROCESSOR "amd64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(CMAKE_SYSTEM_PROCESSOR "aarch64")
|
||||
endif()
|
||||
if(APPLE)
|
||||
string(TOLOWER "${CMAKE_PROJECT_NAME}-macos-${CMAKE_SYSTEM_PROCESSOR}" CPACK_PACKAGE_FILE_NAME) # use macos instead of darwin
|
||||
elseif(IS_MUSL)
|
||||
@@ -2071,7 +2065,7 @@ endif()
|
||||
|
||||
set(CPACK_SET_DESTDIR ON)
|
||||
|
||||
set(CPACK_PACKAGE_CONTACT "Linus Dierheimer <Linus@Dierheimer.de>")
|
||||
set(CPACK_PACKAGE_CONTACT "Carter Li <zhangsongcui@live.cn>")
|
||||
set(CPACK_PACKAGE_DESCRIPTION "\
|
||||
fastfetch is a neofetch-like tool for fetching system information and displaying them in a pretty way. \
|
||||
It is written mostly in C to achieve much better performance.\
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Fastfetch
|
||||
|
||||
Fastfetch 是一款类似 neofetch 的系统信息展示工具,主要用 C 编写,强调性能和可定制性。支持 Linux、macOS、Windows 7+、Android、FreeBSD、OpenBSD、NetBSD、DragonFly、Haiku、SunOS。
|
||||
|
||||
示例配置见 presets/examples,更多截图与平台说明见 Wiki。
|
||||
|
||||
## 安装
|
||||
|
||||
Linux(部分):
|
||||
- Debian 13+ / Ubuntu: apt install fastfetch
|
||||
- Arch: pacman -S fastfetch
|
||||
- Fedora: dnf install fastfetch
|
||||
- openSUSE: zypper install fastfetch
|
||||
- Linuxbrew:brew install fastfetch
|
||||
- 各发行版打包状态:https://repology.org/project/fastfetch/versions
|
||||
|
||||
macOS:
|
||||
- Homebrew:brew install fastfetch
|
||||
- MacPorts:sudo port install fastfetch
|
||||
|
||||
Windows:
|
||||
- scoop install fastfetch
|
||||
- choco install fastfetch
|
||||
- winget install fastfetch
|
||||
- MSYS2:pacman -S mingw-w64-<subsystem>-<arch>-fastfetch
|
||||
|
||||
BSD:
|
||||
- FreeBSD:pkg install fastfetch
|
||||
- NetBSD:pkgin in fastfetch
|
||||
- OpenBSD:pkg_add fastfetch
|
||||
|
||||
Android(Termux):
|
||||
- pkg install fastfetch
|
||||
|
||||
Nightly 构建:
|
||||
- https://nightly.link/fastfetch-cli/fastfetch/workflows/ci/dev?preview
|
||||
|
||||
## 源码构建
|
||||
|
||||
基本上是 `cmake . && make`。详见 Wiki:https://github.com/fastfetch-cli/fastfetch/wiki/Building
|
||||
|
||||
## 使用
|
||||
|
||||
- 默认运行:`fastfetch`
|
||||
- 查看所有可用模块示例:`fastfetch -c all.jsonc`
|
||||
- 以 JSON 输出指定模块:`fastfetch -s <module1>[:<module2>] --format json`
|
||||
- 完整命令行帮助:`fastfetch --help`
|
||||
- 生成最小配置:`fastfetch --gen-config [</path/to/config.jsonc>]`
|
||||
- 生成完整配置:`fastfetch --gen-config-full`
|
||||
- 请使用支持 JSON schema 的编辑器(如 VSCode)编辑配置文件!
|
||||
- 如果你连接 Github 有网络困难(智能提示不生效),可将配置文件中的 `$schema` 的值替换为 `https://gitee.com/carterl/fastfetch/raw/dev/doc/json_schema.json`
|
||||
|
||||
## 定制
|
||||
|
||||
- 配置使用 JSONC,语法与选项见 Wiki:https://github.com/fastfetch-cli/fastfetch/wiki/Configuration
|
||||
- 预设示例位于 presets,可用 `-c <filename>` 加载
|
||||
- Logo 选项与图像显示见文档:https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options
|
||||
- 模块格式化(示例,仅显示 GPU 名称):
|
||||
```jsonc
|
||||
{
|
||||
"modules": [
|
||||
{ "type": "gpu", "format": "{name}" }
|
||||
]
|
||||
}
|
||||
```
|
||||
详见:https://github.com/fastfetch-cli/fastfetch/wiki/Format-String-Guide
|
||||
|
||||
## 反馈与支持
|
||||
|
||||
- 使用问题:Discussions https://github.com/fastfetch-cli/fastfetch/discussions
|
||||
- 疑似缺陷:Issues https://github.com/fastfetch-cli/fastfetch/issues(请填写模版)
|
||||
|
||||
## 赞助
|
||||
|
||||
<img src="https://github.com/user-attachments/assets/a36a6501-e8b0-4a10-9061-b9206d12ffba" width="220">
|
||||
@@ -11,6 +11,7 @@
|
||||
[](https://repology.org/project/fastfetch/versions)
|
||||
[](https://repology.org/project/fastfetch/versions)
|
||||
[](https://deepwiki.com/fastfetch-cli/fastfetch)
|
||||
[](README-cn.md)
|
||||
|
||||
Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying it in a visually appealing way. It is written mainly in C, with a focus on performance and customizability. Currently, it supports Linux, macOS, Windows 7+, Android, FreeBSD, OpenBSD, NetBSD, DragonFly, Haiku, and SunOS.
|
||||
|
||||
@@ -36,7 +37,8 @@ Some distributions package outdated versions of fastfetch. Older versions receiv
|
||||
<img src="https://repology.org/badge/vertical-allrepos/fastfetch.svg?columns=2" alt="Packaging status" align="right">
|
||||
</a>
|
||||
|
||||
* Debian: `apt install fastfetch` (for Debian 13 or newer)
|
||||
* Ubuntu: [`ppa:zhangsongcui3371/fastfetch`](https://launchpad.net/~zhangsongcui3371/+archive/ubuntu/fastfetch) (Ubuntu 22.04 or newer; latest version)
|
||||
* Debian / Ubuntu: `apt install fastfetch` (Debian 13 or newer; Ubuntu 25.04 or newer)
|
||||
* Debian / Ubuntu: Download `fastfetch-linux-<proper architecture>.deb` from [Github release page](https://github.com/fastfetch-cli/fastfetch/releases/latest) and double-click it (for Ubuntu 20.04 or newer and Debian 11 or newer).
|
||||
* Arch Linux: `pacman -S fastfetch`
|
||||
* Fedora: `dnf install fastfetch`
|
||||
@@ -103,6 +105,10 @@ Fastfetch uses JSONC (JSON with comments) for configuration. [See the Wiki for d
|
||||
|
||||
Logos can also be heavily customized; see the [logo documentation](https://github.com/fastfetch-cli/fastfetch/wiki/Logo-options) for more information.
|
||||
|
||||
### WARNING
|
||||
|
||||
Fastfetch supports a `Command` module that can run arbitrary shell commands. If you copy-paste a config file from an untrusted source, it may contain malicious commands that can harm your system or compromise your privacy. Please always review the config file before using it.
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q: Neofetch is good enough. Why do I need fastfetch?
|
||||
@@ -317,6 +323,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!
|
||||
|
||||
+25
-1
@@ -1,3 +1,28 @@
|
||||
fastfetch (2.55.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.55.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 17 Nov 2025 10:15:44 +0800
|
||||
|
||||
fastfetch (2.55.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.55.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 12 Nov 2025 09:15:24 +0800
|
||||
|
||||
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 +460,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
|
||||
|
||||
+40
-5
@@ -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,11 +1020,31 @@
|
||||
},
|
||||
{
|
||||
"const": "icon",
|
||||
"description": "Show icon keys (requires newest nerd font)"
|
||||
"description": "Show builtin icon (requires newest nerd font)"
|
||||
},
|
||||
{
|
||||
"const": "both",
|
||||
"description": "Show both icon and string keys"
|
||||
"description": "Show both icon and string keys (alias of `both-1`)"
|
||||
},
|
||||
{
|
||||
"const": "both-0",
|
||||
"description": "Show both icon and string with no spaces between them"
|
||||
},
|
||||
{
|
||||
"const": "both-1",
|
||||
"description": "Show both icon and string with a space between them"
|
||||
},
|
||||
{
|
||||
"const": "both-2",
|
||||
"description": "Show both icon and string with 2 spaces between them"
|
||||
},
|
||||
{
|
||||
"const": "both-3",
|
||||
"description": "Show both icon and string with 3 spaces between them"
|
||||
},
|
||||
{
|
||||
"const": "both-4",
|
||||
"description": "Show both icon and string with 4 spaces between them"
|
||||
}
|
||||
],
|
||||
"default": "string"
|
||||
@@ -2081,6 +2101,21 @@
|
||||
"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
|
||||
},
|
||||
"splitLines": {
|
||||
"description": "Set if the command output should be split into multiple lines",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/$defs/key"
|
||||
},
|
||||
@@ -2326,7 +2361,7 @@
|
||||
]
|
||||
},
|
||||
"hideFolders": {
|
||||
"description": "A list of folder paths to hide from the disk output",
|
||||
"description": "A list of folder paths (or glob patterns) to hide from the disk output",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
@@ -2342,7 +2377,7 @@
|
||||
"uniqueItems": true
|
||||
}
|
||||
],
|
||||
"default": "/efi:/boot:/boot/efi:/boot/firmware"
|
||||
"default": "/efi:/boot:/boot/*"
|
||||
},
|
||||
"hideFS": {
|
||||
"description": "A list of file systems to hide from the disk output",
|
||||
|
||||
@@ -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 {}"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "fastfetch.h"
|
||||
#include "util/FFlist.h"
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "common/font.h"
|
||||
|
||||
#include <string.h>
|
||||
@@ -192,6 +194,21 @@ void ffFontInitValues(FFfont* font, const char* name, const char* size)
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style)
|
||||
{
|
||||
ffFontInit(font);
|
||||
|
||||
if (name) ffStrbufInitMove(&font->name, name);
|
||||
if (size) ffStrbufInitMove(&font->size, size);
|
||||
if (style)
|
||||
{
|
||||
FFstrbuf* styleBuf = FF_LIST_ADD(FFstrbuf, font->styles);
|
||||
ffStrbufInitMove(styleBuf, style);
|
||||
}
|
||||
|
||||
fontInitPretty(font);
|
||||
}
|
||||
|
||||
void ffFontInitWithSpace(FFfont* font, const char* rawName)
|
||||
{
|
||||
const char* pspace = strrchr(rawName, ' ');
|
||||
|
||||
@@ -15,6 +15,7 @@ void ffFontInit(FFfont* font);
|
||||
void ffFontInitQt(FFfont* font, const char* data);
|
||||
void ffFontInitPango(FFfont* font, const char* data);
|
||||
void ffFontInitValues(FFfont* font, const char* name, const char* size);
|
||||
void ffFontInitMoveValues(FFfont* font, FFstrbuf* name, FFstrbuf* size, FFstrbuf* style);
|
||||
void ffFontInitWithSpace(FFfont* font, const char* rawName);
|
||||
void ffFontDestroy(FFfont* font);
|
||||
|
||||
|
||||
+15
-9
@@ -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
|
||||
@@ -48,7 +49,7 @@ static void defaultConfig(void)
|
||||
|
||||
void ffInitInstance(void)
|
||||
{
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?source=recommendat>
|
||||
setlocale(LC_ALL, ".UTF8");
|
||||
#else
|
||||
@@ -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
|
||||
@@ -88,10 +92,6 @@ static void exitSignalHandler(FF_MAYBE_UNUSED int signal)
|
||||
resetConsole();
|
||||
exit(0);
|
||||
}
|
||||
static void chldSignalHandler(FF_MAYBE_UNUSED int signal)
|
||||
{
|
||||
// empty; used to interrupt the poll and read syscalls
|
||||
}
|
||||
#endif
|
||||
|
||||
void ffStart(void)
|
||||
@@ -117,7 +117,10 @@ void ffStart(void)
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
sigaction(SIGQUIT, &action, NULL);
|
||||
sigaction(SIGCHLD, &(struct sigaction) { .sa_handler = chldSignalHandler }, NULL);
|
||||
sigset_t newmask;
|
||||
sigemptyset(&newmask);
|
||||
sigaddset(&newmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &newmask, NULL);
|
||||
#endif
|
||||
|
||||
//reset everything to default before we start printing
|
||||
@@ -130,14 +133,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();
|
||||
}
|
||||
|
||||
|
||||
+15
-6
@@ -171,19 +171,27 @@ static inline void ffUnsuppressIO(bool* suppressed)
|
||||
|
||||
void ffListFilesRecursively(const char* path, bool pretty);
|
||||
|
||||
static inline bool ffIsValidNativeFD(FFNativeFD fd)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return fd >= 0;
|
||||
#else
|
||||
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
|
||||
return fd != INVALID_HANDLE_VALUE && fd != NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
FF_C_NONNULL(1)
|
||||
static inline bool wrapClose(FFNativeFD* pfd)
|
||||
{
|
||||
assert(pfd);
|
||||
|
||||
#ifndef WIN32
|
||||
if (*pfd < 0)
|
||||
return false;
|
||||
if (!ffIsValidNativeFD(*pfd))
|
||||
return false;
|
||||
|
||||
#ifndef _WIN32
|
||||
close(*pfd);
|
||||
#else
|
||||
// https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443
|
||||
if (*pfd == NULL || *pfd == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
CloseHandle(*pfd);
|
||||
#endif
|
||||
|
||||
@@ -241,6 +249,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
|
||||
|
||||
+20
-11
@@ -15,11 +15,8 @@
|
||||
|
||||
#if FF_HAVE_WORDEXP
|
||||
#include <wordexp.h>
|
||||
#elif FF_HAVE_GLOB
|
||||
#warning "<wordexp.h> is not available, use <glob.h> instead"
|
||||
#include <glob.h>
|
||||
#else
|
||||
#warning "Neither <wordexp.h> nor <glob.h> is available"
|
||||
#include <glob.h>
|
||||
#endif
|
||||
|
||||
static void createSubfolders(const char* fileName)
|
||||
@@ -136,14 +133,14 @@ bool ffAppendFileBufferRelative(int dfd, const char* fileName, FFstrbuf* buffer)
|
||||
return ffAppendFDBuffer(fd, buffer);
|
||||
}
|
||||
|
||||
bool ffPathExpandEnv(FF_MAYBE_UNUSED const char* in, FF_MAYBE_UNUSED FFstrbuf* out)
|
||||
bool ffPathExpandEnv(const char* in, FFstrbuf* out)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
#if FF_HAVE_WORDEXP // https://github.com/termux/termux-packages/pull/7056
|
||||
#if FF_HAVE_WORDEXP
|
||||
|
||||
wordexp_t exp;
|
||||
if (wordexp(in, &exp, 0) != 0)
|
||||
if (wordexp(in, &exp, 0) != 0) // WARN: 0 = no safety flags; command substitution allowed
|
||||
return false;
|
||||
|
||||
if (exp.we_wordc >= 1)
|
||||
@@ -154,16 +151,23 @@ bool ffPathExpandEnv(FF_MAYBE_UNUSED const char* in, FF_MAYBE_UNUSED FFstrbuf* o
|
||||
|
||||
wordfree(&exp);
|
||||
|
||||
#elif FF_HAVE_GLOB
|
||||
#else
|
||||
|
||||
glob_t gb;
|
||||
if (glob(in, GLOB_NOSORT | GLOB_TILDE, NULL, &gb) != 0)
|
||||
if (glob(in, GLOB_NOSORT
|
||||
#ifdef GLOB_TILDE
|
||||
| GLOB_TILDE
|
||||
#endif
|
||||
#ifdef GLOB_BRACE
|
||||
| GLOB_BRACE
|
||||
#endif
|
||||
, NULL, &gb) != 0)
|
||||
return false;
|
||||
|
||||
if (gb.gl_matchc >= 1)
|
||||
if (gb.gl_pathc >= 1)
|
||||
{
|
||||
result = true;
|
||||
ffStrbufSetS(out, gb.gl_pathv[gb.gl_matchc > 1 ? ffTimeGetNow() % gb.gl_matchc : 0]);
|
||||
ffStrbufSetS(out, gb.gl_pathv[gb.gl_pathc > 1 ? ffTimeGetNow() % (unsigned) gb.gl_pathc : 0]);
|
||||
}
|
||||
|
||||
globfree(&gb);
|
||||
@@ -367,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)
|
||||
{
|
||||
|
||||
+7
-1
@@ -44,7 +44,13 @@ typedef enum __attribute__((__packed__)) FFModuleKeyType
|
||||
FF_MODULE_KEY_TYPE_NONE = 0,
|
||||
FF_MODULE_KEY_TYPE_STRING = 1 << 0,
|
||||
FF_MODULE_KEY_TYPE_ICON = 1 << 1,
|
||||
FF_MODULE_KEY_TYPE_BOTH = FF_MODULE_KEY_TYPE_STRING | FF_MODULE_KEY_TYPE_ICON,
|
||||
FF_MODULE_KEY_TYPE_SPACE_SHIFT = 4,
|
||||
FF_MODULE_KEY_TYPE_BOTH_0 = FF_MODULE_KEY_TYPE_STRING | FF_MODULE_KEY_TYPE_ICON,
|
||||
FF_MODULE_KEY_TYPE_BOTH_1 = FF_MODULE_KEY_TYPE_BOTH_0 | (1 << FF_MODULE_KEY_TYPE_SPACE_SHIFT),
|
||||
FF_MODULE_KEY_TYPE_BOTH = FF_MODULE_KEY_TYPE_BOTH_1, // alias
|
||||
FF_MODULE_KEY_TYPE_BOTH_2 = FF_MODULE_KEY_TYPE_BOTH_0 | (2 << FF_MODULE_KEY_TYPE_SPACE_SHIFT),
|
||||
FF_MODULE_KEY_TYPE_BOTH_3 = FF_MODULE_KEY_TYPE_BOTH_0 | (3 << FF_MODULE_KEY_TYPE_SPACE_SHIFT),
|
||||
FF_MODULE_KEY_TYPE_BOTH_4 = FF_MODULE_KEY_TYPE_BOTH_0 | (4 << FF_MODULE_KEY_TYPE_SPACE_SHIFT),
|
||||
FF_MODULE_KEY_TYPE_FORCE_UNSIGNED = UINT8_MAX,
|
||||
} FFModuleKeyType;
|
||||
|
||||
|
||||
@@ -28,17 +28,12 @@ void ffPrintLogoAndKey(const char* moduleName, uint8_t moduleIndex, const FFModu
|
||||
ffPrintColor(&instance.config.display.colorKeys);
|
||||
}
|
||||
|
||||
bool hasIcon = false;
|
||||
if (instance.config.display.keyType & FF_MODULE_KEY_TYPE_ICON && moduleArgs && moduleArgs->keyIcon.length > 0)
|
||||
{
|
||||
ffStrbufWriteTo(&moduleArgs->keyIcon, stdout);
|
||||
hasIcon = true;
|
||||
}
|
||||
|
||||
if (instance.config.display.keyType & FF_MODULE_KEY_TYPE_STRING)
|
||||
{
|
||||
if(hasIcon)
|
||||
putchar(' ');
|
||||
ffPrintCharTimes(' ', instance.config.display.keyType >> FF_MODULE_KEY_TYPE_SPACE_SHIFT);
|
||||
|
||||
//NULL check is required for modules with custom keys, e.g. disk with the folder path
|
||||
if((printType & FF_PRINT_TYPE_NO_CUSTOM_KEY) || !moduleArgs || moduleArgs->key.length == 0)
|
||||
@@ -136,6 +131,12 @@ void ffPrintCharTimes(char c, uint32_t times)
|
||||
if(times == 0)
|
||||
return;
|
||||
|
||||
if(times == 1)
|
||||
{
|
||||
putchar(c);
|
||||
return;
|
||||
}
|
||||
|
||||
char str[32];
|
||||
memset(str, c, sizeof(str)); //2 instructions when compiling with AVX2 enabled
|
||||
for(uint32_t i = sizeof(str); i <= times; i += (uint32_t)sizeof(str))
|
||||
|
||||
+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)
|
||||
{
|
||||
@@ -173,30 +184,13 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use
|
||||
waitpid(childPid, NULL, 0);
|
||||
return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)";
|
||||
}
|
||||
else if (pollret < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
{
|
||||
// The child process has been terminated. See `chldSignalHandler` in `common/init.c`
|
||||
if (waitpid(childPid, NULL, WNOHANG) == childPid)
|
||||
{
|
||||
// Read remaining data from the pipe
|
||||
fcntl(childPipeFd, F_SETFL, O_CLOEXEC | O_NONBLOCK);
|
||||
childPid = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kill(childPid, SIGTERM);
|
||||
waitpid(childPid, NULL, 0);
|
||||
return "poll(&pollfd, 1, timeout) error";
|
||||
}
|
||||
}
|
||||
else if (pollfd.revents & POLLERR)
|
||||
else if (pollret < 0 || (pollfd.revents & POLLERR))
|
||||
{
|
||||
kill(childPid, SIGTERM);
|
||||
waitpid(childPid, NULL, 0);
|
||||
return "poll(&pollfd, 1, timeout) error";
|
||||
return pollret < 0
|
||||
? "poll(&pollfd, 1, timeout) error: pollret < 0"
|
||||
: "poll(&pollfd, 1, timeout) error: pollfd.revents & POLLERR";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,20 +205,15 @@ 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;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
else if (nRead < 0)
|
||||
{
|
||||
if (errno == EAGAIN)
|
||||
return NULL;
|
||||
else
|
||||
break;
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return "read(childPipeFd, str, FF_PIPE_BUFSIZ) failed";
|
||||
}
|
||||
@@ -284,6 +273,10 @@ void ffProcessGetInfoLinux(pid_t pid, FFstrbuf* processName, FFstrbuf* exe, cons
|
||||
ffStrbufEnsureFixedLengthFree(exePath, (uint32_t)length);
|
||||
ffStrbufAppendNS(exePath, (uint32_t)length, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufSetS(exePath, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "common/processing.h"
|
||||
#include "common/io/io.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <windows.h>
|
||||
#include <ntstatus.h>
|
||||
#include <winternl.h>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+27
-2
@@ -63,6 +63,26 @@
|
||||
},
|
||||
"default": "default"
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "json",
|
||||
"short": "j",
|
||||
"arg": {
|
||||
"type": "bool",
|
||||
"default": false,
|
||||
"optional": true
|
||||
},
|
||||
"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": [
|
||||
@@ -70,7 +90,7 @@
|
||||
"short": "c",
|
||||
"long": "config",
|
||||
"desc": "Specify the config file or preset to load",
|
||||
"remark": "The file will be searched according to the order shown by \"fastfetch --list-config-paths\". Use \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for more info",
|
||||
"remark": "The file will be searched according to the order shown by \"fastfetch --list-config-paths\". Use \"-\" to read config from stdin or \"none\" to disable further config loading. See also https://github.com/fastfetch-cli/fastfetch/wiki/Configuration for more info",
|
||||
"arg": {
|
||||
"type": "config"
|
||||
}
|
||||
@@ -555,7 +575,12 @@
|
||||
"none": "Disable keys",
|
||||
"string": "Show string",
|
||||
"icon": "Show icon (requires newest nerd font)",
|
||||
"both": "Show both icon and string"
|
||||
"both": "Show both icon and string (alias of `both-1`)",
|
||||
"both-0": "Show both icon and string with no spaces between them",
|
||||
"both-1": "Show both icon and string with a space between them",
|
||||
"both-2": "Show both icon and string with 2 spaces between them",
|
||||
"both-3": "Show both icon and string with 3 spaces between them",
|
||||
"both-4": "Show both icon and string with 4 spaces between them"
|
||||
},
|
||||
"default": "string"
|
||||
}
|
||||
|
||||
@@ -5,69 +5,55 @@
|
||||
|
||||
const char* ffDetectBios(FFBiosResult* bios)
|
||||
{
|
||||
io_registry_entry_t registryEntry;
|
||||
|
||||
#ifndef __aarch64__
|
||||
|
||||
//https://github.com/osquery/osquery/blob/master/osquery/tables/system/darwin/smbios_tables.cpp
|
||||
//For Intel
|
||||
if((registryEntry = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/rom")))
|
||||
{
|
||||
CFMutableDictionaryRef properties;
|
||||
if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess)
|
||||
{
|
||||
IOObjectRelease(registryEntry);
|
||||
return "IORegistryEntryCreateCFProperties(registryEntry) failed";
|
||||
}
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t deviceRom = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/rom");
|
||||
if (!deviceRom)
|
||||
return "IODeviceTree:/rom not found";
|
||||
|
||||
ffCfDictGetString(properties, CFSTR("vendor"), &bios->vendor);
|
||||
ffCfDictGetString(properties, CFSTR("version"), &bios->version);
|
||||
ffCfDictGetString(properties, CFSTR("release-date"), &bios->date);
|
||||
ffStrbufSetStatic(&bios->type, "UEFI");
|
||||
FF_CFTYPE_AUTO_RELEASE CFMutableDictionaryRef deviceRomProps = NULL;
|
||||
if(IORegistryEntryCreateCFProperties(deviceRom, &deviceRomProps, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess)
|
||||
return "IORegistryEntryCreateCFProperties(deviceRom) failed";
|
||||
|
||||
CFRelease(properties);
|
||||
IOObjectRelease(registryEntry);
|
||||
return NULL;
|
||||
}
|
||||
ffCfDictGetString(deviceRomProps, CFSTR("vendor"), &bios->vendor);
|
||||
ffCfDictGetString(deviceRomProps, CFSTR("version"), &bios->version);
|
||||
ffCfDictGetString(deviceRomProps, CFSTR("release-date"), &bios->date);
|
||||
ffStrbufSetStatic(&bios->type, "UEFI");
|
||||
|
||||
#else
|
||||
|
||||
//For arm64
|
||||
if((registryEntry = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/")))
|
||||
{
|
||||
CFMutableDictionaryRef properties;
|
||||
if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) == kIOReturnSuccess)
|
||||
{
|
||||
ffCfDictGetString(properties, CFSTR("manufacturer"), &bios->vendor);
|
||||
ffCfDictGetString(properties, CFSTR("time-stamp"), &bios->date);
|
||||
CFRelease(properties);
|
||||
}
|
||||
IOObjectRelease(registryEntry);
|
||||
}
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t device = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/");
|
||||
if (!device)
|
||||
return "IODeviceTree:/ not found";
|
||||
|
||||
if((registryEntry = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen")))
|
||||
FF_CFTYPE_AUTO_RELEASE CFMutableDictionaryRef deviceProps = NULL;
|
||||
if(IORegistryEntryCreateCFProperties(device, &deviceProps, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess)
|
||||
return "IORegistryEntryCreateCFProperties(device) failed";
|
||||
|
||||
ffCfDictGetString(deviceProps, CFSTR("manufacturer"), &bios->vendor);
|
||||
ffCfDictGetString(deviceProps, CFSTR("time-stamp"), &bios->date);
|
||||
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t deviceChosen = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen");
|
||||
if (deviceChosen)
|
||||
{
|
||||
CFMutableDictionaryRef properties;
|
||||
if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) == kIOReturnSuccess)
|
||||
FF_CFTYPE_AUTO_RELEASE CFStringRef systemFirmWareVersion = IORegistryEntryCreateCFProperty(deviceChosen, CFSTR("system-firmware-version"), kCFAllocatorDefault, kNilOptions);
|
||||
if (systemFirmWareVersion)
|
||||
{
|
||||
ffCfDictGetString(properties, CFSTR("system-firmware-version"), &bios->version);
|
||||
ffCfStrGetString(systemFirmWareVersion, &bios->version);
|
||||
uint32_t index = ffStrbufFirstIndexC(&bios->version, '-');
|
||||
if (index != bios->version.length)
|
||||
{
|
||||
ffStrbufAppendNS(&bios->type, index, bios->version.chars);
|
||||
ffStrbufRemoveSubstr(&bios->version, 0, index + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufSetStatic(&bios->type, "iBoot");
|
||||
}
|
||||
CFRelease(properties);
|
||||
}
|
||||
IOObjectRelease(registryEntry);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!bios->type.length)
|
||||
ffStrbufSetStatic(&bios->type, "iBoot");
|
||||
#endif
|
||||
|
||||
return "Failed to query bios info";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ static const char* detectWithDisplayServices(const FFDisplayServerResult* displa
|
||||
// Works for Apple Silicon and USB-C adapter connection ( but not HTMI )
|
||||
static const char* detectWithDdcci(FF_MAYBE_UNUSED const FFDisplayServerResult* displayServer, FFBrightnessOptions* options, FFlist* result)
|
||||
{
|
||||
if (!IOAVServiceCreate || !IOAVServiceReadI2C)
|
||||
if (!IOAVServiceCreate || !IOAVServiceReadI2C || !IOAVServiceWriteI2C)
|
||||
return "IOAVService is not available";
|
||||
|
||||
FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL;
|
||||
@@ -123,65 +123,100 @@ static const char* detectWithDdcci(FF_MAYBE_UNUSED const FFDisplayServerResult*
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
static IOOptionBits getSupportedTransactionType(void)
|
||||
{
|
||||
FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL;
|
||||
|
||||
if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceNameMatching("IOFramebufferI2CInterface"), &iterator) != KERN_SUCCESS)
|
||||
return kIOI2CNoTransactionType;
|
||||
|
||||
io_registry_entry_t registryEntry;
|
||||
while ((registryEntry = IOIteratorNext(iterator)) != MACH_PORT_NULL)
|
||||
{
|
||||
FF_IOOBJECT_AUTO_RELEASE io_service_t io_service = registryEntry;
|
||||
FF_CFTYPE_AUTO_RELEASE CFNumberRef IOI2CTransactionTypes = IORegistryEntryCreateCFProperty(io_service, CFSTR(kIOI2CTransactionTypesKey), kCFAllocatorDefault, kNilOptions);
|
||||
|
||||
if (IOI2CTransactionTypes)
|
||||
{
|
||||
int64_t types = 0;
|
||||
ffCfNumGetInt64(IOI2CTransactionTypes, &types);
|
||||
|
||||
if (types) {
|
||||
if ((1 << kIOI2CDDCciReplyTransactionType) & (uint64_t) types)
|
||||
return kIOI2CDDCciReplyTransactionType;
|
||||
if ((1 << kIOI2CSimpleTransactionType) & (uint64_t) types)
|
||||
return kIOI2CSimpleTransactionType;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return kIOI2CNoTransactionType;
|
||||
}
|
||||
|
||||
static const char* detectWithDdcci(const FFDisplayServerResult* displayServer, FFBrightnessOptions* options, FFlist* result)
|
||||
{
|
||||
if (!CGSServiceForDisplayNumber) return "CGSServiceForDisplayNumber is not available";
|
||||
IOOptionBits transactionType = getSupportedTransactionType();
|
||||
if (transactionType == kIOI2CNoTransactionType)
|
||||
return "No supported IOI2C transaction type found";
|
||||
|
||||
FF_LIST_FOR_EACH(FFDisplayResult, display, displayServer->displays)
|
||||
{
|
||||
if (display->type == FF_DISPLAY_TYPE_EXTERNAL)
|
||||
{
|
||||
io_service_t framebuffer = 0;
|
||||
FF_IOOBJECT_AUTO_RELEASE io_service_t framebuffer = IO_OBJECT_NULL;
|
||||
CGSServiceForDisplayNumber((CGDirectDisplayID)display->id, &framebuffer);
|
||||
if (framebuffer == 0) continue;
|
||||
if (framebuffer == IO_OBJECT_NULL) continue;
|
||||
|
||||
IOItemCount count;
|
||||
if (IOFBGetI2CInterfaceCount(framebuffer, &count) != KERN_SUCCESS || count == 0) continue;
|
||||
|
||||
io_service_t interface = 0;
|
||||
if (IOFBCopyI2CInterfaceForBus(framebuffer, 0, &interface) != KERN_SUCCESS) continue;
|
||||
|
||||
uint8_t i2cOut[12] = {};
|
||||
IOI2CConnectRef connect;
|
||||
if (IOI2CInterfaceOpen(interface, kNilOptions, &connect) != KERN_SUCCESS)
|
||||
{
|
||||
IOObjectRelease(interface);
|
||||
if (IOFBGetI2CInterfaceCount(framebuffer, &count) != KERN_SUCCESS || count == 0)
|
||||
continue;
|
||||
|
||||
for (IOItemCount bus = 0; bus < count; ++bus)
|
||||
{
|
||||
FF_IOOBJECT_AUTO_RELEASE io_service_t interface = IO_OBJECT_NULL;
|
||||
if (IOFBCopyI2CInterfaceForBus(framebuffer, bus, &interface) != KERN_SUCCESS) continue;
|
||||
|
||||
uint8_t i2cOut[12] = {};
|
||||
IOI2CConnectRef connect = NULL;
|
||||
if (IOI2CInterfaceOpen(interface, kNilOptions, &connect) != KERN_SUCCESS)
|
||||
continue;
|
||||
|
||||
uint8_t i2cIn[] = { 0x51, 0x82, 0x01, 0x10 /* luminance */, 0 };
|
||||
i2cIn[4] = 0x6E ^ i2cIn[0] ^ i2cIn[1] ^ i2cIn[2] ^ i2cIn[3];
|
||||
|
||||
IOI2CRequest request = {
|
||||
.commFlags = kNilOptions,
|
||||
.sendAddress = 0x6e,
|
||||
.sendTransactionType = kIOI2CSimpleTransactionType,
|
||||
.sendBuffer = (vm_address_t) i2cIn,
|
||||
.sendBytes = ARRAY_SIZE(i2cIn),
|
||||
.minReplyDelay = options->ddcciSleep * 1000ULL,
|
||||
.replyAddress = 0x6F,
|
||||
.replySubAddress = 0x51,
|
||||
.replyTransactionType = transactionType,
|
||||
.replyBytes = ARRAY_SIZE(i2cOut),
|
||||
.replyBuffer = (vm_address_t) i2cOut,
|
||||
};
|
||||
IOReturn ret = IOI2CSendRequest(connect, kNilOptions, &request);
|
||||
IOI2CInterfaceClose(connect, kNilOptions);
|
||||
|
||||
if (ret != KERN_SUCCESS || request.result != kIOReturnSuccess || request.replyBytes < 10) continue;
|
||||
if (i2cOut[2] != 0x02 || i2cOut[3] != 0x00) continue;
|
||||
|
||||
uint32_t current = ((uint32_t) i2cOut[8] << 8u) + (uint32_t) i2cOut[9];
|
||||
uint32_t max = ((uint32_t) i2cOut[6] << 8u) + (uint32_t) i2cOut[7];
|
||||
|
||||
FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result);
|
||||
brightness->max = max;
|
||||
brightness->min = 0;
|
||||
brightness->current = current;
|
||||
ffStrbufInitCopy(&brightness->name, &display->name);
|
||||
brightness->builtin = false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t i2cIn[] = { 0x51, 0x82, 0x01, 0x10 /* luminance */, 0 };
|
||||
i2cIn[4] = 0x6E ^ i2cIn[0] ^ i2cIn[1] ^ i2cIn[2] ^ i2cIn[3];
|
||||
|
||||
IOI2CRequest request = {
|
||||
.commFlags = kNilOptions,
|
||||
.sendAddress = 0x6e,
|
||||
.sendTransactionType = kIOI2CSimpleTransactionType,
|
||||
.sendBuffer = (vm_address_t) i2cIn,
|
||||
.sendBytes = ARRAY_SIZE(i2cIn),
|
||||
.minReplyDelay = options->ddcciSleep,
|
||||
.replyAddress = 0x6F,
|
||||
.replySubAddress = 0x51,
|
||||
.replyTransactionType = kIOI2CDDCciReplyTransactionType,
|
||||
.replyBytes = ARRAY_SIZE(i2cOut),
|
||||
.replyBuffer = (vm_address_t) i2cOut,
|
||||
};
|
||||
IOReturn ret = IOI2CSendRequest(connect, kNilOptions, &request);
|
||||
IOI2CInterfaceClose(connect, kNilOptions);
|
||||
IOObjectRelease(interface);
|
||||
|
||||
if (ret != KERN_SUCCESS || request.result != kIOReturnSuccess) continue;
|
||||
|
||||
if (i2cOut[2] != 0x02 || i2cOut[3] != 0x00) continue;
|
||||
|
||||
uint32_t current = ((uint32_t) i2cOut[8] << 8u) + (uint32_t) i2cOut[9];
|
||||
uint32_t max = ((uint32_t) i2cOut[6] << 8u) + (uint32_t) i2cOut[7];
|
||||
|
||||
FFBrightnessResult* brightness = (FFBrightnessResult*) ffListAdd(result);
|
||||
brightness->max = max;
|
||||
brightness->min = 0;
|
||||
brightness->current = current;
|
||||
ffStrbufInitCopy(&brightness->name, &display->name);
|
||||
brightness->builtin = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -21,6 +21,7 @@ typedef struct FFCPUResult
|
||||
uint16_t coresPhysical;
|
||||
uint16_t coresLogical;
|
||||
uint16_t coresOnline;
|
||||
uint16_t numaNodes;
|
||||
|
||||
uint32_t frequencyBase; // GHz
|
||||
uint32_t frequencyMax; // GHz
|
||||
|
||||
@@ -39,7 +39,7 @@ static const char* detectFrequency(FFCPUResult* cpu)
|
||||
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceNameMatching("pmgr"));
|
||||
if (!entryDevice)
|
||||
return "IOServiceGetMatchingServices() failed";
|
||||
return "IOServiceGetMatchingService() failed";
|
||||
|
||||
if (!IOObjectConformsTo(entryDevice, "AppleARMIODevice"))
|
||||
return "\"pmgr\" should conform to \"AppleARMIODevice\"";
|
||||
|
||||
@@ -100,5 +100,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu)
|
||||
detectThermalTemp(&cpu->temperature);
|
||||
}
|
||||
|
||||
cpu->numaNodes = (uint16_t) ffSysctlGetInt("vm.ndomains", 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+126
-15
@@ -138,6 +138,21 @@ static double detectCPUTemp(void)
|
||||
return FF_CPU_TEMP_UNSET;
|
||||
}
|
||||
|
||||
static void detectNumaNodes(FFCPUResult* cpu)
|
||||
{
|
||||
FF_AUTO_CLOSE_DIR DIR* dir = opendir("/sys/devices/system/node/");
|
||||
if (!dir) return;
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)
|
||||
continue;
|
||||
if (ffStrStartsWith(entry->d_name, "node") && ffCharIsDigit(entry->d_name[strlen("node")]))
|
||||
cpu->numaNodes++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "common/settings.h"
|
||||
|
||||
@@ -145,6 +160,7 @@ static void detectQualcomm(FFCPUResult* cpu)
|
||||
{
|
||||
// https://en.wikipedia.org/wiki/List_of_Qualcomm_Snapdragon_systems_on_chips
|
||||
|
||||
assert(cpu->name.length >= 2);
|
||||
uint32_t code = (uint32_t) strtoul(cpu->name.chars + 2, NULL, 10);
|
||||
const char* name = NULL;
|
||||
|
||||
@@ -189,6 +205,7 @@ static void detectMediaTek(FFCPUResult* cpu)
|
||||
{
|
||||
// https://en.wikipedia.org/wiki/List_of_MediaTek_systems_on_chips
|
||||
|
||||
assert(cpu->name.length >= 2);
|
||||
uint32_t code = (uint32_t) strtoul(cpu->name.chars + 2, NULL, 10);
|
||||
const char* name = NULL;
|
||||
|
||||
@@ -219,25 +236,78 @@ static void detectMediaTek(FFCPUResult* cpu)
|
||||
}
|
||||
}
|
||||
|
||||
static void detectExynos(FFCPUResult* cpu)
|
||||
{
|
||||
// https://en.wikipedia.org/wiki/Exynos
|
||||
|
||||
assert(cpu->name.length > 3);
|
||||
uint32_t code = (uint32_t) strtoul(cpu->name.chars + 3, NULL, 10);
|
||||
const char* name = NULL;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 9955: name = "2500"; break;
|
||||
case 9945: name = "2400"; break;
|
||||
// No 2300
|
||||
case 9925: name = "2200"; break;
|
||||
case 9840: name = "2100"; break;
|
||||
|
||||
case 8855: name = "1580"; break;
|
||||
case 8845: name = "1480"; break;
|
||||
case 8835: name = "1380"; break;
|
||||
case 8535: name = "1330"; break;
|
||||
case 8825: name = "1280"; break;
|
||||
case 9815: name = "1080"; break;
|
||||
|
||||
case 9830: name = "990"; break;
|
||||
case 9630: name = "980"; break;
|
||||
|
||||
case 8805: name = "880"; break;
|
||||
case 3830: name = "850"; break;
|
||||
}
|
||||
|
||||
if (name)
|
||||
{
|
||||
char str[32];
|
||||
ffStrCopy(str, cpu->name.chars, sizeof(str));
|
||||
ffStrbufSetF(&cpu->name, "Samsung Exynos %s [%s]", name, str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void detectAndroid(FFCPUResult* cpu)
|
||||
{
|
||||
if (cpu->name.length == 0)
|
||||
{
|
||||
if (ffSettingsGetAndroidProperty("ro.soc.model", &cpu->name))
|
||||
ffStrbufClear(&cpu->vendor); // We usually detect the vendor of CPU core as ARM, but instead we want the vendor of SOC
|
||||
else if(ffSettingsGetAndroidProperty("ro.mediatek.platform", &cpu->name))
|
||||
ffStrbufSetStatic(&cpu->vendor, "MTK");
|
||||
}
|
||||
if (cpu->vendor.length == 0)
|
||||
{
|
||||
if (!ffSettingsGetAndroidProperty("ro.soc.manufacturer", &cpu->vendor))
|
||||
ffSettingsGetAndroidProperty("ro.product.product.manufacturer", &cpu->vendor);
|
||||
if (!ffSettingsGetAndroidProperty("ro.product.product.manufacturer", &cpu->vendor))
|
||||
if (!ffSettingsGetAndroidProperty("ro.product.vendor.manufacturer", &cpu->vendor))
|
||||
if(ffSettingsGetAndroidProperty("ro.mediatek.platform", &cpu->name))
|
||||
ffStrbufSetStatic(&cpu->vendor, "MediaTek");
|
||||
}
|
||||
|
||||
if (ffStrbufEqualS(&cpu->vendor, "QTI") && ffStrbufStartsWithS(&cpu->name, "SM"))
|
||||
if (ffStrbufEqualS(&cpu->vendor, "QTI"))
|
||||
ffStrbufSetStatic(&cpu->vendor, "Qualcomm");
|
||||
else if (ffStrbufIgnCaseEqualS(&cpu->vendor, "MediaTek")) // sometimes "Mediatek"
|
||||
ffStrbufSetStatic(&cpu->vendor, "MediaTek");
|
||||
else if (cpu->vendor.length > 0)
|
||||
cpu->vendor.chars[0] = (char) toupper(cpu->vendor.chars[0]);
|
||||
|
||||
if (ffStrbufEqualS(&cpu->vendor, "Qualcomm") && ffStrbufStartsWithS(&cpu->name, "SM"))
|
||||
detectQualcomm(cpu);
|
||||
else if (ffStrbufEqualS(&cpu->vendor, "MTK") && ffStrbufStartsWithS(&cpu->name, "MT"))
|
||||
else if (ffStrbufEqualS(&cpu->vendor, "MediaTek") && ffStrbufStartsWithS(&cpu->name, "MT"))
|
||||
detectMediaTek(cpu);
|
||||
else if (ffStrbufEqualS(&cpu->vendor, "Samsung") && ffStrbufStartsWithS(&cpu->name, "s5e"))
|
||||
{
|
||||
cpu->name.chars[0] = 'S';
|
||||
cpu->name.chars[2] = 'E';
|
||||
detectExynos(cpu);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -360,6 +430,10 @@ static const char* parseCpuInfo(
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) ||
|
||||
(cpu->vendor.length == 0 && ffParsePropLine(line, "vendor :", &cpu->vendor)) ||
|
||||
(cpuMHz->length == 0 && ffParsePropLine(line, "cpu MHz :", cpuMHz)) ||
|
||||
#elif __hppa__
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "cpu :", &cpu->name)) ||
|
||||
#elif __sh__
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "cpu type :", &cpu->name)) ||
|
||||
#else
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "model name :", &cpu->name)) ||
|
||||
(cpu->name.length == 0 && ffParsePropLine(line, "model :", &cpu->name)) ||
|
||||
@@ -509,6 +583,8 @@ FF_MAYBE_UNUSED static const char* detectCPUX86(const FFCPUOptions* options, FFC
|
||||
if (!detectFrequency(cpu, options) || cpu->frequencyBase == 0)
|
||||
cpu->frequencyBase = (uint32_t) ffStrbufToUInt(&cpuMHz, 0);
|
||||
|
||||
detectNumaNodes(cpu);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -617,19 +693,46 @@ FF_MAYBE_UNUSED static void detectSocName(FFCPUResult* cpu)
|
||||
if (cpu->name.length > 0)
|
||||
return;
|
||||
|
||||
// device-vendor,device-model\0soc-vendor,soc-model\0
|
||||
char content[256];
|
||||
// [x-vendor,x-model\0]*N
|
||||
char content[512];
|
||||
ssize_t length = ffReadFileData("/proc/device-tree/compatible", ARRAY_SIZE(content), content);
|
||||
if (length <= 2) return;
|
||||
if (length < 4) return; // v,m\0
|
||||
|
||||
// get the second NUL terminated string if it exists
|
||||
char* vendor = memchr(content, '\0', (size_t) length) + 1;
|
||||
if (!vendor || vendor - content >= length) vendor = content;
|
||||
if (content[length - 1] != '\0') return; // must end with \0
|
||||
|
||||
char* model = strchr(vendor, ',');
|
||||
if (!model) return;
|
||||
*model = '\0';
|
||||
++model;
|
||||
--length;
|
||||
|
||||
char* vendor = NULL;
|
||||
char* model = NULL;
|
||||
|
||||
for (char* p; length > 0; length = p ? (ssize_t) (p - content) - 1 : 0)
|
||||
{
|
||||
p = memrchr(content, '\0', (size_t) length);
|
||||
|
||||
vendor = p /* first entry */ ? p + 1 : content;
|
||||
|
||||
size_t partLen = (size_t) (length - (vendor - content));
|
||||
if (partLen < 3) continue;
|
||||
|
||||
char* comma = memchr(vendor, ',', partLen);
|
||||
if (!comma) continue;
|
||||
|
||||
size_t vendorLen = (size_t) (comma - vendor);
|
||||
if (vendorLen == 0) continue;
|
||||
|
||||
model = comma + 1;
|
||||
size_t modelLen = (size_t) (partLen - (size_t) (model - vendor));
|
||||
if (modelLen == 0) continue;
|
||||
|
||||
if ((modelLen >= strlen("-platform") && ffStrEndsWith(model, "-platform")) ||
|
||||
(modelLen >= strlen("-soc") && ffStrEndsWith(model, "-soc")))
|
||||
continue;
|
||||
|
||||
*comma = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
if (!length) return;
|
||||
|
||||
if (false) {}
|
||||
#if __aarch64__
|
||||
@@ -685,6 +788,13 @@ FF_MAYBE_UNUSED static void detectSocName(FFCPUResult* cpu)
|
||||
for (const char* p = model; *p; ++p)
|
||||
ffStrbufAppendC(&cpu->name, (char) toupper(*p));
|
||||
}
|
||||
else if (ffStrEquals(vendor, "thead"))
|
||||
{
|
||||
// Lichee Pi?
|
||||
ffStrbufSetStatic(&cpu->vendor, "T-Head");
|
||||
for (const char* p = model; *p; ++p)
|
||||
ffStrbufAppendC(&cpu->name, (char) toupper(*p));
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufSetS(&cpu->name, model);
|
||||
@@ -784,6 +894,7 @@ FF_MAYBE_UNUSED static const char* detectCPUOthers(const FFCPUOptions* options,
|
||||
detectPhysicalCores(cpu);
|
||||
|
||||
ffCPUDetectByCpuid(cpu);
|
||||
detectNumaNodes(cpu);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -230,6 +230,8 @@ static const char* detectNCores(FFCPUResult* cpu)
|
||||
++cpu->coresPhysical;
|
||||
else if (ptr->Relationship == RelationProcessorPackage)
|
||||
++cpu->packages;
|
||||
else if (ptr->Relationship == RelationNumaNode)
|
||||
++cpu->numaNodes;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#ifdef STATX_BTIME
|
||||
#if defined(STATX_BTIME) && !defined(__ANDROID__)
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -45,6 +45,7 @@ FFDisplayResult* ffdsAppendDisplay(
|
||||
display->manufactureYear = 0;
|
||||
display->manufactureWeek = 0;
|
||||
display->serial = 0;
|
||||
display->drrStatus = FF_DISPLAY_DRR_STATUS_UNKNOWN;
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,13 @@ typedef enum __attribute__((__packed__)) FFDisplayHdrStatus
|
||||
FF_DISPLAY_HDR_STATUS_ENABLED,
|
||||
} FFDisplayHdrStatus;
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFDisplayVrrStatus
|
||||
{
|
||||
FF_DISPLAY_DRR_STATUS_UNKNOWN,
|
||||
FF_DISPLAY_DRR_STATUS_DISABLED,
|
||||
FF_DISPLAY_DRR_STATUS_ENABLED,
|
||||
} FFDisplayVrrStatus;
|
||||
|
||||
typedef struct FFDisplayResult
|
||||
{
|
||||
uint32_t width; // in px
|
||||
@@ -83,6 +90,7 @@ typedef struct FFDisplayResult
|
||||
uint16_t manufactureYear;
|
||||
uint16_t manufactureWeek;
|
||||
uint32_t serial;
|
||||
FFDisplayVrrStatus drrStatus;
|
||||
} FFDisplayResult;
|
||||
|
||||
typedef struct FFDisplayServerResult
|
||||
|
||||
@@ -140,6 +140,61 @@ static bool detectWithGetprop(FFDisplayServerResult* ds)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool detectDE(FFDisplayServerResult* ds)
|
||||
{
|
||||
if (ffSettingsGetAndroidProperty("ro.vivo.os.build.display.id", &ds->dePrettyName)) // OriginOS 6
|
||||
{
|
||||
ffStrbufAppendC(&ds->dePrettyName, ' ');
|
||||
ffSettingsGetAndroidProperty("ro.vivo.product.version", &ds->dePrettyName); // PD2505D_xxx
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.magic", &ds->dePrettyName) ||
|
||||
ffSettingsGetAndroidProperty("ro.build.version.emui", &ds->dePrettyName))
|
||||
{
|
||||
ffStrbufReplaceAllC(&ds->dePrettyName, '_', ' ');
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.mi.os.version.name", &ds->dePrettyName))
|
||||
{
|
||||
// MiUI like
|
||||
ffStrbufClear(&ds->dePrettyName);
|
||||
ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName); // Detail version number
|
||||
if (ffStrbufStartsWithS(&ds->dePrettyName, "OS"))
|
||||
{
|
||||
ds->dePrettyName.chars[0] = 'S';
|
||||
ds->dePrettyName.chars[1] = ' ';
|
||||
ffStrbufPrependS(&ds->dePrettyName, "HyperO");
|
||||
}
|
||||
else if (ffStrbufStartsWithS(&ds->dePrettyName, "V"))
|
||||
{
|
||||
ds->dePrettyName.chars[0] = ' ';
|
||||
ffStrbufPrependS(&ds->dePrettyName, "MiUI");
|
||||
}
|
||||
else
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "MiUI");
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.oplusrom", &ds->dePrettyName))
|
||||
{
|
||||
if (ffStrbufStartsWithS(&ds->dePrettyName, "V"))
|
||||
ffStrbufSubstrAfter(&ds->dePrettyName, 0);
|
||||
ffStrbufPrependS(&ds->dePrettyName, "ColorOS");
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.oxygen.version", &ds->dePrettyName))
|
||||
{
|
||||
ffStrbufPrependS(&ds->dePrettyName, "OxygenOS");
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName) && ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS"))
|
||||
{
|
||||
ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' ');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
|
||||
{
|
||||
const char* error = ffdsConnectXcbRandr(ds);
|
||||
@@ -158,4 +213,6 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
|
||||
|
||||
if (!detectWithGetprop(ds))
|
||||
detectWithDumpsys(ds);
|
||||
|
||||
detectDE(ds);
|
||||
}
|
||||
|
||||
@@ -196,21 +196,5 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
|
||||
}
|
||||
}
|
||||
|
||||
const FFOSResult* os = ffDetectOS();
|
||||
|
||||
char* str_end;
|
||||
const char* version = os->version.chars;
|
||||
unsigned long osNum = strtoul(version, &str_end, 10);
|
||||
if (str_end != version)
|
||||
{
|
||||
if (osNum > 15) { // Tahoe
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Liquid Glass");
|
||||
} else if (osNum < 10) {
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Platinum");
|
||||
} else {
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Aqua");
|
||||
}
|
||||
}
|
||||
|
||||
detectDisplays(ds);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
#include "displayserver.h"
|
||||
#include "detection/os/os.h"
|
||||
#include "util/windows/unicode.h"
|
||||
#include "util/windows/registry.h"
|
||||
#include "util/mallocHelper.h"
|
||||
#include "util/edidHelper.h"
|
||||
|
||||
#include <dwmapi.h>
|
||||
#include <WinUser.h>
|
||||
#include <winuser.h>
|
||||
#include <wchar.h>
|
||||
|
||||
typedef struct FFMonitorInfo
|
||||
@@ -232,6 +229,7 @@ static void detectDisplays(FFDisplayServerResult* ds)
|
||||
}
|
||||
if (edidLength > 0)
|
||||
ffEdidGetSerialAndManufactureDate(edidData, &display->serial, &display->manufactureYear, &display->manufactureWeek);
|
||||
display->drrStatus = path->flags & DISPLAYCONFIG_PATH_BOOST_REFRESH_RATE ? FF_DISPLAY_DRR_STATUS_ENABLED : FF_DISPLAY_DRR_STATUS_DISABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,27 +252,4 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds)
|
||||
}
|
||||
|
||||
detectDisplays(ds);
|
||||
|
||||
//https://github.com/hykilpikonna/hyfetch/blob/master/neofetch#L2067
|
||||
const FFOSResult* os = ffDetectOS();
|
||||
uint32_t ver = (uint32_t) ffStrbufToUInt(&os->version, 0);
|
||||
if (ver > 1000)
|
||||
{
|
||||
// Windows Server
|
||||
if (ver >= 2016)
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Fluent");
|
||||
else if (ver >= 2012)
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Metro");
|
||||
else
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Aero");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ver >= 10)
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Fluent");
|
||||
else if (ver >= 8)
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Metro");
|
||||
else
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Aero");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,8 +215,8 @@ static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output,
|
||||
|
||||
FFDisplayResult* item = ffdsAppendDisplay(
|
||||
data->result,
|
||||
(uint32_t) crtcInfoReply->width,
|
||||
(uint32_t) crtcInfoReply->height,
|
||||
(uint32_t) (currentMode ? currentMode->width : crtcInfoReply->width),
|
||||
(uint32_t) (currentMode ? currentMode->height : crtcInfoReply->height),
|
||||
currentMode ? (double) currentMode->dot_clock / (double) ((uint32_t) currentMode->htotal * currentMode->vtotal) : 0,
|
||||
(uint32_t) (crtcInfoReply->width / scaleFactor + .5),
|
||||
(uint32_t) (crtcInfoReply->height / scaleFactor + .5),
|
||||
@@ -230,7 +230,7 @@ static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output,
|
||||
0,
|
||||
(uint32_t) outputInfoReply->mm_width,
|
||||
(uint32_t) outputInfoReply->mm_height,
|
||||
"xcb-randr-crtc"
|
||||
currentMode ? "xcb-randr-mode" : "xcb-randr-crtc"
|
||||
);
|
||||
if (item && edidLength)
|
||||
{
|
||||
|
||||
@@ -134,8 +134,8 @@ static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf*
|
||||
|
||||
FFDisplayResult* item = ffdsAppendDisplay(
|
||||
data->result,
|
||||
(uint32_t) crtcInfo->width,
|
||||
(uint32_t) crtcInfo->height,
|
||||
(uint32_t) (currentMode ? currentMode->width : crtcInfo->width),
|
||||
(uint32_t) (currentMode ? currentMode->height : crtcInfo->height),
|
||||
currentMode ? (double) currentMode->dotClock / (double) ((uint32_t) currentMode->hTotal * currentMode->vTotal) : 0,
|
||||
(uint32_t) (crtcInfo->width / scaleFactor + .5),
|
||||
(uint32_t) (crtcInfo->height / scaleFactor + .5),
|
||||
@@ -149,7 +149,7 @@ static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf*
|
||||
0,
|
||||
(uint32_t) output->mm_width,
|
||||
(uint32_t) output->mm_height,
|
||||
"xlib-randr-crtc"
|
||||
currentMode ? "xlib-randr-mode" : "xlib-randr-crtc"
|
||||
);
|
||||
|
||||
if (item)
|
||||
|
||||
+19
-1
@@ -15,11 +15,18 @@ extern const char* FF_GPU_VENDOR_NAME_AMD;
|
||||
extern const char* FF_GPU_VENDOR_NAME_INTEL;
|
||||
extern const char* FF_GPU_VENDOR_NAME_NVIDIA;
|
||||
extern const char* FF_GPU_VENDOR_NAME_MTHREADS;
|
||||
extern const char* FF_GPU_VENDOR_NAME_QUALCOMM;
|
||||
extern const char* FF_GPU_VENDOR_NAME_MTK;
|
||||
extern const char* FF_GPU_VENDOR_NAME_VMWARE;
|
||||
extern const char* FF_GPU_VENDOR_NAME_PARALLEL;
|
||||
extern const char* FF_GPU_VENDOR_NAME_MICROSOFT;
|
||||
extern const char* FF_GPU_VENDOR_NAME_REDHAT;
|
||||
extern const char* FF_GPU_VENDOR_NAME_ORACLE;
|
||||
extern const char* FF_GPU_VENDOR_NAME_BROADCOM;
|
||||
extern const char* FF_GPU_VENDOR_NAME_LOONGSON;
|
||||
extern const char* FF_GPU_VENDOR_NAME_JINGJIA_MICRO;
|
||||
extern const char* FF_GPU_VENDOR_NAME_HUAWEI;
|
||||
extern const char* FF_GPU_VENDOR_NAME_ZHAOXIN;
|
||||
|
||||
typedef struct FFGPUMemory
|
||||
{
|
||||
@@ -58,7 +65,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 +80,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;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ static const char* detectFrequency(FFGPUResult* gpu)
|
||||
if (!freqProperty || CFGetTypeID(freqProperty) != CFDataGetTypeID())
|
||||
return "\"voltage-states9-sram\" in \"pmgr\" is not found";
|
||||
|
||||
// voltage-states5-sram stores supported <frequency / voltage> pairs of gpu from the lowest to the highest
|
||||
// voltage-states9-sram stores supported <frequency / voltage> pairs of gpu from the lowest to the highest
|
||||
CFIndex propLength = CFDataGetLength(freqProperty);
|
||||
if (propLength == 0 || propLength % (CFIndex) sizeof(uint32_t) * 2 != 0)
|
||||
return "Invalid \"voltage-states9-sram\" length";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -153,7 +153,7 @@ const char* ffDrmDetectAmdgpu(const FFGPUOptions* options, FFGPUResult* gpu, con
|
||||
|
||||
return NULL;
|
||||
#else
|
||||
FF_UNUSED(gpu, renderPath);
|
||||
FF_UNUSED(options, gpu, renderPath);
|
||||
return "Fastfetch is compiled without libdrm support";
|
||||
#endif
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
#include "detection/gpu/gpu_driver_specific.h"
|
||||
#include "common/io/io.h"
|
||||
#include "common/library.h"
|
||||
#include "modules/gpu/option.h"
|
||||
#include "util/FFstrbuf.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "util/mallocHelper.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef FF_HAVE_DRM_AMDGPU
|
||||
#include <amdgpu.h>
|
||||
@@ -120,7 +123,7 @@ static const char* drmDetectAmdSpecific(const FFGPUOptions* options, FFGPUResult
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
FF_UNUSED(gpu, drmKey, buffer);
|
||||
FF_UNUSED(options, gpu, drmKey, buffer);
|
||||
return "Fastfetch is not compiled with drm support";
|
||||
#endif
|
||||
}
|
||||
@@ -198,7 +201,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;
|
||||
|
||||
@@ -263,7 +266,7 @@ static const char* drmDetectIntelSpecific(FFGPUResult* gpu, const char* drmKey,
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char* pciDetectNouveauSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
|
||||
static const char* pciDetectTempGeneral(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
|
||||
{
|
||||
if (options->temp)
|
||||
{
|
||||
@@ -306,6 +309,48 @@ static const char* drmDetectNouveauSpecific(FFGPUResult* gpu, const char* drmKey
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char* pciDetectZxSpecific(const FFGPUOptions* options, FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer)
|
||||
{
|
||||
gpu->type = FF_GPU_TYPE_INTEGRATED;
|
||||
|
||||
const uint32_t pciDirLen = pciDir->length;
|
||||
ffStrbufAppendS(pciDir, "/zx_info/eclk");
|
||||
if (ffReadFileBuffer(pciDir->chars, buffer))
|
||||
gpu->frequency = (uint32_t) ffStrbufToUInt(buffer, FF_GPU_FREQUENCY_UNSET);
|
||||
ffStrbufSubstrBefore(pciDir, pciDirLen);
|
||||
|
||||
if (options->driverSpecific)
|
||||
{
|
||||
ffStrbufAppendS(pciDir, "/zx_info/engine_3d_usage");
|
||||
if (ffReadFileBuffer(pciDir->chars, buffer))
|
||||
gpu->coreUsage = ffStrbufToDouble(buffer, FF_GPU_CORE_USAGE_UNSET);
|
||||
ffStrbufSubstrBefore(pciDir, pciDirLen);
|
||||
|
||||
ffStrbufAppendS(pciDir, "/zx_info/fb_size");
|
||||
if (ffReadFileBuffer(pciDir->chars, buffer))
|
||||
gpu->shared.total = ffStrbufToUInt(buffer, FF_GPU_VMEM_SIZE_UNSET);
|
||||
ffStrbufSubstrBefore(pciDir, pciDirLen);
|
||||
|
||||
if (gpu->shared.total != FF_GPU_VMEM_SIZE_UNSET)
|
||||
{
|
||||
gpu->shared.total *= 1024 * 1024;
|
||||
|
||||
ffStrbufAppendS(pciDir, "/zx_info/free_fb_mem");
|
||||
if (ffReadFileBuffer(pciDir->chars, buffer))
|
||||
gpu->shared.used = ffStrbufToUInt(buffer, FF_GPU_VMEM_SIZE_UNSET);
|
||||
ffStrbufSubstrBefore(pciDir, pciDirLen);
|
||||
|
||||
if (gpu->shared.used != FF_GPU_VMEM_SIZE_UNSET)
|
||||
{
|
||||
gpu->shared.used *= 1024 * 1024;
|
||||
gpu->shared.used = gpu->shared.total - gpu->shared.used;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* deviceDir, const char* drmKey)
|
||||
{
|
||||
const uint32_t drmDirPathLength = deviceDir->length;
|
||||
@@ -342,6 +387,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 +402,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];
|
||||
@@ -414,10 +462,15 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
|
||||
}
|
||||
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && ffStrbufEqualS(&gpu->driver, "nouveau"))
|
||||
{
|
||||
pciDetectNouveauSpecific(options, gpu, deviceDir, buffer);
|
||||
pciDetectTempGeneral(options, gpu, deviceDir, buffer);
|
||||
if (options->driverSpecific && drmKey)
|
||||
drmDetectNouveauSpecific(gpu, drmKey, buffer);
|
||||
}
|
||||
else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_ZHAOXIN && ffStrbufStartsWithS(&gpu->driver, "zx"))
|
||||
{
|
||||
pciDetectTempGeneral(options, gpu, deviceDir, buffer);
|
||||
pciDetectZxSpecific(options, gpu, deviceDir, buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffGPUDetectDriverSpecific(options, gpu, (FFGpuDriverPciBusId) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -28,7 +28,7 @@ const char* ffDetectMemory(FFMemoryResult* ram)
|
||||
|
||||
if((token = strstr(buf, "MemAvailable:")) != NULL)
|
||||
memAvailable = strtoul(token + strlen("MemAvailable:"), NULL, 10);
|
||||
else
|
||||
if (memAvailable == 0 || memAvailable >= memTotal) // MemAvailable can be unreasonable. #1988
|
||||
{
|
||||
if((token = strstr(buf, "MemFree:")) != NULL)
|
||||
memFree = strtoul(token + strlen("MemFree:"), NULL, 10);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "common/library.h"
|
||||
#include "common/printing.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <windows.h>
|
||||
#include <GL/gl.h>
|
||||
|
||||
typedef struct WGLData
|
||||
|
||||
+12
-27
@@ -8,14 +8,14 @@
|
||||
#include <string.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
static void parseSystemVersion(FFOSResult* os)
|
||||
static bool parseSystemVersion(FFOSResult* os)
|
||||
{
|
||||
NSError* error;
|
||||
NSString* fileName = @"file:///System/Library/CoreServices/SystemVersion.plist";
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName]
|
||||
error:&error];
|
||||
if(error)
|
||||
return;
|
||||
return false;
|
||||
|
||||
NSString* value;
|
||||
|
||||
@@ -30,6 +30,8 @@ static void parseSystemVersion(FFOSResult* os)
|
||||
// macOS 26 Tahoe. #1809
|
||||
os->version.chars[0] = '2';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool detectOSCodeName(FFOSResult* os)
|
||||
@@ -80,41 +82,24 @@ static bool detectOSCodeName(FFOSResult* os)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void parseOSXSoftwareLicense(FFOSResult* os)
|
||||
{
|
||||
FF_AUTO_CLOSE_FILE FILE* rtf = fopen("/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf", "r");
|
||||
if(rtf == NULL)
|
||||
return;
|
||||
|
||||
FF_AUTO_FREE char* line = NULL;
|
||||
size_t len = 0;
|
||||
const char* searchStr = "\\f0\\b SOFTWARE LICENSE AGREEMENT FOR macOS ";
|
||||
while(getline(&line, &len, rtf) != EOF)
|
||||
{
|
||||
if (ffStrStartsWith(line, searchStr))
|
||||
{
|
||||
ffStrbufAppendS(&os->codename, line + strlen(searchStr));
|
||||
ffStrbufTrimRight(&os->codename, '\n');
|
||||
ffStrbufTrimRight(&os->codename, '\\');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ffDetectOSImpl(FFOSResult* os)
|
||||
{
|
||||
parseSystemVersion(os);
|
||||
|
||||
ffStrbufSetStatic(&os->id, "macos");
|
||||
|
||||
if(os->version.length == 0)
|
||||
if(__builtin_expect(os->name.length == 0, 0))
|
||||
ffStrbufSetStatic(&os->name, "macOS");
|
||||
|
||||
if(__builtin_expect(os->version.length == 0, 0))
|
||||
ffSysctlGetString("kern.osproductversion", &os->version);
|
||||
|
||||
if(os->buildID.length == 0)
|
||||
if(__builtin_expect(os->buildID.length == 0, 0))
|
||||
ffSysctlGetString("kern.osversion", &os->buildID);
|
||||
|
||||
ffStrbufAppend(&os->versionID, &os->version);
|
||||
|
||||
if(!detectOSCodeName(os))
|
||||
parseOSXSoftwareLicense(os);
|
||||
detectOSCodeName(os);
|
||||
|
||||
ffStrbufSetF(&os->prettyName, "%s %s %s (%s)", os->name.chars, os->codename.chars, os->version.chars, os->buildID.chars);
|
||||
}
|
||||
|
||||
+31
-28
@@ -59,17 +59,18 @@ FF_MAYBE_UNUSED static bool detectArmbianVersion(FFOSResult* result)
|
||||
return true;
|
||||
}
|
||||
|
||||
FF_MAYBE_UNUSED static void getUbuntuFlavour(FFOSResult* result)
|
||||
// Returns false if PrettyName should be updated by caller
|
||||
FF_MAYBE_UNUSED static bool getUbuntuFlavour(FFOSResult* result)
|
||||
{
|
||||
if (detectArmbianVersion(result))
|
||||
return;
|
||||
return true;
|
||||
else if(ffStrbufStartsWithS(&result->prettyName, "Linux Lite "))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Linux Lite");
|
||||
ffStrbufSetStatic(&result->id, "linuxlite");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
ffStrbufSetS(&result->versionID, result->prettyName.chars + strlen("Linux Lite "));
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
else if(ffStrbufStartsWithS(&result->prettyName, "Rhino Linux "))
|
||||
{
|
||||
@@ -77,12 +78,13 @@ FF_MAYBE_UNUSED static void getUbuntuFlavour(FFOSResult* result)
|
||||
ffStrbufSetStatic(&result->id, "rhinolinux");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
ffStrbufSetS(&result->versionID, result->prettyName.chars + strlen("Rhino Linux "));
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
else if(ffStrbufStartsWithS(&result->prettyName, "VanillaOS "))
|
||||
{
|
||||
ffStrbufSetStatic(&result->id, "vanilla");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ffPathExists("/usr/bin/lliurex-version", FF_PATHTYPE_FILE))
|
||||
@@ -97,93 +99,86 @@ FF_MAYBE_UNUSED static void getUbuntuFlavour(FFOSResult* result)
|
||||
ffStrbufTrimRightSpace(&result->version);
|
||||
ffStrbufSetF(&result->prettyName, "LliureX %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* xdgConfigDirs = getenv("XDG_CONFIG_DIRS");
|
||||
if(!ffStrSet(xdgConfigDirs))
|
||||
return;
|
||||
return false;
|
||||
|
||||
if(ffStrContains(xdgConfigDirs, "kde") || ffStrContains(xdgConfigDirs, "plasma") || ffStrContains(xdgConfigDirs, "kubuntu"))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Kubuntu");
|
||||
ffStrbufSetF(&result->prettyName, "Kubuntu %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->id, "kubuntu");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ffStrContains(xdgConfigDirs, "xfce") || ffStrContains(xdgConfigDirs, "xubuntu"))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Xubuntu");
|
||||
ffStrbufSetF(&result->prettyName, "Xubuntu %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->id, "xubuntu");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ffStrContains(xdgConfigDirs, "lxqt") || ffStrContains(xdgConfigDirs, "lubuntu"))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Lubuntu");
|
||||
ffStrbufSetF(&result->prettyName, "Lubuntu %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->id, "lubuntu");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ffStrContains(xdgConfigDirs, "budgie"))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Ubuntu Budgie");
|
||||
ffStrbufSetF(&result->prettyName, "Ubuntu Budgie %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->id, "ubuntu-budgie");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ffStrContains(xdgConfigDirs, "cinnamon"))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Ubuntu Cinnamon");
|
||||
ffStrbufSetF(&result->prettyName, "Ubuntu Cinnamon %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->id, "ubuntu-cinnamon");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ffStrContains(xdgConfigDirs, "mate"))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Ubuntu MATE");
|
||||
ffStrbufSetF(&result->prettyName, "Ubuntu MATE %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->id, "ubuntu-mate");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ffStrContains(xdgConfigDirs, "studio"))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Ubuntu Studio");
|
||||
ffStrbufSetF(&result->prettyName, "Ubuntu Studio %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->id, "ubuntu-studio");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ffStrContains(xdgConfigDirs, "sway"))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Ubuntu Sway");
|
||||
ffStrbufSetF(&result->prettyName, "Ubuntu Sway %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->id, "ubuntu-sway");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ffStrContains(xdgConfigDirs, "touch"))
|
||||
{
|
||||
ffStrbufSetStatic(&result->name, "Ubuntu Touch");
|
||||
ffStrbufSetF(&result->prettyName, "Ubuntu Touch %s", result->version.chars);
|
||||
ffStrbufSetStatic(&result->id, "ubuntu-touch");
|
||||
ffStrbufSetStatic(&result->idLike, "ubuntu");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FF_MAYBE_UNUSED static void getDebianVersion(FFOSResult* result)
|
||||
@@ -192,8 +187,10 @@ FF_MAYBE_UNUSED static void getDebianVersion(FFOSResult* result)
|
||||
ffAppendFileBuffer("/etc/debian_version", &debianVersion);
|
||||
ffStrbufTrimRightSpace(&debianVersion);
|
||||
if (!debianVersion.length) return;
|
||||
ffStrbufSet(&result->version, &debianVersion);
|
||||
ffStrbufSet(&result->versionID, &debianVersion);
|
||||
ffStrbufDestroy(&result->versionID);
|
||||
ffStrbufInitMove(&result->versionID, &debianVersion);
|
||||
|
||||
ffStrbufSetF(&result->prettyName, "%s %s (%s)", result->name.chars, result->versionID.chars, result->codename.chars);
|
||||
}
|
||||
|
||||
FF_MAYBE_UNUSED static bool detectDebianDerived(FFOSResult* result)
|
||||
@@ -243,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))
|
||||
@@ -372,7 +369,13 @@ void ffDetectOSImpl(FFOSResult* os)
|
||||
|
||||
#if __linux__ || __GNU__
|
||||
if(ffStrbufEqualS(&os->id, "ubuntu"))
|
||||
getUbuntuFlavour(os);
|
||||
{
|
||||
if (!getUbuntuFlavour(os))
|
||||
{
|
||||
if (!ffStrbufEndsWithS(&os->prettyName, " (development branch)"))
|
||||
ffStrbufSetF(&os->prettyName, "%s %s", os->name.chars, os->version.chars); // os->version contains code name
|
||||
}
|
||||
}
|
||||
else if(ffStrbufEqualS(&os->id, "debian"))
|
||||
{
|
||||
if (!detectDebianDerived(os))
|
||||
|
||||
@@ -1,25 +1,76 @@
|
||||
extern "C" {
|
||||
#include "os.h"
|
||||
#include "common/library.h"
|
||||
#include "util/windows/unicode.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "util/windows/registry.h"
|
||||
}
|
||||
#include "util/windows/unicode.hpp"
|
||||
#include "util/windows/wmi.hpp"
|
||||
|
||||
#include <windows.h>
|
||||
static const char* getOsNameByWmi(FFstrbuf* osName)
|
||||
{
|
||||
FFWmiQuery query(L"SELECT Caption FROM Win32_OperatingSystem");
|
||||
if(!query)
|
||||
return "Query WMI service failed";
|
||||
|
||||
if(FFWmiRecord record = query.next())
|
||||
{
|
||||
if(auto vtCaption = record.get(L"Caption"))
|
||||
{
|
||||
ffStrbufSetWSV(osName, vtCaption.get<std::wstring_view>());
|
||||
ffStrbufTrimRight(osName, ' ');
|
||||
return NULL;
|
||||
}
|
||||
return "Get Caption failed";
|
||||
}
|
||||
|
||||
return "No WMI result returned";
|
||||
}
|
||||
|
||||
PWSTR WINAPI BrandingFormatString(PCWSTR format);
|
||||
|
||||
void ffDetectOSImpl(FFOSResult* os)
|
||||
static bool getCodeName(FFOSResult* os)
|
||||
{
|
||||
FF_HKEY_AUTO_DESTROY hKey = NULL;
|
||||
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL))
|
||||
return false;
|
||||
|
||||
if(!ffRegReadStrbuf(hKey, L"DisplayVersion", &os->codename, NULL))
|
||||
{
|
||||
if (!ffRegReadStrbuf(hKey, L"CSDVersion", &os->codename, NULL)) // For Windows 7 and Windows 8
|
||||
if (!ffRegReadStrbuf(hKey, L"ReleaseId", &os->codename, NULL)) // For old Windows 10
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* getOsNameByWinbrand(FFstrbuf* osName)
|
||||
{
|
||||
//https://dennisbabkin.com/blog/?t=how-to-tell-the-real-version-of-windows-your-app-is-running-on#ver_string
|
||||
const wchar_t* rawName = BrandingFormatString(L"%WINDOWS_LONG%");
|
||||
ffStrbufSetWS(&os->variant, rawName);
|
||||
FF_LIBRARY_LOAD(winbrand, "dlopen winbrand" FF_LIBRARY_EXTENSION " failed", "winbrand" FF_LIBRARY_EXTENSION, 1);
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(winbrand, BrandingFormatString);
|
||||
|
||||
const wchar_t* rawName = ffBrandingFormatString(L"%WINDOWS_LONG%");
|
||||
ffStrbufSetWS(osName, rawName);
|
||||
GlobalFree((HGLOBAL)rawName);
|
||||
ffStrbufSet(&os->prettyName, &os->variant);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
void ffDetectOSImpl(FFOSResult* os)
|
||||
{
|
||||
if(getOsNameByWinbrand(&os->variant) && getOsNameByWmi(&os->variant))
|
||||
return;
|
||||
|
||||
ffStrbufTrimRight(&os->variant, ' ');
|
||||
|
||||
//WMI returns the "Microsoft" prefix while BrandingFormatString doesn't. Make them consistent.
|
||||
if(ffStrbufStartsWithS(&os->variant, "Microsoft "))
|
||||
ffStrbufSubstrAfter(&os->variant, strlen("Microsoft ") - 1);
|
||||
|
||||
ffStrbufSet(&os->prettyName, &os->variant);
|
||||
|
||||
if(ffStrbufStartsWithS(&os->variant, "Windows "))
|
||||
{
|
||||
ffStrbufAppendS(&os->name, "Windows");
|
||||
@@ -57,4 +108,7 @@ void ffDetectOSImpl(FFOSResult* os)
|
||||
|
||||
ffStrbufAppendF(&os->id, "%s %s", os->name.chars, os->version.chars);
|
||||
ffStrbufSetStatic(&os->idLike, "Windows");
|
||||
|
||||
if (getCodeName(os) && os->codename.length > 0)
|
||||
ffStrbufAppendF(&os->prettyName, " (%s)", os->codename.chars);
|
||||
}
|
||||
@@ -21,6 +21,7 @@ typedef struct FFPackagesResult
|
||||
uint32_t guixUser;
|
||||
uint32_t hpkgSystem;
|
||||
uint32_t hpkgUser;
|
||||
uint32_t kiss;
|
||||
uint32_t linglong;
|
||||
uint32_t lpkg;
|
||||
uint32_t lpkgbuild;
|
||||
|
||||
@@ -420,6 +420,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts,
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_EMERGE_BIT)) packageCounts->emerge += countFilesRecursive(baseDir, "/var/db/pkg", "SIZE");
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_EOPKG_BIT)) packageCounts->eopkg += getNumElements(baseDir, "/var/lib/eopkg/package", true);
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_FLATPAK_BIT)) packageCounts->flatpakSystem += getFlatpakPackages(baseDir, "/var/lib");
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_KISS_BIT)) packageCounts->kiss += getNumElements(baseDir, "/var/db/kiss/installed", true);
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_NIX_BIT))
|
||||
{
|
||||
packageCounts->nixDefault += ffPackagesGetNix(baseDir, "/nix/var/nix/profiles/default");
|
||||
|
||||
@@ -124,7 +124,7 @@ const char* ffDetectPhysicalMemory(FFlist* result)
|
||||
ffStrbufSetS(&device->locator, ldevice);
|
||||
|
||||
const char* formFactorNames[] = {
|
||||
NULL, // 0x00 (用于索引,实际上没有0x00的类型)
|
||||
NULL, // 0x00 (Placeholder for indexing)
|
||||
"Other", // 0x01
|
||||
"Unknown", // 0x02
|
||||
"SIMM", // 0x03
|
||||
@@ -148,7 +148,7 @@ const char* ffDetectPhysicalMemory(FFlist* result)
|
||||
ffStrbufSetF(&device->formFactor, "Unknown (%d)", (int) data->FormFactor);
|
||||
|
||||
const char* memoryTypeNames[] = {
|
||||
NULL, // 0x00 (用于索引,实际上没有0x00的类型)
|
||||
NULL, // 0x00 (Placeholder for indexing)
|
||||
"Other", // 0x01
|
||||
"Unknown", // 0x02
|
||||
"DRAM", // 0x03
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,8 +21,8 @@ const char* ffDetectSwap(FFlist* result)
|
||||
ffStrbufInitNWS(&swap->name, current->FileName.Length / sizeof(wchar_t), current->FileName.Buffer);
|
||||
if (ffStrbufStartsWithS(&swap->name, "\\??\\"))
|
||||
ffStrbufSubstrAfter(&swap->name, strlen("\\??\\") - 1);
|
||||
swap->bytesUsed = current->TotalUsed * pageSize;
|
||||
swap->bytesTotal = current->CurrentSize * pageSize;
|
||||
swap->bytesUsed = (uint64_t) current->TotalUsed * pageSize;
|
||||
swap->bytesTotal = (uint64_t) current->CurrentSize * pageSize;
|
||||
if (current->NextEntryOffset == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -8,13 +8,18 @@
|
||||
|
||||
static void detectAlacritty(FFTerminalFontResult* terminalFont)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreate();
|
||||
// Maybe using a toml parser to read the config file is better?
|
||||
// https://github.com/cktan/tomlc17
|
||||
|
||||
// Doc: https://alacritty.org/config-alacritty.html#s26
|
||||
FF_STRBUF_AUTO_DESTROY fontNormal = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY fontFamily = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY fontStyle = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate();
|
||||
|
||||
do {
|
||||
// Latest alacritty uses toml instead of yaml
|
||||
FFpropquery fontQueryToml[] = {
|
||||
{"family =", &fontName},
|
||||
{"normal =", &fontNormal},
|
||||
{"size =", &fontSize},
|
||||
};
|
||||
|
||||
@@ -25,29 +30,39 @@ static void detectAlacritty(FFTerminalFontResult* terminalFont)
|
||||
break;
|
||||
if(ffParsePropFileConfigValues(".alacritty.toml", 2, fontQueryToml))
|
||||
break;
|
||||
|
||||
FFpropquery fontQueryYaml[] = {
|
||||
{"family:", &fontName},
|
||||
{"size:", &fontSize},
|
||||
};
|
||||
|
||||
if(ffParsePropFileConfigValues("alacritty/alacritty.yml", 2, fontQueryYaml))
|
||||
break;
|
||||
if(ffParsePropFileConfigValues("alacritty.yml", 2, fontQueryYaml))
|
||||
break;
|
||||
if(ffParsePropFileConfigValues(".alacritty.yml", 2, fontQueryYaml))
|
||||
break;
|
||||
} while (false);
|
||||
|
||||
//by default alacritty uses its own font called alacritty
|
||||
if(fontName.length == 0)
|
||||
ffStrbufAppendS(&fontName, "alacritty");
|
||||
if(fontNormal.length > 0)
|
||||
{
|
||||
// { family = "Fira Code", style = "Medium" }
|
||||
ffStrbufTrimSpace(&fontNormal);
|
||||
ffStrbufTrimRight(&fontNormal, '}');
|
||||
ffStrbufTrimLeft(&fontNormal, '{');
|
||||
ffStrbufTrimSpace(&fontNormal);
|
||||
|
||||
// family = "Fira Code", style = "Medium"
|
||||
ffStrbufReplaceAllC(&fontNormal, ',', '\n'); // Assume no commas in font names
|
||||
ffParsePropLines(fontNormal.chars, "family =", &fontFamily);
|
||||
ffParsePropLines(fontNormal.chars, "style =", &fontStyle);
|
||||
}
|
||||
|
||||
if (fontFamily.length == 0)
|
||||
{
|
||||
#if __APPLE__
|
||||
ffStrbufSetStatic(&fontFamily, "Menlo");
|
||||
#elif _WIN32
|
||||
ffStrbufSetStatic(&fontFamily, "Consolas");
|
||||
#else
|
||||
ffStrbufSetStatic(&fontFamily, "monospace");
|
||||
#endif
|
||||
}
|
||||
if (fontStyle.length == 0)
|
||||
ffStrbufSetStatic(&fontStyle, "Regular");
|
||||
|
||||
// the default font size is 11
|
||||
if(fontSize.length == 0)
|
||||
ffStrbufAppendS(&fontSize, "11");
|
||||
ffStrbufSetStatic(&fontSize, "11.25");
|
||||
|
||||
ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars);
|
||||
ffFontInitMoveValues(&terminalFont->font, &fontFamily, &fontSize, &fontStyle);
|
||||
}
|
||||
|
||||
static void detectGhostty(const FFstrbuf* exe, FFTerminalFontResult* terminalFont)
|
||||
@@ -105,7 +120,13 @@ static void detectGhostty(const FFstrbuf* exe, FFTerminalFontResult* terminalFon
|
||||
}
|
||||
|
||||
if (fontSize.length == 0) {
|
||||
ffStrbufAppendS(&fontSize, "13");
|
||||
ffStrbufAppendS(&fontSize,
|
||||
#if __APPLE__
|
||||
"13"
|
||||
#else
|
||||
"12"
|
||||
#endif
|
||||
);
|
||||
FF_DEBUG("using default size='%s'", fontSize.chars);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
#include "common/io/io.h"
|
||||
#include "common/processing.h"
|
||||
#include "common/properties.h"
|
||||
#include "util/path.h"
|
||||
#include "util/stringUtils.h"
|
||||
#include "util/binary.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#ifdef __FreeBSD__
|
||||
#include <paths.h>
|
||||
#ifndef _PATH_LOCALBASE
|
||||
@@ -63,12 +65,9 @@ static bool extractBashVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, v
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* exePath, FFstrbuf* version)
|
||||
static bool getShellVersionBash(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
const char* path = exePath->chars;
|
||||
if (*path == '\0')
|
||||
path = exe->chars;
|
||||
ffBinaryExtractStrings(path, extractBashVersion, version, (uint32_t) strlen("@(#)Bash version 0.0.0(0) release GNU"));
|
||||
ffBinaryExtractStrings(exe->chars, extractBashVersion, version, (uint32_t) strlen("@(#)Bash version 0.0.0(0) release GNU"));
|
||||
if (version->length > 0) return true;
|
||||
|
||||
if(!getExeVersionRaw(exe, version))
|
||||
@@ -85,8 +84,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;
|
||||
}
|
||||
@@ -119,35 +120,39 @@ static bool getShellVersionPwsh(FFstrbuf* exe, FFstrbuf* version)
|
||||
|
||||
static bool getShellVersionKsh(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
#if __OpenBSD__ || __NetBSD__
|
||||
if(ffProcessAppendStdErr(version, (char* const[]) {
|
||||
exe->chars,
|
||||
"--version",
|
||||
NULL
|
||||
}) == NULL && ffStrbufSubstrAfterFirstS(version, " (AT&T Research) "))
|
||||
{
|
||||
// version sh (AT&T Research) 93u+ 2012-08-01
|
||||
ffStrbufSubstrBeforeFirstC(version, ' ');
|
||||
return true;
|
||||
}
|
||||
|
||||
ffStrbufClear(version);
|
||||
if(ffProcessAppendStdOut(version, (char* const[]) {
|
||||
exe->chars,
|
||||
"-c",
|
||||
"echo $KSH_VERSION",
|
||||
NULL
|
||||
}) != NULL)
|
||||
return false;
|
||||
}) == NULL && ffStrbufSubstrAfterFirstS(version, " KSH "))
|
||||
{
|
||||
// OKSH: @(#)PD KSH v5.2.14 99/07/13.2
|
||||
// MKSH: @(#)MIRBSD KSH R59 2025/04/26 +Debian
|
||||
// $OKSH_VERSION doesn't exist on OpenBSD
|
||||
ffStrbufSubstrBeforeFirstC(version, ' ');
|
||||
ffStrbufTrimLeft(version, 'v');
|
||||
return true;
|
||||
}
|
||||
|
||||
// @(#)PD KSH v5.2.14 99/07/13.2
|
||||
ffStrbufSubstrAfterFirstC(version, 'v');
|
||||
ffStrbufSubstrBeforeFirstC(version, ' ');
|
||||
#else
|
||||
if(ffProcessAppendStdErr(version, (char* const[]) {
|
||||
exe->chars,
|
||||
"--version",
|
||||
NULL
|
||||
}) != NULL)
|
||||
return false;
|
||||
|
||||
// version sh (AT&T Research) 93u+ 2012-08-01
|
||||
ffStrbufSubstrAfterLastC(version, ')');
|
||||
ffStrbufTrim(version, ' ');
|
||||
#endif
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getShellVersionOksh(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
// Homebrew version
|
||||
if(ffProcessAppendStdOut(version, (char* const[]) {
|
||||
exe->chars,
|
||||
"-c",
|
||||
@@ -227,13 +232,9 @@ static bool extractZshVersion(const char* line, FF_MAYBE_UNUSED uint32_t len, vo
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getShellVersionZsh(FFstrbuf* exe, FFstrbuf* exePath, FFstrbuf* version)
|
||||
static bool getShellVersionZsh(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
const char* path = exePath->chars;
|
||||
if (*path == '\0')
|
||||
path = exe->chars;
|
||||
|
||||
ffBinaryExtractStrings(path, extractZshVersion, version, (uint32_t) strlen("zsh-0.0-0"));
|
||||
ffBinaryExtractStrings(exe->chars, extractZshVersion, version, (uint32_t) strlen("zsh-0.0-0"));
|
||||
if (version->length) return true;
|
||||
|
||||
return getExeVersionGeneral(exe, version); //zsh 5.9 (arm-apple-darwin21.3.0)
|
||||
@@ -260,7 +261,7 @@ static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, FFstrbuf* version)
|
||||
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
|
||||
{
|
||||
if (!instance.config.general.detectVersion) return false;
|
||||
|
||||
@@ -268,9 +269,9 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath,
|
||||
return false;
|
||||
|
||||
if(ffStrEqualsIgnCase(exeName, "bash"))
|
||||
return getShellVersionBash(exe, exePath, version);
|
||||
return getShellVersionBash(exe, version);
|
||||
if(ffStrEqualsIgnCase(exeName, "zsh"))
|
||||
return getShellVersionZsh(exe, exePath, version);
|
||||
return getShellVersionZsh(exe, version);
|
||||
if(ffStrEqualsIgnCase(exeName, "fish"))
|
||||
return getShellVersionFish(exe, version);
|
||||
if(ffStrEqualsIgnCase(exeName, "pwsh"))
|
||||
@@ -279,7 +280,7 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath,
|
||||
return getExeVersionGeneral(exe, version); //tcsh 6.24.07 (Astron) 2022-12-21 (aarch64-apple-darwin) options wide,nls,dl,al,kan,sm,rh,color,filec
|
||||
if(ffStrEqualsIgnCase(exeName, "nu"))
|
||||
return getShellVersionNushell(exe, version);
|
||||
if(ffStrEqualsIgnCase(exeName, "ksh"))
|
||||
if(ffStrEqualsIgnCase(exeName, "ksh") || ffStrEqualsIgnCase(exeName, "mksh"))
|
||||
return getShellVersionKsh(exe, version);
|
||||
if(ffStrEqualsIgnCase(exeName, "oksh"))
|
||||
return getShellVersionOksh(exe, version);
|
||||
@@ -291,6 +292,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"))
|
||||
@@ -320,7 +323,7 @@ static bool extractGeneralVersion(const char *str, FF_MAYBE_UNUSED uint32_t len,
|
||||
|
||||
FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
if (exe->chars[0] == '/')
|
||||
if (ffIsAbsolutePath(exe->chars))
|
||||
{
|
||||
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
|
||||
if (version->length) return true;
|
||||
@@ -340,7 +343,7 @@ FF_MAYBE_UNUSED static bool getTerminalVersionGnome(FFstrbuf* exe, FFstrbuf* ver
|
||||
|
||||
FF_MAYBE_UNUSED static bool getTerminalVersionXfce4Terminal(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
if (exe->chars[0] == '/')
|
||||
if (ffIsAbsolutePath(exe->chars))
|
||||
{
|
||||
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
|
||||
if (version->length) return true;
|
||||
@@ -577,6 +580,44 @@ static bool getTerminalVersionZed(FFstrbuf* exe, FFstrbuf* version)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool extractSshdVersion(const char *str, FF_MAYBE_UNUSED uint32_t len, void *userdata)
|
||||
{
|
||||
if (!ffStrStartsWith(str, "OpenSSH_") || !ffCharIsDigit(str[strlen("OpenSSH_")])) return true;
|
||||
str += strlen("OpenSSH_");
|
||||
int count = 0;
|
||||
sscanf(str, "%*d.%*dp%*d%n", &count);
|
||||
if (count == 0) return true;
|
||||
ffStrbufSetS((FFstrbuf*) userdata, str);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getTerminalVersionSshd(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY exePath = ffStrbufCreate();
|
||||
if (ffIsAbsolutePath(exe->chars))
|
||||
ffStrbufSet(&exePath, exe);
|
||||
else if (ffFindExecutableInPath("sshd", &exePath) != NULL)
|
||||
return false;
|
||||
|
||||
ffBinaryExtractStrings(exePath.chars, extractSshdVersion, version, (uint32_t) strlen("OpenSSH0.0"));
|
||||
if (version->length) return true;
|
||||
|
||||
if(ffProcessAppendStdOut(version, (char* const[]) {
|
||||
exePath.chars,
|
||||
"-V",
|
||||
NULL
|
||||
}) != NULL)
|
||||
return false;
|
||||
|
||||
if (ffStrbufStartsWithS(version, "unknown ")) // `unknown option -- V` (ancient OpenSSH version)
|
||||
ffStrbufSubstrAfterFirstC(version, '\n');
|
||||
|
||||
// OpenSSH_10.0p2 Ubuntu-5ubuntu5, OpenSSL 3.5.3 16 Sep 2025
|
||||
ffStrbufSubstrAfterFirstC(version, '_');
|
||||
ffStrbufSubstrBeforeFirstC(version, ',');
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
static bool getTerminalVersionKitty(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
@@ -676,7 +717,7 @@ FF_MAYBE_UNUSED static bool getTerminalVersionPtyxis(FF_MAYBE_UNUSED FFstrbuf* e
|
||||
|
||||
FF_MAYBE_UNUSED static bool getTerminalVersionTilix(FFstrbuf* exe, FFstrbuf* version)
|
||||
{
|
||||
if (exe->chars[0] == '/')
|
||||
if (ffIsAbsolutePath(exe->chars))
|
||||
{
|
||||
ffBinaryExtractStrings(exe->chars, extractGeneralVersion, version, (uint32_t) strlen("0.0.0"));
|
||||
if (version->length) return true;
|
||||
@@ -920,6 +961,9 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe
|
||||
if(ffStrbufStartsWithIgnCaseS(processName, "tmux"))
|
||||
return getTerminalVersionTmux(exe, version);
|
||||
|
||||
if(ffStrbufIgnCaseEqualS(processName, "sshd") || ffStrbufStartsWithIgnCaseS(processName, "sshd-"))
|
||||
return getTerminalVersionSshd(exe, version);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
return getFileVersion(exe, NULL, version);
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
@@ -288,14 +292,14 @@ static void getUserShellFromEnv(FFShellResult* result)
|
||||
}
|
||||
}
|
||||
|
||||
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* exePath, FFstrbuf* version);
|
||||
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
|
||||
|
||||
bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version);
|
||||
|
||||
static void setShellInfoDetails(FFShellResult* result)
|
||||
{
|
||||
ffStrbufClear(&result->version);
|
||||
fftsGetShellVersion(&result->exe, result->exeName, &result->exePath, &result->version);
|
||||
fftsGetShellVersion(result->exePath.length > 0 ? &result->exePath : &result->exe, result->exeName, &result->version);
|
||||
|
||||
if(ffStrbufEqualS(&result->processName, "pwsh"))
|
||||
ffStrbufInitStatic(&result->prettyName, "PowerShell");
|
||||
@@ -327,7 +331,12 @@ static void setTerminalInfoDetails(FFTerminalResult* result)
|
||||
else if(ffStrbufStartsWithS(&result->processName, "screen-"))
|
||||
ffStrbufInitStatic(&result->prettyName, "screen");
|
||||
else if(ffStrbufEqualS(&result->processName, "sshd") || ffStrbufStartsWithS(&result->processName, "sshd-"))
|
||||
ffStrbufInitCopy(&result->prettyName, &result->tty);
|
||||
{
|
||||
if (result->tty.length)
|
||||
ffStrbufInitCopy(&result->prettyName, &result->tty);
|
||||
else
|
||||
ffStrbufSetStatic(&result->prettyName, "sshd");
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
@@ -379,7 +388,7 @@ static void setTerminalInfoDetails(FFTerminalResult* result)
|
||||
else
|
||||
ffStrbufInitCopy(&result->prettyName, &result->processName);
|
||||
|
||||
fftsGetTerminalVersion(&result->processName, &result->exe, &result->version);
|
||||
fftsGetTerminalVersion(&result->processName, result->exePath.length > 0 ? &result->exePath : &result->exe, &result->version);
|
||||
}
|
||||
|
||||
#if defined(MAXPATH)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <wchar.h>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, const FFstrbuf* exePath, FFstrbuf* version);
|
||||
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
|
||||
|
||||
static uint32_t getShellInfo(FFShellResult* result, uint32_t pid)
|
||||
{
|
||||
@@ -332,7 +332,7 @@ const FFShellResult* ffDetectShell(void)
|
||||
strcpy(tmp, result.exeName);
|
||||
char* ext = strrchr(tmp, '.');
|
||||
if (ext) *ext = '\0';
|
||||
fftsGetShellVersion(&result.exe, tmp, &result.exePath, &result.version);
|
||||
fftsGetShellVersion(result.exePath.length > 0 ? &result.exePath : &result.exe, tmp, &result.version);
|
||||
}
|
||||
|
||||
return &result;
|
||||
@@ -368,7 +368,7 @@ const FFTerminalResult* ffDetectTerminal(void)
|
||||
if(result.processName.length > 0)
|
||||
{
|
||||
setTerminalInfoDetails(&result);
|
||||
fftsGetTerminalVersion(&result.processName, &result.exe, &result.version);
|
||||
fftsGetTerminalVersion(&result.processName, result.exePath.length > 0 ? &result.exePath : &result.exe, &result.version);
|
||||
}
|
||||
|
||||
return &result;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "theme.h"
|
||||
|
||||
#include "detection/os/os.h"
|
||||
|
||||
const char* ffDetectTheme(FFThemeResult* result)
|
||||
{
|
||||
const FFOSResult* os = ffDetectOS();
|
||||
|
||||
char* str_end;
|
||||
const char* version = os->version.chars;
|
||||
unsigned long osNum = strtoul(version, &str_end, 10);
|
||||
if (str_end != version)
|
||||
{
|
||||
if (osNum > 15) { // Tahoe
|
||||
ffStrbufSetStatic(&result->theme1, "Liquid Glass");
|
||||
} else if (osNum < 10) {
|
||||
ffStrbufSetStatic(&result->theme1, "Platinum");
|
||||
} else {
|
||||
ffStrbufSetStatic(&result->theme1, "Aqua");
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "theme.h"
|
||||
|
||||
#include "detection/os/os.h"
|
||||
|
||||
const char* ffDetectTheme(FFThemeResult* result)
|
||||
{
|
||||
const FFOSResult* os = ffDetectOS();
|
||||
uint32_t ver = (uint32_t) ffStrbufToUInt(&os->version, 0);
|
||||
if (ver > 1000)
|
||||
{
|
||||
// Windows Server
|
||||
if (ver >= 2016)
|
||||
ffStrbufSetStatic(&result->theme1, "Fluent");
|
||||
else if (ver >= 2012)
|
||||
ffStrbufSetStatic(&result->theme1, "Metro");
|
||||
else
|
||||
ffStrbufSetStatic(&result->theme1, "Aero");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ver >= 10)
|
||||
ffStrbufSetStatic(&result->theme1, "Fluent");
|
||||
else if (ver >= 8)
|
||||
ffStrbufSetStatic(&result->theme1, "Metro");
|
||||
else
|
||||
ffStrbufSetStatic(&result->theme1, "Aero");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "common/io/io.h"
|
||||
#include "common/properties.h"
|
||||
#include "fastfetch.h"
|
||||
#include "users.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#if FF_HAVE_UTMPX
|
||||
#include <utmpx.h>
|
||||
#else
|
||||
@@ -15,7 +19,140 @@
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
const char* ffDetectUsers(FFUsersOptions* options, FFlist* users)
|
||||
#if __linux__
|
||||
bool detectUserBySystemd(const FFstrbuf* pathUsers, FFlist* users)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY state = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY userName = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY loginTime = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY sessions = ffStrbufCreate();
|
||||
|
||||
// WARNING: This is private data. Do not parse
|
||||
if (!ffParsePropFileValues(pathUsers->chars, 4, (FFpropquery[]) {
|
||||
{"NAME=", &userName},
|
||||
{"STATE=", &state},
|
||||
{"REALTIME=", &loginTime},
|
||||
{"ONLINE_SESSIONS=", &sessions},
|
||||
}) || !ffStrbufEqualS(&state, "active"))
|
||||
return false;
|
||||
|
||||
FFUserResult* user = FF_LIST_ADD(FFUserResult, *users);
|
||||
ffStrbufInitMove(&user->name, &userName);
|
||||
ffStrbufInit(&user->hostName);
|
||||
ffStrbufInit(&user->sessionName);
|
||||
ffStrbufInit(&user->clientIp);
|
||||
ffStrbufSubstrBefore(&loginTime, loginTime.length - 3); // converts us to ms
|
||||
user->loginTime = ffStrbufToUInt(&loginTime, 0);
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY pathSessions = ffStrbufCreateS("/run/systemd/sessions/");
|
||||
const uint32_t pathSessionsBaseLen = pathSessions.length;
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY tty = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY remoteHost = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY service = ffStrbufCreate();
|
||||
|
||||
char* token = NULL;
|
||||
size_t n = 0;
|
||||
while (ffStrbufGetdelim(&token, &n, ' ', &sessions))
|
||||
{
|
||||
ffStrbufSubstrBefore(&pathSessions, pathSessionsBaseLen);
|
||||
ffStrbufAppendS(&pathSessions, token);
|
||||
|
||||
ffStrbufClear(&remoteHost);
|
||||
ffStrbufClear(&service);
|
||||
ffStrbufClear(&tty);
|
||||
ffStrbufClear(&loginTime);
|
||||
|
||||
// WARNING: This is private data. Do not parse
|
||||
if (ffParsePropFileValues(pathSessions.chars, 4, (FFpropquery[]) {
|
||||
{"REMOTE_HOST=", &remoteHost},
|
||||
{"TTY=", &tty},
|
||||
{"SERVICE=", &service},
|
||||
{"REALTIME=", &loginTime},
|
||||
}) && !ffStrbufEqualS(&service, "systemd-user"))
|
||||
{
|
||||
if (remoteHost.length)
|
||||
{
|
||||
ffStrbufTrimRight(&remoteHost, ']');
|
||||
ffStrbufTrimLeft(&remoteHost, '[');
|
||||
ffStrbufInitMove(&user->hostName, &remoteHost);
|
||||
}
|
||||
else
|
||||
ffStrbufSetStatic(&user->hostName, "localhost");
|
||||
ffStrbufInitMove(&user->sessionName, tty.length ? &tty : &service);
|
||||
if (loginTime.length)
|
||||
{
|
||||
ffStrbufSubstrBefore(&loginTime, loginTime.length - 3); // converts us to ms
|
||||
user->loginTime = ffStrbufToUInt(&loginTime, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* detectBySystemd(FFUsersOptions* options, FFlist* users)
|
||||
{
|
||||
// For some reason, debian/ubuntu no longer updates `/var/run/utmp` (#2064)
|
||||
// Query systemd instead
|
||||
FF_STRBUF_AUTO_DESTROY pathUsers = ffStrbufCreateS("/run/systemd/users/");
|
||||
|
||||
if (options->myselfOnly)
|
||||
{
|
||||
ffStrbufAppendUInt(&pathUsers, getuid());
|
||||
detectUserBySystemd(&pathUsers, users);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint32_t pathUsersBaseLen = pathUsers.length;
|
||||
FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pathUsers.chars);
|
||||
if (!dirp) return "opendir(\"/run/systemd/users/\") failed";
|
||||
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dirp)))
|
||||
{
|
||||
if (entry->d_type != DT_REG) continue;
|
||||
|
||||
ffStrbufAppendS(&pathUsers, entry->d_name);
|
||||
detectUserBySystemd(&pathUsers, users);
|
||||
ffStrbufSubstrBefore(&pathUsers, pathUsersBaseLen);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __linux__ || __GNU__
|
||||
static void fillUtmpIpAddr(FFUserResult* user, struct utmpx* n)
|
||||
{
|
||||
bool isIpv6 = false;
|
||||
for (int i = 1; i < 4; ++i) {
|
||||
if (n->ut_addr_v6[i] != 0) {
|
||||
isIpv6 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isIpv6) {
|
||||
char ipv6_str[INET6_ADDRSTRLEN];
|
||||
if (inet_ntop(AF_INET6, n->ut_addr_v6, ipv6_str, INET6_ADDRSTRLEN) != NULL) {
|
||||
ffStrbufSetS(&user->clientIp, ipv6_str);
|
||||
}
|
||||
} else if (n->ut_addr_v6[0] != 0) {
|
||||
char ipv4_str[INET_ADDRSTRLEN];
|
||||
if (inet_ntop(AF_INET, n->ut_addr_v6, ipv4_str, INET_ADDRSTRLEN) != NULL) {
|
||||
ffStrbufSetS(&user->clientIp, ipv4_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void fillUtmpIpAddr(FF_MAYBE_UNUSED FFUserResult* user, FF_MAYBE_UNUSED struct utmpx* n)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* detectByUtmp(FFUsersOptions* options, FFlist* users)
|
||||
{
|
||||
struct utmpx* n = NULL;
|
||||
setutxent();
|
||||
@@ -32,37 +169,41 @@ next:
|
||||
FF_LIST_FOR_EACH(FFUserResult, user, *users)
|
||||
{
|
||||
if(ffStrbufEqualS(&user->name, n->ut_user))
|
||||
{
|
||||
uint64_t newLoginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000;
|
||||
if (newLoginTime > user->loginTime)
|
||||
{
|
||||
ffStrbufSetS(&user->hostName, n->ut_host);
|
||||
ffStrbufSetS(&user->sessionName, n->ut_line);
|
||||
fillUtmpIpAddr(user, n);
|
||||
user->loginTime = newLoginTime;
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
FFUserResult* user = (FFUserResult*) ffListAdd(users);
|
||||
FFUserResult* user = FF_LIST_ADD(FFUserResult, *users);
|
||||
ffStrbufInitS(&user->name, n->ut_user);
|
||||
ffStrbufInitS(&user->hostName, n->ut_host);
|
||||
ffStrbufInitS(&user->sessionName, n->ut_line);
|
||||
ffStrbufInit(&user->clientIp);
|
||||
#if __linux__ || __GNU__
|
||||
bool isIpv6 = false;
|
||||
for (int i = 1; i < 4; ++i) {
|
||||
if (n->ut_addr_v6[i] != 0) {
|
||||
isIpv6 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isIpv6) {
|
||||
char ipv6_str[INET6_ADDRSTRLEN];
|
||||
if (inet_ntop(AF_INET6, n->ut_addr_v6, ipv6_str, INET6_ADDRSTRLEN) != NULL) {
|
||||
ffStrbufSetS(&user->clientIp, ipv6_str);
|
||||
}
|
||||
} else if (n->ut_addr_v6[0] != 0) {
|
||||
char ipv4_str[INET_ADDRSTRLEN];
|
||||
if (inet_ntop(AF_INET, n->ut_addr_v6, ipv4_str, INET_ADDRSTRLEN) != NULL) {
|
||||
ffStrbufSetS(&user->clientIp, ipv4_str);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
fillUtmpIpAddr(user, n);
|
||||
user->loginTime = (uint64_t) n->ut_tv.tv_sec * 1000 + (uint64_t) n->ut_tv.tv_usec / 1000;
|
||||
}
|
||||
|
||||
endutxent();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffDetectUsers(FFUsersOptions* options, FFlist* users)
|
||||
{
|
||||
const char* err = detectByUtmp(options, users);
|
||||
if (err) return err;
|
||||
|
||||
#if __linux__
|
||||
if (users->length == 0) detectBySystemd(options, users);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#define FF_ARCHITECTURE "alpha"
|
||||
#elif defined(__hppa__)
|
||||
#define FF_ARCHITECTURE "hppa"
|
||||
#elif defined(__sh__)
|
||||
#define FF_ARCHITECTURE "sh"
|
||||
#elif defined(__m68k__)
|
||||
#define FF_ARCHITECTURE "m68k"
|
||||
#else
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -12,7 +12,7 @@ extern "C"
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <shlobj.h>
|
||||
#include <Propkey.h>
|
||||
#include <propkey.h>
|
||||
|
||||
template <typename Fn>
|
||||
struct on_scope_exit {
|
||||
|
||||
@@ -25,65 +25,24 @@
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
ZPOOL_PROP_INVAL = -1,
|
||||
ZPOOL_PROP_NAME,
|
||||
ZPOOL_PROP_SIZE,
|
||||
ZPOOL_PROP_CAPACITY,
|
||||
ZPOOL_PROP_ALTROOT,
|
||||
ZPOOL_PROP_HEALTH,
|
||||
ZPOOL_PROP_GUID,
|
||||
ZPOOL_PROP_VERSION,
|
||||
ZPOOL_PROP_BOOTFS,
|
||||
ZPOOL_PROP_DELEGATION,
|
||||
ZPOOL_PROP_AUTOREPLACE,
|
||||
ZPOOL_PROP_CACHEFILE,
|
||||
ZPOOL_PROP_FAILUREMODE,
|
||||
ZPOOL_PROP_LISTSNAPS,
|
||||
ZPOOL_PROP_AUTOEXPAND,
|
||||
ZPOOL_PROP_DEDUPDITTO,
|
||||
ZPOOL_PROP_DEDUPRATIO,
|
||||
ZPOOL_PROP_FREE,
|
||||
ZPOOL_PROP_ALLOCATED,
|
||||
ZPOOL_PROP_READONLY,
|
||||
ZPOOL_PROP_ASHIFT,
|
||||
ZPOOL_PROP_COMMENT,
|
||||
ZPOOL_PROP_EXPANDSZ,
|
||||
ZPOOL_PROP_FREEING,
|
||||
ZPOOL_PROP_FRAGMENTATION,
|
||||
ZPOOL_PROP_LEAKED,
|
||||
ZPOOL_PROP_MAXBLOCKSIZE,
|
||||
ZPOOL_PROP_TNAME,
|
||||
ZPOOL_PROP_MAXDNODESIZE,
|
||||
ZPOOL_PROP_MULTIHOST,
|
||||
ZPOOL_PROP_CHECKPOINT,
|
||||
ZPOOL_PROP_LOAD_GUID,
|
||||
ZPOOL_PROP_AUTOTRIM,
|
||||
ZPOOL_PROP_COMPATIBILITY,
|
||||
ZPOOL_PROP_BCLONEUSED,
|
||||
ZPOOL_PROP_BCLONESAVED,
|
||||
ZPOOL_PROP_BCLONERATIO,
|
||||
ZPOOL_NUM_PROPS
|
||||
} zpool_prop_t;
|
||||
|
||||
typedef enum {
|
||||
ZPROP_SRC_NONE = 0x1,
|
||||
ZPROP_SRC_DEFAULT = 0x2,
|
||||
ZPROP_SRC_TEMPORARY = 0x4,
|
||||
ZPROP_SRC_LOCAL = 0x8,
|
||||
ZPROP_SRC_INHERITED = 0x10,
|
||||
ZPROP_SRC_RECEIVED = 0x20
|
||||
} zprop_source_t;
|
||||
|
||||
typedef bool boolean_t;
|
||||
// zpool_prop_t and zprop_source_t were previously enums in upstream OpenZFS.
|
||||
// However, the enum values for these types vary greatly between different platforms,
|
||||
// making it unsafe to use the enum values directly. To ensure portability,
|
||||
// we define them as simple int typedefs and use zpool_name_to_prop to look up
|
||||
// the correct value for a property at runtime.
|
||||
typedef int zpool_prop_t;
|
||||
typedef int zprop_source_t;
|
||||
typedef int boolean_t;
|
||||
|
||||
typedef struct libzfs_handle libzfs_handle_t;
|
||||
typedef struct zpool_handle zpool_handle_t;
|
||||
typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
|
||||
extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
|
||||
|
||||
extern libzfs_handle_t *libzfs_init(void);
|
||||
extern void libzfs_fini(libzfs_handle_t *);
|
||||
extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t, zprop_source_t *);
|
||||
extern const char *zpool_get_name(zpool_handle_t *);
|
||||
extern const char *zpool_get_state_str(zpool_handle_t *);
|
||||
extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
|
||||
extern zpool_prop_t zpool_name_to_prop(const char *);
|
||||
// https://github.com/openzfs/zfs/blob/06c73cffabc30b61a695988ec8e290f43cb3768d/lib/libzfs/libzfs_pool.c#L300
|
||||
extern uint64_t zpool_get_prop_int(zpool_handle_t *zhp, zpool_prop_t prop, zprop_source_t *srctype);
|
||||
extern int zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len, zprop_source_t *srctype, boolean_t literal);
|
||||
extern void zpool_close(zpool_handle_t *);
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
#include "zpool.h"
|
||||
|
||||
#if FF_HAVE_LIBZFS
|
||||
|
||||
#include "util/kmod.h"
|
||||
|
||||
#ifdef __sun
|
||||
#include <libzfs.h>
|
||||
#else
|
||||
#include "libzfs_simplified.h"
|
||||
#endif
|
||||
|
||||
#include "common/library.h"
|
||||
|
||||
typedef struct FFZfsData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(libzfs_fini)
|
||||
FF_LIBRARY_SYMBOL(zpool_get_prop_int)
|
||||
FF_LIBRARY_SYMBOL(zpool_get_prop)
|
||||
FF_LIBRARY_SYMBOL(zpool_close)
|
||||
|
||||
// The fields in this struct store property IDs returned by `zpool_name_to_prop`,
|
||||
// not the property values themselves.
|
||||
struct {
|
||||
int name;
|
||||
int health;
|
||||
int guid;
|
||||
int size;
|
||||
int free;
|
||||
int allocated;
|
||||
int fragmentation;
|
||||
int readonly;
|
||||
} props;
|
||||
|
||||
libzfs_handle_t* handle;
|
||||
FFlist* result;
|
||||
} FFZfsData;
|
||||
|
||||
static inline void cleanLibzfs(FFZfsData* data)
|
||||
{
|
||||
if (data->fflibzfs_fini && data->handle)
|
||||
{
|
||||
data->fflibzfs_fini(data->handle);
|
||||
data->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int enumZpoolCallback(zpool_handle_t* zpool, void* param)
|
||||
{
|
||||
FFZfsData* data = (FFZfsData*) param;
|
||||
zprop_source_t source;
|
||||
FFZpoolResult* item = ffListAdd(data->result);
|
||||
char buf[1024];
|
||||
if (data->ffzpool_get_prop(zpool, data->props.name, buf, ARRAY_SIZE(buf), &source, false) == 0)
|
||||
ffStrbufInitS(&item->name, buf);
|
||||
else
|
||||
ffStrbufInitStatic(&item->name, "unknown");
|
||||
if (data->ffzpool_get_prop(zpool, data->props.health, buf, ARRAY_SIZE(buf), &source, false) == 0)
|
||||
ffStrbufInitS(&item->state, buf);
|
||||
else
|
||||
ffStrbufInitStatic(&item->state, "unknown");
|
||||
item->guid = data->ffzpool_get_prop_int(zpool, data->props.guid, &source);
|
||||
item->total = data->ffzpool_get_prop_int(zpool, data->props.size, &source);
|
||||
item->used = item->total - data->ffzpool_get_prop_int(zpool, data->props.free, &source);
|
||||
item->allocated = data->ffzpool_get_prop_int(zpool, data->props.allocated, &source);
|
||||
uint64_t fragmentation = data->ffzpool_get_prop_int(zpool, data->props.fragmentation, &source);
|
||||
item->fragmentation = fragmentation == UINT64_MAX ? -DBL_MAX : (double) fragmentation;
|
||||
item->readOnly = (bool) data->ffzpool_get_prop_int(zpool, data->props.readonly, &source);
|
||||
data->ffzpool_close(zpool);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */)
|
||||
{
|
||||
FF_LIBRARY_LOAD(libzfs, "dlopen libzfs" FF_LIBRARY_EXTENSION " failed", "libzfs" FF_LIBRARY_EXTENSION, 6);
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, libzfs_init);
|
||||
|
||||
libzfs_handle_t* handle = fflibzfs_init();
|
||||
if (!handle)
|
||||
{
|
||||
if (!ffKmodLoaded("zfs")) return "`zfs` kernel module is not loaded";
|
||||
return "libzfs_init() failed";
|
||||
}
|
||||
|
||||
__attribute__((__cleanup__(cleanLibzfs))) FFZfsData data = {
|
||||
.handle = handle,
|
||||
.result = result,
|
||||
};
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, zpool_name_to_prop);
|
||||
|
||||
#define FF_QUERY_ZPOOL_PROP_FROM_NAME(prop_name) do { \
|
||||
data.props.prop_name = ffzpool_name_to_prop(#prop_name); \
|
||||
if (data.props.prop_name < 0) \
|
||||
return "Failed to query prop: " #prop_name; \
|
||||
} while (false)
|
||||
FF_QUERY_ZPOOL_PROP_FROM_NAME(name);
|
||||
FF_QUERY_ZPOOL_PROP_FROM_NAME(health);
|
||||
FF_QUERY_ZPOOL_PROP_FROM_NAME(guid);
|
||||
FF_QUERY_ZPOOL_PROP_FROM_NAME(size);
|
||||
FF_QUERY_ZPOOL_PROP_FROM_NAME(free);
|
||||
FF_QUERY_ZPOOL_PROP_FROM_NAME(allocated);
|
||||
FF_QUERY_ZPOOL_PROP_FROM_NAME(fragmentation);
|
||||
FF_QUERY_ZPOOL_PROP_FROM_NAME(readonly);
|
||||
#undef FF_QUERY_ZPOOL_PROP_FROM_NAME
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, zpool_iter);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, libzfs_fini);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_prop_int);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_prop);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_close);
|
||||
|
||||
if (ffzpool_iter(handle, enumZpoolCallback, &data) < 0)
|
||||
return "zpool_iter() failed";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result)
|
||||
{
|
||||
return "fastfetch was compiled without libzfs support";
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -7,10 +7,12 @@ typedef struct FFZpoolResult
|
||||
{
|
||||
FFstrbuf name;
|
||||
FFstrbuf state;
|
||||
uint64_t guid;
|
||||
uint64_t used;
|
||||
uint64_t total;
|
||||
uint64_t version;
|
||||
uint64_t allocated;
|
||||
double fragmentation;
|
||||
bool readOnly;
|
||||
} FFZpoolResult;
|
||||
|
||||
const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */);
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
#include "zpool.h"
|
||||
|
||||
#ifdef FF_HAVE_LIBZFS
|
||||
#include "util/kmod.h"
|
||||
|
||||
#ifdef __sun
|
||||
#define FF_DISABLE_DLOPEN
|
||||
#include <libzfs.h>
|
||||
|
||||
const char* zpool_get_state_str(zpool_handle_t* zpool)
|
||||
{
|
||||
if (zpool_get_state(zpool) == POOL_STATE_UNAVAIL)
|
||||
return "FAULTED";
|
||||
else
|
||||
{
|
||||
const char *str;
|
||||
zpool_errata_t errata;
|
||||
zpool_status_t status = zpool_get_status(zpool, (char**) &str, &errata);
|
||||
if (status == ZPOOL_STATUS_IO_FAILURE_WAIT ||
|
||||
status == ZPOOL_STATUS_IO_FAILURE_CONTINUE ||
|
||||
status == ZPOOL_STATUS_IO_FAILURE_MMP)
|
||||
return "SUSPENDED";
|
||||
else
|
||||
{
|
||||
nvlist_t *nvroot = fnvlist_lookup_nvlist(zpool_get_config(zpool, NULL), ZPOOL_CONFIG_VDEV_TREE);
|
||||
uint_t vsc;
|
||||
vdev_stat_t *vs;
|
||||
#ifdef __x86_64__
|
||||
if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint64_t**) &vs, &vsc) != 0)
|
||||
#else
|
||||
if (nvlist_lookup_uint32_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint32_t**) &vs, &vsc) != 0)
|
||||
#endif
|
||||
return "UNKNOWN";
|
||||
else
|
||||
return zpool_state_to_name(vs->vs_state, vs->vs_aux);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#include "libzfs_simplified.h"
|
||||
#endif
|
||||
|
||||
#include "common/library.h"
|
||||
|
||||
typedef struct FFZfsData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(libzfs_fini)
|
||||
FF_LIBRARY_SYMBOL(zpool_iter)
|
||||
FF_LIBRARY_SYMBOL(zpool_get_prop_int)
|
||||
FF_LIBRARY_SYMBOL(zpool_get_name)
|
||||
FF_LIBRARY_SYMBOL(zpool_get_state_str)
|
||||
|
||||
libzfs_handle_t* handle;
|
||||
FFlist* result;
|
||||
} FFZfsData;
|
||||
|
||||
static inline void cleanLibzfs(FFZfsData* data)
|
||||
{
|
||||
if (data->fflibzfs_fini && data->handle)
|
||||
{
|
||||
data->fflibzfs_fini(data->handle);
|
||||
data->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int enumZpoolCallback(zpool_handle_t* zpool, void* param)
|
||||
{
|
||||
FFZfsData* data = (FFZfsData*) param;
|
||||
zprop_source_t source;
|
||||
FFZpoolResult* item = ffListAdd(data->result);
|
||||
ffStrbufInitS(&item->name, data->ffzpool_get_name(zpool));
|
||||
ffStrbufInitS(&item->state, data->ffzpool_get_state_str(zpool));
|
||||
item->version = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_VERSION, &source);
|
||||
item->total = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_SIZE, &source);
|
||||
item->used = item->total - data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_FREE, &source);
|
||||
uint64_t fragmentation = data->ffzpool_get_prop_int(zpool, ZPOOL_PROP_FRAGMENTATION, &source);
|
||||
item->fragmentation = fragmentation == UINT64_MAX ? -DBL_MAX : (double) fragmentation;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* ffDetectZpool(FFlist* result /* list of FFZpoolResult */)
|
||||
{
|
||||
FF_LIBRARY_LOAD(libzfs, "dlopen libzfs" FF_LIBRARY_EXTENSION " failed", "libzfs" FF_LIBRARY_EXTENSION, 6);
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, libzfs_init);
|
||||
|
||||
libzfs_handle_t* handle = fflibzfs_init();
|
||||
if (!handle)
|
||||
{
|
||||
if (!ffKmodLoaded("zfs")) return "`zfs` kernel module is not loaded";
|
||||
return "libzfs_init() failed";
|
||||
}
|
||||
|
||||
__attribute__((__cleanup__(cleanLibzfs))) FFZfsData data = {
|
||||
.handle = handle,
|
||||
.result = result,
|
||||
};
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libzfs, zpool_iter);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, libzfs_fini);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_prop_int);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_name);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libzfs, data, zpool_get_state_str);
|
||||
|
||||
if (ffzpool_iter(handle, enumZpoolCallback, &data) < 0)
|
||||
return "zpool_iter() failed";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result)
|
||||
{
|
||||
return "Fastfetch was compiled without libzfs support";
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,6 +0,0 @@
|
||||
#include "zpool.h"
|
||||
|
||||
const char* ffDetectZpool(FF_MAYBE_UNUSED FFlist* result /* list of FFZpoolResult */)
|
||||
{
|
||||
return "Not supported on this platform";
|
||||
}
|
||||
+71
-27
@@ -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"
|
||||
@@ -13,7 +14,7 @@
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _WIN32
|
||||
#include "util/windows/getline.h"
|
||||
#endif
|
||||
|
||||
@@ -501,7 +502,20 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
|
||||
|
||||
if (parseJsoncFile(absolutePath.chars, flag)) return;
|
||||
|
||||
//Try to load as a relative path
|
||||
//Try to load as a relative path with the config directory
|
||||
|
||||
FF_LIST_FOR_EACH(FFstrbuf, path, instance.state.platform.configDirs)
|
||||
{
|
||||
ffStrbufSet(&absolutePath, path);
|
||||
ffStrbufAppendS(&absolutePath, "fastfetch/");
|
||||
ffStrbufAppendS(&absolutePath, value);
|
||||
if (needExtension)
|
||||
ffStrbufAppendS(&absolutePath, ".jsonc");
|
||||
|
||||
if (parseJsoncFile(absolutePath.chars, flag)) return;
|
||||
}
|
||||
|
||||
//Try to load as a preset
|
||||
|
||||
FF_LIST_FOR_EACH(FFstrbuf, path, instance.state.platform.dataDirs)
|
||||
{
|
||||
@@ -514,7 +528,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
|
||||
if (parseJsoncFile(absolutePath.chars, flag)) return;
|
||||
}
|
||||
|
||||
//Try to load as a relative path with the directory of fastfetch binary
|
||||
//Try to load as a relative path with the directory of fastfetch binary, for Windows support
|
||||
|
||||
if (instance.state.platform.exePath.length)
|
||||
{
|
||||
@@ -549,6 +563,26 @@ static void printVersion()
|
||||
printf("%s %s%s%s (%s)\n", result->projectName, result->version, result->versionTweak, result->debugMode ? "-debug" : "", result->architecture);
|
||||
}
|
||||
|
||||
static void enableJsonOutput(bool enable)
|
||||
{
|
||||
if (!enable)
|
||||
{
|
||||
if (instance.state.resultDoc)
|
||||
{
|
||||
yyjson_mut_doc_free(instance.state.resultDoc);
|
||||
instance.state.resultDoc = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!instance.state.resultDoc)
|
||||
{
|
||||
instance.state.resultDoc = yyjson_mut_doc_new(NULL);
|
||||
yyjson_mut_doc_set_root(instance.state.resultDoc, yyjson_mut_arr(instance.state.resultDoc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parseCommand(FFdata* data, char* key, char* value)
|
||||
{
|
||||
if(ffStrEqualsIgnCase(key, "-h") || ffStrEqualsIgnCase(key, "--help"))
|
||||
@@ -640,30 +674,18 @@ static void parseCommand(FFdata* data, char* key, char* value)
|
||||
generateConfigFile(true, value, true);
|
||||
else if(ffStrEqualsIgnCase(key, "-c") || ffStrEqualsIgnCase(key, "--config"))
|
||||
optionParseConfigFile(data, key, value);
|
||||
else if(ffStrEqualsIgnCase(key, "-j") || ffStrEqualsIgnCase(key, "--json"))
|
||||
enableJsonOutput(ffOptionParseBoolean(value));
|
||||
else if(ffStrEqualsIgnCase(key, "--format"))
|
||||
{
|
||||
switch (ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
|
||||
{ "default", 0},
|
||||
{ "json", 1 },
|
||||
enableJsonOutput(!!ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
|
||||
{ "default", false},
|
||||
{ "json", true },
|
||||
{},
|
||||
}))
|
||||
{
|
||||
case 0:
|
||||
if (instance.state.resultDoc)
|
||||
{
|
||||
yyjson_mut_doc_free(instance.state.resultDoc);
|
||||
instance.state.resultDoc = NULL;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (!instance.state.resultDoc)
|
||||
{
|
||||
instance.state.resultDoc = yyjson_mut_doc_new(NULL);
|
||||
yyjson_mut_doc_set_root(instance.state.resultDoc, yyjson_mut_arr(instance.state.resultDoc));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}));
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(key, "--dynamic-interval"))
|
||||
instance.state.dynamicInterval = ffOptionParseUInt32(key, value); // seconds to milliseconds
|
||||
else
|
||||
return;
|
||||
|
||||
@@ -762,15 +784,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)
|
||||
@@ -829,6 +867,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,13 @@
|
||||
----.---
|
||||
:-==.-==-
|
||||
$2 -=== ===:
|
||||
===-:===.
|
||||
$3 -+++:=++=
|
||||
=+++ +++.
|
||||
$6 .+++-=++=
|
||||
$4.::. ::: $6 :**+ +**-
|
||||
$4 :--:.---. $6 .+**=-***.
|
||||
$5 :--- ---::$6***:***+
|
||||
$5 .---::-$6=##*.*##-
|
||||
$7 ----+$6##=:##*.
|
||||
$7 :-$6###-*##+
|
||||
+12
-12
@@ -1,12 +1,12 @@
|
||||
^"^"^". `"^"""`
|
||||
";;;;;:. `;;;;;;.
|
||||
^;":;:;: .;,,;:;:
|
||||
.'`",, '":::. ^;'';;;;, .:: ,;;;:
|
||||
';;;;:":::::' ^;` `;;;;" ::. ,;;;:
|
||||
";:;:. ^;` ^;;;;^ ,;. ,;;;:
|
||||
";;;: ^;` ";;;;^ ";' ,;;;:
|
||||
";;;: ^;` ,;;;;`^;` ,;;;:
|
||||
";;;: ^;` ,;;;;;^ ,;;;:
|
||||
";:;: ^;` .:;:;" ,;:;:
|
||||
,;;;;. ";" .:;, :;;;;.
|
||||
."""""` ^"^ '" ."""""`
|
||||
#: :+#@@@@%#
|
||||
@@#: .*@@@@@@@@@
|
||||
@@@@#- .@@@@@@@@@@@
|
||||
@@@@@@%- %@@@@@@@@@@@
|
||||
@@@@@@@@%= :@@@@%%%%@@@@
|
||||
@@@@@@@@@@%==*-: .:=#
|
||||
:*@@@@@@@@@@=
|
||||
:*@@@@@@@@=
|
||||
.*@@@@@@=
|
||||
.+@@@@=
|
||||
.+%@=
|
||||
+=
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user