mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 10:22:12 +02:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa3a916d5a | |||
| 6e64b758bf | |||
| 49ef7fc51f | |||
| 0e9a5acdbd | |||
| 4f6e80e940 | |||
| df87cdfa6e | |||
| dee4b36a46 | |||
| d04a26337a | |||
| 2afe8d11d1 | |||
| f31d3b8b21 | |||
| 8a20af9917 | |||
| fc5c2f6213 | |||
| 6c314e148a | |||
| a86d0017a9 | |||
| 4038ed2656 | |||
| 0f4bb47c2f | |||
| 19f7c67ede | |||
| ff7e81ed81 | |||
| 681a102a2b | |||
| ba27f4f2ef | |||
| cbe33a4860 | |||
| 5847e3f269 | |||
| b248dfa01c | |||
| 357aa77e21 | |||
| a44bde6f94 | |||
| 5825bce57d | |||
| 996cdd3155 | |||
| 61cdfcabf8 | |||
| 359a77097c | |||
| 1323d007d3 | |||
| d2215f26ac | |||
| 958e7dbd8f | |||
| 5b10cde59f | |||
| e8f2b9a1c7 | |||
| 0686d1db4a | |||
| 7c284f9ee8 | |||
| dc04273bd7 | |||
| d698be6239 | |||
| e44df27c96 | |||
| b365d4cca1 | |||
| 19d3c948ad | |||
| 4a581039d5 | |||
| 79849fb2b5 | |||
| c116a5abdf | |||
| 8f082730a8 | |||
| b81c4a04db | |||
| e26a05e3ac | |||
| f08a1fb0b4 | |||
| 2a82009ffe | |||
| e851f3a7ed | |||
| 3a865df09e | |||
| bae80e2932 | |||
| 4445e1e78d | |||
| f0e3d3d8e1 | |||
| a313fd1572 | |||
| 927d44d919 | |||
| 3b9a91e8af | |||
| 70b4582bb6 | |||
| 461552b8d0 | |||
| 0f3db37199 | |||
| 67f16b269a | |||
| 25671fa4b4 | |||
| 4eaa04d4a4 | |||
| 7592885b9f | |||
| 508a3fde36 | |||
| f0273224e0 | |||
| 31e833c02e | |||
| a20d915412 | |||
| d3453de73c | |||
| 79a11c4728 | |||
| 24591417ce | |||
| de6d066fc9 | |||
| 6362c896f8 | |||
| 0418e9ca5e | |||
| ba0eeab18c | |||
| 5ef630cbc8 | |||
| 1699d14984 | |||
| 501746a270 | |||
| e6bd1c340f | |||
| 49fc95689d | |||
| 3d84ad19be | |||
| 57aa0cf499 | |||
| 6496dcd7d0 | |||
| 1c066bc69a | |||
| 2f1e37bb7c | |||
| fd3c87abe7 | |||
| af075b8ef8 | |||
| 71d461266e |
@@ -6,35 +6,42 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Tip: A logo can be displayed by fastfetch without getting into fastfetch's official repo.
|
||||
For highly customized logo for personal use, it's recommended to keep it locally.
|
||||
Tip: You can display a logo in fastfetch without adding it to fastfetch's official repo.
|
||||
For highly customized, personal logos, we recommend keeping them locally.
|
||||
Please refer to https://github.com/fastfetch-cli/fastfetch/wiki/Migrate-Neofetch-Logo-To-Fastfetch
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: OS
|
||||
description: Paste content of `/etc/os-release` and `/etc/lsb-release` here. If none of these files exist, describe a way to identify the distro
|
||||
description: Paste the contents of `/etc/os-release` and `/etc/lsb-release` here. If neither file exists, describe how to identify the distro.
|
||||
placeholder: cat /etc/os-release
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Distro Website
|
||||
description: To help prevent spam and verify the request, a distro website is required, and a downloadable ISO must be available on that site.
|
||||
placeholder: https://example.com
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: ASCII Art
|
||||
description: An ASCII logo should not take up too much space (smaller than 50x20 characters). Please also include the color codes if not available in `os-release`
|
||||
description: The ASCII logo should not take up too much space (smaller than 50x20 characters, W x H). Please also include the color codes if they are not available in `os-release`.
|
||||
placeholder: Paste ASCII art here
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Original Image
|
||||
description: If the ASCII art is based on an image, please provide a link to the original image
|
||||
placeholder: Image URL or distro website URL
|
||||
label: Original Image URL
|
||||
description: If the ASCII art is based on an image, please provide a link to the original image file.
|
||||
placeholder: Image URL from the distro website mentioned above
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
options:
|
||||
- label: The ASCII art is smaller than 50x20 characters (width x height)
|
||||
- label: The ASCII art is smaller than 50x20 characters (W x H).
|
||||
required: true
|
||||
- label: The ASCII art contains color codes, or the color codes are available in `os-release`
|
||||
- label: The ASCII art includes 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)
|
||||
- label: The ASCII art has no internal padding (spaces at the start and/or end of lines).
|
||||
required: true
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
## Summary
|
||||
|
||||
<!-- Briefly describe what this PR does. -->
|
||||
|
||||
## Related issue (required for new logos for new distros)
|
||||
|
||||
<!--
|
||||
If this PR adds a new logo, it MUST be linked to an existing "Logo Request" issue and has the requests fulfilled.
|
||||
|
||||
Use one of the following formats so GitHub links it properly:
|
||||
- Closes #1234
|
||||
- Fixes #1234
|
||||
- Resolves #1234
|
||||
|
||||
PRs that add logos without an associated Logo Request issue will not be accepted.
|
||||
-->
|
||||
|
||||
Closes #
|
||||
|
||||
## Changes
|
||||
|
||||
-
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have tested my changes locally.
|
||||
+79
-42
@@ -13,7 +13,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install codespell
|
||||
shell: bash
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
ffversion: ${{ steps.ffversion.outputs.ffversion }}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
|
||||
- name: Initialize CodeQL
|
||||
if: matrix.arch == 'amd64'
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: c
|
||||
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
|
||||
- name: perform CodeQL analysis
|
||||
if: matrix.arch == 'amd64'
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
- name: list features
|
||||
run: ./fastfetch --list-features
|
||||
@@ -151,7 +151,7 @@ jobs:
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-linux-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -164,7 +164,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
@@ -217,7 +217,7 @@ jobs:
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-linux-i686
|
||||
path: ./fastfetch-*.*
|
||||
@@ -230,7 +230,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
@@ -257,7 +257,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-linux-armv7l
|
||||
path: ./fastfetch-*.*
|
||||
@@ -270,7 +270,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
@@ -293,7 +293,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-linux-armv6l
|
||||
path: ./fastfetch-*.*
|
||||
@@ -312,7 +312,7 @@ jobs:
|
||||
- arch: s390x
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
@@ -336,7 +336,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-linux-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
@@ -345,7 +345,7 @@ jobs:
|
||||
name: Musl-amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: setup alpine linux
|
||||
uses: jirutka/setup-alpine@master
|
||||
@@ -374,7 +374,7 @@ jobs:
|
||||
shell: alpine.sh {0}
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-musl-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -394,7 +394,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: uname -a
|
||||
run: uname -a
|
||||
@@ -428,17 +428,17 @@ jobs:
|
||||
run: ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-macos-${{ matrix.arch }}
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
sunos-amd64:
|
||||
omnios-amd64:
|
||||
runs-on: ubuntu-latest
|
||||
name: SunOS-amd64
|
||||
name: OmniOS-amd64
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/omnios-vm@v1
|
||||
@@ -447,23 +447,59 @@ jobs:
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg update --accept
|
||||
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick
|
||||
pkg install gcc14 cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja
|
||||
|
||||
run: |
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On .
|
||||
cmake --build . --target package --verbose -j4
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
|
||||
cmake --build . --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
cpack
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-omnios-amd64")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-sunos-amd64
|
||||
name: fastfetch-omnios-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
solaris-amd64:
|
||||
runs-on: ubuntu-latest
|
||||
name: Solaris-amd64
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/solaris-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
release: "11.4-gcc-14"
|
||||
prepare: |
|
||||
uname -a
|
||||
pkg install cmake git pkg-config glib2 dbus sqlite-3 imagemagick ninja dconf mesa
|
||||
|
||||
run: |
|
||||
export PKG_CONFIG_PATH=/usr/lib/64/pkgconfig
|
||||
cmake -DSET_TWEAK=Off -DBUILD_TESTS=On -GNinja .
|
||||
cmake --build . --verbose -j4
|
||||
./fastfetch --list-features
|
||||
time ./fastfetch -c presets/ci.jsonc --stat false
|
||||
time ./fastfetch -c presets/ci.jsonc --format json
|
||||
time ./flashfetch
|
||||
ldd fastfetch
|
||||
ctest --output-on-failure
|
||||
echo 'set(CPACK_PACKAGE_FILE_NAME "fastfetch-solaris-amd64")' >> CPackConfig.cmake
|
||||
cpack -V
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-solaris-amd64
|
||||
path: ./fastfetch-*.*
|
||||
|
||||
freebsd-amd64:
|
||||
@@ -474,7 +510,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
@@ -483,7 +519,7 @@ jobs:
|
||||
architecture: x86-64
|
||||
cpu_count: 4
|
||||
shell: bash
|
||||
version: '14.3'
|
||||
version: '15.0'
|
||||
run: |
|
||||
uname -a
|
||||
sudo pkg update
|
||||
@@ -498,7 +534,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-freebsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -511,7 +547,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
@@ -535,7 +571,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-openbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -548,7 +584,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
@@ -571,7 +607,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-netbsd-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -584,7 +620,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: vmactions/dragonflybsd-vm@v1
|
||||
@@ -606,7 +642,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-dragonfly-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -619,7 +655,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: run VM
|
||||
uses: cross-platform-actions/action@master
|
||||
@@ -641,7 +677,7 @@ jobs:
|
||||
ctest --output-on-failure
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-haiku-amd64
|
||||
path: ./fastfetch-*.*
|
||||
@@ -681,7 +717,7 @@ jobs:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: setup-msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
@@ -723,7 +759,7 @@ jobs:
|
||||
- if: github.event_name == 'push' && github.repository == 'fastfetch-cli/fastfetch'
|
||||
id: upload-unsigned-artifact
|
||||
name: upload artifacts for signing
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
|
||||
path: |
|
||||
@@ -750,7 +786,7 @@ jobs:
|
||||
run: 7z a -t7z -mx9 -bd -y fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.7z LICENSE *.dll fastfetch.exe flashfetch.exe presets
|
||||
|
||||
- name: upload true artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}
|
||||
path: ./fastfetch-windows-${{ matrix.arch }}${{ matrix.win7-compat-postfix }}.*
|
||||
@@ -772,7 +808,8 @@ jobs:
|
||||
- openbsd-amd64
|
||||
- netbsd-amd64
|
||||
- dragonfly-amd64
|
||||
- sunos-amd64
|
||||
- solaris-amd64
|
||||
- omnios-amd64
|
||||
- haiku-amd64
|
||||
- windows-hosts
|
||||
permissions:
|
||||
|
||||
+60
-2
@@ -1,3 +1,61 @@
|
||||
# 2.59.0
|
||||
|
||||
Changes:
|
||||
* Fastfetch no longer relies on the unreliable environment variables `$USER` or `%USERPROFILE%` to determine the current username (Title)
|
||||
* People who set `$USER` to customize the Fastfetch title should use `{ "type": "title", "format": "your-custom-user-name" }` to achieve the same result.
|
||||
* Fastfetch no longer tries to probe inaccessible remote disk drives on Windows (Disk, Windows)
|
||||
* People who have remote drives may use `{ "type": "disk", "hideFolders": "X:\\" }` to ignore problematic ones.
|
||||
* This change removes some ugly hacks from the codebase and matches the behavior on `*nix`.
|
||||
|
||||
Features:
|
||||
* Adds Oracle Solaris support (#2176, SunOS)
|
||||
* Adds UID / SID detection (Title)
|
||||
* In custom format: `{user-id}`
|
||||
* Switches to native GPU detection on GNU/Hurd and removes the `libpciaccess` dependency (GPU, Hurd)
|
||||
* Improves memory size detection on macOS (Memory, macOS)
|
||||
* Avoids relying on `hw.memsize_usable` by default, which may not be available on older macOS versions
|
||||
* Improves Windows disk detection accuracy and performance (Disk, Windows)
|
||||
* Adds more ARM CPU parts and removes duplicated cases (CPU, ARM)
|
||||
|
||||
Logos:
|
||||
* Adds 6-color support to the NixOS logo (including the small variant) (#2180)
|
||||
|
||||
# 2.58.0
|
||||
|
||||
An early release to fix compatibility issues with KDE Plasma 6.6.
|
||||
|
||||
Breaking changes:
|
||||
* The `de.slowVersionDetection` option has been removed. Slow version detection is now always enabled, as required on non-FHS-compliant distros (e.g., NixOS). (#2149, DE, Linux)
|
||||
|
||||
Features:
|
||||
* Adds the `--structure-disabled <modules...>` command-line flag to temporarily disable module structure printing.
|
||||
* For example: `fastfetch --structure-disabled colors` removes the color blocks from the default output.
|
||||
* Supports chassis type detection on Linux ARM devices when reported via the device tree (Chassis, Linux)
|
||||
* Supports Bedrock Linux version detection (#2155, OS, Linux)
|
||||
* Honors the `DBPath` and `RootDir` settings in `pacman.conf` when detecting Pacman packages (#2154, Packages, Linux)
|
||||
|
||||
Bugfixes:
|
||||
* Fixes a crash issues on KDE Plasma 6.6 (Display, Linux)
|
||||
* Fixes the Command module not working with `--dynamic-interval` (#2152, Command)
|
||||
* Fixes Quartz Compositor version detection. It now correctly reports the version of `WindowServer` (`SkyLight`) instead of `WindowManager`. (WM, macOS)
|
||||
|
||||
Logos:
|
||||
* Adds Kiss2
|
||||
|
||||
# 2.57.1
|
||||
|
||||
Features:
|
||||
* Tiny performance improvements (Windows)
|
||||
* Improves the reliability of hostname retrieval (Title, Windows)
|
||||
|
||||
Bugfixes:
|
||||
* Fixes potential compilation issues on Linux (#2142, Linux)
|
||||
* Fixes compilation errors on macOS when building with older SDKs (#2140, macOS)
|
||||
* Fixes compilation issues when building with `-DENABLE_SYSTEM_YYJSON=ON` (#2143)
|
||||
|
||||
Logos:
|
||||
* Updates PrismLinux and adds a small variant
|
||||
|
||||
# 2.57.0
|
||||
|
||||
Deprecation notice:
|
||||
@@ -15,8 +73,8 @@ Features:
|
||||
* Supports Secure Boot detection (Bootmgr, macOS)
|
||||
* Supports DPI scale factor detection on Windows 7 (Display, Windows)
|
||||
* Supports xterm 256-color codes in color configuration
|
||||
* In `display.color`: "@<color-index>" (e.g., "@34" for color index 34)
|
||||
* In `*.format` strings: "#@<color-index>" (e.g., "#@34" for color index 34)
|
||||
* In `display.color`: "`@<color-index>`" (e.g., "`@34`" for color index `34`)
|
||||
* In `*.format` strings: "`#@<color-index>`" (e.g., "`#@34`" for color index `34`)
|
||||
* Improves uptime accuracy on Windows 10+ (Uptime, Windows)
|
||||
* Adds a new module `Logo` to query built-in logo raw data in JSON output (Logo)
|
||||
* Usage: `fastfetch -s logo -l <logo-name> -j # Supported in JSON format only`
|
||||
|
||||
+30
-8
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url
|
||||
|
||||
project(fastfetch
|
||||
VERSION 2.57.0
|
||||
VERSION 2.59.0
|
||||
LANGUAGES C
|
||||
DESCRIPTION "Fast neofetch-like system information tool"
|
||||
HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch"
|
||||
@@ -92,7 +92,6 @@ 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_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)
|
||||
@@ -110,6 +109,9 @@ option(ENABLE_LIBZFS "Enable libzfs" ON)
|
||||
if(WIN32 AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
option(ENABLE_WIN7_COMPAT "Enable Windows 7 compatibility" ON)
|
||||
endif()
|
||||
if(APPLE)
|
||||
option(ENABLE_APPLE_MEMSIZE_USABLE "Use usable memory size as total memory size in Memory module, to match other systems" OFF)
|
||||
endif()
|
||||
|
||||
set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static)
|
||||
set(BINARY_LINK_TYPE dlopen CACHE STRING "How to link fastfetch")
|
||||
@@ -1259,7 +1261,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_general.c
|
||||
src/detection/gpu/gpu_gnu.c
|
||||
src/detection/gpu/gpu_pci.c
|
||||
src/detection/gtk_qt/gtk.c
|
||||
src/detection/host/host_nosupport.c
|
||||
@@ -1323,6 +1325,10 @@ endif()
|
||||
if(NOT WIN32)
|
||||
check_function_exists(pipe2 HAVE_PIPE2)
|
||||
endif()
|
||||
check_function_exists(memrchr HAVE_MEMRCHR)
|
||||
if(NOT HAVE_MEMRCHR)
|
||||
list(APPEND LIBFASTFETCH_SRC src/common/impl/memrchr.c)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SYSTEM_YYJSON)
|
||||
find_package(yyjson)
|
||||
@@ -1341,6 +1347,15 @@ add_library(libfastfetch OBJECT
|
||||
${LIBFASTFETCH_SRC}
|
||||
)
|
||||
|
||||
include(CheckCSourceCompiles)
|
||||
check_c_source_compiles("int main(void){int arr[1];return _Countof(arr);}" COMPILER_SUPPORTS_COUNT_OF)
|
||||
if(COMPILER_SUPPORTS_COUNT_OF)
|
||||
message(STATUS "_Countof is supported by the compiler")
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_SUPPORTS_COUNT_OF=1)
|
||||
else()
|
||||
message(STATUS "_Countof is NOT supported by the compiler")
|
||||
endif()
|
||||
|
||||
if(yyjson_FOUND)
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_USE_SYSTEM_YYJSON=1)
|
||||
target_link_libraries(libfastfetch PUBLIC yyjson::yyjson)
|
||||
@@ -1424,6 +1439,14 @@ elseif(GNU)
|
||||
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE PATH_MAX=4096 O_PATH=0)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(ENABLE_APPLE_MEMSIZE_USABLE)
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_APPLE_MEMSIZE_USABLE=1)
|
||||
else()
|
||||
target_compile_definitions(libfastfetch PUBLIC FF_APPLE_MEMSIZE_USABLE=0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
check_function_exists(_msize HAVE_MSVC_MSIZE)
|
||||
if(HAVE_MSVC_MSIZE)
|
||||
@@ -1630,10 +1653,6 @@ ff_lib_enable(DIRECTX_HEADERS
|
||||
"DirectX-Headers"
|
||||
"DirectX-Headers"
|
||||
)
|
||||
ff_lib_enable(PCIACCESS
|
||||
"pciaccess"
|
||||
"pciaccess"
|
||||
)
|
||||
|
||||
if(ENABLE_THREADS)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS=1)
|
||||
@@ -1657,6 +1676,9 @@ if(ENABLE_LIBZFS)
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT HAVE_MEMRCHR)
|
||||
target_compile_definitions(libfastfetch PRIVATE FF_HAVE_CUSTOM_MEMRCHR=1)
|
||||
endif()
|
||||
|
||||
if(LINUX)
|
||||
target_link_libraries(libfastfetch
|
||||
@@ -2089,7 +2111,7 @@ if(LINUX)
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||
endif()
|
||||
endif()
|
||||
elseif(FreeBSD)
|
||||
elseif(FreeBSD AND NOT DragonFly)
|
||||
set(CPACK_FREEBSD_PACKAGE_LICENSE "MIT")
|
||||
set(CPACK_GENERATOR "${CPACK_GENERATOR};FREEBSD")
|
||||
endif()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-2023 Linus Dierheimer
|
||||
Copyright (c) 2022-2025 Carter Li
|
||||
Copyright (c) 2022-2026 Carter Li
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
[](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 illumos (SunOS).
|
||||
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, illumos (SunOS), and Solaris.
|
||||
|
||||
<img src="screenshots/example1.png" width="49%" align="left" />
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/2/24/Transparent_Square_Tiles_Texture.png" width="49%" height="16px" align="left" />
|
||||
|
||||
Vendored
+18
@@ -1,3 +1,21 @@
|
||||
fastfetch (2.58.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.58.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Thu, 22 Jan 2026 15:30:58 +0800
|
||||
|
||||
fastfetch (2.57.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.57.1
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Wed, 14 Jan 2026 14:00:17 +0800
|
||||
|
||||
fastfetch (2.57.0~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.57.0
|
||||
|
||||
-- Carter Li <zhangsongcui@live.cn> Mon, 12 Jan 2026 10:11:21 +0800
|
||||
|
||||
fastfetch (2.56.1~#UBUNTU_CODENAME#) #UBUNTU_CODENAME#; urgency=medium
|
||||
|
||||
* Update to 2.56.1
|
||||
|
||||
@@ -318,7 +318,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"cpuFormat": {
|
||||
"description": "Output format of the module `CPU`. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: X86-64 CPU microarchitecture",
|
||||
"description": "Output format of the module `CPU`. See Wiki for formatting syntax\n 1. {name}: Name\n 2. {vendor}: Vendor\n 3. {cores-physical}: Physical core count\n 4. {cores-logical}: Logical core count\n 5. {cores-online}: Online core count\n 6. {freq-base}: Base frequency (formatted)\n 7. {freq-max}: Max frequency (formatted)\n 8. {temperature}: Temperature (formatted)\n 9. {core-types}: Logical core count grouped by frequency\n 10. {packages}: Processor package count\n 11. {march}: CPU microarchitecture\n 12. {numa-nodes}: NUMA node count",
|
||||
"type": "string"
|
||||
},
|
||||
"cpucacheFormat": {
|
||||
@@ -442,7 +442,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"packagesFormat": {
|
||||
"description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of emerge packages\n 7. {eopkg}: Number of eopkg packages\n 8. {xbps}: Number of xbps packages\n 9. {nix-system}: Number of nix-system packages\n 10. {nix-user}: Number of nix-user packages\n 11. {nix-default}: Number of nix-default packages\n 12. {apk}: Number of apk packages\n 13. {pkg}: Number of pkg packages\n 14. {flatpak-system}: Number of flatpak-system app packages\n 15. {flatpak-user}: Number of flatpak-user app packages\n 16. {snap}: Number of snap packages\n 17. {brew}: Number of brew packages\n 18. {brew-cask}: Number of brew-cask packages\n 19. {macports}: Number of macports packages\n 20. {scoop-user}: Number of scoop-user packages\n 21. {scoop-global}: Number of scoop-global packages\n 22. {choco}: Number of choco packages\n 23. {pkgtool}: Number of pkgtool packages\n 24. {paludis}: Number of paludis packages\n 25. {winget}: Number of winget packages\n 26. {opkg}: Number of opkg packages\n 27. {am-system}: Number of am-system packages\n 28. {sorcery}: Number of sorcery packages\n 29. {lpkg}: Number of lpkg packages\n 30. {lpkgbuild}: Number of lpkgbuild packages\n 31. {guix-system}: Number of guix-system packages\n 32. {guix-user}: Number of guix-user packages\n 33. {guix-home}: Number of guix-home packages\n 34. {linglong}: Number of linglong packages\n 35. {pacstall}: Number of pacstall packages\n 36. {mport}: Number of mport packages\n 37. {am-user}: Number of am-user (aka appman) packages\n 38. {pkgsrc}: Number of pkgsrc packages\n 39. {hpkg-system}: Number of hpkg-system packages\n 40. {hpkg-user}: Number of hpkg-user packages\n 41. {pisi}: Number of pisi packages\n 42. {soar}: Number of soar packages\n 43. {nix-all}: Total number of all nix packages\n 44. {flatpak-all}: Total number of all flatpak app packages\n 45. {brew-all}: Total number of all brew packages\n 46. {guix-all}: Total number of all guix packages\n 47. {hpkg-all}: Total number of all hpkg packages",
|
||||
"description": "Output format of the module `Packages`. See Wiki for formatting syntax\n 1. {all}: Number of all packages\n 2. {pacman}: Number of pacman packages\n 3. {pacman-branch}: Pacman branch on manjaro\n 4. {dpkg}: Number of dpkg packages\n 5. {rpm}: Number of rpm packages\n 6. {emerge}: Number of emerge packages\n 7. {eopkg}: Number of eopkg packages\n 8. {xbps}: Number of xbps packages\n 9. {nix-system}: Number of nix-system packages\n 10. {nix-user}: Number of nix-user packages\n 11. {nix-default}: Number of nix-default packages\n 12. {apk}: Number of apk packages\n 13. {pkg}: Number of pkg packages\n 14. {flatpak-system}: Number of flatpak-system app packages\n 15. {flatpak-user}: Number of flatpak-user app packages\n 16. {snap}: Number of snap packages\n 17. {brew}: Number of brew packages\n 18. {brew-cask}: Number of brew-cask packages\n 19. {macports}: Number of macports packages\n 20. {scoop-user}: Number of scoop-user packages\n 21. {scoop-global}: Number of scoop-global packages\n 22. {choco}: Number of choco packages\n 23. {pkgtool}: Number of pkgtool packages\n 24. {paludis}: Number of paludis packages\n 25. {winget}: Number of winget packages\n 26. {opkg}: Number of opkg packages\n 27. {am-system}: Number of am-system packages\n 28. {sorcery}: Number of sorcery packages\n 29. {lpkg}: Number of lpkg packages\n 30. {lpkgbuild}: Number of lpkgbuild packages\n 31. {guix-system}: Number of guix-system packages\n 32. {guix-user}: Number of guix-user packages\n 33. {guix-home}: Number of guix-home packages\n 34. {linglong}: Number of linglong packages\n 35. {pacstall}: Number of pacstall packages\n 36. {mport}: Number of mport packages\n 37. {am-user}: Number of am-user (aka appman) packages\n 38. {pkgsrc}: Number of pkgsrc packages\n 39. {hpkg-system}: Number of hpkg-system packages\n 40. {hpkg-user}: Number of hpkg-user packages\n 41. {pisi}: Number of pisi packages\n 42. {soar}: Number of soar packages\n 43. {kiss}: Number of kiss packages\n 44. {nix-all}: Total number of all nix packages\n 45. {flatpak-all}: Total number of all flatpak app packages\n 46. {brew-all}: Total number of all brew packages\n 47. {guix-all}: Total number of all guix packages\n 48. {hpkg-all}: Total number of all hpkg packages",
|
||||
"type": "string"
|
||||
},
|
||||
"physicaldiskFormat": {
|
||||
@@ -498,7 +498,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"titleFormat": {
|
||||
"description": "Output format of the module `Title`. See Wiki for formatting syntax\n 1. {user-name}: User name\n 2. {host-name}: Host name\n 3. {home-dir}: Home directory\n 4. {exe-path}: Executable path of current process\n 5. {user-shell}: User's default shell\n 6. {user-name-colored}: User name (colored)\n 7. {at-symbol-colored}: @ symbol (colored)\n 8. {host-name-colored}: Host name (colored)\n 9. {full-user-name}: Full user name",
|
||||
"description": "Output format of the module `Title`. See Wiki for formatting syntax\n 1. {user-name}: User name\n 2. {host-name}: Host name\n 3. {home-dir}: Home directory\n 4. {exe-path}: Executable path of current process\n 5. {user-shell}: User's default shell\n 6. {user-name-colored}: User name (colored)\n 7. {at-symbol-colored}: @ symbol (colored)\n 8. {host-name-colored}: Host name (colored)\n 9. {full-user-name}: Full user name\n 10. {user-id}: UID (*nix) / SID (Windows)\n 11. {pid}: PID of current process",
|
||||
"type": "string"
|
||||
},
|
||||
"themeFormat": {
|
||||
@@ -546,7 +546,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"zpoolFormat": {
|
||||
"description": "Output format of the module `Zpool`. See Wiki for formatting syntax\n 1. {name}: Zpool name\n 2. {state}: Zpool state\n 3. {used}: Size used\n 4. {total}: Size total\n 5. {used-percentage}: Size percentage num\n 6. {fragmentation-percentage}: Fragmentation percentage num\n 7. {used-percentage-bar}: Size percentage bar\n 8. {fragmentation-percentage-bar}: Fragmentation percentage bar",
|
||||
"description": "Output format of the module `Zpool`. See Wiki for formatting syntax\n 1. {name}: Zpool name\n 2. {guid}: Zpool guid\n 3. {state}: Zpool state\n 4. {used}: Size used\n 5. {allocated}: Size allocated\n 6. {total}: Size total\n 7. {used-percentage}: Size used percentage num\n 8. {allocated-percentage}: Size allocated percentage num\n 9. {fragmentation-percentage}: Fragmentation percentage num\n 10. {used-percentage-bar}: Size used percentage bar\n 11. {allocated-percentage-bar}: Size allocated percentage bar\n 12. {fragmentation-percentage-bar}: Fragmentation percentage bar\n 13. {is-readonly}: Is read-only",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
@@ -1999,7 +1999,7 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": {
|
||||
"description": "Print some colored blocks",
|
||||
"description": "Display the terminal's 16-color palette",
|
||||
"const": "colors"
|
||||
},
|
||||
"symbol": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Load with --config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Load with --config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/3.jsonc
|
||||
// Load with --config examples/3.jsonc
|
||||
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/4.jsonc
|
||||
// Load with --config examples/4.jsonc
|
||||
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/5.jsonc
|
||||
// Load with --config examples/5.jsonc
|
||||
|
||||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Load with --config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Load with --load-config examples/2.jsonc
|
||||
// Load with --config examples/2.jsonc
|
||||
// Note that you must replace the image path to an existing image to display it.
|
||||
|
||||
{
|
||||
|
||||
@@ -18,8 +18,14 @@ typedef struct FFPlatform
|
||||
FFstrbuf cacheDir; // Trailing slash included
|
||||
FFlist configDirs; // List of FFstrbuf, trailing slash included
|
||||
FFlist dataDirs; // List of FFstrbuf, trailing slash included
|
||||
FFstrbuf exePath; // The real path of current exe
|
||||
FFstrbuf exePath; // The real path of current exe (empty if unavailable)
|
||||
|
||||
uint32_t pid;
|
||||
#ifndef _WIN32
|
||||
uint32_t uid;
|
||||
#else
|
||||
FFstrbuf sid;
|
||||
#endif
|
||||
FFstrbuf userName;
|
||||
FFstrbuf fullUserName;
|
||||
FFstrbuf hostName;
|
||||
|
||||
+12
-4
@@ -103,6 +103,10 @@ FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultV
|
||||
void ffStrbufUpperCase(FFstrbuf* strbuf);
|
||||
void ffStrbufLowerCase(FFstrbuf* strbuf);
|
||||
|
||||
// Function alters the buffer to extract lines or delimited segments (replaces the delimiter with '\0')
|
||||
// so that buffer MUST be heap allocated (NOT a static string)
|
||||
// `lineptr` must be `NULL` and `n` MUST be `0` for the first call
|
||||
// Caller MUST NOT free `*lineptr`
|
||||
bool ffStrbufGetdelim(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
|
||||
void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf* buffer);
|
||||
|
||||
@@ -111,10 +115,14 @@ void ffStrbufGetdelimRestore(char** lineptr, size_t* n, char delimiter, FFstrbuf
|
||||
*
|
||||
* @details Behaves like getline(3) but reads from a FFstrbuf.
|
||||
*
|
||||
* @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line.
|
||||
* Can be NULL for the first call.
|
||||
* @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line
|
||||
(points to buffer's internal memory address to avoid memory allocation and copy).
|
||||
MUST NOT be freed by the caller, unlike `getline(3)`.
|
||||
* MUST be NULL for the first call.
|
||||
* @param[in,out] n The pointer to the size of the buffer of lineptr.
|
||||
* @param[in] buffer The buffer to read from. The buffer must not be a string literal.
|
||||
MUST be 0 for the first call.
|
||||
* @param[in] buffer The buffer to read from.
|
||||
MUST be heap allocated (NOT a static string).
|
||||
*
|
||||
* @return true if a line has been read, false if the end of the buffer has been reached.
|
||||
*/
|
||||
@@ -124,7 +132,7 @@ static inline bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer)
|
||||
}
|
||||
/**
|
||||
* @brief Restore the end of a line that was modified by ffStrbufGetline.
|
||||
* @warning This function should be called before breaking an ffStrbufGetline loop.
|
||||
* @warning This function should be called before breaking an ffStrbufGetline loop if `buffer` will be used later.
|
||||
*/
|
||||
static inline void ffStrbufGetlineRestore(char** lineptr, size_t* n, FFstrbuf* buffer)
|
||||
{
|
||||
|
||||
@@ -166,7 +166,7 @@ static const char *smcReadValue(io_connect_t conn, const UInt32Char_t key, doubl
|
||||
case 1: *value = *(uint8_t *)(val.bytes); break;
|
||||
case 2: *value = ntohs(*(uint16_t *)(val.bytes)); break;
|
||||
case 4: *value = ntohl(*(uint32_t *)(val.bytes)); break;
|
||||
case 8: *value = ntohll(*(uint64_t *)(val.bytes)); break;
|
||||
case 8: *value = (double) ntohll(*(uint64_t *)(val.bytes)); break;
|
||||
default:
|
||||
return "Unsupported SMC unsigned integer data size";
|
||||
}
|
||||
@@ -213,7 +213,7 @@ static const char *smcReadValue(io_connect_t conn, const UInt32Char_t key, doubl
|
||||
case 1: *value = *(int8_t *)(val.bytes); break;
|
||||
case 2: *value = ntohs(*(int16_t *)(val.bytes)); break;
|
||||
case 4: *value = ntohl(*(int32_t *)(val.bytes)); break;
|
||||
case 8: *value = ntohll(*(int64_t *)(val.bytes)); break;
|
||||
case 8: *value = (double)ntohll(*(int64_t *)(val.bytes)); break;
|
||||
default: return "Unsupported SMC signed integer data size";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef __has_builtin
|
||||
#if __has_builtin(__is_array)
|
||||
#if !__cplusplus && FF_SUPPORTS_COUNT_OF
|
||||
#define ARRAY_SIZE(x) _Countof(x)
|
||||
#elif __has_builtin(__is_array)
|
||||
#define ARRAY_SIZE(x) ({ static_assert(__is_array(__typeof__(x)), "Must be an array"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
|
||||
#elif __has_builtin(__builtin_types_compatible_p)
|
||||
#define ARRAY_SIZE(x) ({ static_assert(!__builtin_types_compatible_p(__typeof__(x), __typeof__(&*(x))), "Must not be a pointer"); (uint32_t) (sizeof(x) / sizeof(*(x))); })
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
|
||||
// Things only needed by fastfetch
|
||||
typedef struct FFdata
|
||||
{
|
||||
FFstrbuf structure;
|
||||
bool configLoaded;
|
||||
} FFdata;
|
||||
#include "common/ffdata.h"
|
||||
|
||||
void ffPrepareCommandOption(FFdata* data);
|
||||
void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc);
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data, yyjson_mut_doc* jsonDoc);
|
||||
void ffPrintCommandOption(FFdata* data);
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data);
|
||||
bool ffParseModuleOptions(const char* key, const char* value);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/FFstrbuf.h"
|
||||
|
||||
typedef enum __attribute__((__packed__)) FFDataResultDocType
|
||||
{
|
||||
FF_RESULT_DOC_TYPE_DEFAULT = 0,
|
||||
FF_RESULT_DOC_TYPE_JSON,
|
||||
FF_RESULT_DOC_TYPE_CONFIG,
|
||||
FF_RESULT_DOC_TYPE_CONFIG_FULL,
|
||||
} FFDataResultDocType;
|
||||
|
||||
// FFdata aggregates configuration, generation parameters, and output state used by fastfetch.
|
||||
// It holds the parsed configuration document, a mutable JSON document for results, and related metadata.
|
||||
typedef struct FFdata
|
||||
{
|
||||
yyjson_doc* configDoc; // Parsed JSON configuration document
|
||||
yyjson_mut_doc* resultDoc; // Mutable JSON document for storing results
|
||||
FFstrbuf structure; // Custom output structure from command line
|
||||
FFstrbuf structureDisabled; // Disabled modules in the output structure from command line
|
||||
FFstrbuf genConfigPath; // Path to generate configuration file
|
||||
FFDataResultDocType docType; // Type of result document
|
||||
bool configLoaded;
|
||||
} FFdata;
|
||||
@@ -16,6 +16,10 @@ void ffPlatformInit(FFPlatform* platform)
|
||||
ffStrbufInit(&platform->hostName);
|
||||
ffStrbufInit(&platform->userShell);
|
||||
|
||||
#ifdef _WIN32
|
||||
ffStrbufInit(&platform->sid);
|
||||
#endif
|
||||
|
||||
FFPlatformSysinfo* info = &platform->sysinfo;
|
||||
|
||||
ffStrbufInit(&info->name);
|
||||
@@ -51,6 +55,10 @@ void ffPlatformDestroy(FFPlatform* platform)
|
||||
ffStrbufDestroy(&platform->userShell);
|
||||
ffStrbufDestroy(&platform->fullUserName);
|
||||
|
||||
#ifdef _WIN32
|
||||
ffStrbufDestroy(&platform->sid);
|
||||
#endif
|
||||
|
||||
FFPlatformSysinfo* info = &platform->sysinfo;
|
||||
ffStrbufDestroy(&info->architecture);
|
||||
ffStrbufDestroy(&info->name);
|
||||
|
||||
@@ -28,15 +28,15 @@ static void getExePath(FFPlatform* platform)
|
||||
if (exePathLen >= 0)
|
||||
exePath[exePathLen] = '\0';
|
||||
#elif defined(__APPLE__)
|
||||
int exePathLen = proc_pidpath((int) getpid(), exePath, sizeof(exePath));
|
||||
int exePathLen = proc_pidpath((pid_t) platform->pid, exePath, sizeof(exePath));
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
size_t exePathLen = sizeof(exePath);
|
||||
if(sysctl(
|
||||
(int[]){CTL_KERN,
|
||||
#ifdef __FreeBSD__
|
||||
KERN_PROC, KERN_PROC_PATHNAME, (int) getpid()
|
||||
KERN_PROC, KERN_PROC_PATHNAME, (pid_t) platform->pid
|
||||
#else
|
||||
KERN_PROC_ARGS, (int) getpid(), KERN_PROC_PATHNAME
|
||||
KERN_PROC_ARGS, platform->pid, KERN_PROC_PATHNAME
|
||||
#endif
|
||||
}, 4,
|
||||
exePath, &exePathLen,
|
||||
@@ -177,17 +177,16 @@ static void getDataDirs(FFPlatform* platform)
|
||||
|
||||
static void getUserName(FFPlatform* platform, const struct passwd* pwd)
|
||||
{
|
||||
const char* user = getenv("USER");
|
||||
if(!ffStrSet(user) && pwd)
|
||||
user = pwd->pw_name;
|
||||
|
||||
ffStrbufAppendS(&platform->userName, user);
|
||||
|
||||
if (pwd)
|
||||
{
|
||||
ffStrbufAppendS(&platform->fullUserName, pwd->pw_gecos);
|
||||
ffStrbufSetS(&platform->userName, pwd->pw_name);
|
||||
ffStrbufSetS(&platform->fullUserName, pwd->pw_gecos);
|
||||
ffStrbufTrimSpace(&platform->fullUserName);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufSetS(&platform->userName, getenv("USER"));
|
||||
}
|
||||
}
|
||||
|
||||
static void getHostName(FFPlatform* platform, const struct utsname* uts)
|
||||
@@ -227,7 +226,9 @@ static void getSysinfo(FFPlatformSysinfo* info, const struct utsname* uts)
|
||||
|
||||
void ffPlatformInitImpl(FFPlatform* platform)
|
||||
{
|
||||
struct passwd* pwd = getpwuid(getuid());
|
||||
platform->pid = (uint32_t) getpid();
|
||||
platform->uid = getuid();
|
||||
struct passwd* pwd = getpwuid(platform->uid);
|
||||
|
||||
struct utsname uts;
|
||||
if(uname(&uts) < 0)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "FFPlatform_private.h"
|
||||
#include "common/io.h"
|
||||
#include "common/library.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/windows/registry.h"
|
||||
@@ -8,6 +9,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <sddl.h>
|
||||
|
||||
#define SECURITY_WIN32 1 // For secext.h
|
||||
#include <secext.h>
|
||||
@@ -70,15 +72,15 @@ static void getCacheDir(FFPlatform* platform)
|
||||
static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId)
|
||||
{
|
||||
PWSTR pPath = NULL;
|
||||
if(SUCCEEDED(SHGetKnownFolderPath(folderId, 0, NULL, &pPath)))
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT, NULL, &pPath)))
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateWS(pPath);
|
||||
CoTaskMemFree(pPath);
|
||||
ffStrbufReplaceAllC(&buffer, '\\', '/');
|
||||
ffStrbufEnsureEndsWithC(&buffer, '/');
|
||||
if (!ffListContains(dirs, &buffer, (void*) ffStrbufEqual))
|
||||
ffStrbufInitMove((FFstrbuf*) ffListAdd(dirs), &buffer);
|
||||
}
|
||||
CoTaskMemFree(pPath);
|
||||
}
|
||||
|
||||
static void platformPathAddEnvSuffix(FFlist* dirs, const char* env, const char* suffix)
|
||||
@@ -136,29 +138,51 @@ static void getDataDirs(FFPlatform* platform)
|
||||
|
||||
static void getUserName(FFPlatform* platform)
|
||||
{
|
||||
const char* userName = getenv("USERNAME");
|
||||
if (ffStrSet(userName))
|
||||
ffStrbufSetS(&platform->userName, userName);
|
||||
else
|
||||
{
|
||||
wchar_t buffer[256];
|
||||
DWORD len = ARRAY_SIZE(buffer);
|
||||
if(GetUserNameW(buffer, &len))
|
||||
ffStrbufSetWS(&platform->userName, buffer);
|
||||
}
|
||||
|
||||
wchar_t buffer[256];
|
||||
DWORD len = ARRAY_SIZE(buffer);
|
||||
if (GetUserNameExW(NameDisplay, buffer, &len))
|
||||
DWORD size = ARRAY_SIZE(buffer);
|
||||
if (GetUserNameExW(NameDisplay, buffer, &size))
|
||||
ffStrbufSetWS(&platform->fullUserName, buffer);
|
||||
|
||||
size = ARRAY_SIZE(buffer);
|
||||
if (GetUserNameW(buffer, &size)) // GetUserNameExW(10002)?
|
||||
{
|
||||
ffStrbufSetWS(&platform->userName, buffer);
|
||||
|
||||
size = 0;
|
||||
DWORD refDomainSize = 0;
|
||||
SID_NAME_USE sidNameUse = SidTypeUnknown;
|
||||
LookupAccountNameW(NULL, buffer, NULL, &size, NULL, &refDomainSize, &sidNameUse);
|
||||
if (size > 0)
|
||||
{
|
||||
FF_AUTO_FREE PSID sid = (PSID) malloc(size);
|
||||
FF_AUTO_FREE LPWSTR refDomain = (LPWSTR) malloc(refDomainSize * sizeof(wchar_t));
|
||||
if (LookupAccountNameW(NULL, buffer, sid, &size, refDomain, &refDomainSize, &sidNameUse))
|
||||
{
|
||||
LPWSTR sidString;
|
||||
if (ConvertSidToStringSidW(sid, &sidString))
|
||||
{
|
||||
ffStrbufSetWS(&platform->sid, sidString);
|
||||
LocalFree(sidString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ffStrbufSetS(&platform->userName, getenv("USERNAME"));
|
||||
}
|
||||
|
||||
static void getHostName(FFPlatform* platform)
|
||||
{
|
||||
wchar_t buffer[128];
|
||||
wchar_t buffer[256];
|
||||
DWORD len = ARRAY_SIZE(buffer);
|
||||
if(GetComputerNameExW(ComputerNameDnsHostname, buffer, &len))
|
||||
ffStrbufSetWS(&platform->hostName, buffer);
|
||||
if (GetComputerNameExW(ComputerNameDnsHostname, buffer, &len) && len > 0)
|
||||
ffStrbufSetNWS(&platform->hostName, len, buffer);
|
||||
else
|
||||
{
|
||||
len = ARRAY_SIZE(buffer);
|
||||
if (GetComputerNameExW(ComputerNameNetBIOS, buffer, &len) && len > 0)
|
||||
ffStrbufSetNWS(&platform->hostName, len, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void getUserShell(FFPlatform* platform)
|
||||
@@ -283,6 +307,7 @@ static void getSystemArchitectureAndPageSize(FFPlatformSysinfo* info)
|
||||
|
||||
void ffPlatformInitImpl(FFPlatform* platform)
|
||||
{
|
||||
platform->pid = (uint32_t) GetCurrentProcessId();
|
||||
getExePath(platform);
|
||||
getHomeDir(platform);
|
||||
getCacheDir(platform);
|
||||
|
||||
@@ -183,7 +183,7 @@ const char* ffStrbufAppendSUntilC(FFstrbuf* strbuf, const char* value, char unti
|
||||
if(value == NULL)
|
||||
return NULL;
|
||||
|
||||
char* end = strchr(value, until);
|
||||
const char* end = strchr(value, until);
|
||||
if(end == NULL)
|
||||
ffStrbufAppendS(strbuf, value);
|
||||
else
|
||||
|
||||
@@ -61,7 +61,7 @@ bool ffParseModuleOptions(const char* key, const char* value)
|
||||
}
|
||||
fprintf(stderr, "Error: Unsupported module option: %s\n", key);
|
||||
fputs(" Support of module options has been removed. Please add the flag to the JSON config instead.\n", stderr);
|
||||
fprintf(stderr, " Example (demonstration only): `{ \"modules\": [ { \"type\": \"%s\", \"%s\": %s%s%s } ] }`\n", moduleName.chars, jsonKey.chars, value ? "\"" : "", value ? value : "true", value ? "\"" : "");
|
||||
fprintf(stderr, " Example (demonstration only): `{ \"modules\": [ { \"type\": \"%s\", \"%s\": %s%s%s } ] }`\n", moduleName.chars, jsonKey.chars, value ? "\"" : "", value ?: "true", value ? "\"" : "");
|
||||
fputs(" See <https://github.com/fastfetch-cli/fastfetch/wiki/Configuration> for more information.\n", stderr);
|
||||
exit(477);
|
||||
}
|
||||
@@ -71,40 +71,66 @@ bool ffParseModuleOptions(const char* key, const char* value)
|
||||
|
||||
void ffPrepareCommandOption(FFdata* data)
|
||||
{
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_CPUUSAGE_MODULE_NAME, ':'))
|
||||
ffPrepareCPUUsage();
|
||||
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_DISKIO_MODULE_NAME, ':'))
|
||||
char* moduleType = NULL;
|
||||
size_t moduleLen = 0;
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
|
||||
ffInitDiskIOOptions(&options);
|
||||
ffPrepareDiskIO(&options);
|
||||
}
|
||||
#define FF_IF_MODULE_MATCH(moduleNameConstant) if (moduleLen == strlen(moduleNameConstant) \
|
||||
&& ffStrEqualsIgnCase(moduleType, moduleNameConstant) \
|
||||
&& !ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleNameConstant, ':'))
|
||||
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_NETIO_MODULE_NAME, ':'))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
|
||||
ffInitNetIOOptions(&options);
|
||||
ffPrepareNetIO(&options);
|
||||
}
|
||||
switch (moduleType[0])
|
||||
{
|
||||
case 'C': case 'c':
|
||||
FF_IF_MODULE_MATCH(FF_CPUUSAGE_MODULE_NAME)
|
||||
ffPrepareCPUUsage();
|
||||
break;
|
||||
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_PUBLICIP_MODULE_NAME, ':'))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
|
||||
ffInitPublicIpOptions(&options);
|
||||
ffPreparePublicIp(&options);
|
||||
}
|
||||
case 'D': case 'd':
|
||||
FF_IF_MODULE_MATCH(FF_DISKIO_MODULE_NAME)
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyDiskIOOptions))) FFDiskIOOptions options;
|
||||
ffInitDiskIOOptions(&options);
|
||||
ffPrepareDiskIO(&options);
|
||||
}
|
||||
break;
|
||||
|
||||
if(ffStrbufSeparatedContainIgnCaseS(&data->structure, FF_WEATHER_MODULE_NAME, ':'))
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
|
||||
ffInitWeatherOptions(&options);
|
||||
ffPrepareWeather(&options);
|
||||
case 'N': case 'n':
|
||||
FF_IF_MODULE_MATCH(FF_NETIO_MODULE_NAME)
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyNetIOOptions))) FFNetIOOptions options;
|
||||
ffInitNetIOOptions(&options);
|
||||
ffPrepareNetIO(&options);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'P': case 'p':
|
||||
FF_IF_MODULE_MATCH(FF_PUBLICIP_MODULE_NAME)
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyPublicIpOptions))) FFPublicIPOptions options;
|
||||
ffInitPublicIpOptions(&options);
|
||||
ffPreparePublicIp(&options);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'W': case 'w':
|
||||
FF_IF_MODULE_MATCH(FF_WEATHER_MODULE_NAME)
|
||||
{
|
||||
__attribute__((__cleanup__(ffDestroyWeatherOptions))) FFWeatherOptions options;
|
||||
ffInitWeatherOptions(&options);
|
||||
ffPrepareWeather(&options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#undef FF_IF_MODULE_MATCH
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonConfig(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
|
||||
static void genJsonConfig(FFdata* data, FFModuleBaseInfo* baseInfo, void* options)
|
||||
{
|
||||
yyjson_mut_doc* doc = data->resultDoc;
|
||||
|
||||
yyjson_mut_val* modules = yyjson_mut_obj_get(doc->root, "modules");
|
||||
if (!modules)
|
||||
modules = yyjson_mut_obj_add_arr(doc, doc->root, "modules");
|
||||
@@ -112,7 +138,7 @@ static void genJsonConfig(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_
|
||||
FF_STRBUF_AUTO_DESTROY type = ffStrbufCreateS(baseInfo->name);
|
||||
ffStrbufLowerCase(&type);
|
||||
|
||||
if (instance.state.fullConfig)
|
||||
if (data->docType == FF_RESULT_DOC_TYPE_CONFIG_FULL)
|
||||
{
|
||||
yyjson_mut_val* module = yyjson_mut_obj(doc);
|
||||
yyjson_mut_obj_add_strbuf(doc, module, "type", &type);
|
||||
@@ -131,8 +157,9 @@ static void genJsonConfig(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_
|
||||
}
|
||||
}
|
||||
|
||||
static void genJsonResult(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* doc)
|
||||
static void genJsonResult(FFdata* data, FFModuleBaseInfo* baseInfo, void* options)
|
||||
{
|
||||
yyjson_mut_doc* doc = data->resultDoc;
|
||||
yyjson_mut_val* module = yyjson_mut_arr_add_obj(doc, doc->root);
|
||||
yyjson_mut_obj_add_str(doc, module, "type", baseInfo->name);
|
||||
if (baseInfo->generateJsonResult)
|
||||
@@ -142,9 +169,9 @@ static void genJsonResult(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_
|
||||
}
|
||||
|
||||
static void parseStructureCommand(
|
||||
FFdata* data,
|
||||
const char* line,
|
||||
void (*fn)(FFModuleBaseInfo* baseInfo, void* options, yyjson_mut_doc* jsonDoc),
|
||||
yyjson_mut_doc* jsonDoc
|
||||
void (*fn)(FFdata*, FFModuleBaseInfo* baseInfo, void* options)
|
||||
)
|
||||
{
|
||||
if(ffCharIsEnglishAlphabet(line[0]))
|
||||
@@ -156,8 +183,8 @@ static void parseStructureCommand(
|
||||
{
|
||||
uint8_t optionBuf[FF_OPTION_MAX_SIZE];
|
||||
baseInfo->initOptions(optionBuf);
|
||||
if (__builtin_expect(jsonDoc != NULL, false))
|
||||
fn(baseInfo, optionBuf, jsonDoc);
|
||||
if (__builtin_expect(data->resultDoc != NULL, false))
|
||||
fn(data, baseInfo, optionBuf);
|
||||
else
|
||||
baseInfo->printModule(optionBuf);
|
||||
baseInfo->destroyOptions(optionBuf);
|
||||
@@ -169,30 +196,32 @@ static void parseStructureCommand(
|
||||
ffPrintError(line, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "<no implementation provided>");
|
||||
}
|
||||
|
||||
void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc)
|
||||
void ffPrintCommandOption(FFdata* data)
|
||||
{
|
||||
//Parse the structure and call the modules
|
||||
int32_t thres = instance.config.display.stat;
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < data->structure.length)
|
||||
|
||||
char* moduleType = NULL;
|
||||
size_t moduleLen = 0;
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure))
|
||||
{
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(&data->structure, startIndex, ':');
|
||||
data->structure.chars[colonIndex] = '\0';
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':'))
|
||||
continue;
|
||||
|
||||
double ms = 0;
|
||||
if(thres >= 0)
|
||||
ms = ffTimeGetTick();
|
||||
|
||||
parseStructureCommand(data->structure.chars + startIndex, genJsonResult, jsonDoc);
|
||||
parseStructureCommand(data, moduleType, genJsonResult);
|
||||
|
||||
if(thres >= 0)
|
||||
{
|
||||
ms = ffTimeGetTick() - ms;
|
||||
|
||||
if (jsonDoc)
|
||||
if (data->resultDoc)
|
||||
{
|
||||
yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(jsonDoc->root);
|
||||
yyjson_mut_obj_add_real(jsonDoc, moduleJson, "stat", ms);
|
||||
yyjson_mut_val* moduleJson = yyjson_mut_arr_get_last(data->resultDoc->root);
|
||||
yyjson_mut_obj_add_real(data->resultDoc, moduleJson, "stat", ms);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -205,28 +234,24 @@ void ffPrintCommandOption(FFdata* data, yyjson_mut_doc* jsonDoc)
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (!jsonDoc && !instance.config.display.noBuffer) fflush(stdout);
|
||||
if (!data->resultDoc && !instance.config.display.noBuffer) fflush(stdout);
|
||||
#endif
|
||||
|
||||
startIndex = colonIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data, yyjson_mut_doc* jsonDoc)
|
||||
void ffMigrateCommandOptionToJsonc(FFdata* data)
|
||||
{
|
||||
//If we don't have a custom structure, use the default one
|
||||
if(data->structure.length == 0)
|
||||
ffStrbufAppendS(&data->structure, FASTFETCH_DATATEXT_STRUCTURE); // Cannot use `ffStrbufSetStatic` here because we will modify the string
|
||||
|
||||
//Parse the structure and call the modules
|
||||
uint32_t startIndex = 0;
|
||||
while (startIndex < data->structure.length)
|
||||
char* moduleType = NULL;
|
||||
size_t moduleLen = 0;
|
||||
while (ffStrbufGetdelim(&moduleType, &moduleLen, ':', &data->structure))
|
||||
{
|
||||
uint32_t colonIndex = ffStrbufNextIndexC(&data->structure, startIndex, ':');
|
||||
data->structure.chars[colonIndex] = '\0';
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, moduleType, ':'))
|
||||
continue;
|
||||
|
||||
parseStructureCommand(data->structure.chars + startIndex, genJsonConfig, jsonDoc);
|
||||
|
||||
startIndex = colonIndex + 1;
|
||||
parseStructureCommand(data, moduleType, genJsonConfig);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-13
@@ -28,8 +28,6 @@ static void initState(FFstate* state)
|
||||
state->titleFqdn = false;
|
||||
|
||||
ffPlatformInit(&state->platform);
|
||||
state->configDoc = NULL;
|
||||
state->resultDoc = NULL;
|
||||
state->dynamicInterval = 0;
|
||||
|
||||
{
|
||||
@@ -96,8 +94,8 @@ static void exitSignalHandler(FF_MAYBE_UNUSED int signal)
|
||||
|
||||
void ffStart(void)
|
||||
{
|
||||
ffDisableLinewrap = instance.config.display.disableLinewrap && !instance.config.display.pipe && !instance.state.resultDoc;
|
||||
ffHideCursor = instance.config.display.hideCursor && !instance.config.display.pipe && !instance.state.resultDoc;
|
||||
ffDisableLinewrap = instance.config.display.disableLinewrap && !instance.config.display.pipe;
|
||||
ffHideCursor = instance.config.display.hideCursor && !instance.config.display.pipe;
|
||||
|
||||
#ifdef _WIN32
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||||
@@ -124,7 +122,7 @@ void ffStart(void)
|
||||
#endif
|
||||
|
||||
//reset everything to default before we start printing
|
||||
if(!instance.config.display.pipe && !instance.state.resultDoc)
|
||||
if(!instance.config.display.pipe)
|
||||
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
|
||||
|
||||
if(ffHideCursor)
|
||||
@@ -138,8 +136,6 @@ void ffStart(void)
|
||||
fputs("\033[?1049h\033[H", stdout); // Enable alternate buffer
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
ffLogoPrint();
|
||||
}
|
||||
|
||||
void ffFinish(void)
|
||||
@@ -157,9 +153,6 @@ static void destroyConfig(void)
|
||||
static void destroyState(void)
|
||||
{
|
||||
ffPlatformDestroy(&instance.state.platform);
|
||||
yyjson_doc_free(instance.state.configDoc);
|
||||
yyjson_mut_doc_free(instance.state.resultDoc);
|
||||
ffStrbufDestroy(&instance.state.genConfigPath);
|
||||
}
|
||||
|
||||
void ffDestroyInstance(void)
|
||||
@@ -232,9 +225,6 @@ void ffListFeatures(void)
|
||||
#if FF_HAVE_FREETYPE
|
||||
"freetype\n"
|
||||
#endif
|
||||
#if FF_HAVE_PCIACCESS
|
||||
"libpciaccess\n"
|
||||
#endif
|
||||
#if FF_HAVE_PULSE
|
||||
"libpulse\n"
|
||||
#endif
|
||||
@@ -265,6 +255,9 @@ void ffListFeatures(void)
|
||||
#if FF_WIN7_COMPAT
|
||||
"Windows 7 Compatibility\n"
|
||||
#endif
|
||||
#if FF_APPLE_MEMSIZE_USABLE
|
||||
"Apple memsize_usable\n"
|
||||
#endif
|
||||
""
|
||||
, stdout);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ static void createSubfolders(const char* fileName)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
|
||||
|
||||
char *token = NULL;
|
||||
const char *token = NULL;
|
||||
while((token = strchr(fileName, '/')) != NULL)
|
||||
{
|
||||
ffStrbufAppendNS(&path, (uint32_t)(token - fileName + 1), fileName);
|
||||
|
||||
@@ -230,9 +230,10 @@ static bool matchesJsonArray(const char* str, yyjson_val* val)
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
|
||||
static const char* printJsonConfig(FFdata* data, bool prepare)
|
||||
{
|
||||
yyjson_val* const root = yyjson_doc_get_root(instance.state.configDoc);
|
||||
yyjson_mut_doc* jsonDoc = data->resultDoc;
|
||||
yyjson_val* const root = yyjson_doc_get_root(data->configDoc);
|
||||
assert(root);
|
||||
|
||||
if (!yyjson_is_obj(root))
|
||||
@@ -298,6 +299,9 @@ static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
|
||||
else
|
||||
return "modules must be an array of strings or objects";
|
||||
|
||||
if (ffStrbufSeparatedContainIgnCaseS(&data->structureDisabled, type, ':'))
|
||||
continue;
|
||||
|
||||
if(prepare)
|
||||
prepareModuleJsonObject(type, module);
|
||||
else
|
||||
@@ -329,9 +333,10 @@ static const char* printJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ffPrintJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc)
|
||||
void ffPrintJsonConfig(FFdata* data, bool prepare)
|
||||
{
|
||||
const char* error = printJsonConfig(prepare, jsonDoc);
|
||||
yyjson_mut_doc* jsonDoc = data->resultDoc;
|
||||
const char* error = printJsonConfig(data, prepare);
|
||||
if (error)
|
||||
{
|
||||
if (jsonDoc)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "common/memrchr.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void* memrchr(const void* s, int c, size_t n)
|
||||
{
|
||||
if (n == 0) return NULL;
|
||||
|
||||
const uint8_t uc = (uint8_t) c;
|
||||
|
||||
const uint8_t* p = (const uint8_t*) s + n;
|
||||
|
||||
while (n--)
|
||||
{
|
||||
if (*--p == uc) return (void*) p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -72,7 +72,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
int pid = getpid();
|
||||
uint32_t pid = instance.state.platform.pid;
|
||||
|
||||
struct {
|
||||
struct rt_msghdr hdr;
|
||||
@@ -85,7 +85,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
.rtm_version = RTM_VERSION,
|
||||
.rtm_addrs = RTA_DST | RTA_IFP | RTA_IFA,
|
||||
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
|
||||
.rtm_pid = pid,
|
||||
.rtm_pid = (pid_t) pid,
|
||||
.rtm_seq = 1,
|
||||
},
|
||||
.dst = {
|
||||
@@ -99,7 +99,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
|
||||
return false;
|
||||
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == pid))
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 1 && rtmsg.hdr.rtm_pid == (pid_t) pid))
|
||||
;
|
||||
|
||||
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
|
||||
@@ -145,7 +145,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
setsockopt(pfRoute, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
int pid = getpid();
|
||||
uint32_t pid = instance.state.platform.pid;
|
||||
|
||||
struct {
|
||||
struct rt_msghdr hdr;
|
||||
@@ -158,7 +158,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
.rtm_version = RTM_VERSION,
|
||||
.rtm_addrs = RTA_DST | RTA_IFP,
|
||||
.rtm_msglen = sizeof(rtmsg.hdr) + sizeof(rtmsg.dst),
|
||||
.rtm_pid = pid,
|
||||
.rtm_pid = (pid_t) pid,
|
||||
.rtm_seq = 2,
|
||||
},
|
||||
.dst = {
|
||||
@@ -172,7 +172,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
if (send(pfRoute, &rtmsg, rtmsg.hdr.rtm_msglen, 0) != rtmsg.hdr.rtm_msglen)
|
||||
return false;
|
||||
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == pid))
|
||||
while (recv(pfRoute, &rtmsg, sizeof(rtmsg), 0) > 0 && !(rtmsg.hdr.rtm_seq == 2 && rtmsg.hdr.rtm_pid == (pid_t) pid))
|
||||
;
|
||||
|
||||
#ifndef __sun // On Solaris, the RTF_GATEWAY flag is not set for default routes for some reason
|
||||
|
||||
@@ -19,7 +19,7 @@ bool ffNetifGetDefaultRouteImplV4(FFNetifDefaultRouteResult* result)
|
||||
}
|
||||
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
|
||||
|
||||
unsigned pid = (unsigned) getpid();
|
||||
uint32_t pid = instance.state.platform.pid;
|
||||
FF_DEBUG("Process PID: %u", pid);
|
||||
|
||||
// Bind socket
|
||||
@@ -235,7 +235,7 @@ bool ffNetifGetDefaultRouteImplV6(FFNetifDefaultRouteResult* result)
|
||||
}
|
||||
FF_DEBUG("Created netlink socket: fd=%d", sock_fd);
|
||||
|
||||
unsigned pid = (unsigned) getpid();
|
||||
uint32_t pid = instance.state.platform.pid;
|
||||
FF_DEBUG("Process PID: %u", pid);
|
||||
|
||||
// Bind socket
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/processing.h"
|
||||
#include "common/io.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <windows.h>
|
||||
#include <ntstatus.h>
|
||||
#include <winternl.h>
|
||||
@@ -51,7 +54,7 @@ const char* ffProcessSpawn(char* const argv[], bool useStdErr, FFProcessHandle*
|
||||
|
||||
wchar_t pipeName[32];
|
||||
static unsigned pidCounter = 0;
|
||||
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u-%u", GetCurrentProcessId(), ++pidCounter);
|
||||
swprintf(pipeName, ARRAY_SIZE(pipeName), L"\\\\.\\pipe\\FASTFETCH-%u-%u", instance.state.platform.pid, ++pidCounter);
|
||||
|
||||
FF_AUTO_CLOSE_FD HANDLE hChildPipeRead = CreateNamedPipeW(
|
||||
pipeName,
|
||||
@@ -193,9 +196,16 @@ const char* ffProcessReadOutput(FFProcessHandle* handle, FFstrbuf* buffer)
|
||||
|
||||
exit:
|
||||
{
|
||||
DWORD exitCode = 0;
|
||||
if (GetExitCodeProcess(hProcess, &exitCode) && exitCode != STILL_ACTIVE && exitCode != 0)
|
||||
return "Child process exited with an error";
|
||||
PROCESS_BASIC_INFORMATION info = {};
|
||||
ULONG size;
|
||||
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), &size)))
|
||||
{
|
||||
assert(size == sizeof(info));
|
||||
if (info.ExitStatus != STILL_ACTIVE && info.ExitStatus != 0)
|
||||
return "Child process exited with an error";
|
||||
}
|
||||
else
|
||||
return "NtQueryInformationProcess(ProcessBasicInformation) failed";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -204,7 +214,7 @@ exit:
|
||||
bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName, FFstrbuf* exePath, bool* gui)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD HANDLE hProcess = pid == 0
|
||||
? GetCurrentProcess()
|
||||
? NtCurrentProcess()
|
||||
: OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
|
||||
if (hProcess == NULL)
|
||||
@@ -227,12 +237,15 @@ bool ffProcessGetInfoWindows(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFst
|
||||
}
|
||||
if(exe)
|
||||
{
|
||||
DWORD bufSize = exe->allocated;
|
||||
if(QueryFullProcessImageNameA(hProcess, 0, exe->chars, &bufSize))
|
||||
// TODO: It's possible to query the command line with `NtQueryInformationProcess(60/*ProcessCommandLineInformation*/)` since Windows 8.1
|
||||
|
||||
alignas(alignof(UNICODE_STRING)) uint8_t buffer[4096];
|
||||
ULONG size;
|
||||
if(NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, &buffer, sizeof(buffer), &size)))
|
||||
{
|
||||
// We use full path here
|
||||
// Querying command line of remote processes in Windows requires either WMI or ReadProcessMemory
|
||||
exe->length = bufSize;
|
||||
UNICODE_STRING* imageName = (UNICODE_STRING*)buffer;
|
||||
ffStrbufSetNWS(exe, imageName->Length / sizeof(wchar_t), imageName->Buffer);
|
||||
|
||||
if (exePath) ffStrbufSet(exePath, exe);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -81,3 +81,15 @@ FFTimeGetAgeResult ffTimeGetAge(uint64_t birthMs, uint64_t nowMs)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
double ffQpcMultiplier;
|
||||
|
||||
__attribute__((constructor))
|
||||
static void ffTimeInitQpcMultiplier(void)
|
||||
{
|
||||
LARGE_INTEGER frequency;
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
ffQpcMultiplier = 1000. / (double) frequency.QuadPart;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "fastfetch.h"
|
||||
#include "common/ffdata.h"
|
||||
#include "common/option.h"
|
||||
|
||||
bool ffJsonConfigParseModuleArgs(yyjson_val* key, yyjson_val* val, FFModuleArgs* moduleArgs);
|
||||
const char* ffJsonConfigParseEnum(yyjson_val* val, int* result, FFKeyValuePair pairs[]);
|
||||
void ffPrintJsonConfig(bool prepare, yyjson_mut_doc* jsonDoc);
|
||||
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs);
|
||||
|
||||
yyjson_api_inline yyjson_mut_val* yyjson_mut_strbuf(yyjson_mut_doc *doc, const FFstrbuf* buf) {
|
||||
return yyjson_mut_strncpy(doc, buf->chars, buf->length);
|
||||
@@ -23,3 +22,6 @@ yyjson_api_inline bool yyjson_mut_arr_add_strbuf(yyjson_mut_doc *doc,
|
||||
const FFstrbuf* buf) {
|
||||
return yyjson_mut_arr_add_strncpy(doc, obj, buf->chars, buf->length);
|
||||
}
|
||||
|
||||
void ffPrintJsonConfig(FFdata* data, bool prepare);
|
||||
void ffJsonConfigGenerateModuleArgsConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFModuleArgs* moduleArgs);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// `memrchr` is a GNU extension and may not be declared by system headers even when the symbol exists.
|
||||
// Declare it unconditionally; the build system provides a fallback implementation when missing.
|
||||
void* memrchr(const void* s, int c, size_t n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
+18
-7
@@ -1,11 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#ifdef _WIN32
|
||||
#include <synchapi.h>
|
||||
#include <ntdef.h>
|
||||
#include <ntstatus.h>
|
||||
#include <profileapi.h>
|
||||
#include <sysinfoapi.h>
|
||||
|
||||
NTSYSCALLAPI
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
NtDelayExecution(
|
||||
_In_ BOOLEAN Alertable,
|
||||
_In_ PLARGE_INTEGER DelayInterval);
|
||||
#elif defined(__HAIKU__)
|
||||
#include <OS.h>
|
||||
#endif
|
||||
@@ -15,11 +24,10 @@
|
||||
static inline double ffTimeGetTick(void) //In msec
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER frequency;
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
extern double ffQpcMultiplier;
|
||||
LARGE_INTEGER start;
|
||||
QueryPerformanceCounter(&start);
|
||||
return (double) start.QuadPart * 1000 / (double) frequency.QuadPart;
|
||||
return (double) start.QuadPart * ffQpcMultiplier;
|
||||
#elif defined(__HAIKU__)
|
||||
return (double) system_time() / 1000.;
|
||||
#else
|
||||
@@ -44,12 +52,15 @@ static inline uint64_t ffTimeGetNow(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void ffTimeSleep(uint32_t msec)
|
||||
// Returns true if not interrupted
|
||||
static inline bool ffTimeSleep(uint32_t msec)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SleepEx(msec, TRUE);
|
||||
LARGE_INTEGER interval;
|
||||
interval.QuadPart = -(int64_t) msec * 10000; // Relative time in 100-nanosecond intervals
|
||||
return NtDelayExecution(TRUE, &interval) == STATUS_SUCCESS;
|
||||
#else
|
||||
nanosleep(&(struct timespec){ msec / 1000, (long) (msec % 1000) * 1000000 }, NULL);
|
||||
return nanosleep(&(struct timespec){ msec / 1000, (long) (msec % 1000) * 1000000 }, NULL) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+39
-17
@@ -6,12 +6,12 @@
|
||||
#define D3DKMT_ALIGN64 __attribute__((aligned(8)))
|
||||
|
||||
typedef struct _PROCESSOR_POWER_INFORMATION {
|
||||
ULONG Number;
|
||||
ULONG MaxMhz;
|
||||
ULONG CurrentMhz;
|
||||
ULONG MhzLimit;
|
||||
ULONG MaxIdleState;
|
||||
ULONG CurrentIdleState;
|
||||
ULONG Number;
|
||||
ULONG MaxMhz;
|
||||
ULONG CurrentMhz;
|
||||
ULONG MhzLimit;
|
||||
ULONG MaxIdleState;
|
||||
ULONG CurrentIdleState;
|
||||
} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
|
||||
|
||||
NTSTATUS NTAPI NtPowerInformation(
|
||||
@@ -237,14 +237,36 @@ NTSYSCALLAPI
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
NtQueryDirectoryFile(
|
||||
IN HANDLE FileHandle,
|
||||
IN HANDLE Event OPTIONAL,
|
||||
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
||||
IN PVOID ApcContext OPTIONAL,
|
||||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||||
OUT PVOID FileInformation,
|
||||
IN ULONG Length,
|
||||
IN FILE_INFORMATION_CLASS FileInformationClass,
|
||||
IN BOOLEAN ReturnSingleEntry,
|
||||
IN PUNICODE_STRING FileName OPTIONAL,
|
||||
IN BOOLEAN RestartScan);
|
||||
IN HANDLE FileHandle,
|
||||
IN HANDLE Event OPTIONAL,
|
||||
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
||||
IN PVOID ApcContext OPTIONAL,
|
||||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||||
OUT PVOID FileInformation,
|
||||
IN ULONG Length,
|
||||
IN FILE_INFORMATION_CLASS FileInformationClass,
|
||||
IN BOOLEAN ReturnSingleEntry,
|
||||
IN PUNICODE_STRING FileName OPTIONAL,
|
||||
IN BOOLEAN RestartScan);
|
||||
|
||||
// https://ntdoc.m417z.com/process_devicemap_information_ex
|
||||
typedef struct _PROCESS_DEVICEMAP_INFORMATION_EX
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
HANDLE DirectoryHandle; // A handle to a directory object that can be set as the new device map for the process. This handle must have DIRECTORY_TRAVERSE access.
|
||||
} Set;
|
||||
struct
|
||||
{
|
||||
ULONG DriveMap; // A bitmask that indicates which drive letters are currently in use in the process's device map.
|
||||
UCHAR DriveType[32]; // A value that indicates the type of each drive (e.g., local disk, network drive, etc.). // DRIVE_* WinBase.h
|
||||
} Query;
|
||||
};
|
||||
ULONG Flags; // PROCESS_LUID_DOSDEVICES_ONLY
|
||||
} PROCESS_DEVICEMAP_INFORMATION_EX, *PPROCESS_DEVICEMAP_INFORMATION_EX;
|
||||
|
||||
#ifndef NtCurrentProcess
|
||||
#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1)
|
||||
#endif
|
||||
|
||||
@@ -13,6 +13,11 @@ static inline void ffStrbufInitWSV(FFstrbuf* result, const std::wstring_view sou
|
||||
return ffStrbufInitNWS(result, (uint32_t) source.size(), source.data());
|
||||
}
|
||||
|
||||
static inline FFstrbuf ffStrbufCreateWSV(const std::wstring_view source)
|
||||
{
|
||||
return ffStrbufCreateNWS((uint32_t) source.size(), source.data());
|
||||
}
|
||||
|
||||
static inline void ffStrbufSetWSV(FFstrbuf* result, const std::wstring_view source)
|
||||
{
|
||||
return ffStrbufSetNWS(result, (uint32_t) source.size(), source.data());
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
template <typename Fn>
|
||||
struct on_scope_exit {
|
||||
static_assert(std::is_nothrow_move_constructible<Fn>::value,
|
||||
"Fn must be nothrow move constructible");
|
||||
|
||||
explicit on_scope_exit(Fn &&fn) noexcept
|
||||
: _fn(std::move(fn)) {};
|
||||
on_scope_exit(const on_scope_exit&) = delete;
|
||||
on_scope_exit& operator=(const on_scope_exit&) = delete;
|
||||
~on_scope_exit() noexcept { this->_fn(); }
|
||||
|
||||
private:
|
||||
Fn _fn;
|
||||
};
|
||||
@@ -468,6 +468,14 @@
|
||||
"default": "\"fastfetch --print-structure\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "structure-disabled",
|
||||
"desc": "Disable specific modules in the structure",
|
||||
"remark": "Must be a colon-separated list of keys",
|
||||
"arg": {
|
||||
"type": "structure"
|
||||
}
|
||||
},
|
||||
{
|
||||
"long": "stat",
|
||||
"desc": "Show time usage (in ms) for individual modules",
|
||||
|
||||
@@ -10,7 +10,13 @@
|
||||
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */)
|
||||
{
|
||||
// Actually bluetoothapis.dll, but it's missing on Windows 7
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bthprops.cpl", 1)
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis,
|
||||
#if FF_WIN7_COMPAT
|
||||
"bthprops.cpl"
|
||||
#else
|
||||
"bluetoothapis.dll"
|
||||
#endif
|
||||
, 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstDevice)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextDevice)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindDeviceClose)
|
||||
|
||||
@@ -4,18 +4,10 @@ extern "C"
|
||||
}
|
||||
#include "common/windows/wmi.hpp"
|
||||
#include "common/windows/unicode.hpp"
|
||||
#include "common/windows/util.hpp"
|
||||
|
||||
STDAPI InitVariantFromStringArray(_In_reads_(cElems) PCWSTR *prgsz, _In_ ULONG cElems, _Out_ VARIANT *pvar);
|
||||
|
||||
template <typename Fn>
|
||||
struct on_scope_exit {
|
||||
on_scope_exit(Fn &&fn): _fn(std::move(fn)) {}
|
||||
~on_scope_exit() { this->_fn(); }
|
||||
|
||||
private:
|
||||
Fn _fn;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
const char* ffBluetoothDetectBattery(FFlist* devices)
|
||||
{
|
||||
@@ -87,8 +79,7 @@ const char* ffBluetoothDetectBattery(FFlist* devices)
|
||||
batt = data.get<uint8_t>();
|
||||
else
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY addr; // MAC address without colon
|
||||
ffStrbufInitWSV(&addr, data.get<std::wstring_view>());
|
||||
FF_STRBUF_AUTO_DESTROY addr = ffStrbufCreateWSV(data.get<std::wstring_view>()); // MAC address without colon
|
||||
if (__builtin_expect(addr.length != 12, 0))
|
||||
continue;
|
||||
|
||||
|
||||
@@ -54,7 +54,13 @@ static_assert(sizeof(BTH_LOCAL_RADIO_INFO) == 292, "BTH_LOCAL_RADIO_INFO should
|
||||
const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */)
|
||||
{
|
||||
// Actually bluetoothapis.dll, but it's missing on Windows 7
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bthprops.cpl", 1)
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis,
|
||||
#if FF_WIN7_COMPAT
|
||||
"bthprops.cpl"
|
||||
#else
|
||||
"bluetoothapis.dll"
|
||||
#endif
|
||||
, 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstRadio)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindNextRadio)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindRadioClose)
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#include "common/io.h"
|
||||
#include "common/smbiosHelper.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
const char* ffDetectBoard(FFBoardResult* board)
|
||||
{
|
||||
if (ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_name", "/sys/class/dmi/id/board_name", &board->name))
|
||||
@@ -12,11 +10,17 @@ const char* ffDetectBoard(FFBoardResult* board)
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_vendor", "/sys/class/dmi/id/board_vendor", &board->vendor);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_version", "/sys/class/dmi/id/board_version", &board->version);
|
||||
}
|
||||
else if (ffReadFileBuffer("/proc/device-tree/board", &board->name))
|
||||
else if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/baseboard/product", &board->name))
|
||||
{
|
||||
ffStrbufTrimRight(&board->name, '\0');
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/baseboard/manufacturer", &board->vendor))
|
||||
ffStrbufTrimRight(&board->vendor, '\0');
|
||||
}
|
||||
else if (ffReadFileBuffer("/sys/firmware/devicetree/base/board", &board->name))
|
||||
{
|
||||
ffStrbufTrimRightSpace(&board->name);
|
||||
}
|
||||
else if (ffReadFileBuffer("/proc/device-tree/compatible", &board->vendor))
|
||||
else if (ffReadFileBuffer("/sys/firmware/devicetree/base/compatible", &board->vendor))
|
||||
{
|
||||
uint32_t comma = ffStrbufFirstIndexC(&board->vendor, ',');
|
||||
if (comma < board->vendor.length)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
static const char* detectSecureBoot(bool* result)
|
||||
{
|
||||
#if __aarch64__
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/chosen");
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen");
|
||||
if (!entryDevice)
|
||||
return "IORegistryEntryFromPath() failed";
|
||||
|
||||
@@ -20,7 +20,7 @@ static const char* detectSecureBoot(bool* result)
|
||||
|
||||
*result = (bool) *CFDataGetBytePtr((CFDataRef) prop);
|
||||
#else
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/options");
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/options");
|
||||
if (!entryDevice)
|
||||
return "IORegistryEntryFromPath() failed";
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#endif
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdalign.h>
|
||||
|
||||
#ifdef __NetBSD__
|
||||
typedef uint16_t efi_char;
|
||||
@@ -23,7 +24,7 @@ const char* ffDetectBootmgr(FFBootmgrResult* result)
|
||||
FF_AUTO_CLOSE_FD int efifd = open("/dev/efi", O_RDWR | O_CLOEXEC);
|
||||
if (efifd < 0) return "open(/dev/efi) failed";
|
||||
|
||||
uint8_t buffer[2048];
|
||||
alignas(uint16_t) uint8_t buffer[2048];
|
||||
struct efi_var_ioc ioc = {
|
||||
.vendor = EFI_GLOBAL_VARIABLE,
|
||||
.data = buffer,
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
#include "common/io.h"
|
||||
#include "efi_helper.h"
|
||||
|
||||
#include <stdalign.h>
|
||||
|
||||
#define FF_EFIVARS_PATH_PREFIX "/sys/firmware/efi/efivars/"
|
||||
|
||||
const char* ffDetectBootmgr(FFBootmgrResult* result)
|
||||
{
|
||||
uint8_t buffer[2048];
|
||||
alignas(uint16_t) uint8_t buffer[2048];
|
||||
|
||||
if (ffReadFileData(FF_EFIVARS_PATH_PREFIX "BootCurrent-" FF_EFI_GLOBAL_GUID, sizeof(buffer), buffer) != 6)
|
||||
return "Failed to read efivar: BootCurrent";
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#include "bootmgr.h"
|
||||
#include "efi_helper.h"
|
||||
#include "common/io.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
const char* enablePrivilege(const wchar_t* privilege)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD HANDLE token = NULL;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
|
||||
if (!OpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
|
||||
return "OpenProcessToken() failed";
|
||||
|
||||
TOKEN_PRIVILEGES tp = {
|
||||
|
||||
@@ -4,20 +4,12 @@ extern "C" {
|
||||
}
|
||||
#include "common/windows/com.hpp"
|
||||
#include "common/windows/unicode.hpp"
|
||||
#include "common/windows/util.hpp"
|
||||
|
||||
#include <initguid.h>
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
|
||||
template <typename Fn>
|
||||
struct on_scope_exit {
|
||||
on_scope_exit(Fn &&fn): _fn(std::move(fn)) {}
|
||||
~on_scope_exit() { this->_fn(); }
|
||||
|
||||
private:
|
||||
Fn _fn;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
const char* ffDetectCamera(FF_MAYBE_UNUSED FFlist* result)
|
||||
{
|
||||
|
||||
@@ -2,20 +2,42 @@
|
||||
#include "common/io.h"
|
||||
#include "common/smbiosHelper.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
const char* ffDetectChassis(FFChassisResult* result)
|
||||
{
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_type", "/sys/class/dmi/id/chassis_type", &result->type);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_serial", "/sys/class/dmi/id/chassis_serial", &result->serial);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_vendor", "/sys/class/dmi/id/chassis_vendor", &result->vendor);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_version", "/sys/class/dmi/id/chassis_version", &result->version);
|
||||
|
||||
if(result->type.length)
|
||||
if (ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_type", "/sys/class/dmi/id/chassis_type", &result->type))
|
||||
{
|
||||
const char* typeStr = ffChassisTypeToString((uint32_t) ffStrbufToUInt(&result->type, 9999));
|
||||
if(typeStr)
|
||||
ffStrbufSetS(&result->type, typeStr);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_serial", "/sys/class/dmi/id/chassis_serial", &result->serial);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_vendor", "/sys/class/dmi/id/chassis_vendor", &result->vendor);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_version", "/sys/class/dmi/id/chassis_version", &result->version);
|
||||
|
||||
if(result->type.length)
|
||||
{
|
||||
const char* typeStr = ffChassisTypeToString((uint32_t) ffStrbufToUInt(&result->type, 9999));
|
||||
if(typeStr)
|
||||
ffStrbufSetStatic(&result->type, typeStr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Available on Asahi Linux
|
||||
uint32_t chassisType = 0;
|
||||
if (ffReadFileData("/sys/firmware/devicetree/base/smbios/smbios/chassis/chassis-type", sizeof(chassisType), &chassisType)) // big endian
|
||||
{
|
||||
chassisType = __builtin_bswap32(chassisType);
|
||||
const char* typeStr = ffChassisTypeToString(chassisType);
|
||||
if(typeStr)
|
||||
ffStrbufSetStatic(&result->type, typeStr);
|
||||
|
||||
if(ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/chassis/manufacturer", &result->vendor) && result->vendor.length > 0)
|
||||
ffStrbufTrimRight(&result->vendor, '\0');
|
||||
}
|
||||
else if(ffReadFileBuffer("/sys/firmware/devicetree/base/chassis-type", &result->type) && result->type.length > 0)
|
||||
{
|
||||
ffStrbufTrimRight(&result->type, '\0');
|
||||
result->type.chars[0] = (char) toupper(result->type.chars[0]);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ static const char* armPartId2name(uint32_t partId)
|
||||
case 0xa20: return "ARM1020";
|
||||
case 0xa22: return "ARM1022";
|
||||
case 0xa26: return "ARM1026";
|
||||
case 0xb02: return "ARM11 MPCore";
|
||||
case 0xb02: return "ARM11-MPCore";
|
||||
case 0xb36: return "ARM1136";
|
||||
case 0xb56: return "ARM1156";
|
||||
case 0xb76: return "ARM1176";
|
||||
@@ -82,10 +82,12 @@ static const char* armPartId2name(uint32_t partId)
|
||||
case 0xd0d: return "Cortex-A77";
|
||||
case 0xd0e: return "Cortex-A76AE";
|
||||
case 0xd13: return "Cortex-R52";
|
||||
case 0xd14: return "Cortex-R82AE";
|
||||
case 0xd15: return "Cortex-R82";
|
||||
case 0xd16: return "Cortex-R52+";
|
||||
case 0xd20: return "Cortex-M23";
|
||||
case 0xd21: return "Cortex-M33";
|
||||
case 0xd24: return "Cortex-M52";
|
||||
case 0xd22: return "Cortex-M55";
|
||||
case 0xd23: return "Cortex-M85";
|
||||
case 0xd40: return "Neoverse-V1";
|
||||
@@ -215,7 +217,7 @@ static const char* nvidiaPartId2name(uint32_t partId)
|
||||
switch (partId)
|
||||
{
|
||||
case 0x000: return "Denver";
|
||||
case 0x003: return "Denver 2";
|
||||
case 0x003: return "Denver-2";
|
||||
case 0x004: return "Carmel";
|
||||
case 0x010: return "Olympus";
|
||||
default: return NULL;
|
||||
|
||||
@@ -741,7 +741,7 @@ FF_MAYBE_UNUSED static void detectSocName(FFCPUResult* cpu)
|
||||
|
||||
// [x-vendor,x-model\0]*N
|
||||
char content[512];
|
||||
ssize_t length = ffReadFileData("/proc/device-tree/compatible", ARRAY_SIZE(content), content);
|
||||
ssize_t length = ffReadFileData("/sys/firmware/devicetree/base/compatible", ARRAY_SIZE(content), content);
|
||||
if (length < 4) return; // v,m\0
|
||||
|
||||
if (content[length - 1] != '\0') return; // must end with \0
|
||||
|
||||
@@ -9,7 +9,8 @@ static uint64_t startTime;
|
||||
|
||||
void ffPrepareCPUUsage(void)
|
||||
{
|
||||
assert(cpuTimes1.elementSize == 0);
|
||||
if (cpuTimes1.elementSize != 0) return; // Already prepared
|
||||
|
||||
ffListInit(&cpuTimes1, sizeof(FFCpuUsageInfo));
|
||||
ffGetCpuUsageInfo(&cpuTimes1);
|
||||
startTime = ffTimeGetNow();
|
||||
|
||||
+17
-19
@@ -22,7 +22,7 @@
|
||||
#define _PATH_LOCALBASE "/usr/pkg"
|
||||
#endif
|
||||
|
||||
static void getKDE(FFstrbuf* result, FFDEOptions* options)
|
||||
static void getKDE(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
|
||||
{
|
||||
#ifdef _PATH_LOCALBASE
|
||||
ffParsePropFile(_PATH_LOCALBASE "/share/wayland-sessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result);
|
||||
@@ -44,7 +44,7 @@ static void getKDE(FFstrbuf* result, FFDEOptions* options)
|
||||
if(result->length == 0)
|
||||
ffParsePropFileData("wayland-sessions/plasmawayland5.desktop", "X-KDE-PluginInfo-Version =", result);
|
||||
|
||||
if(result->length == 0 && options->slowVersionDetection)
|
||||
if(result->length == 0)
|
||||
{
|
||||
if (ffProcessAppendStdOut(result, (char* const[]){
|
||||
"plasmashell",
|
||||
@@ -73,7 +73,7 @@ static void getGnome(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
|
||||
{
|
||||
getGnomeByDbus(result);
|
||||
|
||||
if (result->length == 0 && options->slowVersionDetection)
|
||||
if (result->length == 0)
|
||||
{
|
||||
if (ffProcessAppendStdOut(result, (char* const[]){
|
||||
"gnome-shell",
|
||||
@@ -91,7 +91,7 @@ static void getCinnamon(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
|
||||
if (result->length == 0)
|
||||
ffParsePropFileData("applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", result);
|
||||
|
||||
if (result->length == 0 && options->slowVersionDetection)
|
||||
if (result->length == 0)
|
||||
{
|
||||
if (ffProcessAppendStdOut(result, (char* const[]){
|
||||
"cinnamon",
|
||||
@@ -102,7 +102,7 @@ static void getCinnamon(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
|
||||
}
|
||||
}
|
||||
|
||||
static void getMate(FFstrbuf* result, FFDEOptions* options)
|
||||
static void getMate(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY major = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY minor = ffStrbufCreate();
|
||||
@@ -116,7 +116,7 @@ static void getMate(FFstrbuf* result, FFDEOptions* options)
|
||||
|
||||
ffParseSemver(result, &major, &minor, µ);
|
||||
|
||||
if(result->length == 0 && options->slowVersionDetection)
|
||||
if(result->length == 0)
|
||||
{
|
||||
ffProcessAppendStdOut(result, (char* const[]){
|
||||
"mate-session",
|
||||
@@ -143,11 +143,11 @@ static const char* getXfce4ByLib(FFstrbuf* result)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void getXFCE4(FFstrbuf* result, FFDEOptions* options)
|
||||
static void getXFCE4(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
|
||||
{
|
||||
getXfce4ByLib(result);
|
||||
|
||||
if(result->length == 0 && options->slowVersionDetection)
|
||||
if(result->length == 0)
|
||||
{
|
||||
//This is somewhat slow
|
||||
ffProcessAppendStdOut(result, (char* const[]){
|
||||
@@ -162,7 +162,7 @@ static void getXFCE4(FFstrbuf* result, FFDEOptions* options)
|
||||
}
|
||||
}
|
||||
|
||||
static void getLXQt(FFstrbuf* result, FFDEOptions* options)
|
||||
static void getLXQt(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
|
||||
{
|
||||
ffParsePropFileData("gconfig/lxqt.pc", "Version:", result);
|
||||
|
||||
@@ -171,7 +171,7 @@ static void getLXQt(FFstrbuf* result, FFDEOptions* options)
|
||||
if(result->length == 0)
|
||||
ffParsePropFileData("cmake/lxqt/lxqt-config-version.cmake", "set ( PACKAGE_VERSION", result);
|
||||
|
||||
if(result->length == 0 && options->slowVersionDetection)
|
||||
if(result->length == 0)
|
||||
{
|
||||
//This is really, really, really slow. Thank you, LXQt developers
|
||||
ffProcessAppendStdOut(result, (char* const[]){
|
||||
@@ -206,7 +206,7 @@ static bool extractTdeVersion(const char* line, uint32_t len, void *userdata)
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char* getTrinity(FFstrbuf* result, FFDEOptions* options)
|
||||
static const char* getTrinity(FFstrbuf* result, FF_MAYBE_UNUSED FFDEOptions* options)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
|
||||
const char* error = ffFindExecutableInPath("tde-config", &path);
|
||||
@@ -218,15 +218,13 @@ static const char* getTrinity(FFstrbuf* result, FFDEOptions* options)
|
||||
if (ffBinaryExtractStrings(path.chars, extractTdeVersion, result, strlen("R0.0.0")) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (options->slowVersionDetection)
|
||||
ffStrbufClear(&path);
|
||||
if (ffProcessAppendStdOut(&path, (char* const[]){
|
||||
"tde-config",
|
||||
"--version",
|
||||
NULL
|
||||
}) == NULL)
|
||||
{
|
||||
ffStrbufClear(&path);
|
||||
ffProcessAppendStdOut(&path, (char* const[]){
|
||||
"tde-config",
|
||||
"--version",
|
||||
NULL
|
||||
});
|
||||
|
||||
ffParsePropLines(path.chars , "TDE: ", result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1,77 +1,67 @@
|
||||
#include "disk.h"
|
||||
#include "common/io.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/windows/nt.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
static unsigned __stdcall testRemoteVolumeAccessible(void* mountpoint)
|
||||
{
|
||||
FF_AUTO_CLOSE_FD HANDLE handle = CreateFileW(
|
||||
(wchar_t*) mountpoint,
|
||||
READ_CONTROL,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
return 0;
|
||||
}
|
||||
#include <ntstatus.h>
|
||||
#include <stdalign.h>
|
||||
|
||||
const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
{
|
||||
wchar_t buf[MAX_PATH + 1];
|
||||
uint32_t length = GetLogicalDriveStringsW(ARRAY_SIZE(buf), buf);
|
||||
if (length == 0 || length >= ARRAY_SIZE(buf))
|
||||
return "GetLogicalDriveStringsW(ARRAY_SIZE(buf), buf) failed";
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
||||
PROCESS_DEVICEMAP_INFORMATION_EX info = {};
|
||||
ULONG size = 0;
|
||||
if(!NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(), ProcessDeviceMap, &info, sizeof(info), &size)))
|
||||
return "NtQueryInformationProcess(ProcessDeviceMap) failed";
|
||||
|
||||
// For cross-platform portability; used by `presets/examples/13.jsonc`
|
||||
if (__builtin_expect(options->folders.length == 1 && options->folders.chars[0] == '/', 0))
|
||||
if (options->folders.length == 1 && options->folders.chars[0] == '/')
|
||||
{
|
||||
wchar_t path[MAX_PATH + 1];
|
||||
GetSystemWindowsDirectoryW(path, ARRAY_SIZE(path));
|
||||
ffStrbufSetF(&options->folders, "%c:\\", (char) path[0]);
|
||||
options->folders.chars[0] = (char) path[0];
|
||||
ffStrbufAppendS(&options->folders, ":\\");
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < length; i++)
|
||||
wchar_t mountpointW[] = L"X:\\";
|
||||
char mountpointA[] = "X:\\";
|
||||
|
||||
for (wchar_t i = L'A'; i <= L'Z'; i++)
|
||||
{
|
||||
wchar_t* mountpoint = buf + i;
|
||||
if (!(info.Query.DriveMap & (1 << (i - L'A'))))
|
||||
continue;
|
||||
mountpointW[0] = i;
|
||||
mountpointA[0] = (char) i;
|
||||
|
||||
ffStrbufSetWS(&buffer, mountpoint);
|
||||
i += buffer.length;
|
||||
|
||||
UINT driveType = GetDriveTypeW(mountpoint);
|
||||
UINT driveType = info.Query.DriveType[i - L'A'];
|
||||
|
||||
if (__builtin_expect((long) options->folders.length, 0))
|
||||
{
|
||||
if (!ffStrbufSeparatedContain(&options->folders, &buffer, FF_DISK_FOLDER_SEPARATOR))
|
||||
if (!ffStrbufSeparatedContainNS(&options->folders, 3, mountpointA, FF_DISK_FOLDER_SEPARATOR))
|
||||
continue;
|
||||
}
|
||||
else if(driveType == DRIVE_NO_ROOT_DIR)
|
||||
continue;
|
||||
|
||||
if (options->hideFolders.length && ffStrbufSeparatedContain(&options->hideFolders, &buffer, FF_DISK_FOLDER_SEPARATOR))
|
||||
if (options->hideFolders.length && ffStrbufSeparatedContainNS(&options->hideFolders, 3, mountpointA, FF_DISK_FOLDER_SEPARATOR))
|
||||
continue;
|
||||
|
||||
wchar_t diskName[MAX_PATH + 1], diskFileSystem[MAX_PATH + 1];
|
||||
FF_AUTO_CLOSE_FD HANDLE handle = CreateFileW(mountpointW, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
continue;
|
||||
|
||||
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)
|
||||
);
|
||||
IO_STATUS_BLOCK iosb;
|
||||
|
||||
alignas(FILE_FS_ATTRIBUTE_INFORMATION) uint8_t bufFsAttr[1024];
|
||||
FILE_FS_ATTRIBUTE_INFORMATION* fsAttr = NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, bufFsAttr, sizeof(bufFsAttr), FileFsAttributeInformation))
|
||||
? (FILE_FS_ATTRIBUTE_INFORMATION*) bufFsAttr
|
||||
: NULL;
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY diskFileSystemBuf = ffStrbufCreate();
|
||||
|
||||
if (volumeInfoAvailable)
|
||||
if (fsAttr)
|
||||
{
|
||||
ffStrbufSetWS(&diskFileSystemBuf, diskFileSystem);
|
||||
ffStrbufSetNWS(&diskFileSystemBuf, fsAttr->FileSystemNameLength / sizeof(WCHAR), fsAttr->FileSystemName);
|
||||
if (options->hideFS.length && ffStrbufSeparatedContain(&options->hideFS, &diskFileSystemBuf, ':'))
|
||||
continue;
|
||||
}
|
||||
@@ -87,7 +77,7 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
disk->createTime = 0;
|
||||
ffStrbufInit(&disk->filesystem);
|
||||
ffStrbufInit(&disk->name);
|
||||
ffStrbufInitMove(&disk->mountpoint, &buffer);
|
||||
ffStrbufInitNS(&disk->mountpoint, 3, mountpointA);
|
||||
ffStrbufInit(&disk->mountFrom);
|
||||
disk->type = driveType == DRIVE_REMOVABLE || driveType == DRIVE_REMOTE || driveType == DRIVE_CDROM
|
||||
? FF_DISK_VOLUME_TYPE_EXTERNAL_BIT
|
||||
@@ -95,42 +85,42 @@ const char* ffDetectDisksImpl(FFDiskOptions* options, FFlist* disks)
|
||||
? FF_DISK_VOLUME_TYPE_REGULAR_BIT
|
||||
: FF_DISK_VOLUME_TYPE_HIDDEN_BIT;
|
||||
|
||||
if (mountpoint[2] == L'\\' && mountpoint[3] == L'\0')
|
||||
{
|
||||
wchar_t volumeName[MAX_PATH + 1];
|
||||
mountpoint[2] = L'\0';
|
||||
if(QueryDosDeviceW(mountpoint, volumeName, ARRAY_SIZE(volumeName)))
|
||||
mountpointW[2] = L'\0';
|
||||
if(QueryDosDeviceW(mountpointW, volumeName, ARRAY_SIZE(volumeName)))
|
||||
ffStrbufSetWS(&disk->mountFrom, volumeName);
|
||||
mountpoint[2] = L'\\';
|
||||
mountpointW[2] = L'\\';
|
||||
}
|
||||
|
||||
#ifdef FF_HAVE_THREADS
|
||||
if (driveType == DRIVE_REMOTE)
|
||||
alignas(FILE_FS_VOLUME_INFORMATION) uint8_t bufFsVolume[1024];
|
||||
FILE_FS_VOLUME_INFORMATION* fsVolume = NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, bufFsVolume, sizeof(bufFsVolume), FileFsVolumeInformation))
|
||||
? (FILE_FS_VOLUME_INFORMATION*) bufFsVolume
|
||||
: NULL;
|
||||
|
||||
if (fsVolume)
|
||||
{
|
||||
FFThreadType thread = ffThreadCreate(testRemoteVolumeAccessible, mountpoint);
|
||||
if (!ffThreadJoin(thread, 500))
|
||||
continue;
|
||||
if (fsVolume->VolumeLabelLength > 0)
|
||||
ffStrbufSetNWS(&disk->name, fsVolume->VolumeLabelLength / sizeof(WCHAR), fsVolume->VolumeLabel);
|
||||
if (fsVolume->VolumeCreationTime.QuadPart)
|
||||
disk->createTime = ((uint64_t) fsVolume->VolumeCreationTime.QuadPart - 116444736000000000ull) / 10000ull;
|
||||
}
|
||||
#endif
|
||||
|
||||
GetDiskFreeSpaceExW(
|
||||
mountpoint,
|
||||
(PULARGE_INTEGER)&disk->bytesAvailable,
|
||||
(PULARGE_INTEGER)&disk->bytesTotal,
|
||||
(PULARGE_INTEGER)&disk->bytesFree
|
||||
);
|
||||
|
||||
if(volumeInfoAvailable)
|
||||
if (fsAttr)
|
||||
{
|
||||
ffStrbufInitMove(&disk->filesystem, &diskFileSystemBuf);
|
||||
ffStrbufSetWS(&disk->name, diskName);
|
||||
if(diskFlags & FILE_READ_ONLY_VOLUME)
|
||||
if(fsAttr->FileSystemAttributes & FILE_READ_ONLY_VOLUME)
|
||||
disk->type |= FF_DISK_VOLUME_TYPE_READONLY_BIT;
|
||||
}
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||
if(GetFileAttributesExW(mountpoint, GetFileExInfoStandard, &data) && data.ftCreationTime.dwHighDateTime > 0)
|
||||
disk->createTime = (*(uint64_t*) &data.ftCreationTime - 116444736000000000ull) / 10000ull;
|
||||
FILE_FS_FULL_SIZE_INFORMATION fsFullSize;
|
||||
if (NT_SUCCESS(NtQueryVolumeInformationFile(handle, &iosb, &fsFullSize, sizeof(fsFullSize), FileFsFullSizeInformation)))
|
||||
{
|
||||
uint64_t units = fsFullSize.BytesPerSector * fsFullSize.SectorsPerAllocationUnit;
|
||||
disk->bytesTotal = (uint64_t) fsFullSize.TotalAllocationUnits.QuadPart * units;
|
||||
disk->bytesFree = (uint64_t) fsFullSize.ActualAvailableAllocationUnits.QuadPart * units;
|
||||
disk->bytesAvailable = (uint64_t) fsFullSize.CallerAvailableAllocationUnits.QuadPart * units;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -11,6 +11,8 @@ void ffPrepareDiskIO(FFDiskIOOptions* options)
|
||||
{
|
||||
if (options->detectTotal) return;
|
||||
|
||||
if (time1 != 0) return; // Already prepared
|
||||
|
||||
ffListInit(&ioCounters1, sizeof(FFDiskIOResult));
|
||||
ffDiskIOGetIoCounters(&ioCounters1, options);
|
||||
time1 = ffTimeGetNow();
|
||||
|
||||
@@ -186,9 +186,12 @@ static bool detectDE(FFDisplayServerResult* ds)
|
||||
ffStrbufPrependS(&ds->dePrettyName, "OxygenOS");
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName) && ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS"))
|
||||
if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName))
|
||||
{
|
||||
ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' ');
|
||||
if (ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS"))
|
||||
ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' ');
|
||||
|
||||
// Google Pixel uses native Android
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ static const struct kde_output_device_mode_v2_listener modeListener = {
|
||||
.refresh = waylandKdeModeRefreshListener,
|
||||
.preferred = waylandKdeModePreferredListener,
|
||||
.removed = (void*) stubListener,
|
||||
.flags = (void*) stubListener,
|
||||
};
|
||||
|
||||
static void waylandKdeModeListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* _, struct kde_output_device_mode_v2 *mode)
|
||||
|
||||
+23
-13
@@ -1,4 +1,4 @@
|
||||
/* Generated by wayland-scanner 1.22.0 */
|
||||
/* Generated by wayland-scanner 1.24.0 */
|
||||
|
||||
#ifndef WLR_OUTPUT_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
#define WLR_OUTPUT_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
@@ -445,9 +445,8 @@ struct zwlr_output_head_v1_listener {
|
||||
* However, do not assume that the name is a reflection of an
|
||||
* underlying DRM connector, X11 connection, etc.
|
||||
*
|
||||
* If the compositor implements the xdg-output protocol and this
|
||||
* head is enabled, the xdg_output.name event must report the same
|
||||
* name.
|
||||
* If this head matches a wl_output, the wl_output.name event must
|
||||
* report the same name.
|
||||
*
|
||||
* The name event is sent after a wlr_output_head object is
|
||||
* created. This event is only sent once per object, and the name
|
||||
@@ -468,9 +467,8 @@ struct zwlr_output_head_v1_listener {
|
||||
* underlying DRM connector or the display name of the underlying
|
||||
* X11 connection, etc.
|
||||
*
|
||||
* If the compositor implements xdg-output and this head is
|
||||
* enabled, the xdg_output.description must report the same
|
||||
* description.
|
||||
* If this head matches a wl_output, the wl_output.description
|
||||
* event must report the same name.
|
||||
*
|
||||
* The description event is sent after a wlr_output_head object is
|
||||
* created. This event is only sent once per object, and the
|
||||
@@ -486,6 +484,11 @@ struct zwlr_output_head_v1_listener {
|
||||
* This event describes the physical size of the head. This event
|
||||
* is only sent if the head has a physical size (e.g. is not a
|
||||
* projector or a virtual device).
|
||||
*
|
||||
* The physical size event is sent after a wlr_output_head object
|
||||
* is created. This event is only sent once per object, and the
|
||||
* physical size does not change over the lifetime of the
|
||||
* wlr_output_head object.
|
||||
* @param width width in millimeters of the output
|
||||
* @param height height in millimeters of the output
|
||||
*/
|
||||
@@ -568,9 +571,6 @@ struct zwlr_output_head_v1_listener {
|
||||
*
|
||||
* This event describes the manufacturer of the head.
|
||||
*
|
||||
* This must report the same make as the wl_output interface does
|
||||
* in its geometry event.
|
||||
*
|
||||
* Together with the model and serial_number events the purpose is
|
||||
* to allow clients to recognize heads from previous sessions and
|
||||
* for example load head-specific configurations back.
|
||||
@@ -583,6 +583,10 @@ struct zwlr_output_head_v1_listener {
|
||||
* other events but should be aware that there is an increased risk
|
||||
* of false positives.
|
||||
*
|
||||
* If sent, the make event is sent after a wlr_output_head object
|
||||
* is created and only sent once per object. The make does not
|
||||
* change over the lifetime of the wlr_output_head object.
|
||||
*
|
||||
* It is not recommended to display the make string in UI to users.
|
||||
* For that the string provided by the description event should be
|
||||
* preferred.
|
||||
@@ -596,9 +600,6 @@ struct zwlr_output_head_v1_listener {
|
||||
*
|
||||
* This event describes the model of the head.
|
||||
*
|
||||
* This must report the same model as the wl_output interface does
|
||||
* in its geometry event.
|
||||
*
|
||||
* Together with the make and serial_number events the purpose is
|
||||
* to allow clients to recognize heads from previous sessions and
|
||||
* for example load head-specific configurations back.
|
||||
@@ -611,6 +612,10 @@ struct zwlr_output_head_v1_listener {
|
||||
* information from other events but should be aware that there is
|
||||
* an increased risk of false positives.
|
||||
*
|
||||
* If sent, the model event is sent after a wlr_output_head object
|
||||
* is created and only sent once per object. The model does not
|
||||
* change over the lifetime of the wlr_output_head object.
|
||||
*
|
||||
* It is not recommended to display the model string in UI to
|
||||
* users. For that the string provided by the description event
|
||||
* should be preferred.
|
||||
@@ -636,6 +641,11 @@ struct zwlr_output_head_v1_listener {
|
||||
* events but should be aware that there is an increased risk of
|
||||
* false positives.
|
||||
*
|
||||
* If sent, the serial number event is sent after a wlr_output_head
|
||||
* object is created and only sent once per object. The serial
|
||||
* number does not change over the lifetime of the wlr_output_head
|
||||
* object.
|
||||
*
|
||||
* It is not recommended to display the serial_number string in UI
|
||||
* to users. For that the string provided by the description event
|
||||
* should be preferred.
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
#ifdef FF_HAVE_WAYLAND
|
||||
|
||||
/* Generated by wayland-scanner 1.22.0 */
|
||||
/* Generated by wayland-scanner 1.24.0 */
|
||||
|
||||
/*
|
||||
* Copyright © 2019 Purism SPC
|
||||
@@ -27,6 +27,7 @@
|
||||
* THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
@@ -266,7 +266,7 @@ static void applyPrettyNameIfDE(FFDisplayServerResult* result, const char* name)
|
||||
|
||||
static const char* getFromProcesses(FFDisplayServerResult* result)
|
||||
{
|
||||
uint32_t userId = getuid();
|
||||
uint32_t userId = instance.state.platform.uid;
|
||||
|
||||
#if __FreeBSD__
|
||||
#ifdef __DragonFly__
|
||||
|
||||
@@ -13,103 +13,6 @@
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
typedef struct XcbPropertyData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(xcb_intern_atom)
|
||||
FF_LIBRARY_SYMBOL(xcb_intern_atom_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property_value)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property_value_length)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name_name)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name_name_length)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_setup)
|
||||
FF_LIBRARY_SYMBOL(xcb_setup_vendor)
|
||||
FF_LIBRARY_SYMBOL(xcb_setup_vendor_length)
|
||||
} XcbPropertyData;
|
||||
|
||||
static bool xcbInitPropertyData(FF_MAYBE_UNUSED void* libraryHandle, XcbPropertyData* propertyData)
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_intern_atom, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_intern_atom_reply, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_property, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_property_reply, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_property_value, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_property_value_length, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_atom_name, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_atom_name_name, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_atom_name_name_length, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_atom_name_reply, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_get_setup, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_setup_vendor, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, xcb_setup_vendor_length, false)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void* xcbGetProperty(XcbPropertyData* data, xcb_connection_t* connection, xcb_window_t window, const char* request)
|
||||
{
|
||||
xcb_intern_atom_cookie_t requestAtomCookie = data->ffxcb_intern_atom(connection, true, (uint16_t) strlen(request), request);
|
||||
FF_AUTO_FREE xcb_intern_atom_reply_t* requestAtomReply = data->ffxcb_intern_atom_reply(connection, requestAtomCookie, NULL);
|
||||
if(requestAtomReply == NULL)
|
||||
return NULL;
|
||||
|
||||
xcb_get_property_cookie_t propertyCookie = data->ffxcb_get_property(connection, false, window, requestAtomReply->atom, XCB_ATOM_ANY, 0, 8 * 1024);
|
||||
|
||||
FF_AUTO_FREE xcb_get_property_reply_t* propertyReply = data->ffxcb_get_property_reply(connection, propertyCookie, NULL);
|
||||
if(propertyReply == NULL)
|
||||
return NULL;
|
||||
|
||||
int length = data->ffxcb_get_property_value_length(propertyReply);
|
||||
if(length <= 0)
|
||||
return NULL;
|
||||
|
||||
//Why are xcb property strings not null terminated???
|
||||
void* replyValue = malloc((size_t)length + 1);
|
||||
memcpy(replyValue, data->ffxcb_get_property_value(propertyReply), (size_t) length);
|
||||
((char*) replyValue)[length] = '\0';
|
||||
|
||||
return replyValue;
|
||||
}
|
||||
|
||||
static void xcbDetectWMfromEWMH(XcbPropertyData* data, xcb_connection_t* connection, xcb_window_t rootWindow, FFDisplayServerResult* result)
|
||||
{
|
||||
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND) == 0)
|
||||
return;
|
||||
|
||||
FF_AUTO_FREE xcb_window_t* wmWindow = (xcb_window_t*) xcbGetProperty(data, connection, rootWindow, "_NET_SUPPORTING_WM_CHECK");
|
||||
if(wmWindow == NULL)
|
||||
return;
|
||||
|
||||
FF_AUTO_FREE char* wmName = (char*) xcbGetProperty(data, connection, *wmWindow, "WM_NAME");
|
||||
if(!ffStrSet(wmName))
|
||||
wmName = (char*) xcbGetProperty(data, connection, *wmWindow, "_NET_WM_NAME");
|
||||
|
||||
if(!ffStrSet(wmName))
|
||||
return;
|
||||
|
||||
ffStrbufSetS(&result->wmProcessName, wmName);
|
||||
}
|
||||
|
||||
static void xcbFetchServerVendor(XcbPropertyData* data, xcb_connection_t* connection, FFDisplayServerResult* result)
|
||||
{
|
||||
const xcb_setup_t* setup = data->ffxcb_get_setup(connection);
|
||||
|
||||
int length = data->ffxcb_setup_vendor_length(setup);
|
||||
if(length <= 0)
|
||||
return;
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY serverVendor = ffStrbufCreateNS((uint32_t) length, data->ffxcb_setup_vendor(setup));
|
||||
|
||||
if (!ffStrbufEqualS(&serverVendor, "The X.Org Foundation")) // Original
|
||||
{
|
||||
ffStrbufDestroy(&result->wmProtocolName);
|
||||
ffStrbufInitMove(&result->wmProtocolName, &serverVendor);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct XcbRandrData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources_current)
|
||||
@@ -131,15 +34,86 @@ typedef struct XcbRandrData
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_output_property_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_output_property_data)
|
||||
FF_LIBRARY_SYMBOL(xcb_randr_get_output_property_data_length)
|
||||
|
||||
FF_LIBRARY_SYMBOL(xcb_intern_atom)
|
||||
FF_LIBRARY_SYMBOL(xcb_intern_atom_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property_value)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_property_value_length)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name_name)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name_name_length)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_atom_name_reply)
|
||||
FF_LIBRARY_SYMBOL(xcb_get_setup)
|
||||
FF_LIBRARY_SYMBOL(xcb_setup_vendor)
|
||||
FF_LIBRARY_SYMBOL(xcb_setup_vendor_length)
|
||||
|
||||
//init once
|
||||
xcb_connection_t* connection;
|
||||
FFDisplayServerResult* result;
|
||||
XcbPropertyData propData;
|
||||
} XcbRandrData;
|
||||
|
||||
static void* xcbGetProperty(XcbRandrData* data, xcb_window_t window, const char* request)
|
||||
{
|
||||
xcb_intern_atom_cookie_t requestAtomCookie = data->ffxcb_intern_atom(data->connection, true, (uint16_t) strlen(request), request);
|
||||
FF_AUTO_FREE xcb_intern_atom_reply_t* requestAtomReply = data->ffxcb_intern_atom_reply(data->connection, requestAtomCookie, NULL);
|
||||
if(requestAtomReply == NULL)
|
||||
return NULL;
|
||||
|
||||
xcb_get_property_cookie_t propertyCookie = data->ffxcb_get_property(data->connection, false, window, requestAtomReply->atom, XCB_ATOM_ANY, 0, 8 * 1024);
|
||||
FF_AUTO_FREE xcb_get_property_reply_t* propertyReply = data->ffxcb_get_property_reply(data->connection, propertyCookie, NULL);
|
||||
if(propertyReply == NULL)
|
||||
return NULL;
|
||||
|
||||
int length = data->ffxcb_get_property_value_length(propertyReply);
|
||||
if(length <= 0)
|
||||
return NULL;
|
||||
|
||||
//Why are xcb property strings not null terminated???
|
||||
void* replyValue = malloc((size_t)length + 1);
|
||||
memcpy(replyValue, data->ffxcb_get_property_value(propertyReply), (size_t) length);
|
||||
((char*) replyValue)[length] = '\0';
|
||||
|
||||
return replyValue;
|
||||
}
|
||||
|
||||
static void xcbDetectWMfromEWMH(XcbRandrData* data, xcb_window_t rootWindow, FFDisplayServerResult* result)
|
||||
{
|
||||
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND) == 0)
|
||||
return;
|
||||
|
||||
FF_AUTO_FREE xcb_window_t* wmWindow = (xcb_window_t*) xcbGetProperty(data, rootWindow, "_NET_SUPPORTING_WM_CHECK");
|
||||
if(wmWindow == NULL)
|
||||
return;
|
||||
|
||||
FF_AUTO_FREE char* wmName = (char*) xcbGetProperty(data, *wmWindow, "WM_NAME");
|
||||
if(!ffStrSet(wmName))
|
||||
wmName = (char*) xcbGetProperty(data, *wmWindow, "_NET_WM_NAME");
|
||||
|
||||
if(!ffStrSet(wmName))
|
||||
return;
|
||||
|
||||
ffStrbufSetS(&result->wmProcessName, wmName);
|
||||
}
|
||||
|
||||
static void xcbFetchServerVendor(XcbRandrData* data, FFDisplayServerResult* result)
|
||||
{
|
||||
const xcb_setup_t* setup = data->ffxcb_get_setup(data->connection);
|
||||
|
||||
int length = data->ffxcb_setup_vendor_length(setup);
|
||||
if(length <= 0)
|
||||
return;
|
||||
|
||||
FF_STRBUF_AUTO_DESTROY serverVendor = ffStrbufCreateNS((uint32_t) length, data->ffxcb_setup_vendor(setup));
|
||||
|
||||
if (!ffStrbufEqualS(&serverVendor, "The X.Org Foundation")) // Original
|
||||
{
|
||||
ffStrbufDestroy(&result->wmProtocolName);
|
||||
ffStrbufInitMove(&result->wmProtocolName, &serverVendor);
|
||||
}
|
||||
}
|
||||
|
||||
static bool xcbRandrHandleOutput(XcbRandrData* data, xcb_randr_output_t output, FFstrbuf* name, bool primary, FFDisplayType displayType, struct xcb_randr_get_screen_resources_current_reply_t* screenResources, uint8_t bitDepth, double scaleFactor)
|
||||
{
|
||||
xcb_randr_get_output_info_cookie_t outputInfoCookie = data->ffxcb_randr_get_output_info(data->connection, output, XCB_CURRENT_TIME);
|
||||
@@ -251,14 +225,14 @@ static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t*
|
||||
.rem = data->ffxcb_randr_monitor_info_outputs_length(monitor)
|
||||
};
|
||||
|
||||
FF_AUTO_FREE xcb_get_atom_name_reply_t* nameReply = data->propData.ffxcb_get_atom_name_reply(
|
||||
FF_AUTO_FREE xcb_get_atom_name_reply_t* nameReply = data->ffxcb_get_atom_name_reply(
|
||||
data->connection,
|
||||
data->propData.ffxcb_get_atom_name(data->connection, monitor->name),
|
||||
data->ffxcb_get_atom_name(data->connection, monitor->name),
|
||||
NULL
|
||||
);
|
||||
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateNS(
|
||||
(uint32_t) data->propData.ffxcb_get_atom_name_name_length(nameReply),
|
||||
data->propData.ffxcb_get_atom_name_name(nameReply)
|
||||
(uint32_t) data->ffxcb_get_atom_name_name_length(nameReply),
|
||||
data->ffxcb_get_atom_name_name(nameReply)
|
||||
);
|
||||
const FFDisplayType displayType = ffdsGetDisplayType(name.chars);
|
||||
|
||||
@@ -306,7 +280,7 @@ static bool xcbRandrHandleMonitors(XcbRandrData* data, xcb_screen_t* screen)
|
||||
FF_AUTO_FREE struct xcb_randr_get_screen_resources_current_reply_t* screenResources = data->ffxcb_randr_get_screen_resources_current_reply(data->connection, screenResourcesCookie, NULL);
|
||||
|
||||
double scaleFactor = 1;
|
||||
FF_AUTO_FREE const char* resourceManager = xcbGetProperty(&data->propData, data->connection, screen->root, "RESOURCE_MANAGER");
|
||||
FF_AUTO_FREE const char* resourceManager = xcbGetProperty(data, screen->root, "RESOURCE_MANAGER");
|
||||
if (resourceManager)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY dpi = ffStrbufCreate();
|
||||
@@ -369,6 +343,18 @@ const char* ffdsConnectXcbRandr(FFDisplayServerResult* result)
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_intern_atom)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_intern_atom_reply)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_get_property)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_get_property_reply)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_get_property_value)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_get_property_value_length)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_get_atom_name)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_get_atom_name_name)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_get_atom_name_name_length)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_get_atom_name_reply)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_get_setup)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_setup_vendor)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_setup_vendor_length)
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_randr_get_screen_resources_current)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_randr_get_screen_resources_current_reply)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_randr_get_screen_resources_current_modes_iterator)
|
||||
@@ -389,8 +375,6 @@ const char* ffdsConnectXcbRandr(FFDisplayServerResult* result)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_randr_get_crtc_info)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xcbRandr, data, xcb_randr_get_crtc_info_reply)
|
||||
|
||||
bool propertyDataInitialized = xcbInitPropertyData(xcbRandr, &data.propData);
|
||||
|
||||
|
||||
data.connection = ffxcb_connect(NULL, NULL);
|
||||
if(ffxcb_connection_has_error(data.connection) > 0)
|
||||
@@ -405,9 +389,9 @@ const char* ffdsConnectXcbRandr(FFDisplayServerResult* result)
|
||||
xcb_screen_iterator_t iterator = ffxcb_setup_roots_iterator(ffxcb_get_setup(data.connection));
|
||||
|
||||
|
||||
if(iterator.rem > 0 && propertyDataInitialized) {
|
||||
xcbDetectWMfromEWMH(&data.propData, data.connection, iterator.data->root, result);
|
||||
xcbFetchServerVendor(&data.propData, data.connection, result);
|
||||
if(iterator.rem > 0) {
|
||||
xcbDetectWMfromEWMH(&data, iterator.data->root, result);
|
||||
xcbFetchServerVendor(&data, result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,25 +10,29 @@
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
typedef struct X11PropertyData
|
||||
typedef struct XrandrData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(XInternAtom)
|
||||
FF_LIBRARY_SYMBOL(XGetAtomName)
|
||||
FF_LIBRARY_SYMBOL(XGetWindowProperty)
|
||||
FF_LIBRARY_SYMBOL(XServerVendor)
|
||||
FF_LIBRARY_SYMBOL(XFree)
|
||||
} X11PropertyData;
|
||||
FF_LIBRARY_SYMBOL(XRRGetMonitors)
|
||||
FF_LIBRARY_SYMBOL(XRRGetScreenResourcesCurrent)
|
||||
FF_LIBRARY_SYMBOL(XRRGetOutputInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRGetOutputProperty)
|
||||
FF_LIBRARY_SYMBOL(XRRGetCrtcInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeCrtcInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeOutputInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeScreenResources)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeMonitors)
|
||||
|
||||
static bool x11InitPropertyData(FF_MAYBE_UNUSED void* libraryHandle, X11PropertyData* propertyData)
|
||||
{
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XInternAtom, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XGetWindowProperty, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XServerVendor, false)
|
||||
FF_LIBRARY_LOAD_SYMBOL_PTR(libraryHandle, propertyData, XFree, false)
|
||||
//Init once
|
||||
Display* display;
|
||||
FFDisplayServerResult* result;
|
||||
} XrandrData;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned char* x11GetProperty(X11PropertyData* data, Display* display, Window window, const char* request)
|
||||
static unsigned char* x11GetProperty(XrandrData* data, Display* display, Window window, const char* request)
|
||||
{
|
||||
Atom requestAtom = data->ffXInternAtom(display, request, False);
|
||||
if(requestAtom == None)
|
||||
@@ -44,18 +48,18 @@ static unsigned char* x11GetProperty(X11PropertyData* data, Display* display, Wi
|
||||
return result;
|
||||
}
|
||||
|
||||
static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDisplayServerResult* result)
|
||||
static void x11DetectWMFromEWMH(XrandrData* data, FFDisplayServerResult* result)
|
||||
{
|
||||
if(result->wmProcessName.length > 0 || ffStrbufCompS(&result->wmProtocolName, FF_WM_PROTOCOL_WAYLAND) == 0)
|
||||
return;
|
||||
|
||||
Window* wmWindow = (Window*) x11GetProperty(data, display, DefaultRootWindow(display), "_NET_SUPPORTING_WM_CHECK");
|
||||
Window* wmWindow = (Window*) x11GetProperty(data, data->display, DefaultRootWindow(data->display), "_NET_SUPPORTING_WM_CHECK");
|
||||
if(wmWindow == NULL)
|
||||
return;
|
||||
|
||||
char* wmName = (char*) x11GetProperty(data, display, *wmWindow, "WM_NAME");
|
||||
char* wmName = (char*) x11GetProperty(data, data->display, *wmWindow, "WM_NAME");
|
||||
if(!ffStrSet(wmName))
|
||||
wmName = (char*) x11GetProperty(data, display, *wmWindow, "_NET_WM_NAME");
|
||||
wmName = (char*) x11GetProperty(data, data->display, *wmWindow, "_NET_WM_NAME");
|
||||
|
||||
if(ffStrSet(wmName))
|
||||
ffStrbufSetS(&result->wmProcessName, wmName);
|
||||
@@ -64,35 +68,13 @@ static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDispl
|
||||
data->ffXFree(wmWindow);
|
||||
}
|
||||
|
||||
static void x11FetchServerVendor(X11PropertyData* data, Display* display, FFDisplayServerResult* result)
|
||||
static void x11FetchServerVendor(XrandrData* data, FFDisplayServerResult* result)
|
||||
{
|
||||
const char* serverVendor = data->ffXServerVendor(display);
|
||||
if (serverVendor && !ffStrEquals(serverVendor, "The X.Org Foundation")) {
|
||||
const char* serverVendor = data->ffXServerVendor(data->display);
|
||||
if (serverVendor && !ffStrEquals(serverVendor, "The X.Org Foundation"))
|
||||
ffStrbufSetS(&result->wmProtocolName, serverVendor);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct XrandrData
|
||||
{
|
||||
FF_LIBRARY_SYMBOL(XInternAtom)
|
||||
FF_LIBRARY_SYMBOL(XGetAtomName);
|
||||
FF_LIBRARY_SYMBOL(XFree);
|
||||
FF_LIBRARY_SYMBOL(XRRGetMonitors)
|
||||
FF_LIBRARY_SYMBOL(XRRGetScreenResourcesCurrent)
|
||||
FF_LIBRARY_SYMBOL(XRRGetOutputInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRGetOutputProperty)
|
||||
FF_LIBRARY_SYMBOL(XRRGetCrtcInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeCrtcInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeOutputInfo)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeScreenResources)
|
||||
FF_LIBRARY_SYMBOL(XRRFreeMonitors)
|
||||
|
||||
//Init once
|
||||
Display* display;
|
||||
FFDisplayServerResult* result;
|
||||
X11PropertyData* propData;
|
||||
} XrandrData;
|
||||
|
||||
static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf* name, bool primary, FFDisplayType displayType, uint8_t* edidData, uint32_t edidLength, XRRScreenResources* screenResources, uint8_t bitDepth, double scaleFactor)
|
||||
{
|
||||
//We do the check here, because we want the best fallback display if this call failed
|
||||
@@ -247,7 +229,7 @@ static bool xrandrHandleMonitors(XrandrData* data, Screen* screen)
|
||||
XRRScreenResources* screenResources = data->ffXRRGetScreenResourcesCurrent(data->display, RootWindowOfScreen(screen));
|
||||
|
||||
double scaleFactor = 1;
|
||||
char* resourceManager = (char*) x11GetProperty(data->propData, data->display, screen->root, "RESOURCE_MANAGER");
|
||||
char* resourceManager = (char*) x11GetProperty(data, data->display, screen->root, "RESOURCE_MANAGER");
|
||||
if (resourceManager)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY dpi = ffStrbufCreate();
|
||||
@@ -307,6 +289,8 @@ const char* ffdsConnectXrandr(FFDisplayServerResult* result)
|
||||
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XInternAtom);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XGetAtomName);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XGetWindowProperty);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XServerVendor);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XFree);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRGetMonitors);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRGetScreenResourcesCurrent);
|
||||
@@ -318,17 +302,13 @@ const char* ffdsConnectXrandr(FFDisplayServerResult* result)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRFreeScreenResources);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(xrandr, data, XRRFreeMonitors);
|
||||
|
||||
X11PropertyData propertyData;
|
||||
bool propertyDataInitialized = x11InitPropertyData(xrandr, &propertyData);
|
||||
data.propData = &propertyData;
|
||||
|
||||
data.display = ffXOpenDisplay(NULL);
|
||||
if(data.display == NULL)
|
||||
return "XOpenDisplay() failed";
|
||||
|
||||
if(propertyDataInitialized && ScreenCount(data.display) > 0) {
|
||||
x11DetectWMFromEWMH(&propertyData, data.display, result);
|
||||
x11FetchServerVendor(&propertyData, data.display, result);
|
||||
if(ScreenCount(data.display) > 0) {
|
||||
x11DetectWMFromEWMH(&data, result);
|
||||
x11FetchServerVendor(&data, result);
|
||||
}
|
||||
|
||||
data.result = result;
|
||||
|
||||
+50
-2
@@ -1,4 +1,5 @@
|
||||
#include "gpu.h"
|
||||
#include "common/debug.h"
|
||||
#include "detection/vulkan/vulkan.h"
|
||||
#include "detection/opencl/opencl.h"
|
||||
#include "detection/opengl/opengl.h"
|
||||
@@ -50,6 +51,8 @@ const char* ffGPUGetVendorString(unsigned vendorId)
|
||||
|
||||
const char* detectByOpenGL(FFlist* gpus)
|
||||
{
|
||||
FF_DEBUG("Starting OpenGL GPU detection fallback");
|
||||
|
||||
FFOpenGLResult result;
|
||||
ffStrbufInit(&result.version);
|
||||
ffStrbufInit(&result.renderer);
|
||||
@@ -60,6 +63,7 @@ const char* detectByOpenGL(FFlist* gpus)
|
||||
__attribute__((__cleanup__(ffDestroyOpenGLOptions))) FFOpenGLOptions options;
|
||||
ffInitOpenGLOptions(&options);
|
||||
const char* error = ffDetectOpenGL(&options, &result);
|
||||
FF_DEBUG("OpenGL detection returns: %s", error ?: "success");
|
||||
|
||||
if (!error)
|
||||
{
|
||||
@@ -78,6 +82,11 @@ const char* detectByOpenGL(FFlist* gpus)
|
||||
gpu->dedicated = gpu->shared = (FFGPUMemory){0, 0};
|
||||
gpu->deviceId = 0;
|
||||
|
||||
FF_DEBUG("OpenGL reported renderer='%s', vendor='%s', version='%s'",
|
||||
gpu->name.chars,
|
||||
gpu->vendor.chars,
|
||||
result.version.chars);
|
||||
|
||||
if (ffStrbufContainS(&gpu->name, "Apple"))
|
||||
{
|
||||
ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE);
|
||||
@@ -92,6 +101,11 @@ const char* detectByOpenGL(FFlist* gpus)
|
||||
else if (ffStrbufContainS(&gpu->name, "MTT"))
|
||||
ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_MTHREADS);
|
||||
|
||||
FF_DEBUG("OpenGL fallback produced GPU: name='%s', vendor='%s', type=%u",
|
||||
gpu->name.chars,
|
||||
gpu->vendor.chars,
|
||||
gpu->type);
|
||||
|
||||
}
|
||||
|
||||
ffStrbufDestroy(&result.version);
|
||||
@@ -104,43 +118,77 @@ const char* detectByOpenGL(FFlist* gpus)
|
||||
|
||||
const char* ffDetectGPU(const FFGPUOptions* options, FFlist* result)
|
||||
{
|
||||
FF_DEBUG("Starting GPU detection with method=%d", (int) options->detectionMethod);
|
||||
|
||||
if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_PCI)
|
||||
{
|
||||
FF_DEBUG("Trying PCI/native GPU detection");
|
||||
const char* error = ffDetectGPUImpl(options, result);
|
||||
if (!error && result->length > 0) return NULL;
|
||||
if (!error && result->length > 0)
|
||||
{
|
||||
FF_DEBUG("PCI/native GPU detection succeeded with %u GPU(s)", result->length);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG("PCI/native GPU detection did not produce results (error=%s, gpuCount=%u)",
|
||||
error ?: "none",
|
||||
result->length);
|
||||
}
|
||||
if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_VULKAN)
|
||||
{
|
||||
FF_DEBUG("Trying Vulkan GPU detection fallback");
|
||||
FFVulkanResult* vulkan = ffDetectVulkan();
|
||||
if (!vulkan->error && vulkan->gpus.length > 0)
|
||||
{
|
||||
FF_DEBUG("Vulkan detection succeeded with %u GPU(s)", vulkan->gpus.length);
|
||||
ffListDestroy(result);
|
||||
ffListInitMove(result, &vulkan->gpus);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
double ffGPUDetectTempFromTZ(void);
|
||||
if (options->temp && result->length == 1)
|
||||
{
|
||||
FF_DEBUG("Applying Android thermal-zone temperature to single Vulkan GPU");
|
||||
FF_LIST_GET(FFGPUResult, *result, 0)->temperature = ffGPUDetectTempFromTZ();
|
||||
}
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG("Vulkan detection did not produce results (error=%s, gpuCount=%u)",
|
||||
vulkan->error ?: "none",
|
||||
vulkan->gpus.length);
|
||||
}
|
||||
if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_OPENCL)
|
||||
{
|
||||
FF_DEBUG("Trying OpenCL GPU detection fallback");
|
||||
FFOpenCLResult* opencl = ffDetectOpenCL();
|
||||
if (!opencl->error && opencl->gpus.length > 0)
|
||||
{
|
||||
FF_DEBUG("OpenCL detection succeeded with %u GPU(s)", opencl->gpus.length);
|
||||
ffListDestroy(result);
|
||||
ffListInitMove(result, &opencl->gpus);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG("OpenCL detection did not produce results (error=%s, gpuCount=%u)",
|
||||
opencl->error ?: "none",
|
||||
opencl->gpus.length);
|
||||
}
|
||||
if (options->detectionMethod <= FF_GPU_DETECTION_METHOD_OPENGL)
|
||||
{
|
||||
if (detectByOpenGL(result) == NULL)
|
||||
FF_DEBUG("Trying OpenGL GPU detection fallback");
|
||||
const char* error = detectByOpenGL(result);
|
||||
if (error == NULL)
|
||||
{
|
||||
FF_DEBUG("OpenGL fallback succeeded with %u GPU(s)", result->length);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FF_DEBUG("OpenGL fallback failed: %s", error);
|
||||
}
|
||||
|
||||
FF_DEBUG("GPU detection failed in all enabled backends");
|
||||
return "GPU detection failed";
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#include "gpu.h"
|
||||
|
||||
#ifdef FF_HAVE_PCIACCESS
|
||||
|
||||
#include "common/io.h"
|
||||
#include "common/library.h"
|
||||
|
||||
#include <pciaccess.h>
|
||||
|
||||
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
FF_LIBRARY_LOAD_MESSAGE(pciaccess, "libpciaccess" FF_LIBRARY_EXTENSION, 0)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(pciaccess, pci_system_init)
|
||||
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
|
||||
// Same behavior can be observed with `cp $(which scanpci) /tmp/ && /tmp/scanpci`
|
||||
FF_SUPPRESS_IO();
|
||||
if (ffpci_system_init() < 0)
|
||||
return "pci_system_init() failed";
|
||||
}
|
||||
|
||||
struct pci_device_iterator* iter = ffpci_slot_match_iterator_create(NULL);
|
||||
for (struct pci_device* dev = NULL; (dev = ffpci_device_next(iter)); )
|
||||
{
|
||||
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);
|
||||
ffStrbufInit(&gpu->driver);
|
||||
ffStrbufInitStatic(&gpu->platformApi, "libpciaccess");
|
||||
ffStrbufInit(&gpu->memoryType);
|
||||
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(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(subclass, dev->vendor_id, dev->device_id, gpu);
|
||||
}
|
||||
ffpci_iterator_destroy(iter);
|
||||
|
||||
ffpci_system_cleanup();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FF_MAYBE_UNUSED FFlist* gpus)
|
||||
{
|
||||
return "Fastfetch was built without libpciaccess support";
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "gpu.h"
|
||||
#include "common/io.h"
|
||||
|
||||
#include <hurd.h>
|
||||
#include <hurd/pci.h>
|
||||
#include <hurd/paths.h>
|
||||
|
||||
enum {
|
||||
PCI_VENDOR_ID = 0x00,
|
||||
PCI_DEVICE_ID = 0x02,
|
||||
PCI_REVISION_ID = 0x08,
|
||||
PCI_CLASS_PROG = 0x09,
|
||||
PCI_SUBCLASS = 0x0a,
|
||||
PCI_CLASS_DEVICE = 0x0b,
|
||||
PCI_CONF_SIZE = 0x40,
|
||||
};
|
||||
|
||||
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
int dDomainFd = open(_SERVERS_BUS "/pci/0000", O_RDONLY | O_CLOEXEC);
|
||||
if (dDomainFd < 0) return "open(_SERVERS_BUS \"/pci/0000\") failed";
|
||||
|
||||
FF_AUTO_CLOSE_DIR DIR* dirDomain = fdopendir(dDomainFd);
|
||||
if (dirDomain == NULL) return "fdopendir(domain) failed";
|
||||
|
||||
struct dirent* busEntry;
|
||||
while ((busEntry = readdir(dirDomain)) != NULL)
|
||||
{
|
||||
if (busEntry->d_type != DT_DIR || busEntry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
char* endptr;
|
||||
uint16_t pciBus = (uint16_t) strtoul(busEntry->d_name, &endptr, 16);
|
||||
if (*endptr != '\0') continue;
|
||||
|
||||
int dBusFd = openat(dDomainFd, busEntry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
if (dBusFd < 0) continue;
|
||||
|
||||
FF_AUTO_CLOSE_DIR DIR* dirBus = fdopendir(dBusFd);
|
||||
if (dirBus == NULL) continue;
|
||||
|
||||
struct dirent* devEntry;
|
||||
while ((devEntry = readdir(dirBus)) != NULL)
|
||||
{
|
||||
if (devEntry->d_type != DT_DIR || devEntry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
uint8_t pciDev = (uint8_t) strtoul(devEntry->d_name, &endptr, 16);
|
||||
if (*endptr != '\0') continue;
|
||||
|
||||
int dDevFd = openat(dBusFd, devEntry->d_name, O_RDONLY | O_CLOEXEC);
|
||||
if (dDevFd < 0) continue;
|
||||
|
||||
FF_AUTO_CLOSE_DIR DIR* dirDev = fdopendir(dDevFd);
|
||||
if (dirDev == NULL) continue;
|
||||
|
||||
struct dirent* funcEntry;
|
||||
while ((funcEntry = readdir(dirDev)) != NULL)
|
||||
{
|
||||
if (funcEntry->d_type != DT_DIR || funcEntry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
uint8_t pciFunc = (uint8_t) strtoul(funcEntry->d_name, &endptr, 16);
|
||||
if (*endptr != '\0') continue;
|
||||
|
||||
char subpath[PATH_MAX];
|
||||
snprintf(subpath, ARRAY_SIZE(subpath), "%s/%s/%s/%s/config", _SERVERS_BUS "/pci/0000", busEntry->d_name, devEntry->d_name, funcEntry->d_name);
|
||||
|
||||
mach_port_t devicePort = file_name_lookup(subpath, 0, 0);
|
||||
if (devicePort == MACH_PORT_NULL)
|
||||
continue;
|
||||
|
||||
mach_msg_type_number_t nread = 0;
|
||||
|
||||
uint8_t data[PCI_CONF_SIZE];
|
||||
data_t pData = (data_t) data;
|
||||
kern_return_t kr = pci_conf_read(devicePort, 0, &pData, &nread, PCI_CONF_SIZE);
|
||||
mach_port_deallocate(mach_task_self(), devicePort);
|
||||
if (kr != KERN_SUCCESS || nread < PCI_CONF_SIZE) continue;
|
||||
|
||||
if (pData != (data_t) data)
|
||||
{
|
||||
memcpy(data, pData, PCI_CONF_SIZE);
|
||||
vm_deallocate(mach_task_self(), (vm_address_t)pData, nread);
|
||||
}
|
||||
|
||||
uint8_t classBase = data[PCI_CLASS_DEVICE];
|
||||
if (classBase != 0x03 /*PCI_BASE_CLASS_DISPLAY*/)
|
||||
continue;
|
||||
|
||||
uint8_t classSub = data[PCI_SUBCLASS];
|
||||
if (pciFunc > 0 && classSub == 0x80 /*PCI_CLASS_DISPLAY_OTHER*/) // Likely an auxiliary display controller (#2034)
|
||||
continue;
|
||||
|
||||
uint8_t revision = data[PCI_REVISION_ID];
|
||||
uint16_t vendorId = data[PCI_VENDOR_ID] | (data[PCI_VENDOR_ID + 1] << 8);
|
||||
uint16_t deviceId = data[PCI_DEVICE_ID] | (data[PCI_DEVICE_ID + 1] << 8);
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
|
||||
ffStrbufInitStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
|
||||
ffStrbufInit(&gpu->name);
|
||||
ffStrbufInit(&gpu->driver);
|
||||
ffStrbufInitStatic(&gpu->platformApi, "/servers/bus/pci");
|
||||
ffStrbufInit(&gpu->memoryType);
|
||||
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, pciBus, pciDev, pciFunc);
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
|
||||
if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD)
|
||||
ffGPUQueryAmdGpuName(deviceId, revision, gpu);
|
||||
|
||||
if (gpu->name.length == 0)
|
||||
ffGPUFillVendorAndName(classSub, vendorId, deviceId, gpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -560,7 +560,7 @@ static const char* detectOf(FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir, co
|
||||
|
||||
if (!gpu->name.length)
|
||||
{
|
||||
ffStrbufSetS(&gpu->name, name ? name : compatible);
|
||||
ffStrbufSetS(&gpu->name, name ?: compatible);
|
||||
ffStrbufTrimRightSpace(&gpu->name);
|
||||
}
|
||||
if (!gpu->vendor.length && name)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "gpu.h"
|
||||
#include "common/io.h"
|
||||
#include "common/properties.h"
|
||||
#include "common/memrchr.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
@@ -54,6 +54,12 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
}
|
||||
FF_DEBUG("Device instance ID: %lu", devInst);
|
||||
|
||||
for (wchar_t* p = devId; *p; p++)
|
||||
{
|
||||
if (*p >= L'a' && *p <= L'z')
|
||||
*p -= L'a' - L'A';
|
||||
}
|
||||
|
||||
FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus);
|
||||
deviceCount++;
|
||||
FF_DEBUG("Added GPU #%d to list", deviceCount);
|
||||
@@ -73,10 +79,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
|
||||
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
|
||||
|
||||
unsigned vendorId = 0, deviceId = 0, subSystemId = 0, revId = 0;
|
||||
if (
|
||||
(*devId == 'P' && swscanf(devId, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId) == 4) ||
|
||||
(*devId == 'p' && swscanf(devId, L"pci\\ven_%x&dev_%x&subsys_%x&rev_%x", &vendorId, &deviceId, &subSystemId, &revId) == 4) // Windows 7
|
||||
)
|
||||
if (swscanf(devId, L"PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x", &vendorId, &deviceId, &subSystemId, &revId) == 4)
|
||||
{
|
||||
FF_DEBUG("Parsed PCI IDs - Vendor: 0x%x, Device: 0x%x, SubSystem: 0x%x, Rev: 0x%x", vendorId, deviceId, subSystemId, revId);
|
||||
ffStrbufSetStatic(&gpu->vendor, ffGPUGetVendorString(vendorId));
|
||||
|
||||
@@ -6,6 +6,7 @@ extern "C" {
|
||||
#include "detection/gpu/gpu.h"
|
||||
#include "detection/gpu/gpu_driver_specific.h"
|
||||
}
|
||||
#include "common/windows/util.hpp"
|
||||
|
||||
#include <wsl/winadapter.h>
|
||||
#include <directx/dxcore.h>
|
||||
@@ -16,15 +17,6 @@ extern "C" {
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
|
||||
template <typename Fn>
|
||||
struct on_scope_exit {
|
||||
on_scope_exit(Fn &&fn): _fn(std::move(fn)) {}
|
||||
~on_scope_exit() { this->_fn(); }
|
||||
|
||||
private:
|
||||
Fn _fn;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
|
||||
{
|
||||
|
||||
@@ -9,18 +9,14 @@ static void getHostProductName(FFstrbuf* name)
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/model", name))
|
||||
{
|
||||
ffStrbufTrimRightSpace(name);
|
||||
ffStrbufTrimRight(name, '\0');
|
||||
if(ffIsSmbiosValueSet(name))
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/banner-name", name))
|
||||
{
|
||||
ffStrbufTrimRightSpace(name);
|
||||
ffStrbufTrimRight(name, '\0');
|
||||
if(ffIsSmbiosValueSet(name))
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ffReadFileBuffer("/tmp/sysinfo/model", name))
|
||||
@@ -36,20 +32,19 @@ static void getHostProductName(FFstrbuf* name)
|
||||
|
||||
static void getHostSerialNumber(FFstrbuf* serial)
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/serial-number", serial))
|
||||
{
|
||||
ffStrbufTrimRightSpace(serial);
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/serial", serial))
|
||||
ffStrbufTrimRight(serial, '\0');
|
||||
else if (ffReadFileBuffer("/sys/firmware/devicetree/base/serial-number", serial))
|
||||
ffStrbufTrimRight(serial, '\0');
|
||||
if(ffIsSmbiosValueSet(serial))
|
||||
return;
|
||||
}
|
||||
|
||||
ffStrbufClear(serial);
|
||||
}
|
||||
|
||||
const char* ffDetectHost(FFHostResult* host)
|
||||
{
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->family);
|
||||
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->family))
|
||||
{
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/product", &host->family))
|
||||
ffStrbufTrimRight(&host->family, '\0');
|
||||
}
|
||||
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", &host->name))
|
||||
getHostProductName(&host->name);
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_version", "/sys/class/dmi/id/product_version", &host->version);
|
||||
@@ -59,8 +54,8 @@ const char* ffDetectHost(FFHostResult* host)
|
||||
ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_uuid", "/sys/class/dmi/id/product_uuid", &host->uuid);
|
||||
if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/sys_vendor", "/sys/class/dmi/id/sys_vendor", &host->vendor))
|
||||
{
|
||||
if (ffStrbufStartsWithS(&host->name, "Apple "))
|
||||
ffStrbufSetStatic(&host->vendor, "Apple Inc.");
|
||||
if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/manufacturer", &host->vendor))
|
||||
ffStrbufTrimRight(&host->vendor, '\0');
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
@@ -142,7 +142,7 @@ const char* ffDetectLM(FFLMResult* result)
|
||||
{
|
||||
// On some incorrectly configured systems, $XDG_SESSION_ID is not set. Try finding it ourself
|
||||
// WARNING: This is private data. Do not parse
|
||||
ffStrbufSetF(&path, FF_SYSTEMD_USERS_PATH "%d", getuid());
|
||||
ffStrbufSetF(&path, FF_SYSTEMD_USERS_PATH "%d", instance.state.platform.uid);
|
||||
|
||||
// This is actually buggy, and assumes current user is using DE
|
||||
// `sd_pid_get_session` can be a better option, but we need to find a pid to use
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/io.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/memrchr.h"
|
||||
#include "detection/media/media.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "memory.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <mach/mach.h>
|
||||
@@ -8,25 +9,25 @@
|
||||
const char* ffDetectMemory(FFMemoryResult* ram)
|
||||
{
|
||||
size_t length = sizeof(ram->bytesTotal);
|
||||
|
||||
#if FF_APPLE_MEMSIZE_USABLE
|
||||
if (sysctlbyname("hw.memsize_usable", &ram->bytesTotal, &length, NULL, 0) != 0)
|
||||
return "Failed to read hw.memsize_usable";
|
||||
#else
|
||||
if (sysctl((int[]){ CTL_HW, HW_MEMSIZE }, 2, &ram->bytesTotal, &length, NULL, 0) != 0)
|
||||
return "Failed to read hw.memsize";
|
||||
uint64_t usableMemory = 0;
|
||||
length = sizeof(usableMemory);
|
||||
if (sysctlbyname("hw.memsize_usable", &usableMemory, &length, NULL, 0) != 0)
|
||||
usableMemory = ram->bytesTotal;
|
||||
#endif
|
||||
|
||||
mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
|
||||
vm_statistics64_data_t vmstat;
|
||||
if(host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t) (&vmstat), &count) != KERN_SUCCESS)
|
||||
return "Failed to read host_statistics64";
|
||||
|
||||
uint64_t pageSize = instance.state.platform.sysinfo.pageSize;
|
||||
uint64_t appMemory = vmstat.internal_page_count * pageSize;
|
||||
uint64_t wiredMemory = vmstat.wire_count * pageSize;
|
||||
uint64_t compressed = vmstat.compressor_page_count * pageSize;
|
||||
uint64_t reserved = ram->bytesTotal - usableMemory;
|
||||
// https://github.com/st3fan/osx-10.9/blob/34e34a6a539b5a822cda4074e56a7ced9b57da71/system_cmds-597.1.1/vm_stat.tproj/vm_stat.c#L139
|
||||
|
||||
ram->bytesUsed = appMemory + wiredMemory + compressed + reserved;
|
||||
uint64_t pagesFree = vmstat.free_count - vmstat.speculative_count;
|
||||
uint64_t pagesFileBacked = vmstat.external_page_count; // Cached files
|
||||
ram->bytesUsed = ram->bytesTotal - (pagesFree + pagesFileBacked) * instance.state.platform.sysinfo.pageSize;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ const char* ffDetectMemory(FFMemoryResult* ram)
|
||||
MEMORYSTATUSEX statex = {
|
||||
.dwLength = sizeof(statex),
|
||||
};
|
||||
// GlobalMemoryStatusEx() internally uses
|
||||
// NtQuerySystemInformation(SystemBasicPerformanceInformation) in Win 7, and
|
||||
// NtQuerySystemInformation(SystemMemoryUsageInformation) in Win 10
|
||||
if (!GlobalMemoryStatusEx(&statex))
|
||||
return "GlobalMemoryStatusEx() failed";
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ void ffPrepareNetIO(FFNetIOOptions* options)
|
||||
{
|
||||
if (options->detectTotal) return;
|
||||
|
||||
if (time1 != 0) return; // Already prepared
|
||||
|
||||
ffListInit(&ioCounters1, sizeof(FFNetIOResult));
|
||||
ffNetIOGetIoCounters(&ioCounters1, options);
|
||||
time1 = ffTimeGetNow();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "opengl.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/library.h"
|
||||
|
||||
#if __has_include(<GL/gl.h>)
|
||||
@@ -56,26 +57,48 @@ typedef struct EGLData
|
||||
|
||||
static const char* eglHandleContext(FFOpenGLResult* result, EGLData* data)
|
||||
{
|
||||
FF_DEBUG("Making EGL context current");
|
||||
if(data->ffeglMakeCurrent(data->display, data->surface, data->surface, data->context) != EGL_TRUE)
|
||||
{
|
||||
FF_DEBUG("eglMakeCurrent() returned EGL_FALSE");
|
||||
return "eglMakeCurrent returned EGL_FALSE";
|
||||
}
|
||||
|
||||
ffOpenGLHandleResult(result, data->ffglGetString);
|
||||
ffStrbufSetF(&result->library, "EGL %s", data->ffeglQueryString(data->display, EGL_VERSION));
|
||||
FF_DEBUG("OpenGL via EGL detected: version='%s', renderer='%s', vendor='%s', slv='%s', library='%s'",
|
||||
result->version.chars,
|
||||
result->renderer.chars,
|
||||
result->vendor.chars,
|
||||
result->slv.chars,
|
||||
result->library.chars);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data, bool gles)
|
||||
{
|
||||
FF_DEBUG("Creating EGL context (preferred API=%s, client version=%d)", gles ? "OpenGL ES" : "OpenGL", gles ? 2 : 1);
|
||||
data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){
|
||||
EGL_CONTEXT_CLIENT_VERSION, gles ? 2 : 1, // Try GLES 2.0+ first
|
||||
EGL_NONE
|
||||
});
|
||||
if(data->context == EGL_NO_CONTEXT && gles) // Some ANGLE builds support GLES 1.1 only
|
||||
{
|
||||
FF_DEBUG("EGL context creation with GLES 2.x failed, retrying with default attributes (GLES 1.1 fallback)");
|
||||
data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){EGL_NONE});
|
||||
}
|
||||
if(data->context == EGL_NO_CONTEXT)
|
||||
{
|
||||
FF_DEBUG("eglCreateContext() returned EGL_NO_CONTEXT");
|
||||
return "eglCreateContext returned EGL_NO_CONTEXT";
|
||||
}
|
||||
|
||||
FF_DEBUG("EGL context created successfully");
|
||||
|
||||
const char* error = eglHandleContext(result, data);
|
||||
FF_DEBUG("eglHandleContext() returns: %s", error ?: "success");
|
||||
|
||||
FF_DEBUG("Destroying EGL context");
|
||||
data->ffeglDestroyContext(data->display, data->context);
|
||||
return error;
|
||||
}
|
||||
@@ -84,13 +107,21 @@ static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data)
|
||||
{
|
||||
// try use OpenGL API. If failed, use the default API (usually OpenGL ES)
|
||||
bool gles = !data->ffeglBindAPI(EGL_OPENGL_API);
|
||||
FF_DEBUG("eglBindAPI(EGL_OPENGL_API) %s, effective API=%s",
|
||||
gles ? "failed" : "succeeded",
|
||||
gles ? "default (usually OpenGL ES)" : "OpenGL");
|
||||
|
||||
EGLint eglConfigCount;
|
||||
data->ffeglGetConfigs(data->display, &data->config, 1, &eglConfigCount);
|
||||
FF_DEBUG("eglGetConfigs() returned %d config(s)", eglConfigCount);
|
||||
|
||||
if(eglConfigCount == 0)
|
||||
{
|
||||
FF_DEBUG("No EGL config is available");
|
||||
return "eglGetConfigs returned 0 configs";
|
||||
}
|
||||
|
||||
FF_DEBUG("Creating EGL pbuffer surface (%dx%d)", FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT);
|
||||
data->surface = data->ffeglCreatePbufferSurface(data->display, data->config, (EGLint[]){
|
||||
EGL_WIDTH, FF_OPENGL_BUFFER_WIDTH,
|
||||
EGL_HEIGHT, FF_OPENGL_BUFFER_HEIGHT,
|
||||
@@ -98,39 +129,71 @@ static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data)
|
||||
});
|
||||
|
||||
if(data->surface == EGL_NO_SURFACE)
|
||||
{
|
||||
FF_DEBUG("eglCreatePbufferSurface() returned EGL_NO_SURFACE");
|
||||
return "eglCreatePbufferSurface returned EGL_NO_SURFACE";
|
||||
}
|
||||
|
||||
FF_DEBUG("EGL pbuffer surface created successfully");
|
||||
|
||||
const char* error = eglHandleSurface(result, data, gles);
|
||||
FF_DEBUG("eglHandleSurface() returns: %s", error ?: "success");
|
||||
|
||||
FF_DEBUG("Destroying EGL surface");
|
||||
data->ffeglDestroySurface(data->display, data->surface);
|
||||
return error;
|
||||
}
|
||||
|
||||
static const char* eglHandleData(FFOpenGLResult* result, EGLData* data)
|
||||
{
|
||||
FF_DEBUG("Resolving glGetString via eglGetProcAddress()");
|
||||
data->ffglGetString = (__typeof__(&glGetString)) data->ffeglGetProcAddress("glGetString");
|
||||
if(!data->ffglGetString)
|
||||
{
|
||||
FF_DEBUG("eglGetProcAddress('glGetString') returned NULL");
|
||||
return "eglGetProcAddress(glGetString) returned NULL";
|
||||
}
|
||||
|
||||
#if EGL_VERSION_1_5
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC ffeglGetPlatformDisplay = (PFNEGLGETPLATFORMDISPLAYEXTPROC) data->ffeglGetProcAddress("eglGetPlatformDisplay");
|
||||
if (ffeglGetPlatformDisplay)
|
||||
{
|
||||
FF_DEBUG("Trying eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA)");
|
||||
data->display = ffeglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, NULL, NULL);
|
||||
FF_DEBUG("eglGetPlatformDisplay() %s", data->display == EGL_NO_DISPLAY ? "failed" : "succeeded");
|
||||
}
|
||||
else
|
||||
FF_DEBUG("eglGetPlatformDisplay is unavailable, falling back to eglGetDisplay");
|
||||
|
||||
if(!ffeglGetPlatformDisplay || data->display == EGL_NO_DISPLAY)
|
||||
#endif
|
||||
|
||||
{
|
||||
FF_DEBUG("Trying eglGetDisplay(EGL_DEFAULT_DISPLAY)");
|
||||
data->display = data->ffeglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
if(data->display == EGL_NO_DISPLAY)
|
||||
{
|
||||
FF_DEBUG("eglGetDisplay() returned EGL_NO_DISPLAY");
|
||||
return "eglGetDisplay returned EGL_NO_DISPLAY";
|
||||
}
|
||||
|
||||
FF_DEBUG("eglGetDisplay() succeeded");
|
||||
}
|
||||
|
||||
|
||||
EGLint major, minor;
|
||||
if(data->ffeglInitialize(data->display, &major, &minor) == EGL_FALSE)
|
||||
{
|
||||
FF_DEBUG("eglInitialize() returned EGL_FALSE");
|
||||
return "eglInitialize returned EGL_FALSE";
|
||||
}
|
||||
|
||||
FF_DEBUG("EGL initialized successfully: %d.%d", major, minor);
|
||||
|
||||
const char* error = eglHandleDisplay(result, data);
|
||||
FF_DEBUG("eglHandleDisplay() returns: %s", error ?: "success");
|
||||
|
||||
FF_DEBUG("Terminating EGL display connection");
|
||||
data->ffeglTerminate(data->display);
|
||||
return error;
|
||||
}
|
||||
@@ -138,6 +201,7 @@ static const char* eglHandleData(FFOpenGLResult* result, EGLData* data)
|
||||
|
||||
const char* ffOpenGLDetectByEGL(FFOpenGLResult* result)
|
||||
{
|
||||
FF_DEBUG("Starting OpenGL detection via EGL");
|
||||
EGLData eglData;
|
||||
|
||||
FF_LIBRARY_LOAD_MESSAGE(egl, "libEGL" FF_LIBRARY_EXTENSION, 1);
|
||||
@@ -154,8 +218,15 @@ const char* ffOpenGLDetectByEGL(FFOpenGLResult* result)
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroySurface);
|
||||
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglTerminate);
|
||||
|
||||
FF_DEBUG("Loaded EGL library and required symbols");
|
||||
|
||||
FF_SUPPRESS_IO();
|
||||
return eglHandleData(result, &eglData);
|
||||
FF_DEBUG("Suppressed stdout/stderr during EGL probing");
|
||||
|
||||
const char* error = eglHandleData(result, &eglData);
|
||||
FF_DEBUG("OpenGL detection via EGL returns: %s", error ?: "success");
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif //FF_HAVE_EGL
|
||||
|
||||
@@ -313,21 +313,7 @@ static bool detectBedrock(FFOSResult* os)
|
||||
{
|
||||
const char* bedrockRestrict = getenv("BEDROCK_RESTRICT");
|
||||
if(bedrockRestrict && bedrockRestrict[0] == '1') return false;
|
||||
if(parseOsRelease(FASTFETCH_TARGET_DIR_ROOT "/bedrock" FASTFETCH_TARGET_DIR_ETC "/bedrock-release", os))
|
||||
{
|
||||
if(os->id.length == 0)
|
||||
ffStrbufAppendS(&os->id, "bedrock");
|
||||
|
||||
if(os->name.length == 0)
|
||||
ffStrbufAppendS(&os->name, "Bedrock");
|
||||
|
||||
if(os->prettyName.length == 0)
|
||||
ffStrbufAppendS(&os->prettyName, "Bedrock Linux");
|
||||
|
||||
parseOsRelease("/bedrock" FASTFETCH_TARGET_DIR_ETC "/os-release", os);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return parseOsRelease(FASTFETCH_TARGET_DIR_ROOT "/bedrock/strata/bedrock/etc/os-release", os);
|
||||
}
|
||||
|
||||
static void detectOS(FFOSResult* os)
|
||||
|
||||
@@ -412,6 +412,45 @@ static uint32_t getFlatpakPackages(FFstrbuf* baseDir, const char* dirname)
|
||||
return num_elements;
|
||||
}
|
||||
|
||||
static uint32_t getPacmanPackages(FFstrbuf* baseDir)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY dbPath = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY rootDir = ffStrbufCreate();
|
||||
|
||||
// Get path to pacman.conf
|
||||
uint32_t baseDirLen = baseDir->length;
|
||||
ffStrbufAppendS(baseDir, "/etc/pacman.conf");
|
||||
|
||||
bool confFound = ffParsePropFileValues(baseDir->chars, 2, (FFpropquery[]){
|
||||
{ "DBPath =", &dbPath },
|
||||
{ "RootDir =", &rootDir },
|
||||
});
|
||||
ffStrbufSubstrBefore(baseDir, baseDirLen);
|
||||
|
||||
if (confFound)
|
||||
{
|
||||
if (dbPath.length > 0)
|
||||
{
|
||||
// If DBPath is specified, use it
|
||||
ffStrbufEnsureEndsWithC(&dbPath, '/');
|
||||
ffStrbufAppendS(&dbPath, "local");
|
||||
}
|
||||
else if (rootDir.length > 0)
|
||||
{
|
||||
// ... otherwise, use RootDir
|
||||
ffStrbufDestroy(&dbPath);
|
||||
ffStrbufInitMove(&dbPath, &rootDir);
|
||||
ffStrbufEnsureEndsWithC(&dbPath, '/');
|
||||
ffStrbufAppendS(&dbPath, "var/lib/pacman/local");
|
||||
}
|
||||
}
|
||||
|
||||
if (dbPath.length == 0)
|
||||
ffStrbufSetStatic(&dbPath, "/var/lib/pacman/local");
|
||||
|
||||
return getNumElements(baseDir, dbPath.chars, true);
|
||||
}
|
||||
|
||||
static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts, FFPackagesOptions* options)
|
||||
{
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_APK_BIT)) packageCounts->apk += getNumStrings(baseDir, "/lib/apk/db/installed", "C:Q", "apk");
|
||||
@@ -426,7 +465,7 @@ static void getPackageCounts(FFstrbuf* baseDir, FFPackagesResult* packageCounts,
|
||||
packageCounts->nixDefault += ffPackagesGetNix(baseDir, "/nix/var/nix/profiles/default");
|
||||
packageCounts->nixSystem += ffPackagesGetNix(baseDir, "/run/current-system");
|
||||
}
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_PACMAN_BIT)) packageCounts->pacman += getNumElements(baseDir, "/var/lib/pacman/local", true);
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_PACMAN_BIT)) packageCounts->pacman += getPacmanPackages(baseDir);
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_LPKGBUILD_BIT)) packageCounts->lpkgbuild += getNumElements(baseDir, "/opt/Loc-OS-LPKG/lpkgbuild/remove", false);
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_PKGTOOL_BIT)) packageCounts->pkgtool += getNumElements(baseDir, "/var/log/packages", false);
|
||||
if (!(options->disabled & FF_PACKAGES_FLAG_RPM_BIT))
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/io.h"
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <windows.h>
|
||||
#include "common/windows/nt.h"
|
||||
#include <ntstatus.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t* ignore)
|
||||
{
|
||||
@@ -17,7 +19,7 @@ static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t
|
||||
|
||||
bool flag = ignore == NULL;
|
||||
uint32_t counter = 0;
|
||||
uint8_t buffer[64 * 1024] __attribute__((aligned(8))); // Required for WoA
|
||||
alignas(8) uint8_t buffer[64 * 1024];
|
||||
BOOLEAN firstScan = TRUE;
|
||||
|
||||
size_t ignoreLen = ignore ? wcslen(ignore) : 0;
|
||||
@@ -76,7 +78,7 @@ static inline void wrapYyjsonFree(yyjson_doc** doc)
|
||||
static void detectScoop(FFPackagesResult* result)
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY scoopPath = ffStrbufCreateA(MAX_PATH + 3);
|
||||
ffStrbufAppendS(&scoopPath, instance.state.platform.homeDir.chars);
|
||||
ffStrbufAppend(&scoopPath, &instance.state.platform.homeDir);
|
||||
ffStrbufAppendS(&scoopPath, ".config/scoop/config.json");
|
||||
|
||||
yyjson_val* root = NULL;
|
||||
@@ -107,7 +109,12 @@ static void detectScoop(FFPackagesResult* result)
|
||||
ffStrbufSetJsonVal(&scoopPath, yyjson_obj_get(root, "global_path"));
|
||||
if (scoopPath.length == 0)
|
||||
{
|
||||
ffStrbufSetS(&scoopPath, getenv("ProgramData"));
|
||||
PWSTR pPath = NULL;
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramData, KF_FLAG_DEFAULT, NULL, &pPath)))
|
||||
{
|
||||
ffStrbufSetWS(&scoopPath, pPath);
|
||||
CoTaskMemFree(pPath);
|
||||
}
|
||||
ffStrbufAppendS(&scoopPath, "/scoop");
|
||||
}
|
||||
ffStrbufAppendS(&scoopPath, "/apps/");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "common/io.h"
|
||||
#include "common/windows/unicode.h"
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
@@ -135,7 +136,7 @@ static bool detectPhysicalDisk(const wchar_t* szDevice, FFlist* result, FFPhysic
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t buffer[sizeof(GET_MEDIA_TYPES) + sizeof(DEVICE_MEDIA_INFO) * 7] = "";
|
||||
alignas(GET_MEDIA_TYPES) uint8_t buffer[sizeof(GET_MEDIA_TYPES) + sizeof(DEVICE_MEDIA_INFO) * 7] = {};
|
||||
GET_MEDIA_TYPES* gmt = (GET_MEDIA_TYPES*) buffer;
|
||||
if(DeviceIoControl(
|
||||
hDevice,
|
||||
|
||||
@@ -123,7 +123,7 @@ static const char* detectFromSystemProfiler(FFlist* result)
|
||||
|
||||
FF_MAYBE_UNUSED static const char* detectFromIokit(FFlist* result)
|
||||
{
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/chosen");
|
||||
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen");
|
||||
if (!entryDevice)
|
||||
return "IORegistryEntryFromPath() failed";
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/swap.h>
|
||||
#include <limits.h>
|
||||
#include <stdalign.h>
|
||||
|
||||
enum { FFMaxNSwap = 8 };
|
||||
|
||||
const char* ffDetectSwap(FFlist* result)
|
||||
{
|
||||
char strings[FFMaxNSwap][PATH_MAX];
|
||||
uint8_t buffer[sizeof(swaptbl_t) + sizeof(swapent_t) * (FFMaxNSwap - 1)] = {};
|
||||
alignas(swaptbl_t) uint8_t buffer[sizeof(swaptbl_t) + sizeof(swapent_t) * (FFMaxNSwap - 1)] = {};
|
||||
swaptbl_t* table = (swaptbl_t*) buffer;
|
||||
table->swt_n = FFMaxNSwap;
|
||||
for (int i = 0; i < FFMaxNSwap; ++i)
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#include <winternl.h>
|
||||
#include <ntstatus.h>
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <stdalign.h>
|
||||
|
||||
const char* detectByNqsi(FFlist* result)
|
||||
const char* ffDetectSwap(FFlist* result)
|
||||
{
|
||||
uint8_t buffer[4096];
|
||||
alignas(SYSTEM_PAGEFILE_INFORMATION) uint8_t buffer[4096];
|
||||
ULONG size = sizeof(buffer);
|
||||
SYSTEM_PAGEFILE_INFORMATION* pstart = (SYSTEM_PAGEFILE_INFORMATION*) buffer;
|
||||
if(!NT_SUCCESS(NtQuerySystemInformation(SystemPagefileInformation, pstart, size, &size)))
|
||||
@@ -29,25 +29,3 @@ const char* detectByNqsi(FFlist* result)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* detectByKgpi(FFlist* result)
|
||||
{
|
||||
PERFORMANCE_INFORMATION pi = {};
|
||||
if (!K32GetPerformanceInfo(&pi, sizeof(pi)))
|
||||
return "K32GetPerformanceInfo(&pi, sizeof(pi)) failed";
|
||||
FFSwapResult* swap = ffListAdd(result);
|
||||
ffStrbufInitS(&swap->name, "Page File");
|
||||
swap->bytesTotal = (uint64_t) (pi.CommitLimit > pi.PhysicalTotal ? pi.CommitLimit - pi.PhysicalTotal : 0) * pi.PageSize;
|
||||
swap->bytesUsed = (uint64_t) (pi.CommitTotal > pi.PhysicalTotal ? pi.CommitTotal - pi.PhysicalTotal : 0) * pi.PageSize;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffDetectSwap(FFlist* result)
|
||||
{
|
||||
const char* err = detectByNqsi(result);
|
||||
if (err == NULL)
|
||||
return NULL;
|
||||
|
||||
return detectByKgpi(result);
|
||||
}
|
||||
|
||||
@@ -6,11 +6,16 @@
|
||||
#include "common/windows/registry.h"
|
||||
#include "common/windows/unicode.h"
|
||||
#include "common/windows/version.h"
|
||||
#include "common/windows/nt.h"
|
||||
#include "common/stringUtils.h"
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <windows.h>
|
||||
#include <wchar.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <ntstatus.h>
|
||||
#include <winternl.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version);
|
||||
|
||||
@@ -186,17 +191,29 @@ static bool detectDefaultTerminal(FFTerminalResult* result)
|
||||
{
|
||||
ffStrbufSetS(&result->processName, "WindowsTerminal.exe");
|
||||
ffStrbufSetS(&result->prettyName, "WindowsTerminal");
|
||||
ffStrbufSetF(&result->exe, "%s\\WindowsApps\\%s\\WindowsTerminal.exe", getenv("ProgramFiles"), path.chars);
|
||||
if(ffPathExists(result->exe.chars, FF_PATHTYPE_FILE))
|
||||
|
||||
PWSTR programFiles = NULL;
|
||||
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &programFiles)))
|
||||
{
|
||||
result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1;
|
||||
ffStrbufSet(&result->exePath, &result->exe);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufDestroy(&result->exe);
|
||||
ffStrbufInitMove(&result->exe, &path);
|
||||
result->exeName = "";
|
||||
ffStrbufSetWS(&result->exe, programFiles);
|
||||
CoTaskMemFree(programFiles);
|
||||
programFiles = NULL;
|
||||
|
||||
ffStrbufAppendS(&result->exe, "\\WindowsApps\\");
|
||||
ffStrbufAppend(&result->exe, &path);
|
||||
ffStrbufAppendS(&result->exe, "\\WindowsTerminal.exe");
|
||||
|
||||
if(ffPathExists(result->exe.chars, FF_PATHTYPE_FILE))
|
||||
{
|
||||
result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1;
|
||||
ffStrbufSet(&result->exePath, &result->exe);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufDestroy(&result->exe);
|
||||
ffStrbufInitMove(&result->exe, &path);
|
||||
result->exeName = "";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -204,14 +221,19 @@ static bool detectDefaultTerminal(FFTerminalResult* result)
|
||||
}
|
||||
}
|
||||
|
||||
conhost:
|
||||
ffStrbufSetF(&result->exe, "%s\\System32\\conhost.exe", getenv("SystemRoot"));
|
||||
if(ffPathExists(result->exe.chars, FF_PATHTYPE_FILE))
|
||||
conhost:;
|
||||
ULONG_PTR conhostPid = 0;
|
||||
ULONG size;
|
||||
if(NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(), ProcessConsoleHostProcess, &conhostPid, sizeof(conhostPid), &size)) && conhostPid != 0)
|
||||
{
|
||||
ffStrbufSetS(&result->processName, "conhost.exe");
|
||||
ffStrbufSetS(&result->prettyName, "conhost");
|
||||
result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1;
|
||||
return true;
|
||||
// For Windows Terminal, it reports the PID of OpenConsole
|
||||
if(ffProcessGetInfoWindows((uint32_t) conhostPid, NULL, &result->processName, &result->exe, &result->exeName, &result->exePath, NULL))
|
||||
{
|
||||
ffStrbufSet(&result->prettyName, &result->processName);
|
||||
if(ffStrbufEndsWithIgnCaseS(&result->prettyName, ".exe"))
|
||||
ffStrbufSubstrBefore(&result->prettyName, result->prettyName.length - 4);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ffStrbufClear(&result->exe);
|
||||
|
||||
@@ -100,7 +100,7 @@ const char* detectBySystemd(FFUsersOptions* options, FFlist* users)
|
||||
|
||||
if (options->myselfOnly)
|
||||
{
|
||||
ffStrbufAppendUInt(&pathUsers, getuid());
|
||||
ffStrbufAppendUInt(&pathUsers, instance.state.platform.uid);
|
||||
detectUserBySystemd(&pathUsers, users);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/debug.h"
|
||||
#include "detection/gpu/gpu.h"
|
||||
#include "detection/vulkan/vulkan.h"
|
||||
|
||||
@@ -39,6 +40,8 @@ static void applyDriverName(VkPhysicalDeviceDriverPropertiesKHR* properties, FFs
|
||||
|
||||
static const char* detectVulkan(FFVulkanResult* result)
|
||||
{
|
||||
FF_DEBUG("Starting Vulkan detection");
|
||||
|
||||
FF_LIBRARY_LOAD_MESSAGE(vulkan,
|
||||
#if __APPLE__
|
||||
"libMoltenVK" FF_LIBRARY_EXTENSION, -1
|
||||
@@ -56,6 +59,7 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
//Some drivers (nvdc) print messages to stdout
|
||||
//and that is the best way I found to disable that
|
||||
FF_SUPPRESS_IO();
|
||||
FF_DEBUG("Suppressed stdout/stderr during Vulkan probing to avoid noisy drivers");
|
||||
|
||||
FFVersion instanceVersion = FF_VERSION_INIT;
|
||||
|
||||
@@ -66,8 +70,15 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
{
|
||||
uint32_t version;
|
||||
if(ffvkEnumerateInstanceVersion(&version) == VK_SUCCESS)
|
||||
{
|
||||
applyVulkanVersion(version, &instanceVersion);
|
||||
FF_DEBUG("Detected Vulkan instance version: %u.%u.%u", instanceVersion.major, instanceVersion.minor, instanceVersion.patch);
|
||||
}
|
||||
else
|
||||
FF_DEBUG("vkEnumerateInstanceVersion() is available but returned a non-success status");
|
||||
}
|
||||
else
|
||||
FF_DEBUG("vkEnumerateInstanceVersion() is unavailable (likely Vulkan 1.0 runtime)");
|
||||
|
||||
const uint32_t projectVersion = VK_MAKE_VERSION(
|
||||
FASTFETCH_PROJECT_VERSION_MAJOR,
|
||||
@@ -76,6 +87,7 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
);
|
||||
|
||||
VkInstance vkInstance;
|
||||
FF_DEBUG("Creating Vulkan instance with requested API version %s", instanceVersion.minor >= 1 ? "1.1" : "1.0");
|
||||
VkResult res = ffvkCreateInstance(&(VkInstanceCreateInfo) {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pNext = NULL,
|
||||
@@ -98,6 +110,7 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
}, NULL, &vkInstance);
|
||||
if(res != VK_SUCCESS)
|
||||
{
|
||||
FF_DEBUG("ffvkCreateInstance() failed with VkResult=%d", res);
|
||||
switch (res)
|
||||
{
|
||||
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
||||
@@ -116,17 +129,22 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
return "ffvkCreateInstance() failed: unknown error";
|
||||
}
|
||||
}
|
||||
FF_DEBUG("Vulkan instance created successfully");
|
||||
|
||||
//if instance creation succeeded, but vkEnumerateInstanceVersion didn't, this means we are running against a vulkan 1.0 implementation
|
||||
//explicitly set this version, if no device is found, so we still have at least this info
|
||||
if(instanceVersion.major == 0 && instanceVersion.minor == 0 && instanceVersion.patch == 0)
|
||||
{
|
||||
instanceVersion.major = 1;
|
||||
FF_DEBUG("Falling back to Vulkan instance version 1.0 due to unavailable enumerate call");
|
||||
}
|
||||
|
||||
VkPhysicalDevice physicalDevices[128];
|
||||
uint32_t physicalDeviceCount = (uint32_t) ARRAY_SIZE(physicalDevices);
|
||||
res = ffvkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, physicalDevices);
|
||||
if(res != VK_SUCCESS)
|
||||
{
|
||||
FF_DEBUG("ffvkEnumeratePhysicalDevices() failed with VkResult=%d", res);
|
||||
ffvkDestroyInstance(vkInstance, NULL);
|
||||
switch (res)
|
||||
{
|
||||
@@ -142,13 +160,22 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
return "ffvkEnumeratePhysicalDevices() failed";
|
||||
}
|
||||
}
|
||||
FF_DEBUG("Enumerated %u Vulkan physical device(s)", physicalDeviceCount);
|
||||
|
||||
PFN_vkGetPhysicalDeviceProperties ffvkGetPhysicalDeviceProperties = NULL;
|
||||
PFN_vkGetPhysicalDeviceProperties2 ffvkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceProperties2"); // 1.1
|
||||
if(!ffvkGetPhysicalDeviceProperties2)
|
||||
ffvkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceProperties");
|
||||
|
||||
FF_DEBUG("Using %s for querying physical device properties", ffvkGetPhysicalDeviceProperties2 ? "vkGetPhysicalDeviceProperties2" : "vkGetPhysicalDeviceProperties");
|
||||
|
||||
PFN_vkGetPhysicalDeviceMemoryProperties ffvkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties) ffvkGetInstanceProcAddr(vkInstance, "vkGetPhysicalDeviceMemoryProperties");
|
||||
if(!ffvkGetPhysicalDeviceMemoryProperties)
|
||||
{
|
||||
FF_DEBUG("vkGetPhysicalDeviceMemoryProperties is unavailable");
|
||||
ffvkDestroyInstance(vkInstance, NULL);
|
||||
return "vkGetPhysicalDeviceMemoryProperties is not available";
|
||||
}
|
||||
|
||||
FFVersion maxDeviceApiVersion = FF_VERSION_INIT;
|
||||
FFVersion maxDeviceConformanceVersion = FF_VERSION_INIT;
|
||||
@@ -172,10 +199,19 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
else
|
||||
ffvkGetPhysicalDeviceProperties(physicalDevices[i], &physicalDeviceProperties.properties);
|
||||
|
||||
FF_DEBUG("Processing Vulkan device #%u: name='%s', vendorId=0x%04X, deviceId=0x%04X, type=%u", i,
|
||||
physicalDeviceProperties.properties.deviceName,
|
||||
physicalDeviceProperties.properties.vendorID,
|
||||
physicalDeviceProperties.properties.deviceID,
|
||||
physicalDeviceProperties.properties.deviceType);
|
||||
|
||||
|
||||
//We don't want software rasterizers to show up as physical gpu
|
||||
if(physicalDeviceProperties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU)
|
||||
{
|
||||
FF_DEBUG("Skipping CPU Vulkan device '%s'", physicalDeviceProperties.properties.deviceName);
|
||||
continue;
|
||||
}
|
||||
|
||||
//If the device api version is higher than the current highest device api version, overwrite it
|
||||
//In this case, also use the current device driver name as the shown driver name
|
||||
@@ -186,6 +222,11 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
{
|
||||
maxDeviceApiVersion = deviceAPIVersion;
|
||||
applyDriverName(&driverProperties, &result->driver);
|
||||
FF_DEBUG("Updated max Vulkan device API version to %u.%u.%u (driver='%s')",
|
||||
maxDeviceApiVersion.major,
|
||||
maxDeviceApiVersion.minor,
|
||||
maxDeviceApiVersion.patch,
|
||||
result->driver.chars);
|
||||
}
|
||||
|
||||
//If the device conformance version is higher than the current highest device conformance version, overwrite it
|
||||
@@ -198,7 +239,13 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
};
|
||||
|
||||
if(ffVersionCompare(&deviceConformanceVersion, &maxDeviceConformanceVersion) > 0)
|
||||
{
|
||||
maxDeviceConformanceVersion = deviceConformanceVersion;
|
||||
FF_DEBUG("Updated max Vulkan conformance version to %u.%u.%u",
|
||||
maxDeviceConformanceVersion.major,
|
||||
maxDeviceConformanceVersion.minor,
|
||||
maxDeviceConformanceVersion.patch);
|
||||
}
|
||||
}
|
||||
|
||||
//Add the device to the list of devices shown by the GPU module
|
||||
@@ -207,7 +254,10 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
FF_LIST_FOR_EACH(FFGPUResult, gpu, result->gpus)
|
||||
{
|
||||
if (gpu->deviceId == physicalDeviceProperties.properties.deviceID)
|
||||
{
|
||||
FF_DEBUG("Skipping duplicate Vulkan GPU entry for deviceId=0x%04X", physicalDeviceProperties.properties.deviceID);
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
FFGPUResult* gpu = ffListAdd(&result->gpus);
|
||||
@@ -221,6 +271,10 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
ffStrbufInitS(&gpu->vendor, ffGPUGetVendorString(physicalDeviceProperties.properties.vendorID));
|
||||
ffStrbufInitS(&gpu->driver, driverProperties.driverInfo);
|
||||
ffStrbufInit(&gpu->memoryType);
|
||||
FF_DEBUG("Added Vulkan GPU '%s' (vendor='%s', type=%s)",
|
||||
gpu->name.chars,
|
||||
gpu->vendor.chars,
|
||||
gpu->type == FF_GPU_TYPE_DISCRETE ? "discrete" : "integrated");
|
||||
|
||||
VkPhysicalDeviceMemoryProperties memoryProperties = {};
|
||||
ffvkGetPhysicalDeviceMemoryProperties(physicalDevices[i], &memoryProperties);
|
||||
@@ -233,6 +287,10 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
FFGPUMemory* vmem = gpu->type == FF_GPU_TYPE_DISCRETE && (heap->flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) ? &gpu->dedicated : &gpu->shared;
|
||||
vmem->total += heap->size;
|
||||
}
|
||||
FF_DEBUG("Computed memory for '%s': dedicatedTotal=%llu, sharedTotal=%llu",
|
||||
gpu->name.chars,
|
||||
(unsigned long long) gpu->dedicated.total,
|
||||
(unsigned long long) gpu->shared.total);
|
||||
|
||||
//No way to detect those using vulkan
|
||||
gpu->index = FF_GPU_INDEX_UNSET;
|
||||
@@ -249,7 +307,14 @@ static const char* detectVulkan(FFVulkanResult* result)
|
||||
ffVersionToPretty(&maxDeviceApiVersion, &result->apiVersion);
|
||||
ffVersionToPretty(&maxDeviceConformanceVersion, &result->conformanceVersion);
|
||||
|
||||
FF_DEBUG("Vulkan detection finished: instanceVersion=%s, apiVersion=%s, conformanceVersion=%s, gpuCount=%u",
|
||||
result->instanceVersion.chars,
|
||||
result->apiVersion.chars,
|
||||
result->conformanceVersion.chars,
|
||||
result->gpus.length);
|
||||
|
||||
ffvkDestroyInstance(vkInstance, NULL);
|
||||
FF_DEBUG("Destroyed Vulkan instance");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -261,6 +326,7 @@ FFVulkanResult* ffDetectVulkan(void)
|
||||
|
||||
if (result.gpus.elementSize == 0)
|
||||
{
|
||||
FF_DEBUG("Initializing Vulkan detection cache");
|
||||
ffStrbufInit(&result.driver);
|
||||
ffStrbufInit(&result.apiVersion);
|
||||
ffStrbufInit(&result.conformanceVersion);
|
||||
@@ -269,10 +335,15 @@ FFVulkanResult* ffDetectVulkan(void)
|
||||
|
||||
#ifdef FF_HAVE_VULKAN
|
||||
result.error = detectVulkan(&result);
|
||||
if (result.error)
|
||||
FF_DEBUG("Vulkan detection returned error: %s", result.error);
|
||||
#else
|
||||
result.error = "fastfetch was compiled without vulkan support";
|
||||
FF_DEBUG("Vulkan support is disabled at compile time");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
FF_DEBUG("Reusing cached Vulkan detection result");
|
||||
|
||||
return &result;
|
||||
}
|
||||
|
||||
@@ -50,9 +50,16 @@ const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_MAYBE
|
||||
if (ffStrbufEqualS(wmName, "WindowServer"))
|
||||
{
|
||||
NSError* error;
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"file:///System/Library/CoreServices/WindowManager.app/Contents/version.plist"]
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"file:///System/Library/PrivateFrameworks/SkyLight.framework/Resources/version.plist"]
|
||||
error:&error];
|
||||
ffStrbufInitS(result, ((NSString*) dict[@"CFBundleVersion"]).UTF8String);
|
||||
if (!dict)
|
||||
{
|
||||
dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:@"file:///System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/version.plist"]
|
||||
error:&error];
|
||||
}
|
||||
|
||||
if (dict)
|
||||
ffStrbufInitS(result, ((NSString*) dict[@"CFBundleShortVersionString"]).UTF8String);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -7,22 +7,13 @@ extern "C"
|
||||
}
|
||||
|
||||
#include "common/windows/com.hpp"
|
||||
#include "common/windows/util.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <shlobj.h>
|
||||
#include <propkey.h>
|
||||
|
||||
template <typename Fn>
|
||||
struct on_scope_exit {
|
||||
on_scope_exit(Fn &&fn): _fn(std::move(fn)) {}
|
||||
~on_scope_exit() { this->_fn(); }
|
||||
|
||||
private:
|
||||
Fn _fn;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
const char* ffDetectWMPlugin(FFstrbuf* pluginName)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
|
||||
#ifdef __sun
|
||||
#include <libzfs.h>
|
||||
#ifndef __illumos__
|
||||
// On Solaris 11, zpool_get_prop has only 5 arguments. #2173
|
||||
#define ffzpool_get_prop(zhp, prop, buf, len, srctype, literal) \
|
||||
ffzpool_get_prop(zhp, prop, buf, len, srctype)
|
||||
#endif
|
||||
#else
|
||||
#include "libzfs_simplified.h"
|
||||
#endif
|
||||
|
||||
+81
-64
@@ -1,4 +1,5 @@
|
||||
#include "fastfetch.h"
|
||||
#include "common/ffdata.h"
|
||||
#include "detection/version/version.h"
|
||||
#include "logo/logo.h"
|
||||
#include "common/commandoption.h"
|
||||
@@ -379,16 +380,16 @@ static void listModules(bool pretty)
|
||||
}
|
||||
}
|
||||
|
||||
static bool parseJsoncFile(const char* path, yyjson_read_flag flg)
|
||||
static bool parseJsoncFile(FFdata* data, const char* path, yyjson_read_flag flg)
|
||||
{
|
||||
assert(!instance.state.configDoc);
|
||||
assert(!data->configDoc);
|
||||
|
||||
{
|
||||
yyjson_read_err error;
|
||||
instance.state.configDoc = path
|
||||
data->configDoc = path
|
||||
? yyjson_read_file(path, flg, NULL, &error)
|
||||
: yyjson_read_fp(stdin, flg, NULL, &error);
|
||||
if (!instance.state.configDoc)
|
||||
if (!data->configDoc)
|
||||
{
|
||||
if (error.code != YYJSON_READ_ERROR_FILE_OPEN)
|
||||
{
|
||||
@@ -412,7 +413,7 @@ static bool parseJsoncFile(const char* path, yyjson_read_flag flg)
|
||||
{
|
||||
const char* error = NULL;
|
||||
|
||||
yyjson_val* const root = yyjson_doc_get_root(instance.state.configDoc);
|
||||
yyjson_val* const root = yyjson_doc_get_root(data->configDoc);
|
||||
if (!yyjson_is_obj(root))
|
||||
error = "Invalid JSON config format. Root value must be an object";
|
||||
|
||||
@@ -432,8 +433,14 @@ static bool parseJsoncFile(const char* path, yyjson_read_flag flg)
|
||||
}
|
||||
|
||||
|
||||
static void generateConfigFile(bool force, const char* filePath, bool fullConfig)
|
||||
static void generateConfigFile(FFdata* data, bool force, const char* filePath, bool fullConfig)
|
||||
{
|
||||
if (data->resultDoc)
|
||||
{
|
||||
fprintf(stderr, "Error: duplicated `--gen-config` or `--format json` flags found\n");
|
||||
exit(477);
|
||||
}
|
||||
|
||||
if (!filePath)
|
||||
{
|
||||
if (instance.state.platform.configDirs.length == 0)
|
||||
@@ -443,22 +450,23 @@ static void generateConfigFile(bool force, const char* filePath, bool fullConfig
|
||||
}
|
||||
|
||||
FFstrbuf* configDir = FF_LIST_FIRST(FFstrbuf, instance.state.platform.configDirs);
|
||||
ffStrbufEnsureFixedLengthFree(&instance.state.genConfigPath, configDir->length + strlen("fastfetch/config.jsonc"));
|
||||
ffStrbufSet(&instance.state.genConfigPath, configDir);
|
||||
ffStrbufAppendS(&instance.state.genConfigPath, "fastfetch/config.jsonc");
|
||||
ffStrbufEnsureFixedLengthFree(&data->genConfigPath, configDir->length + strlen("fastfetch/config.jsonc"));
|
||||
ffStrbufSet(&data->genConfigPath, configDir);
|
||||
ffStrbufAppendS(&data->genConfigPath, "fastfetch/config.jsonc");
|
||||
}
|
||||
else
|
||||
{
|
||||
ffStrbufSetS(&instance.state.genConfigPath, filePath);
|
||||
ffStrbufSetS(&data->genConfigPath, filePath);
|
||||
}
|
||||
|
||||
if (!force && ffPathExists(instance.state.genConfigPath.chars, FF_PATHTYPE_ANY))
|
||||
if (!force && ffPathExists(data->genConfigPath.chars, FF_PATHTYPE_ANY))
|
||||
{
|
||||
fprintf(stderr, "Error: file `%s` exists. Use `--gen-config%s-force` to overwrite\n", instance.state.genConfigPath.chars, fullConfig ? "-full" : "");
|
||||
fprintf(stderr, "Error: file `%s` exists. Use `--gen-config%s-force` to overwrite\n", data->genConfigPath.chars, fullConfig ? "-full" : "");
|
||||
exit(477);
|
||||
}
|
||||
|
||||
instance.state.fullConfig = fullConfig;
|
||||
data->docType = fullConfig ? FF_RESULT_DOC_TYPE_CONFIG_FULL : FF_RESULT_DOC_TYPE_CONFIG;
|
||||
data->resultDoc = yyjson_mut_doc_new(NULL);
|
||||
}
|
||||
|
||||
static void optionParseConfigFile(FFdata* data, const char* key, const char* value)
|
||||
@@ -482,7 +490,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
|
||||
|
||||
if (value[0] == '-' && value[1] == '\0')
|
||||
{
|
||||
parseJsoncFile(NULL, false);
|
||||
parseJsoncFile(data, NULL, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -503,7 +511,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
|
||||
? YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS
|
||||
: YYJSON_READ_JSON5;
|
||||
|
||||
if (parseJsoncFile(absolutePath.chars, flag)) return;
|
||||
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
|
||||
|
||||
//Try to load as a relative path with the config directory
|
||||
|
||||
@@ -515,7 +523,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
|
||||
if (needExtension)
|
||||
ffStrbufAppendS(&absolutePath, ".jsonc");
|
||||
|
||||
if (parseJsoncFile(absolutePath.chars, flag)) return;
|
||||
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
|
||||
}
|
||||
|
||||
//Try to load as a preset
|
||||
@@ -528,7 +536,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
|
||||
if (needExtension)
|
||||
ffStrbufAppendS(&absolutePath, ".jsonc");
|
||||
|
||||
if (parseJsoncFile(absolutePath.chars, flag)) return;
|
||||
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
|
||||
}
|
||||
|
||||
//Try to load as a relative path with the directory of fastfetch binary, for Windows support
|
||||
@@ -543,7 +551,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
|
||||
ffStrbufAppendS(&absolutePath, value);
|
||||
if (needExtension)
|
||||
ffStrbufAppendS(&absolutePath, ".jsonc");
|
||||
if (parseJsoncFile(absolutePath.chars, flag)) return;
|
||||
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
|
||||
|
||||
// Try {exePath}/presets/
|
||||
ffStrbufSubstrBefore(&absolutePath, lastSlash);
|
||||
@@ -551,7 +559,7 @@ static void optionParseConfigFile(FFdata* data, const char* key, const char* val
|
||||
ffStrbufAppendS(&absolutePath, value);
|
||||
if (needExtension)
|
||||
ffStrbufAppendS(&absolutePath, ".jsonc");
|
||||
if (parseJsoncFile(absolutePath.chars, flag)) return;
|
||||
if (parseJsoncFile(data, absolutePath.chars, flag)) return;
|
||||
}
|
||||
|
||||
//File not found
|
||||
@@ -566,24 +574,17 @@ 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)
|
||||
static void enableJsonOutput(FFdata* data)
|
||||
{
|
||||
if (!enable)
|
||||
if (data->resultDoc)
|
||||
{
|
||||
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));
|
||||
}
|
||||
fprintf(stderr, "Error: duplicated `--gen-config` or `--format json` flags found\n");
|
||||
exit(477);
|
||||
}
|
||||
|
||||
data->resultDoc = yyjson_mut_doc_new(NULL);
|
||||
data->docType = FF_RESULT_DOC_TYPE_JSON;
|
||||
yyjson_mut_doc_set_root(data->resultDoc, yyjson_mut_arr(data->resultDoc));
|
||||
}
|
||||
|
||||
static void parseCommand(FFdata* data, char* key, char* value)
|
||||
@@ -668,24 +669,26 @@ static void parseCommand(FFdata* data, char* key, char* value)
|
||||
exit(0);
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(key, "--gen-config"))
|
||||
generateConfigFile(false, value, false);
|
||||
generateConfigFile(data, false, value, false);
|
||||
else if(ffStrEqualsIgnCase(key, "--gen-config-force"))
|
||||
generateConfigFile(true, value, false);
|
||||
generateConfigFile(data, true, value, false);
|
||||
else if(ffStrEqualsIgnCase(key, "--gen-config-full"))
|
||||
generateConfigFile(false, value, true);
|
||||
generateConfigFile(data, false, value, true);
|
||||
else if(ffStrEqualsIgnCase(key, "--gen-config-full-force"))
|
||||
generateConfigFile(true, value, true);
|
||||
generateConfigFile(data, 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));
|
||||
{
|
||||
if (ffOptionParseBoolean(value)) enableJsonOutput(data);
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(key, "--format"))
|
||||
{
|
||||
enableJsonOutput(!!ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
|
||||
if (!!ffOptionParseEnum(key, value, (FFKeyValuePair[]) {
|
||||
{ "default", false},
|
||||
{ "json", true },
|
||||
{},
|
||||
}));
|
||||
})) enableJsonOutput(data);
|
||||
}
|
||||
else if(ffStrEqualsIgnCase(key, "--dynamic-interval"))
|
||||
instance.state.dynamicInterval = ffOptionParseUInt32(key, value); // seconds to milliseconds
|
||||
@@ -710,6 +713,9 @@ static void parseOption(FFdata* data, const char* key, const char* value)
|
||||
ffParseModuleOptions(key, value)
|
||||
) {}
|
||||
|
||||
else if(ffStrEqualsIgnCase(key, "--structure-disabled"))
|
||||
ffOptionParseString(key, value, &data->structureDisabled);
|
||||
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: unknown option: %s\n", key);
|
||||
@@ -717,21 +723,21 @@ static void parseOption(FFdata* data, const char* key, const char* value)
|
||||
}
|
||||
}
|
||||
|
||||
static void parseConfigFiles(void)
|
||||
static void parseConfigFiles(FFdata* data)
|
||||
{
|
||||
if (__builtin_expect(instance.state.genConfigPath.length == 0, true))
|
||||
if (__builtin_expect(data->genConfigPath.length == 0, true))
|
||||
{
|
||||
FF_LIST_FOR_EACH(FFstrbuf, dir, instance.state.platform.configDirs)
|
||||
{
|
||||
uint32_t dirLength = dir->length;
|
||||
|
||||
ffStrbufAppendS(dir, "fastfetch/config.jsonc");
|
||||
bool success = parseJsoncFile(dir->chars, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS);
|
||||
bool success = parseJsoncFile(data, dir->chars, YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
if (success) return;
|
||||
|
||||
ffStrbufAppendS(dir, "fastfetch/config.json5");
|
||||
success = parseJsoncFile(dir->chars, YYJSON_READ_JSON5);
|
||||
success = parseJsoncFile(data, dir->chars, YYJSON_READ_JSON5);
|
||||
ffStrbufSubstrBefore(dir, dirLength);
|
||||
if (success) return;
|
||||
}
|
||||
@@ -769,10 +775,10 @@ static void parseArguments(FFdata* data, int argc, char** argv, void (*parser)(F
|
||||
|
||||
static void run(FFdata* data)
|
||||
{
|
||||
const bool useJsonConfig = data->structure.length == 0 && instance.state.configDoc;
|
||||
const bool useJsonConfig = data->structure.length == 0 && data->configDoc;
|
||||
|
||||
if (useJsonConfig)
|
||||
ffPrintJsonConfig(true /* prepare */, instance.state.resultDoc);
|
||||
ffPrintJsonConfig(data, true /* prepare */);
|
||||
else
|
||||
{
|
||||
//If we don't have a custom structure, use the default one
|
||||
@@ -783,6 +789,9 @@ static void run(FFdata* data)
|
||||
|
||||
ffStart();
|
||||
|
||||
if (!data->resultDoc)
|
||||
ffLogoPrint();
|
||||
|
||||
#if defined(_WIN32)
|
||||
if (!instance.config.display.noBuffer) fflush(stdout);
|
||||
#endif
|
||||
@@ -790,9 +799,9 @@ static void run(FFdata* data)
|
||||
while (true)
|
||||
{
|
||||
if (useJsonConfig)
|
||||
ffPrintJsonConfig(false, instance.state.resultDoc);
|
||||
ffPrintJsonConfig(data, false);
|
||||
else
|
||||
ffPrintCommandOption(data, instance.state.resultDoc);
|
||||
ffPrintCommandOption(data);
|
||||
|
||||
if (instance.state.dynamicInterval > 0)
|
||||
{
|
||||
@@ -802,10 +811,15 @@ static void run(FFdata* data)
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
if (useJsonConfig)
|
||||
ffPrintJsonConfig(data, true /* prepare */);
|
||||
else
|
||||
ffPrepareCommandOption(data);
|
||||
}
|
||||
|
||||
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);
|
||||
if (data->resultDoc)
|
||||
yyjson_mut_write_fp(stdout, data->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)
|
||||
@@ -816,20 +830,20 @@ static void run(FFdata* data)
|
||||
|
||||
static void writeConfigFile(FFdata* data)
|
||||
{
|
||||
const FFstrbuf* filename = &instance.state.genConfigPath;
|
||||
const FFstrbuf* filename = &data->genConfigPath;
|
||||
|
||||
yyjson_mut_doc* doc = yyjson_mut_doc_new(NULL);
|
||||
yyjson_mut_doc* doc = data->resultDoc;
|
||||
yyjson_mut_val* root = yyjson_mut_obj(doc);
|
||||
yyjson_mut_doc_set_root(doc, root);
|
||||
yyjson_mut_obj_add_str(doc, root, "$schema", "https://github.com/fastfetch-cli/fastfetch/raw/master/doc/json_schema.json");
|
||||
|
||||
if (instance.state.fullConfig)
|
||||
if (data->docType == FF_RESULT_DOC_TYPE_CONFIG_FULL)
|
||||
{
|
||||
ffOptionsGenerateLogoJsonConfig(&instance.config.logo, doc);
|
||||
ffOptionsGenerateDisplayJsonConfig(&instance.config.display, doc);
|
||||
ffOptionsGenerateGeneralJsonConfig(&instance.config.general, doc);
|
||||
ffOptionsGenerateLogoJsonConfig(data, &instance.config.logo);
|
||||
ffOptionsGenerateDisplayJsonConfig(data, &instance.config.display);
|
||||
ffOptionsGenerateGeneralJsonConfig(data, &instance.config.general);
|
||||
}
|
||||
ffMigrateCommandOptionToJsonc(data, doc);
|
||||
ffMigrateCommandOptionToJsonc(data);
|
||||
|
||||
if (ffStrbufEqualS(filename, "-"))
|
||||
yyjson_mut_write_fp(stdout, doc, YYJSON_WRITE_INF_AND_NAN_AS_NULL | YYJSON_WRITE_PRETTY_TWO_SPACES | YYJSON_WRITE_NEWLINE_AT_END, NULL, NULL);
|
||||
@@ -854,8 +868,6 @@ static void writeConfigFile(FFdata* data)
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
yyjson_mut_doc_free(doc);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
@@ -866,24 +878,29 @@ int main(int argc, char** argv)
|
||||
//Data stores things only needed for the configuration of fastfetch
|
||||
FFdata data = {
|
||||
.structure = ffStrbufCreate(),
|
||||
.configLoaded = false,
|
||||
.structureDisabled = ffStrbufCreate(),
|
||||
.genConfigPath = ffStrbufCreate(),
|
||||
};
|
||||
|
||||
parseArguments(&data, argc, argv, parseCommand);
|
||||
if(instance.state.dynamicInterval && instance.state.resultDoc)
|
||||
if(instance.state.dynamicInterval && data.resultDoc)
|
||||
{
|
||||
fprintf(stderr, "Error: --dynamic-interval cannot be used with --json\n");
|
||||
exit(400);
|
||||
}
|
||||
|
||||
if(!data.configLoaded && !getenv("NO_CONFIG"))
|
||||
parseConfigFiles();
|
||||
parseConfigFiles(&data);
|
||||
parseArguments(&data, argc, argv, (void*) parseOption);
|
||||
|
||||
if (__builtin_expect(instance.state.genConfigPath.length == 0, true))
|
||||
if (__builtin_expect(data.genConfigPath.length == 0, true))
|
||||
run(&data);
|
||||
else
|
||||
writeConfigFile(&data);
|
||||
|
||||
ffStrbufDestroy(&data.structure);
|
||||
ffStrbufDestroy(&data.structureDisabled);
|
||||
yyjson_doc_free(data.configDoc);
|
||||
yyjson_mut_doc_free(data.resultDoc);
|
||||
ffStrbufDestroy(&data.genConfigPath);
|
||||
}
|
||||
|
||||
+1
-12
@@ -5,12 +5,6 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef FF_USE_SYSTEM_YYJSON
|
||||
#include <yyjson.h>
|
||||
#else
|
||||
#include "3rdparty/yyjson/yyjson.h"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
@@ -40,13 +34,8 @@ typedef struct FFstate
|
||||
uint32_t keysHeight;
|
||||
bool terminalLightTheme;
|
||||
bool titleFqdn;
|
||||
|
||||
FFPlatform platform;
|
||||
yyjson_doc* configDoc;
|
||||
yyjson_mut_doc* resultDoc;
|
||||
FFstrbuf genConfigPath;
|
||||
bool fullConfig;
|
||||
uint32_t dynamicInterval;
|
||||
FFPlatform platform;
|
||||
} FFstate;
|
||||
|
||||
typedef struct FFinstance
|
||||
|
||||
+4
-1
@@ -16,9 +16,12 @@ int main(void)
|
||||
instance.config.display.freqSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
instance.config.display.sizeSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
|
||||
// Logo printing and other preparation stuff
|
||||
// Some preparation stuff
|
||||
ffStart();
|
||||
|
||||
// Print logo
|
||||
ffLogoPrint();
|
||||
|
||||
// Print all modules
|
||||
{
|
||||
__attribute__((cleanup(ffDestroyTitleOptions))) FFTitleOptions options;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
██████ ██████
|
||||
██$2██████$1████$2██████$1██
|
||||
██$3████$2████████████████$1██
|
||||
██$2██$3████$2██████████████████$1██
|
||||
██$2██$3██$2████$3████████████$2████████$1██
|
||||
██$2██████$3████████████████$2██████$1██
|
||||
██$2████████████████████████████$1██
|
||||
██$2████████████████████████$1██
|
||||
██$2██████$3████████$2██████$1██
|
||||
████$2████████████$1████
|
||||
████████████
|
||||
+20
-20
@@ -1,20 +1,20 @@
|
||||
$1 ▗▄▄▄ $2▗▄▄▄▄ ▄▄▄▖
|
||||
$1 ▜███▙ $2▜███▙ ▟███▛
|
||||
$1 ▜███▙ $2▜███▙▟███▛
|
||||
$1 ▜███▙ $2▜██████▛
|
||||
$1 ▟█████████████████▙ $2▜████▛ $1▟▙
|
||||
$1 ▟███████████████████▙ $2▜███▙ $1▟██▙
|
||||
$2 ▄▄▄▄▖ ▜███▙ $1▟███▛
|
||||
$2 ▟███▛ ▜██▛ $1▟███▛
|
||||
$2 ▟███▛ ▜▛ $1▟███▛
|
||||
$2▟███████████▛ $1▟██████████▙
|
||||
$2▜██████████▛ $1▟███████████▛
|
||||
$2 ▟███▛ $1▟▙ ▟███▛
|
||||
$2 ▟███▛ $1▟██▙ ▟███▛
|
||||
$2 ▟███▛ $1▜███▙ ▝▀▀▀▀
|
||||
$2 ▜██▛ $1▜███▙ $2▜██████████████████▛
|
||||
$2 ▜▛ $1▟████▙ $2▜████████████████▛
|
||||
$1 ▟██████▙ $2▜███▙
|
||||
$1 ▟███▛▜███▙ $2▜███▙
|
||||
$1 ▟███▛ ▜███▙ $2▜███▙
|
||||
$1 ▝▀▀▀ ▀▀▀▀▘ $2▀▀▀▘
|
||||
$1▗▄▄▄ $2▗▄▄▄▄ ▄▄▄▖
|
||||
$1▜███▙ $2▜███▙ ▟███▛
|
||||
$1▜███▙ $2▜███▙▟███▛
|
||||
$1▜███▙ $2▜██████▛
|
||||
$1▟█████████████████▙ $2▜████▛ $3▟▙
|
||||
$1▟███████████████████▙ $2▜███▙ $3▟██▙
|
||||
$6▄▄▄▄▖ $2▜███▙ $3▟███▛
|
||||
$6▟███▛ $2▜██▛ $3▟███▛
|
||||
$6▟███▛ $2▜▛ $3▟███▛
|
||||
$6▟███████████▛ $3▟██████████▙
|
||||
$6▜██████████▛ $3▟███████████▛
|
||||
$6▟███▛ $5▟▙ $3▟███▛
|
||||
$6▟███▛ $5▟██▙ $3▟███▛
|
||||
$6▟███▛ $5▜███▙ $3▝▀▀▀▀
|
||||
$6▜██▛ $5▜███▙ $4▜██████████████████▛
|
||||
$6▜▛ $5▟████▙ $4▜████████████████▛
|
||||
$5▟██████▙ $4▜███▙
|
||||
$5▟███▛▜███▙ $4▜███▙
|
||||
$5▟███▛ ▜███▙ $4▜███▙
|
||||
$5▝▀▀▀ ▀▀▀▀▘ $4▀▀▀▘
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
$1 ▗▄ $2▗▄ ▄▖
|
||||
$1 ▄▄🬸█▄▄▄$2🬸█▛ $1▃
|
||||
$2 ▟▛ ▜$1▃▟🬕
|
||||
$2🬋🬋🬫█ $1█🬛🬋🬋
|
||||
$2 🬷▛🮃$1▙ ▟▛
|
||||
$2 🮃 $1▟█🬴$2▀▀▀█🬴▀▀
|
||||
$1 ▝▀ ▀▘ $2▀▘
|
||||
$1 ▄▄🬸█▄▄▄$2🬸█▛ $3▃
|
||||
$6 ▟▛ ▜$3▃▟🬕
|
||||
$6🬋🬋🬫█ $3█🬛🬋🬋
|
||||
$6 🬷▛🮃$5▙ $4▟▛
|
||||
$6 🮃 $5▟█🬴$4▀▀▀█🬴▀▀
|
||||
$5▝▀ ▀▘ $4▀▘
|
||||
@@ -1,17 +1,21 @@
|
||||
.;oo;.
|
||||
.;ldddol;.
|
||||
.:cldddddolc:.
|
||||
':lloddddoooolcc:'
|
||||
':lllodddddooooolccc:,
|
||||
,:lllloddddooooollllccccc,.
|
||||
.,clllllddddooooollllllccc::::,.
|
||||
.,clllllloodoooooooollllccc:::::::,.
|
||||
'cllllllllllllloooooollcccc::::::::::,
|
||||
,lllllllllllllooooolllccc::::::::::,
|
||||
,cllllllllllllloollllcc::::::::::;
|
||||
'clllllllllllllllcccc::::::::;:,
|
||||
.clllllllccccc::cc:::::::::;:'
|
||||
.cllllccccc:;;;:cc::::::;;:.
|
||||
.cccccccc;;;:::::cc:::;;:.
|
||||
.:cccc;;;:::::::::c::;:.
|
||||
.;::::;;;;;;;;;;;;;::.
|
||||
⣤⣤ ⢀⡄
|
||||
⢀⣾⣦ ⠙⠛ ⢀⣴⣿⣷ ⢠⣤
|
||||
⣤⡄ ⣸⣿⣿⣷⣄ ⣠⣶⣿⣿⣿⣿ ⠈⠉ ⢀⣤⡀
|
||||
⠉⠁ ⢠⣿⣿⣿⣿⣿⣦⡈⢿⣿⣿⣿⣿⣿⡇⢀⣀⣤⣤⣤⣤⣴⣶⠆⠘⠿⠃ ⠉ ⡀
|
||||
⢤⣄⡀ ⣾⣿⣿⣿⣿⣿⣿⣷⡄⠻⣿⣿⣿⣿⡇⢸⣿⣿⣿⣿⣿⣿⡏ ⡇
|
||||
⠘⣿⣿⣿⣿⣶⣤⣈⣉⠛⠿⢿⣿⣿⣿⣿⣦⠘⢿⣿⣿⣷⠈⣿⣿⣿⣿⣿⠏ ⠐⠁
|
||||
⢿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣦⣤⣉⡛⠻⢿⣷⣄⠻⣿⣿ ⣿⣿⣿⣿⠟⢠⣀ ⡐⠁
|
||||
⢰⣶ ⠘⣿⣿⣿⣿⣿⣿⣿⠿⠿⠛⠛⠉⠉ ⠈⠉ ⠘⣿ ⣿⣿⣿⡏⢠⣿⣿⣿⣶⣦⣄⡀ ⠂
|
||||
⠈⠁ ⠙⠋⣉⣉⣤⣤⣶⣶⠾⠉ ⠈ ⢹⣿⠏⢠⣿⣿⣿⣿⣿⣿⣿⣿⠟
|
||||
⢀⣠⣤⣴⣶⣿⣿⣿⣿⣿⣿⣿⠟⠁ ⢸⠟⣰⣿⣿⣿⣿⣿⡿⠟⠉
|
||||
⠈⠛⢿⣿⣿⣿⣿⣿⣿⣿⠟⢁⣴⡏ ⠈⣰⣿⣿⣿⠿⠛⣁⡀ ⢀⡒
|
||||
⢤⡀ ⠙⢿⣿⣿⣿⠟⢁⣴⣿⣿ ⣰⡿⠟⢉⣠⣴⣾⣿⣿⣆
|
||||
⠛⠁ ⠔ ⠈⠛⢁⣴⣿⣿⣿⡇⢰⡆ ⠊⣁⣤⣾⣿⣿⣿⣿⣿⣿⣿⣷⡀
|
||||
⡠⠂ ⢀⣴⣿⣿⣿⣿⣿⠃⣾⣿⡄⢢⡀ ⠤⠶⠿⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡄
|
||||
⢀⠈ ⢀⣴⣿⣿⣿⣿⣿⣿⡿⢠⣿⣿⣷ ⢿⣷⣄⠑⢶⣶⣶⣶⣶⣶⣤⣤⣤⣤⣤⣤
|
||||
⠠⠁ ⠚⠛⠿⠿⢿⣿⣿⣿⡇⢸⣿⣿⣿⣧⠈⢿⣿⣿⣦⣌⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⡄ ⢰⣶
|
||||
⡆ ⣼⣿⣿⣿⣿⣇⠘⣿⣿⣿⣿⣿⣦⣈⠛⢿⣿⣿⣿⣿⣿⡇
|
||||
⢁ ⣀ ⠄⠂⠁⢀⣿⣿⣿⣿⣿⣿⡆⠹⣿⣿⣿⣿⣿⣿⠗ ⠈⠛⢿⣿⣿⡇
|
||||
⠁⠂ ⠒⠒⠈⠁ ⢸⣿⣿⣿⠿⠛⠉ ⢻⣿⣿⣿⣿⠟ ⠈⠙⠃
|
||||
⠼⠛⠉⠁ ⢻⣿⣿⠏ ⢀⣄
|
||||
⠿⠇ ⠈⠿⠁ ⠘⠋
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
⢠⣦⡀⠘⠁⢀⣴⣾ ⢀⡄
|
||||
⠛ ⣸⣿⣿⣄⢰⣿⣿⣿⡄⢀⣀⣀⣀⡀⢾⠆⠐⠂⢄
|
||||
⠰⣤⣄⡀ ⢿⣿⣿⣿⣧⡹⣿⣿⡇⣿⣿⣿⡿⠁
|
||||
⢻⣿⣿⣿⣶⣶⣭⣝⡻⠷⣌⢿⡇⢸⣿⡿⢁⣀
|
||||
⠘⠛ ⠸⠿⠟⣛⣋⡭⠅ ⠻⢸⡿⢡⣿⣿⣿⣶⣤⠁
|
||||
⠠⣤⣶⣶⣿⣿⡿⢋ ⠸⣡⣿⣿⡿⠟⠉
|
||||
⢠ ⠈⠻⣿⡿⢋⣴⡟ ⢰⠟⣋⣥⣾⣦⡀ ⠁
|
||||
⠈⠠⠊ ⢈⣴⣿⣿⣇⣧⢀ ⢀⣠⣴⣿⣿⣿⣿⣿⣷⡄
|
||||
⣰⣿⣿⣿⣿⢹⣿⣆⢳⣦⣐⠶⣶⣦⣤⣬⣭⣭⠉⢉⠉⠉
|
||||
⡀ ⠈⠉⠉⢉⢸⣿⣿⣆⢻⣿⣷⣮⡙⢿⣿⣿⣿ ⠉
|
||||
⣀⣀ ⠄⠐⠈ ⣾⣿⣿⠿ ⣿⣿⣿⡿ ⠉⠻⢿
|
||||
⠛⠉ ⢀⡀⠘⣿⠟ ⢠⡀
|
||||
⠈⠁ ⠁
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user